From d78b38e80f348db25511c37191ce154edc7ea094 Mon Sep 17 00:00:00 2001 From: Enoch Date: Fri, 9 Aug 2024 22:16:39 +0800 Subject: [PATCH] INIT --- .env.case | 24 + .gitignore | 3 + LICENSE | 21 + README.md | 10 + app/controller/IndexController.php | 28 + app/controller/LayAuth.php | 88 + app/functions.php | 4 + app/middleware/StaticFile.php | 42 + app/model/Test.php | 29 + app/view/404.html | 68 + app/view/auth.html | 146 + app/view/success.html | 101 + composer.json | 71 + composer.lock | 4863 ++++++++++++++++ config/app.php | 26 + config/autoload.php | 21 + config/bootstrap.php | 18 + config/container.php | 15 + config/database.php | 38 + config/dependence.php | 15 + config/event.php | 5 + config/exception.php | 17 + config/log.php | 32 + config/middleware.php | 15 + config/plugin/webman/event/app.php | 4 + config/plugin/webman/event/bootstrap.php | 17 + config/plugin/webman/event/command.php | 7 + config/process.php | 42 + config/redis.php | 22 + config/route.php | 23 + config/server.php | 31 + config/session.php | 65 + config/static.php | 23 + config/translation.php | 25 + config/view.php | 22 + process/Monitor.php | 243 + public/404.html | 12 + public/favicon.ico | Bin 0 -> 4286 bytes start.php | 4 + support/Request.php | 24 + support/Response.php | 24 + support/bootstrap.php | 133 + support/helpers.php | 540 ++ vendor/autoload.php | 25 + vendor/bin/carbon | 119 + vendor/bin/phinx | 119 + vendor/bin/var-dump-server | 119 + vendor/brick/math/CHANGELOG.md | 445 ++ vendor/brick/math/LICENSE | 20 + vendor/brick/math/composer.json | 34 + vendor/brick/math/src/BigDecimal.php | 786 +++ vendor/brick/math/src/BigInteger.php | 1079 ++++ vendor/brick/math/src/BigNumber.php | 512 ++ vendor/brick/math/src/BigRational.php | 445 ++ .../src/Exception/DivisionByZeroException.php | 35 + .../Exception/IntegerOverflowException.php | 23 + .../math/src/Exception/MathException.php | 12 + .../src/Exception/NegativeNumberException.php | 12 + .../src/Exception/NumberFormatException.php | 33 + .../Exception/RoundingNecessaryException.php | 19 + vendor/brick/math/src/Internal/Calculator.php | 676 +++ .../Internal/Calculator/BcMathCalculator.php | 75 + .../src/Internal/Calculator/GmpCalculator.php | 108 + .../Internal/Calculator/NativeCalculator.php | 581 ++ vendor/brick/math/src/RoundingMode.php | 107 + vendor/cakephp/core/App.php | 269 + vendor/cakephp/core/BasePlugin.php | 305 + vendor/cakephp/core/ClassLoader.php | 139 + vendor/cakephp/core/Configure.php | 498 ++ .../core/Configure/ConfigEngineInterface.php | 44 + .../core/Configure/Engine/IniConfig.php | 203 + .../core/Configure/Engine/JsonConfig.php | 115 + .../core/Configure/Engine/PhpConfig.php | 114 + .../core/Configure/FileConfigTrait.php | 72 + .../core/ConsoleApplicationInterface.php | 42 + vendor/cakephp/core/Container.php | 28 + .../core/ContainerApplicationInterface.php | 45 + vendor/cakephp/core/ContainerInterface.php | 32 + vendor/cakephp/core/ConventionsTrait.php | 156 + .../cakephp/core/Exception/CakeException.php | 123 + vendor/cakephp/core/Exception/Exception.php | 21 + .../core/Exception/MissingPluginException.php | 26 + .../cakephp/core/HttpApplicationInterface.php | 43 + vendor/cakephp/core/InstanceConfigTrait.php | 312 + vendor/cakephp/core/LICENSE.txt | 22 + vendor/cakephp/core/ObjectRegistry.php | 416 ++ vendor/cakephp/core/Plugin.php | 136 + .../core/PluginApplicationInterface.php | 75 + vendor/cakephp/core/PluginCollection.php | 363 ++ vendor/cakephp/core/PluginInterface.php | 135 + vendor/cakephp/core/README.md | 37 + vendor/cakephp/core/Retry/CommandRetry.php | 94 + .../core/Retry/RetryStrategyInterface.php | 35 + vendor/cakephp/core/ServiceConfig.php | 50 + vendor/cakephp/core/ServiceProvider.php | 132 + vendor/cakephp/core/StaticConfigTrait.php | 325 ++ .../core/TestSuite/ContainerStubTrait.php | 181 + vendor/cakephp/core/composer.json | 44 + vendor/cakephp/core/functions.php | 340 ++ vendor/cakephp/core/functions_global.php | 190 + vendor/cakephp/database/Connection.php | 1197 ++++ .../cakephp/database/ConstraintsInterface.php | 49 + vendor/cakephp/database/Driver.php | 559 ++ vendor/cakephp/database/Driver/Mysql.php | 345 ++ vendor/cakephp/database/Driver/Postgres.php | 348 ++ .../database/Driver/SqlDialectTrait.php | 315 + vendor/cakephp/database/Driver/Sqlite.php | 385 ++ vendor/cakephp/database/Driver/Sqlserver.php | 569 ++ .../Driver/TupleComparisonTranslatorTrait.php | 113 + vendor/cakephp/database/DriverInterface.php | 336 ++ vendor/cakephp/database/Exception.php | 10 + .../database/Exception/DatabaseException.php | 37 + .../Exception/MissingConnectionException.php | 30 + .../Exception/MissingDriverException.php | 30 + .../Exception/MissingExtensionException.php | 31 + .../NestedTransactionRollbackException.php | 41 + .../Expression/AggregateExpression.php | 253 + .../database/Expression/BetweenExpression.php | 144 + .../database/Expression/CaseExpression.php | 251 + .../Expression/CaseExpressionTrait.php | 109 + .../Expression/CaseStatementExpression.php | 597 ++ .../Expression/CommonTableExpression.php | 239 + .../database/Expression/Comparison.php | 7 + .../Expression/ComparisonExpression.php | 324 ++ .../database/Expression/FieldInterface.php | 39 + .../database/Expression/FieldTrait.php | 51 + .../Expression/FunctionExpression.php | 178 + .../Expression/IdentifierExpression.php | 119 + .../database/Expression/OrderByExpression.php | 88 + .../Expression/OrderClauseExpression.php | 90 + .../database/Expression/QueryExpression.php | 876 +++ .../database/Expression/StringExpression.php | 87 + .../database/Expression/TupleComparison.php | 231 + .../database/Expression/UnaryExpression.php | 118 + .../database/Expression/ValuesExpression.php | 325 ++ .../Expression/WhenThenExpression.php | 350 ++ .../database/Expression/WindowExpression.php | 335 ++ .../database/Expression/WindowInterface.php | 163 + .../cakephp/database/ExpressionInterface.php | 44 + .../cakephp/database/FieldTypeConverter.php | 140 + vendor/cakephp/database/FunctionsBuilder.php | 376 ++ vendor/cakephp/database/IdentifierQuoter.php | 269 + vendor/cakephp/database/LICENSE.txt | 22 + vendor/cakephp/database/Log/LoggedQuery.php | 176 + .../cakephp/database/Log/LoggingStatement.php | 219 + vendor/cakephp/database/Log/QueryLogger.php | 57 + vendor/cakephp/database/PostgresCompiler.php | 93 + vendor/cakephp/database/Query.php | 2516 ++++++++ vendor/cakephp/database/Query/DeleteQuery.php | 222 + vendor/cakephp/database/Query/InsertQuery.php | 322 ++ vendor/cakephp/database/Query/SelectQuery.php | 127 + vendor/cakephp/database/Query/UpdateQuery.php | 182 + vendor/cakephp/database/QueryCompiler.php | 462 ++ vendor/cakephp/database/README.md | 358 ++ .../database/Retry/ErrorCodeWaitStrategy.php | 69 + .../database/Retry/ReconnectStrategy.php | 124 + vendor/cakephp/database/Schema/BaseSchema.php | 10 + .../database/Schema/CachedCollection.php | 131 + vendor/cakephp/database/Schema/Collection.php | 168 + .../database/Schema/CollectionInterface.php | 54 + .../cakephp/database/Schema/MysqlSchema.php | 10 + .../database/Schema/MysqlSchemaDialect.php | 669 +++ .../database/Schema/PostgresSchema.php | 10 + .../database/Schema/PostgresSchemaDialect.php | 704 +++ .../cakephp/database/Schema/SchemaDialect.php | 346 ++ .../database/Schema/SqlGeneratorInterface.php | 72 + .../cakephp/database/Schema/SqliteSchema.php | 10 + .../database/Schema/SqliteSchemaDialect.php | 649 +++ .../database/Schema/SqlserverSchema.php | 10 + .../Schema/SqlserverSchemaDialect.php | 707 +++ .../cakephp/database/Schema/TableSchema.php | 793 +++ .../Schema/TableSchemaAwareInterface.php | 38 + .../database/Schema/TableSchemaInterface.php | 283 + vendor/cakephp/database/SchemaCache.php | 114 + vendor/cakephp/database/SqlDialectTrait.php | 10 + vendor/cakephp/database/SqliteCompiler.php | 33 + vendor/cakephp/database/SqlserverCompiler.php | 167 + .../database/Statement/BufferResultsTrait.php | 45 + .../database/Statement/BufferedStatement.php | 361 ++ .../database/Statement/CallbackStatement.php | 77 + .../database/Statement/MysqlStatement.php | 46 + .../database/Statement/PDOStatement.php | 176 + .../database/Statement/SqliteStatement.php | 70 + .../database/Statement/SqlserverStatement.php | 53 + .../database/Statement/StatementDecorator.php | 373 ++ .../cakephp/database/StatementInterface.php | 203 + vendor/cakephp/database/Type.php | 9 + vendor/cakephp/database/Type/BaseType.php | 80 + .../database/Type/BatchCastingInterface.php | 37 + vendor/cakephp/database/Type/BinaryType.php | 92 + .../cakephp/database/Type/BinaryUuidType.php | 145 + vendor/cakephp/database/Type/BoolType.php | 126 + .../Type/ColumnSchemaAwareInterface.php | 29 + .../database/Type/DateTimeFractionalType.php | 42 + .../database/Type/DateTimeTimezoneType.php | 44 + vendor/cakephp/database/Type/DateTimeType.php | 563 ++ vendor/cakephp/database/Type/DateType.php | 174 + vendor/cakephp/database/Type/DecimalType.php | 190 + .../Type/ExpressionTypeCasterTrait.php | 80 + .../database/Type/ExpressionTypeInterface.php | 36 + vendor/cakephp/database/Type/FloatType.php | 171 + vendor/cakephp/database/Type/IntegerType.php | 129 + vendor/cakephp/database/Type/JsonType.php | 124 + .../Type/OptionalConvertInterface.php | 32 + vendor/cakephp/database/Type/StringType.php | 110 + vendor/cakephp/database/Type/TimeType.php | 52 + vendor/cakephp/database/Type/UuidType.php | 67 + .../cakephp/database/TypeConverterTrait.php | 66 + vendor/cakephp/database/TypeFactory.php | 171 + vendor/cakephp/database/TypeInterface.php | 91 + vendor/cakephp/database/TypeMap.php | 160 + vendor/cakephp/database/TypeMapTrait.php | 89 + .../cakephp/database/TypedResultInterface.php | 38 + vendor/cakephp/database/TypedResultTrait.php | 53 + vendor/cakephp/database/ValueBinder.php | 163 + vendor/cakephp/database/composer.json | 40 + .../datasource/ConnectionInterface.php | 154 + .../cakephp/datasource/ConnectionManager.php | 216 + .../cakephp/datasource/ConnectionRegistry.php | 104 + vendor/cakephp/datasource/EntityInterface.php | 288 + vendor/cakephp/datasource/EntityTrait.php | 1304 +++++ .../Exception/InvalidPrimaryKeyException.php | 26 + .../MissingDatasourceConfigException.php | 28 + .../Exception/MissingDatasourceException.php | 28 + .../Exception/MissingModelException.php | 30 + .../Exception/PageOutOfBoundsException.php | 10 + .../Exception/RecordNotFoundException.php | 26 + vendor/cakephp/datasource/FactoryLocator.php | 103 + .../cakephp/datasource/FixtureInterface.php | 73 + .../datasource/InvalidPropertyInterface.php | 61 + vendor/cakephp/datasource/LICENSE.txt | 22 + .../datasource/Locator/AbstractLocator.php | 115 + .../datasource/Locator/LocatorInterface.php | 68 + vendor/cakephp/datasource/ModelAwareTrait.php | 241 + vendor/cakephp/datasource/Paginator.php | 10 + .../cakephp/datasource/PaginatorInterface.php | 10 + .../Exception/PageOutOfBoundsException.php | 35 + .../datasource/Paging/NumericPaginator.php | 734 +++ .../datasource/Paging/PaginatorInterface.php | 50 + .../datasource/Paging/SimplePaginator.php | 49 + vendor/cakephp/datasource/QueryCacher.php | 137 + vendor/cakephp/datasource/QueryInterface.php | 406 ++ vendor/cakephp/datasource/QueryTrait.php | 1465 +++++ vendor/cakephp/datasource/README.md | 82 + .../datasource/RepositoryInterface.php | 252 + .../cakephp/datasource/ResultSetDecorator.php | 61 + .../cakephp/datasource/ResultSetInterface.php | 30 + vendor/cakephp/datasource/RuleInvoker.php | 142 + vendor/cakephp/datasource/RulesAwareTrait.php | 120 + vendor/cakephp/datasource/RulesChecker.php | 330 ++ vendor/cakephp/datasource/SchemaInterface.php | 166 + vendor/cakephp/datasource/SimplePaginator.php | 10 + vendor/cakephp/datasource/composer.json | 42 + vendor/cakephp/utility/CookieCryptTrait.php | 192 + vendor/cakephp/utility/Crypto/OpenSsl.php | 79 + .../utility/Exception/XmlException.php | 25 + vendor/cakephp/utility/Hash.php | 1274 ++++ vendor/cakephp/utility/Inflector.php | 524 ++ vendor/cakephp/utility/LICENSE.txt | 22 + .../cakephp/utility/MergeVariablesTrait.php | 118 + vendor/cakephp/utility/README.md | 91 + vendor/cakephp/utility/Security.php | 309 + vendor/cakephp/utility/Text.php | 1180 ++++ vendor/cakephp/utility/Xml.php | 502 ++ vendor/cakephp/utility/bootstrap.php | 21 + vendor/cakephp/utility/composer.json | 43 + .../carbonphp/carbon-doctrine-types/LICENSE | 21 + .../carbonphp/carbon-doctrine-types/README.md | 14 + .../carbon-doctrine-types/composer.json | 36 + .../Carbon/Doctrine/CarbonDoctrineType.php | 14 + .../Carbon/Doctrine/CarbonImmutableType.php | 7 + .../src/Carbon/Doctrine/CarbonType.php | 7 + .../Carbon/Doctrine/CarbonTypeConverter.php | 141 + .../Doctrine/DateTimeDefaultPrecision.php | 28 + .../Carbon/Doctrine/DateTimeImmutableType.php | 20 + .../src/Carbon/Doctrine/DateTimeType.php | 12 + vendor/composer/ClassLoader.php | 579 ++ vendor/composer/InstalledVersions.php | 359 ++ vendor/composer/LICENSE | 21 + vendor/composer/autoload_classmap.php | 18 + vendor/composer/autoload_files.php | 30 + vendor/composer/autoload_namespaces.php | 10 + vendor/composer/autoload_psr4.php | 77 + vendor/composer/autoload_real.php | 50 + vendor/composer/autoload_static.php | 463 ++ vendor/composer/installed.json | 5046 ++++++++++++++++ vendor/composer/installed.php | 679 +++ vendor/composer/platform_check.php | 30 + vendor/doctrine/inflector/LICENSE | 19 + vendor/doctrine/inflector/README.md | 7 + vendor/doctrine/inflector/composer.json | 41 + vendor/doctrine/inflector/docs/en/index.rst | 226 + .../Inflector/CachedWordInflector.php | 24 + .../GenericLanguageInflectorFactory.php | 66 + .../lib/Doctrine/Inflector/Inflector.php | 507 ++ .../Doctrine/Inflector/InflectorFactory.php | 52 + .../lib/Doctrine/Inflector/Language.php | 19 + .../Inflector/LanguageInflectorFactory.php | 33 + .../Doctrine/Inflector/NoopWordInflector.php | 13 + .../Inflector/Rules/English/Inflectible.php | 184 + .../Rules/English/InflectorFactory.php | 21 + .../Inflector/Rules/English/Rules.php | 31 + .../Inflector/Rules/English/Uninflected.php | 189 + .../Inflector/Rules/French/Inflectible.php | 44 + .../Rules/French/InflectorFactory.php | 21 + .../Doctrine/Inflector/Rules/French/Rules.php | 31 + .../Inflector/Rules/French/Uninflected.php | 28 + .../Rules/NorwegianBokmal/Inflectible.php | 34 + .../NorwegianBokmal/InflectorFactory.php | 21 + .../Inflector/Rules/NorwegianBokmal/Rules.php | 31 + .../Rules/NorwegianBokmal/Uninflected.php | 30 + .../lib/Doctrine/Inflector/Rules/Pattern.php | 42 + .../lib/Doctrine/Inflector/Rules/Patterns.php | 34 + .../Rules/Portuguese/Inflectible.php | 98 + .../Rules/Portuguese/InflectorFactory.php | 21 + .../Inflector/Rules/Portuguese/Rules.php | 31 + .../Rules/Portuguese/Uninflected.php | 32 + .../lib/Doctrine/Inflector/Rules/Ruleset.php | 39 + .../Inflector/Rules/Spanish/Inflectible.php | 47 + .../Rules/Spanish/InflectorFactory.php | 21 + .../Inflector/Rules/Spanish/Rules.php | 31 + .../Inflector/Rules/Spanish/Uninflected.php | 30 + .../Doctrine/Inflector/Rules/Substitution.php | 30 + .../Inflector/Rules/Substitutions.php | 57 + .../Inflector/Rules/Transformation.php | 39 + .../Inflector/Rules/Transformations.php | 29 + .../Inflector/Rules/Turkish/Inflectible.php | 34 + .../Rules/Turkish/InflectorFactory.php | 21 + .../Inflector/Rules/Turkish/Rules.php | 31 + .../Inflector/Rules/Turkish/Uninflected.php | 30 + .../lib/Doctrine/Inflector/Rules/Word.php | 21 + .../Doctrine/Inflector/RulesetInflector.php | 56 + .../lib/Doctrine/Inflector/WordInflector.php | 10 + vendor/graham-campbell/result-type/LICENSE | 21 + .../graham-campbell/result-type/composer.json | 33 + .../graham-campbell/result-type/src/Error.php | 121 + .../result-type/src/Result.php | 69 + .../result-type/src/Success.php | 120 + vendor/guzzlehttp/guzzle/CHANGELOG.md | 1664 ++++++ vendor/guzzlehttp/guzzle/LICENSE | 27 + vendor/guzzlehttp/guzzle/README.md | 94 + vendor/guzzlehttp/guzzle/UPGRADING.md | 1253 ++++ vendor/guzzlehttp/guzzle/composer.json | 131 + .../guzzlehttp/guzzle/src/BodySummarizer.php | 28 + .../guzzle/src/BodySummarizerInterface.php | 13 + vendor/guzzlehttp/guzzle/src/Client.php | 483 ++ .../guzzlehttp/guzzle/src/ClientInterface.php | 84 + vendor/guzzlehttp/guzzle/src/ClientTrait.php | 241 + .../guzzle/src/Cookie/CookieJar.php | 307 + .../guzzle/src/Cookie/CookieJarInterface.php | 80 + .../guzzle/src/Cookie/FileCookieJar.php | 101 + .../guzzle/src/Cookie/SessionCookieJar.php | 77 + .../guzzle/src/Cookie/SetCookie.php | 488 ++ .../src/Exception/BadResponseException.php | 39 + .../guzzle/src/Exception/ClientException.php | 10 + .../guzzle/src/Exception/ConnectException.php | 56 + .../guzzle/src/Exception/GuzzleException.php | 9 + .../Exception/InvalidArgumentException.php | 7 + .../guzzle/src/Exception/RequestException.php | 150 + .../guzzle/src/Exception/ServerException.php | 10 + .../Exception/TooManyRedirectsException.php | 7 + .../src/Exception/TransferException.php | 7 + .../guzzle/src/Handler/CurlFactory.php | 736 +++ .../src/Handler/CurlFactoryInterface.php | 25 + .../guzzle/src/Handler/CurlHandler.php | 49 + .../guzzle/src/Handler/CurlMultiHandler.php | 284 + .../guzzle/src/Handler/EasyHandle.php | 112 + .../guzzle/src/Handler/HeaderProcessor.php | 42 + .../guzzle/src/Handler/MockHandler.php | 212 + .../guzzlehttp/guzzle/src/Handler/Proxy.php | 51 + .../guzzle/src/Handler/StreamHandler.php | 621 ++ vendor/guzzlehttp/guzzle/src/HandlerStack.php | 275 + .../guzzle/src/MessageFormatter.php | 199 + .../guzzle/src/MessageFormatterInterface.php | 18 + vendor/guzzlehttp/guzzle/src/Middleware.php | 268 + vendor/guzzlehttp/guzzle/src/Pool.php | 125 + .../guzzle/src/PrepareBodyMiddleware.php | 105 + .../guzzle/src/RedirectMiddleware.php | 228 + .../guzzlehttp/guzzle/src/RequestOptions.php | 274 + .../guzzlehttp/guzzle/src/RetryMiddleware.php | 119 + .../guzzlehttp/guzzle/src/TransferStats.php | 133 + vendor/guzzlehttp/guzzle/src/Utils.php | 384 ++ vendor/guzzlehttp/guzzle/src/functions.php | 167 + .../guzzle/src/functions_include.php | 6 + vendor/guzzlehttp/promises/CHANGELOG.md | 116 + vendor/guzzlehttp/promises/LICENSE | 24 + vendor/guzzlehttp/promises/README.md | 546 ++ vendor/guzzlehttp/promises/composer.json | 53 + .../promises/src/AggregateException.php | 17 + .../promises/src/CancellationException.php | 10 + vendor/guzzlehttp/promises/src/Coroutine.php | 169 + vendor/guzzlehttp/promises/src/Create.php | 84 + vendor/guzzlehttp/promises/src/Each.php | 90 + .../guzzlehttp/promises/src/EachPromise.php | 247 + .../promises/src/FulfilledPromise.php | 84 + vendor/guzzlehttp/promises/src/Is.php | 46 + vendor/guzzlehttp/promises/src/Promise.php | 278 + .../promises/src/PromiseInterface.php | 97 + .../promises/src/PromisorInterface.php | 16 + .../promises/src/RejectedPromise.php | 91 + .../promises/src/RejectionException.php | 48 + vendor/guzzlehttp/promises/src/TaskQueue.php | 67 + .../promises/src/TaskQueueInterface.php | 24 + vendor/guzzlehttp/promises/src/Utils.php | 276 + vendor/guzzlehttp/promises/src/functions.php | 363 ++ .../promises/src/functions_include.php | 6 + vendor/guzzlehttp/psr7/CHANGELOG.md | 465 ++ vendor/guzzlehttp/psr7/LICENSE | 26 + vendor/guzzlehttp/psr7/README.md | 887 +++ vendor/guzzlehttp/psr7/composer.json | 93 + vendor/guzzlehttp/psr7/src/AppendStream.php | 248 + vendor/guzzlehttp/psr7/src/BufferStream.php | 147 + vendor/guzzlehttp/psr7/src/CachingStream.php | 153 + vendor/guzzlehttp/psr7/src/DroppingStream.php | 49 + .../src/Exception/MalformedUriException.php | 14 + vendor/guzzlehttp/psr7/src/FnStream.php | 180 + vendor/guzzlehttp/psr7/src/Header.php | 134 + vendor/guzzlehttp/psr7/src/HttpFactory.php | 94 + vendor/guzzlehttp/psr7/src/InflateStream.php | 37 + vendor/guzzlehttp/psr7/src/LazyOpenStream.php | 49 + vendor/guzzlehttp/psr7/src/LimitStream.php | 157 + vendor/guzzlehttp/psr7/src/Message.php | 246 + vendor/guzzlehttp/psr7/src/MessageTrait.php | 265 + vendor/guzzlehttp/psr7/src/MimeType.php | 1259 ++++ .../guzzlehttp/psr7/src/MultipartStream.php | 165 + vendor/guzzlehttp/psr7/src/NoSeekStream.php | 28 + vendor/guzzlehttp/psr7/src/PumpStream.php | 179 + vendor/guzzlehttp/psr7/src/Query.php | 118 + vendor/guzzlehttp/psr7/src/Request.php | 159 + vendor/guzzlehttp/psr7/src/Response.php | 161 + vendor/guzzlehttp/psr7/src/Rfc7230.php | 23 + vendor/guzzlehttp/psr7/src/ServerRequest.php | 340 ++ vendor/guzzlehttp/psr7/src/Stream.php | 283 + .../psr7/src/StreamDecoratorTrait.php | 156 + vendor/guzzlehttp/psr7/src/StreamWrapper.php | 207 + vendor/guzzlehttp/psr7/src/UploadedFile.php | 211 + vendor/guzzlehttp/psr7/src/Uri.php | 743 +++ vendor/guzzlehttp/psr7/src/UriComparator.php | 52 + vendor/guzzlehttp/psr7/src/UriNormalizer.php | 220 + vendor/guzzlehttp/psr7/src/UriResolver.php | 211 + vendor/guzzlehttp/psr7/src/Utils.php | 477 ++ vendor/illuminate/bus/Batch.php | 480 ++ vendor/illuminate/bus/BatchFactory.php | 58 + vendor/illuminate/bus/BatchRepository.php | 92 + vendor/illuminate/bus/Batchable.php | 108 + vendor/illuminate/bus/BusServiceProvider.php | 70 + .../bus/DatabaseBatchRepository.php | 388 ++ vendor/illuminate/bus/Dispatcher.php | 295 + .../illuminate/bus/Events/BatchDispatched.php | 26 + vendor/illuminate/bus/LICENSE.md | 21 + vendor/illuminate/bus/PendingBatch.php | 319 + .../bus/PrunableBatchRepository.php | 16 + vendor/illuminate/bus/Queueable.php | 276 + vendor/illuminate/bus/UniqueLock.php | 75 + .../illuminate/bus/UpdatedBatchJobCounts.php | 43 + vendor/illuminate/bus/composer.json | 40 + vendor/illuminate/collections/Arr.php | 858 +++ vendor/illuminate/collections/Collection.php | 1753 ++++++ vendor/illuminate/collections/Enumerable.php | 1257 ++++ .../HigherOrderCollectionProxy.php | 63 + .../collections/ItemNotFoundException.php | 9 + vendor/illuminate/collections/LICENSE.md | 21 + .../illuminate/collections/LazyCollection.php | 1720 ++++++ .../MultipleItemsFoundException.php | 40 + .../collections/Traits/EnumeratesValues.php | 1083 ++++ vendor/illuminate/collections/composer.json | 42 + vendor/illuminate/collections/helpers.php | 190 + .../conditionable/HigherOrderWhenProxy.php | 109 + vendor/illuminate/conditionable/LICENSE.md | 21 + .../conditionable/Traits/Conditionable.php | 73 + vendor/illuminate/conditionable/composer.json | 33 + vendor/illuminate/container/BoundMethod.php | 202 + vendor/illuminate/container/Container.php | 1479 +++++ .../container/ContextualBindingBuilder.php | 96 + .../container/EntryNotFoundException.php | 11 + vendor/illuminate/container/LICENSE.md | 21 + .../container/RewindableGenerator.php | 61 + vendor/illuminate/container/Util.php | 74 + vendor/illuminate/container/composer.json | 38 + .../contracts/Auth/Access/Authorizable.php | 15 + .../illuminate/contracts/Auth/Access/Gate.php | 150 + .../contracts/Auth/Authenticatable.php | 49 + .../contracts/Auth/CanResetPassword.php | 21 + vendor/illuminate/contracts/Auth/Factory.php | 22 + vendor/illuminate/contracts/Auth/Guard.php | 57 + .../Auth/Middleware/AuthenticatesRequests.php | 8 + .../contracts/Auth/MustVerifyEmail.php | 34 + .../contracts/Auth/PasswordBroker.php | 61 + .../contracts/Auth/PasswordBrokerFactory.php | 14 + .../contracts/Auth/StatefulGuard.php | 63 + .../contracts/Auth/SupportsBasicAuth.php | 24 + .../contracts/Auth/UserProvider.php | 49 + .../contracts/Broadcasting/Broadcaster.php | 35 + .../contracts/Broadcasting/Factory.php | 14 + .../Broadcasting/HasBroadcastChannel.php | 20 + .../contracts/Broadcasting/ShouldBeUnique.php | 8 + .../Broadcasting/ShouldBroadcast.php | 13 + .../Broadcasting/ShouldBroadcastNow.php | 8 + .../illuminate/contracts/Bus/Dispatcher.php | 66 + .../contracts/Bus/QueueingDispatcher.php | 30 + vendor/illuminate/contracts/Cache/Factory.php | 14 + vendor/illuminate/contracts/Cache/Lock.php | 44 + .../contracts/Cache/LockProvider.php | 25 + .../contracts/Cache/LockTimeoutException.php | 10 + .../illuminate/contracts/Cache/Repository.php | 116 + vendor/illuminate/contracts/Cache/Store.php | 92 + .../contracts/Config/Repository.php | 57 + .../contracts/Console/Application.php | 23 + .../contracts/Console/Isolatable.php | 8 + .../illuminate/contracts/Console/Kernel.php | 64 + .../Console/PromptsForMissingInput.php | 8 + .../Container/BindingResolutionException.php | 11 + .../Container/CircularDependencyException.php | 11 + .../contracts/Container/Container.php | 210 + .../Container/ContextualBindingBuilder.php | 39 + .../illuminate/contracts/Cookie/Factory.php | 47 + .../contracts/Cookie/QueueingFactory.php | 30 + .../contracts/Database/Eloquent/Builder.php | 14 + .../contracts/Database/Eloquent/Castable.php | 14 + .../Database/Eloquent/CastsAttributes.php | 32 + .../Eloquent/CastsInboundAttributes.php | 17 + .../Eloquent/DeviatesCastableAttributes.php | 28 + .../Eloquent/SerializesCastableAttributes.php | 17 + .../Eloquent/SupportsPartialRelations.php | 30 + .../Database/Events/MigrationEvent.php | 8 + .../contracts/Database/ModelIdentifier.php | 73 + .../contracts/Database/Query/Builder.php | 12 + .../contracts/Debug/ExceptionHandler.php | 48 + .../contracts/Encryption/DecryptException.php | 10 + .../contracts/Encryption/EncryptException.php | 10 + .../contracts/Encryption/Encrypter.php | 35 + .../contracts/Encryption/StringEncrypter.php | 26 + .../contracts/Events/Dispatcher.php | 82 + .../illuminate/contracts/Filesystem/Cloud.php | 14 + .../contracts/Filesystem/Factory.php | 14 + .../Filesystem/FileNotFoundException.php | 10 + .../contracts/Filesystem/Filesystem.php | 191 + .../Filesystem/LockTimeoutException.php | 10 + .../contracts/Foundation/Application.php | 231 + .../Foundation/CachesConfiguration.php | 27 + .../contracts/Foundation/CachesRoutes.php | 20 + .../Foundation/ExceptionRenderer.php | 14 + .../contracts/Foundation/MaintenanceMode.php | 35 + .../illuminate/contracts/Hashing/Hasher.php | 42 + vendor/illuminate/contracts/Http/Kernel.php | 37 + vendor/illuminate/contracts/LICENSE.md | 21 + .../illuminate/contracts/Mail/Attachable.php | 13 + vendor/illuminate/contracts/Mail/Factory.php | 14 + .../illuminate/contracts/Mail/MailQueue.php | 25 + vendor/illuminate/contracts/Mail/Mailable.php | 76 + vendor/illuminate/contracts/Mail/Mailer.php | 41 + .../contracts/Notifications/Dispatcher.php | 24 + .../contracts/Notifications/Factory.php | 32 + .../contracts/Pagination/CursorPaginator.php | 117 + .../Pagination/LengthAwarePaginator.php | 29 + .../contracts/Pagination/Paginator.php | 124 + vendor/illuminate/contracts/Pipeline/Hub.php | 15 + .../contracts/Pipeline/Pipeline.php | 40 + .../contracts/Queue/ClearableQueue.php | 14 + .../Queue/EntityNotFoundException.php | 22 + .../contracts/Queue/EntityResolver.php | 15 + vendor/illuminate/contracts/Queue/Factory.php | 14 + vendor/illuminate/contracts/Queue/Job.php | 164 + vendor/illuminate/contracts/Queue/Monitor.php | 30 + vendor/illuminate/contracts/Queue/Queue.php | 99 + .../contracts/Queue/QueueableCollection.php | 34 + .../contracts/Queue/QueueableEntity.php | 27 + .../contracts/Queue/ShouldBeEncrypted.php | 8 + .../contracts/Queue/ShouldBeUnique.php | 8 + .../Queue/ShouldBeUniqueUntilProcessing.php | 8 + .../contracts/Queue/ShouldQueue.php | 8 + .../illuminate/contracts/Redis/Connection.php | 35 + .../illuminate/contracts/Redis/Connector.php | 25 + vendor/illuminate/contracts/Redis/Factory.php | 14 + .../Redis/LimiterTimeoutException.php | 10 + .../contracts/Routing/BindingRegistrar.php | 23 + .../contracts/Routing/Registrar.php | 105 + .../contracts/Routing/ResponseFactory.php | 155 + .../contracts/Routing/UrlGenerator.php | 86 + .../contracts/Routing/UrlRoutable.php | 39 + .../Middleware/AuthenticatesSessions.php | 8 + .../illuminate/contracts/Session/Session.php | 204 + .../contracts/Support/Arrayable.php | 17 + .../Support/CanBeEscapedWhenCastToString.php | 14 + .../contracts/Support/DeferrableProvider.php | 13 + .../Support/DeferringDisplayableValue.php | 13 + .../illuminate/contracts/Support/Htmlable.php | 13 + .../illuminate/contracts/Support/Jsonable.php | 14 + .../contracts/Support/MessageBag.php | 102 + .../contracts/Support/MessageProvider.php | 13 + .../contracts/Support/Renderable.php | 13 + .../contracts/Support/Responsable.php | 14 + .../contracts/Support/ValidatedData.php | 11 + .../Translation/HasLocalePreference.php | 13 + .../contracts/Translation/Loader.php | 40 + .../contracts/Translation/Translator.php | 42 + .../contracts/Validation/DataAwareRule.php | 14 + .../contracts/Validation/Factory.php | 46 + .../contracts/Validation/ImplicitRule.php | 8 + .../contracts/Validation/InvokableRule.php | 16 + .../illuminate/contracts/Validation/Rule.php | 22 + .../Validation/UncompromisedVerifier.php | 14 + .../Validation/ValidatesWhenResolved.php | 13 + .../contracts/Validation/Validator.php | 65 + .../Validation/ValidatorAwareRule.php | 14 + vendor/illuminate/contracts/View/Engine.php | 15 + vendor/illuminate/contracts/View/Factory.php | 79 + vendor/illuminate/contracts/View/View.php | 31 + .../View/ViewCompilationException.php | 10 + vendor/illuminate/contracts/composer.json | 35 + .../illuminate/database/Capsule/Manager.php | 202 + .../database/ClassMorphViolationException.php | 29 + .../database/Concerns/BuildsQueries.php | 506 ++ .../database/Concerns/CompilesJsonPaths.php | 64 + .../database/Concerns/ExplainsQueries.php | 24 + .../database/Concerns/ManagesTransactions.php | 352 ++ .../database/Concerns/ParsesSearchPath.php | 25 + .../database/ConfigurationUrlParser.php | 10 + vendor/illuminate/database/Connection.php | 1593 +++++ .../database/ConnectionInterface.php | 170 + .../database/ConnectionResolver.php | 92 + .../database/ConnectionResolverInterface.php | 29 + .../database/Connectors/ConnectionFactory.php | 277 + .../database/Connectors/Connector.php | 139 + .../Connectors/ConnectorInterface.php | 14 + .../database/Connectors/MySqlConnector.php | 208 + .../database/Connectors/PostgresConnector.php | 216 + .../database/Connectors/SQLiteConnector.php | 39 + .../Connectors/SqlServerConnector.php | 233 + .../Console/DatabaseInspectionCommand.php | 246 + .../illuminate/database/Console/DbCommand.php | 227 + .../database/Console/DumpCommand.php | 101 + .../Console/Factories/FactoryMakeCommand.php | 154 + .../Console/Factories/stubs/factory.stub | 23 + .../Console/Migrations/BaseCommand.php | 51 + .../Console/Migrations/FreshCommand.php | 120 + .../Console/Migrations/InstallCommand.php | 70 + .../Console/Migrations/MigrateCommand.php | 281 + .../Console/Migrations/MigrateMakeCommand.php | 144 + .../Console/Migrations/RefreshCommand.php | 159 + .../Console/Migrations/ResetCommand.php | 91 + .../Console/Migrations/RollbackCommand.php | 91 + .../Console/Migrations/StatusCommand.php | 132 + .../Console/Migrations/TableGuesser.php | 37 + .../database/Console/MonitorCommand.php | 151 + .../database/Console/PruneCommand.php | 186 + .../database/Console/Seeds/SeedCommand.php | 151 + .../Console/Seeds/SeederMakeCommand.php | 103 + .../Console/Seeds/WithoutModelEvents.php | 19 + .../database/Console/Seeds/stubs/seeder.stub | 19 + .../database/Console/ShowCommand.php | 189 + .../database/Console/TableCommand.php | 246 + .../database/Console/WipeCommand.php | 125 + .../database/DBAL/TimestampType.php | 99 + .../illuminate/database/DatabaseManager.php | 471 ++ .../database/DatabaseServiceProvider.php | 113 + .../database/DatabaseTransactionRecord.php | 73 + .../database/DatabaseTransactionsManager.php | 133 + .../illuminate/database/DeadlockException.php | 10 + .../database/DetectsConcurrencyErrors.php | 37 + .../database/DetectsLostConnections.php | 66 + .../BroadcastableModelEventOccurred.php | 144 + .../database/Eloquent/BroadcastsEvents.php | 197 + .../illuminate/database/Eloquent/Builder.php | 1952 +++++++ .../database/Eloquent/Casts/ArrayObject.php | 46 + .../database/Eloquent/Casts/AsArrayObject.php | 42 + .../database/Eloquent/Casts/AsCollection.php | 38 + .../Eloquent/Casts/AsEncryptedArrayObject.php | 45 + .../Eloquent/Casts/AsEncryptedCollection.php | 41 + .../Eloquent/Casts/AsEnumArrayObject.php | 84 + .../Eloquent/Casts/AsEnumCollection.php | 80 + .../database/Eloquent/Casts/AsStringable.php | 32 + .../database/Eloquent/Casts/Attribute.php | 105 + .../database/Eloquent/Collection.php | 782 +++ .../Eloquent/Concerns/GuardsAttributes.php | 255 + .../Eloquent/Concerns/HasAttributes.php | 2232 +++++++ .../database/Eloquent/Concerns/HasEvents.php | 415 ++ .../Eloquent/Concerns/HasGlobalScopes.php | 71 + .../Eloquent/Concerns/HasRelationships.php | 927 +++ .../Eloquent/Concerns/HasTimestamps.php | 224 + .../database/Eloquent/Concerns/HasUlids.php | 96 + .../database/Eloquent/Concerns/HasUuids.php | 96 + .../Eloquent/Concerns/HidesAttributes.php | 124 + .../Concerns/QueriesRelationships.php | 879 +++ .../Factories/BelongsToManyRelationship.php | 76 + .../Factories/BelongsToRelationship.php | 97 + .../Eloquent/Factories/CrossJoinSequence.php | 26 + .../database/Eloquent/Factories/Factory.php | 926 +++ .../Eloquent/Factories/HasFactory.php | 32 + .../Eloquent/Factories/Relationship.php | 75 + .../database/Eloquent/Factories/Sequence.php | 63 + .../Eloquent/HigherOrderBuilderProxy.php | 50 + .../Eloquent/InvalidCastException.php | 48 + .../Eloquent/JsonEncodingException.php | 49 + .../Eloquent/MassAssignmentException.php | 10 + .../database/Eloquent/MassPrunable.php | 48 + .../Eloquent/MissingAttributeException.php | 23 + vendor/illuminate/database/Eloquent/Model.php | 2396 ++++++++ .../Eloquent/ModelNotFoundException.php | 69 + .../PendingHasThroughRelationship.php | 90 + .../illuminate/database/Eloquent/Prunable.php | 67 + .../database/Eloquent/QueueEntityResolver.php | 29 + .../Eloquent/RelationNotFoundException.php | 46 + .../database/Eloquent/Relations/BelongsTo.php | 383 ++ .../Eloquent/Relations/BelongsToMany.php | 1503 +++++ .../Eloquent/Relations/Concerns/AsPivot.php | 333 ++ .../Relations/Concerns/CanBeOneOfMany.php | 310 + .../Concerns/ComparesRelatedModels.php | 77 + .../Concerns/InteractsWithDictionary.php | 36 + .../Concerns/InteractsWithPivotTable.php | 681 +++ .../Concerns/SupportsDefaultModels.php | 63 + .../database/Eloquent/Relations/HasMany.php | 49 + .../Eloquent/Relations/HasManyThrough.php | 772 +++ .../database/Eloquent/Relations/HasOne.php | 137 + .../Eloquent/Relations/HasOneOrMany.php | 486 ++ .../Eloquent/Relations/HasOneThrough.php | 77 + .../database/Eloquent/Relations/MorphMany.php | 62 + .../database/Eloquent/Relations/MorphOne.php | 137 + .../Eloquent/Relations/MorphOneOrMany.php | 127 + .../Eloquent/Relations/MorphPivot.php | 182 + .../database/Eloquent/Relations/MorphTo.php | 396 ++ .../Eloquent/Relations/MorphToMany.php | 209 + .../database/Eloquent/Relations/Pivot.php | 25 + .../database/Eloquent/Relations/Relation.php | 503 ++ vendor/illuminate/database/Eloquent/Scope.php | 15 + .../database/Eloquent/SoftDeletes.php | 249 + .../database/Eloquent/SoftDeletingScope.php | 148 + .../database/Events/ConnectionEstablished.php | 8 + .../database/Events/ConnectionEvent.php | 32 + .../database/Events/DatabaseBusy.php | 32 + .../database/Events/DatabaseRefreshed.php | 10 + .../database/Events/MigrationEnded.php | 8 + .../database/Events/MigrationEvent.php | 36 + .../database/Events/MigrationStarted.php | 8 + .../database/Events/MigrationsEnded.php | 8 + .../database/Events/MigrationsEvent.php | 26 + .../database/Events/MigrationsStarted.php | 8 + .../database/Events/ModelsPruned.php | 33 + .../database/Events/NoPendingMigrations.php | 24 + .../database/Events/QueryExecuted.php | 59 + .../database/Events/SchemaDumped.php | 41 + .../database/Events/SchemaLoaded.php | 41 + .../database/Events/StatementPrepared.php | 33 + .../database/Events/TransactionBeginning.php | 8 + .../database/Events/TransactionCommitted.php | 8 + .../database/Events/TransactionCommitting.php | 8 + .../database/Events/TransactionRolledBack.php | 8 + vendor/illuminate/database/Grammar.php | 253 + vendor/illuminate/database/LICENSE.md | 21 + .../LazyLoadingViolationException.php | 39 + .../database/LostConnectionException.php | 10 + .../database/MigrationServiceProvider.php | 222 + .../DatabaseMigrationRepository.php | 224 + .../database/Migrations/Migration.php | 30 + .../database/Migrations/MigrationCreator.php | 231 + .../MigrationRepositoryInterface.php | 88 + .../database/Migrations/Migrator.php | 773 +++ .../Migrations/stubs/migration.create.stub | 31 + .../database/Migrations/stubs/migration.stub | 28 + .../Migrations/stubs/migration.update.stub | 32 + .../MultipleColumnsSelectedException.php | 10 + .../MultipleRecordsFoundException.php | 40 + .../illuminate/database/MySqlConnection.php | 91 + .../PDO/Concerns/ConnectsToDatabase.php | 30 + vendor/illuminate/database/PDO/Connection.php | 182 + .../illuminate/database/PDO/MySqlDriver.php | 19 + .../database/PDO/PostgresDriver.php | 19 + .../illuminate/database/PDO/SQLiteDriver.php | 19 + .../database/PDO/SqlServerConnection.php | 152 + .../database/PDO/SqlServerDriver.php | 32 + .../database/PostgresConnection.php | 80 + vendor/illuminate/database/Query/Builder.php | 3874 +++++++++++++ .../illuminate/database/Query/Expression.php | 44 + .../database/Query/Grammars/Grammar.php | 1348 +++++ .../database/Query/Grammars/MySqlGrammar.php | 381 ++ .../Query/Grammars/PostgresGrammar.php | 701 +++ .../database/Query/Grammars/SQLiteGrammar.php | 369 ++ .../Query/Grammars/SqlServerGrammar.php | 634 ++ .../illuminate/database/Query/IndexHint.php | 33 + .../illuminate/database/Query/JoinClause.php | 146 + .../Query/Processors/MySqlProcessor.php | 19 + .../Query/Processors/PostgresProcessor.php | 45 + .../database/Query/Processors/Processor.php | 49 + .../Query/Processors/SQLiteProcessor.php | 19 + .../Query/Processors/SqlServerProcessor.php | 70 + vendor/illuminate/database/QueryException.php | 79 + vendor/illuminate/database/README.md | 69 + .../database/RecordsNotFoundException.php | 10 + .../illuminate/database/SQLiteConnection.php | 115 + .../SQLiteDatabaseDoesNotExistException.php | 28 + .../illuminate/database/Schema/Blueprint.php | 1829 ++++++ vendor/illuminate/database/Schema/Builder.php | 495 ++ .../database/Schema/ColumnDefinition.php | 38 + .../Schema/ForeignIdColumnDefinition.php | 52 + .../database/Schema/ForeignKeyDefinition.php | 76 + .../database/Schema/Grammars/ChangeColumn.php | 235 + .../database/Schema/Grammars/Grammar.php | 345 ++ .../database/Schema/Grammars/MySqlGrammar.php | 1221 ++++ .../Schema/Grammars/PostgresGrammar.php | 1141 ++++ .../database/Schema/Grammars/RenameColumn.php | 84 + .../Schema/Grammars/SQLiteGrammar.php | 1005 ++++ .../Schema/Grammars/SqlServerGrammar.php | 973 ++++ .../database/Schema/IndexDefinition.php | 16 + .../database/Schema/MySqlBuilder.php | 140 + .../database/Schema/MySqlSchemaState.php | 170 + .../database/Schema/PostgresBuilder.php | 248 + .../database/Schema/PostgresSchemaState.php | 79 + .../database/Schema/SQLiteBuilder.php | 102 + .../database/Schema/SchemaState.php | 122 + .../database/Schema/SqlServerBuilder.php | 78 + .../database/Schema/SqliteSchemaState.php | 99 + vendor/illuminate/database/Seeder.php | 195 + .../database/SqlServerConnection.php | 123 + vendor/illuminate/database/composer.json | 52 + .../illuminate/events/CallQueuedListener.php | 180 + vendor/illuminate/events/Dispatcher.php | 705 +++ .../events/EventServiceProvider.php | 23 + .../illuminate/events/InvokeQueuedClosure.php | 34 + vendor/illuminate/events/LICENSE.md | 21 + vendor/illuminate/events/NullDispatcher.php | 144 + vendor/illuminate/events/QueuedClosure.php | 125 + vendor/illuminate/events/composer.json | 42 + vendor/illuminate/events/functions.php | 18 + vendor/illuminate/macroable/LICENSE.md | 21 + .../illuminate/macroable/Traits/Macroable.php | 126 + vendor/illuminate/macroable/composer.json | 33 + .../pagination/AbstractCursorPaginator.php | 671 +++ .../pagination/AbstractPaginator.php | 797 +++ vendor/illuminate/pagination/Cursor.php | 132 + .../illuminate/pagination/CursorPaginator.php | 172 + vendor/illuminate/pagination/LICENSE.md | 21 + .../pagination/LengthAwarePaginator.php | 231 + .../pagination/PaginationServiceProvider.php | 34 + .../illuminate/pagination/PaginationState.php | 35 + vendor/illuminate/pagination/Paginator.php | 176 + vendor/illuminate/pagination/UrlWindow.php | 220 + vendor/illuminate/pagination/composer.json | 37 + .../resources/views/bootstrap-4.blade.php | 46 + .../resources/views/bootstrap-5.blade.php | 88 + .../resources/views/default.blade.php | 46 + .../resources/views/semantic-ui.blade.php | 36 + .../views/simple-bootstrap-4.blade.php | 27 + .../views/simple-bootstrap-5.blade.php | 29 + .../resources/views/simple-default.blade.php | 19 + .../resources/views/simple-tailwind.blade.php | 25 + .../resources/views/tailwind.blade.php | 106 + vendor/illuminate/pipeline/Hub.php | 97 + vendor/illuminate/pipeline/LICENSE.md | 21 + vendor/illuminate/pipeline/Pipeline.php | 271 + .../pipeline/PipelineServiceProvider.php | 34 + vendor/illuminate/pipeline/composer.json | 35 + .../redis/Connections/Connection.php | 231 + .../redis/Connections/PacksPhpRedisValues.php | 183 + .../Connections/PhpRedisClusterConnection.php | 24 + .../redis/Connections/PhpRedisConnection.php | 567 ++ .../Connections/PredisClusterConnection.php | 25 + .../redis/Connections/PredisConnection.php | 70 + .../redis/Connectors/PhpRedisConnector.php | 234 + .../redis/Connectors/PredisConnector.php | 53 + .../redis/Events/CommandExecuted.php | 59 + vendor/illuminate/redis/LICENSE.md | 21 + .../redis/Limiters/ConcurrencyLimiter.php | 167 + .../Limiters/ConcurrencyLimiterBuilder.php | 142 + .../redis/Limiters/DurationLimiter.php | 203 + .../redis/Limiters/DurationLimiterBuilder.php | 142 + vendor/illuminate/redis/RedisManager.php | 278 + .../illuminate/redis/RedisServiceProvider.php | 38 + vendor/illuminate/redis/composer.json | 41 + .../support/AggregateServiceProvider.php | 52 + vendor/illuminate/support/Benchmark.php | 50 + vendor/illuminate/support/Carbon.php | 21 + vendor/illuminate/support/Composer.php | 132 + .../support/ConfigurationUrlParser.php | 191 + vendor/illuminate/support/DateFactory.php | 231 + vendor/illuminate/support/Env.php | 101 + .../support/Exceptions/MathException.php | 10 + vendor/illuminate/support/Facades/App.php | 141 + vendor/illuminate/support/Facades/Artisan.php | 35 + vendor/illuminate/support/Facades/Auth.php | 98 + vendor/illuminate/support/Facades/Blade.php | 60 + .../illuminate/support/Facades/Broadcast.php | 46 + vendor/illuminate/support/Facades/Bus.php | 91 + vendor/illuminate/support/Facades/Cache.php | 68 + vendor/illuminate/support/Facades/Config.php | 31 + vendor/illuminate/support/Facades/Cookie.php | 58 + vendor/illuminate/support/Facades/Crypt.php | 27 + vendor/illuminate/support/Facades/DB.php | 119 + vendor/illuminate/support/Facades/Date.php | 122 + vendor/illuminate/support/Facades/Event.php | 124 + vendor/illuminate/support/Facades/Facade.php | 340 ++ vendor/illuminate/support/Facades/File.php | 71 + vendor/illuminate/support/Facades/Gate.php | 47 + vendor/illuminate/support/Facades/Hash.php | 35 + vendor/illuminate/support/Facades/Http.php | 150 + vendor/illuminate/support/Facades/Lang.php | 46 + vendor/illuminate/support/Facades/Log.php | 48 + vendor/illuminate/support/Facades/Mail.php | 83 + .../support/Facades/Notification.php | 78 + .../support/Facades/ParallelTesting.php | 34 + .../illuminate/support/Facades/Password.php | 68 + vendor/illuminate/support/Facades/Queue.php | 91 + .../support/Facades/RateLimiter.php | 32 + .../illuminate/support/Facades/Redirect.php | 39 + vendor/illuminate/support/Facades/Redis.php | 46 + vendor/illuminate/support/Facades/Request.php | 196 + .../illuminate/support/Facades/Response.php | 40 + vendor/illuminate/support/Facades/Route.php | 114 + vendor/illuminate/support/Facades/Schema.php | 68 + vendor/illuminate/support/Facades/Session.php | 80 + vendor/illuminate/support/Facades/Storage.php | 141 + vendor/illuminate/support/Facades/URL.php | 62 + .../illuminate/support/Facades/Validator.php | 34 + vendor/illuminate/support/Facades/View.php | 97 + vendor/illuminate/support/Facades/Vite.php | 41 + vendor/illuminate/support/Fluent.php | 201 + .../support/HigherOrderTapProxy.php | 38 + vendor/illuminate/support/HtmlString.php | 66 + .../illuminate/support/InteractsWithTime.php | 64 + vendor/illuminate/support/Js.php | 150 + vendor/illuminate/support/LICENSE.md | 21 + vendor/illuminate/support/Lottery.php | 271 + vendor/illuminate/support/Manager.php | 193 + vendor/illuminate/support/MessageBag.php | 420 ++ .../support/MultipleInstanceManager.php | 191 + .../support/NamespacedItemResolver.php | 112 + vendor/illuminate/support/Optional.php | 131 + vendor/illuminate/support/Pluralizer.php | 127 + vendor/illuminate/support/ProcessUtils.php | 69 + vendor/illuminate/support/Reflector.php | 162 + vendor/illuminate/support/ServiceProvider.php | 437 ++ vendor/illuminate/support/Str.php | 1367 +++++ vendor/illuminate/support/Stringable.php | 1228 ++++ .../support/Testing/Fakes/BatchFake.php | 163 + .../Testing/Fakes/BatchRepositoryFake.php | 153 + .../support/Testing/Fakes/BusFake.php | 809 +++ .../support/Testing/Fakes/EventFake.php | 385 ++ .../support/Testing/Fakes/MailFake.php | 444 ++ .../Testing/Fakes/NotificationFake.php | 375 ++ .../Testing/Fakes/PendingBatchFake.php | 49 + .../Testing/Fakes/PendingChainFake.php | 56 + .../support/Testing/Fakes/PendingMailFake.php | 42 + .../support/Testing/Fakes/QueueFake.php | 503 ++ vendor/illuminate/support/Timebox.php | 72 + .../support/Traits/CapsuleManagerTrait.php | 69 + .../support/Traits/ForwardsCalls.php | 75 + .../illuminate/support/Traits/Localizable.php | 34 + .../support/Traits/ReflectsClosures.php | 88 + vendor/illuminate/support/Traits/Tappable.php | 17 + vendor/illuminate/support/ValidatedInput.php | 245 + vendor/illuminate/support/ViewErrorBag.php | 130 + vendor/illuminate/support/composer.json | 58 + vendor/illuminate/support/helpers.php | 425 ++ vendor/jasongrimes/paginator/.gitignore | 5 + vendor/jasongrimes/paginator/.travis.yml | 14 + vendor/jasongrimes/paginator/LICENSE | 22 + vendor/jasongrimes/paginator/README.md | 170 + vendor/jasongrimes/paginator/composer.json | 29 + .../paginator/examples/pager.phtml | 21 + .../jasongrimes/paginator/examples/pager.twig | 19 + .../paginator/examples/pagerSmall.phtml | 49 + .../paginator/examples/pagerSmall.twig | 49 + .../examples/screenshot-default-first.png | Bin 0 -> 12648 bytes .../examples/screenshot-default-last.png | Bin 0 -> 14620 bytes .../examples/screenshot-default-mid.png | Bin 0 -> 15306 bytes .../examples/screenshot-small-first.png | Bin 0 -> 8489 bytes .../examples/screenshot-small-last.png | Bin 0 -> 9014 bytes .../examples/screenshot-small-mid-open.png | Bin 0 -> 46197 bytes .../examples/screenshot-small-mid.png | Bin 0 -> 11171 bytes vendor/jasongrimes/paginator/phpunit.xml.dist | 18 + .../paginator/src/JasonGrimes/Paginator.php | 345 ++ .../tests/JasonGrimes/Tests/PaginatorTest.php | 123 + vendor/monolog/monolog/CHANGELOG.md | 633 ++ vendor/monolog/monolog/LICENSE | 19 + vendor/monolog/monolog/README.md | 112 + vendor/monolog/monolog/UPGRADE.md | 72 + vendor/monolog/monolog/composer.json | 81 + .../Monolog/Attribute/AsMonologProcessor.php | 46 + .../monolog/src/Monolog/DateTimeImmutable.php | 51 + .../monolog/src/Monolog/ErrorHandler.php | 307 + .../Monolog/Formatter/ChromePHPFormatter.php | 83 + .../Monolog/Formatter/ElasticaFormatter.php | 89 + .../Formatter/ElasticsearchFormatter.php | 89 + .../Monolog/Formatter/FlowdockFormatter.php | 112 + .../Monolog/Formatter/FluentdFormatter.php | 88 + .../Monolog/Formatter/FormatterInterface.php | 42 + .../Formatter/GelfMessageFormatter.php | 175 + .../Formatter/GoogleCloudLoggingFormatter.php | 40 + .../src/Monolog/Formatter/HtmlFormatter.php | 142 + .../src/Monolog/Formatter/JsonFormatter.php | 224 + .../src/Monolog/Formatter/LineFormatter.php | 246 + .../src/Monolog/Formatter/LogglyFormatter.php | 45 + .../Monolog/Formatter/LogmaticFormatter.php | 66 + .../Monolog/Formatter/LogstashFormatter.php | 101 + .../Monolog/Formatter/MongoDBFormatter.php | 162 + .../Monolog/Formatter/NormalizerFormatter.php | 290 + .../src/Monolog/Formatter/ScalarFormatter.php | 51 + .../Monolog/Formatter/WildfireFormatter.php | 139 + .../src/Monolog/Handler/AbstractHandler.php | 112 + .../Handler/AbstractProcessingHandler.php | 69 + .../Monolog/Handler/AbstractSyslogHandler.php | 106 + .../src/Monolog/Handler/AmqpHandler.php | 171 + .../Monolog/Handler/BrowserConsoleHandler.php | 308 + .../src/Monolog/Handler/BufferHandler.php | 167 + .../src/Monolog/Handler/ChromePHPHandler.php | 196 + .../src/Monolog/Handler/CouchDBHandler.php | 77 + .../src/Monolog/Handler/CubeHandler.php | 167 + .../monolog/src/Monolog/Handler/Curl/Util.php | 71 + .../Monolog/Handler/DeduplicationHandler.php | 186 + .../Handler/DoctrineCouchDBHandler.php | 47 + .../src/Monolog/Handler/DynamoDbHandler.php | 104 + .../src/Monolog/Handler/ElasticaHandler.php | 129 + .../Monolog/Handler/ElasticsearchHandler.php | 218 + .../src/Monolog/Handler/ErrorLogHandler.php | 91 + .../Monolog/Handler/FallbackGroupHandler.php | 71 + .../src/Monolog/Handler/FilterHandler.php | 212 + .../ActivationStrategyInterface.php | 29 + .../ChannelLevelActivationStrategy.php | 77 + .../ErrorLevelActivationStrategy.php | 46 + .../Monolog/Handler/FingersCrossedHandler.php | 252 + .../src/Monolog/Handler/FirePHPHandler.php | 180 + .../src/Monolog/Handler/FleepHookHandler.php | 135 + .../src/Monolog/Handler/FlowdockHandler.php | 133 + .../Handler/FormattableHandlerInterface.php | 37 + .../Handler/FormattableHandlerTrait.php | 60 + .../src/Monolog/Handler/GelfHandler.php | 57 + .../src/Monolog/Handler/GroupHandler.php | 132 + .../monolog/src/Monolog/Handler/Handler.php | 62 + .../src/Monolog/Handler/HandlerInterface.php | 85 + .../src/Monolog/Handler/HandlerWrapper.php | 136 + .../src/Monolog/Handler/IFTTTHandler.php | 74 + .../src/Monolog/Handler/InsightOpsHandler.php | 76 + .../src/Monolog/Handler/LogEntriesHandler.php | 70 + .../src/Monolog/Handler/LogglyHandler.php | 160 + .../src/Monolog/Handler/LogmaticHandler.php | 106 + .../src/Monolog/Handler/MailHandler.php | 95 + .../src/Monolog/Handler/MandrillHandler.php | 83 + .../Handler/MissingExtensionException.php | 21 + .../src/Monolog/Handler/MongoDBHandler.php | 86 + .../Monolog/Handler/NativeMailerHandler.php | 174 + .../src/Monolog/Handler/NewRelicHandler.php | 199 + .../src/Monolog/Handler/NoopHandler.php | 40 + .../src/Monolog/Handler/NullHandler.php | 60 + .../src/Monolog/Handler/OverflowHandler.php | 149 + .../src/Monolog/Handler/PHPConsoleHandler.php | 263 + .../src/Monolog/Handler/ProcessHandler.php | 191 + .../Handler/ProcessableHandlerInterface.php | 44 + .../Handler/ProcessableHandlerTrait.php | 77 + .../src/Monolog/Handler/PsrHandler.php | 95 + .../src/Monolog/Handler/PushoverHandler.php | 246 + .../src/Monolog/Handler/RedisHandler.php | 101 + .../Monolog/Handler/RedisPubSubHandler.php | 67 + .../src/Monolog/Handler/RollbarHandler.php | 131 + .../Monolog/Handler/RotatingFileHandler.php | 207 + .../src/Monolog/Handler/SamplingHandler.php | 132 + .../src/Monolog/Handler/SendGridHandler.php | 102 + .../src/Monolog/Handler/Slack/SlackRecord.php | 387 ++ .../src/Monolog/Handler/SlackHandler.php | 256 + .../Monolog/Handler/SlackWebhookHandler.php | 130 + .../src/Monolog/Handler/SocketHandler.php | 448 ++ .../src/Monolog/Handler/SqsHandler.php | 62 + .../src/Monolog/Handler/StreamHandler.php | 224 + .../Monolog/Handler/SwiftMailerHandler.php | 115 + .../Monolog/Handler/SymfonyMailerHandler.php | 111 + .../src/Monolog/Handler/SyslogHandler.php | 68 + .../Monolog/Handler/SyslogUdp/UdpSocket.php | 88 + .../src/Monolog/Handler/SyslogUdpHandler.php | 150 + .../Monolog/Handler/TelegramBotHandler.php | 274 + .../src/Monolog/Handler/TestHandler.php | 231 + .../Handler/WebRequestRecognizerTrait.php | 24 + .../Handler/WhatFailureGroupHandler.php | 81 + .../Monolog/Handler/ZendMonitorHandler.php | 101 + .../monolog/monolog/src/Monolog/LogRecord.php | 34 + vendor/monolog/monolog/src/Monolog/Logger.php | 761 +++ .../src/Monolog/Processor/GitProcessor.php | 77 + .../Monolog/Processor/HostnameProcessor.php | 36 + .../Processor/IntrospectionProcessor.php | 123 + .../Processor/MemoryPeakUsageProcessor.php | 37 + .../src/Monolog/Processor/MemoryProcessor.php | 61 + .../Processor/MemoryUsageProcessor.php | 37 + .../Monolog/Processor/MercurialProcessor.php | 77 + .../Monolog/Processor/ProcessIdProcessor.php | 30 + .../Monolog/Processor/ProcessorInterface.php | 30 + .../Processor/PsrLogMessageProcessor.php | 88 + .../src/Monolog/Processor/TagProcessor.php | 61 + .../src/Monolog/Processor/UidProcessor.php | 59 + .../src/Monolog/Processor/WebProcessor.php | 111 + .../monolog/monolog/src/Monolog/Registry.php | 134 + .../src/Monolog/ResettableInterface.php | 34 + .../monolog/src/Monolog/SignalHandler.php | 120 + .../monolog/src/Monolog/Test/TestCase.php | 85 + vendor/monolog/monolog/src/Monolog/Utils.php | 284 + vendor/nesbot/carbon/.phpstorm.meta.php | 10 + vendor/nesbot/carbon/LICENSE | 19 + vendor/nesbot/carbon/bin/carbon | 23 + vendor/nesbot/carbon/bin/carbon.bat | 4 + vendor/nesbot/carbon/composer.json | 127 + vendor/nesbot/carbon/extension.neon | 5 + .../MessageFormatterMapperStrongType.php | 28 + .../MessageFormatterMapperWeakType.php | 36 + .../Carbon/PHPStan/AbstractMacroBuiltin.php | 36 + .../Carbon/PHPStan/AbstractMacroStatic.php | 45 + .../lazy/Carbon/PHPStan/MacroStrongType.php | 45 + .../lazy/Carbon/PHPStan/MacroWeakType.php | 51 + .../lazy/Carbon/TranslatorStrongType.php | 52 + .../carbon/lazy/Carbon/TranslatorWeakType.php | 32 + vendor/nesbot/carbon/readme.md | 176 + vendor/nesbot/carbon/sponsors.php | 129 + .../carbon/src/Carbon/AbstractTranslator.php | 398 ++ vendor/nesbot/carbon/src/Carbon/Carbon.php | 523 ++ .../src/Carbon/CarbonConverterInterface.php | 19 + .../carbon/src/Carbon/CarbonImmutable.php | 582 ++ .../carbon/src/Carbon/CarbonInterface.php | 5142 +++++++++++++++++ .../carbon/src/Carbon/CarbonInterval.php | 3054 ++++++++++ .../nesbot/carbon/src/Carbon/CarbonPeriod.php | 2742 +++++++++ .../src/Carbon/CarbonPeriodImmutable.php | 40 + .../carbon/src/Carbon/CarbonTimeZone.php | 320 + .../nesbot/carbon/src/Carbon/Cli/Invoker.php | 38 + .../Exceptions/BadComparisonUnitException.php | 48 + .../BadFluentConstructorException.php | 49 + .../Exceptions/BadFluentSetterException.php | 49 + .../Exceptions/BadMethodCallException.php | 17 + .../Exceptions/EndLessPeriodException.php | 19 + .../src/Carbon/Exceptions/Exception.php | 17 + .../Carbon/Exceptions/ImmutableException.php | 48 + .../Exceptions/InvalidArgumentException.php | 17 + .../Exceptions/InvalidCastException.php | 19 + .../Exceptions/InvalidDateException.php | 67 + .../Exceptions/InvalidFormatException.php | 19 + .../Exceptions/InvalidIntervalException.php | 19 + .../Exceptions/InvalidPeriodDateException.php | 19 + .../InvalidPeriodParameterException.php | 19 + .../Exceptions/InvalidTimeZoneException.php | 19 + .../Exceptions/InvalidTypeException.php | 19 + .../Exceptions/NotACarbonClassException.php | 50 + .../Carbon/Exceptions/NotAPeriodException.php | 19 + .../Exceptions/NotLocaleAwareException.php | 32 + .../Carbon/Exceptions/OutOfRangeException.php | 101 + .../Carbon/Exceptions/ParseErrorException.php | 88 + .../Carbon/Exceptions/RuntimeException.php | 17 + .../src/Carbon/Exceptions/UnitException.php | 19 + .../Exceptions/UnitNotConfiguredException.php | 48 + .../Exceptions/UnknownGetterException.php | 49 + .../Exceptions/UnknownMethodException.php | 49 + .../Exceptions/UnknownSetterException.php | 49 + .../Exceptions/UnknownUnitException.php | 48 + .../Exceptions/UnreachableException.php | 19 + vendor/nesbot/carbon/src/Carbon/Factory.php | 326 ++ .../carbon/src/Carbon/FactoryImmutable.php | 259 + vendor/nesbot/carbon/src/Carbon/Lang/aa.php | 15 + .../nesbot/carbon/src/Carbon/Lang/aa_DJ.php | 44 + .../nesbot/carbon/src/Carbon/Lang/aa_ER.php | 28 + .../carbon/src/Carbon/Lang/aa_ER@saaho.php | 28 + .../nesbot/carbon/src/Carbon/Lang/aa_ET.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/af.php | 79 + .../nesbot/carbon/src/Carbon/Lang/af_NA.php | 28 + .../nesbot/carbon/src/Carbon/Lang/af_ZA.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/agq.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/agr.php | 15 + .../nesbot/carbon/src/Carbon/Lang/agr_PE.php | 44 + vendor/nesbot/carbon/src/Carbon/Lang/ak.php | 15 + .../nesbot/carbon/src/Carbon/Lang/ak_GH.php | 40 + vendor/nesbot/carbon/src/Carbon/Lang/am.php | 15 + .../nesbot/carbon/src/Carbon/Lang/am_ET.php | 58 + vendor/nesbot/carbon/src/Carbon/Lang/an.php | 15 + .../nesbot/carbon/src/Carbon/Lang/an_ES.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/anp.php | 15 + .../nesbot/carbon/src/Carbon/Lang/anp_IN.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/ar.php | 93 + .../nesbot/carbon/src/Carbon/Lang/ar_AE.php | 29 + .../nesbot/carbon/src/Carbon/Lang/ar_BH.php | 29 + .../nesbot/carbon/src/Carbon/Lang/ar_DJ.php | 13 + .../nesbot/carbon/src/Carbon/Lang/ar_DZ.php | 92 + .../nesbot/carbon/src/Carbon/Lang/ar_EG.php | 29 + .../nesbot/carbon/src/Carbon/Lang/ar_EH.php | 13 + .../nesbot/carbon/src/Carbon/Lang/ar_ER.php | 13 + .../nesbot/carbon/src/Carbon/Lang/ar_IL.php | 13 + .../nesbot/carbon/src/Carbon/Lang/ar_IN.php | 26 + .../nesbot/carbon/src/Carbon/Lang/ar_IQ.php | 29 + .../nesbot/carbon/src/Carbon/Lang/ar_JO.php | 29 + .../nesbot/carbon/src/Carbon/Lang/ar_KM.php | 13 + .../nesbot/carbon/src/Carbon/Lang/ar_KW.php | 95 + .../nesbot/carbon/src/Carbon/Lang/ar_LB.php | 29 + .../nesbot/carbon/src/Carbon/Lang/ar_LY.php | 92 + .../nesbot/carbon/src/Carbon/Lang/ar_MA.php | 92 + .../nesbot/carbon/src/Carbon/Lang/ar_MR.php | 13 + .../nesbot/carbon/src/Carbon/Lang/ar_OM.php | 29 + .../nesbot/carbon/src/Carbon/Lang/ar_PS.php | 18 + .../nesbot/carbon/src/Carbon/Lang/ar_QA.php | 29 + .../nesbot/carbon/src/Carbon/Lang/ar_SA.php | 94 + .../nesbot/carbon/src/Carbon/Lang/ar_SD.php | 29 + .../nesbot/carbon/src/Carbon/Lang/ar_SO.php | 13 + .../nesbot/carbon/src/Carbon/Lang/ar_SS.php | 27 + .../nesbot/carbon/src/Carbon/Lang/ar_SY.php | 29 + .../carbon/src/Carbon/Lang/ar_Shakl.php | 95 + .../nesbot/carbon/src/Carbon/Lang/ar_TD.php | 13 + .../nesbot/carbon/src/Carbon/Lang/ar_TN.php | 91 + .../nesbot/carbon/src/Carbon/Lang/ar_YE.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/as.php | 15 + .../nesbot/carbon/src/Carbon/Lang/as_IN.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/asa.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/ast.php | 59 + .../nesbot/carbon/src/Carbon/Lang/ast_ES.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/ayc.php | 15 + .../nesbot/carbon/src/Carbon/Lang/ayc_PE.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/az.php | 128 + .../nesbot/carbon/src/Carbon/Lang/az_AZ.php | 21 + .../nesbot/carbon/src/Carbon/Lang/az_Cyrl.php | 20 + .../nesbot/carbon/src/Carbon/Lang/az_IR.php | 27 + .../nesbot/carbon/src/Carbon/Lang/az_Latn.php | 29 + vendor/nesbot/carbon/src/Carbon/Lang/bas.php | 32 + vendor/nesbot/carbon/src/Carbon/Lang/be.php | 172 + .../nesbot/carbon/src/Carbon/Lang/be_BY.php | 22 + .../carbon/src/Carbon/Lang/be_BY@latin.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/bem.php | 15 + .../nesbot/carbon/src/Carbon/Lang/bem_ZM.php | 56 + vendor/nesbot/carbon/src/Carbon/Lang/ber.php | 15 + .../nesbot/carbon/src/Carbon/Lang/ber_DZ.php | 27 + .../nesbot/carbon/src/Carbon/Lang/ber_MA.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/bez.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/bg.php | 114 + .../nesbot/carbon/src/Carbon/Lang/bg_BG.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/bhb.php | 15 + .../nesbot/carbon/src/Carbon/Lang/bhb_IN.php | 26 + vendor/nesbot/carbon/src/Carbon/Lang/bho.php | 15 + .../nesbot/carbon/src/Carbon/Lang/bho_IN.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/bi.php | 15 + .../nesbot/carbon/src/Carbon/Lang/bi_VU.php | 53 + vendor/nesbot/carbon/src/Carbon/Lang/bm.php | 70 + vendor/nesbot/carbon/src/Carbon/Lang/bn.php | 100 + .../nesbot/carbon/src/Carbon/Lang/bn_BD.php | 27 + .../nesbot/carbon/src/Carbon/Lang/bn_IN.php | 26 + vendor/nesbot/carbon/src/Carbon/Lang/bo.php | 71 + .../nesbot/carbon/src/Carbon/Lang/bo_CN.php | 12 + .../nesbot/carbon/src/Carbon/Lang/bo_IN.php | 29 + vendor/nesbot/carbon/src/Carbon/Lang/br.php | 76 + .../nesbot/carbon/src/Carbon/Lang/br_FR.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/brx.php | 15 + .../nesbot/carbon/src/Carbon/Lang/brx_IN.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/bs.php | 97 + .../nesbot/carbon/src/Carbon/Lang/bs_BA.php | 12 + .../nesbot/carbon/src/Carbon/Lang/bs_Cyrl.php | 28 + .../nesbot/carbon/src/Carbon/Lang/bs_Latn.php | 13 + vendor/nesbot/carbon/src/Carbon/Lang/byn.php | 15 + .../nesbot/carbon/src/Carbon/Lang/byn_ER.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/ca.php | 117 + .../nesbot/carbon/src/Carbon/Lang/ca_AD.php | 13 + .../nesbot/carbon/src/Carbon/Lang/ca_ES.php | 12 + .../carbon/src/Carbon/Lang/ca_ES_Valencia.php | 23 + .../nesbot/carbon/src/Carbon/Lang/ca_FR.php | 13 + .../nesbot/carbon/src/Carbon/Lang/ca_IT.php | 13 + vendor/nesbot/carbon/src/Carbon/Lang/ccp.php | 27 + .../nesbot/carbon/src/Carbon/Lang/ccp_IN.php | 14 + vendor/nesbot/carbon/src/Carbon/Lang/ce.php | 15 + .../nesbot/carbon/src/Carbon/Lang/ce_RU.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/cgg.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/chr.php | 15 + .../nesbot/carbon/src/Carbon/Lang/chr_US.php | 58 + vendor/nesbot/carbon/src/Carbon/Lang/ckb.php | 89 + vendor/nesbot/carbon/src/Carbon/Lang/cmn.php | 15 + .../nesbot/carbon/src/Carbon/Lang/cmn_TW.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/crh.php | 15 + .../nesbot/carbon/src/Carbon/Lang/crh_UA.php | 56 + vendor/nesbot/carbon/src/Carbon/Lang/cs.php | 123 + .../nesbot/carbon/src/Carbon/Lang/cs_CZ.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/csb.php | 15 + .../nesbot/carbon/src/Carbon/Lang/csb_PL.php | 41 + vendor/nesbot/carbon/src/Carbon/Lang/cu.php | 52 + vendor/nesbot/carbon/src/Carbon/Lang/cv.php | 65 + .../nesbot/carbon/src/Carbon/Lang/cv_RU.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/cy.php | 79 + .../nesbot/carbon/src/Carbon/Lang/cy_GB.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/da.php | 81 + .../nesbot/carbon/src/Carbon/Lang/da_DK.php | 12 + .../nesbot/carbon/src/Carbon/Lang/da_GL.php | 19 + vendor/nesbot/carbon/src/Carbon/Lang/dav.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/de.php | 117 + .../nesbot/carbon/src/Carbon/Lang/de_AT.php | 27 + .../nesbot/carbon/src/Carbon/Lang/de_BE.php | 20 + .../nesbot/carbon/src/Carbon/Lang/de_CH.php | 20 + .../nesbot/carbon/src/Carbon/Lang/de_DE.php | 16 + .../nesbot/carbon/src/Carbon/Lang/de_IT.php | 16 + .../nesbot/carbon/src/Carbon/Lang/de_LI.php | 12 + .../nesbot/carbon/src/Carbon/Lang/de_LU.php | 20 + vendor/nesbot/carbon/src/Carbon/Lang/dje.php | 40 + vendor/nesbot/carbon/src/Carbon/Lang/doi.php | 15 + .../nesbot/carbon/src/Carbon/Lang/doi_IN.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/dsb.php | 15 + .../nesbot/carbon/src/Carbon/Lang/dsb_DE.php | 60 + vendor/nesbot/carbon/src/Carbon/Lang/dua.php | 56 + vendor/nesbot/carbon/src/Carbon/Lang/dv.php | 89 + .../nesbot/carbon/src/Carbon/Lang/dv_MV.php | 87 + vendor/nesbot/carbon/src/Carbon/Lang/dyo.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/dz.php | 15 + .../nesbot/carbon/src/Carbon/Lang/dz_BT.php | 43 + vendor/nesbot/carbon/src/Carbon/Lang/ebu.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/ee.php | 56 + .../nesbot/carbon/src/Carbon/Lang/ee_TG.php | 19 + vendor/nesbot/carbon/src/Carbon/Lang/el.php | 93 + .../nesbot/carbon/src/Carbon/Lang/el_CY.php | 19 + .../nesbot/carbon/src/Carbon/Lang/el_GR.php | 19 + vendor/nesbot/carbon/src/Carbon/Lang/en.php | 87 + .../nesbot/carbon/src/Carbon/Lang/en_001.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_150.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_AG.php | 21 + .../nesbot/carbon/src/Carbon/Lang/en_AI.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_AS.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_AT.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_AU.php | 31 + .../nesbot/carbon/src/Carbon/Lang/en_BB.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_BE.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_BI.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_BM.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_BS.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_BW.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_BZ.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_CA.php | 29 + .../nesbot/carbon/src/Carbon/Lang/en_CC.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_CH.php | 22 + .../nesbot/carbon/src/Carbon/Lang/en_CK.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_CM.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_CX.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_CY.php | 28 + .../nesbot/carbon/src/Carbon/Lang/en_DE.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_DG.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_DK.php | 22 + .../nesbot/carbon/src/Carbon/Lang/en_DM.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_ER.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_FI.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_FJ.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_FK.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_FM.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_GB.php | 30 + .../nesbot/carbon/src/Carbon/Lang/en_GD.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_GG.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_GH.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_GI.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_GM.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_GU.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_GY.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_HK.php | 18 + .../nesbot/carbon/src/Carbon/Lang/en_IE.php | 31 + .../nesbot/carbon/src/Carbon/Lang/en_IL.php | 29 + .../nesbot/carbon/src/Carbon/Lang/en_IM.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_IN.php | 26 + .../nesbot/carbon/src/Carbon/Lang/en_IO.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_ISO.php | 21 + .../nesbot/carbon/src/Carbon/Lang/en_JE.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_JM.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_KE.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_KI.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_KN.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_KY.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_LC.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_LR.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_LS.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_MG.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_MH.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_MO.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_MP.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_MS.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_MT.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_MU.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_MW.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_MY.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_NA.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_NF.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_NG.php | 18 + .../nesbot/carbon/src/Carbon/Lang/en_NL.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_NR.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_NU.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_NZ.php | 31 + .../nesbot/carbon/src/Carbon/Lang/en_PG.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_PH.php | 18 + .../nesbot/carbon/src/Carbon/Lang/en_PK.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_PN.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_PR.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_PW.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_RW.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_SB.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_SC.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_SD.php | 15 + .../nesbot/carbon/src/Carbon/Lang/en_SE.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_SG.php | 24 + .../nesbot/carbon/src/Carbon/Lang/en_SH.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_SI.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_SL.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_SS.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_SX.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_SZ.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_TC.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_TK.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_TO.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_TT.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_TV.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_TZ.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_UG.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_UM.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_US.php | 12 + .../carbon/src/Carbon/Lang/en_US_Posix.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_VC.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_VG.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_VI.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_VU.php | 14 + .../nesbot/carbon/src/Carbon/Lang/en_WS.php | 12 + .../nesbot/carbon/src/Carbon/Lang/en_ZA.php | 26 + .../nesbot/carbon/src/Carbon/Lang/en_ZM.php | 22 + .../nesbot/carbon/src/Carbon/Lang/en_ZW.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/eo.php | 77 + vendor/nesbot/carbon/src/Carbon/Lang/es.php | 121 + .../nesbot/carbon/src/Carbon/Lang/es_419.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_AR.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_BO.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_BR.php | 14 + .../nesbot/carbon/src/Carbon/Lang/es_BZ.php | 14 + .../nesbot/carbon/src/Carbon/Lang/es_CL.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_CO.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_CR.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_CU.php | 14 + .../nesbot/carbon/src/Carbon/Lang/es_DO.php | 31 + .../nesbot/carbon/src/Carbon/Lang/es_EA.php | 14 + .../nesbot/carbon/src/Carbon/Lang/es_EC.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_ES.php | 16 + .../nesbot/carbon/src/Carbon/Lang/es_GQ.php | 14 + .../nesbot/carbon/src/Carbon/Lang/es_GT.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_HN.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_IC.php | 14 + .../nesbot/carbon/src/Carbon/Lang/es_MX.php | 20 + .../nesbot/carbon/src/Carbon/Lang/es_NI.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_PA.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_PE.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_PH.php | 22 + .../nesbot/carbon/src/Carbon/Lang/es_PR.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_PY.php | 19 + .../nesbot/carbon/src/Carbon/Lang/es_SV.php | 20 + .../nesbot/carbon/src/Carbon/Lang/es_US.php | 38 + .../nesbot/carbon/src/Carbon/Lang/es_UY.php | 21 + .../nesbot/carbon/src/Carbon/Lang/es_VE.php | 19 + vendor/nesbot/carbon/src/Carbon/Lang/et.php | 93 + .../nesbot/carbon/src/Carbon/Lang/et_EE.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/eu.php | 67 + .../nesbot/carbon/src/Carbon/Lang/eu_ES.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/ewo.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/fa.php | 84 + .../nesbot/carbon/src/Carbon/Lang/fa_AF.php | 21 + .../nesbot/carbon/src/Carbon/Lang/fa_IR.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/ff.php | 60 + .../nesbot/carbon/src/Carbon/Lang/ff_CM.php | 12 + .../nesbot/carbon/src/Carbon/Lang/ff_GN.php | 12 + .../nesbot/carbon/src/Carbon/Lang/ff_MR.php | 21 + .../nesbot/carbon/src/Carbon/Lang/ff_SN.php | 16 + vendor/nesbot/carbon/src/Carbon/Lang/fi.php | 88 + .../nesbot/carbon/src/Carbon/Lang/fi_FI.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/fil.php | 15 + .../nesbot/carbon/src/Carbon/Lang/fil_PH.php | 62 + vendor/nesbot/carbon/src/Carbon/Lang/fo.php | 69 + .../nesbot/carbon/src/Carbon/Lang/fo_DK.php | 19 + .../nesbot/carbon/src/Carbon/Lang/fo_FO.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/fr.php | 123 + .../nesbot/carbon/src/Carbon/Lang/fr_BE.php | 18 + .../nesbot/carbon/src/Carbon/Lang/fr_BF.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_BI.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_BJ.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_BL.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_CA.php | 25 + .../nesbot/carbon/src/Carbon/Lang/fr_CD.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_CF.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_CG.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_CH.php | 24 + .../nesbot/carbon/src/Carbon/Lang/fr_CI.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_CM.php | 14 + .../nesbot/carbon/src/Carbon/Lang/fr_DJ.php | 22 + .../nesbot/carbon/src/Carbon/Lang/fr_DZ.php | 23 + .../nesbot/carbon/src/Carbon/Lang/fr_FR.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_GA.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_GF.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_GN.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_GP.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_GQ.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_HT.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_KM.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_LU.php | 21 + .../nesbot/carbon/src/Carbon/Lang/fr_MA.php | 15 + .../nesbot/carbon/src/Carbon/Lang/fr_MC.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_MF.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_MG.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_ML.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_MQ.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_MR.php | 21 + .../nesbot/carbon/src/Carbon/Lang/fr_MU.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_NC.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_NE.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_PF.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_PM.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_RE.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_RW.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_SC.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_SN.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_SY.php | 23 + .../nesbot/carbon/src/Carbon/Lang/fr_TD.php | 21 + .../nesbot/carbon/src/Carbon/Lang/fr_TG.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_TN.php | 22 + .../nesbot/carbon/src/Carbon/Lang/fr_VU.php | 21 + .../nesbot/carbon/src/Carbon/Lang/fr_WF.php | 12 + .../nesbot/carbon/src/Carbon/Lang/fr_YT.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/fur.php | 15 + .../nesbot/carbon/src/Carbon/Lang/fur_IT.php | 39 + vendor/nesbot/carbon/src/Carbon/Lang/fy.php | 76 + .../nesbot/carbon/src/Carbon/Lang/fy_DE.php | 27 + .../nesbot/carbon/src/Carbon/Lang/fy_NL.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/ga.php | 77 + .../nesbot/carbon/src/Carbon/Lang/ga_IE.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/gd.php | 75 + .../nesbot/carbon/src/Carbon/Lang/gd_GB.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/gez.php | 15 + .../nesbot/carbon/src/Carbon/Lang/gez_ER.php | 56 + .../nesbot/carbon/src/Carbon/Lang/gez_ET.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/gl.php | 98 + .../nesbot/carbon/src/Carbon/Lang/gl_ES.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/gom.php | 15 + .../carbon/src/Carbon/Lang/gom_Latn.php | 79 + vendor/nesbot/carbon/src/Carbon/Lang/gsw.php | 49 + .../nesbot/carbon/src/Carbon/Lang/gsw_CH.php | 12 + .../nesbot/carbon/src/Carbon/Lang/gsw_FR.php | 20 + .../nesbot/carbon/src/Carbon/Lang/gsw_LI.php | 20 + vendor/nesbot/carbon/src/Carbon/Lang/gu.php | 82 + .../nesbot/carbon/src/Carbon/Lang/gu_IN.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/guz.php | 47 + vendor/nesbot/carbon/src/Carbon/Lang/gv.php | 15 + .../nesbot/carbon/src/Carbon/Lang/gv_GB.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/ha.php | 60 + .../nesbot/carbon/src/Carbon/Lang/ha_GH.php | 12 + .../nesbot/carbon/src/Carbon/Lang/ha_NE.php | 12 + .../nesbot/carbon/src/Carbon/Lang/ha_NG.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/hak.php | 15 + .../nesbot/carbon/src/Carbon/Lang/hak_TW.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/haw.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/he.php | 86 + .../nesbot/carbon/src/Carbon/Lang/he_IL.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/hi.php | 82 + .../nesbot/carbon/src/Carbon/Lang/hi_IN.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/hif.php | 15 + .../nesbot/carbon/src/Carbon/Lang/hif_FJ.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/hne.php | 15 + .../nesbot/carbon/src/Carbon/Lang/hne_IN.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/hr.php | 111 + .../nesbot/carbon/src/Carbon/Lang/hr_BA.php | 32 + .../nesbot/carbon/src/Carbon/Lang/hr_HR.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/hsb.php | 15 + .../nesbot/carbon/src/Carbon/Lang/hsb_DE.php | 60 + vendor/nesbot/carbon/src/Carbon/Lang/ht.php | 15 + .../nesbot/carbon/src/Carbon/Lang/ht_HT.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/hu.php | 118 + .../nesbot/carbon/src/Carbon/Lang/hu_HU.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/hy.php | 95 + .../nesbot/carbon/src/Carbon/Lang/hy_AM.php | 24 + vendor/nesbot/carbon/src/Carbon/Lang/i18n.php | 23 + vendor/nesbot/carbon/src/Carbon/Lang/ia.php | 15 + .../nesbot/carbon/src/Carbon/Lang/ia_FR.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/id.php | 92 + .../nesbot/carbon/src/Carbon/Lang/id_ID.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/ig.php | 15 + .../nesbot/carbon/src/Carbon/Lang/ig_NG.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/ii.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/ik.php | 15 + .../nesbot/carbon/src/Carbon/Lang/ik_CA.php | 50 + vendor/nesbot/carbon/src/Carbon/Lang/in.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/is.php | 55 + .../nesbot/carbon/src/Carbon/Lang/is_IS.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/it.php | 115 + .../nesbot/carbon/src/Carbon/Lang/it_CH.php | 20 + .../nesbot/carbon/src/Carbon/Lang/it_IT.php | 16 + .../nesbot/carbon/src/Carbon/Lang/it_SM.php | 12 + .../nesbot/carbon/src/Carbon/Lang/it_VA.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/iu.php | 15 + .../nesbot/carbon/src/Carbon/Lang/iu_CA.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/iw.php | 58 + vendor/nesbot/carbon/src/Carbon/Lang/ja.php | 102 + .../nesbot/carbon/src/Carbon/Lang/ja_JP.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/jgo.php | 13 + vendor/nesbot/carbon/src/Carbon/Lang/jmc.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/jv.php | 71 + vendor/nesbot/carbon/src/Carbon/Lang/ka.php | 204 + .../nesbot/carbon/src/Carbon/Lang/ka_GE.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/kab.php | 15 + .../nesbot/carbon/src/Carbon/Lang/kab_DZ.php | 56 + vendor/nesbot/carbon/src/Carbon/Lang/kam.php | 50 + vendor/nesbot/carbon/src/Carbon/Lang/kde.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/kea.php | 49 + vendor/nesbot/carbon/src/Carbon/Lang/khq.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/ki.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/kk.php | 103 + .../nesbot/carbon/src/Carbon/Lang/kk_KZ.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/kkj.php | 13 + vendor/nesbot/carbon/src/Carbon/Lang/kl.php | 15 + .../nesbot/carbon/src/Carbon/Lang/kl_GL.php | 64 + vendor/nesbot/carbon/src/Carbon/Lang/kln.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/km.php | 71 + .../nesbot/carbon/src/Carbon/Lang/km_KH.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/kn.php | 75 + .../nesbot/carbon/src/Carbon/Lang/kn_IN.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/ko.php | 91 + .../nesbot/carbon/src/Carbon/Lang/ko_KP.php | 14 + .../nesbot/carbon/src/Carbon/Lang/ko_KR.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/kok.php | 15 + .../nesbot/carbon/src/Carbon/Lang/kok_IN.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/ks.php | 15 + .../nesbot/carbon/src/Carbon/Lang/ks_IN.php | 51 + .../src/Carbon/Lang/ks_IN@devanagari.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/ksb.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/ksf.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/ksh.php | 57 + vendor/nesbot/carbon/src/Carbon/Lang/ku.php | 40 + .../nesbot/carbon/src/Carbon/Lang/ku_TR.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/kw.php | 15 + .../nesbot/carbon/src/Carbon/Lang/kw_GB.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/ky.php | 106 + .../nesbot/carbon/src/Carbon/Lang/ky_KG.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/lag.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/lb.php | 88 + .../nesbot/carbon/src/Carbon/Lang/lb_LU.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/lg.php | 15 + .../nesbot/carbon/src/Carbon/Lang/lg_UG.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/li.php | 15 + .../nesbot/carbon/src/Carbon/Lang/li_NL.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/lij.php | 15 + .../nesbot/carbon/src/Carbon/Lang/lij_IT.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/lkt.php | 41 + vendor/nesbot/carbon/src/Carbon/Lang/ln.php | 60 + .../nesbot/carbon/src/Carbon/Lang/ln_AO.php | 17 + .../nesbot/carbon/src/Carbon/Lang/ln_CD.php | 16 + .../nesbot/carbon/src/Carbon/Lang/ln_CF.php | 17 + .../nesbot/carbon/src/Carbon/Lang/ln_CG.php | 17 + vendor/nesbot/carbon/src/Carbon/Lang/lo.php | 62 + .../nesbot/carbon/src/Carbon/Lang/lo_LA.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/lrc.php | 17 + .../nesbot/carbon/src/Carbon/Lang/lrc_IQ.php | 13 + vendor/nesbot/carbon/src/Carbon/Lang/lt.php | 135 + .../nesbot/carbon/src/Carbon/Lang/lt_LT.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/lu.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/luo.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/luy.php | 57 + vendor/nesbot/carbon/src/Carbon/Lang/lv.php | 183 + .../nesbot/carbon/src/Carbon/Lang/lv_LV.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/lzh.php | 15 + .../nesbot/carbon/src/Carbon/Lang/lzh_TW.php | 56 + vendor/nesbot/carbon/src/Carbon/Lang/mag.php | 15 + .../nesbot/carbon/src/Carbon/Lang/mag_IN.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/mai.php | 15 + .../nesbot/carbon/src/Carbon/Lang/mai_IN.php | 51 + vendor/nesbot/carbon/src/Carbon/Lang/mas.php | 51 + .../nesbot/carbon/src/Carbon/Lang/mas_TZ.php | 14 + vendor/nesbot/carbon/src/Carbon/Lang/mer.php | 43 + vendor/nesbot/carbon/src/Carbon/Lang/mfe.php | 15 + .../nesbot/carbon/src/Carbon/Lang/mfe_MU.php | 53 + vendor/nesbot/carbon/src/Carbon/Lang/mg.php | 15 + .../nesbot/carbon/src/Carbon/Lang/mg_MG.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/mgh.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/mgo.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/mhr.php | 15 + .../nesbot/carbon/src/Carbon/Lang/mhr_RU.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/mi.php | 66 + .../nesbot/carbon/src/Carbon/Lang/mi_NZ.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/miq.php | 15 + .../nesbot/carbon/src/Carbon/Lang/miq_NI.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/mjw.php | 15 + .../nesbot/carbon/src/Carbon/Lang/mjw_IN.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/mk.php | 116 + .../nesbot/carbon/src/Carbon/Lang/mk_MK.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/ml.php | 76 + .../nesbot/carbon/src/Carbon/Lang/ml_IN.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/mn.php | 116 + .../nesbot/carbon/src/Carbon/Lang/mn_MN.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/mni.php | 15 + .../nesbot/carbon/src/Carbon/Lang/mni_IN.php | 35 + vendor/nesbot/carbon/src/Carbon/Lang/mo.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/mr.php | 86 + .../nesbot/carbon/src/Carbon/Lang/mr_IN.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/ms.php | 104 + .../nesbot/carbon/src/Carbon/Lang/ms_BN.php | 22 + .../nesbot/carbon/src/Carbon/Lang/ms_MY.php | 18 + .../nesbot/carbon/src/Carbon/Lang/ms_SG.php | 22 + vendor/nesbot/carbon/src/Carbon/Lang/mt.php | 65 + .../nesbot/carbon/src/Carbon/Lang/mt_MT.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/mua.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/my.php | 70 + .../nesbot/carbon/src/Carbon/Lang/my_MM.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/mzn.php | 25 + vendor/nesbot/carbon/src/Carbon/Lang/nan.php | 15 + .../nesbot/carbon/src/Carbon/Lang/nan_TW.php | 55 + .../carbon/src/Carbon/Lang/nan_TW@latin.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/naq.php | 52 + vendor/nesbot/carbon/src/Carbon/Lang/nb.php | 84 + .../nesbot/carbon/src/Carbon/Lang/nb_NO.php | 12 + .../nesbot/carbon/src/Carbon/Lang/nb_SJ.php | 18 + vendor/nesbot/carbon/src/Carbon/Lang/nd.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/nds.php | 15 + .../nesbot/carbon/src/Carbon/Lang/nds_DE.php | 60 + .../nesbot/carbon/src/Carbon/Lang/nds_NL.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/ne.php | 82 + .../nesbot/carbon/src/Carbon/Lang/ne_IN.php | 25 + .../nesbot/carbon/src/Carbon/Lang/ne_NP.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/nhn.php | 15 + .../nesbot/carbon/src/Carbon/Lang/nhn_MX.php | 50 + vendor/nesbot/carbon/src/Carbon/Lang/niu.php | 15 + .../nesbot/carbon/src/Carbon/Lang/niu_NU.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/nl.php | 113 + .../nesbot/carbon/src/Carbon/Lang/nl_AW.php | 27 + .../nesbot/carbon/src/Carbon/Lang/nl_BE.php | 27 + .../nesbot/carbon/src/Carbon/Lang/nl_BQ.php | 12 + .../nesbot/carbon/src/Carbon/Lang/nl_CW.php | 12 + .../nesbot/carbon/src/Carbon/Lang/nl_NL.php | 24 + .../nesbot/carbon/src/Carbon/Lang/nl_SR.php | 12 + .../nesbot/carbon/src/Carbon/Lang/nl_SX.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/nmg.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/nn.php | 78 + .../nesbot/carbon/src/Carbon/Lang/nn_NO.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/nnh.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/no.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/nr.php | 15 + .../nesbot/carbon/src/Carbon/Lang/nr_ZA.php | 26 + vendor/nesbot/carbon/src/Carbon/Lang/nso.php | 15 + .../nesbot/carbon/src/Carbon/Lang/nso_ZA.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/nus.php | 36 + vendor/nesbot/carbon/src/Carbon/Lang/nyn.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/oc.php | 100 + .../nesbot/carbon/src/Carbon/Lang/oc_FR.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/om.php | 60 + .../nesbot/carbon/src/Carbon/Lang/om_ET.php | 12 + .../nesbot/carbon/src/Carbon/Lang/om_KE.php | 14 + vendor/nesbot/carbon/src/Carbon/Lang/or.php | 15 + .../nesbot/carbon/src/Carbon/Lang/or_IN.php | 51 + vendor/nesbot/carbon/src/Carbon/Lang/os.php | 15 + .../nesbot/carbon/src/Carbon/Lang/os_RU.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/pa.php | 76 + .../nesbot/carbon/src/Carbon/Lang/pa_Arab.php | 26 + .../nesbot/carbon/src/Carbon/Lang/pa_Guru.php | 27 + .../nesbot/carbon/src/Carbon/Lang/pa_IN.php | 19 + .../nesbot/carbon/src/Carbon/Lang/pa_PK.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/pap.php | 39 + .../nesbot/carbon/src/Carbon/Lang/pap_AW.php | 16 + .../nesbot/carbon/src/Carbon/Lang/pap_CW.php | 16 + vendor/nesbot/carbon/src/Carbon/Lang/pl.php | 126 + .../nesbot/carbon/src/Carbon/Lang/pl_PL.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/prg.php | 52 + vendor/nesbot/carbon/src/Carbon/Lang/ps.php | 55 + .../nesbot/carbon/src/Carbon/Lang/ps_AF.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/pt.php | 116 + .../nesbot/carbon/src/Carbon/Lang/pt_AO.php | 12 + .../nesbot/carbon/src/Carbon/Lang/pt_BR.php | 39 + .../nesbot/carbon/src/Carbon/Lang/pt_CH.php | 12 + .../nesbot/carbon/src/Carbon/Lang/pt_CV.php | 12 + .../nesbot/carbon/src/Carbon/Lang/pt_GQ.php | 12 + .../nesbot/carbon/src/Carbon/Lang/pt_GW.php | 12 + .../nesbot/carbon/src/Carbon/Lang/pt_LU.php | 12 + .../nesbot/carbon/src/Carbon/Lang/pt_MO.php | 20 + .../nesbot/carbon/src/Carbon/Lang/pt_MZ.php | 14 + .../nesbot/carbon/src/Carbon/Lang/pt_PT.php | 27 + .../nesbot/carbon/src/Carbon/Lang/pt_ST.php | 12 + .../nesbot/carbon/src/Carbon/Lang/pt_TL.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/qu.php | 22 + .../nesbot/carbon/src/Carbon/Lang/qu_BO.php | 14 + .../nesbot/carbon/src/Carbon/Lang/qu_EC.php | 14 + vendor/nesbot/carbon/src/Carbon/Lang/quz.php | 15 + .../nesbot/carbon/src/Carbon/Lang/quz_PE.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/raj.php | 15 + .../nesbot/carbon/src/Carbon/Lang/raj_IN.php | 47 + vendor/nesbot/carbon/src/Carbon/Lang/rm.php | 51 + vendor/nesbot/carbon/src/Carbon/Lang/rn.php | 56 + vendor/nesbot/carbon/src/Carbon/Lang/ro.php | 77 + .../nesbot/carbon/src/Carbon/Lang/ro_MD.php | 21 + .../nesbot/carbon/src/Carbon/Lang/ro_RO.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/rof.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/ru.php | 191 + .../nesbot/carbon/src/Carbon/Lang/ru_BY.php | 12 + .../nesbot/carbon/src/Carbon/Lang/ru_KG.php | 12 + .../nesbot/carbon/src/Carbon/Lang/ru_KZ.php | 12 + .../nesbot/carbon/src/Carbon/Lang/ru_MD.php | 12 + .../nesbot/carbon/src/Carbon/Lang/ru_RU.php | 12 + .../nesbot/carbon/src/Carbon/Lang/ru_UA.php | 20 + vendor/nesbot/carbon/src/Carbon/Lang/rw.php | 15 + .../nesbot/carbon/src/Carbon/Lang/rw_RW.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/rwk.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/sa.php | 15 + .../nesbot/carbon/src/Carbon/Lang/sa_IN.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/sah.php | 15 + .../nesbot/carbon/src/Carbon/Lang/sah_RU.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/saq.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/sat.php | 15 + .../nesbot/carbon/src/Carbon/Lang/sat_IN.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/sbp.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/sc.php | 15 + .../nesbot/carbon/src/Carbon/Lang/sc_IT.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/sd.php | 81 + .../nesbot/carbon/src/Carbon/Lang/sd_IN.php | 26 + .../src/Carbon/Lang/sd_IN@devanagari.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/se.php | 73 + .../nesbot/carbon/src/Carbon/Lang/se_FI.php | 27 + .../nesbot/carbon/src/Carbon/Lang/se_NO.php | 12 + .../nesbot/carbon/src/Carbon/Lang/se_SE.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/seh.php | 26 + vendor/nesbot/carbon/src/Carbon/Lang/ses.php | 56 + vendor/nesbot/carbon/src/Carbon/Lang/sg.php | 52 + vendor/nesbot/carbon/src/Carbon/Lang/sgs.php | 15 + .../nesbot/carbon/src/Carbon/Lang/sgs_LT.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/sh.php | 68 + vendor/nesbot/carbon/src/Carbon/Lang/shi.php | 57 + .../carbon/src/Carbon/Lang/shi_Latn.php | 33 + .../carbon/src/Carbon/Lang/shi_Tfng.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/shn.php | 15 + .../nesbot/carbon/src/Carbon/Lang/shn_MM.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/shs.php | 15 + .../nesbot/carbon/src/Carbon/Lang/shs_CA.php | 38 + vendor/nesbot/carbon/src/Carbon/Lang/si.php | 78 + .../nesbot/carbon/src/Carbon/Lang/si_LK.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/sid.php | 15 + .../nesbot/carbon/src/Carbon/Lang/sid_ET.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/sk.php | 155 + .../nesbot/carbon/src/Carbon/Lang/sk_SK.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/sl.php | 129 + .../nesbot/carbon/src/Carbon/Lang/sl_SI.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/sm.php | 15 + .../nesbot/carbon/src/Carbon/Lang/sm_WS.php | 53 + vendor/nesbot/carbon/src/Carbon/Lang/smn.php | 57 + vendor/nesbot/carbon/src/Carbon/Lang/sn.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/so.php | 74 + .../nesbot/carbon/src/Carbon/Lang/so_DJ.php | 20 + .../nesbot/carbon/src/Carbon/Lang/so_ET.php | 16 + .../nesbot/carbon/src/Carbon/Lang/so_KE.php | 16 + .../nesbot/carbon/src/Carbon/Lang/so_SO.php | 16 + vendor/nesbot/carbon/src/Carbon/Lang/sq.php | 79 + .../nesbot/carbon/src/Carbon/Lang/sq_AL.php | 12 + .../nesbot/carbon/src/Carbon/Lang/sq_MK.php | 19 + .../nesbot/carbon/src/Carbon/Lang/sq_XK.php | 19 + vendor/nesbot/carbon/src/Carbon/Lang/sr.php | 112 + .../nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php | 112 + .../carbon/src/Carbon/Lang/sr_Cyrl_BA.php | 33 + .../carbon/src/Carbon/Lang/sr_Cyrl_ME.php | 118 + .../carbon/src/Carbon/Lang/sr_Cyrl_XK.php | 24 + .../nesbot/carbon/src/Carbon/Lang/sr_Latn.php | 12 + .../carbon/src/Carbon/Lang/sr_Latn_BA.php | 33 + .../carbon/src/Carbon/Lang/sr_Latn_ME.php | 76 + .../carbon/src/Carbon/Lang/sr_Latn_XK.php | 24 + .../nesbot/carbon/src/Carbon/Lang/sr_ME.php | 12 + .../nesbot/carbon/src/Carbon/Lang/sr_RS.php | 16 + .../carbon/src/Carbon/Lang/sr_RS@latin.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/ss.php | 78 + .../nesbot/carbon/src/Carbon/Lang/ss_ZA.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/st.php | 15 + .../nesbot/carbon/src/Carbon/Lang/st_ZA.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/sv.php | 87 + .../nesbot/carbon/src/Carbon/Lang/sv_AX.php | 19 + .../nesbot/carbon/src/Carbon/Lang/sv_FI.php | 12 + .../nesbot/carbon/src/Carbon/Lang/sv_SE.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/sw.php | 74 + .../nesbot/carbon/src/Carbon/Lang/sw_CD.php | 17 + .../nesbot/carbon/src/Carbon/Lang/sw_KE.php | 27 + .../nesbot/carbon/src/Carbon/Lang/sw_TZ.php | 28 + .../nesbot/carbon/src/Carbon/Lang/sw_UG.php | 17 + vendor/nesbot/carbon/src/Carbon/Lang/szl.php | 15 + .../nesbot/carbon/src/Carbon/Lang/szl_PL.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/ta.php | 97 + .../nesbot/carbon/src/Carbon/Lang/ta_IN.php | 27 + .../nesbot/carbon/src/Carbon/Lang/ta_LK.php | 28 + .../nesbot/carbon/src/Carbon/Lang/ta_MY.php | 28 + .../nesbot/carbon/src/Carbon/Lang/ta_SG.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/tcy.php | 15 + .../nesbot/carbon/src/Carbon/Lang/tcy_IN.php | 39 + vendor/nesbot/carbon/src/Carbon/Lang/te.php | 89 + .../nesbot/carbon/src/Carbon/Lang/te_IN.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/teo.php | 28 + .../nesbot/carbon/src/Carbon/Lang/teo_KE.php | 14 + vendor/nesbot/carbon/src/Carbon/Lang/tet.php | 64 + vendor/nesbot/carbon/src/Carbon/Lang/tg.php | 104 + .../nesbot/carbon/src/Carbon/Lang/tg_TJ.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/th.php | 73 + .../nesbot/carbon/src/Carbon/Lang/th_TH.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/the.php | 15 + .../nesbot/carbon/src/Carbon/Lang/the_NP.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/ti.php | 15 + .../nesbot/carbon/src/Carbon/Lang/ti_ER.php | 56 + .../nesbot/carbon/src/Carbon/Lang/ti_ET.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/tig.php | 15 + .../nesbot/carbon/src/Carbon/Lang/tig_ER.php | 56 + vendor/nesbot/carbon/src/Carbon/Lang/tk.php | 15 + .../nesbot/carbon/src/Carbon/Lang/tk_TM.php | 77 + vendor/nesbot/carbon/src/Carbon/Lang/tl.php | 61 + .../nesbot/carbon/src/Carbon/Lang/tl_PH.php | 18 + vendor/nesbot/carbon/src/Carbon/Lang/tlh.php | 72 + vendor/nesbot/carbon/src/Carbon/Lang/tn.php | 15 + .../nesbot/carbon/src/Carbon/Lang/tn_ZA.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/to.php | 15 + .../nesbot/carbon/src/Carbon/Lang/to_TO.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/tpi.php | 15 + .../nesbot/carbon/src/Carbon/Lang/tpi_PG.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/tr.php | 121 + .../nesbot/carbon/src/Carbon/Lang/tr_CY.php | 23 + .../nesbot/carbon/src/Carbon/Lang/tr_TR.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/ts.php | 15 + .../nesbot/carbon/src/Carbon/Lang/ts_ZA.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/tt.php | 15 + .../nesbot/carbon/src/Carbon/Lang/tt_RU.php | 39 + .../carbon/src/Carbon/Lang/tt_RU@iqtelif.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/twq.php | 14 + vendor/nesbot/carbon/src/Carbon/Lang/tzl.php | 65 + vendor/nesbot/carbon/src/Carbon/Lang/tzm.php | 57 + .../carbon/src/Carbon/Lang/tzm_Latn.php | 64 + vendor/nesbot/carbon/src/Carbon/Lang/ug.php | 90 + .../nesbot/carbon/src/Carbon/Lang/ug_CN.php | 17 + vendor/nesbot/carbon/src/Carbon/Lang/uk.php | 212 + .../nesbot/carbon/src/Carbon/Lang/uk_UA.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/unm.php | 15 + .../nesbot/carbon/src/Carbon/Lang/unm_US.php | 57 + vendor/nesbot/carbon/src/Carbon/Lang/ur.php | 94 + .../nesbot/carbon/src/Carbon/Lang/ur_IN.php | 26 + .../nesbot/carbon/src/Carbon/Lang/ur_PK.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/uz.php | 85 + .../nesbot/carbon/src/Carbon/Lang/uz_Arab.php | 28 + .../nesbot/carbon/src/Carbon/Lang/uz_Cyrl.php | 20 + .../nesbot/carbon/src/Carbon/Lang/uz_Latn.php | 74 + .../nesbot/carbon/src/Carbon/Lang/uz_UZ.php | 27 + .../carbon/src/Carbon/Lang/uz_UZ@cyrillic.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/vai.php | 35 + .../carbon/src/Carbon/Lang/vai_Latn.php | 27 + .../carbon/src/Carbon/Lang/vai_Vaii.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/ve.php | 15 + .../nesbot/carbon/src/Carbon/Lang/ve_ZA.php | 49 + vendor/nesbot/carbon/src/Carbon/Lang/vi.php | 76 + .../nesbot/carbon/src/Carbon/Lang/vi_VN.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/vo.php | 52 + vendor/nesbot/carbon/src/Carbon/Lang/vun.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/wa.php | 15 + .../nesbot/carbon/src/Carbon/Lang/wa_BE.php | 55 + vendor/nesbot/carbon/src/Carbon/Lang/wae.php | 15 + .../nesbot/carbon/src/Carbon/Lang/wae_CH.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/wal.php | 15 + .../nesbot/carbon/src/Carbon/Lang/wal_ET.php | 27 + vendor/nesbot/carbon/src/Carbon/Lang/wo.php | 15 + .../nesbot/carbon/src/Carbon/Lang/wo_SN.php | 39 + vendor/nesbot/carbon/src/Carbon/Lang/xh.php | 15 + .../nesbot/carbon/src/Carbon/Lang/xh_ZA.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/xog.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/yav.php | 28 + vendor/nesbot/carbon/src/Carbon/Lang/yi.php | 15 + .../nesbot/carbon/src/Carbon/Lang/yi_US.php | 54 + vendor/nesbot/carbon/src/Carbon/Lang/yo.php | 65 + .../nesbot/carbon/src/Carbon/Lang/yo_BJ.php | 28 + .../nesbot/carbon/src/Carbon/Lang/yo_NG.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/yue.php | 15 + .../nesbot/carbon/src/Carbon/Lang/yue_HK.php | 28 + .../carbon/src/Carbon/Lang/yue_Hans.php | 12 + .../carbon/src/Carbon/Lang/yue_Hant.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/yuw.php | 15 + .../nesbot/carbon/src/Carbon/Lang/yuw_PG.php | 26 + vendor/nesbot/carbon/src/Carbon/Lang/zgh.php | 80 + vendor/nesbot/carbon/src/Carbon/Lang/zh.php | 29 + .../nesbot/carbon/src/Carbon/Lang/zh_CN.php | 33 + .../nesbot/carbon/src/Carbon/Lang/zh_HK.php | 12 + .../nesbot/carbon/src/Carbon/Lang/zh_Hans.php | 109 + .../carbon/src/Carbon/Lang/zh_Hans_HK.php | 12 + .../carbon/src/Carbon/Lang/zh_Hans_MO.php | 12 + .../carbon/src/Carbon/Lang/zh_Hans_SG.php | 12 + .../nesbot/carbon/src/Carbon/Lang/zh_Hant.php | 111 + .../carbon/src/Carbon/Lang/zh_Hant_HK.php | 12 + .../carbon/src/Carbon/Lang/zh_Hant_MO.php | 12 + .../carbon/src/Carbon/Lang/zh_Hant_TW.php | 12 + .../nesbot/carbon/src/Carbon/Lang/zh_MO.php | 21 + .../nesbot/carbon/src/Carbon/Lang/zh_SG.php | 26 + .../nesbot/carbon/src/Carbon/Lang/zh_TW.php | 12 + .../nesbot/carbon/src/Carbon/Lang/zh_YUE.php | 20 + vendor/nesbot/carbon/src/Carbon/Lang/zu.php | 15 + .../nesbot/carbon/src/Carbon/Lang/zu_ZA.php | 54 + vendor/nesbot/carbon/src/Carbon/Language.php | 342 ++ .../src/Carbon/Laravel/ServiceProvider.php | 127 + .../carbon/src/Carbon/List/languages.php | 1239 ++++ .../nesbot/carbon/src/Carbon/List/regions.php | 265 + .../MessageFormatterMapper.php | 44 + .../src/Carbon/PHPStan/AbstractMacro.php | 286 + .../carbon/src/Carbon/PHPStan/Macro.php | 34 + .../src/Carbon/PHPStan/MacroExtension.php | 88 + .../src/Carbon/PHPStan/MacroScanner.php | 83 + .../carbon/src/Carbon/Traits/Boundaries.php | 443 ++ .../nesbot/carbon/src/Carbon/Traits/Cast.php | 43 + .../carbon/src/Carbon/Traits/Comparison.php | 1129 ++++ .../carbon/src/Carbon/Traits/Converter.php | 639 ++ .../carbon/src/Carbon/Traits/Creator.php | 977 ++++ .../nesbot/carbon/src/Carbon/Traits/Date.php | 2747 +++++++++ .../Carbon/Traits/DeprecatedProperties.php | 61 + .../carbon/src/Carbon/Traits/Difference.php | 1182 ++++ .../src/Carbon/Traits/IntervalRounding.php | 57 + .../carbon/src/Carbon/Traits/IntervalStep.php | 93 + .../carbon/src/Carbon/Traits/Localization.php | 840 +++ .../nesbot/carbon/src/Carbon/Traits/Macro.php | 136 + .../src/Carbon/Traits/MagicParameter.php | 33 + .../nesbot/carbon/src/Carbon/Traits/Mixin.php | 226 + .../carbon/src/Carbon/Traits/Modifiers.php | 472 ++ .../carbon/src/Carbon/Traits/Mutability.php | 71 + .../Carbon/Traits/ObjectInitialisation.php | 22 + .../carbon/src/Carbon/Traits/Options.php | 471 ++ .../carbon/src/Carbon/Traits/Rounding.php | 254 + .../src/Carbon/Traits/Serialization.php | 326 ++ .../nesbot/carbon/src/Carbon/Traits/Test.php | 228 + .../carbon/src/Carbon/Traits/Timestamp.php | 198 + .../src/Carbon/Traits/ToStringFormat.php | 56 + .../nesbot/carbon/src/Carbon/Traits/Units.php | 412 ++ .../nesbot/carbon/src/Carbon/Traits/Week.php | 219 + .../nesbot/carbon/src/Carbon/Translator.php | 32 + .../carbon/src/Carbon/TranslatorImmutable.php | 99 + .../Carbon/TranslatorStrongTypeInterface.php | 22 + vendor/nikic/fast-route/.gitignore | 5 + vendor/nikic/fast-route/.hhconfig | 1 + vendor/nikic/fast-route/.travis.yml | 20 + vendor/nikic/fast-route/FastRoute.hhi | 126 + vendor/nikic/fast-route/LICENSE | 31 + vendor/nikic/fast-route/README.md | 313 + vendor/nikic/fast-route/composer.json | 24 + vendor/nikic/fast-route/phpunit.xml | 24 + vendor/nikic/fast-route/psalm.xml | 28 + .../fast-route/src/BadRouteException.php | 7 + vendor/nikic/fast-route/src/DataGenerator.php | 26 + .../src/DataGenerator/CharCountBased.php | 31 + .../src/DataGenerator/GroupCountBased.php | 30 + .../src/DataGenerator/GroupPosBased.php | 27 + .../src/DataGenerator/MarkBased.php | 27 + .../src/DataGenerator/RegexBasedAbstract.php | 186 + vendor/nikic/fast-route/src/Dispatcher.php | 26 + .../src/Dispatcher/CharCountBased.php | 31 + .../src/Dispatcher/GroupCountBased.php | 31 + .../src/Dispatcher/GroupPosBased.php | 33 + .../fast-route/src/Dispatcher/MarkBased.php | 31 + .../src/Dispatcher/RegexBasedAbstract.php | 88 + vendor/nikic/fast-route/src/Route.php | 47 + .../nikic/fast-route/src/RouteCollector.php | 152 + vendor/nikic/fast-route/src/RouteParser.php | 37 + .../nikic/fast-route/src/RouteParser/Std.php | 87 + vendor/nikic/fast-route/src/bootstrap.php | 12 + vendor/nikic/fast-route/src/functions.php | 74 + .../test/Dispatcher/CharCountBasedTest.php | 16 + .../test/Dispatcher/DispatcherTest.php | 581 ++ .../test/Dispatcher/GroupCountBasedTest.php | 16 + .../test/Dispatcher/GroupPosBasedTest.php | 16 + .../test/Dispatcher/MarkBasedTest.php | 24 + .../HackTypechecker/HackTypecheckerTest.php | 44 + .../HackTypechecker/fixtures/all_options.php | 29 + .../fixtures/empty_options.php | 11 + .../HackTypechecker/fixtures/no_options.php | 11 + .../fast-route/test/RouteCollectorTest.php | 108 + .../fast-route/test/RouteParser/StdTest.php | 154 + vendor/nikic/fast-route/test/bootstrap.php | 11 + vendor/phpoption/phpoption/LICENSE | 201 + vendor/phpoption/phpoption/composer.json | 50 + .../phpoption/src/PhpOption/LazyOption.php | 175 + .../phpoption/src/PhpOption/None.php | 136 + .../phpoption/src/PhpOption/Option.php | 434 ++ .../phpoption/src/PhpOption/Some.php | 169 + vendor/psr/cache/CHANGELOG.md | 16 + vendor/psr/cache/LICENSE.txt | 19 + vendor/psr/cache/README.md | 12 + vendor/psr/cache/composer.json | 25 + vendor/psr/cache/src/CacheException.php | 10 + vendor/psr/cache/src/CacheItemInterface.php | 105 + .../psr/cache/src/CacheItemPoolInterface.php | 138 + .../cache/src/InvalidArgumentException.php | 13 + vendor/psr/clock/CHANGELOG.md | 11 + vendor/psr/clock/LICENSE | 19 + vendor/psr/clock/README.md | 61 + vendor/psr/clock/composer.json | 21 + vendor/psr/clock/src/ClockInterface.php | 13 + vendor/psr/container/.gitignore | 3 + vendor/psr/container/LICENSE | 21 + vendor/psr/container/README.md | 13 + vendor/psr/container/composer.json | 27 + .../src/ContainerExceptionInterface.php | 12 + .../psr/container/src/ContainerInterface.php | 36 + .../src/NotFoundExceptionInterface.php | 10 + vendor/psr/http-client/CHANGELOG.md | 31 + vendor/psr/http-client/LICENSE | 19 + vendor/psr/http-client/README.md | 12 + vendor/psr/http-client/composer.json | 30 + .../src/ClientExceptionInterface.php | 10 + .../psr/http-client/src/ClientInterface.php | 20 + .../src/NetworkExceptionInterface.php | 24 + .../src/RequestExceptionInterface.php | 24 + vendor/psr/http-factory/LICENSE | 21 + vendor/psr/http-factory/README.md | 12 + vendor/psr/http-factory/composer.json | 35 + .../src/RequestFactoryInterface.php | 18 + .../src/ResponseFactoryInterface.php | 18 + .../src/ServerRequestFactoryInterface.php | 24 + .../src/StreamFactoryInterface.php | 45 + .../src/UploadedFileFactoryInterface.php | 34 + .../http-factory/src/UriFactoryInterface.php | 17 + vendor/psr/http-message/CHANGELOG.md | 36 + vendor/psr/http-message/LICENSE | 19 + vendor/psr/http-message/README.md | 16 + vendor/psr/http-message/composer.json | 26 + .../psr/http-message/docs/PSR7-Interfaces.md | 130 + vendor/psr/http-message/docs/PSR7-Usage.md | 159 + .../psr/http-message/src/MessageInterface.php | 187 + .../psr/http-message/src/RequestInterface.php | 130 + .../http-message/src/ResponseInterface.php | 68 + .../src/ServerRequestInterface.php | 261 + .../psr/http-message/src/StreamInterface.php | 158 + .../src/UploadedFileInterface.php | 123 + vendor/psr/http-message/src/UriInterface.php | 324 ++ vendor/psr/log/LICENSE | 19 + vendor/psr/log/README.md | 58 + vendor/psr/log/composer.json | 26 + vendor/psr/log/src/AbstractLogger.php | 15 + .../psr/log/src/InvalidArgumentException.php | 7 + vendor/psr/log/src/LogLevel.php | 18 + vendor/psr/log/src/LoggerAwareInterface.php | 18 + vendor/psr/log/src/LoggerAwareTrait.php | 26 + vendor/psr/log/src/LoggerInterface.php | 125 + vendor/psr/log/src/LoggerTrait.php | 142 + vendor/psr/log/src/NullLogger.php | 30 + vendor/psr/simple-cache/.editorconfig | 12 + vendor/psr/simple-cache/LICENSE.md | 21 + vendor/psr/simple-cache/README.md | 8 + vendor/psr/simple-cache/composer.json | 25 + .../psr/simple-cache/src/CacheException.php | 10 + .../psr/simple-cache/src/CacheInterface.php | 114 + .../src/InvalidArgumentException.php | 13 + vendor/ralouphie/getallheaders/LICENSE | 21 + vendor/ralouphie/getallheaders/README.md | 27 + vendor/ralouphie/getallheaders/composer.json | 26 + .../getallheaders/src/getallheaders.php | 46 + vendor/respect/stringifier/LICENSE.md | 21 + vendor/respect/stringifier/README.md | 46 + vendor/respect/stringifier/composer.json | 36 + vendor/respect/stringifier/src/Quoter.php | 27 + .../stringifier/src/Quoters/CodeQuoter.php | 36 + .../respect/stringifier/src/Stringifier.php | 27 + .../src/Stringifiers/ArrayStringifier.php | 114 + .../src/Stringifiers/BoolStringifier.php | 53 + .../src/Stringifiers/ClusterStringifier.php | 117 + .../src/Stringifiers/DateTimeStringifier.php | 76 + .../src/Stringifiers/InfiniteStringifier.php | 58 + .../Stringifiers/JsonParsableStringifier.php | 40 + .../JsonSerializableStringifier.php | 67 + .../src/Stringifiers/NanStringifier.php | 58 + .../src/Stringifiers/NullStringifier.php | 52 + .../src/Stringifiers/ObjectStringifier.php | 70 + .../src/Stringifiers/ResourceStringifier.php | 61 + .../StringableObjectStringifier.php | 57 + .../src/Stringifiers/ThrowableStringifier.php | 84 + .../Stringifiers/TraversableStringifier.php | 69 + vendor/respect/stringifier/src/stringify.php | 27 + vendor/robmorgan/phinx/.stickler.yml | 12 + vendor/robmorgan/phinx/LICENSE | 23 + vendor/robmorgan/phinx/README.md | 143 + vendor/robmorgan/phinx/app/phinx.php | 36 + vendor/robmorgan/phinx/app/web.php | 83 + vendor/robmorgan/phinx/bin/phinx | 28 + vendor/robmorgan/phinx/bin/phinx.bat | 41 + vendor/robmorgan/phinx/composer.json | 87 + vendor/robmorgan/phinx/data/phinx.json.dist | 38 + vendor/robmorgan/phinx/data/phinx.php.dist | 41 + vendor/robmorgan/phinx/data/phinx.yml.dist | 35 + .../robmorgan/phinx/docs/config/__init__.py | 0 vendor/robmorgan/phinx/docs/config/all.py | 43 + vendor/robmorgan/phinx/docs/en/commands.rst | 411 ++ vendor/robmorgan/phinx/docs/en/conf.py | 9 + .../robmorgan/phinx/docs/en/configuration.rst | 534 ++ vendor/robmorgan/phinx/docs/en/contents.rst | 14 + vendor/robmorgan/phinx/docs/en/copyright.rst | 29 + vendor/robmorgan/phinx/docs/en/goals.rst | 13 + vendor/robmorgan/phinx/docs/en/index.rst | 33 + vendor/robmorgan/phinx/docs/en/install.rst | 28 + vendor/robmorgan/phinx/docs/en/intro.rst | 16 + vendor/robmorgan/phinx/docs/en/migrations.rst | 1934 +++++++ vendor/robmorgan/phinx/docs/en/namespaces.rst | 154 + vendor/robmorgan/phinx/docs/en/seeding.rst | 234 + vendor/robmorgan/phinx/phpstan-baseline.neon | 27 + .../phinx/src/Phinx/Config/Config.php | 553 ++ .../src/Phinx/Config/ConfigInterface.php | 171 + .../phinx/src/Phinx/Config/FeatureFlags.php | 41 + .../Phinx/Config/NamespaceAwareInterface.php | 33 + .../src/Phinx/Config/NamespaceAwareTrait.php | 74 + .../Phinx/Console/Command/AbstractCommand.php | 427 ++ .../src/Phinx/Console/Command/Breakpoint.php | 109 + .../src/Phinx/Console/Command/Create.php | 326 ++ .../phinx/src/Phinx/Console/Command/Init.php | 171 + .../src/Phinx/Console/Command/ListAliases.php | 80 + .../src/Phinx/Console/Command/Migrate.php | 140 + .../src/Phinx/Console/Command/Rollback.php | 171 + .../src/Phinx/Console/Command/SeedCreate.php | 219 + .../src/Phinx/Console/Command/SeedRun.php | 121 + .../src/Phinx/Console/Command/Status.php | 94 + .../phinx/src/Phinx/Console/Command/Test.php | 97 + .../src/Phinx/Console/PhinxApplication.php | 72 + .../phinx/src/Phinx/Db/Action/Action.php | 38 + .../phinx/src/Phinx/Db/Action/AddColumn.php | 62 + .../src/Phinx/Db/Action/AddForeignKey.php | 78 + .../phinx/src/Phinx/Db/Action/AddIndex.php | 67 + .../src/Phinx/Db/Action/ChangeColumn.php | 87 + .../src/Phinx/Db/Action/ChangeComment.php | 42 + .../src/Phinx/Db/Action/ChangePrimaryKey.php | 42 + .../phinx/src/Phinx/Db/Action/CreateTable.php | 12 + .../src/Phinx/Db/Action/DropForeignKey.php | 68 + .../phinx/src/Phinx/Db/Action/DropIndex.php | 75 + .../phinx/src/Phinx/Db/Action/DropTable.php | 12 + .../src/Phinx/Db/Action/RemoveColumn.php | 59 + .../src/Phinx/Db/Action/RenameColumn.php | 79 + .../phinx/src/Phinx/Db/Action/RenameTable.php | 42 + .../src/Phinx/Db/Adapter/AbstractAdapter.php | 412 ++ .../src/Phinx/Db/Adapter/AdapterFactory.php | 172 + .../src/Phinx/Db/Adapter/AdapterInterface.php | 505 ++ .../src/Phinx/Db/Adapter/AdapterWrapper.php | 487 ++ .../Db/Adapter/DirectActionInterface.php | 139 + .../src/Phinx/Db/Adapter/MysqlAdapter.php | 1545 +++++ .../phinx/src/Phinx/Db/Adapter/PdoAdapter.php | 1029 ++++ .../src/Phinx/Db/Adapter/PostgresAdapter.php | 1675 ++++++ .../src/Phinx/Db/Adapter/ProxyAdapter.php | 129 + .../src/Phinx/Db/Adapter/SQLiteAdapter.php | 1953 +++++++ .../src/Phinx/Db/Adapter/SqlServerAdapter.php | 1373 +++++ .../Phinx/Db/Adapter/TablePrefixAdapter.php | 494 ++ .../Phinx/Db/Adapter/TimedOutputAdapter.php | 423 ++ .../UnsupportedColumnTypeException.php | 19 + .../src/Phinx/Db/Adapter/WrapperInterface.php | 39 + .../phinx/src/Phinx/Db/Plan/AlterTable.php | 72 + .../phinx/src/Phinx/Db/Plan/Intent.php | 55 + .../phinx/src/Phinx/Db/Plan/NewTable.php | 101 + .../phinx/src/Phinx/Db/Plan/Plan.php | 492 ++ .../Phinx/Db/Plan/Solver/ActionSplitter.php | 103 + vendor/robmorgan/phinx/src/Phinx/Db/Table.php | 721 +++ .../phinx/src/Phinx/Db/Table/Column.php | 801 +++ .../phinx/src/Phinx/Db/Table/ForeignKey.php | 237 + .../phinx/src/Phinx/Db/Table/Index.php | 227 + .../phinx/src/Phinx/Db/Table/Table.php | 84 + .../src/Phinx/Db/Util/AlterInstructions.php | 122 + .../src/Phinx/Migration/AbstractMigration.php | 338 ++ .../Migration/AbstractTemplateCreation.php | 74 + .../src/Phinx/Migration/CreationInterface.php | 69 + .../IrreversibleMigrationException.php | 20 + .../phinx/src/Phinx/Migration/Manager.php | 1141 ++++ .../Phinx/Migration/Manager/Environment.php | 397 ++ .../Migration.change.template.php.dist | 24 + .../Migration.up_down.template.php.dist | 18 + .../Phinx/Migration/MigrationInterface.php | 269 + .../phinx/src/Phinx/Seed/AbstractSeed.php | 222 + .../src/Phinx/Seed/Seed.template.php.dist | 20 + .../phinx/src/Phinx/Seed/SeedInterface.php | 188 + .../phinx/src/Phinx/Util/Expression.php | 41 + .../phinx/src/Phinx/Util/Literal.php | 41 + .../robmorgan/phinx/src/Phinx/Util/Util.php | 361 ++ .../phinx/src/Phinx/Wrapper/TextWrapper.php | 249 + .../phinx/src/composer_autoloader.php | 23 + vendor/symfony/cache-contracts/.gitignore | 3 + vendor/symfony/cache-contracts/CHANGELOG.md | 5 + .../cache-contracts/CacheInterface.php | 55 + vendor/symfony/cache-contracts/CacheTrait.php | 78 + .../cache-contracts/CallbackInterface.php | 30 + .../symfony/cache-contracts/ItemInterface.php | 65 + vendor/symfony/cache-contracts/LICENSE | 19 + vendor/symfony/cache-contracts/README.md | 9 + .../TagAwareCacheInterface.php | 38 + vendor/symfony/cache-contracts/composer.json | 38 + .../symfony/cache/Adapter/AbstractAdapter.php | 204 + .../cache/Adapter/AbstractTagAwareAdapter.php | 328 ++ .../cache/Adapter/AdapterInterface.php | 43 + vendor/symfony/cache/Adapter/ApcuAdapter.php | 131 + vendor/symfony/cache/Adapter/ArrayAdapter.php | 391 ++ vendor/symfony/cache/Adapter/ChainAdapter.php | 328 ++ .../cache/Adapter/CouchbaseBucketAdapter.php | 247 + .../Adapter/CouchbaseCollectionAdapter.php | 214 + .../cache/Adapter/DoctrineDbalAdapter.php | 393 ++ .../cache/Adapter/FilesystemAdapter.php | 29 + .../Adapter/FilesystemTagAwareAdapter.php | 235 + .../cache/Adapter/MemcachedAdapter.php | 349 ++ vendor/symfony/cache/Adapter/NullAdapter.php | 138 + .../cache/Adapter/ParameterNormalizer.php | 35 + vendor/symfony/cache/Adapter/PdoAdapter.php | 383 ++ .../symfony/cache/Adapter/PhpArrayAdapter.php | 419 ++ .../symfony/cache/Adapter/PhpFilesAdapter.php | 327 ++ vendor/symfony/cache/Adapter/ProxyAdapter.php | 254 + vendor/symfony/cache/Adapter/Psr16Adapter.php | 86 + vendor/symfony/cache/Adapter/RedisAdapter.php | 27 + .../cache/Adapter/RedisTagAwareAdapter.php | 320 + .../symfony/cache/Adapter/TagAwareAdapter.php | 409 ++ .../Adapter/TagAwareAdapterInterface.php | 31 + .../cache/Adapter/TraceableAdapter.php | 284 + .../Adapter/TraceableTagAwareAdapter.php | 38 + vendor/symfony/cache/CHANGELOG.md | 114 + vendor/symfony/cache/CacheItem.php | 184 + .../DataCollector/CacheDataCollector.php | 181 + .../CacheCollectorPass.php | 79 + .../CachePoolClearerPass.php | 41 + .../DependencyInjection/CachePoolPass.php | 247 + .../CachePoolPrunerPass.php | 51 + .../cache/Exception/CacheException.php | 25 + .../Exception/InvalidArgumentException.php | 25 + .../cache/Exception/LogicException.php | 25 + vendor/symfony/cache/LICENSE | 19 + vendor/symfony/cache/LockRegistry.php | 164 + .../cache/Marshaller/DefaultMarshaller.php | 104 + .../cache/Marshaller/DeflateMarshaller.php | 53 + .../cache/Marshaller/MarshallerInterface.php | 38 + .../cache/Marshaller/SodiumMarshaller.php | 80 + .../cache/Marshaller/TagAwareMarshaller.php | 89 + .../Messenger/EarlyExpirationDispatcher.php | 61 + .../Messenger/EarlyExpirationHandler.php | 80 + .../Messenger/EarlyExpirationMessage.php | 100 + vendor/symfony/cache/PruneableInterface.php | 20 + vendor/symfony/cache/Psr16Cache.php | 269 + vendor/symfony/cache/README.md | 19 + vendor/symfony/cache/ResettableInterface.php | 21 + .../cache/Traits/AbstractAdapterTrait.php | 403 ++ .../symfony/cache/Traits/ContractsTrait.php | 118 + .../cache/Traits/FilesystemCommonTrait.php | 193 + .../symfony/cache/Traits/FilesystemTrait.php | 121 + vendor/symfony/cache/Traits/ProxyTrait.php | 43 + .../cache/Traits/RedisClusterNodeProxy.php | 50 + .../cache/Traits/RedisClusterProxy.php | 63 + vendor/symfony/cache/Traits/RedisProxy.php | 65 + vendor/symfony/cache/Traits/RedisTrait.php | 607 ++ vendor/symfony/cache/composer.json | 56 + .../symfony/config/Builder/ClassBuilder.php | 171 + .../config/Builder/ConfigBuilderGenerator.php | 581 ++ .../ConfigBuilderGeneratorInterface.php | 27 + .../config/Builder/ConfigBuilderInterface.php | 30 + vendor/symfony/config/Builder/Method.php | 34 + vendor/symfony/config/Builder/Property.php | 86 + vendor/symfony/config/CHANGELOG.md | 134 + vendor/symfony/config/ConfigCache.php | 60 + vendor/symfony/config/ConfigCacheFactory.php | 47 + .../config/ConfigCacheFactoryInterface.php | 30 + .../symfony/config/ConfigCacheInterface.php | 45 + .../symfony/config/Definition/ArrayNode.php | 402 ++ vendor/symfony/config/Definition/BaseNode.php | 510 ++ .../symfony/config/Definition/BooleanNode.php | 55 + .../Builder/ArrayNodeDefinition.php | 525 ++ .../Builder/BooleanNodeDefinition.php | 51 + .../Builder/BuilderAwareInterface.php | 25 + .../Definition/Builder/EnumNodeDefinition.php | 54 + .../config/Definition/Builder/ExprBuilder.php | 242 + .../Builder/FloatNodeDefinition.php | 30 + .../Builder/IntegerNodeDefinition.php | 30 + .../Definition/Builder/MergeBuilder.php | 61 + .../config/Definition/Builder/NodeBuilder.php | 201 + .../Definition/Builder/NodeDefinition.php | 342 ++ .../Builder/NodeParentInterface.php | 21 + .../Builder/NormalizationBuilder.php | 60 + .../Builder/NumericNodeDefinition.php | 69 + .../Builder/ParentNodeDefinitionInterface.php | 49 + .../Builder/ScalarNodeDefinition.php | 30 + .../config/Definition/Builder/TreeBuilder.php | 61 + .../Definition/Builder/ValidationBuilder.php | 44 + .../Builder/VariableNodeDefinition.php | 67 + .../Definition/ConfigurationInterface.php | 29 + .../Definition/Dumper/XmlReferenceDumper.php | 304 + .../Definition/Dumper/YamlReferenceDumper.php | 251 + vendor/symfony/config/Definition/EnumNode.php | 65 + .../Exception/DuplicateKeyException.php | 22 + .../config/Definition/Exception/Exception.php | 21 + .../Exception/ForbiddenOverwriteException.php | 22 + .../InvalidConfigurationException.php | 47 + .../Exception/InvalidDefinitionException.php | 21 + .../Exception/InvalidTypeException.php | 21 + .../Exception/UnsetKeyException.php | 22 + .../symfony/config/Definition/FloatNode.php | 51 + .../symfony/config/Definition/IntegerNode.php | 46 + .../config/Definition/NodeInterface.php | 77 + .../symfony/config/Definition/NumericNode.php | 64 + .../symfony/config/Definition/Processor.php | 91 + .../Definition/PrototypeNodeInterface.php | 25 + .../config/Definition/PrototypedArrayNode.php | 344 ++ .../symfony/config/Definition/ScalarNode.php | 67 + .../config/Definition/VariableNode.php | 138 + ...LoaderImportCircularReferenceException.php | 27 + .../FileLocatorFileNotFoundException.php | 34 + .../config/Exception/LoaderLoadException.php | 104 + vendor/symfony/config/FileLocator.php | 94 + .../symfony/config/FileLocatorInterface.php | 34 + vendor/symfony/config/LICENSE | 19 + .../config/Loader/DelegatingLoader.php | 50 + vendor/symfony/config/Loader/FileLoader.php | 178 + .../symfony/config/Loader/GlobFileLoader.php | 36 + vendor/symfony/config/Loader/Loader.php | 76 + .../symfony/config/Loader/LoaderInterface.php | 50 + .../symfony/config/Loader/LoaderResolver.php | 68 + .../config/Loader/LoaderResolverInterface.php | 27 + .../config/Loader/ParamConfigurator.php | 32 + vendor/symfony/config/README.md | 15 + .../Resource/ClassExistenceResource.php | 232 + .../config/Resource/ComposerResource.php | 67 + .../config/Resource/DirectoryResource.php | 99 + .../config/Resource/FileExistenceResource.php | 56 + .../symfony/config/Resource/FileResource.php | 63 + .../symfony/config/Resource/GlobResource.php | 243 + .../Resource/ReflectionClassResource.php | 257 + .../config/Resource/ResourceInterface.php | 31 + .../Resource/SelfCheckingResourceChecker.php | 46 + .../SelfCheckingResourceInterface.php | 28 + .../config/ResourceCheckerConfigCache.php | 182 + .../ResourceCheckerConfigCacheFactory.php | 44 + .../config/ResourceCheckerInterface.php | 45 + .../Util/Exception/InvalidXmlException.php | 22 + .../Util/Exception/XmlParsingException.php | 21 + vendor/symfony/config/Util/XmlUtils.php | 269 + vendor/symfony/config/composer.json | 45 + vendor/symfony/console/Application.php | 1267 ++++ .../symfony/console/Attribute/AsCommand.php | 39 + vendor/symfony/console/CHANGELOG.md | 226 + .../console/CI/GithubActionReporter.php | 99 + vendor/symfony/console/Color.php | 180 + vendor/symfony/console/Command/Command.php | 676 +++ .../console/Command/CompleteCommand.php | 205 + .../console/Command/DumpCompletionCommand.php | 139 + .../symfony/console/Command/HelpCommand.php | 99 + .../symfony/console/Command/LazyCommand.php | 194 + .../symfony/console/Command/ListCommand.php | 95 + .../symfony/console/Command/LockableTrait.php | 67 + .../Command/SignalableCommandInterface.php | 30 + .../CommandLoader/CommandLoaderInterface.php | 38 + .../CommandLoader/ContainerCommandLoader.php | 64 + .../CommandLoader/FactoryCommandLoader.php | 63 + .../console/Completion/CompletionInput.php | 249 + .../Completion/CompletionSuggestions.php | 97 + .../Output/BashCompletionOutput.php | 33 + .../Output/CompletionOutputInterface.php | 25 + .../symfony/console/Completion/Suggestion.php | 37 + vendor/symfony/console/ConsoleEvents.php | 72 + vendor/symfony/console/Cursor.php | 207 + .../AddConsoleCommandPass.php | 131 + .../Descriptor/ApplicationDescription.php | 139 + .../symfony/console/Descriptor/Descriptor.php | 94 + .../Descriptor/DescriptorInterface.php | 24 + .../console/Descriptor/JsonDescriptor.php | 181 + .../console/Descriptor/MarkdownDescriptor.php | 206 + .../console/Descriptor/TextDescriptor.php | 339 ++ .../console/Descriptor/XmlDescriptor.php | 247 + .../console/Event/ConsoleCommandEvent.php | 51 + .../console/Event/ConsoleErrorEvent.php | 58 + vendor/symfony/console/Event/ConsoleEvent.php | 61 + .../console/Event/ConsoleSignalEvent.php | 35 + .../console/Event/ConsoleTerminateEvent.php | 43 + .../console/EventListener/ErrorListener.php | 95 + .../Exception/CommandNotFoundException.php | 43 + .../console/Exception/ExceptionInterface.php | 21 + .../Exception/InvalidArgumentException.php | 19 + .../Exception/InvalidOptionException.php | 21 + .../console/Exception/LogicException.php | 19 + .../Exception/MissingInputException.php | 21 + .../Exception/NamespaceNotFoundException.php | 21 + .../console/Exception/RuntimeException.php | 19 + .../console/Formatter/NullOutputFormatter.php | 69 + .../Formatter/NullOutputFormatterStyle.php | 66 + .../console/Formatter/OutputFormatter.php | 280 + .../Formatter/OutputFormatterInterface.php | 52 + .../Formatter/OutputFormatterStyle.php | 106 + .../OutputFormatterStyleInterface.php | 50 + .../Formatter/OutputFormatterStyleStack.php | 103 + .../WrappableOutputFormatterInterface.php | 25 + .../console/Helper/DebugFormatterHelper.php | 101 + .../console/Helper/DescriptorHelper.php | 92 + vendor/symfony/console/Helper/Dumper.php | 64 + .../console/Helper/FormatterHelper.php | 84 + vendor/symfony/console/Helper/Helper.php | 154 + .../console/Helper/HelperInterface.php | 37 + vendor/symfony/console/Helper/HelperSet.php | 74 + .../console/Helper/InputAwareHelper.php | 33 + .../symfony/console/Helper/ProcessHelper.php | 140 + vendor/symfony/console/Helper/ProgressBar.php | 603 ++ .../console/Helper/ProgressIndicator.php | 244 + .../symfony/console/Helper/QuestionHelper.php | 598 ++ .../console/Helper/SymfonyQuestionHelper.php | 109 + vendor/symfony/console/Helper/Table.php | 849 +++ vendor/symfony/console/Helper/TableCell.php | 72 + .../symfony/console/Helper/TableCellStyle.php | 86 + vendor/symfony/console/Helper/TableRows.php | 30 + .../symfony/console/Helper/TableSeparator.php | 25 + vendor/symfony/console/Helper/TableStyle.php | 362 ++ vendor/symfony/console/Input/ArgvInput.php | 376 ++ vendor/symfony/console/Input/ArrayInput.php | 205 + vendor/symfony/console/Input/Input.php | 211 + .../symfony/console/Input/InputArgument.php | 121 + .../console/Input/InputAwareInterface.php | 26 + .../symfony/console/Input/InputDefinition.php | 402 ++ .../symfony/console/Input/InputInterface.php | 137 + vendor/symfony/console/Input/InputOption.php | 218 + .../Input/StreamableInputInterface.php | 37 + vendor/symfony/console/Input/StringInput.php | 84 + vendor/symfony/console/LICENSE | 19 + .../symfony/console/Logger/ConsoleLogger.php | 122 + .../symfony/console/Output/BufferedOutput.php | 43 + .../symfony/console/Output/ConsoleOutput.php | 168 + .../console/Output/ConsoleOutputInterface.php | 30 + .../console/Output/ConsoleSectionOutput.php | 141 + vendor/symfony/console/Output/NullOutput.php | 125 + vendor/symfony/console/Output/Output.php | 174 + .../console/Output/OutputInterface.php | 94 + .../symfony/console/Output/StreamOutput.php | 115 + .../console/Output/TrimmedBufferOutput.php | 61 + .../console/Question/ChoiceQuestion.php | 177 + .../console/Question/ConfirmationQuestion.php | 57 + vendor/symfony/console/Question/Question.php | 283 + vendor/symfony/console/README.md | 36 + .../console/Resources/bin/hiddeninput.exe | Bin 0 -> 9216 bytes .../symfony/console/Resources/completion.bash | 84 + .../console/SignalRegistry/SignalRegistry.php | 65 + .../console/SingleCommandApplication.php | 72 + vendor/symfony/console/Style/OutputStyle.php | 150 + .../symfony/console/Style/StyleInterface.php | 110 + vendor/symfony/console/Style/SymfonyStyle.php | 500 ++ vendor/symfony/console/Terminal.php | 168 + .../console/Tester/ApplicationTester.php | 85 + .../Tester/CommandCompletionTester.php | 56 + .../symfony/console/Tester/CommandTester.php | 76 + .../Tester/Constraint/CommandIsSuccessful.php | 55 + vendor/symfony/console/Tester/TesterTrait.php | 180 + vendor/symfony/console/composer.json | 56 + .../symfony/deprecation-contracts/.gitignore | 3 + .../deprecation-contracts/CHANGELOG.md | 5 + vendor/symfony/deprecation-contracts/LICENSE | 19 + .../symfony/deprecation-contracts/README.md | 26 + .../deprecation-contracts/composer.json | 35 + .../deprecation-contracts/function.php | 27 + vendor/symfony/filesystem/CHANGELOG.md | 82 + .../Exception/ExceptionInterface.php | 21 + .../Exception/FileNotFoundException.php | 34 + .../filesystem/Exception/IOException.php | 39 + .../Exception/IOExceptionInterface.php | 25 + .../Exception/InvalidArgumentException.php | 19 + .../filesystem/Exception/RuntimeException.php | 19 + vendor/symfony/filesystem/Filesystem.php | 737 +++ vendor/symfony/filesystem/LICENSE | 19 + vendor/symfony/filesystem/Path.php | 819 +++ vendor/symfony/filesystem/README.md | 13 + vendor/symfony/filesystem/composer.json | 30 + vendor/symfony/polyfill-ctype/Ctype.php | 232 + vendor/symfony/polyfill-ctype/LICENSE | 19 + vendor/symfony/polyfill-ctype/README.md | 12 + vendor/symfony/polyfill-ctype/bootstrap.php | 50 + vendor/symfony/polyfill-ctype/bootstrap80.php | 46 + vendor/symfony/polyfill-ctype/composer.json | 38 + .../polyfill-intl-grapheme/Grapheme.php | 247 + vendor/symfony/polyfill-intl-grapheme/LICENSE | 19 + .../symfony/polyfill-intl-grapheme/README.md | 31 + .../polyfill-intl-grapheme/bootstrap.php | 58 + .../polyfill-intl-grapheme/bootstrap80.php | 50 + .../polyfill-intl-grapheme/composer.json | 35 + .../symfony/polyfill-intl-normalizer/LICENSE | 19 + .../polyfill-intl-normalizer/Normalizer.php | 310 + .../polyfill-intl-normalizer/README.md | 14 + .../Resources/stubs/Normalizer.php | 17 + .../unidata/canonicalComposition.php | 945 +++ .../unidata/canonicalDecomposition.php | 2065 +++++++ .../Resources/unidata/combiningClass.php | 876 +++ .../unidata/compatibilityDecomposition.php | 3695 ++++++++++++ .../polyfill-intl-normalizer/bootstrap.php | 23 + .../polyfill-intl-normalizer/bootstrap80.php | 19 + .../polyfill-intl-normalizer/composer.json | 36 + vendor/symfony/polyfill-mbstring/LICENSE | 19 + vendor/symfony/polyfill-mbstring/Mbstring.php | 947 +++ vendor/symfony/polyfill-mbstring/README.md | 13 + .../Resources/unidata/caseFolding.php | 119 + .../Resources/unidata/lowerCase.php | 1397 +++++ .../Resources/unidata/titleCaseRegexp.php | 5 + .../Resources/unidata/upperCase.php | 1489 +++++ .../symfony/polyfill-mbstring/bootstrap.php | 151 + .../symfony/polyfill-mbstring/bootstrap80.php | 147 + .../symfony/polyfill-mbstring/composer.json | 38 + vendor/symfony/polyfill-php80/LICENSE | 19 + vendor/symfony/polyfill-php80/Php80.php | 115 + vendor/symfony/polyfill-php80/PhpToken.php | 103 + vendor/symfony/polyfill-php80/README.md | 25 + .../Resources/stubs/Attribute.php | 31 + .../Resources/stubs/PhpToken.php | 16 + .../Resources/stubs/Stringable.php | 20 + .../Resources/stubs/UnhandledMatchError.php | 16 + .../Resources/stubs/ValueError.php | 16 + vendor/symfony/polyfill-php80/bootstrap.php | 42 + vendor/symfony/polyfill-php80/composer.json | 37 + vendor/symfony/polyfill-php81/LICENSE | 19 + vendor/symfony/polyfill-php81/Php81.php | 37 + vendor/symfony/polyfill-php81/README.md | 18 + .../Resources/stubs/CURLStringFile.php | 51 + .../Resources/stubs/ReturnTypeWillChange.php | 20 + vendor/symfony/polyfill-php81/bootstrap.php | 28 + vendor/symfony/polyfill-php81/composer.json | 33 + vendor/symfony/service-contracts/.gitignore | 3 + .../service-contracts/Attribute/Required.php | 25 + .../Attribute/SubscribedService.php | 33 + vendor/symfony/service-contracts/CHANGELOG.md | 5 + vendor/symfony/service-contracts/LICENSE | 19 + vendor/symfony/service-contracts/README.md | 9 + .../service-contracts/ResetInterface.php | 30 + .../service-contracts/ServiceLocatorTrait.php | 124 + .../ServiceProviderInterface.php | 36 + .../ServiceSubscriberInterface.php | 53 + .../ServiceSubscriberTrait.php | 77 + .../Test/ServiceLocatorTest.php | 92 + .../symfony/service-contracts/composer.json | 41 + vendor/symfony/string/AbstractString.php | 716 +++ .../symfony/string/AbstractUnicodeString.php | 606 ++ vendor/symfony/string/ByteString.php | 493 ++ vendor/symfony/string/CHANGELOG.md | 35 + vendor/symfony/string/CodePointString.php | 260 + .../string/Exception/ExceptionInterface.php | 16 + .../Exception/InvalidArgumentException.php | 16 + .../string/Exception/RuntimeException.php | 16 + .../string/Inflector/EnglishInflector.php | 511 ++ .../string/Inflector/FrenchInflector.php | 157 + .../string/Inflector/InflectorInterface.php | 33 + vendor/symfony/string/LICENSE | 19 + vendor/symfony/string/LazyString.php | 143 + vendor/symfony/string/README.md | 14 + .../Resources/data/wcswidth_table_wide.php | 1143 ++++ .../Resources/data/wcswidth_table_zero.php | 1415 +++++ vendor/symfony/string/Resources/functions.php | 38 + .../symfony/string/Slugger/AsciiSlugger.php | 176 + .../string/Slugger/SluggerInterface.php | 27 + vendor/symfony/string/UnicodeString.php | 358 ++ vendor/symfony/string/composer.json | 42 + .../symfony/translation-contracts/.gitignore | 3 + .../translation-contracts/CHANGELOG.md | 5 + vendor/symfony/translation-contracts/LICENSE | 19 + .../LocaleAwareInterface.php | 27 + .../symfony/translation-contracts/README.md | 9 + .../Test/TranslatorTest.php | 384 ++ .../TranslatableInterface.php | 20 + .../TranslatorInterface.php | 68 + .../translation-contracts/TranslatorTrait.php | 260 + .../translation-contracts/composer.json | 37 + vendor/symfony/translation/CHANGELOG.md | 176 + .../Catalogue/AbstractOperation.php | 203 + .../translation/Catalogue/MergeOperation.php | 60 + .../Catalogue/OperationInterface.php | 61 + .../translation/Catalogue/TargetOperation.php | 74 + .../Command/TranslationPullCommand.php | 187 + .../Command/TranslationPushCommand.php | 188 + .../translation/Command/TranslationTrait.php | 77 + .../translation/Command/XliffLintCommand.php | 284 + .../TranslationDataCollector.php | 160 + .../translation/DataCollectorTranslator.php | 162 + .../TranslationDumperPass.php | 35 + .../TranslationExtractorPass.php | 40 + .../DependencyInjection/TranslatorPass.php | 74 + .../TranslatorPathsPass.php | 147 + .../translation/Dumper/CsvFileDumper.php | 60 + .../translation/Dumper/DumperInterface.php | 30 + .../symfony/translation/Dumper/FileDumper.php | 108 + .../translation/Dumper/IcuResFileDumper.php | 104 + .../translation/Dumper/IniFileDumper.php | 45 + .../translation/Dumper/JsonFileDumper.php | 40 + .../translation/Dumper/MoFileDumper.php | 82 + .../translation/Dumper/PhpFileDumper.php | 38 + .../translation/Dumper/PoFileDumper.php | 137 + .../translation/Dumper/QtFileDumper.php | 61 + .../translation/Dumper/XliffFileDumper.php | 203 + .../translation/Dumper/YamlFileDumper.php | 62 + .../Exception/ExceptionInterface.php | 21 + .../Exception/IncompleteDsnException.php | 24 + .../Exception/InvalidArgumentException.php | 21 + .../Exception/InvalidResourceException.php | 21 + .../translation/Exception/LogicException.php | 21 + .../MissingRequiredOptionException.php | 25 + .../Exception/NotFoundResourceException.php | 21 + .../Exception/ProviderException.php | 41 + .../Exception/ProviderExceptionInterface.php | 23 + .../Exception/RuntimeException.php | 21 + .../Exception/UnsupportedSchemeException.php | 54 + .../Extractor/AbstractFileExtractor.php | 67 + .../translation/Extractor/ChainExtractor.php | 57 + .../Extractor/ExtractorInterface.php | 35 + .../translation/Extractor/PhpExtractor.php | 330 ++ .../Extractor/PhpStringTokenParser.php | 136 + .../translation/Formatter/IntlFormatter.php | 60 + .../Formatter/IntlFormatterInterface.php | 27 + .../Formatter/MessageFormatter.php | 56 + .../Formatter/MessageFormatterInterface.php | 28 + .../translation/IdentityTranslator.php | 26 + vendor/symfony/translation/LICENSE | 19 + .../translation/Loader/ArrayLoader.php | 58 + .../translation/Loader/CsvFileLoader.php | 65 + .../symfony/translation/Loader/FileLoader.php | 62 + .../translation/Loader/IcuDatFileLoader.php | 61 + .../translation/Loader/IcuResFileLoader.php | 89 + .../translation/Loader/IniFileLoader.php | 28 + .../translation/Loader/JsonFileLoader.php | 60 + .../translation/Loader/LoaderInterface.php | 32 + .../translation/Loader/MoFileLoader.php | 140 + .../translation/Loader/PhpFileLoader.php | 42 + .../translation/Loader/PoFileLoader.php | 149 + .../translation/Loader/QtFileLoader.php | 82 + .../translation/Loader/XliffFileLoader.php | 232 + .../translation/Loader/YamlFileLoader.php | 54 + .../symfony/translation/LoggingTranslator.php | 129 + .../symfony/translation/MessageCatalogue.php | 309 + .../translation/MessageCatalogueInterface.php | 124 + .../translation/MetadataAwareInterface.php | 44 + .../Provider/AbstractProviderFactory.php | 45 + vendor/symfony/translation/Provider/Dsn.php | 110 + .../Provider/FilteringProvider.php | 65 + .../translation/Provider/NullProvider.php | 39 + .../Provider/NullProviderFactory.php | 34 + .../Provider/ProviderFactoryInterface.php | 26 + .../Provider/ProviderInterface.php | 32 + .../TranslationProviderCollection.php | 57 + .../TranslationProviderCollectionFactory.php | 57 + .../PseudoLocalizationTranslator.php | 368 ++ vendor/symfony/translation/README.md | 48 + .../translation/Reader/TranslationReader.php | 62 + .../Reader/TranslationReaderInterface.php | 27 + .../Resources/bin/translation-status.php | 278 + .../translation/Resources/data/parents.json | 141 + .../translation/Resources/functions.php | 22 + .../schemas/xliff-core-1.2-strict.xsd | 2223 +++++++ .../Resources/schemas/xliff-core-2.0.xsd | 411 ++ .../translation/Resources/schemas/xml.xsd | 309 + .../Test/ProviderFactoryTestCase.php | 147 + .../translation/Test/ProviderTestCase.php | 76 + .../translation/TranslatableMessage.php | 62 + vendor/symfony/translation/Translator.php | 469 ++ vendor/symfony/translation/TranslatorBag.php | 108 + .../translation/TranslatorBagInterface.php | 36 + .../translation/Util/ArrayConverter.php | 97 + .../symfony/translation/Util/XliffUtils.php | 191 + .../translation/Writer/TranslationWriter.php | 71 + .../Writer/TranslationWriterInterface.php | 33 + vendor/symfony/translation/composer.json | 60 + vendor/symfony/var-dumper/CHANGELOG.md | 72 + .../symfony/var-dumper/Caster/AmqpCaster.php | 212 + vendor/symfony/var-dumper/Caster/ArgsStub.php | 80 + vendor/symfony/var-dumper/Caster/Caster.php | 170 + .../symfony/var-dumper/Caster/ClassStub.php | 106 + .../symfony/var-dumper/Caster/ConstStub.php | 33 + .../var-dumper/Caster/CutArrayStub.php | 30 + vendor/symfony/var-dumper/Caster/CutStub.php | 64 + .../symfony/var-dumper/Caster/DOMCaster.php | 304 + .../symfony/var-dumper/Caster/DateCaster.php | 127 + .../var-dumper/Caster/DoctrineCaster.php | 62 + vendor/symfony/var-dumper/Caster/DsCaster.php | 70 + .../symfony/var-dumper/Caster/DsPairStub.php | 28 + vendor/symfony/var-dumper/Caster/EnumStub.php | 30 + .../var-dumper/Caster/ExceptionCaster.php | 388 ++ .../symfony/var-dumper/Caster/FiberCaster.php | 43 + .../symfony/var-dumper/Caster/FrameStub.php | 30 + .../symfony/var-dumper/Caster/GmpCaster.php | 32 + .../var-dumper/Caster/ImagineCaster.php | 37 + vendor/symfony/var-dumper/Caster/ImgStub.php | 26 + .../symfony/var-dumper/Caster/IntlCaster.php | 172 + vendor/symfony/var-dumper/Caster/LinkStub.php | 108 + .../var-dumper/Caster/MemcachedCaster.php | 81 + .../var-dumper/Caster/MysqliCaster.php | 33 + .../symfony/var-dumper/Caster/PdoCaster.php | 122 + .../symfony/var-dumper/Caster/PgSqlCaster.php | 156 + .../var-dumper/Caster/ProxyManagerCaster.php | 33 + .../var-dumper/Caster/RdKafkaCaster.php | 186 + .../symfony/var-dumper/Caster/RedisCaster.php | 149 + .../var-dumper/Caster/ReflectionCaster.php | 440 ++ .../var-dumper/Caster/ResourceCaster.php | 100 + .../symfony/var-dumper/Caster/SplCaster.php | 234 + .../symfony/var-dumper/Caster/StubCaster.php | 84 + .../var-dumper/Caster/SymfonyCaster.php | 97 + .../symfony/var-dumper/Caster/TraceStub.php | 36 + .../symfony/var-dumper/Caster/UuidCaster.php | 30 + .../var-dumper/Caster/XmlReaderCaster.php | 91 + .../var-dumper/Caster/XmlResourceCaster.php | 63 + .../var-dumper/Cloner/AbstractCloner.php | 388 ++ .../var-dumper/Cloner/ClonerInterface.php | 23 + vendor/symfony/var-dumper/Cloner/Cursor.php | 43 + vendor/symfony/var-dumper/Cloner/Data.php | 422 ++ .../var-dumper/Cloner/DumperInterface.php | 53 + vendor/symfony/var-dumper/Cloner/Stub.php | 67 + .../symfony/var-dumper/Cloner/VarCloner.php | 286 + .../Command/Descriptor/CliDescriptor.php | 79 + .../Descriptor/DumpDescriptorInterface.php | 23 + .../Command/Descriptor/HtmlDescriptor.php | 119 + .../var-dumper/Command/ServerDumpCommand.php | 112 + .../var-dumper/Dumper/AbstractDumper.php | 197 + .../symfony/var-dumper/Dumper/CliDumper.php | 643 +++ .../ContextProvider/CliContextProvider.php | 32 + .../ContextProviderInterface.php | 22 + .../RequestContextProvider.php | 51 + .../ContextProvider/SourceContextProvider.php | 126 + .../Dumper/ContextualizedDumper.php | 43 + .../var-dumper/Dumper/DataDumperInterface.php | 24 + .../symfony/var-dumper/Dumper/HtmlDumper.php | 986 ++++ .../var-dumper/Dumper/ServerDumper.php | 53 + .../Exception/ThrowingCasterException.php | 26 + vendor/symfony/var-dumper/LICENSE | 19 + vendor/symfony/var-dumper/README.md | 15 + .../var-dumper/Resources/bin/var-dump-server | 67 + .../Resources/css/htmlDescriptor.css | 130 + .../var-dumper/Resources/functions/dump.php | 50 + .../var-dumper/Resources/js/htmlDescriptor.js | 10 + .../symfony/var-dumper/Server/Connection.php | 99 + .../symfony/var-dumper/Server/DumpServer.php | 115 + .../var-dumper/Test/VarDumperTestTrait.php | 84 + vendor/symfony/var-dumper/VarDumper.php | 112 + vendor/symfony/var-dumper/composer.json | 49 + vendor/symfony/var-exporter/CHANGELOG.md | 12 + .../Exception/ClassNotFoundException.php | 20 + .../Exception/ExceptionInterface.php | 16 + .../NotInstantiableTypeException.php | 20 + vendor/symfony/var-exporter/Instantiator.php | 92 + .../var-exporter/Internal/Exporter.php | 410 ++ .../var-exporter/Internal/Hydrator.php | 152 + .../var-exporter/Internal/Reference.php | 30 + .../var-exporter/Internal/Registry.php | 146 + .../symfony/var-exporter/Internal/Values.php | 27 + vendor/symfony/var-exporter/LICENSE | 19 + vendor/symfony/var-exporter/README.md | 38 + vendor/symfony/var-exporter/VarExporter.php | 114 + vendor/symfony/var-exporter/composer.json | 31 + vendor/vlucas/phpdotenv/LICENSE | 30 + vendor/vlucas/phpdotenv/composer.json | 60 + vendor/vlucas/phpdotenv/src/Dotenv.php | 267 + .../src/Exception/ExceptionInterface.php | 12 + .../Exception/InvalidEncodingException.php | 12 + .../src/Exception/InvalidFileException.php | 12 + .../src/Exception/InvalidPathException.php | 12 + .../src/Exception/ValidationException.php | 12 + vendor/vlucas/phpdotenv/src/Loader/Loader.php | 47 + .../phpdotenv/src/Loader/LoaderInterface.php | 20 + .../vlucas/phpdotenv/src/Loader/Resolver.php | 65 + vendor/vlucas/phpdotenv/src/Parser/Entry.php | 59 + .../phpdotenv/src/Parser/EntryParser.php | 300 + vendor/vlucas/phpdotenv/src/Parser/Lexer.php | 58 + vendor/vlucas/phpdotenv/src/Parser/Lines.php | 127 + vendor/vlucas/phpdotenv/src/Parser/Parser.php | 53 + .../phpdotenv/src/Parser/ParserInterface.php | 19 + vendor/vlucas/phpdotenv/src/Parser/Value.php | 88 + .../Repository/Adapter/AdapterInterface.php | 15 + .../src/Repository/Adapter/ApacheAdapter.php | 89 + .../src/Repository/Adapter/ArrayAdapter.php | 80 + .../Repository/Adapter/EnvConstAdapter.php | 89 + .../src/Repository/Adapter/GuardedWriter.php | 85 + .../Repository/Adapter/ImmutableWriter.php | 110 + .../src/Repository/Adapter/MultiReader.php | 48 + .../src/Repository/Adapter/MultiWriter.php | 64 + .../src/Repository/Adapter/PutenvAdapter.php | 91 + .../Repository/Adapter/ReaderInterface.php | 17 + .../Repository/Adapter/ReplacingWriter.php | 104 + .../Repository/Adapter/ServerConstAdapter.php | 89 + .../Repository/Adapter/WriterInterface.php | 27 + .../src/Repository/AdapterRepository.php | 107 + .../src/Repository/RepositoryBuilder.php | 272 + .../src/Repository/RepositoryInterface.php | 51 + .../vlucas/phpdotenv/src/Store/File/Paths.php | 44 + .../phpdotenv/src/Store/File/Reader.php | 81 + .../vlucas/phpdotenv/src/Store/FileStore.php | 72 + .../phpdotenv/src/Store/StoreBuilder.php | 141 + .../phpdotenv/src/Store/StoreInterface.php | 17 + .../phpdotenv/src/Store/StringStore.php | 37 + vendor/vlucas/phpdotenv/src/Util/Regex.php | 112 + vendor/vlucas/phpdotenv/src/Util/Str.php | 98 + vendor/vlucas/phpdotenv/src/Validator.php | 209 + vendor/voku/portable-ascii/CHANGELOG.md | 202 + vendor/voku/portable-ascii/LICENSE.txt | 19 + vendor/voku/portable-ascii/README.md | 451 ++ vendor/voku/portable-ascii/composer.json | 37 + .../portable-ascii/src/voku/helper/ASCII.php | 1501 +++++ .../voku/helper/data/ascii_by_languages.php | 2950 ++++++++++ .../helper/data/ascii_extras_by_languages.php | 759 +++ .../helper/data/ascii_language_max_key.php | 65 + .../src/voku/helper/data/ascii_ord.php | 1 + .../src/voku/helper/data/x000.php | 16 + .../src/voku/helper/data/x001.php | 1 + .../src/voku/helper/data/x002.php | 1 + .../src/voku/helper/data/x003.php | 1 + .../src/voku/helper/data/x004.php | 1 + .../src/voku/helper/data/x005.php | 1 + .../src/voku/helper/data/x006.php | 1 + .../src/voku/helper/data/x007.php | 1 + .../src/voku/helper/data/x009.php | 1 + .../src/voku/helper/data/x00a.php | 1 + .../src/voku/helper/data/x00b.php | 1 + .../src/voku/helper/data/x00c.php | 1 + .../src/voku/helper/data/x00d.php | 1 + .../src/voku/helper/data/x00e.php | 1 + .../src/voku/helper/data/x00f.php | 1 + .../src/voku/helper/data/x010.php | 1 + .../src/voku/helper/data/x011.php | 1 + .../src/voku/helper/data/x012.php | 1 + .../src/voku/helper/data/x013.php | 1 + .../src/voku/helper/data/x014.php | 1 + .../src/voku/helper/data/x015.php | 1 + .../src/voku/helper/data/x016.php | 1 + .../src/voku/helper/data/x017.php | 1 + .../src/voku/helper/data/x018.php | 1 + .../src/voku/helper/data/x01d.php | 1 + .../src/voku/helper/data/x01e.php | 1 + .../src/voku/helper/data/x01f.php | 1 + .../src/voku/helper/data/x020.php | 4 + .../src/voku/helper/data/x021.php | 1 + .../src/voku/helper/data/x022.php | 1 + .../src/voku/helper/data/x023.php | 1 + .../src/voku/helper/data/x024.php | 1 + .../src/voku/helper/data/x025.php | 1 + .../src/voku/helper/data/x026.php | 1 + .../src/voku/helper/data/x027.php | 1 + .../src/voku/helper/data/x028.php | 1 + .../src/voku/helper/data/x029.php | 1 + .../src/voku/helper/data/x02a.php | 1 + .../src/voku/helper/data/x02c.php | 1 + .../src/voku/helper/data/x02e.php | 1 + .../src/voku/helper/data/x02f.php | 1 + .../src/voku/helper/data/x030.php | 9 + .../src/voku/helper/data/x031.php | 1 + .../src/voku/helper/data/x032.php | 1 + .../src/voku/helper/data/x033.php | 1 + .../src/voku/helper/data/x04d.php | 1 + .../src/voku/helper/data/x04e.php | 1 + .../src/voku/helper/data/x04f.php | 1 + .../src/voku/helper/data/x050.php | 1 + .../src/voku/helper/data/x051.php | 1 + .../src/voku/helper/data/x052.php | 1 + .../src/voku/helper/data/x053.php | 1 + .../src/voku/helper/data/x054.php | 1 + .../src/voku/helper/data/x055.php | 1 + .../src/voku/helper/data/x056.php | 1 + .../src/voku/helper/data/x057.php | 1 + .../src/voku/helper/data/x058.php | 1 + .../src/voku/helper/data/x059.php | 1 + .../src/voku/helper/data/x05a.php | 1 + .../src/voku/helper/data/x05b.php | 1 + .../src/voku/helper/data/x05c.php | 1 + .../src/voku/helper/data/x05d.php | 1 + .../src/voku/helper/data/x05e.php | 1 + .../src/voku/helper/data/x05f.php | 1 + .../src/voku/helper/data/x060.php | 1 + .../src/voku/helper/data/x061.php | 1 + .../src/voku/helper/data/x062.php | 1 + .../src/voku/helper/data/x063.php | 1 + .../src/voku/helper/data/x064.php | 1 + .../src/voku/helper/data/x065.php | 1 + .../src/voku/helper/data/x066.php | 1 + .../src/voku/helper/data/x067.php | 1 + .../src/voku/helper/data/x068.php | 1 + .../src/voku/helper/data/x069.php | 1 + .../src/voku/helper/data/x06a.php | 1 + .../src/voku/helper/data/x06b.php | 1 + .../src/voku/helper/data/x06c.php | 1 + .../src/voku/helper/data/x06d.php | 1 + .../src/voku/helper/data/x06e.php | 1 + .../src/voku/helper/data/x06f.php | 1 + .../src/voku/helper/data/x070.php | 1 + .../src/voku/helper/data/x071.php | 1 + .../src/voku/helper/data/x072.php | 1 + .../src/voku/helper/data/x073.php | 1 + .../src/voku/helper/data/x074.php | 1 + .../src/voku/helper/data/x075.php | 1 + .../src/voku/helper/data/x076.php | 1 + .../src/voku/helper/data/x077.php | 1 + .../src/voku/helper/data/x078.php | 1 + .../src/voku/helper/data/x079.php | 1 + .../src/voku/helper/data/x07a.php | 1 + .../src/voku/helper/data/x07b.php | 1 + .../src/voku/helper/data/x07c.php | 1 + .../src/voku/helper/data/x07d.php | 1 + .../src/voku/helper/data/x07e.php | 1 + .../src/voku/helper/data/x07f.php | 1 + .../src/voku/helper/data/x080.php | 1 + .../src/voku/helper/data/x081.php | 1 + .../src/voku/helper/data/x082.php | 1 + .../src/voku/helper/data/x083.php | 1 + .../src/voku/helper/data/x084.php | 1 + .../src/voku/helper/data/x085.php | 1 + .../src/voku/helper/data/x086.php | 1 + .../src/voku/helper/data/x087.php | 1 + .../src/voku/helper/data/x088.php | 1 + .../src/voku/helper/data/x089.php | 1 + .../src/voku/helper/data/x08a.php | 1 + .../src/voku/helper/data/x08b.php | 1 + .../src/voku/helper/data/x08c.php | 1 + .../src/voku/helper/data/x08d.php | 1 + .../src/voku/helper/data/x08e.php | 1 + .../src/voku/helper/data/x08f.php | 1 + .../src/voku/helper/data/x090.php | 1 + .../src/voku/helper/data/x091.php | 1 + .../src/voku/helper/data/x092.php | 1 + .../src/voku/helper/data/x093.php | 1 + .../src/voku/helper/data/x094.php | 1 + .../src/voku/helper/data/x095.php | 1 + .../src/voku/helper/data/x096.php | 1 + .../src/voku/helper/data/x097.php | 1 + .../src/voku/helper/data/x098.php | 1 + .../src/voku/helper/data/x099.php | 1 + .../src/voku/helper/data/x09a.php | 1 + .../src/voku/helper/data/x09b.php | 1 + .../src/voku/helper/data/x09c.php | 1 + .../src/voku/helper/data/x09d.php | 1 + .../src/voku/helper/data/x09e.php | 1 + .../src/voku/helper/data/x09f.php | 1 + .../src/voku/helper/data/x0a0.php | 1 + .../src/voku/helper/data/x0a1.php | 1 + .../src/voku/helper/data/x0a2.php | 1 + .../src/voku/helper/data/x0a3.php | 1 + .../src/voku/helper/data/x0a4.php | 1 + .../src/voku/helper/data/x0ac.php | 1 + .../src/voku/helper/data/x0ad.php | 1 + .../src/voku/helper/data/x0ae.php | 1 + .../src/voku/helper/data/x0af.php | 1 + .../src/voku/helper/data/x0b0.php | 1 + .../src/voku/helper/data/x0b1.php | 1 + .../src/voku/helper/data/x0b2.php | 1 + .../src/voku/helper/data/x0b3.php | 1 + .../src/voku/helper/data/x0b4.php | 1 + .../src/voku/helper/data/x0b5.php | 1 + .../src/voku/helper/data/x0b6.php | 1 + .../src/voku/helper/data/x0b7.php | 1 + .../src/voku/helper/data/x0b8.php | 1 + .../src/voku/helper/data/x0b9.php | 1 + .../src/voku/helper/data/x0ba.php | 1 + .../src/voku/helper/data/x0bb.php | 1 + .../src/voku/helper/data/x0bc.php | 1 + .../src/voku/helper/data/x0bd.php | 1 + .../src/voku/helper/data/x0be.php | 1 + .../src/voku/helper/data/x0bf.php | 1 + .../src/voku/helper/data/x0c0.php | 1 + .../src/voku/helper/data/x0c1.php | 1 + .../src/voku/helper/data/x0c2.php | 1 + .../src/voku/helper/data/x0c3.php | 1 + .../src/voku/helper/data/x0c4.php | 1 + .../src/voku/helper/data/x0c5.php | 1 + .../src/voku/helper/data/x0c6.php | 1 + .../src/voku/helper/data/x0c7.php | 1 + .../src/voku/helper/data/x0c8.php | 1 + .../src/voku/helper/data/x0c9.php | 1 + .../src/voku/helper/data/x0ca.php | 1 + .../src/voku/helper/data/x0cb.php | 1 + .../src/voku/helper/data/x0cc.php | 1 + .../src/voku/helper/data/x0cd.php | 1 + .../src/voku/helper/data/x0ce.php | 1 + .../src/voku/helper/data/x0cf.php | 1 + .../src/voku/helper/data/x0d0.php | 1 + .../src/voku/helper/data/x0d1.php | 1 + .../src/voku/helper/data/x0d2.php | 1 + .../src/voku/helper/data/x0d3.php | 1 + .../src/voku/helper/data/x0d4.php | 1 + .../src/voku/helper/data/x0d5.php | 1 + .../src/voku/helper/data/x0d6.php | 1 + .../src/voku/helper/data/x0d7.php | 1 + .../src/voku/helper/data/x0f9.php | 1 + .../src/voku/helper/data/x0fa.php | 1 + .../src/voku/helper/data/x0fb.php | 1 + .../src/voku/helper/data/x0fc.php | 1 + .../src/voku/helper/data/x0fd.php | 1 + .../src/voku/helper/data/x0fe.php | 1 + .../src/voku/helper/data/x0ff.php | 1 + .../src/voku/helper/data/x1d4.php | 1 + .../src/voku/helper/data/x1d5.php | 4 + .../src/voku/helper/data/x1d6.php | 1 + .../src/voku/helper/data/x1d7.php | 1 + .../src/voku/helper/data/x1f1.php | 2 + vendor/webman/captcha/.gitignore | 5 + vendor/webman/captcha/.travis.yml | 16 + vendor/webman/captcha/LICENSE | 19 + vendor/webman/captcha/README.md | 142 + vendor/webman/captcha/composer.json | 35 + vendor/webman/captcha/demo/demo.php | 11 + vendor/webman/captcha/demo/fingerprint.php | 12 + vendor/webman/captcha/demo/form.php | 32 + vendor/webman/captcha/demo/index.php | 15 + vendor/webman/captcha/demo/inline.php | 22 + vendor/webman/captcha/demo/ocr.php | 39 + vendor/webman/captcha/demo/output.php | 12 + vendor/webman/captcha/demo/session.php | 22 + vendor/webman/captcha/phpunit.xml.dist | 15 + vendor/webman/captcha/src/CaptchaBuilder.php | 773 +++ .../captcha/src/CaptchaBuilderInterface.php | 29 + vendor/webman/captcha/src/Font/captcha0.ttf | Bin 0 -> 49224 bytes vendor/webman/captcha/src/Font/captcha1.ttf | Bin 0 -> 76232 bytes vendor/webman/captcha/src/Font/captcha2.ttf | Bin 0 -> 24108 bytes vendor/webman/captcha/src/Font/captcha3.ttf | Bin 0 -> 15976 bytes vendor/webman/captcha/src/Font/captcha4.ttf | Bin 0 -> 906980 bytes vendor/webman/captcha/src/Font/captcha5.ttf | Bin 0 -> 49724 bytes .../webman/captcha/src/ImageFileHandler.php | 78 + vendor/webman/captcha/src/PhraseBuilder.php | 75 + .../captcha/src/PhraseBuilderInterface.php | 21 + .../captcha/tests/CaptchaBuilderTest.php | 30 + vendor/webman/event/LICENSE | 21 + vendor/webman/event/README.md | 4 + vendor/webman/event/composer.json | 13 + vendor/webman/event/src/BootStrap.php | 90 + vendor/webman/event/src/Event.php | 161 + vendor/webman/event/src/EventListCommand.php | 50 + vendor/webman/event/src/Install.php | 82 + .../src/config/plugin/webman/event/app.php | 4 + .../config/plugin/webman/event/bootstrap.php | 17 + .../config/plugin/webman/event/command.php | 7 + vendor/workerman/crontab/README.md | 29 + vendor/workerman/crontab/composer.json | 34 + vendor/workerman/crontab/example/test.php | 16 + vendor/workerman/crontab/src/Crontab.php | 177 + vendor/workerman/crontab/src/Parser.php | 171 + vendor/workerman/validation/CHANGELOG.md | 161 + vendor/workerman/validation/CONTRIBUTING.md | 244 + vendor/workerman/validation/LICENSE | 21 + vendor/workerman/validation/README.md | 20 + vendor/workerman/validation/composer.json | 73 + .../data/domain/public-suffix/AC.php | 11 + .../data/domain/public-suffix/AD.php | 6 + .../data/domain/public-suffix/AE.php | 12 + .../data/domain/public-suffix/AERO.php | 91 + .../data/domain/public-suffix/AF.php | 10 + .../data/domain/public-suffix/AG.php | 10 + .../data/domain/public-suffix/AI.php | 9 + .../data/domain/public-suffix/AL.php | 11 + .../data/domain/public-suffix/AM.php | 10 + .../data/domain/public-suffix/AO.php | 11 + .../data/domain/public-suffix/AR.php | 19 + .../data/domain/public-suffix/ARPA.php | 11 + .../data/domain/public-suffix/AS.php | 6 + .../data/domain/public-suffix/AT.php | 10 + .../data/domain/public-suffix/AU.php | 38 + .../data/domain/public-suffix/AW.php | 6 + .../data/domain/public-suffix/AZ.php | 17 + .../data/domain/public-suffix/BA.php | 11 + .../data/domain/public-suffix/BB.php | 15 + .../data/domain/public-suffix/BE.php | 6 + .../data/domain/public-suffix/BF.php | 6 + .../data/domain/public-suffix/BG.php | 41 + .../data/domain/public-suffix/BH.php | 10 + .../data/domain/public-suffix/BI.php | 10 + .../data/domain/public-suffix/BJ.php | 25 + .../data/domain/public-suffix/BM.php | 10 + .../data/domain/public-suffix/BN.php | 10 + .../data/domain/public-suffix/BO.php | 46 + .../data/domain/public-suffix/BR.php | 173 + .../data/domain/public-suffix/BS.php | 10 + .../data/domain/public-suffix/BT.php | 10 + .../data/domain/public-suffix/BW.php | 7 + .../data/domain/public-suffix/BY.php | 9 + .../data/domain/public-suffix/BZ.php | 10 + .../data/domain/public-suffix/CA.php | 20 + .../data/domain/public-suffix/CD.php | 6 + .../data/domain/public-suffix/CI.php | 20 + .../data/domain/public-suffix/CK.php | 6 + .../data/domain/public-suffix/CL.php | 9 + .../data/domain/public-suffix/CM.php | 9 + .../data/domain/public-suffix/CN.php | 49 + .../data/domain/public-suffix/CO.php | 18 + .../data/domain/public-suffix/CR.php | 12 + .../data/domain/public-suffix/CU.php | 11 + .../data/domain/public-suffix/CV.php | 10 + .../data/domain/public-suffix/CW.php | 9 + .../data/domain/public-suffix/CX.php | 6 + .../data/domain/public-suffix/CY.php | 17 + .../data/domain/public-suffix/DM.php | 10 + .../data/domain/public-suffix/DO.php | 15 + .../data/domain/public-suffix/DZ.php | 15 + .../data/domain/public-suffix/EC.php | 17 + .../data/domain/public-suffix/EE.php | 15 + .../data/domain/public-suffix/EG.php | 14 + .../data/domain/public-suffix/ES.php | 10 + .../data/domain/public-suffix/ET.php | 13 + .../data/domain/public-suffix/FI.php | 6 + .../data/domain/public-suffix/FJ.php | 15 + .../data/domain/public-suffix/FM.php | 9 + .../data/domain/public-suffix/FR.php | 26 + .../data/domain/public-suffix/GD.php | 7 + .../data/domain/public-suffix/GE.php | 12 + .../data/domain/public-suffix/GG.php | 8 + .../data/domain/public-suffix/GH.php | 10 + .../data/domain/public-suffix/GI.php | 11 + .../data/domain/public-suffix/GL.php | 10 + .../data/domain/public-suffix/GN.php | 11 + .../data/domain/public-suffix/GP.php | 11 + .../data/domain/public-suffix/GR.php | 10 + .../data/domain/public-suffix/GT.php | 12 + .../data/domain/public-suffix/GU.php | 13 + .../data/domain/public-suffix/GY.php | 11 + .../data/domain/public-suffix/HK.php | 26 + .../data/domain/public-suffix/HN.php | 11 + .../data/domain/public-suffix/HR.php | 9 + .../data/domain/public-suffix/HT.php | 22 + .../data/domain/public-suffix/HU.php | 36 + .../data/domain/public-suffix/ID.php | 17 + .../data/domain/public-suffix/IE.php | 6 + .../data/domain/public-suffix/IL.php | 13 + .../data/domain/public-suffix/IM.php | 14 + .../data/domain/public-suffix/IN.php | 46 + .../data/domain/public-suffix/INT.php | 6 + .../data/domain/public-suffix/IO.php | 6 + .../data/domain/public-suffix/IQ.php | 11 + .../data/domain/public-suffix/IR.php | 14 + .../data/domain/public-suffix/IS.php | 11 + .../data/domain/public-suffix/IT.php | 411 ++ .../data/domain/public-suffix/JE.php | 8 + .../data/domain/public-suffix/JO.php | 13 + .../data/domain/public-suffix/JP.php | 1795 ++++++ .../data/domain/public-suffix/KE.php | 14 + .../data/domain/public-suffix/KG.php | 11 + .../data/domain/public-suffix/KI.php | 12 + .../data/domain/public-suffix/KM.php | 22 + .../data/domain/public-suffix/KN.php | 9 + .../data/domain/public-suffix/KP.php | 11 + .../data/domain/public-suffix/KR.php | 34 + .../data/domain/public-suffix/KW.php | 12 + .../data/domain/public-suffix/KY.php | 9 + .../data/domain/public-suffix/KZ.php | 11 + .../data/domain/public-suffix/LA.php | 13 + .../data/domain/public-suffix/LB.php | 10 + .../data/domain/public-suffix/LC.php | 11 + .../data/domain/public-suffix/LK.php | 20 + .../data/domain/public-suffix/LR.php | 10 + .../data/domain/public-suffix/LS.php | 14 + .../data/domain/public-suffix/LT.php | 6 + .../data/domain/public-suffix/LV.php | 14 + .../data/domain/public-suffix/LY.php | 14 + .../data/domain/public-suffix/MA.php | 11 + .../data/domain/public-suffix/MC.php | 7 + .../data/domain/public-suffix/ME.php | 13 + .../data/domain/public-suffix/MG.php | 14 + .../data/domain/public-suffix/MK.php | 12 + .../data/domain/public-suffix/ML.php | 12 + .../data/domain/public-suffix/MN.php | 8 + .../data/domain/public-suffix/MO.php | 10 + .../data/domain/public-suffix/MR.php | 6 + .../data/domain/public-suffix/MS.php | 10 + .../data/domain/public-suffix/MT.php | 9 + .../data/domain/public-suffix/MU.php | 12 + .../data/domain/public-suffix/MV.php | 19 + .../data/domain/public-suffix/MW.php | 16 + .../data/domain/public-suffix/MX.php | 10 + .../data/domain/public-suffix/MY.php | 13 + .../data/domain/public-suffix/MZ.php | 13 + .../data/domain/public-suffix/NA.php | 22 + .../data/domain/public-suffix/NC.php | 7 + .../data/domain/public-suffix/NF.php | 15 + .../data/domain/public-suffix/NG.php | 15 + .../data/domain/public-suffix/NI.php | 19 + .../data/domain/public-suffix/NO.php | 758 +++ .../data/domain/public-suffix/NR.php | 12 + .../data/domain/public-suffix/NZ.php | 21 + .../data/domain/public-suffix/OM.php | 14 + .../data/domain/public-suffix/PA.php | 16 + .../data/domain/public-suffix/PE.php | 12 + .../data/domain/public-suffix/PF.php | 8 + .../data/domain/public-suffix/PH.php | 13 + .../data/domain/public-suffix/PK.php | 19 + .../data/domain/public-suffix/PL.php | 213 + .../data/domain/public-suffix/PN.php | 10 + .../data/domain/public-suffix/PR.php | 18 + .../data/domain/public-suffix/PRO.php | 16 + .../data/domain/public-suffix/PS.php | 12 + .../data/domain/public-suffix/PT.php | 13 + .../data/domain/public-suffix/PW.php | 11 + .../data/domain/public-suffix/PY.php | 12 + .../data/domain/public-suffix/QA.php | 13 + .../data/domain/public-suffix/RE.php | 8 + .../data/domain/public-suffix/RO.php | 16 + .../data/domain/public-suffix/RS.php | 11 + .../data/domain/public-suffix/RW.php | 12 + .../data/domain/public-suffix/SA.php | 13 + .../data/domain/public-suffix/SB.php | 10 + .../data/domain/public-suffix/SC.php | 10 + .../data/domain/public-suffix/SD.php | 13 + .../data/domain/public-suffix/SE.php | 44 + .../data/domain/public-suffix/SG.php | 11 + .../data/domain/public-suffix/SH.php | 10 + .../data/domain/public-suffix/SL.php | 10 + .../data/domain/public-suffix/SN.php | 12 + .../data/domain/public-suffix/SO.php | 11 + .../data/domain/public-suffix/SS.php | 13 + .../data/domain/public-suffix/ST.php | 16 + .../data/domain/public-suffix/SV.php | 10 + .../data/domain/public-suffix/SX.php | 6 + .../data/domain/public-suffix/SY.php | 11 + .../data/domain/public-suffix/SZ.php | 8 + .../data/domain/public-suffix/TH.php | 12 + .../data/domain/public-suffix/TJ.php | 20 + .../data/domain/public-suffix/TL.php | 6 + .../data/domain/public-suffix/TM.php | 13 + .../data/domain/public-suffix/TN.php | 18 + .../data/domain/public-suffix/TO.php | 11 + .../data/domain/public-suffix/TR.php | 28 + .../data/domain/public-suffix/TT.php | 22 + .../data/domain/public-suffix/TW.php | 18 + .../data/domain/public-suffix/TZ.php | 17 + .../data/domain/public-suffix/UA.php | 81 + .../data/domain/public-suffix/UG.php | 13 + .../data/domain/public-suffix/UK.php | 16 + .../data/domain/public-suffix/US.php | 234 + .../data/domain/public-suffix/UY.php | 11 + .../data/domain/public-suffix/UZ.php | 9 + .../data/domain/public-suffix/VC.php | 11 + .../data/domain/public-suffix/VE.php | 25 + .../data/domain/public-suffix/VI.php | 10 + .../data/domain/public-suffix/VN.php | 17 + .../data/domain/public-suffix/VU.php | 9 + .../data/domain/public-suffix/WS.php | 10 + .../domain/public-suffix/XN--4DBRK0CE.php | 9 + .../data/domain/public-suffix/XN--90A3AC.php | 11 + .../data/domain/public-suffix/XN--J6W193G.php | 11 + .../data/domain/public-suffix/XN--O3CW4H.php | 11 + .../data/domain/public-suffix/YE.php | 11 + .../data/domain/public-suffix/ZA.php | 23 + .../data/domain/public-suffix/ZM.php | 16 + .../data/domain/public-suffix/ZW.php | 10 + .../validation/data/iso_3166-2/AD.php | 13 + .../validation/data/iso_3166-2/AE.php | 13 + .../validation/data/iso_3166-2/AF.php | 40 + .../validation/data/iso_3166-2/AG.php | 14 + .../validation/data/iso_3166-2/AI.php | 6 + .../validation/data/iso_3166-2/AL.php | 18 + .../validation/data/iso_3166-2/AM.php | 17 + .../validation/data/iso_3166-2/AO.php | 24 + .../validation/data/iso_3166-2/AQ.php | 6 + .../validation/data/iso_3166-2/AR.php | 30 + .../validation/data/iso_3166-2/AS.php | 6 + .../validation/data/iso_3166-2/AT.php | 15 + .../validation/data/iso_3166-2/AU.php | 14 + .../validation/data/iso_3166-2/AW.php | 6 + .../validation/data/iso_3166-2/AX.php | 6 + .../validation/data/iso_3166-2/AZ.php | 84 + .../validation/data/iso_3166-2/BA.php | 9 + .../validation/data/iso_3166-2/BB.php | 17 + .../validation/data/iso_3166-2/BD.php | 78 + .../validation/data/iso_3166-2/BE.php | 19 + .../validation/data/iso_3166-2/BF.php | 64 + .../validation/data/iso_3166-2/BG.php | 34 + .../validation/data/iso_3166-2/BH.php | 10 + .../validation/data/iso_3166-2/BI.php | 24 + .../validation/data/iso_3166-2/BJ.php | 18 + .../validation/data/iso_3166-2/BL.php | 6 + .../validation/data/iso_3166-2/BM.php | 6 + .../validation/data/iso_3166-2/BN.php | 10 + .../validation/data/iso_3166-2/BO.php | 15 + .../validation/data/iso_3166-2/BQ.php | 9 + .../validation/data/iso_3166-2/BR.php | 33 + .../validation/data/iso_3166-2/BS.php | 38 + .../validation/data/iso_3166-2/BT.php | 26 + .../validation/data/iso_3166-2/BV.php | 6 + .../validation/data/iso_3166-2/BW.php | 22 + .../validation/data/iso_3166-2/BY.php | 13 + .../validation/data/iso_3166-2/BZ.php | 12 + .../validation/data/iso_3166-2/CA.php | 19 + .../validation/data/iso_3166-2/CC.php | 6 + .../validation/data/iso_3166-2/CD.php | 32 + .../validation/data/iso_3166-2/CF.php | 23 + .../validation/data/iso_3166-2/CG.php | 18 + .../validation/data/iso_3166-2/CH.php | 32 + .../validation/data/iso_3166-2/CI.php | 20 + .../validation/data/iso_3166-2/CK.php | 6 + .../validation/data/iso_3166-2/CL.php | 22 + .../validation/data/iso_3166-2/CM.php | 16 + .../validation/data/iso_3166-2/CN.php | 40 + .../validation/data/iso_3166-2/CO.php | 39 + .../validation/data/iso_3166-2/CR.php | 13 + .../validation/data/iso_3166-2/CU.php | 22 + .../validation/data/iso_3166-2/CV.php | 30 + .../validation/data/iso_3166-2/CW.php | 6 + .../validation/data/iso_3166-2/CX.php | 6 + .../validation/data/iso_3166-2/CY.php | 12 + .../validation/data/iso_3166-2/CZ.php | 96 + .../validation/data/iso_3166-2/DE.php | 22 + .../validation/data/iso_3166-2/DJ.php | 12 + .../validation/data/iso_3166-2/DK.php | 11 + .../validation/data/iso_3166-2/DM.php | 16 + .../validation/data/iso_3166-2/DO.php | 48 + .../validation/data/iso_3166-2/DZ.php | 54 + .../validation/data/iso_3166-2/EC.php | 30 + .../validation/data/iso_3166-2/EE.php | 100 + .../validation/data/iso_3166-2/EG.php | 33 + .../validation/data/iso_3166-2/EH.php | 6 + .../validation/data/iso_3166-2/ER.php | 12 + .../validation/data/iso_3166-2/ES.php | 75 + .../validation/data/iso_3166-2/ET.php | 17 + .../validation/data/iso_3166-2/FI.php | 25 + .../validation/data/iso_3166-2/FJ.php | 25 + .../validation/data/iso_3166-2/FK.php | 6 + .../validation/data/iso_3166-2/FM.php | 10 + .../validation/data/iso_3166-2/FO.php | 6 + .../validation/data/iso_3166-2/FR.php | 133 + .../validation/data/iso_3166-2/GA.php | 15 + .../validation/data/iso_3166-2/GB.php | 226 + .../validation/data/iso_3166-2/GD.php | 13 + .../validation/data/iso_3166-2/GE.php | 18 + .../validation/data/iso_3166-2/GF.php | 6 + .../validation/data/iso_3166-2/GG.php | 6 + .../validation/data/iso_3166-2/GH.php | 22 + .../validation/data/iso_3166-2/GI.php | 6 + .../validation/data/iso_3166-2/GL.php | 11 + .../validation/data/iso_3166-2/GM.php | 12 + .../validation/data/iso_3166-2/GN.php | 47 + .../validation/data/iso_3166-2/GP.php | 6 + .../validation/data/iso_3166-2/GQ.php | 16 + .../validation/data/iso_3166-2/GR.php | 20 + .../validation/data/iso_3166-2/GS.php | 6 + .../validation/data/iso_3166-2/GT.php | 28 + .../validation/data/iso_3166-2/GU.php | 6 + .../validation/data/iso_3166-2/GW.php | 18 + .../validation/data/iso_3166-2/GY.php | 16 + .../validation/data/iso_3166-2/HK.php | 6 + .../validation/data/iso_3166-2/HM.php | 6 + .../validation/data/iso_3166-2/HN.php | 24 + .../validation/data/iso_3166-2/HR.php | 27 + .../validation/data/iso_3166-2/HT.php | 16 + .../validation/data/iso_3166-2/HU.php | 49 + .../validation/data/iso_3166-2/ID.php | 47 + .../validation/data/iso_3166-2/IE.php | 36 + .../validation/data/iso_3166-2/IL.php | 12 + .../validation/data/iso_3166-2/IM.php | 6 + .../validation/data/iso_3166-2/IN.php | 42 + .../validation/data/iso_3166-2/IO.php | 6 + .../validation/data/iso_3166-2/IQ.php | 24 + .../validation/data/iso_3166-2/IR.php | 37 + .../validation/data/iso_3166-2/IS.php | 86 + .../validation/data/iso_3166-2/IT.php | 132 + .../validation/data/iso_3166-2/JE.php | 6 + .../validation/data/iso_3166-2/JM.php | 20 + .../validation/data/iso_3166-2/JO.php | 18 + .../validation/data/iso_3166-2/JP.php | 53 + .../validation/data/iso_3166-2/KE.php | 53 + .../validation/data/iso_3166-2/KG.php | 15 + .../validation/data/iso_3166-2/KH.php | 31 + .../validation/data/iso_3166-2/KI.php | 9 + .../validation/data/iso_3166-2/KM.php | 9 + .../validation/data/iso_3166-2/KN.php | 22 + .../validation/data/iso_3166-2/KP.php | 18 + .../validation/data/iso_3166-2/KR.php | 23 + .../validation/data/iso_3166-2/KW.php | 12 + .../validation/data/iso_3166-2/KY.php | 6 + .../validation/data/iso_3166-2/KZ.php | 23 + .../validation/data/iso_3166-2/LA.php | 24 + .../validation/data/iso_3166-2/LB.php | 14 + .../validation/data/iso_3166-2/LC.php | 16 + .../validation/data/iso_3166-2/LI.php | 17 + .../validation/data/iso_3166-2/LK.php | 40 + .../validation/data/iso_3166-2/LR.php | 21 + .../validation/data/iso_3166-2/LS.php | 16 + .../validation/data/iso_3166-2/LT.php | 76 + .../validation/data/iso_3166-2/LU.php | 18 + .../validation/data/iso_3166-2/LV.php | 125 + .../validation/data/iso_3166-2/LY.php | 28 + .../validation/data/iso_3166-2/MA.php | 93 + .../validation/data/iso_3166-2/MC.php | 23 + .../validation/data/iso_3166-2/MD.php | 43 + .../validation/data/iso_3166-2/ME.php | 30 + .../validation/data/iso_3166-2/MF.php | 6 + .../validation/data/iso_3166-2/MG.php | 12 + .../validation/data/iso_3166-2/MH.php | 32 + .../validation/data/iso_3166-2/MK.php | 86 + .../validation/data/iso_3166-2/ML.php | 17 + .../validation/data/iso_3166-2/MM.php | 21 + .../validation/data/iso_3166-2/MN.php | 28 + .../validation/data/iso_3166-2/MO.php | 6 + .../validation/data/iso_3166-2/MP.php | 6 + .../validation/data/iso_3166-2/MQ.php | 6 + .../validation/data/iso_3166-2/MR.php | 21 + .../validation/data/iso_3166-2/MS.php | 6 + .../validation/data/iso_3166-2/MT.php | 74 + .../validation/data/iso_3166-2/MU.php | 18 + .../validation/data/iso_3166-2/MV.php | 27 + .../validation/data/iso_3166-2/MW.php | 37 + .../validation/data/iso_3166-2/MX.php | 38 + .../validation/data/iso_3166-2/MY.php | 22 + .../validation/data/iso_3166-2/MZ.php | 17 + .../validation/data/iso_3166-2/NA.php | 20 + .../validation/data/iso_3166-2/NC.php | 6 + .../validation/data/iso_3166-2/NE.php | 14 + .../validation/data/iso_3166-2/NF.php | 6 + .../validation/data/iso_3166-2/NG.php | 43 + .../validation/data/iso_3166-2/NI.php | 23 + .../validation/data/iso_3166-2/NL.php | 24 + .../validation/data/iso_3166-2/NO.php | 19 + .../validation/data/iso_3166-2/NP.php | 32 + .../validation/data/iso_3166-2/NR.php | 20 + .../validation/data/iso_3166-2/NU.php | 6 + .../validation/data/iso_3166-2/NZ.php | 23 + .../validation/data/iso_3166-2/OM.php | 17 + .../validation/data/iso_3166-2/PA.php | 19 + .../validation/data/iso_3166-2/PE.php | 32 + .../validation/data/iso_3166-2/PF.php | 6 + .../validation/data/iso_3166-2/PG.php | 28 + .../validation/data/iso_3166-2/PH.php | 104 + .../validation/data/iso_3166-2/PK.php | 13 + .../validation/data/iso_3166-2/PL.php | 22 + .../validation/data/iso_3166-2/PM.php | 6 + .../validation/data/iso_3166-2/PN.php | 6 + .../validation/data/iso_3166-2/PR.php | 6 + .../validation/data/iso_3166-2/PS.php | 22 + .../validation/data/iso_3166-2/PT.php | 26 + .../validation/data/iso_3166-2/PW.php | 22 + .../validation/data/iso_3166-2/PY.php | 24 + .../validation/data/iso_3166-2/QA.php | 14 + .../validation/data/iso_3166-2/RE.php | 6 + .../validation/data/iso_3166-2/RO.php | 48 + .../validation/data/iso_3166-2/RS.php | 38 + .../validation/data/iso_3166-2/RU.php | 89 + .../validation/data/iso_3166-2/RW.php | 11 + .../validation/data/iso_3166-2/SA.php | 19 + .../validation/data/iso_3166-2/SB.php | 16 + .../validation/data/iso_3166-2/SC.php | 33 + .../validation/data/iso_3166-2/SD.php | 24 + .../validation/data/iso_3166-2/SE.php | 27 + .../validation/data/iso_3166-2/SG.php | 11 + .../validation/data/iso_3166-2/SH.php | 9 + .../validation/data/iso_3166-2/SI.php | 218 + .../validation/data/iso_3166-2/SJ.php | 6 + .../validation/data/iso_3166-2/SK.php | 14 + .../validation/data/iso_3166-2/SL.php | 11 + .../validation/data/iso_3166-2/SM.php | 15 + .../validation/data/iso_3166-2/SN.php | 20 + .../validation/data/iso_3166-2/SO.php | 24 + .../validation/data/iso_3166-2/SR.php | 16 + .../validation/data/iso_3166-2/SS.php | 16 + .../validation/data/iso_3166-2/ST.php | 13 + .../validation/data/iso_3166-2/SV.php | 20 + .../validation/data/iso_3166-2/SX.php | 6 + .../validation/data/iso_3166-2/SY.php | 20 + .../validation/data/iso_3166-2/SZ.php | 10 + .../validation/data/iso_3166-2/TC.php | 6 + .../validation/data/iso_3166-2/TD.php | 29 + .../validation/data/iso_3166-2/TF.php | 6 + .../validation/data/iso_3166-2/TG.php | 11 + .../validation/data/iso_3166-2/TH.php | 84 + .../validation/data/iso_3166-2/TJ.php | 11 + .../validation/data/iso_3166-2/TK.php | 6 + .../validation/data/iso_3166-2/TL.php | 19 + .../validation/data/iso_3166-2/TM.php | 12 + .../validation/data/iso_3166-2/TN.php | 30 + .../validation/data/iso_3166-2/TO.php | 11 + .../validation/data/iso_3166-2/TR.php | 87 + .../validation/data/iso_3166-2/TT.php | 21 + .../validation/data/iso_3166-2/TV.php | 14 + .../validation/data/iso_3166-2/TW.php | 28 + .../validation/data/iso_3166-2/TZ.php | 37 + .../validation/data/iso_3166-2/UA.php | 33 + .../validation/data/iso_3166-2/UG.php | 145 + .../validation/data/iso_3166-2/UM.php | 15 + .../validation/data/iso_3166-2/US.php | 63 + .../validation/data/iso_3166-2/UY.php | 25 + .../validation/data/iso_3166-2/UZ.php | 20 + .../validation/data/iso_3166-2/VA.php | 6 + .../validation/data/iso_3166-2/VC.php | 12 + .../validation/data/iso_3166-2/VE.php | 31 + .../validation/data/iso_3166-2/VG.php | 6 + .../validation/data/iso_3166-2/VI.php | 6 + .../validation/data/iso_3166-2/VN.php | 69 + .../validation/data/iso_3166-2/VU.php | 12 + .../validation/data/iso_3166-2/WF.php | 9 + .../validation/data/iso_3166-2/WS.php | 17 + .../validation/data/iso_3166-2/YE.php | 28 + .../validation/data/iso_3166-2/YT.php | 6 + .../validation/data/iso_3166-2/ZA.php | 15 + .../validation/data/iso_3166-2/ZM.php | 16 + .../validation/data/iso_3166-2/ZW.php | 16 + .../validation/library/ChainedValidator.php | 377 ++ .../library/Exceptions/AllOfException.php | 31 + .../library/Exceptions/AlnumException.php | 31 + .../library/Exceptions/AlphaException.php | 31 + .../Exceptions/AlwaysInvalidException.php | 34 + .../Exceptions/AlwaysValidException.php | 30 + .../library/Exceptions/AnyOfException.php | 29 + .../library/Exceptions/ArrayTypeException.php | 31 + .../library/Exceptions/ArrayValException.php | 30 + .../library/Exceptions/AttributeException.php | 45 + .../library/Exceptions/Base64Exception.php | 30 + .../library/Exceptions/BaseException.php | 30 + .../library/Exceptions/BetweenException.php | 29 + .../library/Exceptions/BoolTypeException.php | 31 + .../library/Exceptions/BoolValException.php | 30 + .../library/Exceptions/BsnException.php | 30 + .../library/Exceptions/CallException.php | 29 + .../Exceptions/CallableTypeException.php | 30 + .../library/Exceptions/CallbackException.php | 19 + .../library/Exceptions/CharsetException.php | 30 + .../library/Exceptions/CnhException.php | 30 + .../library/Exceptions/CnpjException.php | 30 + .../library/Exceptions/ComponentException.php | 21 + .../library/Exceptions/ConsonantException.php | 32 + .../Exceptions/ContainsAnyException.php | 28 + .../library/Exceptions/ContainsException.php | 30 + .../library/Exceptions/ControlException.php | 32 + .../library/Exceptions/CountableException.php | 30 + .../Exceptions/CountryCodeException.php | 30 + .../library/Exceptions/CpfException.php | 30 + .../Exceptions/CreditCardException.php | 48 + .../Exceptions/CurrencyCodeException.php | 30 + .../library/Exceptions/DateException.php | 29 + .../library/Exceptions/DateTimeException.php | 41 + .../library/Exceptions/DecimalException.php | 28 + .../library/Exceptions/DigitException.php | 31 + .../library/Exceptions/DirectoryException.php | 29 + .../library/Exceptions/DomainException.php | 29 + .../library/Exceptions/EachException.php | 52 + .../library/Exceptions/EmailException.php | 34 + .../library/Exceptions/EndsWithException.php | 30 + .../library/Exceptions/EqualsException.php | 30 + .../Exceptions/EquivalentException.php | 28 + .../library/Exceptions/EvenException.php | 32 + .../library/Exceptions/Exception.php | 20 + .../Exceptions/ExecutableException.php | 29 + .../library/Exceptions/ExistsException.php | 29 + .../library/Exceptions/ExtensionException.php | 31 + .../library/Exceptions/FactorException.php | 30 + .../library/Exceptions/FalseValException.php | 29 + .../library/Exceptions/FibonacciException.php | 30 + .../library/Exceptions/FileException.php | 29 + .../library/Exceptions/FilterVarException.php | 17 + .../FilteredValidationException.php | 26 + .../library/Exceptions/FiniteException.php | 29 + .../library/Exceptions/FloatTypeException.php | 31 + .../library/Exceptions/FloatValException.php | 30 + .../library/Exceptions/GraphException.php | 32 + .../Exceptions/GreaterThanException.php | 28 + .../Exceptions/GroupedValidationException.php | 47 + .../Exceptions/HexRgbColorException.php | 29 + .../library/Exceptions/IbanException.php | 28 + .../library/Exceptions/IdenticalException.php | 28 + .../library/Exceptions/ImageException.php | 30 + .../library/Exceptions/ImeiException.php | 30 + .../library/Exceptions/InException.php | 30 + .../library/Exceptions/InfiniteException.php | 29 + .../library/Exceptions/InstanceException.php | 30 + .../library/Exceptions/IntTypeException.php | 30 + .../library/Exceptions/IntValException.php | 30 + .../Exceptions/InvalidClassException.php | 21 + .../library/Exceptions/IpException.php | 47 + .../library/Exceptions/IsbnException.php | 29 + .../Exceptions/IterableTypeException.php | 28 + .../library/Exceptions/JsonException.php | 30 + .../library/Exceptions/KeyException.php | 45 + .../library/Exceptions/KeyNestedException.php | 45 + .../library/Exceptions/KeySetException.php | 50 + .../library/Exceptions/KeyValueException.php | 37 + .../Exceptions/LanguageCodeException.php | 30 + .../library/Exceptions/LeapDateException.php | 29 + .../library/Exceptions/LeapYearException.php | 29 + .../library/Exceptions/LengthException.php | 70 + .../library/Exceptions/LessThanException.php | 28 + .../library/Exceptions/LowercaseException.php | 30 + .../library/Exceptions/LuhnException.php | 30 + .../Exceptions/MacAddressException.php | 30 + .../library/Exceptions/MaxAgeException.php | 29 + .../library/Exceptions/MaxException.php | 30 + .../library/Exceptions/MimetypeException.php | 31 + .../library/Exceptions/MinAgeException.php | 30 + .../library/Exceptions/MinException.php | 31 + .../library/Exceptions/MultipleException.php | 30 + .../library/Exceptions/NegativeException.php | 30 + .../Exceptions/NestedValidationException.php | 255 + .../Exceptions/NfeAccessKeyException.php | 30 + .../library/Exceptions/NifException.php | 29 + .../library/Exceptions/NipException.php | 29 + .../library/Exceptions/NoException.php | 28 + .../Exceptions/NoWhitespaceException.php | 30 + .../Exceptions/NonOmissibleException.php | 18 + .../library/Exceptions/NoneOfException.php | 29 + .../library/Exceptions/NotBlankException.php | 45 + .../library/Exceptions/NotEmojiException.php | 28 + .../library/Exceptions/NotEmptyException.php | 46 + .../library/Exceptions/NotException.php | 18 + .../Exceptions/NotOptionalException.php | 45 + .../library/Exceptions/NullTypeException.php | 31 + .../library/Exceptions/NullableException.php | 45 + .../library/Exceptions/NumberException.php | 30 + .../Exceptions/NumericValException.php | 30 + .../Exceptions/ObjectTypeException.php | 31 + .../library/Exceptions/OddException.php | 30 + .../library/Exceptions/OneOfException.php | 29 + .../library/Exceptions/OptionalException.php | 37 + .../Exceptions/PerfectSquareException.php | 30 + .../library/Exceptions/PeselException.php | 30 + .../library/Exceptions/PhoneException.php | 55 + .../library/Exceptions/PhpLabelException.php | 30 + .../library/Exceptions/PisException.php | 30 + .../Exceptions/PolishIdCardException.php | 28 + .../Exceptions/PortugueseNifException.php | 28 + .../library/Exceptions/PositiveException.php | 30 + .../Exceptions/PostalCodeException.php | 28 + .../Exceptions/PrimeNumberException.php | 30 + .../library/Exceptions/PrintableException.php | 35 + .../PublicDomainSuffixException.php | 28 + .../library/Exceptions/PunctException.php | 32 + .../library/Exceptions/ReadableException.php | 29 + .../Exceptions/RecursiveExceptionIterator.php | 85 + .../library/Exceptions/RegexException.php | 30 + .../Exceptions/ResourceTypeException.php | 30 + .../library/Exceptions/RomanException.php | 29 + .../library/Exceptions/ScalarValException.php | 28 + .../library/Exceptions/SfException.php | 33 + .../library/Exceptions/SizeException.php | 54 + .../library/Exceptions/SlugException.php | 30 + .../library/Exceptions/SortedException.php | 44 + .../library/Exceptions/SpaceException.php | 31 + .../Exceptions/StartsWithException.php | 29 + .../Exceptions/StringTypeException.php | 29 + .../library/Exceptions/StringValException.php | 29 + .../Exceptions/SubdivisionCodeException.php | 28 + .../library/Exceptions/SubsetException.php | 29 + .../Exceptions/SymbolicLinkException.php | 29 + .../library/Exceptions/TimeException.php | 28 + .../library/Exceptions/TldException.php | 33 + .../library/Exceptions/TrueValException.php | 31 + .../library/Exceptions/TypeException.php | 31 + .../library/Exceptions/UniqueException.php | 32 + .../library/Exceptions/UploadedException.php | 32 + .../library/Exceptions/UppercaseException.php | 30 + .../library/Exceptions/UrlException.php | 28 + .../library/Exceptions/UuidException.php | 46 + .../Exceptions/ValidationException.php | 158 + .../library/Exceptions/ValidatorException.php | 17 + .../library/Exceptions/VersionException.php | 29 + .../library/Exceptions/VideoUrlException.php | 46 + .../library/Exceptions/VowelException.php | 31 + .../library/Exceptions/WhenException.php | 19 + .../library/Exceptions/WritableException.php | 29 + .../library/Exceptions/XdigitException.php | 31 + .../library/Exceptions/YesException.php | 29 + .../workerman/validation/library/Factory.php | 266 + .../library/Helpers/CanCompareValues.php | 68 + .../library/Helpers/CanValidateDateTime.php | 86 + .../library/Helpers/CanValidateIterable.php | 33 + .../library/Helpers/CanValidateUndefined.php | 30 + .../library/Helpers/CountryInfo.php | 54 + .../validation/library/Helpers/DomainInfo.php | 46 + .../validation/library/Message/Formatter.php | 54 + .../library/Message/ParameterStringifier.php | 18 + .../Stringifier/KeepOriginalStringName.php | 30 + .../validation/library/NonNegatable.php | 18 + .../validation/library/Rules/AbstractAge.php | 98 + .../library/Rules/AbstractComparison.php | 60 + .../library/Rules/AbstractComposite.php | 150 + .../library/Rules/AbstractEnvelope.php | 61 + .../library/Rules/AbstractFilterRule.php | 61 + .../library/Rules/AbstractRelated.php | 154 + .../validation/library/Rules/AbstractRule.php | 126 + .../library/Rules/AbstractSearcher.php | 50 + .../library/Rules/AbstractWrapper.php | 68 + .../validation/library/Rules/AllOf.php | 67 + .../validation/library/Rules/Alnum.php | 33 + .../validation/library/Rules/Alpha.php | 30 + .../library/Rules/AlwaysInvalid.php | 28 + .../validation/library/Rules/AlwaysValid.php | 28 + .../validation/library/Rules/AnyOf.php | 78 + .../validation/library/Rules/ArrayType.php | 31 + .../validation/library/Rules/ArrayVal.php | 35 + .../validation/library/Rules/Attribute.php | 53 + .../validation/library/Rules/Base.php | 64 + .../validation/library/Rules/Base64.php | 40 + .../validation/library/Rules/Between.php | 50 + .../validation/library/Rules/BoolType.php | 29 + .../validation/library/Rules/BoolVal.php | 34 + .../validation/library/Rules/Bsn.php | 55 + .../validation/library/Rules/Call.php | 107 + .../validation/library/Rules/CallableType.php | 28 + .../validation/library/Rules/Callback.php | 67 + .../validation/library/Rules/Charset.php | 55 + .../validation/library/Rules/Cnh.php | 59 + .../validation/library/Rules/Cnpj.php | 83 + .../validation/library/Rules/Consonant.php | 30 + .../validation/library/Rules/Contains.php | 78 + .../validation/library/Rules/ContainsAny.php | 50 + .../validation/library/Rules/Control.php | 31 + .../validation/library/Rules/Countable.php | 32 + .../validation/library/Rules/CountryCode.php | 375 ++ .../validation/library/Rules/Cpf.php | 59 + .../validation/library/Rules/CreditCard.php | 102 + .../validation/library/Rules/CurrencyCode.php | 212 + .../validation/library/Rules/Date.php | 67 + .../validation/library/Rules/DateTime.php | 66 + .../validation/library/Rules/Decimal.php | 71 + .../validation/library/Rules/Digit.php | 30 + .../validation/library/Rules/Directory.php | 45 + .../validation/library/Rules/Domain.php | 180 + .../validation/library/Rules/Each.php | 96 + .../validation/library/Rules/Email.php | 70 + .../validation/library/Rules/EndsWith.php | 82 + .../validation/library/Rules/Equals.php | 43 + .../validation/library/Rules/Equivalent.php | 57 + .../validation/library/Rules/Even.php | 36 + .../validation/library/Rules/Executable.php | 40 + .../validation/library/Rules/Exists.php | 34 + .../validation/library/Rules/Extension.php | 55 + .../validation/library/Rules/Factor.php | 61 + .../validation/library/Rules/FalseVal.php | 32 + .../validation/library/Rules/Fibonacci.php | 41 + .../validation/library/Rules/File.php | 36 + .../validation/library/Rules/FilterVar.php | 70 + .../validation/library/Rules/Finite.php | 30 + .../validation/library/Rules/FloatType.php | 29 + .../validation/library/Rules/FloatVal.php | 34 + .../validation/library/Rules/Graph.php | 31 + .../validation/library/Rules/GreaterThan.php | 26 + .../validation/library/Rules/HexRgbColor.php | 24 + .../validation/library/Rules/Iban.php | 149 + .../validation/library/Rules/Identical.php | 41 + .../validation/library/Rules/Image.php | 62 + .../validation/library/Rules/Imei.php | 46 + .../workerman/validation/library/Rules/In.php | 90 + .../validation/library/Rules/Infinite.php | 30 + .../validation/library/Rules/Instance.php | 41 + .../validation/library/Rules/IntType.php | 28 + .../validation/library/Rules/IntVal.php | 42 + .../workerman/validation/library/Rules/Ip.php | 209 + .../validation/library/Rules/Isbn.php | 45 + .../validation/library/Rules/IterableType.php | 30 + .../validation/library/Rules/Json.php | 44 + .../validation/library/Rules/Key.php | 53 + .../validation/library/Rules/KeyNested.php | 146 + .../validation/library/Rules/KeySet.php | 142 + .../validation/library/Rules/KeyValue.php | 144 + .../validation/library/Rules/LanguageCode.php | 549 ++ .../validation/library/Rules/LeapDate.php | 54 + .../validation/library/Rules/LeapYear.php | 50 + .../validation/library/Rules/Length.php | 130 + .../validation/library/Rules/LessThan.php | 26 + .../validation/library/Rules/Lowercase.php | 36 + .../validation/library/Rules/Luhn.php | 58 + .../validation/library/Rules/MacAddress.php | 36 + .../validation/library/Rules/Max.php | 27 + .../validation/library/Rules/MaxAge.php | 27 + .../validation/library/Rules/Mimetype.php | 66 + .../validation/library/Rules/Min.php | 27 + .../validation/library/Rules/MinAge.php | 29 + .../validation/library/Rules/Mobile.php | 30 + .../validation/library/Rules/Multiple.php | 40 + .../validation/library/Rules/Negative.php | 34 + .../validation/library/Rules/NfeAccessKey.php | 56 + .../validation/library/Rules/Nif.php | 99 + .../validation/library/Rules/Nip.php | 54 + .../workerman/validation/library/Rules/No.php | 32 + .../validation/library/Rules/NoWhitespace.php | 41 + .../validation/library/Rules/NoneOf.php | 52 + .../validation/library/Rules/Not.php | 128 + .../validation/library/Rules/NotBlank.php | 51 + .../validation/library/Rules/NotEmoji.php | 206 + .../validation/library/Rules/NotEmpty.php | 35 + .../validation/library/Rules/NotOptional.php | 33 + .../validation/library/Rules/NullType.php | 29 + .../validation/library/Rules/Nullable.php | 54 + .../validation/library/Rules/Number.php | 35 + .../validation/library/Rules/NumericVal.php | 30 + .../validation/library/Rules/ObjectType.php | 29 + .../validation/library/Rules/Odd.php | 41 + .../validation/library/Rules/OneOf.php | 82 + .../validation/library/Rules/Optional.php | 56 + .../library/Rules/PerfectSquare.php | 33 + .../validation/library/Rules/Pesel.php | 51 + .../validation/library/Rules/Phone.php | 73 + .../validation/library/Rules/PhpLabel.php | 31 + .../validation/library/Rules/Pis.php | 53 + .../validation/library/Rules/PolishIdCard.php | 63 + .../library/Rules/PortugueseNif.php | 105 + .../validation/library/Rules/Positive.php | 34 + .../validation/library/Rules/PostalCode.php | 222 + .../validation/library/Rules/PrimeNumber.php | 48 + .../validation/library/Rules/Printable.php | 32 + .../library/Rules/PublicDomainSuffix.php | 37 + .../validation/library/Rules/Punct.php | 31 + .../validation/library/Rules/Readable.php | 41 + .../validation/library/Rules/Regex.php | 48 + .../validation/library/Rules/ResourceType.php | 28 + .../validation/library/Rules/Roman.php | 25 + .../validation/library/Rules/ScalarVal.php | 28 + .../validation/library/Rules/Size.php | 124 + .../validation/library/Rules/Slug.php | 41 + .../validation/library/Rules/Sorted.php | 94 + .../validation/library/Rules/Space.php | 31 + .../validation/library/Rules/StartsWith.php | 89 + .../validation/library/Rules/StringType.php | 29 + .../validation/library/Rules/StringVal.php | 31 + .../library/Rules/SubdivisionCode.php | 52 + .../validation/library/Rules/Subset.php | 49 + .../validation/library/Rules/SymbolicLink.php | 36 + .../validation/library/Rules/Time.php | 66 + .../validation/library/Rules/Tld.php | 256 + .../validation/library/Rules/TrueVal.php | 32 + .../validation/library/Rules/Type.php | 86 + .../validation/library/Rules/Unique.php | 37 + .../validation/library/Rules/Uploaded.php | 45 + .../validation/library/Rules/Uppercase.php | 36 + .../validation/library/Rules/Url.php | 32 + .../validation/library/Rules/Uuid.php | 80 + .../validation/library/Rules/Version.php | 36 + .../validation/library/Rules/VideoUrl.php | 90 + .../validation/library/Rules/Vowel.php | 29 + .../validation/library/Rules/When.php | 91 + .../validation/library/Rules/Writable.php | 41 + .../validation/library/Rules/Xdigit.php | 27 + .../validation/library/Rules/Yes.php | 59 + .../validation/library/StaticValidator.php | 375 ++ .../validation/library/Validatable.php | 48 + .../validation/library/Validator.php | 99 + vendor/workerman/webman-framework/.gitignore | 5 + vendor/workerman/webman-framework/README.md | 5 + .../workerman/webman-framework/composer.json | 47 + vendor/workerman/webman-framework/src/App.php | 939 +++ .../webman-framework/src/Bootstrap.php | 28 + .../workerman/webman-framework/src/Config.php | 296 + .../webman-framework/src/Container.php | 84 + .../webman-framework/src/Context.php | 129 + .../src/Exception/ExceptionHandler.php | 118 + .../Exception/ExceptionHandlerInterface.php | 35 + .../src/Exception/FileException.php | 25 + .../src/Exception/NotFoundException.php | 25 + .../workerman/webman-framework/src/File.php | 56 + .../src/FileSessionHandler.php | 25 + .../webman-framework/src/Http/Request.php | 321 + .../webman-framework/src/Http/Response.php | 87 + .../webman-framework/src/Http/UploadFile.php | 111 + .../webman-framework/src/Install.php | 59 + .../webman-framework/src/Middleware.php | 90 + .../src/MiddlewareInterface.php | 30 + .../workerman/webman-framework/src/Route.php | 472 ++ .../webman-framework/src/Route/Route.php | 199 + .../src/Session/FileSessionHandler.php | 26 + .../Session/RedisClusterSessionHandler.php | 22 + .../src/Session/RedisSessionHandler.php | 26 + .../workerman/webman-framework/src/Util.php | 44 + .../workerman/webman-framework/src/View.php | 27 + .../webman-framework/src/support/App.php | 151 + .../webman-framework/src/support/Cache.php | 50 + .../src/support/Container.php | 48 + .../webman-framework/src/support/Context.php | 25 + .../webman-framework/src/support/Db.php | 36 + .../webman-framework/src/support/Log.php | 139 + .../webman-framework/src/support/Model.php | 262 + .../webman-framework/src/support/Plugin.php | 108 + .../webman-framework/src/support/Redis.php | 283 + .../webman-framework/src/support/Request.php | 24 + .../webman-framework/src/support/Response.php | 24 + .../src/support/Translation.php | 107 + .../webman-framework/src/support/View.php | 35 + .../src/support/bootstrap/LaravelDb.php | 125 + .../src/support/bootstrap/Session.php | 61 + .../support/exception/BusinessException.php | 38 + .../src/support/exception/Handler.php | 47 + .../src/support/view/Blade.php | 73 + .../webman-framework/src/support/view/Raw.php | 78 + .../src/support/view/ThinkPHP.php | 75 + .../src/support/view/Twig.php | 76 + .../workerman/workerman/.github/FUNDING.yml | 4 + vendor/workerman/workerman/.gitignore | 6 + vendor/workerman/workerman/Autoloader.php | 69 + .../Connection/AsyncTcpConnection.php | 378 ++ .../Connection/AsyncUdpConnection.php | 203 + .../Connection/ConnectionInterface.php | 126 + .../workerman/Connection/TcpConnection.php | 982 ++++ .../workerman/Connection/UdpConnection.php | 208 + vendor/workerman/workerman/Events/Ev.php | 189 + vendor/workerman/workerman/Events/Event.php | 215 + .../workerman/Events/EventInterface.php | 107 + .../workerman/workerman/Events/Libevent.php | 225 + .../workerman/workerman/Events/React/Base.php | 264 + .../workerman/Events/React/ExtEventLoop.php | 27 + .../Events/React/ExtLibEventLoop.php | 27 + .../Events/React/StreamSelectLoop.php | 26 + vendor/workerman/workerman/Events/Select.php | 357 ++ vendor/workerman/workerman/Events/Swoole.php | 230 + vendor/workerman/workerman/Events/Uv.php | 260 + vendor/workerman/workerman/Lib/Constants.php | 44 + vendor/workerman/workerman/Lib/Timer.php | 22 + vendor/workerman/workerman/MIT-LICENSE.txt | 21 + .../workerman/workerman/Protocols/Frame.php | 61 + vendor/workerman/workerman/Protocols/Http.php | 323 ++ .../workerman/Protocols/Http/Chunk.php | 48 + .../workerman/Protocols/Http/Request.php | 694 +++ .../workerman/Protocols/Http/Response.php | 458 ++ .../Protocols/Http/ServerSentEvents.php | 64 + .../workerman/Protocols/Http/Session.php | 461 ++ .../Http/Session/FileSessionHandler.php | 183 + .../Session/RedisClusterSessionHandler.php | 46 + .../Http/Session/RedisSessionHandler.php | 154 + .../Http/Session/SessionHandlerInterface.php | 114 + .../workerman/Protocols/Http/mime.types | 90 + .../workerman/Protocols/ProtocolInterface.php | 52 + vendor/workerman/workerman/Protocols/Text.php | 70 + .../workerman/Protocols/Websocket.php | 564 ++ vendor/workerman/workerman/Protocols/Ws.php | 432 ++ vendor/workerman/workerman/README.md | 342 ++ vendor/workerman/workerman/Timer.php | 220 + vendor/workerman/workerman/Worker.php | 2672 +++++++++ vendor/workerman/workerman/composer.json | 38 + vendor/yzh52521/easyhttp/LICENSE | 22 + vendor/yzh52521/easyhttp/README.md | 415 ++ vendor/yzh52521/easyhttp/composer.json | 33 + .../easyhttp/src/ConnectionException.php | 10 + vendor/yzh52521/easyhttp/src/Facade.php | 26 + vendor/yzh52521/easyhttp/src/Http.php | 64 + vendor/yzh52521/easyhttp/src/Logger.php | 273 + vendor/yzh52521/easyhttp/src/Request.php | 711 +++ .../easyhttp/src/RequestException.php | 43 + vendor/yzh52521/easyhttp/src/Response.php | 212 + vendor/yzh52521/easyhttp/src/Retry.php | 46 + webmaninit/.gitignore | 8 + windows.bat | 3 + windows.php | 118 + 3984 files changed, 416946 insertions(+) create mode 100644 .env.case create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 app/controller/IndexController.php create mode 100644 app/controller/LayAuth.php create mode 100644 app/functions.php create mode 100644 app/middleware/StaticFile.php create mode 100644 app/model/Test.php create mode 100644 app/view/404.html create mode 100644 app/view/auth.html create mode 100644 app/view/success.html create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 config/app.php create mode 100644 config/autoload.php create mode 100644 config/bootstrap.php create mode 100644 config/container.php create mode 100644 config/database.php create mode 100644 config/dependence.php create mode 100644 config/event.php create mode 100644 config/exception.php create mode 100644 config/log.php create mode 100644 config/middleware.php create mode 100644 config/plugin/webman/event/app.php create mode 100644 config/plugin/webman/event/bootstrap.php create mode 100644 config/plugin/webman/event/command.php create mode 100644 config/process.php create mode 100644 config/redis.php create mode 100644 config/route.php create mode 100644 config/server.php create mode 100644 config/session.php create mode 100644 config/static.php create mode 100644 config/translation.php create mode 100644 config/view.php create mode 100644 process/Monitor.php create mode 100644 public/404.html create mode 100644 public/favicon.ico create mode 100755 start.php create mode 100644 support/Request.php create mode 100644 support/Response.php create mode 100644 support/bootstrap.php create mode 100644 support/helpers.php create mode 100644 vendor/autoload.php create mode 100755 vendor/bin/carbon create mode 100755 vendor/bin/phinx create mode 100755 vendor/bin/var-dump-server create mode 100644 vendor/brick/math/CHANGELOG.md create mode 100644 vendor/brick/math/LICENSE create mode 100644 vendor/brick/math/composer.json create mode 100644 vendor/brick/math/src/BigDecimal.php create mode 100644 vendor/brick/math/src/BigInteger.php create mode 100644 vendor/brick/math/src/BigNumber.php create mode 100644 vendor/brick/math/src/BigRational.php create mode 100644 vendor/brick/math/src/Exception/DivisionByZeroException.php create mode 100644 vendor/brick/math/src/Exception/IntegerOverflowException.php create mode 100644 vendor/brick/math/src/Exception/MathException.php create mode 100644 vendor/brick/math/src/Exception/NegativeNumberException.php create mode 100644 vendor/brick/math/src/Exception/NumberFormatException.php create mode 100644 vendor/brick/math/src/Exception/RoundingNecessaryException.php create mode 100644 vendor/brick/math/src/Internal/Calculator.php create mode 100644 vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php create mode 100644 vendor/brick/math/src/Internal/Calculator/GmpCalculator.php create mode 100644 vendor/brick/math/src/Internal/Calculator/NativeCalculator.php create mode 100644 vendor/brick/math/src/RoundingMode.php create mode 100644 vendor/cakephp/core/App.php create mode 100644 vendor/cakephp/core/BasePlugin.php create mode 100644 vendor/cakephp/core/ClassLoader.php create mode 100644 vendor/cakephp/core/Configure.php create mode 100644 vendor/cakephp/core/Configure/ConfigEngineInterface.php create mode 100644 vendor/cakephp/core/Configure/Engine/IniConfig.php create mode 100644 vendor/cakephp/core/Configure/Engine/JsonConfig.php create mode 100644 vendor/cakephp/core/Configure/Engine/PhpConfig.php create mode 100644 vendor/cakephp/core/Configure/FileConfigTrait.php create mode 100644 vendor/cakephp/core/ConsoleApplicationInterface.php create mode 100644 vendor/cakephp/core/Container.php create mode 100644 vendor/cakephp/core/ContainerApplicationInterface.php create mode 100644 vendor/cakephp/core/ContainerInterface.php create mode 100644 vendor/cakephp/core/ConventionsTrait.php create mode 100644 vendor/cakephp/core/Exception/CakeException.php create mode 100644 vendor/cakephp/core/Exception/Exception.php create mode 100644 vendor/cakephp/core/Exception/MissingPluginException.php create mode 100644 vendor/cakephp/core/HttpApplicationInterface.php create mode 100644 vendor/cakephp/core/InstanceConfigTrait.php create mode 100644 vendor/cakephp/core/LICENSE.txt create mode 100644 vendor/cakephp/core/ObjectRegistry.php create mode 100644 vendor/cakephp/core/Plugin.php create mode 100644 vendor/cakephp/core/PluginApplicationInterface.php create mode 100644 vendor/cakephp/core/PluginCollection.php create mode 100644 vendor/cakephp/core/PluginInterface.php create mode 100644 vendor/cakephp/core/README.md create mode 100644 vendor/cakephp/core/Retry/CommandRetry.php create mode 100644 vendor/cakephp/core/Retry/RetryStrategyInterface.php create mode 100644 vendor/cakephp/core/ServiceConfig.php create mode 100644 vendor/cakephp/core/ServiceProvider.php create mode 100644 vendor/cakephp/core/StaticConfigTrait.php create mode 100644 vendor/cakephp/core/TestSuite/ContainerStubTrait.php create mode 100644 vendor/cakephp/core/composer.json create mode 100644 vendor/cakephp/core/functions.php create mode 100644 vendor/cakephp/core/functions_global.php create mode 100644 vendor/cakephp/database/Connection.php create mode 100644 vendor/cakephp/database/ConstraintsInterface.php create mode 100644 vendor/cakephp/database/Driver.php create mode 100644 vendor/cakephp/database/Driver/Mysql.php create mode 100644 vendor/cakephp/database/Driver/Postgres.php create mode 100644 vendor/cakephp/database/Driver/SqlDialectTrait.php create mode 100644 vendor/cakephp/database/Driver/Sqlite.php create mode 100644 vendor/cakephp/database/Driver/Sqlserver.php create mode 100644 vendor/cakephp/database/Driver/TupleComparisonTranslatorTrait.php create mode 100644 vendor/cakephp/database/DriverInterface.php create mode 100644 vendor/cakephp/database/Exception.php create mode 100644 vendor/cakephp/database/Exception/DatabaseException.php create mode 100644 vendor/cakephp/database/Exception/MissingConnectionException.php create mode 100644 vendor/cakephp/database/Exception/MissingDriverException.php create mode 100644 vendor/cakephp/database/Exception/MissingExtensionException.php create mode 100644 vendor/cakephp/database/Exception/NestedTransactionRollbackException.php create mode 100644 vendor/cakephp/database/Expression/AggregateExpression.php create mode 100644 vendor/cakephp/database/Expression/BetweenExpression.php create mode 100644 vendor/cakephp/database/Expression/CaseExpression.php create mode 100644 vendor/cakephp/database/Expression/CaseExpressionTrait.php create mode 100644 vendor/cakephp/database/Expression/CaseStatementExpression.php create mode 100644 vendor/cakephp/database/Expression/CommonTableExpression.php create mode 100644 vendor/cakephp/database/Expression/Comparison.php create mode 100644 vendor/cakephp/database/Expression/ComparisonExpression.php create mode 100644 vendor/cakephp/database/Expression/FieldInterface.php create mode 100644 vendor/cakephp/database/Expression/FieldTrait.php create mode 100644 vendor/cakephp/database/Expression/FunctionExpression.php create mode 100644 vendor/cakephp/database/Expression/IdentifierExpression.php create mode 100644 vendor/cakephp/database/Expression/OrderByExpression.php create mode 100644 vendor/cakephp/database/Expression/OrderClauseExpression.php create mode 100644 vendor/cakephp/database/Expression/QueryExpression.php create mode 100644 vendor/cakephp/database/Expression/StringExpression.php create mode 100644 vendor/cakephp/database/Expression/TupleComparison.php create mode 100644 vendor/cakephp/database/Expression/UnaryExpression.php create mode 100644 vendor/cakephp/database/Expression/ValuesExpression.php create mode 100644 vendor/cakephp/database/Expression/WhenThenExpression.php create mode 100644 vendor/cakephp/database/Expression/WindowExpression.php create mode 100644 vendor/cakephp/database/Expression/WindowInterface.php create mode 100644 vendor/cakephp/database/ExpressionInterface.php create mode 100644 vendor/cakephp/database/FieldTypeConverter.php create mode 100644 vendor/cakephp/database/FunctionsBuilder.php create mode 100644 vendor/cakephp/database/IdentifierQuoter.php create mode 100644 vendor/cakephp/database/LICENSE.txt create mode 100644 vendor/cakephp/database/Log/LoggedQuery.php create mode 100644 vendor/cakephp/database/Log/LoggingStatement.php create mode 100644 vendor/cakephp/database/Log/QueryLogger.php create mode 100644 vendor/cakephp/database/PostgresCompiler.php create mode 100644 vendor/cakephp/database/Query.php create mode 100644 vendor/cakephp/database/Query/DeleteQuery.php create mode 100644 vendor/cakephp/database/Query/InsertQuery.php create mode 100644 vendor/cakephp/database/Query/SelectQuery.php create mode 100644 vendor/cakephp/database/Query/UpdateQuery.php create mode 100644 vendor/cakephp/database/QueryCompiler.php create mode 100644 vendor/cakephp/database/README.md create mode 100644 vendor/cakephp/database/Retry/ErrorCodeWaitStrategy.php create mode 100644 vendor/cakephp/database/Retry/ReconnectStrategy.php create mode 100644 vendor/cakephp/database/Schema/BaseSchema.php create mode 100644 vendor/cakephp/database/Schema/CachedCollection.php create mode 100644 vendor/cakephp/database/Schema/Collection.php create mode 100644 vendor/cakephp/database/Schema/CollectionInterface.php create mode 100644 vendor/cakephp/database/Schema/MysqlSchema.php create mode 100644 vendor/cakephp/database/Schema/MysqlSchemaDialect.php create mode 100644 vendor/cakephp/database/Schema/PostgresSchema.php create mode 100644 vendor/cakephp/database/Schema/PostgresSchemaDialect.php create mode 100644 vendor/cakephp/database/Schema/SchemaDialect.php create mode 100644 vendor/cakephp/database/Schema/SqlGeneratorInterface.php create mode 100644 vendor/cakephp/database/Schema/SqliteSchema.php create mode 100644 vendor/cakephp/database/Schema/SqliteSchemaDialect.php create mode 100644 vendor/cakephp/database/Schema/SqlserverSchema.php create mode 100644 vendor/cakephp/database/Schema/SqlserverSchemaDialect.php create mode 100644 vendor/cakephp/database/Schema/TableSchema.php create mode 100644 vendor/cakephp/database/Schema/TableSchemaAwareInterface.php create mode 100644 vendor/cakephp/database/Schema/TableSchemaInterface.php create mode 100644 vendor/cakephp/database/SchemaCache.php create mode 100644 vendor/cakephp/database/SqlDialectTrait.php create mode 100644 vendor/cakephp/database/SqliteCompiler.php create mode 100644 vendor/cakephp/database/SqlserverCompiler.php create mode 100644 vendor/cakephp/database/Statement/BufferResultsTrait.php create mode 100644 vendor/cakephp/database/Statement/BufferedStatement.php create mode 100644 vendor/cakephp/database/Statement/CallbackStatement.php create mode 100644 vendor/cakephp/database/Statement/MysqlStatement.php create mode 100644 vendor/cakephp/database/Statement/PDOStatement.php create mode 100644 vendor/cakephp/database/Statement/SqliteStatement.php create mode 100644 vendor/cakephp/database/Statement/SqlserverStatement.php create mode 100644 vendor/cakephp/database/Statement/StatementDecorator.php create mode 100644 vendor/cakephp/database/StatementInterface.php create mode 100644 vendor/cakephp/database/Type.php create mode 100644 vendor/cakephp/database/Type/BaseType.php create mode 100644 vendor/cakephp/database/Type/BatchCastingInterface.php create mode 100644 vendor/cakephp/database/Type/BinaryType.php create mode 100644 vendor/cakephp/database/Type/BinaryUuidType.php create mode 100644 vendor/cakephp/database/Type/BoolType.php create mode 100644 vendor/cakephp/database/Type/ColumnSchemaAwareInterface.php create mode 100644 vendor/cakephp/database/Type/DateTimeFractionalType.php create mode 100644 vendor/cakephp/database/Type/DateTimeTimezoneType.php create mode 100644 vendor/cakephp/database/Type/DateTimeType.php create mode 100644 vendor/cakephp/database/Type/DateType.php create mode 100644 vendor/cakephp/database/Type/DecimalType.php create mode 100644 vendor/cakephp/database/Type/ExpressionTypeCasterTrait.php create mode 100644 vendor/cakephp/database/Type/ExpressionTypeInterface.php create mode 100644 vendor/cakephp/database/Type/FloatType.php create mode 100644 vendor/cakephp/database/Type/IntegerType.php create mode 100644 vendor/cakephp/database/Type/JsonType.php create mode 100644 vendor/cakephp/database/Type/OptionalConvertInterface.php create mode 100644 vendor/cakephp/database/Type/StringType.php create mode 100644 vendor/cakephp/database/Type/TimeType.php create mode 100644 vendor/cakephp/database/Type/UuidType.php create mode 100644 vendor/cakephp/database/TypeConverterTrait.php create mode 100644 vendor/cakephp/database/TypeFactory.php create mode 100644 vendor/cakephp/database/TypeInterface.php create mode 100644 vendor/cakephp/database/TypeMap.php create mode 100644 vendor/cakephp/database/TypeMapTrait.php create mode 100644 vendor/cakephp/database/TypedResultInterface.php create mode 100644 vendor/cakephp/database/TypedResultTrait.php create mode 100644 vendor/cakephp/database/ValueBinder.php create mode 100644 vendor/cakephp/database/composer.json create mode 100644 vendor/cakephp/datasource/ConnectionInterface.php create mode 100644 vendor/cakephp/datasource/ConnectionManager.php create mode 100644 vendor/cakephp/datasource/ConnectionRegistry.php create mode 100644 vendor/cakephp/datasource/EntityInterface.php create mode 100644 vendor/cakephp/datasource/EntityTrait.php create mode 100644 vendor/cakephp/datasource/Exception/InvalidPrimaryKeyException.php create mode 100644 vendor/cakephp/datasource/Exception/MissingDatasourceConfigException.php create mode 100644 vendor/cakephp/datasource/Exception/MissingDatasourceException.php create mode 100644 vendor/cakephp/datasource/Exception/MissingModelException.php create mode 100644 vendor/cakephp/datasource/Exception/PageOutOfBoundsException.php create mode 100644 vendor/cakephp/datasource/Exception/RecordNotFoundException.php create mode 100644 vendor/cakephp/datasource/FactoryLocator.php create mode 100644 vendor/cakephp/datasource/FixtureInterface.php create mode 100644 vendor/cakephp/datasource/InvalidPropertyInterface.php create mode 100644 vendor/cakephp/datasource/LICENSE.txt create mode 100644 vendor/cakephp/datasource/Locator/AbstractLocator.php create mode 100644 vendor/cakephp/datasource/Locator/LocatorInterface.php create mode 100644 vendor/cakephp/datasource/ModelAwareTrait.php create mode 100644 vendor/cakephp/datasource/Paginator.php create mode 100644 vendor/cakephp/datasource/PaginatorInterface.php create mode 100644 vendor/cakephp/datasource/Paging/Exception/PageOutOfBoundsException.php create mode 100644 vendor/cakephp/datasource/Paging/NumericPaginator.php create mode 100644 vendor/cakephp/datasource/Paging/PaginatorInterface.php create mode 100644 vendor/cakephp/datasource/Paging/SimplePaginator.php create mode 100644 vendor/cakephp/datasource/QueryCacher.php create mode 100644 vendor/cakephp/datasource/QueryInterface.php create mode 100644 vendor/cakephp/datasource/QueryTrait.php create mode 100644 vendor/cakephp/datasource/README.md create mode 100644 vendor/cakephp/datasource/RepositoryInterface.php create mode 100644 vendor/cakephp/datasource/ResultSetDecorator.php create mode 100644 vendor/cakephp/datasource/ResultSetInterface.php create mode 100644 vendor/cakephp/datasource/RuleInvoker.php create mode 100644 vendor/cakephp/datasource/RulesAwareTrait.php create mode 100644 vendor/cakephp/datasource/RulesChecker.php create mode 100644 vendor/cakephp/datasource/SchemaInterface.php create mode 100644 vendor/cakephp/datasource/SimplePaginator.php create mode 100644 vendor/cakephp/datasource/composer.json create mode 100644 vendor/cakephp/utility/CookieCryptTrait.php create mode 100644 vendor/cakephp/utility/Crypto/OpenSsl.php create mode 100644 vendor/cakephp/utility/Exception/XmlException.php create mode 100644 vendor/cakephp/utility/Hash.php create mode 100644 vendor/cakephp/utility/Inflector.php create mode 100644 vendor/cakephp/utility/LICENSE.txt create mode 100644 vendor/cakephp/utility/MergeVariablesTrait.php create mode 100644 vendor/cakephp/utility/README.md create mode 100644 vendor/cakephp/utility/Security.php create mode 100644 vendor/cakephp/utility/Text.php create mode 100644 vendor/cakephp/utility/Xml.php create mode 100644 vendor/cakephp/utility/bootstrap.php create mode 100644 vendor/cakephp/utility/composer.json create mode 100644 vendor/carbonphp/carbon-doctrine-types/LICENSE create mode 100644 vendor/carbonphp/carbon-doctrine-types/README.md create mode 100644 vendor/carbonphp/carbon-doctrine-types/composer.json create mode 100644 vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonDoctrineType.php create mode 100644 vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonImmutableType.php create mode 100644 vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonType.php create mode 100644 vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonTypeConverter.php create mode 100644 vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeDefaultPrecision.php create mode 100644 vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeImmutableType.php create mode 100644 vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeType.php create mode 100644 vendor/composer/ClassLoader.php create mode 100644 vendor/composer/InstalledVersions.php create mode 100644 vendor/composer/LICENSE create mode 100644 vendor/composer/autoload_classmap.php create mode 100644 vendor/composer/autoload_files.php create mode 100644 vendor/composer/autoload_namespaces.php create mode 100644 vendor/composer/autoload_psr4.php create mode 100644 vendor/composer/autoload_real.php create mode 100644 vendor/composer/autoload_static.php create mode 100644 vendor/composer/installed.json create mode 100644 vendor/composer/installed.php create mode 100644 vendor/composer/platform_check.php create mode 100644 vendor/doctrine/inflector/LICENSE create mode 100644 vendor/doctrine/inflector/README.md create mode 100644 vendor/doctrine/inflector/composer.json create mode 100644 vendor/doctrine/inflector/docs/en/index.rst create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/CachedWordInflector.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/GenericLanguageInflectorFactory.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Inflector.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/InflectorFactory.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Language.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/LanguageInflectorFactory.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/NoopWordInflector.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Inflectible.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/InflectorFactory.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Rules.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Uninflected.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Inflectible.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/InflectorFactory.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Rules.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Uninflected.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Inflectible.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/InflectorFactory.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Rules.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Uninflected.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Pattern.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Patterns.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Inflectible.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/InflectorFactory.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Rules.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Uninflected.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Ruleset.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Inflectible.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/InflectorFactory.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Rules.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Uninflected.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Substitution.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Substitutions.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformation.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformations.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Inflectible.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/InflectorFactory.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Rules.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Uninflected.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Word.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/RulesetInflector.php create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Inflector/WordInflector.php create mode 100644 vendor/graham-campbell/result-type/LICENSE create mode 100644 vendor/graham-campbell/result-type/composer.json create mode 100644 vendor/graham-campbell/result-type/src/Error.php create mode 100644 vendor/graham-campbell/result-type/src/Result.php create mode 100644 vendor/graham-campbell/result-type/src/Success.php create mode 100644 vendor/guzzlehttp/guzzle/CHANGELOG.md create mode 100644 vendor/guzzlehttp/guzzle/LICENSE create mode 100644 vendor/guzzlehttp/guzzle/README.md create mode 100644 vendor/guzzlehttp/guzzle/UPGRADING.md create mode 100644 vendor/guzzlehttp/guzzle/composer.json create mode 100644 vendor/guzzlehttp/guzzle/src/BodySummarizer.php create mode 100644 vendor/guzzlehttp/guzzle/src/BodySummarizerInterface.php create mode 100644 vendor/guzzlehttp/guzzle/src/Client.php create mode 100644 vendor/guzzlehttp/guzzle/src/ClientInterface.php create mode 100644 vendor/guzzlehttp/guzzle/src/ClientTrait.php create mode 100644 vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php create mode 100644 vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php create mode 100644 vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php create mode 100644 vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php create mode 100644 vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/ClientException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/InvalidArgumentException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/RequestException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/ServerException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Exception/TransferException.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/Proxy.php create mode 100644 vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php create mode 100644 vendor/guzzlehttp/guzzle/src/HandlerStack.php create mode 100644 vendor/guzzlehttp/guzzle/src/MessageFormatter.php create mode 100644 vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php create mode 100644 vendor/guzzlehttp/guzzle/src/Middleware.php create mode 100644 vendor/guzzlehttp/guzzle/src/Pool.php create mode 100644 vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php create mode 100644 vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php create mode 100644 vendor/guzzlehttp/guzzle/src/RequestOptions.php create mode 100644 vendor/guzzlehttp/guzzle/src/RetryMiddleware.php create mode 100644 vendor/guzzlehttp/guzzle/src/TransferStats.php create mode 100644 vendor/guzzlehttp/guzzle/src/Utils.php create mode 100644 vendor/guzzlehttp/guzzle/src/functions.php create mode 100644 vendor/guzzlehttp/guzzle/src/functions_include.php create mode 100644 vendor/guzzlehttp/promises/CHANGELOG.md create mode 100644 vendor/guzzlehttp/promises/LICENSE create mode 100644 vendor/guzzlehttp/promises/README.md create mode 100644 vendor/guzzlehttp/promises/composer.json create mode 100644 vendor/guzzlehttp/promises/src/AggregateException.php create mode 100644 vendor/guzzlehttp/promises/src/CancellationException.php create mode 100644 vendor/guzzlehttp/promises/src/Coroutine.php create mode 100644 vendor/guzzlehttp/promises/src/Create.php create mode 100644 vendor/guzzlehttp/promises/src/Each.php create mode 100644 vendor/guzzlehttp/promises/src/EachPromise.php create mode 100644 vendor/guzzlehttp/promises/src/FulfilledPromise.php create mode 100644 vendor/guzzlehttp/promises/src/Is.php create mode 100644 vendor/guzzlehttp/promises/src/Promise.php create mode 100644 vendor/guzzlehttp/promises/src/PromiseInterface.php create mode 100644 vendor/guzzlehttp/promises/src/PromisorInterface.php create mode 100644 vendor/guzzlehttp/promises/src/RejectedPromise.php create mode 100644 vendor/guzzlehttp/promises/src/RejectionException.php create mode 100644 vendor/guzzlehttp/promises/src/TaskQueue.php create mode 100644 vendor/guzzlehttp/promises/src/TaskQueueInterface.php create mode 100644 vendor/guzzlehttp/promises/src/Utils.php create mode 100644 vendor/guzzlehttp/promises/src/functions.php create mode 100644 vendor/guzzlehttp/promises/src/functions_include.php create mode 100644 vendor/guzzlehttp/psr7/CHANGELOG.md create mode 100644 vendor/guzzlehttp/psr7/LICENSE create mode 100644 vendor/guzzlehttp/psr7/README.md create mode 100644 vendor/guzzlehttp/psr7/composer.json create mode 100644 vendor/guzzlehttp/psr7/src/AppendStream.php create mode 100644 vendor/guzzlehttp/psr7/src/BufferStream.php create mode 100644 vendor/guzzlehttp/psr7/src/CachingStream.php create mode 100644 vendor/guzzlehttp/psr7/src/DroppingStream.php create mode 100644 vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php create mode 100644 vendor/guzzlehttp/psr7/src/FnStream.php create mode 100644 vendor/guzzlehttp/psr7/src/Header.php create mode 100644 vendor/guzzlehttp/psr7/src/HttpFactory.php create mode 100644 vendor/guzzlehttp/psr7/src/InflateStream.php create mode 100644 vendor/guzzlehttp/psr7/src/LazyOpenStream.php create mode 100644 vendor/guzzlehttp/psr7/src/LimitStream.php create mode 100644 vendor/guzzlehttp/psr7/src/Message.php create mode 100644 vendor/guzzlehttp/psr7/src/MessageTrait.php create mode 100644 vendor/guzzlehttp/psr7/src/MimeType.php create mode 100644 vendor/guzzlehttp/psr7/src/MultipartStream.php create mode 100644 vendor/guzzlehttp/psr7/src/NoSeekStream.php create mode 100644 vendor/guzzlehttp/psr7/src/PumpStream.php create mode 100644 vendor/guzzlehttp/psr7/src/Query.php create mode 100644 vendor/guzzlehttp/psr7/src/Request.php create mode 100644 vendor/guzzlehttp/psr7/src/Response.php create mode 100644 vendor/guzzlehttp/psr7/src/Rfc7230.php create mode 100644 vendor/guzzlehttp/psr7/src/ServerRequest.php create mode 100644 vendor/guzzlehttp/psr7/src/Stream.php create mode 100644 vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php create mode 100644 vendor/guzzlehttp/psr7/src/StreamWrapper.php create mode 100644 vendor/guzzlehttp/psr7/src/UploadedFile.php create mode 100644 vendor/guzzlehttp/psr7/src/Uri.php create mode 100644 vendor/guzzlehttp/psr7/src/UriComparator.php create mode 100644 vendor/guzzlehttp/psr7/src/UriNormalizer.php create mode 100644 vendor/guzzlehttp/psr7/src/UriResolver.php create mode 100644 vendor/guzzlehttp/psr7/src/Utils.php create mode 100644 vendor/illuminate/bus/Batch.php create mode 100644 vendor/illuminate/bus/BatchFactory.php create mode 100644 vendor/illuminate/bus/BatchRepository.php create mode 100644 vendor/illuminate/bus/Batchable.php create mode 100644 vendor/illuminate/bus/BusServiceProvider.php create mode 100644 vendor/illuminate/bus/DatabaseBatchRepository.php create mode 100644 vendor/illuminate/bus/Dispatcher.php create mode 100644 vendor/illuminate/bus/Events/BatchDispatched.php create mode 100644 vendor/illuminate/bus/LICENSE.md create mode 100644 vendor/illuminate/bus/PendingBatch.php create mode 100644 vendor/illuminate/bus/PrunableBatchRepository.php create mode 100644 vendor/illuminate/bus/Queueable.php create mode 100644 vendor/illuminate/bus/UniqueLock.php create mode 100644 vendor/illuminate/bus/UpdatedBatchJobCounts.php create mode 100644 vendor/illuminate/bus/composer.json create mode 100644 vendor/illuminate/collections/Arr.php create mode 100644 vendor/illuminate/collections/Collection.php create mode 100644 vendor/illuminate/collections/Enumerable.php create mode 100644 vendor/illuminate/collections/HigherOrderCollectionProxy.php create mode 100644 vendor/illuminate/collections/ItemNotFoundException.php create mode 100644 vendor/illuminate/collections/LICENSE.md create mode 100644 vendor/illuminate/collections/LazyCollection.php create mode 100644 vendor/illuminate/collections/MultipleItemsFoundException.php create mode 100644 vendor/illuminate/collections/Traits/EnumeratesValues.php create mode 100644 vendor/illuminate/collections/composer.json create mode 100644 vendor/illuminate/collections/helpers.php create mode 100644 vendor/illuminate/conditionable/HigherOrderWhenProxy.php create mode 100644 vendor/illuminate/conditionable/LICENSE.md create mode 100644 vendor/illuminate/conditionable/Traits/Conditionable.php create mode 100644 vendor/illuminate/conditionable/composer.json create mode 100644 vendor/illuminate/container/BoundMethod.php create mode 100755 vendor/illuminate/container/Container.php create mode 100644 vendor/illuminate/container/ContextualBindingBuilder.php create mode 100644 vendor/illuminate/container/EntryNotFoundException.php create mode 100644 vendor/illuminate/container/LICENSE.md create mode 100644 vendor/illuminate/container/RewindableGenerator.php create mode 100644 vendor/illuminate/container/Util.php create mode 100755 vendor/illuminate/container/composer.json create mode 100644 vendor/illuminate/contracts/Auth/Access/Authorizable.php create mode 100644 vendor/illuminate/contracts/Auth/Access/Gate.php create mode 100644 vendor/illuminate/contracts/Auth/Authenticatable.php create mode 100644 vendor/illuminate/contracts/Auth/CanResetPassword.php create mode 100644 vendor/illuminate/contracts/Auth/Factory.php create mode 100644 vendor/illuminate/contracts/Auth/Guard.php create mode 100644 vendor/illuminate/contracts/Auth/Middleware/AuthenticatesRequests.php create mode 100644 vendor/illuminate/contracts/Auth/MustVerifyEmail.php create mode 100644 vendor/illuminate/contracts/Auth/PasswordBroker.php create mode 100644 vendor/illuminate/contracts/Auth/PasswordBrokerFactory.php create mode 100644 vendor/illuminate/contracts/Auth/StatefulGuard.php create mode 100644 vendor/illuminate/contracts/Auth/SupportsBasicAuth.php create mode 100644 vendor/illuminate/contracts/Auth/UserProvider.php create mode 100644 vendor/illuminate/contracts/Broadcasting/Broadcaster.php create mode 100644 vendor/illuminate/contracts/Broadcasting/Factory.php create mode 100644 vendor/illuminate/contracts/Broadcasting/HasBroadcastChannel.php create mode 100644 vendor/illuminate/contracts/Broadcasting/ShouldBeUnique.php create mode 100644 vendor/illuminate/contracts/Broadcasting/ShouldBroadcast.php create mode 100644 vendor/illuminate/contracts/Broadcasting/ShouldBroadcastNow.php create mode 100644 vendor/illuminate/contracts/Bus/Dispatcher.php create mode 100644 vendor/illuminate/contracts/Bus/QueueingDispatcher.php create mode 100644 vendor/illuminate/contracts/Cache/Factory.php create mode 100644 vendor/illuminate/contracts/Cache/Lock.php create mode 100644 vendor/illuminate/contracts/Cache/LockProvider.php create mode 100644 vendor/illuminate/contracts/Cache/LockTimeoutException.php create mode 100644 vendor/illuminate/contracts/Cache/Repository.php create mode 100644 vendor/illuminate/contracts/Cache/Store.php create mode 100644 vendor/illuminate/contracts/Config/Repository.php create mode 100644 vendor/illuminate/contracts/Console/Application.php create mode 100644 vendor/illuminate/contracts/Console/Isolatable.php create mode 100644 vendor/illuminate/contracts/Console/Kernel.php create mode 100644 vendor/illuminate/contracts/Console/PromptsForMissingInput.php create mode 100644 vendor/illuminate/contracts/Container/BindingResolutionException.php create mode 100644 vendor/illuminate/contracts/Container/CircularDependencyException.php create mode 100644 vendor/illuminate/contracts/Container/Container.php create mode 100644 vendor/illuminate/contracts/Container/ContextualBindingBuilder.php create mode 100644 vendor/illuminate/contracts/Cookie/Factory.php create mode 100644 vendor/illuminate/contracts/Cookie/QueueingFactory.php create mode 100644 vendor/illuminate/contracts/Database/Eloquent/Builder.php create mode 100644 vendor/illuminate/contracts/Database/Eloquent/Castable.php create mode 100644 vendor/illuminate/contracts/Database/Eloquent/CastsAttributes.php create mode 100644 vendor/illuminate/contracts/Database/Eloquent/CastsInboundAttributes.php create mode 100644 vendor/illuminate/contracts/Database/Eloquent/DeviatesCastableAttributes.php create mode 100644 vendor/illuminate/contracts/Database/Eloquent/SerializesCastableAttributes.php create mode 100644 vendor/illuminate/contracts/Database/Eloquent/SupportsPartialRelations.php create mode 100644 vendor/illuminate/contracts/Database/Events/MigrationEvent.php create mode 100644 vendor/illuminate/contracts/Database/ModelIdentifier.php create mode 100644 vendor/illuminate/contracts/Database/Query/Builder.php create mode 100644 vendor/illuminate/contracts/Debug/ExceptionHandler.php create mode 100644 vendor/illuminate/contracts/Encryption/DecryptException.php create mode 100644 vendor/illuminate/contracts/Encryption/EncryptException.php create mode 100644 vendor/illuminate/contracts/Encryption/Encrypter.php create mode 100644 vendor/illuminate/contracts/Encryption/StringEncrypter.php create mode 100644 vendor/illuminate/contracts/Events/Dispatcher.php create mode 100644 vendor/illuminate/contracts/Filesystem/Cloud.php create mode 100644 vendor/illuminate/contracts/Filesystem/Factory.php create mode 100644 vendor/illuminate/contracts/Filesystem/FileNotFoundException.php create mode 100644 vendor/illuminate/contracts/Filesystem/Filesystem.php create mode 100644 vendor/illuminate/contracts/Filesystem/LockTimeoutException.php create mode 100644 vendor/illuminate/contracts/Foundation/Application.php create mode 100644 vendor/illuminate/contracts/Foundation/CachesConfiguration.php create mode 100644 vendor/illuminate/contracts/Foundation/CachesRoutes.php create mode 100644 vendor/illuminate/contracts/Foundation/ExceptionRenderer.php create mode 100644 vendor/illuminate/contracts/Foundation/MaintenanceMode.php create mode 100644 vendor/illuminate/contracts/Hashing/Hasher.php create mode 100644 vendor/illuminate/contracts/Http/Kernel.php create mode 100644 vendor/illuminate/contracts/LICENSE.md create mode 100644 vendor/illuminate/contracts/Mail/Attachable.php create mode 100644 vendor/illuminate/contracts/Mail/Factory.php create mode 100644 vendor/illuminate/contracts/Mail/MailQueue.php create mode 100644 vendor/illuminate/contracts/Mail/Mailable.php create mode 100644 vendor/illuminate/contracts/Mail/Mailer.php create mode 100644 vendor/illuminate/contracts/Notifications/Dispatcher.php create mode 100644 vendor/illuminate/contracts/Notifications/Factory.php create mode 100644 vendor/illuminate/contracts/Pagination/CursorPaginator.php create mode 100644 vendor/illuminate/contracts/Pagination/LengthAwarePaginator.php create mode 100644 vendor/illuminate/contracts/Pagination/Paginator.php create mode 100644 vendor/illuminate/contracts/Pipeline/Hub.php create mode 100644 vendor/illuminate/contracts/Pipeline/Pipeline.php create mode 100644 vendor/illuminate/contracts/Queue/ClearableQueue.php create mode 100644 vendor/illuminate/contracts/Queue/EntityNotFoundException.php create mode 100644 vendor/illuminate/contracts/Queue/EntityResolver.php create mode 100644 vendor/illuminate/contracts/Queue/Factory.php create mode 100644 vendor/illuminate/contracts/Queue/Job.php create mode 100644 vendor/illuminate/contracts/Queue/Monitor.php create mode 100644 vendor/illuminate/contracts/Queue/Queue.php create mode 100644 vendor/illuminate/contracts/Queue/QueueableCollection.php create mode 100644 vendor/illuminate/contracts/Queue/QueueableEntity.php create mode 100644 vendor/illuminate/contracts/Queue/ShouldBeEncrypted.php create mode 100644 vendor/illuminate/contracts/Queue/ShouldBeUnique.php create mode 100644 vendor/illuminate/contracts/Queue/ShouldBeUniqueUntilProcessing.php create mode 100644 vendor/illuminate/contracts/Queue/ShouldQueue.php create mode 100644 vendor/illuminate/contracts/Redis/Connection.php create mode 100644 vendor/illuminate/contracts/Redis/Connector.php create mode 100644 vendor/illuminate/contracts/Redis/Factory.php create mode 100644 vendor/illuminate/contracts/Redis/LimiterTimeoutException.php create mode 100644 vendor/illuminate/contracts/Routing/BindingRegistrar.php create mode 100644 vendor/illuminate/contracts/Routing/Registrar.php create mode 100644 vendor/illuminate/contracts/Routing/ResponseFactory.php create mode 100644 vendor/illuminate/contracts/Routing/UrlGenerator.php create mode 100644 vendor/illuminate/contracts/Routing/UrlRoutable.php create mode 100644 vendor/illuminate/contracts/Session/Middleware/AuthenticatesSessions.php create mode 100644 vendor/illuminate/contracts/Session/Session.php create mode 100755 vendor/illuminate/contracts/Support/Arrayable.php create mode 100644 vendor/illuminate/contracts/Support/CanBeEscapedWhenCastToString.php create mode 100644 vendor/illuminate/contracts/Support/DeferrableProvider.php create mode 100644 vendor/illuminate/contracts/Support/DeferringDisplayableValue.php create mode 100644 vendor/illuminate/contracts/Support/Htmlable.php create mode 100755 vendor/illuminate/contracts/Support/Jsonable.php create mode 100644 vendor/illuminate/contracts/Support/MessageBag.php create mode 100755 vendor/illuminate/contracts/Support/MessageProvider.php create mode 100755 vendor/illuminate/contracts/Support/Renderable.php create mode 100644 vendor/illuminate/contracts/Support/Responsable.php create mode 100644 vendor/illuminate/contracts/Support/ValidatedData.php create mode 100644 vendor/illuminate/contracts/Translation/HasLocalePreference.php create mode 100755 vendor/illuminate/contracts/Translation/Loader.php create mode 100644 vendor/illuminate/contracts/Translation/Translator.php create mode 100644 vendor/illuminate/contracts/Validation/DataAwareRule.php create mode 100644 vendor/illuminate/contracts/Validation/Factory.php create mode 100644 vendor/illuminate/contracts/Validation/ImplicitRule.php create mode 100644 vendor/illuminate/contracts/Validation/InvokableRule.php create mode 100644 vendor/illuminate/contracts/Validation/Rule.php create mode 100644 vendor/illuminate/contracts/Validation/UncompromisedVerifier.php create mode 100644 vendor/illuminate/contracts/Validation/ValidatesWhenResolved.php create mode 100644 vendor/illuminate/contracts/Validation/Validator.php create mode 100644 vendor/illuminate/contracts/Validation/ValidatorAwareRule.php create mode 100755 vendor/illuminate/contracts/View/Engine.php create mode 100644 vendor/illuminate/contracts/View/Factory.php create mode 100644 vendor/illuminate/contracts/View/View.php create mode 100644 vendor/illuminate/contracts/View/ViewCompilationException.php create mode 100644 vendor/illuminate/contracts/composer.json create mode 100755 vendor/illuminate/database/Capsule/Manager.php create mode 100644 vendor/illuminate/database/ClassMorphViolationException.php create mode 100644 vendor/illuminate/database/Concerns/BuildsQueries.php create mode 100644 vendor/illuminate/database/Concerns/CompilesJsonPaths.php create mode 100644 vendor/illuminate/database/Concerns/ExplainsQueries.php create mode 100644 vendor/illuminate/database/Concerns/ManagesTransactions.php create mode 100644 vendor/illuminate/database/Concerns/ParsesSearchPath.php create mode 100644 vendor/illuminate/database/ConfigurationUrlParser.php create mode 100755 vendor/illuminate/database/Connection.php create mode 100755 vendor/illuminate/database/ConnectionInterface.php create mode 100755 vendor/illuminate/database/ConnectionResolver.php create mode 100755 vendor/illuminate/database/ConnectionResolverInterface.php create mode 100755 vendor/illuminate/database/Connectors/ConnectionFactory.php create mode 100755 vendor/illuminate/database/Connectors/Connector.php create mode 100755 vendor/illuminate/database/Connectors/ConnectorInterface.php create mode 100755 vendor/illuminate/database/Connectors/MySqlConnector.php create mode 100755 vendor/illuminate/database/Connectors/PostgresConnector.php create mode 100755 vendor/illuminate/database/Connectors/SQLiteConnector.php create mode 100755 vendor/illuminate/database/Connectors/SqlServerConnector.php create mode 100644 vendor/illuminate/database/Console/DatabaseInspectionCommand.php create mode 100644 vendor/illuminate/database/Console/DbCommand.php create mode 100644 vendor/illuminate/database/Console/DumpCommand.php create mode 100644 vendor/illuminate/database/Console/Factories/FactoryMakeCommand.php create mode 100644 vendor/illuminate/database/Console/Factories/stubs/factory.stub create mode 100755 vendor/illuminate/database/Console/Migrations/BaseCommand.php create mode 100644 vendor/illuminate/database/Console/Migrations/FreshCommand.php create mode 100755 vendor/illuminate/database/Console/Migrations/InstallCommand.php create mode 100755 vendor/illuminate/database/Console/Migrations/MigrateCommand.php create mode 100644 vendor/illuminate/database/Console/Migrations/MigrateMakeCommand.php create mode 100755 vendor/illuminate/database/Console/Migrations/RefreshCommand.php create mode 100755 vendor/illuminate/database/Console/Migrations/ResetCommand.php create mode 100755 vendor/illuminate/database/Console/Migrations/RollbackCommand.php create mode 100644 vendor/illuminate/database/Console/Migrations/StatusCommand.php create mode 100644 vendor/illuminate/database/Console/Migrations/TableGuesser.php create mode 100644 vendor/illuminate/database/Console/MonitorCommand.php create mode 100644 vendor/illuminate/database/Console/PruneCommand.php create mode 100644 vendor/illuminate/database/Console/Seeds/SeedCommand.php create mode 100644 vendor/illuminate/database/Console/Seeds/SeederMakeCommand.php create mode 100644 vendor/illuminate/database/Console/Seeds/WithoutModelEvents.php create mode 100644 vendor/illuminate/database/Console/Seeds/stubs/seeder.stub create mode 100644 vendor/illuminate/database/Console/ShowCommand.php create mode 100644 vendor/illuminate/database/Console/TableCommand.php create mode 100644 vendor/illuminate/database/Console/WipeCommand.php create mode 100644 vendor/illuminate/database/DBAL/TimestampType.php create mode 100755 vendor/illuminate/database/DatabaseManager.php create mode 100755 vendor/illuminate/database/DatabaseServiceProvider.php create mode 100755 vendor/illuminate/database/DatabaseTransactionRecord.php create mode 100755 vendor/illuminate/database/DatabaseTransactionsManager.php create mode 100644 vendor/illuminate/database/DeadlockException.php create mode 100644 vendor/illuminate/database/DetectsConcurrencyErrors.php create mode 100644 vendor/illuminate/database/DetectsLostConnections.php create mode 100644 vendor/illuminate/database/Eloquent/BroadcastableModelEventOccurred.php create mode 100644 vendor/illuminate/database/Eloquent/BroadcastsEvents.php create mode 100755 vendor/illuminate/database/Eloquent/Builder.php create mode 100644 vendor/illuminate/database/Eloquent/Casts/ArrayObject.php create mode 100644 vendor/illuminate/database/Eloquent/Casts/AsArrayObject.php create mode 100644 vendor/illuminate/database/Eloquent/Casts/AsCollection.php create mode 100644 vendor/illuminate/database/Eloquent/Casts/AsEncryptedArrayObject.php create mode 100644 vendor/illuminate/database/Eloquent/Casts/AsEncryptedCollection.php create mode 100644 vendor/illuminate/database/Eloquent/Casts/AsEnumArrayObject.php create mode 100644 vendor/illuminate/database/Eloquent/Casts/AsEnumCollection.php create mode 100644 vendor/illuminate/database/Eloquent/Casts/AsStringable.php create mode 100644 vendor/illuminate/database/Eloquent/Casts/Attribute.php create mode 100755 vendor/illuminate/database/Eloquent/Collection.php create mode 100644 vendor/illuminate/database/Eloquent/Concerns/GuardsAttributes.php create mode 100644 vendor/illuminate/database/Eloquent/Concerns/HasAttributes.php create mode 100644 vendor/illuminate/database/Eloquent/Concerns/HasEvents.php create mode 100644 vendor/illuminate/database/Eloquent/Concerns/HasGlobalScopes.php create mode 100644 vendor/illuminate/database/Eloquent/Concerns/HasRelationships.php create mode 100644 vendor/illuminate/database/Eloquent/Concerns/HasTimestamps.php create mode 100644 vendor/illuminate/database/Eloquent/Concerns/HasUlids.php create mode 100644 vendor/illuminate/database/Eloquent/Concerns/HasUuids.php create mode 100644 vendor/illuminate/database/Eloquent/Concerns/HidesAttributes.php create mode 100644 vendor/illuminate/database/Eloquent/Concerns/QueriesRelationships.php create mode 100644 vendor/illuminate/database/Eloquent/Factories/BelongsToManyRelationship.php create mode 100644 vendor/illuminate/database/Eloquent/Factories/BelongsToRelationship.php create mode 100644 vendor/illuminate/database/Eloquent/Factories/CrossJoinSequence.php create mode 100644 vendor/illuminate/database/Eloquent/Factories/Factory.php create mode 100644 vendor/illuminate/database/Eloquent/Factories/HasFactory.php create mode 100644 vendor/illuminate/database/Eloquent/Factories/Relationship.php create mode 100644 vendor/illuminate/database/Eloquent/Factories/Sequence.php create mode 100644 vendor/illuminate/database/Eloquent/HigherOrderBuilderProxy.php create mode 100644 vendor/illuminate/database/Eloquent/InvalidCastException.php create mode 100644 vendor/illuminate/database/Eloquent/JsonEncodingException.php create mode 100755 vendor/illuminate/database/Eloquent/MassAssignmentException.php create mode 100644 vendor/illuminate/database/Eloquent/MassPrunable.php create mode 100755 vendor/illuminate/database/Eloquent/MissingAttributeException.php create mode 100644 vendor/illuminate/database/Eloquent/Model.php create mode 100755 vendor/illuminate/database/Eloquent/ModelNotFoundException.php create mode 100644 vendor/illuminate/database/Eloquent/PendingHasThroughRelationship.php create mode 100644 vendor/illuminate/database/Eloquent/Prunable.php create mode 100644 vendor/illuminate/database/Eloquent/QueueEntityResolver.php create mode 100755 vendor/illuminate/database/Eloquent/RelationNotFoundException.php create mode 100755 vendor/illuminate/database/Eloquent/Relations/BelongsTo.php create mode 100755 vendor/illuminate/database/Eloquent/Relations/BelongsToMany.php create mode 100644 vendor/illuminate/database/Eloquent/Relations/Concerns/AsPivot.php create mode 100644 vendor/illuminate/database/Eloquent/Relations/Concerns/CanBeOneOfMany.php create mode 100644 vendor/illuminate/database/Eloquent/Relations/Concerns/ComparesRelatedModels.php create mode 100644 vendor/illuminate/database/Eloquent/Relations/Concerns/InteractsWithDictionary.php create mode 100644 vendor/illuminate/database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php create mode 100644 vendor/illuminate/database/Eloquent/Relations/Concerns/SupportsDefaultModels.php create mode 100755 vendor/illuminate/database/Eloquent/Relations/HasMany.php create mode 100644 vendor/illuminate/database/Eloquent/Relations/HasManyThrough.php create mode 100755 vendor/illuminate/database/Eloquent/Relations/HasOne.php create mode 100755 vendor/illuminate/database/Eloquent/Relations/HasOneOrMany.php create mode 100644 vendor/illuminate/database/Eloquent/Relations/HasOneThrough.php create mode 100755 vendor/illuminate/database/Eloquent/Relations/MorphMany.php create mode 100755 vendor/illuminate/database/Eloquent/Relations/MorphOne.php create mode 100755 vendor/illuminate/database/Eloquent/Relations/MorphOneOrMany.php create mode 100644 vendor/illuminate/database/Eloquent/Relations/MorphPivot.php create mode 100644 vendor/illuminate/database/Eloquent/Relations/MorphTo.php create mode 100644 vendor/illuminate/database/Eloquent/Relations/MorphToMany.php create mode 100755 vendor/illuminate/database/Eloquent/Relations/Pivot.php create mode 100755 vendor/illuminate/database/Eloquent/Relations/Relation.php create mode 100644 vendor/illuminate/database/Eloquent/Scope.php create mode 100644 vendor/illuminate/database/Eloquent/SoftDeletes.php create mode 100644 vendor/illuminate/database/Eloquent/SoftDeletingScope.php create mode 100644 vendor/illuminate/database/Events/ConnectionEstablished.php create mode 100644 vendor/illuminate/database/Events/ConnectionEvent.php create mode 100644 vendor/illuminate/database/Events/DatabaseBusy.php create mode 100644 vendor/illuminate/database/Events/DatabaseRefreshed.php create mode 100644 vendor/illuminate/database/Events/MigrationEnded.php create mode 100644 vendor/illuminate/database/Events/MigrationEvent.php create mode 100644 vendor/illuminate/database/Events/MigrationStarted.php create mode 100644 vendor/illuminate/database/Events/MigrationsEnded.php create mode 100644 vendor/illuminate/database/Events/MigrationsEvent.php create mode 100644 vendor/illuminate/database/Events/MigrationsStarted.php create mode 100644 vendor/illuminate/database/Events/ModelsPruned.php create mode 100644 vendor/illuminate/database/Events/NoPendingMigrations.php create mode 100644 vendor/illuminate/database/Events/QueryExecuted.php create mode 100644 vendor/illuminate/database/Events/SchemaDumped.php create mode 100644 vendor/illuminate/database/Events/SchemaLoaded.php create mode 100644 vendor/illuminate/database/Events/StatementPrepared.php create mode 100644 vendor/illuminate/database/Events/TransactionBeginning.php create mode 100644 vendor/illuminate/database/Events/TransactionCommitted.php create mode 100644 vendor/illuminate/database/Events/TransactionCommitting.php create mode 100644 vendor/illuminate/database/Events/TransactionRolledBack.php create mode 100755 vendor/illuminate/database/Grammar.php create mode 100644 vendor/illuminate/database/LICENSE.md create mode 100644 vendor/illuminate/database/LazyLoadingViolationException.php create mode 100644 vendor/illuminate/database/LostConnectionException.php create mode 100755 vendor/illuminate/database/MigrationServiceProvider.php create mode 100755 vendor/illuminate/database/Migrations/DatabaseMigrationRepository.php create mode 100755 vendor/illuminate/database/Migrations/Migration.php create mode 100755 vendor/illuminate/database/Migrations/MigrationCreator.php create mode 100755 vendor/illuminate/database/Migrations/MigrationRepositoryInterface.php create mode 100755 vendor/illuminate/database/Migrations/Migrator.php create mode 100755 vendor/illuminate/database/Migrations/stubs/migration.create.stub create mode 100755 vendor/illuminate/database/Migrations/stubs/migration.stub create mode 100755 vendor/illuminate/database/Migrations/stubs/migration.update.stub create mode 100644 vendor/illuminate/database/MultipleColumnsSelectedException.php create mode 100755 vendor/illuminate/database/MultipleRecordsFoundException.php create mode 100755 vendor/illuminate/database/MySqlConnection.php create mode 100644 vendor/illuminate/database/PDO/Concerns/ConnectsToDatabase.php create mode 100644 vendor/illuminate/database/PDO/Connection.php create mode 100644 vendor/illuminate/database/PDO/MySqlDriver.php create mode 100644 vendor/illuminate/database/PDO/PostgresDriver.php create mode 100644 vendor/illuminate/database/PDO/SQLiteDriver.php create mode 100644 vendor/illuminate/database/PDO/SqlServerConnection.php create mode 100644 vendor/illuminate/database/PDO/SqlServerDriver.php create mode 100755 vendor/illuminate/database/PostgresConnection.php create mode 100755 vendor/illuminate/database/Query/Builder.php create mode 100755 vendor/illuminate/database/Query/Expression.php create mode 100755 vendor/illuminate/database/Query/Grammars/Grammar.php create mode 100755 vendor/illuminate/database/Query/Grammars/MySqlGrammar.php create mode 100755 vendor/illuminate/database/Query/Grammars/PostgresGrammar.php create mode 100755 vendor/illuminate/database/Query/Grammars/SQLiteGrammar.php create mode 100755 vendor/illuminate/database/Query/Grammars/SqlServerGrammar.php create mode 100755 vendor/illuminate/database/Query/IndexHint.php create mode 100755 vendor/illuminate/database/Query/JoinClause.php create mode 100644 vendor/illuminate/database/Query/Processors/MySqlProcessor.php create mode 100755 vendor/illuminate/database/Query/Processors/PostgresProcessor.php create mode 100755 vendor/illuminate/database/Query/Processors/Processor.php create mode 100644 vendor/illuminate/database/Query/Processors/SQLiteProcessor.php create mode 100755 vendor/illuminate/database/Query/Processors/SqlServerProcessor.php create mode 100644 vendor/illuminate/database/QueryException.php create mode 100755 vendor/illuminate/database/README.md create mode 100755 vendor/illuminate/database/RecordsNotFoundException.php create mode 100755 vendor/illuminate/database/SQLiteConnection.php create mode 100644 vendor/illuminate/database/SQLiteDatabaseDoesNotExistException.php create mode 100755 vendor/illuminate/database/Schema/Blueprint.php create mode 100755 vendor/illuminate/database/Schema/Builder.php create mode 100644 vendor/illuminate/database/Schema/ColumnDefinition.php create mode 100644 vendor/illuminate/database/Schema/ForeignIdColumnDefinition.php create mode 100644 vendor/illuminate/database/Schema/ForeignKeyDefinition.php create mode 100644 vendor/illuminate/database/Schema/Grammars/ChangeColumn.php create mode 100755 vendor/illuminate/database/Schema/Grammars/Grammar.php create mode 100755 vendor/illuminate/database/Schema/Grammars/MySqlGrammar.php create mode 100755 vendor/illuminate/database/Schema/Grammars/PostgresGrammar.php create mode 100644 vendor/illuminate/database/Schema/Grammars/RenameColumn.php create mode 100755 vendor/illuminate/database/Schema/Grammars/SQLiteGrammar.php create mode 100755 vendor/illuminate/database/Schema/Grammars/SqlServerGrammar.php create mode 100644 vendor/illuminate/database/Schema/IndexDefinition.php create mode 100755 vendor/illuminate/database/Schema/MySqlBuilder.php create mode 100644 vendor/illuminate/database/Schema/MySqlSchemaState.php create mode 100755 vendor/illuminate/database/Schema/PostgresBuilder.php create mode 100644 vendor/illuminate/database/Schema/PostgresSchemaState.php create mode 100644 vendor/illuminate/database/Schema/SQLiteBuilder.php create mode 100644 vendor/illuminate/database/Schema/SchemaState.php create mode 100644 vendor/illuminate/database/Schema/SqlServerBuilder.php create mode 100644 vendor/illuminate/database/Schema/SqliteSchemaState.php create mode 100755 vendor/illuminate/database/Seeder.php create mode 100755 vendor/illuminate/database/SqlServerConnection.php create mode 100644 vendor/illuminate/database/composer.json create mode 100644 vendor/illuminate/events/CallQueuedListener.php create mode 100755 vendor/illuminate/events/Dispatcher.php create mode 100755 vendor/illuminate/events/EventServiceProvider.php create mode 100644 vendor/illuminate/events/InvokeQueuedClosure.php create mode 100644 vendor/illuminate/events/LICENSE.md create mode 100644 vendor/illuminate/events/NullDispatcher.php create mode 100644 vendor/illuminate/events/QueuedClosure.php create mode 100755 vendor/illuminate/events/composer.json create mode 100644 vendor/illuminate/events/functions.php create mode 100644 vendor/illuminate/macroable/LICENSE.md create mode 100644 vendor/illuminate/macroable/Traits/Macroable.php create mode 100644 vendor/illuminate/macroable/composer.json create mode 100644 vendor/illuminate/pagination/AbstractCursorPaginator.php create mode 100644 vendor/illuminate/pagination/AbstractPaginator.php create mode 100644 vendor/illuminate/pagination/Cursor.php create mode 100644 vendor/illuminate/pagination/CursorPaginator.php create mode 100644 vendor/illuminate/pagination/LICENSE.md create mode 100644 vendor/illuminate/pagination/LengthAwarePaginator.php create mode 100755 vendor/illuminate/pagination/PaginationServiceProvider.php create mode 100644 vendor/illuminate/pagination/PaginationState.php create mode 100644 vendor/illuminate/pagination/Paginator.php create mode 100644 vendor/illuminate/pagination/UrlWindow.php create mode 100755 vendor/illuminate/pagination/composer.json create mode 100644 vendor/illuminate/pagination/resources/views/bootstrap-4.blade.php create mode 100644 vendor/illuminate/pagination/resources/views/bootstrap-5.blade.php create mode 100644 vendor/illuminate/pagination/resources/views/default.blade.php create mode 100644 vendor/illuminate/pagination/resources/views/semantic-ui.blade.php create mode 100644 vendor/illuminate/pagination/resources/views/simple-bootstrap-4.blade.php create mode 100644 vendor/illuminate/pagination/resources/views/simple-bootstrap-5.blade.php create mode 100644 vendor/illuminate/pagination/resources/views/simple-default.blade.php create mode 100644 vendor/illuminate/pagination/resources/views/simple-tailwind.blade.php create mode 100644 vendor/illuminate/pagination/resources/views/tailwind.blade.php create mode 100644 vendor/illuminate/pipeline/Hub.php create mode 100644 vendor/illuminate/pipeline/LICENSE.md create mode 100644 vendor/illuminate/pipeline/Pipeline.php create mode 100644 vendor/illuminate/pipeline/PipelineServiceProvider.php create mode 100644 vendor/illuminate/pipeline/composer.json create mode 100644 vendor/illuminate/redis/Connections/Connection.php create mode 100644 vendor/illuminate/redis/Connections/PacksPhpRedisValues.php create mode 100644 vendor/illuminate/redis/Connections/PhpRedisClusterConnection.php create mode 100644 vendor/illuminate/redis/Connections/PhpRedisConnection.php create mode 100644 vendor/illuminate/redis/Connections/PredisClusterConnection.php create mode 100644 vendor/illuminate/redis/Connections/PredisConnection.php create mode 100644 vendor/illuminate/redis/Connectors/PhpRedisConnector.php create mode 100644 vendor/illuminate/redis/Connectors/PredisConnector.php create mode 100644 vendor/illuminate/redis/Events/CommandExecuted.php create mode 100644 vendor/illuminate/redis/LICENSE.md create mode 100644 vendor/illuminate/redis/Limiters/ConcurrencyLimiter.php create mode 100644 vendor/illuminate/redis/Limiters/ConcurrencyLimiterBuilder.php create mode 100644 vendor/illuminate/redis/Limiters/DurationLimiter.php create mode 100644 vendor/illuminate/redis/Limiters/DurationLimiterBuilder.php create mode 100644 vendor/illuminate/redis/RedisManager.php create mode 100755 vendor/illuminate/redis/RedisServiceProvider.php create mode 100755 vendor/illuminate/redis/composer.json create mode 100644 vendor/illuminate/support/AggregateServiceProvider.php create mode 100644 vendor/illuminate/support/Benchmark.php create mode 100644 vendor/illuminate/support/Carbon.php create mode 100644 vendor/illuminate/support/Composer.php create mode 100644 vendor/illuminate/support/ConfigurationUrlParser.php create mode 100644 vendor/illuminate/support/DateFactory.php create mode 100644 vendor/illuminate/support/Env.php create mode 100644 vendor/illuminate/support/Exceptions/MathException.php create mode 100755 vendor/illuminate/support/Facades/App.php create mode 100755 vendor/illuminate/support/Facades/Artisan.php create mode 100755 vendor/illuminate/support/Facades/Auth.php create mode 100755 vendor/illuminate/support/Facades/Blade.php create mode 100644 vendor/illuminate/support/Facades/Broadcast.php create mode 100644 vendor/illuminate/support/Facades/Bus.php create mode 100755 vendor/illuminate/support/Facades/Cache.php create mode 100755 vendor/illuminate/support/Facades/Config.php create mode 100755 vendor/illuminate/support/Facades/Cookie.php create mode 100755 vendor/illuminate/support/Facades/Crypt.php create mode 100755 vendor/illuminate/support/Facades/DB.php create mode 100644 vendor/illuminate/support/Facades/Date.php create mode 100755 vendor/illuminate/support/Facades/Event.php create mode 100755 vendor/illuminate/support/Facades/Facade.php create mode 100755 vendor/illuminate/support/Facades/File.php create mode 100644 vendor/illuminate/support/Facades/Gate.php create mode 100755 vendor/illuminate/support/Facades/Hash.php create mode 100644 vendor/illuminate/support/Facades/Http.php create mode 100755 vendor/illuminate/support/Facades/Lang.php create mode 100755 vendor/illuminate/support/Facades/Log.php create mode 100755 vendor/illuminate/support/Facades/Mail.php create mode 100644 vendor/illuminate/support/Facades/Notification.php create mode 100644 vendor/illuminate/support/Facades/ParallelTesting.php create mode 100755 vendor/illuminate/support/Facades/Password.php create mode 100755 vendor/illuminate/support/Facades/Queue.php create mode 100644 vendor/illuminate/support/Facades/RateLimiter.php create mode 100755 vendor/illuminate/support/Facades/Redirect.php create mode 100755 vendor/illuminate/support/Facades/Redis.php create mode 100755 vendor/illuminate/support/Facades/Request.php create mode 100755 vendor/illuminate/support/Facades/Response.php create mode 100755 vendor/illuminate/support/Facades/Route.php create mode 100755 vendor/illuminate/support/Facades/Schema.php create mode 100755 vendor/illuminate/support/Facades/Session.php create mode 100644 vendor/illuminate/support/Facades/Storage.php create mode 100755 vendor/illuminate/support/Facades/URL.php create mode 100755 vendor/illuminate/support/Facades/Validator.php create mode 100755 vendor/illuminate/support/Facades/View.php create mode 100644 vendor/illuminate/support/Facades/Vite.php create mode 100755 vendor/illuminate/support/Fluent.php create mode 100644 vendor/illuminate/support/HigherOrderTapProxy.php create mode 100644 vendor/illuminate/support/HtmlString.php create mode 100644 vendor/illuminate/support/InteractsWithTime.php create mode 100644 vendor/illuminate/support/Js.php create mode 100644 vendor/illuminate/support/LICENSE.md create mode 100644 vendor/illuminate/support/Lottery.php create mode 100755 vendor/illuminate/support/Manager.php create mode 100755 vendor/illuminate/support/MessageBag.php create mode 100644 vendor/illuminate/support/MultipleInstanceManager.php create mode 100755 vendor/illuminate/support/NamespacedItemResolver.php create mode 100644 vendor/illuminate/support/Optional.php create mode 100755 vendor/illuminate/support/Pluralizer.php create mode 100644 vendor/illuminate/support/ProcessUtils.php create mode 100644 vendor/illuminate/support/Reflector.php create mode 100755 vendor/illuminate/support/ServiceProvider.php create mode 100644 vendor/illuminate/support/Str.php create mode 100644 vendor/illuminate/support/Stringable.php create mode 100644 vendor/illuminate/support/Testing/Fakes/BatchFake.php create mode 100644 vendor/illuminate/support/Testing/Fakes/BatchRepositoryFake.php create mode 100644 vendor/illuminate/support/Testing/Fakes/BusFake.php create mode 100644 vendor/illuminate/support/Testing/Fakes/EventFake.php create mode 100644 vendor/illuminate/support/Testing/Fakes/MailFake.php create mode 100644 vendor/illuminate/support/Testing/Fakes/NotificationFake.php create mode 100644 vendor/illuminate/support/Testing/Fakes/PendingBatchFake.php create mode 100644 vendor/illuminate/support/Testing/Fakes/PendingChainFake.php create mode 100644 vendor/illuminate/support/Testing/Fakes/PendingMailFake.php create mode 100644 vendor/illuminate/support/Testing/Fakes/QueueFake.php create mode 100644 vendor/illuminate/support/Timebox.php create mode 100644 vendor/illuminate/support/Traits/CapsuleManagerTrait.php create mode 100644 vendor/illuminate/support/Traits/ForwardsCalls.php create mode 100644 vendor/illuminate/support/Traits/Localizable.php create mode 100644 vendor/illuminate/support/Traits/ReflectsClosures.php create mode 100644 vendor/illuminate/support/Traits/Tappable.php create mode 100644 vendor/illuminate/support/ValidatedInput.php create mode 100644 vendor/illuminate/support/ViewErrorBag.php create mode 100644 vendor/illuminate/support/composer.json create mode 100755 vendor/illuminate/support/helpers.php create mode 100644 vendor/jasongrimes/paginator/.gitignore create mode 100644 vendor/jasongrimes/paginator/.travis.yml create mode 100644 vendor/jasongrimes/paginator/LICENSE create mode 100644 vendor/jasongrimes/paginator/README.md create mode 100644 vendor/jasongrimes/paginator/composer.json create mode 100644 vendor/jasongrimes/paginator/examples/pager.phtml create mode 100644 vendor/jasongrimes/paginator/examples/pager.twig create mode 100644 vendor/jasongrimes/paginator/examples/pagerSmall.phtml create mode 100644 vendor/jasongrimes/paginator/examples/pagerSmall.twig create mode 100644 vendor/jasongrimes/paginator/examples/screenshot-default-first.png create mode 100644 vendor/jasongrimes/paginator/examples/screenshot-default-last.png create mode 100644 vendor/jasongrimes/paginator/examples/screenshot-default-mid.png create mode 100644 vendor/jasongrimes/paginator/examples/screenshot-small-first.png create mode 100644 vendor/jasongrimes/paginator/examples/screenshot-small-last.png create mode 100644 vendor/jasongrimes/paginator/examples/screenshot-small-mid-open.png create mode 100644 vendor/jasongrimes/paginator/examples/screenshot-small-mid.png create mode 100644 vendor/jasongrimes/paginator/phpunit.xml.dist create mode 100644 vendor/jasongrimes/paginator/src/JasonGrimes/Paginator.php create mode 100644 vendor/jasongrimes/paginator/tests/JasonGrimes/Tests/PaginatorTest.php create mode 100644 vendor/monolog/monolog/CHANGELOG.md create mode 100644 vendor/monolog/monolog/LICENSE create mode 100644 vendor/monolog/monolog/README.md create mode 100644 vendor/monolog/monolog/UPGRADE.md create mode 100644 vendor/monolog/monolog/composer.json create mode 100644 vendor/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/DateTimeImmutable.php create mode 100644 vendor/monolog/monolog/src/Monolog/ErrorHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/Handler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/LogRecord.php create mode 100644 vendor/monolog/monolog/src/Monolog/Logger.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Registry.php create mode 100644 vendor/monolog/monolog/src/Monolog/ResettableInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/SignalHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Test/TestCase.php create mode 100644 vendor/monolog/monolog/src/Monolog/Utils.php create mode 100644 vendor/nesbot/carbon/.phpstorm.meta.php create mode 100644 vendor/nesbot/carbon/LICENSE create mode 100755 vendor/nesbot/carbon/bin/carbon create mode 100644 vendor/nesbot/carbon/bin/carbon.bat create mode 100644 vendor/nesbot/carbon/composer.json create mode 100644 vendor/nesbot/carbon/extension.neon create mode 100644 vendor/nesbot/carbon/lazy/Carbon/MessageFormatter/MessageFormatterMapperStrongType.php create mode 100644 vendor/nesbot/carbon/lazy/Carbon/MessageFormatter/MessageFormatterMapperWeakType.php create mode 100644 vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroBuiltin.php create mode 100644 vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroStatic.php create mode 100644 vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroStrongType.php create mode 100644 vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroWeakType.php create mode 100644 vendor/nesbot/carbon/lazy/Carbon/TranslatorStrongType.php create mode 100644 vendor/nesbot/carbon/lazy/Carbon/TranslatorWeakType.php create mode 100644 vendor/nesbot/carbon/readme.md create mode 100644 vendor/nesbot/carbon/sponsors.php create mode 100644 vendor/nesbot/carbon/src/Carbon/AbstractTranslator.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Carbon.php create mode 100644 vendor/nesbot/carbon/src/Carbon/CarbonConverterInterface.php create mode 100644 vendor/nesbot/carbon/src/Carbon/CarbonImmutable.php create mode 100644 vendor/nesbot/carbon/src/Carbon/CarbonInterface.php create mode 100644 vendor/nesbot/carbon/src/Carbon/CarbonInterval.php create mode 100644 vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php create mode 100644 vendor/nesbot/carbon/src/Carbon/CarbonPeriodImmutable.php create mode 100644 vendor/nesbot/carbon/src/Carbon/CarbonTimeZone.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Cli/Invoker.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/BadComparisonUnitException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentConstructorException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentSetterException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/BadMethodCallException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/EndLessPeriodException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/Exception.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/ImmutableException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidArgumentException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidCastException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidFormatException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidIntervalException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodDateException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodParameterException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTimeZoneException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTypeException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/NotACarbonClassException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/NotAPeriodException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/NotLocaleAwareException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/OutOfRangeException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/ParseErrorException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/RuntimeException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/UnitException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/UnitNotConfiguredException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownGetterException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownMethodException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownSetterException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownUnitException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/UnreachableException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Factory.php create mode 100644 vendor/nesbot/carbon/src/Carbon/FactoryImmutable.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/aa.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/aa_DJ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/aa_ER.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/aa_ER@saaho.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/aa_ET.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/af.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/af_NA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/af_ZA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/agq.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/agr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/agr_PE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ak.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ak_GH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/am.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/am_ET.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/an.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/an_ES.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/anp.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/anp_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_AE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_BH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_DJ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_DZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_EG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_EH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_ER.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_IL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_IQ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_JO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_KM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_KW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_LB.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_LY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_MA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_MR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_OM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_PS.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_QA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_SA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_SD.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_SO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_SS.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_SY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_TD.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_TN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_YE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/as.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/as_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/asa.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ast.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ast_ES.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ayc.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ayc_PE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/az.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/az_AZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/az_Cyrl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/az_IR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/az_Latn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bas.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/be.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/be_BY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/be_BY@latin.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bem.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bem_ZM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ber.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ber_DZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ber_MA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bez.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bg.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bg_BG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bhb.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bhb_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bho.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bho_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bi.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bi_VU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bm.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bn_BD.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bn_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bo_CN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bo_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/br.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/br_FR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/brx.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/brx_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bs.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bs_Cyrl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bs_Latn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/byn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/byn_ER.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ca.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ca_AD.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ca_ES.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ca_ES_Valencia.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ca_FR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ca_IT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ccp.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ccp_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ce.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ce_RU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/cgg.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/chr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/chr_US.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ckb.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/cmn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/cmn_TW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/crh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/crh_UA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/cs.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/cs_CZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/csb.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/csb_PL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/cu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/cv.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/cv_RU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/cy.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/cy_GB.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/da.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/da_DK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/da_GL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/dav.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/de.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/de_AT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/de_BE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/de_CH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/de_DE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/de_IT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/de_LI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/de_LU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/dje.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/doi.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/doi_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/dsb.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/dsb_DE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/dua.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/dv.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/dv_MV.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/dyo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/dz.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/dz_BT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ebu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ee.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ee_TG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/el.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/el_CY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/el_GR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_001.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_150.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_AG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_AI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_AS.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_AT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_AU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_BB.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_BE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_BI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_BM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_BS.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_BW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_BZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_CA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_CC.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_CH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_CK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_CM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_CX.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_CY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_DE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_DG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_DK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_DM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_ER.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_FI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_FJ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_FK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_FM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_GB.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_GD.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_GG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_GH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_GI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_GM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_GU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_GY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_HK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_IE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_IL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_IM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_IO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_ISO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_JE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_JM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_KE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_KI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_KN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_KY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_LC.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_LR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_LS.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_MG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_MH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_MO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_MP.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_MS.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_MT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_MU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_MW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_MY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_NA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_NF.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_NG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_NL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_NR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_NU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_NZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_PG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_PH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_PK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_PN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_PR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_PW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_RW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_SB.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_SC.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_SD.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_SE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_SG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_SH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_SI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_SL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_SS.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_SX.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_SZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_TC.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_TK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_TO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_TT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_TV.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_TZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_UG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_UM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_US.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_US_Posix.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_VC.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_VG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_VI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_VU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_WS.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_ZA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_ZM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en_ZW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/eo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_419.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_AR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_BO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_BR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_BZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_CL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_CO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_CR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_CU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_DO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_EA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_EC.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_ES.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_GQ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_GT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_HN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_IC.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_MX.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_NI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_PA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_PE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_PH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_PR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_PY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_SV.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_US.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_UY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es_VE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/et.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/et_EE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/eu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/eu_ES.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ewo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fa.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fa_AF.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fa_IR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ff.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ff_CM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ff_GN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ff_MR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ff_SN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fi.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fi_FI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fil.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fil_PH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fo_DK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fo_FO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_BE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_BF.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_BI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_BJ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_BL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_CA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_CD.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_CF.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_CG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_CH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_CI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_CM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_DJ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_DZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_FR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_GA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_GF.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_GN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_GP.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_GQ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_HT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_KM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_LU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_MA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_MC.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_MF.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_MG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_ML.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_MQ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_MR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_MU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_NC.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_NE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_PF.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_PM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_RE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_RW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_SC.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_SN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_SY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_TD.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_TG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_TN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_VU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_WF.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr_YT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fur.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fur_IT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fy.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fy_DE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fy_NL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ga.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ga_IE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gd.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gd_GB.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gez.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gez_ER.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gez_ET.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gl_ES.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gom.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gom_Latn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gsw.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gsw_CH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gsw_FR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gsw_LI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gu_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/guz.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gv.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gv_GB.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ha.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ha_GH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ha_NE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ha_NG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hak.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hak_TW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/haw.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/he.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/he_IL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hi.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hi_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hif.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hif_FJ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hne.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hne_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hr_BA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hr_HR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hsb.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hsb_DE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ht.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ht_HT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hu_HU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hy.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hy_AM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/i18n.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ia.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ia_FR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/id.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/id_ID.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ig.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ig_NG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ii.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ik.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ik_CA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/in.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/is.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/is_IS.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/it.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/it_CH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/it_IT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/it_SM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/it_VA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/iu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/iu_CA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/iw.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ja.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ja_JP.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/jgo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/jmc.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/jv.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ka.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ka_GE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kab.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kab_DZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kam.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kde.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kea.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/khq.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ki.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kk.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kk_KZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kkj.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kl_GL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kln.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/km.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/km_KH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kn_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ko.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ko_KP.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ko_KR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kok.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kok_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ks.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ks_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ks_IN@devanagari.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ksb.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ksf.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ksh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ku.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ku_TR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kw.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kw_GB.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ky.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ky_KG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lag.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lb.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lb_LU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lg.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lg_UG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/li.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/li_NL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lij.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lij_IT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lkt.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ln.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ln_AO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ln_CD.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ln_CF.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ln_CG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lo_LA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lrc.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lrc_IQ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lt.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lt_LT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/luo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/luy.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lv.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lv_LV.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lzh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lzh_TW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mag.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mag_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mai.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mai_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mas.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mas_TZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mer.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mfe.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mfe_MU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mg.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mg_MG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mgh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mgo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mhr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mhr_RU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mi.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mi_NZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/miq.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/miq_NI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mjw.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mjw_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mk.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mk_MK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ml.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ml_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mn_MN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mni.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mni_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mr_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ms.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ms_BN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ms_MY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ms_SG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mt.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mt_MT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mua.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/my.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/my_MM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mzn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nan.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nan_TW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nan_TW@latin.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/naq.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nb.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nb_NO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nb_SJ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nd.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nds.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nds_DE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nds_NL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ne.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ne_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ne_NP.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nhn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nhn_MX.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/niu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/niu_NU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nl_AW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nl_BE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nl_BQ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nl_CW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nl_NL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nl_SR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nl_SX.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nmg.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nn_NO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nnh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/no.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nr_ZA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nso.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nso_ZA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nus.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nyn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/oc.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/oc_FR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/om.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/om_ET.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/om_KE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/or.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/or_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/os.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/os_RU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pa.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pa_Arab.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pa_Guru.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pa_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pa_PK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pap.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pap_AW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pap_CW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pl_PL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/prg.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ps.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ps_AF.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_AO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_CH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_CV.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_GQ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_GW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_LU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_MO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_MZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_PT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_ST.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_TL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/qu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/qu_BO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/qu_EC.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/quz.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/quz_PE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/raj.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/raj_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/rm.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/rn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ro.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ro_MD.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ro_RO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/rof.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ru.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ru_BY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ru_KG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ru_KZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ru_MD.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ru_RU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ru_UA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/rw.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/rw_RW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/rwk.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sa.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sa_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sah.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sah_RU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/saq.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sat.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sat_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sbp.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sc.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sc_IT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sd.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sd_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sd_IN@devanagari.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/se.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/se_FI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/se_NO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/se_SE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/seh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ses.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sg.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sgs.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sgs_LT.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/shi.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/shi_Latn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/shi_Tfng.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/shn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/shn_MM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/shs.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/shs_CA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/si.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/si_LK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sid.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sid_ET.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sk.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sk_SK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sl_SI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sm.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sm_WS.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/smn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/so.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/so_DJ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/so_ET.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/so_KE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/so_SO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sq.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sq_AL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sq_MK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sq_XK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_BA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_XK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_BA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_XK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_ME.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_RS.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_RS@latin.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ss.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ss_ZA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/st.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/st_ZA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sv.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sv_AX.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sv_FI.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sv_SE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sw.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sw_CD.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sw_KE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sw_TZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sw_UG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/szl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/szl_PL.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ta.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ta_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ta_LK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ta_MY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ta_SG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tcy.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tcy_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/te.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/te_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/teo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/teo_KE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tet.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tg.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tg_TJ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/th.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/th_TH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/the.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/the_NP.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ti.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ti_ER.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ti_ET.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tig.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tig_ER.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tk.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tk_TM.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tl_PH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tlh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tn_ZA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/to.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/to_TO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tpi.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tpi_PG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tr_CY.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tr_TR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ts.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ts_ZA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tt.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tt_RU.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tt_RU@iqtelif.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/twq.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tzl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tzm.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tzm_Latn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ug.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ug_CN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/uk.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/uk_UA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/unm.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/unm_US.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ur.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ur_IN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ur_PK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/uz.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/uz_Arab.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/uz_Cyrl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/uz_Latn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ@cyrillic.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/vai.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/vai_Latn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/vai_Vaii.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ve.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ve_ZA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/vi.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/vi_VN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/vo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/vun.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/wa.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/wa_BE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/wae.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/wae_CH.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/wal.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/wal_ET.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/wo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/wo_SN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/xh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/xh_ZA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/xog.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/yav.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/yi.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/yi_US.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/yo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/yo_BJ.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/yo_NG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/yue.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/yue_HK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/yue_Hans.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/yue_Hant.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/yuw.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/yuw_PG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zgh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_CN.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_HK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_HK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_MO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_SG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_HK.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_MO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_TW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_MO.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_SG.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_YUE.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zu_ZA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Language.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php create mode 100644 vendor/nesbot/carbon/src/Carbon/List/languages.php create mode 100644 vendor/nesbot/carbon/src/Carbon/List/regions.php create mode 100644 vendor/nesbot/carbon/src/Carbon/MessageFormatter/MessageFormatterMapper.php create mode 100644 vendor/nesbot/carbon/src/Carbon/PHPStan/AbstractMacro.php create mode 100644 vendor/nesbot/carbon/src/Carbon/PHPStan/Macro.php create mode 100644 vendor/nesbot/carbon/src/Carbon/PHPStan/MacroExtension.php create mode 100644 vendor/nesbot/carbon/src/Carbon/PHPStan/MacroScanner.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Boundaries.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Cast.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Comparison.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Converter.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Creator.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Date.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedProperties.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Difference.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/IntervalRounding.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/IntervalStep.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Localization.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Macro.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/MagicParameter.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Mixin.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Modifiers.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Mutability.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/ObjectInitialisation.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Options.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Rounding.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Serialization.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Test.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Timestamp.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/ToStringFormat.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Units.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Traits/Week.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Translator.php create mode 100644 vendor/nesbot/carbon/src/Carbon/TranslatorImmutable.php create mode 100644 vendor/nesbot/carbon/src/Carbon/TranslatorStrongTypeInterface.php create mode 100644 vendor/nikic/fast-route/.gitignore create mode 100644 vendor/nikic/fast-route/.hhconfig create mode 100644 vendor/nikic/fast-route/.travis.yml create mode 100644 vendor/nikic/fast-route/FastRoute.hhi create mode 100644 vendor/nikic/fast-route/LICENSE create mode 100644 vendor/nikic/fast-route/README.md create mode 100644 vendor/nikic/fast-route/composer.json create mode 100644 vendor/nikic/fast-route/phpunit.xml create mode 100644 vendor/nikic/fast-route/psalm.xml create mode 100644 vendor/nikic/fast-route/src/BadRouteException.php create mode 100644 vendor/nikic/fast-route/src/DataGenerator.php create mode 100644 vendor/nikic/fast-route/src/DataGenerator/CharCountBased.php create mode 100644 vendor/nikic/fast-route/src/DataGenerator/GroupCountBased.php create mode 100644 vendor/nikic/fast-route/src/DataGenerator/GroupPosBased.php create mode 100644 vendor/nikic/fast-route/src/DataGenerator/MarkBased.php create mode 100644 vendor/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php create mode 100644 vendor/nikic/fast-route/src/Dispatcher.php create mode 100644 vendor/nikic/fast-route/src/Dispatcher/CharCountBased.php create mode 100644 vendor/nikic/fast-route/src/Dispatcher/GroupCountBased.php create mode 100644 vendor/nikic/fast-route/src/Dispatcher/GroupPosBased.php create mode 100644 vendor/nikic/fast-route/src/Dispatcher/MarkBased.php create mode 100644 vendor/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php create mode 100644 vendor/nikic/fast-route/src/Route.php create mode 100644 vendor/nikic/fast-route/src/RouteCollector.php create mode 100644 vendor/nikic/fast-route/src/RouteParser.php create mode 100644 vendor/nikic/fast-route/src/RouteParser/Std.php create mode 100644 vendor/nikic/fast-route/src/bootstrap.php create mode 100644 vendor/nikic/fast-route/src/functions.php create mode 100644 vendor/nikic/fast-route/test/Dispatcher/CharCountBasedTest.php create mode 100644 vendor/nikic/fast-route/test/Dispatcher/DispatcherTest.php create mode 100644 vendor/nikic/fast-route/test/Dispatcher/GroupCountBasedTest.php create mode 100644 vendor/nikic/fast-route/test/Dispatcher/GroupPosBasedTest.php create mode 100644 vendor/nikic/fast-route/test/Dispatcher/MarkBasedTest.php create mode 100644 vendor/nikic/fast-route/test/HackTypechecker/HackTypecheckerTest.php create mode 100644 vendor/nikic/fast-route/test/HackTypechecker/fixtures/all_options.php create mode 100644 vendor/nikic/fast-route/test/HackTypechecker/fixtures/empty_options.php create mode 100644 vendor/nikic/fast-route/test/HackTypechecker/fixtures/no_options.php create mode 100644 vendor/nikic/fast-route/test/RouteCollectorTest.php create mode 100644 vendor/nikic/fast-route/test/RouteParser/StdTest.php create mode 100644 vendor/nikic/fast-route/test/bootstrap.php create mode 100644 vendor/phpoption/phpoption/LICENSE create mode 100644 vendor/phpoption/phpoption/composer.json create mode 100644 vendor/phpoption/phpoption/src/PhpOption/LazyOption.php create mode 100644 vendor/phpoption/phpoption/src/PhpOption/None.php create mode 100644 vendor/phpoption/phpoption/src/PhpOption/Option.php create mode 100644 vendor/phpoption/phpoption/src/PhpOption/Some.php create mode 100644 vendor/psr/cache/CHANGELOG.md create mode 100644 vendor/psr/cache/LICENSE.txt create mode 100644 vendor/psr/cache/README.md create mode 100644 vendor/psr/cache/composer.json create mode 100644 vendor/psr/cache/src/CacheException.php create mode 100644 vendor/psr/cache/src/CacheItemInterface.php create mode 100644 vendor/psr/cache/src/CacheItemPoolInterface.php create mode 100644 vendor/psr/cache/src/InvalidArgumentException.php create mode 100644 vendor/psr/clock/CHANGELOG.md create mode 100644 vendor/psr/clock/LICENSE create mode 100644 vendor/psr/clock/README.md create mode 100644 vendor/psr/clock/composer.json create mode 100644 vendor/psr/clock/src/ClockInterface.php create mode 100644 vendor/psr/container/.gitignore create mode 100644 vendor/psr/container/LICENSE create mode 100644 vendor/psr/container/README.md create mode 100644 vendor/psr/container/composer.json create mode 100644 vendor/psr/container/src/ContainerExceptionInterface.php create mode 100644 vendor/psr/container/src/ContainerInterface.php create mode 100644 vendor/psr/container/src/NotFoundExceptionInterface.php create mode 100644 vendor/psr/http-client/CHANGELOG.md create mode 100644 vendor/psr/http-client/LICENSE create mode 100644 vendor/psr/http-client/README.md create mode 100644 vendor/psr/http-client/composer.json create mode 100644 vendor/psr/http-client/src/ClientExceptionInterface.php create mode 100644 vendor/psr/http-client/src/ClientInterface.php create mode 100644 vendor/psr/http-client/src/NetworkExceptionInterface.php create mode 100644 vendor/psr/http-client/src/RequestExceptionInterface.php create mode 100644 vendor/psr/http-factory/LICENSE create mode 100644 vendor/psr/http-factory/README.md create mode 100644 vendor/psr/http-factory/composer.json create mode 100644 vendor/psr/http-factory/src/RequestFactoryInterface.php create mode 100644 vendor/psr/http-factory/src/ResponseFactoryInterface.php create mode 100644 vendor/psr/http-factory/src/ServerRequestFactoryInterface.php create mode 100644 vendor/psr/http-factory/src/StreamFactoryInterface.php create mode 100644 vendor/psr/http-factory/src/UploadedFileFactoryInterface.php create mode 100644 vendor/psr/http-factory/src/UriFactoryInterface.php create mode 100644 vendor/psr/http-message/CHANGELOG.md create mode 100644 vendor/psr/http-message/LICENSE create mode 100644 vendor/psr/http-message/README.md create mode 100644 vendor/psr/http-message/composer.json create mode 100644 vendor/psr/http-message/docs/PSR7-Interfaces.md create mode 100644 vendor/psr/http-message/docs/PSR7-Usage.md create mode 100644 vendor/psr/http-message/src/MessageInterface.php create mode 100644 vendor/psr/http-message/src/RequestInterface.php create mode 100644 vendor/psr/http-message/src/ResponseInterface.php create mode 100644 vendor/psr/http-message/src/ServerRequestInterface.php create mode 100644 vendor/psr/http-message/src/StreamInterface.php create mode 100644 vendor/psr/http-message/src/UploadedFileInterface.php create mode 100644 vendor/psr/http-message/src/UriInterface.php create mode 100644 vendor/psr/log/LICENSE create mode 100644 vendor/psr/log/README.md create mode 100644 vendor/psr/log/composer.json create mode 100644 vendor/psr/log/src/AbstractLogger.php create mode 100644 vendor/psr/log/src/InvalidArgumentException.php create mode 100644 vendor/psr/log/src/LogLevel.php create mode 100644 vendor/psr/log/src/LoggerAwareInterface.php create mode 100644 vendor/psr/log/src/LoggerAwareTrait.php create mode 100644 vendor/psr/log/src/LoggerInterface.php create mode 100644 vendor/psr/log/src/LoggerTrait.php create mode 100644 vendor/psr/log/src/NullLogger.php create mode 100644 vendor/psr/simple-cache/.editorconfig create mode 100644 vendor/psr/simple-cache/LICENSE.md create mode 100644 vendor/psr/simple-cache/README.md create mode 100644 vendor/psr/simple-cache/composer.json create mode 100644 vendor/psr/simple-cache/src/CacheException.php create mode 100644 vendor/psr/simple-cache/src/CacheInterface.php create mode 100644 vendor/psr/simple-cache/src/InvalidArgumentException.php create mode 100644 vendor/ralouphie/getallheaders/LICENSE create mode 100644 vendor/ralouphie/getallheaders/README.md create mode 100644 vendor/ralouphie/getallheaders/composer.json create mode 100644 vendor/ralouphie/getallheaders/src/getallheaders.php create mode 100644 vendor/respect/stringifier/LICENSE.md create mode 100644 vendor/respect/stringifier/README.md create mode 100644 vendor/respect/stringifier/composer.json create mode 100644 vendor/respect/stringifier/src/Quoter.php create mode 100644 vendor/respect/stringifier/src/Quoters/CodeQuoter.php create mode 100644 vendor/respect/stringifier/src/Stringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/ArrayStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/BoolStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/ClusterStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/DateTimeStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/InfiniteStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/JsonParsableStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/JsonSerializableStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/NanStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/NullStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/ObjectStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/ResourceStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/StringableObjectStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/ThrowableStringifier.php create mode 100644 vendor/respect/stringifier/src/Stringifiers/TraversableStringifier.php create mode 100644 vendor/respect/stringifier/src/stringify.php create mode 100644 vendor/robmorgan/phinx/.stickler.yml create mode 100644 vendor/robmorgan/phinx/LICENSE create mode 100644 vendor/robmorgan/phinx/README.md create mode 100644 vendor/robmorgan/phinx/app/phinx.php create mode 100644 vendor/robmorgan/phinx/app/web.php create mode 100755 vendor/robmorgan/phinx/bin/phinx create mode 100644 vendor/robmorgan/phinx/bin/phinx.bat create mode 100644 vendor/robmorgan/phinx/composer.json create mode 100644 vendor/robmorgan/phinx/data/phinx.json.dist create mode 100644 vendor/robmorgan/phinx/data/phinx.php.dist create mode 100644 vendor/robmorgan/phinx/data/phinx.yml.dist create mode 100644 vendor/robmorgan/phinx/docs/config/__init__.py create mode 100644 vendor/robmorgan/phinx/docs/config/all.py create mode 100644 vendor/robmorgan/phinx/docs/en/commands.rst create mode 100644 vendor/robmorgan/phinx/docs/en/conf.py create mode 100644 vendor/robmorgan/phinx/docs/en/configuration.rst create mode 100644 vendor/robmorgan/phinx/docs/en/contents.rst create mode 100644 vendor/robmorgan/phinx/docs/en/copyright.rst create mode 100644 vendor/robmorgan/phinx/docs/en/goals.rst create mode 100644 vendor/robmorgan/phinx/docs/en/index.rst create mode 100644 vendor/robmorgan/phinx/docs/en/install.rst create mode 100644 vendor/robmorgan/phinx/docs/en/intro.rst create mode 100644 vendor/robmorgan/phinx/docs/en/migrations.rst create mode 100644 vendor/robmorgan/phinx/docs/en/namespaces.rst create mode 100644 vendor/robmorgan/phinx/docs/en/seeding.rst create mode 100644 vendor/robmorgan/phinx/phpstan-baseline.neon create mode 100644 vendor/robmorgan/phinx/src/Phinx/Config/Config.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Config/ConfigInterface.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Config/FeatureFlags.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Config/NamespaceAwareInterface.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Config/NamespaceAwareTrait.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Console/Command/AbstractCommand.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Console/Command/Breakpoint.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Console/Command/Create.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Console/Command/Init.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Console/Command/ListAliases.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Console/Command/Migrate.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Console/Command/Rollback.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Console/Command/SeedCreate.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Console/Command/SeedRun.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Console/Command/Status.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Console/Command/Test.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Console/PhinxApplication.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/Action.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/AddColumn.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/AddForeignKey.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/AddIndex.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/ChangeColumn.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/ChangeComment.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/ChangePrimaryKey.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/CreateTable.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/DropForeignKey.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/DropIndex.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/DropTable.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/RemoveColumn.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/RenameColumn.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Action/RenameTable.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AbstractAdapter.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterFactory.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterInterface.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/DirectActionInterface.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/PdoAdapter.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/PostgresAdapter.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/ProxyAdapter.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/TimedOutputAdapter.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/UnsupportedColumnTypeException.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Adapter/WrapperInterface.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Plan/AlterTable.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Plan/Intent.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Plan/NewTable.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Plan/Plan.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Plan/Solver/ActionSplitter.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Table.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Table/Column.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Table/ForeignKey.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Table/Index.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Table/Table.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Db/Util/AlterInstructions.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Migration/AbstractMigration.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Migration/AbstractTemplateCreation.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Migration/CreationInterface.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Migration/IrreversibleMigrationException.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Migration/Manager.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Migration/Manager/Environment.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Migration/Migration.change.template.php.dist create mode 100644 vendor/robmorgan/phinx/src/Phinx/Migration/Migration.up_down.template.php.dist create mode 100644 vendor/robmorgan/phinx/src/Phinx/Migration/MigrationInterface.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Seed/AbstractSeed.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Seed/Seed.template.php.dist create mode 100644 vendor/robmorgan/phinx/src/Phinx/Seed/SeedInterface.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Util/Expression.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Util/Literal.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Util/Util.php create mode 100644 vendor/robmorgan/phinx/src/Phinx/Wrapper/TextWrapper.php create mode 100644 vendor/robmorgan/phinx/src/composer_autoloader.php create mode 100644 vendor/symfony/cache-contracts/.gitignore create mode 100644 vendor/symfony/cache-contracts/CHANGELOG.md create mode 100644 vendor/symfony/cache-contracts/CacheInterface.php create mode 100644 vendor/symfony/cache-contracts/CacheTrait.php create mode 100644 vendor/symfony/cache-contracts/CallbackInterface.php create mode 100644 vendor/symfony/cache-contracts/ItemInterface.php create mode 100644 vendor/symfony/cache-contracts/LICENSE create mode 100644 vendor/symfony/cache-contracts/README.md create mode 100644 vendor/symfony/cache-contracts/TagAwareCacheInterface.php create mode 100644 vendor/symfony/cache-contracts/composer.json create mode 100644 vendor/symfony/cache/Adapter/AbstractAdapter.php create mode 100644 vendor/symfony/cache/Adapter/AbstractTagAwareAdapter.php create mode 100644 vendor/symfony/cache/Adapter/AdapterInterface.php create mode 100644 vendor/symfony/cache/Adapter/ApcuAdapter.php create mode 100644 vendor/symfony/cache/Adapter/ArrayAdapter.php create mode 100644 vendor/symfony/cache/Adapter/ChainAdapter.php create mode 100644 vendor/symfony/cache/Adapter/CouchbaseBucketAdapter.php create mode 100644 vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php create mode 100644 vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php create mode 100644 vendor/symfony/cache/Adapter/FilesystemAdapter.php create mode 100644 vendor/symfony/cache/Adapter/FilesystemTagAwareAdapter.php create mode 100644 vendor/symfony/cache/Adapter/MemcachedAdapter.php create mode 100644 vendor/symfony/cache/Adapter/NullAdapter.php create mode 100644 vendor/symfony/cache/Adapter/ParameterNormalizer.php create mode 100644 vendor/symfony/cache/Adapter/PdoAdapter.php create mode 100644 vendor/symfony/cache/Adapter/PhpArrayAdapter.php create mode 100644 vendor/symfony/cache/Adapter/PhpFilesAdapter.php create mode 100644 vendor/symfony/cache/Adapter/ProxyAdapter.php create mode 100644 vendor/symfony/cache/Adapter/Psr16Adapter.php create mode 100644 vendor/symfony/cache/Adapter/RedisAdapter.php create mode 100644 vendor/symfony/cache/Adapter/RedisTagAwareAdapter.php create mode 100644 vendor/symfony/cache/Adapter/TagAwareAdapter.php create mode 100644 vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php create mode 100644 vendor/symfony/cache/Adapter/TraceableAdapter.php create mode 100644 vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php create mode 100644 vendor/symfony/cache/CHANGELOG.md create mode 100644 vendor/symfony/cache/CacheItem.php create mode 100644 vendor/symfony/cache/DataCollector/CacheDataCollector.php create mode 100644 vendor/symfony/cache/DependencyInjection/CacheCollectorPass.php create mode 100644 vendor/symfony/cache/DependencyInjection/CachePoolClearerPass.php create mode 100644 vendor/symfony/cache/DependencyInjection/CachePoolPass.php create mode 100644 vendor/symfony/cache/DependencyInjection/CachePoolPrunerPass.php create mode 100644 vendor/symfony/cache/Exception/CacheException.php create mode 100644 vendor/symfony/cache/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/cache/Exception/LogicException.php create mode 100644 vendor/symfony/cache/LICENSE create mode 100644 vendor/symfony/cache/LockRegistry.php create mode 100644 vendor/symfony/cache/Marshaller/DefaultMarshaller.php create mode 100644 vendor/symfony/cache/Marshaller/DeflateMarshaller.php create mode 100644 vendor/symfony/cache/Marshaller/MarshallerInterface.php create mode 100644 vendor/symfony/cache/Marshaller/SodiumMarshaller.php create mode 100644 vendor/symfony/cache/Marshaller/TagAwareMarshaller.php create mode 100644 vendor/symfony/cache/Messenger/EarlyExpirationDispatcher.php create mode 100644 vendor/symfony/cache/Messenger/EarlyExpirationHandler.php create mode 100644 vendor/symfony/cache/Messenger/EarlyExpirationMessage.php create mode 100644 vendor/symfony/cache/PruneableInterface.php create mode 100644 vendor/symfony/cache/Psr16Cache.php create mode 100644 vendor/symfony/cache/README.md create mode 100644 vendor/symfony/cache/ResettableInterface.php create mode 100644 vendor/symfony/cache/Traits/AbstractAdapterTrait.php create mode 100644 vendor/symfony/cache/Traits/ContractsTrait.php create mode 100644 vendor/symfony/cache/Traits/FilesystemCommonTrait.php create mode 100644 vendor/symfony/cache/Traits/FilesystemTrait.php create mode 100644 vendor/symfony/cache/Traits/ProxyTrait.php create mode 100644 vendor/symfony/cache/Traits/RedisClusterNodeProxy.php create mode 100644 vendor/symfony/cache/Traits/RedisClusterProxy.php create mode 100644 vendor/symfony/cache/Traits/RedisProxy.php create mode 100644 vendor/symfony/cache/Traits/RedisTrait.php create mode 100644 vendor/symfony/cache/composer.json create mode 100644 vendor/symfony/config/Builder/ClassBuilder.php create mode 100644 vendor/symfony/config/Builder/ConfigBuilderGenerator.php create mode 100644 vendor/symfony/config/Builder/ConfigBuilderGeneratorInterface.php create mode 100644 vendor/symfony/config/Builder/ConfigBuilderInterface.php create mode 100644 vendor/symfony/config/Builder/Method.php create mode 100644 vendor/symfony/config/Builder/Property.php create mode 100644 vendor/symfony/config/CHANGELOG.md create mode 100644 vendor/symfony/config/ConfigCache.php create mode 100644 vendor/symfony/config/ConfigCacheFactory.php create mode 100644 vendor/symfony/config/ConfigCacheFactoryInterface.php create mode 100644 vendor/symfony/config/ConfigCacheInterface.php create mode 100644 vendor/symfony/config/Definition/ArrayNode.php create mode 100644 vendor/symfony/config/Definition/BaseNode.php create mode 100644 vendor/symfony/config/Definition/BooleanNode.php create mode 100644 vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/BuilderAwareInterface.php create mode 100644 vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/ExprBuilder.php create mode 100644 vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/MergeBuilder.php create mode 100644 vendor/symfony/config/Definition/Builder/NodeBuilder.php create mode 100644 vendor/symfony/config/Definition/Builder/NodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/NodeParentInterface.php create mode 100644 vendor/symfony/config/Definition/Builder/NormalizationBuilder.php create mode 100644 vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php create mode 100644 vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/TreeBuilder.php create mode 100644 vendor/symfony/config/Definition/Builder/ValidationBuilder.php create mode 100644 vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/ConfigurationInterface.php create mode 100644 vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php create mode 100644 vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php create mode 100644 vendor/symfony/config/Definition/EnumNode.php create mode 100644 vendor/symfony/config/Definition/Exception/DuplicateKeyException.php create mode 100644 vendor/symfony/config/Definition/Exception/Exception.php create mode 100644 vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.php create mode 100644 vendor/symfony/config/Definition/Exception/InvalidConfigurationException.php create mode 100644 vendor/symfony/config/Definition/Exception/InvalidDefinitionException.php create mode 100644 vendor/symfony/config/Definition/Exception/InvalidTypeException.php create mode 100644 vendor/symfony/config/Definition/Exception/UnsetKeyException.php create mode 100644 vendor/symfony/config/Definition/FloatNode.php create mode 100644 vendor/symfony/config/Definition/IntegerNode.php create mode 100644 vendor/symfony/config/Definition/NodeInterface.php create mode 100644 vendor/symfony/config/Definition/NumericNode.php create mode 100644 vendor/symfony/config/Definition/Processor.php create mode 100644 vendor/symfony/config/Definition/PrototypeNodeInterface.php create mode 100644 vendor/symfony/config/Definition/PrototypedArrayNode.php create mode 100644 vendor/symfony/config/Definition/ScalarNode.php create mode 100644 vendor/symfony/config/Definition/VariableNode.php create mode 100644 vendor/symfony/config/Exception/FileLoaderImportCircularReferenceException.php create mode 100644 vendor/symfony/config/Exception/FileLocatorFileNotFoundException.php create mode 100644 vendor/symfony/config/Exception/LoaderLoadException.php create mode 100644 vendor/symfony/config/FileLocator.php create mode 100644 vendor/symfony/config/FileLocatorInterface.php create mode 100644 vendor/symfony/config/LICENSE create mode 100644 vendor/symfony/config/Loader/DelegatingLoader.php create mode 100644 vendor/symfony/config/Loader/FileLoader.php create mode 100644 vendor/symfony/config/Loader/GlobFileLoader.php create mode 100644 vendor/symfony/config/Loader/Loader.php create mode 100644 vendor/symfony/config/Loader/LoaderInterface.php create mode 100644 vendor/symfony/config/Loader/LoaderResolver.php create mode 100644 vendor/symfony/config/Loader/LoaderResolverInterface.php create mode 100644 vendor/symfony/config/Loader/ParamConfigurator.php create mode 100644 vendor/symfony/config/README.md create mode 100644 vendor/symfony/config/Resource/ClassExistenceResource.php create mode 100644 vendor/symfony/config/Resource/ComposerResource.php create mode 100644 vendor/symfony/config/Resource/DirectoryResource.php create mode 100644 vendor/symfony/config/Resource/FileExistenceResource.php create mode 100644 vendor/symfony/config/Resource/FileResource.php create mode 100644 vendor/symfony/config/Resource/GlobResource.php create mode 100644 vendor/symfony/config/Resource/ReflectionClassResource.php create mode 100644 vendor/symfony/config/Resource/ResourceInterface.php create mode 100644 vendor/symfony/config/Resource/SelfCheckingResourceChecker.php create mode 100644 vendor/symfony/config/Resource/SelfCheckingResourceInterface.php create mode 100644 vendor/symfony/config/ResourceCheckerConfigCache.php create mode 100644 vendor/symfony/config/ResourceCheckerConfigCacheFactory.php create mode 100644 vendor/symfony/config/ResourceCheckerInterface.php create mode 100644 vendor/symfony/config/Util/Exception/InvalidXmlException.php create mode 100644 vendor/symfony/config/Util/Exception/XmlParsingException.php create mode 100644 vendor/symfony/config/Util/XmlUtils.php create mode 100644 vendor/symfony/config/composer.json create mode 100644 vendor/symfony/console/Application.php create mode 100644 vendor/symfony/console/Attribute/AsCommand.php create mode 100644 vendor/symfony/console/CHANGELOG.md create mode 100644 vendor/symfony/console/CI/GithubActionReporter.php create mode 100644 vendor/symfony/console/Color.php create mode 100644 vendor/symfony/console/Command/Command.php create mode 100644 vendor/symfony/console/Command/CompleteCommand.php create mode 100644 vendor/symfony/console/Command/DumpCompletionCommand.php create mode 100644 vendor/symfony/console/Command/HelpCommand.php create mode 100644 vendor/symfony/console/Command/LazyCommand.php create mode 100644 vendor/symfony/console/Command/ListCommand.php create mode 100644 vendor/symfony/console/Command/LockableTrait.php create mode 100644 vendor/symfony/console/Command/SignalableCommandInterface.php create mode 100644 vendor/symfony/console/CommandLoader/CommandLoaderInterface.php create mode 100644 vendor/symfony/console/CommandLoader/ContainerCommandLoader.php create mode 100644 vendor/symfony/console/CommandLoader/FactoryCommandLoader.php create mode 100644 vendor/symfony/console/Completion/CompletionInput.php create mode 100644 vendor/symfony/console/Completion/CompletionSuggestions.php create mode 100644 vendor/symfony/console/Completion/Output/BashCompletionOutput.php create mode 100644 vendor/symfony/console/Completion/Output/CompletionOutputInterface.php create mode 100644 vendor/symfony/console/Completion/Suggestion.php create mode 100644 vendor/symfony/console/ConsoleEvents.php create mode 100644 vendor/symfony/console/Cursor.php create mode 100644 vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php create mode 100644 vendor/symfony/console/Descriptor/ApplicationDescription.php create mode 100644 vendor/symfony/console/Descriptor/Descriptor.php create mode 100644 vendor/symfony/console/Descriptor/DescriptorInterface.php create mode 100644 vendor/symfony/console/Descriptor/JsonDescriptor.php create mode 100644 vendor/symfony/console/Descriptor/MarkdownDescriptor.php create mode 100644 vendor/symfony/console/Descriptor/TextDescriptor.php create mode 100644 vendor/symfony/console/Descriptor/XmlDescriptor.php create mode 100644 vendor/symfony/console/Event/ConsoleCommandEvent.php create mode 100644 vendor/symfony/console/Event/ConsoleErrorEvent.php create mode 100644 vendor/symfony/console/Event/ConsoleEvent.php create mode 100644 vendor/symfony/console/Event/ConsoleSignalEvent.php create mode 100644 vendor/symfony/console/Event/ConsoleTerminateEvent.php create mode 100644 vendor/symfony/console/EventListener/ErrorListener.php create mode 100644 vendor/symfony/console/Exception/CommandNotFoundException.php create mode 100644 vendor/symfony/console/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/console/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/console/Exception/InvalidOptionException.php create mode 100644 vendor/symfony/console/Exception/LogicException.php create mode 100644 vendor/symfony/console/Exception/MissingInputException.php create mode 100644 vendor/symfony/console/Exception/NamespaceNotFoundException.php create mode 100644 vendor/symfony/console/Exception/RuntimeException.php create mode 100644 vendor/symfony/console/Formatter/NullOutputFormatter.php create mode 100644 vendor/symfony/console/Formatter/NullOutputFormatterStyle.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatter.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatterInterface.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatterStyle.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatterStyleStack.php create mode 100644 vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php create mode 100644 vendor/symfony/console/Helper/DebugFormatterHelper.php create mode 100644 vendor/symfony/console/Helper/DescriptorHelper.php create mode 100644 vendor/symfony/console/Helper/Dumper.php create mode 100644 vendor/symfony/console/Helper/FormatterHelper.php create mode 100644 vendor/symfony/console/Helper/Helper.php create mode 100644 vendor/symfony/console/Helper/HelperInterface.php create mode 100644 vendor/symfony/console/Helper/HelperSet.php create mode 100644 vendor/symfony/console/Helper/InputAwareHelper.php create mode 100644 vendor/symfony/console/Helper/ProcessHelper.php create mode 100644 vendor/symfony/console/Helper/ProgressBar.php create mode 100644 vendor/symfony/console/Helper/ProgressIndicator.php create mode 100644 vendor/symfony/console/Helper/QuestionHelper.php create mode 100644 vendor/symfony/console/Helper/SymfonyQuestionHelper.php create mode 100644 vendor/symfony/console/Helper/Table.php create mode 100644 vendor/symfony/console/Helper/TableCell.php create mode 100644 vendor/symfony/console/Helper/TableCellStyle.php create mode 100644 vendor/symfony/console/Helper/TableRows.php create mode 100644 vendor/symfony/console/Helper/TableSeparator.php create mode 100644 vendor/symfony/console/Helper/TableStyle.php create mode 100644 vendor/symfony/console/Input/ArgvInput.php create mode 100644 vendor/symfony/console/Input/ArrayInput.php create mode 100644 vendor/symfony/console/Input/Input.php create mode 100644 vendor/symfony/console/Input/InputArgument.php create mode 100644 vendor/symfony/console/Input/InputAwareInterface.php create mode 100644 vendor/symfony/console/Input/InputDefinition.php create mode 100644 vendor/symfony/console/Input/InputInterface.php create mode 100644 vendor/symfony/console/Input/InputOption.php create mode 100644 vendor/symfony/console/Input/StreamableInputInterface.php create mode 100644 vendor/symfony/console/Input/StringInput.php create mode 100644 vendor/symfony/console/LICENSE create mode 100644 vendor/symfony/console/Logger/ConsoleLogger.php create mode 100644 vendor/symfony/console/Output/BufferedOutput.php create mode 100644 vendor/symfony/console/Output/ConsoleOutput.php create mode 100644 vendor/symfony/console/Output/ConsoleOutputInterface.php create mode 100644 vendor/symfony/console/Output/ConsoleSectionOutput.php create mode 100644 vendor/symfony/console/Output/NullOutput.php create mode 100644 vendor/symfony/console/Output/Output.php create mode 100644 vendor/symfony/console/Output/OutputInterface.php create mode 100644 vendor/symfony/console/Output/StreamOutput.php create mode 100644 vendor/symfony/console/Output/TrimmedBufferOutput.php create mode 100644 vendor/symfony/console/Question/ChoiceQuestion.php create mode 100644 vendor/symfony/console/Question/ConfirmationQuestion.php create mode 100644 vendor/symfony/console/Question/Question.php create mode 100644 vendor/symfony/console/README.md create mode 100644 vendor/symfony/console/Resources/bin/hiddeninput.exe create mode 100644 vendor/symfony/console/Resources/completion.bash create mode 100644 vendor/symfony/console/SignalRegistry/SignalRegistry.php create mode 100644 vendor/symfony/console/SingleCommandApplication.php create mode 100644 vendor/symfony/console/Style/OutputStyle.php create mode 100644 vendor/symfony/console/Style/StyleInterface.php create mode 100644 vendor/symfony/console/Style/SymfonyStyle.php create mode 100644 vendor/symfony/console/Terminal.php create mode 100644 vendor/symfony/console/Tester/ApplicationTester.php create mode 100644 vendor/symfony/console/Tester/CommandCompletionTester.php create mode 100644 vendor/symfony/console/Tester/CommandTester.php create mode 100644 vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php create mode 100644 vendor/symfony/console/Tester/TesterTrait.php create mode 100644 vendor/symfony/console/composer.json create mode 100644 vendor/symfony/deprecation-contracts/.gitignore create mode 100644 vendor/symfony/deprecation-contracts/CHANGELOG.md create mode 100644 vendor/symfony/deprecation-contracts/LICENSE create mode 100644 vendor/symfony/deprecation-contracts/README.md create mode 100644 vendor/symfony/deprecation-contracts/composer.json create mode 100644 vendor/symfony/deprecation-contracts/function.php create mode 100644 vendor/symfony/filesystem/CHANGELOG.md create mode 100644 vendor/symfony/filesystem/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/filesystem/Exception/FileNotFoundException.php create mode 100644 vendor/symfony/filesystem/Exception/IOException.php create mode 100644 vendor/symfony/filesystem/Exception/IOExceptionInterface.php create mode 100644 vendor/symfony/filesystem/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/filesystem/Exception/RuntimeException.php create mode 100644 vendor/symfony/filesystem/Filesystem.php create mode 100644 vendor/symfony/filesystem/LICENSE create mode 100644 vendor/symfony/filesystem/Path.php create mode 100644 vendor/symfony/filesystem/README.md create mode 100644 vendor/symfony/filesystem/composer.json create mode 100644 vendor/symfony/polyfill-ctype/Ctype.php create mode 100644 vendor/symfony/polyfill-ctype/LICENSE create mode 100644 vendor/symfony/polyfill-ctype/README.md create mode 100644 vendor/symfony/polyfill-ctype/bootstrap.php create mode 100644 vendor/symfony/polyfill-ctype/bootstrap80.php create mode 100644 vendor/symfony/polyfill-ctype/composer.json create mode 100644 vendor/symfony/polyfill-intl-grapheme/Grapheme.php create mode 100644 vendor/symfony/polyfill-intl-grapheme/LICENSE create mode 100644 vendor/symfony/polyfill-intl-grapheme/README.md create mode 100644 vendor/symfony/polyfill-intl-grapheme/bootstrap.php create mode 100644 vendor/symfony/polyfill-intl-grapheme/bootstrap80.php create mode 100644 vendor/symfony/polyfill-intl-grapheme/composer.json create mode 100644 vendor/symfony/polyfill-intl-normalizer/LICENSE create mode 100644 vendor/symfony/polyfill-intl-normalizer/Normalizer.php create mode 100644 vendor/symfony/polyfill-intl-normalizer/README.md create mode 100644 vendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php create mode 100644 vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalComposition.php create mode 100644 vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php create mode 100644 vendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php create mode 100644 vendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php create mode 100644 vendor/symfony/polyfill-intl-normalizer/bootstrap.php create mode 100644 vendor/symfony/polyfill-intl-normalizer/bootstrap80.php create mode 100644 vendor/symfony/polyfill-intl-normalizer/composer.json create mode 100644 vendor/symfony/polyfill-mbstring/LICENSE create mode 100644 vendor/symfony/polyfill-mbstring/Mbstring.php create mode 100644 vendor/symfony/polyfill-mbstring/README.md create mode 100644 vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php create mode 100644 vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php create mode 100644 vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php create mode 100644 vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php create mode 100644 vendor/symfony/polyfill-mbstring/bootstrap.php create mode 100644 vendor/symfony/polyfill-mbstring/bootstrap80.php create mode 100644 vendor/symfony/polyfill-mbstring/composer.json create mode 100644 vendor/symfony/polyfill-php80/LICENSE create mode 100644 vendor/symfony/polyfill-php80/Php80.php create mode 100644 vendor/symfony/polyfill-php80/PhpToken.php create mode 100644 vendor/symfony/polyfill-php80/README.md create mode 100644 vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php create mode 100644 vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php create mode 100644 vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php create mode 100644 vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php create mode 100644 vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php create mode 100644 vendor/symfony/polyfill-php80/bootstrap.php create mode 100644 vendor/symfony/polyfill-php80/composer.json create mode 100644 vendor/symfony/polyfill-php81/LICENSE create mode 100644 vendor/symfony/polyfill-php81/Php81.php create mode 100644 vendor/symfony/polyfill-php81/README.md create mode 100644 vendor/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php create mode 100644 vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php create mode 100644 vendor/symfony/polyfill-php81/bootstrap.php create mode 100644 vendor/symfony/polyfill-php81/composer.json create mode 100644 vendor/symfony/service-contracts/.gitignore create mode 100644 vendor/symfony/service-contracts/Attribute/Required.php create mode 100644 vendor/symfony/service-contracts/Attribute/SubscribedService.php create mode 100644 vendor/symfony/service-contracts/CHANGELOG.md create mode 100644 vendor/symfony/service-contracts/LICENSE create mode 100644 vendor/symfony/service-contracts/README.md create mode 100644 vendor/symfony/service-contracts/ResetInterface.php create mode 100644 vendor/symfony/service-contracts/ServiceLocatorTrait.php create mode 100644 vendor/symfony/service-contracts/ServiceProviderInterface.php create mode 100644 vendor/symfony/service-contracts/ServiceSubscriberInterface.php create mode 100644 vendor/symfony/service-contracts/ServiceSubscriberTrait.php create mode 100644 vendor/symfony/service-contracts/Test/ServiceLocatorTest.php create mode 100644 vendor/symfony/service-contracts/composer.json create mode 100644 vendor/symfony/string/AbstractString.php create mode 100644 vendor/symfony/string/AbstractUnicodeString.php create mode 100644 vendor/symfony/string/ByteString.php create mode 100644 vendor/symfony/string/CHANGELOG.md create mode 100644 vendor/symfony/string/CodePointString.php create mode 100644 vendor/symfony/string/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/string/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/string/Exception/RuntimeException.php create mode 100644 vendor/symfony/string/Inflector/EnglishInflector.php create mode 100644 vendor/symfony/string/Inflector/FrenchInflector.php create mode 100644 vendor/symfony/string/Inflector/InflectorInterface.php create mode 100644 vendor/symfony/string/LICENSE create mode 100644 vendor/symfony/string/LazyString.php create mode 100644 vendor/symfony/string/README.md create mode 100644 vendor/symfony/string/Resources/data/wcswidth_table_wide.php create mode 100644 vendor/symfony/string/Resources/data/wcswidth_table_zero.php create mode 100644 vendor/symfony/string/Resources/functions.php create mode 100644 vendor/symfony/string/Slugger/AsciiSlugger.php create mode 100644 vendor/symfony/string/Slugger/SluggerInterface.php create mode 100644 vendor/symfony/string/UnicodeString.php create mode 100644 vendor/symfony/string/composer.json create mode 100644 vendor/symfony/translation-contracts/.gitignore create mode 100644 vendor/symfony/translation-contracts/CHANGELOG.md create mode 100644 vendor/symfony/translation-contracts/LICENSE create mode 100644 vendor/symfony/translation-contracts/LocaleAwareInterface.php create mode 100644 vendor/symfony/translation-contracts/README.md create mode 100644 vendor/symfony/translation-contracts/Test/TranslatorTest.php create mode 100644 vendor/symfony/translation-contracts/TranslatableInterface.php create mode 100644 vendor/symfony/translation-contracts/TranslatorInterface.php create mode 100644 vendor/symfony/translation-contracts/TranslatorTrait.php create mode 100644 vendor/symfony/translation-contracts/composer.json create mode 100644 vendor/symfony/translation/CHANGELOG.md create mode 100644 vendor/symfony/translation/Catalogue/AbstractOperation.php create mode 100644 vendor/symfony/translation/Catalogue/MergeOperation.php create mode 100644 vendor/symfony/translation/Catalogue/OperationInterface.php create mode 100644 vendor/symfony/translation/Catalogue/TargetOperation.php create mode 100644 vendor/symfony/translation/Command/TranslationPullCommand.php create mode 100644 vendor/symfony/translation/Command/TranslationPushCommand.php create mode 100644 vendor/symfony/translation/Command/TranslationTrait.php create mode 100644 vendor/symfony/translation/Command/XliffLintCommand.php create mode 100644 vendor/symfony/translation/DataCollector/TranslationDataCollector.php create mode 100644 vendor/symfony/translation/DataCollectorTranslator.php create mode 100644 vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php create mode 100644 vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php create mode 100644 vendor/symfony/translation/DependencyInjection/TranslatorPass.php create mode 100644 vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php create mode 100644 vendor/symfony/translation/Dumper/CsvFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/DumperInterface.php create mode 100644 vendor/symfony/translation/Dumper/FileDumper.php create mode 100644 vendor/symfony/translation/Dumper/IcuResFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/IniFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/JsonFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/MoFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/PhpFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/PoFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/QtFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/XliffFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/YamlFileDumper.php create mode 100644 vendor/symfony/translation/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/translation/Exception/IncompleteDsnException.php create mode 100644 vendor/symfony/translation/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/translation/Exception/InvalidResourceException.php create mode 100644 vendor/symfony/translation/Exception/LogicException.php create mode 100644 vendor/symfony/translation/Exception/MissingRequiredOptionException.php create mode 100644 vendor/symfony/translation/Exception/NotFoundResourceException.php create mode 100644 vendor/symfony/translation/Exception/ProviderException.php create mode 100644 vendor/symfony/translation/Exception/ProviderExceptionInterface.php create mode 100644 vendor/symfony/translation/Exception/RuntimeException.php create mode 100644 vendor/symfony/translation/Exception/UnsupportedSchemeException.php create mode 100644 vendor/symfony/translation/Extractor/AbstractFileExtractor.php create mode 100644 vendor/symfony/translation/Extractor/ChainExtractor.php create mode 100644 vendor/symfony/translation/Extractor/ExtractorInterface.php create mode 100644 vendor/symfony/translation/Extractor/PhpExtractor.php create mode 100644 vendor/symfony/translation/Extractor/PhpStringTokenParser.php create mode 100644 vendor/symfony/translation/Formatter/IntlFormatter.php create mode 100644 vendor/symfony/translation/Formatter/IntlFormatterInterface.php create mode 100644 vendor/symfony/translation/Formatter/MessageFormatter.php create mode 100644 vendor/symfony/translation/Formatter/MessageFormatterInterface.php create mode 100644 vendor/symfony/translation/IdentityTranslator.php create mode 100644 vendor/symfony/translation/LICENSE create mode 100644 vendor/symfony/translation/Loader/ArrayLoader.php create mode 100644 vendor/symfony/translation/Loader/CsvFileLoader.php create mode 100644 vendor/symfony/translation/Loader/FileLoader.php create mode 100644 vendor/symfony/translation/Loader/IcuDatFileLoader.php create mode 100644 vendor/symfony/translation/Loader/IcuResFileLoader.php create mode 100644 vendor/symfony/translation/Loader/IniFileLoader.php create mode 100644 vendor/symfony/translation/Loader/JsonFileLoader.php create mode 100644 vendor/symfony/translation/Loader/LoaderInterface.php create mode 100644 vendor/symfony/translation/Loader/MoFileLoader.php create mode 100644 vendor/symfony/translation/Loader/PhpFileLoader.php create mode 100644 vendor/symfony/translation/Loader/PoFileLoader.php create mode 100644 vendor/symfony/translation/Loader/QtFileLoader.php create mode 100644 vendor/symfony/translation/Loader/XliffFileLoader.php create mode 100644 vendor/symfony/translation/Loader/YamlFileLoader.php create mode 100644 vendor/symfony/translation/LoggingTranslator.php create mode 100644 vendor/symfony/translation/MessageCatalogue.php create mode 100644 vendor/symfony/translation/MessageCatalogueInterface.php create mode 100644 vendor/symfony/translation/MetadataAwareInterface.php create mode 100644 vendor/symfony/translation/Provider/AbstractProviderFactory.php create mode 100644 vendor/symfony/translation/Provider/Dsn.php create mode 100644 vendor/symfony/translation/Provider/FilteringProvider.php create mode 100644 vendor/symfony/translation/Provider/NullProvider.php create mode 100644 vendor/symfony/translation/Provider/NullProviderFactory.php create mode 100644 vendor/symfony/translation/Provider/ProviderFactoryInterface.php create mode 100644 vendor/symfony/translation/Provider/ProviderInterface.php create mode 100644 vendor/symfony/translation/Provider/TranslationProviderCollection.php create mode 100644 vendor/symfony/translation/Provider/TranslationProviderCollectionFactory.php create mode 100644 vendor/symfony/translation/PseudoLocalizationTranslator.php create mode 100644 vendor/symfony/translation/README.md create mode 100644 vendor/symfony/translation/Reader/TranslationReader.php create mode 100644 vendor/symfony/translation/Reader/TranslationReaderInterface.php create mode 100644 vendor/symfony/translation/Resources/bin/translation-status.php create mode 100644 vendor/symfony/translation/Resources/data/parents.json create mode 100644 vendor/symfony/translation/Resources/functions.php create mode 100644 vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd create mode 100644 vendor/symfony/translation/Resources/schemas/xliff-core-2.0.xsd create mode 100644 vendor/symfony/translation/Resources/schemas/xml.xsd create mode 100644 vendor/symfony/translation/Test/ProviderFactoryTestCase.php create mode 100644 vendor/symfony/translation/Test/ProviderTestCase.php create mode 100644 vendor/symfony/translation/TranslatableMessage.php create mode 100644 vendor/symfony/translation/Translator.php create mode 100644 vendor/symfony/translation/TranslatorBag.php create mode 100644 vendor/symfony/translation/TranslatorBagInterface.php create mode 100644 vendor/symfony/translation/Util/ArrayConverter.php create mode 100644 vendor/symfony/translation/Util/XliffUtils.php create mode 100644 vendor/symfony/translation/Writer/TranslationWriter.php create mode 100644 vendor/symfony/translation/Writer/TranslationWriterInterface.php create mode 100644 vendor/symfony/translation/composer.json create mode 100644 vendor/symfony/var-dumper/CHANGELOG.md create mode 100644 vendor/symfony/var-dumper/Caster/AmqpCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/ArgsStub.php create mode 100644 vendor/symfony/var-dumper/Caster/Caster.php create mode 100644 vendor/symfony/var-dumper/Caster/ClassStub.php create mode 100644 vendor/symfony/var-dumper/Caster/ConstStub.php create mode 100644 vendor/symfony/var-dumper/Caster/CutArrayStub.php create mode 100644 vendor/symfony/var-dumper/Caster/CutStub.php create mode 100644 vendor/symfony/var-dumper/Caster/DOMCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/DateCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/DoctrineCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/DsCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/DsPairStub.php create mode 100644 vendor/symfony/var-dumper/Caster/EnumStub.php create mode 100644 vendor/symfony/var-dumper/Caster/ExceptionCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/FiberCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/FrameStub.php create mode 100644 vendor/symfony/var-dumper/Caster/GmpCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/ImagineCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/ImgStub.php create mode 100644 vendor/symfony/var-dumper/Caster/IntlCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/LinkStub.php create mode 100644 vendor/symfony/var-dumper/Caster/MemcachedCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/MysqliCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/PdoCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/PgSqlCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/RdKafkaCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/RedisCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/ReflectionCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/ResourceCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/SplCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/StubCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/SymfonyCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/TraceStub.php create mode 100644 vendor/symfony/var-dumper/Caster/UuidCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/XmlReaderCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/XmlResourceCaster.php create mode 100644 vendor/symfony/var-dumper/Cloner/AbstractCloner.php create mode 100644 vendor/symfony/var-dumper/Cloner/ClonerInterface.php create mode 100644 vendor/symfony/var-dumper/Cloner/Cursor.php create mode 100644 vendor/symfony/var-dumper/Cloner/Data.php create mode 100644 vendor/symfony/var-dumper/Cloner/DumperInterface.php create mode 100644 vendor/symfony/var-dumper/Cloner/Stub.php create mode 100644 vendor/symfony/var-dumper/Cloner/VarCloner.php create mode 100644 vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php create mode 100644 vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php create mode 100644 vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php create mode 100644 vendor/symfony/var-dumper/Command/ServerDumpCommand.php create mode 100644 vendor/symfony/var-dumper/Dumper/AbstractDumper.php create mode 100644 vendor/symfony/var-dumper/Dumper/CliDumper.php create mode 100644 vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php create mode 100644 vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php create mode 100644 vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php create mode 100644 vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php create mode 100644 vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php create mode 100644 vendor/symfony/var-dumper/Dumper/DataDumperInterface.php create mode 100644 vendor/symfony/var-dumper/Dumper/HtmlDumper.php create mode 100644 vendor/symfony/var-dumper/Dumper/ServerDumper.php create mode 100644 vendor/symfony/var-dumper/Exception/ThrowingCasterException.php create mode 100644 vendor/symfony/var-dumper/LICENSE create mode 100644 vendor/symfony/var-dumper/README.md create mode 100755 vendor/symfony/var-dumper/Resources/bin/var-dump-server create mode 100644 vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css create mode 100644 vendor/symfony/var-dumper/Resources/functions/dump.php create mode 100644 vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js create mode 100644 vendor/symfony/var-dumper/Server/Connection.php create mode 100644 vendor/symfony/var-dumper/Server/DumpServer.php create mode 100644 vendor/symfony/var-dumper/Test/VarDumperTestTrait.php create mode 100644 vendor/symfony/var-dumper/VarDumper.php create mode 100644 vendor/symfony/var-dumper/composer.json create mode 100644 vendor/symfony/var-exporter/CHANGELOG.md create mode 100644 vendor/symfony/var-exporter/Exception/ClassNotFoundException.php create mode 100644 vendor/symfony/var-exporter/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/var-exporter/Exception/NotInstantiableTypeException.php create mode 100644 vendor/symfony/var-exporter/Instantiator.php create mode 100644 vendor/symfony/var-exporter/Internal/Exporter.php create mode 100644 vendor/symfony/var-exporter/Internal/Hydrator.php create mode 100644 vendor/symfony/var-exporter/Internal/Reference.php create mode 100644 vendor/symfony/var-exporter/Internal/Registry.php create mode 100644 vendor/symfony/var-exporter/Internal/Values.php create mode 100644 vendor/symfony/var-exporter/LICENSE create mode 100644 vendor/symfony/var-exporter/README.md create mode 100644 vendor/symfony/var-exporter/VarExporter.php create mode 100644 vendor/symfony/var-exporter/composer.json create mode 100644 vendor/vlucas/phpdotenv/LICENSE create mode 100644 vendor/vlucas/phpdotenv/composer.json create mode 100644 vendor/vlucas/phpdotenv/src/Dotenv.php create mode 100644 vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php create mode 100644 vendor/vlucas/phpdotenv/src/Exception/InvalidEncodingException.php create mode 100644 vendor/vlucas/phpdotenv/src/Exception/InvalidFileException.php create mode 100644 vendor/vlucas/phpdotenv/src/Exception/InvalidPathException.php create mode 100644 vendor/vlucas/phpdotenv/src/Exception/ValidationException.php create mode 100644 vendor/vlucas/phpdotenv/src/Loader/Loader.php create mode 100644 vendor/vlucas/phpdotenv/src/Loader/LoaderInterface.php create mode 100644 vendor/vlucas/phpdotenv/src/Loader/Resolver.php create mode 100644 vendor/vlucas/phpdotenv/src/Parser/Entry.php create mode 100644 vendor/vlucas/phpdotenv/src/Parser/EntryParser.php create mode 100644 vendor/vlucas/phpdotenv/src/Parser/Lexer.php create mode 100644 vendor/vlucas/phpdotenv/src/Parser/Lines.php create mode 100644 vendor/vlucas/phpdotenv/src/Parser/Parser.php create mode 100644 vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php create mode 100644 vendor/vlucas/phpdotenv/src/Parser/Value.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/AdapterInterface.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/GuardedWriter.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/ImmutableWriter.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiReader.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiWriter.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/ReplacingWriter.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/AdapterRepository.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php create mode 100644 vendor/vlucas/phpdotenv/src/Repository/RepositoryInterface.php create mode 100644 vendor/vlucas/phpdotenv/src/Store/File/Paths.php create mode 100644 vendor/vlucas/phpdotenv/src/Store/File/Reader.php create mode 100644 vendor/vlucas/phpdotenv/src/Store/FileStore.php create mode 100644 vendor/vlucas/phpdotenv/src/Store/StoreBuilder.php create mode 100644 vendor/vlucas/phpdotenv/src/Store/StoreInterface.php create mode 100644 vendor/vlucas/phpdotenv/src/Store/StringStore.php create mode 100644 vendor/vlucas/phpdotenv/src/Util/Regex.php create mode 100644 vendor/vlucas/phpdotenv/src/Util/Str.php create mode 100644 vendor/vlucas/phpdotenv/src/Validator.php create mode 100644 vendor/voku/portable-ascii/CHANGELOG.md create mode 100644 vendor/voku/portable-ascii/LICENSE.txt create mode 100644 vendor/voku/portable-ascii/README.md create mode 100644 vendor/voku/portable-ascii/composer.json create mode 100644 vendor/voku/portable-ascii/src/voku/helper/ASCII.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/ascii_by_languages.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/ascii_extras_by_languages.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/ascii_language_max_key.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/ascii_ord.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x000.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x001.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x002.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x003.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x004.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x005.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x006.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x007.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x009.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x00a.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x00b.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x00c.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x00d.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x00e.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x00f.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x010.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x011.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x012.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x013.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x014.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x015.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x016.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x017.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x018.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x01d.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x01e.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x01f.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x020.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x021.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x022.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x023.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x024.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x025.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x026.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x027.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x028.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x029.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x02a.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x02c.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x02e.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x02f.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x030.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x031.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x032.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x033.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x04d.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x04e.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x04f.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x050.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x051.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x052.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x053.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x054.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x055.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x056.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x057.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x058.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x059.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x05a.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x05b.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x05c.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x05d.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x05e.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x05f.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x060.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x061.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x062.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x063.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x064.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x065.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x066.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x067.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x068.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x069.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x06a.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x06b.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x06c.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x06d.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x06e.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x06f.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x070.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x071.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x072.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x073.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x074.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x075.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x076.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x077.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x078.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x079.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x07a.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x07b.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x07c.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x07d.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x07e.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x07f.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x080.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x081.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x082.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x083.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x084.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x085.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x086.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x087.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x088.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x089.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x08a.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x08b.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x08c.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x08d.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x08e.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x08f.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x090.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x091.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x092.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x093.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x094.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x095.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x096.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x097.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x098.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x099.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x09a.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x09b.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x09c.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x09d.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x09e.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x09f.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0a0.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0a1.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0a2.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0a3.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0a4.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0ac.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0ad.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0ae.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0af.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0b0.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0b1.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0b2.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0b3.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0b4.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0b5.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0b6.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0b7.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0b8.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0b9.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0ba.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0bb.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0bc.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0bd.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0be.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0bf.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0c0.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0c1.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0c2.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0c3.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0c4.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0c5.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0c6.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0c7.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0c8.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0c9.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0ca.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0cb.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0cc.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0cd.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0ce.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0cf.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0d0.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0d1.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0d2.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0d3.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0d4.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0d5.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0d6.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0d7.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0f9.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0fa.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0fb.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0fc.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0fd.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0fe.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x0ff.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x1d4.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x1d5.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x1d6.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x1d7.php create mode 100644 vendor/voku/portable-ascii/src/voku/helper/data/x1f1.php create mode 100644 vendor/webman/captcha/.gitignore create mode 100644 vendor/webman/captcha/.travis.yml create mode 100644 vendor/webman/captcha/LICENSE create mode 100644 vendor/webman/captcha/README.md create mode 100644 vendor/webman/captcha/composer.json create mode 100644 vendor/webman/captcha/demo/demo.php create mode 100644 vendor/webman/captcha/demo/fingerprint.php create mode 100644 vendor/webman/captcha/demo/form.php create mode 100644 vendor/webman/captcha/demo/index.php create mode 100644 vendor/webman/captcha/demo/inline.php create mode 100644 vendor/webman/captcha/demo/ocr.php create mode 100644 vendor/webman/captcha/demo/output.php create mode 100644 vendor/webman/captcha/demo/session.php create mode 100644 vendor/webman/captcha/phpunit.xml.dist create mode 100644 vendor/webman/captcha/src/CaptchaBuilder.php create mode 100644 vendor/webman/captcha/src/CaptchaBuilderInterface.php create mode 100644 vendor/webman/captcha/src/Font/captcha0.ttf create mode 100644 vendor/webman/captcha/src/Font/captcha1.ttf create mode 100644 vendor/webman/captcha/src/Font/captcha2.ttf create mode 100644 vendor/webman/captcha/src/Font/captcha3.ttf create mode 100644 vendor/webman/captcha/src/Font/captcha4.ttf create mode 100644 vendor/webman/captcha/src/Font/captcha5.ttf create mode 100644 vendor/webman/captcha/src/ImageFileHandler.php create mode 100644 vendor/webman/captcha/src/PhraseBuilder.php create mode 100644 vendor/webman/captcha/src/PhraseBuilderInterface.php create mode 100644 vendor/webman/captcha/tests/CaptchaBuilderTest.php create mode 100644 vendor/webman/event/LICENSE create mode 100644 vendor/webman/event/README.md create mode 100644 vendor/webman/event/composer.json create mode 100644 vendor/webman/event/src/BootStrap.php create mode 100644 vendor/webman/event/src/Event.php create mode 100644 vendor/webman/event/src/EventListCommand.php create mode 100644 vendor/webman/event/src/Install.php create mode 100644 vendor/webman/event/src/config/plugin/webman/event/app.php create mode 100644 vendor/webman/event/src/config/plugin/webman/event/bootstrap.php create mode 100644 vendor/webman/event/src/config/plugin/webman/event/command.php create mode 100644 vendor/workerman/crontab/README.md create mode 100644 vendor/workerman/crontab/composer.json create mode 100644 vendor/workerman/crontab/example/test.php create mode 100644 vendor/workerman/crontab/src/Crontab.php create mode 100644 vendor/workerman/crontab/src/Parser.php create mode 100644 vendor/workerman/validation/CHANGELOG.md create mode 100644 vendor/workerman/validation/CONTRIBUTING.md create mode 100644 vendor/workerman/validation/LICENSE create mode 100644 vendor/workerman/validation/README.md create mode 100644 vendor/workerman/validation/composer.json create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AC.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AD.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AERO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AF.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AG.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AI.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AL.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AM.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/ARPA.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AS.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AT.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AU.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AW.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/AZ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BA.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BB.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BF.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BG.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BH.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BI.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BJ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BM.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BN.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BS.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BT.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BW.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BY.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/BZ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CA.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CD.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CI.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CK.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CL.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CM.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CN.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CU.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CV.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CW.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CX.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/CY.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/DM.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/DO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/DZ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/EC.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/EE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/EG.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/ES.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/ET.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/FI.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/FJ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/FM.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/FR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/GD.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/GE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/GG.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/GH.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/GI.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/GL.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/GN.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/GP.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/GR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/GT.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/GU.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/GY.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/HK.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/HN.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/HR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/HT.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/HU.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/ID.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/IE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/IL.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/IM.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/IN.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/INT.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/IO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/IQ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/IR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/IS.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/IT.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/JE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/JO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/JP.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/KE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/KG.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/KI.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/KM.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/KN.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/KP.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/KR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/KW.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/KY.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/KZ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/LA.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/LB.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/LC.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/LK.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/LR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/LS.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/LT.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/LV.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/LY.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MA.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MC.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/ME.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MG.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MK.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/ML.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MN.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MS.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MT.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MU.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MV.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MW.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MX.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MY.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/MZ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/NA.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/NC.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/NF.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/NG.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/NI.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/NO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/NR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/NZ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/OM.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PA.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PF.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PH.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PK.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PL.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PN.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PRO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PS.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PT.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PW.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/PY.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/QA.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/RE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/RO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/RS.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/RW.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SA.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SB.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SC.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SD.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SG.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SH.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SL.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SN.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SS.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/ST.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SV.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SX.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SY.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/SZ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/TH.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/TJ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/TL.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/TM.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/TN.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/TO.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/TR.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/TT.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/TW.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/TZ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/UA.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/UG.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/UK.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/US.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/UY.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/UZ.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/VC.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/VE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/VI.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/VN.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/VU.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/WS.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/XN--4DBRK0CE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/XN--90A3AC.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/XN--J6W193G.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/XN--O3CW4H.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/YE.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/ZA.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/ZM.php create mode 100644 vendor/workerman/validation/data/domain/public-suffix/ZW.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AD.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AF.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AI.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AL.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AO.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AQ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AS.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AT.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AU.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AW.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AX.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/AZ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BA.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BB.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BD.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BF.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BH.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BI.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BJ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BL.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BN.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BO.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BQ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BS.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BT.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BV.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BW.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BY.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/BZ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CA.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CC.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CD.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CF.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CH.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CI.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CK.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CL.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CN.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CO.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CU.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CV.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CW.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CX.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CY.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/CZ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/DE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/DJ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/DK.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/DM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/DO.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/DZ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/EC.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/EE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/EG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/EH.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/ER.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/ES.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/ET.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/FI.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/FJ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/FK.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/FM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/FO.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/FR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GA.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GB.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GD.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GF.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GH.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GI.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GL.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GN.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GP.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GQ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GS.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GT.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GU.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GW.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/GY.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/HK.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/HM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/HN.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/HR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/HT.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/HU.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/ID.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/IE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/IL.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/IM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/IN.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/IO.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/IQ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/IR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/IS.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/IT.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/JE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/JM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/JO.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/JP.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/KE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/KG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/KH.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/KI.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/KM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/KN.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/KP.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/KR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/KW.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/KY.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/KZ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/LA.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/LB.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/LC.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/LI.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/LK.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/LR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/LS.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/LT.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/LU.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/LV.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/LY.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MA.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MC.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MD.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/ME.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MF.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MH.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MK.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/ML.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MN.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MO.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MP.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MQ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MS.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MT.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MU.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MV.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MW.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MX.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MY.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/MZ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/NA.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/NC.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/NE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/NF.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/NG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/NI.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/NL.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/NO.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/NP.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/NR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/NU.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/NZ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/OM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PA.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PF.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PH.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PK.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PL.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PN.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PS.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PT.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PW.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/PY.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/QA.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/RE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/RO.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/RS.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/RU.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/RW.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SA.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SB.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SC.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SD.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SH.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SI.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SJ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SK.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SL.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SN.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SO.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SS.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/ST.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SV.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SX.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SY.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/SZ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TC.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TD.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TF.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TH.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TJ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TK.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TL.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TN.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TO.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TR.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TT.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TV.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TW.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/TZ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/UA.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/UG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/UM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/US.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/UY.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/UZ.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/VA.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/VC.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/VE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/VG.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/VI.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/VN.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/VU.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/WF.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/WS.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/YE.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/YT.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/ZA.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/ZM.php create mode 100644 vendor/workerman/validation/data/iso_3166-2/ZW.php create mode 100644 vendor/workerman/validation/library/ChainedValidator.php create mode 100644 vendor/workerman/validation/library/Exceptions/AllOfException.php create mode 100644 vendor/workerman/validation/library/Exceptions/AlnumException.php create mode 100644 vendor/workerman/validation/library/Exceptions/AlphaException.php create mode 100644 vendor/workerman/validation/library/Exceptions/AlwaysInvalidException.php create mode 100644 vendor/workerman/validation/library/Exceptions/AlwaysValidException.php create mode 100644 vendor/workerman/validation/library/Exceptions/AnyOfException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ArrayTypeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ArrayValException.php create mode 100644 vendor/workerman/validation/library/Exceptions/AttributeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/Base64Exception.php create mode 100644 vendor/workerman/validation/library/Exceptions/BaseException.php create mode 100644 vendor/workerman/validation/library/Exceptions/BetweenException.php create mode 100644 vendor/workerman/validation/library/Exceptions/BoolTypeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/BoolValException.php create mode 100644 vendor/workerman/validation/library/Exceptions/BsnException.php create mode 100644 vendor/workerman/validation/library/Exceptions/CallException.php create mode 100644 vendor/workerman/validation/library/Exceptions/CallableTypeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/CallbackException.php create mode 100644 vendor/workerman/validation/library/Exceptions/CharsetException.php create mode 100644 vendor/workerman/validation/library/Exceptions/CnhException.php create mode 100644 vendor/workerman/validation/library/Exceptions/CnpjException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ComponentException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ConsonantException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ContainsAnyException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ContainsException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ControlException.php create mode 100644 vendor/workerman/validation/library/Exceptions/CountableException.php create mode 100644 vendor/workerman/validation/library/Exceptions/CountryCodeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/CpfException.php create mode 100644 vendor/workerman/validation/library/Exceptions/CreditCardException.php create mode 100644 vendor/workerman/validation/library/Exceptions/CurrencyCodeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/DateException.php create mode 100644 vendor/workerman/validation/library/Exceptions/DateTimeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/DecimalException.php create mode 100644 vendor/workerman/validation/library/Exceptions/DigitException.php create mode 100644 vendor/workerman/validation/library/Exceptions/DirectoryException.php create mode 100644 vendor/workerman/validation/library/Exceptions/DomainException.php create mode 100644 vendor/workerman/validation/library/Exceptions/EachException.php create mode 100644 vendor/workerman/validation/library/Exceptions/EmailException.php create mode 100644 vendor/workerman/validation/library/Exceptions/EndsWithException.php create mode 100644 vendor/workerman/validation/library/Exceptions/EqualsException.php create mode 100644 vendor/workerman/validation/library/Exceptions/EquivalentException.php create mode 100644 vendor/workerman/validation/library/Exceptions/EvenException.php create mode 100644 vendor/workerman/validation/library/Exceptions/Exception.php create mode 100644 vendor/workerman/validation/library/Exceptions/ExecutableException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ExistsException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ExtensionException.php create mode 100644 vendor/workerman/validation/library/Exceptions/FactorException.php create mode 100644 vendor/workerman/validation/library/Exceptions/FalseValException.php create mode 100644 vendor/workerman/validation/library/Exceptions/FibonacciException.php create mode 100644 vendor/workerman/validation/library/Exceptions/FileException.php create mode 100644 vendor/workerman/validation/library/Exceptions/FilterVarException.php create mode 100644 vendor/workerman/validation/library/Exceptions/FilteredValidationException.php create mode 100644 vendor/workerman/validation/library/Exceptions/FiniteException.php create mode 100644 vendor/workerman/validation/library/Exceptions/FloatTypeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/FloatValException.php create mode 100644 vendor/workerman/validation/library/Exceptions/GraphException.php create mode 100644 vendor/workerman/validation/library/Exceptions/GreaterThanException.php create mode 100644 vendor/workerman/validation/library/Exceptions/GroupedValidationException.php create mode 100644 vendor/workerman/validation/library/Exceptions/HexRgbColorException.php create mode 100644 vendor/workerman/validation/library/Exceptions/IbanException.php create mode 100644 vendor/workerman/validation/library/Exceptions/IdenticalException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ImageException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ImeiException.php create mode 100644 vendor/workerman/validation/library/Exceptions/InException.php create mode 100644 vendor/workerman/validation/library/Exceptions/InfiniteException.php create mode 100644 vendor/workerman/validation/library/Exceptions/InstanceException.php create mode 100644 vendor/workerman/validation/library/Exceptions/IntTypeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/IntValException.php create mode 100644 vendor/workerman/validation/library/Exceptions/InvalidClassException.php create mode 100644 vendor/workerman/validation/library/Exceptions/IpException.php create mode 100644 vendor/workerman/validation/library/Exceptions/IsbnException.php create mode 100644 vendor/workerman/validation/library/Exceptions/IterableTypeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/JsonException.php create mode 100644 vendor/workerman/validation/library/Exceptions/KeyException.php create mode 100644 vendor/workerman/validation/library/Exceptions/KeyNestedException.php create mode 100644 vendor/workerman/validation/library/Exceptions/KeySetException.php create mode 100644 vendor/workerman/validation/library/Exceptions/KeyValueException.php create mode 100644 vendor/workerman/validation/library/Exceptions/LanguageCodeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/LeapDateException.php create mode 100644 vendor/workerman/validation/library/Exceptions/LeapYearException.php create mode 100644 vendor/workerman/validation/library/Exceptions/LengthException.php create mode 100644 vendor/workerman/validation/library/Exceptions/LessThanException.php create mode 100644 vendor/workerman/validation/library/Exceptions/LowercaseException.php create mode 100644 vendor/workerman/validation/library/Exceptions/LuhnException.php create mode 100644 vendor/workerman/validation/library/Exceptions/MacAddressException.php create mode 100644 vendor/workerman/validation/library/Exceptions/MaxAgeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/MaxException.php create mode 100644 vendor/workerman/validation/library/Exceptions/MimetypeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/MinAgeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/MinException.php create mode 100644 vendor/workerman/validation/library/Exceptions/MultipleException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NegativeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NestedValidationException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NfeAccessKeyException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NifException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NipException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NoException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NoWhitespaceException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NonOmissibleException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NoneOfException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NotBlankException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NotEmojiException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NotEmptyException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NotException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NotOptionalException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NullTypeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NullableException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NumberException.php create mode 100644 vendor/workerman/validation/library/Exceptions/NumericValException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ObjectTypeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/OddException.php create mode 100644 vendor/workerman/validation/library/Exceptions/OneOfException.php create mode 100644 vendor/workerman/validation/library/Exceptions/OptionalException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PerfectSquareException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PeselException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PhoneException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PhpLabelException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PisException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PolishIdCardException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PortugueseNifException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PositiveException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PostalCodeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PrimeNumberException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PrintableException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PublicDomainSuffixException.php create mode 100644 vendor/workerman/validation/library/Exceptions/PunctException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ReadableException.php create mode 100644 vendor/workerman/validation/library/Exceptions/RecursiveExceptionIterator.php create mode 100644 vendor/workerman/validation/library/Exceptions/RegexException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ResourceTypeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/RomanException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ScalarValException.php create mode 100644 vendor/workerman/validation/library/Exceptions/SfException.php create mode 100644 vendor/workerman/validation/library/Exceptions/SizeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/SlugException.php create mode 100644 vendor/workerman/validation/library/Exceptions/SortedException.php create mode 100644 vendor/workerman/validation/library/Exceptions/SpaceException.php create mode 100644 vendor/workerman/validation/library/Exceptions/StartsWithException.php create mode 100644 vendor/workerman/validation/library/Exceptions/StringTypeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/StringValException.php create mode 100644 vendor/workerman/validation/library/Exceptions/SubdivisionCodeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/SubsetException.php create mode 100644 vendor/workerman/validation/library/Exceptions/SymbolicLinkException.php create mode 100644 vendor/workerman/validation/library/Exceptions/TimeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/TldException.php create mode 100644 vendor/workerman/validation/library/Exceptions/TrueValException.php create mode 100644 vendor/workerman/validation/library/Exceptions/TypeException.php create mode 100644 vendor/workerman/validation/library/Exceptions/UniqueException.php create mode 100644 vendor/workerman/validation/library/Exceptions/UploadedException.php create mode 100644 vendor/workerman/validation/library/Exceptions/UppercaseException.php create mode 100644 vendor/workerman/validation/library/Exceptions/UrlException.php create mode 100644 vendor/workerman/validation/library/Exceptions/UuidException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ValidationException.php create mode 100644 vendor/workerman/validation/library/Exceptions/ValidatorException.php create mode 100644 vendor/workerman/validation/library/Exceptions/VersionException.php create mode 100644 vendor/workerman/validation/library/Exceptions/VideoUrlException.php create mode 100644 vendor/workerman/validation/library/Exceptions/VowelException.php create mode 100644 vendor/workerman/validation/library/Exceptions/WhenException.php create mode 100644 vendor/workerman/validation/library/Exceptions/WritableException.php create mode 100644 vendor/workerman/validation/library/Exceptions/XdigitException.php create mode 100644 vendor/workerman/validation/library/Exceptions/YesException.php create mode 100644 vendor/workerman/validation/library/Factory.php create mode 100644 vendor/workerman/validation/library/Helpers/CanCompareValues.php create mode 100644 vendor/workerman/validation/library/Helpers/CanValidateDateTime.php create mode 100644 vendor/workerman/validation/library/Helpers/CanValidateIterable.php create mode 100644 vendor/workerman/validation/library/Helpers/CanValidateUndefined.php create mode 100644 vendor/workerman/validation/library/Helpers/CountryInfo.php create mode 100644 vendor/workerman/validation/library/Helpers/DomainInfo.php create mode 100644 vendor/workerman/validation/library/Message/Formatter.php create mode 100644 vendor/workerman/validation/library/Message/ParameterStringifier.php create mode 100644 vendor/workerman/validation/library/Message/Stringifier/KeepOriginalStringName.php create mode 100644 vendor/workerman/validation/library/NonNegatable.php create mode 100644 vendor/workerman/validation/library/Rules/AbstractAge.php create mode 100644 vendor/workerman/validation/library/Rules/AbstractComparison.php create mode 100644 vendor/workerman/validation/library/Rules/AbstractComposite.php create mode 100644 vendor/workerman/validation/library/Rules/AbstractEnvelope.php create mode 100644 vendor/workerman/validation/library/Rules/AbstractFilterRule.php create mode 100644 vendor/workerman/validation/library/Rules/AbstractRelated.php create mode 100644 vendor/workerman/validation/library/Rules/AbstractRule.php create mode 100644 vendor/workerman/validation/library/Rules/AbstractSearcher.php create mode 100644 vendor/workerman/validation/library/Rules/AbstractWrapper.php create mode 100644 vendor/workerman/validation/library/Rules/AllOf.php create mode 100644 vendor/workerman/validation/library/Rules/Alnum.php create mode 100644 vendor/workerman/validation/library/Rules/Alpha.php create mode 100644 vendor/workerman/validation/library/Rules/AlwaysInvalid.php create mode 100644 vendor/workerman/validation/library/Rules/AlwaysValid.php create mode 100644 vendor/workerman/validation/library/Rules/AnyOf.php create mode 100644 vendor/workerman/validation/library/Rules/ArrayType.php create mode 100644 vendor/workerman/validation/library/Rules/ArrayVal.php create mode 100644 vendor/workerman/validation/library/Rules/Attribute.php create mode 100644 vendor/workerman/validation/library/Rules/Base.php create mode 100644 vendor/workerman/validation/library/Rules/Base64.php create mode 100644 vendor/workerman/validation/library/Rules/Between.php create mode 100644 vendor/workerman/validation/library/Rules/BoolType.php create mode 100644 vendor/workerman/validation/library/Rules/BoolVal.php create mode 100644 vendor/workerman/validation/library/Rules/Bsn.php create mode 100644 vendor/workerman/validation/library/Rules/Call.php create mode 100644 vendor/workerman/validation/library/Rules/CallableType.php create mode 100644 vendor/workerman/validation/library/Rules/Callback.php create mode 100644 vendor/workerman/validation/library/Rules/Charset.php create mode 100644 vendor/workerman/validation/library/Rules/Cnh.php create mode 100644 vendor/workerman/validation/library/Rules/Cnpj.php create mode 100644 vendor/workerman/validation/library/Rules/Consonant.php create mode 100644 vendor/workerman/validation/library/Rules/Contains.php create mode 100644 vendor/workerman/validation/library/Rules/ContainsAny.php create mode 100644 vendor/workerman/validation/library/Rules/Control.php create mode 100644 vendor/workerman/validation/library/Rules/Countable.php create mode 100644 vendor/workerman/validation/library/Rules/CountryCode.php create mode 100644 vendor/workerman/validation/library/Rules/Cpf.php create mode 100644 vendor/workerman/validation/library/Rules/CreditCard.php create mode 100644 vendor/workerman/validation/library/Rules/CurrencyCode.php create mode 100644 vendor/workerman/validation/library/Rules/Date.php create mode 100644 vendor/workerman/validation/library/Rules/DateTime.php create mode 100644 vendor/workerman/validation/library/Rules/Decimal.php create mode 100644 vendor/workerman/validation/library/Rules/Digit.php create mode 100644 vendor/workerman/validation/library/Rules/Directory.php create mode 100644 vendor/workerman/validation/library/Rules/Domain.php create mode 100644 vendor/workerman/validation/library/Rules/Each.php create mode 100644 vendor/workerman/validation/library/Rules/Email.php create mode 100644 vendor/workerman/validation/library/Rules/EndsWith.php create mode 100644 vendor/workerman/validation/library/Rules/Equals.php create mode 100644 vendor/workerman/validation/library/Rules/Equivalent.php create mode 100644 vendor/workerman/validation/library/Rules/Even.php create mode 100644 vendor/workerman/validation/library/Rules/Executable.php create mode 100644 vendor/workerman/validation/library/Rules/Exists.php create mode 100644 vendor/workerman/validation/library/Rules/Extension.php create mode 100644 vendor/workerman/validation/library/Rules/Factor.php create mode 100644 vendor/workerman/validation/library/Rules/FalseVal.php create mode 100644 vendor/workerman/validation/library/Rules/Fibonacci.php create mode 100644 vendor/workerman/validation/library/Rules/File.php create mode 100644 vendor/workerman/validation/library/Rules/FilterVar.php create mode 100644 vendor/workerman/validation/library/Rules/Finite.php create mode 100644 vendor/workerman/validation/library/Rules/FloatType.php create mode 100644 vendor/workerman/validation/library/Rules/FloatVal.php create mode 100644 vendor/workerman/validation/library/Rules/Graph.php create mode 100644 vendor/workerman/validation/library/Rules/GreaterThan.php create mode 100644 vendor/workerman/validation/library/Rules/HexRgbColor.php create mode 100644 vendor/workerman/validation/library/Rules/Iban.php create mode 100644 vendor/workerman/validation/library/Rules/Identical.php create mode 100644 vendor/workerman/validation/library/Rules/Image.php create mode 100644 vendor/workerman/validation/library/Rules/Imei.php create mode 100644 vendor/workerman/validation/library/Rules/In.php create mode 100644 vendor/workerman/validation/library/Rules/Infinite.php create mode 100644 vendor/workerman/validation/library/Rules/Instance.php create mode 100644 vendor/workerman/validation/library/Rules/IntType.php create mode 100644 vendor/workerman/validation/library/Rules/IntVal.php create mode 100644 vendor/workerman/validation/library/Rules/Ip.php create mode 100644 vendor/workerman/validation/library/Rules/Isbn.php create mode 100644 vendor/workerman/validation/library/Rules/IterableType.php create mode 100644 vendor/workerman/validation/library/Rules/Json.php create mode 100644 vendor/workerman/validation/library/Rules/Key.php create mode 100644 vendor/workerman/validation/library/Rules/KeyNested.php create mode 100644 vendor/workerman/validation/library/Rules/KeySet.php create mode 100644 vendor/workerman/validation/library/Rules/KeyValue.php create mode 100644 vendor/workerman/validation/library/Rules/LanguageCode.php create mode 100644 vendor/workerman/validation/library/Rules/LeapDate.php create mode 100644 vendor/workerman/validation/library/Rules/LeapYear.php create mode 100644 vendor/workerman/validation/library/Rules/Length.php create mode 100644 vendor/workerman/validation/library/Rules/LessThan.php create mode 100644 vendor/workerman/validation/library/Rules/Lowercase.php create mode 100644 vendor/workerman/validation/library/Rules/Luhn.php create mode 100644 vendor/workerman/validation/library/Rules/MacAddress.php create mode 100644 vendor/workerman/validation/library/Rules/Max.php create mode 100644 vendor/workerman/validation/library/Rules/MaxAge.php create mode 100644 vendor/workerman/validation/library/Rules/Mimetype.php create mode 100644 vendor/workerman/validation/library/Rules/Min.php create mode 100644 vendor/workerman/validation/library/Rules/MinAge.php create mode 100644 vendor/workerman/validation/library/Rules/Mobile.php create mode 100644 vendor/workerman/validation/library/Rules/Multiple.php create mode 100644 vendor/workerman/validation/library/Rules/Negative.php create mode 100644 vendor/workerman/validation/library/Rules/NfeAccessKey.php create mode 100644 vendor/workerman/validation/library/Rules/Nif.php create mode 100644 vendor/workerman/validation/library/Rules/Nip.php create mode 100644 vendor/workerman/validation/library/Rules/No.php create mode 100644 vendor/workerman/validation/library/Rules/NoWhitespace.php create mode 100644 vendor/workerman/validation/library/Rules/NoneOf.php create mode 100644 vendor/workerman/validation/library/Rules/Not.php create mode 100644 vendor/workerman/validation/library/Rules/NotBlank.php create mode 100644 vendor/workerman/validation/library/Rules/NotEmoji.php create mode 100644 vendor/workerman/validation/library/Rules/NotEmpty.php create mode 100644 vendor/workerman/validation/library/Rules/NotOptional.php create mode 100644 vendor/workerman/validation/library/Rules/NullType.php create mode 100644 vendor/workerman/validation/library/Rules/Nullable.php create mode 100644 vendor/workerman/validation/library/Rules/Number.php create mode 100644 vendor/workerman/validation/library/Rules/NumericVal.php create mode 100644 vendor/workerman/validation/library/Rules/ObjectType.php create mode 100644 vendor/workerman/validation/library/Rules/Odd.php create mode 100644 vendor/workerman/validation/library/Rules/OneOf.php create mode 100644 vendor/workerman/validation/library/Rules/Optional.php create mode 100644 vendor/workerman/validation/library/Rules/PerfectSquare.php create mode 100644 vendor/workerman/validation/library/Rules/Pesel.php create mode 100644 vendor/workerman/validation/library/Rules/Phone.php create mode 100644 vendor/workerman/validation/library/Rules/PhpLabel.php create mode 100644 vendor/workerman/validation/library/Rules/Pis.php create mode 100644 vendor/workerman/validation/library/Rules/PolishIdCard.php create mode 100644 vendor/workerman/validation/library/Rules/PortugueseNif.php create mode 100644 vendor/workerman/validation/library/Rules/Positive.php create mode 100644 vendor/workerman/validation/library/Rules/PostalCode.php create mode 100644 vendor/workerman/validation/library/Rules/PrimeNumber.php create mode 100644 vendor/workerman/validation/library/Rules/Printable.php create mode 100644 vendor/workerman/validation/library/Rules/PublicDomainSuffix.php create mode 100644 vendor/workerman/validation/library/Rules/Punct.php create mode 100644 vendor/workerman/validation/library/Rules/Readable.php create mode 100644 vendor/workerman/validation/library/Rules/Regex.php create mode 100644 vendor/workerman/validation/library/Rules/ResourceType.php create mode 100644 vendor/workerman/validation/library/Rules/Roman.php create mode 100644 vendor/workerman/validation/library/Rules/ScalarVal.php create mode 100644 vendor/workerman/validation/library/Rules/Size.php create mode 100644 vendor/workerman/validation/library/Rules/Slug.php create mode 100644 vendor/workerman/validation/library/Rules/Sorted.php create mode 100644 vendor/workerman/validation/library/Rules/Space.php create mode 100644 vendor/workerman/validation/library/Rules/StartsWith.php create mode 100644 vendor/workerman/validation/library/Rules/StringType.php create mode 100644 vendor/workerman/validation/library/Rules/StringVal.php create mode 100644 vendor/workerman/validation/library/Rules/SubdivisionCode.php create mode 100644 vendor/workerman/validation/library/Rules/Subset.php create mode 100644 vendor/workerman/validation/library/Rules/SymbolicLink.php create mode 100644 vendor/workerman/validation/library/Rules/Time.php create mode 100644 vendor/workerman/validation/library/Rules/Tld.php create mode 100644 vendor/workerman/validation/library/Rules/TrueVal.php create mode 100644 vendor/workerman/validation/library/Rules/Type.php create mode 100644 vendor/workerman/validation/library/Rules/Unique.php create mode 100644 vendor/workerman/validation/library/Rules/Uploaded.php create mode 100644 vendor/workerman/validation/library/Rules/Uppercase.php create mode 100644 vendor/workerman/validation/library/Rules/Url.php create mode 100644 vendor/workerman/validation/library/Rules/Uuid.php create mode 100644 vendor/workerman/validation/library/Rules/Version.php create mode 100644 vendor/workerman/validation/library/Rules/VideoUrl.php create mode 100644 vendor/workerman/validation/library/Rules/Vowel.php create mode 100644 vendor/workerman/validation/library/Rules/When.php create mode 100644 vendor/workerman/validation/library/Rules/Writable.php create mode 100644 vendor/workerman/validation/library/Rules/Xdigit.php create mode 100644 vendor/workerman/validation/library/Rules/Yes.php create mode 100644 vendor/workerman/validation/library/StaticValidator.php create mode 100644 vendor/workerman/validation/library/Validatable.php create mode 100644 vendor/workerman/validation/library/Validator.php create mode 100644 vendor/workerman/webman-framework/.gitignore create mode 100644 vendor/workerman/webman-framework/README.md create mode 100644 vendor/workerman/webman-framework/composer.json create mode 100644 vendor/workerman/webman-framework/src/App.php create mode 100644 vendor/workerman/webman-framework/src/Bootstrap.php create mode 100644 vendor/workerman/webman-framework/src/Config.php create mode 100644 vendor/workerman/webman-framework/src/Container.php create mode 100644 vendor/workerman/webman-framework/src/Context.php create mode 100644 vendor/workerman/webman-framework/src/Exception/ExceptionHandler.php create mode 100644 vendor/workerman/webman-framework/src/Exception/ExceptionHandlerInterface.php create mode 100644 vendor/workerman/webman-framework/src/Exception/FileException.php create mode 100644 vendor/workerman/webman-framework/src/Exception/NotFoundException.php create mode 100644 vendor/workerman/webman-framework/src/File.php create mode 100644 vendor/workerman/webman-framework/src/FileSessionHandler.php create mode 100644 vendor/workerman/webman-framework/src/Http/Request.php create mode 100644 vendor/workerman/webman-framework/src/Http/Response.php create mode 100644 vendor/workerman/webman-framework/src/Http/UploadFile.php create mode 100644 vendor/workerman/webman-framework/src/Install.php create mode 100644 vendor/workerman/webman-framework/src/Middleware.php create mode 100644 vendor/workerman/webman-framework/src/MiddlewareInterface.php create mode 100644 vendor/workerman/webman-framework/src/Route.php create mode 100644 vendor/workerman/webman-framework/src/Route/Route.php create mode 100644 vendor/workerman/webman-framework/src/Session/FileSessionHandler.php create mode 100644 vendor/workerman/webman-framework/src/Session/RedisClusterSessionHandler.php create mode 100644 vendor/workerman/webman-framework/src/Session/RedisSessionHandler.php create mode 100644 vendor/workerman/webman-framework/src/Util.php create mode 100644 vendor/workerman/webman-framework/src/View.php create mode 100644 vendor/workerman/webman-framework/src/support/App.php create mode 100644 vendor/workerman/webman-framework/src/support/Cache.php create mode 100644 vendor/workerman/webman-framework/src/support/Container.php create mode 100644 vendor/workerman/webman-framework/src/support/Context.php create mode 100644 vendor/workerman/webman-framework/src/support/Db.php create mode 100644 vendor/workerman/webman-framework/src/support/Log.php create mode 100644 vendor/workerman/webman-framework/src/support/Model.php create mode 100644 vendor/workerman/webman-framework/src/support/Plugin.php create mode 100644 vendor/workerman/webman-framework/src/support/Redis.php create mode 100644 vendor/workerman/webman-framework/src/support/Request.php create mode 100644 vendor/workerman/webman-framework/src/support/Response.php create mode 100644 vendor/workerman/webman-framework/src/support/Translation.php create mode 100644 vendor/workerman/webman-framework/src/support/View.php create mode 100644 vendor/workerman/webman-framework/src/support/bootstrap/LaravelDb.php create mode 100644 vendor/workerman/webman-framework/src/support/bootstrap/Session.php create mode 100644 vendor/workerman/webman-framework/src/support/exception/BusinessException.php create mode 100644 vendor/workerman/webman-framework/src/support/exception/Handler.php create mode 100644 vendor/workerman/webman-framework/src/support/view/Blade.php create mode 100644 vendor/workerman/webman-framework/src/support/view/Raw.php create mode 100644 vendor/workerman/webman-framework/src/support/view/ThinkPHP.php create mode 100644 vendor/workerman/webman-framework/src/support/view/Twig.php create mode 100644 vendor/workerman/workerman/.github/FUNDING.yml create mode 100644 vendor/workerman/workerman/.gitignore create mode 100644 vendor/workerman/workerman/Autoloader.php create mode 100644 vendor/workerman/workerman/Connection/AsyncTcpConnection.php create mode 100644 vendor/workerman/workerman/Connection/AsyncUdpConnection.php create mode 100644 vendor/workerman/workerman/Connection/ConnectionInterface.php create mode 100644 vendor/workerman/workerman/Connection/TcpConnection.php create mode 100644 vendor/workerman/workerman/Connection/UdpConnection.php create mode 100644 vendor/workerman/workerman/Events/Ev.php create mode 100644 vendor/workerman/workerman/Events/Event.php create mode 100644 vendor/workerman/workerman/Events/EventInterface.php create mode 100644 vendor/workerman/workerman/Events/Libevent.php create mode 100644 vendor/workerman/workerman/Events/React/Base.php create mode 100644 vendor/workerman/workerman/Events/React/ExtEventLoop.php create mode 100644 vendor/workerman/workerman/Events/React/ExtLibEventLoop.php create mode 100644 vendor/workerman/workerman/Events/React/StreamSelectLoop.php create mode 100644 vendor/workerman/workerman/Events/Select.php create mode 100644 vendor/workerman/workerman/Events/Swoole.php create mode 100644 vendor/workerman/workerman/Events/Uv.php create mode 100644 vendor/workerman/workerman/Lib/Constants.php create mode 100644 vendor/workerman/workerman/Lib/Timer.php create mode 100644 vendor/workerman/workerman/MIT-LICENSE.txt create mode 100644 vendor/workerman/workerman/Protocols/Frame.php create mode 100644 vendor/workerman/workerman/Protocols/Http.php create mode 100644 vendor/workerman/workerman/Protocols/Http/Chunk.php create mode 100644 vendor/workerman/workerman/Protocols/Http/Request.php create mode 100644 vendor/workerman/workerman/Protocols/Http/Response.php create mode 100644 vendor/workerman/workerman/Protocols/Http/ServerSentEvents.php create mode 100644 vendor/workerman/workerman/Protocols/Http/Session.php create mode 100644 vendor/workerman/workerman/Protocols/Http/Session/FileSessionHandler.php create mode 100644 vendor/workerman/workerman/Protocols/Http/Session/RedisClusterSessionHandler.php create mode 100644 vendor/workerman/workerman/Protocols/Http/Session/RedisSessionHandler.php create mode 100644 vendor/workerman/workerman/Protocols/Http/Session/SessionHandlerInterface.php create mode 100644 vendor/workerman/workerman/Protocols/Http/mime.types create mode 100644 vendor/workerman/workerman/Protocols/ProtocolInterface.php create mode 100644 vendor/workerman/workerman/Protocols/Text.php create mode 100644 vendor/workerman/workerman/Protocols/Websocket.php create mode 100644 vendor/workerman/workerman/Protocols/Ws.php create mode 100644 vendor/workerman/workerman/README.md create mode 100644 vendor/workerman/workerman/Timer.php create mode 100644 vendor/workerman/workerman/Worker.php create mode 100644 vendor/workerman/workerman/composer.json create mode 100644 vendor/yzh52521/easyhttp/LICENSE create mode 100644 vendor/yzh52521/easyhttp/README.md create mode 100644 vendor/yzh52521/easyhttp/composer.json create mode 100644 vendor/yzh52521/easyhttp/src/ConnectionException.php create mode 100755 vendor/yzh52521/easyhttp/src/Facade.php create mode 100644 vendor/yzh52521/easyhttp/src/Http.php create mode 100644 vendor/yzh52521/easyhttp/src/Logger.php create mode 100644 vendor/yzh52521/easyhttp/src/Request.php create mode 100644 vendor/yzh52521/easyhttp/src/RequestException.php create mode 100644 vendor/yzh52521/easyhttp/src/Response.php create mode 100644 vendor/yzh52521/easyhttp/src/Retry.php create mode 100644 webmaninit/.gitignore create mode 100644 windows.bat create mode 100644 windows.php diff --git a/.env.case b/.env.case new file mode 100644 index 0000000..2054dad --- /dev/null +++ b/.env.case @@ -0,0 +1,24 @@ +appid="" +#企业微信的企业ID + + +agentid="" +#企业微信APPID + +token="" +#企业微信APP Token + +weburl="auth.laysense.cn" +#本站URL + + +aeskey="" +#16位AES加密key + + +aesiv="" +#16位AES加密iv + +dbuser="" +dbpass="" +#数据库用户名和密码 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..99b8d20 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +token/qywx/*.token +runtime/*/*.log diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2c66292 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 walkor and contributors (see https://github.com/walkor/webman/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/README.md b/README.md new file mode 100644 index 0000000..16ed3e1 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Webman INIT + +Webman的启动模板: + +``` +wget https://git.laysense.com/enoch/webmaninit/archive/main.tar.gz +tar -zxvf ./main.tar.gz +rm -rf ./main.tar.gz +mv webmaninit/* ./ +``` \ No newline at end of file diff --git a/app/controller/IndexController.php b/app/controller/IndexController.php new file mode 100644 index 0000000..435a182 --- /dev/null +++ b/app/controller/IndexController.php @@ -0,0 +1,28 @@ + 'webman']); + } + + public function json(Request $request) + { + return json(['code' => 0, 'msg' => 'ok']); + } + +} diff --git a/app/controller/LayAuth.php b/app/controller/LayAuth.php new file mode 100644 index 0000000..44e590f --- /dev/null +++ b/app/controller/LayAuth.php @@ -0,0 +1,88 @@ +where('ID', $appid); + if($appquery->doesntExist()){ + return view('404'); + } + $app=$appquery->first(); + $provider= Db::table('Provider')->where('ID', $app->provider)->first(); + return view('auth', ['app'=>$app,'provider'=>$provider]); + + } + + public function callback(Request $request,$appid,$gateway) + { + $appquery= Db::table('App')->where('ID', $appid); + if($appquery->doesntExist()){ + return view('404'); + } + $app=$appquery->first(); + $provider= Db::table('Provider')->where('ID', $app->provider)->first(); + switch ($gateway) { + case "qywx": + $code = $request->input('code','null'); + if($code=='null'){ + return view('auth', ['app'=>$app,'provider'=>$provider,'special'=>'登陆信息无效']); + } + $tokenfile=base_path().'/token/qywx/innerQYWX.token'; + if(file_exists($tokenfile)){ + $tokencontent=json_decode(file_get_contents($tokenfile)); + $fulltoken=$tokencontent->token; + $ddl=$tokencontent->ddl; + if($ddl-time()<180){ + $reapply=true; + }else{ + $reapply=false; + } + }else{ + $reapply=true; + } + if($reapply==true){ + $apply=$response = Http::get('https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid='.getenv('appid').'&corpsecret='.getenv('token'))->json(); + $fulltoken=$apply->access_token; + $ddl=time()+$apply->expires_in; + $file=fopen($tokenfile,"w"); + fwrite($file, json_encode(array('token'=>$fulltoken,'ddl'=>$ddl))); + fclose($file); + } + $lookup= Http::get('https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token='.$fulltoken.'&code='.$code)->json(); + if($lookup->errcode!=0){ + return view('auth', ['app'=>$app,'provider'=>$provider,'special'=>'登陆信息无效']); + }else{ + $userid=$lookup->userid; + $userinfo=Http::get('https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token='.$fulltoken.'&userid='.$userid)->json(); + $username=$userinfo->name; + $userposition=$userinfo->position; + $WT=json_encode(['id'=>$userid,'name'=>$username,'position'=>$userposition,'time'=>time()]); + $key = getenv('aeskey'); + $iv = getenv('aesiv'); + $WT = encryptAES($WT, $key, $iv); + $dest=$app->redirect; + return view('success', ['app'=>$app,'provider'=>$provider,'dest'=>$dest,'userinfo'=>$userinfo])->cookie('WT', $WT,time()+9600,'/','.laysense.cn'); + } + break; + default: + return view('auth', ['app'=>$app,'provider'=>$provider,'special'=>'验证方式无效或不存在']); + } + + return view('auth', ['app'=>$app,'provider'=>$provider,'special'=>'验证方式无效或不存在']); + } + + public function check(Request $request,$appid) + { + return json(['code' => 0, 'msg' => 'ok']); + } + +} diff --git a/app/functions.php b/app/functions.php new file mode 100644 index 0000000..5c9c58d --- /dev/null +++ b/app/functions.php @@ -0,0 +1,4 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace app\middleware; + +use Webman\MiddlewareInterface; +use Webman\Http\Response; +use Webman\Http\Request; + +/** + * Class StaticFile + * @package app\middleware + */ +class StaticFile implements MiddlewareInterface +{ + public function process(Request $request, callable $next): Response + { + // Access to files beginning with. Is prohibited + if (strpos($request->path(), '/.') !== false) { + return response('

403 forbidden

', 403); + } + /** @var Response $response */ + $response = $next($request); + // Add cross domain HTTP header + /*$response->withHeaders([ + 'Access-Control-Allow-Origin' => '*', + 'Access-Control-Allow-Credentials' => 'true', + ]);*/ + return $response; + } +} diff --git a/app/model/Test.php b/app/model/Test.php new file mode 100644 index 0000000..92d70e3 --- /dev/null +++ b/app/model/Test.php @@ -0,0 +1,29 @@ + + + + + + + + + + + Laysense Auth + + + + + + + + + + + + + + + +
+ +
+
+
+

应用未注册或不存在

+

+ 抱歉,未能找到该应用的注册信息,可能是应用不存在或已被删除,您可以选择 + + 前往来笙 + +

+ + +
+
+
+ +

+ ©上海来笙信息科技有限公司 2024

+ +
+ + + + + + + + + + + \ No newline at end of file diff --git a/app/view/auth.html b/app/view/auth.html new file mode 100644 index 0000000..493b24d --- /dev/null +++ b/app/view/auth.html @@ -0,0 +1,146 @@ + + + + + + + + + + + + Laysense Auth + + + + + + + + + + + + + + + +
+ +
+
+
+

来笙统一认证

+

+ 没有账号? + + 注册来笙账号 + +

+
+ +
您即将登录
+ +
+
+ Avatar +
+

name);?>

+

info);?>

+
+
+

+ 由 + + name);?> + + 提供 +

+
+ + +
公共登录
+ +
" > + +
+

" > + 当前应用仅支持来笙内部使用,不对外开放 +

+ +
来笙内部登录
+ + +
+
+ + +
" > + +
+ + + +

+ + + 举报该应用 + +

+

+ ©上海来笙信息科技有限公司 2024

+ +

+ + + 来笙隐私条款  + +  此应用的隐私政策 + +

+
+ + + + + + + + + + + \ No newline at end of file diff --git a/app/view/success.html b/app/view/success.html new file mode 100644 index 0000000..d888b9c --- /dev/null +++ b/app/view/success.html @@ -0,0 +1,101 @@ + + + + + + + + + + + Laysense Auth + + + + + + + + + + + + + + + +
+ +
+
+
+

欢迎回来,name);?>

+

+ position);?> +

+
+ +
您即将进入
+ +
+
+ Avatar +
+

name);?>

+

info);?>

+
+
+

+ 由 + + name);?> + + 提供 +

+
+ + +
正在前往,请稍等
+ +
+ + +
+
+
+ + + + + +

+ ©上海来笙信息科技有限公司 2024

+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..daa6a33 --- /dev/null +++ b/composer.json @@ -0,0 +1,71 @@ +{ + "name": "workerman/webman", + "type": "project", + "keywords": [ + "high performance", + "http service" + ], + "homepage": "https://www.workerman.net", + "license": "MIT", + "description": "High performance HTTP Service Framework.", + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net", + "homepage": "https://www.workerman.net", + "role": "Developer" + } + ], + "support": { + "email": "walkor@workerman.net", + "issues": "https://github.com/walkor/webman/issues", + "forum": "https://wenda.workerman.net/", + "wiki": "https://workerman.net/doc/webman", + "source": "https://github.com/walkor/webman" + }, + "require": { + "php": ">=7.2", + "workerman/webman-framework": "^1.5.0", + "monolog/monolog": "^2.0", + "illuminate/database": "^9.52", + "illuminate/pagination": "^9.52", + "illuminate/events": "^9.52", + "symfony/var-dumper": "^6.0", + "jasongrimes/paginator": "~1.0", + "robmorgan/phinx": "^0.14.0", + "illuminate/redis": "^9.52", + "symfony/cache": "^6.0", + "workerman/validation": "^3.1", + "symfony/translation": "^6.0", + "webman/captcha": "^1.0", + "webman/event": "^1.0", + "vlucas/phpdotenv": "^5.6", + "workerman/crontab": "^1.0", + "yzh52521/easyhttp": "^1.1" + }, + "suggest": { + "ext-event": "For better performance. " + }, + "autoload": { + "psr-4": { + "": "./", + "app\\": "./app", + "App\\": "./app", + "app\\View\\Components\\": "./app/view/components" + }, + "files": [ + "./support/helpers.php" + ] + }, + "scripts": { + "post-package-install": [ + "support\\Plugin::install" + ], + "post-package-update": [ + "support\\Plugin::install" + ], + "pre-package-uninstall": [ + "support\\Plugin::uninstall" + ] + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..34b9e08 --- /dev/null +++ b/composer.lock @@ -0,0 +1,4863 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "d3b2156d13053c4f5c49b090883e3785", + "packages": [ + { + "name": "brick/math", + "version": "0.11.0", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478", + "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^9.0", + "vimeo/psalm": "5.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "brick", + "math" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.11.0" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2023-01-15T23:15:59+00:00" + }, + { + "name": "cakephp/core", + "version": "4.5.5", + "source": { + "type": "git", + "url": "https://github.com/cakephp/core.git", + "reference": "c2f4dff110d41e475d1041f2abe236f1c62d0cd0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/core/zipball/c2f4dff110d41e475d1041f2abe236f1c62d0cd0", + "reference": "c2f4dff110d41e475d1041f2abe236f1c62d0cd0", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "cakephp/utility": "^4.0", + "php": ">=7.4.0" + }, + "provide": { + "psr/container-implementation": "^1.0 || ^2.0" + }, + "suggest": { + "cakephp/cache": "To use Configure::store() and restore().", + "cakephp/event": "To use PluginApplicationInterface or plugin applications.", + "league/container": "To use Container and ServiceProvider classes" + }, + "type": "library", + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Cake\\Core\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/core/graphs/contributors" + } + ], + "description": "CakePHP Framework Core classes", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "core", + "framework" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/core" + }, + "time": "2023-10-21T13:30:46+00:00" + }, + { + "name": "cakephp/database", + "version": "4.5.6", + "source": { + "type": "git", + "url": "https://github.com/cakephp/database.git", + "reference": "317739cc32060ef19b6c19c87ac6b64848d78e27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/database/zipball/317739cc32060ef19b6c19c87ac6b64848d78e27", + "reference": "317739cc32060ef19b6c19c87ac6b64848d78e27", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "cakephp/core": "^4.0", + "cakephp/datasource": "^4.0", + "php": ">=7.4.0" + }, + "suggest": { + "cakephp/i18n": "If you are using locale-aware datetime formats or Chronos types.", + "cakephp/log": "If you want to use query logging without providing a logger yourself." + }, + "type": "library", + "autoload": { + "psr-4": { + "Cake\\Database\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/database/graphs/contributors" + } + ], + "description": "Flexible and powerful Database abstraction library with a familiar PDO-like API", + "homepage": "https://cakephp.org", + "keywords": [ + "abstraction", + "cakephp", + "database", + "database abstraction", + "pdo" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/database" + }, + "time": "2023-12-07T12:23:54+00:00" + }, + { + "name": "cakephp/datasource", + "version": "4.5.6", + "source": { + "type": "git", + "url": "https://github.com/cakephp/datasource.git", + "reference": "5d11a35ffc09dee744faaab7f758aeb42c17cfec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/datasource/zipball/5d11a35ffc09dee744faaab7f758aeb42c17cfec", + "reference": "5d11a35ffc09dee744faaab7f758aeb42c17cfec", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "cakephp/core": "^4.0", + "php": ">=7.4.0", + "psr/log": "^1.0 || ^2.0", + "psr/simple-cache": "^1.0 || ^2.0" + }, + "suggest": { + "cakephp/cache": "If you decide to use Query caching.", + "cakephp/collection": "If you decide to use ResultSetInterface.", + "cakephp/utility": "If you decide to use EntityTrait." + }, + "type": "library", + "autoload": { + "psr-4": { + "Cake\\Datasource\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/datasource/graphs/contributors" + } + ], + "description": "Provides connection managing and traits for Entities and Queries that can be reused for different datastores", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "connection management", + "datasource", + "entity", + "query" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/datasource" + }, + "time": "2023-11-05T07:32:10+00:00" + }, + { + "name": "cakephp/utility", + "version": "4.5.6", + "source": { + "type": "git", + "url": "https://github.com/cakephp/utility.git", + "reference": "708929115e5b400e1b5b76d8120ca2e51e2de199" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/utility/zipball/708929115e5b400e1b5b76d8120ca2e51e2de199", + "reference": "708929115e5b400e1b5b76d8120ca2e51e2de199", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "cakephp/core": "^4.0", + "php": ">=7.4.0" + }, + "suggest": { + "ext-intl": "To use Text::transliterate() or Text::slug()", + "lib-ICU": "To use Text::transliterate() or Text::slug()" + }, + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Cake\\Utility\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/utility/graphs/contributors" + } + ], + "description": "CakePHP Utility classes such as Inflector, String, Hash, and Security", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "hash", + "inflector", + "security", + "string", + "utility" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/utility" + }, + "time": "2024-06-23T00:11:14+00:00" + }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "doctrine/dbal": "<3.7.0 || >=4.0.0" + }, + "require-dev": { + "doctrine/dbal": "^3.7.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.1.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2023-12-11T17:09:12+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.10", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.10" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2024-02-18T20:23:39+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.2", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862", + "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2023-11-12T22:16:48+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.9.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2024-07-24T11:22:20+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "1.5.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/67ab6e18aaa14d753cc148911d273f6e6cb6721e", + "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4 || ^5.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2023-05-21T12:31:43+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2024-07-18T11:15:46+00:00" + }, + { + "name": "illuminate/bus", + "version": "v9.52.16", + "source": { + "type": "git", + "url": "https://github.com/illuminate/bus.git", + "reference": "4c719a19c3d8c34b2494a7206f8ffde3eff3f983" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/bus/zipball/4c719a19c3d8c34b2494a7206f8ffde3eff3f983", + "reference": "4c719a19c3d8c34b2494a7206f8ffde3eff3f983", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/pipeline": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2" + }, + "suggest": { + "illuminate/queue": "Required to use closures when chaining jobs (^7.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Bus\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Bus package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-04-18T13:42:14+00:00" + }, + { + "name": "illuminate/collections", + "version": "v9.52.16", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "d3710b0b244bfc62c288c1a87eaa62dd28352d1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/d3710b0b244bfc62c288c1a87eaa62dd28352d1f", + "reference": "d3710b0b244bfc62c288c1a87eaa62dd28352d1f", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "illuminate/conditionable": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "php": "^8.0.2" + }, + "suggest": { + "symfony/var-dumper": "Required to use the dump method (^6.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-06-11T21:17:10+00:00" + }, + { + "name": "illuminate/conditionable", + "version": "v9.52.16", + "source": { + "type": "git", + "url": "https://github.com/illuminate/conditionable.git", + "reference": "bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364", + "reference": "bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Conditionable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-02-01T21:42:32+00:00" + }, + { + "name": "illuminate/container", + "version": "v9.52.16", + "source": { + "type": "git", + "url": "https://github.com/illuminate/container.git", + "reference": "1641dda2d0750b68bb1264a3b37ff3973f2e6265" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/container/zipball/1641dda2d0750b68bb1264a3b37ff3973f2e6265", + "reference": "1641dda2d0750b68bb1264a3b37ff3973f2e6265", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "illuminate/contracts": "^9.0", + "php": "^8.0.2", + "psr/container": "^1.1.1|^2.0.1" + }, + "provide": { + "psr/container-implementation": "1.1|2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Container\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Container package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-01-24T16:54:18+00:00" + }, + { + "name": "illuminate/contracts", + "version": "v9.52.16", + "source": { + "type": "git", + "url": "https://github.com/illuminate/contracts.git", + "reference": "44f65d723b13823baa02ff69751a5948bde60c22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/44f65d723b13823baa02ff69751a5948bde60c22", + "reference": "44f65d723b13823baa02ff69751a5948bde60c22", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^8.0.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/simple-cache": "^1.0|^2.0|^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Contracts\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-02-08T14:36:30+00:00" + }, + { + "name": "illuminate/database", + "version": "v9.52.16", + "source": { + "type": "git", + "url": "https://github.com/illuminate/database.git", + "reference": "93cfc8e1f9ac147e6a2851ecabe8d8f21ad85182" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/database/zipball/93cfc8e1f9ac147e6a2851ecabe8d8f21ad85182", + "reference": "93cfc8e1f9ac147e6a2851ecabe8d8f21ad85182", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "brick/math": "^0.9.3|^0.10.2|^0.11", + "ext-pdo": "*", + "illuminate/collections": "^9.0", + "illuminate/container": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2", + "symfony/console": "^6.0.9" + }, + "suggest": { + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).", + "ext-filter": "Required to use the Postgres database driver.", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.21).", + "illuminate/console": "Required to use the database commands (^9.0).", + "illuminate/events": "Required to use the observers with Eloquent (^9.0).", + "illuminate/filesystem": "Required to use the migrations (^9.0).", + "illuminate/pagination": "Required to paginate the result set (^9.0).", + "symfony/finder": "Required to use Eloquent model factories (^6.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Database\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Database package.", + "homepage": "https://laravel.com", + "keywords": [ + "database", + "laravel", + "orm", + "sql" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-06-11T21:17:10+00:00" + }, + { + "name": "illuminate/events", + "version": "v9.52.16", + "source": { + "type": "git", + "url": "https://github.com/illuminate/events.git", + "reference": "8e534676bac23bc17925f5c74c128f9c09b98f69" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/events/zipball/8e534676bac23bc17925f5c74c128f9c09b98f69", + "reference": "8e534676bac23bc17925f5c74c128f9c09b98f69", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "illuminate/bus": "^9.0", + "illuminate/collections": "^9.0", + "illuminate/container": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Illuminate\\Events\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Events package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-09-15T13:14:12+00:00" + }, + { + "name": "illuminate/macroable", + "version": "v9.52.16", + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "e3bfaf6401742a9c6abca61b9b10e998e5b6449a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/e3bfaf6401742a9c6abca61b9b10e998e5b6449a", + "reference": "e3bfaf6401742a9c6abca61b9b10e998e5b6449a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-08-09T13:29:29+00:00" + }, + { + "name": "illuminate/pagination", + "version": "v9.52.16", + "source": { + "type": "git", + "url": "https://github.com/illuminate/pagination.git", + "reference": "0c913d6af303ae0060d94d74d68d537637f7e6d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/pagination/zipball/0c913d6af303ae0060d94d74d68d537637f7e6d4", + "reference": "0c913d6af303ae0060d94d74d68d537637f7e6d4", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-filter": "*", + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Pagination\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Pagination package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-02-06T02:52:41+00:00" + }, + { + "name": "illuminate/pipeline", + "version": "v9.52.16", + "source": { + "type": "git", + "url": "https://github.com/illuminate/pipeline.git", + "reference": "e0be3f3f79f8235ad7334919ca4094d5074e02f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/pipeline/zipball/e0be3f3f79f8235ad7334919ca4094d5074e02f6", + "reference": "e0be3f3f79f8235ad7334919ca4094d5074e02f6", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "illuminate/contracts": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Pipeline\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Pipeline package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2022-06-09T14:13:53+00:00" + }, + { + "name": "illuminate/redis", + "version": "v9.52.16", + "source": { + "type": "git", + "url": "https://github.com/illuminate/redis.git", + "reference": "d4702b8d6a64a48cfab7259248ecbecf3b6135e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/redis/zipball/d4702b8d6a64a48cfab7259248ecbecf3b6135e2", + "reference": "d4702b8d6a64a48cfab7259248ecbecf3b6135e2", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "php": "^8.0.2" + }, + "suggest": { + "ext-redis": "Required to use the phpredis connector (^4.0|^5.0).", + "predis/predis": "Required to use the predis connector (^1.1.9|^2.0.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Redis\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Redis package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-07-31T15:01:27+00:00" + }, + { + "name": "illuminate/support", + "version": "v9.52.16", + "source": { + "type": "git", + "url": "https://github.com/illuminate/support.git", + "reference": "223c608dbca27232df6213f776bfe7bdeec24874" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/support/zipball/223c608dbca27232df6213f776bfe7bdeec24874", + "reference": "223c608dbca27232df6213f776bfe7bdeec24874", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/inflector": "^2.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-mbstring": "*", + "illuminate/collections": "^9.0", + "illuminate/conditionable": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "nesbot/carbon": "^2.62.1", + "php": "^8.0.2", + "voku/portable-ascii": "^2.0" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "suggest": { + "illuminate/filesystem": "Required to use the composer class (^9.0).", + "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.0.2).", + "ramsey/uuid": "Required to use Str::uuid() (^4.7).", + "symfony/process": "Required to use the composer class (^6.0).", + "symfony/uid": "Required to use Str::ulid() (^6.0).", + "symfony/var-dumper": "Required to use the dd function (^6.0).", + "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Support package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-06-11T21:11:53+00:00" + }, + { + "name": "jasongrimes/paginator", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/jasongrimes/php-paginator.git", + "reference": "3411e3cd0c6479a0b514f26e4358f0273552f221" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jasongrimes/php-paginator/zipball/3411e3cd0c6479a0b514f26e4358f0273552f221", + "reference": "3411e3cd0c6479a0b514f26e4358f0273552f221", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "autoload": { + "psr-0": { + "JasonGrimes": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jason Grimes", + "email": "jason@grimesit.com" + } + ], + "description": "A lightweight PHP paginator, for generating pagination controls in the style of Stack Overflow and Flickr. The 'first' and 'last' page links are shown inline as page numbers, and excess page numbers are replaced by ellipses.", + "homepage": "http://github.com/jasongrimes/php-paginator", + "keywords": [ + "pager", + "pagination", + "paginator" + ], + "support": { + "issues": "https://github.com/jasongrimes/php-paginator/issues", + "source": "https://github.com/jasongrimes/php-paginator/tree/master" + }, + "time": "2018-07-11T18:11:49+00:00" + }, + { + "name": "monolog/monolog", + "version": "2.9.3", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/a30bfe2e142720dfa990d0a7e573997f5d884215", + "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.2", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2@dev", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpspec/prophecy": "^1.15", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.38 || ^9.6.19", + "predis/predis": "^1.1 || ^2.0", + "rollbar/rollbar": "^1.3 || ^2 || ^3", + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/2.9.3" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2024-04-12T20:52:51+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.72.5", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "afd46589c216118ecd48ff2b95d77596af1e57ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/afd46589c216118ecd48ff2b95d77596af1e57ed", + "reference": "afd46589c216118ecd48ff2b95d77596af1e57ed", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "carbonphp/carbon-doctrine-types": "*", + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", + "doctrine/orm": "^2.7 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "*", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev", + "dev-2.x": "2.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2024-06-03T19:18:41+00:00" + }, + { + "name": "nikic/fast-route", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "FastRoute\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "support": { + "issues": "https://github.com/nikic/FastRoute/issues", + "source": "https://github.com/nikic/FastRoute/tree/master" + }, + "time": "2018-02-13T20:26:39+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.3", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:41:07+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "e616d01114759c4c489f93b099585439f795fe35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + }, + "time": "2023-04-10T20:10:41+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/2.0.0" + }, + "time": "2021-07-14T16:41:46+00:00" + }, + { + "name": "psr/simple-cache", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "8707bf3cea6f710bf6ef05491234e3ab06f6432a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/8707bf3cea6f710bf6ef05491234e3ab06f6432a", + "reference": "8707bf3cea6f710bf6ef05491234e3ab06f6432a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/2.0.0" + }, + "time": "2021-10-29T13:22:09+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "respect/stringifier", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/Respect/Stringifier.git", + "reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Respect/Stringifier/zipball/e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59", + "reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.8", + "malukenho/docheader": "^0.1.7", + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "autoload": { + "files": [ + "src/stringify.php" + ], + "psr-4": { + "Respect\\Stringifier\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Respect/Stringifier Contributors", + "homepage": "https://github.com/Respect/Stringifier/graphs/contributors" + } + ], + "description": "Converts any value to a string", + "homepage": "http://respect.github.io/Stringifier/", + "keywords": [ + "respect", + "stringifier", + "stringify" + ], + "support": { + "issues": "https://github.com/Respect/Stringifier/issues", + "source": "https://github.com/Respect/Stringifier/tree/0.2.0" + }, + "time": "2017-12-29T19:39:25+00:00" + }, + { + "name": "robmorgan/phinx", + "version": "0.14.0", + "source": { + "type": "git", + "url": "https://github.com/cakephp/phinx.git", + "reference": "7bc24bae664b2124f3d5b8d1e98fdb8abaf70e87" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/phinx/zipball/7bc24bae664b2124f3d5b8d1e98fdb8abaf70e87", + "reference": "7bc24bae664b2124f3d5b8d1e98fdb8abaf70e87", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "cakephp/database": "^4.0", + "php-64bit": ">=7.3", + "psr/container": "^1.0 || ^2.0", + "symfony/config": "^3.4|^4.0|^5.0|^6.0", + "symfony/console": "^3.4|^4.0|^5.0|^6.0" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "^4.0", + "ext-json": "*", + "ext-pdo": "*", + "phpunit/phpunit": "^9.5", + "sebastian/comparator": ">=1.2.3", + "symfony/yaml": "^3.4|^4.0|^5.0" + }, + "suggest": { + "ext-json": "Install if using JSON configuration format", + "ext-pdo": "PDO extension is needed", + "symfony/yaml": "Install if using YAML configuration format" + }, + "bin": [ + "bin/phinx" + ], + "type": "library", + "autoload": { + "psr-4": { + "Phinx\\": "src/Phinx/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rob Morgan", + "email": "robbym@gmail.com", + "homepage": "https://robmorgan.id.au", + "role": "Lead Developer" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com", + "homepage": "https://shadowhand.me", + "role": "Developer" + }, + { + "name": "Richard Quadling", + "email": "rquadling@gmail.com", + "role": "Developer" + }, + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/phinx/graphs/contributors", + "role": "Developer" + } + ], + "description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.", + "homepage": "https://phinx.org", + "keywords": [ + "database", + "database migrations", + "db", + "migrations", + "phinx" + ], + "support": { + "issues": "https://github.com/cakephp/phinx/issues", + "source": "https://github.com/cakephp/phinx/tree/0.14.0" + }, + "time": "2023-09-07T14:26:14+00:00" + }, + { + "name": "symfony/cache", + "version": "v6.0.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "81ca309f056e836480928b20280ec52ce8369bb3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/81ca309f056e836480928b20280ec52ce8369bb3", + "reference": "81ca309f056e836480928b20280ec52ce8369bb3", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.2", + "psr/cache": "^2.0|^3.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^1.1.7|^2|^3", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/var-exporter": "^5.4|^6.0" + }, + "conflict": { + "doctrine/dbal": "<2.13.1", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/var-dumper": "<5.4" + }, + "provide": { + "psr/cache-implementation": "2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0", + "symfony/cache-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/dbal": "^2.13.1|^3.0", + "predis/predis": "^1.1", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "support": { + "source": "https://github.com/symfony/cache/tree/v6.0.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-20T17:44:14+00:00" + }, + { + "name": "symfony/cache-contracts", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache-contracts.git", + "reference": "1c0a181c9ee221afe4fa55b2d13fc63c5ae14348" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/1c0a181c9ee221afe4fa55b2d13fc63c5ae14348", + "reference": "1c0a181c9ee221afe4fa55b2d13fc63c5ae14348", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.2", + "psr/cache": "^3.0" + }, + "suggest": { + "symfony/cache-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Cache\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to caching", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v3.0.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:55:41+00:00" + }, + { + "name": "symfony/config", + "version": "v6.0.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3", + "reference": "db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/filesystem": "^5.4|^6.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php81": "^1.22" + }, + "conflict": { + "symfony/finder": "<4.4" + }, + "require-dev": { + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v6.0.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-09T04:36:00+00:00" + }, + { + "name": "symfony/console", + "version": "v6.0.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/c3ebc83d031b71c39da318ca8b7a07ecc67507ed", + "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.4|^6.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.0.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:36:10+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:55:41+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.0.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/3d49eec03fda1f0fc19b7349fbbe55ebc1004214", + "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.0.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-20T17:44:14+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af", + "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-19T12:30:46+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d78d39c1599bd1188b8e26bb341da52c3c6d8a66", + "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.2", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.0.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-30T19:17:58+00:00" + }, + { + "name": "symfony/string", + "version": "v6.0.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/d9e72497367c23e08bf94176d2be45b00a9d232a", + "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.0" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.0.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:36:10+00:00" + }, + { + "name": "symfony/translation", + "version": "v6.0.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f", + "reference": "9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.3|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/intl": "^5.4|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v6.0.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:36:10+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "acbfbb274e730e5a0236f619b6168d9dedb3e282" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/acbfbb274e730e5a0236f619b6168d9dedb3e282", + "reference": "acbfbb274e730e5a0236f619b6168d9dedb3e282", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.2" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.0.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-06-27T17:10:44+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.0.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "eb980457fa6899840fe1687e8627a03a7d8a3d52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/eb980457fa6899840fe1687e8627a03a7d8a3d52", + "reference": "eb980457fa6899840fe1687e8627a03a7d8a3d52", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "phpunit/phpunit": "<5.4.3", + "symfony/console": "<5.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.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" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v6.0.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-20T17:44:14+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v6.0.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "df56f53818c2d5d9f683f4ad2e365ba73a3b69d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/df56f53818c2d5d9f683f4ad2e365ba73a3b69d2", + "reference": "df56f53818c2d5d9f683f4ad2e365ba73a3b69d2", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.2" + }, + "require-dev": { + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v6.0.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-13T08:34:10+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.0", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.2", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.2", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2023-11-12T22:43:29+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b56450eed252f6801410d810c8e1727224ae0743" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", + "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "http://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2022-03-08T17:03:00+00:00" + }, + { + "name": "webman/captcha", + "version": "v1.0.3", + "source": { + "type": "git", + "url": "https://github.com/webman-php/captcha.git", + "reference": "e80f271af99cb337406118fa7d90c0699ee1f3bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webman-php/captcha/zipball/e80f271af99cb337406118fa7d90c0699ee1f3bc", + "reference": "e80f271af99cb337406118fa7d90c0699ee1f3bc", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-gd": "*", + "ext-mbstring": "*", + "php": ">=5.6.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Webman\\Captcha\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net" + }, + { + "name": "Grégoire Passault", + "email": "g.passault@gmail.com", + "homepage": "http://www.gregwar.com/" + }, + { + "name": "Jeremy Livingston", + "email": "jeremy.j.livingston@gmail.com" + } + ], + "description": "Captcha generator", + "keywords": [ + "bot", + "captcha", + "spam" + ], + "support": { + "source": "https://github.com/webman-php/captcha/tree/v1.0.3" + }, + "time": "2024-03-28T07:54:04+00:00" + }, + { + "name": "webman/event", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/webman-php/event.git", + "reference": "f4478941c3b7efa4d7e9c063f2f6efd7ee3071a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webman-php/event/zipball/f4478941c3b7efa4d7e9c063f2f6efd7ee3071a7", + "reference": "f4478941c3b7efa4d7e9c063f2f6efd7ee3071a7", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "type": "library", + "autoload": { + "psr-4": { + "Webman\\Event\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Webman event plugin", + "support": { + "issues": "https://github.com/webman-php/event/issues", + "source": "https://github.com/webman-php/event/tree/v1.0.4" + }, + "time": "2023-03-28T04:01:23+00:00" + }, + { + "name": "workerman/crontab", + "version": "v1.0.6", + "source": { + "type": "git", + "url": "https://github.com/walkor/crontab.git", + "reference": "b78f1556f2977715b9cb5653129e6d9cf160d966" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/walkor/crontab/zipball/b78f1556f2977715b9cb5653129e6d9cf160d966", + "reference": "b78f1556f2977715b9cb5653129e6d9cf160d966", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.0", + "workerman/workerman": ">=4.0.20" + }, + "type": "library", + "autoload": { + "psr-4": { + "Workerman\\Crontab\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net", + "homepage": "http://www.workerman.net", + "role": "Developer" + } + ], + "description": "A crontab written in PHP based on workerman", + "homepage": "http://www.workerman.net", + "keywords": [ + "crontab" + ], + "support": { + "email": "walkor@workerman.net", + "forum": "http://wenda.workerman.net/", + "issues": "https://github.com/walkor/workerman/issues", + "source": "https://github.com/walkor/crontab", + "wiki": "http://doc.workerman.net/" + }, + "time": "2022-10-17T01:59:19+00:00" + }, + { + "name": "workerman/validation", + "version": "v3.1.2", + "source": { + "type": "git", + "url": "https://github.com/walkor/validation.git", + "reference": "a4e9896e76b2fac92aff9a9f784df55f615571a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/walkor/validation/zipball/a4e9896e76b2fac92aff9a9f784df55f615571a0", + "reference": "a4e9896e76b2fac92aff9a9f784df55f615571a0", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^8.0 || ^8.1 || ^8.2", + "respect/stringifier": "^0.2.0", + "symfony/polyfill-mbstring": "^1.2" + }, + "require-dev": { + "egulias/email-validator": "^3.0", + "giggsey/libphonenumber-for-php-lite": "^8.13", + "malukenho/docheader": "^1.0", + "mikey179/vfsstream": "^1.6", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.6", + "psr/http-message": "^1.0", + "respect/coding-standard": "^3.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "suggest": { + "egulias/email-validator": "Improves the Email rule if available", + "ext-bcmath": "Arbitrary Precision Mathematics", + "ext-fileinfo": "File Information", + "ext-mbstring": "Multibyte String Functions", + "giggsey/libphonenumber-for-php-lite": "Enables the phone rule if available" + }, + "type": "library", + "autoload": { + "psr-4": { + "Respect\\Validation\\": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Respect/Validation Contributors", + "homepage": "https://github.com/Respect/Validation/graphs/contributors" + } + ], + "description": "The most awesome validation engine ever created for PHP. Respect/Validation 汉化版本", + "homepage": "http://respect.github.io/Validation/", + "keywords": [ + "respect", + "validation", + "validator" + ], + "support": { + "source": "https://github.com/walkor/validation/tree/v3.1.2" + }, + "time": "2023-12-07T06:19:04+00:00" + }, + { + "name": "workerman/webman-framework", + "version": "v1.5.19", + "source": { + "type": "git", + "url": "https://github.com/walkor/webman-framework.git", + "reference": "9ac7c136b0197a15a31f5092782366abff9a6e06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/walkor/webman-framework/zipball/9ac7c136b0197a15a31f5092782366abff9a6e06", + "reference": "9ac7c136b0197a15a31f5092782366abff9a6e06", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-json": "*", + "nikic/fast-route": "^1.3", + "php": ">=7.2", + "psr/container": ">=1.0", + "workerman/workerman": "^4.0.4 || ^5.0.0" + }, + "suggest": { + "ext-event": "For better performance. " + }, + "type": "library", + "autoload": { + "psr-4": { + "Webman\\": "./src", + "Support\\": "./src/support", + "support\\": "./src/support", + "Support\\View\\": "./src/support/view", + "Support\\Bootstrap\\": "./src/support/bootstrap", + "Support\\Exception\\": "./src/support/exception" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net", + "homepage": "https://www.workerman.net", + "role": "Developer" + } + ], + "description": "High performance HTTP Service Framework.", + "homepage": "https://www.workerman.net", + "keywords": [ + "High Performance", + "http service" + ], + "support": { + "email": "walkor@workerman.net", + "forum": "https://wenda.workerman.net/", + "issues": "https://github.com/walkor/webman/issues", + "source": "https://github.com/walkor/webman-framework", + "wiki": "https://doc.workerman.net/" + }, + "time": "2024-06-17T01:51:40+00:00" + }, + { + "name": "workerman/workerman", + "version": "v4.1.15", + "source": { + "type": "git", + "url": "https://github.com/walkor/workerman.git", + "reference": "afc8242fc769ab7cf22eb4ac22b97cb59d465e4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/walkor/workerman/zipball/afc8242fc769ab7cf22eb4ac22b97cb59d465e4e", + "reference": "afc8242fc769ab7cf22eb4ac22b97cb59d465e4e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.0" + }, + "suggest": { + "ext-event": "For better performance. " + }, + "type": "library", + "autoload": { + "psr-4": { + "Workerman\\": "./" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net", + "homepage": "http://www.workerman.net", + "role": "Developer" + } + ], + "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.", + "homepage": "http://www.workerman.net", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "email": "walkor@workerman.net", + "forum": "http://wenda.workerman.net/", + "issues": "https://github.com/walkor/workerman/issues", + "source": "https://github.com/walkor/workerman", + "wiki": "http://doc.workerman.net/" + }, + "funding": [ + { + "url": "https://opencollective.com/workerman", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/walkor", + "type": "patreon" + } + ], + "time": "2024-02-19T02:10:39+00:00" + }, + { + "name": "yzh52521/easyhttp", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/yuanzhihai/easyhttp.git", + "reference": "02bcf47eaf723520fa3905d0e6f1852168fe646c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yuanzhihai/easyhttp/zipball/02bcf47eaf723520fa3905d0e6f1852168fe646c", + "reference": "02bcf47eaf723520fa3905d0e6f1852168fe646c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "guzzlehttp/guzzle": "^6.0|^7.0", + "php": ">=7.2.5", + "psr/log": "^1.0|^2.0|^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "yzh52521\\EasyHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "yzh52521", + "email": "396751927@qq.com" + } + ], + "description": "EasyHttp 是一个轻量级、语义化、对IDE友好的HTTP客户端,支持常见的HTTP请求、异步请求和并发请求,让你可以快速地使用 HTTP 请求与其他 Web 应用进行通信。", + "homepage": "https://github.com/yzh52521/easyhttp", + "keywords": [ + "EasyHttp", + "curl", + "easy-http", + "http", + "php", + "php-http", + "phphttp" + ], + "support": { + "issues": "https://github.com/yuanzhihai/easyhttp/issues", + "source": "https://github.com/yuanzhihai/easyhttp/tree/v1.1.3" + }, + "time": "2023-11-14T05:49:02+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.2" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/config/app.php b/config/app.php new file mode 100644 index 0000000..f26e358 --- /dev/null +++ b/config/app.php @@ -0,0 +1,26 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +use support\Request; + +return [ + 'debug' => true, + 'error_reporting' => E_ALL, + 'default_timezone' => 'Asia/Shanghai', + 'request_class' => Request::class, + 'public_path' => base_path() . DIRECTORY_SEPARATOR . 'public', + 'runtime_path' => base_path(false) . DIRECTORY_SEPARATOR . 'runtime', + 'controller_suffix' => 'Controller', + 'controller_reuse' => false, +]; diff --git a/config/autoload.php b/config/autoload.php new file mode 100644 index 0000000..69a8135 --- /dev/null +++ b/config/autoload.php @@ -0,0 +1,21 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + 'files' => [ + base_path() . '/app/functions.php', + base_path() . '/support/Request.php', + base_path() . '/support/Response.php', + ] +]; diff --git a/config/bootstrap.php b/config/bootstrap.php new file mode 100644 index 0000000..44054e0 --- /dev/null +++ b/config/bootstrap.php @@ -0,0 +1,18 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + support\bootstrap\Session::class, + support\bootstrap\LaravelDb::class, +]; diff --git a/config/container.php b/config/container.php new file mode 100644 index 0000000..106b7b4 --- /dev/null +++ b/config/container.php @@ -0,0 +1,15 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return new Webman\Container; \ No newline at end of file diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000..7538c45 --- /dev/null +++ b/config/database.php @@ -0,0 +1,38 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + + 'default' => 'mysql', + // 各种数据库配置 + 'connections' => [ + + 'mysql' => [ + 'driver' => 'mysql', + 'host' => '127.0.0.1', + 'port' => 3306, + 'database' => 'LaysenseSSO', + 'username' => getenv('dbuser'), + 'password' => getenv('dbpass'), + 'unix_socket' => '', + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + 'prefix' => '', + 'strict' => true, + 'engine' => null, + ], + + ], + +]; diff --git a/config/dependence.php b/config/dependence.php new file mode 100644 index 0000000..8e964ed --- /dev/null +++ b/config/dependence.php @@ -0,0 +1,15 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return []; \ No newline at end of file diff --git a/config/event.php b/config/event.php new file mode 100644 index 0000000..28a3b2c --- /dev/null +++ b/config/event.php @@ -0,0 +1,5 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + '' => support\exception\Handler::class, +]; \ No newline at end of file diff --git a/config/log.php b/config/log.php new file mode 100644 index 0000000..7f05de5 --- /dev/null +++ b/config/log.php @@ -0,0 +1,32 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + 'default' => [ + 'handlers' => [ + [ + 'class' => Monolog\Handler\RotatingFileHandler::class, + 'constructor' => [ + runtime_path() . '/logs/webman.log', + 7, //$maxFiles + Monolog\Logger::DEBUG, + ], + 'formatter' => [ + 'class' => Monolog\Formatter\LineFormatter::class, + 'constructor' => [null, 'Y-m-d H:i:s', true], + ], + ] + ], + ], +]; diff --git a/config/middleware.php b/config/middleware.php new file mode 100644 index 0000000..8e964ed --- /dev/null +++ b/config/middleware.php @@ -0,0 +1,15 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return []; \ No newline at end of file diff --git a/config/plugin/webman/event/app.php b/config/plugin/webman/event/app.php new file mode 100644 index 0000000..8f9c426 --- /dev/null +++ b/config/plugin/webman/event/app.php @@ -0,0 +1,4 @@ + true, +]; \ No newline at end of file diff --git a/config/plugin/webman/event/bootstrap.php b/config/plugin/webman/event/bootstrap.php new file mode 100644 index 0000000..e5b09ba --- /dev/null +++ b/config/plugin/webman/event/bootstrap.php @@ -0,0 +1,17 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + Webman\Event\BootStrap::class, +]; diff --git a/config/plugin/webman/event/command.php b/config/plugin/webman/event/command.php new file mode 100644 index 0000000..e860cf7 --- /dev/null +++ b/config/plugin/webman/event/command.php @@ -0,0 +1,7 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +global $argv; + +return [ + // File update detection and automatic reload + 'monitor' => [ + 'handler' => process\Monitor::class, + 'reloadable' => false, + 'constructor' => [ + // Monitor these directories + 'monitorDir' => array_merge([ + app_path(), + config_path(), + base_path() . '/process', + base_path() . '/support', + base_path() . '/resource', + base_path() . '/.env', + ], glob(base_path() . '/plugin/*/app'), glob(base_path() . '/plugin/*/config'), glob(base_path() . '/plugin/*/api')), + // Files with these suffixes will be monitored + 'monitorExtensions' => [ + 'php', 'html', 'htm', 'env' + ], + 'options' => [ + 'enable_file_monitor' => !in_array('-d', $argv) && DIRECTORY_SEPARATOR === '/', + 'enable_memory_monitor' => DIRECTORY_SEPARATOR === '/', + ] + ] + ] +]; diff --git a/config/redis.php b/config/redis.php new file mode 100644 index 0000000..2f9757a --- /dev/null +++ b/config/redis.php @@ -0,0 +1,22 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + 'default' => [ + 'host' => '127.0.0.1', + 'password' => null, + 'port' => 6379, + 'database' => 0, + ], +]; diff --git a/config/route.php b/config/route.php new file mode 100644 index 0000000..591418f --- /dev/null +++ b/config/route.php @@ -0,0 +1,23 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +use Webman\Route; + + +#Laysense Simple Login +Route::group('/auth/lay', function () { + Route::any('/{appid}',[app\controller\LayAuth::class, 'index']); + Route::any('/{appid}/{gateway}/callback',[app\controller\LayAuth::class, 'callback']); + Route::any('/{appid}/check',[app\controller\LayAuth::class, 'check']); + }); diff --git a/config/server.php b/config/server.php new file mode 100644 index 0000000..67ab8d1 --- /dev/null +++ b/config/server.php @@ -0,0 +1,31 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + 'listen' => 'http://0.0.0.0:15566', + 'transport' => 'tcp', + 'context' => [], + 'name' => 'webman', + 'count' => cpu_count() * 1, + 'user' => '', + 'group' => '', + 'reusePort' => false, + 'event_loop' => '', + 'stop_timeout' => 2, + 'pid_file' => runtime_path() . '/webman.pid', + 'status_file' => runtime_path() . '/webman.status', + 'stdout_file' => runtime_path() . '/logs/stdout.log', + 'log_file' => runtime_path() . '/logs/workerman.log', + 'max_package_size' => 10 * 1024 * 1024 +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 0000000..043f8c4 --- /dev/null +++ b/config/session.php @@ -0,0 +1,65 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +use Webman\Session\FileSessionHandler; +use Webman\Session\RedisSessionHandler; +use Webman\Session\RedisClusterSessionHandler; + +return [ + + 'type' => 'file', // or redis or redis_cluster + + 'handler' => FileSessionHandler::class, + + 'config' => [ + 'file' => [ + 'save_path' => runtime_path() . '/sessions', + ], + 'redis' => [ + 'host' => '127.0.0.1', + 'port' => 6379, + 'auth' => '', + 'timeout' => 2, + 'database' => '', + 'prefix' => 'redis_session_', + ], + 'redis_cluster' => [ + 'host' => ['127.0.0.1:7000', '127.0.0.1:7001', '127.0.0.1:7001'], + 'timeout' => 2, + 'auth' => '', + 'prefix' => 'redis_session_', + ] + ], + + 'session_name' => 'PHPSID', + + 'auto_update_timestamp' => false, + + 'lifetime' => 7*24*60*60, + + 'cookie_lifetime' => 365*24*60*60, + + 'cookie_path' => '/', + + 'domain' => '', + + 'http_only' => true, + + 'secure' => false, + + 'same_site' => '', + + 'gc_probability' => [1, 1000], + +]; diff --git a/config/static.php b/config/static.php new file mode 100644 index 0000000..2f76cf3 --- /dev/null +++ b/config/static.php @@ -0,0 +1,23 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +/** + * Static file settings + */ +return [ + 'enable' => true, + 'middleware' => [ // Static file Middleware + //app\middleware\StaticFile::class, + ], +]; \ No newline at end of file diff --git a/config/translation.php b/config/translation.php new file mode 100644 index 0000000..96589b2 --- /dev/null +++ b/config/translation.php @@ -0,0 +1,25 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +/** + * Multilingual configuration + */ +return [ + // Default language + 'locale' => 'zh_CN', + // Fallback language + 'fallback_locale' => ['zh_CN', 'en'], + // Folder where language files are stored + 'path' => base_path() . '/resource/translations', +]; \ No newline at end of file diff --git a/config/view.php b/config/view.php new file mode 100644 index 0000000..e3a7b85 --- /dev/null +++ b/config/view.php @@ -0,0 +1,22 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +use support\view\Raw; +use support\view\Twig; +use support\view\Blade; +use support\view\ThinkPHP; + +return [ + 'handler' => Raw::class +]; diff --git a/process/Monitor.php b/process/Monitor.php new file mode 100644 index 0000000..92b3716 --- /dev/null +++ b/process/Monitor.php @@ -0,0 +1,243 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace process; + +use FilesystemIterator; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use SplFileInfo; +use Workerman\Timer; +use Workerman\Worker; + +/** + * Class FileMonitor + * @package process + */ +class Monitor +{ + /** + * @var array + */ + protected $paths = []; + + /** + * @var array + */ + protected $extensions = []; + + /** + * @var string + */ + public static $lockFile = __DIR__ . '/../runtime/monitor.lock'; + + /** + * Pause monitor + * @return void + */ + public static function pause() + { + file_put_contents(static::$lockFile, time()); + } + + /** + * Resume monitor + * @return void + */ + public static function resume(): void + { + clearstatcache(); + if (is_file(static::$lockFile)) { + unlink(static::$lockFile); + } + } + + /** + * Whether monitor is paused + * @return bool + */ + public static function isPaused(): bool + { + clearstatcache(); + return file_exists(static::$lockFile); + } + + /** + * FileMonitor constructor. + * @param $monitorDir + * @param $monitorExtensions + * @param array $options + */ + public function __construct($monitorDir, $monitorExtensions, array $options = []) + { + static::resume(); + $this->paths = (array)$monitorDir; + $this->extensions = $monitorExtensions; + if (!Worker::getAllWorkers()) { + return; + } + $disableFunctions = explode(',', ini_get('disable_functions')); + if (in_array('exec', $disableFunctions, true)) { + echo "\nMonitor file change turned off because exec() has been disabled by disable_functions setting in " . PHP_CONFIG_FILE_PATH . "/php.ini\n"; + } else { + if ($options['enable_file_monitor'] ?? true) { + Timer::add(1, function () { + $this->checkAllFilesChange(); + }); + } + } + + $memoryLimit = $this->getMemoryLimit($options['memory_limit'] ?? null); + if ($memoryLimit && ($options['enable_memory_monitor'] ?? true)) { + Timer::add(60, [$this, 'checkMemory'], [$memoryLimit]); + } + } + + /** + * @param $monitorDir + * @return bool + */ + public function checkFilesChange($monitorDir): bool + { + static $lastMtime, $tooManyFilesCheck; + if (!$lastMtime) { + $lastMtime = time(); + } + clearstatcache(); + if (!is_dir($monitorDir)) { + if (!is_file($monitorDir)) { + return false; + } + $iterator = [new SplFileInfo($monitorDir)]; + } else { + // recursive traversal directory + $dirIterator = new RecursiveDirectoryIterator($monitorDir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS); + $iterator = new RecursiveIteratorIterator($dirIterator); + } + $count = 0; + foreach ($iterator as $file) { + $count ++; + /** var SplFileInfo $file */ + if (is_dir($file->getRealPath())) { + continue; + } + // check mtime + if (in_array($file->getExtension(), $this->extensions, true) && $lastMtime < $file->getMTime()) { + $var = 0; + exec('"'.PHP_BINARY . '" -l ' . $file, $out, $var); + $lastMtime = $file->getMTime(); + if ($var) { + continue; + } + echo $file . " update and reload\n"; + // send SIGUSR1 signal to master process for reload + if (DIRECTORY_SEPARATOR === '/') { + posix_kill(posix_getppid(), SIGUSR1); + } else { + return true; + } + break; + } + } + if (!$tooManyFilesCheck && $count > 1000) { + echo "Monitor: There are too many files ($count files) in $monitorDir which makes file monitoring very slow\n"; + $tooManyFilesCheck = 1; + } + return false; + } + + /** + * @return bool + */ + public function checkAllFilesChange(): bool + { + if (static::isPaused()) { + return false; + } + foreach ($this->paths as $path) { + if ($this->checkFilesChange($path)) { + return true; + } + } + return false; + } + + /** + * @param $memoryLimit + * @return void + */ + public function checkMemory($memoryLimit) + { + if (static::isPaused() || $memoryLimit <= 0) { + return; + } + $ppid = posix_getppid(); + $childrenFile = "/proc/$ppid/task/$ppid/children"; + if (!is_file($childrenFile) || !($children = file_get_contents($childrenFile))) { + return; + } + foreach (explode(' ', $children) as $pid) { + $pid = (int)$pid; + $statusFile = "/proc/$pid/status"; + if (!is_file($statusFile) || !($status = file_get_contents($statusFile))) { + continue; + } + $mem = 0; + if (preg_match('/VmRSS\s*?:\s*?(\d+?)\s*?kB/', $status, $match)) { + $mem = $match[1]; + } + $mem = (int)($mem / 1024); + if ($mem >= $memoryLimit) { + posix_kill($pid, SIGINT); + } + } + } + + /** + * Get memory limit + * @return float + */ + protected function getMemoryLimit($memoryLimit) + { + if ($memoryLimit === 0) { + return 0; + } + $usePhpIni = false; + if (!$memoryLimit) { + $memoryLimit = ini_get('memory_limit'); + $usePhpIni = true; + } + + if ($memoryLimit == -1) { + return 0; + } + $unit = strtolower($memoryLimit[strlen($memoryLimit) - 1]); + if ($unit === 'g') { + $memoryLimit = 1024 * (int)$memoryLimit; + } else if ($unit === 'm') { + $memoryLimit = (int)$memoryLimit; + } else if ($unit === 'k') { + $memoryLimit = ((int)$memoryLimit / 1024); + } else { + $memoryLimit = ((int)$memoryLimit / (1024 * 1024)); + } + if ($memoryLimit < 30) { + $memoryLimit = 30; + } + if ($usePhpIni) { + $memoryLimit = (int)(0.8 * $memoryLimit); + } + return $memoryLimit; + } +} diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..2bde119 --- /dev/null +++ b/public/404.html @@ -0,0 +1,12 @@ + + + 404 Not Found - webman + + +
+

404 Not Found

+
+
+
webman
+ + diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b9f722e0eeed8dc7f3639bd3295a7b0376b98eca GIT binary patch literal 4286 zcmbuDc}!GC9LI-{lvLYdn`%sIykgTPHU44S)cR&9&1g0})ukRu+b zsI4iH+Tum=wxm?+g|1x7X%O&eYs0dFEXuJv?|~SRU%$V%yN_jIWzi)cX5Q?Z`F`g& z^PBfT2tWLcA20Z~I66XzQ9_8f0h+{zfZMeXzI$!B14qNqe+Ms>0t^d|k|#hpcn^q~ z;CVg3dWG2j%ifLf3af($0x zFqHZ@&|>{}K^tIw<{IEO5%LUi;PHV0JOSi}L_G^kF%-24L#d6mnNZLTT>4y=fJs32 z>T3n#v;Jb>u1$s#{kl4$sCDYsB{mVVOo|!b%Y>Or`)Ip-WMT~hhU&qo|1rvkx`)Dc z3eN*q@6-It4HR~%KIfv@I@xr$`J)wSwu_-_>;xY!T~{fgJ^dN=dHt(3eaw6QQ5(&Q zwbSe!_bDQ$p3rC#TC=DQ1t2=B$w95sS3oCG8?O+V{O^KwP2A z#JKe59cj_##AAmi*eL7j1FC3lq3dmSsf7^;4_WBvlMm=;#>pmNa_ zbN6mz6D^FlYx?YA-dafqFMD`^pBqx8l17ELWJ#L6Tv|SjloLN+GW0x%FxdnZY!{J* zO>bu3zE@n@Xrm(g1kiF6>)}|NZnR~9*Ma-cgVuwiY@)K+DvIix zgRsv#uD3~jt+NXh3XhXo&T-yFZlPoKYP(QS z*TQ4y`W)ZW)fHq&oMLL^ISVq=dQNX$&xlL$K1x# z*7(XDl{RE3ywB*nWQl#}H3ly4Cosk+Zyun@=e8?%w36eyYL}hLt={+9v0{aOIj?AY z&fVhxb&f+O2n7A&0BwGLmwI}bwM^2!bF$w)ca6_E*FOh3kc$sBYV94a`E&Ugq}JFv zUI2YO@bePdQ$U|M@LuPAo?E48`R&s6T0+lcHq#kXA^lZmqVmQj`0L|ALEl9{RJ+9? zBwEvF5AT3{ps$4w`&^&nANhHd5c>@!tuxTZHQ}@=WhEJJ-SqVEFZ8Fd2lo#&S)Z8* z_5ps5X+Ag(Ntt}s_vQln>$*J0l6s4#{qxQkI&ok-*hafkmQvcTMCbFO_kQ%<4_@^2 zzeWJ+2eNu`#%p8DK354;Lol7p6fud@%wulH<1{44HRY0Ks{v&TtXTdEpTWA|O34LUvWxqcXMUTb6W9?1XuHtqZ&nG)l^rKAmuHEs4) zB@AIt%@K#v30S=-d0QdDYxZT9iy zD*2z!VqR{nZf6e)`*;iRdk3Gh&V8v;dak0+*M06>|6-+l@VPb}Ttsel z@|n4OhceLL37RLIYdu32dP)cEJ5k+^SlHD-nud)91PZ{o+ zzq%#)Txl0+$CLm(L*M6p4y`kYcOBka9)liSH{d#+x;9dMR}dLv$B}VbAQ?vokWn}= z3x8s4pi9FKr63Hx5d?v8ATSXag$OVU;co-o_?H7Z`k|xHI-r3NX+lrJ=!7o`Ul1Dq E2mbpSrT_o{ literal 0 HcmV?d00001 diff --git a/start.php b/start.php new file mode 100755 index 0000000..489e447 --- /dev/null +++ b/start.php @@ -0,0 +1,4 @@ +#!/usr/bin/env php + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +/** + * Class Request + * @package support + */ +class Request extends \Webman\Http\Request +{ + +} \ No newline at end of file diff --git a/support/Response.php b/support/Response.php new file mode 100644 index 0000000..9bc4e1e --- /dev/null +++ b/support/Response.php @@ -0,0 +1,24 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +/** + * Class Response + * @package support + */ +class Response extends \Webman\Http\Response +{ + +} \ No newline at end of file diff --git a/support/bootstrap.php b/support/bootstrap.php new file mode 100644 index 0000000..d9471e6 --- /dev/null +++ b/support/bootstrap.php @@ -0,0 +1,133 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +use Dotenv\Dotenv; +use support\Log; +use Webman\Bootstrap; +use Webman\Config; +use Webman\Middleware; +use Webman\Route; +use Webman\Util; + +$worker = $worker ?? null; + +set_error_handler(function ($level, $message, $file = '', $line = 0) { + if (error_reporting() & $level) { + throw new ErrorException($message, 0, $level, $file, $line); + } +}); + +if ($worker) { + register_shutdown_function(function ($startTime) { + if (time() - $startTime <= 0.1) { + sleep(1); + } + }, time()); +} + +if (class_exists('Dotenv\Dotenv') && file_exists(base_path(false) . '/.env')) { + if (method_exists('Dotenv\Dotenv', 'createUnsafeMutable')) { + Dotenv::createUnsafeMutable(base_path(false))->load(); + } else { + Dotenv::createMutable(base_path(false))->load(); + } +} + +Config::clear(); +support\App::loadAllConfig(['route']); +if ($timezone = config('app.default_timezone')) { + date_default_timezone_set($timezone); +} + +foreach (config('autoload.files', []) as $file) { + include_once $file; +} +foreach (config('plugin', []) as $firm => $projects) { + foreach ($projects as $name => $project) { + if (!is_array($project)) { + continue; + } + foreach ($project['autoload']['files'] ?? [] as $file) { + include_once $file; + } + } + foreach ($projects['autoload']['files'] ?? [] as $file) { + include_once $file; + } +} + +Middleware::load(config('middleware', [])); +foreach (config('plugin', []) as $firm => $projects) { + foreach ($projects as $name => $project) { + if (!is_array($project) || $name === 'static') { + continue; + } + Middleware::load($project['middleware'] ?? []); + } + Middleware::load($projects['middleware'] ?? [], $firm); + if ($staticMiddlewares = config("plugin.$firm.static.middleware")) { + Middleware::load(['__static__' => $staticMiddlewares], $firm); + } +} +Middleware::load(['__static__' => config('static.middleware', [])]); + +foreach (config('bootstrap', []) as $className) { + if (!class_exists($className)) { + $log = "Warning: Class $className setting in config/bootstrap.php not found\r\n"; + echo $log; + Log::error($log); + continue; + } + /** @var Bootstrap $className */ + $className::start($worker); +} + +foreach (config('plugin', []) as $firm => $projects) { + foreach ($projects as $name => $project) { + if (!is_array($project)) { + continue; + } + foreach ($project['bootstrap'] ?? [] as $className) { + if (!class_exists($className)) { + $log = "Warning: Class $className setting in config/plugin/$firm/$name/bootstrap.php not found\r\n"; + echo $log; + Log::error($log); + continue; + } + /** @var Bootstrap $className */ + $className::start($worker); + } + } + foreach ($projects['bootstrap'] ?? [] as $className) { + /** @var string $className */ + if (!class_exists($className)) { + $log = "Warning: Class $className setting in plugin/$firm/config/bootstrap.php not found\r\n"; + echo $log; + Log::error($log); + continue; + } + /** @var Bootstrap $className */ + $className::start($worker); + } +} + +$directory = base_path() . '/plugin'; +$paths = [config_path()]; +foreach (Util::scanDir($directory) as $path) { + if (is_dir($path = "$path/config")) { + $paths[] = $path; + } +} +Route::load($paths); + diff --git a/support/helpers.php b/support/helpers.php new file mode 100644 index 0000000..87806fd --- /dev/null +++ b/support/helpers.php @@ -0,0 +1,540 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +use support\Container; +use support\Request; +use support\Response; +use support\Translation; +use support\view\Blade; +use support\view\Raw; +use support\view\ThinkPHP; +use support\view\Twig; +use Twig\Error\LoaderError; +use Twig\Error\RuntimeError; +use Twig\Error\SyntaxError; +use Webman\App; +use Webman\Config; +use Webman\Route; +use Workerman\Protocols\Http\Session; +use Workerman\Worker; + +// Project base path +define('BASE_PATH', dirname(__DIR__)); + +/** + * return the program execute directory + * @param string $path + * @return string + */ +function run_path(string $path = ''): string +{ + static $runPath = ''; + if (!$runPath) { + $runPath = is_phar() ? dirname(Phar::running(false)) : BASE_PATH; + } + return path_combine($runPath, $path); +} + +/** + * if the param $path equal false,will return this program current execute directory + * @param string|false $path + * @return string + */ +function base_path($path = ''): string +{ + if (false === $path) { + return run_path(); + } + return path_combine(BASE_PATH, $path); +} + +/** + * App path + * @param string $path + * @return string + */ +function app_path(string $path = ''): string +{ + return path_combine(BASE_PATH . DIRECTORY_SEPARATOR . 'app', $path); +} + +/** + * Public path + * @param string $path + * @return string + */ +function public_path(string $path = ''): string +{ + static $publicPath = ''; + if (!$publicPath) { + $publicPath = \config('app.public_path') ?: run_path('public'); + } + return path_combine($publicPath, $path); +} + +/** + * Config path + * @param string $path + * @return string + */ +function config_path(string $path = ''): string +{ + return path_combine(BASE_PATH . DIRECTORY_SEPARATOR . 'config', $path); +} + +/** + * Runtime path + * @param string $path + * @return string + */ +function runtime_path(string $path = ''): string +{ + static $runtimePath = ''; + if (!$runtimePath) { + $runtimePath = \config('app.runtime_path') ?: run_path('runtime'); + } + return path_combine($runtimePath, $path); +} + +/** + * Generate paths based on given information + * @param string $front + * @param string $back + * @return string + */ +function path_combine(string $front, string $back): string +{ + return $front . ($back ? (DIRECTORY_SEPARATOR . ltrim($back, DIRECTORY_SEPARATOR)) : $back); +} + +/** + * Response + * @param int $status + * @param array $headers + * @param string $body + * @return Response + */ +function response(string $body = '', int $status = 200, array $headers = []): Response +{ + return new Response($status, $headers, $body); +} + +/** + * Json response + * @param $data + * @param int $options + * @return Response + */ +function json($data, int $options = JSON_UNESCAPED_UNICODE): Response +{ + return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options)); +} + +/** + * Xml response + * @param $xml + * @return Response + */ +function xml($xml): Response +{ + if ($xml instanceof SimpleXMLElement) { + $xml = $xml->asXML(); + } + return new Response(200, ['Content-Type' => 'text/xml'], $xml); +} + +/** + * Jsonp response + * @param $data + * @param string $callbackName + * @return Response + */ +function jsonp($data, string $callbackName = 'callback'): Response +{ + if (!is_scalar($data) && null !== $data) { + $data = json_encode($data); + } + return new Response(200, [], "$callbackName($data)"); +} + +/** + * Redirect response + * @param string $location + * @param int $status + * @param array $headers + * @return Response + */ +function redirect(string $location, int $status = 302, array $headers = []): Response +{ + $response = new Response($status, ['Location' => $location]); + if (!empty($headers)) { + $response->withHeaders($headers); + } + return $response; +} + +/** + * View response + * @param string $template + * @param array $vars + * @param string|null $app + * @param string|null $plugin + * @return Response + */ +function view(string $template, array $vars = [], string $app = null, string $plugin = null): Response +{ + $request = \request(); + $plugin = $plugin === null ? ($request->plugin ?? '') : $plugin; + $handler = \config($plugin ? "plugin.$plugin.view.handler" : 'view.handler'); + return new Response(200, [], $handler::render($template, $vars, $app, $plugin)); +} + +/** + * Raw view response + * @param string $template + * @param array $vars + * @param string|null $app + * @return Response + * @throws Throwable + */ +function raw_view(string $template, array $vars = [], string $app = null): Response +{ + return new Response(200, [], Raw::render($template, $vars, $app)); +} + +/** + * Blade view response + * @param string $template + * @param array $vars + * @param string|null $app + * @return Response + */ +function blade_view(string $template, array $vars = [], string $app = null): Response +{ + return new Response(200, [], Blade::render($template, $vars, $app)); +} + +/** + * Think view response + * @param string $template + * @param array $vars + * @param string|null $app + * @return Response + */ +function think_view(string $template, array $vars = [], string $app = null): Response +{ + return new Response(200, [], ThinkPHP::render($template, $vars, $app)); +} + +/** + * Twig view response + * @param string $template + * @param array $vars + * @param string|null $app + * @return Response + * @throws LoaderError + * @throws RuntimeError + * @throws SyntaxError + */ +function twig_view(string $template, array $vars = [], string $app = null): Response +{ + return new Response(200, [], Twig::render($template, $vars, $app)); +} + +/** + * Get request + * @return \Webman\Http\Request|Request|null + */ +function request() +{ + return App::request(); +} + +/** + * Get config + * @param string|null $key + * @param $default + * @return array|mixed|null + */ +function config(string $key = null, $default = null) +{ + return Config::get($key, $default); +} + +/** + * Create url + * @param string $name + * @param ...$parameters + * @return string + */ +function route(string $name, ...$parameters): string +{ + $route = Route::getByName($name); + if (!$route) { + return ''; + } + + if (!$parameters) { + return $route->url(); + } + + if (is_array(current($parameters))) { + $parameters = current($parameters); + } + + return $route->url($parameters); +} + +/** + * Session + * @param mixed $key + * @param mixed $default + * @return mixed|bool|Session + */ +function session($key = null, $default = null) +{ + $session = \request()->session(); + if (null === $key) { + return $session; + } + if (is_array($key)) { + $session->put($key); + return null; + } + if (strpos($key, '.')) { + $keyArray = explode('.', $key); + $value = $session->all(); + foreach ($keyArray as $index) { + if (!isset($value[$index])) { + return $default; + } + $value = $value[$index]; + } + return $value; + } + return $session->get($key, $default); +} + +/** + * Translation + * @param string $id + * @param array $parameters + * @param string|null $domain + * @param string|null $locale + * @return string + */ +function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string +{ + $res = Translation::trans($id, $parameters, $domain, $locale); + return $res === '' ? $id : $res; +} + +/** + * Locale + * @param string|null $locale + * @return string + */ +function locale(string $locale = null): string +{ + if (!$locale) { + return Translation::getLocale(); + } + Translation::setLocale($locale); + return $locale; +} + +/** + * 404 not found + * @return Response + */ +function not_found(): Response +{ + return new Response(404, [], file_get_contents(public_path() . '/404.html')); +} + +/** + * Copy dir + * @param string $source + * @param string $dest + * @param bool $overwrite + * @return void + */ +function copy_dir(string $source, string $dest, bool $overwrite = false) +{ + if (is_dir($source)) { + if (!is_dir($dest)) { + mkdir($dest); + } + $files = scandir($source); + foreach ($files as $file) { + if ($file !== "." && $file !== "..") { + copy_dir("$source/$file", "$dest/$file", $overwrite); + } + } + } else if (file_exists($source) && ($overwrite || !file_exists($dest))) { + copy($source, $dest); + } +} + +/** + * Remove dir + * @param string $dir + * @return bool + */ +function remove_dir(string $dir): bool +{ + if (is_link($dir) || is_file($dir)) { + return unlink($dir); + } + $files = array_diff(scandir($dir), array('.', '..')); + foreach ($files as $file) { + (is_dir("$dir/$file") && !is_link($dir)) ? remove_dir("$dir/$file") : unlink("$dir/$file"); + } + return rmdir($dir); +} + +/** + * Bind worker + * @param $worker + * @param $class + */ +function worker_bind($worker, $class) +{ + $callbackMap = [ + 'onConnect', + 'onMessage', + 'onClose', + 'onError', + 'onBufferFull', + 'onBufferDrain', + 'onWorkerStop', + 'onWebSocketConnect', + 'onWorkerReload' + ]; + foreach ($callbackMap as $name) { + if (method_exists($class, $name)) { + $worker->$name = [$class, $name]; + } + } + if (method_exists($class, 'onWorkerStart')) { + call_user_func([$class, 'onWorkerStart'], $worker); + } +} + +/** + * Start worker + * @param $processName + * @param $config + * @return void + */ +function worker_start($processName, $config) +{ + $worker = new Worker($config['listen'] ?? null, $config['context'] ?? []); + $propertyMap = [ + 'count', + 'user', + 'group', + 'reloadable', + 'reusePort', + 'transport', + 'protocol', + ]; + $worker->name = $processName; + foreach ($propertyMap as $property) { + if (isset($config[$property])) { + $worker->$property = $config[$property]; + } + } + + $worker->onWorkerStart = function ($worker) use ($config) { + require_once base_path('/support/bootstrap.php'); + if (isset($config['handler'])) { + if (!class_exists($config['handler'])) { + echo "process error: class {$config['handler']} not exists\r\n"; + return; + } + + $instance = Container::make($config['handler'], $config['constructor'] ?? []); + worker_bind($worker, $instance); + } + }; +} + +/** + * Get realpath + * @param string $filePath + * @return string + */ +function get_realpath(string $filePath): string +{ + if (strpos($filePath, 'phar://') === 0) { + return $filePath; + } else { + return realpath($filePath); + } +} + +/** + * Is phar + * @return bool + */ +function is_phar(): bool +{ + return class_exists(Phar::class, false) && Phar::running(); +} + +/** + * Get cpu count + * @return int + */ +function cpu_count(): int +{ + // Windows does not support the number of processes setting. + if (DIRECTORY_SEPARATOR === '\\') { + return 1; + } + $count = 4; + if (is_callable('shell_exec')) { + if (strtolower(PHP_OS) === 'darwin') { + $count = (int)shell_exec('sysctl -n machdep.cpu.core_count'); + } else { + $count = (int)shell_exec('nproc'); + } + } + return $count > 0 ? $count : 4; +} + +/** + * Get request parameters, if no parameter name is passed, an array of all values is returned, default values is supported + * @param string|null $param param's name + * @param mixed|null $default default value + * @return mixed|null + */ +function input(string $param = null, $default = null) +{ + return is_null($param) ? request()->all() : request()->input($param, $default); +} + +function encryptAES($data, $key, $iv) { + $encrypted = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv); + $encrypted = base64_encode($encrypted); + return $encrypted; +} + +function decryptAES($encryptedData, $key, $iv) { + $encryptedData = base64_decode($encryptedData); + $decrypted = openssl_decrypt($encryptedData, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv); + return $decrypted; +} diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000..c80aee2 --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,25 @@ +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/nesbot/carbon/bin/carbon'); + } +} + +return include __DIR__ . '/..'.'/nesbot/carbon/bin/carbon'; diff --git a/vendor/bin/phinx b/vendor/bin/phinx new file mode 100755 index 0000000..20ed10c --- /dev/null +++ b/vendor/bin/phinx @@ -0,0 +1,119 @@ +#!/usr/bin/env php +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/robmorgan/phinx/bin/phinx'); + } +} + +return include __DIR__ . '/..'.'/robmorgan/phinx/bin/phinx'; diff --git a/vendor/bin/var-dump-server b/vendor/bin/var-dump-server new file mode 100755 index 0000000..18db1c1 --- /dev/null +++ b/vendor/bin/var-dump-server @@ -0,0 +1,119 @@ +#!/usr/bin/env php +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server'); + } +} + +return include __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server'; diff --git a/vendor/brick/math/CHANGELOG.md b/vendor/brick/math/CHANGELOG.md new file mode 100644 index 0000000..17cea8d --- /dev/null +++ b/vendor/brick/math/CHANGELOG.md @@ -0,0 +1,445 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [0.11.0](https://github.com/brick/math/releases/tag/0.11.0) - 2023-01-16 + +💥 **Breaking changes** + +- Minimum PHP version is now 8.0 +- Methods accepting a union of types are now strongly typed* +- `MathException` now extends `Exception` instead of `RuntimeException` + +* You may now run into type errors if you were passing `Stringable` objects to `of()` or any of the methods +internally calling `of()`, with `strict_types` enabled. You can fix this by casting `Stringable` objects to `string` +first. + +## [0.10.2](https://github.com/brick/math/releases/tag/0.10.2) - 2022-08-11 + +👌 **Improvements** + +- `BigRational::toFloat()` now simplifies the fraction before performing division (#73) thanks to @olsavmic + +## [0.10.1](https://github.com/brick/math/releases/tag/0.10.1) - 2022-08-02 + +✨ **New features** + +- `BigInteger::gcdMultiple()` returns the GCD of multiple `BigInteger` numbers + +## [0.10.0](https://github.com/brick/math/releases/tag/0.10.0) - 2022-06-18 + +💥 **Breaking changes** + +- Minimum PHP version is now 7.4 + +## [0.9.3](https://github.com/brick/math/releases/tag/0.9.3) - 2021-08-15 + +🚀 **Compatibility with PHP 8.1** + +- Support for custom object serialization; this removes a warning on PHP 8.1 due to the `Serializable` interface being deprecated (#60) thanks @TRowbotham + +## [0.9.2](https://github.com/brick/math/releases/tag/0.9.2) - 2021-01-20 + +🐛 **Bug fix** + +- Incorrect results could be returned when using the BCMath calculator, with a default scale set with `bcscale()`, on PHP >= 7.2 (#55). + +## [0.9.1](https://github.com/brick/math/releases/tag/0.9.1) - 2020-08-19 + +✨ **New features** + +- `BigInteger::not()` returns the bitwise `NOT` value + +🐛 **Bug fixes** + +- `BigInteger::toBytes()` could return an incorrect binary representation for some numbers +- The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available + +## [0.9.0](https://github.com/brick/math/releases/tag/0.9.0) - 2020-08-18 + +👌 **Improvements** + +- `BigNumber::of()` now accepts `.123` and `123.` formats, both of which return a `BigDecimal` + +💥 **Breaking changes** + +- Deprecated method `BigInteger::powerMod()` has been removed - use `modPow()` instead +- Deprecated method `BigInteger::parse()` has been removed - use `fromBase()` instead + +## [0.8.17](https://github.com/brick/math/releases/tag/0.8.17) - 2020-08-19 + +🐛 **Bug fix** + +- `BigInteger::toBytes()` could return an incorrect binary representation for some numbers +- The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available + +## [0.8.16](https://github.com/brick/math/releases/tag/0.8.16) - 2020-08-18 + +🚑 **Critical fix** + +- This version reintroduces the deprecated `BigInteger::parse()` method, that has been removed by mistake in version `0.8.9` and should have lasted for the whole `0.8` release cycle. + +✨ **New features** + +- `BigInteger::modInverse()` calculates a modular multiplicative inverse +- `BigInteger::fromBytes()` creates a `BigInteger` from a byte string +- `BigInteger::toBytes()` converts a `BigInteger` to a byte string +- `BigInteger::randomBits()` creates a pseudo-random `BigInteger` of a given bit length +- `BigInteger::randomRange()` creates a pseudo-random `BigInteger` between two bounds + +💩 **Deprecations** + +- `BigInteger::powerMod()` is now deprecated in favour of `modPow()` + +## [0.8.15](https://github.com/brick/math/releases/tag/0.8.15) - 2020-04-15 + +🐛 **Fixes** + +- added missing `ext-json` requirement, due to `BigNumber` implementing `JsonSerializable` + +⚡️ **Optimizations** + +- additional optimization in `BigInteger::remainder()` + +## [0.8.14](https://github.com/brick/math/releases/tag/0.8.14) - 2020-02-18 + +✨ **New features** + +- `BigInteger::getLowestSetBit()` returns the index of the rightmost one bit + +## [0.8.13](https://github.com/brick/math/releases/tag/0.8.13) - 2020-02-16 + +✨ **New features** + +- `BigInteger::isEven()` tests whether the number is even +- `BigInteger::isOdd()` tests whether the number is odd +- `BigInteger::testBit()` tests if a bit is set +- `BigInteger::getBitLength()` returns the number of bits in the minimal representation of the number + +## [0.8.12](https://github.com/brick/math/releases/tag/0.8.12) - 2020-02-03 + +🛠️ **Maintenance release** + +Classes are now annotated for better static analysis with [psalm](https://psalm.dev/). + +This is a maintenance release: no bug fixes, no new features, no breaking changes. + +## [0.8.11](https://github.com/brick/math/releases/tag/0.8.11) - 2020-01-23 + +✨ **New feature** + +`BigInteger::powerMod()` performs a power-with-modulo operation. Useful for crypto. + +## [0.8.10](https://github.com/brick/math/releases/tag/0.8.10) - 2020-01-21 + +✨ **New feature** + +`BigInteger::mod()` returns the **modulo** of two numbers. The *modulo* differs from the *remainder* when the signs of the operands are different. + +## [0.8.9](https://github.com/brick/math/releases/tag/0.8.9) - 2020-01-08 + +⚡️ **Performance improvements** + +A few additional optimizations in `BigInteger` and `BigDecimal` when one of the operands can be returned as is. Thanks to @tomtomsen in #24. + +## [0.8.8](https://github.com/brick/math/releases/tag/0.8.8) - 2019-04-25 + +🐛 **Bug fixes** + +- `BigInteger::toBase()` could return an empty string for zero values (BCMath & Native calculators only, GMP calculator unaffected) + +✨ **New features** + +- `BigInteger::toArbitraryBase()` converts a number to an arbitrary base, using a custom alphabet +- `BigInteger::fromArbitraryBase()` converts a string in an arbitrary base, using a custom alphabet, back to a number + +These methods can be used as the foundation to convert strings between different bases/alphabets, using BigInteger as an intermediate representation. + +💩 **Deprecations** + +- `BigInteger::parse()` is now deprecated in favour of `fromBase()` + +`BigInteger::fromBase()` works the same way as `parse()`, with 2 minor differences: + +- the `$base` parameter is required, it does not default to `10` +- it throws a `NumberFormatException` instead of an `InvalidArgumentException` when the number is malformed + +## [0.8.7](https://github.com/brick/math/releases/tag/0.8.7) - 2019-04-20 + +**Improvements** + +- Safer conversion from `float` when using custom locales +- **Much faster** `NativeCalculator` implementation 🚀 + +You can expect **at least a 3x performance improvement** for common arithmetic operations when using the library on systems without GMP or BCMath; it gets exponentially faster on multiplications with a high number of digits. This is due to calculations now being performed on whole blocks of digits (the block size depending on the platform, 32-bit or 64-bit) instead of digit-by-digit as before. + +## [0.8.6](https://github.com/brick/math/releases/tag/0.8.6) - 2019-04-11 + +**New method** + +`BigNumber::sum()` returns the sum of one or more numbers. + +## [0.8.5](https://github.com/brick/math/releases/tag/0.8.5) - 2019-02-12 + +**Bug fix**: `of()` factory methods could fail when passing a `float` in environments using a `LC_NUMERIC` locale with a decimal separator other than `'.'` (#20). + +Thanks @manowark 👍 + +## [0.8.4](https://github.com/brick/math/releases/tag/0.8.4) - 2018-12-07 + +**New method** + +`BigDecimal::sqrt()` calculates the square root of a decimal number, to a given scale. + +## [0.8.3](https://github.com/brick/math/releases/tag/0.8.3) - 2018-12-06 + +**New method** + +`BigInteger::sqrt()` calculates the square root of a number (thanks @peter279k). + +**New exception** + +`NegativeNumberException` is thrown when calling `sqrt()` on a negative number. + +## [0.8.2](https://github.com/brick/math/releases/tag/0.8.2) - 2018-11-08 + +**Performance update** + +- Further improvement of `toInt()` performance +- `NativeCalculator` can now perform some multiplications more efficiently + +## [0.8.1](https://github.com/brick/math/releases/tag/0.8.1) - 2018-11-07 + +Performance optimization of `toInt()` methods. + +## [0.8.0](https://github.com/brick/math/releases/tag/0.8.0) - 2018-10-13 + +**Breaking changes** + +The following deprecated methods have been removed. Use the new method name instead: + +| Method removed | Replacement method | +| --- | --- | +| `BigDecimal::getIntegral()` | `BigDecimal::getIntegralPart()` | +| `BigDecimal::getFraction()` | `BigDecimal::getFractionalPart()` | + +--- + +**New features** + +`BigInteger` has been augmented with 5 new methods for bitwise operations: + +| New method | Description | +| --- | --- | +| `and()` | performs a bitwise `AND` operation on two numbers | +| `or()` | performs a bitwise `OR` operation on two numbers | +| `xor()` | performs a bitwise `XOR` operation on two numbers | +| `shiftedLeft()` | returns the number shifted left by a number of bits | +| `shiftedRight()` | returns the number shifted right by a number of bits | + +Thanks to @DASPRiD 👍 + +## [0.7.3](https://github.com/brick/math/releases/tag/0.7.3) - 2018-08-20 + +**New method:** `BigDecimal::hasNonZeroFractionalPart()` + +**Renamed/deprecated methods:** + +- `BigDecimal::getIntegral()` has been renamed to `getIntegralPart()` and is now deprecated +- `BigDecimal::getFraction()` has been renamed to `getFractionalPart()` and is now deprecated + +## [0.7.2](https://github.com/brick/math/releases/tag/0.7.2) - 2018-07-21 + +**Performance update** + +`BigInteger::parse()` and `toBase()` now use GMP's built-in base conversion features when available. + +## [0.7.1](https://github.com/brick/math/releases/tag/0.7.1) - 2018-03-01 + +This is a maintenance release, no code has been changed. + +- When installed with `--no-dev`, the autoloader does not autoload tests anymore +- Tests and other files unnecessary for production are excluded from the dist package + +This will help make installations more compact. + +## [0.7.0](https://github.com/brick/math/releases/tag/0.7.0) - 2017-10-02 + +Methods renamed: + +- `BigNumber:sign()` has been renamed to `getSign()` +- `BigDecimal::unscaledValue()` has been renamed to `getUnscaledValue()` +- `BigDecimal::scale()` has been renamed to `getScale()` +- `BigDecimal::integral()` has been renamed to `getIntegral()` +- `BigDecimal::fraction()` has been renamed to `getFraction()` +- `BigRational::numerator()` has been renamed to `getNumerator()` +- `BigRational::denominator()` has been renamed to `getDenominator()` + +Classes renamed: + +- `ArithmeticException` has been renamed to `MathException` + +## [0.6.2](https://github.com/brick/math/releases/tag/0.6.2) - 2017-10-02 + +The base class for all exceptions is now `MathException`. +`ArithmeticException` has been deprecated, and will be removed in 0.7.0. + +## [0.6.1](https://github.com/brick/math/releases/tag/0.6.1) - 2017-10-02 + +A number of methods have been renamed: + +- `BigNumber:sign()` is deprecated; use `getSign()` instead +- `BigDecimal::unscaledValue()` is deprecated; use `getUnscaledValue()` instead +- `BigDecimal::scale()` is deprecated; use `getScale()` instead +- `BigDecimal::integral()` is deprecated; use `getIntegral()` instead +- `BigDecimal::fraction()` is deprecated; use `getFraction()` instead +- `BigRational::numerator()` is deprecated; use `getNumerator()` instead +- `BigRational::denominator()` is deprecated; use `getDenominator()` instead + +The old methods will be removed in version 0.7.0. + +## [0.6.0](https://github.com/brick/math/releases/tag/0.6.0) - 2017-08-25 + +- Minimum PHP version is now [7.1](https://gophp71.org/); for PHP 5.6 and PHP 7.0 support, use version `0.5` +- Deprecated method `BigDecimal::withScale()` has been removed; use `toScale()` instead +- Method `BigNumber::toInteger()` has been renamed to `toInt()` + +## [0.5.4](https://github.com/brick/math/releases/tag/0.5.4) - 2016-10-17 + +`BigNumber` classes now implement [JsonSerializable](http://php.net/manual/en/class.jsonserializable.php). +The JSON output is always a string. + +## [0.5.3](https://github.com/brick/math/releases/tag/0.5.3) - 2016-03-31 + +This is a bugfix release. Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6. + +## [0.5.2](https://github.com/brick/math/releases/tag/0.5.2) - 2015-08-06 + +The `$scale` parameter of `BigDecimal::dividedBy()` is now optional again. + +## [0.5.1](https://github.com/brick/math/releases/tag/0.5.1) - 2015-07-05 + +**New method: `BigNumber::toScale()`** + +This allows to convert any `BigNumber` to a `BigDecimal` with a given scale, using rounding if necessary. + +## [0.5.0](https://github.com/brick/math/releases/tag/0.5.0) - 2015-07-04 + +**New features** +- Common `BigNumber` interface for all classes, with the following methods: + - `sign()` and derived methods (`isZero()`, `isPositive()`, ...) + - `compareTo()` and derived methods (`isEqualTo()`, `isGreaterThan()`, ...) that work across different `BigNumber` types + - `toBigInteger()`, `toBigDecimal()`, `toBigRational`() conversion methods + - `toInteger()` and `toFloat()` conversion methods to native types +- Unified `of()` behaviour: every class now accepts any type of number, provided that it can be safely converted to the current type +- New method: `BigDecimal::exactlyDividedBy()`; this method automatically computes the scale of the result, provided that the division yields a finite number of digits +- New methods: `BigRational::quotient()` and `remainder()` +- Fine-grained exceptions: `DivisionByZeroException`, `RoundingNecessaryException`, `NumberFormatException` +- Factory methods `zero()`, `one()` and `ten()` available in all classes +- Rounding mode reintroduced in `BigInteger::dividedBy()` + +This release also comes with many performance improvements. + +--- + +**Breaking changes** +- `BigInteger`: + - `getSign()` is renamed to `sign()` + - `toString()` is renamed to `toBase()` + - `BigInteger::dividedBy()` now throws an exception by default if the remainder is not zero; use `quotient()` to get the previous behaviour +- `BigDecimal`: + - `getSign()` is renamed to `sign()` + - `getUnscaledValue()` is renamed to `unscaledValue()` + - `getScale()` is renamed to `scale()` + - `getIntegral()` is renamed to `integral()` + - `getFraction()` is renamed to `fraction()` + - `divideAndRemainder()` is renamed to `quotientAndRemainder()` + - `dividedBy()` now takes a **mandatory** `$scale` parameter **before** the rounding mode + - `toBigInteger()` does not accept a `$roundingMode` parameter anymore + - `toBigRational()` does not simplify the fraction anymore; explicitly add `->simplified()` to get the previous behaviour +- `BigRational`: + - `getSign()` is renamed to `sign()` + - `getNumerator()` is renamed to `numerator()` + - `getDenominator()` is renamed to `denominator()` + - `of()` is renamed to `nd()`, while `parse()` is renamed to `of()` +- Miscellaneous: + - `ArithmeticException` is moved to an `Exception\` sub-namespace + - `of()` factory methods now throw `NumberFormatException` instead of `InvalidArgumentException` + +## [0.4.3](https://github.com/brick/math/releases/tag/0.4.3) - 2016-03-31 + +Backport of two bug fixes from the 0.5 branch: +- `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected +- Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6. + +## [0.4.2](https://github.com/brick/math/releases/tag/0.4.2) - 2015-06-16 + +New method: `BigDecimal::stripTrailingZeros()` + +## [0.4.1](https://github.com/brick/math/releases/tag/0.4.1) - 2015-06-12 + +Introducing a `BigRational` class, to perform calculations on fractions of any size. + +## [0.4.0](https://github.com/brick/math/releases/tag/0.4.0) - 2015-06-12 + +Rounding modes have been removed from `BigInteger`, and are now a concept specific to `BigDecimal`. + +`BigInteger::dividedBy()` now always returns the quotient of the division. + +## [0.3.5](https://github.com/brick/math/releases/tag/0.3.5) - 2016-03-31 + +Backport of two bug fixes from the 0.5 branch: + +- `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected +- Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6. + +## [0.3.4](https://github.com/brick/math/releases/tag/0.3.4) - 2015-06-11 + +New methods: +- `BigInteger::remainder()` returns the remainder of a division only +- `BigInteger::gcd()` returns the greatest common divisor of two numbers + +## [0.3.3](https://github.com/brick/math/releases/tag/0.3.3) - 2015-06-07 + +Fix `toString()` not handling negative numbers. + +## [0.3.2](https://github.com/brick/math/releases/tag/0.3.2) - 2015-06-07 + +`BigInteger` and `BigDecimal` now have a `getSign()` method that returns: +- `-1` if the number is negative +- `0` if the number is zero +- `1` if the number is positive + +## [0.3.1](https://github.com/brick/math/releases/tag/0.3.1) - 2015-06-05 + +Minor performance improvements + +## [0.3.0](https://github.com/brick/math/releases/tag/0.3.0) - 2015-06-04 + +The `$roundingMode` and `$scale` parameters have been swapped in `BigDecimal::dividedBy()`. + +## [0.2.2](https://github.com/brick/math/releases/tag/0.2.2) - 2015-06-04 + +Stronger immutability guarantee for `BigInteger` and `BigDecimal`. + +So far, it would have been possible to break immutability of these classes by calling the `unserialize()` internal function. This release fixes that. + +## [0.2.1](https://github.com/brick/math/releases/tag/0.2.1) - 2015-06-02 + +Added `BigDecimal::divideAndRemainder()` + +## [0.2.0](https://github.com/brick/math/releases/tag/0.2.0) - 2015-05-22 + +- `min()` and `max()` do not accept an `array` anymore, but a variable number of parameters +- **minimum PHP version is now 5.6** +- continuous integration with PHP 7 + +## [0.1.1](https://github.com/brick/math/releases/tag/0.1.1) - 2014-09-01 + +- Added `BigInteger::power()` +- Added HHVM support + +## [0.1.0](https://github.com/brick/math/releases/tag/0.1.0) - 2014-08-31 + +First beta release. + diff --git a/vendor/brick/math/LICENSE b/vendor/brick/math/LICENSE new file mode 100644 index 0000000..f9b724f --- /dev/null +++ b/vendor/brick/math/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013-present Benjamin Morel + +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/brick/math/composer.json b/vendor/brick/math/composer.json new file mode 100644 index 0000000..ed817bd --- /dev/null +++ b/vendor/brick/math/composer.json @@ -0,0 +1,34 @@ +{ + "name": "brick/math", + "description": "Arbitrary-precision arithmetic library", + "type": "library", + "keywords": [ + "Brick", + "Math", + "Arbitrary-precision", + "Arithmetic", + "BigInteger", + "BigDecimal", + "BigRational", + "Bignum" + ], + "license": "MIT", + "require": { + "php": "^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0", + "php-coveralls/php-coveralls": "^2.2", + "vimeo/psalm": "5.0.0" + }, + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Brick\\Math\\Tests\\": "tests/" + } + } +} diff --git a/vendor/brick/math/src/BigDecimal.php b/vendor/brick/math/src/BigDecimal.php new file mode 100644 index 0000000..02fc656 --- /dev/null +++ b/vendor/brick/math/src/BigDecimal.php @@ -0,0 +1,786 @@ +value = $value; + $this->scale = $scale; + } + + /** + * Creates a BigDecimal of the given value. + * + * @throws MathException If the value cannot be converted to a BigDecimal. + * + * @psalm-pure + */ + public static function of(BigNumber|int|float|string $value) : BigDecimal + { + return parent::of($value)->toBigDecimal(); + } + + /** + * Creates a BigDecimal from an unscaled value and a scale. + * + * Example: `(12345, 3)` will result in the BigDecimal `12.345`. + * + * @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger. + * @param int $scale The scale of the number, positive or zero. + * + * @throws \InvalidArgumentException If the scale is negative. + * + * @psalm-pure + */ + public static function ofUnscaledValue(BigNumber|int|float|string $value, int $scale = 0) : BigDecimal + { + if ($scale < 0) { + throw new \InvalidArgumentException('The scale cannot be negative.'); + } + + return new BigDecimal((string) BigInteger::of($value), $scale); + } + + /** + * Returns a BigDecimal representing zero, with a scale of zero. + * + * @psalm-pure + */ + public static function zero() : BigDecimal + { + /** + * @psalm-suppress ImpureStaticVariable + * @var BigDecimal|null $zero + */ + static $zero; + + if ($zero === null) { + $zero = new BigDecimal('0'); + } + + return $zero; + } + + /** + * Returns a BigDecimal representing one, with a scale of zero. + * + * @psalm-pure + */ + public static function one() : BigDecimal + { + /** + * @psalm-suppress ImpureStaticVariable + * @var BigDecimal|null $one + */ + static $one; + + if ($one === null) { + $one = new BigDecimal('1'); + } + + return $one; + } + + /** + * Returns a BigDecimal representing ten, with a scale of zero. + * + * @psalm-pure + */ + public static function ten() : BigDecimal + { + /** + * @psalm-suppress ImpureStaticVariable + * @var BigDecimal|null $ten + */ + static $ten; + + if ($ten === null) { + $ten = new BigDecimal('10'); + } + + return $ten; + } + + /** + * Returns the sum of this number and the given one. + * + * The result has a scale of `max($this->scale, $that->scale)`. + * + * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal. + * + * @throws MathException If the number is not valid, or is not convertible to a BigDecimal. + */ + public function plus(BigNumber|int|float|string $that) : BigDecimal + { + $that = BigDecimal::of($that); + + if ($that->value === '0' && $that->scale <= $this->scale) { + return $this; + } + + if ($this->value === '0' && $this->scale <= $that->scale) { + return $that; + } + + [$a, $b] = $this->scaleValues($this, $that); + + $value = Calculator::get()->add($a, $b); + $scale = $this->scale > $that->scale ? $this->scale : $that->scale; + + return new BigDecimal($value, $scale); + } + + /** + * Returns the difference of this number and the given one. + * + * The result has a scale of `max($this->scale, $that->scale)`. + * + * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal. + * + * @throws MathException If the number is not valid, or is not convertible to a BigDecimal. + */ + public function minus(BigNumber|int|float|string $that) : BigDecimal + { + $that = BigDecimal::of($that); + + if ($that->value === '0' && $that->scale <= $this->scale) { + return $this; + } + + [$a, $b] = $this->scaleValues($this, $that); + + $value = Calculator::get()->sub($a, $b); + $scale = $this->scale > $that->scale ? $this->scale : $that->scale; + + return new BigDecimal($value, $scale); + } + + /** + * Returns the product of this number and the given one. + * + * The result has a scale of `$this->scale + $that->scale`. + * + * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal. + * + * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal. + */ + public function multipliedBy(BigNumber|int|float|string $that) : BigDecimal + { + $that = BigDecimal::of($that); + + if ($that->value === '1' && $that->scale === 0) { + return $this; + } + + if ($this->value === '1' && $this->scale === 0) { + return $that; + } + + $value = Calculator::get()->mul($this->value, $that->value); + $scale = $this->scale + $that->scale; + + return new BigDecimal($value, $scale); + } + + /** + * Returns the result of the division of this number by the given one, at the given scale. + * + * @param BigNumber|int|float|string $that The divisor. + * @param int|null $scale The desired scale, or null to use the scale of this number. + * @param int $roundingMode An optional rounding mode. + * + * @throws \InvalidArgumentException If the scale or rounding mode is invalid. + * @throws MathException If the number is invalid, is zero, or rounding was necessary. + */ + public function dividedBy(BigNumber|int|float|string $that, ?int $scale = null, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal + { + $that = BigDecimal::of($that); + + if ($that->isZero()) { + throw DivisionByZeroException::divisionByZero(); + } + + if ($scale === null) { + $scale = $this->scale; + } elseif ($scale < 0) { + throw new \InvalidArgumentException('Scale cannot be negative.'); + } + + if ($that->value === '1' && $that->scale === 0 && $scale === $this->scale) { + return $this; + } + + $p = $this->valueWithMinScale($that->scale + $scale); + $q = $that->valueWithMinScale($this->scale - $scale); + + $result = Calculator::get()->divRound($p, $q, $roundingMode); + + return new BigDecimal($result, $scale); + } + + /** + * Returns the exact result of the division of this number by the given one. + * + * The scale of the result is automatically calculated to fit all the fraction digits. + * + * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal. + * + * @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero, + * or the result yields an infinite number of digits. + */ + public function exactlyDividedBy(BigNumber|int|float|string $that) : BigDecimal + { + $that = BigDecimal::of($that); + + if ($that->value === '0') { + throw DivisionByZeroException::divisionByZero(); + } + + [, $b] = $this->scaleValues($this, $that); + + $d = \rtrim($b, '0'); + $scale = \strlen($b) - \strlen($d); + + $calculator = Calculator::get(); + + foreach ([5, 2] as $prime) { + for (;;) { + $lastDigit = (int) $d[-1]; + + if ($lastDigit % $prime !== 0) { + break; + } + + $d = $calculator->divQ($d, (string) $prime); + $scale++; + } + } + + return $this->dividedBy($that, $scale)->stripTrailingZeros(); + } + + /** + * Returns this number exponentiated to the given value. + * + * The result has a scale of `$this->scale * $exponent`. + * + * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000. + */ + public function power(int $exponent) : BigDecimal + { + if ($exponent === 0) { + return BigDecimal::one(); + } + + if ($exponent === 1) { + return $this; + } + + if ($exponent < 0 || $exponent > Calculator::MAX_POWER) { + throw new \InvalidArgumentException(\sprintf( + 'The exponent %d is not in the range 0 to %d.', + $exponent, + Calculator::MAX_POWER + )); + } + + return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent); + } + + /** + * Returns the quotient of the division of this number by this given one. + * + * The quotient has a scale of `0`. + * + * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal. + * + * @throws MathException If the divisor is not a valid decimal number, or is zero. + */ + public function quotient(BigNumber|int|float|string $that) : BigDecimal + { + $that = BigDecimal::of($that); + + if ($that->isZero()) { + throw DivisionByZeroException::divisionByZero(); + } + + $p = $this->valueWithMinScale($that->scale); + $q = $that->valueWithMinScale($this->scale); + + $quotient = Calculator::get()->divQ($p, $q); + + return new BigDecimal($quotient, 0); + } + + /** + * Returns the remainder of the division of this number by this given one. + * + * The remainder has a scale of `max($this->scale, $that->scale)`. + * + * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal. + * + * @throws MathException If the divisor is not a valid decimal number, or is zero. + */ + public function remainder(BigNumber|int|float|string $that) : BigDecimal + { + $that = BigDecimal::of($that); + + if ($that->isZero()) { + throw DivisionByZeroException::divisionByZero(); + } + + $p = $this->valueWithMinScale($that->scale); + $q = $that->valueWithMinScale($this->scale); + + $remainder = Calculator::get()->divR($p, $q); + + $scale = $this->scale > $that->scale ? $this->scale : $that->scale; + + return new BigDecimal($remainder, $scale); + } + + /** + * Returns the quotient and remainder of the division of this number by the given one. + * + * The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`. + * + * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal. + * + * @return BigDecimal[] An array containing the quotient and the remainder. + * + * @throws MathException If the divisor is not a valid decimal number, or is zero. + */ + public function quotientAndRemainder(BigNumber|int|float|string $that) : array + { + $that = BigDecimal::of($that); + + if ($that->isZero()) { + throw DivisionByZeroException::divisionByZero(); + } + + $p = $this->valueWithMinScale($that->scale); + $q = $that->valueWithMinScale($this->scale); + + [$quotient, $remainder] = Calculator::get()->divQR($p, $q); + + $scale = $this->scale > $that->scale ? $this->scale : $that->scale; + + $quotient = new BigDecimal($quotient, 0); + $remainder = new BigDecimal($remainder, $scale); + + return [$quotient, $remainder]; + } + + /** + * Returns the square root of this number, rounded down to the given number of decimals. + * + * @throws \InvalidArgumentException If the scale is negative. + * @throws NegativeNumberException If this number is negative. + */ + public function sqrt(int $scale) : BigDecimal + { + if ($scale < 0) { + throw new \InvalidArgumentException('Scale cannot be negative.'); + } + + if ($this->value === '0') { + return new BigDecimal('0', $scale); + } + + if ($this->value[0] === '-') { + throw new NegativeNumberException('Cannot calculate the square root of a negative number.'); + } + + $value = $this->value; + $addDigits = 2 * $scale - $this->scale; + + if ($addDigits > 0) { + // add zeros + $value .= \str_repeat('0', $addDigits); + } elseif ($addDigits < 0) { + // trim digits + if (-$addDigits >= \strlen($this->value)) { + // requesting a scale too low, will always yield a zero result + return new BigDecimal('0', $scale); + } + + $value = \substr($value, 0, $addDigits); + } + + $value = Calculator::get()->sqrt($value); + + return new BigDecimal($value, $scale); + } + + /** + * Returns a copy of this BigDecimal with the decimal point moved $n places to the left. + */ + public function withPointMovedLeft(int $n) : BigDecimal + { + if ($n === 0) { + return $this; + } + + if ($n < 0) { + return $this->withPointMovedRight(-$n); + } + + return new BigDecimal($this->value, $this->scale + $n); + } + + /** + * Returns a copy of this BigDecimal with the decimal point moved $n places to the right. + */ + public function withPointMovedRight(int $n) : BigDecimal + { + if ($n === 0) { + return $this; + } + + if ($n < 0) { + return $this->withPointMovedLeft(-$n); + } + + $value = $this->value; + $scale = $this->scale - $n; + + if ($scale < 0) { + if ($value !== '0') { + $value .= \str_repeat('0', -$scale); + } + $scale = 0; + } + + return new BigDecimal($value, $scale); + } + + /** + * Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part. + */ + public function stripTrailingZeros() : BigDecimal + { + if ($this->scale === 0) { + return $this; + } + + $trimmedValue = \rtrim($this->value, '0'); + + if ($trimmedValue === '') { + return BigDecimal::zero(); + } + + $trimmableZeros = \strlen($this->value) - \strlen($trimmedValue); + + if ($trimmableZeros === 0) { + return $this; + } + + if ($trimmableZeros > $this->scale) { + $trimmableZeros = $this->scale; + } + + $value = \substr($this->value, 0, -$trimmableZeros); + $scale = $this->scale - $trimmableZeros; + + return new BigDecimal($value, $scale); + } + + /** + * Returns the absolute value of this number. + */ + public function abs() : BigDecimal + { + return $this->isNegative() ? $this->negated() : $this; + } + + /** + * Returns the negated value of this number. + */ + public function negated() : BigDecimal + { + return new BigDecimal(Calculator::get()->neg($this->value), $this->scale); + } + + public function compareTo(BigNumber|int|float|string $that) : int + { + $that = BigNumber::of($that); + + if ($that instanceof BigInteger) { + $that = $that->toBigDecimal(); + } + + if ($that instanceof BigDecimal) { + [$a, $b] = $this->scaleValues($this, $that); + + return Calculator::get()->cmp($a, $b); + } + + return - $that->compareTo($this); + } + + public function getSign() : int + { + return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1); + } + + public function getUnscaledValue() : BigInteger + { + return self::newBigInteger($this->value); + } + + public function getScale() : int + { + return $this->scale; + } + + /** + * Returns a string representing the integral part of this decimal number. + * + * Example: `-123.456` => `-123`. + */ + public function getIntegralPart() : string + { + if ($this->scale === 0) { + return $this->value; + } + + $value = $this->getUnscaledValueWithLeadingZeros(); + + return \substr($value, 0, -$this->scale); + } + + /** + * Returns a string representing the fractional part of this decimal number. + * + * If the scale is zero, an empty string is returned. + * + * Examples: `-123.456` => '456', `123` => ''. + */ + public function getFractionalPart() : string + { + if ($this->scale === 0) { + return ''; + } + + $value = $this->getUnscaledValueWithLeadingZeros(); + + return \substr($value, -$this->scale); + } + + /** + * Returns whether this decimal number has a non-zero fractional part. + */ + public function hasNonZeroFractionalPart() : bool + { + return $this->getFractionalPart() !== \str_repeat('0', $this->scale); + } + + public function toBigInteger() : BigInteger + { + $zeroScaleDecimal = $this->scale === 0 ? $this : $this->dividedBy(1, 0); + + return self::newBigInteger($zeroScaleDecimal->value); + } + + public function toBigDecimal() : BigDecimal + { + return $this; + } + + public function toBigRational() : BigRational + { + $numerator = self::newBigInteger($this->value); + $denominator = self::newBigInteger('1' . \str_repeat('0', $this->scale)); + + return self::newBigRational($numerator, $denominator, false); + } + + public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal + { + if ($scale === $this->scale) { + return $this; + } + + return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode); + } + + public function toInt() : int + { + return $this->toBigInteger()->toInt(); + } + + public function toFloat() : float + { + return (float) (string) $this; + } + + public function __toString() : string + { + if ($this->scale === 0) { + return $this->value; + } + + $value = $this->getUnscaledValueWithLeadingZeros(); + + return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale); + } + + /** + * This method is required for serializing the object and SHOULD NOT be accessed directly. + * + * @internal + * + * @return array{value: string, scale: int} + */ + public function __serialize(): array + { + return ['value' => $this->value, 'scale' => $this->scale]; + } + + /** + * This method is only here to allow unserializing the object and cannot be accessed directly. + * + * @internal + * @psalm-suppress RedundantPropertyInitializationCheck + * + * @param array{value: string, scale: int} $data + * + * @throws \LogicException + */ + public function __unserialize(array $data): void + { + if (isset($this->value)) { + throw new \LogicException('__unserialize() is an internal function, it must not be called directly.'); + } + + $this->value = $data['value']; + $this->scale = $data['scale']; + } + + /** + * This method is required by interface Serializable and SHOULD NOT be accessed directly. + * + * @internal + */ + public function serialize() : string + { + return $this->value . ':' . $this->scale; + } + + /** + * This method is only here to implement interface Serializable and cannot be accessed directly. + * + * @internal + * @psalm-suppress RedundantPropertyInitializationCheck + * + * @throws \LogicException + */ + public function unserialize($value) : void + { + if (isset($this->value)) { + throw new \LogicException('unserialize() is an internal function, it must not be called directly.'); + } + + [$value, $scale] = \explode(':', $value); + + $this->value = $value; + $this->scale = (int) $scale; + } + + /** + * Puts the internal values of the given decimal numbers on the same scale. + * + * @return array{string, string} The scaled integer values of $x and $y. + */ + private function scaleValues(BigDecimal $x, BigDecimal $y) : array + { + $a = $x->value; + $b = $y->value; + + if ($b !== '0' && $x->scale > $y->scale) { + $b .= \str_repeat('0', $x->scale - $y->scale); + } elseif ($a !== '0' && $x->scale < $y->scale) { + $a .= \str_repeat('0', $y->scale - $x->scale); + } + + return [$a, $b]; + } + + private function valueWithMinScale(int $scale) : string + { + $value = $this->value; + + if ($this->value !== '0' && $scale > $this->scale) { + $value .= \str_repeat('0', $scale - $this->scale); + } + + return $value; + } + + /** + * Adds leading zeros if necessary to the unscaled value to represent the full decimal number. + */ + private function getUnscaledValueWithLeadingZeros() : string + { + $value = $this->value; + $targetLength = $this->scale + 1; + $negative = ($value[0] === '-'); + $length = \strlen($value); + + if ($negative) { + $length--; + } + + if ($length >= $targetLength) { + return $this->value; + } + + if ($negative) { + $value = \substr($value, 1); + } + + $value = \str_pad($value, $targetLength, '0', STR_PAD_LEFT); + + if ($negative) { + $value = '-' . $value; + } + + return $value; + } +} diff --git a/vendor/brick/math/src/BigInteger.php b/vendor/brick/math/src/BigInteger.php new file mode 100644 index 0000000..4356793 --- /dev/null +++ b/vendor/brick/math/src/BigInteger.php @@ -0,0 +1,1079 @@ +value = $value; + } + + /** + * Creates a BigInteger of the given value. + * + * @throws MathException If the value cannot be converted to a BigInteger. + * + * @psalm-pure + */ + public static function of(BigNumber|int|float|string $value) : BigInteger + { + return parent::of($value)->toBigInteger(); + } + + /** + * Creates a number from a string in a given base. + * + * The string can optionally be prefixed with the `+` or `-` sign. + * + * Bases greater than 36 are not supported by this method, as there is no clear consensus on which of the lowercase + * or uppercase characters should come first. Instead, this method accepts any base up to 36, and does not + * differentiate lowercase and uppercase characters, which are considered equal. + * + * For bases greater than 36, and/or custom alphabets, use the fromArbitraryBase() method. + * + * @param string $number The number to convert, in the given base. + * @param int $base The base of the number, between 2 and 36. + * + * @throws NumberFormatException If the number is empty, or contains invalid chars for the given base. + * @throws \InvalidArgumentException If the base is out of range. + * + * @psalm-pure + */ + public static function fromBase(string $number, int $base) : BigInteger + { + if ($number === '') { + throw new NumberFormatException('The number cannot be empty.'); + } + + if ($base < 2 || $base > 36) { + throw new \InvalidArgumentException(\sprintf('Base %d is not in range 2 to 36.', $base)); + } + + if ($number[0] === '-') { + $sign = '-'; + $number = \substr($number, 1); + } elseif ($number[0] === '+') { + $sign = ''; + $number = \substr($number, 1); + } else { + $sign = ''; + } + + if ($number === '') { + throw new NumberFormatException('The number cannot be empty.'); + } + + $number = \ltrim($number, '0'); + + if ($number === '') { + // The result will be the same in any base, avoid further calculation. + return BigInteger::zero(); + } + + if ($number === '1') { + // The result will be the same in any base, avoid further calculation. + return new BigInteger($sign . '1'); + } + + $pattern = '/[^' . \substr(Calculator::ALPHABET, 0, $base) . ']/'; + + if (\preg_match($pattern, \strtolower($number), $matches) === 1) { + throw new NumberFormatException(\sprintf('"%s" is not a valid character in base %d.', $matches[0], $base)); + } + + if ($base === 10) { + // The number is usable as is, avoid further calculation. + return new BigInteger($sign . $number); + } + + $result = Calculator::get()->fromBase($number, $base); + + return new BigInteger($sign . $result); + } + + /** + * Parses a string containing an integer in an arbitrary base, using a custom alphabet. + * + * Because this method accepts an alphabet with any character, including dash, it does not handle negative numbers. + * + * @param string $number The number to parse. + * @param string $alphabet The alphabet, for example '01' for base 2, or '01234567' for base 8. + * + * @throws NumberFormatException If the given number is empty or contains invalid chars for the given alphabet. + * @throws \InvalidArgumentException If the alphabet does not contain at least 2 chars. + * + * @psalm-pure + */ + public static function fromArbitraryBase(string $number, string $alphabet) : BigInteger + { + if ($number === '') { + throw new NumberFormatException('The number cannot be empty.'); + } + + $base = \strlen($alphabet); + + if ($base < 2) { + throw new \InvalidArgumentException('The alphabet must contain at least 2 chars.'); + } + + $pattern = '/[^' . \preg_quote($alphabet, '/') . ']/'; + + if (\preg_match($pattern, $number, $matches) === 1) { + throw NumberFormatException::charNotInAlphabet($matches[0]); + } + + $number = Calculator::get()->fromArbitraryBase($number, $alphabet, $base); + + return new BigInteger($number); + } + + /** + * Translates a string of bytes containing the binary representation of a BigInteger into a BigInteger. + * + * The input string is assumed to be in big-endian byte-order: the most significant byte is in the zeroth element. + * + * If `$signed` is true, the input is assumed to be in two's-complement representation, and the leading bit is + * interpreted as a sign bit. If `$signed` is false, the input is interpreted as an unsigned number, and the + * resulting BigInteger will always be positive or zero. + * + * This method can be used to retrieve a number exported by `toBytes()`, as long as the `$signed` flags match. + * + * @param string $value The byte string. + * @param bool $signed Whether to interpret as a signed number in two's-complement representation with a leading + * sign bit. + * + * @throws NumberFormatException If the string is empty. + */ + public static function fromBytes(string $value, bool $signed = true) : BigInteger + { + if ($value === '') { + throw new NumberFormatException('The byte string must not be empty.'); + } + + $twosComplement = false; + + if ($signed) { + $x = \ord($value[0]); + + if (($twosComplement = ($x >= 0x80))) { + $value = ~$value; + } + } + + $number = self::fromBase(\bin2hex($value), 16); + + if ($twosComplement) { + return $number->plus(1)->negated(); + } + + return $number; + } + + /** + * Generates a pseudo-random number in the range 0 to 2^numBits - 1. + * + * Using the default random bytes generator, this method is suitable for cryptographic use. + * + * @psalm-param (callable(int): string)|null $randomBytesGenerator + * + * @param int $numBits The number of bits. + * @param callable|null $randomBytesGenerator A function that accepts a number of bytes as an integer, and returns a + * string of random bytes of the given length. Defaults to the + * `random_bytes()` function. + * + * @throws \InvalidArgumentException If $numBits is negative. + */ + public static function randomBits(int $numBits, ?callable $randomBytesGenerator = null) : BigInteger + { + if ($numBits < 0) { + throw new \InvalidArgumentException('The number of bits cannot be negative.'); + } + + if ($numBits === 0) { + return BigInteger::zero(); + } + + if ($randomBytesGenerator === null) { + $randomBytesGenerator = 'random_bytes'; + } + + $byteLength = \intdiv($numBits - 1, 8) + 1; + + $extraBits = ($byteLength * 8 - $numBits); + $bitmask = \chr(0xFF >> $extraBits); + + $randomBytes = $randomBytesGenerator($byteLength); + $randomBytes[0] = $randomBytes[0] & $bitmask; + + return self::fromBytes($randomBytes, false); + } + + /** + * Generates a pseudo-random number between `$min` and `$max`. + * + * Using the default random bytes generator, this method is suitable for cryptographic use. + * + * @psalm-param (callable(int): string)|null $randomBytesGenerator + * + * @param BigNumber|int|float|string $min The lower bound. Must be convertible to a BigInteger. + * @param BigNumber|int|float|string $max The upper bound. Must be convertible to a BigInteger. + * @param callable|null $randomBytesGenerator A function that accepts a number of bytes as an integer, + * and returns a string of random bytes of the given length. + * Defaults to the `random_bytes()` function. + * + * @throws MathException If one of the parameters cannot be converted to a BigInteger, + * or `$min` is greater than `$max`. + */ + public static function randomRange( + BigNumber|int|float|string $min, + BigNumber|int|float|string $max, + ?callable $randomBytesGenerator = null + ) : BigInteger { + $min = BigInteger::of($min); + $max = BigInteger::of($max); + + if ($min->isGreaterThan($max)) { + throw new MathException('$min cannot be greater than $max.'); + } + + if ($min->isEqualTo($max)) { + return $min; + } + + $diff = $max->minus($min); + $bitLength = $diff->getBitLength(); + + // try until the number is in range (50% to 100% chance of success) + do { + $randomNumber = self::randomBits($bitLength, $randomBytesGenerator); + } while ($randomNumber->isGreaterThan($diff)); + + return $randomNumber->plus($min); + } + + /** + * Returns a BigInteger representing zero. + * + * @psalm-pure + */ + public static function zero() : BigInteger + { + /** + * @psalm-suppress ImpureStaticVariable + * @var BigInteger|null $zero + */ + static $zero; + + if ($zero === null) { + $zero = new BigInteger('0'); + } + + return $zero; + } + + /** + * Returns a BigInteger representing one. + * + * @psalm-pure + */ + public static function one() : BigInteger + { + /** + * @psalm-suppress ImpureStaticVariable + * @var BigInteger|null $one + */ + static $one; + + if ($one === null) { + $one = new BigInteger('1'); + } + + return $one; + } + + /** + * Returns a BigInteger representing ten. + * + * @psalm-pure + */ + public static function ten() : BigInteger + { + /** + * @psalm-suppress ImpureStaticVariable + * @var BigInteger|null $ten + */ + static $ten; + + if ($ten === null) { + $ten = new BigInteger('10'); + } + + return $ten; + } + + public static function gcdMultiple(BigInteger $a, BigInteger ...$n): BigInteger + { + $result = $a; + + foreach ($n as $next) { + $result = $result->gcd($next); + + if ($result->isEqualTo(1)) { + return $result; + } + } + + return $result; + } + + /** + * Returns the sum of this number and the given one. + * + * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigInteger. + * + * @throws MathException If the number is not valid, or is not convertible to a BigInteger. + */ + public function plus(BigNumber|int|float|string $that) : BigInteger + { + $that = BigInteger::of($that); + + if ($that->value === '0') { + return $this; + } + + if ($this->value === '0') { + return $that; + } + + $value = Calculator::get()->add($this->value, $that->value); + + return new BigInteger($value); + } + + /** + * Returns the difference of this number and the given one. + * + * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigInteger. + * + * @throws MathException If the number is not valid, or is not convertible to a BigInteger. + */ + public function minus(BigNumber|int|float|string $that) : BigInteger + { + $that = BigInteger::of($that); + + if ($that->value === '0') { + return $this; + } + + $value = Calculator::get()->sub($this->value, $that->value); + + return new BigInteger($value); + } + + /** + * Returns the product of this number and the given one. + * + * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigInteger. + * + * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigInteger. + */ + public function multipliedBy(BigNumber|int|float|string $that) : BigInteger + { + $that = BigInteger::of($that); + + if ($that->value === '1') { + return $this; + } + + if ($this->value === '1') { + return $that; + } + + $value = Calculator::get()->mul($this->value, $that->value); + + return new BigInteger($value); + } + + /** + * Returns the result of the division of this number by the given one. + * + * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. + * @param int $roundingMode An optional rounding mode. + * + * @throws MathException If the divisor is not a valid number, is not convertible to a BigInteger, is zero, + * or RoundingMode::UNNECESSARY is used and the remainder is not zero. + */ + public function dividedBy(BigNumber|int|float|string $that, int $roundingMode = RoundingMode::UNNECESSARY) : BigInteger + { + $that = BigInteger::of($that); + + if ($that->value === '1') { + return $this; + } + + if ($that->value === '0') { + throw DivisionByZeroException::divisionByZero(); + } + + $result = Calculator::get()->divRound($this->value, $that->value, $roundingMode); + + return new BigInteger($result); + } + + /** + * Returns this number exponentiated to the given value. + * + * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000. + */ + public function power(int $exponent) : BigInteger + { + if ($exponent === 0) { + return BigInteger::one(); + } + + if ($exponent === 1) { + return $this; + } + + if ($exponent < 0 || $exponent > Calculator::MAX_POWER) { + throw new \InvalidArgumentException(\sprintf( + 'The exponent %d is not in the range 0 to %d.', + $exponent, + Calculator::MAX_POWER + )); + } + + return new BigInteger(Calculator::get()->pow($this->value, $exponent)); + } + + /** + * Returns the quotient of the division of this number by the given one. + * + * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. + * + * @throws DivisionByZeroException If the divisor is zero. + */ + public function quotient(BigNumber|int|float|string $that) : BigInteger + { + $that = BigInteger::of($that); + + if ($that->value === '1') { + return $this; + } + + if ($that->value === '0') { + throw DivisionByZeroException::divisionByZero(); + } + + $quotient = Calculator::get()->divQ($this->value, $that->value); + + return new BigInteger($quotient); + } + + /** + * Returns the remainder of the division of this number by the given one. + * + * The remainder, when non-zero, has the same sign as the dividend. + * + * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. + * + * @throws DivisionByZeroException If the divisor is zero. + */ + public function remainder(BigNumber|int|float|string $that) : BigInteger + { + $that = BigInteger::of($that); + + if ($that->value === '1') { + return BigInteger::zero(); + } + + if ($that->value === '0') { + throw DivisionByZeroException::divisionByZero(); + } + + $remainder = Calculator::get()->divR($this->value, $that->value); + + return new BigInteger($remainder); + } + + /** + * Returns the quotient and remainder of the division of this number by the given one. + * + * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. + * + * @return BigInteger[] An array containing the quotient and the remainder. + * + * @throws DivisionByZeroException If the divisor is zero. + */ + public function quotientAndRemainder(BigNumber|int|float|string $that) : array + { + $that = BigInteger::of($that); + + if ($that->value === '0') { + throw DivisionByZeroException::divisionByZero(); + } + + [$quotient, $remainder] = Calculator::get()->divQR($this->value, $that->value); + + return [ + new BigInteger($quotient), + new BigInteger($remainder) + ]; + } + + /** + * Returns the modulo of this number and the given one. + * + * The modulo operation yields the same result as the remainder operation when both operands are of the same sign, + * and may differ when signs are different. + * + * The result of the modulo operation, when non-zero, has the same sign as the divisor. + * + * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger. + * + * @throws DivisionByZeroException If the divisor is zero. + */ + public function mod(BigNumber|int|float|string $that) : BigInteger + { + $that = BigInteger::of($that); + + if ($that->value === '0') { + throw DivisionByZeroException::modulusMustNotBeZero(); + } + + $value = Calculator::get()->mod($this->value, $that->value); + + return new BigInteger($value); + } + + /** + * Returns the modular multiplicative inverse of this BigInteger modulo $m. + * + * @throws DivisionByZeroException If $m is zero. + * @throws NegativeNumberException If $m is negative. + * @throws MathException If this BigInteger has no multiplicative inverse mod m (that is, this BigInteger + * is not relatively prime to m). + */ + public function modInverse(BigInteger $m) : BigInteger + { + if ($m->value === '0') { + throw DivisionByZeroException::modulusMustNotBeZero(); + } + + if ($m->isNegative()) { + throw new NegativeNumberException('Modulus must not be negative.'); + } + + if ($m->value === '1') { + return BigInteger::zero(); + } + + $value = Calculator::get()->modInverse($this->value, $m->value); + + if ($value === null) { + throw new MathException('Unable to compute the modInverse for the given modulus.'); + } + + return new BigInteger($value); + } + + /** + * Returns this number raised into power with modulo. + * + * This operation only works on positive numbers. + * + * @param BigNumber|int|float|string $exp The exponent. Must be positive or zero. + * @param BigNumber|int|float|string $mod The modulus. Must be strictly positive. + * + * @throws NegativeNumberException If any of the operands is negative. + * @throws DivisionByZeroException If the modulus is zero. + */ + public function modPow(BigNumber|int|float|string $exp, BigNumber|int|float|string $mod) : BigInteger + { + $exp = BigInteger::of($exp); + $mod = BigInteger::of($mod); + + if ($this->isNegative() || $exp->isNegative() || $mod->isNegative()) { + throw new NegativeNumberException('The operands cannot be negative.'); + } + + if ($mod->isZero()) { + throw DivisionByZeroException::modulusMustNotBeZero(); + } + + $result = Calculator::get()->modPow($this->value, $exp->value, $mod->value); + + return new BigInteger($result); + } + + /** + * Returns the greatest common divisor of this number and the given one. + * + * The GCD is always positive, unless both operands are zero, in which case it is zero. + * + * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number. + */ + public function gcd(BigNumber|int|float|string $that) : BigInteger + { + $that = BigInteger::of($that); + + if ($that->value === '0' && $this->value[0] !== '-') { + return $this; + } + + if ($this->value === '0' && $that->value[0] !== '-') { + return $that; + } + + $value = Calculator::get()->gcd($this->value, $that->value); + + return new BigInteger($value); + } + + /** + * Returns the integer square root number of this number, rounded down. + * + * The result is the largest x such that x² ≤ n. + * + * @throws NegativeNumberException If this number is negative. + */ + public function sqrt() : BigInteger + { + if ($this->value[0] === '-') { + throw new NegativeNumberException('Cannot calculate the square root of a negative number.'); + } + + $value = Calculator::get()->sqrt($this->value); + + return new BigInteger($value); + } + + /** + * Returns the absolute value of this number. + */ + public function abs() : BigInteger + { + return $this->isNegative() ? $this->negated() : $this; + } + + /** + * Returns the inverse of this number. + */ + public function negated() : BigInteger + { + return new BigInteger(Calculator::get()->neg($this->value)); + } + + /** + * Returns the integer bitwise-and combined with another integer. + * + * This method returns a negative BigInteger if and only if both operands are negative. + * + * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number. + */ + public function and(BigNumber|int|float|string $that) : BigInteger + { + $that = BigInteger::of($that); + + return new BigInteger(Calculator::get()->and($this->value, $that->value)); + } + + /** + * Returns the integer bitwise-or combined with another integer. + * + * This method returns a negative BigInteger if and only if either of the operands is negative. + * + * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number. + */ + public function or(BigNumber|int|float|string $that) : BigInteger + { + $that = BigInteger::of($that); + + return new BigInteger(Calculator::get()->or($this->value, $that->value)); + } + + /** + * Returns the integer bitwise-xor combined with another integer. + * + * This method returns a negative BigInteger if and only if exactly one of the operands is negative. + * + * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number. + */ + public function xor(BigNumber|int|float|string $that) : BigInteger + { + $that = BigInteger::of($that); + + return new BigInteger(Calculator::get()->xor($this->value, $that->value)); + } + + /** + * Returns the bitwise-not of this BigInteger. + */ + public function not() : BigInteger + { + return $this->negated()->minus(1); + } + + /** + * Returns the integer left shifted by a given number of bits. + */ + public function shiftedLeft(int $distance) : BigInteger + { + if ($distance === 0) { + return $this; + } + + if ($distance < 0) { + return $this->shiftedRight(- $distance); + } + + return $this->multipliedBy(BigInteger::of(2)->power($distance)); + } + + /** + * Returns the integer right shifted by a given number of bits. + */ + public function shiftedRight(int $distance) : BigInteger + { + if ($distance === 0) { + return $this; + } + + if ($distance < 0) { + return $this->shiftedLeft(- $distance); + } + + $operand = BigInteger::of(2)->power($distance); + + if ($this->isPositiveOrZero()) { + return $this->quotient($operand); + } + + return $this->dividedBy($operand, RoundingMode::UP); + } + + /** + * Returns the number of bits in the minimal two's-complement representation of this BigInteger, excluding a sign bit. + * + * For positive BigIntegers, this is equivalent to the number of bits in the ordinary binary representation. + * Computes (ceil(log2(this < 0 ? -this : this+1))). + */ + public function getBitLength() : int + { + if ($this->value === '0') { + return 0; + } + + if ($this->isNegative()) { + return $this->abs()->minus(1)->getBitLength(); + } + + return \strlen($this->toBase(2)); + } + + /** + * Returns the index of the rightmost (lowest-order) one bit in this BigInteger. + * + * Returns -1 if this BigInteger contains no one bits. + */ + public function getLowestSetBit() : int + { + $n = $this; + $bitLength = $this->getBitLength(); + + for ($i = 0; $i <= $bitLength; $i++) { + if ($n->isOdd()) { + return $i; + } + + $n = $n->shiftedRight(1); + } + + return -1; + } + + /** + * Returns whether this number is even. + */ + public function isEven() : bool + { + return \in_array($this->value[-1], ['0', '2', '4', '6', '8'], true); + } + + /** + * Returns whether this number is odd. + */ + public function isOdd() : bool + { + return \in_array($this->value[-1], ['1', '3', '5', '7', '9'], true); + } + + /** + * Returns true if and only if the designated bit is set. + * + * Computes ((this & (1<shiftedRight($n)->isOdd(); + } + + public function compareTo(BigNumber|int|float|string $that) : int + { + $that = BigNumber::of($that); + + if ($that instanceof BigInteger) { + return Calculator::get()->cmp($this->value, $that->value); + } + + return - $that->compareTo($this); + } + + public function getSign() : int + { + return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1); + } + + public function toBigInteger() : BigInteger + { + return $this; + } + + public function toBigDecimal() : BigDecimal + { + return self::newBigDecimal($this->value); + } + + public function toBigRational() : BigRational + { + return self::newBigRational($this, BigInteger::one(), false); + } + + public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal + { + return $this->toBigDecimal()->toScale($scale, $roundingMode); + } + + public function toInt() : int + { + $intValue = (int) $this->value; + + if ($this->value !== (string) $intValue) { + throw IntegerOverflowException::toIntOverflow($this); + } + + return $intValue; + } + + public function toFloat() : float + { + return (float) $this->value; + } + + /** + * Returns a string representation of this number in the given base. + * + * The output will always be lowercase for bases greater than 10. + * + * @throws \InvalidArgumentException If the base is out of range. + */ + public function toBase(int $base) : string + { + if ($base === 10) { + return $this->value; + } + + if ($base < 2 || $base > 36) { + throw new \InvalidArgumentException(\sprintf('Base %d is out of range [2, 36]', $base)); + } + + return Calculator::get()->toBase($this->value, $base); + } + + /** + * Returns a string representation of this number in an arbitrary base with a custom alphabet. + * + * Because this method accepts an alphabet with any character, including dash, it does not handle negative numbers; + * a NegativeNumberException will be thrown when attempting to call this method on a negative number. + * + * @param string $alphabet The alphabet, for example '01' for base 2, or '01234567' for base 8. + * + * @throws NegativeNumberException If this number is negative. + * @throws \InvalidArgumentException If the given alphabet does not contain at least 2 chars. + */ + public function toArbitraryBase(string $alphabet) : string + { + $base = \strlen($alphabet); + + if ($base < 2) { + throw new \InvalidArgumentException('The alphabet must contain at least 2 chars.'); + } + + if ($this->value[0] === '-') { + throw new NegativeNumberException(__FUNCTION__ . '() does not support negative numbers.'); + } + + return Calculator::get()->toArbitraryBase($this->value, $alphabet, $base); + } + + /** + * Returns a string of bytes containing the binary representation of this BigInteger. + * + * The string is in big-endian byte-order: the most significant byte is in the zeroth element. + * + * If `$signed` is true, the output will be in two's-complement representation, and a sign bit will be prepended to + * the output. If `$signed` is false, no sign bit will be prepended, and this method will throw an exception if the + * number is negative. + * + * The string will contain the minimum number of bytes required to represent this BigInteger, including a sign bit + * if `$signed` is true. + * + * This representation is compatible with the `fromBytes()` factory method, as long as the `$signed` flags match. + * + * @param bool $signed Whether to output a signed number in two's-complement representation with a leading sign bit. + * + * @throws NegativeNumberException If $signed is false, and the number is negative. + */ + public function toBytes(bool $signed = true) : string + { + if (! $signed && $this->isNegative()) { + throw new NegativeNumberException('Cannot convert a negative number to a byte string when $signed is false.'); + } + + $hex = $this->abs()->toBase(16); + + if (\strlen($hex) % 2 !== 0) { + $hex = '0' . $hex; + } + + $baseHexLength = \strlen($hex); + + if ($signed) { + if ($this->isNegative()) { + $bin = \hex2bin($hex); + assert($bin !== false); + + $hex = \bin2hex(~$bin); + $hex = self::fromBase($hex, 16)->plus(1)->toBase(16); + + $hexLength = \strlen($hex); + + if ($hexLength < $baseHexLength) { + $hex = \str_repeat('0', $baseHexLength - $hexLength) . $hex; + } + + if ($hex[0] < '8') { + $hex = 'FF' . $hex; + } + } else { + if ($hex[0] >= '8') { + $hex = '00' . $hex; + } + } + } + + return \hex2bin($hex); + } + + public function __toString() : string + { + return $this->value; + } + + /** + * This method is required for serializing the object and SHOULD NOT be accessed directly. + * + * @internal + * + * @return array{value: string} + */ + public function __serialize(): array + { + return ['value' => $this->value]; + } + + /** + * This method is only here to allow unserializing the object and cannot be accessed directly. + * + * @internal + * @psalm-suppress RedundantPropertyInitializationCheck + * + * @param array{value: string} $data + * + * @throws \LogicException + */ + public function __unserialize(array $data): void + { + if (isset($this->value)) { + throw new \LogicException('__unserialize() is an internal function, it must not be called directly.'); + } + + $this->value = $data['value']; + } + + /** + * This method is required by interface Serializable and SHOULD NOT be accessed directly. + * + * @internal + */ + public function serialize() : string + { + return $this->value; + } + + /** + * This method is only here to implement interface Serializable and cannot be accessed directly. + * + * @internal + * @psalm-suppress RedundantPropertyInitializationCheck + * + * @throws \LogicException + */ + public function unserialize($value) : void + { + if (isset($this->value)) { + throw new \LogicException('unserialize() is an internal function, it must not be called directly.'); + } + + $this->value = $value; + } +} diff --git a/vendor/brick/math/src/BigNumber.php b/vendor/brick/math/src/BigNumber.php new file mode 100644 index 0000000..80146d2 --- /dev/null +++ b/vendor/brick/math/src/BigNumber.php @@ -0,0 +1,512 @@ +[\-\+])?' . + '(?:' . + '(?:' . + '(?[0-9]+)?' . + '(?\.)?' . + '(?[0-9]+)?' . + '(?:[eE](?[\-\+]?[0-9]+))?' . + ')|(?:' . + '(?[0-9]+)' . + '\/?' . + '(?[0-9]+)' . + ')' . + ')' . + '$/'; + + /** + * Creates a BigNumber of the given value. + * + * The concrete return type is dependent on the given value, with the following rules: + * + * - BigNumber instances are returned as is + * - integer numbers are returned as BigInteger + * - floating point numbers are converted to a string then parsed as such + * - strings containing a `/` character are returned as BigRational + * - strings containing a `.` character or using an exponential notation are returned as BigDecimal + * - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger + * + * @throws NumberFormatException If the format of the number is not valid. + * @throws DivisionByZeroException If the value represents a rational number with a denominator of zero. + * + * @psalm-pure + */ + public static function of(BigNumber|int|float|string $value) : BigNumber + { + if ($value instanceof BigNumber) { + return $value; + } + + if (\is_int($value)) { + return new BigInteger((string) $value); + } + + $value = \is_float($value) ? self::floatToString($value) : $value; + + $throw = static function() use ($value) : void { + throw new NumberFormatException(\sprintf( + 'The given value "%s" does not represent a valid number.', + $value + )); + }; + + if (\preg_match(self::PARSE_REGEXP, $value, $matches) !== 1) { + $throw(); + } + + $getMatch = static fn(string $value): ?string => (($matches[$value] ?? '') !== '') ? $matches[$value] : null; + + $sign = $getMatch('sign'); + $numerator = $getMatch('numerator'); + $denominator = $getMatch('denominator'); + + if ($numerator !== null) { + assert($denominator !== null); + + if ($sign !== null) { + $numerator = $sign . $numerator; + } + + $numerator = self::cleanUp($numerator); + $denominator = self::cleanUp($denominator); + + if ($denominator === '0') { + throw DivisionByZeroException::denominatorMustNotBeZero(); + } + + return new BigRational( + new BigInteger($numerator), + new BigInteger($denominator), + false + ); + } + + $point = $getMatch('point'); + $integral = $getMatch('integral'); + $fractional = $getMatch('fractional'); + $exponent = $getMatch('exponent'); + + if ($integral === null && $fractional === null) { + $throw(); + } + + if ($integral === null) { + $integral = '0'; + } + + if ($point !== null || $exponent !== null) { + $fractional = ($fractional ?? ''); + $exponent = ($exponent !== null) ? (int) $exponent : 0; + + if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) { + throw new NumberFormatException('Exponent too large.'); + } + + $unscaledValue = self::cleanUp(($sign ?? ''). $integral . $fractional); + + $scale = \strlen($fractional) - $exponent; + + if ($scale < 0) { + if ($unscaledValue !== '0') { + $unscaledValue .= \str_repeat('0', - $scale); + } + $scale = 0; + } + + return new BigDecimal($unscaledValue, $scale); + } + + $integral = self::cleanUp(($sign ?? '') . $integral); + + return new BigInteger($integral); + } + + /** + * Safely converts float to string, avoiding locale-dependent issues. + * + * @see https://github.com/brick/math/pull/20 + * + * @psalm-pure + * @psalm-suppress ImpureFunctionCall + */ + private static function floatToString(float $float) : string + { + $currentLocale = \setlocale(LC_NUMERIC, '0'); + \setlocale(LC_NUMERIC, 'C'); + + $result = (string) $float; + + \setlocale(LC_NUMERIC, $currentLocale); + + return $result; + } + + /** + * Proxy method to access BigInteger's protected constructor from sibling classes. + * + * @internal + * @psalm-pure + */ + protected function newBigInteger(string $value) : BigInteger + { + return new BigInteger($value); + } + + /** + * Proxy method to access BigDecimal's protected constructor from sibling classes. + * + * @internal + * @psalm-pure + */ + protected function newBigDecimal(string $value, int $scale = 0) : BigDecimal + { + return new BigDecimal($value, $scale); + } + + /** + * Proxy method to access BigRational's protected constructor from sibling classes. + * + * @internal + * @psalm-pure + */ + protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) : BigRational + { + return new BigRational($numerator, $denominator, $checkDenominator); + } + + /** + * Returns the minimum of the given values. + * + * @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible + * to an instance of the class this method is called on. + * + * @throws \InvalidArgumentException If no values are given. + * @throws MathException If an argument is not valid. + * + * @psalm-suppress LessSpecificReturnStatement + * @psalm-suppress MoreSpecificReturnType + * @psalm-pure + */ + public static function min(BigNumber|int|float|string ...$values) : static + { + $min = null; + + foreach ($values as $value) { + $value = static::of($value); + + if ($min === null || $value->isLessThan($min)) { + $min = $value; + } + } + + if ($min === null) { + throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.'); + } + + return $min; + } + + /** + * Returns the maximum of the given values. + * + * @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible + * to an instance of the class this method is called on. + * + * @throws \InvalidArgumentException If no values are given. + * @throws MathException If an argument is not valid. + * + * @psalm-suppress LessSpecificReturnStatement + * @psalm-suppress MoreSpecificReturnType + * @psalm-pure + */ + public static function max(BigNumber|int|float|string ...$values) : static + { + $max = null; + + foreach ($values as $value) { + $value = static::of($value); + + if ($max === null || $value->isGreaterThan($max)) { + $max = $value; + } + } + + if ($max === null) { + throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.'); + } + + return $max; + } + + /** + * Returns the sum of the given values. + * + * @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible + * to an instance of the class this method is called on. + * + * @throws \InvalidArgumentException If no values are given. + * @throws MathException If an argument is not valid. + * + * @psalm-pure + */ + public static function sum(BigNumber|int|float|string ...$values) : static + { + /** @var static|null $sum */ + $sum = null; + + foreach ($values as $value) { + $value = static::of($value); + + $sum = $sum === null ? $value : self::add($sum, $value); + } + + if ($sum === null) { + throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.'); + } + + return $sum; + } + + /** + * Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException. + * + * @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to + * concrete classes the responsibility to perform the addition themselves or delegate it to the given number, + * depending on their ability to perform the operation. This will also require a version bump because we're + * potentially breaking custom BigNumber implementations (if any...) + * + * @psalm-pure + */ + private static function add(BigNumber $a, BigNumber $b) : BigNumber + { + if ($a instanceof BigRational) { + return $a->plus($b); + } + + if ($b instanceof BigRational) { + return $b->plus($a); + } + + if ($a instanceof BigDecimal) { + return $a->plus($b); + } + + if ($b instanceof BigDecimal) { + return $b->plus($a); + } + + /** @var BigInteger $a */ + + return $a->plus($b); + } + + /** + * Removes optional leading zeros and + sign from the given number. + * + * @param string $number The number, validated as a non-empty string of digits with optional leading sign. + * + * @psalm-pure + */ + private static function cleanUp(string $number) : string + { + $firstChar = $number[0]; + + if ($firstChar === '+' || $firstChar === '-') { + $number = \substr($number, 1); + } + + $number = \ltrim($number, '0'); + + if ($number === '') { + return '0'; + } + + if ($firstChar === '-') { + return '-' . $number; + } + + return $number; + } + + /** + * Checks if this number is equal to the given one. + */ + public function isEqualTo(BigNumber|int|float|string $that) : bool + { + return $this->compareTo($that) === 0; + } + + /** + * Checks if this number is strictly lower than the given one. + */ + public function isLessThan(BigNumber|int|float|string $that) : bool + { + return $this->compareTo($that) < 0; + } + + /** + * Checks if this number is lower than or equal to the given one. + */ + public function isLessThanOrEqualTo(BigNumber|int|float|string $that) : bool + { + return $this->compareTo($that) <= 0; + } + + /** + * Checks if this number is strictly greater than the given one. + */ + public function isGreaterThan(BigNumber|int|float|string $that) : bool + { + return $this->compareTo($that) > 0; + } + + /** + * Checks if this number is greater than or equal to the given one. + */ + public function isGreaterThanOrEqualTo(BigNumber|int|float|string $that) : bool + { + return $this->compareTo($that) >= 0; + } + + /** + * Checks if this number equals zero. + */ + public function isZero() : bool + { + return $this->getSign() === 0; + } + + /** + * Checks if this number is strictly negative. + */ + public function isNegative() : bool + { + return $this->getSign() < 0; + } + + /** + * Checks if this number is negative or zero. + */ + public function isNegativeOrZero() : bool + { + return $this->getSign() <= 0; + } + + /** + * Checks if this number is strictly positive. + */ + public function isPositive() : bool + { + return $this->getSign() > 0; + } + + /** + * Checks if this number is positive or zero. + */ + public function isPositiveOrZero() : bool + { + return $this->getSign() >= 0; + } + + /** + * Returns the sign of this number. + * + * @return int -1 if the number is negative, 0 if zero, 1 if positive. + */ + abstract public function getSign() : int; + + /** + * Compares this number to the given one. + * + * @return int [-1,0,1] If `$this` is lower than, equal to, or greater than `$that`. + * + * @throws MathException If the number is not valid. + */ + abstract public function compareTo(BigNumber|int|float|string $that) : int; + + /** + * Converts this number to a BigInteger. + * + * @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding. + */ + abstract public function toBigInteger() : BigInteger; + + /** + * Converts this number to a BigDecimal. + * + * @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding. + */ + abstract public function toBigDecimal() : BigDecimal; + + /** + * Converts this number to a BigRational. + */ + abstract public function toBigRational() : BigRational; + + /** + * Converts this number to a BigDecimal with the given scale, using rounding if necessary. + * + * @param int $scale The scale of the resulting `BigDecimal`. + * @param int $roundingMode A `RoundingMode` constant. + * + * @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding. + * This only applies when RoundingMode::UNNECESSARY is used. + */ + abstract public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal; + + /** + * Returns the exact value of this number as a native integer. + * + * If this number cannot be converted to a native integer without losing precision, an exception is thrown. + * Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit. + * + * @throws MathException If this number cannot be exactly converted to a native integer. + */ + abstract public function toInt() : int; + + /** + * Returns an approximation of this number as a floating-point value. + * + * Note that this method can discard information as the precision of a floating-point value + * is inherently limited. + * + * If the number is greater than the largest representable floating point number, positive infinity is returned. + * If the number is less than the smallest representable floating point number, negative infinity is returned. + */ + abstract public function toFloat() : float; + + /** + * Returns a string representation of this number. + * + * The output of this method can be parsed by the `of()` factory method; + * this will yield an object equal to this one, without any information loss. + */ + abstract public function __toString() : string; + + public function jsonSerialize() : string + { + return $this->__toString(); + } +} diff --git a/vendor/brick/math/src/BigRational.php b/vendor/brick/math/src/BigRational.php new file mode 100644 index 0000000..31f2904 --- /dev/null +++ b/vendor/brick/math/src/BigRational.php @@ -0,0 +1,445 @@ +isZero()) { + throw DivisionByZeroException::denominatorMustNotBeZero(); + } + + if ($denominator->isNegative()) { + $numerator = $numerator->negated(); + $denominator = $denominator->negated(); + } + } + + $this->numerator = $numerator; + $this->denominator = $denominator; + } + + /** + * Creates a BigRational of the given value. + * + * @throws MathException If the value cannot be converted to a BigRational. + * + * @psalm-pure + */ + public static function of(BigNumber|int|float|string $value) : BigRational + { + return parent::of($value)->toBigRational(); + } + + /** + * Creates a BigRational out of a numerator and a denominator. + * + * If the denominator is negative, the signs of both the numerator and the denominator + * will be inverted to ensure that the denominator is always positive. + * + * @param BigNumber|int|float|string $numerator The numerator. Must be convertible to a BigInteger. + * @param BigNumber|int|float|string $denominator The denominator. Must be convertible to a BigInteger. + * + * @throws NumberFormatException If an argument does not represent a valid number. + * @throws RoundingNecessaryException If an argument represents a non-integer number. + * @throws DivisionByZeroException If the denominator is zero. + * + * @psalm-pure + */ + public static function nd( + BigNumber|int|float|string $numerator, + BigNumber|int|float|string $denominator, + ) : BigRational { + $numerator = BigInteger::of($numerator); + $denominator = BigInteger::of($denominator); + + return new BigRational($numerator, $denominator, true); + } + + /** + * Returns a BigRational representing zero. + * + * @psalm-pure + */ + public static function zero() : BigRational + { + /** + * @psalm-suppress ImpureStaticVariable + * @var BigRational|null $zero + */ + static $zero; + + if ($zero === null) { + $zero = new BigRational(BigInteger::zero(), BigInteger::one(), false); + } + + return $zero; + } + + /** + * Returns a BigRational representing one. + * + * @psalm-pure + */ + public static function one() : BigRational + { + /** + * @psalm-suppress ImpureStaticVariable + * @var BigRational|null $one + */ + static $one; + + if ($one === null) { + $one = new BigRational(BigInteger::one(), BigInteger::one(), false); + } + + return $one; + } + + /** + * Returns a BigRational representing ten. + * + * @psalm-pure + */ + public static function ten() : BigRational + { + /** + * @psalm-suppress ImpureStaticVariable + * @var BigRational|null $ten + */ + static $ten; + + if ($ten === null) { + $ten = new BigRational(BigInteger::ten(), BigInteger::one(), false); + } + + return $ten; + } + + public function getNumerator() : BigInteger + { + return $this->numerator; + } + + public function getDenominator() : BigInteger + { + return $this->denominator; + } + + /** + * Returns the quotient of the division of the numerator by the denominator. + */ + public function quotient() : BigInteger + { + return $this->numerator->quotient($this->denominator); + } + + /** + * Returns the remainder of the division of the numerator by the denominator. + */ + public function remainder() : BigInteger + { + return $this->numerator->remainder($this->denominator); + } + + /** + * Returns the quotient and remainder of the division of the numerator by the denominator. + * + * @return BigInteger[] + */ + public function quotientAndRemainder() : array + { + return $this->numerator->quotientAndRemainder($this->denominator); + } + + /** + * Returns the sum of this number and the given one. + * + * @param BigNumber|int|float|string $that The number to add. + * + * @throws MathException If the number is not valid. + */ + public function plus(BigNumber|int|float|string $that) : BigRational + { + $that = BigRational::of($that); + + $numerator = $this->numerator->multipliedBy($that->denominator); + $numerator = $numerator->plus($that->numerator->multipliedBy($this->denominator)); + $denominator = $this->denominator->multipliedBy($that->denominator); + + return new BigRational($numerator, $denominator, false); + } + + /** + * Returns the difference of this number and the given one. + * + * @param BigNumber|int|float|string $that The number to subtract. + * + * @throws MathException If the number is not valid. + */ + public function minus(BigNumber|int|float|string $that) : BigRational + { + $that = BigRational::of($that); + + $numerator = $this->numerator->multipliedBy($that->denominator); + $numerator = $numerator->minus($that->numerator->multipliedBy($this->denominator)); + $denominator = $this->denominator->multipliedBy($that->denominator); + + return new BigRational($numerator, $denominator, false); + } + + /** + * Returns the product of this number and the given one. + * + * @param BigNumber|int|float|string $that The multiplier. + * + * @throws MathException If the multiplier is not a valid number. + */ + public function multipliedBy(BigNumber|int|float|string $that) : BigRational + { + $that = BigRational::of($that); + + $numerator = $this->numerator->multipliedBy($that->numerator); + $denominator = $this->denominator->multipliedBy($that->denominator); + + return new BigRational($numerator, $denominator, false); + } + + /** + * Returns the result of the division of this number by the given one. + * + * @param BigNumber|int|float|string $that The divisor. + * + * @throws MathException If the divisor is not a valid number, or is zero. + */ + public function dividedBy(BigNumber|int|float|string $that) : BigRational + { + $that = BigRational::of($that); + + $numerator = $this->numerator->multipliedBy($that->denominator); + $denominator = $this->denominator->multipliedBy($that->numerator); + + return new BigRational($numerator, $denominator, true); + } + + /** + * Returns this number exponentiated to the given value. + * + * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000. + */ + public function power(int $exponent) : BigRational + { + if ($exponent === 0) { + $one = BigInteger::one(); + + return new BigRational($one, $one, false); + } + + if ($exponent === 1) { + return $this; + } + + return new BigRational( + $this->numerator->power($exponent), + $this->denominator->power($exponent), + false + ); + } + + /** + * Returns the reciprocal of this BigRational. + * + * The reciprocal has the numerator and denominator swapped. + * + * @throws DivisionByZeroException If the numerator is zero. + */ + public function reciprocal() : BigRational + { + return new BigRational($this->denominator, $this->numerator, true); + } + + /** + * Returns the absolute value of this BigRational. + */ + public function abs() : BigRational + { + return new BigRational($this->numerator->abs(), $this->denominator, false); + } + + /** + * Returns the negated value of this BigRational. + */ + public function negated() : BigRational + { + return new BigRational($this->numerator->negated(), $this->denominator, false); + } + + /** + * Returns the simplified value of this BigRational. + */ + public function simplified() : BigRational + { + $gcd = $this->numerator->gcd($this->denominator); + + $numerator = $this->numerator->quotient($gcd); + $denominator = $this->denominator->quotient($gcd); + + return new BigRational($numerator, $denominator, false); + } + + public function compareTo(BigNumber|int|float|string $that) : int + { + return $this->minus($that)->getSign(); + } + + public function getSign() : int + { + return $this->numerator->getSign(); + } + + public function toBigInteger() : BigInteger + { + $simplified = $this->simplified(); + + if (! $simplified->denominator->isEqualTo(1)) { + throw new RoundingNecessaryException('This rational number cannot be represented as an integer value without rounding.'); + } + + return $simplified->numerator; + } + + public function toBigDecimal() : BigDecimal + { + return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator); + } + + public function toBigRational() : BigRational + { + return $this; + } + + public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal + { + return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode); + } + + public function toInt() : int + { + return $this->toBigInteger()->toInt(); + } + + public function toFloat() : float + { + $simplified = $this->simplified(); + return $simplified->numerator->toFloat() / $simplified->denominator->toFloat(); + } + + public function __toString() : string + { + $numerator = (string) $this->numerator; + $denominator = (string) $this->denominator; + + if ($denominator === '1') { + return $numerator; + } + + return $this->numerator . '/' . $this->denominator; + } + + /** + * This method is required for serializing the object and SHOULD NOT be accessed directly. + * + * @internal + * + * @return array{numerator: BigInteger, denominator: BigInteger} + */ + public function __serialize(): array + { + return ['numerator' => $this->numerator, 'denominator' => $this->denominator]; + } + + /** + * This method is only here to allow unserializing the object and cannot be accessed directly. + * + * @internal + * @psalm-suppress RedundantPropertyInitializationCheck + * + * @param array{numerator: BigInteger, denominator: BigInteger} $data + * + * @throws \LogicException + */ + public function __unserialize(array $data): void + { + if (isset($this->numerator)) { + throw new \LogicException('__unserialize() is an internal function, it must not be called directly.'); + } + + $this->numerator = $data['numerator']; + $this->denominator = $data['denominator']; + } + + /** + * This method is required by interface Serializable and SHOULD NOT be accessed directly. + * + * @internal + */ + public function serialize() : string + { + return $this->numerator . '/' . $this->denominator; + } + + /** + * This method is only here to implement interface Serializable and cannot be accessed directly. + * + * @internal + * @psalm-suppress RedundantPropertyInitializationCheck + * + * @throws \LogicException + */ + public function unserialize($value) : void + { + if (isset($this->numerator)) { + throw new \LogicException('unserialize() is an internal function, it must not be called directly.'); + } + + [$numerator, $denominator] = \explode('/', $value); + + $this->numerator = BigInteger::of($numerator); + $this->denominator = BigInteger::of($denominator); + } +} diff --git a/vendor/brick/math/src/Exception/DivisionByZeroException.php b/vendor/brick/math/src/Exception/DivisionByZeroException.php new file mode 100644 index 0000000..ce7769a --- /dev/null +++ b/vendor/brick/math/src/Exception/DivisionByZeroException.php @@ -0,0 +1,35 @@ + 126) { + $char = \strtoupper(\dechex($ord)); + + if ($ord < 10) { + $char = '0' . $char; + } + } else { + $char = '"' . $char . '"'; + } + + return new self(sprintf('Char %s is not a valid character in the given alphabet.', $char)); + } +} diff --git a/vendor/brick/math/src/Exception/RoundingNecessaryException.php b/vendor/brick/math/src/Exception/RoundingNecessaryException.php new file mode 100644 index 0000000..57bfcd8 --- /dev/null +++ b/vendor/brick/math/src/Exception/RoundingNecessaryException.php @@ -0,0 +1,19 @@ +init($a, $b); + + if ($aNeg && ! $bNeg) { + return -1; + } + + if ($bNeg && ! $aNeg) { + return 1; + } + + $aLen = \strlen($aDig); + $bLen = \strlen($bDig); + + if ($aLen < $bLen) { + $result = -1; + } elseif ($aLen > $bLen) { + $result = 1; + } else { + $result = $aDig <=> $bDig; + } + + return $aNeg ? -$result : $result; + } + + /** + * Adds two numbers. + */ + abstract public function add(string $a, string $b) : string; + + /** + * Subtracts two numbers. + */ + abstract public function sub(string $a, string $b) : string; + + /** + * Multiplies two numbers. + */ + abstract public function mul(string $a, string $b) : string; + + /** + * Returns the quotient of the division of two numbers. + * + * @param string $a The dividend. + * @param string $b The divisor, must not be zero. + * + * @return string The quotient. + */ + abstract public function divQ(string $a, string $b) : string; + + /** + * Returns the remainder of the division of two numbers. + * + * @param string $a The dividend. + * @param string $b The divisor, must not be zero. + * + * @return string The remainder. + */ + abstract public function divR(string $a, string $b) : string; + + /** + * Returns the quotient and remainder of the division of two numbers. + * + * @param string $a The dividend. + * @param string $b The divisor, must not be zero. + * + * @return array{string, string} An array containing the quotient and remainder. + */ + abstract public function divQR(string $a, string $b) : array; + + /** + * Exponentiates a number. + * + * @param string $a The base number. + * @param int $e The exponent, validated as an integer between 0 and MAX_POWER. + * + * @return string The power. + */ + abstract public function pow(string $a, int $e) : string; + + /** + * @param string $b The modulus; must not be zero. + */ + public function mod(string $a, string $b) : string + { + return $this->divR($this->add($this->divR($a, $b), $b), $b); + } + + /** + * Returns the modular multiplicative inverse of $x modulo $m. + * + * If $x has no multiplicative inverse mod m, this method must return null. + * + * This method can be overridden by the concrete implementation if the underlying library has built-in support. + * + * @param string $m The modulus; must not be negative or zero. + */ + public function modInverse(string $x, string $m) : ?string + { + if ($m === '1') { + return '0'; + } + + $modVal = $x; + + if ($x[0] === '-' || ($this->cmp($this->abs($x), $m) >= 0)) { + $modVal = $this->mod($x, $m); + } + + [$g, $x] = $this->gcdExtended($modVal, $m); + + if ($g !== '1') { + return null; + } + + return $this->mod($this->add($this->mod($x, $m), $m), $m); + } + + /** + * Raises a number into power with modulo. + * + * @param string $base The base number; must be positive or zero. + * @param string $exp The exponent; must be positive or zero. + * @param string $mod The modulus; must be strictly positive. + */ + abstract public function modPow(string $base, string $exp, string $mod) : string; + + /** + * Returns the greatest common divisor of the two numbers. + * + * This method can be overridden by the concrete implementation if the underlying library + * has built-in support for GCD calculations. + * + * @return string The GCD, always positive, or zero if both arguments are zero. + */ + public function gcd(string $a, string $b) : string + { + if ($a === '0') { + return $this->abs($b); + } + + if ($b === '0') { + return $this->abs($a); + } + + return $this->gcd($b, $this->divR($a, $b)); + } + + /** + * @return array{string, string, string} GCD, X, Y + */ + private function gcdExtended(string $a, string $b) : array + { + if ($a === '0') { + return [$b, '0', '1']; + } + + [$gcd, $x1, $y1] = $this->gcdExtended($this->mod($b, $a), $a); + + $x = $this->sub($y1, $this->mul($this->divQ($b, $a), $x1)); + $y = $x1; + + return [$gcd, $x, $y]; + } + + /** + * Returns the square root of the given number, rounded down. + * + * The result is the largest x such that x² ≤ n. + * The input MUST NOT be negative. + */ + abstract public function sqrt(string $n) : string; + + /** + * Converts a number from an arbitrary base. + * + * This method can be overridden by the concrete implementation if the underlying library + * has built-in support for base conversion. + * + * @param string $number The number, positive or zero, non-empty, case-insensitively validated for the given base. + * @param int $base The base of the number, validated from 2 to 36. + * + * @return string The converted number, following the Calculator conventions. + */ + public function fromBase(string $number, int $base) : string + { + return $this->fromArbitraryBase(\strtolower($number), self::ALPHABET, $base); + } + + /** + * Converts a number to an arbitrary base. + * + * This method can be overridden by the concrete implementation if the underlying library + * has built-in support for base conversion. + * + * @param string $number The number to convert, following the Calculator conventions. + * @param int $base The base to convert to, validated from 2 to 36. + * + * @return string The converted number, lowercase. + */ + public function toBase(string $number, int $base) : string + { + $negative = ($number[0] === '-'); + + if ($negative) { + $number = \substr($number, 1); + } + + $number = $this->toArbitraryBase($number, self::ALPHABET, $base); + + if ($negative) { + return '-' . $number; + } + + return $number; + } + + /** + * Converts a non-negative number in an arbitrary base using a custom alphabet, to base 10. + * + * @param string $number The number to convert, validated as a non-empty string, + * containing only chars in the given alphabet/base. + * @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum. + * @param int $base The base of the number, validated from 2 to alphabet length. + * + * @return string The number in base 10, following the Calculator conventions. + */ + final public function fromArbitraryBase(string $number, string $alphabet, int $base) : string + { + // remove leading "zeros" + $number = \ltrim($number, $alphabet[0]); + + if ($number === '') { + return '0'; + } + + // optimize for "one" + if ($number === $alphabet[1]) { + return '1'; + } + + $result = '0'; + $power = '1'; + + $base = (string) $base; + + for ($i = \strlen($number) - 1; $i >= 0; $i--) { + $index = \strpos($alphabet, $number[$i]); + + if ($index !== 0) { + $result = $this->add($result, ($index === 1) + ? $power + : $this->mul($power, (string) $index) + ); + } + + if ($i !== 0) { + $power = $this->mul($power, $base); + } + } + + return $result; + } + + /** + * Converts a non-negative number to an arbitrary base using a custom alphabet. + * + * @param string $number The number to convert, positive or zero, following the Calculator conventions. + * @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum. + * @param int $base The base to convert to, validated from 2 to alphabet length. + * + * @return string The converted number in the given alphabet. + */ + final public function toArbitraryBase(string $number, string $alphabet, int $base) : string + { + if ($number === '0') { + return $alphabet[0]; + } + + $base = (string) $base; + $result = ''; + + while ($number !== '0') { + [$number, $remainder] = $this->divQR($number, $base); + $remainder = (int) $remainder; + + $result .= $alphabet[$remainder]; + } + + return \strrev($result); + } + + /** + * Performs a rounded division. + * + * Rounding is performed when the remainder of the division is not zero. + * + * @param string $a The dividend. + * @param string $b The divisor, must not be zero. + * @param int $roundingMode The rounding mode. + * + * @throws \InvalidArgumentException If the rounding mode is invalid. + * @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary. + * + * @psalm-suppress ImpureFunctionCall + */ + final public function divRound(string $a, string $b, int $roundingMode) : string + { + [$quotient, $remainder] = $this->divQR($a, $b); + + $hasDiscardedFraction = ($remainder !== '0'); + $isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-'); + + $discardedFractionSign = function() use ($remainder, $b) : int { + $r = $this->abs($this->mul($remainder, '2')); + $b = $this->abs($b); + + return $this->cmp($r, $b); + }; + + $increment = false; + + switch ($roundingMode) { + case RoundingMode::UNNECESSARY: + if ($hasDiscardedFraction) { + throw RoundingNecessaryException::roundingNecessary(); + } + break; + + case RoundingMode::UP: + $increment = $hasDiscardedFraction; + break; + + case RoundingMode::DOWN: + break; + + case RoundingMode::CEILING: + $increment = $hasDiscardedFraction && $isPositiveOrZero; + break; + + case RoundingMode::FLOOR: + $increment = $hasDiscardedFraction && ! $isPositiveOrZero; + break; + + case RoundingMode::HALF_UP: + $increment = $discardedFractionSign() >= 0; + break; + + case RoundingMode::HALF_DOWN: + $increment = $discardedFractionSign() > 0; + break; + + case RoundingMode::HALF_CEILING: + $increment = $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0; + break; + + case RoundingMode::HALF_FLOOR: + $increment = $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0; + break; + + case RoundingMode::HALF_EVEN: + $lastDigit = (int) $quotient[-1]; + $lastDigitIsEven = ($lastDigit % 2 === 0); + $increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0; + break; + + default: + throw new \InvalidArgumentException('Invalid rounding mode.'); + } + + if ($increment) { + return $this->add($quotient, $isPositiveOrZero ? '1' : '-1'); + } + + return $quotient; + } + + /** + * Calculates bitwise AND of two numbers. + * + * This method can be overridden by the concrete implementation if the underlying library + * has built-in support for bitwise operations. + */ + public function and(string $a, string $b) : string + { + return $this->bitwise('and', $a, $b); + } + + /** + * Calculates bitwise OR of two numbers. + * + * This method can be overridden by the concrete implementation if the underlying library + * has built-in support for bitwise operations. + */ + public function or(string $a, string $b) : string + { + return $this->bitwise('or', $a, $b); + } + + /** + * Calculates bitwise XOR of two numbers. + * + * This method can be overridden by the concrete implementation if the underlying library + * has built-in support for bitwise operations. + */ + public function xor(string $a, string $b) : string + { + return $this->bitwise('xor', $a, $b); + } + + /** + * Performs a bitwise operation on a decimal number. + * + * @param 'and'|'or'|'xor' $operator The operator to use. + * @param string $a The left operand. + * @param string $b The right operand. + */ + private function bitwise(string $operator, string $a, string $b) : string + { + [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); + + $aBin = $this->toBinary($aDig); + $bBin = $this->toBinary($bDig); + + $aLen = \strlen($aBin); + $bLen = \strlen($bBin); + + if ($aLen > $bLen) { + $bBin = \str_repeat("\x00", $aLen - $bLen) . $bBin; + } elseif ($bLen > $aLen) { + $aBin = \str_repeat("\x00", $bLen - $aLen) . $aBin; + } + + if ($aNeg) { + $aBin = $this->twosComplement($aBin); + } + if ($bNeg) { + $bBin = $this->twosComplement($bBin); + } + + switch ($operator) { + case 'and': + $value = $aBin & $bBin; + $negative = ($aNeg and $bNeg); + break; + + case 'or': + $value = $aBin | $bBin; + $negative = ($aNeg or $bNeg); + break; + + case 'xor': + $value = $aBin ^ $bBin; + $negative = ($aNeg xor $bNeg); + break; + + // @codeCoverageIgnoreStart + default: + throw new \InvalidArgumentException('Invalid bitwise operator.'); + // @codeCoverageIgnoreEnd + } + + if ($negative) { + $value = $this->twosComplement($value); + } + + $result = $this->toDecimal($value); + + return $negative ? $this->neg($result) : $result; + } + + /** + * @param string $number A positive, binary number. + */ + private function twosComplement(string $number) : string + { + $xor = \str_repeat("\xff", \strlen($number)); + + $number ^= $xor; + + for ($i = \strlen($number) - 1; $i >= 0; $i--) { + $byte = \ord($number[$i]); + + if (++$byte !== 256) { + $number[$i] = \chr($byte); + break; + } + + $number[$i] = "\x00"; + + if ($i === 0) { + $number = "\x01" . $number; + } + } + + return $number; + } + + /** + * Converts a decimal number to a binary string. + * + * @param string $number The number to convert, positive or zero, only digits. + */ + private function toBinary(string $number) : string + { + $result = ''; + + while ($number !== '0') { + [$number, $remainder] = $this->divQR($number, '256'); + $result .= \chr((int) $remainder); + } + + return \strrev($result); + } + + /** + * Returns the positive decimal representation of a binary number. + * + * @param string $bytes The bytes representing the number. + */ + private function toDecimal(string $bytes) : string + { + $result = '0'; + $power = '1'; + + for ($i = \strlen($bytes) - 1; $i >= 0; $i--) { + $index = \ord($bytes[$i]); + + if ($index !== 0) { + $result = $this->add($result, ($index === 1) + ? $power + : $this->mul($power, (string) $index) + ); + } + + if ($i !== 0) { + $power = $this->mul($power, '256'); + } + } + + return $result; + } +} diff --git a/vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php b/vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php new file mode 100644 index 0000000..5457a3c --- /dev/null +++ b/vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php @@ -0,0 +1,75 @@ +maxDigits = 9; + break; + + case 8: + $this->maxDigits = 18; + break; + + default: + throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.'); + } + } + + public function add(string $a, string $b) : string + { + /** + * @psalm-var numeric-string $a + * @psalm-var numeric-string $b + */ + $result = $a + $b; + + if (is_int($result)) { + return (string) $result; + } + + if ($a === '0') { + return $b; + } + + if ($b === '0') { + return $a; + } + + [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); + + $result = $aNeg === $bNeg ? $this->doAdd($aDig, $bDig) : $this->doSub($aDig, $bDig); + + if ($aNeg) { + $result = $this->neg($result); + } + + return $result; + } + + public function sub(string $a, string $b) : string + { + return $this->add($a, $this->neg($b)); + } + + public function mul(string $a, string $b) : string + { + /** + * @psalm-var numeric-string $a + * @psalm-var numeric-string $b + */ + $result = $a * $b; + + if (is_int($result)) { + return (string) $result; + } + + if ($a === '0' || $b === '0') { + return '0'; + } + + if ($a === '1') { + return $b; + } + + if ($b === '1') { + return $a; + } + + if ($a === '-1') { + return $this->neg($b); + } + + if ($b === '-1') { + return $this->neg($a); + } + + [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); + + $result = $this->doMul($aDig, $bDig); + + if ($aNeg !== $bNeg) { + $result = $this->neg($result); + } + + return $result; + } + + public function divQ(string $a, string $b) : string + { + return $this->divQR($a, $b)[0]; + } + + public function divR(string $a, string $b): string + { + return $this->divQR($a, $b)[1]; + } + + public function divQR(string $a, string $b) : array + { + if ($a === '0') { + return ['0', '0']; + } + + if ($a === $b) { + return ['1', '0']; + } + + if ($b === '1') { + return [$a, '0']; + } + + if ($b === '-1') { + return [$this->neg($a), '0']; + } + + /** @psalm-var numeric-string $a */ + $na = $a * 1; // cast to number + + if (is_int($na)) { + /** @psalm-var numeric-string $b */ + $nb = $b * 1; + + if (is_int($nb)) { + // the only division that may overflow is PHP_INT_MIN / -1, + // which cannot happen here as we've already handled a divisor of -1 above. + $r = $na % $nb; + $q = ($na - $r) / $nb; + + assert(is_int($q)); + + return [ + (string) $q, + (string) $r + ]; + } + } + + [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); + + [$q, $r] = $this->doDiv($aDig, $bDig); + + if ($aNeg !== $bNeg) { + $q = $this->neg($q); + } + + if ($aNeg) { + $r = $this->neg($r); + } + + return [$q, $r]; + } + + public function pow(string $a, int $e) : string + { + if ($e === 0) { + return '1'; + } + + if ($e === 1) { + return $a; + } + + $odd = $e % 2; + $e -= $odd; + + $aa = $this->mul($a, $a); + + /** @psalm-suppress PossiblyInvalidArgument We're sure that $e / 2 is an int now */ + $result = $this->pow($aa, $e / 2); + + if ($odd === 1) { + $result = $this->mul($result, $a); + } + + return $result; + } + + /** + * Algorithm from: https://www.geeksforgeeks.org/modular-exponentiation-power-in-modular-arithmetic/ + */ + public function modPow(string $base, string $exp, string $mod) : string + { + // special case: the algorithm below fails with 0 power 0 mod 1 (returns 1 instead of 0) + if ($base === '0' && $exp === '0' && $mod === '1') { + return '0'; + } + + // special case: the algorithm below fails with power 0 mod 1 (returns 1 instead of 0) + if ($exp === '0' && $mod === '1') { + return '0'; + } + + $x = $base; + + $res = '1'; + + // numbers are positive, so we can use remainder instead of modulo + $x = $this->divR($x, $mod); + + while ($exp !== '0') { + if (in_array($exp[-1], ['1', '3', '5', '7', '9'])) { // odd + $res = $this->divR($this->mul($res, $x), $mod); + } + + $exp = $this->divQ($exp, '2'); + $x = $this->divR($this->mul($x, $x), $mod); + } + + return $res; + } + + /** + * Adapted from https://cp-algorithms.com/num_methods/roots_newton.html + */ + public function sqrt(string $n) : string + { + if ($n === '0') { + return '0'; + } + + // initial approximation + $x = \str_repeat('9', \intdiv(\strlen($n), 2) ?: 1); + + $decreased = false; + + for (;;) { + $nx = $this->divQ($this->add($x, $this->divQ($n, $x)), '2'); + + if ($x === $nx || $this->cmp($nx, $x) > 0 && $decreased) { + break; + } + + $decreased = $this->cmp($nx, $x) < 0; + $x = $nx; + } + + return $x; + } + + /** + * Performs the addition of two non-signed large integers. + */ + private function doAdd(string $a, string $b) : string + { + [$a, $b, $length] = $this->pad($a, $b); + + $carry = 0; + $result = ''; + + for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) { + $blockLength = $this->maxDigits; + + if ($i < 0) { + $blockLength += $i; + /** @psalm-suppress LoopInvalidation */ + $i = 0; + } + + /** @psalm-var numeric-string $blockA */ + $blockA = \substr($a, $i, $blockLength); + + /** @psalm-var numeric-string $blockB */ + $blockB = \substr($b, $i, $blockLength); + + $sum = (string) ($blockA + $blockB + $carry); + $sumLength = \strlen($sum); + + if ($sumLength > $blockLength) { + $sum = \substr($sum, 1); + $carry = 1; + } else { + if ($sumLength < $blockLength) { + $sum = \str_repeat('0', $blockLength - $sumLength) . $sum; + } + $carry = 0; + } + + $result = $sum . $result; + + if ($i === 0) { + break; + } + } + + if ($carry === 1) { + $result = '1' . $result; + } + + return $result; + } + + /** + * Performs the subtraction of two non-signed large integers. + */ + private function doSub(string $a, string $b) : string + { + if ($a === $b) { + return '0'; + } + + // Ensure that we always subtract to a positive result: biggest minus smallest. + $cmp = $this->doCmp($a, $b); + + $invert = ($cmp === -1); + + if ($invert) { + $c = $a; + $a = $b; + $b = $c; + } + + [$a, $b, $length] = $this->pad($a, $b); + + $carry = 0; + $result = ''; + + $complement = 10 ** $this->maxDigits; + + for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) { + $blockLength = $this->maxDigits; + + if ($i < 0) { + $blockLength += $i; + /** @psalm-suppress LoopInvalidation */ + $i = 0; + } + + /** @psalm-var numeric-string $blockA */ + $blockA = \substr($a, $i, $blockLength); + + /** @psalm-var numeric-string $blockB */ + $blockB = \substr($b, $i, $blockLength); + + $sum = $blockA - $blockB - $carry; + + if ($sum < 0) { + $sum += $complement; + $carry = 1; + } else { + $carry = 0; + } + + $sum = (string) $sum; + $sumLength = \strlen($sum); + + if ($sumLength < $blockLength) { + $sum = \str_repeat('0', $blockLength - $sumLength) . $sum; + } + + $result = $sum . $result; + + if ($i === 0) { + break; + } + } + + // Carry cannot be 1 when the loop ends, as a > b + assert($carry === 0); + + $result = \ltrim($result, '0'); + + if ($invert) { + $result = $this->neg($result); + } + + return $result; + } + + /** + * Performs the multiplication of two non-signed large integers. + */ + private function doMul(string $a, string $b) : string + { + $x = \strlen($a); + $y = \strlen($b); + + $maxDigits = \intdiv($this->maxDigits, 2); + $complement = 10 ** $maxDigits; + + $result = '0'; + + for ($i = $x - $maxDigits;; $i -= $maxDigits) { + $blockALength = $maxDigits; + + if ($i < 0) { + $blockALength += $i; + /** @psalm-suppress LoopInvalidation */ + $i = 0; + } + + $blockA = (int) \substr($a, $i, $blockALength); + + $line = ''; + $carry = 0; + + for ($j = $y - $maxDigits;; $j -= $maxDigits) { + $blockBLength = $maxDigits; + + if ($j < 0) { + $blockBLength += $j; + /** @psalm-suppress LoopInvalidation */ + $j = 0; + } + + $blockB = (int) \substr($b, $j, $blockBLength); + + $mul = $blockA * $blockB + $carry; + $value = $mul % $complement; + $carry = ($mul - $value) / $complement; + + $value = (string) $value; + $value = \str_pad($value, $maxDigits, '0', STR_PAD_LEFT); + + $line = $value . $line; + + if ($j === 0) { + break; + } + } + + if ($carry !== 0) { + $line = $carry . $line; + } + + $line = \ltrim($line, '0'); + + if ($line !== '') { + $line .= \str_repeat('0', $x - $blockALength - $i); + $result = $this->add($result, $line); + } + + if ($i === 0) { + break; + } + } + + return $result; + } + + /** + * Performs the division of two non-signed large integers. + * + * @return string[] The quotient and remainder. + */ + private function doDiv(string $a, string $b) : array + { + $cmp = $this->doCmp($a, $b); + + if ($cmp === -1) { + return ['0', $a]; + } + + $x = \strlen($a); + $y = \strlen($b); + + // we now know that a >= b && x >= y + + $q = '0'; // quotient + $r = $a; // remainder + $z = $y; // focus length, always $y or $y+1 + + for (;;) { + $focus = \substr($a, 0, $z); + + $cmp = $this->doCmp($focus, $b); + + if ($cmp === -1) { + if ($z === $x) { // remainder < dividend + break; + } + + $z++; + } + + $zeros = \str_repeat('0', $x - $z); + + $q = $this->add($q, '1' . $zeros); + $a = $this->sub($a, $b . $zeros); + + $r = $a; + + if ($r === '0') { // remainder == 0 + break; + } + + $x = \strlen($a); + + if ($x < $y) { // remainder < dividend + break; + } + + $z = $y; + } + + return [$q, $r]; + } + + /** + * Compares two non-signed large numbers. + * + * @return int [-1, 0, 1] + */ + private function doCmp(string $a, string $b) : int + { + $x = \strlen($a); + $y = \strlen($b); + + $cmp = $x <=> $y; + + if ($cmp !== 0) { + return $cmp; + } + + return \strcmp($a, $b) <=> 0; // enforce [-1, 0, 1] + } + + /** + * Pads the left of one of the given numbers with zeros if necessary to make both numbers the same length. + * + * The numbers must only consist of digits, without leading minus sign. + * + * @return array{string, string, int} + */ + private function pad(string $a, string $b) : array + { + $x = \strlen($a); + $y = \strlen($b); + + if ($x > $y) { + $b = \str_repeat('0', $x - $y) . $b; + + return [$a, $b, $x]; + } + + if ($x < $y) { + $a = \str_repeat('0', $y - $x) . $a; + + return [$a, $b, $y]; + } + + return [$a, $b, $x]; + } +} diff --git a/vendor/brick/math/src/RoundingMode.php b/vendor/brick/math/src/RoundingMode.php new file mode 100644 index 0000000..06936d8 --- /dev/null +++ b/vendor/brick/math/src/RoundingMode.php @@ -0,0 +1,107 @@ += 0.5; otherwise, behaves as for DOWN. + * Note that this is the rounding mode commonly taught at school. + */ + public const HALF_UP = 5; + + /** + * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round down. + * + * Behaves as for UP if the discarded fraction is > 0.5; otherwise, behaves as for DOWN. + */ + public const HALF_DOWN = 6; + + /** + * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards positive infinity. + * + * If the result is positive, behaves as for HALF_UP; if negative, behaves as for HALF_DOWN. + */ + public const HALF_CEILING = 7; + + /** + * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards negative infinity. + * + * If the result is positive, behaves as for HALF_DOWN; if negative, behaves as for HALF_UP. + */ + public const HALF_FLOOR = 8; + + /** + * Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor. + * + * Behaves as for HALF_UP if the digit to the left of the discarded fraction is odd; + * behaves as for HALF_DOWN if it's even. + * + * Note that this is the rounding mode that statistically minimizes + * cumulative error when applied repeatedly over a sequence of calculations. + * It is sometimes known as "Banker's rounding", and is chiefly used in the USA. + */ + public const HALF_EVEN = 9; +} diff --git a/vendor/cakephp/core/App.php b/vendor/cakephp/core/App.php new file mode 100644 index 0000000..3f5c9a0 --- /dev/null +++ b/vendor/cakephp/core/App.php @@ -0,0 +1,269 @@ + + * @link https://book.cakephp.org/4/en/core-libraries/app.html#finding-paths-to-namespaces + */ + public static function path(string $type, ?string $plugin = null): array + { + if ($plugin === null && $type[0] === strtolower($type[0])) { + return (array)Configure::read('App.paths.' . $type); + } + + if ($type === 'templates') { + /** @psalm-suppress PossiblyNullArgument */ + return [Plugin::templatePath($plugin)]; + } + + if ($type === 'locales') { + /** @psalm-suppress PossiblyNullArgument */ + return [Plugin::path($plugin) . 'resources' . DIRECTORY_SEPARATOR . 'locales' . DIRECTORY_SEPARATOR]; + } + + deprecationWarning( + 'App::path() is deprecated for class path.' + . ' Use \Cake\Core\App::classPath() or \Cake\Core\Plugin::classPath() instead.' + ); + + return static::classPath($type, $plugin); + } + + /** + * Gets the path to a class type in the application or a plugin. + * + * Example: + * + * ``` + * App::classPath('Model/Table'); + * ``` + * + * Will return the path for tables - e.g. `src/Model/Table/`. + * + * ``` + * App::classPath('Model/Table', 'My/Plugin'); + * ``` + * + * Will return the plugin based path for those. + * + * @param string $type Package type. + * @param string|null $plugin Plugin name. + * @return array + */ + public static function classPath(string $type, ?string $plugin = null): array + { + if ($plugin !== null) { + return [ + Plugin::classPath($plugin) . $type . DIRECTORY_SEPARATOR, + ]; + } + + return [APP . $type . DIRECTORY_SEPARATOR]; + } + + /** + * Returns the full path to a package inside the CakePHP core + * + * Usage: + * + * ``` + * App::core('Cache/Engine'); + * ``` + * + * Will return the full path to the cache engines package. + * + * @param string $type Package type. + * @return array Full path to package + */ + public static function core(string $type): array + { + if ($type === 'templates') { + return [CORE_PATH . 'templates' . DIRECTORY_SEPARATOR]; + } + + return [CAKE . str_replace('/', DIRECTORY_SEPARATOR, $type) . DIRECTORY_SEPARATOR]; + } +} diff --git a/vendor/cakephp/core/BasePlugin.php b/vendor/cakephp/core/BasePlugin.php new file mode 100644 index 0000000..a748be3 --- /dev/null +++ b/vendor/cakephp/core/BasePlugin.php @@ -0,0 +1,305 @@ + $options Options + */ + public function __construct(array $options = []) + { + foreach (static::VALID_HOOKS as $key) { + if (isset($options[$key])) { + $this->{"{$key}Enabled"} = (bool)$options[$key]; + } + } + foreach (['name', 'path', 'classPath', 'configPath', 'templatePath'] as $path) { + if (isset($options[$path])) { + $this->{$path} = $options[$path]; + } + } + + $this->initialize(); + } + + /** + * Initialization hook called from constructor. + * + * @return void + */ + public function initialize(): void + { + } + + /** + * @inheritDoc + */ + public function getName(): string + { + if ($this->name) { + return $this->name; + } + $parts = explode('\\', static::class); + array_pop($parts); + $this->name = implode('/', $parts); + + return $this->name; + } + + /** + * @inheritDoc + */ + public function getPath(): string + { + if ($this->path) { + return $this->path; + } + $reflection = new ReflectionClass($this); + $path = dirname($reflection->getFileName()); + + // Trim off src + if (substr($path, -3) === 'src') { + $path = substr($path, 0, -3); + } + $this->path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + + return $this->path; + } + + /** + * @inheritDoc + */ + public function getConfigPath(): string + { + if ($this->configPath) { + return $this->configPath; + } + $path = $this->getPath(); + + return $path . 'config' . DIRECTORY_SEPARATOR; + } + + /** + * @inheritDoc + */ + public function getClassPath(): string + { + if ($this->classPath) { + return $this->classPath; + } + $path = $this->getPath(); + + return $path . 'src' . DIRECTORY_SEPARATOR; + } + + /** + * @inheritDoc + */ + public function getTemplatePath(): string + { + if ($this->templatePath) { + return $this->templatePath; + } + $path = $this->getPath(); + + return $this->templatePath = $path . 'templates' . DIRECTORY_SEPARATOR; + } + + /** + * @inheritDoc + */ + public function enable(string $hook) + { + $this->checkHook($hook); + $this->{"{$hook}Enabled"} = true; + + return $this; + } + + /** + * @inheritDoc + */ + public function disable(string $hook) + { + $this->checkHook($hook); + $this->{"{$hook}Enabled"} = false; + + return $this; + } + + /** + * @inheritDoc + */ + public function isEnabled(string $hook): bool + { + $this->checkHook($hook); + + return $this->{"{$hook}Enabled"} === true; + } + + /** + * Check if a hook name is valid + * + * @param string $hook The hook name to check + * @throws \InvalidArgumentException on invalid hooks + * @return void + */ + protected function checkHook(string $hook): void + { + if (!in_array($hook, static::VALID_HOOKS, true)) { + throw new InvalidArgumentException( + "`$hook` is not a valid hook name. Must be one of " . implode(', ', static::VALID_HOOKS) + ); + } + } + + /** + * @inheritDoc + */ + public function routes(RouteBuilder $routes): void + { + $path = $this->getConfigPath() . 'routes.php'; + if (is_file($path)) { + $return = require $path; + if ($return instanceof Closure) { + $return($routes); + } + } + } + + /** + * @inheritDoc + */ + public function bootstrap(PluginApplicationInterface $app): void + { + $bootstrap = $this->getConfigPath() . 'bootstrap.php'; + if (is_file($bootstrap)) { + require $bootstrap; + } + } + + /** + * @inheritDoc + */ + public function console(CommandCollection $commands): CommandCollection + { + return $commands->addMany($commands->discoverPlugin($this->getName())); + } + + /** + * @inheritDoc + */ + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + return $middlewareQueue; + } + + /** + * Register container services for this plugin. + * + * @param \Cake\Core\ContainerInterface $container The container to add services to. + * @return void + */ + public function services(ContainerInterface $container): void + { + } +} diff --git a/vendor/cakephp/core/ClassLoader.php b/vendor/cakephp/core/ClassLoader.php new file mode 100644 index 0000000..e2a7525 --- /dev/null +++ b/vendor/cakephp/core/ClassLoader.php @@ -0,0 +1,139 @@ + + */ + protected $_prefixes = []; + + /** + * Register loader with SPL autoloader stack. + * + * @return void + */ + public function register(): void + { + /** @var callable $callable */ + $callable = [$this, 'loadClass']; + spl_autoload_register($callable); + } + + /** + * Adds a base directory for a namespace prefix. + * + * @param string $prefix The namespace prefix. + * @param string $baseDir A base directory for class files in the + * namespace. + * @param bool $prepend If true, prepend the base directory to the stack + * instead of appending it; this causes it to be searched first rather + * than last. + * @return void + */ + public function addNamespace(string $prefix, string $baseDir, bool $prepend = false): void + { + $prefix = trim($prefix, '\\') . '\\'; + + $baseDir = rtrim($baseDir, '/') . DIRECTORY_SEPARATOR; + $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/'; + + $this->_prefixes[$prefix] = $this->_prefixes[$prefix] ?? []; + + if ($prepend) { + array_unshift($this->_prefixes[$prefix], $baseDir); + } else { + $this->_prefixes[$prefix][] = $baseDir; + } + } + + /** + * Loads the class file for a given class name. + * + * @param string $class The fully-qualified class name. + * @return string|false The mapped file name on success, or boolean false on + * failure. + */ + public function loadClass(string $class) + { + $prefix = $class; + + while (($pos = strrpos($prefix, '\\')) !== false) { + $prefix = substr($class, 0, $pos + 1); + $relativeClass = substr($class, $pos + 1); + + $mappedFile = $this->_loadMappedFile($prefix, $relativeClass); + if ($mappedFile) { + return $mappedFile; + } + + $prefix = rtrim($prefix, '\\'); + } + + return false; + } + + /** + * Load the mapped file for a namespace prefix and relative class. + * + * @param string $prefix The namespace prefix. + * @param string $relativeClass The relative class name. + * @return string|false Boolean false if no mapped file can be loaded, or the + * name of the mapped file that was loaded. + */ + protected function _loadMappedFile(string $prefix, string $relativeClass) + { + if (!isset($this->_prefixes[$prefix])) { + return false; + } + + foreach ($this->_prefixes[$prefix] as $baseDir) { + $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php'; + + if ($this->_requireFile($file)) { + return $file; + } + } + + return false; + } + + /** + * If a file exists, require it from the file system. + * + * @param string $file The file to require. + * @return bool True if the file exists, false if not. + */ + protected function _requireFile(string $file): bool + { + if (file_exists($file)) { + require $file; + + return true; + } + + return false; + } +} diff --git a/vendor/cakephp/core/Configure.php b/vendor/cakephp/core/Configure.php new file mode 100644 index 0000000..99e8259 --- /dev/null +++ b/vendor/cakephp/core/Configure.php @@ -0,0 +1,498 @@ + + */ + protected static $_values = [ + 'debug' => false, + ]; + + /** + * Configured engine classes, used to load config files from resources + * + * @see \Cake\Core\Configure::load() + * @var array<\Cake\Core\Configure\ConfigEngineInterface> + */ + protected static $_engines = []; + + /** + * Flag to track whether ini_set exists. + * + * @var bool|null + */ + protected static $_hasIniSet; + + /** + * Used to store a dynamic variable in Configure. + * + * Usage: + * ``` + * Configure::write('One.key1', 'value of the Configure::One[key1]'); + * Configure::write(['One.key1' => 'value of the Configure::One[key1]']); + * Configure::write('One', [ + * 'key1' => 'value of the Configure::One[key1]', + * 'key2' => 'value of the Configure::One[key2]' + * ]); + * + * Configure::write([ + * 'One.key1' => 'value of the Configure::One[key1]', + * 'One.key2' => 'value of the Configure::One[key2]' + * ]); + * ``` + * + * @param array|string $config The key to write, can be a dot notation value. + * Alternatively can be an array containing key(s) and value(s). + * @param mixed $value Value to set for the given key. + * @return void + * @link https://book.cakephp.org/4/en/development/configuration.html#writing-configuration-data + */ + public static function write($config, $value = null): void + { + if (!is_array($config)) { + $config = [$config => $value]; + } + + foreach ($config as $name => $valueToInsert) { + static::$_values = Hash::insert(static::$_values, $name, $valueToInsert); + } + + if (isset($config['debug'])) { + if (static::$_hasIniSet === null) { + static::$_hasIniSet = function_exists('ini_set'); + } + if (static::$_hasIniSet) { + ini_set('display_errors', $config['debug'] ? '1' : '0'); + } + } + } + + /** + * Used to read information stored in Configure. It's not + * possible to store `null` values in Configure. + * + * Usage: + * ``` + * Configure::read('Name'); will return all values for Name + * Configure::read('Name.key'); will return only the value of Configure::Name[key] + * ``` + * + * @param string|null $var Variable to obtain. Use '.' to access array elements. + * @param mixed $default The return value when the configure does not exist + * @return mixed Value stored in configure, or null. + * @link https://book.cakephp.org/4/en/development/configuration.html#reading-configuration-data + */ + public static function read(?string $var = null, $default = null) + { + if ($var === null) { + return static::$_values; + } + + return Hash::get(static::$_values, $var, $default); + } + + /** + * Returns true if given variable is set in Configure. + * + * @param string $var Variable name to check for + * @return bool True if variable is there + */ + public static function check(string $var): bool + { + if (empty($var)) { + return false; + } + + return static::read($var) !== null; + } + + /** + * Used to get information stored in Configure. It's not + * possible to store `null` values in Configure. + * + * Acts as a wrapper around Configure::read() and Configure::check(). + * The configure key/value pair fetched via this method is expected to exist. + * In case it does not an exception will be thrown. + * + * Usage: + * ``` + * Configure::readOrFail('Name'); will return all values for Name + * Configure::readOrFail('Name.key'); will return only the value of Configure::Name[key] + * ``` + * + * @param string $var Variable to obtain. Use '.' to access array elements. + * @return mixed Value stored in configure. + * @throws \RuntimeException if the requested configuration is not set. + * @link https://book.cakephp.org/4/en/development/configuration.html#reading-configuration-data + */ + public static function readOrFail(string $var) + { + if (!static::check($var)) { + throw new RuntimeException(sprintf('Expected configuration key "%s" not found.', $var)); + } + + return static::read($var); + } + + /** + * Used to delete a variable from Configure. + * + * Usage: + * ``` + * Configure::delete('Name'); will delete the entire Configure::Name + * Configure::delete('Name.key'); will delete only the Configure::Name[key] + * ``` + * + * @param string $var the var to be deleted + * @return void + * @link https://book.cakephp.org/4/en/development/configuration.html#deleting-configuration-data + */ + public static function delete(string $var): void + { + static::$_values = Hash::remove(static::$_values, $var); + } + + /** + * Used to consume information stored in Configure. It's not + * possible to store `null` values in Configure. + * + * Acts as a wrapper around Configure::consume() and Configure::check(). + * The configure key/value pair consumed via this method is expected to exist. + * In case it does not an exception will be thrown. + * + * @param string $var Variable to consume. Use '.' to access array elements. + * @return mixed Value stored in configure. + * @throws \RuntimeException if the requested configuration is not set. + * @since 3.6.0 + */ + public static function consumeOrFail(string $var) + { + if (!static::check($var)) { + throw new RuntimeException(sprintf('Expected configuration key "%s" not found.', $var)); + } + + return static::consume($var); + } + + /** + * Used to read and delete a variable from Configure. + * + * This is primarily used during bootstrapping to move configuration data + * out of configure into the various other classes in CakePHP. + * + * @param string $var The key to read and remove. + * @return array|string|null + */ + public static function consume(string $var) + { + if (strpos($var, '.') === false) { + if (!isset(static::$_values[$var])) { + return null; + } + $value = static::$_values[$var]; + unset(static::$_values[$var]); + + return $value; + } + $value = Hash::get(static::$_values, $var); + static::delete($var); + + return $value; + } + + /** + * Add a new engine to Configure. Engines allow you to read configuration + * files in various formats/storage locations. CakePHP comes with two built-in engines + * PhpConfig and IniConfig. You can also implement your own engine classes in your application. + * + * To add a new engine to Configure: + * + * ``` + * Configure::config('ini', new IniConfig()); + * ``` + * + * @param string $name The name of the engine being configured. This alias is used later to + * read values from a specific engine. + * @param \Cake\Core\Configure\ConfigEngineInterface $engine The engine to append. + * @return void + */ + public static function config(string $name, ConfigEngineInterface $engine): void + { + static::$_engines[$name] = $engine; + } + + /** + * Returns true if the Engine objects is configured. + * + * @param string $name Engine name. + * @return bool + */ + public static function isConfigured(string $name): bool + { + return isset(static::$_engines[$name]); + } + + /** + * Gets the names of the configured Engine objects. + * + * @return array + */ + public static function configured(): array + { + $engines = array_keys(static::$_engines); + + return array_map(function ($key) { + return (string)$key; + }, $engines); + } + + /** + * Remove a configured engine. This will unset the engine + * and make any future attempts to use it cause an Exception. + * + * @param string $name Name of the engine to drop. + * @return bool Success + */ + public static function drop(string $name): bool + { + if (!isset(static::$_engines[$name])) { + return false; + } + unset(static::$_engines[$name]); + + return true; + } + + /** + * Loads stored configuration information from a resource. You can add + * config file resource engines with `Configure::config()`. + * + * Loaded configuration information will be merged with the current + * runtime configuration. You can load configuration files from plugins + * by preceding the filename with the plugin name. + * + * `Configure::load('Users.user', 'default')` + * + * Would load the 'user' config file using the default config engine. You can load + * app config files by giving the name of the resource you want loaded. + * + * ``` + * Configure::load('setup', 'default'); + * ``` + * + * If using `default` config and no engine has been configured for it yet, + * one will be automatically created using PhpConfig + * + * @param string $key name of configuration resource to load. + * @param string $config Name of the configured engine to use to read the resource identified by $key. + * @param bool $merge if config files should be merged instead of simply overridden + * @return bool True if load successful. + * @throws \Cake\Core\Exception\CakeException if the $config engine is not found + * @link https://book.cakephp.org/4/en/development/configuration.html#reading-and-writing-configuration-files + */ + public static function load(string $key, string $config = 'default', bool $merge = true): bool + { + $engine = static::_getEngine($config); + if (!$engine) { + throw new CakeException( + sprintf( + 'Config %s engine not found when attempting to load %s.', + $config, + $key + ) + ); + } + + $values = $engine->read($key); + + if ($merge) { + $values = Hash::merge(static::$_values, $values); + } + + static::write($values); + + return true; + } + + /** + * Dump data currently in Configure into $key. The serialization format + * is decided by the config engine attached as $config. For example, if the + * 'default' adapter is a PhpConfig, the generated file will be a PHP + * configuration file loadable by the PhpConfig. + * + * ### Usage + * + * Given that the 'default' engine is an instance of PhpConfig. + * Save all data in Configure to the file `my_config.php`: + * + * ``` + * Configure::dump('my_config', 'default'); + * ``` + * + * Save only the error handling configuration: + * + * ``` + * Configure::dump('error', 'default', ['Error', 'Exception']; + * ``` + * + * @param string $key The identifier to create in the config adapter. + * This could be a filename or a cache key depending on the adapter being used. + * @param string $config The name of the configured adapter to dump data with. + * @param array $keys The name of the top-level keys you want to dump. + * This allows you save only some data stored in Configure. + * @return bool Success + * @throws \Cake\Core\Exception\CakeException if the adapter does not implement a `dump` method. + */ + public static function dump(string $key, string $config = 'default', array $keys = []): bool + { + $engine = static::_getEngine($config); + if (!$engine) { + throw new CakeException(sprintf('There is no "%s" config engine.', $config)); + } + $values = static::$_values; + if (!empty($keys)) { + $values = array_intersect_key($values, array_flip($keys)); + } + + return $engine->dump($key, $values); + } + + /** + * Get the configured engine. Internally used by `Configure::load()` and `Configure::dump()` + * Will create new PhpConfig for default if not configured yet. + * + * @param string $config The name of the configured adapter + * @return \Cake\Core\Configure\ConfigEngineInterface|null Engine instance or null + */ + protected static function _getEngine(string $config): ?ConfigEngineInterface + { + if (!isset(static::$_engines[$config])) { + if ($config !== 'default') { + return null; + } + static::config($config, new PhpConfig()); + } + + return static::$_engines[$config]; + } + + /** + * Used to determine the current version of CakePHP. + * + * Usage + * ``` + * Configure::version(); + * ``` + * + * @return string Current version of CakePHP + */ + public static function version(): string + { + $version = static::read('Cake.version'); + if ($version !== null) { + return $version; + } + + $path = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'config/config.php'; + if (is_file($path)) { + $config = require $path; + static::write($config); + + return static::read('Cake.version'); + } + + return 'unknown'; + } + + /** + * Used to write runtime configuration into Cache. Stored runtime configuration can be + * restored using `Configure::restore()`. These methods can be used to enable configuration managers + * frontends, or other GUI type interfaces for configuration. + * + * @param string $name The storage name for the saved configuration. + * @param string $cacheConfig The cache configuration to save into. Defaults to 'default' + * @param array|null $data Either an array of data to store, or leave empty to store all values. + * @return bool Success + * @throws \RuntimeException + */ + public static function store(string $name, string $cacheConfig = 'default', ?array $data = null): bool + { + if ($data === null) { + $data = static::$_values; + } + if (!class_exists(Cache::class)) { + throw new RuntimeException('You must install cakephp/cache to use Configure::store()'); + } + + return Cache::write($name, $data, $cacheConfig); + } + + /** + * Restores configuration data stored in the Cache into configure. Restored + * values will overwrite existing ones. + * + * @param string $name Name of the stored config file to load. + * @param string $cacheConfig Name of the Cache configuration to read from. + * @return bool Success. + * @throws \RuntimeException + */ + public static function restore(string $name, string $cacheConfig = 'default'): bool + { + if (!class_exists(Cache::class)) { + throw new RuntimeException('You must install cakephp/cache to use Configure::restore()'); + } + $values = Cache::read($name, $cacheConfig); + if ($values) { + static::write($values); + + return true; + } + + return false; + } + + /** + * Clear all values stored in Configure. + * + * @return void + */ + public static function clear(): void + { + static::$_values = []; + } +} diff --git a/vendor/cakephp/core/Configure/ConfigEngineInterface.php b/vendor/cakephp/core/Configure/ConfigEngineInterface.php new file mode 100644 index 0000000..a8f59ad --- /dev/null +++ b/vendor/cakephp/core/Configure/ConfigEngineInterface.php @@ -0,0 +1,44 @@ + ['password' => 'secret']]` + * + * You can nest properties as deeply as needed using `.`'s. In addition to using `.` you + * can use standard ini section notation to create nested structures: + * + * ``` + * [section] + * key = value + * ``` + * + * Once loaded into Configure, the above would be accessed using: + * + * `Configure::read('section.key'); + * + * You can also use `.` separated values in section names to create more deeply + * nested structures. + * + * IniConfig also manipulates how the special ini values of + * 'yes', 'no', 'on', 'off', 'null' are handled. These values will be + * converted to their boolean equivalents. + * + * @see https://secure.php.net/parse_ini_file + */ +class IniConfig implements ConfigEngineInterface +{ + use FileConfigTrait; + + /** + * File extension. + * + * @var string + */ + protected $_extension = '.ini'; + + /** + * The section to read, if null all sections will be read. + * + * @var string|null + */ + protected $_section; + + /** + * Build and construct a new ini file parser. The parser can be used to read + * ini files that are on the filesystem. + * + * @param string|null $path Path to load ini config files from. Defaults to CONFIG. + * @param string|null $section Only get one section, leave null to parse and fetch + * all sections in the ini file. + */ + public function __construct(?string $path = null, ?string $section = null) + { + if ($path === null) { + $path = CONFIG; + } + $this->_path = $path; + $this->_section = $section; + } + + /** + * Read an ini file and return the results as an array. + * + * @param string $key The identifier to read from. If the key has a . it will be treated + * as a plugin prefix. The chosen file must be on the engine's path. + * @return array Parsed configuration values. + * @throws \Cake\Core\Exception\CakeException when files don't exist. + * Or when files contain '..' as this could lead to abusive reads. + */ + public function read(string $key): array + { + $file = $this->_getFilePath($key, true); + + $contents = parse_ini_file($file, true); + if ($this->_section && isset($contents[$this->_section])) { + $values = $this->_parseNestedValues($contents[$this->_section]); + } else { + $values = []; + foreach ($contents as $section => $attribs) { + if (is_array($attribs)) { + $values[$section] = $this->_parseNestedValues($attribs); + } else { + $parse = $this->_parseNestedValues([$attribs]); + $values[$section] = array_shift($parse); + } + } + } + + return $values; + } + + /** + * parses nested values out of keys. + * + * @param array $values Values to be exploded. + * @return array Array of values exploded + */ + protected function _parseNestedValues(array $values): array + { + foreach ($values as $key => $value) { + if ($value === '1') { + $value = true; + } + if ($value === '') { + $value = false; + } + unset($values[$key]); + if (strpos((string)$key, '.') !== false) { + $values = Hash::insert($values, $key, $value); + } else { + $values[$key] = $value; + } + } + + return $values; + } + + /** + * Dumps the state of Configure data into an ini formatted string. + * + * @param string $key The identifier to write to. If the key has a . it will be treated + * as a plugin prefix. + * @param array $data The data to convert to ini file. + * @return bool Success. + */ + public function dump(string $key, array $data): bool + { + $result = []; + foreach ($data as $k => $value) { + $isSection = false; + /** @psalm-suppress InvalidArrayAccess */ + if ($k[0] !== '[') { + $result[] = "[$k]"; + $isSection = true; + } + if (is_array($value)) { + $kValues = Hash::flatten($value, '.'); + foreach ($kValues as $k2 => $v) { + $result[] = "$k2 = " . $this->_value($v); + } + } + if ($isSection) { + $result[] = ''; + } + } + $contents = trim(implode("\n", $result)); + + $filename = $this->_getFilePath($key); + + return file_put_contents($filename, $contents) > 0; + } + + /** + * Converts a value into the ini equivalent + * + * @param mixed $value Value to export. + * @return string String value for ini file. + */ + protected function _value($value): string + { + if ($value === null) { + return 'null'; + } + if ($value === true) { + return 'true'; + } + if ($value === false) { + return 'false'; + } + + return (string)$value; + } +} diff --git a/vendor/cakephp/core/Configure/Engine/JsonConfig.php b/vendor/cakephp/core/Configure/Engine/JsonConfig.php new file mode 100644 index 0000000..66e8a90 --- /dev/null +++ b/vendor/cakephp/core/Configure/Engine/JsonConfig.php @@ -0,0 +1,115 @@ +_path = $path; + } + + /** + * Read a config file and return its contents. + * + * Files with `.` in the name will be treated as values in plugins. Instead of + * reading from the initialized path, plugin keys will be located using Plugin::path(). + * + * @param string $key The identifier to read from. If the key has a . it will be treated + * as a plugin prefix. + * @return array Parsed configuration values. + * @throws \Cake\Core\Exception\CakeException When files don't exist or when + * files contain '..' (as this could lead to abusive reads) or when there + * is an error parsing the JSON string. + */ + public function read(string $key): array + { + $file = $this->_getFilePath($key, true); + + $values = json_decode(file_get_contents($file), true); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new CakeException(sprintf( + 'Error parsing JSON string fetched from config file "%s.json": %s', + $key, + json_last_error_msg() + )); + } + if (!is_array($values)) { + throw new CakeException(sprintf( + 'Decoding JSON config file "%s.json" did not return an array', + $key + )); + } + + return $values; + } + + /** + * Converts the provided $data into a JSON string that can be used saved + * into a file and loaded later. + * + * @param string $key The identifier to write to. If the key has a . it will + * be treated as a plugin prefix. + * @param array $data Data to dump. + * @return bool Success + */ + public function dump(string $key, array $data): bool + { + $filename = $this->_getFilePath($key); + + return file_put_contents($filename, json_encode($data, JSON_PRETTY_PRINT)) > 0; + } +} diff --git a/vendor/cakephp/core/Configure/Engine/PhpConfig.php b/vendor/cakephp/core/Configure/Engine/PhpConfig.php new file mode 100644 index 0000000..6bdf3a4 --- /dev/null +++ b/vendor/cakephp/core/Configure/Engine/PhpConfig.php @@ -0,0 +1,114 @@ + false, + * 'Security' => [ + * 'salt' => 'its-secret' + * ], + * 'App' => [ + * 'namespace' => 'App' + * ] + * ]; + * ``` + * + * @see \Cake\Core\Configure::load() for how to load custom configuration files. + */ +class PhpConfig implements ConfigEngineInterface +{ + use FileConfigTrait; + + /** + * File extension. + * + * @var string + */ + protected $_extension = '.php'; + + /** + * Constructor for PHP Config file reading. + * + * @param string|null $path The path to read config files from. Defaults to CONFIG. + */ + public function __construct(?string $path = null) + { + if ($path === null) { + $path = CONFIG; + } + $this->_path = $path; + } + + /** + * Read a config file and return its contents. + * + * Files with `.` in the name will be treated as values in plugins. Instead of + * reading from the initialized path, plugin keys will be located using Plugin::path(). + * + * @param string $key The identifier to read from. If the key has a . it will be treated + * as a plugin prefix. + * @return array Parsed configuration values. + * @throws \Cake\Core\Exception\CakeException when files don't exist or they don't contain `$config`. + * Or when files contain '..' as this could lead to abusive reads. + */ + public function read(string $key): array + { + $file = $this->_getFilePath($key, true); + + $config = null; + + $return = include $file; + if (is_array($return)) { + return $return; + } + + throw new CakeException(sprintf('Config file "%s" did not return an array', $key . '.php')); + } + + /** + * Converts the provided $data into a string of PHP code that can + * be used saved into a file and loaded later. + * + * @param string $key The identifier to write to. If the key has a . it will be treated + * as a plugin prefix. + * @param array $data Data to dump. + * @return bool Success + */ + public function dump(string $key, array $data): bool + { + $contents = '_getFilePath($key); + + return file_put_contents($filename, $contents) > 0; + } +} diff --git a/vendor/cakephp/core/Configure/FileConfigTrait.php b/vendor/cakephp/core/Configure/FileConfigTrait.php new file mode 100644 index 0000000..79bcdea --- /dev/null +++ b/vendor/cakephp/core/Configure/FileConfigTrait.php @@ -0,0 +1,72 @@ +_path . $key; + } + + $file .= $this->_extension; + + if (!$checkExists || is_file($file)) { + return $file; + } + + $realPath = realpath($file); + if ($realPath !== false && is_file($realPath)) { + return $realPath; + } + + throw new CakeException(sprintf('Could not load configuration file: %s', $file)); + } +} diff --git a/vendor/cakephp/core/ConsoleApplicationInterface.php b/vendor/cakephp/core/ConsoleApplicationInterface.php new file mode 100644 index 0000000..b2a532a --- /dev/null +++ b/vendor/cakephp/core/ConsoleApplicationInterface.php @@ -0,0 +1,42 @@ +_attributes = $message; + $message = vsprintf($this->_messageTemplate, $message); + } + parent::__construct($message, $code ?? $this->_defaultCode, $previous); + } + + /** + * Get the passed in attributes + * + * @return array + */ + public function getAttributes(): array + { + return $this->_attributes; + } + + /** + * Get/set the response header to be used + * + * See also {@link \Cake\Http\Response::withHeader()} + * + * @param array|string|null $header A single header string or an associative + * array of "header name" => "header value" + * @param string|null $value The header value. + * @return array|null + * @deprecated 4.2.0 Use `HttpException::setHeaders()` instead. Response headers + * should be set for HttpException only. + */ + public function responseHeader($header = null, $value = null): ?array + { + if ($header === null) { + return $this->_responseHeaders; + } + + deprecationWarning( + 'Setting HTTP response headers from Exception directly is deprecated. ' . + 'If your exceptions extend Exception, they must now extend HttpException. ' . + 'You should only set HTTP headers on HttpException instances via the `setHeaders()` method.' + ); + if (is_array($header)) { + return $this->_responseHeaders = $header; + } + + return $this->_responseHeaders = [$header => $value]; + } +} + +// phpcs:disable +class_alias( + 'Cake\Core\Exception\CakeException', + 'Cake\Core\Exception\Exception' +); +// phpcs:enable diff --git a/vendor/cakephp/core/Exception/Exception.php b/vendor/cakephp/core/Exception/Exception.php new file mode 100644 index 0000000..bd93c50 --- /dev/null +++ b/vendor/cakephp/core/Exception/Exception.php @@ -0,0 +1,21 @@ + + */ + protected $_config = []; + + /** + * Whether the config property has already been configured with defaults + * + * @var bool + */ + protected $_configInitialized = false; + + /** + * Sets the config. + * + * ### Usage + * + * Setting a specific value: + * + * ``` + * $this->setConfig('key', $value); + * ``` + * + * Setting a nested value: + * + * ``` + * $this->setConfig('some.nested.key', $value); + * ``` + * + * Updating multiple config settings at the same time: + * + * ``` + * $this->setConfig(['one' => 'value', 'another' => 'value']); + * ``` + * + * @param array|string $key The key to set, or a complete array of configs. + * @param mixed|null $value The value to set. + * @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true. + * @return $this + * @throws \Cake\Core\Exception\CakeException When trying to set a key that is invalid. + */ + public function setConfig($key, $value = null, $merge = true) + { + if (!$this->_configInitialized) { + $this->_config = $this->_defaultConfig; + $this->_configInitialized = true; + } + + $this->_configWrite($key, $value, $merge); + + return $this; + } + + /** + * Returns the config. + * + * ### Usage + * + * Reading the whole config: + * + * ``` + * $this->getConfig(); + * ``` + * + * Reading a specific value: + * + * ``` + * $this->getConfig('key'); + * ``` + * + * Reading a nested value: + * + * ``` + * $this->getConfig('some.nested.key'); + * ``` + * + * Reading with default value: + * + * ``` + * $this->getConfig('some-key', 'default-value'); + * ``` + * + * @param string|null $key The key to get or null for the whole config. + * @param mixed $default The return value when the key does not exist. + * @return mixed Configuration data at the named key or null if the key does not exist. + */ + public function getConfig(?string $key = null, $default = null) + { + if (!$this->_configInitialized) { + $this->_config = $this->_defaultConfig; + $this->_configInitialized = true; + } + + $return = $this->_configRead($key); + + return $return ?? $default; + } + + /** + * Returns the config for this specific key. + * + * The config value for this key must exist, it can never be null. + * + * @param string $key The key to get. + * @return mixed Configuration data at the named key + * @throws \InvalidArgumentException + */ + public function getConfigOrFail(string $key) + { + $config = $this->getConfig($key); + if ($config === null) { + throw new InvalidArgumentException(sprintf('Expected configuration `%s` not found.', $key)); + } + + return $config; + } + + /** + * Merge provided config with existing config. Unlike `config()` which does + * a recursive merge for nested keys, this method does a simple merge. + * + * Setting a specific value: + * + * ``` + * $this->configShallow('key', $value); + * ``` + * + * Setting a nested value: + * + * ``` + * $this->configShallow('some.nested.key', $value); + * ``` + * + * Updating multiple config settings at the same time: + * + * ``` + * $this->configShallow(['one' => 'value', 'another' => 'value']); + * ``` + * + * @param array|string $key The key to set, or a complete array of configs. + * @param mixed|null $value The value to set. + * @return $this + */ + public function configShallow($key, $value = null) + { + if (!$this->_configInitialized) { + $this->_config = $this->_defaultConfig; + $this->_configInitialized = true; + } + + $this->_configWrite($key, $value, 'shallow'); + + return $this; + } + + /** + * Reads a config key. + * + * @param string|null $key Key to read. + * @return mixed + */ + protected function _configRead(?string $key) + { + if ($key === null) { + return $this->_config; + } + + if (strpos($key, '.') === false) { + return $this->_config[$key] ?? null; + } + + $return = $this->_config; + + foreach (explode('.', $key) as $k) { + if (!is_array($return) || !isset($return[$k])) { + $return = null; + break; + } + + $return = $return[$k]; + } + + return $return; + } + + /** + * Writes a config key. + * + * @param array|string $key Key to write to. + * @param mixed $value Value to write. + * @param string|bool $merge True to merge recursively, 'shallow' for simple merge, + * false to overwrite, defaults to false. + * @return void + * @throws \Cake\Core\Exception\CakeException if attempting to clobber existing config + */ + protected function _configWrite($key, $value, $merge = false): void + { + if (is_string($key) && $value === null) { + $this->_configDelete($key); + + return; + } + + if ($merge) { + $update = is_array($key) ? $key : [$key => $value]; + if ($merge === 'shallow') { + $this->_config = array_merge($this->_config, Hash::expand($update)); + } else { + $this->_config = Hash::merge($this->_config, Hash::expand($update)); + } + + return; + } + + if (is_array($key)) { + foreach ($key as $k => $val) { + $this->_configWrite($k, $val); + } + + return; + } + + if (strpos($key, '.') === false) { + $this->_config[$key] = $value; + + return; + } + + $update = &$this->_config; + $stack = explode('.', $key); + + foreach ($stack as $k) { + if (!is_array($update)) { + throw new CakeException(sprintf('Cannot set %s value', $key)); + } + + $update[$k] = $update[$k] ?? []; + + $update = &$update[$k]; + } + + $update = $value; + } + + /** + * Deletes a single config key. + * + * @param string $key Key to delete. + * @return void + * @throws \Cake\Core\Exception\CakeException if attempting to clobber existing config + */ + protected function _configDelete(string $key): void + { + if (strpos($key, '.') === false) { + unset($this->_config[$key]); + + return; + } + + $update = &$this->_config; + $stack = explode('.', $key); + $length = count($stack); + + foreach ($stack as $i => $k) { + if (!is_array($update)) { + throw new CakeException(sprintf('Cannot unset %s value', $key)); + } + + if (!isset($update[$k])) { + break; + } + + if ($i === $length - 1) { + unset($update[$k]); + break; + } + + $update = &$update[$k]; + } + } +} diff --git a/vendor/cakephp/core/LICENSE.txt b/vendor/cakephp/core/LICENSE.txt new file mode 100644 index 0000000..b938c9e --- /dev/null +++ b/vendor/cakephp/core/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) + +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/cakephp/core/ObjectRegistry.php b/vendor/cakephp/core/ObjectRegistry.php new file mode 100644 index 0000000..25b8d16 --- /dev/null +++ b/vendor/cakephp/core/ObjectRegistry.php @@ -0,0 +1,416 @@ + + */ +abstract class ObjectRegistry implements Countable, IteratorAggregate +{ + /** + * Map of loaded objects. + * + * @var array + * @psalm-var array + */ + protected $_loaded = []; + + /** + * Loads/constructs an object instance. + * + * Will return the instance in the registry if it already exists. + * If a subclass provides event support, you can use `$config['enabled'] = false` + * to exclude constructed objects from being registered for events. + * + * Using {@link \Cake\Controller\Component::$components} as an example. You can alias + * an object by setting the 'className' key, i.e., + * + * ``` + * protected $components = [ + * 'Email' => [ + * 'className' => 'App\Controller\Component\AliasedEmailComponent' + * ]; + * ]; + * ``` + * + * All calls to the `Email` component would use `AliasedEmail` instead. + * + * @param string $name The name/class of the object to load. + * @param array $config Additional settings to use when loading the object. + * @return mixed + * @psalm-return TObject + * @throws \Exception If the class cannot be found. + */ + public function load(string $name, array $config = []) + { + if (isset($config['className'])) { + $objName = $name; + $name = $config['className']; + } else { + [, $objName] = pluginSplit($name); + } + + $loaded = isset($this->_loaded[$objName]); + if ($loaded && !empty($config)) { + $this->_checkDuplicate($objName, $config); + } + if ($loaded) { + return $this->_loaded[$objName]; + } + + $className = $name; + if (is_string($name)) { + $className = $this->_resolveClassName($name); + if ($className === null) { + [$plugin, $name] = pluginSplit($name); + $this->_throwMissingClassError($name, $plugin); + } + } + + /** + * @psalm-var TObject $instance + * @psalm-suppress PossiblyNullArgument + **/ + $instance = $this->_create($className, $objName, $config); + $this->_loaded[$objName] = $instance; + + return $instance; + } + + /** + * Check for duplicate object loading. + * + * If a duplicate is being loaded and has different configuration, that is + * bad and an exception will be raised. + * + * An exception is raised, as replacing the object will not update any + * references other objects may have. Additionally, simply updating the runtime + * configuration is not a good option as we may be missing important constructor + * logic dependent on the configuration. + * + * @param string $name The name of the alias in the registry. + * @param array $config The config data for the new instance. + * @return void + * @throws \RuntimeException When a duplicate is found. + */ + protected function _checkDuplicate(string $name, array $config): void + { + $existing = $this->_loaded[$name]; + $msg = sprintf('The "%s" alias has already been loaded.', $name); + $hasConfig = method_exists($existing, 'getConfig'); + if (!$hasConfig) { + throw new RuntimeException($msg); + } + if (empty($config)) { + return; + } + $existingConfig = $existing->getConfig(); + unset($config['enabled'], $existingConfig['enabled']); + + $failure = null; + foreach ($config as $key => $value) { + if (!array_key_exists($key, $existingConfig)) { + $failure = " The `{$key}` was not defined in the previous configuration data."; + break; + } + if (isset($existingConfig[$key]) && $existingConfig[$key] !== $value) { + $failure = sprintf( + ' The `%s` key has a value of `%s` but previously had a value of `%s`', + $key, + json_encode($value), + json_encode($existingConfig[$key]) + ); + break; + } + } + if ($failure) { + throw new RuntimeException($msg . $failure); + } + } + + /** + * Should resolve the classname for a given object type. + * + * @param string $class The class to resolve. + * @return string|null The resolved name or null for failure. + * @psalm-return class-string|null + */ + abstract protected function _resolveClassName(string $class): ?string; + + /** + * Throw an exception when the requested object name is missing. + * + * @param string $class The class that is missing. + * @param string|null $plugin The plugin $class is missing from. + * @return void + * @throws \Exception + */ + abstract protected function _throwMissingClassError(string $class, ?string $plugin): void; + + /** + * Create an instance of a given classname. + * + * This method should construct and do any other initialization logic + * required. + * + * @param object|string $class The class to build. + * @param string $alias The alias of the object. + * @param array $config The Configuration settings for construction + * @return object + * @psalm-param TObject|string $class + * @psalm-return TObject + */ + abstract protected function _create($class, string $alias, array $config); + + /** + * Get the list of loaded objects. + * + * @return array List of object names. + */ + public function loaded(): array + { + return array_keys($this->_loaded); + } + + /** + * Check whether a given object is loaded. + * + * @param string $name The object name to check for. + * @return bool True is object is loaded else false. + */ + public function has(string $name): bool + { + return isset($this->_loaded[$name]); + } + + /** + * Get loaded object instance. + * + * @param string $name Name of object. + * @return object Object instance. + * @throws \RuntimeException If not loaded or found. + * @psalm-return TObject + */ + public function get(string $name) + { + if (!isset($this->_loaded[$name])) { + throw new RuntimeException(sprintf('Unknown object "%s"', $name)); + } + + return $this->_loaded[$name]; + } + + /** + * Provide public read access to the loaded objects + * + * @param string $name Name of property to read + * @return object|null + * @psalm-return TObject|null + */ + public function __get(string $name) + { + return $this->_loaded[$name] ?? null; + } + + /** + * Provide isset access to _loaded + * + * @param string $name Name of object being checked. + * @return bool + */ + public function __isset(string $name): bool + { + return $this->has($name); + } + + /** + * Sets an object. + * + * @param string $name Name of a property to set. + * @param object $object Object to set. + * @psalm-param TObject $object + * @return void + */ + public function __set(string $name, $object): void + { + $this->set($name, $object); + } + + /** + * Unsets an object. + * + * @param string $name Name of a property to unset. + * @return void + */ + public function __unset(string $name): void + { + $this->unload($name); + } + + /** + * Normalizes an object array, creates an array that makes lazy loading + * easier + * + * @param array $objects Array of child objects to normalize. + * @return array Array of normalized objects. + */ + public function normalizeArray(array $objects): array + { + $normal = []; + foreach ($objects as $i => $objectName) { + $config = []; + if (!is_int($i)) { + $config = (array)$objectName; + $objectName = $i; + } + [, $name] = pluginSplit($objectName); + if (isset($config['class'])) { + $normal[$name] = $config + ['config' => []]; + } else { + $normal[$name] = ['class' => $objectName, 'config' => $config]; + } + } + + return $normal; + } + + /** + * Clear loaded instances in the registry. + * + * If the registry subclass has an event manager, the objects will be detached from events as well. + * + * @return $this + */ + public function reset() + { + foreach (array_keys($this->_loaded) as $name) { + $this->unload((string)$name); + } + + return $this; + } + + /** + * Set an object directly into the registry by name. + * + * If this collection implements events, the passed object will + * be attached into the event manager + * + * @param string $name The name of the object to set in the registry. + * @param object $object instance to store in the registry + * @return $this + * @psalm-param TObject $object + */ + public function set(string $name, object $object) + { + [, $objName] = pluginSplit($name); + + // Just call unload if the object was loaded before + if (array_key_exists($name, $this->_loaded)) { + $this->unload($name); + } + if ($this instanceof EventDispatcherInterface && $object instanceof EventListenerInterface) { + $this->getEventManager()->on($object); + } + $this->_loaded[$objName] = $object; + + return $this; + } + + /** + * Remove an object from the registry. + * + * If this registry has an event manager, the object will be detached from any events as well. + * + * @param string $name The name of the object to remove from the registry. + * @return $this + */ + public function unload(string $name) + { + if (empty($this->_loaded[$name])) { + [$plugin, $name] = pluginSplit($name); + $this->_throwMissingClassError($name, $plugin); + } + + $object = $this->_loaded[$name]; + if ($this instanceof EventDispatcherInterface && $object instanceof EventListenerInterface) { + $this->getEventManager()->off($object); + } + unset($this->_loaded[$name]); + + return $this; + } + + /** + * Returns an array iterator. + * + * @return \Traversable + * @psalm-return \Traversable + */ + public function getIterator(): Traversable + { + return new ArrayIterator($this->_loaded); + } + + /** + * Returns the number of loaded objects. + * + * @return int + */ + public function count(): int + { + return count($this->_loaded); + } + + /** + * Debug friendly object properties. + * + * @return array + */ + public function __debugInfo(): array + { + $properties = get_object_vars($this); + if (isset($properties['_loaded'])) { + $properties['_loaded'] = array_keys($properties['_loaded']); + } + + return $properties; + } +} diff --git a/vendor/cakephp/core/Plugin.php b/vendor/cakephp/core/Plugin.php new file mode 100644 index 0000000..67e1a63 --- /dev/null +++ b/vendor/cakephp/core/Plugin.php @@ -0,0 +1,136 @@ +get($name); + + return $plugin->getPath(); + } + + /** + * Returns the filesystem path for plugin's folder containing class files. + * + * @param string $name name of the plugin in CamelCase format. + * @return string Path to the plugin folder containing class files. + * @throws \Cake\Core\Exception\MissingPluginException If plugin has not been loaded. + */ + public static function classPath(string $name): string + { + $plugin = static::getCollection()->get($name); + + return $plugin->getClassPath(); + } + + /** + * Returns the filesystem path for plugin's folder containing config files. + * + * @param string $name name of the plugin in CamelCase format. + * @return string Path to the plugin folder containing config files. + * @throws \Cake\Core\Exception\MissingPluginException If plugin has not been loaded. + */ + public static function configPath(string $name): string + { + $plugin = static::getCollection()->get($name); + + return $plugin->getConfigPath(); + } + + /** + * Returns the filesystem path for plugin's folder containing template files. + * + * @param string $name name of the plugin in CamelCase format. + * @return string Path to the plugin folder containing template files. + * @throws \Cake\Core\Exception\MissingPluginException If plugin has not been loaded. + */ + public static function templatePath(string $name): string + { + $plugin = static::getCollection()->get($name); + + return $plugin->getTemplatePath(); + } + + /** + * Returns true if the plugin $plugin is already loaded. + * + * @param string $plugin Plugin name. + * @return bool + * @since 3.7.0 + */ + public static function isLoaded(string $plugin): bool + { + return static::getCollection()->has($plugin); + } + + /** + * Return a list of loaded plugins. + * + * @return array A list of plugins that have been loaded + */ + public static function loaded(): array + { + $names = []; + foreach (static::getCollection() as $plugin) { + $names[] = $plugin->getName(); + } + sort($names); + + return $names; + } + + /** + * Get the shared plugin collection. + * + * This method should generally not be used during application + * runtime as plugins should be set during Application startup. + * + * @return \Cake\Core\PluginCollection + */ + public static function getCollection(): PluginCollection + { + if (!isset(static::$plugins)) { + static::$plugins = new PluginCollection(); + } + + return static::$plugins; + } +} diff --git a/vendor/cakephp/core/PluginApplicationInterface.php b/vendor/cakephp/core/PluginApplicationInterface.php new file mode 100644 index 0000000..0ba0771 --- /dev/null +++ b/vendor/cakephp/core/PluginApplicationInterface.php @@ -0,0 +1,75 @@ + $config The configuration data for the plugin if using a string for $name + * @return $this + */ + public function addPlugin($name, array $config = []); + + /** + * Run bootstrap logic for loaded plugins. + * + * @return void + */ + public function pluginBootstrap(): void; + + /** + * Run routes hooks for loaded plugins + * + * @param \Cake\Routing\RouteBuilder $routes The route builder to use. + * @return \Cake\Routing\RouteBuilder + */ + public function pluginRoutes(RouteBuilder $routes): RouteBuilder; + + /** + * Run middleware hooks for plugins + * + * @param \Cake\Http\MiddlewareQueue $middleware The MiddlewareQueue to use. + * @return \Cake\Http\MiddlewareQueue + */ + public function pluginMiddleware(MiddlewareQueue $middleware): MiddlewareQueue; + + /** + * Run console hooks for plugins + * + * @param \Cake\Console\CommandCollection $commands The CommandCollection to use. + * @return \Cake\Console\CommandCollection + */ + public function pluginConsole(CommandCollection $commands): CommandCollection; +} diff --git a/vendor/cakephp/core/PluginCollection.php b/vendor/cakephp/core/PluginCollection.php new file mode 100644 index 0000000..a9fc4a1 --- /dev/null +++ b/vendor/cakephp/core/PluginCollection.php @@ -0,0 +1,363 @@ + + */ +class PluginCollection implements Iterator, Countable +{ + /** + * Plugin list + * + * @var array<\Cake\Core\PluginInterface> + */ + protected $plugins = []; + + /** + * Names of plugins + * + * @var array + */ + protected $names = []; + + /** + * Iterator position stack. + * + * @var array + */ + protected $positions = []; + + /** + * Loop depth + * + * @var int + */ + protected $loopDepth = -1; + + /** + * Constructor + * + * @param array<\Cake\Core\PluginInterface> $plugins The map of plugins to add to the collection. + */ + public function __construct(array $plugins = []) + { + foreach ($plugins as $plugin) { + $this->add($plugin); + } + $this->loadConfig(); + } + + /** + * Load the path information stored in vendor/cakephp-plugins.php + * + * This file is generated by the cakephp/plugin-installer package and used + * to locate plugins on the filesystem as applications can use `extra.plugin-paths` + * in their composer.json file to move plugin outside of vendor/ + * + * @internal + * @return void + */ + protected function loadConfig(): void + { + if (Configure::check('plugins')) { + return; + } + $vendorFile = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'cakephp-plugins.php'; + if (!is_file($vendorFile)) { + $vendorFile = dirname(dirname(dirname(dirname(__DIR__)))) . DIRECTORY_SEPARATOR . 'cakephp-plugins.php'; + if (!is_file($vendorFile)) { + Configure::write(['plugins' => []]); + + return; + } + } + + $config = require $vendorFile; + Configure::write($config); + } + + /** + * Locate a plugin path by looking at configuration data. + * + * This will use the `plugins` Configure key, and fallback to enumerating `App::path('plugins')` + * + * This method is not part of the official public API as plugins with + * no plugin class are being phased out. + * + * @param string $name The plugin name to locate a path for. + * @return string + * @throws \Cake\Core\Exception\MissingPluginException when a plugin path cannot be resolved. + * @internal + */ + public function findPath(string $name): string + { + // Ensure plugin config is loaded each time. This is necessary primarily + // for testing because the Configure::clear() call in TestCase::tearDown() + // wipes out all configuration including plugin paths config. + $this->loadConfig(); + + $path = Configure::read('plugins.' . $name); + if ($path) { + return $path; + } + + $pluginPath = str_replace('/', DIRECTORY_SEPARATOR, $name); + $paths = App::path('plugins'); + foreach ($paths as $path) { + if (is_dir($path . $pluginPath)) { + return $path . $pluginPath . DIRECTORY_SEPARATOR; + } + } + + throw new MissingPluginException(['plugin' => $name]); + } + + /** + * Add a plugin to the collection + * + * Plugins will be keyed by their names. + * + * @param \Cake\Core\PluginInterface $plugin The plugin to load. + * @return $this + */ + public function add(PluginInterface $plugin) + { + $name = $plugin->getName(); + $this->plugins[$name] = $plugin; + $this->names = array_keys($this->plugins); + + return $this; + } + + /** + * Remove a plugin from the collection if it exists. + * + * @param string $name The named plugin. + * @return $this + */ + public function remove(string $name) + { + unset($this->plugins[$name]); + $this->names = array_keys($this->plugins); + + return $this; + } + + /** + * Remove all plugins from the collection + * + * @return $this + */ + public function clear() + { + $this->plugins = []; + $this->names = []; + $this->positions = []; + $this->loopDepth = -1; + + return $this; + } + + /** + * Check whether the named plugin exists in the collection. + * + * @param string $name The named plugin. + * @return bool + */ + public function has(string $name): bool + { + return isset($this->plugins[$name]); + } + + /** + * Get the a plugin by name. + * + * If a plugin isn't already loaded it will be autoloaded on first access + * and that plugins loaded this way may miss some hook methods. + * + * @param string $name The plugin to get. + * @return \Cake\Core\PluginInterface The plugin. + * @throws \Cake\Core\Exception\MissingPluginException when unknown plugins are fetched. + */ + public function get(string $name): PluginInterface + { + if ($this->has($name)) { + return $this->plugins[$name]; + } + + $plugin = $this->create($name); + $this->add($plugin); + + return $plugin; + } + + /** + * Create a plugin instance from a name/classname and configuration. + * + * @param string $name The plugin name or classname + * @param array $config Configuration options for the plugin. + * @return \Cake\Core\PluginInterface + * @throws \Cake\Core\Exception\MissingPluginException When plugin instance could not be created. + */ + public function create(string $name, array $config = []): PluginInterface + { + if ($name === '') { + throw new CakeException('Cannot create a plugin with empty name'); + } + + if (strpos($name, '\\') !== false) { + /** @var \Cake\Core\PluginInterface */ + return new $name($config); + } + + $config += ['name' => $name]; + $namespace = str_replace('/', '\\', $name); + + $className = $namespace . '\\' . 'Plugin'; + // Check for [Vendor/]Foo/Plugin class + if (!class_exists($className)) { + $pos = strpos($name, '/'); + if ($pos === false) { + $className = $namespace . '\\' . $name . 'Plugin'; + } else { + $className = $namespace . '\\' . substr($name, $pos + 1) . 'Plugin'; + } + + // Check for [Vendor/]Foo/FooPlugin + if (!class_exists($className)) { + $className = BasePlugin::class; + if (empty($config['path'])) { + $config['path'] = $this->findPath($name); + } + } + } + + /** @var class-string<\Cake\Core\PluginInterface> $className */ + return new $className($config); + } + + /** + * Implementation of Countable. + * + * Get the number of plugins in the collection. + * + * @return int + */ + public function count(): int + { + return count($this->plugins); + } + + /** + * Part of Iterator Interface + * + * @return void + */ + public function next(): void + { + $this->positions[$this->loopDepth]++; + } + + /** + * Part of Iterator Interface + * + * @return string + */ + public function key(): string + { + return $this->names[$this->positions[$this->loopDepth]]; + } + + /** + * Part of Iterator Interface + * + * @return \Cake\Core\PluginInterface + */ + public function current(): PluginInterface + { + $position = $this->positions[$this->loopDepth]; + $name = $this->names[$position]; + + return $this->plugins[$name]; + } + + /** + * Part of Iterator Interface + * + * @return void + */ + public function rewind(): void + { + $this->positions[] = 0; + $this->loopDepth += 1; + } + + /** + * Part of Iterator Interface + * + * @return bool + */ + public function valid(): bool + { + $valid = isset($this->names[$this->positions[$this->loopDepth]]); + if (!$valid) { + array_pop($this->positions); + $this->loopDepth -= 1; + } + + return $valid; + } + + /** + * Filter the plugins to those with the named hook enabled. + * + * @param string $hook The hook to filter plugins by + * @return \Generator<\Cake\Core\PluginInterface> A generator containing matching plugins. + * @throws \InvalidArgumentException on invalid hooks + */ + public function with(string $hook): Generator + { + if (!in_array($hook, PluginInterface::VALID_HOOKS, true)) { + throw new InvalidArgumentException("The `{$hook}` hook is not a known plugin hook."); + } + foreach ($this as $plugin) { + if ($plugin->isEnabled($hook)) { + yield $plugin; + } + } + } +} diff --git a/vendor/cakephp/core/PluginInterface.php b/vendor/cakephp/core/PluginInterface.php new file mode 100644 index 0000000..e251cdf --- /dev/null +++ b/vendor/cakephp/core/PluginInterface.php @@ -0,0 +1,135 @@ + + */ + public const VALID_HOOKS = ['bootstrap', 'console', 'middleware', 'routes', 'services']; + + /** + * Get the name of this plugin. + * + * @return string + */ + public function getName(): string; + + /** + * Get the filesystem path to this plugin + * + * @return string + */ + public function getPath(): string; + + /** + * Get the filesystem path to configuration for this plugin + * + * @return string + */ + public function getConfigPath(): string; + + /** + * Get the filesystem path to configuration for this plugin + * + * @return string + */ + public function getClassPath(): string; + + /** + * Get the filesystem path to templates for this plugin + * + * @return string + */ + public function getTemplatePath(): string; + + /** + * Load all the application configuration and bootstrap logic. + * + * The default implementation of this method will include the `config/bootstrap.php` in the plugin if it exist. You + * can override this method to replace that behavior. + * + * The host application is provided as an argument. This allows you to load additional + * plugin dependencies, or attach events. + * + * @param \Cake\Core\PluginApplicationInterface $app The host application + * @return void + */ + public function bootstrap(PluginApplicationInterface $app): void; + + /** + * Add console commands for the plugin. + * + * @param \Cake\Console\CommandCollection $commands The command collection to update + * @return \Cake\Console\CommandCollection + */ + public function console(CommandCollection $commands): CommandCollection; + + /** + * Add middleware for the plugin. + * + * @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to update. + * @return \Cake\Http\MiddlewareQueue + */ + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue; + + /** + * Add routes for the plugin. + * + * The default implementation of this method will include the `config/routes.php` in the plugin if it exists. You + * can override this method to replace that behavior. + * + * @param \Cake\Routing\RouteBuilder $routes The route builder to update. + * @return void + */ + public function routes(RouteBuilder $routes): void; + + /** + * Disables the named hook + * + * @param string $hook The hook to disable + * @return $this + */ + public function disable(string $hook); + + /** + * Enables the named hook + * + * @param string $hook The hook to disable + * @return $this + */ + public function enable(string $hook); + + /** + * Check if the named hook is enabled + * + * @param string $hook The hook to check + * @return bool + */ + public function isEnabled(string $hook): bool; +} diff --git a/vendor/cakephp/core/README.md b/vendor/cakephp/core/README.md new file mode 100644 index 0000000..f26cba6 --- /dev/null +++ b/vendor/cakephp/core/README.md @@ -0,0 +1,37 @@ +[![Total Downloads](https://img.shields.io/packagist/dt/cakephp/core.svg?style=flat-square)](https://packagist.org/packages/cakephp/core) +[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE.txt) + +# CakePHP Core Classes + +A set of classes used for configuration files reading and storing. +This repository contains the classes that are used as glue for creating the CakePHP framework. + +## Usage + +You can use the `Configure` class to store arbitrary configuration data: + +```php +use Cake\Core\Configure; +use Cake\Core\Configure\Engine\PhpConfig; + +Configure::write('Company.name','Pizza, Inc.'); +Configure::read('Company.name'); // Returns: 'Pizza, Inc.' +``` + +It also possible to load configuration from external files: + +```php +Configure::config('default', new PhpConfig('/path/to/config/folder')); +Configure::load('app', 'default', false); +Configure::load('other_config', 'default'); +``` + +And write the configuration back into files: + +```php +Configure::dump('my_config', 'default'); +``` + +## Documentation + +Please make sure you check the [official documentation](https://book.cakephp.org/4/en/development/configuration.html) diff --git a/vendor/cakephp/core/Retry/CommandRetry.php b/vendor/cakephp/core/Retry/CommandRetry.php new file mode 100644 index 0000000..1211695 --- /dev/null +++ b/vendor/cakephp/core/Retry/CommandRetry.php @@ -0,0 +1,94 @@ +strategy = $strategy; + $this->maxRetries = $maxRetries; + } + + /** + * The number of retries to perform in case of failure + * + * @param callable $action The callable action to execute with a retry strategy + * @return mixed The return value of the passed action callable + * @throws \Exception + */ + public function run(callable $action) + { + $this->numRetries = 0; + while (true) { + try { + return $action(); + } catch (Exception $e) { + if ( + $this->numRetries < $this->maxRetries && + $this->strategy->shouldRetry($e, $this->numRetries) + ) { + $this->numRetries++; + continue; + } + + throw $e; + } + } + } + + /** + * Returns the last number of retry attemps. + * + * @return int + */ + public function getRetries(): int + { + return $this->numRetries; + } +} diff --git a/vendor/cakephp/core/Retry/RetryStrategyInterface.php b/vendor/cakephp/core/Retry/RetryStrategyInterface.php new file mode 100644 index 0000000..e2ef9e2 --- /dev/null +++ b/vendor/cakephp/core/Retry/RetryStrategyInterface.php @@ -0,0 +1,35 @@ + + * @see ServiceProvider::provides() + */ + protected $provides = []; + + /** + * Get the container. + * + * This method's actual return type and documented return type differ + * because PHP 7.2 doesn't support return type narrowing. + * + * @return \Cake\Core\ContainerInterface + */ + public function getContainer(): DefinitionContainerInterface + { + $container = parent::getContainer(); + + if (!($container instanceof ContainerInterface)) { + $message = sprintf( + 'Unexpected container type. Expected `%s` got `%s` instead.', + ContainerInterface::class, + getTypeName($container) + ); + throw new RuntimeException($message); + } + + return $container; + } + + /** + * Delegate to the bootstrap() method + * + * This method wraps the league/container function so users + * only need to use the CakePHP bootstrap() interface. + * + * @return void + */ + public function boot(): void + { + $this->bootstrap($this->getContainer()); + } + + /** + * Bootstrap hook for ServiceProviders + * + * This hook should be implemented if your service provider + * needs to register additional service providers, load configuration + * files or do any other work when the service provider is added to the + * container. + * + * @param \Cake\Core\ContainerInterface $container The container to add services to. + * @return void + */ + public function bootstrap(ContainerInterface $container): void + { + } + + /** + * Call the abstract services() method. + * + * This method primarily exists as a shim between the interface + * that league/container has and the one we want to offer in CakePHP. + * + * @return void + */ + public function register(): void + { + $this->services($this->getContainer()); + } + + /** + * The provides method is a way to let the container know that a service + * is provided by this service provider. + * + * Every service that is registered via this service provider must have an + * alias added to this array or it will be ignored. + * + * @param string $id Identifier. + * @return bool + */ + public function provides(string $id): bool + { + return in_array($id, $this->provides, true); + } + + /** + * Register the services in a provider. + * + * All services registered in this method should also be included in the $provides + * property so that services can be located. + * + * @param \Cake\Core\ContainerInterface $container The container to add services to. + * @return void + */ + abstract public function services(ContainerInterface $container): void; +} diff --git a/vendor/cakephp/core/StaticConfigTrait.php b/vendor/cakephp/core/StaticConfigTrait.php new file mode 100644 index 0000000..2db8624 --- /dev/null +++ b/vendor/cakephp/core/StaticConfigTrait.php @@ -0,0 +1,325 @@ + + */ + protected static $_config = []; + + /** + * This method can be used to define configuration adapters for an application. + * + * To change an adapter's configuration at runtime, first drop the adapter and then + * reconfigure it. + * + * Adapters will not be constructed until the first operation is done. + * + * ### Usage + * + * Assuming that the class' name is `Cache` the following scenarios + * are supported: + * + * Setting a cache engine up. + * + * ``` + * Cache::setConfig('default', $settings); + * ``` + * + * Injecting a constructed adapter in: + * + * ``` + * Cache::setConfig('default', $instance); + * ``` + * + * Configure multiple adapters at once: + * + * ``` + * Cache::setConfig($arrayOfConfig); + * ``` + * + * @param array|string $key The name of the configuration, or an array of multiple configs. + * @param mixed $config Configuration value. Generally an array of name => configuration data for adapter. + * @throws \BadMethodCallException When trying to modify an existing config. + * @throws \LogicException When trying to store an invalid structured config array. + * @return void + */ + public static function setConfig($key, $config = null): void + { + if ($config === null) { + if (!is_array($key)) { + throw new LogicException('If config is null, key must be an array.'); + } + foreach ($key as $name => $settings) { + static::setConfig($name, $settings); + } + + return; + } + + if (isset(static::$_config[$key])) { + /** @psalm-suppress PossiblyInvalidArgument */ + throw new BadMethodCallException(sprintf('Cannot reconfigure existing key "%s"', $key)); + } + + if (is_object($config)) { + $config = ['className' => $config]; + } + + if (is_array($config) && isset($config['url'])) { + $parsed = static::parseDsn($config['url']); + unset($config['url']); + $config = $parsed + $config; + } + + if (isset($config['engine']) && empty($config['className'])) { + $config['className'] = $config['engine']; + unset($config['engine']); + } + /** @psalm-suppress InvalidPropertyAssignmentValue */ + static::$_config[$key] = $config; + } + + /** + * Reads existing configuration. + * + * @param string $key The name of the configuration. + * @return mixed|null Configuration data at the named key or null if the key does not exist. + */ + public static function getConfig(string $key) + { + return static::$_config[$key] ?? null; + } + + /** + * Reads existing configuration for a specific key. + * + * The config value for this key must exist, it can never be null. + * + * @param string $key The name of the configuration. + * @return mixed Configuration data at the named key. + * @throws \InvalidArgumentException If value does not exist. + */ + public static function getConfigOrFail(string $key) + { + if (!isset(static::$_config[$key])) { + throw new InvalidArgumentException(sprintf('Expected configuration `%s` not found.', $key)); + } + + return static::$_config[$key]; + } + + /** + * Drops a constructed adapter. + * + * If you wish to modify an existing configuration, you should drop it, + * change configuration and then re-add it. + * + * If the implementing objects supports a `$_registry` object the named configuration + * will also be unloaded from the registry. + * + * @param string $config An existing configuration you wish to remove. + * @return bool Success of the removal, returns false when the config does not exist. + */ + public static function drop(string $config): bool + { + if (!isset(static::$_config[$config])) { + return false; + } + /** @psalm-suppress RedundantPropertyInitializationCheck */ + if (isset(static::$_registry)) { + static::$_registry->unload($config); + } + unset(static::$_config[$config]); + + return true; + } + + /** + * Returns an array containing the named configurations + * + * @return array Array of configurations. + */ + public static function configured(): array + { + $configurations = array_keys(static::$_config); + + return array_map(function ($key) { + return (string)$key; + }, $configurations); + } + + /** + * Parses a DSN into a valid connection configuration + * + * This method allows setting a DSN using formatting similar to that used by PEAR::DB. + * The following is an example of its usage: + * + * ``` + * $dsn = 'mysql://user:pass@localhost/database?'; + * $config = ConnectionManager::parseDsn($dsn); + * + * $dsn = 'Cake\Log\Engine\FileLog://?types=notice,info,debug&file=debug&path=LOGS'; + * $config = Log::parseDsn($dsn); + * + * $dsn = 'smtp://user:secret@localhost:25?timeout=30&client=null&tls=null'; + * $config = Email::parseDsn($dsn); + * + * $dsn = 'file:///?className=\My\Cache\Engine\FileEngine'; + * $config = Cache::parseDsn($dsn); + * + * $dsn = 'File://?prefix=myapp_cake_core_&serialize=true&duration=+2 minutes&path=/tmp/persistent/'; + * $config = Cache::parseDsn($dsn); + * ``` + * + * For all classes, the value of `scheme` is set as the value of both the `className` + * unless they have been otherwise specified. + * + * Note that querystring arguments are also parsed and set as values in the returned configuration. + * + * @param string $dsn The DSN string to convert to a configuration array + * @return array The configuration array to be stored after parsing the DSN + * @throws \InvalidArgumentException If not passed a string, or passed an invalid string + */ + public static function parseDsn(string $dsn): array + { + if (empty($dsn)) { + return []; + } + + $pattern = <<<'REGEXP' +{ + ^ + (?P<_scheme> + (?P[\w\\\\]+):// + ) + (?P<_username> + (?P.*?) + (?P<_password> + :(?P.*?) + )? + @ + )? + (?P<_host> + (?P[^?#/:@]+) + (?P<_port> + :(?P\d+) + )? + )? + (?P<_path> + (?P/[^?#]*) + )? + (?P<_query> + \?(?P[^#]*) + )? + (?P<_fragment> + \#(?P.*) + )? + $ +}x +REGEXP; + + preg_match($pattern, $dsn, $parsed); + + if (!$parsed) { + throw new InvalidArgumentException("The DSN string '{$dsn}' could not be parsed."); + } + + $exists = []; + foreach ($parsed as $k => $v) { + if (is_int($k)) { + unset($parsed[$k]); + } elseif (strpos($k, '_') === 0) { + $exists[substr($k, 1)] = ($v !== ''); + unset($parsed[$k]); + } elseif ($v === '' && !$exists[$k]) { + unset($parsed[$k]); + } + } + + $query = ''; + + if (isset($parsed['query'])) { + $query = $parsed['query']; + unset($parsed['query']); + } + + parse_str($query, $queryArgs); + + foreach ($queryArgs as $key => $value) { + if ($value === 'true') { + $queryArgs[$key] = true; + } elseif ($value === 'false') { + $queryArgs[$key] = false; + } elseif ($value === 'null') { + $queryArgs[$key] = null; + } + } + + $parsed = $queryArgs + $parsed; + + if (empty($parsed['className'])) { + $classMap = static::getDsnClassMap(); + + $parsed['className'] = $parsed['scheme']; + if (isset($classMap[$parsed['scheme']])) { + /** @psalm-suppress PossiblyNullArrayOffset */ + $parsed['className'] = $classMap[$parsed['scheme']]; + } + } + + return $parsed; + } + + /** + * Updates the DSN class map for this class. + * + * @param array $map Additions/edits to the class map to apply. + * @return void + * @psalm-param array $map + */ + public static function setDsnClassMap(array $map): void + { + static::$_dsnClassMap = $map + static::$_dsnClassMap; + } + + /** + * Returns the DSN class map for this class. + * + * @return array + * @psalm-return array + */ + public static function getDsnClassMap(): array + { + return static::$_dsnClassMap; + } +} diff --git a/vendor/cakephp/core/TestSuite/ContainerStubTrait.php b/vendor/cakephp/core/TestSuite/ContainerStubTrait.php new file mode 100644 index 0000000..9c6ae1e --- /dev/null +++ b/vendor/cakephp/core/TestSuite/ContainerStubTrait.php @@ -0,0 +1,181 @@ +|class-string<\Cake\Core\ConsoleApplicationInterface>|null + * @var string|null + */ + protected $_appClass; + + /** + * The customized application constructor arguments. + * + * @var array|null + */ + protected $_appArgs; + + /** + * The collection of container services. + * + * @var array + */ + private $containerServices = []; + + /** + * Configure the application class to use in integration tests. + * + * @param string $class The application class name. + * @param array|null $constructorArgs The constructor arguments for your application class. + * @return void + * @psalm-param class-string<\Cake\Core\HttpApplicationInterface>|class-string<\Cake\Core\ConsoleApplicationInterface> $class + */ + public function configApplication(string $class, ?array $constructorArgs): void + { + $this->_appClass = $class; + $this->_appArgs = $constructorArgs; + } + + /** + * Create an application instance. + * + * Uses the configuration set in `configApplication()`. + * + * @return \Cake\Core\HttpApplicationInterface|\Cake\Core\ConsoleApplicationInterface + */ + protected function createApp() + { + if ($this->_appClass) { + $appClass = $this->_appClass; + } else { + /** @psalm-var class-string<\Cake\Http\BaseApplication> */ + $appClass = Configure::read('App.namespace') . '\Application'; + } + if (!class_exists($appClass)) { + throw new LogicException("Cannot load `{$appClass}` for use in integration testing."); + } + $appArgs = $this->_appArgs ?: [CONFIG]; + + $app = new $appClass(...$appArgs); + if (!empty($this->containerServices) && method_exists($app, 'getEventManager')) { + $app->getEventManager()->on('Application.buildContainer', [$this, 'modifyContainer']); + } + + return $app; + } + + /** + * Add a mocked service to the container. + * + * When the container is created the provided classname + * will be mapped to the factory function. The factory + * function will be used to create mocked services. + * + * @param string $class The class or interface you want to define. + * @param \Closure $factory The factory function for mocked services. + * @return $this + */ + public function mockService(string $class, Closure $factory) + { + $this->containerServices[$class] = $factory; + + return $this; + } + + /** + * Remove a mocked service to the container. + * + * @param string $class The class or interface you want to remove. + * @return $this + */ + public function removeMockService(string $class) + { + unset($this->containerServices[$class]); + + return $this; + } + + /** + * Wrap the application's container with one containing mocks. + * + * If any mocked services are defined, the application's container + * will be replaced with one containing mocks. The original + * container will be set as a delegate to the mock container. + * + * @param \Cake\Event\EventInterface $event The event + * @param \Cake\Core\ContainerInterface $container The container to wrap. + * @return \Cake\Core\ContainerInterface|null + */ + public function modifyContainer(EventInterface $event, ContainerInterface $container): ?ContainerInterface + { + if (empty($this->containerServices)) { + return null; + } + foreach ($this->containerServices as $key => $factory) { + if ($container->has($key)) { + try { + $container->extend($key)->setConcrete($factory); + } catch (NotFoundException $e) { + $container->add($key, $factory); + } + } else { + $container->add($key, $factory); + } + } + + return $container; + } + + /** + * Clears any mocks that were defined and cleans + * up application class configuration. + * + * @after + * @return void + */ + public function cleanupContainer(): void + { + $this->_appArgs = null; + $this->_appClass = null; + $this->containerServices = []; + } +} + +// phpcs:disable +class_alias( + 'Cake\Core\TestSuite\ContainerStubTrait', + 'Cake\TestSuite\ContainerStubTrait' +); +// phpcs:enable diff --git a/vendor/cakephp/core/composer.json b/vendor/cakephp/core/composer.json new file mode 100644 index 0000000..d5d4cf7 --- /dev/null +++ b/vendor/cakephp/core/composer.json @@ -0,0 +1,44 @@ +{ + "name": "cakephp/core", + "description": "CakePHP Framework Core classes", + "type": "library", + "keywords": [ + "cakephp", + "framework", + "core" + ], + "homepage": "https://cakephp.org", + "license": "MIT", + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/core/graphs/contributors" + } + ], + "support": { + "issues": "https://github.com/cakephp/cakephp/issues", + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "source": "https://github.com/cakephp/core" + }, + "require": { + "php": ">=7.4.0", + "cakephp/utility": "^4.0" + }, + "provide": { + "psr/container-implementation": "^1.0 || ^2.0" + }, + "suggest": { + "cakephp/event": "To use PluginApplicationInterface or plugin applications.", + "cakephp/cache": "To use Configure::store() and restore().", + "league/container": "To use Container and ServiceProvider classes" + }, + "autoload": { + "psr-4": { + "Cake\\Core\\": "." + }, + "files": [ + "functions.php" + ] + } +} diff --git a/vendor/cakephp/core/functions.php b/vendor/cakephp/core/functions.php new file mode 100644 index 0000000..af2d2b6 --- /dev/null +++ b/vendor/cakephp/core/functions.php @@ -0,0 +1,340 @@ + $t) { + $texts[$k] = h($t, $double, $charset); + } + + return $texts; + } elseif (is_object($text)) { + if (method_exists($text, '__toString')) { + $text = $text->__toString(); + } else { + $text = '(object)' . get_class($text); + } + } elseif ($text === null || is_scalar($text)) { + return $text; + } + + static $defaultCharset = false; + if ($defaultCharset === false) { + $defaultCharset = mb_internal_encoding() ?: 'UTF-8'; + } + + return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, $charset ?: $defaultCharset, $double); + } +} + +if (!function_exists('Cake\Core\pluginSplit')) { + /** + * Splits a dot syntax plugin name into its plugin and class name. + * If $name does not have a dot, then index 0 will be null. + * + * Commonly used like + * ``` + * list($plugin, $name) = pluginSplit($name); + * ``` + * + * @param string $name The name you want to plugin split. + * @param bool $dotAppend Set to true if you want the plugin to have a '.' appended to it. + * @param string|null $plugin Optional default plugin to use if no plugin is found. Defaults to null. + * @return array Array with 2 indexes. 0 => plugin name, 1 => class name. + * @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pluginSplit + * @psalm-return array{string|null, string} + */ + function pluginSplit(string $name, bool $dotAppend = false, ?string $plugin = null): array + { + if (strpos($name, '.') !== false) { + $parts = explode('.', $name, 2); + if ($dotAppend) { + $parts[0] .= '.'; + } + + /** @psalm-var array{string, string} */ + return $parts; + } + + return [$plugin, $name]; + } +} + +if (!function_exists('Cake\Core\namespaceSplit')) { + /** + * Split the namespace from the classname. + * + * Commonly used like `list($namespace, $className) = namespaceSplit($class);`. + * + * @param string $class The full class name, ie `Cake\Core\App`. + * @return array Array with 2 indexes. 0 => namespace, 1 => classname. + */ + function namespaceSplit(string $class): array + { + $pos = strrpos($class, '\\'); + if ($pos === false) { + return ['', $class]; + } + + return [substr($class, 0, $pos), substr($class, $pos + 1)]; + } +} + +if (!function_exists('Cake\Core\pr')) { + /** + * print_r() convenience function. + * + * In terminals this will act similar to using print_r() directly, when not run on CLI + * print_r() will also wrap `
` tags around the output of given variable. Similar to debug().
+     *
+     * This function returns the same variable that was passed.
+     *
+     * @param mixed $var Variable to print out.
+     * @return mixed the same $var that was passed to this function
+     * @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pr
+     * @see debug()
+     */
+    function pr($var)
+    {
+        if (!Configure::read('debug')) {
+            return $var;
+        }
+
+        $template = PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ? '
%s
' : "\n%s\n\n"; + printf($template, trim(print_r($var, true))); + + return $var; + } +} + +if (!function_exists('Cake\Core\pj')) { + /** + * JSON pretty print convenience function. + * + * In terminals this will act similar to using json_encode() with JSON_PRETTY_PRINT directly, when not run on CLI + * will also wrap `
` tags around the output of given variable. Similar to pr().
+     *
+     * This function returns the same variable that was passed.
+     *
+     * @param mixed $var Variable to print out.
+     * @return mixed the same $var that was passed to this function
+     * @see pr()
+     * @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pj
+     */
+    function pj($var)
+    {
+        if (!Configure::read('debug')) {
+            return $var;
+        }
+
+        $template = PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ? '
%s
' : "\n%s\n\n"; + printf($template, trim(json_encode($var, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES))); + + return $var; + } +} + +if (!function_exists('Cake\Core\env')) { + /** + * Gets an environment variable from available sources, and provides emulation + * for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on + * IIS, or SCRIPT_NAME in CGI mode). Also exposes some additional custom + * environment information. + * + * @param string $key Environment variable name. + * @param string|bool|null $default Specify a default value in case the environment variable is not defined. + * @return string|bool|null Environment variable setting. + * @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#env + */ + function env(string $key, $default = null) + { + if ($key === 'HTTPS') { + if (isset($_SERVER['HTTPS'])) { + return !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'; + } + + return strpos((string)env('SCRIPT_URI'), 'https://') === 0; + } + + if ($key === 'SCRIPT_NAME' && env('CGI_MODE') && isset($_ENV['SCRIPT_URL'])) { + $key = 'SCRIPT_URL'; + } + + /** @var string|null $val */ + $val = $_SERVER[$key] ?? $_ENV[$key] ?? null; + if ($val == null && getenv($key) !== false) { + /** @var string|false $val */ + $val = getenv($key); + } + + if ($key === 'REMOTE_ADDR' && $val === env('SERVER_ADDR')) { + $addr = env('HTTP_PC_REMOTE_ADDR'); + if ($addr !== null) { + $val = $addr; + } + } + + if ($val !== null) { + return $val; + } + + switch ($key) { + case 'DOCUMENT_ROOT': + $name = (string)env('SCRIPT_NAME'); + $filename = (string)env('SCRIPT_FILENAME'); + $offset = 0; + if (!strpos($name, '.php')) { + $offset = 4; + } + + return substr($filename, 0, -(strlen($name) + $offset)); + case 'PHP_SELF': + return str_replace((string)env('DOCUMENT_ROOT'), '', (string)env('SCRIPT_FILENAME')); + case 'CGI_MODE': + return PHP_SAPI === 'cgi'; + } + + return $default; + } +} + +if (!function_exists('Cake\Core\triggerWarning')) { + /** + * Triggers an E_USER_WARNING. + * + * @param string $message The warning message. + * @return void + */ + function triggerWarning(string $message): void + { + $trace = debug_backtrace(); + if (isset($trace[1])) { + $frame = $trace[1]; + $frame += ['file' => '[internal]', 'line' => '??']; + $message = sprintf( + '%s - %s, line: %s', + $message, + $frame['file'], + $frame['line'] + ); + } + trigger_error($message, E_USER_WARNING); + } +} + +if (!function_exists('Cake\Core\deprecationWarning')) { + /** + * Helper method for outputting deprecation warnings + * + * @param string $message The message to output as a deprecation warning. + * @param int $stackFrame The stack frame to include in the error. Defaults to 1 + * as that should point to application/plugin code. + * @return void + */ + function deprecationWarning(string $message, int $stackFrame = 1): void + { + if (!(error_reporting() & E_USER_DEPRECATED)) { + return; + } + + $trace = debug_backtrace(); + if (isset($trace[$stackFrame])) { + $frame = $trace[$stackFrame]; + $frame += ['file' => '[internal]', 'line' => '??']; + + // Assuming we're installed in vendor/cakephp/cakephp/src/Core/functions.php + $root = dirname(__DIR__, 5); + if (defined('ROOT')) { + $root = ROOT; + } + $relative = str_replace( + DIRECTORY_SEPARATOR, + '/', + substr($frame['file'], strlen($root) + 1) + ); + $patterns = (array)Configure::read('Error.ignoredDeprecationPaths'); + foreach ($patterns as $pattern) { + $pattern = str_replace(DIRECTORY_SEPARATOR, '/', $pattern); + if (fnmatch($pattern, $relative)) { + return; + } + } + + $message = sprintf( + "%s\n%s, line: %s\n" . 'You can disable all deprecation warnings by setting `Error.errorLevel` to ' . + '`E_ALL & ~E_USER_DEPRECATED`. Adding `%s` to `Error.ignoredDeprecationPaths` ' . + 'in your `config/app.php` config will mute deprecations from that file only.', + $message, + $frame['file'], + $frame['line'], + $relative + ); + } + + static $errors = []; + $checksum = md5($message); + $duplicate = (bool)Configure::read('Error.allowDuplicateDeprecations', false); + if (isset($errors[$checksum]) && !$duplicate) { + return; + } + if (!$duplicate) { + $errors[$checksum] = true; + } + + trigger_error($message, E_USER_DEPRECATED); + } +} + +if (!function_exists('Cake\Core\getTypeName')) { + /** + * Returns the objects class or var type of it's not an object + * + * @param mixed $var Variable to check + * @return string Returns the class name or variable type + */ + function getTypeName($var): string + { + return is_object($var) ? get_class($var) : gettype($var); + } +} + +/** + * Include global functions. + */ +if (!getenv('CAKE_DISABLE_GLOBAL_FUNCS')) { + include 'functions_global.php'; +} diff --git a/vendor/cakephp/core/functions_global.php b/vendor/cakephp/core/functions_global.php new file mode 100644 index 0000000..bb2a714 --- /dev/null +++ b/vendor/cakephp/core/functions_global.php @@ -0,0 +1,190 @@ + plugin name, 1 => class name. + * @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pluginSplit + * @psalm-return array{string|null, string} + */ + function pluginSplit(string $name, bool $dotAppend = false, ?string $plugin = null): array + { + return cakePluginSplit($name, $dotAppend, $plugin); + } +} + +if (!function_exists('namespaceSplit')) { + /** + * Split the namespace from the classname. + * + * Commonly used like `list($namespace, $className) = namespaceSplit($class);`. + * + * @param string $class The full class name, ie `Cake\Core\App`. + * @return array Array with 2 indexes. 0 => namespace, 1 => classname. + */ + function namespaceSplit(string $class): array + { + return cakeNamespaceSplit($class); + } +} + +if (!function_exists('pr')) { + /** + * print_r() convenience function. + * + * In terminals this will act similar to using print_r() directly, when not run on CLI + * print_r() will also wrap `
` tags around the output of given variable. Similar to debug().
+     *
+     * This function returns the same variable that was passed.
+     *
+     * @param mixed $var Variable to print out.
+     * @return mixed the same $var that was passed to this function
+     * @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pr
+     * @see debug()
+     */
+    function pr($var)
+    {
+        return cakePr($var);
+    }
+}
+
+if (!function_exists('pj')) {
+    /**
+     * JSON pretty print convenience function.
+     *
+     * In terminals this will act similar to using json_encode() with JSON_PRETTY_PRINT directly, when not run on CLI
+     * will also wrap `
` tags around the output of given variable. Similar to pr().
+     *
+     * This function returns the same variable that was passed.
+     *
+     * @param mixed $var Variable to print out.
+     * @return mixed the same $var that was passed to this function
+     * @see pr()
+     * @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pj
+     */
+    function pj($var)
+    {
+        return cakePj($var);
+    }
+}
+
+if (!function_exists('env')) {
+    /**
+     * Gets an environment variable from available sources, and provides emulation
+     * for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on
+     * IIS, or SCRIPT_NAME in CGI mode). Also exposes some additional custom
+     * environment information.
+     *
+     * @param string $key Environment variable name.
+     * @param string|bool|null $default Specify a default value in case the environment variable is not defined.
+     * @return string|bool|null Environment variable setting.
+     * @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#env
+     */
+    function env(string $key, $default = null)
+    {
+        return cakeEnv($key, $default);
+    }
+}
+
+if (!function_exists('triggerWarning')) {
+    /**
+     * Triggers an E_USER_WARNING.
+     *
+     * @param string $message The warning message.
+     * @return void
+     */
+    function triggerWarning(string $message): void
+    {
+        cakeTriggerWarning($message);
+    }
+}
+
+if (!function_exists('deprecationWarning')) {
+    /**
+     * Helper method for outputting deprecation warnings
+     *
+     * @param string $message The message to output as a deprecation warning.
+     * @param int $stackFrame The stack frame to include in the error. Defaults to 1
+     *   as that should point to application/plugin code.
+     * @return void
+     */
+    function deprecationWarning(string $message, int $stackFrame = 1): void
+    {
+        cakeDeprecationWarning($message, $stackFrame + 1);
+    }
+}
+
+if (!function_exists('getTypeName')) {
+    /**
+     * Returns the objects class or var type of it's not an object
+     *
+     * @param mixed $var Variable to check
+     * @return string Returns the class name or variable type
+     */
+    function getTypeName($var): string
+    {
+        return cakeGetTypeName($var);
+    }
+}
diff --git a/vendor/cakephp/database/Connection.php b/vendor/cakephp/database/Connection.php
new file mode 100644
index 0000000..83777d7
--- /dev/null
+++ b/vendor/cakephp/database/Connection.php
@@ -0,0 +1,1197 @@
+
+     */
+    protected $_config;
+
+    /**
+     * @var \Cake\Database\DriverInterface
+     */
+    protected DriverInterface $readDriver;
+
+    /**
+     * @var \Cake\Database\DriverInterface
+     */
+    protected DriverInterface $writeDriver;
+
+    /**
+     * Contains how many nested transactions have been started.
+     *
+     * @var int
+     */
+    protected $_transactionLevel = 0;
+
+    /**
+     * Whether a transaction is active in this connection.
+     *
+     * @var bool
+     */
+    protected $_transactionStarted = false;
+
+    /**
+     * Whether this connection can and should use savepoints for nested
+     * transactions.
+     *
+     * @var bool
+     */
+    protected $_useSavePoints = false;
+
+    /**
+     * Whether to log queries generated during this connection.
+     *
+     * @var bool
+     */
+    protected $_logQueries = false;
+
+    /**
+     * Logger object instance.
+     *
+     * @var \Psr\Log\LoggerInterface|null
+     */
+    protected $_logger;
+
+    /**
+     * Cacher object instance.
+     *
+     * @var \Psr\SimpleCache\CacheInterface|null
+     */
+    protected $cacher;
+
+    /**
+     * The schema collection object
+     *
+     * @var \Cake\Database\Schema\CollectionInterface|null
+     */
+    protected $_schemaCollection;
+
+    /**
+     * NestedTransactionRollbackException object instance, will be stored if
+     * the rollback method is called in some nested transaction.
+     *
+     * @var \Cake\Database\Exception\NestedTransactionRollbackException|null
+     */
+    protected $nestedTransactionRollbackException;
+
+    /**
+     * Constructor.
+     *
+     * ### Available options:
+     *
+     * - `driver` Sort name or FCQN for driver.
+     * - `log` Boolean indicating whether to use query logging.
+     * - `name` Connection name.
+     * - `cacheMetaData` Boolean indicating whether metadata (datasource schemas) should be cached.
+     *    If set to a string it will be used as the name of cache config to use.
+     * - `cacheKeyPrefix` Custom prefix to use when generation cache keys. Defaults to connection name.
+     *
+     * @param array $config Configuration array.
+     */
+    public function __construct(array $config)
+    {
+        $this->_config = $config;
+        [self::ROLE_READ => $this->readDriver, self::ROLE_WRITE => $this->writeDriver] = $this->createDrivers($config);
+
+        if (!empty($config['log'])) {
+            $this->enableQueryLogging((bool)$config['log']);
+        }
+    }
+
+    /**
+     * Creates read and write drivers.
+     *
+     * @param array $config Connection config
+     * @return array
+     * @psalm-return array{read: \Cake\Database\DriverInterface, write: \Cake\Database\DriverInterface}
+     */
+    protected function createDrivers(array $config): array
+    {
+        $driver = $config['driver'] ?? '';
+        if (!is_string($driver)) {
+            /** @var \Cake\Database\DriverInterface $driver */
+            if (!$driver->enabled()) {
+                throw new MissingExtensionException(['driver' => get_class($driver), 'name' => $this->configName()]);
+            }
+
+            // Legacy support for setting instance instead of driver class
+            return [self::ROLE_READ => $driver, self::ROLE_WRITE => $driver];
+        }
+
+        /** @var class-string<\Cake\Database\DriverInterface>|null $driverClass */
+        $driverClass = App::className($driver, 'Database/Driver');
+        if ($driverClass === null) {
+            throw new MissingDriverException(['driver' => $driver, 'connection' => $this->configName()]);
+        }
+
+        $sharedConfig = array_diff_key($config, array_flip([
+            'name',
+            'driver',
+            'log',
+            'cacheMetaData',
+            'cacheKeyPrefix',
+        ]));
+
+        $writeConfig = $config['write'] ?? [] + $sharedConfig;
+        $readConfig = $config['read'] ?? [] + $sharedConfig;
+        if ($readConfig == $writeConfig) {
+            $readDriver = $writeDriver = new $driverClass(['_role' => self::ROLE_WRITE] + $writeConfig);
+        } else {
+            $readDriver = new $driverClass(['_role' => self::ROLE_READ] + $readConfig);
+            $writeDriver = new $driverClass(['_role' => self::ROLE_WRITE] + $writeConfig);
+        }
+
+        if (!$writeDriver->enabled()) {
+            throw new MissingExtensionException(['driver' => get_class($writeDriver), 'name' => $this->configName()]);
+        }
+
+        return [self::ROLE_READ => $readDriver, self::ROLE_WRITE => $writeDriver];
+    }
+
+    /**
+     * Destructor
+     *
+     * Disconnects the driver to release the connection.
+     */
+    public function __destruct()
+    {
+        if ($this->_transactionStarted && class_exists(Log::class)) {
+            Log::warning('The connection is going to be closed but there is an active transaction.');
+        }
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function config(): array
+    {
+        return $this->_config;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function configName(): string
+    {
+        return $this->_config['name'] ?? '';
+    }
+
+    /**
+     * Returns the connection role: read or write.
+     *
+     * @return string
+     */
+    public function role(): string
+    {
+        return preg_match('/:read$/', $this->configName()) === 1 ? static::ROLE_READ : static::ROLE_WRITE;
+    }
+
+    /**
+     * Sets the driver instance. If a string is passed it will be treated
+     * as a class name and will be instantiated.
+     *
+     * @param \Cake\Database\DriverInterface|string $driver The driver instance to use.
+     * @param array $config Config for a new driver.
+     * @throws \Cake\Database\Exception\MissingDriverException When a driver class is missing.
+     * @throws \Cake\Database\Exception\MissingExtensionException When a driver's PHP extension is missing.
+     * @return $this
+     * @deprecated 4.4.0 Setting the driver is deprecated. Use the connection config instead.
+     */
+    public function setDriver($driver, $config = [])
+    {
+        deprecationWarning('Setting the driver is deprecated. Use the connection config instead.');
+
+        $driver = $this->createDriver($driver, $config);
+        $this->readDriver = $this->writeDriver = $driver;
+
+        return $this;
+    }
+
+    /**
+     * Creates driver from name, class name or instance.
+     *
+     * @param \Cake\Database\DriverInterface|string $name Driver name, class name or instance.
+     * @param array $config Driver config if $name is not an instance.
+     * @return \Cake\Database\DriverInterface
+     * @throws \Cake\Database\Exception\MissingDriverException When a driver class is missing.
+     * @throws \Cake\Database\Exception\MissingExtensionException When a driver's PHP extension is missing.
+     */
+    protected function createDriver($name, array $config): DriverInterface
+    {
+        $driver = $name;
+        if (is_string($driver)) {
+            /** @psalm-var class-string<\Cake\Database\DriverInterface>|null $className */
+            $className = App::className($driver, 'Database/Driver');
+            if ($className === null) {
+                throw new MissingDriverException(['driver' => $driver, 'connection' => $this->configName()]);
+            }
+            $driver = new $className(['_role' => self::ROLE_WRITE] + $config);
+        }
+
+        if (!$driver->enabled()) {
+            throw new MissingExtensionException(['driver' => get_class($driver), 'name' => $this->configName()]);
+        }
+
+        return $driver;
+    }
+
+    /**
+     * Get the retry wrapper object that is allows recovery from server disconnects
+     * while performing certain database actions, such as executing a query.
+     *
+     * @return \Cake\Core\Retry\CommandRetry The retry wrapper
+     */
+    public function getDisconnectRetry(): CommandRetry
+    {
+        return new CommandRetry(new ReconnectStrategy($this));
+    }
+
+    /**
+     * Gets the driver instance.
+     *
+     * @param string $role Connection role ('read' or 'write')
+     * @return \Cake\Database\DriverInterface
+     */
+    public function getDriver(string $role = self::ROLE_WRITE): DriverInterface
+    {
+        assert($role === self::ROLE_READ || $role === self::ROLE_WRITE);
+
+        return $role === self::ROLE_READ ? $this->readDriver : $this->writeDriver;
+    }
+
+    /**
+     * Connects to the configured database.
+     *
+     * @throws \Cake\Database\Exception\MissingConnectionException If database connection could not be established.
+     * @return bool true, if the connection was already established or the attempt was successful.
+     * @deprecated 4.5.0 Use getDriver()->connect() instead.
+     */
+    public function connect(): bool
+    {
+        deprecationWarning(
+            'If you cannot use automatic connection management, use $connection->getDriver()->connect() instead.'
+        );
+
+        $connected = true;
+        foreach ([self::ROLE_READ, self::ROLE_WRITE] as $role) {
+            try {
+                $connected = $connected && $this->getDriver($role)->connect();
+            } catch (MissingConnectionException $e) {
+                throw $e;
+            } catch (Throwable $e) {
+                throw new MissingConnectionException(
+                    [
+                        'driver' => App::shortName(get_class($this->getDriver($role)), 'Database/Driver'),
+                        'reason' => $e->getMessage(),
+                    ],
+                    null,
+                    $e
+                );
+            }
+        }
+
+        return $connected;
+    }
+
+    /**
+     * Disconnects from database server.
+     *
+     * @return void
+     * @deprecated 4.5.0 Use getDriver()->disconnect() instead.
+     */
+    public function disconnect(): void
+    {
+        deprecationWarning(
+            'If you cannot use automatic connection management, use $connection->getDriver()->disconnect() instead.'
+        );
+
+        $this->getDriver(self::ROLE_READ)->disconnect();
+        $this->getDriver(self::ROLE_WRITE)->disconnect();
+    }
+
+    /**
+     * Returns whether connection to database server was already established.
+     *
+     * @return bool
+     * @deprecated 4.5.0 Use getDriver()->isConnected() instead.
+     */
+    public function isConnected(): bool
+    {
+        deprecationWarning('Use $connection->getDriver()->isConnected() instead.');
+
+        return $this->getDriver(self::ROLE_READ)->isConnected() && $this->getDriver(self::ROLE_WRITE)->isConnected();
+    }
+
+    /**
+     * Prepares a SQL statement to be executed.
+     *
+     * @param \Cake\Database\Query|string $query The SQL to convert into a prepared statement.
+     * @return \Cake\Database\StatementInterface
+     * @deprecated 4.5.0 Use getDriver()->prepare() instead.
+     */
+    public function prepare($query): StatementInterface
+    {
+        $role = $query instanceof Query ? $query->getConnectionRole() : self::ROLE_WRITE;
+
+        return $this->getDisconnectRetry()->run(function () use ($query, $role) {
+            $statement = $this->getDriver($role)->prepare($query);
+
+            if ($this->_logQueries) {
+                $statement = $this->_newLogger($statement);
+            }
+
+            return $statement;
+        });
+    }
+
+    /**
+     * Executes a query using $params for interpolating values and $types as a hint for each
+     * those params.
+     *
+     * @param string $sql SQL to be executed and interpolated with $params
+     * @param array $params list or associative array of params to be interpolated in $sql as values
+     * @param array $types list or associative array of types to be used for casting values in query
+     * @return \Cake\Database\StatementInterface executed statement
+     */
+    public function execute(string $sql, array $params = [], array $types = []): StatementInterface
+    {
+        return $this->getDisconnectRetry()->run(function () use ($sql, $params, $types) {
+            $statement = $this->prepare($sql);
+            if (!empty($params)) {
+                $statement->bind($params, $types);
+            }
+            $statement->execute();
+
+            return $statement;
+        });
+    }
+
+    /**
+     * Compiles a Query object into a SQL string according to the dialect for this
+     * connection's driver
+     *
+     * @param \Cake\Database\Query $query The query to be compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder
+     * @return string
+     * @deprecated 4.5.0 Use getDriver()->compileQuery() instead.
+     */
+    public function compileQuery(Query $query, ValueBinder $binder): string
+    {
+        deprecationWarning('Use getDriver()->compileQuery() instead.');
+
+        return $this->getDriver($query->getConnectionRole())->compileQuery($query, $binder)[1];
+    }
+
+    /**
+     * Executes the provided query after compiling it for the specific driver
+     * dialect and returns the executed Statement object.
+     *
+     * @param \Cake\Database\Query $query The query to be executed
+     * @return \Cake\Database\StatementInterface executed statement
+     */
+    public function run(Query $query): StatementInterface
+    {
+        return $this->getDisconnectRetry()->run(function () use ($query) {
+            $statement = $this->prepare($query);
+            $query->getValueBinder()->attachTo($statement);
+            $statement->execute();
+
+            return $statement;
+        });
+    }
+
+    /**
+     * Create a new SelectQuery instance for this connection.
+     *
+     * @param \Cake\Database\ExpressionInterface|callable|array|string $fields fields to be added to the list.
+     * @param array|string $table The table or list of tables to query.
+     * @param array $types Associative array containing the types to be used for casting.
+     * @return \Cake\Database\Query\SelectQuery
+     */
+    public function selectQuery(
+        $fields = [],
+        $table = [],
+        array $types = []
+    ): SelectQuery {
+        $query = new SelectQuery($this);
+        if ($table) {
+            $query->from($table);
+        }
+        if ($fields) {
+            $query->select($fields, false);
+        }
+        $query->setDefaultTypes($types);
+
+        return $query;
+    }
+
+    /**
+     * Executes a SQL statement and returns the Statement object as result.
+     *
+     * @param string $sql The SQL query to execute.
+     * @return \Cake\Database\StatementInterface
+     * @deprecated 4.5.0 Use either `selectQuery`, `insertQuery`, `deleteQuery`, `updateQuery` instead.
+     */
+    public function query(string $sql): StatementInterface
+    {
+        deprecationWarning('Use either `selectQuery`, `insertQuery`, `deleteQuery`, `updateQuery` instead.');
+
+        return $this->getDisconnectRetry()->run(function () use ($sql) {
+            $statement = $this->prepare($sql);
+            $statement->execute();
+
+            return $statement;
+        });
+    }
+
+    /**
+     * Create a new Query instance for this connection.
+     *
+     * @return \Cake\Database\Query
+     * @deprecated 4.5.0 Use `insertQuery()`, `deleteQuery()`, `selectQuery()` or `updateQuery()` instead.
+     */
+    public function newQuery(): Query
+    {
+        deprecationWarning(
+            'As of 4.5.0, using newQuery() is deprecated. Instead, use `insertQuery()`, ' .
+            '`deleteQuery()`, `selectQuery()` or `updateQuery()`. The query objects ' .
+            'returned by these methods will emit deprecations that will become fatal errors in 5.0.' .
+            'See https://book.cakephp.org/4/en/appendices/4-5-migration-guide.html for more information.'
+        );
+
+        return new Query($this);
+    }
+
+    /**
+     * Sets a Schema\Collection object for this connection.
+     *
+     * @param \Cake\Database\Schema\CollectionInterface $collection The schema collection object
+     * @return $this
+     */
+    public function setSchemaCollection(SchemaCollectionInterface $collection)
+    {
+        $this->_schemaCollection = $collection;
+
+        return $this;
+    }
+
+    /**
+     * Gets a Schema\Collection object for this connection.
+     *
+     * @return \Cake\Database\Schema\CollectionInterface
+     */
+    public function getSchemaCollection(): SchemaCollectionInterface
+    {
+        if ($this->_schemaCollection !== null) {
+            return $this->_schemaCollection;
+        }
+
+        if (!empty($this->_config['cacheMetadata'])) {
+            return $this->_schemaCollection = new CachedCollection(
+                new SchemaCollection($this),
+                empty($this->_config['cacheKeyPrefix']) ? $this->configName() : $this->_config['cacheKeyPrefix'],
+                $this->getCacher()
+            );
+        }
+
+        return $this->_schemaCollection = new SchemaCollection($this);
+    }
+
+    /**
+     * Executes an INSERT query on the specified table.
+     *
+     * @param string $table the table to insert values in
+     * @param array $values values to be inserted
+     * @param array $types Array containing the types to be used for casting
+     * @return \Cake\Database\StatementInterface
+     */
+    public function insert(string $table, array $values, array $types = []): StatementInterface
+    {
+        return $this->getDisconnectRetry()->run(function () use ($table, $values, $types) {
+            return $this->insertQuery($table, $values, $types)->execute();
+        });
+    }
+
+    /**
+     * Create a new InsertQuery instance for this connection.
+     *
+     * @param string|null $table The table to insert rows into.
+     * @param array $values Associative array of column => value to be inserted.
+     * @param array $types Associative array containing the types to be used for casting.
+     * @return \Cake\Database\Query\InsertQuery
+     */
+    public function insertQuery(?string $table = null, array $values = [], array $types = []): InsertQuery
+    {
+        $query = new InsertQuery($this);
+        if ($table) {
+            $query->into($table);
+        }
+        if ($values) {
+            $columns = array_keys($values);
+            $query->insert($columns, $types)
+                ->values($values);
+        }
+
+        return $query;
+    }
+
+    /**
+     * Executes an UPDATE statement on the specified table.
+     *
+     * @param string $table the table to update rows from
+     * @param array $values values to be updated
+     * @param array $conditions conditions to be set for update statement
+     * @param array $types list of associative array containing the types to be used for casting
+     * @return \Cake\Database\StatementInterface
+     */
+    public function update(string $table, array $values, array $conditions = [], array $types = []): StatementInterface
+    {
+        return $this->getDisconnectRetry()->run(function () use ($table, $values, $conditions, $types) {
+            return $this->updateQuery($table, $values, $conditions, $types)->execute();
+        });
+    }
+
+    /**
+     * Create a new UpdateQuery instance for this connection.
+     *
+     * @param \Cake\Database\ExpressionInterface|string|null $table The table to update rows of.
+     * @param array $values Values to be updated.
+     * @param array $conditions Conditions to be set for the update statement.
+     * @param array $types Associative array containing the types to be used for casting.
+     * @return \Cake\Database\Query\UpdateQuery
+     */
+    public function updateQuery(
+        $table = null,
+        array $values = [],
+        array $conditions = [],
+        array $types = []
+    ): UpdateQuery {
+        $query = new UpdateQuery($this);
+        if ($table) {
+            $query->update($table);
+        }
+        if ($values) {
+            $query->set($values, $types);
+        }
+        if ($conditions) {
+            $query->where($conditions, $types);
+        }
+
+        return $query;
+    }
+
+    /**
+     * Executes a DELETE statement on the specified table.
+     *
+     * @param string $table the table to delete rows from
+     * @param array $conditions conditions to be set for delete statement
+     * @param array $types list of associative array containing the types to be used for casting
+     * @return \Cake\Database\StatementInterface
+     */
+    public function delete(string $table, array $conditions = [], array $types = []): StatementInterface
+    {
+        return $this->getDisconnectRetry()->run(function () use ($table, $conditions, $types) {
+            return $this->deleteQuery($table, $conditions, $types)->execute();
+        });
+    }
+
+    /**
+     * Create a new DeleteQuery instance for this connection.
+     *
+     * @param string|null $table The table to delete rows from.
+     * @param array $conditions Conditions to be set for the delete statement.
+     * @param array $types Associative array containing the types to be used for casting.
+     * @return \Cake\Database\Query\DeleteQuery
+     */
+    public function deleteQuery(?string $table = null, array $conditions = [], array $types = []): DeleteQuery
+    {
+        $query = new DeleteQuery($this);
+        if ($table) {
+            $query->from($table);
+        }
+        if ($conditions) {
+            $query->where($conditions, $types);
+        }
+
+        return $query;
+    }
+
+    /**
+     * Starts a new transaction.
+     *
+     * @return void
+     */
+    public function begin(): void
+    {
+        if (!$this->_transactionStarted) {
+            if ($this->_logQueries) {
+                $this->log('BEGIN');
+            }
+
+            $this->getDisconnectRetry()->run(function (): void {
+                $this->getDriver()->beginTransaction();
+            });
+
+            $this->_transactionLevel = 0;
+            $this->_transactionStarted = true;
+            $this->nestedTransactionRollbackException = null;
+
+            return;
+        }
+
+        $this->_transactionLevel++;
+        if ($this->isSavePointsEnabled()) {
+            $this->createSavePoint((string)$this->_transactionLevel);
+        }
+    }
+
+    /**
+     * Commits current transaction.
+     *
+     * @return bool true on success, false otherwise
+     */
+    public function commit(): bool
+    {
+        if (!$this->_transactionStarted) {
+            return false;
+        }
+
+        if ($this->_transactionLevel === 0) {
+            if ($this->wasNestedTransactionRolledback()) {
+                /** @var \Cake\Database\Exception\NestedTransactionRollbackException $e */
+                $e = $this->nestedTransactionRollbackException;
+                $this->nestedTransactionRollbackException = null;
+                throw $e;
+            }
+
+            $this->_transactionStarted = false;
+            $this->nestedTransactionRollbackException = null;
+            if ($this->_logQueries) {
+                $this->log('COMMIT');
+            }
+
+            return $this->getDriver()->commitTransaction();
+        }
+        if ($this->isSavePointsEnabled()) {
+            $this->releaseSavePoint((string)$this->_transactionLevel);
+        }
+
+        $this->_transactionLevel--;
+
+        return true;
+    }
+
+    /**
+     * Rollback current transaction.
+     *
+     * @param bool|null $toBeginning Whether the transaction should be rolled back to the
+     * beginning of it. Defaults to false if using savepoints, or true if not.
+     * @return bool
+     */
+    public function rollback(?bool $toBeginning = null): bool
+    {
+        if (!$this->_transactionStarted) {
+            return false;
+        }
+
+        $useSavePoint = $this->isSavePointsEnabled();
+        if ($toBeginning === null) {
+            $toBeginning = !$useSavePoint;
+        }
+        if ($this->_transactionLevel === 0 || $toBeginning) {
+            $this->_transactionLevel = 0;
+            $this->_transactionStarted = false;
+            $this->nestedTransactionRollbackException = null;
+            if ($this->_logQueries) {
+                $this->log('ROLLBACK');
+            }
+            $this->getDriver()->rollbackTransaction();
+
+            return true;
+        }
+
+        $savePoint = $this->_transactionLevel--;
+        if ($useSavePoint) {
+            $this->rollbackSavepoint($savePoint);
+        } elseif ($this->nestedTransactionRollbackException === null) {
+            $this->nestedTransactionRollbackException = new NestedTransactionRollbackException();
+        }
+
+        return true;
+    }
+
+    /**
+     * Enables/disables the usage of savepoints, enables only if driver the allows it.
+     *
+     * If you are trying to enable this feature, make sure you check
+     * `isSavePointsEnabled()` to verify that savepoints were enabled successfully.
+     *
+     * @param bool $enable Whether save points should be used.
+     * @return $this
+     */
+    public function enableSavePoints(bool $enable = true)
+    {
+        if ($enable === false) {
+            $this->_useSavePoints = false;
+        } else {
+            $this->_useSavePoints = $this->getDriver()->supports(DriverInterface::FEATURE_SAVEPOINT);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Disables the usage of savepoints.
+     *
+     * @return $this
+     */
+    public function disableSavePoints()
+    {
+        $this->_useSavePoints = false;
+
+        return $this;
+    }
+
+    /**
+     * Returns whether this connection is using savepoints for nested transactions
+     *
+     * @return bool true if enabled, false otherwise
+     */
+    public function isSavePointsEnabled(): bool
+    {
+        return $this->_useSavePoints;
+    }
+
+    /**
+     * Creates a new save point for nested transactions.
+     *
+     * @param string|int $name Save point name or id
+     * @return void
+     */
+    public function createSavePoint($name): void
+    {
+        $this->execute($this->getDriver()->savePointSQL($name))->closeCursor();
+    }
+
+    /**
+     * Releases a save point by its name.
+     *
+     * @param string|int $name Save point name or id
+     * @return void
+     */
+    public function releaseSavePoint($name): void
+    {
+        $sql = $this->getDriver()->releaseSavePointSQL($name);
+        if ($sql) {
+            $this->execute($sql)->closeCursor();
+        }
+    }
+
+    /**
+     * Rollback a save point by its name.
+     *
+     * @param string|int $name Save point name or id
+     * @return void
+     */
+    public function rollbackSavepoint($name): void
+    {
+        $this->execute($this->getDriver()->rollbackSavePointSQL($name))->closeCursor();
+    }
+
+    /**
+     * Run driver specific SQL to disable foreign key checks.
+     *
+     * @return void
+     */
+    public function disableForeignKeys(): void
+    {
+        $this->getDisconnectRetry()->run(function (): void {
+            $this->execute($this->getDriver()->disableForeignKeySQL())->closeCursor();
+        });
+    }
+
+    /**
+     * Run driver specific SQL to enable foreign key checks.
+     *
+     * @return void
+     */
+    public function enableForeignKeys(): void
+    {
+        $this->getDisconnectRetry()->run(function (): void {
+            $this->execute($this->getDriver()->enableForeignKeySQL())->closeCursor();
+        });
+    }
+
+    /**
+     * Returns whether the driver supports adding or dropping constraints
+     * to already created tables.
+     *
+     * @return bool true if driver supports dynamic constraints
+     * @deprecated 4.3.0 Fixtures no longer dynamically drop and create constraints.
+     */
+    public function supportsDynamicConstraints(): bool
+    {
+        return $this->getDriver()->supportsDynamicConstraints();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function transactional(callable $callback)
+    {
+        $this->begin();
+
+        try {
+            $result = $callback($this);
+        } catch (Throwable $e) {
+            $this->rollback(false);
+            throw $e;
+        }
+
+        if ($result === false) {
+            $this->rollback(false);
+
+            return false;
+        }
+
+        try {
+            $this->commit();
+        } catch (NestedTransactionRollbackException $e) {
+            $this->rollback(false);
+            throw $e;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns whether some nested transaction has been already rolled back.
+     *
+     * @return bool
+     */
+    protected function wasNestedTransactionRolledback(): bool
+    {
+        return $this->nestedTransactionRollbackException instanceof NestedTransactionRollbackException;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function disableConstraints(callable $callback)
+    {
+        return $this->getDisconnectRetry()->run(function () use ($callback) {
+            $this->disableForeignKeys();
+
+            try {
+                $result = $callback($this);
+            } finally {
+                $this->enableForeignKeys();
+            }
+
+            return $result;
+        });
+    }
+
+    /**
+     * Checks if a transaction is running.
+     *
+     * @return bool True if a transaction is running else false.
+     */
+    public function inTransaction(): bool
+    {
+        return $this->_transactionStarted;
+    }
+
+    /**
+     * Quotes value to be used safely in database query.
+     *
+     * This uses `PDO::quote()` and requires `supportsQuoting()` to work.
+     *
+     * @param mixed $value The value to quote.
+     * @param \Cake\Database\TypeInterface|string|int $type Type to be used for determining kind of quoting to perform
+     * @return string Quoted value
+     * @deprecated 4.5.0 Use getDriver()->quote() instead.
+     */
+    public function quote($value, $type = 'string'): string
+    {
+        deprecationWarning('Use getDriver()->quote() instead.');
+        [$value, $type] = $this->cast($value, $type);
+
+        return $this->getDriver()->quote($value, $type);
+    }
+
+    /**
+     * Checks if using `quote()` is supported.
+     *
+     * This is not required to use `quoteIdentifier()`.
+     *
+     * @return bool
+     * @deprecated 4.5.0 Use getDriver()->supportsQuoting() instead.
+     */
+    public function supportsQuoting(): bool
+    {
+        deprecationWarning('Use getDriver()->supportsQuoting() instead.');
+
+        return $this->getDriver()->supports(DriverInterface::FEATURE_QUOTE);
+    }
+
+    /**
+     * Quotes a database identifier (a column name, table name, etc..) to
+     * be used safely in queries without the risk of using reserved words.
+     *
+     * This does not require `supportsQuoting()` to work.
+     *
+     * @param string $identifier The identifier to quote.
+     * @return string
+     * @deprecated 4.5.0 Use getDriver()->quoteIdentifier() instead.
+     */
+    public function quoteIdentifier(string $identifier): string
+    {
+        deprecationWarning('Use getDriver()->quoteIdentifier() instead.');
+
+        return $this->getDriver()->quoteIdentifier($identifier);
+    }
+
+    /**
+     * Enables or disables metadata caching for this connection
+     *
+     * Changing this setting will not modify existing schema collections objects.
+     *
+     * @param string|bool $cache Either boolean false to disable metadata caching, or
+     *   true to use `_cake_model_` or the name of the cache config to use.
+     * @return void
+     */
+    public function cacheMetadata($cache): void
+    {
+        $this->_schemaCollection = null;
+        $this->_config['cacheMetadata'] = $cache;
+        if (is_string($cache)) {
+            $this->cacher = null;
+        }
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function setCacher(CacheInterface $cacher)
+    {
+        $this->cacher = $cacher;
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getCacher(): CacheInterface
+    {
+        if ($this->cacher !== null) {
+            return $this->cacher;
+        }
+
+        $configName = $this->_config['cacheMetadata'] ?? '_cake_model_';
+        if (!is_string($configName)) {
+            $configName = '_cake_model_';
+        }
+
+        if (!class_exists(Cache::class)) {
+            throw new RuntimeException(
+                'To use caching you must either set a cacher using Connection::setCacher()' .
+                ' or require the cakephp/cache package in your composer config.'
+            );
+        }
+
+        return $this->cacher = Cache::pool($configName);
+    }
+
+    /**
+     * Enable/disable query logging
+     *
+     * @param bool $enable Enable/disable query logging
+     * @return $this
+     * @deprecated 4.5.0 Connection logging is moving to the driver in 5.x
+     */
+    public function enableQueryLogging(bool $enable = true)
+    {
+        $this->_logQueries = $enable;
+
+        return $this;
+    }
+
+    /**
+     * Disable query logging
+     *
+     * @return $this
+     * @deprecated 4.5.0 Connection logging is moving to the driver in 5.x
+     */
+    public function disableQueryLogging()
+    {
+        $this->_logQueries = false;
+
+        return $this;
+    }
+
+    /**
+     * Check if query logging is enabled.
+     *
+     * @return bool
+     * @deprecated 4.5.0 Connection logging is moving to the driver in 5.x
+     */
+    public function isQueryLoggingEnabled(): bool
+    {
+        return $this->_logQueries;
+    }
+
+    /**
+     * Sets a logger
+     *
+     * @param \Psr\Log\LoggerInterface $logger Logger object
+     * @return $this
+     * @psalm-suppress ImplementedReturnTypeMismatch
+     */
+    public function setLogger(LoggerInterface $logger)
+    {
+        $this->_logger = $logger;
+
+        return $this;
+    }
+
+    /**
+     * Gets the logger object
+     *
+     * @return \Psr\Log\LoggerInterface logger instance
+     */
+    public function getLogger(): LoggerInterface
+    {
+        if ($this->_logger !== null) {
+            return $this->_logger;
+        }
+
+        if (!class_exists(BaseLog::class)) {
+            throw new RuntimeException(
+                'For logging you must either set a logger using Connection::setLogger()' .
+                ' or require the cakephp/log package in your composer config.'
+            );
+        }
+
+        return $this->_logger = new QueryLogger(['connection' => $this->configName()]);
+    }
+
+    /**
+     * Logs a Query string using the configured logger object.
+     *
+     * @param string $sql string to be logged
+     * @return void
+     */
+    public function log(string $sql): void
+    {
+        $query = new LoggedQuery();
+        $query->query = $sql;
+        $this->getLogger()->debug((string)$query, ['query' => $query]);
+    }
+
+    /**
+     * Returns a new statement object that will log the activity
+     * for the passed original statement instance.
+     *
+     * @param \Cake\Database\StatementInterface $statement the instance to be decorated
+     * @return \Cake\Database\Log\LoggingStatement
+     */
+    protected function _newLogger(StatementInterface $statement): LoggingStatement
+    {
+        $log = new LoggingStatement($statement, $this->getDriver());
+        $log->setLogger($this->getLogger());
+
+        return $log;
+    }
+
+    /**
+     * Returns an array that can be used to describe the internal state of this
+     * object.
+     *
+     * @return array
+     */
+    public function __debugInfo(): array
+    {
+        $secrets = [
+            'password' => '*****',
+            'username' => '*****',
+            'host' => '*****',
+            'database' => '*****',
+            'port' => '*****',
+        ];
+        $replace = array_intersect_key($secrets, $this->_config);
+        $config = $replace + $this->_config;
+
+        if (isset($config['read'])) {
+            /** @psalm-suppress PossiblyInvalidArgument */
+            $config['read'] = array_intersect_key($secrets, $config['read']) + $config['read'];
+        }
+        if (isset($config['write'])) {
+            /** @psalm-suppress PossiblyInvalidArgument */
+            $config['write'] = array_intersect_key($secrets, $config['write']) + $config['write'];
+        }
+
+        return [
+            'config' => $config,
+            'readDriver' => $this->readDriver,
+            'writeDriver' => $this->writeDriver,
+            'transactionLevel' => $this->_transactionLevel,
+            'transactionStarted' => $this->_transactionStarted,
+            'useSavePoints' => $this->_useSavePoints,
+            'logQueries' => $this->_logQueries,
+            'logger' => $this->_logger,
+        ];
+    }
+}
diff --git a/vendor/cakephp/database/ConstraintsInterface.php b/vendor/cakephp/database/ConstraintsInterface.php
new file mode 100644
index 0000000..f1fe3c1
--- /dev/null
+++ b/vendor/cakephp/database/ConstraintsInterface.php
@@ -0,0 +1,49 @@
+  DB-specific error codes that allow connect retry
+     */
+    protected const RETRY_ERROR_CODES = [];
+
+    /**
+     * Instance of PDO.
+     *
+     * @var \PDO
+     */
+    protected $_connection;
+
+    /**
+     * Configuration data.
+     *
+     * @var array
+     */
+    protected $_config;
+
+    /**
+     * Base configuration that is merged into the user
+     * supplied configuration data.
+     *
+     * @var array
+     */
+    protected $_baseConfig = [];
+
+    /**
+     * Indicates whether the driver is doing automatic identifier quoting
+     * for all queries
+     *
+     * @var bool
+     */
+    protected $_autoQuoting = false;
+
+    /**
+     * The server version
+     *
+     * @var string|null
+     */
+    protected $_version;
+
+    /**
+     * The last number of connection retry attempts.
+     *
+     * @var int
+     */
+    protected $connectRetries = 0;
+
+    /**
+     * Constructor
+     *
+     * @param array $config The configuration for the driver.
+     * @throws \InvalidArgumentException
+     */
+    public function __construct(array $config = [])
+    {
+        if (empty($config['username']) && !empty($config['login'])) {
+            throw new InvalidArgumentException(
+                'Please pass "username" instead of "login" for connecting to the database'
+            );
+        }
+        $config += $this->_baseConfig;
+        $this->_config = $config;
+        if (!empty($config['quoteIdentifiers'])) {
+            $this->enableAutoQuoting();
+        }
+    }
+
+    /**
+     * Get the configuration data used to create the driver.
+     *
+     * @return array
+     */
+    public function config(): array
+    {
+        return $this->_config;
+    }
+
+    /**
+     * Establishes a connection to the database server
+     *
+     * @param string $dsn A Driver-specific PDO-DSN
+     * @param array $config configuration to be used for creating connection
+     * @return bool true on success
+     */
+    protected function _connect(string $dsn, array $config): bool
+    {
+        $action = function () use ($dsn, $config) {
+            $this->setConnection(new PDO(
+                $dsn,
+                $config['username'] ?: null,
+                $config['password'] ?: null,
+                $config['flags']
+            ));
+        };
+
+        $retry = new CommandRetry(new ErrorCodeWaitStrategy(static::RETRY_ERROR_CODES, 5), 4);
+        try {
+            $retry->run($action);
+        } catch (PDOException $e) {
+            throw new MissingConnectionException(
+                [
+                    'driver' => App::shortName(static::class, 'Database/Driver'),
+                    'reason' => $e->getMessage(),
+                ],
+                null,
+                $e
+            );
+        } finally {
+            $this->connectRetries = $retry->getRetries();
+        }
+
+        return true;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    abstract public function connect(): bool;
+
+    /**
+     * @inheritDoc
+     */
+    public function disconnect(): void
+    {
+        /** @psalm-suppress PossiblyNullPropertyAssignmentValue */
+        $this->_connection = null;
+        $this->_version = null;
+    }
+
+    /**
+     * Returns connected server version.
+     *
+     * @return string
+     */
+    public function version(): string
+    {
+        if ($this->_version === null) {
+            $this->connect();
+            $this->_version = (string)$this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION);
+        }
+
+        return $this->_version;
+    }
+
+    /**
+     * Get the internal PDO connection instance.
+     *
+     * @return \PDO
+     */
+    public function getConnection()
+    {
+        if ($this->_connection === null) {
+            throw new MissingConnectionException([
+                'driver' => App::shortName(static::class, 'Database/Driver'),
+                'reason' => 'Unknown',
+            ]);
+        }
+
+        return $this->_connection;
+    }
+
+    /**
+     * Set the internal PDO connection instance.
+     *
+     * @param \PDO $connection PDO instance.
+     * @return $this
+     * @psalm-suppress MoreSpecificImplementedParamType
+     */
+    public function setConnection($connection)
+    {
+        $this->_connection = $connection;
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    abstract public function enabled(): bool;
+
+    /**
+     * @inheritDoc
+     */
+    public function prepare($query): StatementInterface
+    {
+        $this->connect();
+        $statement = $this->_connection->prepare($query instanceof Query ? $query->sql() : $query);
+
+        return new PDOStatement($statement, $this);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function beginTransaction(): bool
+    {
+        $this->connect();
+        if ($this->_connection->inTransaction()) {
+            return true;
+        }
+
+        return $this->_connection->beginTransaction();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function commitTransaction(): bool
+    {
+        $this->connect();
+        if (!$this->_connection->inTransaction()) {
+            return false;
+        }
+
+        return $this->_connection->commit();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function rollbackTransaction(): bool
+    {
+        $this->connect();
+        if (!$this->_connection->inTransaction()) {
+            return false;
+        }
+
+        return $this->_connection->rollBack();
+    }
+
+    /**
+     * Returns whether a transaction is active for connection.
+     *
+     * @return bool
+     */
+    public function inTransaction(): bool
+    {
+        $this->connect();
+
+        return $this->_connection->inTransaction();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function supportsSavePoints(): bool
+    {
+        deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
+
+        return $this->supports(static::FEATURE_SAVEPOINT);
+    }
+
+    /**
+     * Returns true if the server supports common table expressions.
+     *
+     * @return bool
+     * @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_QUOTE)` instead
+     */
+    public function supportsCTEs(): bool
+    {
+        deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
+
+        return $this->supports(static::FEATURE_CTE);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function quote($value, $type = PDO::PARAM_STR): string
+    {
+        $this->connect();
+
+        return $this->_connection->quote((string)$value, $type);
+    }
+
+    /**
+     * Checks if the driver supports quoting, as PDO_ODBC does not support it.
+     *
+     * @return bool
+     * @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_QUOTE)` instead
+     */
+    public function supportsQuoting(): bool
+    {
+        deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
+
+        return $this->supports(static::FEATURE_QUOTE);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    abstract public function queryTranslator(string $type): Closure;
+
+    /**
+     * @inheritDoc
+     */
+    abstract public function schemaDialect(): SchemaDialect;
+
+    /**
+     * @inheritDoc
+     */
+    abstract public function quoteIdentifier(string $identifier): string;
+
+    /**
+     * @inheritDoc
+     */
+    public function schemaValue($value): string
+    {
+        if ($value === null) {
+            return 'NULL';
+        }
+        if ($value === false) {
+            return 'FALSE';
+        }
+        if ($value === true) {
+            return 'TRUE';
+        }
+        if (is_float($value)) {
+            return str_replace(',', '.', (string)$value);
+        }
+        /** @psalm-suppress InvalidArgument */
+        if (
+            (
+                is_int($value) ||
+                $value === '0'
+            ) ||
+            (
+                is_numeric($value) &&
+                strpos($value, ',') === false &&
+                substr($value, 0, 1) !== '0' &&
+                strpos($value, 'e') === false
+            )
+        ) {
+            return (string)$value;
+        }
+
+        return $this->_connection->quote((string)$value, PDO::PARAM_STR);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function schema(): string
+    {
+        return $this->_config['schema'];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function lastInsertId(?string $table = null, ?string $column = null)
+    {
+        $this->connect();
+
+        if ($this->_connection instanceof PDO) {
+            return $this->_connection->lastInsertId($table);
+        }
+
+        return $this->_connection->lastInsertId($table);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function isConnected(): bool
+    {
+        if ($this->_connection === null) {
+            $connected = false;
+        } else {
+            try {
+                $connected = (bool)$this->_connection->query('SELECT 1');
+            } catch (PDOException $e) {
+                $connected = false;
+            }
+        }
+
+        return $connected;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function enableAutoQuoting(bool $enable = true)
+    {
+        $this->_autoQuoting = $enable;
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function disableAutoQuoting()
+    {
+        $this->_autoQuoting = false;
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function isAutoQuotingEnabled(): bool
+    {
+        return $this->_autoQuoting;
+    }
+
+    /**
+     * Returns whether the driver supports the feature.
+     *
+     * Defaults to true for FEATURE_QUOTE and FEATURE_SAVEPOINT.
+     *
+     * @param string $feature Driver feature name
+     * @return bool
+     */
+    public function supports(string $feature): bool
+    {
+        switch ($feature) {
+            case static::FEATURE_DISABLE_CONSTRAINT_WITHOUT_TRANSACTION:
+            case static::FEATURE_QUOTE:
+            case static::FEATURE_SAVEPOINT:
+                return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function compileQuery(Query $query, ValueBinder $binder): array
+    {
+        $processor = $this->newCompiler();
+        $translator = $this->queryTranslator($query->type());
+        $query = $translator($query);
+
+        return [$query, $processor->compile($query, $binder)];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function newCompiler(): QueryCompiler
+    {
+        return new QueryCompiler();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function newTableSchema(string $table, array $columns = []): TableSchema
+    {
+        $className = TableSchema::class;
+        if (isset($this->_config['tableSchema'])) {
+            /** @var class-string<\Cake\Database\Schema\TableSchema> $className */
+            $className = $this->_config['tableSchema'];
+        }
+
+        return new $className($table, $columns);
+    }
+
+    /**
+     * Returns the maximum alias length allowed.
+     * This can be different from the maximum identifier length for columns.
+     *
+     * @return int|null Maximum alias length or null if no limit
+     */
+    public function getMaxAliasLength(): ?int
+    {
+        return static::MAX_ALIAS_LENGTH;
+    }
+
+    /**
+     * Returns the number of connection retry attempts made.
+     *
+     * @return int
+     */
+    public function getConnectRetries(): int
+    {
+        return $this->connectRetries;
+    }
+
+    /**
+     * Returns the connection role this driver performs.
+     *
+     * @return string
+     */
+    public function getRole(): string
+    {
+        return $this->_config['_role'] ?? Connection::ROLE_WRITE;
+    }
+
+    /**
+     * Destructor
+     */
+    public function __destruct()
+    {
+        /** @psalm-suppress PossiblyNullPropertyAssignmentValue */
+        $this->_connection = null;
+    }
+
+    /**
+     * Returns an array that can be used to describe the internal state of this
+     * object.
+     *
+     * @return array
+     */
+    public function __debugInfo(): array
+    {
+        return [
+            'connected' => $this->_connection !== null,
+            'role' => $this->getRole(),
+        ];
+    }
+}
diff --git a/vendor/cakephp/database/Driver/Mysql.php b/vendor/cakephp/database/Driver/Mysql.php
new file mode 100644
index 0000000..b7d30b6
--- /dev/null
+++ b/vendor/cakephp/database/Driver/Mysql.php
@@ -0,0 +1,345 @@
+
+     */
+    protected $_baseConfig = [
+        'persistent' => true,
+        'host' => 'localhost',
+        'username' => 'root',
+        'password' => '',
+        'database' => 'cake',
+        'port' => '3306',
+        'flags' => [],
+        'encoding' => 'utf8mb4',
+        'timezone' => null,
+        'init' => [],
+    ];
+
+    /**
+     * The schema dialect for this driver
+     *
+     * @var \Cake\Database\Schema\MysqlSchemaDialect|null
+     */
+    protected $_schemaDialect;
+
+    /**
+     * String used to start a database identifier quoting to make it safe
+     *
+     * @var string
+     */
+    protected $_startQuote = '`';
+
+    /**
+     * String used to end a database identifier quoting to make it safe
+     *
+     * @var string
+     */
+    protected $_endQuote = '`';
+
+    /**
+     * Server type.
+     *
+     * If the underlying server is MariaDB, its value will get set to `'mariadb'`
+     * after `version()` method is called.
+     *
+     * @var string
+     */
+    protected $serverType = self::SERVER_TYPE_MYSQL;
+
+    /**
+     * Mapping of feature to db server version for feature availability checks.
+     *
+     * @var array>
+     */
+    protected $featureVersions = [
+        'mysql' => [
+            'json' => '5.7.0',
+            'cte' => '8.0.0',
+            'window' => '8.0.0',
+        ],
+        'mariadb' => [
+            'json' => '10.2.7',
+            'cte' => '10.2.1',
+            'window' => '10.2.0',
+        ],
+    ];
+
+    /**
+     * Establishes a connection to the database server
+     *
+     * @return bool true on success
+     */
+    public function connect(): bool
+    {
+        if ($this->_connection) {
+            return true;
+        }
+        $config = $this->_config;
+
+        if ($config['timezone'] === 'UTC') {
+            $config['timezone'] = '+0:00';
+        }
+
+        if (!empty($config['timezone'])) {
+            $config['init'][] = sprintf("SET time_zone = '%s'", $config['timezone']);
+        }
+
+        $config['flags'] += [
+            PDO::ATTR_PERSISTENT => $config['persistent'],
+            PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
+            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+        ];
+
+        if (!empty($config['ssl_key']) && !empty($config['ssl_cert'])) {
+            $config['flags'][PDO::MYSQL_ATTR_SSL_KEY] = $config['ssl_key'];
+            $config['flags'][PDO::MYSQL_ATTR_SSL_CERT] = $config['ssl_cert'];
+        }
+        if (!empty($config['ssl_ca'])) {
+            $config['flags'][PDO::MYSQL_ATTR_SSL_CA] = $config['ssl_ca'];
+        }
+
+        if (empty($config['unix_socket'])) {
+            $dsn = "mysql:host={$config['host']};port={$config['port']};dbname={$config['database']}";
+        } else {
+            $dsn = "mysql:unix_socket={$config['unix_socket']};dbname={$config['database']}";
+        }
+
+        if (!empty($config['encoding'])) {
+            $dsn .= ";charset={$config['encoding']}";
+        }
+
+        $this->_connect($dsn, $config);
+
+        if (!empty($config['init'])) {
+            $connection = $this->getConnection();
+            foreach ((array)$config['init'] as $command) {
+                $connection->exec($command);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns whether php is able to use this driver for connecting to database
+     *
+     * @return bool true if it is valid to use this driver
+     */
+    public function enabled(): bool
+    {
+        return in_array('mysql', PDO::getAvailableDrivers(), true);
+    }
+
+    /**
+     * Prepares a sql statement to be executed
+     *
+     * @param \Cake\Database\Query|string $query The query to prepare.
+     * @return \Cake\Database\StatementInterface
+     */
+    public function prepare($query): StatementInterface
+    {
+        $this->connect();
+        $isObject = $query instanceof Query;
+        /**
+         * @psalm-suppress PossiblyInvalidMethodCall
+         * @psalm-suppress PossiblyInvalidArgument
+         */
+        $statement = $this->_connection->prepare($isObject ? $query->sql() : $query);
+        $result = new MysqlStatement($statement, $this);
+        /** @psalm-suppress PossiblyInvalidMethodCall */
+        if ($isObject && $query->isBufferedResultsEnabled() === false) {
+            $result->bufferResults(false);
+        }
+
+        return $result;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function schemaDialect(): SchemaDialect
+    {
+        if ($this->_schemaDialect === null) {
+            $this->_schemaDialect = new MysqlSchemaDialect($this);
+        }
+
+        return $this->_schemaDialect;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function schema(): string
+    {
+        return $this->_config['database'];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function disableForeignKeySQL(): string
+    {
+        return 'SET foreign_key_checks = 0';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function enableForeignKeySQL(): string
+    {
+        return 'SET foreign_key_checks = 1';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function supports(string $feature): bool
+    {
+        switch ($feature) {
+            case static::FEATURE_CTE:
+            case static::FEATURE_JSON:
+            case static::FEATURE_WINDOW:
+                return version_compare(
+                    $this->version(),
+                    $this->featureVersions[$this->serverType][$feature],
+                    '>='
+                );
+        }
+
+        return parent::supports($feature);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function supportsDynamicConstraints(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Returns true if the connected server is MariaDB.
+     *
+     * @return bool
+     */
+    public function isMariadb(): bool
+    {
+        $this->version();
+
+        return $this->serverType === static::SERVER_TYPE_MARIADB;
+    }
+
+    /**
+     * Returns connected server version.
+     *
+     * @return string
+     */
+    public function version(): string
+    {
+        if ($this->_version === null) {
+            $this->connect();
+            $this->_version = (string)$this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION);
+
+            if (strpos($this->_version, 'MariaDB') !== false) {
+                $this->serverType = static::SERVER_TYPE_MARIADB;
+                preg_match('/^(?:5\.5\.5-)?(\d+\.\d+\.\d+.*-MariaDB[^:]*)/', $this->_version, $matches);
+                $this->_version = $matches[1];
+            }
+        }
+
+        return $this->_version;
+    }
+
+    /**
+     * Returns true if the server supports common table expressions.
+     *
+     * @return bool
+     * @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_CTE)` instead
+     */
+    public function supportsCTEs(): bool
+    {
+        deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
+
+        return $this->supports(static::FEATURE_CTE);
+    }
+
+    /**
+     * Returns true if the server supports native JSON columns
+     *
+     * @return bool
+     * @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_JSON)` instead
+     */
+    public function supportsNativeJson(): bool
+    {
+        deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
+
+        return $this->supports(static::FEATURE_JSON);
+    }
+
+    /**
+     * Returns true if the connected server supports window functions.
+     *
+     * @return bool
+     * @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_WINDOW)` instead
+     */
+    public function supportsWindowFunctions(): bool
+    {
+        deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
+
+        return $this->supports(static::FEATURE_WINDOW);
+    }
+}
diff --git a/vendor/cakephp/database/Driver/Postgres.php b/vendor/cakephp/database/Driver/Postgres.php
new file mode 100644
index 0000000..2a32264
--- /dev/null
+++ b/vendor/cakephp/database/Driver/Postgres.php
@@ -0,0 +1,348 @@
+
+     */
+    protected $_baseConfig = [
+        'persistent' => true,
+        'host' => 'localhost',
+        'username' => 'root',
+        'password' => '',
+        'database' => 'cake',
+        'schema' => 'public',
+        'port' => 5432,
+        'encoding' => 'utf8',
+        'timezone' => null,
+        'flags' => [],
+        'init' => [],
+    ];
+
+    /**
+     * The schema dialect class for this driver
+     *
+     * @var \Cake\Database\Schema\PostgresSchemaDialect|null
+     */
+    protected $_schemaDialect;
+
+    /**
+     * String used to start a database identifier quoting to make it safe
+     *
+     * @var string
+     */
+    protected $_startQuote = '"';
+
+    /**
+     * String used to end a database identifier quoting to make it safe
+     *
+     * @var string
+     */
+    protected $_endQuote = '"';
+
+    /**
+     * Establishes a connection to the database server
+     *
+     * @return bool true on success
+     */
+    public function connect(): bool
+    {
+        if ($this->_connection) {
+            return true;
+        }
+        $config = $this->_config;
+        $config['flags'] += [
+            PDO::ATTR_PERSISTENT => $config['persistent'],
+            PDO::ATTR_EMULATE_PREPARES => false,
+            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+        ];
+        if (empty($config['unix_socket'])) {
+            $dsn = "pgsql:host={$config['host']};port={$config['port']};dbname={$config['database']}";
+        } else {
+            $dsn = "pgsql:dbname={$config['database']}";
+        }
+
+        $this->_connect($dsn, $config);
+        $this->_connection = $connection = $this->getConnection();
+        if (!empty($config['encoding'])) {
+            $this->setEncoding($config['encoding']);
+        }
+
+        if (!empty($config['schema'])) {
+            $this->setSchema($config['schema']);
+        }
+
+        if (!empty($config['timezone'])) {
+            $config['init'][] = sprintf('SET timezone = %s', $connection->quote($config['timezone']));
+        }
+
+        foreach ($config['init'] as $command) {
+            $connection->exec($command);
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns whether php is able to use this driver for connecting to database
+     *
+     * @return bool true if it is valid to use this driver
+     */
+    public function enabled(): bool
+    {
+        return in_array('pgsql', PDO::getAvailableDrivers(), true);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function schemaDialect(): SchemaDialect
+    {
+        if ($this->_schemaDialect === null) {
+            $this->_schemaDialect = new PostgresSchemaDialect($this);
+        }
+
+        return $this->_schemaDialect;
+    }
+
+    /**
+     * Sets connection encoding
+     *
+     * @param string $encoding The encoding to use.
+     * @return void
+     */
+    public function setEncoding(string $encoding): void
+    {
+        $this->connect();
+        $this->_connection->exec('SET NAMES ' . $this->_connection->quote($encoding));
+    }
+
+    /**
+     * Sets connection default schema, if any relation defined in a query is not fully qualified
+     * postgres will fallback to looking the relation into defined default schema
+     *
+     * @param string $schema The schema names to set `search_path` to.
+     * @return void
+     */
+    public function setSchema(string $schema): void
+    {
+        $this->connect();
+        $this->_connection->exec('SET search_path TO ' . $this->_connection->quote($schema));
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function disableForeignKeySQL(): string
+    {
+        return 'SET CONSTRAINTS ALL DEFERRED';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function enableForeignKeySQL(): string
+    {
+        return 'SET CONSTRAINTS ALL IMMEDIATE';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function supports(string $feature): bool
+    {
+        switch ($feature) {
+            case static::FEATURE_CTE:
+            case static::FEATURE_JSON:
+            case static::FEATURE_TRUNCATE_WITH_CONSTRAINTS:
+            case static::FEATURE_WINDOW:
+                return true;
+
+            case static::FEATURE_DISABLE_CONSTRAINT_WITHOUT_TRANSACTION:
+                return false;
+        }
+
+        return parent::supports($feature);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function supportsDynamicConstraints(): bool
+    {
+        return true;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function _transformDistinct(Query $query): Query
+    {
+        return $query;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function _insertQueryTranslator(Query $query): Query
+    {
+        if (!$query->clause('epilog')) {
+            $query->epilog('RETURNING *');
+        }
+
+        return $query;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function _expressionTranslators(): array
+    {
+        return [
+            IdentifierExpression::class => '_transformIdentifierExpression',
+            FunctionExpression::class => '_transformFunctionExpression',
+            StringExpression::class => '_transformStringExpression',
+        ];
+    }
+
+    /**
+     * Changes identifer expression into postgresql format.
+     *
+     * @param \Cake\Database\Expression\IdentifierExpression $expression The expression to tranform.
+     * @return void
+     */
+    protected function _transformIdentifierExpression(IdentifierExpression $expression): void
+    {
+        $collation = $expression->getCollation();
+        if ($collation) {
+            // use trim() to work around expression being transformed multiple times
+            $expression->setCollation('"' . trim($collation, '"') . '"');
+        }
+    }
+
+    /**
+     * Receives a FunctionExpression and changes it so that it conforms to this
+     * SQL dialect.
+     *
+     * @param \Cake\Database\Expression\FunctionExpression $expression The function expression to convert
+     *   to postgres SQL.
+     * @return void
+     */
+    protected function _transformFunctionExpression(FunctionExpression $expression): void
+    {
+        switch ($expression->getName()) {
+            case 'CONCAT':
+                // CONCAT function is expressed as exp1 || exp2
+                $expression->setName('')->setConjunction(' ||');
+                break;
+            case 'DATEDIFF':
+                $expression
+                    ->setName('')
+                    ->setConjunction('-')
+                    ->iterateParts(function ($p) {
+                        if (is_string($p)) {
+                            $p = ['value' => [$p => 'literal'], 'type' => null];
+                        } else {
+                            $p['value'] = [$p['value']];
+                        }
+
+                        return new FunctionExpression('DATE', $p['value'], [$p['type']]);
+                    });
+                break;
+            case 'CURRENT_DATE':
+                $time = new FunctionExpression('LOCALTIMESTAMP', [' 0 ' => 'literal']);
+                $expression->setName('CAST')->setConjunction(' AS ')->add([$time, 'date' => 'literal']);
+                break;
+            case 'CURRENT_TIME':
+                $time = new FunctionExpression('LOCALTIMESTAMP', [' 0 ' => 'literal']);
+                $expression->setName('CAST')->setConjunction(' AS ')->add([$time, 'time' => 'literal']);
+                break;
+            case 'NOW':
+                $expression->setName('LOCALTIMESTAMP')->add([' 0 ' => 'literal']);
+                break;
+            case 'RAND':
+                $expression->setName('RANDOM');
+                break;
+            case 'DATE_ADD':
+                $expression
+                    ->setName('')
+                    ->setConjunction(' + INTERVAL')
+                    ->iterateParts(function ($p, $key) {
+                        if ($key === 1) {
+                            $p = sprintf("'%s'", $p);
+                        }
+
+                        return $p;
+                    });
+                break;
+            case 'DAYOFWEEK':
+                $expression
+                    ->setName('EXTRACT')
+                    ->setConjunction(' ')
+                    ->add(['DOW FROM' => 'literal'], [], true)
+                    ->add([') + (1' => 'literal']); // Postgres starts on index 0 but Sunday should be 1
+                break;
+        }
+    }
+
+    /**
+     * Changes string expression into postgresql format.
+     *
+     * @param \Cake\Database\Expression\StringExpression $expression The string expression to tranform.
+     * @return void
+     */
+    protected function _transformStringExpression(StringExpression $expression): void
+    {
+        // use trim() to work around expression being transformed multiple times
+        $expression->setCollation('"' . trim($expression->getCollation(), '"') . '"');
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return \Cake\Database\PostgresCompiler
+     */
+    public function newCompiler(): QueryCompiler
+    {
+        return new PostgresCompiler();
+    }
+}
diff --git a/vendor/cakephp/database/Driver/SqlDialectTrait.php b/vendor/cakephp/database/Driver/SqlDialectTrait.php
new file mode 100644
index 0000000..94a5e88
--- /dev/null
+++ b/vendor/cakephp/database/Driver/SqlDialectTrait.php
@@ -0,0 +1,315 @@
+_startQuote . $identifier . $this->_endQuote;
+        }
+
+        // string.string
+        if (preg_match('/^[\w-]+\.[^ \*]*$/u', $identifier)) {
+            $items = explode('.', $identifier);
+
+            return $this->_startQuote . implode($this->_endQuote . '.' . $this->_startQuote, $items) . $this->_endQuote;
+        }
+
+        // string.*
+        if (preg_match('/^[\w-]+\.\*$/u', $identifier)) {
+            return $this->_startQuote . str_replace('.*', $this->_endQuote . '.*', $identifier);
+        }
+
+        // Functions
+        if (preg_match('/^([\w-]+)\((.*)\)$/', $identifier, $matches)) {
+            return $matches[1] . '(' . $this->quoteIdentifier($matches[2]) . ')';
+        }
+
+        // Alias.field AS thing
+        if (preg_match('/^([\w-]+(\.[\w\s-]+|\(.*\))*)\s+AS\s*([\w-]+)$/ui', $identifier, $matches)) {
+            return $this->quoteIdentifier($matches[1]) . ' AS ' . $this->quoteIdentifier($matches[3]);
+        }
+
+        // string.string with spaces
+        if (preg_match('/^([\w-]+\.[\w][\w\s-]*[\w])(.*)/u', $identifier, $matches)) {
+            $items = explode('.', $matches[1]);
+            $field = implode($this->_endQuote . '.' . $this->_startQuote, $items);
+
+            return $this->_startQuote . $field . $this->_endQuote . $matches[2];
+        }
+
+        if (preg_match('/^[\w\s-]*[\w-]+/u', $identifier)) {
+            return $this->_startQuote . $identifier . $this->_endQuote;
+        }
+
+        return $identifier;
+    }
+
+    /**
+     * Returns a callable function that will be used to transform a passed Query object.
+     * This function, in turn, will return an instance of a Query object that has been
+     * transformed to accommodate any specificities of the SQL dialect in use.
+     *
+     * @param string $type the type of query to be transformed
+     * (select, insert, update, delete)
+     * @return \Closure
+     */
+    public function queryTranslator(string $type): Closure
+    {
+        return function ($query) use ($type) {
+            if ($this->isAutoQuotingEnabled()) {
+                $query = (new IdentifierQuoter($this))->quote($query);
+            }
+
+            /** @var \Cake\ORM\Query $query */
+            $query = $this->{'_' . $type . 'QueryTranslator'}($query);
+            $translators = $this->_expressionTranslators();
+            if (!$translators) {
+                return $query;
+            }
+
+            $query->traverseExpressions(function ($expression) use ($translators, $query): void {
+                foreach ($translators as $class => $method) {
+                    if ($expression instanceof $class) {
+                        $this->{$method}($expression, $query);
+                    }
+                }
+            });
+
+            return $query;
+        };
+    }
+
+    /**
+     * Returns an associative array of methods that will transform Expression
+     * objects to conform with the specific SQL dialect. Keys are class names
+     * and values a method in this class.
+     *
+     * @psalm-return array
+     * @return array
+     */
+    protected function _expressionTranslators(): array
+    {
+        return [];
+    }
+
+    /**
+     * Apply translation steps to select queries.
+     *
+     * @param \Cake\Database\Query $query The query to translate
+     * @return \Cake\Database\Query The modified query
+     */
+    protected function _selectQueryTranslator(Query $query): Query
+    {
+        return $this->_transformDistinct($query);
+    }
+
+    /**
+     * Returns the passed query after rewriting the DISTINCT clause, so that drivers
+     * that do not support the "ON" part can provide the actual way it should be done
+     *
+     * @param \Cake\Database\Query $query The query to be transformed
+     * @return \Cake\Database\Query
+     */
+    protected function _transformDistinct(Query $query): Query
+    {
+        if (is_array($query->clause('distinct'))) {
+            $query->group($query->clause('distinct'), true);
+            $query->distinct(false);
+        }
+
+        return $query;
+    }
+
+    /**
+     * Apply translation steps to delete queries.
+     *
+     * Chops out aliases on delete query conditions as most database dialects do not
+     * support aliases in delete queries. This also removes aliases
+     * in table names as they frequently don't work either.
+     *
+     * We are intentionally not supporting deletes with joins as they have even poorer support.
+     *
+     * @param \Cake\Database\Query $query The query to translate
+     * @return \Cake\Database\Query The modified query
+     */
+    protected function _deleteQueryTranslator(Query $query): Query
+    {
+        $hadAlias = false;
+        $tables = [];
+        foreach ($query->clause('from') as $alias => $table) {
+            if (is_string($alias)) {
+                $hadAlias = true;
+            }
+            $tables[] = $table;
+        }
+        if ($hadAlias) {
+            $query->from($tables, true);
+        }
+
+        if (!$hadAlias) {
+            return $query;
+        }
+
+        return $this->_removeAliasesFromConditions($query);
+    }
+
+    /**
+     * Apply translation steps to update queries.
+     *
+     * Chops out aliases on update query conditions as not all database dialects do support
+     * aliases in update queries.
+     *
+     * Just like for delete queries, joins are currently not supported for update queries.
+     *
+     * @param \Cake\Database\Query $query The query to translate
+     * @return \Cake\Database\Query The modified query
+     */
+    protected function _updateQueryTranslator(Query $query): Query
+    {
+        return $this->_removeAliasesFromConditions($query);
+    }
+
+    /**
+     * Removes aliases from the `WHERE` clause of a query.
+     *
+     * @param \Cake\Database\Query $query The query to process.
+     * @return \Cake\Database\Query The modified query.
+     * @throws \RuntimeException In case the processed query contains any joins, as removing
+     *  aliases from the conditions can break references to the joined tables.
+     */
+    protected function _removeAliasesFromConditions(Query $query): Query
+    {
+        if ($query->clause('join')) {
+            throw new RuntimeException(
+                'Aliases are being removed from conditions for UPDATE/DELETE queries, ' .
+                'this can break references to joined tables.'
+            );
+        }
+
+        $conditions = $query->clause('where');
+        if ($conditions) {
+            $conditions->traverse(function ($expression) {
+                if ($expression instanceof ComparisonExpression) {
+                    $field = $expression->getField();
+                    if (
+                        is_string($field) &&
+                        strpos($field, '.') !== false
+                    ) {
+                        [, $unaliasedField] = explode('.', $field, 2);
+                        $expression->setField($unaliasedField);
+                    }
+
+                    return $expression;
+                }
+
+                if ($expression instanceof IdentifierExpression) {
+                    $identifier = $expression->getIdentifier();
+                    if (strpos($identifier, '.') !== false) {
+                        [, $unaliasedIdentifier] = explode('.', $identifier, 2);
+                        $expression->setIdentifier($unaliasedIdentifier);
+                    }
+
+                    return $expression;
+                }
+
+                return $expression;
+            });
+        }
+
+        return $query;
+    }
+
+    /**
+     * Apply translation steps to insert queries.
+     *
+     * @param \Cake\Database\Query $query The query to translate
+     * @return \Cake\Database\Query The modified query
+     */
+    protected function _insertQueryTranslator(Query $query): Query
+    {
+        return $query;
+    }
+
+    /**
+     * Returns a SQL snippet for creating a new transaction savepoint
+     *
+     * @param string|int $name save point name
+     * @return string
+     */
+    public function savePointSQL($name): string
+    {
+        return 'SAVEPOINT LEVEL' . $name;
+    }
+
+    /**
+     * Returns a SQL snippet for releasing a previously created save point
+     *
+     * @param string|int $name save point name
+     * @return string
+     */
+    public function releaseSavePointSQL($name): string
+    {
+        return 'RELEASE SAVEPOINT LEVEL' . $name;
+    }
+
+    /**
+     * Returns a SQL snippet for rollbacking a previously created save point
+     *
+     * @param string|int $name save point name
+     * @return string
+     */
+    public function rollbackSavePointSQL($name): string
+    {
+        return 'ROLLBACK TO SAVEPOINT LEVEL' . $name;
+    }
+}
+
+// phpcs:disable
+class_alias(
+    'Cake\Database\Driver\SqlDialectTrait',
+    'Cake\Database\SqlDialectTrait'
+);
+// phpcs:enable
diff --git a/vendor/cakephp/database/Driver/Sqlite.php b/vendor/cakephp/database/Driver/Sqlite.php
new file mode 100644
index 0000000..65c3e7a
--- /dev/null
+++ b/vendor/cakephp/database/Driver/Sqlite.php
@@ -0,0 +1,385 @@
+
+     */
+    protected $_baseConfig = [
+        'persistent' => false,
+        'username' => null,
+        'password' => null,
+        'database' => ':memory:',
+        'encoding' => 'utf8',
+        'mask' => 0644,
+        'cache' => null,
+        'mode' => null,
+        'flags' => [],
+        'init' => [],
+    ];
+
+    /**
+     * The schema dialect class for this driver
+     *
+     * @var \Cake\Database\Schema\SqliteSchemaDialect|null
+     */
+    protected $_schemaDialect;
+
+    /**
+     * Whether the connected server supports window functions.
+     *
+     * @var bool|null
+     */
+    protected $_supportsWindowFunctions;
+
+    /**
+     * String used to start a database identifier quoting to make it safe
+     *
+     * @var string
+     */
+    protected $_startQuote = '"';
+
+    /**
+     * String used to end a database identifier quoting to make it safe
+     *
+     * @var string
+     */
+    protected $_endQuote = '"';
+
+    /**
+     * Mapping of date parts.
+     *
+     * @var array
+     */
+    protected $_dateParts = [
+        'day' => 'd',
+        'hour' => 'H',
+        'month' => 'm',
+        'minute' => 'M',
+        'second' => 'S',
+        'week' => 'W',
+        'year' => 'Y',
+    ];
+
+    /**
+     * Mapping of feature to db server version for feature availability checks.
+     *
+     * @var array
+     */
+    protected $featureVersions = [
+        'cte' => '3.8.3',
+        'window' => '3.28.0',
+    ];
+
+    /**
+     * Establishes a connection to the database server
+     *
+     * @return bool true on success
+     */
+    public function connect(): bool
+    {
+        if ($this->_connection) {
+            return true;
+        }
+        $config = $this->_config;
+        $config['flags'] += [
+            PDO::ATTR_PERSISTENT => $config['persistent'],
+            PDO::ATTR_EMULATE_PREPARES => false,
+            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+        ];
+        if (!is_string($config['database']) || $config['database'] === '') {
+            $name = $config['name'] ?? 'unknown';
+            throw new InvalidArgumentException(
+                "The `database` key for the `{$name}` SQLite connection needs to be a non-empty string."
+            );
+        }
+
+        $chmodFile = false;
+        if ($config['database'] !== ':memory:' && $config['mode'] !== 'memory') {
+            $chmodFile = !file_exists($config['database']);
+        }
+
+        $params = [];
+        if ($config['cache']) {
+            $params[] = 'cache=' . $config['cache'];
+        }
+        if ($config['mode']) {
+            $params[] = 'mode=' . $config['mode'];
+        }
+
+        if ($params) {
+            if (PHP_VERSION_ID < 80100) {
+                throw new RuntimeException('SQLite URI support requires PHP 8.1.');
+            }
+            $dsn = 'sqlite:file:' . $config['database'] . '?' . implode('&', $params);
+        } else {
+            $dsn = 'sqlite:' . $config['database'];
+        }
+
+        $this->_connect($dsn, $config);
+        if ($chmodFile) {
+            // phpcs:disable
+            @chmod($config['database'], $config['mask']);
+            // phpcs:enable
+        }
+
+        if (!empty($config['init'])) {
+            foreach ((array)$config['init'] as $command) {
+                $this->getConnection()->exec($command);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns whether php is able to use this driver for connecting to database
+     *
+     * @return bool true if it is valid to use this driver
+     */
+    public function enabled(): bool
+    {
+        return in_array('sqlite', PDO::getAvailableDrivers(), true);
+    }
+
+    /**
+     * Prepares a sql statement to be executed
+     *
+     * @param \Cake\Database\Query|string $query The query to prepare.
+     * @return \Cake\Database\StatementInterface
+     */
+    public function prepare($query): StatementInterface
+    {
+        $this->connect();
+        $isObject = $query instanceof Query;
+        /**
+         * @psalm-suppress PossiblyInvalidMethodCall
+         * @psalm-suppress PossiblyInvalidArgument
+         */
+        $statement = $this->_connection->prepare($isObject ? $query->sql() : $query);
+        $result = new SqliteStatement(new PDOStatement($statement, $this), $this);
+        /** @psalm-suppress PossiblyInvalidMethodCall */
+        if ($isObject && $query->isBufferedResultsEnabled() === false) {
+            $result->bufferResults(false);
+        }
+
+        return $result;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function disableForeignKeySQL(): string
+    {
+        return 'PRAGMA foreign_keys = OFF';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function enableForeignKeySQL(): string
+    {
+        return 'PRAGMA foreign_keys = ON';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function supports(string $feature): bool
+    {
+        switch ($feature) {
+            case static::FEATURE_CTE:
+            case static::FEATURE_WINDOW:
+                return version_compare(
+                    $this->version(),
+                    $this->featureVersions[$feature],
+                    '>='
+                );
+
+            case static::FEATURE_TRUNCATE_WITH_CONSTRAINTS:
+                return true;
+        }
+
+        return parent::supports($feature);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function supportsDynamicConstraints(): bool
+    {
+        return false;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function schemaDialect(): SchemaDialect
+    {
+        if ($this->_schemaDialect === null) {
+            $this->_schemaDialect = new SqliteSchemaDialect($this);
+        }
+
+        return $this->_schemaDialect;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function newCompiler(): QueryCompiler
+    {
+        return new SqliteCompiler();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function _expressionTranslators(): array
+    {
+        return [
+            FunctionExpression::class => '_transformFunctionExpression',
+            TupleComparison::class => '_transformTupleComparison',
+        ];
+    }
+
+    /**
+     * Receives a FunctionExpression and changes it so that it conforms to this
+     * SQL dialect.
+     *
+     * @param \Cake\Database\Expression\FunctionExpression $expression The function expression to convert to TSQL.
+     * @return void
+     */
+    protected function _transformFunctionExpression(FunctionExpression $expression): void
+    {
+        switch ($expression->getName()) {
+            case 'CONCAT':
+                // CONCAT function is expressed as exp1 || exp2
+                $expression->setName('')->setConjunction(' ||');
+                break;
+            case 'DATEDIFF':
+                $expression
+                    ->setName('ROUND')
+                    ->setConjunction('-')
+                    ->iterateParts(function ($p) {
+                        return new FunctionExpression('JULIANDAY', [$p['value']], [$p['type']]);
+                    });
+                break;
+            case 'NOW':
+                $expression->setName('DATETIME')->add(["'now'" => 'literal']);
+                break;
+            case 'RAND':
+                $expression
+                    ->setName('ABS')
+                    ->add(['RANDOM() % 1' => 'literal'], [], true);
+                break;
+            case 'CURRENT_DATE':
+                $expression->setName('DATE')->add(["'now'" => 'literal']);
+                break;
+            case 'CURRENT_TIME':
+                $expression->setName('TIME')->add(["'now'" => 'literal']);
+                break;
+            case 'EXTRACT':
+                $expression
+                    ->setName('STRFTIME')
+                    ->setConjunction(' ,')
+                    ->iterateParts(function ($p, $key) {
+                        if ($key === 0) {
+                            $value = rtrim(strtolower($p), 's');
+                            if (isset($this->_dateParts[$value])) {
+                                $p = ['value' => '%' . $this->_dateParts[$value], 'type' => null];
+                            }
+                        }
+
+                        return $p;
+                    });
+                break;
+            case 'DATE_ADD':
+                $expression
+                    ->setName('DATE')
+                    ->setConjunction(',')
+                    ->iterateParts(function ($p, $key) {
+                        if ($key === 1) {
+                            $p = ['value' => $p, 'type' => null];
+                        }
+
+                        return $p;
+                    });
+                break;
+            case 'DAYOFWEEK':
+                $expression
+                    ->setName('STRFTIME')
+                    ->setConjunction(' ')
+                    ->add(["'%w', " => 'literal'], [], true)
+                    ->add([') + (1' => 'literal']); // Sqlite starts on index 0 but Sunday should be 1
+                break;
+        }
+    }
+
+    /**
+     * Returns true if the server supports common table expressions.
+     *
+     * @return bool
+     * @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_CTE)` instead
+     */
+    public function supportsCTEs(): bool
+    {
+        deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
+
+        return $this->supports(static::FEATURE_CTE);
+    }
+
+    /**
+     * Returns true if the connected server supports window functions.
+     *
+     * @return bool
+     * @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_WINDOW)` instead
+     */
+    public function supportsWindowFunctions(): bool
+    {
+        deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
+
+        return $this->supports(static::FEATURE_WINDOW);
+    }
+}
diff --git a/vendor/cakephp/database/Driver/Sqlserver.php b/vendor/cakephp/database/Driver/Sqlserver.php
new file mode 100644
index 0000000..a0dc2ec
--- /dev/null
+++ b/vendor/cakephp/database/Driver/Sqlserver.php
@@ -0,0 +1,569 @@
+
+     */
+    protected $_baseConfig = [
+        'host' => 'localhost\SQLEXPRESS',
+        'username' => '',
+        'password' => '',
+        'database' => 'cake',
+        'port' => '',
+        // PDO::SQLSRV_ENCODING_UTF8
+        'encoding' => 65001,
+        'flags' => [],
+        'init' => [],
+        'settings' => [],
+        'attributes' => [],
+        'app' => null,
+        'connectionPooling' => null,
+        'failoverPartner' => null,
+        'loginTimeout' => null,
+        'multiSubnetFailover' => null,
+        'encrypt' => null,
+        'trustServerCertificate' => null,
+    ];
+
+    /**
+     * The schema dialect class for this driver
+     *
+     * @var \Cake\Database\Schema\SqlserverSchemaDialect|null
+     */
+    protected $_schemaDialect;
+
+    /**
+     * String used to start a database identifier quoting to make it safe
+     *
+     * @var string
+     */
+    protected $_startQuote = '[';
+
+    /**
+     * String used to end a database identifier quoting to make it safe
+     *
+     * @var string
+     */
+    protected $_endQuote = ']';
+
+    /**
+     * Establishes a connection to the database server.
+     *
+     * Please note that the PDO::ATTR_PERSISTENT attribute is not supported by
+     * the SQL Server PHP PDO drivers.  As a result you cannot use the
+     * persistent config option when connecting to a SQL Server  (for more
+     * information see: https://github.com/Microsoft/msphpsql/issues/65).
+     *
+     * @throws \InvalidArgumentException if an unsupported setting is in the driver config
+     * @return bool true on success
+     */
+    public function connect(): bool
+    {
+        if ($this->_connection) {
+            return true;
+        }
+        $config = $this->_config;
+
+        if (isset($config['persistent']) && $config['persistent']) {
+            throw new InvalidArgumentException(
+                'Config setting "persistent" cannot be set to true, '
+                . 'as the Sqlserver PDO driver does not support PDO::ATTR_PERSISTENT'
+            );
+        }
+
+        $config['flags'] += [
+            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+        ];
+
+        if (!empty($config['encoding'])) {
+            $config['flags'][PDO::SQLSRV_ATTR_ENCODING] = $config['encoding'];
+        }
+        $port = '';
+        if ($config['port']) {
+            $port = ',' . $config['port'];
+        }
+
+        $dsn = "sqlsrv:Server={$config['host']}{$port};Database={$config['database']};MultipleActiveResultSets=false";
+        if ($config['app'] !== null) {
+            $dsn .= ";APP={$config['app']}";
+        }
+        if ($config['connectionPooling'] !== null) {
+            $dsn .= ";ConnectionPooling={$config['connectionPooling']}";
+        }
+        if ($config['failoverPartner'] !== null) {
+            $dsn .= ";Failover_Partner={$config['failoverPartner']}";
+        }
+        if ($config['loginTimeout'] !== null) {
+            $dsn .= ";LoginTimeout={$config['loginTimeout']}";
+        }
+        if ($config['multiSubnetFailover'] !== null) {
+            $dsn .= ";MultiSubnetFailover={$config['multiSubnetFailover']}";
+        }
+        if ($config['encrypt'] !== null) {
+            $dsn .= ";Encrypt={$config['encrypt']}";
+        }
+        if ($config['trustServerCertificate'] !== null) {
+            $dsn .= ";TrustServerCertificate={$config['trustServerCertificate']}";
+        }
+        $this->_connect($dsn, $config);
+
+        $connection = $this->getConnection();
+        if (!empty($config['init'])) {
+            foreach ((array)$config['init'] as $command) {
+                $connection->exec($command);
+            }
+        }
+        if (!empty($config['settings']) && is_array($config['settings'])) {
+            foreach ($config['settings'] as $key => $value) {
+                $connection->exec("SET {$key} {$value}");
+            }
+        }
+        if (!empty($config['attributes']) && is_array($config['attributes'])) {
+            foreach ($config['attributes'] as $key => $value) {
+                $connection->setAttribute($key, $value);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns whether PHP is able to use this driver for connecting to database
+     *
+     * @return bool true if it is valid to use this driver
+     */
+    public function enabled(): bool
+    {
+        return in_array('sqlsrv', PDO::getAvailableDrivers(), true);
+    }
+
+    /**
+     * Prepares a sql statement to be executed
+     *
+     * @param \Cake\Database\Query|string $query The query to prepare.
+     * @return \Cake\Database\StatementInterface
+     */
+    public function prepare($query): StatementInterface
+    {
+        $this->connect();
+
+        $sql = $query;
+        $options = [
+            PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL,
+            PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED,
+        ];
+        if ($query instanceof Query) {
+            $sql = $query->sql();
+            if (count($query->getValueBinder()->bindings()) > 2100) {
+                throw new InvalidArgumentException(
+                    'Exceeded maximum number of parameters (2100) for prepared statements in Sql Server. ' .
+                    'This is probably due to a very large WHERE IN () clause which generates a parameter ' .
+                    'for each value in the array. ' .
+                    'If using an Association, try changing the `strategy` from select to subquery.'
+                );
+            }
+
+            if (!$query->isBufferedResultsEnabled()) {
+                $options = [];
+            }
+        }
+
+        /** @psalm-suppress PossiblyInvalidArgument */
+        $statement = $this->_connection->prepare($sql, $options);
+
+        return new SqlserverStatement($statement, $this);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function savePointSQL($name): string
+    {
+        return 'SAVE TRANSACTION t' . $name;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function releaseSavePointSQL($name): string
+    {
+        // SQLServer has no release save point operation.
+        return '';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function rollbackSavePointSQL($name): string
+    {
+        return 'ROLLBACK TRANSACTION t' . $name;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function disableForeignKeySQL(): string
+    {
+        return 'EXEC sp_MSforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function enableForeignKeySQL(): string
+    {
+        return 'EXEC sp_MSforeachtable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function supports(string $feature): bool
+    {
+        switch ($feature) {
+            case static::FEATURE_CTE:
+            case static::FEATURE_TRUNCATE_WITH_CONSTRAINTS:
+            case static::FEATURE_WINDOW:
+                return true;
+
+            case static::FEATURE_QUOTE:
+                $this->connect();
+
+                return $this->_connection->getAttribute(PDO::ATTR_DRIVER_NAME) !== 'odbc';
+        }
+
+        return parent::supports($feature);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function supportsDynamicConstraints(): bool
+    {
+        return true;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function schemaDialect(): SchemaDialect
+    {
+        if ($this->_schemaDialect === null) {
+            $this->_schemaDialect = new SqlserverSchemaDialect($this);
+        }
+
+        return $this->_schemaDialect;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return \Cake\Database\SqlserverCompiler
+     */
+    public function newCompiler(): QueryCompiler
+    {
+        return new SqlserverCompiler();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function _selectQueryTranslator(Query $query): Query
+    {
+        $limit = $query->clause('limit');
+        $offset = $query->clause('offset');
+
+        if ($limit && $offset === null) {
+            $query->modifier(['_auto_top_' => sprintf('TOP %d', $limit)]);
+        }
+
+        if ($offset !== null && !$query->clause('order')) {
+            $query->order($query->newExpr()->add('(SELECT NULL)'));
+        }
+
+        if ($this->version() < 11 && $offset !== null) {
+            return $this->_pagingSubquery($query, $limit, $offset);
+        }
+
+        return $this->_transformDistinct($query);
+    }
+
+    /**
+     * Generate a paging subquery for older versions of SQLserver.
+     *
+     * Prior to SQLServer 2012 there was no equivalent to LIMIT OFFSET, so a subquery must
+     * be used.
+     *
+     * @param \Cake\Database\Query $original The query to wrap in a subquery.
+     * @param int|null $limit The number of rows to fetch.
+     * @param int|null $offset The number of rows to offset.
+     * @return \Cake\Database\Query Modified query object.
+     */
+    protected function _pagingSubquery(Query $original, ?int $limit, ?int $offset): Query
+    {
+        $field = '_cake_paging_._cake_page_rownum_';
+
+        if ($original->clause('order')) {
+            // SQL server does not support column aliases in OVER clauses.  But
+            // the only practical way to specify the use of calculated columns
+            // is with their alias.  So substitute the select SQL in place of
+            // any column aliases for those entries in the order clause.
+            $select = $original->clause('select');
+            $order = new OrderByExpression();
+            $original
+                ->clause('order')
+                ->iterateParts(function ($direction, $orderBy) use ($select, $order) {
+                    $key = $orderBy;
+                    if (
+                        isset($select[$orderBy]) &&
+                        $select[$orderBy] instanceof ExpressionInterface
+                    ) {
+                        $order->add(new OrderClauseExpression($select[$orderBy], $direction));
+                    } else {
+                        $order->add([$key => $direction]);
+                    }
+
+                    // Leave original order clause unchanged.
+                    return $orderBy;
+                });
+        } else {
+            $order = new OrderByExpression('(SELECT NULL)');
+        }
+
+        $query = clone $original;
+        $query->select([
+                '_cake_page_rownum_' => new UnaryExpression('ROW_NUMBER() OVER', $order),
+            ])->limit(null)
+            ->offset(null)
+            ->order([], true);
+
+        $outer = new Query($query->getConnection());
+        $outer->select('*')
+            ->from(['_cake_paging_' => $query]);
+
+        if ($offset) {
+            $outer->where(["$field > " . $offset]);
+        }
+        if ($limit) {
+            $value = (int)$offset + $limit;
+            $outer->where(["$field <= $value"]);
+        }
+
+        // Decorate the original query as that is what the
+        // end developer will be calling execute() on originally.
+        $original->decorateResults(function ($row) {
+            if (isset($row['_cake_page_rownum_'])) {
+                unset($row['_cake_page_rownum_']);
+            }
+
+            return $row;
+        });
+
+        return $outer;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function _transformDistinct(Query $query): Query
+    {
+        if (!is_array($query->clause('distinct'))) {
+            return $query;
+        }
+
+        $original = $query;
+        $query = clone $original;
+
+        $distinct = $query->clause('distinct');
+        $query->distinct(false);
+
+        $order = new OrderByExpression($distinct);
+        $query
+            ->select(function ($q) use ($distinct, $order) {
+                $over = $q->newExpr('ROW_NUMBER() OVER')
+                    ->add('(PARTITION BY')
+                    ->add($q->newExpr()->add($distinct)->setConjunction(','))
+                    ->add($order)
+                    ->add(')')
+                    ->setConjunction(' ');
+
+                return [
+                    '_cake_distinct_pivot_' => $over,
+                ];
+            })
+            ->limit(null)
+            ->offset(null)
+            ->order([], true);
+
+        $outer = new Query($query->getConnection());
+        $outer->select('*')
+            ->from(['_cake_distinct_' => $query])
+            ->where(['_cake_distinct_pivot_' => 1]);
+
+        // Decorate the original query as that is what the
+        // end developer will be calling execute() on originally.
+        $original->decorateResults(function ($row) {
+            if (isset($row['_cake_distinct_pivot_'])) {
+                unset($row['_cake_distinct_pivot_']);
+            }
+
+            return $row;
+        });
+
+        return $outer;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function _expressionTranslators(): array
+    {
+        return [
+            FunctionExpression::class => '_transformFunctionExpression',
+            TupleComparison::class => '_transformTupleComparison',
+        ];
+    }
+
+    /**
+     * Receives a FunctionExpression and changes it so that it conforms to this
+     * SQL dialect.
+     *
+     * @param \Cake\Database\Expression\FunctionExpression $expression The function expression to convert to TSQL.
+     * @return void
+     */
+    protected function _transformFunctionExpression(FunctionExpression $expression): void
+    {
+        switch ($expression->getName()) {
+            case 'CONCAT':
+                // CONCAT function is expressed as exp1 + exp2
+                $expression->setName('')->setConjunction(' +');
+                break;
+            case 'DATEDIFF':
+                /** @var bool $hasDay */
+                $hasDay = false;
+                $visitor = function ($value) use (&$hasDay) {
+                    if ($value === 'day') {
+                        $hasDay = true;
+                    }
+
+                    return $value;
+                };
+                $expression->iterateParts($visitor);
+
+                if (!$hasDay) {
+                    $expression->add(['day' => 'literal'], [], true);
+                }
+                break;
+            case 'CURRENT_DATE':
+                $time = new FunctionExpression('GETUTCDATE');
+                $expression->setName('CONVERT')->add(['date' => 'literal', $time]);
+                break;
+            case 'CURRENT_TIME':
+                $time = new FunctionExpression('GETUTCDATE');
+                $expression->setName('CONVERT')->add(['time' => 'literal', $time]);
+                break;
+            case 'NOW':
+                $expression->setName('GETUTCDATE');
+                break;
+            case 'EXTRACT':
+                $expression->setName('DATEPART')->setConjunction(' ,');
+                break;
+            case 'DATE_ADD':
+                $params = [];
+                $visitor = function ($p, $key) use (&$params) {
+                    if ($key === 0) {
+                        $params[2] = $p;
+                    } else {
+                        $valueUnit = explode(' ', $p);
+                        $params[0] = rtrim($valueUnit[1], 's');
+                        $params[1] = $valueUnit[0];
+                    }
+
+                    return $p;
+                };
+                $manipulator = function ($p, $key) use (&$params) {
+                    return $params[$key];
+                };
+
+                $expression
+                    ->setName('DATEADD')
+                    ->setConjunction(',')
+                    ->iterateParts($visitor)
+                    ->iterateParts($manipulator)
+                    ->add([$params[2] => 'literal']);
+                break;
+            case 'DAYOFWEEK':
+                $expression
+                    ->setName('DATEPART')
+                    ->setConjunction(' ')
+                    ->add(['weekday, ' => 'literal'], [], true);
+                break;
+            case 'SUBSTR':
+                $expression->setName('SUBSTRING');
+                if (count($expression) < 4) {
+                    $params = [];
+                    $expression
+                        ->iterateParts(function ($p) use (&$params) {
+                            return $params[] = $p;
+                        })
+                        ->add([new FunctionExpression('LEN', [$params[0]]), ['string']]);
+                }
+
+                break;
+        }
+    }
+}
diff --git a/vendor/cakephp/database/Driver/TupleComparisonTranslatorTrait.php b/vendor/cakephp/database/Driver/TupleComparisonTranslatorTrait.php
new file mode 100644
index 0000000..e21a79d
--- /dev/null
+++ b/vendor/cakephp/database/Driver/TupleComparisonTranslatorTrait.php
@@ -0,0 +1,113 @@
+getField();
+
+        if (!is_array($fields)) {
+            return;
+        }
+
+        $operator = strtoupper($expression->getOperator());
+        if (!in_array($operator, ['IN', '='])) {
+            throw new RuntimeException(
+                sprintf(
+                    'Tuple comparison transform only supports the `IN` and `=` operators, `%s` given.',
+                    $operator
+                )
+            );
+        }
+
+        $value = $expression->getValue();
+        $true = new QueryExpression('1');
+
+        if ($value instanceof Query) {
+            $selected = array_values($value->clause('select'));
+            foreach ($fields as $i => $field) {
+                $value->andWhere([$field => new IdentifierExpression($selected[$i])]);
+            }
+            $value->select($true, true);
+            $expression->setField($true);
+            $expression->setOperator('=');
+
+            return;
+        }
+
+        $type = $expression->getType();
+        if ($type) {
+            /** @var array $typeMap */
+            $typeMap = array_combine($fields, $type) ?: [];
+        } else {
+            $typeMap = [];
+        }
+
+        $surrogate = $query->getConnection()
+            ->selectQuery($true);
+
+        if (!is_array(current($value))) {
+            $value = [$value];
+        }
+
+        $conditions = ['OR' => []];
+        foreach ($value as $tuple) {
+            $item = [];
+            foreach (array_values($tuple) as $i => $value2) {
+                $item[] = [$fields[$i] => $value2];
+            }
+            $conditions['OR'][] = $item;
+        }
+        $surrogate->where($conditions, $typeMap);
+
+        $expression->setField($true);
+        $expression->setValue($surrogate);
+        $expression->setOperator('=');
+    }
+}
diff --git a/vendor/cakephp/database/DriverInterface.php b/vendor/cakephp/database/DriverInterface.php
new file mode 100644
index 0000000..914d6b7
--- /dev/null
+++ b/vendor/cakephp/database/DriverInterface.php
@@ -0,0 +1,336 @@
+ $types Associative array of type names used to bind values to query
+     * @return $this
+     * @see \Cake\Database\Query::where()
+     */
+    public function filter($conditions, array $types = [])
+    {
+        if ($this->filter === null) {
+            $this->filter = new QueryExpression();
+        }
+
+        if ($conditions instanceof Closure) {
+            $conditions = $conditions(new QueryExpression());
+        }
+
+        $this->filter->add($conditions, $types);
+
+        return $this;
+    }
+
+    /**
+     * Adds an empty `OVER()` window expression or a named window epression.
+     *
+     * @param string|null $name Window name
+     * @return $this
+     */
+    public function over(?string $name = null)
+    {
+        if ($this->window === null) {
+            $this->window = new WindowExpression();
+        }
+        if ($name) {
+            // Set name manually in case this was chained from FunctionsBuilder wrapper
+            $this->window->name($name);
+        }
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function partition($partitions)
+    {
+        $this->over();
+        $this->window->partition($partitions);
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function order($fields)
+    {
+        $this->over();
+        $this->window->order($fields);
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function range($start, $end = 0)
+    {
+        $this->over();
+        $this->window->range($start, $end);
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function rows(?int $start, ?int $end = 0)
+    {
+        $this->over();
+        $this->window->rows($start, $end);
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function groups(?int $start, ?int $end = 0)
+    {
+        $this->over();
+        $this->window->groups($start, $end);
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function frame(
+        string $type,
+        $startOffset,
+        string $startDirection,
+        $endOffset,
+        string $endDirection
+    ) {
+        $this->over();
+        $this->window->frame($type, $startOffset, $startDirection, $endOffset, $endDirection);
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function excludeCurrent()
+    {
+        $this->over();
+        $this->window->excludeCurrent();
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function excludeGroup()
+    {
+        $this->over();
+        $this->window->excludeGroup();
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function excludeTies()
+    {
+        $this->over();
+        $this->window->excludeTies();
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        $sql = parent::sql($binder);
+        if ($this->filter !== null) {
+            $sql .= ' FILTER (WHERE ' . $this->filter->sql($binder) . ')';
+        }
+        if ($this->window !== null) {
+            if ($this->window->isNamedOnly()) {
+                $sql .= ' OVER ' . $this->window->sql($binder);
+            } else {
+                $sql .= ' OVER (' . $this->window->sql($binder) . ')';
+            }
+        }
+
+        return $sql;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        parent::traverse($callback);
+        if ($this->filter !== null) {
+            $callback($this->filter);
+            $this->filter->traverse($callback);
+        }
+        if ($this->window !== null) {
+            $callback($this->window);
+            $this->window->traverse($callback);
+        }
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function count(): int
+    {
+        $count = parent::count();
+        if ($this->window !== null) {
+            $count = $count + 1;
+        }
+
+        return $count;
+    }
+
+    /**
+     * Clone this object and its subtree of expressions.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        parent::__clone();
+        if ($this->filter !== null) {
+            $this->filter = clone $this->filter;
+        }
+        if ($this->window !== null) {
+            $this->window = clone $this->window;
+        }
+    }
+}
diff --git a/vendor/cakephp/database/Expression/BetweenExpression.php b/vendor/cakephp/database/Expression/BetweenExpression.php
new file mode 100644
index 0000000..3466147
--- /dev/null
+++ b/vendor/cakephp/database/Expression/BetweenExpression.php
@@ -0,0 +1,144 @@
+_castToExpression($from, $type);
+            $to = $this->_castToExpression($to, $type);
+        }
+
+        $this->_field = $field;
+        $this->_from = $from;
+        $this->_to = $to;
+        $this->_type = $type;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        $parts = [
+            'from' => $this->_from,
+            'to' => $this->_to,
+        ];
+
+        /** @var \Cake\Database\ExpressionInterface|string $field */
+        $field = $this->_field;
+        if ($field instanceof ExpressionInterface) {
+            $field = $field->sql($binder);
+        }
+
+        foreach ($parts as $name => $part) {
+            if ($part instanceof ExpressionInterface) {
+                $parts[$name] = $part->sql($binder);
+                continue;
+            }
+            $parts[$name] = $this->_bindValue($part, $binder, $this->_type);
+        }
+
+        return sprintf('%s BETWEEN %s AND %s', $field, $parts['from'], $parts['to']);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        foreach ([$this->_field, $this->_from, $this->_to] as $part) {
+            if ($part instanceof ExpressionInterface) {
+                $callback($part);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Registers a value in the placeholder generator and returns the generated placeholder
+     *
+     * @param mixed $value The value to bind
+     * @param \Cake\Database\ValueBinder $binder The value binder to use
+     * @param string $type The type of $value
+     * @return string generated placeholder
+     */
+    protected function _bindValue($value, $binder, $type): string
+    {
+        $placeholder = $binder->placeholder('c');
+        $binder->bind($placeholder, $value, $type);
+
+        return $placeholder;
+    }
+
+    /**
+     * Do a deep clone of this expression.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        foreach (['_field', '_from', '_to'] as $part) {
+            if ($this->{$part} instanceof ExpressionInterface) {
+                $this->{$part} = clone $this->{$part};
+            }
+        }
+    }
+}
diff --git a/vendor/cakephp/database/Expression/CaseExpression.php b/vendor/cakephp/database/Expression/CaseExpression.php
new file mode 100644
index 0000000..864a3bc
--- /dev/null
+++ b/vendor/cakephp/database/Expression/CaseExpression.php
@@ -0,0 +1,251 @@
+ :value"
+     *
+     * @var array
+     */
+    protected $_conditions = [];
+
+    /**
+     * Values that are associated with the conditions in the $_conditions array.
+     * Each value represents the 'true' value for the condition with the corresponding key.
+     *
+     * @var array
+     */
+    protected $_values = [];
+
+    /**
+     * The `ELSE` value for the case statement. If null then no `ELSE` will be included.
+     *
+     * @var \Cake\Database\ExpressionInterface|array|string|null
+     */
+    protected $_elseValue;
+
+    /**
+     * Constructs the case expression
+     *
+     * @param \Cake\Database\ExpressionInterface|array $conditions The conditions to test. Must be a ExpressionInterface
+     * instance, or an array of ExpressionInterface instances.
+     * @param \Cake\Database\ExpressionInterface|array $values Associative array of values to be associated with the
+     * conditions passed in $conditions. If there are more $values than $conditions,
+     * the last $value is used as the `ELSE` value.
+     * @param array $types Associative array of types to be associated with the values
+     * passed in $values
+     */
+    public function __construct($conditions = [], $values = [], $types = [])
+    {
+        $conditions = is_array($conditions) ? $conditions : [$conditions];
+        $values = is_array($values) ? $values : [$values];
+        $types = is_array($types) ? $types : [$types];
+
+        if (!empty($conditions)) {
+            $this->add($conditions, $values, $types);
+        }
+
+        if (count($values) > count($conditions)) {
+            end($values);
+            $key = key($values);
+            $this->elseValue($values[$key], $types[$key] ?? null);
+        }
+    }
+
+    /**
+     * Adds one or more conditions and their respective true values to the case object.
+     * Conditions must be a one dimensional array or a QueryExpression.
+     * The trueValues must be a similar structure, but may contain a string value.
+     *
+     * @param \Cake\Database\ExpressionInterface|array $conditions Must be a ExpressionInterface instance,
+     *   or an array of ExpressionInterface instances.
+     * @param \Cake\Database\ExpressionInterface|array $values Associative array of values of each condition
+     * @param array $types Associative array of types to be associated with the values
+     * @return $this
+     */
+    public function add($conditions = [], $values = [], $types = [])
+    {
+        $conditions = is_array($conditions) ? $conditions : [$conditions];
+        $values = is_array($values) ? $values : [$values];
+        $types = is_array($types) ? $types : [$types];
+
+        $this->_addExpressions($conditions, $values, $types);
+
+        return $this;
+    }
+
+    /**
+     * Iterates over the passed in conditions and ensures that there is a matching true value for each.
+     * If no matching true value, then it is defaulted to '1'.
+     *
+     * @param array $conditions Array of ExpressionInterface instances.
+     * @param array $values Associative array of values of each condition
+     * @param array $types Associative array of types to be associated with the values
+     * @return void
+     */
+    protected function _addExpressions(array $conditions, array $values, array $types): void
+    {
+        $rawValues = array_values($values);
+        $keyValues = array_keys($values);
+
+        foreach ($conditions as $k => $c) {
+            $numericKey = is_numeric($k);
+
+            if ($numericKey && empty($c)) {
+                continue;
+            }
+
+            if (!$c instanceof ExpressionInterface) {
+                continue;
+            }
+
+            $this->_conditions[] = $c;
+            $value = $rawValues[$k] ?? 1;
+
+            if ($value === 'literal') {
+                $value = $keyValues[$k];
+                $this->_values[] = $value;
+                continue;
+            }
+
+            if ($value === 'identifier') {
+                /** @var string $identifier */
+                $identifier = $keyValues[$k];
+                $value = new IdentifierExpression($identifier);
+                $this->_values[] = $value;
+                continue;
+            }
+
+            $type = $types[$k] ?? null;
+
+            if ($type !== null && !$value instanceof ExpressionInterface) {
+                $value = $this->_castToExpression($value, $type);
+            }
+
+            if ($value instanceof ExpressionInterface) {
+                $this->_values[] = $value;
+                continue;
+            }
+
+            $this->_values[] = ['value' => $value, 'type' => $type];
+        }
+    }
+
+    /**
+     * Sets the default value
+     *
+     * @param \Cake\Database\ExpressionInterface|array|string|null $value Value to set
+     * @param string|null $type Type of value
+     * @return void
+     */
+    public function elseValue($value = null, ?string $type = null): void
+    {
+        if (is_array($value)) {
+            end($value);
+            $value = key($value);
+        }
+
+        if ($value !== null && !$value instanceof ExpressionInterface) {
+            $value = $this->_castToExpression($value, $type);
+        }
+
+        if (!$value instanceof ExpressionInterface) {
+            $value = ['value' => $value, 'type' => $type];
+        }
+
+        $this->_elseValue = $value;
+    }
+
+    /**
+     * Compiles the relevant parts into sql
+     *
+     * @param \Cake\Database\ExpressionInterface|array|string $part The part to compile
+     * @param \Cake\Database\ValueBinder $binder Sql generator
+     * @return string
+     */
+    protected function _compile($part, ValueBinder $binder): string
+    {
+        if ($part instanceof ExpressionInterface) {
+            $part = $part->sql($binder);
+        } elseif (is_array($part)) {
+            $placeholder = $binder->placeholder('param');
+            $binder->bind($placeholder, $part['value'], $part['type']);
+            $part = $placeholder;
+        }
+
+        return $part;
+    }
+
+    /**
+     * Converts the Node into a SQL string fragment.
+     *
+     * @param \Cake\Database\ValueBinder $binder Placeholder generator object
+     * @return string
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        $parts = [];
+        $parts[] = 'CASE';
+        foreach ($this->_conditions as $k => $part) {
+            $value = $this->_values[$k];
+            $parts[] = 'WHEN ' . $this->_compile($part, $binder) . ' THEN ' . $this->_compile($value, $binder);
+        }
+        if ($this->_elseValue !== null) {
+            $parts[] = 'ELSE';
+            $parts[] = $this->_compile($this->_elseValue, $binder);
+        }
+        $parts[] = 'END';
+
+        return implode(' ', $parts);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        foreach (['_conditions', '_values'] as $part) {
+            foreach ($this->{$part} as $c) {
+                if ($c instanceof ExpressionInterface) {
+                    $callback($c);
+                    $c->traverse($callback);
+                }
+            }
+        }
+        if ($this->_elseValue instanceof ExpressionInterface) {
+            $callback($this->_elseValue);
+            $this->_elseValue->traverse($callback);
+        }
+
+        return $this;
+    }
+}
diff --git a/vendor/cakephp/database/Expression/CaseExpressionTrait.php b/vendor/cakephp/database/Expression/CaseExpressionTrait.php
new file mode 100644
index 0000000..1994ec9
--- /dev/null
+++ b/vendor/cakephp/database/Expression/CaseExpressionTrait.php
@@ -0,0 +1,109 @@
+_typeMap !== null &&
+            $value instanceof IdentifierExpression
+        ) {
+            $type = $this->_typeMap->type($value->getIdentifier());
+        } elseif ($value instanceof TypedResultInterface) {
+            $type = $value->getReturnType();
+        }
+
+        return $type;
+    }
+
+    /**
+     * Compiles a nullable value to SQL.
+     *
+     * @param \Cake\Database\ValueBinder $binder The value binder to use.
+     * @param \Cake\Database\ExpressionInterface|object|scalar|null $value The value to compile.
+     * @param string|null $type The value type.
+     * @return string
+     */
+    protected function compileNullableValue(ValueBinder $binder, $value, ?string $type = null): string
+    {
+        if (
+            $type !== null &&
+            !($value instanceof ExpressionInterface)
+        ) {
+            $value = $this->_castToExpression($value, $type);
+        }
+
+        if ($value === null) {
+            $value = 'NULL';
+        } elseif ($value instanceof Query) {
+            $value = sprintf('(%s)', $value->sql($binder));
+        } elseif ($value instanceof ExpressionInterface) {
+            $value = $value->sql($binder);
+        } else {
+            $placeholder = $binder->placeholder('c');
+            $binder->bind($placeholder, $value, $type);
+            $value = $placeholder;
+        }
+
+        return $value;
+    }
+}
diff --git a/vendor/cakephp/database/Expression/CaseStatementExpression.php b/vendor/cakephp/database/Expression/CaseStatementExpression.php
new file mode 100644
index 0000000..8e6ebd1
--- /dev/null
+++ b/vendor/cakephp/database/Expression/CaseStatementExpression.php
@@ -0,0 +1,597 @@
+
+     */
+    protected $validClauseNames = [
+        'value',
+        'when',
+        'else',
+    ];
+
+    /**
+     * Whether this is a simple case expression.
+     *
+     * @var bool
+     */
+    protected $isSimpleVariant = false;
+
+    /**
+     * The case value.
+     *
+     * @var \Cake\Database\ExpressionInterface|object|scalar|null
+     */
+    protected $value = null;
+
+    /**
+     * The case value type.
+     *
+     * @var string|null
+     */
+    protected $valueType = null;
+
+    /**
+     * The `WHEN ... THEN ...` expressions.
+     *
+     * @var array<\Cake\Database\Expression\WhenThenExpression>
+     */
+    protected $when = [];
+
+    /**
+     * Buffer that holds values and types for use with `then()`.
+     *
+     * @var array|null
+     */
+    protected $whenBuffer = null;
+
+    /**
+     * The else part result value.
+     *
+     * @var \Cake\Database\ExpressionInterface|object|scalar|null
+     */
+    protected $else = null;
+
+    /**
+     * The else part result type.
+     *
+     * @var string|null
+     */
+    protected $elseType = null;
+
+    /**
+     * The return type.
+     *
+     * @var string|null
+     */
+    protected $returnType = null;
+
+    /**
+     * Constructor.
+     *
+     * When a value is set, the syntax generated is
+     * `CASE case_value WHEN when_value ... END` (simple case),
+     * where the `when_value`'s are compared against the
+     * `case_value`.
+     *
+     * When no value is set, the syntax generated is
+     * `CASE WHEN when_conditions ... END` (searched case),
+     * where the conditions hold the comparisons.
+     *
+     * Note that `null` is a valid case value, and thus should
+     * only be passed if you actually want to create the simple
+     * case expression variant!
+     *
+     * @param \Cake\Database\ExpressionInterface|object|scalar|null $value The case value.
+     * @param string|null $type The case value type. If no type is provided, the type will be tried to be inferred
+     *  from the value.
+     */
+    public function __construct($value = null, ?string $type = null)
+    {
+        if (func_num_args() > 0) {
+            if (
+                $value !== null &&
+                !is_scalar($value) &&
+                !(is_object($value) && !($value instanceof Closure))
+            ) {
+                throw new InvalidArgumentException(sprintf(
+                    'The `$value` argument must be either `null`, a scalar value, an object, ' .
+                    'or an instance of `\%s`, `%s` given.',
+                    ExpressionInterface::class,
+                    getTypeName($value)
+                ));
+            }
+
+            $this->value = $value;
+
+            if (
+                $value !== null &&
+                $type === null &&
+                !($value instanceof ExpressionInterface)
+            ) {
+                $type = $this->inferType($value);
+            }
+            $this->valueType = $type;
+
+            $this->isSimpleVariant = true;
+        }
+    }
+
+    /**
+     * Sets the `WHEN` value for a `WHEN ... THEN ...` expression, or a
+     * self-contained expression that holds both the value for `WHEN`
+     * and the value for `THEN`.
+     *
+     * ### Order based syntax
+     *
+     * When passing a value other than a self-contained
+     * `\Cake\Database\Expression\WhenThenExpression`,
+     * instance, the `WHEN ... THEN ...` statement must be closed off with
+     * a call to `then()` before invoking `when()` again or `else()`:
+     *
+     * ```
+     * $queryExpression
+     *     ->case($query->identifier('Table.column'))
+     *     ->when(true)
+     *     ->then('Yes')
+     *     ->when(false)
+     *     ->then('No')
+     *     ->else('Maybe');
+     * ```
+     *
+     * ### Self-contained expressions
+     *
+     * When passing an instance of `\Cake\Database\Expression\WhenThenExpression`,
+     * being it directly, or via a callable, then there is no need to close
+     * using `then()` on this object, instead the statement will be closed
+     * on the `\Cake\Database\Expression\WhenThenExpression`
+     * object using
+     * `\Cake\Database\Expression\WhenThenExpression::then()`.
+     *
+     * Callables will receive an instance of `\Cake\Database\Expression\WhenThenExpression`,
+     * and must return one, being it the same object, or a custom one:
+     *
+     * ```
+     * $queryExpression
+     *     ->case()
+     *     ->when(function (\Cake\Database\Expression\WhenThenExpression $whenThen) {
+     *         return $whenThen
+     *             ->when(['Table.column' => true])
+     *             ->then('Yes');
+     *     })
+     *     ->when(function (\Cake\Database\Expression\WhenThenExpression $whenThen) {
+     *         return $whenThen
+     *             ->when(['Table.column' => false])
+     *             ->then('No');
+     *     })
+     *     ->else('Maybe');
+     * ```
+     *
+     * ### Type handling
+     *
+     * The types provided via the `$type` argument will be merged with the
+     * type map set for this expression. When using callables for `$when`,
+     * the `\Cake\Database\Expression\WhenThenExpression`
+     * instance received by the callables will inherit that type map, however
+     * the types passed here will _not_ be merged in case of using callables,
+     * instead the types must be passed in
+     * `\Cake\Database\Expression\WhenThenExpression::when()`:
+     *
+     * ```
+     * $queryExpression
+     *     ->case()
+     *     ->when(function (\Cake\Database\Expression\WhenThenExpression $whenThen) {
+     *         return $whenThen
+     *             ->when(['unmapped_column' => true], ['unmapped_column' => 'bool'])
+     *             ->then('Yes');
+     *     })
+     *     ->when(function (\Cake\Database\Expression\WhenThenExpression $whenThen) {
+     *         return $whenThen
+     *             ->when(['unmapped_column' => false], ['unmapped_column' => 'bool'])
+     *             ->then('No');
+     *     })
+     *     ->else('Maybe');
+     * ```
+     *
+     * ### User data safety
+     *
+     * When passing user data, be aware that allowing a user defined array
+     * to be passed, is a potential SQL injection vulnerability, as it
+     * allows for raw SQL to slip in!
+     *
+     * The following is _unsafe_ usage that must be avoided:
+     *
+     * ```
+     * $case
+     *      ->when($userData)
+     * ```
+     *
+     * A safe variant for the above would be to define a single type for
+     * the value:
+     *
+     * ```
+     * $case
+     *      ->when($userData, 'integer')
+     * ```
+     *
+     * This way an exception would be triggered when an array is passed for
+     * the value, thus preventing raw SQL from slipping in, and all other
+     * types of values would be forced to be bound as an integer.
+     *
+     * Another way to safely pass user data is when using a conditions
+     * array, and passing user data only on the value side of the array
+     * entries, which will cause them to be bound:
+     *
+     * ```
+     * $case
+     *      ->when([
+     *          'Table.column' => $userData,
+     *      ])
+     * ```
+     *
+     * Lastly, data can also be bound manually:
+     *
+     * ```
+     * $query
+     *      ->select([
+     *          'val' => $query->newExpr()
+     *              ->case()
+     *              ->when($query->newExpr(':userData'))
+     *              ->then(123)
+     *      ])
+     *      ->bind(':userData', $userData, 'integer')
+     * ```
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|object|array|scalar $when The `WHEN` value. When using an
+     *  array of conditions, it must be compatible with `\Cake\Database\Query::where()`. Note that this argument is
+     *  _not_ completely safe for use with user data, as a user supplied array would allow for raw SQL to slip in! If
+     *  you plan to use user data, either pass a single type for the `$type` argument (which forces the `$when` value to
+     *  be a non-array, and then always binds the data), use a conditions array where the user data is only passed on
+     *  the value side of the array entries, or custom bindings!
+     * @param array|string|null $type The when value type. Either an associative array when using array style
+     *  conditions, or else a string. If no type is provided, the type will be tried to be inferred from the value.
+     * @return $this
+     * @throws \LogicException In case this a closing `then()` call is required before calling this method.
+     * @throws \LogicException In case the callable doesn't return an instance of
+     *  `\Cake\Database\Expression\WhenThenExpression`.
+     */
+    public function when($when, $type = null)
+    {
+        if ($this->whenBuffer !== null) {
+            throw new LogicException('Cannot call `when()` between `when()` and `then()`.');
+        }
+
+        if ($when instanceof Closure) {
+            $when = $when(new WhenThenExpression($this->getTypeMap()));
+            if (!($when instanceof WhenThenExpression)) {
+                throw new LogicException(sprintf(
+                    '`when()` callables must return an instance of `\%s`, `%s` given.',
+                    WhenThenExpression::class,
+                    getTypeName($when)
+                ));
+            }
+        }
+
+        if ($when instanceof WhenThenExpression) {
+            $this->when[] = $when;
+        } else {
+            $this->whenBuffer = ['when' => $when, 'type' => $type];
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets the `THEN` result value for the last `WHEN ... THEN ...`
+     * statement that was opened using `when()`.
+     *
+     * ### Order based syntax
+     *
+     * This method can only be invoked in case `when()` was previously
+     * used with a value other than a closure or an instance of
+     * `\Cake\Database\Expression\WhenThenExpression`:
+     *
+     * ```
+     * $case
+     *     ->when(['Table.column' => true])
+     *     ->then('Yes')
+     *     ->when(['Table.column' => false])
+     *     ->then('No')
+     *     ->else('Maybe');
+     * ```
+     *
+     * The following would all fail with an exception:
+     *
+     * ```
+     * $case
+     *     ->when(['Table.column' => true])
+     *     ->when(['Table.column' => false])
+     *     // ...
+     * ```
+     *
+     * ```
+     * $case
+     *     ->when(['Table.column' => true])
+     *     ->else('Maybe')
+     *     // ...
+     * ```
+     *
+     * ```
+     * $case
+     *     ->then('Yes')
+     *     // ...
+     * ```
+     *
+     * ```
+     * $case
+     *     ->when(['Table.column' => true])
+     *     ->then('Yes')
+     *     ->then('No')
+     *     // ...
+     * ```
+     *
+     * @param \Cake\Database\ExpressionInterface|object|scalar|null $result The result value.
+     * @param string|null $type The result type. If no type is provided, the type will be tried to be inferred from the
+     *  value.
+     * @return $this
+     * @throws \LogicException In case `when()` wasn't previously called with a value other than a closure or an
+     *  instance of `\Cake\Database\Expression\WhenThenExpression`.
+     */
+    public function then($result, ?string $type = null)
+    {
+        if ($this->whenBuffer === null) {
+            throw new LogicException('Cannot call `then()` before `when()`.');
+        }
+
+        $whenThen = (new WhenThenExpression($this->getTypeMap()))
+            ->when($this->whenBuffer['when'], $this->whenBuffer['type'])
+            ->then($result, $type);
+
+        $this->whenBuffer = null;
+
+        $this->when[] = $whenThen;
+
+        return $this;
+    }
+
+    /**
+     * Sets the `ELSE` result value.
+     *
+     * @param \Cake\Database\ExpressionInterface|object|scalar|null $result The result value.
+     * @param string|null $type The result type. If no type is provided, the type will be tried to be inferred from the
+     *  value.
+     * @return $this
+     * @throws \LogicException In case a closing `then()` call is required before calling this method.
+     * @throws \InvalidArgumentException In case the `$result` argument is neither a scalar value, nor an object, an
+     *  instance of `\Cake\Database\ExpressionInterface`, or `null`.
+     */
+    public function else($result, ?string $type = null)
+    {
+        if ($this->whenBuffer !== null) {
+            throw new LogicException('Cannot call `else()` between `when()` and `then()`.');
+        }
+
+        if (
+            $result !== null &&
+            !is_scalar($result) &&
+            !(is_object($result) && !($result instanceof Closure))
+        ) {
+            throw new InvalidArgumentException(sprintf(
+                'The `$result` argument must be either `null`, a scalar value, an object, ' .
+                'or an instance of `\%s`, `%s` given.',
+                ExpressionInterface::class,
+                getTypeName($result)
+            ));
+        }
+
+        if ($type === null) {
+            $type = $this->inferType($result);
+        }
+
+        $this->else = $result;
+        $this->elseType = $type;
+
+        return $this;
+    }
+
+    /**
+     * Returns the abstract type that this expression will return.
+     *
+     * If no type has been explicitly set via `setReturnType()`, this
+     * method will try to obtain the type from the result types of the
+     * `then()` and `else() `calls. All types must be identical in order
+     * for this to work, otherwise the type will default to `string`.
+     *
+     * @return string
+     * @see CaseStatementExpression::then()
+     */
+    public function getReturnType(): string
+    {
+        if ($this->returnType !== null) {
+            return $this->returnType;
+        }
+
+        $types = [];
+        foreach ($this->when as $when) {
+            $type = $when->getResultType();
+            if ($type !== null) {
+                $types[] = $type;
+            }
+        }
+
+        if ($this->elseType !== null) {
+            $types[] = $this->elseType;
+        }
+
+        $types = array_unique($types);
+        if (count($types) === 1) {
+            return $types[0];
+        }
+
+        return 'string';
+    }
+
+    /**
+     * Sets the abstract type that this expression will return.
+     *
+     * If no type is being explicitly set via this method, then the
+     * `getReturnType()` method will try to infer the type from the
+     * result types of the `then()` and `else() `calls.
+     *
+     * @param string $type The type name to use.
+     * @return $this
+     */
+    public function setReturnType(string $type)
+    {
+        $this->returnType = $type;
+
+        return $this;
+    }
+
+    /**
+     * Returns the available data for the given clause.
+     *
+     * ### Available clauses
+     *
+     * The following clause names are available:
+     *
+     * * `value`: The case value for a `CASE case_value WHEN ...` expression.
+     * * `when`: An array of `WHEN ... THEN ...` expressions.
+     * * `else`: The `ELSE` result value.
+     *
+     * @param string $clause The name of the clause to obtain.
+     * @return \Cake\Database\ExpressionInterface|object|array<\Cake\Database\Expression\WhenThenExpression>|scalar|null
+     * @throws \InvalidArgumentException In case the given clause name is invalid.
+     */
+    public function clause(string $clause)
+    {
+        if (!in_array($clause, $this->validClauseNames, true)) {
+            throw new InvalidArgumentException(
+                sprintf(
+                    'The `$clause` argument must be one of `%s`, the given value `%s` is invalid.',
+                    implode('`, `', $this->validClauseNames),
+                    $clause
+                )
+            );
+        }
+
+        return $this->{$clause};
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        if ($this->whenBuffer !== null) {
+            throw new LogicException('Case expression has incomplete when clause. Missing `then()` after `when()`.');
+        }
+
+        if (empty($this->when)) {
+            throw new LogicException('Case expression must have at least one when statement.');
+        }
+
+        $value = '';
+        if ($this->isSimpleVariant) {
+            $value = $this->compileNullableValue($binder, $this->value, $this->valueType) . ' ';
+        }
+
+        $whenThenExpressions = [];
+        foreach ($this->when as $whenThen) {
+            $whenThenExpressions[] = $whenThen->sql($binder);
+        }
+        $whenThen = implode(' ', $whenThenExpressions);
+
+        $else = $this->compileNullableValue($binder, $this->else, $this->elseType);
+
+        return "CASE {$value}{$whenThen} ELSE $else END";
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        if ($this->whenBuffer !== null) {
+            throw new LogicException('Case expression has incomplete when clause. Missing `then()` after `when()`.');
+        }
+
+        if ($this->value instanceof ExpressionInterface) {
+            $callback($this->value);
+            $this->value->traverse($callback);
+        }
+
+        foreach ($this->when as $when) {
+            $callback($when);
+            $when->traverse($callback);
+        }
+
+        if ($this->else instanceof ExpressionInterface) {
+            $callback($this->else);
+            $this->else->traverse($callback);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Clones the inner expression objects.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        if ($this->whenBuffer !== null) {
+            throw new LogicException('Case expression has incomplete when clause. Missing `then()` after `when()`.');
+        }
+
+        if ($this->value instanceof ExpressionInterface) {
+            $this->value = clone $this->value;
+        }
+
+        foreach ($this->when as $key => $when) {
+            $this->when[$key] = clone $this->when[$key];
+        }
+
+        if ($this->else instanceof ExpressionInterface) {
+            $this->else = clone $this->else;
+        }
+    }
+}
diff --git a/vendor/cakephp/database/Expression/CommonTableExpression.php b/vendor/cakephp/database/Expression/CommonTableExpression.php
new file mode 100644
index 0000000..f3d359e
--- /dev/null
+++ b/vendor/cakephp/database/Expression/CommonTableExpression.php
@@ -0,0 +1,239 @@
+
+     */
+    protected $fields = [];
+
+    /**
+     * The CTE query definition.
+     *
+     * @var \Cake\Database\ExpressionInterface|null
+     */
+    protected $query;
+
+    /**
+     * Whether the CTE is materialized or not materialized.
+     *
+     * @var string|null
+     */
+    protected $materialized = null;
+
+    /**
+     * Whether the CTE is recursive.
+     *
+     * @var bool
+     */
+    protected $recursive = false;
+
+    /**
+     * Constructor.
+     *
+     * @param string $name The CTE name.
+     * @param \Cake\Database\ExpressionInterface|\Closure $query CTE query
+     */
+    public function __construct(string $name = '', $query = null)
+    {
+        $this->name = new IdentifierExpression($name);
+        if ($query) {
+            $this->query($query);
+        }
+    }
+
+    /**
+     * Sets the name of this CTE.
+     *
+     * This is the named you used to reference the expression
+     * in select, insert, etc queries.
+     *
+     * @param string $name The CTE name.
+     * @return $this
+     */
+    public function name(string $name)
+    {
+        $this->name = new IdentifierExpression($name);
+
+        return $this;
+    }
+
+    /**
+     * Sets the query for this CTE.
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure $query CTE query
+     * @return $this
+     */
+    public function query($query)
+    {
+        if ($query instanceof Closure) {
+            $query = $query();
+            if (!($query instanceof ExpressionInterface)) {
+                throw new RuntimeException(
+                    'You must return an `ExpressionInterface` from a Closure passed to `query()`.'
+                );
+            }
+        }
+        $this->query = $query;
+
+        return $this;
+    }
+
+    /**
+     * Adds one or more fields (arguments) to the CTE.
+     *
+     * @param \Cake\Database\Expression\IdentifierExpression|array<\Cake\Database\Expression\IdentifierExpression>|array|string $fields Field names
+     * @return $this
+     */
+    public function field($fields)
+    {
+        $fields = (array)$fields;
+        foreach ($fields as &$field) {
+            if (!($field instanceof IdentifierExpression)) {
+                $field = new IdentifierExpression($field);
+            }
+        }
+        $this->fields = array_merge($this->fields, $fields);
+
+        return $this;
+    }
+
+    /**
+     * Sets this CTE as materialized.
+     *
+     * @return $this
+     */
+    public function materialized()
+    {
+        $this->materialized = 'MATERIALIZED';
+
+        return $this;
+    }
+
+    /**
+     * Sets this CTE as not materialized.
+     *
+     * @return $this
+     */
+    public function notMaterialized()
+    {
+        $this->materialized = 'NOT MATERIALIZED';
+
+        return $this;
+    }
+
+    /**
+     * Gets whether this CTE is recursive.
+     *
+     * @return bool
+     */
+    public function isRecursive(): bool
+    {
+        return $this->recursive;
+    }
+
+    /**
+     * Sets this CTE as recursive.
+     *
+     * @return $this
+     */
+    public function recursive()
+    {
+        $this->recursive = true;
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        $fields = '';
+        if ($this->fields) {
+            $expressions = array_map(function (IdentifierExpression $e) use ($binder) {
+                return $e->sql($binder);
+            }, $this->fields);
+            $fields = sprintf('(%s)', implode(', ', $expressions));
+        }
+
+        $suffix = $this->materialized ? $this->materialized . ' ' : '';
+
+        return sprintf(
+            '%s%s AS %s(%s)',
+            $this->name->sql($binder),
+            $fields,
+            $suffix,
+            $this->query ? $this->query->sql($binder) : ''
+        );
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        $callback($this->name);
+        foreach ($this->fields as $field) {
+            $callback($field);
+            $field->traverse($callback);
+        }
+
+        if ($this->query) {
+            $callback($this->query);
+            $this->query->traverse($callback);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Clones the inner expression objects.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        $this->name = clone $this->name;
+        if ($this->query) {
+            $this->query = clone $this->query;
+        }
+
+        foreach ($this->fields as $key => $field) {
+            $this->fields[$key] = clone $field;
+        }
+    }
+}
diff --git a/vendor/cakephp/database/Expression/Comparison.php b/vendor/cakephp/database/Expression/Comparison.php
new file mode 100644
index 0000000..01bdbd5
--- /dev/null
+++ b/vendor/cakephp/database/Expression/Comparison.php
@@ -0,0 +1,7 @@
+
+     */
+    protected $_valueExpressions = [];
+
+    /**
+     * Constructor
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field the field name to compare to a value
+     * @param mixed $value The value to be used in comparison
+     * @param string|null $type the type name used to cast the value
+     * @param string $operator the operator used for comparing field and value
+     */
+    public function __construct($field, $value, ?string $type = null, string $operator = '=')
+    {
+        $this->_type = $type;
+        $this->setField($field);
+        $this->setValue($value);
+        $this->_operator = $operator;
+    }
+
+    /**
+     * Sets the value
+     *
+     * @param mixed $value The value to compare
+     * @return void
+     */
+    public function setValue($value): void
+    {
+        $value = $this->_castToExpression($value, $this->_type);
+
+        $isMultiple = $this->_type && strpos($this->_type, '[]') !== false;
+        if ($isMultiple) {
+            [$value, $this->_valueExpressions] = $this->_collectExpressions($value);
+        }
+
+        $this->_isMultiple = $isMultiple;
+        $this->_value = $value;
+    }
+
+    /**
+     * Returns the value used for comparison
+     *
+     * @return mixed
+     */
+    public function getValue()
+    {
+        return $this->_value;
+    }
+
+    /**
+     * Sets the operator to use for the comparison
+     *
+     * @param string $operator The operator to be used for the comparison.
+     * @return void
+     */
+    public function setOperator(string $operator): void
+    {
+        $this->_operator = $operator;
+    }
+
+    /**
+     * Returns the operator used for comparison
+     *
+     * @return string
+     */
+    public function getOperator(): string
+    {
+        return $this->_operator;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        /** @var \Cake\Database\ExpressionInterface|string $field */
+        $field = $this->_field;
+
+        if ($field instanceof ExpressionInterface) {
+            $field = $field->sql($binder);
+        }
+
+        if ($this->_value instanceof IdentifierExpression) {
+            $template = '%s %s %s';
+            $value = $this->_value->sql($binder);
+        } elseif ($this->_value instanceof ExpressionInterface) {
+            $template = '%s %s (%s)';
+            $value = $this->_value->sql($binder);
+        } else {
+            [$template, $value] = $this->_stringExpression($binder);
+        }
+
+        return sprintf($template, $field, $this->_operator, $value);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        if ($this->_field instanceof ExpressionInterface) {
+            $callback($this->_field);
+            $this->_field->traverse($callback);
+        }
+
+        if ($this->_value instanceof ExpressionInterface) {
+            $callback($this->_value);
+            $this->_value->traverse($callback);
+        }
+
+        foreach ($this->_valueExpressions as $v) {
+            $callback($v);
+            $v->traverse($callback);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Create a deep clone.
+     *
+     * Clones the field and value if they are expression objects.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        foreach (['_value', '_field'] as $prop) {
+            if ($this->{$prop} instanceof ExpressionInterface) {
+                $this->{$prop} = clone $this->{$prop};
+            }
+        }
+    }
+
+    /**
+     * Returns a template and a placeholder for the value after registering it
+     * with the placeholder $binder
+     *
+     * @param \Cake\Database\ValueBinder $binder The value binder to use.
+     * @return array First position containing the template and the second a placeholder
+     */
+    protected function _stringExpression(ValueBinder $binder): array
+    {
+        $template = '%s ';
+
+        if ($this->_field instanceof ExpressionInterface && !$this->_field instanceof IdentifierExpression) {
+            $template = '(%s) ';
+        }
+
+        if ($this->_isMultiple) {
+            $template .= '%s (%s)';
+            $type = $this->_type;
+            if ($type !== null) {
+                $type = str_replace('[]', '', $type);
+            }
+            $value = $this->_flattenValue($this->_value, $binder, $type);
+
+            // To avoid SQL errors when comparing a field to a list of empty values,
+            // better just throw an exception here
+            if ($value === '') {
+                $field = $this->_field instanceof ExpressionInterface ? $this->_field->sql($binder) : $this->_field;
+                /** @psalm-suppress PossiblyInvalidCast */
+                throw new DatabaseException(
+                    "Impossible to generate condition with empty list of values for field ($field)"
+                );
+            }
+        } else {
+            $template .= '%s %s';
+            $value = $this->_bindValue($this->_value, $binder, $this->_type);
+        }
+
+        return [$template, $value];
+    }
+
+    /**
+     * Registers a value in the placeholder generator and returns the generated placeholder
+     *
+     * @param mixed $value The value to bind
+     * @param \Cake\Database\ValueBinder $binder The value binder to use
+     * @param string|null $type The type of $value
+     * @return string generated placeholder
+     */
+    protected function _bindValue($value, ValueBinder $binder, ?string $type = null): string
+    {
+        $placeholder = $binder->placeholder('c');
+        $binder->bind($placeholder, $value, $type);
+
+        return $placeholder;
+    }
+
+    /**
+     * Converts a traversable value into a set of placeholders generated by
+     * $binder and separated by `,`
+     *
+     * @param iterable $value the value to flatten
+     * @param \Cake\Database\ValueBinder $binder The value binder to use
+     * @param string|null $type the type to cast values to
+     * @return string
+     */
+    protected function _flattenValue(iterable $value, ValueBinder $binder, ?string $type = null): string
+    {
+        $parts = [];
+        if (is_array($value)) {
+            foreach ($this->_valueExpressions as $k => $v) {
+                $parts[$k] = $v->sql($binder);
+                unset($value[$k]);
+            }
+        }
+
+        if (!empty($value)) {
+            $parts += $binder->generateManyNamed($value, $type);
+        }
+
+        return implode(',', $parts);
+    }
+
+    /**
+     * Returns an array with the original $values in the first position
+     * and all ExpressionInterface objects that could be found in the second
+     * position.
+     *
+     * @param \Cake\Database\ExpressionInterface|iterable $values The rows to insert
+     * @return array
+     */
+    protected function _collectExpressions($values): array
+    {
+        if ($values instanceof ExpressionInterface) {
+            return [$values, []];
+        }
+
+        $expressions = $result = [];
+        $isArray = is_array($values);
+
+        if ($isArray) {
+            /** @var array $result */
+            $result = $values;
+        }
+
+        foreach ($values as $k => $v) {
+            if ($v instanceof ExpressionInterface) {
+                $expressions[$k] = $v;
+            }
+
+            if ($isArray) {
+                $result[$k] = $v;
+            }
+        }
+
+        return [$result, $expressions];
+    }
+}
+
+// phpcs:disable
+class_alias(
+    'Cake\Database\Expression\ComparisonExpression',
+    'Cake\Database\Expression\Comparison'
+);
+// phpcs:enable
diff --git a/vendor/cakephp/database/Expression/FieldInterface.php b/vendor/cakephp/database/Expression/FieldInterface.php
new file mode 100644
index 0000000..f0554d9
--- /dev/null
+++ b/vendor/cakephp/database/Expression/FieldInterface.php
@@ -0,0 +1,39 @@
+_field = $field;
+    }
+
+    /**
+     * Returns the field name
+     *
+     * @return \Cake\Database\ExpressionInterface|array|string
+     */
+    public function getField()
+    {
+        return $this->_field;
+    }
+}
diff --git a/vendor/cakephp/database/Expression/FunctionExpression.php b/vendor/cakephp/database/Expression/FunctionExpression.php
new file mode 100644
index 0000000..105655b
--- /dev/null
+++ b/vendor/cakephp/database/Expression/FunctionExpression.php
@@ -0,0 +1,178 @@
+ 'literal', ' rules']);`
+     *
+     * Will produce `CONCAT(name, ' rules')`
+     *
+     * @param string $name the name of the function to be constructed
+     * @param array $params list of arguments to be passed to the function
+     * If associative the key would be used as argument when value is 'literal'
+     * @param array|array $types Associative array of types to be associated with the
+     * passed arguments
+     * @param string $returnType The return type of this expression
+     */
+    public function __construct(string $name, array $params = [], array $types = [], string $returnType = 'string')
+    {
+        $this->_name = $name;
+        $this->_returnType = $returnType;
+        parent::__construct($params, $types, ',');
+    }
+
+    /**
+     * Sets the name of the SQL function to be invoke in this expression.
+     *
+     * @param string $name The name of the function
+     * @return $this
+     */
+    public function setName(string $name)
+    {
+        $this->_name = $name;
+
+        return $this;
+    }
+
+    /**
+     * Gets the name of the SQL function to be invoke in this expression.
+     *
+     * @return string
+     */
+    public function getName(): string
+    {
+        return $this->_name;
+    }
+
+    /**
+     * Adds one or more arguments for the function call.
+     *
+     * @param array $conditions list of arguments to be passed to the function
+     * If associative the key would be used as argument when value is 'literal'
+     * @param array $types Associative array of types to be associated with the
+     * passed arguments
+     * @param bool $prepend Whether to prepend or append to the list of arguments
+     * @see \Cake\Database\Expression\FunctionExpression::__construct() for more details.
+     * @return $this
+     * @psalm-suppress MoreSpecificImplementedParamType
+     */
+    public function add($conditions, array $types = [], bool $prepend = false)
+    {
+        $put = $prepend ? 'array_unshift' : 'array_push';
+        $typeMap = $this->getTypeMap()->setTypes($types);
+        foreach ($conditions as $k => $p) {
+            if ($p === 'literal') {
+                $put($this->_conditions, $k);
+                continue;
+            }
+
+            if ($p === 'identifier') {
+                $put($this->_conditions, new IdentifierExpression($k));
+                continue;
+            }
+
+            $type = $typeMap->type($k);
+
+            if ($type !== null && !$p instanceof ExpressionInterface) {
+                $p = $this->_castToExpression($p, $type);
+            }
+
+            if ($p instanceof ExpressionInterface) {
+                $put($this->_conditions, $p);
+                continue;
+            }
+
+            $put($this->_conditions, ['value' => $p, 'type' => $type]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        $parts = [];
+        foreach ($this->_conditions as $condition) {
+            if ($condition instanceof Query) {
+                $condition = sprintf('(%s)', $condition->sql($binder));
+            } elseif ($condition instanceof ExpressionInterface) {
+                $condition = $condition->sql($binder);
+            } elseif (is_array($condition)) {
+                $p = $binder->placeholder('param');
+                $binder->bind($p, $condition['value'], $condition['type']);
+                $condition = $p;
+            }
+            $parts[] = $condition;
+        }
+
+        return $this->_name . sprintf('(%s)', implode(
+            $this->_conjunction . ' ',
+            $parts
+        ));
+    }
+
+    /**
+     * The name of the function is in itself an expression to generate, thus
+     * always adding 1 to the amount of expressions stored in this object.
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        return 1 + count($this->_conditions);
+    }
+}
diff --git a/vendor/cakephp/database/Expression/IdentifierExpression.php b/vendor/cakephp/database/Expression/IdentifierExpression.php
new file mode 100644
index 0000000..69486a4
--- /dev/null
+++ b/vendor/cakephp/database/Expression/IdentifierExpression.php
@@ -0,0 +1,119 @@
+_identifier = $identifier;
+        $this->collation = $collation;
+    }
+
+    /**
+     * Sets the identifier this expression represents
+     *
+     * @param string $identifier The identifier
+     * @return void
+     */
+    public function setIdentifier(string $identifier): void
+    {
+        $this->_identifier = $identifier;
+    }
+
+    /**
+     * Returns the identifier this expression represents
+     *
+     * @return string
+     */
+    public function getIdentifier(): string
+    {
+        return $this->_identifier;
+    }
+
+    /**
+     * Sets the collation.
+     *
+     * @param string $collation Identifier collation
+     * @return void
+     */
+    public function setCollation(string $collation): void
+    {
+        $this->collation = $collation;
+    }
+
+    /**
+     * Returns the collation.
+     *
+     * @return string|null
+     */
+    public function getCollation(): ?string
+    {
+        return $this->collation;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        $sql = $this->_identifier;
+        if ($this->collation) {
+            $sql .= ' COLLATE ' . $this->collation;
+        }
+
+        return $sql;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        return $this;
+    }
+}
diff --git a/vendor/cakephp/database/Expression/OrderByExpression.php b/vendor/cakephp/database/Expression/OrderByExpression.php
new file mode 100644
index 0000000..74fbf21
--- /dev/null
+++ b/vendor/cakephp/database/Expression/OrderByExpression.php
@@ -0,0 +1,88 @@
+ $types The types for each column.
+     * @param string $conjunction The glue used to join conditions together.
+     */
+    public function __construct($conditions = [], $types = [], $conjunction = '')
+    {
+        parent::__construct($conditions, $types, $conjunction);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        $order = [];
+        foreach ($this->_conditions as $k => $direction) {
+            if ($direction instanceof ExpressionInterface) {
+                $direction = $direction->sql($binder);
+            }
+            $order[] = is_numeric($k) ? $direction : sprintf('%s %s', $k, $direction);
+        }
+
+        return sprintf('ORDER BY %s', implode(', ', $order));
+    }
+
+    /**
+     * Auxiliary function used for decomposing a nested array of conditions and
+     * building a tree structure inside this object to represent the full SQL expression.
+     *
+     * New order by expressions are merged to existing ones
+     *
+     * @param array $conditions list of order by expressions
+     * @param array $types list of types associated on fields referenced in $conditions
+     * @return void
+     */
+    protected function _addConditions(array $conditions, array $types): void
+    {
+        foreach ($conditions as $key => $val) {
+            if (
+                is_string($key) &&
+                is_string($val) &&
+                !in_array(strtoupper($val), ['ASC', 'DESC'], true)
+            ) {
+                throw new RuntimeException(
+                    sprintf(
+                        'Passing extra expressions by associative array (`\'%s\' => \'%s\'`) ' .
+                        'is not allowed to avoid potential SQL injection. ' .
+                        'Use QueryExpression or numeric array instead.',
+                        $key,
+                        $val
+                    )
+                );
+            }
+        }
+
+        $this->_conditions = array_merge($this->_conditions, $conditions);
+    }
+}
diff --git a/vendor/cakephp/database/Expression/OrderClauseExpression.php b/vendor/cakephp/database/Expression/OrderClauseExpression.php
new file mode 100644
index 0000000..dfab8b9
--- /dev/null
+++ b/vendor/cakephp/database/Expression/OrderClauseExpression.php
@@ -0,0 +1,90 @@
+_field = $field;
+        $this->_direction = strtolower($direction) === 'asc' ? 'ASC' : 'DESC';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        /** @var \Cake\Database\ExpressionInterface|string $field */
+        $field = $this->_field;
+        if ($field instanceof Query) {
+            $field = sprintf('(%s)', $field->sql($binder));
+        } elseif ($field instanceof ExpressionInterface) {
+            $field = $field->sql($binder);
+        }
+
+        return sprintf('%s %s', $field, $this->_direction);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        if ($this->_field instanceof ExpressionInterface) {
+            $callback($this->_field);
+            $this->_field->traverse($callback);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Create a deep clone of the order clause.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        if ($this->_field instanceof ExpressionInterface) {
+            $this->_field = clone $this->_field;
+        }
+    }
+}
diff --git a/vendor/cakephp/database/Expression/QueryExpression.php b/vendor/cakephp/database/Expression/QueryExpression.php
new file mode 100644
index 0000000..0578c00
--- /dev/null
+++ b/vendor/cakephp/database/Expression/QueryExpression.php
@@ -0,0 +1,876 @@
+ :value"
+     *
+     * @var array
+     */
+    protected $_conditions = [];
+
+    /**
+     * Constructor. A new expression object can be created without any params and
+     * be built dynamically. Otherwise, it is possible to pass an array of conditions
+     * containing either a tree-like array structure to be parsed and/or other
+     * expression objects. Optionally, you can set the conjunction keyword to be used
+     * for joining each part of this level of the expression tree.
+     *
+     * @param \Cake\Database\ExpressionInterface|array|string $conditions Tree like array structure
+     * containing all the conditions to be added or nested inside this expression object.
+     * @param \Cake\Database\TypeMap|array $types Associative array of types to be associated with the values
+     * passed in $conditions.
+     * @param string $conjunction the glue that will join all the string conditions at this
+     * level of the expression tree. For example "AND", "OR", "XOR"...
+     * @see \Cake\Database\Expression\QueryExpression::add() for more details on $conditions and $types
+     */
+    public function __construct($conditions = [], $types = [], $conjunction = 'AND')
+    {
+        $this->setTypeMap($types);
+        $this->setConjunction(strtoupper($conjunction));
+        if (!empty($conditions)) {
+            $this->add($conditions, $this->getTypeMap()->getTypes());
+        }
+    }
+
+    /**
+     * Changes the conjunction for the conditions at this level of the expression tree.
+     *
+     * @param string $conjunction Value to be used for joining conditions
+     * @return $this
+     */
+    public function setConjunction(string $conjunction)
+    {
+        $this->_conjunction = strtoupper($conjunction);
+
+        return $this;
+    }
+
+    /**
+     * Gets the currently configured conjunction for the conditions at this level of the expression tree.
+     *
+     * @return string
+     */
+    public function getConjunction(): string
+    {
+        return $this->_conjunction;
+    }
+
+    /**
+     * Adds one or more conditions to this expression object. Conditions can be
+     * expressed in a one dimensional array, that will cause all conditions to
+     * be added directly at this level of the tree or they can be nested arbitrarily
+     * making it create more expression objects that will be nested inside and
+     * configured to use the specified conjunction.
+     *
+     * If the type passed for any of the fields is expressed "type[]" (note braces)
+     * then it will cause the placeholder to be re-written dynamically so if the
+     * value is an array, it will create as many placeholders as values are in it.
+     *
+     * @param \Cake\Database\ExpressionInterface|array|string $conditions single or multiple conditions to
+     * be added. When using an array and the key is 'OR' or 'AND' a new expression
+     * object will be created with that conjunction and internal array value passed
+     * as conditions.
+     * @param array $types Associative array of fields pointing to the type of the
+     * values that are being passed. Used for correctly binding values to statements.
+     * @see \Cake\Database\Query::where() for examples on conditions
+     * @return $this
+     */
+    public function add($conditions, array $types = [])
+    {
+        if (is_string($conditions) || $conditions instanceof ExpressionInterface) {
+            $this->_conditions[] = $conditions;
+
+            return $this;
+        }
+
+        $this->_addConditions($conditions, $types);
+
+        return $this;
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "field = value".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
+     * @param mixed $value The value to be bound to $field for comparison
+     * @param string|null $type the type name for $value as configured using the Type map.
+     * If it is suffixed with "[]" and the value is an array then multiple placeholders
+     * will be created, one per each value in the array.
+     * @return $this
+     */
+    public function eq($field, $value, ?string $type = null)
+    {
+        if ($type === null) {
+            $type = $this->_calculateType($field);
+        }
+
+        return $this->add(new ComparisonExpression($field, $value, $type, '='));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "field != value".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
+     * @param mixed $value The value to be bound to $field for comparison
+     * @param string|null $type the type name for $value as configured using the Type map.
+     * If it is suffixed with "[]" and the value is an array then multiple placeholders
+     * will be created, one per each value in the array.
+     * @return $this
+     */
+    public function notEq($field, $value, $type = null)
+    {
+        if ($type === null) {
+            $type = $this->_calculateType($field);
+        }
+
+        return $this->add(new ComparisonExpression($field, $value, $type, '!='));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "field > value".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
+     * @param mixed $value The value to be bound to $field for comparison
+     * @param string|null $type the type name for $value as configured using the Type map.
+     * @return $this
+     */
+    public function gt($field, $value, $type = null)
+    {
+        if ($type === null) {
+            $type = $this->_calculateType($field);
+        }
+
+        return $this->add(new ComparisonExpression($field, $value, $type, '>'));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "field < value".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
+     * @param mixed $value The value to be bound to $field for comparison
+     * @param string|null $type the type name for $value as configured using the Type map.
+     * @return $this
+     */
+    public function lt($field, $value, $type = null)
+    {
+        if ($type === null) {
+            $type = $this->_calculateType($field);
+        }
+
+        return $this->add(new ComparisonExpression($field, $value, $type, '<'));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "field >= value".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
+     * @param mixed $value The value to be bound to $field for comparison
+     * @param string|null $type the type name for $value as configured using the Type map.
+     * @return $this
+     */
+    public function gte($field, $value, $type = null)
+    {
+        if ($type === null) {
+            $type = $this->_calculateType($field);
+        }
+
+        return $this->add(new ComparisonExpression($field, $value, $type, '>='));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "field <= value".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
+     * @param mixed $value The value to be bound to $field for comparison
+     * @param string|null $type the type name for $value as configured using the Type map.
+     * @return $this
+     */
+    public function lte($field, $value, $type = null)
+    {
+        if ($type === null) {
+            $type = $this->_calculateType($field);
+        }
+
+        return $this->add(new ComparisonExpression($field, $value, $type, '<='));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "field IS NULL".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field database field to be
+     * tested for null
+     * @return $this
+     */
+    public function isNull($field)
+    {
+        if (!($field instanceof ExpressionInterface)) {
+            $field = new IdentifierExpression($field);
+        }
+
+        return $this->add(new UnaryExpression('IS NULL', $field, UnaryExpression::POSTFIX));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "field IS NOT NULL".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field database field to be
+     * tested for not null
+     * @return $this
+     */
+    public function isNotNull($field)
+    {
+        if (!($field instanceof ExpressionInterface)) {
+            $field = new IdentifierExpression($field);
+        }
+
+        return $this->add(new UnaryExpression('IS NOT NULL', $field, UnaryExpression::POSTFIX));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "field LIKE value".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
+     * @param mixed $value The value to be bound to $field for comparison
+     * @param string|null $type the type name for $value as configured using the Type map.
+     * @return $this
+     */
+    public function like($field, $value, $type = null)
+    {
+        if ($type === null) {
+            $type = $this->_calculateType($field);
+        }
+
+        return $this->add(new ComparisonExpression($field, $value, $type, 'LIKE'));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "field NOT LIKE value".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
+     * @param mixed $value The value to be bound to $field for comparison
+     * @param string|null $type the type name for $value as configured using the Type map.
+     * @return $this
+     */
+    public function notLike($field, $value, $type = null)
+    {
+        if ($type === null) {
+            $type = $this->_calculateType($field);
+        }
+
+        return $this->add(new ComparisonExpression($field, $value, $type, 'NOT LIKE'));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form
+     * "field IN (value1, value2)".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
+     * @param \Cake\Database\ExpressionInterface|array|string $values the value to be bound to $field for comparison
+     * @param string|null $type the type name for $value as configured using the Type map.
+     * @return $this
+     */
+    public function in($field, $values, $type = null)
+    {
+        if ($type === null) {
+            $type = $this->_calculateType($field);
+        }
+        $type = $type ?: 'string';
+        $type .= '[]';
+        $values = $values instanceof ExpressionInterface ? $values : (array)$values;
+
+        return $this->add(new ComparisonExpression($field, $values, $type, 'IN'));
+    }
+
+    /**
+     * Adds a new case expression to the expression object
+     *
+     * @param \Cake\Database\ExpressionInterface|array $conditions The conditions to test. Must be a ExpressionInterface
+     * instance, or an array of ExpressionInterface instances.
+     * @param \Cake\Database\ExpressionInterface|array $values Associative array of values to be associated with the
+     * conditions passed in $conditions. If there are more $values than $conditions,
+     * the last $value is used as the `ELSE` value.
+     * @param array $types Associative array of types to be associated with the values
+     * passed in $values
+     * @return $this
+     * @deprecated 4.3.0 Use QueryExpression::case() or CaseStatementExpression instead
+     */
+    public function addCase($conditions, $values = [], $types = [])
+    {
+        deprecationWarning('QueryExpression::addCase() is deprecated, use case() instead.');
+
+        return $this->add(new CaseExpression($conditions, $values, $types));
+    }
+
+    /**
+     * Returns a new case expression object.
+     *
+     * When a value is set, the syntax generated is
+     * `CASE case_value WHEN when_value ... END` (simple case),
+     * where the `when_value`'s are compared against the
+     * `case_value`.
+     *
+     * When no value is set, the syntax generated is
+     * `CASE WHEN when_conditions ... END` (searched case),
+     * where the conditions hold the comparisons.
+     *
+     * Note that `null` is a valid case value, and thus should
+     * only be passed if you actually want to create the simple
+     * case expression variant!
+     *
+     * @param \Cake\Database\ExpressionInterface|object|scalar|null $value The case value.
+     * @param string|null $type The case value type. If no type is provided, the type will be tried to be inferred
+     *  from the value.
+     * @return \Cake\Database\Expression\CaseStatementExpression
+     */
+    public function case($value = null, ?string $type = null): CaseStatementExpression
+    {
+        if (func_num_args() > 0) {
+            $expression = new CaseStatementExpression($value, $type);
+        } else {
+            $expression = new CaseStatementExpression();
+        }
+
+        return $expression->setTypeMap($this->getTypeMap());
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form
+     * "field NOT IN (value1, value2)".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
+     * @param \Cake\Database\ExpressionInterface|array|string $values the value to be bound to $field for comparison
+     * @param string|null $type the type name for $value as configured using the Type map.
+     * @return $this
+     */
+    public function notIn($field, $values, $type = null)
+    {
+        if ($type === null) {
+            $type = $this->_calculateType($field);
+        }
+        $type = $type ?: 'string';
+        $type .= '[]';
+        $values = $values instanceof ExpressionInterface ? $values : (array)$values;
+
+        return $this->add(new ComparisonExpression($field, $values, $type, 'NOT IN'));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form
+     * "(field NOT IN (value1, value2) OR field IS NULL".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
+     * @param \Cake\Database\ExpressionInterface|array|string $values the value to be bound to $field for comparison
+     * @param string|null $type the type name for $value as configured using the Type map.
+     * @return $this
+     */
+    public function notInOrNull($field, $values, ?string $type = null)
+    {
+        $or = new static([], [], 'OR');
+        $or
+            ->notIn($field, $values, $type)
+            ->isNull($field);
+
+        return $this->add($or);
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "EXISTS (...)".
+     *
+     * @param \Cake\Database\ExpressionInterface $expression the inner query
+     * @return $this
+     */
+    public function exists(ExpressionInterface $expression)
+    {
+        return $this->add(new UnaryExpression('EXISTS', $expression, UnaryExpression::PREFIX));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "NOT EXISTS (...)".
+     *
+     * @param \Cake\Database\ExpressionInterface $expression the inner query
+     * @return $this
+     */
+    public function notExists(ExpressionInterface $expression)
+    {
+        return $this->add(new UnaryExpression('NOT EXISTS', $expression, UnaryExpression::PREFIX));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form
+     * "field BETWEEN from AND to".
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field The field name to compare for values inbetween the range.
+     * @param mixed $from The initial value of the range.
+     * @param mixed $to The ending value in the comparison range.
+     * @param string|null $type the type name for $value as configured using the Type map.
+     * @return $this
+     */
+    public function between($field, $from, $to, $type = null)
+    {
+        if ($type === null) {
+            $type = $this->_calculateType($field);
+        }
+
+        return $this->add(new BetweenExpression($field, $from, $to, $type));
+    }
+
+    /**
+     * Returns a new QueryExpression object containing all the conditions passed
+     * and set up the conjunction to be "AND"
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions to be joined with AND
+     * @param array $types Associative array of fields pointing to the type of the
+     * values that are being passed. Used for correctly binding values to statements.
+     * @return \Cake\Database\Expression\QueryExpression
+     */
+    public function and($conditions, $types = [])
+    {
+        if ($conditions instanceof Closure) {
+            return $conditions(new static([], $this->getTypeMap()->setTypes($types)));
+        }
+
+        return new static($conditions, $this->getTypeMap()->setTypes($types));
+    }
+
+    /**
+     * Returns a new QueryExpression object containing all the conditions passed
+     * and set up the conjunction to be "OR"
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions to be joined with OR
+     * @param array $types Associative array of fields pointing to the type of the
+     * values that are being passed. Used for correctly binding values to statements.
+     * @return \Cake\Database\Expression\QueryExpression
+     */
+    public function or($conditions, $types = [])
+    {
+        if ($conditions instanceof Closure) {
+            return $conditions(new static([], $this->getTypeMap()->setTypes($types), 'OR'));
+        }
+
+        return new static($conditions, $this->getTypeMap()->setTypes($types), 'OR');
+    }
+
+    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
+
+    /**
+     * Returns a new QueryExpression object containing all the conditions passed
+     * and set up the conjunction to be "AND"
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions to be joined with AND
+     * @param array $types Associative array of fields pointing to the type of the
+     * values that are being passed. Used for correctly binding values to statements.
+     * @return \Cake\Database\Expression\QueryExpression
+     * @deprecated 4.0.0 Use {@link and()} instead.
+     */
+    public function and_($conditions, $types = [])
+    {
+        deprecationWarning('QueryExpression::and_() is deprecated use and() instead.');
+
+        return $this->and($conditions, $types);
+    }
+
+    /**
+     * Returns a new QueryExpression object containing all the conditions passed
+     * and set up the conjunction to be "OR"
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions to be joined with OR
+     * @param array $types Associative array of fields pointing to the type of the
+     * values that are being passed. Used for correctly binding values to statements.
+     * @return \Cake\Database\Expression\QueryExpression
+     * @deprecated 4.0.0 Use {@link or()} instead.
+     */
+    public function or_($conditions, $types = [])
+    {
+        deprecationWarning('QueryExpression::or_() is deprecated use or() instead.');
+
+        return $this->or($conditions, $types);
+    }
+
+    // phpcs:enable
+
+    /**
+     * Adds a new set of conditions to this level of the tree and negates
+     * the final result by prepending a NOT, it will look like
+     * "NOT ( (condition1) AND (conditions2) )" conjunction depends on the one
+     * currently configured for this object.
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions to be added and negated
+     * @param array $types Associative array of fields pointing to the type of the
+     * values that are being passed. Used for correctly binding values to statements.
+     * @return $this
+     */
+    public function not($conditions, $types = [])
+    {
+        return $this->add(['NOT' => $conditions], $types);
+    }
+
+    /**
+     * Returns the number of internal conditions that are stored in this expression.
+     * Useful to determine if this expression object is void or it will generate
+     * a non-empty string when compiled
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        return count($this->_conditions);
+    }
+
+    /**
+     * Builds equal condition or assignment with identifier wrapping.
+     *
+     * @param string $leftField Left join condition field name.
+     * @param string $rightField Right join condition field name.
+     * @return $this
+     */
+    public function equalFields(string $leftField, string $rightField)
+    {
+        $wrapIdentifier = function ($field) {
+            if ($field instanceof ExpressionInterface) {
+                return $field;
+            }
+
+            return new IdentifierExpression($field);
+        };
+
+        return $this->eq($wrapIdentifier($leftField), $wrapIdentifier($rightField));
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        $len = $this->count();
+        if ($len === 0) {
+            return '';
+        }
+        $conjunction = $this->_conjunction;
+        $template = $len === 1 ? '%s' : '(%s)';
+        $parts = [];
+        foreach ($this->_conditions as $part) {
+            if ($part instanceof Query) {
+                $part = '(' . $part->sql($binder) . ')';
+            } elseif ($part instanceof ExpressionInterface) {
+                $part = $part->sql($binder);
+            }
+            if ($part !== '') {
+                $parts[] = $part;
+            }
+        }
+
+        return sprintf($template, implode(" $conjunction ", $parts));
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        foreach ($this->_conditions as $c) {
+            if ($c instanceof ExpressionInterface) {
+                $callback($c);
+                $c->traverse($callback);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Executes a callable function for each of the parts that form this expression.
+     *
+     * The callable function is required to return a value with which the currently
+     * visited part will be replaced. If the callable function returns null then
+     * the part will be discarded completely from this expression.
+     *
+     * The callback function will receive each of the conditions as first param and
+     * the key as second param. It is possible to declare the second parameter as
+     * passed by reference, this will enable you to change the key under which the
+     * modified part is stored.
+     *
+     * @param callable $callback The callable to apply to each part.
+     * @return $this
+     */
+    public function iterateParts(callable $callback)
+    {
+        $parts = [];
+        foreach ($this->_conditions as $k => $c) {
+            $key = &$k;
+            $part = $callback($c, $key);
+            if ($part !== null) {
+                $parts[$key] = $part;
+            }
+        }
+        $this->_conditions = $parts;
+
+        return $this;
+    }
+
+    /**
+     * Check whether a callable is acceptable.
+     *
+     * We don't accept ['class', 'method'] style callbacks,
+     * as they often contain user input and arrays of strings
+     * are easy to sneak in.
+     *
+     * @param \Cake\Database\ExpressionInterface|callable|array|string $callable The callable to check.
+     * @return bool Valid callable.
+     * @deprecated 4.2.0 This method is unused.
+     * @codeCoverageIgnore
+     */
+    public function isCallable($callable): bool
+    {
+        if (is_string($callable)) {
+            return false;
+        }
+        if (is_object($callable) && is_callable($callable)) {
+            return true;
+        }
+
+        return is_array($callable) && isset($callable[0]) && is_object($callable[0]) && is_callable($callable);
+    }
+
+    /**
+     * Returns true if this expression contains any other nested
+     * ExpressionInterface objects
+     *
+     * @return bool
+     */
+    public function hasNestedExpression(): bool
+    {
+        foreach ($this->_conditions as $c) {
+            if ($c instanceof ExpressionInterface) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Auxiliary function used for decomposing a nested array of conditions and build
+     * a tree structure inside this object to represent the full SQL expression.
+     * String conditions are stored directly in the conditions, while any other
+     * representation is wrapped around an adequate instance or of this class.
+     *
+     * @param array $conditions list of conditions to be stored in this object
+     * @param array $types list of types associated on fields referenced in $conditions
+     * @return void
+     */
+    protected function _addConditions(array $conditions, array $types): void
+    {
+        $operators = ['and', 'or', 'xor'];
+
+        $typeMap = $this->getTypeMap()->setTypes($types);
+
+        foreach ($conditions as $k => $c) {
+            $numericKey = is_numeric($k);
+
+            if ($c instanceof Closure) {
+                $expr = new static([], $typeMap);
+                $c = $c($expr, $this);
+            }
+
+            if ($numericKey && empty($c)) {
+                continue;
+            }
+
+            $isArray = is_array($c);
+            $isOperator = $isNot = false;
+            if (!$numericKey) {
+                $normalizedKey = strtolower($k);
+                $isOperator = in_array($normalizedKey, $operators);
+                $isNot = $normalizedKey === 'not';
+            }
+
+            if (($isOperator || $isNot) && ($isArray || $c instanceof Countable) && count($c) === 0) {
+                continue;
+            }
+
+            if ($numericKey && $c instanceof ExpressionInterface) {
+                $this->_conditions[] = $c;
+                continue;
+            }
+
+            if ($numericKey && is_string($c)) {
+                $this->_conditions[] = $c;
+                continue;
+            }
+
+            if ($numericKey && $isArray || $isOperator) {
+                $this->_conditions[] = new static($c, $typeMap, $numericKey ? 'AND' : $k);
+                continue;
+            }
+
+            if ($isNot) {
+                $this->_conditions[] = new UnaryExpression('NOT', new static($c, $typeMap));
+                continue;
+            }
+
+            if (!$numericKey) {
+                $this->_conditions[] = $this->_parseCondition($k, $c);
+            }
+        }
+    }
+
+    /**
+     * Parses a string conditions by trying to extract the operator inside it if any
+     * and finally returning either an adequate QueryExpression object or a plain
+     * string representation of the condition. This function is responsible for
+     * generating the placeholders and replacing the values by them, while storing
+     * the value elsewhere for future binding.
+     *
+     * @param string $field The value from which the actual field and operator will
+     * be extracted.
+     * @param mixed $value The value to be bound to a placeholder for the field
+     * @return \Cake\Database\ExpressionInterface
+     * @throws \InvalidArgumentException If operator is invalid or missing on NULL usage.
+     */
+    protected function _parseCondition(string $field, $value)
+    {
+        $field = trim($field);
+        $operator = '=';
+        $expression = $field;
+
+        $spaces = substr_count($field, ' ');
+        // Handle field values that contain multiple spaces, such as
+        // operators with a space in them like `field IS NOT` and
+        // `field NOT LIKE`, or combinations with function expressions
+        // like `CONCAT(first_name, ' ', last_name) IN`.
+        if ($spaces > 1) {
+            $parts = explode(' ', $field);
+            if (preg_match('/(is not|not \w+)$/i', $field)) {
+                $last = array_pop($parts);
+                $second = array_pop($parts);
+                $parts[] = "{$second} {$last}";
+            }
+            $operator = array_pop($parts);
+            $expression = implode(' ', $parts);
+        } elseif ($spaces == 1) {
+            $parts = explode(' ', $field, 2);
+            [$expression, $operator] = $parts;
+        }
+        $operator = strtolower(trim($operator));
+        $type = $this->getTypeMap()->type($expression);
+
+        $typeMultiple = (is_string($type) && strpos($type, '[]') !== false);
+        if (in_array($operator, ['in', 'not in']) || $typeMultiple) {
+            $type = $type ?: 'string';
+            if (!$typeMultiple) {
+                $type .= '[]';
+            }
+            $operator = $operator === '=' ? 'IN' : $operator;
+            $operator = $operator === '!=' ? 'NOT IN' : $operator;
+            $typeMultiple = true;
+        }
+
+        if ($typeMultiple) {
+            $value = $value instanceof ExpressionInterface ? $value : (array)$value;
+        }
+
+        if ($operator === 'is' && $value === null) {
+            return new UnaryExpression(
+                'IS NULL',
+                new IdentifierExpression($expression),
+                UnaryExpression::POSTFIX
+            );
+        }
+
+        if ($operator === 'is not' && $value === null) {
+            return new UnaryExpression(
+                'IS NOT NULL',
+                new IdentifierExpression($expression),
+                UnaryExpression::POSTFIX
+            );
+        }
+
+        if ($operator === 'is' && $value !== null) {
+            $operator = '=';
+        }
+
+        if ($operator === 'is not' && $value !== null) {
+            $operator = '!=';
+        }
+
+        if ($value === null && $this->_conjunction !== ',') {
+            throw new InvalidArgumentException(
+                sprintf('Expression `%s` is missing operator (IS, IS NOT) with `null` value.', $expression)
+            );
+        }
+
+        return new ComparisonExpression($expression, $value, $type, $operator);
+    }
+
+    /**
+     * Returns the type name for the passed field if it was stored in the typeMap
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field The field name to get a type for.
+     * @return string|null The computed type or null, if the type is unknown.
+     */
+    protected function _calculateType($field): ?string
+    {
+        $field = $field instanceof IdentifierExpression ? $field->getIdentifier() : $field;
+        if (is_string($field)) {
+            return $this->getTypeMap()->type($field);
+        }
+
+        return null;
+    }
+
+    /**
+     * Clone this object and its subtree of expressions.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        foreach ($this->_conditions as $i => $condition) {
+            if ($condition instanceof ExpressionInterface) {
+                $this->_conditions[$i] = clone $condition;
+            }
+        }
+    }
+}
diff --git a/vendor/cakephp/database/Expression/StringExpression.php b/vendor/cakephp/database/Expression/StringExpression.php
new file mode 100644
index 0000000..e438074
--- /dev/null
+++ b/vendor/cakephp/database/Expression/StringExpression.php
@@ -0,0 +1,87 @@
+string = $string;
+        $this->collation = $collation;
+    }
+
+    /**
+     * Sets the string collation.
+     *
+     * @param string $collation String collation
+     * @return void
+     */
+    public function setCollation(string $collation): void
+    {
+        $this->collation = $collation;
+    }
+
+    /**
+     * Returns the string collation.
+     *
+     * @return string
+     */
+    public function getCollation(): string
+    {
+        return $this->collation;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        $placeholder = $binder->placeholder('c');
+        $binder->bind($placeholder, $this->string, 'string');
+
+        return $placeholder . ' COLLATE ' . $this->collation;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        return $this;
+    }
+}
diff --git a/vendor/cakephp/database/Expression/TupleComparison.php b/vendor/cakephp/database/Expression/TupleComparison.php
new file mode 100644
index 0000000..b5c36f0
--- /dev/null
+++ b/vendor/cakephp/database/Expression/TupleComparison.php
@@ -0,0 +1,231 @@
+
+     * @psalm-suppress NonInvariantDocblockPropertyType
+     */
+    protected $_type;
+
+    /**
+     * Constructor
+     *
+     * @param \Cake\Database\ExpressionInterface|array|string $fields the fields to use to form a tuple
+     * @param \Cake\Database\ExpressionInterface|array $values the values to use to form a tuple
+     * @param array $types the types names to use for casting each of the values, only
+     * one type per position in the value array in needed
+     * @param string $conjunction the operator used for comparing field and value
+     */
+    public function __construct($fields, $values, array $types = [], string $conjunction = '=')
+    {
+        $this->_type = $types;
+        $this->setField($fields);
+        $this->_operator = $conjunction;
+        $this->setValue($values);
+    }
+
+    /**
+     * Returns the type to be used for casting the value to a database representation
+     *
+     * @return array
+     */
+    public function getType(): array
+    {
+        return $this->_type;
+    }
+
+    /**
+     * Sets the value
+     *
+     * @param mixed $value The value to compare
+     * @return void
+     */
+    public function setValue($value): void
+    {
+        if ($this->isMulti()) {
+            if (is_array($value) && !is_array(current($value))) {
+                throw new InvalidArgumentException(
+                    'Multi-tuple comparisons require a multi-tuple value, single-tuple given.'
+                );
+            }
+        } else {
+            if (is_array($value) && is_array(current($value))) {
+                throw new InvalidArgumentException(
+                    'Single-tuple comparisons require a single-tuple value, multi-tuple given.'
+                );
+            }
+        }
+
+        $this->_value = $value;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        $template = '(%s) %s (%s)';
+        $fields = [];
+        $originalFields = $this->getField();
+
+        if (!is_array($originalFields)) {
+            $originalFields = [$originalFields];
+        }
+
+        foreach ($originalFields as $field) {
+            $fields[] = $field instanceof ExpressionInterface ? $field->sql($binder) : $field;
+        }
+
+        $values = $this->_stringifyValues($binder);
+
+        $field = implode(', ', $fields);
+
+        return sprintf($template, $field, $this->_operator, $values);
+    }
+
+    /**
+     * Returns a string with the values as placeholders in a string to be used
+     * for the SQL version of this expression
+     *
+     * @param \Cake\Database\ValueBinder $binder The value binder to convert expressions with.
+     * @return string
+     */
+    protected function _stringifyValues(ValueBinder $binder): string
+    {
+        $values = [];
+        $parts = $this->getValue();
+
+        if ($parts instanceof ExpressionInterface) {
+            return $parts->sql($binder);
+        }
+
+        foreach ($parts as $i => $value) {
+            if ($value instanceof ExpressionInterface) {
+                $values[] = $value->sql($binder);
+                continue;
+            }
+
+            $type = $this->_type;
+            $isMultiOperation = $this->isMulti();
+            if (empty($type)) {
+                $type = null;
+            }
+
+            if ($isMultiOperation) {
+                $bound = [];
+                foreach ($value as $k => $val) {
+                    /** @var string $valType */
+                    $valType = $type && isset($type[$k]) ? $type[$k] : $type;
+                    $bound[] = $this->_bindValue($val, $binder, $valType);
+                }
+
+                $values[] = sprintf('(%s)', implode(',', $bound));
+                continue;
+            }
+
+            /** @var string $valType */
+            $valType = $type && isset($type[$i]) ? $type[$i] : $type;
+            $values[] = $this->_bindValue($value, $binder, $valType);
+        }
+
+        return implode(', ', $values);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function _bindValue($value, ValueBinder $binder, ?string $type = null): string
+    {
+        $placeholder = $binder->placeholder('tuple');
+        $binder->bind($placeholder, $value, $type);
+
+        return $placeholder;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        /** @var array $fields */
+        $fields = $this->getField();
+        foreach ($fields as $field) {
+            $this->_traverseValue($field, $callback);
+        }
+
+        $value = $this->getValue();
+        if ($value instanceof ExpressionInterface) {
+            $callback($value);
+            $value->traverse($callback);
+
+            return $this;
+        }
+
+        foreach ($value as $val) {
+            if ($this->isMulti()) {
+                foreach ($val as $v) {
+                    $this->_traverseValue($v, $callback);
+                }
+            } else {
+                $this->_traverseValue($val, $callback);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Conditionally executes the callback for the passed value if
+     * it is an ExpressionInterface
+     *
+     * @param mixed $value The value to traverse
+     * @param \Closure $callback The callable to use when traversing
+     * @return void
+     */
+    protected function _traverseValue($value, Closure $callback): void
+    {
+        if ($value instanceof ExpressionInterface) {
+            $callback($value);
+            $value->traverse($callback);
+        }
+    }
+
+    /**
+     * Determines if each of the values in this expressions is a tuple in
+     * itself
+     *
+     * @return bool
+     */
+    public function isMulti(): bool
+    {
+        return in_array(strtolower($this->_operator), ['in', 'not in']);
+    }
+}
diff --git a/vendor/cakephp/database/Expression/UnaryExpression.php b/vendor/cakephp/database/Expression/UnaryExpression.php
new file mode 100644
index 0000000..1c40f91
--- /dev/null
+++ b/vendor/cakephp/database/Expression/UnaryExpression.php
@@ -0,0 +1,118 @@
+_operator = $operator;
+        $this->_value = $value;
+        $this->position = $position;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        $operand = $this->_value;
+        if ($operand instanceof ExpressionInterface) {
+            $operand = $operand->sql($binder);
+        }
+
+        if ($this->position === self::POSTFIX) {
+            return '(' . $operand . ') ' . $this->_operator;
+        }
+
+        return $this->_operator . ' (' . $operand . ')';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        if ($this->_value instanceof ExpressionInterface) {
+            $callback($this->_value);
+            $this->_value->traverse($callback);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Perform a deep clone of the inner expression.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        if ($this->_value instanceof ExpressionInterface) {
+            $this->_value = clone $this->_value;
+        }
+    }
+}
diff --git a/vendor/cakephp/database/Expression/ValuesExpression.php b/vendor/cakephp/database/Expression/ValuesExpression.php
new file mode 100644
index 0000000..68dbf25
--- /dev/null
+++ b/vendor/cakephp/database/Expression/ValuesExpression.php
@@ -0,0 +1,325 @@
+ type names
+     */
+    public function __construct(array $columns, TypeMap $typeMap)
+    {
+        $this->_columns = $columns;
+        $this->setTypeMap($typeMap);
+    }
+
+    /**
+     * Add a row of data to be inserted.
+     *
+     * @param \Cake\Database\Query|array $values Array of data to append into the insert, or
+     *   a query for doing INSERT INTO .. SELECT style commands
+     * @return void
+     * @throws \Cake\Database\Exception\DatabaseException When mixing array + Query data types.
+     */
+    public function add($values): void
+    {
+        if (
+            (
+                count($this->_values) &&
+                $values instanceof Query
+            ) ||
+            (
+                $this->_query &&
+                is_array($values)
+            )
+        ) {
+            throw new DatabaseException(
+                'You cannot mix subqueries and array values in inserts.'
+            );
+        }
+        if ($values instanceof Query) {
+            $this->setQuery($values);
+
+            return;
+        }
+        $this->_values[] = $values;
+        $this->_castedExpressions = false;
+    }
+
+    /**
+     * Sets the columns to be inserted.
+     *
+     * @param array $columns Array with columns to be inserted.
+     * @return $this
+     */
+    public function setColumns(array $columns)
+    {
+        $this->_columns = $columns;
+        $this->_castedExpressions = false;
+
+        return $this;
+    }
+
+    /**
+     * Gets the columns to be inserted.
+     *
+     * @return array
+     */
+    public function getColumns(): array
+    {
+        return $this->_columns;
+    }
+
+    /**
+     * Get the bare column names.
+     *
+     * Because column names could be identifier quoted, we
+     * need to strip the identifiers off of the columns.
+     *
+     * @return array
+     */
+    protected function _columnNames(): array
+    {
+        $columns = [];
+        foreach ($this->_columns as $col) {
+            if (is_string($col)) {
+                $col = trim($col, '`[]"');
+            }
+            $columns[] = $col;
+        }
+
+        return $columns;
+    }
+
+    /**
+     * Sets the values to be inserted.
+     *
+     * @param array $values Array with values to be inserted.
+     * @return $this
+     */
+    public function setValues(array $values)
+    {
+        $this->_values = $values;
+        $this->_castedExpressions = false;
+
+        return $this;
+    }
+
+    /**
+     * Gets the values to be inserted.
+     *
+     * @return array
+     */
+    public function getValues(): array
+    {
+        if (!$this->_castedExpressions) {
+            $this->_processExpressions();
+        }
+
+        return $this->_values;
+    }
+
+    /**
+     * Sets the query object to be used as the values expression to be evaluated
+     * to insert records in the table.
+     *
+     * @param \Cake\Database\Query $query The query to set
+     * @return $this
+     */
+    public function setQuery(Query $query)
+    {
+        $this->_query = $query;
+
+        return $this;
+    }
+
+    /**
+     * Gets the query object to be used as the values expression to be evaluated
+     * to insert records in the table.
+     *
+     * @return \Cake\Database\Query|null
+     */
+    public function getQuery(): ?Query
+    {
+        return $this->_query;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        if (empty($this->_values) && empty($this->_query)) {
+            return '';
+        }
+
+        if (!$this->_castedExpressions) {
+            $this->_processExpressions();
+        }
+
+        $columns = $this->_columnNames();
+        $defaults = array_fill_keys($columns, null);
+        $placeholders = [];
+
+        $types = [];
+        $typeMap = $this->getTypeMap();
+        foreach ($defaults as $col => $v) {
+            $types[$col] = $typeMap->type($col);
+        }
+
+        foreach ($this->_values as $row) {
+            $row += $defaults;
+            $rowPlaceholders = [];
+
+            foreach ($columns as $column) {
+                $value = $row[$column];
+
+                if ($value instanceof ExpressionInterface) {
+                    $rowPlaceholders[] = '(' . $value->sql($binder) . ')';
+                    continue;
+                }
+
+                $placeholder = $binder->placeholder('c');
+                $rowPlaceholders[] = $placeholder;
+                $binder->bind($placeholder, $value, $types[$column]);
+            }
+
+            $placeholders[] = implode(', ', $rowPlaceholders);
+        }
+
+        $query = $this->getQuery();
+        if ($query) {
+            return ' ' . $query->sql($binder);
+        }
+
+        return sprintf(' VALUES (%s)', implode('), (', $placeholders));
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        if ($this->_query) {
+            return $this;
+        }
+
+        if (!$this->_castedExpressions) {
+            $this->_processExpressions();
+        }
+
+        foreach ($this->_values as $v) {
+            if ($v instanceof ExpressionInterface) {
+                $v->traverse($callback);
+            }
+            if (!is_array($v)) {
+                continue;
+            }
+            foreach ($v as $field) {
+                if ($field instanceof ExpressionInterface) {
+                    $callback($field);
+                    $field->traverse($callback);
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Converts values that need to be casted to expressions
+     *
+     * @return void
+     */
+    protected function _processExpressions(): void
+    {
+        $types = [];
+        $typeMap = $this->getTypeMap();
+
+        $columns = $this->_columnNames();
+        foreach ($columns as $c) {
+            if (!is_string($c) && !is_int($c)) {
+                continue;
+            }
+            $types[$c] = $typeMap->type($c);
+        }
+
+        $types = $this->_requiresToExpressionCasting($types);
+
+        if (empty($types)) {
+            return;
+        }
+
+        foreach ($this->_values as $row => $values) {
+            foreach ($types as $col => $type) {
+                /** @var \Cake\Database\Type\ExpressionTypeInterface $type */
+                $this->_values[$row][$col] = $type->toExpression($values[$col]);
+            }
+        }
+        $this->_castedExpressions = true;
+    }
+}
diff --git a/vendor/cakephp/database/Expression/WhenThenExpression.php b/vendor/cakephp/database/Expression/WhenThenExpression.php
new file mode 100644
index 0000000..bbc4154
--- /dev/null
+++ b/vendor/cakephp/database/Expression/WhenThenExpression.php
@@ -0,0 +1,350 @@
+
+     */
+    protected $validClauseNames = [
+        'when',
+        'then',
+    ];
+
+    /**
+     * The type map to use when using an array of conditions for the
+     * `WHEN` value.
+     *
+     * @var \Cake\Database\TypeMap
+     */
+    protected $_typeMap;
+
+    /**
+     * Then `WHEN` value.
+     *
+     * @var \Cake\Database\ExpressionInterface|object|scalar|null
+     */
+    protected $when = null;
+
+    /**
+     * The `WHEN` value type.
+     *
+     * @var array|string|null
+     */
+    protected $whenType = null;
+
+    /**
+     * The `THEN` value.
+     *
+     * @var \Cake\Database\ExpressionInterface|object|scalar|null
+     */
+    protected $then = null;
+
+    /**
+     * Whether the `THEN` value has been defined, eg whether `then()`
+     * has been invoked.
+     *
+     * @var bool
+     */
+    protected $hasThenBeenDefined = false;
+
+    /**
+     * The `THEN` result type.
+     *
+     * @var string|null
+     */
+    protected $thenType = null;
+
+    /**
+     * Constructor.
+     *
+     * @param \Cake\Database\TypeMap|null $typeMap The type map to use when using an array of conditions for the `WHEN`
+     *  value.
+     */
+    public function __construct(?TypeMap $typeMap = null)
+    {
+        if ($typeMap === null) {
+            $typeMap = new TypeMap();
+        }
+        $this->_typeMap = $typeMap;
+    }
+
+    /**
+     * Sets the `WHEN` value.
+     *
+     * @param \Cake\Database\ExpressionInterface|object|array|scalar $when The `WHEN` value. When using an array of
+     *  conditions, it must be compatible with `\Cake\Database\Query::where()`. Note that this argument is _not_
+     *  completely safe for use with user data, as a user supplied array would allow for raw SQL to slip in! If you
+     *  plan to use user data, either pass a single type for the `$type` argument (which forces the `$when` value to be
+     *  a non-array, and then always binds the data), use a conditions array where the user data is only passed on the
+     *  value side of the array entries, or custom bindings!
+     * @param array|string|null $type The when value type. Either an associative array when using array style
+     *  conditions, or else a string. If no type is provided, the type will be tried to be inferred from the value.
+     * @return $this
+     * @throws \InvalidArgumentException In case the `$when` argument is neither a non-empty array, nor a scalar value,
+     *  an object, or an instance of `\Cake\Database\ExpressionInterface`.
+     * @throws \InvalidArgumentException In case the `$type` argument is neither an array, a string, nor null.
+     * @throws \InvalidArgumentException In case the `$when` argument is an array, and the `$type` argument is neither
+     * an array, nor null.
+     * @throws \InvalidArgumentException In case the `$when` argument is a non-array value, and the `$type` argument is
+     * neither a string, nor null.
+     * @see CaseStatementExpression::when() for a more detailed usage explanation.
+     */
+    public function when($when, $type = null)
+    {
+        if (
+            !(is_array($when) && !empty($when)) &&
+            !is_scalar($when) &&
+            !is_object($when)
+        ) {
+            throw new InvalidArgumentException(sprintf(
+                'The `$when` argument must be either a non-empty array, a scalar value, an object, ' .
+                'or an instance of `\%s`, `%s` given.',
+                ExpressionInterface::class,
+                is_array($when) ? '[]' : getTypeName($when)
+            ));
+        }
+
+        if (
+            $type !== null &&
+            !is_array($type) &&
+            !is_string($type)
+        ) {
+            throw new InvalidArgumentException(sprintf(
+                'The `$type` argument must be either an array, a string, or `null`, `%s` given.',
+                getTypeName($type)
+            ));
+        }
+
+        if (is_array($when)) {
+            if (
+                $type !== null &&
+                !is_array($type)
+            ) {
+                throw new InvalidArgumentException(sprintf(
+                    'When using an array for the `$when` argument, the `$type` argument must be an ' .
+                    'array too, `%s` given.',
+                    getTypeName($type)
+                ));
+            }
+
+            // avoid dirtying the type map for possible consecutive `when()` calls
+            $typeMap = clone $this->_typeMap;
+            if (
+                is_array($type) &&
+                count($type) > 0
+            ) {
+                $typeMap = $typeMap->setTypes($type);
+            }
+
+            $when = new QueryExpression($when, $typeMap);
+        } else {
+            if (
+                $type !== null &&
+                !is_string($type)
+            ) {
+                throw new InvalidArgumentException(sprintf(
+                    'When using a non-array value for the `$when` argument, the `$type` argument must ' .
+                    'be a string, `%s` given.',
+                    getTypeName($type)
+                ));
+            }
+
+            if (
+                $type === null &&
+                !($when instanceof ExpressionInterface)
+            ) {
+                $type = $this->inferType($when);
+            }
+        }
+
+        $this->when = $when;
+        $this->whenType = $type;
+
+        return $this;
+    }
+
+    /**
+     * Sets the `THEN` result value.
+     *
+     * @param \Cake\Database\ExpressionInterface|object|scalar|null $result The result value.
+     * @param string|null $type The result type. If no type is provided, the type will be inferred from the given
+     *  result value.
+     * @return $this
+     */
+    public function then($result, ?string $type = null)
+    {
+        if (
+            $result !== null &&
+            !is_scalar($result) &&
+            !(is_object($result) && !($result instanceof Closure))
+        ) {
+            throw new InvalidArgumentException(sprintf(
+                'The `$result` argument must be either `null`, a scalar value, an object, ' .
+                'or an instance of `\%s`, `%s` given.',
+                ExpressionInterface::class,
+                getTypeName($result)
+            ));
+        }
+
+        $this->then = $result;
+
+        if ($type === null) {
+            $type = $this->inferType($result);
+        }
+
+        $this->thenType = $type;
+
+        $this->hasThenBeenDefined = true;
+
+        return $this;
+    }
+
+    /**
+     * Returns the expression's result value type.
+     *
+     * @return string|null
+     * @see WhenThenExpression::then()
+     */
+    public function getResultType(): ?string
+    {
+        return $this->thenType;
+    }
+
+    /**
+     * Returns the available data for the given clause.
+     *
+     * ### Available clauses
+     *
+     * The following clause names are available:
+     *
+     * * `when`: The `WHEN` value.
+     * * `then`: The `THEN` result value.
+     *
+     * @param string $clause The name of the clause to obtain.
+     * @return \Cake\Database\ExpressionInterface|object|scalar|null
+     * @throws \InvalidArgumentException In case the given clause name is invalid.
+     */
+    public function clause(string $clause)
+    {
+        if (!in_array($clause, $this->validClauseNames, true)) {
+            throw new InvalidArgumentException(
+                sprintf(
+                    'The `$clause` argument must be one of `%s`, the given value `%s` is invalid.',
+                    implode('`, `', $this->validClauseNames),
+                    $clause
+                )
+            );
+        }
+
+        return $this->{$clause};
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        if ($this->when === null) {
+            throw new LogicException('Case expression has incomplete when clause. Missing `when()`.');
+        }
+
+        if (!$this->hasThenBeenDefined) {
+            throw new LogicException('Case expression has incomplete when clause. Missing `then()` after `when()`.');
+        }
+
+        $when = $this->when;
+        if (
+            is_string($this->whenType) &&
+            !($when instanceof ExpressionInterface)
+        ) {
+            $when = $this->_castToExpression($when, $this->whenType);
+        }
+        if ($when instanceof Query) {
+            $when = sprintf('(%s)', $when->sql($binder));
+        } elseif ($when instanceof ExpressionInterface) {
+            $when = $when->sql($binder);
+        } else {
+            $placeholder = $binder->placeholder('c');
+            if (is_string($this->whenType)) {
+                $whenType = $this->whenType;
+            } else {
+                $whenType = null;
+            }
+            $binder->bind($placeholder, $when, $whenType);
+            $when = $placeholder;
+        }
+
+        $then = $this->compileNullableValue($binder, $this->then, $this->thenType);
+
+        return "WHEN $when THEN $then";
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        if ($this->when instanceof ExpressionInterface) {
+            $callback($this->when);
+            $this->when->traverse($callback);
+        }
+
+        if ($this->then instanceof ExpressionInterface) {
+            $callback($this->then);
+            $this->then->traverse($callback);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Clones the inner expression objects.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        if ($this->when instanceof ExpressionInterface) {
+            $this->when = clone $this->when;
+        }
+
+        if ($this->then instanceof ExpressionInterface) {
+            $this->then = clone $this->then;
+        }
+    }
+}
diff --git a/vendor/cakephp/database/Expression/WindowExpression.php b/vendor/cakephp/database/Expression/WindowExpression.php
new file mode 100644
index 0000000..383e652
--- /dev/null
+++ b/vendor/cakephp/database/Expression/WindowExpression.php
@@ -0,0 +1,335 @@
+
+     */
+    protected $partitions = [];
+
+    /**
+     * @var \Cake\Database\Expression\OrderByExpression|null
+     */
+    protected $order;
+
+    /**
+     * @var array|null
+     */
+    protected $frame;
+
+    /**
+     * @var string|null
+     */
+    protected $exclusion;
+
+    /**
+     * @param string $name Window name
+     */
+    public function __construct(string $name = '')
+    {
+        $this->name = new IdentifierExpression($name);
+    }
+
+    /**
+     * Return whether is only a named window expression.
+     *
+     * These window expressions only specify a named window and do not
+     * specify their own partitions, frame or order.
+     *
+     * @return bool
+     */
+    public function isNamedOnly(): bool
+    {
+        return $this->name->getIdentifier() && (!$this->partitions && !$this->frame && !$this->order);
+    }
+
+    /**
+     * Sets the window name.
+     *
+     * @param string $name Window name
+     * @return $this
+     */
+    public function name(string $name)
+    {
+        $this->name = new IdentifierExpression($name);
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function partition($partitions)
+    {
+        if (!$partitions) {
+            return $this;
+        }
+
+        if ($partitions instanceof Closure) {
+            $partitions = $partitions(new QueryExpression([], [], ''));
+        }
+
+        if (!is_array($partitions)) {
+            $partitions = [$partitions];
+        }
+
+        foreach ($partitions as &$partition) {
+            if (is_string($partition)) {
+                $partition = new IdentifierExpression($partition);
+            }
+        }
+
+        $this->partitions = array_merge($this->partitions, $partitions);
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function order($fields)
+    {
+        if (!$fields) {
+            return $this;
+        }
+
+        if ($this->order === null) {
+            $this->order = new OrderByExpression();
+        }
+
+        if ($fields instanceof Closure) {
+            $fields = $fields(new QueryExpression([], [], ''));
+        }
+
+        $this->order->add($fields);
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function range($start, $end = 0)
+    {
+        return $this->frame(self::RANGE, $start, self::PRECEDING, $end, self::FOLLOWING);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function rows(?int $start, ?int $end = 0)
+    {
+        return $this->frame(self::ROWS, $start, self::PRECEDING, $end, self::FOLLOWING);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function groups(?int $start, ?int $end = 0)
+    {
+        return $this->frame(self::GROUPS, $start, self::PRECEDING, $end, self::FOLLOWING);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function frame(
+        string $type,
+        $startOffset,
+        string $startDirection,
+        $endOffset,
+        string $endDirection
+    ) {
+        $this->frame = [
+            'type' => $type,
+            'start' => [
+                'offset' => $startOffset,
+                'direction' => $startDirection,
+            ],
+            'end' => [
+                'offset' => $endOffset,
+                'direction' => $endDirection,
+            ],
+        ];
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function excludeCurrent()
+    {
+        $this->exclusion = 'CURRENT ROW';
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function excludeGroup()
+    {
+        $this->exclusion = 'GROUP';
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function excludeTies()
+    {
+        $this->exclusion = 'TIES';
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function sql(ValueBinder $binder): string
+    {
+        $clauses = [];
+        if ($this->name->getIdentifier()) {
+            $clauses[] = $this->name->sql($binder);
+        }
+
+        if ($this->partitions) {
+            $expressions = [];
+            foreach ($this->partitions as $partition) {
+                $expressions[] = $partition->sql($binder);
+            }
+
+            $clauses[] = 'PARTITION BY ' . implode(', ', $expressions);
+        }
+
+        if ($this->order) {
+            $clauses[] = $this->order->sql($binder);
+        }
+
+        if ($this->frame) {
+            $start = $this->buildOffsetSql(
+                $binder,
+                $this->frame['start']['offset'],
+                $this->frame['start']['direction']
+            );
+            $end = $this->buildOffsetSql(
+                $binder,
+                $this->frame['end']['offset'],
+                $this->frame['end']['direction']
+            );
+
+            $frameSql = sprintf('%s BETWEEN %s AND %s', $this->frame['type'], $start, $end);
+
+            if ($this->exclusion !== null) {
+                $frameSql .= ' EXCLUDE ' . $this->exclusion;
+            }
+
+            $clauses[] = $frameSql;
+        }
+
+        return implode(' ', $clauses);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function traverse(Closure $callback)
+    {
+        $callback($this->name);
+        foreach ($this->partitions as $partition) {
+            $callback($partition);
+            $partition->traverse($callback);
+        }
+
+        if ($this->order) {
+            $callback($this->order);
+            $this->order->traverse($callback);
+        }
+
+        if ($this->frame !== null) {
+            $offset = $this->frame['start']['offset'];
+            if ($offset instanceof ExpressionInterface) {
+                $callback($offset);
+                $offset->traverse($callback);
+            }
+            $offset = $this->frame['end']['offset'] ?? null;
+            if ($offset instanceof ExpressionInterface) {
+                $callback($offset);
+                $offset->traverse($callback);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Builds frame offset sql.
+     *
+     * @param \Cake\Database\ValueBinder $binder Value binder
+     * @param \Cake\Database\ExpressionInterface|string|int|null $offset Frame offset
+     * @param string $direction Frame offset direction
+     * @return string
+     */
+    protected function buildOffsetSql(ValueBinder $binder, $offset, string $direction): string
+    {
+        if ($offset === 0) {
+            return 'CURRENT ROW';
+        }
+
+        if ($offset instanceof ExpressionInterface) {
+            $offset = $offset->sql($binder);
+        }
+
+        return sprintf(
+            '%s %s',
+            $offset ?? 'UNBOUNDED',
+            $direction
+        );
+    }
+
+    /**
+     * Clone this object and its subtree of expressions.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        $this->name = clone $this->name;
+        foreach ($this->partitions as $i => $partition) {
+            $this->partitions[$i] = clone $partition;
+        }
+        if ($this->order !== null) {
+            $this->order = clone $this->order;
+        }
+    }
+}
diff --git a/vendor/cakephp/database/Expression/WindowInterface.php b/vendor/cakephp/database/Expression/WindowInterface.php
new file mode 100644
index 0000000..7a7bac5
--- /dev/null
+++ b/vendor/cakephp/database/Expression/WindowInterface.php
@@ -0,0 +1,163 @@
+|string $partitions Partition expressions
+     * @return $this
+     */
+    public function partition($partitions);
+
+    /**
+     * Adds one or more order clauses to the window.
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|array<\Cake\Database\ExpressionInterface|string>|string $fields Order expressions
+     * @return $this
+     */
+    public function order($fields);
+
+    /**
+     * Adds a simple range frame to the window.
+     *
+     * `$start`:
+     *  - `0` - 'CURRENT ROW'
+     *  - `null` - 'UNBOUNDED PRECEDING'
+     *  - offset - 'offset PRECEDING'
+     *
+     * `$end`:
+     *  - `0` - 'CURRENT ROW'
+     *  - `null` - 'UNBOUNDED FOLLOWING'
+     *  - offset - 'offset FOLLOWING'
+     *
+     * If you need to use 'FOLLOWING' with frame start or
+     * 'PRECEDING' with frame end, use `frame()` instead.
+     *
+     * @param \Cake\Database\ExpressionInterface|string|int|null $start Frame start
+     * @param \Cake\Database\ExpressionInterface|string|int|null $end Frame end
+     *  If not passed in, only frame start SQL will be generated.
+     * @return $this
+     */
+    public function range($start, $end = 0);
+
+    /**
+     * Adds a simple rows frame to the window.
+     *
+     * See `range()` for details.
+     *
+     * @param int|null $start Frame start
+     * @param int|null $end Frame end
+     *  If not passed in, only frame start SQL will be generated.
+     * @return $this
+     */
+    public function rows(?int $start, ?int $end = 0);
+
+    /**
+     * Adds a simple groups frame to the window.
+     *
+     * See `range()` for details.
+     *
+     * @param int|null $start Frame start
+     * @param int|null $end Frame end
+     *  If not passed in, only frame start SQL will be generated.
+     * @return $this
+     */
+    public function groups(?int $start, ?int $end = 0);
+
+    /**
+     * Adds a frame to the window.
+     *
+     * Use the `range()`, `rows()` or `groups()` helpers if you need simple
+     * 'BETWEEN offset PRECEDING and offset FOLLOWING' frames.
+     *
+     * You can specify any direction for both frame start and frame end.
+     *
+     * With both `$startOffset` and `$endOffset`:
+     *  - `0` - 'CURRENT ROW'
+     *  - `null` - 'UNBOUNDED'
+     *
+     * @param string $type Frame type
+     * @param \Cake\Database\ExpressionInterface|string|int|null $startOffset Frame start offset
+     * @param string $startDirection Frame start direction
+     * @param \Cake\Database\ExpressionInterface|string|int|null $endOffset Frame end offset
+     * @param string $endDirection Frame end direction
+     * @return $this
+     * @throws \InvalidArgumentException WHen offsets are negative.
+     * @psalm-param self::RANGE|self::ROWS|self::GROUPS $type
+     * @psalm-param self::PRECEDING|self::FOLLOWING $startDirection
+     * @psalm-param self::PRECEDING|self::FOLLOWING $endDirection
+     */
+    public function frame(
+        string $type,
+        $startOffset,
+        string $startDirection,
+        $endOffset,
+        string $endDirection
+    );
+
+    /**
+     * Adds current row frame exclusion.
+     *
+     * @return $this
+     */
+    public function excludeCurrent();
+
+    /**
+     * Adds group frame exclusion.
+     *
+     * @return $this
+     */
+    public function excludeGroup();
+
+    /**
+     * Adds ties frame exclusion.
+     *
+     * @return $this
+     */
+    public function excludeTies();
+}
diff --git a/vendor/cakephp/database/ExpressionInterface.php b/vendor/cakephp/database/ExpressionInterface.php
new file mode 100644
index 0000000..3e3588c
--- /dev/null
+++ b/vendor/cakephp/database/ExpressionInterface.php
@@ -0,0 +1,44 @@
+
+     */
+    protected $_typeMap;
+
+    /**
+     * An array containing the name of the fields and the Type objects
+     * each should use when converting them using batching.
+     *
+     * @var array
+     */
+    protected $batchingTypeMap;
+
+    /**
+     * An array containing all the types registered in the Type system
+     * at the moment this object is created. Used so that the types list
+     * is not fetched on each single row of the results.
+     *
+     * @var array<\Cake\Database\TypeInterface|\Cake\Database\Type\BatchCastingInterface>
+     */
+    protected $types;
+
+    /**
+     * The driver object to be used in the type conversion
+     *
+     * @var \Cake\Database\DriverInterface
+     */
+    protected $_driver;
+
+    /**
+     * Builds the type map
+     *
+     * @param \Cake\Database\TypeMap $typeMap Contains the types to use for converting results
+     * @param \Cake\Database\DriverInterface $driver The driver to use for the type conversion
+     */
+    public function __construct(TypeMap $typeMap, DriverInterface $driver)
+    {
+        $this->_driver = $driver;
+        $map = $typeMap->toArray();
+        $types = TypeFactory::buildAll();
+
+        $simpleMap = $batchingMap = [];
+        $simpleResult = $batchingResult = [];
+
+        foreach ($types as $k => $type) {
+            if ($type instanceof OptionalConvertInterface && !$type->requiresToPhpCast()) {
+                continue;
+            }
+
+            if ($type instanceof BatchCastingInterface) {
+                $batchingMap[$k] = $type;
+                continue;
+            }
+
+            $simpleMap[$k] = $type;
+        }
+
+        foreach ($map as $field => $type) {
+            if (isset($simpleMap[$type])) {
+                $simpleResult[$field] = $simpleMap[$type];
+                continue;
+            }
+            if (isset($batchingMap[$type])) {
+                $batchingResult[$type][] = $field;
+            }
+        }
+
+        // Using batching when there is only a couple for the type is actually slower,
+        // so, let's check for that case here.
+        foreach ($batchingResult as $type => $fields) {
+            if (count($fields) > 2) {
+                continue;
+            }
+
+            foreach ($fields as $f) {
+                $simpleResult[$f] = $batchingMap[$type];
+            }
+            unset($batchingResult[$type]);
+        }
+
+        $this->types = $types;
+        $this->_typeMap = $simpleResult;
+        $this->batchingTypeMap = $batchingResult;
+    }
+
+    /**
+     * Converts each of the fields in the array that are present in the type map
+     * using the corresponding Type class.
+     *
+     * @param array $row The array with the fields to be casted
+     * @return array
+     */
+    public function __invoke(array $row): array
+    {
+        if (!empty($this->_typeMap)) {
+            foreach ($this->_typeMap as $field => $type) {
+                $row[$field] = $type->toPHP($row[$field], $this->_driver);
+            }
+        }
+
+        if (!empty($this->batchingTypeMap)) {
+            foreach ($this->batchingTypeMap as $t => $fields) {
+                /** @psalm-suppress PossiblyUndefinedMethod */
+                $row = $this->types[$t]->manyToPHP($row, $fields, $this->_driver);
+            }
+        }
+
+        return $row;
+    }
+}
diff --git a/vendor/cakephp/database/FunctionsBuilder.php b/vendor/cakephp/database/FunctionsBuilder.php
new file mode 100644
index 0000000..9e50ad6
--- /dev/null
+++ b/vendor/cakephp/database/FunctionsBuilder.php
@@ -0,0 +1,376 @@
+aggregate('SUM', $this->toLiteralParam($expression), $types, $returnType);
+    }
+
+    /**
+     * Returns a AggregateExpression representing a call to SQL AVG function.
+     *
+     * @param \Cake\Database\ExpressionInterface|string $expression the function argument
+     * @param array $types list of types to bind to the arguments
+     * @return \Cake\Database\Expression\AggregateExpression
+     */
+    public function avg($expression, $types = []): AggregateExpression
+    {
+        return $this->aggregate('AVG', $this->toLiteralParam($expression), $types, 'float');
+    }
+
+    /**
+     * Returns a AggregateExpression representing a call to SQL MAX function.
+     *
+     * @param \Cake\Database\ExpressionInterface|string $expression the function argument
+     * @param array $types list of types to bind to the arguments
+     * @return \Cake\Database\Expression\AggregateExpression
+     */
+    public function max($expression, $types = []): AggregateExpression
+    {
+        return $this->aggregate('MAX', $this->toLiteralParam($expression), $types, current($types) ?: 'float');
+    }
+
+    /**
+     * Returns a AggregateExpression representing a call to SQL MIN function.
+     *
+     * @param \Cake\Database\ExpressionInterface|string $expression the function argument
+     * @param array $types list of types to bind to the arguments
+     * @return \Cake\Database\Expression\AggregateExpression
+     */
+    public function min($expression, $types = []): AggregateExpression
+    {
+        return $this->aggregate('MIN', $this->toLiteralParam($expression), $types, current($types) ?: 'float');
+    }
+
+    /**
+     * Returns a AggregateExpression representing a call to SQL COUNT function.
+     *
+     * @param \Cake\Database\ExpressionInterface|string $expression the function argument
+     * @param array $types list of types to bind to the arguments
+     * @return \Cake\Database\Expression\AggregateExpression
+     */
+    public function count($expression, $types = []): AggregateExpression
+    {
+        return $this->aggregate('COUNT', $this->toLiteralParam($expression), $types, 'integer');
+    }
+
+    /**
+     * Returns a FunctionExpression representing a string concatenation
+     *
+     * @param array $args List of strings or expressions to concatenate
+     * @param array $types list of types to bind to the arguments
+     * @return \Cake\Database\Expression\FunctionExpression
+     */
+    public function concat(array $args, array $types = []): FunctionExpression
+    {
+        return new FunctionExpression('CONCAT', $args, $types, 'string');
+    }
+
+    /**
+     * Returns a FunctionExpression representing a call to SQL COALESCE function.
+     *
+     * @param array $args List of expressions to evaluate as function parameters
+     * @param array $types list of types to bind to the arguments
+     * @return \Cake\Database\Expression\FunctionExpression
+     */
+    public function coalesce(array $args, array $types = []): FunctionExpression
+    {
+        return new FunctionExpression('COALESCE', $args, $types, current($types) ?: 'string');
+    }
+
+    /**
+     * Returns a FunctionExpression representing a SQL CAST.
+     *
+     * The `$type` parameter is a SQL type. The return type for the returned expression
+     * is the default type name. Use `setReturnType()` to update it.
+     *
+     * @param \Cake\Database\ExpressionInterface|string $field Field or expression to cast.
+     * @param string $type The SQL data type
+     * @return \Cake\Database\Expression\FunctionExpression
+     */
+    public function cast($field, string $type = ''): FunctionExpression
+    {
+        if (is_array($field)) {
+            deprecationWarning(
+                'Build cast function by FunctionsBuilder::cast(array $args) is deprecated. ' .
+                'Use FunctionsBuilder::cast($field, string $type) instead.'
+            );
+
+            return new FunctionExpression('CAST', $field);
+        }
+
+        if (empty($type)) {
+            throw new InvalidArgumentException('The `$type` in a cast cannot be empty.');
+        }
+
+        $expression = new FunctionExpression('CAST', $this->toLiteralParam($field));
+        $expression->setConjunction(' AS')->add([$type => 'literal']);
+
+        return $expression;
+    }
+
+    /**
+     * Returns a FunctionExpression representing the difference in days between
+     * two dates.
+     *
+     * @param array $args List of expressions to obtain the difference in days.
+     * @param array $types list of types to bind to the arguments
+     * @return \Cake\Database\Expression\FunctionExpression
+     */
+    public function dateDiff(array $args, array $types = []): FunctionExpression
+    {
+        return new FunctionExpression('DATEDIFF', $args, $types, 'integer');
+    }
+
+    /**
+     * Returns the specified date part from the SQL expression.
+     *
+     * @param string $part Part of the date to return.
+     * @param \Cake\Database\ExpressionInterface|string $expression Expression to obtain the date part from.
+     * @param array $types list of types to bind to the arguments
+     * @return \Cake\Database\Expression\FunctionExpression
+     */
+    public function datePart(string $part, $expression, array $types = []): FunctionExpression
+    {
+        return $this->extract($part, $expression, $types);
+    }
+
+    /**
+     * Returns the specified date part from the SQL expression.
+     *
+     * @param string $part Part of the date to return.
+     * @param \Cake\Database\ExpressionInterface|string $expression Expression to obtain the date part from.
+     * @param array $types list of types to bind to the arguments
+     * @return \Cake\Database\Expression\FunctionExpression
+     */
+    public function extract(string $part, $expression, array $types = []): FunctionExpression
+    {
+        $expression = new FunctionExpression('EXTRACT', $this->toLiteralParam($expression), $types, 'integer');
+        $expression->setConjunction(' FROM')->add([$part => 'literal'], [], true);
+
+        return $expression;
+    }
+
+    /**
+     * Add the time unit to the date expression
+     *
+     * @param \Cake\Database\ExpressionInterface|string $expression Expression to obtain the date part from.
+     * @param string|int $value Value to be added. Use negative to subtract.
+     * @param string $unit Unit of the value e.g. hour or day.
+     * @param array $types list of types to bind to the arguments
+     * @return \Cake\Database\Expression\FunctionExpression
+     */
+    public function dateAdd($expression, $value, string $unit, array $types = []): FunctionExpression
+    {
+        if (!is_numeric($value)) {
+            $value = 0;
+        }
+        $interval = $value . ' ' . $unit;
+        $expression = new FunctionExpression('DATE_ADD', $this->toLiteralParam($expression), $types, 'datetime');
+        $expression->setConjunction(', INTERVAL')->add([$interval => 'literal']);
+
+        return $expression;
+    }
+
+    /**
+     * Returns a FunctionExpression representing a call to SQL WEEKDAY function.
+     * 1 - Sunday, 2 - Monday, 3 - Tuesday...
+     *
+     * @param \Cake\Database\ExpressionInterface|string $expression the function argument
+     * @param array $types list of types to bind to the arguments
+     * @return \Cake\Database\Expression\FunctionExpression
+     */
+    public function dayOfWeek($expression, $types = []): FunctionExpression
+    {
+        return new FunctionExpression('DAYOFWEEK', $this->toLiteralParam($expression), $types, 'integer');
+    }
+
+    /**
+     * Returns a FunctionExpression representing a call to SQL WEEKDAY function.
+     * 1 - Sunday, 2 - Monday, 3 - Tuesday...
+     *
+     * @param \Cake\Database\ExpressionInterface|string $expression the function argument
+     * @param array $types list of types to bind to the arguments
+     * @return \Cake\Database\Expression\FunctionExpression
+     */
+    public function weekday($expression, $types = []): FunctionExpression
+    {
+        return $this->dayOfWeek($expression, $types);
+    }
+
+    /**
+     * Returns a FunctionExpression representing a call that will return the current
+     * date and time. By default it returns both date and time, but you can also
+     * make it generate only the date or only the time.
+     *
+     * @param string $type (datetime|date|time)
+     * @return \Cake\Database\Expression\FunctionExpression
+     */
+    public function now(string $type = 'datetime'): FunctionExpression
+    {
+        if ($type === 'datetime') {
+            return new FunctionExpression('NOW', [], [], 'datetime');
+        }
+        if ($type === 'date') {
+            return new FunctionExpression('CURRENT_DATE', [], [], 'date');
+        }
+        if ($type === 'time') {
+            return new FunctionExpression('CURRENT_TIME', [], [], 'time');
+        }
+
+        throw new InvalidArgumentException('Invalid argument for FunctionsBuilder::now(): ' . $type);
+    }
+
+    /**
+     * Returns an AggregateExpression representing call to SQL ROW_NUMBER().
+     *
+     * @return \Cake\Database\Expression\AggregateExpression
+     */
+    public function rowNumber(): AggregateExpression
+    {
+        return (new AggregateExpression('ROW_NUMBER', [], [], 'integer'))->over();
+    }
+
+    /**
+     * Returns an AggregateExpression representing call to SQL LAG().
+     *
+     * @param \Cake\Database\ExpressionInterface|string $expression The value evaluated at offset
+     * @param int $offset The row offset
+     * @param mixed $default The default value if offset doesn't exist
+     * @param string $type The output type of the lag expression. Defaults to float.
+     * @return \Cake\Database\Expression\AggregateExpression
+     */
+    public function lag($expression, int $offset, $default = null, $type = null): AggregateExpression
+    {
+        $params = $this->toLiteralParam($expression) + [$offset => 'literal'];
+        if ($default !== null) {
+            $params[] = $default;
+        }
+
+        $types = [];
+        if ($type !== null) {
+            $types = [$type, 'integer', $type];
+        }
+
+        return (new AggregateExpression('LAG', $params, $types, $type ?? 'float'))->over();
+    }
+
+    /**
+     * Returns an AggregateExpression representing call to SQL LEAD().
+     *
+     * @param \Cake\Database\ExpressionInterface|string $expression The value evaluated at offset
+     * @param int $offset The row offset
+     * @param mixed $default The default value if offset doesn't exist
+     * @param string $type The output type of the lead expression. Defaults to float.
+     * @return \Cake\Database\Expression\AggregateExpression
+     */
+    public function lead($expression, int $offset, $default = null, $type = null): AggregateExpression
+    {
+        $params = $this->toLiteralParam($expression) + [$offset => 'literal'];
+        if ($default !== null) {
+            $params[] = $default;
+        }
+
+        $types = [];
+        if ($type !== null) {
+            $types = [$type, 'integer', $type];
+        }
+
+        return (new AggregateExpression('LEAD', $params, $types, $type ?? 'float'))->over();
+    }
+
+    /**
+     * Helper method to create arbitrary SQL aggregate function calls.
+     *
+     * @param string $name The SQL aggregate function name
+     * @param array $params Array of arguments to be passed to the function.
+     *     Can be an associative array with the literal value or identifier:
+     *     `['value' => 'literal']` or `['value' => 'identifier']
+     * @param array $types Array of types that match the names used in `$params`:
+     *     `['name' => 'type']`
+     * @param string $return Return type of the entire expression. Defaults to float.
+     * @return \Cake\Database\Expression\AggregateExpression
+     */
+    public function aggregate(string $name, array $params = [], array $types = [], string $return = 'float')
+    {
+        return new AggregateExpression($name, $params, $types, $return);
+    }
+
+    /**
+     * Magic method dispatcher to create custom SQL function calls
+     *
+     * @param string $name the SQL function name to construct
+     * @param array $args list with up to 3 arguments, first one being an array with
+     * parameters for the SQL function, the second one a list of types to bind to those
+     * params, and the third one the return type of the function
+     * @return \Cake\Database\Expression\FunctionExpression
+     */
+    public function __call(string $name, array $args): FunctionExpression
+    {
+        return new FunctionExpression($name, ...$args);
+    }
+
+    /**
+     * Creates function parameter array from expression or string literal.
+     *
+     * @param \Cake\Database\ExpressionInterface|string $expression function argument
+     * @return array<\Cake\Database\ExpressionInterface|string>
+     */
+    protected function toLiteralParam($expression)
+    {
+        if (is_string($expression)) {
+            return [$expression => 'literal'];
+        }
+
+        return [$expression];
+    }
+}
diff --git a/vendor/cakephp/database/IdentifierQuoter.php b/vendor/cakephp/database/IdentifierQuoter.php
new file mode 100644
index 0000000..b3a93e2
--- /dev/null
+++ b/vendor/cakephp/database/IdentifierQuoter.php
@@ -0,0 +1,269 @@
+_driver = $driver;
+    }
+
+    /**
+     * Iterates over each of the clauses in a query looking for identifiers and
+     * quotes them
+     *
+     * @param \Cake\Database\Query $query The query to have its identifiers quoted
+     * @return \Cake\Database\Query
+     */
+    public function quote(Query $query): Query
+    {
+        $binder = $query->getValueBinder();
+        $query->setValueBinder(null);
+
+        if ($query->type() === 'insert') {
+            $this->_quoteInsert($query);
+        } elseif ($query->type() === 'update') {
+            $this->_quoteUpdate($query);
+        } else {
+            $this->_quoteParts($query);
+        }
+
+        $query->traverseExpressions([$this, 'quoteExpression']);
+        $query->setValueBinder($binder);
+
+        return $query;
+    }
+
+    /**
+     * Quotes identifiers inside expression objects
+     *
+     * @param \Cake\Database\ExpressionInterface $expression The expression object to walk and quote.
+     * @return void
+     */
+    public function quoteExpression(ExpressionInterface $expression): void
+    {
+        if ($expression instanceof FieldInterface) {
+            $this->_quoteComparison($expression);
+
+            return;
+        }
+
+        if ($expression instanceof OrderByExpression) {
+            $this->_quoteOrderBy($expression);
+
+            return;
+        }
+
+        if ($expression instanceof IdentifierExpression) {
+            $this->_quoteIdentifierExpression($expression);
+
+            return;
+        }
+    }
+
+    /**
+     * Quotes all identifiers in each of the clauses of a query
+     *
+     * @param \Cake\Database\Query $query The query to quote.
+     * @return void
+     */
+    protected function _quoteParts(Query $query): void
+    {
+        foreach (['distinct', 'select', 'from', 'group'] as $part) {
+            $contents = $query->clause($part);
+
+            if (!is_array($contents)) {
+                continue;
+            }
+
+            $result = $this->_basicQuoter($contents);
+            if (!empty($result)) {
+                $query->{$part}($result, true);
+            }
+        }
+
+        $joins = $query->clause('join');
+        if ($joins) {
+            $joins = $this->_quoteJoins($joins);
+            $query->join($joins, [], true);
+        }
+    }
+
+    /**
+     * A generic identifier quoting function used for various parts of the query
+     *
+     * @param array $part the part of the query to quote
+     * @return array
+     */
+    protected function _basicQuoter(array $part): array
+    {
+        $result = [];
+        foreach ($part as $alias => $value) {
+            $value = !is_string($value) ? $value : $this->_driver->quoteIdentifier($value);
+            $alias = is_numeric($alias) ? $alias : $this->_driver->quoteIdentifier($alias);
+            $result[$alias] = $value;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Quotes both the table and alias for an array of joins as stored in a Query
+     * object
+     *
+     * @param array $joins The joins to quote.
+     * @return array
+     */
+    protected function _quoteJoins(array $joins): array
+    {
+        $result = [];
+        foreach ($joins as $value) {
+            $alias = '';
+            if (!empty($value['alias'])) {
+                $alias = $this->_driver->quoteIdentifier($value['alias']);
+                $value['alias'] = $alias;
+            }
+
+            if (is_string($value['table'])) {
+                $value['table'] = $this->_driver->quoteIdentifier($value['table']);
+            }
+
+            $result[$alias] = $value;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Quotes the table name and columns for an insert query
+     *
+     * @param \Cake\Database\Query $query The insert query to quote.
+     * @return void
+     */
+    protected function _quoteInsert(Query $query): void
+    {
+        $insert = $query->clause('insert');
+        if (!isset($insert[0]) || !isset($insert[1])) {
+            return;
+        }
+        [$table, $columns] = $insert;
+        $table = $this->_driver->quoteIdentifier($table);
+        foreach ($columns as &$column) {
+            if (is_scalar($column)) {
+                $column = $this->_driver->quoteIdentifier((string)$column);
+            }
+        }
+        $query->insert($columns)->into($table);
+    }
+
+    /**
+     * Quotes the table name for an update query
+     *
+     * @param \Cake\Database\Query $query The update query to quote.
+     * @return void
+     */
+    protected function _quoteUpdate(Query $query): void
+    {
+        $table = $query->clause('update')[0];
+
+        if (is_string($table)) {
+            $query->update($this->_driver->quoteIdentifier($table));
+        }
+    }
+
+    /**
+     * Quotes identifiers in expression objects implementing the field interface
+     *
+     * @param \Cake\Database\Expression\FieldInterface $expression The expression to quote.
+     * @return void
+     */
+    protected function _quoteComparison(FieldInterface $expression): void
+    {
+        $field = $expression->getField();
+        if (is_string($field)) {
+            $expression->setField($this->_driver->quoteIdentifier($field));
+        } elseif (is_array($field)) {
+            $quoted = [];
+            foreach ($field as $f) {
+                $quoted[] = $this->_driver->quoteIdentifier($f);
+            }
+            $expression->setField($quoted);
+        } elseif ($field instanceof ExpressionInterface) {
+            $this->quoteExpression($field);
+        }
+    }
+
+    /**
+     * Quotes identifiers in "order by" expression objects
+     *
+     * Strings with spaces are treated as literal expressions
+     * and will not have identifiers quoted.
+     *
+     * @param \Cake\Database\Expression\OrderByExpression $expression The expression to quote.
+     * @return void
+     */
+    protected function _quoteOrderBy(OrderByExpression $expression): void
+    {
+        $expression->iterateParts(function ($part, &$field) {
+            if (is_string($field)) {
+                $field = $this->_driver->quoteIdentifier($field);
+
+                return $part;
+            }
+            if (is_string($part) && strpos($part, ' ') === false) {
+                return $this->_driver->quoteIdentifier($part);
+            }
+
+            return $part;
+        });
+    }
+
+    /**
+     * Quotes identifiers in "order by" expression objects
+     *
+     * @param \Cake\Database\Expression\IdentifierExpression $expression The identifiers to quote.
+     * @return void
+     */
+    protected function _quoteIdentifierExpression(IdentifierExpression $expression): void
+    {
+        $expression->setIdentifier(
+            $this->_driver->quoteIdentifier($expression->getIdentifier())
+        );
+    }
+}
diff --git a/vendor/cakephp/database/LICENSE.txt b/vendor/cakephp/database/LICENSE.txt
new file mode 100644
index 0000000..b938c9e
--- /dev/null
+++ b/vendor/cakephp/database/LICENSE.txt
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org)
+Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org)
+
+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/cakephp/database/Log/LoggedQuery.php b/vendor/cakephp/database/Log/LoggedQuery.php
new file mode 100644
index 0000000..f029bc9
--- /dev/null
+++ b/vendor/cakephp/database/Log/LoggedQuery.php
@@ -0,0 +1,176 @@
+driver instanceof Sqlserver) {
+                    return $p ? '1' : '0';
+                }
+
+                return $p ? 'TRUE' : 'FALSE';
+            }
+
+            if (is_string($p)) {
+                // Likely binary data like a blob or binary uuid.
+                // pattern matches ascii control chars.
+                if (preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $p) !== $p) {
+                    $p = bin2hex($p);
+                }
+
+                $replacements = [
+                    '$' => '\\$',
+                    '\\' => '\\\\\\\\',
+                    "'" => "''",
+                ];
+
+                $p = strtr($p, $replacements);
+
+                return "'$p'";
+            }
+
+            return $p;
+        }, $this->params);
+
+        $keys = [];
+        $limit = is_int(key($params)) ? 1 : -1;
+        foreach ($params as $key => $param) {
+            $keys[] = is_string($key) ? "/:$key\b/" : '/[?]/';
+        }
+
+        return preg_replace($keys, $params, $this->query, $limit);
+    }
+
+    /**
+     * Get the logging context data for a query.
+     *
+     * @return array
+     */
+    public function getContext(): array
+    {
+        return [
+            'numRows' => $this->numRows,
+            'took' => $this->took,
+            'role' => $this->driver ? $this->driver->getRole() : '',
+        ];
+    }
+
+    /**
+     * Returns data that will be serialized as JSON
+     *
+     * @return array
+     */
+    public function jsonSerialize(): array
+    {
+        $error = $this->error;
+        if ($error !== null) {
+            $error = [
+                'class' => get_class($error),
+                'message' => $error->getMessage(),
+                'code' => $error->getCode(),
+            ];
+        }
+
+        return [
+            'query' => $this->query,
+            'numRows' => $this->numRows,
+            'params' => $this->params,
+            'took' => $this->took,
+            'error' => $error,
+        ];
+    }
+
+    /**
+     * Returns the string representation of this logged query
+     *
+     * @return string
+     */
+    public function __toString(): string
+    {
+        $sql = $this->query;
+        if (!empty($this->params)) {
+            $sql = $this->interpolate();
+        }
+
+        return $sql;
+    }
+}
diff --git a/vendor/cakephp/database/Log/LoggingStatement.php b/vendor/cakephp/database/Log/LoggingStatement.php
new file mode 100644
index 0000000..e83f6f6
--- /dev/null
+++ b/vendor/cakephp/database/Log/LoggingStatement.php
@@ -0,0 +1,219 @@
+
+     */
+    protected $_compiledParams = [];
+
+    /**
+     * Query execution start time.
+     *
+     * @var float
+     */
+    protected $startTime = 0.0;
+
+    /**
+     * Logged query
+     *
+     * @var \Cake\Database\Log\LoggedQuery|null
+     */
+    protected $loggedQuery;
+
+    /**
+     * Wrapper for the execute function to calculate time spent
+     * and log the query afterwards.
+     *
+     * @param array|null $params List of values to be bound to query
+     * @return bool True on success, false otherwise
+     * @throws \Exception Re-throws any exception raised during query execution.
+     */
+    public function execute(?array $params = null): bool
+    {
+        $this->startTime = microtime(true);
+
+        $this->loggedQuery = new LoggedQuery();
+        $this->loggedQuery->driver = $this->_driver;
+        $this->loggedQuery->params = $params ?: $this->_compiledParams;
+
+        try {
+            $result = parent::execute($params);
+            $this->loggedQuery->took = (int)round((microtime(true) - $this->startTime) * 1000, 0);
+        } catch (Exception $e) {
+            $this->loggedQuery->error = $e;
+            $this->_log();
+
+            if (Configure::read('Error.convertStatementToDatabaseException', false) === true) {
+                $code = $e->getCode();
+                if (!is_int($code)) {
+                    $code = null;
+                }
+
+                throw new DatabaseException([
+                    'message' => $e->getMessage(),
+                    'queryString' => $this->queryString,
+                ], $code, $e);
+            }
+
+            if (version_compare(PHP_VERSION, '8.2.0', '<')) {
+                deprecationWarning(
+                    '4.4.12 - Having queryString set on exceptions is deprecated.' .
+                    'If you are not using this attribute there is no action to take.' .
+                    'Otherwise, enable Error.convertStatementToDatabaseException.'
+                );
+                /** @psalm-suppress UndefinedPropertyAssignment */
+                $e->queryString = $this->queryString;
+            }
+
+            throw $e;
+        }
+
+        if (preg_match('/^(?!SELECT)/i', $this->queryString)) {
+            $this->rowCount();
+        }
+
+        return $result;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function fetch($type = self::FETCH_TYPE_NUM)
+    {
+        $record = parent::fetch($type);
+
+        if ($this->loggedQuery) {
+            $this->rowCount();
+        }
+
+        return $record;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function fetchAll($type = self::FETCH_TYPE_NUM)
+    {
+        $results = parent::fetchAll($type);
+
+        if ($this->loggedQuery) {
+            $this->rowCount();
+        }
+
+        return $results;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function rowCount(): int
+    {
+        $result = parent::rowCount();
+
+        if ($this->loggedQuery) {
+            $this->loggedQuery->numRows = $result;
+            $this->_log();
+        }
+
+        return $result;
+    }
+
+    /**
+     * Copies the logging data to the passed LoggedQuery and sends it
+     * to the logging system.
+     *
+     * @return void
+     */
+    protected function _log(): void
+    {
+        if ($this->loggedQuery === null) {
+            return;
+        }
+
+        $this->loggedQuery->query = $this->queryString;
+        $this->getLogger()->debug((string)$this->loggedQuery, ['query' => $this->loggedQuery]);
+
+        $this->loggedQuery = null;
+    }
+
+    /**
+     * Wrapper for bindValue function to gather each parameter to be later used
+     * in the logger function.
+     *
+     * @param string|int $column Name or param position to be bound
+     * @param mixed $value The value to bind to variable in query
+     * @param string|int|null $type PDO type or name of configured Type class
+     * @return void
+     */
+    public function bindValue($column, $value, $type = 'string'): void
+    {
+        parent::bindValue($column, $value, $type);
+
+        if ($type === null) {
+            $type = 'string';
+        }
+        if (!ctype_digit($type)) {
+            $value = $this->cast($value, $type)[0];
+        }
+        $this->_compiledParams[$column] = $value;
+    }
+
+    /**
+     * Sets a logger
+     *
+     * @param \Psr\Log\LoggerInterface $logger Logger object
+     * @return void
+     */
+    public function setLogger(LoggerInterface $logger): void
+    {
+        $this->_logger = $logger;
+    }
+
+    /**
+     * Gets the logger object
+     *
+     * @return \Psr\Log\LoggerInterface logger instance
+     */
+    public function getLogger(): LoggerInterface
+    {
+        return $this->_logger;
+    }
+}
diff --git a/vendor/cakephp/database/Log/QueryLogger.php b/vendor/cakephp/database/Log/QueryLogger.php
new file mode 100644
index 0000000..e2faadc
--- /dev/null
+++ b/vendor/cakephp/database/Log/QueryLogger.php
@@ -0,0 +1,57 @@
+ $config Configuration array
+     */
+    public function __construct(array $config = [])
+    {
+        $this->_defaultConfig['scopes'] = ['queriesLog'];
+        $this->_defaultConfig['connection'] = '';
+
+        parent::__construct($config);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function log($level, $message, array $context = [])
+    {
+        $context['scope'] = $this->scopes() ?: ['queriesLog'];
+        $context['connection'] = $this->getConfig('connection');
+
+        if ($context['query'] instanceof LoggedQuery) {
+            $context = $context['query']->getContext() + $context;
+            $message = 'connection={connection} role={role} duration={took} rows={numRows} ' . $message;
+        }
+        Log::write('debug', (string)$message, $context);
+    }
+}
diff --git a/vendor/cakephp/database/PostgresCompiler.php b/vendor/cakephp/database/PostgresCompiler.php
new file mode 100644
index 0000000..7beae8b
--- /dev/null
+++ b/vendor/cakephp/database/PostgresCompiler.php
@@ -0,0 +1,93 @@
+ 'DELETE',
+        'where' => ' WHERE %s',
+        'group' => ' GROUP BY %s',
+        'order' => ' %s',
+        'limit' => ' LIMIT %s',
+        'offset' => ' OFFSET %s',
+        'epilog' => ' %s',
+    ];
+
+    /**
+     * Helper function used to build the string representation of a HAVING clause,
+     * it constructs the field list taking care of aliasing and
+     * converting expression objects to string.
+     *
+     * @param array $parts list of fields to be transformed to string
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string
+     */
+    protected function _buildHavingPart($parts, $query, $binder)
+    {
+        $selectParts = $query->clause('select');
+
+        foreach ($selectParts as $selectKey => $selectPart) {
+            if (!$selectPart instanceof FunctionExpression) {
+                continue;
+            }
+            foreach ($parts as $k => $p) {
+                if (!is_string($p)) {
+                    continue;
+                }
+                preg_match_all(
+                    '/\b' . trim($selectKey, '"') . '\b/i',
+                    $p,
+                    $matches
+                );
+
+                if (empty($matches[0])) {
+                    continue;
+                }
+
+                $parts[$k] = preg_replace(
+                    ['/"/', '/\b' . trim($selectKey, '"') . '\b/i'],
+                    ['', $selectPart->sql($binder)],
+                    $p
+                );
+            }
+        }
+
+        return sprintf(' HAVING %s', implode(', ', $parts));
+    }
+}
diff --git a/vendor/cakephp/database/Query.php b/vendor/cakephp/database/Query.php
new file mode 100644
index 0000000..594b8a7
--- /dev/null
+++ b/vendor/cakephp/database/Query.php
@@ -0,0 +1,2516 @@
+
+     */
+    protected $_parts = [
+        'delete' => true,
+        'update' => [],
+        'set' => [],
+        'insert' => [],
+        'values' => [],
+        'with' => [],
+        'select' => [],
+        'distinct' => false,
+        'modifier' => [],
+        'from' => [],
+        'join' => [],
+        'where' => null,
+        'group' => [],
+        'having' => null,
+        'window' => [],
+        'order' => null,
+        'limit' => null,
+        'offset' => null,
+        'union' => [],
+        'epilog' => null,
+    ];
+
+    /**
+     * The list of query clauses to traverse for generating a SELECT statement
+     *
+     * @var array
+     * @deprecated 4.4.3 This property is unused.
+     */
+    protected $_selectParts = [
+        'with', 'select', 'from', 'join', 'where', 'group', 'having', 'order', 'limit',
+        'offset', 'union', 'epilog',
+    ];
+
+    /**
+     * The list of query clauses to traverse for generating an UPDATE statement
+     *
+     * @var array
+     * @deprecated 4.4.3 This property is unused.
+     */
+    protected $_updateParts = ['with', 'update', 'set', 'where', 'epilog'];
+
+    /**
+     * The list of query clauses to traverse for generating a DELETE statement
+     *
+     * @var array
+     * @deprecated 4.4.3 This property is unused.
+     */
+    protected $_deleteParts = ['with', 'delete', 'modifier', 'from', 'where', 'epilog'];
+
+    /**
+     * The list of query clauses to traverse for generating an INSERT statement
+     *
+     * @var array
+     * @deprecated 4.4.3 This property is unused.
+     */
+    protected $_insertParts = ['with', 'insert', 'values', 'epilog'];
+
+    /**
+     * Indicates whether internal state of this query was changed, this is used to
+     * discard internal cached objects such as the transformed query or the reference
+     * to the executed statement.
+     *
+     * @var bool
+     */
+    protected $_dirty = false;
+
+    /**
+     * A list of callback functions to be called to alter each row from resulting
+     * statement upon retrieval. Each one of the callback function will receive
+     * the row array as first argument.
+     *
+     * @var array
+     */
+    protected $_resultDecorators = [];
+
+    /**
+     * Statement object resulting from executing this query.
+     *
+     * @var \Cake\Database\StatementInterface|null
+     */
+    protected $_iterator;
+
+    /**
+     * The object responsible for generating query placeholders and temporarily store values
+     * associated to each of those.
+     *
+     * @var \Cake\Database\ValueBinder|null
+     */
+    protected $_valueBinder;
+
+    /**
+     * Instance of functions builder object used for generating arbitrary SQL functions.
+     *
+     * @var \Cake\Database\FunctionsBuilder|null
+     */
+    protected $_functionsBuilder;
+
+    /**
+     * Boolean for tracking whether buffered results
+     * are enabled.
+     *
+     * @var bool
+     * @deprecated 4.5.0 Results will always be buffered in 5.0.
+     */
+    protected $_useBufferedResults = true;
+
+    /**
+     * The Type map for fields in the select clause
+     *
+     * @var \Cake\Database\TypeMap|null
+     */
+    protected $_selectTypeMap;
+
+    /**
+     * Tracking flag to disable casting
+     *
+     * @var bool
+     */
+    protected $typeCastEnabled = true;
+
+    /**
+     * Constructor.
+     *
+     * @param \Cake\Database\Connection $connection The connection
+     * object to be used for transforming and executing this query
+     */
+    public function __construct(Connection $connection)
+    {
+        $this->setConnection($connection);
+    }
+
+    /**
+     * Sets the connection instance to be used for executing and transforming this query.
+     *
+     * @param \Cake\Database\Connection $connection Connection instance
+     * @return $this
+     */
+    public function setConnection(Connection $connection)
+    {
+        $this->_dirty();
+        $this->_connection = $connection;
+
+        return $this;
+    }
+
+    /**
+     * Gets the connection instance to be used for executing and transforming this query.
+     *
+     * @return \Cake\Database\Connection
+     */
+    public function getConnection(): Connection
+    {
+        return $this->_connection;
+    }
+
+    /**
+     * Returns the connection role ('read' or 'write')
+     *
+     * @return string
+     */
+    public function getConnectionRole(): string
+    {
+        return $this->connectionRole;
+    }
+
+    /**
+     * Compiles the SQL representation of this query and executes it using the
+     * configured connection object. Returns the resulting statement object.
+     *
+     * Executing a query internally executes several steps, the first one is
+     * letting the connection transform this object to fit its particular dialect,
+     * this might result in generating a different Query object that will be the one
+     * to actually be executed. Immediately after, literal values are passed to the
+     * connection so they are bound to the query in a safe way. Finally, the resulting
+     * statement is decorated with custom objects to execute callbacks for each row
+     * retrieved if necessary.
+     *
+     * Resulting statement is traversable, so it can be used in any loop as you would
+     * with an array.
+     *
+     * This method can be overridden in query subclasses to decorate behavior
+     * around query execution.
+     *
+     * @return \Cake\Database\StatementInterface
+     */
+    public function execute(): StatementInterface
+    {
+        $statement = $this->_connection->run($this);
+        $this->_iterator = $this->_decorateStatement($statement);
+        $this->_dirty = false;
+
+        return $this->_iterator;
+    }
+
+    /**
+     * Executes the SQL of this query and immediately closes the statement before returning the row count of records
+     * changed.
+     *
+     * This method can be used with UPDATE and DELETE queries, but is not recommended for SELECT queries and is not
+     * used to count records.
+     *
+     * ## Example
+     *
+     * ```
+     * $rowCount = $query->update('articles')
+     *                 ->set(['published'=>true])
+     *                 ->where(['published'=>false])
+     *                 ->rowCountAndClose();
+     * ```
+     *
+     * The above example will change the published column to true for all false records, and return the number of
+     * records that were updated.
+     *
+     * @return int
+     */
+    public function rowCountAndClose(): int
+    {
+        $statement = $this->execute();
+        try {
+            return $statement->rowCount();
+        } finally {
+            $statement->closeCursor();
+        }
+    }
+
+    /**
+     * Returns the SQL representation of this object.
+     *
+     * This function will compile this query to make it compatible
+     * with the SQL dialect that is used by the connection, This process might
+     * add, remove or alter any query part or internal expression to make it
+     * executable in the target platform.
+     *
+     * The resulting query may have placeholders that will be replaced with the actual
+     * values when the query is executed, hence it is most suitable to use with
+     * prepared statements.
+     *
+     * @param \Cake\Database\ValueBinder|null $binder Value binder that generates parameter placeholders
+     * @return string
+     */
+    public function sql(?ValueBinder $binder = null): string
+    {
+        if (!$binder) {
+            $binder = $this->getValueBinder();
+            $binder->resetCount();
+        }
+        $connection = $this->getConnection();
+
+        return $connection->getDriver($this->getConnectionRole())->compileQuery($this, $binder)[1];
+    }
+
+    /**
+     * Will iterate over every specified part. Traversing functions can aggregate
+     * results using variables in the closure or instance variables. This function
+     * is commonly used as a way for traversing all query parts that
+     * are going to be used for constructing a query.
+     *
+     * The callback will receive 2 parameters, the first one is the value of the query
+     * part that is being iterated and the second the name of such part.
+     *
+     * ### Example
+     * ```
+     * $query->select(['title'])->from('articles')->traverse(function ($value, $clause) {
+     *     if ($clause === 'select') {
+     *         var_dump($value);
+     *     }
+     * });
+     * ```
+     *
+     * @param callable $callback A function or callable to be executed for each part
+     * @return $this
+     */
+    public function traverse($callback)
+    {
+        foreach ($this->_parts as $name => $part) {
+            $callback($part, $name);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Will iterate over the provided parts.
+     *
+     * Traversing functions can aggregate results using variables in the closure
+     * or instance variables. This method can be used to traverse a subset of
+     * query parts in order to render a SQL query.
+     *
+     * The callback will receive 2 parameters, the first one is the value of the query
+     * part that is being iterated and the second the name of such part.
+     *
+     * ### Example
+     *
+     * ```
+     * $query->select(['title'])->from('articles')->traverse(function ($value, $clause) {
+     *     if ($clause === 'select') {
+     *         var_dump($value);
+     *     }
+     * }, ['select', 'from']);
+     * ```
+     *
+     * @param callable $visitor A function or callable to be executed for each part
+     * @param array $parts The list of query parts to traverse
+     * @return $this
+     */
+    public function traverseParts(callable $visitor, array $parts)
+    {
+        foreach ($parts as $name) {
+            $visitor($this->_parts[$name], $name);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Adds a new common table expression (CTE) to the query.
+     *
+     * ### Examples:
+     *
+     * Common table expressions can either be passed as preconstructed expression
+     * objects:
+     *
+     * ```
+     * $cte = new \Cake\Database\Expression\CommonTableExpression(
+     *     'cte',
+     *     $connection
+     *         ->newQuery()
+     *         ->select('*')
+     *         ->from('articles')
+     * );
+     *
+     * $query->with($cte);
+     * ```
+     *
+     * or returned from a closure, which will receive a new common table expression
+     * object as the first argument, and a new blank query object as
+     * the second argument:
+     *
+     * ```
+     * $query->with(function (
+     *     \Cake\Database\Expression\CommonTableExpression $cte,
+     *     \Cake\Database\Query $query
+     *  ) {
+     *     $cteQuery = $query
+     *         ->select('*')
+     *         ->from('articles');
+     *
+     *     return $cte
+     *         ->name('cte')
+     *         ->query($cteQuery);
+     * });
+     * ```
+     *
+     * @param \Cake\Database\Expression\CommonTableExpression|\Closure $cte The CTE to add.
+     * @param bool $overwrite Whether to reset the list of CTEs.
+     * @return $this
+     */
+    public function with($cte, bool $overwrite = false)
+    {
+        if ($overwrite) {
+            $this->_parts['with'] = [];
+        }
+
+        if ($cte instanceof Closure) {
+            $query = $this->getConnection()->selectQuery();
+            $cte = $cte(new CommonTableExpression(), $query);
+            if (!($cte instanceof CommonTableExpression)) {
+                throw new RuntimeException(
+                    'You must return a `CommonTableExpression` from a Closure passed to `with()`.'
+                );
+            }
+        }
+
+        $this->_parts['with'][] = $cte;
+        $this->_dirty();
+
+        return $this;
+    }
+
+    /**
+     * Adds new fields to be returned by a `SELECT` statement when this query is
+     * executed. Fields can be passed as an array of strings, array of expression
+     * objects, a single expression or a single string.
+     *
+     * If an array is passed, keys will be used to alias fields using the value as the
+     * real field to be aliased. It is possible to alias strings, Expression objects or
+     * even other Query objects.
+     *
+     * If a callable function is passed, the returning array of the function will
+     * be used as the list of fields.
+     *
+     * By default this function will append any passed argument to the list of fields
+     * to be selected, unless the second argument is set to true.
+     *
+     * ### Examples:
+     *
+     * ```
+     * $query->select(['id', 'title']); // Produces SELECT id, title
+     * $query->select(['author' => 'author_id']); // Appends author: SELECT id, title, author_id as author
+     * $query->select('id', true); // Resets the list: SELECT id
+     * $query->select(['total' => $countQuery]); // SELECT id, (SELECT ...) AS total
+     * $query->select(function ($query) {
+     *     return ['article_id', 'total' => $query->count('*')];
+     * })
+     * ```
+     *
+     * By default no fields are selected, if you have an instance of `Cake\ORM\Query` and try to append
+     * fields you should also call `Cake\ORM\Query::enableAutoFields()` to select the default fields
+     * from the table.
+     *
+     * @param \Cake\Database\ExpressionInterface|callable|array|string $fields fields to be added to the list.
+     * @param bool $overwrite whether to reset fields with passed list or not
+     * @return $this
+     */
+    public function select($fields = [], bool $overwrite = false)
+    {
+        if (!is_string($fields) && is_callable($fields)) {
+            $fields = $fields($this);
+        }
+
+        if (!is_array($fields)) {
+            $fields = [$fields];
+        }
+
+        if ($overwrite) {
+            $this->_parts['select'] = $fields;
+        } else {
+            $this->_parts['select'] = array_merge($this->_parts['select'], $fields);
+        }
+
+        $this->_dirty();
+        $this->_type = 'select';
+
+        return $this;
+    }
+
+    /**
+     * Adds a `DISTINCT` clause to the query to remove duplicates from the result set.
+     * This clause can only be used for select statements.
+     *
+     * If you wish to filter duplicates based of those rows sharing a particular field
+     * or set of fields, you may pass an array of fields to filter on. Beware that
+     * this option might not be fully supported in all database systems.
+     *
+     * ### Examples:
+     *
+     * ```
+     * // Filters products with the same name and city
+     * $query->select(['name', 'city'])->from('products')->distinct();
+     *
+     * // Filters products in the same city
+     * $query->distinct(['city']);
+     * $query->distinct('city');
+     *
+     * // Filter products with the same name
+     * $query->distinct(['name'], true);
+     * $query->distinct('name', true);
+     * ```
+     *
+     * @param \Cake\Database\ExpressionInterface|array|string|bool $on Enable/disable distinct class
+     * or list of fields to be filtered on
+     * @param bool $overwrite whether to reset fields with passed list or not
+     * @return $this
+     */
+    public function distinct($on = [], $overwrite = false)
+    {
+        if ($on === []) {
+            $on = true;
+        } elseif (is_string($on)) {
+            $on = [$on];
+        }
+
+        if (is_array($on)) {
+            $merge = [];
+            if (is_array($this->_parts['distinct'])) {
+                $merge = $this->_parts['distinct'];
+            }
+            $on = $overwrite ? array_values($on) : array_merge($merge, array_values($on));
+        }
+
+        $this->_parts['distinct'] = $on;
+        $this->_dirty();
+
+        return $this;
+    }
+
+    /**
+     * Adds a single or multiple `SELECT` modifiers to be used in the `SELECT`.
+     *
+     * By default this function will append any passed argument to the list of modifiers
+     * to be applied, unless the second argument is set to true.
+     *
+     * ### Example:
+     *
+     * ```
+     * // Ignore cache query in MySQL
+     * $query->select(['name', 'city'])->from('products')->modifier('SQL_NO_CACHE');
+     * // It will produce the SQL: SELECT SQL_NO_CACHE name, city FROM products
+     *
+     * // Or with multiple modifiers
+     * $query->select(['name', 'city'])->from('products')->modifier(['HIGH_PRIORITY', 'SQL_NO_CACHE']);
+     * // It will produce the SQL: SELECT HIGH_PRIORITY SQL_NO_CACHE name, city FROM products
+     * ```
+     *
+     * @param \Cake\Database\ExpressionInterface|array|string $modifiers modifiers to be applied to the query
+     * @param bool $overwrite whether to reset order with field list or not
+     * @return $this
+     */
+    public function modifier($modifiers, $overwrite = false)
+    {
+        $this->_dirty();
+        if ($overwrite) {
+            $this->_parts['modifier'] = [];
+        }
+        if (!is_array($modifiers)) {
+            $modifiers = [$modifiers];
+        }
+        $this->_parts['modifier'] = array_merge($this->_parts['modifier'], $modifiers);
+
+        return $this;
+    }
+
+    /**
+     * Adds a single or multiple tables to be used in the FROM clause for this query.
+     * Tables can be passed as an array of strings, array of expression
+     * objects, a single expression or a single string.
+     *
+     * If an array is passed, keys will be used to alias tables using the value as the
+     * real field to be aliased. It is possible to alias strings, ExpressionInterface objects or
+     * even other Query objects.
+     *
+     * By default this function will append any passed argument to the list of tables
+     * to be selected from, unless the second argument is set to true.
+     *
+     * This method can be used for select, update and delete statements.
+     *
+     * ### Examples:
+     *
+     * ```
+     * $query->from(['p' => 'posts']); // Produces FROM posts p
+     * $query->from('authors'); // Appends authors: FROM posts p, authors
+     * $query->from(['products'], true); // Resets the list: FROM products
+     * $query->from(['sub' => $countQuery]); // FROM (SELECT ...) sub
+     * ```
+     *
+     * @param array|string $tables tables to be added to the list. This argument, can be
+     *  passed as an array of strings, array of expression objects, or a single string. See
+     *  the examples above for the valid call types.
+     * @param bool $overwrite whether to reset tables with passed list or not
+     * @return $this
+     */
+    public function from($tables = [], $overwrite = false)
+    {
+        $tables = (array)$tables;
+
+        if ($overwrite) {
+            $this->_parts['from'] = $tables;
+        } else {
+            $this->_parts['from'] = array_merge($this->_parts['from'], $tables);
+        }
+
+        $this->_dirty();
+
+        return $this;
+    }
+
+    /**
+     * Adds a single or multiple tables to be used as JOIN clauses to this query.
+     * Tables can be passed as an array of strings, an array describing the
+     * join parts, an array with multiple join descriptions, or a single string.
+     *
+     * By default this function will append any passed argument to the list of tables
+     * to be joined, unless the third argument is set to true.
+     *
+     * When no join type is specified an `INNER JOIN` is used by default:
+     * `$query->join(['authors'])` will produce `INNER JOIN authors ON 1 = 1`
+     *
+     * It is also possible to alias joins using the array key:
+     * `$query->join(['a' => 'authors'])` will produce `INNER JOIN authors a ON 1 = 1`
+     *
+     * A join can be fully described and aliased using the array notation:
+     *
+     * ```
+     * $query->join([
+     *     'a' => [
+     *         'table' => 'authors',
+     *         'type' => 'LEFT',
+     *         'conditions' => 'a.id = b.author_id'
+     *     ]
+     * ]);
+     * // Produces LEFT JOIN authors a ON a.id = b.author_id
+     * ```
+     *
+     * You can even specify multiple joins in an array, including the full description:
+     *
+     * ```
+     * $query->join([
+     *     'a' => [
+     *         'table' => 'authors',
+     *         'type' => 'LEFT',
+     *         'conditions' => 'a.id = b.author_id'
+     *     ],
+     *     'p' => [
+     *         'table' => 'publishers',
+     *         'type' => 'INNER',
+     *         'conditions' => 'p.id = b.publisher_id AND p.name = "Cake Software Foundation"'
+     *     ]
+     * ]);
+     * // LEFT JOIN authors a ON a.id = b.author_id
+     * // INNER JOIN publishers p ON p.id = b.publisher_id AND p.name = "Cake Software Foundation"
+     * ```
+     *
+     * ### Using conditions and types
+     *
+     * Conditions can be expressed, as in the examples above, using a string for comparing
+     * columns, or string with already quoted literal values. Additionally it is
+     * possible to use conditions expressed in arrays or expression objects.
+     *
+     * When using arrays for expressing conditions, it is often desirable to convert
+     * the literal values to the correct database representation. This is achieved
+     * using the second parameter of this function.
+     *
+     * ```
+     * $query->join(['a' => [
+     *     'table' => 'articles',
+     *     'conditions' => [
+     *         'a.posted >=' => new DateTime('-3 days'),
+     *         'a.published' => true,
+     *         'a.author_id = authors.id'
+     *     ]
+     * ]], ['a.posted' => 'datetime', 'a.published' => 'boolean'])
+     * ```
+     *
+     * ### Overwriting joins
+     *
+     * When creating aliased joins using the array notation, you can override
+     * previous join definitions by using the same alias in consequent
+     * calls to this function or you can replace all previously defined joins
+     * with another list if the third parameter for this function is set to true.
+     *
+     * ```
+     * $query->join(['alias' => 'table']); // joins table with as alias
+     * $query->join(['alias' => 'another_table']); // joins another_table with as alias
+     * $query->join(['something' => 'different_table'], [], true); // resets joins list
+     * ```
+     *
+     * @param array|string $tables list of tables to be joined in the query
+     * @param array $types Associative array of type names used to bind values to query
+     * @param bool $overwrite whether to reset joins with passed list or not
+     * @see \Cake\Database\TypeFactory
+     * @return $this
+     */
+    public function join($tables, $types = [], $overwrite = false)
+    {
+        if (is_string($tables) || isset($tables['table'])) {
+            $tables = [$tables];
+        }
+
+        $joins = [];
+        $i = count($this->_parts['join']);
+        foreach ($tables as $alias => $t) {
+            if (!is_array($t)) {
+                $t = ['table' => $t, 'conditions' => $this->newExpr()];
+            }
+
+            if (!is_string($t['conditions']) && is_callable($t['conditions'])) {
+                $t['conditions'] = $t['conditions']($this->newExpr(), $this);
+            }
+
+            if (!($t['conditions'] instanceof ExpressionInterface)) {
+                $t['conditions'] = $this->newExpr()->add($t['conditions'], $types);
+            }
+            $alias = is_string($alias) ? $alias : null;
+            $joins[$alias ?: $i++] = $t + ['type' => static::JOIN_TYPE_INNER, 'alias' => $alias];
+        }
+
+        if ($overwrite) {
+            $this->_parts['join'] = $joins;
+        } else {
+            $this->_parts['join'] = array_merge($this->_parts['join'], $joins);
+        }
+
+        $this->_dirty();
+
+        return $this;
+    }
+
+    /**
+     * Remove a join if it has been defined.
+     *
+     * Useful when you are redefining joins or want to re-order
+     * the join clauses.
+     *
+     * @param string $name The alias/name of the join to remove.
+     * @return $this
+     */
+    public function removeJoin(string $name)
+    {
+        unset($this->_parts['join'][$name]);
+        $this->_dirty();
+
+        return $this;
+    }
+
+    /**
+     * Adds a single `LEFT JOIN` clause to the query.
+     *
+     * This is a shorthand method for building joins via `join()`.
+     *
+     * The table name can be passed as a string, or as an array in case it needs to
+     * be aliased:
+     *
+     * ```
+     * // LEFT JOIN authors ON authors.id = posts.author_id
+     * $query->leftJoin('authors', 'authors.id = posts.author_id');
+     *
+     * // LEFT JOIN authors a ON a.id = posts.author_id
+     * $query->leftJoin(['a' => 'authors'], 'a.id = posts.author_id');
+     * ```
+     *
+     * Conditions can be passed as strings, arrays, or expression objects. When
+     * using arrays it is possible to combine them with the `$types` parameter
+     * in order to define how to convert the values:
+     *
+     * ```
+     * $query->leftJoin(['a' => 'articles'], [
+     *      'a.posted >=' => new DateTime('-3 days'),
+     *      'a.published' => true,
+     *      'a.author_id = authors.id'
+     * ], ['a.posted' => 'datetime', 'a.published' => 'boolean']);
+     * ```
+     *
+     * See `join()` for further details on conditions and types.
+     *
+     * @param array|string $table The table to join with
+     * @param \Cake\Database\ExpressionInterface|array|string $conditions The conditions
+     * to use for joining.
+     * @param array $types a list of types associated to the conditions used for converting
+     * values to the corresponding database representation.
+     * @return $this
+     */
+    public function leftJoin($table, $conditions = [], $types = [])
+    {
+        $this->join($this->_makeJoin($table, $conditions, static::JOIN_TYPE_LEFT), $types);
+
+        return $this;
+    }
+
+    /**
+     * Adds a single `RIGHT JOIN` clause to the query.
+     *
+     * This is a shorthand method for building joins via `join()`.
+     *
+     * The arguments of this method are identical to the `leftJoin()` shorthand, please refer
+     * to that methods description for further details.
+     *
+     * @param array|string $table The table to join with
+     * @param \Cake\Database\ExpressionInterface|array|string $conditions The conditions
+     * to use for joining.
+     * @param array $types a list of types associated to the conditions used for converting
+     * values to the corresponding database representation.
+     * @return $this
+     */
+    public function rightJoin($table, $conditions = [], $types = [])
+    {
+        $this->join($this->_makeJoin($table, $conditions, static::JOIN_TYPE_RIGHT), $types);
+
+        return $this;
+    }
+
+    /**
+     * Adds a single `INNER JOIN` clause to the query.
+     *
+     * This is a shorthand method for building joins via `join()`.
+     *
+     * The arguments of this method are identical to the `leftJoin()` shorthand, please refer
+     * to that method's description for further details.
+     *
+     * @param array|string $table The table to join with
+     * @param \Cake\Database\ExpressionInterface|array|string $conditions The conditions
+     * to use for joining.
+     * @param array $types a list of types associated to the conditions used for converting
+     * values to the corresponding database representation.
+     * @return $this
+     */
+    public function innerJoin($table, $conditions = [], $types = [])
+    {
+        $this->join($this->_makeJoin($table, $conditions, static::JOIN_TYPE_INNER), $types);
+
+        return $this;
+    }
+
+    /**
+     * Returns an array that can be passed to the join method describing a single join clause
+     *
+     * @param array|string $table The table to join with
+     * @param \Cake\Database\ExpressionInterface|array|string $conditions The conditions
+     * to use for joining.
+     * @param string $type the join type to use
+     * @return array
+     */
+    protected function _makeJoin($table, $conditions, $type): array
+    {
+        $alias = $table;
+
+        if (is_array($table)) {
+            $alias = key($table);
+            $table = current($table);
+        }
+
+        /**
+         * @psalm-suppress InvalidArrayOffset
+         */
+        return [
+            $alias => [
+                'table' => $table,
+                'conditions' => $conditions,
+                'type' => $type,
+            ],
+        ];
+    }
+
+    /**
+     * Adds a condition or set of conditions to be used in the WHERE clause for this
+     * query. Conditions can be expressed as an array of fields as keys with
+     * comparison operators in it, the values for the array will be used for comparing
+     * the field to such literal. Finally, conditions can be expressed as a single
+     * string or an array of strings.
+     *
+     * When using arrays, each entry will be joined to the rest of the conditions using
+     * an `AND` operator. Consecutive calls to this function will also join the new
+     * conditions specified using the AND operator. Additionally, values can be
+     * expressed using expression objects which can include other query objects.
+     *
+     * Any conditions created with this methods can be used with any `SELECT`, `UPDATE`
+     * and `DELETE` type of queries.
+     *
+     * ### Conditions using operators:
+     *
+     * ```
+     * $query->where([
+     *     'posted >=' => new DateTime('3 days ago'),
+     *     'title LIKE' => 'Hello W%',
+     *     'author_id' => 1,
+     * ], ['posted' => 'datetime']);
+     * ```
+     *
+     * The previous example produces:
+     *
+     * `WHERE posted >= 2012-01-27 AND title LIKE 'Hello W%' AND author_id = 1`
+     *
+     * Second parameter is used to specify what type is expected for each passed
+     * key. Valid types can be used from the mapped with Database\Type class.
+     *
+     * ### Nesting conditions with conjunctions:
+     *
+     * ```
+     * $query->where([
+     *     'author_id !=' => 1,
+     *     'OR' => ['published' => true, 'posted <' => new DateTime('now')],
+     *     'NOT' => ['title' => 'Hello']
+     * ], ['published' => boolean, 'posted' => 'datetime']
+     * ```
+     *
+     * The previous example produces:
+     *
+     * `WHERE author_id = 1 AND (published = 1 OR posted < '2012-02-01') AND NOT (title = 'Hello')`
+     *
+     * You can nest conditions using conjunctions as much as you like. Sometimes, you
+     * may want to define 2 different options for the same key, in that case, you can
+     * wrap each condition inside a new array:
+     *
+     * `$query->where(['OR' => [['published' => false], ['published' => true]])`
+     *
+     * Would result in:
+     *
+     * `WHERE (published = false) OR (published = true)`
+     *
+     * Keep in mind that every time you call where() with the third param set to false
+     * (default), it will join the passed conditions to the previous stored list using
+     * the `AND` operator. Also, using the same array key twice in consecutive calls to
+     * this method will not override the previous value.
+     *
+     * ### Using expressions objects:
+     *
+     * ```
+     * $exp = $query->newExpr()->add(['id !=' => 100, 'author_id' != 1])->tieWith('OR');
+     * $query->where(['published' => true], ['published' => 'boolean'])->where($exp);
+     * ```
+     *
+     * The previous example produces:
+     *
+     * `WHERE (id != 100 OR author_id != 1) AND published = 1`
+     *
+     * Other Query objects that be used as conditions for any field.
+     *
+     * ### Adding conditions in multiple steps:
+     *
+     * You can use callable functions to construct complex expressions, functions
+     * receive as first argument a new QueryExpression object and this query instance
+     * as second argument. Functions must return an expression object, that will be
+     * added the list of conditions for the query using the `AND` operator.
+     *
+     * ```
+     * $query
+     *   ->where(['title !=' => 'Hello World'])
+     *   ->where(function ($exp, $query) {
+     *     $or = $exp->or(['id' => 1]);
+     *     $and = $exp->and(['id >' => 2, 'id <' => 10]);
+     *    return $or->add($and);
+     *   });
+     * ```
+     *
+     * * The previous example produces:
+     *
+     * `WHERE title != 'Hello World' AND (id = 1 OR (id > 2 AND id < 10))`
+     *
+     * ### Conditions as strings:
+     *
+     * ```
+     * $query->where(['articles.author_id = authors.id', 'modified IS NULL']);
+     * ```
+     *
+     * The previous example produces:
+     *
+     * `WHERE articles.author_id = authors.id AND modified IS NULL`
+     *
+     * Please note that when using the array notation or the expression objects, all
+     * *values* will be correctly quoted and transformed to the correspondent database
+     * data type automatically for you, thus securing your application from SQL injections.
+     * The keys however, are not treated as unsafe input, and should be validated/sanitized.
+     *
+     * If you use string conditions make sure that your values are correctly quoted.
+     * The safest thing you can do is to never use string conditions.
+     *
+     * ### Using null-able values
+     *
+     * When using values that can be null you can use the 'IS' keyword to let the ORM generate the correct SQL based on the value's type
+     *
+     * ```
+     * $query->where([
+     *     'posted >=' => new DateTime('3 days ago'),
+     *     'category_id IS' => $category,
+     * ]);
+     * ```
+     *
+     * If $category is `null` - it will actually convert that into `category_id IS NULL` - if it's `4` it will convert it into `category_id = 4`
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|array|string|null $conditions The conditions to filter on.
+     * @param array $types Associative array of type names used to bind values to query
+     * @param bool $overwrite whether to reset conditions with passed list or not
+     * @see \Cake\Database\TypeFactory
+     * @see \Cake\Database\Expression\QueryExpression
+     * @return $this
+     */
+    public function where($conditions = null, array $types = [], bool $overwrite = false)
+    {
+        if ($overwrite) {
+            $this->_parts['where'] = $this->newExpr();
+        }
+        $this->_conjugate('where', $conditions, 'AND', $types);
+
+        return $this;
+    }
+
+    /**
+     * Convenience method that adds a NOT NULL condition to the query
+     *
+     * @param \Cake\Database\ExpressionInterface|array|string $fields A single field or expressions or a list of them
+     *  that should be not null.
+     * @return $this
+     */
+    public function whereNotNull($fields)
+    {
+        if (!is_array($fields)) {
+            $fields = [$fields];
+        }
+
+        $exp = $this->newExpr();
+
+        foreach ($fields as $field) {
+            $exp->isNotNull($field);
+        }
+
+        return $this->where($exp);
+    }
+
+    /**
+     * Convenience method that adds a IS NULL condition to the query
+     *
+     * @param \Cake\Database\ExpressionInterface|array|string $fields A single field or expressions or a list of them
+     *   that should be null.
+     * @return $this
+     */
+    public function whereNull($fields)
+    {
+        if (!is_array($fields)) {
+            $fields = [$fields];
+        }
+
+        $exp = $this->newExpr();
+
+        foreach ($fields as $field) {
+            $exp->isNull($field);
+        }
+
+        return $this->where($exp);
+    }
+
+    /**
+     * Adds an IN condition or set of conditions to be used in the WHERE clause for this
+     * query.
+     *
+     * This method does allow empty inputs in contrast to where() if you set
+     * 'allowEmpty' to true.
+     * Be careful about using it without proper sanity checks.
+     *
+     * Options:
+     *
+     * - `types` - Associative array of type names used to bind values to query
+     * - `allowEmpty` - Allow empty array.
+     *
+     * @param string $field Field
+     * @param array $values Array of values
+     * @param array $options Options
+     * @return $this
+     */
+    public function whereInList(string $field, array $values, array $options = [])
+    {
+        $options += [
+            'types' => [],
+            'allowEmpty' => false,
+        ];
+
+        if ($options['allowEmpty'] && !$values) {
+            return $this->where('1=0');
+        }
+
+        return $this->where([$field . ' IN' => $values], $options['types']);
+    }
+
+    /**
+     * Adds a NOT IN condition or set of conditions to be used in the WHERE clause for this
+     * query.
+     *
+     * This method does allow empty inputs in contrast to where() if you set
+     * 'allowEmpty' to true.
+     * Be careful about using it without proper sanity checks.
+     *
+     * @param string $field Field
+     * @param array $values Array of values
+     * @param array $options Options
+     * @return $this
+     */
+    public function whereNotInList(string $field, array $values, array $options = [])
+    {
+        $options += [
+            'types' => [],
+            'allowEmpty' => false,
+        ];
+
+        if ($options['allowEmpty'] && !$values) {
+            return $this->where([$field . ' IS NOT' => null]);
+        }
+
+        return $this->where([$field . ' NOT IN' => $values], $options['types']);
+    }
+
+    /**
+     * Adds a NOT IN condition or set of conditions to be used in the WHERE clause for this
+     * query. This also allows the field to be null with a IS NULL condition since the null
+     * value would cause the NOT IN condition to always fail.
+     *
+     * This method does allow empty inputs in contrast to where() if you set
+     * 'allowEmpty' to true.
+     * Be careful about using it without proper sanity checks.
+     *
+     * @param string $field Field
+     * @param array $values Array of values
+     * @param array $options Options
+     * @return $this
+     */
+    public function whereNotInListOrNull(string $field, array $values, array $options = [])
+    {
+        $options += [
+            'types' => [],
+            'allowEmpty' => false,
+        ];
+
+        if ($options['allowEmpty'] && !$values) {
+            return $this->where([$field . ' IS NOT' => null]);
+        }
+
+        return $this->where(
+            [
+                'OR' => [$field . ' NOT IN' => $values, $field . ' IS' => null],
+            ],
+            $options['types']
+        );
+    }
+
+    /**
+     * Connects any previously defined set of conditions to the provided list
+     * using the AND operator. This function accepts the conditions list in the same
+     * format as the method `where` does, hence you can use arrays, expression objects
+     * callback functions or strings.
+     *
+     * It is important to notice that when calling this function, any previous set
+     * of conditions defined for this query will be treated as a single argument for
+     * the AND operator. This function will not only operate the most recently defined
+     * condition, but all the conditions as a whole.
+     *
+     * When using an array for defining conditions, creating constraints form each
+     * array entry will use the same logic as with the `where()` function. This means
+     * that each array entry will be joined to the other using the AND operator, unless
+     * you nest the conditions in the array using other operator.
+     *
+     * ### Examples:
+     *
+     * ```
+     * $query->where(['title' => 'Hello World')->andWhere(['author_id' => 1]);
+     * ```
+     *
+     * Will produce:
+     *
+     * `WHERE title = 'Hello World' AND author_id = 1`
+     *
+     * ```
+     * $query
+     *   ->where(['OR' => ['published' => false, 'published is NULL']])
+     *   ->andWhere(['author_id' => 1, 'comments_count >' => 10])
+     * ```
+     *
+     * Produces:
+     *
+     * `WHERE (published = 0 OR published IS NULL) AND author_id = 1 AND comments_count > 10`
+     *
+     * ```
+     * $query
+     *   ->where(['title' => 'Foo'])
+     *   ->andWhere(function ($exp, $query) {
+     *     return $exp
+     *       ->or(['author_id' => 1])
+     *       ->add(['author_id' => 2]);
+     *   });
+     * ```
+     *
+     * Generates the following conditions:
+     *
+     * `WHERE (title = 'Foo') AND (author_id = 1 OR author_id = 2)`
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions The conditions to add with AND.
+     * @param array $types Associative array of type names used to bind values to query
+     * @see \Cake\Database\Query::where()
+     * @see \Cake\Database\TypeFactory
+     * @return $this
+     */
+    public function andWhere($conditions, array $types = [])
+    {
+        $this->_conjugate('where', $conditions, 'AND', $types);
+
+        return $this;
+    }
+
+    /**
+     * Adds a single or multiple fields to be used in the ORDER clause for this query.
+     * Fields can be passed as an array of strings, array of expression
+     * objects, a single expression or a single string.
+     *
+     * If an array is passed, keys will be used as the field itself and the value will
+     * represent the order in which such field should be ordered. When called multiple
+     * times with the same fields as key, the last order definition will prevail over
+     * the others.
+     *
+     * By default this function will append any passed argument to the list of fields
+     * to be selected, unless the second argument is set to true.
+     *
+     * ### Examples:
+     *
+     * ```
+     * $query->order(['title' => 'DESC', 'author_id' => 'ASC']);
+     * ```
+     *
+     * Produces:
+     *
+     * `ORDER BY title DESC, author_id ASC`
+     *
+     * ```
+     * $query
+     *     ->order(['title' => $query->newExpr('DESC NULLS FIRST')])
+     *     ->order('author_id');
+     * ```
+     *
+     * Will generate:
+     *
+     * `ORDER BY title DESC NULLS FIRST, author_id`
+     *
+     * ```
+     * $expression = $query->newExpr()->add(['id % 2 = 0']);
+     * $query->order($expression)->order(['title' => 'ASC']);
+     * ```
+     *
+     * and
+     *
+     * ```
+     * $query->order(function ($exp, $query) {
+     *     return [$exp->add(['id % 2 = 0']), 'title' => 'ASC'];
+     * });
+     * ```
+     *
+     * Will both become:
+     *
+     * `ORDER BY (id %2 = 0), title ASC`
+     *
+     * Order fields/directions are not sanitized by the query builder.
+     * You should use an allowed list of fields/directions when passing
+     * in user-supplied data to `order()`.
+     *
+     * If you need to set complex expressions as order conditions, you
+     * should use `orderAsc()` or `orderDesc()`.
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|array|string $fields fields to be added to the list
+     * @param bool $overwrite whether to reset order with field list or not
+     * @return $this
+     */
+    public function order($fields, $overwrite = false)
+    {
+        if ($overwrite) {
+            $this->_parts['order'] = null;
+        }
+
+        if (!$fields) {
+            return $this;
+        }
+
+        if (!$this->_parts['order']) {
+            $this->_parts['order'] = new OrderByExpression();
+        }
+        $this->_conjugate('order', $fields, '', []);
+
+        return $this;
+    }
+
+    /**
+     * Add an ORDER BY clause with an ASC direction.
+     *
+     * This method allows you to set complex expressions
+     * as order conditions unlike order()
+     *
+     * Order fields are not suitable for use with user supplied data as they are
+     * not sanitized by the query builder.
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|string $field The field to order on.
+     * @param bool $overwrite Whether to reset the order clauses.
+     * @return $this
+     */
+    public function orderAsc($field, $overwrite = false)
+    {
+        if ($overwrite) {
+            $this->_parts['order'] = null;
+        }
+        if (!$field) {
+            return $this;
+        }
+
+        if ($field instanceof Closure) {
+            $field = $field($this->newExpr(), $this);
+        }
+
+        if (!$this->_parts['order']) {
+            $this->_parts['order'] = new OrderByExpression();
+        }
+        $this->_parts['order']->add(new OrderClauseExpression($field, 'ASC'));
+
+        return $this;
+    }
+
+    /**
+     * Add an ORDER BY clause with a DESC direction.
+     *
+     * This method allows you to set complex expressions
+     * as order conditions unlike order()
+     *
+     * Order fields are not suitable for use with user supplied data as they are
+     * not sanitized by the query builder.
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|string $field The field to order on.
+     * @param bool $overwrite Whether to reset the order clauses.
+     * @return $this
+     */
+    public function orderDesc($field, $overwrite = false)
+    {
+        if ($overwrite) {
+            $this->_parts['order'] = null;
+        }
+        if (!$field) {
+            return $this;
+        }
+
+        if ($field instanceof Closure) {
+            $field = $field($this->newExpr(), $this);
+        }
+
+        if (!$this->_parts['order']) {
+            $this->_parts['order'] = new OrderByExpression();
+        }
+        $this->_parts['order']->add(new OrderClauseExpression($field, 'DESC'));
+
+        return $this;
+    }
+
+    /**
+     * Adds a single or multiple fields to be used in the GROUP BY clause for this query.
+     * Fields can be passed as an array of strings, array of expression
+     * objects, a single expression or a single string.
+     *
+     * By default this function will append any passed argument to the list of fields
+     * to be grouped, unless the second argument is set to true.
+     *
+     * ### Examples:
+     *
+     * ```
+     * // Produces GROUP BY id, title
+     * $query->group(['id', 'title']);
+     *
+     * // Produces GROUP BY title
+     * $query->group('title');
+     * ```
+     *
+     * Group fields are not suitable for use with user supplied data as they are
+     * not sanitized by the query builder.
+     *
+     * @param \Cake\Database\ExpressionInterface|array|string $fields fields to be added to the list
+     * @param bool $overwrite whether to reset fields with passed list or not
+     * @return $this
+     */
+    public function group($fields, $overwrite = false)
+    {
+        if ($overwrite) {
+            $this->_parts['group'] = [];
+        }
+
+        if (!is_array($fields)) {
+            $fields = [$fields];
+        }
+
+        $this->_parts['group'] = array_merge($this->_parts['group'], array_values($fields));
+        $this->_dirty();
+
+        return $this;
+    }
+
+    /**
+     * Adds a condition or set of conditions to be used in the `HAVING` clause for this
+     * query. This method operates in exactly the same way as the method `where()`
+     * does. Please refer to its documentation for an insight on how to using each
+     * parameter.
+     *
+     * Having fields are not suitable for use with user supplied data as they are
+     * not sanitized by the query builder.
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|array|string|null $conditions The having conditions.
+     * @param array $types Associative array of type names used to bind values to query
+     * @param bool $overwrite whether to reset conditions with passed list or not
+     * @see \Cake\Database\Query::where()
+     * @return $this
+     */
+    public function having($conditions = null, $types = [], $overwrite = false)
+    {
+        if ($overwrite) {
+            $this->_parts['having'] = $this->newExpr();
+        }
+        $this->_conjugate('having', $conditions, 'AND', $types);
+
+        return $this;
+    }
+
+    /**
+     * Connects any previously defined set of conditions to the provided list
+     * using the AND operator in the HAVING clause. This method operates in exactly
+     * the same way as the method `andWhere()` does. Please refer to its
+     * documentation for an insight on how to using each parameter.
+     *
+     * Having fields are not suitable for use with user supplied data as they are
+     * not sanitized by the query builder.
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions The AND conditions for HAVING.
+     * @param array $types Associative array of type names used to bind values to query
+     * @see \Cake\Database\Query::andWhere()
+     * @return $this
+     */
+    public function andHaving($conditions, $types = [])
+    {
+        $this->_conjugate('having', $conditions, 'AND', $types);
+
+        return $this;
+    }
+
+    /**
+     * Adds a named window expression.
+     *
+     * You are responsible for adding windows in the order your database requires.
+     *
+     * @param string $name Window name
+     * @param \Cake\Database\Expression\WindowExpression|\Closure $window Window expression
+     * @param bool $overwrite Clear all previous query window expressions
+     * @return $this
+     */
+    public function window(string $name, $window, bool $overwrite = false)
+    {
+        if ($overwrite) {
+            $this->_parts['window'] = [];
+        }
+
+        if ($window instanceof Closure) {
+            $window = $window(new WindowExpression(), $this);
+            if (!($window instanceof WindowExpression)) {
+                throw new RuntimeException('You must return a `WindowExpression` from a Closure passed to `window()`.');
+            }
+        }
+
+        $this->_parts['window'][] = ['name' => new IdentifierExpression($name), 'window' => $window];
+        $this->_dirty();
+
+        return $this;
+    }
+
+    /**
+     * Set the page of results you want.
+     *
+     * This method provides an easier to use interface to set the limit + offset
+     * in the record set you want as results. If empty the limit will default to
+     * the existing limit clause, and if that too is empty, then `25` will be used.
+     *
+     * Pages must start at 1.
+     *
+     * @param int $num The page number you want.
+     * @param int|null $limit The number of rows you want in the page. If null
+     *  the current limit clause will be used.
+     * @return $this
+     * @throws \InvalidArgumentException If page number < 1.
+     */
+    public function page(int $num, ?int $limit = null)
+    {
+        if ($num < 1) {
+            throw new InvalidArgumentException('Pages must start at 1.');
+        }
+        if ($limit !== null) {
+            $this->limit($limit);
+        }
+        $limit = $this->clause('limit');
+        if ($limit === null) {
+            $limit = 25;
+            $this->limit($limit);
+        }
+        $offset = ($num - 1) * $limit;
+        if (PHP_INT_MAX <= $offset) {
+            $offset = PHP_INT_MAX;
+        }
+        $this->offset((int)$offset);
+
+        return $this;
+    }
+
+    /**
+     * Sets the number of records that should be retrieved from database,
+     * accepts an integer or an expression object that evaluates to an integer.
+     * In some databases, this operation might not be supported or will require
+     * the query to be transformed in order to limit the result set size.
+     *
+     * ### Examples
+     *
+     * ```
+     * $query->limit(10) // generates LIMIT 10
+     * $query->limit($query->newExpr()->add(['1 + 1'])); // LIMIT (1 + 1)
+     * ```
+     *
+     * @param \Cake\Database\ExpressionInterface|int|null $limit number of records to be returned
+     * @return $this
+     */
+    public function limit($limit)
+    {
+        if (is_string($limit) && !is_numeric($limit)) {
+            throw new InvalidArgumentException('Invalid value for `limit()`');
+        }
+        $this->_dirty();
+        $this->_parts['limit'] = $limit;
+
+        return $this;
+    }
+
+    /**
+     * Sets the number of records that should be skipped from the original result set
+     * This is commonly used for paginating large results. Accepts an integer or an
+     * expression object that evaluates to an integer.
+     *
+     * In some databases, this operation might not be supported or will require
+     * the query to be transformed in order to limit the result set size.
+     *
+     * ### Examples
+     *
+     * ```
+     * $query->offset(10) // generates OFFSET 10
+     * $query->offset($query->newExpr()->add(['1 + 1'])); // OFFSET (1 + 1)
+     * ```
+     *
+     * @param \Cake\Database\ExpressionInterface|int|null $offset number of records to be skipped
+     * @return $this
+     */
+    public function offset($offset)
+    {
+        if (is_string($offset) && !is_numeric($offset)) {
+            throw new InvalidArgumentException('Invalid value for `offset()`');
+        }
+        $this->_dirty();
+        $this->_parts['offset'] = $offset;
+
+        return $this;
+    }
+
+    /**
+     * Adds a complete query to be used in conjunction with an UNION operator with
+     * this query. This is used to combine the result set of this query with the one
+     * that will be returned by the passed query. You can add as many queries as you
+     * required by calling multiple times this method with different queries.
+     *
+     * By default, the UNION operator will remove duplicate rows, if you wish to include
+     * every row for all queries, use unionAll().
+     *
+     * ### Examples
+     *
+     * ```
+     * $union = (new Query($conn))->select(['id', 'title'])->from(['a' => 'articles']);
+     * $query->select(['id', 'name'])->from(['d' => 'things'])->union($union);
+     * ```
+     *
+     * Will produce:
+     *
+     * `SELECT id, name FROM things d UNION SELECT id, title FROM articles a`
+     *
+     * @param \Cake\Database\Query|string $query full SQL query to be used in UNION operator
+     * @param bool $overwrite whether to reset the list of queries to be operated or not
+     * @return $this
+     */
+    public function union($query, $overwrite = false)
+    {
+        if ($overwrite) {
+            $this->_parts['union'] = [];
+        }
+        $this->_parts['union'][] = [
+            'all' => false,
+            'query' => $query,
+        ];
+        $this->_dirty();
+
+        return $this;
+    }
+
+    /**
+     * Adds a complete query to be used in conjunction with the UNION ALL operator with
+     * this query. This is used to combine the result set of this query with the one
+     * that will be returned by the passed query. You can add as many queries as you
+     * required by calling multiple times this method with different queries.
+     *
+     * Unlike UNION, UNION ALL will not remove duplicate rows.
+     *
+     * ```
+     * $union = (new Query($conn))->select(['id', 'title'])->from(['a' => 'articles']);
+     * $query->select(['id', 'name'])->from(['d' => 'things'])->unionAll($union);
+     * ```
+     *
+     * Will produce:
+     *
+     * `SELECT id, name FROM things d UNION ALL SELECT id, title FROM articles a`
+     *
+     * @param \Cake\Database\Query|string $query full SQL query to be used in UNION operator
+     * @param bool $overwrite whether to reset the list of queries to be operated or not
+     * @return $this
+     */
+    public function unionAll($query, $overwrite = false)
+    {
+        if ($overwrite) {
+            $this->_parts['union'] = [];
+        }
+        $this->_parts['union'][] = [
+            'all' => true,
+            'query' => $query,
+        ];
+        $this->_dirty();
+
+        return $this;
+    }
+
+    /**
+     * Create an insert query.
+     *
+     * Note calling this method will reset any data previously set
+     * with Query::values().
+     *
+     * @param array $columns The columns to insert into.
+     * @param array $types A map between columns & their datatypes.
+     * @return $this
+     * @throws \RuntimeException When there are 0 columns.
+     */
+    public function insert(array $columns, array $types = [])
+    {
+        if (empty($columns)) {
+            throw new RuntimeException('At least 1 column is required to perform an insert.');
+        }
+        $this->_dirty();
+        $this->_type = 'insert';
+        $this->_parts['insert'][1] = $columns;
+        if (!$this->_parts['values']) {
+            $this->_parts['values'] = new ValuesExpression($columns, $this->getTypeMap()->setTypes($types));
+        } else {
+            $this->_parts['values']->setColumns($columns);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set the table name for insert queries.
+     *
+     * @param string $table The table name to insert into.
+     * @return $this
+     */
+    public function into(string $table)
+    {
+        $this->_dirty();
+        $this->_type = 'insert';
+        $this->_parts['insert'][0] = $table;
+
+        return $this;
+    }
+
+    /**
+     * Creates an expression that refers to an identifier. Identifiers are used to refer to field names and allow
+     * the SQL compiler to apply quotes or escape the identifier.
+     *
+     * The value is used as is, and you might be required to use aliases or include the table reference in
+     * the identifier. Do not use this method to inject SQL methods or logical statements.
+     *
+     * ### Example
+     *
+     * ```
+     * $query->newExpr()->lte('count', $query->identifier('total'));
+     * ```
+     *
+     * @param string $identifier The identifier for an expression
+     * @return \Cake\Database\ExpressionInterface
+     */
+    public function identifier(string $identifier): ExpressionInterface
+    {
+        return new IdentifierExpression($identifier);
+    }
+
+    /**
+     * Set the values for an insert query.
+     *
+     * Multi inserts can be performed by calling values() more than one time,
+     * or by providing an array of value sets. Additionally $data can be a Query
+     * instance to insert data from another SELECT statement.
+     *
+     * @param \Cake\Database\Expression\ValuesExpression|\Cake\Database\Query|array $data The data to insert.
+     * @return $this
+     * @throws \Cake\Database\Exception\DatabaseException if you try to set values before declaring columns.
+     *   Or if you try to set values on non-insert queries.
+     */
+    public function values($data)
+    {
+        if ($this->_type !== 'insert') {
+            throw new DatabaseException(
+                'You cannot add values before defining columns to use.'
+            );
+        }
+        if (empty($this->_parts['insert'])) {
+            throw new DatabaseException(
+                'You cannot add values before defining columns to use.'
+            );
+        }
+
+        $this->_dirty();
+        if ($data instanceof ValuesExpression) {
+            $this->_parts['values'] = $data;
+
+            return $this;
+        }
+
+        $this->_parts['values']->add($data);
+
+        return $this;
+    }
+
+    /**
+     * Create an update query.
+     *
+     * Can be combined with set() and where() methods to create update queries.
+     *
+     * @param \Cake\Database\ExpressionInterface|string $table The table you want to update.
+     * @return $this
+     */
+    public function update($table)
+    {
+        if (!is_string($table) && !($table instanceof ExpressionInterface)) {
+            $text = 'Table must be of type string or "%s", got "%s"';
+            $message = sprintf($text, ExpressionInterface::class, gettype($table));
+            throw new InvalidArgumentException($message);
+        }
+
+        $this->_dirty();
+        $this->_type = 'update';
+        $this->_parts['update'][0] = $table;
+
+        return $this;
+    }
+
+    /**
+     * Set one or many fields to update.
+     *
+     * ### Examples
+     *
+     * Passing a string:
+     *
+     * ```
+     * $query->update('articles')->set('title', 'The Title');
+     * ```
+     *
+     * Passing an array:
+     *
+     * ```
+     * $query->update('articles')->set(['title' => 'The Title'], ['title' => 'string']);
+     * ```
+     *
+     * Passing a callable:
+     *
+     * ```
+     * $query->update('articles')->set(function ($exp) {
+     *   return $exp->eq('title', 'The title', 'string');
+     * });
+     * ```
+     *
+     * @param \Cake\Database\Expression\QueryExpression|\Closure|array|string $key The column name or array of keys
+     *    + values to set. This can also be a QueryExpression containing a SQL fragment.
+     *    It can also be a Closure, that is required to return an expression object.
+     * @param mixed $value The value to update $key to. Can be null if $key is an
+     *    array or QueryExpression. When $key is an array, this parameter will be
+     *    used as $types instead.
+     * @param array|string $types The column types to treat data as.
+     * @return $this
+     */
+    public function set($key, $value = null, $types = [])
+    {
+        if (empty($this->_parts['set'])) {
+            $this->_parts['set'] = $this->newExpr()->setConjunction(',');
+        }
+
+        if ($key instanceof Closure) {
+            $exp = $this->newExpr()->setConjunction(',');
+            $this->_parts['set']->add($key($exp));
+
+            return $this;
+        }
+
+        if (is_array($key) || $key instanceof ExpressionInterface) {
+            $types = (array)$value;
+            $this->_parts['set']->add($key, $types);
+
+            return $this;
+        }
+
+        if (!is_string($types)) {
+            $types = null;
+        }
+        $this->_parts['set']->eq($key, $value, $types);
+
+        return $this;
+    }
+
+    /**
+     * Create a delete query.
+     *
+     * Can be combined with from(), where() and other methods to
+     * create delete queries with specific conditions.
+     *
+     * @param string|null $table The table to use when deleting.
+     * @return $this
+     */
+    public function delete(?string $table = null)
+    {
+        $this->_dirty();
+        $this->_type = 'delete';
+        if ($table !== null) {
+            $this->from($table);
+        }
+
+        return $this;
+    }
+
+    /**
+     * A string or expression that will be appended to the generated query
+     *
+     * ### Examples:
+     * ```
+     * $query->select('id')->where(['author_id' => 1])->epilog('FOR UPDATE');
+     * $query
+     *  ->insert('articles', ['title'])
+     *  ->values(['author_id' => 1])
+     *  ->epilog('RETURNING id');
+     * ```
+     *
+     * Epliog content is raw SQL and not suitable for use with user supplied data.
+     *
+     * @param \Cake\Database\ExpressionInterface|string|null $expression The expression to be appended
+     * @return $this
+     */
+    public function epilog($expression = null)
+    {
+        $this->_dirty();
+        $this->_parts['epilog'] = $expression;
+
+        return $this;
+    }
+
+    /**
+     * Returns the type of this query (select, insert, update, delete)
+     *
+     * @return string
+     */
+    public function type(): string
+    {
+        return $this->_type;
+    }
+
+    /**
+     * Returns a new QueryExpression object. This is a handy function when
+     * building complex queries using a fluent interface. You can also override
+     * this function in subclasses to use a more specialized QueryExpression class
+     * if required.
+     *
+     * You can optionally pass a single raw SQL string or an array or expressions in
+     * any format accepted by \Cake\Database\Expression\QueryExpression:
+     *
+     * ```
+     * $expression = $query->expr(); // Returns an empty expression object
+     * $expression = $query->expr('Table.column = Table2.column'); // Return a raw SQL expression
+     * ```
+     *
+     * @param \Cake\Database\ExpressionInterface|array|string|null $rawExpression A string, array or anything you want wrapped in an expression object
+     * @return \Cake\Database\Expression\QueryExpression
+     */
+    public function newExpr($rawExpression = null): QueryExpression
+    {
+        return $this->expr($rawExpression);
+    }
+
+    /**
+     * Returns a new QueryExpression object. This is a handy function when
+     * building complex queries using a fluent interface. You can also override
+     * this function in subclasses to use a more specialized QueryExpression class
+     * if required.
+     *
+     * You can optionally pass a single raw SQL string or an array or expressions in
+     * any format accepted by \Cake\Database\Expression\QueryExpression:
+     *
+     * ```
+     * $expression = $query->expr(); // Returns an empty expression object
+     * $expression = $query->expr('Table.column = Table2.column'); // Return a raw SQL expression
+     * ```
+     *
+     * @param \Cake\Database\ExpressionInterface|array|string|null $rawExpression A string, array or anything you want wrapped in an expression object
+     * @return \Cake\Database\Expression\QueryExpression
+     */
+    public function expr($rawExpression = null): QueryExpression
+    {
+        $expression = new QueryExpression([], $this->getTypeMap());
+
+        if ($rawExpression !== null) {
+            $expression->add($rawExpression);
+        }
+
+        return $expression;
+    }
+
+    /**
+     * Returns an instance of a functions builder object that can be used for
+     * generating arbitrary SQL functions.
+     *
+     * ### Example:
+     *
+     * ```
+     * $query->func()->count('*');
+     * $query->func()->dateDiff(['2012-01-05', '2012-01-02'])
+     * ```
+     *
+     * @return \Cake\Database\FunctionsBuilder
+     */
+    public function func(): FunctionsBuilder
+    {
+        if ($this->_functionsBuilder === null) {
+            $this->_functionsBuilder = new FunctionsBuilder();
+        }
+
+        return $this->_functionsBuilder;
+    }
+
+    /**
+     * Executes this query and returns a results iterator. This function is required
+     * for implementing the IteratorAggregate interface and allows the query to be
+     * iterated without having to call execute() manually, thus making it look like
+     * a result set instead of the query itself.
+     *
+     * @return \Cake\Database\StatementInterface
+     * @psalm-suppress ImplementedReturnTypeMismatch
+     */
+    #[\ReturnTypeWillChange]
+    public function getIterator()
+    {
+        if ($this->_iterator === null || $this->_dirty) {
+            $this->_iterator = $this->execute();
+        }
+
+        return $this->_iterator;
+    }
+
+    /**
+     * Returns any data that was stored in the specified clause. This is useful for
+     * modifying any internal part of the query and it is used by the SQL dialects
+     * to transform the query accordingly before it is executed. The valid clauses that
+     * can be retrieved are: delete, update, set, insert, values, select, distinct,
+     * from, join, set, where, group, having, order, limit, offset and union.
+     *
+     * The return value for each of those parts may vary. Some clauses use QueryExpression
+     * to internally store their state, some use arrays and others may use booleans or
+     * integers. This is summary of the return types for each clause.
+     *
+     * - update: string The name of the table to update
+     * - set: QueryExpression
+     * - insert: array, will return an array containing the table + columns.
+     * - values: ValuesExpression
+     * - select: array, will return empty array when no fields are set
+     * - distinct: boolean
+     * - from: array of tables
+     * - join: array
+     * - set: array
+     * - where: QueryExpression, returns null when not set
+     * - group: array
+     * - having: QueryExpression, returns null when not set
+     * - order: OrderByExpression, returns null when not set
+     * - limit: integer or QueryExpression, null when not set
+     * - offset: integer or QueryExpression, null when not set
+     * - union: array
+     *
+     * @param string $name name of the clause to be returned
+     * @return mixed
+     * @throws \InvalidArgumentException When the named clause does not exist.
+     */
+    public function clause(string $name)
+    {
+        if (!array_key_exists($name, $this->_parts)) {
+            $clauses = implode(', ', array_keys($this->_parts));
+            throw new InvalidArgumentException("The '$name' clause is not defined. Valid clauses are: $clauses");
+        }
+
+        return $this->_parts[$name];
+    }
+
+    /**
+     * Registers a callback to be executed for each result that is fetched from the
+     * result set, the callback function will receive as first parameter an array with
+     * the raw data from the database for every row that is fetched and must return the
+     * row with any possible modifications.
+     *
+     * Callbacks will be executed lazily, if only 3 rows are fetched for database it will
+     * called 3 times, event though there might be more rows to be fetched in the cursor.
+     *
+     * Callbacks are stacked in the order they are registered, if you wish to reset the stack
+     * the call this function with the second parameter set to true.
+     *
+     * If you wish to remove all decorators from the stack, set the first parameter
+     * to null and the second to true.
+     *
+     * ### Example
+     *
+     * ```
+     * $query->decorateResults(function ($row) {
+     *   $row['order_total'] = $row['subtotal'] + ($row['subtotal'] * $row['tax']);
+     *    return $row;
+     * });
+     * ```
+     *
+     * @param callable|null $callback The callback to invoke when results are fetched.
+     * @param bool $overwrite Whether this should append or replace all existing decorators.
+     * @return $this
+     */
+    public function decorateResults(?callable $callback, bool $overwrite = false)
+    {
+        if ($overwrite) {
+            $this->_resultDecorators = [];
+        }
+
+        if ($callback !== null) {
+            $this->_resultDecorators[] = $callback;
+        }
+
+        return $this;
+    }
+
+    /**
+     * This function works similar to the traverse() function, with the difference
+     * that it does a full depth traversal of the entire expression tree. This will execute
+     * the provided callback function for each ExpressionInterface object that is
+     * stored inside this query at any nesting depth in any part of the query.
+     *
+     * Callback will receive as first parameter the currently visited expression.
+     *
+     * @param callable $callback the function to be executed for each ExpressionInterface
+     *   found inside this query.
+     * @return $this
+     */
+    public function traverseExpressions(callable $callback)
+    {
+        if (!$callback instanceof Closure) {
+            $callback = Closure::fromCallable($callback);
+        }
+
+        foreach ($this->_parts as $part) {
+            $this->_expressionsVisitor($part, $callback);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Query parts traversal method used by traverseExpressions()
+     *
+     * @param \Cake\Database\ExpressionInterface|array<\Cake\Database\ExpressionInterface> $expression Query expression or
+     *   array of expressions.
+     * @param \Closure $callback The callback to be executed for each ExpressionInterface
+     *   found inside this query.
+     * @return void
+     */
+    protected function _expressionsVisitor($expression, Closure $callback): void
+    {
+        if (is_array($expression)) {
+            foreach ($expression as $e) {
+                $this->_expressionsVisitor($e, $callback);
+            }
+
+            return;
+        }
+
+        if ($expression instanceof ExpressionInterface) {
+            $expression->traverse(function ($exp) use ($callback) {
+                $this->_expressionsVisitor($exp, $callback);
+            });
+
+            if (!$expression instanceof self) {
+                $callback($expression);
+            }
+        }
+    }
+
+    /**
+     * Associates a query placeholder to a value and a type.
+     *
+     * ```
+     * $query->bind(':id', 1, 'integer');
+     * ```
+     *
+     * @param string|int $param placeholder to be replaced with quoted version
+     *   of $value
+     * @param mixed $value The value to be bound
+     * @param string|int|null $type the mapped type name, used for casting when sending
+     *   to database
+     * @return $this
+     */
+    public function bind($param, $value, $type = null)
+    {
+        $this->getValueBinder()->bind($param, $value, $type);
+
+        return $this;
+    }
+
+    /**
+     * Returns the currently used ValueBinder instance.
+     *
+     * A ValueBinder is responsible for generating query placeholders and temporarily
+     * associate values to those placeholders so that they can be passed correctly
+     * to the statement object.
+     *
+     * @return \Cake\Database\ValueBinder
+     */
+    public function getValueBinder(): ValueBinder
+    {
+        if ($this->_valueBinder === null) {
+            $this->_valueBinder = new ValueBinder();
+        }
+
+        return $this->_valueBinder;
+    }
+
+    /**
+     * Overwrite the current value binder
+     *
+     * A ValueBinder is responsible for generating query placeholders and temporarily
+     * associate values to those placeholders so that they can be passed correctly
+     * to the statement object.
+     *
+     * @param \Cake\Database\ValueBinder|null $binder The binder or null to disable binding.
+     * @return $this
+     */
+    public function setValueBinder(?ValueBinder $binder)
+    {
+        $this->_valueBinder = $binder;
+
+        return $this;
+    }
+
+    /**
+     * Enables/Disables buffered results.
+     *
+     * When enabled the results returned by this Query will be
+     * buffered. This enables you to iterate a result set multiple times, or
+     * both cache and iterate it.
+     *
+     * When disabled it will consume less memory as fetched results are not
+     * remembered for future iterations.
+     *
+     * @param bool $enable Whether to enable buffering
+     * @return $this
+     * @deprecated 4.5.0 Results will always be buffered in 5.0.
+     */
+    public function enableBufferedResults(bool $enable = true)
+    {
+        if (!$enable) {
+            deprecationWarning(
+                '4.5.0 enableBufferedResults() is deprecated. Results will always be buffered in 5.0.'
+            );
+        }
+        $this->_dirty();
+        $this->_useBufferedResults = $enable;
+
+        return $this;
+    }
+
+    /**
+     * Disables buffered results.
+     *
+     * Disabling buffering will consume less memory as fetched results are not
+     * remembered for future iterations.
+     *
+     * @return $this
+     * @deprecated 4.5.0 Results will always be buffered in 5.0.
+     */
+    public function disableBufferedResults()
+    {
+        $this->_dirty();
+        $this->_useBufferedResults = false;
+
+        return $this;
+    }
+
+    /**
+     * Returns whether buffered results are enabled/disabled.
+     *
+     * When enabled the results returned by this Query will be
+     * buffered. This enables you to iterate a result set multiple times, or
+     * both cache and iterate it.
+     *
+     * When disabled it will consume less memory as fetched results are not
+     * remembered for future iterations.
+     *
+     * @return bool
+     * @deprecated 4.5.0 Results will always be buffered in 5.0.
+     */
+    public function isBufferedResultsEnabled(): bool
+    {
+        return $this->_useBufferedResults;
+    }
+
+    /**
+     * Sets the TypeMap class where the types for each of the fields in the
+     * select clause are stored.
+     *
+     * @param \Cake\Database\TypeMap $typeMap The map object to use
+     * @return $this
+     */
+    public function setSelectTypeMap(TypeMap $typeMap)
+    {
+        $this->_selectTypeMap = $typeMap;
+        $this->_dirty();
+
+        return $this;
+    }
+
+    /**
+     * Gets the TypeMap class where the types for each of the fields in the
+     * select clause are stored.
+     *
+     * @return \Cake\Database\TypeMap
+     */
+    public function getSelectTypeMap(): TypeMap
+    {
+        if ($this->_selectTypeMap === null) {
+            $this->_selectTypeMap = new TypeMap();
+        }
+
+        return $this->_selectTypeMap;
+    }
+
+    /**
+     * Disables result casting.
+     *
+     * When disabled, the fields will be returned as received from the database
+     * driver (which in most environments means they are being returned as
+     * strings), which can improve performance with larger datasets.
+     *
+     * @return $this
+     */
+    public function disableResultsCasting()
+    {
+        $this->typeCastEnabled = false;
+
+        return $this;
+    }
+
+    /**
+     * Enables result casting.
+     *
+     * When enabled, the fields in the results returned by this Query will be
+     * cast to their corresponding PHP data type.
+     *
+     * @return $this
+     */
+    public function enableResultsCasting()
+    {
+        $this->typeCastEnabled = true;
+
+        return $this;
+    }
+
+    /**
+     * Returns whether result casting is enabled/disabled.
+     *
+     * When enabled, the fields in the results returned by this Query will be
+     * casted to their corresponding PHP data type.
+     *
+     * When disabled, the fields will be returned as received from the database
+     * driver (which in most environments means they are being returned as
+     * strings), which can improve performance with larger datasets.
+     *
+     * @return bool
+     */
+    public function isResultsCastingEnabled(): bool
+    {
+        return $this->typeCastEnabled;
+    }
+
+    /**
+     * Auxiliary function used to wrap the original statement from the driver with
+     * any registered callbacks.
+     *
+     * @param \Cake\Database\StatementInterface $statement to be decorated
+     * @return \Cake\Database\Statement\CallbackStatement|\Cake\Database\StatementInterface
+     */
+    protected function _decorateStatement(StatementInterface $statement)
+    {
+        $typeMap = $this->getSelectTypeMap();
+        $driver = $this->getConnection()->getDriver($this->connectionRole);
+
+        if ($this->typeCastEnabled && $typeMap->toArray()) {
+            $statement = new CallbackStatement($statement, $driver, new FieldTypeConverter($typeMap, $driver));
+        }
+
+        foreach ($this->_resultDecorators as $f) {
+            $statement = new CallbackStatement($statement, $driver, $f);
+        }
+
+        return $statement;
+    }
+
+    /**
+     * Helper function used to build conditions by composing QueryExpression objects.
+     *
+     * @param string $part Name of the query part to append the new part to
+     * @param \Cake\Database\ExpressionInterface|\Closure|array|string|null $append Expression or builder function to append.
+     *   to append.
+     * @param string $conjunction type of conjunction to be used to operate part
+     * @param array $types Associative array of type names used to bind values to query
+     * @return void
+     */
+    protected function _conjugate(string $part, $append, $conjunction, array $types): void
+    {
+        $expression = $this->_parts[$part] ?: $this->newExpr();
+        if (empty($append)) {
+            $this->_parts[$part] = $expression;
+
+            return;
+        }
+
+        if ($append instanceof Closure) {
+            $append = $append($this->newExpr(), $this);
+        }
+
+        if ($expression->getConjunction() === $conjunction) {
+            $expression->add($append, $types);
+        } else {
+            $expression = $this->newExpr()
+                ->setConjunction($conjunction)
+                ->add([$expression, $append], $types);
+        }
+
+        $this->_parts[$part] = $expression;
+        $this->_dirty();
+    }
+
+    /**
+     * Marks a query as dirty, removing any preprocessed information
+     * from in memory caching.
+     *
+     * @return void
+     */
+    protected function _dirty(): void
+    {
+        $this->_dirty = true;
+
+        if ($this->_iterator && $this->_valueBinder) {
+            $this->getValueBinder()->reset();
+        }
+    }
+
+    /**
+     * Handles clearing iterator and cloning all expressions and value binders.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        $this->_iterator = null;
+        if ($this->_valueBinder !== null) {
+            $this->_valueBinder = clone $this->_valueBinder;
+        }
+        if ($this->_selectTypeMap !== null) {
+            $this->_selectTypeMap = clone $this->_selectTypeMap;
+        }
+        foreach ($this->_parts as $name => $part) {
+            if (empty($part)) {
+                continue;
+            }
+            if (is_array($part)) {
+                foreach ($part as $i => $piece) {
+                    if (is_array($piece)) {
+                        foreach ($piece as $j => $value) {
+                            if ($value instanceof ExpressionInterface) {
+                                /** @psalm-suppress PossiblyUndefinedMethod */
+                                $this->_parts[$name][$i][$j] = clone $value;
+                            }
+                        }
+                    } elseif ($piece instanceof ExpressionInterface) {
+                        $this->_parts[$name][$i] = clone $piece;
+                    }
+                }
+            }
+            if ($part instanceof ExpressionInterface) {
+                $this->_parts[$name] = clone $part;
+            }
+        }
+    }
+
+    /**
+     * Returns string representation of this query (complete SQL statement).
+     *
+     * @return string
+     */
+    public function __toString(): string
+    {
+        return $this->sql();
+    }
+
+    /**
+     * Returns an array that can be used to describe the internal state of this
+     * object.
+     *
+     * @return array
+     */
+    public function __debugInfo(): array
+    {
+        try {
+            set_error_handler(
+                /** @return no-return */
+                function ($errno, $errstr) {
+                    throw new RuntimeException($errstr, $errno);
+                },
+                E_ALL
+            );
+            $sql = $this->sql();
+            $params = $this->getValueBinder()->bindings();
+        } catch (Throwable $e) {
+            $sql = 'SQL could not be generated for this query as it is incomplete.';
+            $params = [];
+        } finally {
+            restore_error_handler();
+        }
+
+        return [
+            '(help)' => 'This is a Query object, to get the results execute or iterate it.',
+            'sql' => $sql,
+            'params' => $params,
+            'defaultTypes' => $this->getDefaultTypes(),
+            'decorators' => count($this->_resultDecorators),
+            'executed' => $this->_iterator ? true : false,
+        ];
+    }
+
+    /**
+     * Helper for Query deprecation methods.
+     *
+     * @param string $method The method that is invalid.
+     * @param string $message An additional message.
+     * @return void
+     * @internal
+     */
+    protected function _deprecatedMethod($method, $message = '')
+    {
+        $class = static::class;
+        $text = "As of 4.5.0 calling {$method}() on {$class} is deprecated. " . $message;
+        deprecationWarning($text);
+    }
+}
diff --git a/vendor/cakephp/database/Query/DeleteQuery.php b/vendor/cakephp/database/Query/DeleteQuery.php
new file mode 100644
index 0000000..5c11ae9
--- /dev/null
+++ b/vendor/cakephp/database/Query/DeleteQuery.php
@@ -0,0 +1,222 @@
+_deprecatedMethod('select()', 'Create your query with selectQuery() instead.');
+
+        return parent::select($fields, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function distinct($on = [], $overwrite = false)
+    {
+        $this->_deprecatedMethod('distint()');
+
+        return parent::distinct($on, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function modifier($modifiers, $overwrite = false)
+    {
+        $this->_deprecatedMethod('modifier()');
+
+        return parent::modifier($modifiers, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function order($fields, $overwrite = false)
+    {
+        $this->_deprecatedMethod('order()');
+
+        return parent::order($fields, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function orderAsc($field, $overwrite = false)
+    {
+        $this->_deprecatedMethod('orderAsc()');
+
+        return parent::orderAsc($field, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function orderDesc($field, $overwrite = false)
+    {
+        $this->_deprecatedMethod('orderDesc()');
+
+        return parent::orderDesc($field, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function group($fields, $overwrite = false)
+    {
+        $this->_deprecatedMethod('group()');
+
+        return parent::group($fields, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function having($conditions = null, $types = [], $overwrite = false)
+    {
+        $this->_deprecatedMethod('having()');
+
+        return parent::having($conditions, $types, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function andHaving($conditions, $types = [])
+    {
+        $this->_deprecatedMethod('andHaving()');
+
+        return parent::andHaving($conditions, $types);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function page(int $num, ?int $limit = null)
+    {
+        $this->_deprecatedMethod('page()');
+
+        return parent::page($num, $limit);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function limit($limit)
+    {
+        $this->_deprecatedMethod('limit()');
+
+        return parent::limit($limit);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function offset($offset)
+    {
+        $this->_deprecatedMethod('offset()');
+
+        return parent::offset($offset);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function union($query, $overwrite = false)
+    {
+        $this->_deprecatedMethod('union()');
+
+        return parent::union($query, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function unionAll($query, $overwrite = false)
+    {
+        $this->_deprecatedMethod('unionAll()');
+
+        return parent::unionAll($query, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function insert(array $columns, array $types = [])
+    {
+        $this->_deprecatedMethod('insert()', 'Create your query with insertQuery() instead.');
+
+        return parent::insert($columns, $types);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function into(string $table)
+    {
+        $this->_deprecatedMethod('into()');
+
+        return parent::into($table);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function values($data)
+    {
+        $this->_deprecatedMethod('values()');
+
+        return parent::values($data);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function update($table)
+    {
+        $this->_deprecatedMethod('update()', 'Create your query with updateQuery() instead.');
+
+        return parent::update($table);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function set($key, $value = null, $types = [])
+    {
+        $this->_deprecatedMethod('set()');
+
+        return parent::set($key, $value, $types);
+    }
+}
diff --git a/vendor/cakephp/database/Query/InsertQuery.php b/vendor/cakephp/database/Query/InsertQuery.php
new file mode 100644
index 0000000..7d32623
--- /dev/null
+++ b/vendor/cakephp/database/Query/InsertQuery.php
@@ -0,0 +1,322 @@
+_deprecatedMethod('delete()', 'Create your query with deleteQuery() instead.');
+
+        return parent::delete($table);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function select($fields = [], bool $overwrite = false)
+    {
+        $this->_deprecatedMethod('select()', 'Create your query with selectQuery() instead.');
+
+        return parent::select($fields, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function distinct($on = [], $overwrite = false)
+    {
+        $this->_deprecatedMethod('distinct()');
+
+        return parent::distinct($on, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function modifier($modifiers, $overwrite = false)
+    {
+        $this->_deprecatedMethod('modifier()');
+
+        return parent::modifier($modifiers, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function join($tables, $types = [], $overwrite = false)
+    {
+        $this->_deprecatedMethod('join()');
+
+        return parent::join($tables, $types, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function removeJoin(string $name)
+    {
+        $this->_deprecatedMethod('removeJoin()');
+
+        return parent::removeJoin($name);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function leftJoin($table, $conditions = [], $types = [])
+    {
+        $this->_deprecatedMethod('leftJoin()');
+
+        return parent::leftJoin($table, $conditions, $types);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function rightJoin($table, $conditions = [], $types = [])
+    {
+        $this->_deprecatedMethod('rightJoin()');
+
+        return parent::rightJoin($table, $conditions, $types);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function innerJoin($table, $conditions = [], $types = [])
+    {
+        $this->_deprecatedMethod('innerJoin()');
+
+        return parent::innerJoin($table, $conditions, $types);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function group($fields, $overwrite = false)
+    {
+        $this->_deprecatedMethod('group()');
+
+        return parent::group($fields, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function having($conditions = null, $types = [], $overwrite = false)
+    {
+        $this->_deprecatedMethod('having()');
+
+        return parent::having($conditions, $types, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function andHaving($conditions, $types = [])
+    {
+        $this->_deprecatedMethod('andHaving()');
+
+        return parent::andHaving($conditions, $types);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function page(int $num, ?int $limit = null)
+    {
+        $this->_deprecatedMethod('page()');
+
+        return parent::page($num, $limit);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function offset($offset)
+    {
+        $this->_deprecatedMethod('offset()');
+
+        return parent::offset($offset);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function union($query, $overwrite = false)
+    {
+        $this->_deprecatedMethod('union()');
+
+        return parent::union($query, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function unionAll($query, $overwrite = false)
+    {
+        $this->_deprecatedMethod('union()');
+
+        return parent::unionAll($query, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function from($tables = [], $overwrite = false)
+    {
+        $this->_deprecatedMethod('from()', 'Use into() instead.');
+
+        return parent::from($tables, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function update($table)
+    {
+        $this->_deprecatedMethod('update()', 'Create your query with updateQuery() instead.');
+
+        return parent::update($table);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function set($key, $value = null, $types = [])
+    {
+        $this->_deprecatedMethod('set()');
+
+        return parent::set($key, $value, $types);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function where($conditions = null, array $types = [], bool $overwrite = false)
+    {
+        $this->_deprecatedMethod('where()');
+
+        return parent::where($conditions, $types, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function whereNotNull($fields)
+    {
+        $this->_deprecatedMethod('whereNotNull()');
+
+        return parent::whereNotNull($fields);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function whereNull($fields)
+    {
+        $this->_deprecatedMethod('whereNull()');
+
+        return parent::whereNull($fields);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function whereInList(string $field, array $values, array $options = [])
+    {
+        $this->_deprecatedMethod('whereInList()');
+
+        return parent::whereInList($field, $values, $options);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function whereNotInList(string $field, array $values, array $options = [])
+    {
+        $this->_deprecatedMethod('whereNotInList()');
+
+        return parent::whereNotInList($field, $values, $options);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function whereNotInListOrNull(string $field, array $values, array $options = [])
+    {
+        $this->_deprecatedMethod('whereNotInListOrNull()');
+
+        return parent::whereNotInListOrNull($field, $values, $options);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function andWhere($conditions, array $types = [])
+    {
+        $this->_deprecatedMethod('andWhere()');
+
+        return parent::andWhere($conditions, $types);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function order($fields, $overwrite = false)
+    {
+        $this->_deprecatedMethod('order()');
+
+        return parent::order($fields, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function orderAsc($field, $overwrite = false)
+    {
+        $this->_deprecatedMethod('orderAsc()');
+
+        return parent::orderAsc($field, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function orderDesc($field, $overwrite = false)
+    {
+        $this->_deprecatedMethod('orderDesc()');
+
+        return parent::orderDesc($field, $overwrite);
+    }
+}
diff --git a/vendor/cakephp/database/Query/SelectQuery.php b/vendor/cakephp/database/Query/SelectQuery.php
new file mode 100644
index 0000000..7d2986a
--- /dev/null
+++ b/vendor/cakephp/database/Query/SelectQuery.php
@@ -0,0 +1,127 @@
+_deprecatedMethod('delete()', 'Create your query with deleteQuery() instead.');
+
+        return parent::delete($table);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function insert(array $columns, array $types = [])
+    {
+        $this->_deprecatedMethod('insert()', 'Create your query with insertQuery() instead.');
+
+        return parent::insert($columns, $types);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function into(string $table)
+    {
+        $this->_deprecatedMethod('into()', 'Use from() instead.');
+
+        return parent::into($table);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function values($data)
+    {
+        $this->_deprecatedMethod('values()');
+
+        return parent::values($data);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function update($table)
+    {
+        $this->_deprecatedMethod('update()', 'Create your query with updateQuery() instead.');
+
+        return parent::update($table);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function set($key, $value = null, $types = [])
+    {
+        $this->_deprecatedMethod('set()');
+
+        return parent::set($key, $value, $types);
+    }
+
+    /**
+     * Sets the connection role.
+     *
+     * @param string $role Connection role ('read' or 'write')
+     * @return $this
+     */
+    public function setConnectionRole(string $role)
+    {
+        assert($role === Connection::ROLE_READ || $role === Connection::ROLE_WRITE);
+        $this->connectionRole = $role;
+
+        return $this;
+    }
+
+    /**
+     * Sets the connection role to read.
+     *
+     * @return $this
+     */
+    public function useReadRole()
+    {
+        return $this->setConnectionRole(Connection::ROLE_READ);
+    }
+
+    /**
+     * Sets the connection role to write.
+     *
+     * @return $this
+     */
+    public function useWriteRole()
+    {
+        return $this->setConnectionRole(Connection::ROLE_WRITE);
+    }
+}
diff --git a/vendor/cakephp/database/Query/UpdateQuery.php b/vendor/cakephp/database/Query/UpdateQuery.php
new file mode 100644
index 0000000..0f84cce
--- /dev/null
+++ b/vendor/cakephp/database/Query/UpdateQuery.php
@@ -0,0 +1,182 @@
+_deprecatedMethod('delete()', 'Create your query with deleteQuery() instead.');
+
+        return parent::delete($table);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function select($fields = [], bool $overwrite = false)
+    {
+        $this->_deprecatedMethod('select()', 'Create your query with selectQuery() instead.');
+
+        return parent::select($fields, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function distinct($on = [], $overwrite = false)
+    {
+        $this->_deprecatedMethod('distinct()');
+
+        return parent::distinct($on, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function modifier($modifiers, $overwrite = false)
+    {
+        $this->_deprecatedMethod('modifier()');
+
+        return parent::modifier($modifiers, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function group($fields, $overwrite = false)
+    {
+        $this->_deprecatedMethod('group()');
+
+        return parent::group($fields, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function having($conditions = null, $types = [], $overwrite = false)
+    {
+        $this->_deprecatedMethod('having()');
+
+        return parent::having($conditions, $types, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function andHaving($conditions, $types = [])
+    {
+        $this->_deprecatedMethod('andHaving()');
+
+        return parent::andHaving($conditions, $types);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function page(int $num, ?int $limit = null)
+    {
+        $this->_deprecatedMethod('page()');
+
+        return parent::page($num, $limit);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function offset($offset)
+    {
+        $this->_deprecatedMethod('offset()');
+
+        return parent::offset($offset);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function union($query, $overwrite = false)
+    {
+        $this->_deprecatedMethod('union()');
+
+        return parent::union($query, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function unionAll($query, $overwrite = false)
+    {
+        $this->_deprecatedMethod('union()');
+
+        return parent::unionAll($query, $overwrite);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function insert(array $columns, array $types = [])
+    {
+        $this->_deprecatedMethod('insert()', 'Create your query with insertQuery() instead.');
+
+        return parent::insert($columns, $types);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function into(string $table)
+    {
+        $this->_deprecatedMethod('into()', 'Use update() instead.');
+
+        return parent::into($table);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function values($data)
+    {
+        $this->_deprecatedMethod('values()');
+
+        return parent::values($data);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function from($tables = [], $overwrite = false)
+    {
+        $this->_deprecatedMethod('from()', 'Use update() instead.');
+
+        return parent::from($tables, $overwrite);
+    }
+}
diff --git a/vendor/cakephp/database/QueryCompiler.php b/vendor/cakephp/database/QueryCompiler.php
new file mode 100644
index 0000000..9ed4422
--- /dev/null
+++ b/vendor/cakephp/database/QueryCompiler.php
@@ -0,0 +1,462 @@
+
+     */
+    protected $_templates = [
+        'delete' => 'DELETE',
+        'where' => ' WHERE %s',
+        'group' => ' GROUP BY %s ',
+        'having' => ' HAVING %s ',
+        'order' => ' %s',
+        'limit' => ' LIMIT %s',
+        'offset' => ' OFFSET %s',
+        'epilog' => ' %s',
+    ];
+
+    /**
+     * The list of query clauses to traverse for generating a SELECT statement
+     *
+     * @var array
+     */
+    protected $_selectParts = [
+        'with', 'select', 'from', 'join', 'where', 'group', 'having', 'window', 'order',
+        'limit', 'offset', 'union', 'epilog',
+    ];
+
+    /**
+     * The list of query clauses to traverse for generating an UPDATE statement
+     *
+     * @var array
+     */
+    protected $_updateParts = ['with', 'update', 'set', 'where', 'epilog'];
+
+    /**
+     * The list of query clauses to traverse for generating a DELETE statement
+     *
+     * @var array
+     */
+    protected $_deleteParts = ['with', 'delete', 'modifier', 'from', 'where', 'epilog'];
+
+    /**
+     * The list of query clauses to traverse for generating an INSERT statement
+     *
+     * @var array
+     */
+    protected $_insertParts = ['with', 'insert', 'values', 'epilog'];
+
+    /**
+     * Indicate whether this query dialect supports ordered unions.
+     *
+     * Overridden in subclasses.
+     *
+     * @var bool
+     */
+    protected $_orderedUnion = true;
+
+    /**
+     * Indicate whether aliases in SELECT clause need to be always quoted.
+     *
+     * @var bool
+     */
+    protected $_quotedSelectAliases = false;
+
+    /**
+     * Returns the SQL representation of the provided query after generating
+     * the placeholders for the bound values using the provided generator
+     *
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholders
+     * @return string
+     */
+    public function compile(Query $query, ValueBinder $binder): string
+    {
+        $sql = '';
+        $type = $query->type();
+        $query->traverseParts(
+            $this->_sqlCompiler($sql, $query, $binder),
+            $this->{"_{$type}Parts"}
+        );
+
+        // Propagate bound parameters from sub-queries if the
+        // placeholders can be found in the SQL statement.
+        if ($query->getValueBinder() !== $binder) {
+            foreach ($query->getValueBinder()->bindings() as $binding) {
+                $placeholder = ':' . $binding['placeholder'];
+                if (preg_match('/' . $placeholder . '(?:\W|$)/', $sql) > 0) {
+                    $binder->bind($placeholder, $binding['value'], $binding['type']);
+                }
+            }
+        }
+
+        return $sql;
+    }
+
+    /**
+     * Returns a callable object that can be used to compile a SQL string representation
+     * of this query.
+     *
+     * @param string $sql initial sql string to append to
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return \Closure
+     */
+    protected function _sqlCompiler(string &$sql, Query $query, ValueBinder $binder): Closure
+    {
+        return function ($part, $partName) use (&$sql, $query, $binder) {
+            if (
+                $part === null ||
+                (is_array($part) && empty($part)) ||
+                ($part instanceof Countable && count($part) === 0)
+            ) {
+                return;
+            }
+
+            if ($part instanceof ExpressionInterface) {
+                $part = [$part->sql($binder)];
+            }
+            if (isset($this->_templates[$partName])) {
+                $part = $this->_stringifyExpressions((array)$part, $binder);
+                $sql .= sprintf($this->_templates[$partName], implode(', ', $part));
+
+                return;
+            }
+
+            $sql .= $this->{'_build' . $partName . 'Part'}($part, $query, $binder);
+        };
+    }
+
+    /**
+     * Helper function used to build the string representation of a `WITH` clause,
+     * it constructs the CTE definitions list and generates the `RECURSIVE`
+     * keyword when required.
+     *
+     * @param array $parts List of CTEs to be transformed to string
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string
+     */
+    protected function _buildWithPart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        $recursive = false;
+        $expressions = [];
+        foreach ($parts as $cte) {
+            $recursive = $recursive || $cte->isRecursive();
+            $expressions[] = $cte->sql($binder);
+        }
+
+        $recursive = $recursive ? 'RECURSIVE ' : '';
+
+        return sprintf('WITH %s%s ', $recursive, implode(', ', $expressions));
+    }
+
+    /**
+     * Helper function used to build the string representation of a SELECT clause,
+     * it constructs the field list taking care of aliasing and
+     * converting expression objects to string. This function also constructs the
+     * DISTINCT clause for the query.
+     *
+     * @param array $parts list of fields to be transformed to string
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string
+     */
+    protected function _buildSelectPart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        $select = 'SELECT%s %s%s';
+        if ($this->_orderedUnion && $query->clause('union')) {
+            $select = '(SELECT%s %s%s';
+        }
+        $distinct = $query->clause('distinct');
+        $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $binder);
+
+        $driver = $query->getConnection()->getDriver($query->getConnectionRole());
+        $quoteIdentifiers = $driver->isAutoQuotingEnabled() || $this->_quotedSelectAliases;
+        $normalized = [];
+        $parts = $this->_stringifyExpressions($parts, $binder);
+        foreach ($parts as $k => $p) {
+            if (!is_numeric($k)) {
+                $p = $p . ' AS ';
+                if ($quoteIdentifiers) {
+                    $p .= $driver->quoteIdentifier($k);
+                } else {
+                    $p .= $k;
+                }
+            }
+            $normalized[] = $p;
+        }
+
+        if ($distinct === true) {
+            $distinct = 'DISTINCT ';
+        }
+
+        if (is_array($distinct)) {
+            $distinct = $this->_stringifyExpressions($distinct, $binder);
+            $distinct = sprintf('DISTINCT ON (%s) ', implode(', ', $distinct));
+        }
+
+        return sprintf($select, $modifiers, $distinct, implode(', ', $normalized));
+    }
+
+    /**
+     * Helper function used to build the string representation of a FROM clause,
+     * it constructs the tables list taking care of aliasing and
+     * converting expression objects to string.
+     *
+     * @param array $parts list of tables to be transformed to string
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string
+     */
+    protected function _buildFromPart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        $select = ' FROM %s';
+        $normalized = [];
+        $parts = $this->_stringifyExpressions($parts, $binder);
+        foreach ($parts as $k => $p) {
+            if (!is_numeric($k)) {
+                $p = $p . ' ' . $k;
+            }
+            $normalized[] = $p;
+        }
+
+        return sprintf($select, implode(', ', $normalized));
+    }
+
+    /**
+     * Helper function used to build the string representation of multiple JOIN clauses,
+     * it constructs the joins list taking care of aliasing and converting
+     * expression objects to string in both the table to be joined and the conditions
+     * to be used.
+     *
+     * @param array $parts list of joins to be transformed to string
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string
+     */
+    protected function _buildJoinPart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        $joins = '';
+        foreach ($parts as $join) {
+            if (!isset($join['table'])) {
+                throw new DatabaseException(sprintf(
+                    'Could not compile join clause for alias `%s`. No table was specified. ' .
+                    'Use the `table` key to define a table.',
+                    $join['alias']
+                ));
+            }
+            if ($join['table'] instanceof ExpressionInterface) {
+                $join['table'] = '(' . $join['table']->sql($binder) . ')';
+            }
+
+            $joins .= sprintf(' %s JOIN %s %s', $join['type'], $join['table'], $join['alias']);
+
+            $condition = '';
+            if (isset($join['conditions']) && $join['conditions'] instanceof ExpressionInterface) {
+                $condition = $join['conditions']->sql($binder);
+            }
+            if ($condition === '') {
+                $joins .= ' ON 1 = 1';
+            } else {
+                $joins .= " ON {$condition}";
+            }
+        }
+
+        return $joins;
+    }
+
+    /**
+     * Helper function to build the string representation of a window clause.
+     *
+     * @param array $parts List of windows to be transformed to string
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string
+     */
+    protected function _buildWindowPart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        $windows = [];
+        foreach ($parts as $window) {
+            $windows[] = $window['name']->sql($binder) . ' AS (' . $window['window']->sql($binder) . ')';
+        }
+
+        return ' WINDOW ' . implode(', ', $windows);
+    }
+
+    /**
+     * Helper function to generate SQL for SET expressions.
+     *
+     * @param array $parts List of keys & values to set.
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string
+     */
+    protected function _buildSetPart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        $set = [];
+        foreach ($parts as $part) {
+            if ($part instanceof ExpressionInterface) {
+                $part = $part->sql($binder);
+            }
+            if ($part[0] === '(') {
+                $part = substr($part, 1, -1);
+            }
+            $set[] = $part;
+        }
+
+        return ' SET ' . implode('', $set);
+    }
+
+    /**
+     * Builds the SQL string for all the UNION clauses in this query, when dealing
+     * with query objects it will also transform them using their configured SQL
+     * dialect.
+     *
+     * @param array $parts list of queries to be operated with UNION
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string
+     */
+    protected function _buildUnionPart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        $parts = array_map(function ($p) use ($binder) {
+            $p['query'] = $p['query']->sql($binder);
+            $p['query'] = $p['query'][0] === '(' ? trim($p['query'], '()') : $p['query'];
+            $prefix = $p['all'] ? 'ALL ' : '';
+            if ($this->_orderedUnion) {
+                return "{$prefix}({$p['query']})";
+            }
+
+            return $prefix . $p['query'];
+        }, $parts);
+
+        if ($this->_orderedUnion) {
+            return sprintf(")\nUNION %s", implode("\nUNION ", $parts));
+        }
+
+        return sprintf("\nUNION %s", implode("\nUNION ", $parts));
+    }
+
+    /**
+     * Builds the SQL fragment for INSERT INTO.
+     *
+     * @param array $parts The insert parts.
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string SQL fragment.
+     */
+    protected function _buildInsertPart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        if (!isset($parts[0])) {
+            throw new DatabaseException(
+                'Could not compile insert query. No table was specified. ' .
+                'Use `into()` to define a table.'
+            );
+        }
+        $table = $parts[0];
+        $columns = $this->_stringifyExpressions($parts[1], $binder);
+        $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $binder);
+
+        return sprintf('INSERT%s INTO %s (%s)', $modifiers, $table, implode(', ', $columns));
+    }
+
+    /**
+     * Builds the SQL fragment for INSERT INTO.
+     *
+     * @param array $parts The values parts.
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string SQL fragment.
+     */
+    protected function _buildValuesPart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        return implode('', $this->_stringifyExpressions($parts, $binder));
+    }
+
+    /**
+     * Builds the SQL fragment for UPDATE.
+     *
+     * @param array $parts The update parts.
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string SQL fragment.
+     */
+    protected function _buildUpdatePart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        $table = $this->_stringifyExpressions($parts, $binder);
+        $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $binder);
+
+        return sprintf('UPDATE%s %s', $modifiers, implode(',', $table));
+    }
+
+    /**
+     * Builds the SQL modifier fragment
+     *
+     * @param array $parts The query modifier parts
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string SQL fragment.
+     */
+    protected function _buildModifierPart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        if ($parts === []) {
+            return '';
+        }
+
+        return ' ' . implode(' ', $this->_stringifyExpressions($parts, $binder, false));
+    }
+
+    /**
+     * Helper function used to covert ExpressionInterface objects inside an array
+     * into their string representation.
+     *
+     * @param array $expressions list of strings and ExpressionInterface objects
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @param bool $wrap Whether to wrap each expression object with parenthesis
+     * @return array
+     */
+    protected function _stringifyExpressions(array $expressions, ValueBinder $binder, bool $wrap = true): array
+    {
+        $result = [];
+        foreach ($expressions as $k => $expression) {
+            if ($expression instanceof ExpressionInterface) {
+                $value = $expression->sql($binder);
+                $expression = $wrap ? '(' . $value . ')' : $value;
+            }
+            $result[$k] = $expression;
+        }
+
+        return $result;
+    }
+}
diff --git a/vendor/cakephp/database/README.md b/vendor/cakephp/database/README.md
new file mode 100644
index 0000000..d7bbe8e
--- /dev/null
+++ b/vendor/cakephp/database/README.md
@@ -0,0 +1,358 @@
+[![Total Downloads](https://img.shields.io/packagist/dt/cakephp/database.svg?style=flat-square)](https://packagist.org/packages/cakephp/database)
+[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE.txt)
+
+# A flexible and lightweight Database Library for PHP
+
+This library abstracts and provides help with most aspects of dealing with relational
+databases such as keeping connections to the server, building queries,
+preventing SQL injections, inspecting and altering schemas, and with debugging and
+profiling queries sent to the database.
+
+It adopts the API from the native PDO extension in PHP for familiarity, but solves many of the
+inconsistencies PDO has, while also providing several features that extend PDO's capabilities.
+
+A distinguishing factor of this library when compared to similar database connection packages,
+is that it takes the concept of "data types" to its core. It lets you work with complex PHP objects
+or structures that can be passed as query conditions or to be inserted in the database.
+
+The typing system will intelligently convert the PHP structures when passing them to the database, and
+convert them back when retrieving.
+
+
+## Connecting to the database
+
+This library is able to work with the following databases:
+
+* MySQL
+* Postgres
+* SQLite
+* Microsoft SQL Server (2008 and above)
+
+The first thing you need to do when using this library is create a connection object.
+Before performing any operations with the connection, you need to specify a driver
+to use:
+
+```php
+use Cake\Database\Connection;
+use Cake\Database\Driver\Mysql;
+use Cake\Database\Driver\Sqlite;
+
+$connection = new Connection([
+	'driver' => Mysql::class,
+	'database' => 'test',
+	'username' => 'root',
+	'password' => 'secret',
+]);
+
+$connection2 = new Connection([
+	'driver' => Sqlite::class,
+	'database' => '/path/to/file.db'
+]);
+```
+
+Drivers are classes responsible for actually executing the commands to the database and
+correctly building the SQL according to the database specific dialect.
+
+### Connection options
+
+This is a list of possible options that can be passed when creating a connection:
+
+* `driver`: Driver class name
+* `persistent`: Creates a persistent connection
+* `host`: The server host
+* `database`: The database name
+* `username`: Login credential
+* `password`: Connection secret
+* `encoding`: The connection encoding (or charset)
+* `timezone`: The connection timezone or time offset
+
+## Using connections
+
+After creating a connection, you can immediately interact with the database. You can choose
+either to use the shorthand methods `execute()`, `insert()`, `update()`, `delete()` or use the
+`newQuery()` for using a query builder.
+
+The easiest way of executing queries is by using the `execute()` method, it will return a
+`Cake\Database\StatementInterface` that you can use to get the data back:
+
+```php
+$statement = $connection->execute('SELECT * FROM articles');
+
+while($row = $statement->fetch('assoc')) {
+	echo $row['title'] . PHP_EOL;
+}
+```
+Binding values to parametrized arguments is also possible with the execute function:
+
+```php
+$statement = $connection->execute('SELECT * FROM articles WHERE id = :id', ['id' => 1], ['id' => 'integer']);
+$results = $statement->fetch('assoc');
+```
+
+The third parameter is the types the passed values should be converted to when passed to the database. If
+no types are passed, all arguments will be interpreted as a string.
+
+Alternatively you can construct a statement manually and then fetch rows from it:
+
+```php
+$statement = $connection->prepare('SELECT * from articles WHERE id != :id');
+$statement->bind(['id' => 1], ['id' => 'integer']);
+$results = $statement->fetchAll('assoc');
+```
+
+The default types that are understood by this library and can be passed to the `bind()` function or to `execute()`
+are:
+
+* biginteger
+* binary
+* date
+* float
+* decimal
+* integer
+* time
+* datetime
+* timestamp
+* uuid
+
+More types can be added dynamically in a bit.
+
+Statements can be reused by binding new values to the parameters in the query:
+
+```php
+$statement = $connection->prepare('SELECT * from articles WHERE id = :id');
+$statement->bind(['id' => 1], ['id' => 'integer']);
+$results = $statement->fetchAll('assoc');
+
+$statement->bind(['id' => 1], ['id' => 'integer']);
+$results = $statement->fetchAll('assoc');
+```
+
+### Updating Rows
+
+Updating can be done using the `update()` function in the connection object. In the following
+example we will update the title of the article with id = 1:
+
+```php
+$connection->update('articles', ['title' => 'New title'], ['id' => 1]);
+```
+
+The concept of data types is central to this library, so you can use the last parameter of the function
+to specify what types should be used:
+
+```php
+$connection->update(
+	'articles',
+	['title' => 'New title'],
+	['created >=' => new DateTime('-3 day'), 'created <' => new DateTime('now')],
+	['created' => 'datetime']
+);
+```
+
+The example above will execute the following SQL:
+
+```sql
+UPDATE articles SET title = 'New Title' WHERE created >= '2014-10-10 00:00:00' AND created < '2014-10-13 00:00:00';
+```
+
+More on creating complex where conditions or more complex update queries later.
+
+### Deleting Rows
+
+Similarly, the `delete()` method is used to delete rows from the database:
+
+```php
+$connection->delete('articles', ['created <' => DateTime('now')], ['created' => 'date']);
+```
+
+Will generate the following SQL
+
+```sql
+DELETE FROM articles where created < '2014-10-10'
+```
+
+### Inserting Rows
+
+Rows can be inserted using the `insert()` method:
+
+```php
+$connection->insert(
+	'articles',
+	['title' => 'My Title', 'body' => 'Some paragraph', 'created' => new DateTime()],
+	['created' => 'datetime']
+);
+```
+
+More complex updates, deletes and insert queries can be generated using the `Query` class.
+
+## Query Builder
+
+One of the goals of this library is to allow the generation of both simple and complex queries with
+ease. The query builder can be accessed by getting a new instance of a query:
+
+```php
+$query = $connection->newQuery();
+```
+
+### Selecting Fields
+
+Adding fields to the `SELECT` clause:
+
+```php
+$query->select(['id', 'title', 'body']);
+
+// Results in SELECT id AS pk, title AS aliased_title, body ...
+$query->select(['pk' => 'id', 'aliased_title' => 'title', 'body']);
+
+// Use a closure
+$query->select(function ($query) {
+	return ['id', 'title', 'body'];
+});
+```
+
+### Where Conditions
+
+Generating conditions:
+
+```php
+// WHERE id = 1
+$query->where(['id' => 1]);
+
+// WHERE id > 2
+$query->where(['id >' => 1]);
+```
+
+As you can see you can use any operator by placing it with a space after the field name.
+Adding multiple conditions is easy as well:
+
+```php
+$query->where(['id >' => 1])->andWhere(['title' => 'My Title']);
+
+// Equivalent to
+$query->where(['id >' => 1, 'title' => 'My title']);
+```
+
+It is possible to generate `OR` conditions as well
+
+```php
+$query->where(['OR' => ['id >' => 1, 'title' => 'My title']]);
+```
+
+For even more complex conditions you can use closures and expression objects:
+
+```php
+$query->where(function ($exp) {
+        return $exp
+            ->eq('author_id', 2)
+            ->eq('published', true)
+            ->notEq('spam', true)
+            ->gt('view_count', 10);
+    });
+```
+
+Which results in:
+
+```sql
+SELECT * FROM articles
+WHERE
+	author_id = 2
+	AND published = 1
+	AND spam != 1
+	AND view_count > 10
+```
+
+Combining expressions is also possible:
+
+```php
+$query->where(function ($exp) {
+        $orConditions = $exp->or(['author_id' => 2])
+            ->eq('author_id', 5);
+        return $exp
+            ->not($orConditions)
+            ->lte('view_count', 10);
+    });
+```
+
+That generates:
+
+```sql
+SELECT *
+FROM articles
+WHERE
+	NOT (author_id = 2 OR author_id = 5)
+	AND view_count <= 10
+```
+
+When using the expression objects you can use the following methods to create conditions:
+
+* `eq()` Creates an equality condition.
+* `notEq()` Create an inequality condition
+* `like()` Create a condition using the LIKE operator.
+* `notLike()` Create a negated LIKE condition.
+* `in()` Create a condition using IN.
+* `notIn()` Create a negated condition using IN.
+* `gt()` Create a > condition.
+* `gte()` Create a >= condition.
+* `lt()` Create a < condition.
+* `lte()` Create a <= condition.
+* `isNull()` Create an IS NULL condition.
+* `isNotNull()` Create a negated IS NULL condition.
+
+### Aggregates and SQL Functions
+
+```php
+// Results in SELECT COUNT(*) count FROM ...
+$query->select(['count' => $query->func()->count('*')]);
+```
+
+A number of commonly used functions can be created with the func() method:
+
+* `sum()` Calculate a sum. The arguments will be treated as literal values.
+* `avg()` Calculate an average. The arguments will be treated as literal values.
+* `min()` Calculate the min of a column. The arguments will be treated as literal values.
+* `max()` Calculate the max of a column. The arguments will be treated as literal values.
+* `count()` Calculate the count. The arguments will be treated as literal values.
+* `concat()` Concatenate two values together. The arguments are treated as bound parameters unless marked as literal.
+* `coalesce()` Coalesce values. The arguments are treated as bound parameters unless marked as literal.
+* `dateDiff()` Get the difference between two dates/times. The arguments are treated as bound parameters unless marked as literal.
+* `now()` Take either 'time' or 'date' as an argument allowing you to get either the current time, or current date.
+
+When providing arguments for SQL functions, there are two kinds of parameters you can use, literal arguments and bound parameters. Literal
+parameters allow you to reference columns or other SQL literals. Bound parameters can be used to safely add user data to SQL functions.
+For example:
+
+```php
+$concat = $query->func()->concat([
+    'title' => 'literal',
+    ' NEW'
+]);
+$query->select(['title' => $concat]);
+```
+
+The above generates:
+
+```sql
+SELECT CONCAT(title, :c0) ...;
+```
+
+### Other SQL Clauses
+
+Read of all other SQL clauses that the builder is capable of generating in the [official API docs](https://api.cakephp.org/4.x/class-Cake.Database.Query.html)
+
+### Getting Results out of a Query
+
+Once you’ve made your query, you’ll want to retrieve rows from it. There are a few ways of doing this:
+
+```php
+// Iterate the query
+foreach ($query as $row) {
+    // Do stuff.
+}
+
+// Get the statement and fetch all results
+$results = $query->execute()->fetchAll('assoc');
+```
+
+## Official API
+
+You can read the official [official API docs](https://api.cakephp.org/4.x/namespace-Cake.Database.html) to learn more of what this library
+has to offer.
diff --git a/vendor/cakephp/database/Retry/ErrorCodeWaitStrategy.php b/vendor/cakephp/database/Retry/ErrorCodeWaitStrategy.php
new file mode 100644
index 0000000..010a531
--- /dev/null
+++ b/vendor/cakephp/database/Retry/ErrorCodeWaitStrategy.php
@@ -0,0 +1,69 @@
+
+     */
+    protected $errorCodes;
+
+    /**
+     * @var int
+     */
+    protected $retryInterval;
+
+    /**
+     * @param array $errorCodes DB-specific error codes that allow retrying
+     * @param int $retryInterval Seconds to wait before allowing next retry, 0 for no wait.
+     */
+    public function __construct(array $errorCodes, int $retryInterval)
+    {
+        $this->errorCodes = $errorCodes;
+        $this->retryInterval = $retryInterval;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function shouldRetry(Exception $exception, int $retryCount): bool
+    {
+        if (
+            $exception instanceof PDOException &&
+            $exception->errorInfo &&
+            in_array($exception->errorInfo[1], $this->errorCodes)
+        ) {
+            if ($this->retryInterval > 0) {
+                sleep($this->retryInterval);
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/vendor/cakephp/database/Retry/ReconnectStrategy.php b/vendor/cakephp/database/Retry/ReconnectStrategy.php
new file mode 100644
index 0000000..ac84781
--- /dev/null
+++ b/vendor/cakephp/database/Retry/ReconnectStrategy.php
@@ -0,0 +1,124 @@
+
+     */
+    protected static $causes = [
+        'gone away',
+        'Lost connection',
+        'Transaction() on null',
+        'closed the connection unexpectedly',
+        'closed unexpectedly',
+        'deadlock avoided',
+        'decryption failed or bad record mac',
+        'is dead or not enabled',
+        'no connection to the server',
+        'query_wait_timeout',
+        'reset by peer',
+        'terminate due to client_idle_limit',
+        'while sending',
+        'writing data to the connection',
+    ];
+
+    /**
+     * The connection to check for validity
+     *
+     * @var \Cake\Database\Connection
+     */
+    protected $connection;
+
+    /**
+     * Creates the ReconnectStrategy object by storing a reference to the
+     * passed connection. This reference will be used to automatically
+     * reconnect to the server in case of failure.
+     *
+     * @param \Cake\Database\Connection $connection The connection to check
+     */
+    public function __construct(Connection $connection)
+    {
+        $this->connection = $connection;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Checks whether the exception was caused by a lost connection,
+     * and returns true if it was able to successfully reconnect.
+     */
+    public function shouldRetry(Exception $exception, int $retryCount): bool
+    {
+        $message = $exception->getMessage();
+
+        foreach (static::$causes as $cause) {
+            if (strstr($message, $cause) !== false) {
+                return $this->reconnect();
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Tries to re-establish the connection to the server, if it is safe to do so
+     *
+     * @return bool Whether the connection was re-established
+     */
+    protected function reconnect(): bool
+    {
+        if ($this->connection->inTransaction()) {
+            // It is not safe to blindly reconnect in the middle of a transaction
+            return false;
+        }
+
+        try {
+            // Make sure we free any resources associated with the old connection
+            $this->connection->getDriver()->disconnect();
+        } catch (Exception $e) {
+        }
+
+        try {
+            $this->connection->connect();
+            if ($this->connection->isQueryLoggingEnabled()) {
+                $this->connection->log('[RECONNECT]');
+            }
+
+            return true;
+        } catch (Exception $e) {
+            // If there was an error connecting again, don't report it back,
+            // let the retry handler do it.
+            return false;
+        }
+    }
+}
diff --git a/vendor/cakephp/database/Schema/BaseSchema.php b/vendor/cakephp/database/Schema/BaseSchema.php
new file mode 100644
index 0000000..8d32d34
--- /dev/null
+++ b/vendor/cakephp/database/Schema/BaseSchema.php
@@ -0,0 +1,10 @@
+collection = $collection;
+        $this->prefix = $prefix;
+        $this->cacher = $cacher;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function listTablesWithoutViews(): array
+    {
+        return $this->collection->listTablesWithoutViews();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function listTables(): array
+    {
+        return $this->collection->listTables();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describe(string $name, array $options = []): TableSchemaInterface
+    {
+        $options += ['forceRefresh' => false];
+        $cacheKey = $this->cacheKey($name);
+
+        if (!$options['forceRefresh']) {
+            $cached = $this->cacher->get($cacheKey);
+            if ($cached !== null) {
+                return $cached;
+            }
+        }
+
+        $table = $this->collection->describe($name, $options);
+        $this->cacher->set($cacheKey, $table);
+
+        return $table;
+    }
+
+    /**
+     * Get the cache key for a given name.
+     *
+     * @param string $name The name to get a cache key for.
+     * @return string The cache key.
+     */
+    public function cacheKey(string $name): string
+    {
+        return $this->prefix . '_' . $name;
+    }
+
+    /**
+     * Set a cacher.
+     *
+     * @param \Psr\SimpleCache\CacheInterface $cacher Cacher object
+     * @return $this
+     */
+    public function setCacher(CacheInterface $cacher)
+    {
+        $this->cacher = $cacher;
+
+        return $this;
+    }
+
+    /**
+     * Get a cacher.
+     *
+     * @return \Psr\SimpleCache\CacheInterface $cacher Cacher object
+     */
+    public function getCacher(): CacheInterface
+    {
+        return $this->cacher;
+    }
+}
diff --git a/vendor/cakephp/database/Schema/Collection.php b/vendor/cakephp/database/Schema/Collection.php
new file mode 100644
index 0000000..181ef24
--- /dev/null
+++ b/vendor/cakephp/database/Schema/Collection.php
@@ -0,0 +1,168 @@
+_connection = $connection;
+        $this->_dialect = $connection->getDriver()->schemaDialect();
+    }
+
+    /**
+     * Get the list of tables, excluding any views, available in the current connection.
+     *
+     * @return array The list of tables in the connected database/schema.
+     */
+    public function listTablesWithoutViews(): array
+    {
+        [$sql, $params] = $this->_dialect->listTablesWithoutViewsSql($this->_connection->getDriver()->config());
+        $result = [];
+        $statement = $this->_connection->execute($sql, $params);
+        while ($row = $statement->fetch()) {
+            $result[] = $row[0];
+        }
+        $statement->closeCursor();
+
+        return $result;
+    }
+
+    /**
+     * Get the list of tables and views available in the current connection.
+     *
+     * @return array The list of tables and views in the connected database/schema.
+     */
+    public function listTables(): array
+    {
+        [$sql, $params] = $this->_dialect->listTablesSql($this->_connection->getDriver()->config());
+        $result = [];
+        $statement = $this->_connection->execute($sql, $params);
+        while ($row = $statement->fetch()) {
+            $result[] = $row[0];
+        }
+        $statement->closeCursor();
+
+        return $result;
+    }
+
+    /**
+     * Get the column metadata for a table.
+     *
+     * The name can include a database schema name in the form 'schema.table'.
+     *
+     * Caching will be applied if `cacheMetadata` key is present in the Connection
+     * configuration options. Defaults to _cake_model_ when true.
+     *
+     * ### Options
+     *
+     * - `forceRefresh` - Set to true to force rebuilding the cached metadata.
+     *   Defaults to false.
+     *
+     * @param string $name The name of the table to describe.
+     * @param array $options The options to use, see above.
+     * @return \Cake\Database\Schema\TableSchema Object with column metadata.
+     * @throws \Cake\Database\Exception\DatabaseException when table cannot be described.
+     */
+    public function describe(string $name, array $options = []): TableSchemaInterface
+    {
+        $config = $this->_connection->getDriver()->config();
+        if (strpos($name, '.')) {
+            [$config['schema'], $name] = explode('.', $name);
+        }
+        $table = $this->_connection->getDriver()->newTableSchema($name);
+
+        $this->_reflect('Column', $name, $config, $table);
+        if (count($table->columns()) === 0) {
+            throw new DatabaseException(sprintf('Cannot describe %s. It has 0 columns.', $name));
+        }
+
+        $this->_reflect('Index', $name, $config, $table);
+        $this->_reflect('ForeignKey', $name, $config, $table);
+        $this->_reflect('Options', $name, $config, $table);
+
+        return $table;
+    }
+
+    /**
+     * Helper method for running each step of the reflection process.
+     *
+     * @param string $stage The stage name.
+     * @param string $name The table name.
+     * @param array $config The config data.
+     * @param \Cake\Database\Schema\TableSchema $schema The table schema instance.
+     * @return void
+     * @throws \Cake\Database\Exception\DatabaseException on query failure.
+     * @uses \Cake\Database\Schema\SchemaDialect::describeColumnSql
+     * @uses \Cake\Database\Schema\SchemaDialect::describeIndexSql
+     * @uses \Cake\Database\Schema\SchemaDialect::describeForeignKeySql
+     * @uses \Cake\Database\Schema\SchemaDialect::describeOptionsSql
+     * @uses \Cake\Database\Schema\SchemaDialect::convertColumnDescription
+     * @uses \Cake\Database\Schema\SchemaDialect::convertIndexDescription
+     * @uses \Cake\Database\Schema\SchemaDialect::convertForeignKeyDescription
+     * @uses \Cake\Database\Schema\SchemaDialect::convertOptionsDescription
+     */
+    protected function _reflect(string $stage, string $name, array $config, TableSchema $schema): void
+    {
+        $describeMethod = "describe{$stage}Sql";
+        $convertMethod = "convert{$stage}Description";
+
+        [$sql, $params] = $this->_dialect->{$describeMethod}($name, $config);
+        if (empty($sql)) {
+            return;
+        }
+        try {
+            $statement = $this->_connection->execute($sql, $params);
+        } catch (PDOException $e) {
+            throw new DatabaseException($e->getMessage(), 500, $e);
+        }
+        /** @psalm-suppress PossiblyFalseIterator */
+        foreach ($statement->fetchAll('assoc') as $row) {
+            $this->_dialect->{$convertMethod}($schema, $row);
+        }
+        $statement->closeCursor();
+    }
+}
diff --git a/vendor/cakephp/database/Schema/CollectionInterface.php b/vendor/cakephp/database/Schema/CollectionInterface.php
new file mode 100644
index 0000000..1861e36
--- /dev/null
+++ b/vendor/cakephp/database/Schema/CollectionInterface.php
@@ -0,0 +1,54 @@
+ listTablesWithoutViews() Get the list of tables available in the current connection.
+ * This will exclude any views in the schema.
+ */
+interface CollectionInterface
+{
+    /**
+     * Get the list of tables available in the current connection.
+     *
+     * @return array The list of tables in the connected database/schema.
+     */
+    public function listTables(): array;
+
+    /**
+     * Get the column metadata for a table.
+     *
+     * Caching will be applied if `cacheMetadata` key is present in the Connection
+     * configuration options. Defaults to _cake_model_ when true.
+     *
+     * ### Options
+     *
+     * - `forceRefresh` - Set to true to force rebuilding the cached metadata.
+     *   Defaults to false.
+     *
+     * @param string $name The name of the table to describe.
+     * @param array $options The options to use, see above.
+     * @return \Cake\Database\Schema\TableSchemaInterface Object with column metadata.
+     * @throws \Cake\Database\Exception\DatabaseException when table cannot be described.
+     */
+    public function describe(string $name, array $options = []): TableSchemaInterface;
+}
diff --git a/vendor/cakephp/database/Schema/MysqlSchema.php b/vendor/cakephp/database/Schema/MysqlSchema.php
new file mode 100644
index 0000000..30b20db
--- /dev/null
+++ b/vendor/cakephp/database/Schema/MysqlSchema.php
@@ -0,0 +1,10 @@
+ $config The connection configuration to use for
+     *    getting tables from.
+     * @return array An array of (sql, params) to execute.
+     */
+    public function listTablesSql(array $config): array
+    {
+        return ['SHOW FULL TABLES FROM ' . $this->_driver->quoteIdentifier($config['database']), []];
+    }
+
+    /**
+     * Generate the SQL to list the tables, excluding all views.
+     *
+     * @param array $config The connection configuration to use for
+     *    getting tables from.
+     * @return array An array of (sql, params) to execute.
+     */
+    public function listTablesWithoutViewsSql(array $config): array
+    {
+        return [
+            'SHOW FULL TABLES FROM ' . $this->_driver->quoteIdentifier($config['database'])
+            . ' WHERE TABLE_TYPE = "BASE TABLE"'
+        , []];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeColumnSql(string $tableName, array $config): array
+    {
+        return ['SHOW FULL COLUMNS FROM ' . $this->_driver->quoteIdentifier($tableName), []];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeIndexSql(string $tableName, array $config): array
+    {
+        return ['SHOW INDEXES FROM ' . $this->_driver->quoteIdentifier($tableName), []];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeOptionsSql(string $tableName, array $config): array
+    {
+        return ['SHOW TABLE STATUS WHERE Name = ?', [$tableName]];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function convertOptionsDescription(TableSchema $schema, array $row): void
+    {
+        $schema->setOptions([
+            'engine' => $row['Engine'],
+            'collation' => $row['Collation'],
+        ]);
+    }
+
+    /**
+     * Convert a MySQL column type into an abstract type.
+     *
+     * The returned type will be a type that Cake\Database\TypeFactory can handle.
+     *
+     * @param string $column The column type + length
+     * @return array Array of column information.
+     * @throws \Cake\Database\Exception\DatabaseException When column type cannot be parsed.
+     */
+    protected function _convertColumn(string $column): array
+    {
+        preg_match('/([a-z]+)(?:\(([0-9,]+)\))?\s*([a-z]+)?/i', $column, $matches);
+        if (empty($matches)) {
+            throw new DatabaseException(sprintf('Unable to parse column type from "%s"', $column));
+        }
+
+        $col = strtolower($matches[1]);
+        $length = $precision = $scale = null;
+        if (isset($matches[2]) && strlen($matches[2])) {
+            $length = $matches[2];
+            if (strpos($matches[2], ',') !== false) {
+                [$length, $precision] = explode(',', $length);
+            }
+            $length = (int)$length;
+            $precision = (int)$precision;
+        }
+
+        $type = $this->_applyTypeSpecificColumnConversion(
+            $col,
+            compact('length', 'precision', 'scale')
+        );
+        if ($type !== null) {
+            return $type;
+        }
+
+        if (in_array($col, ['date', 'time'])) {
+            return ['type' => $col, 'length' => null];
+        }
+        if (in_array($col, ['datetime', 'timestamp'])) {
+            $typeName = $col;
+            if ($length > 0) {
+                $typeName = $col . 'fractional';
+            }
+
+            return ['type' => $typeName, 'length' => null, 'precision' => $length];
+        }
+
+        if (($col === 'tinyint' && $length === 1) || $col === 'boolean') {
+            return ['type' => TableSchema::TYPE_BOOLEAN, 'length' => null];
+        }
+
+        $unsigned = (isset($matches[3]) && strtolower($matches[3]) === 'unsigned');
+        if (strpos($col, 'bigint') !== false || $col === 'bigint') {
+            return ['type' => TableSchema::TYPE_BIGINTEGER, 'length' => null, 'unsigned' => $unsigned];
+        }
+        if ($col === 'tinyint') {
+            return ['type' => TableSchema::TYPE_TINYINTEGER, 'length' => null, 'unsigned' => $unsigned];
+        }
+        if ($col === 'smallint') {
+            return ['type' => TableSchema::TYPE_SMALLINTEGER, 'length' => null, 'unsigned' => $unsigned];
+        }
+        if (in_array($col, ['int', 'integer', 'mediumint'])) {
+            return ['type' => TableSchema::TYPE_INTEGER, 'length' => null, 'unsigned' => $unsigned];
+        }
+        if ($col === 'char' && $length === 36) {
+            return ['type' => TableSchema::TYPE_UUID, 'length' => null];
+        }
+        if ($col === 'char') {
+            return ['type' => TableSchema::TYPE_CHAR, 'length' => $length];
+        }
+        if (strpos($col, 'char') !== false) {
+            return ['type' => TableSchema::TYPE_STRING, 'length' => $length];
+        }
+        if (strpos($col, 'text') !== false) {
+            $lengthName = substr($col, 0, -4);
+            $length = TableSchema::$columnLengths[$lengthName] ?? null;
+
+            return ['type' => TableSchema::TYPE_TEXT, 'length' => $length];
+        }
+        if ($col === 'binary' && $length === 16) {
+            return ['type' => TableSchema::TYPE_BINARY_UUID, 'length' => null];
+        }
+        if (strpos($col, 'blob') !== false || in_array($col, ['binary', 'varbinary'])) {
+            $lengthName = substr($col, 0, -4);
+            $length = TableSchema::$columnLengths[$lengthName] ?? $length;
+
+            return ['type' => TableSchema::TYPE_BINARY, 'length' => $length];
+        }
+        if (strpos($col, 'float') !== false || strpos($col, 'double') !== false) {
+            return [
+                'type' => TableSchema::TYPE_FLOAT,
+                'length' => $length,
+                'precision' => $precision,
+                'unsigned' => $unsigned,
+            ];
+        }
+        if (strpos($col, 'decimal') !== false) {
+            return [
+                'type' => TableSchema::TYPE_DECIMAL,
+                'length' => $length,
+                'precision' => $precision,
+                'unsigned' => $unsigned,
+            ];
+        }
+
+        if (strpos($col, 'json') !== false) {
+            return ['type' => TableSchema::TYPE_JSON, 'length' => null];
+        }
+
+        return ['type' => TableSchema::TYPE_STRING, 'length' => null];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function convertColumnDescription(TableSchema $schema, array $row): void
+    {
+        $field = $this->_convertColumn($row['Type']);
+        $field += [
+            'null' => $row['Null'] === 'YES',
+            'default' => $row['Default'],
+            'collate' => $row['Collation'],
+            'comment' => $row['Comment'],
+        ];
+        if (isset($row['Extra']) && $row['Extra'] === 'auto_increment') {
+            $field['autoIncrement'] = true;
+        }
+        $schema->addColumn($row['Field'], $field);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function convertIndexDescription(TableSchema $schema, array $row): void
+    {
+        $type = null;
+        $columns = $length = [];
+
+        $name = $row['Key_name'];
+        if ($name === 'PRIMARY') {
+            $name = $type = TableSchema::CONSTRAINT_PRIMARY;
+        }
+
+        if (!empty($row['Column_name'])) {
+            $columns[] = $row['Column_name'];
+        }
+
+        if ($row['Index_type'] === 'FULLTEXT') {
+            $type = TableSchema::INDEX_FULLTEXT;
+        } elseif ((int)$row['Non_unique'] === 0 && $type !== 'primary') {
+            $type = TableSchema::CONSTRAINT_UNIQUE;
+        } elseif ($type !== 'primary') {
+            $type = TableSchema::INDEX_INDEX;
+        }
+
+        if (!empty($row['Sub_part'])) {
+            $length[$row['Column_name']] = $row['Sub_part'];
+        }
+        $isIndex = (
+            $type === TableSchema::INDEX_INDEX ||
+            $type === TableSchema::INDEX_FULLTEXT
+        );
+        if ($isIndex) {
+            $existing = $schema->getIndex($name);
+        } else {
+            $existing = $schema->getConstraint($name);
+        }
+
+        // MySQL multi column indexes come back as multiple rows.
+        if (!empty($existing)) {
+            $columns = array_merge($existing['columns'], $columns);
+            $length = array_merge($existing['length'], $length);
+        }
+        if ($isIndex) {
+            $schema->addIndex($name, [
+                'type' => $type,
+                'columns' => $columns,
+                'length' => $length,
+            ]);
+        } else {
+            $schema->addConstraint($name, [
+                'type' => $type,
+                'columns' => $columns,
+                'length' => $length,
+            ]);
+        }
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeForeignKeySql(string $tableName, array $config): array
+    {
+        $sql = 'SELECT * FROM information_schema.key_column_usage AS kcu
+            INNER JOIN information_schema.referential_constraints AS rc
+            ON (
+                kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
+                AND kcu.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA
+            )
+            WHERE kcu.TABLE_SCHEMA = ? AND kcu.TABLE_NAME = ? AND rc.TABLE_NAME = ?
+            ORDER BY kcu.ORDINAL_POSITION ASC';
+
+        return [$sql, [$config['database'], $tableName, $tableName]];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function convertForeignKeyDescription(TableSchema $schema, array $row): void
+    {
+        $data = [
+            'type' => TableSchema::CONSTRAINT_FOREIGN,
+            'columns' => [$row['COLUMN_NAME']],
+            'references' => [$row['REFERENCED_TABLE_NAME'], $row['REFERENCED_COLUMN_NAME']],
+            'update' => $this->_convertOnClause($row['UPDATE_RULE']),
+            'delete' => $this->_convertOnClause($row['DELETE_RULE']),
+        ];
+        $name = $row['CONSTRAINT_NAME'];
+        $schema->addConstraint($name, $data);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function truncateTableSql(TableSchema $schema): array
+    {
+        return [sprintf('TRUNCATE TABLE `%s`', $schema->name())];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function createTableSql(TableSchema $schema, array $columns, array $constraints, array $indexes): array
+    {
+        $content = implode(",\n", array_merge($columns, $constraints, $indexes));
+        $temporary = $schema->isTemporary() ? ' TEMPORARY ' : ' ';
+        $content = sprintf("CREATE%sTABLE `%s` (\n%s\n)", $temporary, $schema->name(), $content);
+        $options = $schema->getOptions();
+        if (isset($options['engine'])) {
+            $content .= sprintf(' ENGINE=%s', $options['engine']);
+        }
+        if (isset($options['charset'])) {
+            $content .= sprintf(' DEFAULT CHARSET=%s', $options['charset']);
+        }
+        if (isset($options['collate'])) {
+            $content .= sprintf(' COLLATE=%s', $options['collate']);
+        }
+
+        return [$content];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function columnSql(TableSchema $schema, string $name): string
+    {
+        /** @var array $data */
+        $data = $schema->getColumn($name);
+
+        $sql = $this->_getTypeSpecificColumnSql($data['type'], $schema, $name);
+        if ($sql !== null) {
+            return $sql;
+        }
+
+        $out = $this->_driver->quoteIdentifier($name);
+        $nativeJson = $this->_driver->supports(DriverInterface::FEATURE_JSON);
+
+        $typeMap = [
+            TableSchema::TYPE_TINYINTEGER => ' TINYINT',
+            TableSchema::TYPE_SMALLINTEGER => ' SMALLINT',
+            TableSchema::TYPE_INTEGER => ' INTEGER',
+            TableSchema::TYPE_BIGINTEGER => ' BIGINT',
+            TableSchema::TYPE_BINARY_UUID => ' BINARY(16)',
+            TableSchema::TYPE_BOOLEAN => ' BOOLEAN',
+            TableSchema::TYPE_FLOAT => ' FLOAT',
+            TableSchema::TYPE_DECIMAL => ' DECIMAL',
+            TableSchema::TYPE_DATE => ' DATE',
+            TableSchema::TYPE_TIME => ' TIME',
+            TableSchema::TYPE_DATETIME => ' DATETIME',
+            TableSchema::TYPE_DATETIME_FRACTIONAL => ' DATETIME',
+            TableSchema::TYPE_TIMESTAMP => ' TIMESTAMP',
+            TableSchema::TYPE_TIMESTAMP_FRACTIONAL => ' TIMESTAMP',
+            TableSchema::TYPE_TIMESTAMP_TIMEZONE => ' TIMESTAMP',
+            TableSchema::TYPE_CHAR => ' CHAR',
+            TableSchema::TYPE_UUID => ' CHAR(36)',
+            TableSchema::TYPE_JSON => $nativeJson ? ' JSON' : ' LONGTEXT',
+        ];
+        $specialMap = [
+            'string' => true,
+            'text' => true,
+            'char' => true,
+            'binary' => true,
+        ];
+        if (isset($typeMap[$data['type']])) {
+            $out .= $typeMap[$data['type']];
+        }
+        if (isset($specialMap[$data['type']])) {
+            switch ($data['type']) {
+                case TableSchema::TYPE_STRING:
+                    $out .= ' VARCHAR';
+                    if (!isset($data['length'])) {
+                        $data['length'] = 255;
+                    }
+                    break;
+                case TableSchema::TYPE_TEXT:
+                    $isKnownLength = in_array($data['length'], TableSchema::$columnLengths);
+                    if (empty($data['length']) || !$isKnownLength) {
+                        $out .= ' TEXT';
+                        break;
+                    }
+
+                    /** @var string $length */
+                    $length = array_search($data['length'], TableSchema::$columnLengths);
+                    $out .= ' ' . strtoupper($length) . 'TEXT';
+
+                    break;
+                case TableSchema::TYPE_BINARY:
+                    $isKnownLength = in_array($data['length'], TableSchema::$columnLengths);
+                    if ($isKnownLength) {
+                        /** @var string $length */
+                        $length = array_search($data['length'], TableSchema::$columnLengths);
+                        $out .= ' ' . strtoupper($length) . 'BLOB';
+                        break;
+                    }
+
+                    if (empty($data['length'])) {
+                        $out .= ' BLOB';
+                        break;
+                    }
+
+                    if ($data['length'] > 2) {
+                        $out .= ' VARBINARY(' . $data['length'] . ')';
+                    } else {
+                        $out .= ' BINARY(' . $data['length'] . ')';
+                    }
+                    break;
+            }
+        }
+        $hasLength = [
+            TableSchema::TYPE_INTEGER,
+            TableSchema::TYPE_CHAR,
+            TableSchema::TYPE_SMALLINTEGER,
+            TableSchema::TYPE_TINYINTEGER,
+            TableSchema::TYPE_STRING,
+        ];
+        if (in_array($data['type'], $hasLength, true) && isset($data['length'])) {
+            $out .= '(' . $data['length'] . ')';
+        }
+
+        $lengthAndPrecisionTypes = [TableSchema::TYPE_FLOAT, TableSchema::TYPE_DECIMAL];
+        if (in_array($data['type'], $lengthAndPrecisionTypes, true) && isset($data['length'])) {
+            if (isset($data['precision'])) {
+                $out .= '(' . (int)$data['length'] . ',' . (int)$data['precision'] . ')';
+            } else {
+                $out .= '(' . (int)$data['length'] . ')';
+            }
+        }
+
+        $precisionTypes = [TableSchema::TYPE_DATETIME_FRACTIONAL, TableSchema::TYPE_TIMESTAMP_FRACTIONAL];
+        if (in_array($data['type'], $precisionTypes, true) && isset($data['precision'])) {
+            $out .= '(' . (int)$data['precision'] . ')';
+        }
+
+        $hasUnsigned = [
+            TableSchema::TYPE_TINYINTEGER,
+            TableSchema::TYPE_SMALLINTEGER,
+            TableSchema::TYPE_INTEGER,
+            TableSchema::TYPE_BIGINTEGER,
+            TableSchema::TYPE_FLOAT,
+            TableSchema::TYPE_DECIMAL,
+        ];
+        if (
+            in_array($data['type'], $hasUnsigned, true) &&
+            isset($data['unsigned']) &&
+            $data['unsigned'] === true
+        ) {
+            $out .= ' UNSIGNED';
+        }
+
+        $hasCollate = [
+            TableSchema::TYPE_TEXT,
+            TableSchema::TYPE_CHAR,
+            TableSchema::TYPE_STRING,
+        ];
+        if (in_array($data['type'], $hasCollate, true) && isset($data['collate']) && $data['collate'] !== '') {
+            $out .= ' COLLATE ' . $data['collate'];
+        }
+
+        if (isset($data['null']) && $data['null'] === false) {
+            $out .= ' NOT NULL';
+        }
+        $addAutoIncrement = (
+            $schema->getPrimaryKey() === [$name] &&
+            !$schema->hasAutoincrement() &&
+            !isset($data['autoIncrement'])
+        );
+        if (
+            in_array($data['type'], [TableSchema::TYPE_INTEGER, TableSchema::TYPE_BIGINTEGER]) &&
+            (
+                $data['autoIncrement'] === true ||
+                $addAutoIncrement
+            )
+        ) {
+            $out .= ' AUTO_INCREMENT';
+        }
+
+        $timestampTypes = [
+            TableSchema::TYPE_TIMESTAMP,
+            TableSchema::TYPE_TIMESTAMP_FRACTIONAL,
+            TableSchema::TYPE_TIMESTAMP_TIMEZONE,
+        ];
+        if (isset($data['null']) && $data['null'] === true && in_array($data['type'], $timestampTypes, true)) {
+            $out .= ' NULL';
+            unset($data['default']);
+        }
+
+        $dateTimeTypes = [
+            TableSchema::TYPE_DATETIME,
+            TableSchema::TYPE_DATETIME_FRACTIONAL,
+            TableSchema::TYPE_TIMESTAMP,
+            TableSchema::TYPE_TIMESTAMP_FRACTIONAL,
+            TableSchema::TYPE_TIMESTAMP_TIMEZONE,
+        ];
+        if (
+            isset($data['default']) &&
+            in_array($data['type'], $dateTimeTypes) &&
+            strpos(strtolower($data['default']), 'current_timestamp') !== false
+        ) {
+            $out .= ' DEFAULT CURRENT_TIMESTAMP';
+            if (isset($data['precision'])) {
+                $out .= '(' . $data['precision'] . ')';
+            }
+            unset($data['default']);
+        }
+        if (isset($data['default'])) {
+            $out .= ' DEFAULT ' . $this->_driver->schemaValue($data['default']);
+            unset($data['default']);
+        }
+        if (isset($data['comment']) && $data['comment'] !== '') {
+            $out .= ' COMMENT ' . $this->_driver->schemaValue($data['comment']);
+        }
+
+        return $out;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function constraintSql(TableSchema $schema, string $name): string
+    {
+        /** @var array $data */
+        $data = $schema->getConstraint($name);
+        if ($data['type'] === TableSchema::CONSTRAINT_PRIMARY) {
+            $columns = array_map(
+                [$this->_driver, 'quoteIdentifier'],
+                $data['columns']
+            );
+
+            return sprintf('PRIMARY KEY (%s)', implode(', ', $columns));
+        }
+
+        $out = '';
+        if ($data['type'] === TableSchema::CONSTRAINT_UNIQUE) {
+            $out = 'UNIQUE KEY ';
+        }
+        if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) {
+            $out = 'CONSTRAINT ';
+        }
+        $out .= $this->_driver->quoteIdentifier($name);
+
+        return $this->_keySql($out, $data);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function addConstraintSql(TableSchema $schema): array
+    {
+        $sqlPattern = 'ALTER TABLE %s ADD %s;';
+        $sql = [];
+
+        foreach ($schema->constraints() as $name) {
+            /** @var array $constraint */
+            $constraint = $schema->getConstraint($name);
+            if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) {
+                $tableName = $this->_driver->quoteIdentifier($schema->name());
+                $sql[] = sprintf($sqlPattern, $tableName, $this->constraintSql($schema, $name));
+            }
+        }
+
+        return $sql;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function dropConstraintSql(TableSchema $schema): array
+    {
+        $sqlPattern = 'ALTER TABLE %s DROP FOREIGN KEY %s;';
+        $sql = [];
+
+        foreach ($schema->constraints() as $name) {
+            /** @var array $constraint */
+            $constraint = $schema->getConstraint($name);
+            if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) {
+                $tableName = $this->_driver->quoteIdentifier($schema->name());
+                $constraintName = $this->_driver->quoteIdentifier($name);
+                $sql[] = sprintf($sqlPattern, $tableName, $constraintName);
+            }
+        }
+
+        return $sql;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function indexSql(TableSchema $schema, string $name): string
+    {
+        /** @var array $data */
+        $data = $schema->getIndex($name);
+        $out = '';
+        if ($data['type'] === TableSchema::INDEX_INDEX) {
+            $out = 'KEY ';
+        }
+        if ($data['type'] === TableSchema::INDEX_FULLTEXT) {
+            $out = 'FULLTEXT KEY ';
+        }
+        $out .= $this->_driver->quoteIdentifier($name);
+
+        return $this->_keySql($out, $data);
+    }
+
+    /**
+     * Helper method for generating key SQL snippets.
+     *
+     * @param string $prefix The key prefix
+     * @param array $data Key data.
+     * @return string
+     */
+    protected function _keySql(string $prefix, array $data): string
+    {
+        $columns = array_map(
+            [$this->_driver, 'quoteIdentifier'],
+            $data['columns']
+        );
+        foreach ($data['columns'] as $i => $column) {
+            if (isset($data['length'][$column])) {
+                $columns[$i] .= sprintf('(%d)', $data['length'][$column]);
+            }
+        }
+        if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) {
+            return $prefix . sprintf(
+                ' FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE %s ON DELETE %s',
+                implode(', ', $columns),
+                $this->_driver->quoteIdentifier($data['references'][0]),
+                $this->_convertConstraintColumns($data['references'][1]),
+                $this->_foreignOnClause($data['update']),
+                $this->_foreignOnClause($data['delete'])
+            );
+        }
+
+        return $prefix . ' (' . implode(', ', $columns) . ')';
+    }
+}
+
+// phpcs:disable
+class_alias(
+    'Cake\Database\Schema\MysqlSchemaDialect',
+    'Cake\Database\Schema\MysqlSchema'
+);
+// phpcs:enable
diff --git a/vendor/cakephp/database/Schema/PostgresSchema.php b/vendor/cakephp/database/Schema/PostgresSchema.php
new file mode 100644
index 0000000..bd45991
--- /dev/null
+++ b/vendor/cakephp/database/Schema/PostgresSchema.php
@@ -0,0 +1,10 @@
+ $config The connection configuration to use for
+     *    getting tables from.
+     * @return array An array of (sql, params) to execute.
+     */
+    public function listTablesSql(array $config): array
+    {
+        $sql = 'SELECT table_name as name FROM information_schema.tables
+                WHERE table_schema = ? ORDER BY name';
+        $schema = empty($config['schema']) ? 'public' : $config['schema'];
+
+        return [$sql, [$schema]];
+    }
+
+    /**
+     * Generate the SQL to list the tables, excluding all views.
+     *
+     * @param array $config The connection configuration to use for
+     *    getting tables from.
+     * @return array An array of (sql, params) to execute.
+     */
+    public function listTablesWithoutViewsSql(array $config): array
+    {
+        $sql = 'SELECT table_name as name FROM information_schema.tables
+                WHERE table_schema = ? AND table_type = \'BASE TABLE\' ORDER BY name';
+        $schema = empty($config['schema']) ? 'public' : $config['schema'];
+
+        return [$sql, [$schema]];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeColumnSql(string $tableName, array $config): array
+    {
+        $sql = 'SELECT DISTINCT table_schema AS schema,
+            column_name AS name,
+            data_type AS type,
+            is_nullable AS null, column_default AS default,
+            character_maximum_length AS char_length,
+            c.collation_name,
+            d.description as comment,
+            ordinal_position,
+            c.datetime_precision,
+            c.numeric_precision as column_precision,
+            c.numeric_scale as column_scale,
+            pg_get_serial_sequence(attr.attrelid::regclass::text, attr.attname) IS NOT NULL AS has_serial
+        FROM information_schema.columns c
+        INNER JOIN pg_catalog.pg_namespace ns ON (ns.nspname = table_schema)
+        INNER JOIN pg_catalog.pg_class cl ON (cl.relnamespace = ns.oid AND cl.relname = table_name)
+        LEFT JOIN pg_catalog.pg_index i ON (i.indrelid = cl.oid AND i.indkey[0] = c.ordinal_position)
+        LEFT JOIN pg_catalog.pg_description d on (cl.oid = d.objoid AND d.objsubid = c.ordinal_position)
+        LEFT JOIN pg_catalog.pg_attribute attr ON (cl.oid = attr.attrelid AND column_name = attr.attname)
+        WHERE table_name = ? AND table_schema = ? AND table_catalog = ?
+        ORDER BY ordinal_position';
+
+        $schema = empty($config['schema']) ? 'public' : $config['schema'];
+
+        return [$sql, [$tableName, $schema, $config['database']]];
+    }
+
+    /**
+     * Convert a column definition to the abstract types.
+     *
+     * The returned type will be a type that
+     * Cake\Database\TypeFactory can handle.
+     *
+     * @param string $column The column type + length
+     * @throws \Cake\Database\Exception\DatabaseException when column cannot be parsed.
+     * @return array Array of column information.
+     */
+    protected function _convertColumn(string $column): array
+    {
+        preg_match('/([a-z\s]+)(?:\(([0-9,]+)\))?/i', $column, $matches);
+        if (empty($matches)) {
+            throw new DatabaseException(sprintf('Unable to parse column type from "%s"', $column));
+        }
+
+        $col = strtolower($matches[1]);
+        $length = $precision = $scale = null;
+        if (isset($matches[2])) {
+            $length = (int)$matches[2];
+        }
+
+        $type = $this->_applyTypeSpecificColumnConversion(
+            $col,
+            compact('length', 'precision', 'scale')
+        );
+        if ($type !== null) {
+            return $type;
+        }
+
+        if (in_array($col, ['date', 'time', 'boolean'], true)) {
+            return ['type' => $col, 'length' => null];
+        }
+        if (in_array($col, ['timestamptz', 'timestamp with time zone'], true)) {
+            return ['type' => TableSchema::TYPE_TIMESTAMP_TIMEZONE, 'length' => null];
+        }
+        if (strpos($col, 'timestamp') !== false) {
+            return ['type' => TableSchema::TYPE_TIMESTAMP_FRACTIONAL, 'length' => null];
+        }
+        if (strpos($col, 'time') !== false) {
+            return ['type' => TableSchema::TYPE_TIME, 'length' => null];
+        }
+        if ($col === 'serial' || $col === 'integer') {
+            return ['type' => TableSchema::TYPE_INTEGER, 'length' => 10];
+        }
+        if ($col === 'bigserial' || $col === 'bigint') {
+            return ['type' => TableSchema::TYPE_BIGINTEGER, 'length' => 20];
+        }
+        if ($col === 'smallint') {
+            return ['type' => TableSchema::TYPE_SMALLINTEGER, 'length' => 5];
+        }
+        if ($col === 'inet') {
+            return ['type' => TableSchema::TYPE_STRING, 'length' => 39];
+        }
+        if ($col === 'uuid') {
+            return ['type' => TableSchema::TYPE_UUID, 'length' => null];
+        }
+        if ($col === 'char') {
+            return ['type' => TableSchema::TYPE_CHAR, 'length' => $length];
+        }
+        if (strpos($col, 'character') !== false) {
+            return ['type' => TableSchema::TYPE_STRING, 'length' => $length];
+        }
+        // money is 'string' as it includes arbitrary text content
+        // before the number value.
+        if (strpos($col, 'money') !== false || $col === 'string') {
+            return ['type' => TableSchema::TYPE_STRING, 'length' => $length];
+        }
+        if (strpos($col, 'text') !== false) {
+            return ['type' => TableSchema::TYPE_TEXT, 'length' => null];
+        }
+        if ($col === 'bytea') {
+            return ['type' => TableSchema::TYPE_BINARY, 'length' => null];
+        }
+        if ($col === 'real' || strpos($col, 'double') !== false) {
+            return ['type' => TableSchema::TYPE_FLOAT, 'length' => null];
+        }
+        if (
+            strpos($col, 'numeric') !== false ||
+            strpos($col, 'decimal') !== false
+        ) {
+            return ['type' => TableSchema::TYPE_DECIMAL, 'length' => null];
+        }
+
+        if (strpos($col, 'json') !== false) {
+            return ['type' => TableSchema::TYPE_JSON, 'length' => null];
+        }
+
+        $length = is_numeric($length) ? $length : null;
+
+        return ['type' => TableSchema::TYPE_STRING, 'length' => $length];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function convertColumnDescription(TableSchema $schema, array $row): void
+    {
+        $field = $this->_convertColumn($row['type']);
+
+        if ($field['type'] === TableSchema::TYPE_BOOLEAN) {
+            if ($row['default'] === 'true') {
+                $row['default'] = 1;
+            }
+            if ($row['default'] === 'false') {
+                $row['default'] = 0;
+            }
+        }
+        if (!empty($row['has_serial'])) {
+            $field['autoIncrement'] = true;
+        }
+
+        $field += [
+            'default' => $this->_defaultValue($row['default']),
+            'null' => $row['null'] === 'YES',
+            'collate' => $row['collation_name'],
+            'comment' => $row['comment'],
+        ];
+        $field['length'] = $row['char_length'] ?: $field['length'];
+
+        if ($field['type'] === 'numeric' || $field['type'] === 'decimal') {
+            $field['length'] = $row['column_precision'];
+            $field['precision'] = $row['column_scale'] ?: null;
+        }
+
+        if ($field['type'] === TableSchema::TYPE_TIMESTAMP_FRACTIONAL) {
+            $field['precision'] = $row['datetime_precision'];
+            if ($field['precision'] === 0) {
+                $field['type'] = TableSchema::TYPE_TIMESTAMP;
+            }
+        }
+
+        if ($field['type'] === TableSchema::TYPE_TIMESTAMP_TIMEZONE) {
+            $field['precision'] = $row['datetime_precision'];
+        }
+
+        $schema->addColumn($row['name'], $field);
+    }
+
+    /**
+     * Manipulate the default value.
+     *
+     * Postgres includes sequence data and casting information in default values.
+     * We need to remove those.
+     *
+     * @param string|int|null $default The default value.
+     * @return string|int|null
+     */
+    protected function _defaultValue($default)
+    {
+        if (is_numeric($default) || $default === null) {
+            return $default;
+        }
+        // Sequences
+        if (strpos($default, 'nextval') === 0) {
+            return null;
+        }
+
+        if (strpos($default, 'NULL::') === 0) {
+            return null;
+        }
+
+        // Remove quotes and postgres casts
+        return preg_replace(
+            "/^'(.*)'(?:::.*)$/",
+            '$1',
+            $default
+        );
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeIndexSql(string $tableName, array $config): array
+    {
+        $sql = 'SELECT
+        c2.relname,
+        a.attname,
+        i.indisprimary,
+        i.indisunique
+        FROM pg_catalog.pg_namespace n
+        INNER JOIN pg_catalog.pg_class c ON (n.oid = c.relnamespace)
+        INNER JOIN pg_catalog.pg_index i ON (c.oid = i.indrelid)
+        INNER JOIN pg_catalog.pg_class c2 ON (c2.oid = i.indexrelid)
+        INNER JOIN pg_catalog.pg_attribute a ON (a.attrelid = c.oid AND i.indrelid::regclass = a.attrelid::regclass)
+        WHERE n.nspname = ?
+        AND a.attnum = ANY(i.indkey)
+        AND c.relname = ?
+        ORDER BY i.indisprimary DESC, i.indisunique DESC, c.relname, a.attnum';
+
+        $schema = 'public';
+        if (!empty($config['schema'])) {
+            $schema = $config['schema'];
+        }
+
+        return [$sql, [$schema, $tableName]];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function convertIndexDescription(TableSchema $schema, array $row): void
+    {
+        $type = TableSchema::INDEX_INDEX;
+        $name = $row['relname'];
+        if ($row['indisprimary']) {
+            $name = $type = TableSchema::CONSTRAINT_PRIMARY;
+        }
+        if ($row['indisunique'] && $type === TableSchema::INDEX_INDEX) {
+            $type = TableSchema::CONSTRAINT_UNIQUE;
+        }
+        if ($type === TableSchema::CONSTRAINT_PRIMARY || $type === TableSchema::CONSTRAINT_UNIQUE) {
+            $this->_convertConstraint($schema, $name, $type, $row);
+
+            return;
+        }
+        $index = $schema->getIndex($name);
+        if (!$index) {
+            $index = [
+                'type' => $type,
+                'columns' => [],
+            ];
+        }
+        $index['columns'][] = $row['attname'];
+        $schema->addIndex($name, $index);
+    }
+
+    /**
+     * Add/update a constraint into the schema object.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table to update.
+     * @param string $name The index name.
+     * @param string $type The index type.
+     * @param array $row The metadata record to update with.
+     * @return void
+     */
+    protected function _convertConstraint(TableSchema $schema, string $name, string $type, array $row): void
+    {
+        $constraint = $schema->getConstraint($name);
+        if (!$constraint) {
+            $constraint = [
+                'type' => $type,
+                'columns' => [],
+            ];
+        }
+        $constraint['columns'][] = $row['attname'];
+        $schema->addConstraint($name, $constraint);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeForeignKeySql(string $tableName, array $config): array
+    {
+        // phpcs:disable Generic.Files.LineLength
+        $sql = 'SELECT
+        c.conname AS name,
+        c.contype AS type,
+        a.attname AS column_name,
+        c.confmatchtype AS match_type,
+        c.confupdtype AS on_update,
+        c.confdeltype AS on_delete,
+        c.confrelid::regclass AS references_table,
+        ab.attname AS references_field
+        FROM pg_catalog.pg_namespace n
+        INNER JOIN pg_catalog.pg_class cl ON (n.oid = cl.relnamespace)
+        INNER JOIN pg_catalog.pg_constraint c ON (n.oid = c.connamespace)
+        INNER JOIN pg_catalog.pg_attribute a ON (a.attrelid = cl.oid AND c.conrelid = a.attrelid AND a.attnum = ANY(c.conkey))
+        INNER JOIN pg_catalog.pg_attribute ab ON (a.attrelid = cl.oid AND c.confrelid = ab.attrelid AND ab.attnum = ANY(c.confkey))
+        WHERE n.nspname = ?
+        AND cl.relname = ?
+        ORDER BY name, a.attnum, ab.attnum DESC';
+        // phpcs:enable Generic.Files.LineLength
+
+        $schema = empty($config['schema']) ? 'public' : $config['schema'];
+
+        return [$sql, [$schema, $tableName]];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function convertForeignKeyDescription(TableSchema $schema, array $row): void
+    {
+        $data = [
+            'type' => TableSchema::CONSTRAINT_FOREIGN,
+            'columns' => $row['column_name'],
+            'references' => [$row['references_table'], $row['references_field']],
+            'update' => $this->_convertOnClause($row['on_update']),
+            'delete' => $this->_convertOnClause($row['on_delete']),
+        ];
+        $schema->addConstraint($row['name'], $data);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function _convertOnClause(string $clause): string
+    {
+        if ($clause === 'r') {
+            return TableSchema::ACTION_RESTRICT;
+        }
+        if ($clause === 'a') {
+            return TableSchema::ACTION_NO_ACTION;
+        }
+        if ($clause === 'c') {
+            return TableSchema::ACTION_CASCADE;
+        }
+
+        return TableSchema::ACTION_SET_NULL;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function columnSql(TableSchema $schema, string $name): string
+    {
+        /** @var array $data */
+        $data = $schema->getColumn($name);
+
+        $sql = $this->_getTypeSpecificColumnSql($data['type'], $schema, $name);
+        if ($sql !== null) {
+            return $sql;
+        }
+
+        $out = $this->_driver->quoteIdentifier($name);
+        $typeMap = [
+            TableSchema::TYPE_TINYINTEGER => ' SMALLINT',
+            TableSchema::TYPE_SMALLINTEGER => ' SMALLINT',
+            TableSchema::TYPE_BINARY_UUID => ' UUID',
+            TableSchema::TYPE_BOOLEAN => ' BOOLEAN',
+            TableSchema::TYPE_FLOAT => ' FLOAT',
+            TableSchema::TYPE_DECIMAL => ' DECIMAL',
+            TableSchema::TYPE_DATE => ' DATE',
+            TableSchema::TYPE_TIME => ' TIME',
+            TableSchema::TYPE_DATETIME => ' TIMESTAMP',
+            TableSchema::TYPE_DATETIME_FRACTIONAL => ' TIMESTAMP',
+            TableSchema::TYPE_TIMESTAMP => ' TIMESTAMP',
+            TableSchema::TYPE_TIMESTAMP_FRACTIONAL => ' TIMESTAMP',
+            TableSchema::TYPE_TIMESTAMP_TIMEZONE => ' TIMESTAMPTZ',
+            TableSchema::TYPE_UUID => ' UUID',
+            TableSchema::TYPE_CHAR => ' CHAR',
+            TableSchema::TYPE_JSON => ' JSONB',
+        ];
+
+        if (isset($typeMap[$data['type']])) {
+            $out .= $typeMap[$data['type']];
+        }
+
+        if ($data['type'] === TableSchema::TYPE_INTEGER || $data['type'] === TableSchema::TYPE_BIGINTEGER) {
+            $type = $data['type'] === TableSchema::TYPE_INTEGER ? ' INTEGER' : ' BIGINT';
+            if ($schema->getPrimaryKey() === [$name] || $data['autoIncrement'] === true) {
+                $type = $data['type'] === TableSchema::TYPE_INTEGER ? ' SERIAL' : ' BIGSERIAL';
+                unset($data['null'], $data['default']);
+            }
+            $out .= $type;
+        }
+
+        if ($data['type'] === TableSchema::TYPE_TEXT && $data['length'] !== TableSchema::LENGTH_TINY) {
+            $out .= ' TEXT';
+        }
+        if ($data['type'] === TableSchema::TYPE_BINARY) {
+            $out .= ' BYTEA';
+        }
+
+        if ($data['type'] === TableSchema::TYPE_CHAR) {
+            $out .= '(' . $data['length'] . ')';
+        }
+
+        if (
+            $data['type'] === TableSchema::TYPE_STRING ||
+            (
+                $data['type'] === TableSchema::TYPE_TEXT &&
+                $data['length'] === TableSchema::LENGTH_TINY
+            )
+        ) {
+            $out .= ' VARCHAR';
+            if (isset($data['length']) && $data['length'] !== '') {
+                $out .= '(' . $data['length'] . ')';
+            }
+        }
+
+        $hasCollate = [TableSchema::TYPE_TEXT, TableSchema::TYPE_STRING, TableSchema::TYPE_CHAR];
+        if (in_array($data['type'], $hasCollate, true) && isset($data['collate']) && $data['collate'] !== '') {
+            $out .= ' COLLATE "' . $data['collate'] . '"';
+        }
+
+        $hasPrecision = [
+            TableSchema::TYPE_FLOAT,
+            TableSchema::TYPE_DATETIME,
+            TableSchema::TYPE_DATETIME_FRACTIONAL,
+            TableSchema::TYPE_TIMESTAMP,
+            TableSchema::TYPE_TIMESTAMP_FRACTIONAL,
+            TableSchema::TYPE_TIMESTAMP_TIMEZONE,
+        ];
+        if (in_array($data['type'], $hasPrecision) && isset($data['precision'])) {
+            $out .= '(' . $data['precision'] . ')';
+        }
+
+        if (
+            $data['type'] === TableSchema::TYPE_DECIMAL &&
+            (
+                isset($data['length']) ||
+                isset($data['precision'])
+            )
+        ) {
+            $out .= '(' . $data['length'] . ',' . (int)$data['precision'] . ')';
+        }
+
+        if (isset($data['null']) && $data['null'] === false) {
+            $out .= ' NOT NULL';
+        }
+
+        $datetimeTypes = [
+            TableSchema::TYPE_DATETIME,
+            TableSchema::TYPE_DATETIME_FRACTIONAL,
+            TableSchema::TYPE_TIMESTAMP,
+            TableSchema::TYPE_TIMESTAMP_FRACTIONAL,
+            TableSchema::TYPE_TIMESTAMP_TIMEZONE,
+        ];
+        if (
+            isset($data['default']) &&
+            in_array($data['type'], $datetimeTypes) &&
+            strtolower($data['default']) === 'current_timestamp'
+        ) {
+            $out .= ' DEFAULT CURRENT_TIMESTAMP';
+        } elseif (isset($data['default'])) {
+            $defaultValue = $data['default'];
+            if ($data['type'] === 'boolean') {
+                $defaultValue = (bool)$defaultValue;
+            }
+            $out .= ' DEFAULT ' . $this->_driver->schemaValue($defaultValue);
+        } elseif (isset($data['null']) && $data['null'] !== false) {
+            $out .= ' DEFAULT NULL';
+        }
+
+        return $out;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function addConstraintSql(TableSchema $schema): array
+    {
+        $sqlPattern = 'ALTER TABLE %s ADD %s;';
+        $sql = [];
+
+        foreach ($schema->constraints() as $name) {
+            /** @var array $constraint */
+            $constraint = $schema->getConstraint($name);
+            if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) {
+                $tableName = $this->_driver->quoteIdentifier($schema->name());
+                $sql[] = sprintf($sqlPattern, $tableName, $this->constraintSql($schema, $name));
+            }
+        }
+
+        return $sql;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function dropConstraintSql(TableSchema $schema): array
+    {
+        $sqlPattern = 'ALTER TABLE %s DROP CONSTRAINT %s;';
+        $sql = [];
+
+        foreach ($schema->constraints() as $name) {
+            /** @var array $constraint */
+            $constraint = $schema->getConstraint($name);
+            if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) {
+                $tableName = $this->_driver->quoteIdentifier($schema->name());
+                $constraintName = $this->_driver->quoteIdentifier($name);
+                $sql[] = sprintf($sqlPattern, $tableName, $constraintName);
+            }
+        }
+
+        return $sql;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function indexSql(TableSchema $schema, string $name): string
+    {
+        /** @var array $data */
+        $data = $schema->getIndex($name);
+        $columns = array_map(
+            [$this->_driver, 'quoteIdentifier'],
+            $data['columns']
+        );
+
+        return sprintf(
+            'CREATE INDEX %s ON %s (%s)',
+            $this->_driver->quoteIdentifier($name),
+            $this->_driver->quoteIdentifier($schema->name()),
+            implode(', ', $columns)
+        );
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function constraintSql(TableSchema $schema, string $name): string
+    {
+        /** @var array $data */
+        $data = $schema->getConstraint($name);
+        $out = 'CONSTRAINT ' . $this->_driver->quoteIdentifier($name);
+        if ($data['type'] === TableSchema::CONSTRAINT_PRIMARY) {
+            $out = 'PRIMARY KEY';
+        }
+        if ($data['type'] === TableSchema::CONSTRAINT_UNIQUE) {
+            $out .= ' UNIQUE';
+        }
+
+        return $this->_keySql($out, $data);
+    }
+
+    /**
+     * Helper method for generating key SQL snippets.
+     *
+     * @param string $prefix The key prefix
+     * @param array $data Key data.
+     * @return string
+     */
+    protected function _keySql(string $prefix, array $data): string
+    {
+        $columns = array_map(
+            [$this->_driver, 'quoteIdentifier'],
+            $data['columns']
+        );
+        if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) {
+            return $prefix . sprintf(
+                ' FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE %s ON DELETE %s DEFERRABLE INITIALLY IMMEDIATE',
+                implode(', ', $columns),
+                $this->_driver->quoteIdentifier($data['references'][0]),
+                $this->_convertConstraintColumns($data['references'][1]),
+                $this->_foreignOnClause($data['update']),
+                $this->_foreignOnClause($data['delete'])
+            );
+        }
+
+        return $prefix . ' (' . implode(', ', $columns) . ')';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function createTableSql(TableSchema $schema, array $columns, array $constraints, array $indexes): array
+    {
+        $content = array_merge($columns, $constraints);
+        $content = implode(",\n", array_filter($content));
+        $tableName = $this->_driver->quoteIdentifier($schema->name());
+        $dbSchema = $this->_driver->schema();
+        if ($dbSchema != 'public') {
+            $tableName = $this->_driver->quoteIdentifier($dbSchema) . '.' . $tableName;
+        }
+        $temporary = $schema->isTemporary() ? ' TEMPORARY ' : ' ';
+        $out = [];
+        $out[] = sprintf("CREATE%sTABLE %s (\n%s\n)", $temporary, $tableName, $content);
+        foreach ($indexes as $index) {
+            $out[] = $index;
+        }
+        foreach ($schema->columns() as $column) {
+            $columnData = $schema->getColumn($column);
+            if (isset($columnData['comment'])) {
+                $out[] = sprintf(
+                    'COMMENT ON COLUMN %s.%s IS %s',
+                    $tableName,
+                    $this->_driver->quoteIdentifier($column),
+                    $this->_driver->schemaValue($columnData['comment'])
+                );
+            }
+        }
+
+        return $out;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function truncateTableSql(TableSchema $schema): array
+    {
+        $name = $this->_driver->quoteIdentifier($schema->name());
+
+        return [
+            sprintf('TRUNCATE %s RESTART IDENTITY CASCADE', $name),
+        ];
+    }
+
+    /**
+     * Generate the SQL to drop a table.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema Table instance
+     * @return array SQL statements to drop a table.
+     */
+    public function dropTableSql(TableSchema $schema): array
+    {
+        $sql = sprintf(
+            'DROP TABLE %s CASCADE',
+            $this->_driver->quoteIdentifier($schema->name())
+        );
+
+        return [$sql];
+    }
+}
+
+// phpcs:disable
+class_alias(
+    'Cake\Database\Schema\PostgresSchemaDialect',
+    'Cake\Database\Schema\PostgresSchema'
+);
+// phpcs:enable
diff --git a/vendor/cakephp/database/Schema/SchemaDialect.php b/vendor/cakephp/database/Schema/SchemaDialect.php
new file mode 100644
index 0000000..a9a1f91
--- /dev/null
+++ b/vendor/cakephp/database/Schema/SchemaDialect.php
@@ -0,0 +1,346 @@
+ listTablesWithoutViewsSql(array $config) Generate the SQL to list the tables, excluding all views.
+ */
+abstract class SchemaDialect
+{
+    /**
+     * The driver instance being used.
+     *
+     * @var \Cake\Database\DriverInterface
+     */
+    protected $_driver;
+
+    /**
+     * Constructor
+     *
+     * This constructor will connect the driver so that methods like columnSql() and others
+     * will fail when the driver has not been connected.
+     *
+     * @param \Cake\Database\DriverInterface $driver The driver to use.
+     */
+    public function __construct(DriverInterface $driver)
+    {
+        $driver->connect();
+        $this->_driver = $driver;
+    }
+
+    /**
+     * Generate an ON clause for a foreign key.
+     *
+     * @param string $on The on clause
+     * @return string
+     */
+    protected function _foreignOnClause(string $on): string
+    {
+        if ($on === TableSchema::ACTION_SET_NULL) {
+            return 'SET NULL';
+        }
+        if ($on === TableSchema::ACTION_SET_DEFAULT) {
+            return 'SET DEFAULT';
+        }
+        if ($on === TableSchema::ACTION_CASCADE) {
+            return 'CASCADE';
+        }
+        if ($on === TableSchema::ACTION_RESTRICT) {
+            return 'RESTRICT';
+        }
+        if ($on === TableSchema::ACTION_NO_ACTION) {
+            return 'NO ACTION';
+        }
+
+        throw new InvalidArgumentException('Invalid value for "on": ' . $on);
+    }
+
+    /**
+     * Convert string on clauses to the abstract ones.
+     *
+     * @param string $clause The on clause to convert.
+     * @return string
+     */
+    protected function _convertOnClause(string $clause): string
+    {
+        if ($clause === 'CASCADE' || $clause === 'RESTRICT') {
+            return strtolower($clause);
+        }
+        if ($clause === 'NO ACTION') {
+            return TableSchema::ACTION_NO_ACTION;
+        }
+
+        return TableSchema::ACTION_SET_NULL;
+    }
+
+    /**
+     * Convert foreign key constraints references to a valid
+     * stringified list
+     *
+     * @param array|string $references The referenced columns of a foreign key constraint statement
+     * @return string
+     */
+    protected function _convertConstraintColumns($references): string
+    {
+        if (is_string($references)) {
+            return $this->_driver->quoteIdentifier($references);
+        }
+
+        return implode(', ', array_map(
+            [$this->_driver, 'quoteIdentifier'],
+            $references
+        ));
+    }
+
+    /**
+     * Tries to use a matching database type to generate the SQL
+     * fragment for a single column in a table.
+     *
+     * @param string $columnType The column type.
+     * @param \Cake\Database\Schema\TableSchemaInterface $schema The table schema instance the column is in.
+     * @param string $column The name of the column.
+     * @return string|null An SQL fragment, or `null` in case no corresponding type was found or the type didn't provide
+     *  custom column SQL.
+     */
+    protected function _getTypeSpecificColumnSql(
+        string $columnType,
+        TableSchemaInterface $schema,
+        string $column
+    ): ?string {
+        if (!TypeFactory::getMap($columnType)) {
+            return null;
+        }
+
+        $type = TypeFactory::build($columnType);
+        if (!($type instanceof ColumnSchemaAwareInterface)) {
+            return null;
+        }
+
+        return $type->getColumnSql($schema, $column, $this->_driver);
+    }
+
+    /**
+     * Tries to use a matching database type to convert a SQL column
+     * definition to an abstract type definition.
+     *
+     * @param string $columnType The column type.
+     * @param array $definition The column definition.
+     * @return array|null Array of column information, or `null` in case no corresponding type was found or the type
+     *  didn't provide custom column information.
+     */
+    protected function _applyTypeSpecificColumnConversion(string $columnType, array $definition): ?array
+    {
+        if (!TypeFactory::getMap($columnType)) {
+            return null;
+        }
+
+        $type = TypeFactory::build($columnType);
+        if (!($type instanceof ColumnSchemaAwareInterface)) {
+            return null;
+        }
+
+        return $type->convertColumnDefinition($definition, $this->_driver);
+    }
+
+    /**
+     * Generate the SQL to drop a table.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema Schema instance
+     * @return array SQL statements to drop a table.
+     */
+    public function dropTableSql(TableSchema $schema): array
+    {
+        $sql = sprintf(
+            'DROP TABLE %s',
+            $this->_driver->quoteIdentifier($schema->name())
+        );
+
+        return [$sql];
+    }
+
+    /**
+     * Generate the SQL to list the tables.
+     *
+     * @param array $config The connection configuration to use for
+     *    getting tables from.
+     * @return array An array of (sql, params) to execute.
+     */
+    abstract public function listTablesSql(array $config): array;
+
+    /**
+     * Generate the SQL to describe a table.
+     *
+     * @param string $tableName The table name to get information on.
+     * @param array $config The connection configuration.
+     * @return array An array of (sql, params) to execute.
+     */
+    abstract public function describeColumnSql(string $tableName, array $config): array;
+
+    /**
+     * Generate the SQL to describe the indexes in a table.
+     *
+     * @param string $tableName The table name to get information on.
+     * @param array $config The connection configuration.
+     * @return array An array of (sql, params) to execute.
+     */
+    abstract public function describeIndexSql(string $tableName, array $config): array;
+
+    /**
+     * Generate the SQL to describe the foreign keys in a table.
+     *
+     * @param string $tableName The table name to get information on.
+     * @param array $config The connection configuration.
+     * @return array An array of (sql, params) to execute.
+     */
+    abstract public function describeForeignKeySql(string $tableName, array $config): array;
+
+    /**
+     * Generate the SQL to describe table options
+     *
+     * @param string $tableName Table name.
+     * @param array $config The connection configuration.
+     * @return array SQL statements to get options for a table.
+     */
+    public function describeOptionsSql(string $tableName, array $config): array
+    {
+        return ['', ''];
+    }
+
+    /**
+     * Convert field description results into abstract schema fields.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table object to append fields to.
+     * @param array $row The row data from `describeColumnSql`.
+     * @return void
+     */
+    abstract public function convertColumnDescription(TableSchema $schema, array $row): void;
+
+    /**
+     * Convert an index description results into abstract schema indexes or constraints.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table object to append
+     *    an index or constraint to.
+     * @param array $row The row data from `describeIndexSql`.
+     * @return void
+     */
+    abstract public function convertIndexDescription(TableSchema $schema, array $row): void;
+
+    /**
+     * Convert a foreign key description into constraints on the Table object.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table object to append
+     *    a constraint to.
+     * @param array $row The row data from `describeForeignKeySql`.
+     * @return void
+     */
+    abstract public function convertForeignKeyDescription(TableSchema $schema, array $row): void;
+
+    /**
+     * Convert options data into table options.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema Table instance.
+     * @param array $row The row of data.
+     * @return void
+     */
+    public function convertOptionsDescription(TableSchema $schema, array $row): void
+    {
+    }
+
+    /**
+     * Generate the SQL to create a table.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema Table instance.
+     * @param array $columns The columns to go inside the table.
+     * @param array $constraints The constraints for the table.
+     * @param array $indexes The indexes for the table.
+     * @return array SQL statements to create a table.
+     */
+    abstract public function createTableSql(
+        TableSchema $schema,
+        array $columns,
+        array $constraints,
+        array $indexes
+    ): array;
+
+    /**
+     * Generate the SQL fragment for a single column in a table.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table instance the column is in.
+     * @param string $name The name of the column.
+     * @return string SQL fragment.
+     */
+    abstract public function columnSql(TableSchema $schema, string $name): string;
+
+    /**
+     * Generate the SQL queries needed to add foreign key constraints to the table
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table instance the foreign key constraints are.
+     * @return array SQL fragment.
+     */
+    abstract public function addConstraintSql(TableSchema $schema): array;
+
+    /**
+     * Generate the SQL queries needed to drop foreign key constraints from the table
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table instance the foreign key constraints are.
+     * @return array SQL fragment.
+     */
+    abstract public function dropConstraintSql(TableSchema $schema): array;
+
+    /**
+     * Generate the SQL fragments for defining table constraints.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table instance the column is in.
+     * @param string $name The name of the column.
+     * @return string SQL fragment.
+     */
+    abstract public function constraintSql(TableSchema $schema, string $name): string;
+
+    /**
+     * Generate the SQL fragment for a single index in a table.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table object the column is in.
+     * @param string $name The name of the column.
+     * @return string SQL fragment.
+     */
+    abstract public function indexSql(TableSchema $schema, string $name): string;
+
+    /**
+     * Generate the SQL to truncate a table.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema Table instance.
+     * @return array SQL statements to truncate a table.
+     */
+    abstract public function truncateTableSql(TableSchema $schema): array;
+}
+
+// phpcs:disable
+class_alias(
+    'Cake\Database\Schema\SchemaDialect',
+    'Cake\Database\Schema\BaseSchema'
+);
+// phpcs:enable
diff --git a/vendor/cakephp/database/Schema/SqlGeneratorInterface.php b/vendor/cakephp/database/Schema/SqlGeneratorInterface.php
new file mode 100644
index 0000000..9fbd591
--- /dev/null
+++ b/vendor/cakephp/database/Schema/SqlGeneratorInterface.php
@@ -0,0 +1,72 @@
+
+     */
+    protected $_constraintsIdMap = [];
+
+    /**
+     * Whether there is any table in this connection to SQLite containing sequences.
+     *
+     * @var bool
+     */
+    protected $_hasSequences;
+
+    /**
+     * Convert a column definition to the abstract types.
+     *
+     * The returned type will be a type that
+     * Cake\Database\TypeFactory can handle.
+     *
+     * @param string $column The column type + length
+     * @throws \Cake\Database\Exception\DatabaseException when unable to parse column type
+     * @return array Array of column information.
+     */
+    protected function _convertColumn(string $column): array
+    {
+        if ($column === '') {
+            return ['type' => TableSchema::TYPE_TEXT, 'length' => null];
+        }
+
+        preg_match('/(unsigned)?\s*([a-z]+)(?:\(([0-9,]+)\))?/i', $column, $matches);
+        if (empty($matches)) {
+            throw new DatabaseException(sprintf('Unable to parse column type from "%s"', $column));
+        }
+
+        $unsigned = false;
+        if (strtolower($matches[1]) === 'unsigned') {
+            $unsigned = true;
+        }
+
+        $col = strtolower($matches[2]);
+        $length = $precision = $scale = null;
+        if (isset($matches[3])) {
+            $length = $matches[3];
+            if (strpos($length, ',') !== false) {
+                [$length, $precision] = explode(',', $length);
+            }
+            $length = (int)$length;
+            $precision = (int)$precision;
+        }
+
+        $type = $this->_applyTypeSpecificColumnConversion(
+            $col,
+            compact('length', 'precision', 'scale')
+        );
+        if ($type !== null) {
+            return $type;
+        }
+
+        if ($col === 'bigint') {
+            return ['type' => TableSchema::TYPE_BIGINTEGER, 'length' => $length, 'unsigned' => $unsigned];
+        }
+        if ($col === 'smallint') {
+            return ['type' => TableSchema::TYPE_SMALLINTEGER, 'length' => $length, 'unsigned' => $unsigned];
+        }
+        if ($col === 'tinyint') {
+            return ['type' => TableSchema::TYPE_TINYINTEGER, 'length' => $length, 'unsigned' => $unsigned];
+        }
+        if (strpos($col, 'int') !== false) {
+            return ['type' => TableSchema::TYPE_INTEGER, 'length' => $length, 'unsigned' => $unsigned];
+        }
+        if (strpos($col, 'decimal') !== false) {
+            return [
+                'type' => TableSchema::TYPE_DECIMAL,
+                'length' => $length,
+                'precision' => $precision,
+                'unsigned' => $unsigned,
+            ];
+        }
+        if (in_array($col, ['float', 'real', 'double'])) {
+            return [
+                'type' => TableSchema::TYPE_FLOAT,
+                'length' => $length,
+                'precision' => $precision,
+                'unsigned' => $unsigned,
+            ];
+        }
+
+        if (strpos($col, 'boolean') !== false) {
+            return ['type' => TableSchema::TYPE_BOOLEAN, 'length' => null];
+        }
+
+        if (($col === 'char' && $length === 36) || $col === 'uuid') {
+            return ['type' => TableSchema::TYPE_UUID, 'length' => null];
+        }
+        if ($col === 'char') {
+            return ['type' => TableSchema::TYPE_CHAR, 'length' => $length];
+        }
+        if (strpos($col, 'char') !== false) {
+            return ['type' => TableSchema::TYPE_STRING, 'length' => $length];
+        }
+
+        if ($col === 'binary' && $length === 16) {
+            return ['type' => TableSchema::TYPE_BINARY_UUID, 'length' => null];
+        }
+        if (in_array($col, ['blob', 'clob', 'binary', 'varbinary'])) {
+            return ['type' => TableSchema::TYPE_BINARY, 'length' => $length];
+        }
+
+        $datetimeTypes = [
+            'date',
+            'time',
+            'timestamp',
+            'timestampfractional',
+            'timestamptimezone',
+            'datetime',
+            'datetimefractional',
+        ];
+        if (in_array($col, $datetimeTypes)) {
+            return ['type' => $col, 'length' => null];
+        }
+
+        return ['type' => TableSchema::TYPE_TEXT, 'length' => null];
+    }
+
+    /**
+     * Generate the SQL to list the tables and views.
+     *
+     * @param array $config The connection configuration to use for
+     *    getting tables from.
+     * @return array An array of (sql, params) to execute.
+     */
+    public function listTablesSql(array $config): array
+    {
+        return [
+            'SELECT name FROM sqlite_master ' .
+            'WHERE (type="table" OR type="view") ' .
+            'AND name != "sqlite_sequence" ORDER BY name',
+            [],
+        ];
+    }
+
+    /**
+     * Generate the SQL to list the tables, excluding all views.
+     *
+     * @param array $config The connection configuration to use for
+     *    getting tables from.
+     * @return array An array of (sql, params) to execute.
+     */
+    public function listTablesWithoutViewsSql(array $config): array
+    {
+        return [
+            'SELECT name FROM sqlite_master WHERE type="table" ' .
+            'AND name != "sqlite_sequence" ORDER BY name',
+            [],
+        ];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeColumnSql(string $tableName, array $config): array
+    {
+        $sql = sprintf(
+            'PRAGMA table_info(%s)',
+            $this->_driver->quoteIdentifier($tableName)
+        );
+
+        return [$sql, []];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function convertColumnDescription(TableSchema $schema, array $row): void
+    {
+        $field = $this->_convertColumn($row['type']);
+        $field += [
+            'null' => !$row['notnull'],
+            'default' => $this->_defaultValue($row['dflt_value']),
+        ];
+        $primary = $schema->getConstraint('primary');
+
+        if ($row['pk'] && empty($primary)) {
+            $field['null'] = false;
+            $field['autoIncrement'] = true;
+        }
+
+        // SQLite does not support autoincrement on composite keys.
+        if ($row['pk'] && !empty($primary)) {
+            $existingColumn = $primary['columns'][0];
+            /** @psalm-suppress PossiblyNullOperand */
+            $schema->addColumn($existingColumn, ['autoIncrement' => null] + $schema->getColumn($existingColumn));
+        }
+
+        $schema->addColumn($row['name'], $field);
+        if ($row['pk']) {
+            $constraint = (array)$schema->getConstraint('primary') + [
+                'type' => TableSchema::CONSTRAINT_PRIMARY,
+                'columns' => [],
+            ];
+            $constraint['columns'] = array_merge($constraint['columns'], [$row['name']]);
+            $schema->addConstraint('primary', $constraint);
+        }
+    }
+
+    /**
+     * Manipulate the default value.
+     *
+     * Sqlite includes quotes and bared NULLs in default values.
+     * We need to remove those.
+     *
+     * @param string|int|null $default The default value.
+     * @return string|int|null
+     */
+    protected function _defaultValue($default)
+    {
+        if ($default === 'NULL' || $default === null) {
+            return null;
+        }
+
+        // Remove quotes
+        if (is_string($default) && preg_match("/^'(.*)'$/", $default, $matches)) {
+            return str_replace("''", "'", $matches[1]);
+        }
+
+        return $default;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeIndexSql(string $tableName, array $config): array
+    {
+        $sql = sprintf(
+            'PRAGMA index_list(%s)',
+            $this->_driver->quoteIdentifier($tableName)
+        );
+
+        return [$sql, []];
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Since SQLite does not have a way to get metadata about all indexes at once,
+     * additional queries are done here. Sqlite constraint names are not
+     * stable, and the names for constraints will not match those used to create
+     * the table. This is a limitation in Sqlite's metadata features.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table object to append
+     *    an index or constraint to.
+     * @param array $row The row data from `describeIndexSql`.
+     * @return void
+     */
+    public function convertIndexDescription(TableSchema $schema, array $row): void
+    {
+        $sql = sprintf(
+            'PRAGMA index_info(%s)',
+            $this->_driver->quoteIdentifier($row['name'])
+        );
+        $statement = $this->_driver->prepare($sql);
+        $statement->execute();
+        $columns = [];
+        /** @psalm-suppress PossiblyFalseIterator */
+        foreach ($statement->fetchAll('assoc') as $column) {
+            $columns[] = $column['name'];
+        }
+        $statement->closeCursor();
+        if ($row['unique']) {
+            $schema->addConstraint($row['name'], [
+                'type' => TableSchema::CONSTRAINT_UNIQUE,
+                'columns' => $columns,
+            ]);
+        } else {
+            $schema->addIndex($row['name'], [
+                'type' => TableSchema::INDEX_INDEX,
+                'columns' => $columns,
+            ]);
+        }
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeForeignKeySql(string $tableName, array $config): array
+    {
+        $sql = sprintf('PRAGMA foreign_key_list(%s)', $this->_driver->quoteIdentifier($tableName));
+
+        return [$sql, []];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function convertForeignKeyDescription(TableSchema $schema, array $row): void
+    {
+        $name = $row['from'] . '_fk';
+
+        $update = $row['on_update'] ?? '';
+        $delete = $row['on_delete'] ?? '';
+        $data = [
+            'type' => TableSchema::CONSTRAINT_FOREIGN,
+            'columns' => [$row['from']],
+            'references' => [$row['table'], $row['to']],
+            'update' => $this->_convertOnClause($update),
+            'delete' => $this->_convertOnClause($delete),
+        ];
+
+        if (isset($this->_constraintsIdMap[$schema->name()][$row['id']])) {
+            $name = $this->_constraintsIdMap[$schema->name()][$row['id']];
+        } else {
+            $this->_constraintsIdMap[$schema->name()][$row['id']] = $name;
+        }
+
+        $schema->addConstraint($name, $data);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table instance the column is in.
+     * @param string $name The name of the column.
+     * @return string SQL fragment.
+     * @throws \Cake\Database\Exception\DatabaseException when the column type is unknown
+     */
+    public function columnSql(TableSchema $schema, string $name): string
+    {
+        /** @var array $data */
+        $data = $schema->getColumn($name);
+
+        $sql = $this->_getTypeSpecificColumnSql($data['type'], $schema, $name);
+        if ($sql !== null) {
+            return $sql;
+        }
+
+        $typeMap = [
+            TableSchema::TYPE_BINARY_UUID => ' BINARY(16)',
+            TableSchema::TYPE_UUID => ' CHAR(36)',
+            TableSchema::TYPE_CHAR => ' CHAR',
+            TableSchema::TYPE_TINYINTEGER => ' TINYINT',
+            TableSchema::TYPE_SMALLINTEGER => ' SMALLINT',
+            TableSchema::TYPE_INTEGER => ' INTEGER',
+            TableSchema::TYPE_BIGINTEGER => ' BIGINT',
+            TableSchema::TYPE_BOOLEAN => ' BOOLEAN',
+            TableSchema::TYPE_FLOAT => ' FLOAT',
+            TableSchema::TYPE_DECIMAL => ' DECIMAL',
+            TableSchema::TYPE_DATE => ' DATE',
+            TableSchema::TYPE_TIME => ' TIME',
+            TableSchema::TYPE_DATETIME => ' DATETIME',
+            TableSchema::TYPE_DATETIME_FRACTIONAL => ' DATETIMEFRACTIONAL',
+            TableSchema::TYPE_TIMESTAMP => ' TIMESTAMP',
+            TableSchema::TYPE_TIMESTAMP_FRACTIONAL => ' TIMESTAMPFRACTIONAL',
+            TableSchema::TYPE_TIMESTAMP_TIMEZONE => ' TIMESTAMPTIMEZONE',
+            TableSchema::TYPE_JSON => ' TEXT',
+        ];
+
+        $out = $this->_driver->quoteIdentifier($name);
+        $hasUnsigned = [
+            TableSchema::TYPE_TINYINTEGER,
+            TableSchema::TYPE_SMALLINTEGER,
+            TableSchema::TYPE_INTEGER,
+            TableSchema::TYPE_BIGINTEGER,
+            TableSchema::TYPE_FLOAT,
+            TableSchema::TYPE_DECIMAL,
+        ];
+
+        if (
+            in_array($data['type'], $hasUnsigned, true) &&
+            isset($data['unsigned']) &&
+            $data['unsigned'] === true
+        ) {
+            if ($data['type'] !== TableSchema::TYPE_INTEGER || $schema->getPrimaryKey() !== [$name]) {
+                $out .= ' UNSIGNED';
+            }
+        }
+
+        if (isset($typeMap[$data['type']])) {
+            $out .= $typeMap[$data['type']];
+        }
+
+        if ($data['type'] === TableSchema::TYPE_TEXT && $data['length'] !== TableSchema::LENGTH_TINY) {
+            $out .= ' TEXT';
+        }
+
+        if ($data['type'] === TableSchema::TYPE_CHAR) {
+            $out .= '(' . $data['length'] . ')';
+        }
+
+        if (
+            $data['type'] === TableSchema::TYPE_STRING ||
+            (
+                $data['type'] === TableSchema::TYPE_TEXT &&
+                $data['length'] === TableSchema::LENGTH_TINY
+            )
+        ) {
+            $out .= ' VARCHAR';
+
+            if (isset($data['length'])) {
+                $out .= '(' . $data['length'] . ')';
+            }
+        }
+
+        if ($data['type'] === TableSchema::TYPE_BINARY) {
+            if (isset($data['length'])) {
+                $out .= ' BLOB(' . $data['length'] . ')';
+            } else {
+                $out .= ' BLOB';
+            }
+        }
+
+        $integerTypes = [
+            TableSchema::TYPE_TINYINTEGER,
+            TableSchema::TYPE_SMALLINTEGER,
+            TableSchema::TYPE_INTEGER,
+        ];
+        if (
+            in_array($data['type'], $integerTypes, true) &&
+            isset($data['length']) &&
+            $schema->getPrimaryKey() !== [$name]
+        ) {
+            $out .= '(' . (int)$data['length'] . ')';
+        }
+
+        $hasPrecision = [TableSchema::TYPE_FLOAT, TableSchema::TYPE_DECIMAL];
+        if (
+            in_array($data['type'], $hasPrecision, true) &&
+            (
+                isset($data['length']) ||
+                isset($data['precision'])
+            )
+        ) {
+            $out .= '(' . (int)$data['length'] . ',' . (int)$data['precision'] . ')';
+        }
+
+        if (isset($data['null']) && $data['null'] === false) {
+            $out .= ' NOT NULL';
+        }
+
+        if ($data['type'] === TableSchema::TYPE_INTEGER && $schema->getPrimaryKey() === [$name]) {
+            $out .= ' PRIMARY KEY AUTOINCREMENT';
+        }
+
+        $timestampTypes = [
+            TableSchema::TYPE_DATETIME,
+            TableSchema::TYPE_DATETIME_FRACTIONAL,
+            TableSchema::TYPE_TIMESTAMP,
+            TableSchema::TYPE_TIMESTAMP_FRACTIONAL,
+            TableSchema::TYPE_TIMESTAMP_TIMEZONE,
+        ];
+        if (isset($data['null']) && $data['null'] === true && in_array($data['type'], $timestampTypes, true)) {
+            $out .= ' DEFAULT NULL';
+        }
+        if (isset($data['default'])) {
+            $out .= ' DEFAULT ' . $this->_driver->schemaValue($data['default']);
+        }
+
+        return $out;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Note integer primary keys will return ''. This is intentional as Sqlite requires
+     * that integer primary keys be defined in the column definition.
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table instance the column is in.
+     * @param string $name The name of the column.
+     * @return string SQL fragment.
+     */
+    public function constraintSql(TableSchema $schema, string $name): string
+    {
+        /** @var array $data */
+        $data = $schema->getConstraint($name);
+        /** @psalm-suppress PossiblyNullArrayAccess */
+        if (
+            $data['type'] === TableSchema::CONSTRAINT_PRIMARY &&
+            count($data['columns']) === 1 &&
+            $schema->getColumn($data['columns'][0])['type'] === TableSchema::TYPE_INTEGER
+        ) {
+            return '';
+        }
+        $clause = '';
+        $type = '';
+        if ($data['type'] === TableSchema::CONSTRAINT_PRIMARY) {
+            $type = 'PRIMARY KEY';
+        }
+        if ($data['type'] === TableSchema::CONSTRAINT_UNIQUE) {
+            $type = 'UNIQUE';
+        }
+        if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) {
+            $type = 'FOREIGN KEY';
+
+            $clause = sprintf(
+                ' REFERENCES %s (%s) ON UPDATE %s ON DELETE %s',
+                $this->_driver->quoteIdentifier($data['references'][0]),
+                $this->_convertConstraintColumns($data['references'][1]),
+                $this->_foreignOnClause($data['update']),
+                $this->_foreignOnClause($data['delete'])
+            );
+        }
+        $columns = array_map(
+            [$this->_driver, 'quoteIdentifier'],
+            $data['columns']
+        );
+
+        return sprintf(
+            'CONSTRAINT %s %s (%s)%s',
+            $this->_driver->quoteIdentifier($name),
+            $type,
+            implode(', ', $columns),
+            $clause
+        );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * SQLite can not properly handle adding a constraint to an existing table.
+     * This method is no-op
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table instance the foreign key constraints are.
+     * @return array SQL fragment.
+     */
+    public function addConstraintSql(TableSchema $schema): array
+    {
+        return [];
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * SQLite can not properly handle dropping a constraint to an existing table.
+     * This method is no-op
+     *
+     * @param \Cake\Database\Schema\TableSchema $schema The table instance the foreign key constraints are.
+     * @return array SQL fragment.
+     */
+    public function dropConstraintSql(TableSchema $schema): array
+    {
+        return [];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function indexSql(TableSchema $schema, string $name): string
+    {
+        /** @var array $data */
+        $data = $schema->getIndex($name);
+        $columns = array_map(
+            [$this->_driver, 'quoteIdentifier'],
+            $data['columns']
+        );
+
+        return sprintf(
+            'CREATE INDEX %s ON %s (%s)',
+            $this->_driver->quoteIdentifier($name),
+            $this->_driver->quoteIdentifier($schema->name()),
+            implode(', ', $columns)
+        );
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function createTableSql(TableSchema $schema, array $columns, array $constraints, array $indexes): array
+    {
+        $lines = array_merge($columns, $constraints);
+        $content = implode(",\n", array_filter($lines));
+        $temporary = $schema->isTemporary() ? ' TEMPORARY ' : ' ';
+        $table = sprintf("CREATE%sTABLE \"%s\" (\n%s\n)", $temporary, $schema->name(), $content);
+        $out = [$table];
+        foreach ($indexes as $index) {
+            $out[] = $index;
+        }
+
+        return $out;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function truncateTableSql(TableSchema $schema): array
+    {
+        $name = $schema->name();
+        $sql = [];
+        if ($this->hasSequences()) {
+            $sql[] = sprintf('DELETE FROM sqlite_sequence WHERE name="%s"', $name);
+        }
+
+        $sql[] = sprintf('DELETE FROM "%s"', $name);
+
+        return $sql;
+    }
+
+    /**
+     * Returns whether there is any table in this connection to SQLite containing
+     * sequences
+     *
+     * @return bool
+     */
+    public function hasSequences(): bool
+    {
+        $result = $this->_driver->prepare(
+            'SELECT 1 FROM sqlite_master WHERE name = "sqlite_sequence"'
+        );
+        $result->execute();
+        $this->_hasSequences = (bool)$result->rowCount();
+        $result->closeCursor();
+
+        return $this->_hasSequences;
+    }
+}
+
+// phpcs:disable
+class_alias(
+    'Cake\Database\Schema\SqliteSchemaDialect',
+    'Cake\Database\Schema\SqliteSchema'
+);
+// phpcs:enable
diff --git a/vendor/cakephp/database/Schema/SqlserverSchema.php b/vendor/cakephp/database/Schema/SqlserverSchema.php
new file mode 100644
index 0000000..62e3f69
--- /dev/null
+++ b/vendor/cakephp/database/Schema/SqlserverSchema.php
@@ -0,0 +1,10 @@
+ $config The connection configuration to use for
+     *    getting tables from.
+     * @return array An array of (sql, params) to execute.
+     */
+    public function listTablesSql(array $config): array
+    {
+        $sql = "SELECT TABLE_NAME
+            FROM INFORMATION_SCHEMA.TABLES
+            WHERE TABLE_SCHEMA = ?
+            AND (TABLE_TYPE = 'BASE TABLE' OR TABLE_TYPE = 'VIEW')
+            ORDER BY TABLE_NAME";
+        $schema = empty($config['schema']) ? static::DEFAULT_SCHEMA_NAME : $config['schema'];
+
+        return [$sql, [$schema]];
+    }
+
+    /**
+     * Generate the SQL to list the tables, excluding all views.
+     *
+     * @param array $config The connection configuration to use for
+     *    getting tables from.
+     * @return array An array of (sql, params) to execute.
+     */
+    public function listTablesWithoutViewsSql(array $config): array
+    {
+        $sql = "SELECT TABLE_NAME
+            FROM INFORMATION_SCHEMA.TABLES
+            WHERE TABLE_SCHEMA = ?
+            AND (TABLE_TYPE = 'BASE TABLE')
+            ORDER BY TABLE_NAME";
+        $schema = empty($config['schema']) ? static::DEFAULT_SCHEMA_NAME : $config['schema'];
+
+        return [$sql, [$schema]];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeColumnSql(string $tableName, array $config): array
+    {
+        $sql = 'SELECT DISTINCT
+            AC.column_id AS [column_id],
+            AC.name AS [name],
+            TY.name AS [type],
+            AC.max_length AS [char_length],
+            AC.precision AS [precision],
+            AC.scale AS [scale],
+            AC.is_identity AS [autoincrement],
+            AC.is_nullable AS [null],
+            OBJECT_DEFINITION(AC.default_object_id) AS [default],
+            AC.collation_name AS [collation_name]
+            FROM sys.[objects] T
+            INNER JOIN sys.[schemas] S ON S.[schema_id] = T.[schema_id]
+            INNER JOIN sys.[all_columns] AC ON T.[object_id] = AC.[object_id]
+            INNER JOIN sys.[types] TY ON TY.[user_type_id] = AC.[user_type_id]
+            WHERE T.[name] = ? AND S.[name] = ?
+            ORDER BY column_id';
+
+        $schema = empty($config['schema']) ? static::DEFAULT_SCHEMA_NAME : $config['schema'];
+
+        return [$sql, [$tableName, $schema]];
+    }
+
+    /**
+     * Convert a column definition to the abstract types.
+     *
+     * The returned type will be a type that
+     * Cake\Database\TypeFactory  can handle.
+     *
+     * @param string $col The column type
+     * @param int|null $length the column length
+     * @param int|null $precision The column precision
+     * @param int|null $scale The column scale
+     * @return array Array of column information.
+     * @link https://technet.microsoft.com/en-us/library/ms187752.aspx
+     */
+    protected function _convertColumn(
+        string $col,
+        ?int $length = null,
+        ?int $precision = null,
+        ?int $scale = null
+    ): array {
+        $col = strtolower($col);
+
+        $type = $this->_applyTypeSpecificColumnConversion(
+            $col,
+            compact('length', 'precision', 'scale')
+        );
+        if ($type !== null) {
+            return $type;
+        }
+
+        if (in_array($col, ['date', 'time'])) {
+            return ['type' => $col, 'length' => null];
+        }
+
+        if ($col === 'datetime') {
+            // datetime cannot parse more than 3 digits of precision and isn't accurate
+            return ['type' => TableSchema::TYPE_DATETIME, 'length' => null];
+        }
+        if (strpos($col, 'datetime') !== false) {
+            $typeName = TableSchema::TYPE_DATETIME;
+            if ($scale > 0) {
+                $typeName = TableSchema::TYPE_DATETIME_FRACTIONAL;
+            }
+
+            return ['type' => $typeName, 'length' => null, 'precision' => $scale];
+        }
+
+        if ($col === 'char') {
+            return ['type' => TableSchema::TYPE_CHAR, 'length' => $length];
+        }
+
+        if ($col === 'tinyint') {
+            return ['type' => TableSchema::TYPE_TINYINTEGER, 'length' => $precision ?: 3];
+        }
+        if ($col === 'smallint') {
+            return ['type' => TableSchema::TYPE_SMALLINTEGER, 'length' => $precision ?: 5];
+        }
+        if ($col === 'int' || $col === 'integer') {
+            return ['type' => TableSchema::TYPE_INTEGER, 'length' => $precision ?: 10];
+        }
+        if ($col === 'bigint') {
+            return ['type' => TableSchema::TYPE_BIGINTEGER, 'length' => $precision ?: 20];
+        }
+        if ($col === 'bit') {
+            return ['type' => TableSchema::TYPE_BOOLEAN, 'length' => null];
+        }
+        if (
+            strpos($col, 'numeric') !== false ||
+            strpos($col, 'money') !== false ||
+            strpos($col, 'decimal') !== false
+        ) {
+            return ['type' => TableSchema::TYPE_DECIMAL, 'length' => $precision, 'precision' => $scale];
+        }
+
+        if ($col === 'real' || $col === 'float') {
+            return ['type' => TableSchema::TYPE_FLOAT, 'length' => null];
+        }
+        // SqlServer schema reflection returns double length for unicode
+        // columns because internally it uses UTF16/UCS2
+        if ($col === 'nvarchar' || $col === 'nchar' || $col === 'ntext') {
+            $length /= 2;
+        }
+        if (strpos($col, 'varchar') !== false && $length < 0) {
+            return ['type' => TableSchema::TYPE_TEXT, 'length' => null];
+        }
+
+        if (strpos($col, 'varchar') !== false) {
+            return ['type' => TableSchema::TYPE_STRING, 'length' => $length ?: 255];
+        }
+
+        if (strpos($col, 'char') !== false) {
+            return ['type' => TableSchema::TYPE_CHAR, 'length' => $length];
+        }
+
+        if (strpos($col, 'text') !== false) {
+            return ['type' => TableSchema::TYPE_TEXT, 'length' => null];
+        }
+
+        if ($col === 'image' || strpos($col, 'binary') !== false) {
+            // -1 is the value for MAX which we treat as a 'long' binary
+            if ($length == -1) {
+                $length = TableSchema::LENGTH_LONG;
+            }
+
+            return ['type' => TableSchema::TYPE_BINARY, 'length' => $length];
+        }
+
+        if ($col === 'uniqueidentifier') {
+            return ['type' => TableSchema::TYPE_UUID];
+        }
+
+        return ['type' => TableSchema::TYPE_STRING, 'length' => null];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function convertColumnDescription(TableSchema $schema, array $row): void
+    {
+        $field = $this->_convertColumn(
+            $row['type'],
+            $row['char_length'] !== null ? (int)$row['char_length'] : null,
+            $row['precision'] !== null ? (int)$row['precision'] : null,
+            $row['scale'] !== null ? (int)$row['scale'] : null
+        );
+
+        if (!empty($row['autoincrement'])) {
+            $field['autoIncrement'] = true;
+        }
+
+        $field += [
+            'null' => $row['null'] === '1',
+            'default' => $this->_defaultValue($field['type'], $row['default']),
+            'collate' => $row['collation_name'],
+        ];
+        $schema->addColumn($row['name'], $field);
+    }
+
+    /**
+     * Manipulate the default value.
+     *
+     * Removes () wrapping default values, extracts strings from
+     * N'' wrappers and collation text and converts NULL strings.
+     *
+     * @param string $type The schema type
+     * @param string|null $default The default value.
+     * @return string|int|null
+     */
+    protected function _defaultValue($type, $default)
+    {
+        if ($default === null) {
+            return null;
+        }
+
+        // remove () surrounding value (NULL) but leave () at the end of functions
+        // integers might have two ((0)) wrapping value
+        if (preg_match('/^\(+(.*?(\(\))?)\)+$/', $default, $matches)) {
+            $default = $matches[1];
+        }
+
+        if ($default === 'NULL') {
+            return null;
+        }
+
+        if ($type === TableSchema::TYPE_BOOLEAN) {
+            return (int)$default;
+        }
+
+        // Remove quotes
+        if (preg_match("/^\(?N?'(.*)'\)?/", $default, $matches)) {
+            return str_replace("''", "'", $matches[1]);
+        }
+
+        return $default;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeIndexSql(string $tableName, array $config): array
+    {
+        $sql = "SELECT
+                I.[name] AS [index_name],
+                IC.[index_column_id] AS [index_order],
+                AC.[name] AS [column_name],
+                I.[is_unique], I.[is_primary_key],
+                I.[is_unique_constraint]
+            FROM sys.[tables] AS T
+            INNER JOIN sys.[schemas] S ON S.[schema_id] = T.[schema_id]
+            INNER JOIN sys.[indexes] I ON T.[object_id] = I.[object_id]
+            INNER JOIN sys.[index_columns] IC ON I.[object_id] = IC.[object_id] AND I.[index_id] = IC.[index_id]
+            INNER JOIN sys.[all_columns] AC ON T.[object_id] = AC.[object_id] AND IC.[column_id] = AC.[column_id]
+            WHERE T.[is_ms_shipped] = 0 AND I.[type_desc] <> 'HEAP' AND T.[name] = ? AND S.[name] = ?
+            ORDER BY I.[index_id], IC.[index_column_id]";
+
+        $schema = empty($config['schema']) ? static::DEFAULT_SCHEMA_NAME : $config['schema'];
+
+        return [$sql, [$tableName, $schema]];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function convertIndexDescription(TableSchema $schema, array $row): void
+    {
+        $type = TableSchema::INDEX_INDEX;
+        $name = $row['index_name'];
+        if ($row['is_primary_key']) {
+            $name = $type = TableSchema::CONSTRAINT_PRIMARY;
+        }
+        if ($row['is_unique_constraint'] && $type === TableSchema::INDEX_INDEX) {
+            $type = TableSchema::CONSTRAINT_UNIQUE;
+        }
+
+        if ($type === TableSchema::INDEX_INDEX) {
+            $existing = $schema->getIndex($name);
+        } else {
+            $existing = $schema->getConstraint($name);
+        }
+
+        $columns = [$row['column_name']];
+        if (!empty($existing)) {
+            $columns = array_merge($existing['columns'], $columns);
+        }
+
+        if ($type === TableSchema::CONSTRAINT_PRIMARY || $type === TableSchema::CONSTRAINT_UNIQUE) {
+            $schema->addConstraint($name, [
+                'type' => $type,
+                'columns' => $columns,
+            ]);
+
+            return;
+        }
+        $schema->addIndex($name, [
+            'type' => $type,
+            'columns' => $columns,
+        ]);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function describeForeignKeySql(string $tableName, array $config): array
+    {
+        // phpcs:disable Generic.Files.LineLength
+        $sql = 'SELECT FK.[name] AS [foreign_key_name], FK.[delete_referential_action_desc] AS [delete_type],
+                FK.[update_referential_action_desc] AS [update_type], C.name AS [column], RT.name AS [reference_table],
+                RC.name AS [reference_column]
+            FROM sys.foreign_keys FK
+            INNER JOIN sys.foreign_key_columns FKC ON FKC.constraint_object_id = FK.object_id
+            INNER JOIN sys.tables T ON T.object_id = FKC.parent_object_id
+            INNER JOIN sys.tables RT ON RT.object_id = FKC.referenced_object_id
+            INNER JOIN sys.schemas S ON S.schema_id = T.schema_id AND S.schema_id = RT.schema_id
+            INNER JOIN sys.columns C ON C.column_id = FKC.parent_column_id AND C.object_id = FKC.parent_object_id
+            INNER JOIN sys.columns RC ON RC.column_id = FKC.referenced_column_id AND RC.object_id = FKC.referenced_object_id
+            WHERE FK.is_ms_shipped = 0 AND T.name = ? AND S.name = ?
+            ORDER BY FKC.constraint_column_id';
+        // phpcs:enable Generic.Files.LineLength
+
+        $schema = empty($config['schema']) ? static::DEFAULT_SCHEMA_NAME : $config['schema'];
+
+        return [$sql, [$tableName, $schema]];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function convertForeignKeyDescription(TableSchema $schema, array $row): void
+    {
+        $data = [
+            'type' => TableSchema::CONSTRAINT_FOREIGN,
+            'columns' => [$row['column']],
+            'references' => [$row['reference_table'], $row['reference_column']],
+            'update' => $this->_convertOnClause($row['update_type']),
+            'delete' => $this->_convertOnClause($row['delete_type']),
+        ];
+        $name = $row['foreign_key_name'];
+        $schema->addConstraint($name, $data);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function _foreignOnClause(string $on): string
+    {
+        $parent = parent::_foreignOnClause($on);
+
+        return $parent === 'RESTRICT' ? parent::_foreignOnClause(TableSchema::ACTION_NO_ACTION) : $parent;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function _convertOnClause(string $clause): string
+    {
+        switch ($clause) {
+            case 'NO_ACTION':
+                return TableSchema::ACTION_NO_ACTION;
+            case 'CASCADE':
+                return TableSchema::ACTION_CASCADE;
+            case 'SET_NULL':
+                return TableSchema::ACTION_SET_NULL;
+            case 'SET_DEFAULT':
+                return TableSchema::ACTION_SET_DEFAULT;
+        }
+
+        return TableSchema::ACTION_SET_NULL;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function columnSql(TableSchema $schema, string $name): string
+    {
+        /** @var array $data */
+        $data = $schema->getColumn($name);
+
+        $sql = $this->_getTypeSpecificColumnSql($data['type'], $schema, $name);
+        if ($sql !== null) {
+            return $sql;
+        }
+
+        $out = $this->_driver->quoteIdentifier($name);
+        $typeMap = [
+            TableSchema::TYPE_TINYINTEGER => ' TINYINT',
+            TableSchema::TYPE_SMALLINTEGER => ' SMALLINT',
+            TableSchema::TYPE_INTEGER => ' INTEGER',
+            TableSchema::TYPE_BIGINTEGER => ' BIGINT',
+            TableSchema::TYPE_BINARY_UUID => ' UNIQUEIDENTIFIER',
+            TableSchema::TYPE_BOOLEAN => ' BIT',
+            TableSchema::TYPE_CHAR => ' NCHAR',
+            TableSchema::TYPE_FLOAT => ' FLOAT',
+            TableSchema::TYPE_DECIMAL => ' DECIMAL',
+            TableSchema::TYPE_DATE => ' DATE',
+            TableSchema::TYPE_TIME => ' TIME',
+            TableSchema::TYPE_DATETIME => ' DATETIME2',
+            TableSchema::TYPE_DATETIME_FRACTIONAL => ' DATETIME2',
+            TableSchema::TYPE_TIMESTAMP => ' DATETIME2',
+            TableSchema::TYPE_TIMESTAMP_FRACTIONAL => ' DATETIME2',
+            TableSchema::TYPE_TIMESTAMP_TIMEZONE => ' DATETIME2',
+            TableSchema::TYPE_UUID => ' UNIQUEIDENTIFIER',
+            TableSchema::TYPE_JSON => ' NVARCHAR(MAX)',
+        ];
+
+        if (isset($typeMap[$data['type']])) {
+            $out .= $typeMap[$data['type']];
+        }
+
+        if ($data['type'] === TableSchema::TYPE_INTEGER || $data['type'] === TableSchema::TYPE_BIGINTEGER) {
+            if ($schema->getPrimaryKey() === [$name] || $data['autoIncrement'] === true) {
+                unset($data['null'], $data['default']);
+                $out .= ' IDENTITY(1, 1)';
+            }
+        }
+
+        if ($data['type'] === TableSchema::TYPE_TEXT && $data['length'] !== TableSchema::LENGTH_TINY) {
+            $out .= ' NVARCHAR(MAX)';
+        }
+
+        if ($data['type'] === TableSchema::TYPE_CHAR) {
+            $out .= '(' . $data['length'] . ')';
+        }
+
+        if ($data['type'] === TableSchema::TYPE_BINARY) {
+            if (
+                !isset($data['length'])
+                || in_array($data['length'], [TableSchema::LENGTH_MEDIUM, TableSchema::LENGTH_LONG], true)
+            ) {
+                $data['length'] = 'MAX';
+            }
+
+            if ($data['length'] === 1) {
+                $out .= ' BINARY(1)';
+            } else {
+                $out .= ' VARBINARY';
+
+                $out .= sprintf('(%s)', $data['length']);
+            }
+        }
+
+        if (
+            $data['type'] === TableSchema::TYPE_STRING ||
+            (
+                $data['type'] === TableSchema::TYPE_TEXT &&
+                $data['length'] === TableSchema::LENGTH_TINY
+            )
+        ) {
+            $type = ' NVARCHAR';
+            $length = $data['length'] ?? TableSchema::LENGTH_TINY;
+            $out .= sprintf('%s(%d)', $type, $length);
+        }
+
+        $hasCollate = [TableSchema::TYPE_TEXT, TableSchema::TYPE_STRING, TableSchema::TYPE_CHAR];
+        if (in_array($data['type'], $hasCollate, true) && isset($data['collate']) && $data['collate'] !== '') {
+            $out .= ' COLLATE ' . $data['collate'];
+        }
+
+        $precisionTypes = [
+            TableSchema::TYPE_FLOAT,
+            TableSchema::TYPE_DATETIME,
+            TableSchema::TYPE_DATETIME_FRACTIONAL,
+            TableSchema::TYPE_TIMESTAMP,
+            TableSchema::TYPE_TIMESTAMP_FRACTIONAL,
+        ];
+        if (in_array($data['type'], $precisionTypes, true) && isset($data['precision'])) {
+            $out .= '(' . (int)$data['precision'] . ')';
+        }
+
+        if (
+            $data['type'] === TableSchema::TYPE_DECIMAL &&
+            (
+                isset($data['length']) ||
+                isset($data['precision'])
+            )
+        ) {
+            $out .= '(' . (int)$data['length'] . ',' . (int)$data['precision'] . ')';
+        }
+
+        if (isset($data['null']) && $data['null'] === false) {
+            $out .= ' NOT NULL';
+        }
+
+        $dateTimeTypes = [
+            TableSchema::TYPE_DATETIME,
+            TableSchema::TYPE_DATETIME_FRACTIONAL,
+            TableSchema::TYPE_TIMESTAMP,
+            TableSchema::TYPE_TIMESTAMP_FRACTIONAL,
+        ];
+        $dateTimeDefaults = [
+            'current_timestamp',
+            'getdate()',
+            'getutcdate()',
+            'sysdatetime()',
+            'sysutcdatetime()',
+            'sysdatetimeoffset()',
+        ];
+        if (
+            isset($data['default']) &&
+            in_array($data['type'], $dateTimeTypes, true) &&
+            in_array(strtolower($data['default']), $dateTimeDefaults, true)
+        ) {
+            $out .= ' DEFAULT ' . strtoupper($data['default']);
+        } elseif (isset($data['default'])) {
+            $default = is_bool($data['default'])
+                ? (int)$data['default']
+                : $this->_driver->schemaValue($data['default']);
+            $out .= ' DEFAULT ' . $default;
+        } elseif (isset($data['null']) && $data['null'] !== false) {
+            $out .= ' DEFAULT NULL';
+        }
+
+        return $out;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function addConstraintSql(TableSchema $schema): array
+    {
+        $sqlPattern = 'ALTER TABLE %s ADD %s;';
+        $sql = [];
+
+        foreach ($schema->constraints() as $name) {
+            /** @var array $constraint */
+            $constraint = $schema->getConstraint($name);
+            if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) {
+                $tableName = $this->_driver->quoteIdentifier($schema->name());
+                $sql[] = sprintf($sqlPattern, $tableName, $this->constraintSql($schema, $name));
+            }
+        }
+
+        return $sql;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function dropConstraintSql(TableSchema $schema): array
+    {
+        $sqlPattern = 'ALTER TABLE %s DROP CONSTRAINT %s;';
+        $sql = [];
+
+        foreach ($schema->constraints() as $name) {
+            /** @var array $constraint */
+            $constraint = $schema->getConstraint($name);
+            if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) {
+                $tableName = $this->_driver->quoteIdentifier($schema->name());
+                $constraintName = $this->_driver->quoteIdentifier($name);
+                $sql[] = sprintf($sqlPattern, $tableName, $constraintName);
+            }
+        }
+
+        return $sql;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function indexSql(TableSchema $schema, string $name): string
+    {
+        /** @var array $data */
+        $data = $schema->getIndex($name);
+        $columns = array_map(
+            [$this->_driver, 'quoteIdentifier'],
+            $data['columns']
+        );
+
+        return sprintf(
+            'CREATE INDEX %s ON %s (%s)',
+            $this->_driver->quoteIdentifier($name),
+            $this->_driver->quoteIdentifier($schema->name()),
+            implode(', ', $columns)
+        );
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function constraintSql(TableSchema $schema, string $name): string
+    {
+        /** @var array $data */
+        $data = $schema->getConstraint($name);
+        $out = 'CONSTRAINT ' . $this->_driver->quoteIdentifier($name);
+        if ($data['type'] === TableSchema::CONSTRAINT_PRIMARY) {
+            $out = 'PRIMARY KEY';
+        }
+        if ($data['type'] === TableSchema::CONSTRAINT_UNIQUE) {
+            $out .= ' UNIQUE';
+        }
+
+        return $this->_keySql($out, $data);
+    }
+
+    /**
+     * Helper method for generating key SQL snippets.
+     *
+     * @param string $prefix The key prefix
+     * @param array $data Key data.
+     * @return string
+     */
+    protected function _keySql(string $prefix, array $data): string
+    {
+        $columns = array_map(
+            [$this->_driver, 'quoteIdentifier'],
+            $data['columns']
+        );
+        if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) {
+            return $prefix . sprintf(
+                ' FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE %s ON DELETE %s',
+                implode(', ', $columns),
+                $this->_driver->quoteIdentifier($data['references'][0]),
+                $this->_convertConstraintColumns($data['references'][1]),
+                $this->_foreignOnClause($data['update']),
+                $this->_foreignOnClause($data['delete'])
+            );
+        }
+
+        return $prefix . ' (' . implode(', ', $columns) . ')';
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function createTableSql(TableSchema $schema, array $columns, array $constraints, array $indexes): array
+    {
+        $content = array_merge($columns, $constraints);
+        $content = implode(",\n", array_filter($content));
+        $tableName = $this->_driver->quoteIdentifier($schema->name());
+        $out = [];
+        $out[] = sprintf("CREATE TABLE %s (\n%s\n)", $tableName, $content);
+        foreach ($indexes as $index) {
+            $out[] = $index;
+        }
+
+        return $out;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function truncateTableSql(TableSchema $schema): array
+    {
+        $name = $this->_driver->quoteIdentifier($schema->name());
+        $queries = [
+            sprintf('DELETE FROM %s', $name),
+        ];
+
+        // Restart identity sequences
+        $pk = $schema->getPrimaryKey();
+        if (count($pk) === 1) {
+            /** @var array $column */
+            $column = $schema->getColumn($pk[0]);
+            if (in_array($column['type'], ['integer', 'biginteger'])) {
+                $queries[] = sprintf(
+                    "IF EXISTS (SELECT * FROM sys.identity_columns WHERE OBJECT_NAME(OBJECT_ID) = '%s' AND " .
+                    "last_value IS NOT NULL) DBCC CHECKIDENT('%s', RESEED, 0)",
+                    $schema->name(),
+                    $schema->name()
+                );
+            }
+        }
+
+        return $queries;
+    }
+}
+
+// phpcs:disable
+class_alias(
+    'Cake\Database\Schema\SqlserverSchemaDialect', 
+    'Cake\Database\Schema\SqlserverSchema'
+);
+// phpcs:enable
diff --git a/vendor/cakephp/database/Schema/TableSchema.php b/vendor/cakephp/database/Schema/TableSchema.php
new file mode 100644
index 0000000..a58779a
--- /dev/null
+++ b/vendor/cakephp/database/Schema/TableSchema.php
@@ -0,0 +1,793 @@
+
+     */
+    protected $_columns = [];
+
+    /**
+     * A map with columns to types
+     *
+     * @var array
+     */
+    protected $_typeMap = [];
+
+    /**
+     * Indexes in the table.
+     *
+     * @var array
+     */
+    protected $_indexes = [];
+
+    /**
+     * Constraints in the table.
+     *
+     * @var array>
+     */
+    protected $_constraints = [];
+
+    /**
+     * Options for the table.
+     *
+     * @var array
+     */
+    protected $_options = [];
+
+    /**
+     * Whether the table is temporary
+     *
+     * @var bool
+     */
+    protected $_temporary = false;
+
+    /**
+     * Column length when using a `tiny` column type
+     *
+     * @var int
+     */
+    public const LENGTH_TINY = 255;
+
+    /**
+     * Column length when using a `medium` column type
+     *
+     * @var int
+     */
+    public const LENGTH_MEDIUM = 16777215;
+
+    /**
+     * Column length when using a `long` column type
+     *
+     * @var int
+     */
+    public const LENGTH_LONG = 4294967295;
+
+    /**
+     * Valid column length that can be used with text type columns
+     *
+     * @var array
+     */
+    public static $columnLengths = [
+        'tiny' => self::LENGTH_TINY,
+        'medium' => self::LENGTH_MEDIUM,
+        'long' => self::LENGTH_LONG,
+    ];
+
+    /**
+     * The valid keys that can be used in a column
+     * definition.
+     *
+     * @var array
+     */
+    protected static $_columnKeys = [
+        'type' => null,
+        'baseType' => null,
+        'length' => null,
+        'precision' => null,
+        'null' => null,
+        'default' => null,
+        'comment' => null,
+    ];
+
+    /**
+     * Additional type specific properties.
+     *
+     * @var array>
+     */
+    protected static $_columnExtras = [
+        'string' => [
+            'collate' => null,
+        ],
+        'char' => [
+            'collate' => null,
+        ],
+        'text' => [
+            'collate' => null,
+        ],
+        'tinyinteger' => [
+            'unsigned' => null,
+        ],
+        'smallinteger' => [
+            'unsigned' => null,
+        ],
+        'integer' => [
+            'unsigned' => null,
+            'autoIncrement' => null,
+        ],
+        'biginteger' => [
+            'unsigned' => null,
+            'autoIncrement' => null,
+        ],
+        'decimal' => [
+            'unsigned' => null,
+        ],
+        'float' => [
+            'unsigned' => null,
+        ],
+    ];
+
+    /**
+     * The valid keys that can be used in an index
+     * definition.
+     *
+     * @var array
+     */
+    protected static $_indexKeys = [
+        'type' => null,
+        'columns' => [],
+        'length' => [],
+        'references' => [],
+        'update' => 'restrict',
+        'delete' => 'restrict',
+    ];
+
+    /**
+     * Names of the valid index types.
+     *
+     * @var array
+     */
+    protected static $_validIndexTypes = [
+        self::INDEX_INDEX,
+        self::INDEX_FULLTEXT,
+    ];
+
+    /**
+     * Names of the valid constraint types.
+     *
+     * @var array
+     */
+    protected static $_validConstraintTypes = [
+        self::CONSTRAINT_PRIMARY,
+        self::CONSTRAINT_UNIQUE,
+        self::CONSTRAINT_FOREIGN,
+    ];
+
+    /**
+     * Names of the valid foreign key actions.
+     *
+     * @var array
+     */
+    protected static $_validForeignKeyActions = [
+        self::ACTION_CASCADE,
+        self::ACTION_SET_NULL,
+        self::ACTION_SET_DEFAULT,
+        self::ACTION_NO_ACTION,
+        self::ACTION_RESTRICT,
+    ];
+
+    /**
+     * Primary constraint type
+     *
+     * @var string
+     */
+    public const CONSTRAINT_PRIMARY = 'primary';
+
+    /**
+     * Unique constraint type
+     *
+     * @var string
+     */
+    public const CONSTRAINT_UNIQUE = 'unique';
+
+    /**
+     * Foreign constraint type
+     *
+     * @var string
+     */
+    public const CONSTRAINT_FOREIGN = 'foreign';
+
+    /**
+     * Index - index type
+     *
+     * @var string
+     */
+    public const INDEX_INDEX = 'index';
+
+    /**
+     * Fulltext index type
+     *
+     * @var string
+     */
+    public const INDEX_FULLTEXT = 'fulltext';
+
+    /**
+     * Foreign key cascade action
+     *
+     * @var string
+     */
+    public const ACTION_CASCADE = 'cascade';
+
+    /**
+     * Foreign key set null action
+     *
+     * @var string
+     */
+    public const ACTION_SET_NULL = 'setNull';
+
+    /**
+     * Foreign key no action
+     *
+     * @var string
+     */
+    public const ACTION_NO_ACTION = 'noAction';
+
+    /**
+     * Foreign key restrict action
+     *
+     * @var string
+     */
+    public const ACTION_RESTRICT = 'restrict';
+
+    /**
+     * Foreign key restrict default
+     *
+     * @var string
+     */
+    public const ACTION_SET_DEFAULT = 'setDefault';
+
+    /**
+     * Constructor.
+     *
+     * @param string $table The table name.
+     * @param array $columns The list of columns for the schema.
+     */
+    public function __construct(string $table, array $columns = [])
+    {
+        $this->_table = $table;
+        foreach ($columns as $field => $definition) {
+            $this->addColumn($field, $definition);
+        }
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function name(): string
+    {
+        return $this->_table;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function addColumn(string $name, $attrs)
+    {
+        if (is_string($attrs)) {
+            $attrs = ['type' => $attrs];
+        }
+        $valid = static::$_columnKeys;
+        if (isset(static::$_columnExtras[$attrs['type']])) {
+            $valid += static::$_columnExtras[$attrs['type']];
+        }
+        $attrs = array_intersect_key($attrs, $valid);
+        $this->_columns[$name] = $attrs + $valid;
+        $this->_typeMap[$name] = $this->_columns[$name]['type'];
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function removeColumn(string $name)
+    {
+        unset($this->_columns[$name], $this->_typeMap[$name]);
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function columns(): array
+    {
+        return array_keys($this->_columns);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getColumn(string $name): ?array
+    {
+        if (!isset($this->_columns[$name])) {
+            return null;
+        }
+        $column = $this->_columns[$name];
+        unset($column['baseType']);
+
+        return $column;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getColumnType(string $name): ?string
+    {
+        if (!isset($this->_columns[$name])) {
+            return null;
+        }
+
+        return $this->_columns[$name]['type'];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function setColumnType(string $name, string $type)
+    {
+        if (!isset($this->_columns[$name])) {
+            return $this;
+        }
+
+        $this->_columns[$name]['type'] = $type;
+        $this->_typeMap[$name] = $type;
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function hasColumn(string $name): bool
+    {
+        return isset($this->_columns[$name]);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function baseColumnType(string $column): ?string
+    {
+        if (isset($this->_columns[$column]['baseType'])) {
+            return $this->_columns[$column]['baseType'];
+        }
+
+        $type = $this->getColumnType($column);
+
+        if ($type === null) {
+            return null;
+        }
+
+        if (TypeFactory::getMap($type)) {
+            $type = TypeFactory::build($type)->getBaseType();
+        }
+
+        return $this->_columns[$column]['baseType'] = $type;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function typeMap(): array
+    {
+        return $this->_typeMap;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function isNullable(string $name): bool
+    {
+        if (!isset($this->_columns[$name])) {
+            return true;
+        }
+
+        return $this->_columns[$name]['null'] === true;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function defaultValues(): array
+    {
+        $defaults = [];
+        foreach ($this->_columns as $name => $data) {
+            if (!array_key_exists('default', $data)) {
+                continue;
+            }
+            if ($data['default'] === null && $data['null'] !== true) {
+                continue;
+            }
+            $defaults[$name] = $data['default'];
+        }
+
+        return $defaults;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function addIndex(string $name, $attrs)
+    {
+        if (is_string($attrs)) {
+            $attrs = ['type' => $attrs];
+        }
+        $attrs = array_intersect_key($attrs, static::$_indexKeys);
+        $attrs += static::$_indexKeys;
+        unset($attrs['references'], $attrs['update'], $attrs['delete']);
+
+        if (!in_array($attrs['type'], static::$_validIndexTypes, true)) {
+            throw new DatabaseException(sprintf(
+                'Invalid index type "%s" in index "%s" in table "%s".',
+                $attrs['type'],
+                $name,
+                $this->_table
+            ));
+        }
+        $attrs['columns'] = (array)$attrs['columns'];
+        foreach ($attrs['columns'] as $field) {
+            if (empty($this->_columns[$field])) {
+                $msg = sprintf(
+                    'Columns used in index "%s" in table "%s" must be added to the Table schema first. ' .
+                    'The column "%s" was not found.',
+                    $name,
+                    $this->_table,
+                    $field
+                );
+                throw new DatabaseException($msg);
+            }
+        }
+        $this->_indexes[$name] = $attrs;
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function indexes(): array
+    {
+        return array_keys($this->_indexes);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getIndex(string $name): ?array
+    {
+        if (!isset($this->_indexes[$name])) {
+            return null;
+        }
+
+        return $this->_indexes[$name];
+    }
+
+    /**
+     * Get the column(s) used for the primary key.
+     *
+     * @return array Column name(s) for the primary key. An
+     *   empty list will be returned when the table has no primary key.
+     * @deprecated 4.0.0 Renamed to {@link getPrimaryKey()}.
+     */
+    public function primaryKey(): array
+    {
+        deprecationWarning('`TableSchema::primaryKey()` is deprecated. Use `TableSchema::getPrimaryKey()`.');
+
+        return $this->getPrimarykey();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getPrimaryKey(): array
+    {
+        foreach ($this->_constraints as $data) {
+            if ($data['type'] === static::CONSTRAINT_PRIMARY) {
+                return $data['columns'];
+            }
+        }
+
+        return [];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function addConstraint(string $name, $attrs)
+    {
+        if (is_string($attrs)) {
+            $attrs = ['type' => $attrs];
+        }
+        $attrs = array_intersect_key($attrs, static::$_indexKeys);
+        $attrs += static::$_indexKeys;
+        if (!in_array($attrs['type'], static::$_validConstraintTypes, true)) {
+            throw new DatabaseException(sprintf(
+                'Invalid constraint type "%s" in table "%s".',
+                $attrs['type'],
+                $this->_table
+            ));
+        }
+        if (empty($attrs['columns'])) {
+            throw new DatabaseException(sprintf(
+                'Constraints in table "%s" must have at least one column.',
+                $this->_table
+            ));
+        }
+        $attrs['columns'] = (array)$attrs['columns'];
+        foreach ($attrs['columns'] as $field) {
+            if (empty($this->_columns[$field])) {
+                $msg = sprintf(
+                    'Columns used in constraints must be added to the Table schema first. ' .
+                    'The column "%s" was not found in table "%s".',
+                    $field,
+                    $this->_table
+                );
+                throw new DatabaseException($msg);
+            }
+        }
+
+        if ($attrs['type'] === static::CONSTRAINT_FOREIGN) {
+            $attrs = $this->_checkForeignKey($attrs);
+
+            if (isset($this->_constraints[$name])) {
+                $this->_constraints[$name]['columns'] = array_unique(array_merge(
+                    $this->_constraints[$name]['columns'],
+                    $attrs['columns']
+                ));
+
+                if (isset($this->_constraints[$name]['references'])) {
+                    $this->_constraints[$name]['references'][1] = array_unique(array_merge(
+                        (array)$this->_constraints[$name]['references'][1],
+                        [$attrs['references'][1]]
+                    ));
+                }
+
+                return $this;
+            }
+        } else {
+            unset($attrs['references'], $attrs['update'], $attrs['delete']);
+        }
+
+        $this->_constraints[$name] = $attrs;
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function dropConstraint(string $name)
+    {
+        if (isset($this->_constraints[$name])) {
+            unset($this->_constraints[$name]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Check whether a table has an autoIncrement column defined.
+     *
+     * @return bool
+     */
+    public function hasAutoincrement(): bool
+    {
+        foreach ($this->_columns as $column) {
+            if (isset($column['autoIncrement']) && $column['autoIncrement']) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Helper method to check/validate foreign keys.
+     *
+     * @param array $attrs Attributes to set.
+     * @return array
+     * @throws \Cake\Database\Exception\DatabaseException When foreign key definition is not valid.
+     */
+    protected function _checkForeignKey(array $attrs): array
+    {
+        if (count($attrs['references']) < 2) {
+            throw new DatabaseException('References must contain a table and column.');
+        }
+        if (!in_array($attrs['update'], static::$_validForeignKeyActions)) {
+            throw new DatabaseException(sprintf(
+                'Update action is invalid. Must be one of %s',
+                implode(',', static::$_validForeignKeyActions)
+            ));
+        }
+        if (!in_array($attrs['delete'], static::$_validForeignKeyActions)) {
+            throw new DatabaseException(sprintf(
+                'Delete action is invalid. Must be one of %s',
+                implode(',', static::$_validForeignKeyActions)
+            ));
+        }
+
+        return $attrs;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function constraints(): array
+    {
+        return array_keys($this->_constraints);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getConstraint(string $name): ?array
+    {
+        return $this->_constraints[$name] ?? null;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function setOptions(array $options)
+    {
+        $this->_options = $options + $this->_options;
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getOptions(): array
+    {
+        return $this->_options;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function setTemporary(bool $temporary)
+    {
+        $this->_temporary = $temporary;
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function isTemporary(): bool
+    {
+        return $this->_temporary;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function createSql(Connection $connection): array
+    {
+        $dialect = $connection->getDriver()->schemaDialect();
+        $columns = $constraints = $indexes = [];
+        foreach (array_keys($this->_columns) as $name) {
+            $columns[] = $dialect->columnSql($this, $name);
+        }
+        foreach (array_keys($this->_constraints) as $name) {
+            $constraints[] = $dialect->constraintSql($this, $name);
+        }
+        foreach (array_keys($this->_indexes) as $name) {
+            $indexes[] = $dialect->indexSql($this, $name);
+        }
+
+        return $dialect->createTableSql($this, $columns, $constraints, $indexes);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function dropSql(Connection $connection): array
+    {
+        $dialect = $connection->getDriver()->schemaDialect();
+
+        return $dialect->dropTableSql($this);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function truncateSql(Connection $connection): array
+    {
+        $dialect = $connection->getDriver()->schemaDialect();
+
+        return $dialect->truncateTableSql($this);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function addConstraintSql(Connection $connection): array
+    {
+        $dialect = $connection->getDriver()->schemaDialect();
+
+        return $dialect->addConstraintSql($this);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function dropConstraintSql(Connection $connection): array
+    {
+        $dialect = $connection->getDriver()->schemaDialect();
+
+        return $dialect->dropConstraintSql($this);
+    }
+
+    /**
+     * Returns an array of the table schema.
+     *
+     * @return array
+     */
+    public function __debugInfo(): array
+    {
+        return [
+            'table' => $this->_table,
+            'columns' => $this->_columns,
+            'indexes' => $this->_indexes,
+            'constraints' => $this->_constraints,
+            'options' => $this->_options,
+            'typeMap' => $this->_typeMap,
+            'temporary' => $this->_temporary,
+        ];
+    }
+}
diff --git a/vendor/cakephp/database/Schema/TableSchemaAwareInterface.php b/vendor/cakephp/database/Schema/TableSchemaAwareInterface.php
new file mode 100644
index 0000000..d4045f9
--- /dev/null
+++ b/vendor/cakephp/database/Schema/TableSchemaAwareInterface.php
@@ -0,0 +1,38 @@
+ Column name(s) for the primary key. An
+     *   empty list will be returned when the table has no primary key.
+     */
+    public function getPrimaryKey(): array;
+
+    /**
+     * Add an index.
+     *
+     * Used to add indexes, and full text indexes in platforms that support
+     * them.
+     *
+     * ### Attributes
+     *
+     * - `type` The type of index being added.
+     * - `columns` The columns in the index.
+     *
+     * @param string $name The name of the index.
+     * @param array|string $attrs The attributes for the index.
+     *   If string it will be used as `type`.
+     * @return $this
+     * @throws \Cake\Database\Exception\DatabaseException
+     */
+    public function addIndex(string $name, $attrs);
+
+    /**
+     * Read information about an index based on name.
+     *
+     * @param string $name The name of the index.
+     * @return array|null Array of index data, or null
+     */
+    public function getIndex(string $name): ?array;
+
+    /**
+     * Get the names of all the indexes in the table.
+     *
+     * @return array
+     */
+    public function indexes(): array;
+
+    /**
+     * Add a constraint.
+     *
+     * Used to add constraints to a table. For example primary keys, unique
+     * keys and foreign keys.
+     *
+     * ### Attributes
+     *
+     * - `type` The type of constraint being added.
+     * - `columns` The columns in the index.
+     * - `references` The table, column a foreign key references.
+     * - `update` The behavior on update. Options are 'restrict', 'setNull', 'cascade', 'noAction'.
+     * - `delete` The behavior on delete. Options are 'restrict', 'setNull', 'cascade', 'noAction'.
+     *
+     * The default for 'update' & 'delete' is 'cascade'.
+     *
+     * @param string $name The name of the constraint.
+     * @param array|string $attrs The attributes for the constraint.
+     *   If string it will be used as `type`.
+     * @return $this
+     * @throws \Cake\Database\Exception\DatabaseException
+     */
+    public function addConstraint(string $name, $attrs);
+
+    /**
+     * Read information about a constraint based on name.
+     *
+     * @param string $name The name of the constraint.
+     * @return array|null Array of constraint data, or null
+     */
+    public function getConstraint(string $name): ?array;
+
+    /**
+     * Remove a constraint.
+     *
+     * @param string $name Name of the constraint to remove
+     * @return $this
+     */
+    public function dropConstraint(string $name);
+
+    /**
+     * Get the names of all the constraints in the table.
+     *
+     * @return array
+     */
+    public function constraints(): array;
+}
diff --git a/vendor/cakephp/database/SchemaCache.php b/vendor/cakephp/database/SchemaCache.php
new file mode 100644
index 0000000..953ff1e
--- /dev/null
+++ b/vendor/cakephp/database/SchemaCache.php
@@ -0,0 +1,114 @@
+_schema = $this->getSchema($connection);
+    }
+
+    /**
+     * Build metadata.
+     *
+     * @param string|null $name The name of the table to build cache data for.
+     * @return array Returns a list build table caches
+     */
+    public function build(?string $name = null): array
+    {
+        if ($name) {
+            $tables = [$name];
+        } else {
+            $tables = $this->_schema->listTables();
+        }
+
+        foreach ($tables as $table) {
+            $this->_schema->describe($table, ['forceRefresh' => true]);
+        }
+
+        return $tables;
+    }
+
+    /**
+     * Clear metadata.
+     *
+     * @param string|null $name The name of the table to clear cache data for.
+     * @return array Returns a list of cleared table caches
+     */
+    public function clear(?string $name = null): array
+    {
+        if ($name) {
+            $tables = [$name];
+        } else {
+            $tables = $this->_schema->listTables();
+        }
+
+        $cacher = $this->_schema->getCacher();
+
+        foreach ($tables as $table) {
+            $key = $this->_schema->cacheKey($table);
+            $cacher->delete($key);
+        }
+
+        return $tables;
+    }
+
+    /**
+     * Helper method to get the schema collection.
+     *
+     * @param \Cake\Database\Connection $connection Connection object
+     * @return \Cake\Database\Schema\CachedCollection
+     * @throws \RuntimeException If given connection object is not compatible with schema caching
+     */
+    public function getSchema(Connection $connection): CachedCollection
+    {
+        $config = $connection->config();
+        if (empty($config['cacheMetadata'])) {
+            $connection->cacheMetadata(true);
+        }
+
+        /** @var \Cake\Database\Schema\CachedCollection $schemaCollection */
+        $schemaCollection = $connection->getSchemaCollection();
+
+        return $schemaCollection;
+    }
+}
diff --git a/vendor/cakephp/database/SqlDialectTrait.php b/vendor/cakephp/database/SqlDialectTrait.php
new file mode 100644
index 0000000..763f8fd
--- /dev/null
+++ b/vendor/cakephp/database/SqlDialectTrait.php
@@ -0,0 +1,10 @@
+ 'DELETE',
+        'where' => ' WHERE %s',
+        'group' => ' GROUP BY %s',
+        'order' => ' %s',
+        'offset' => ' OFFSET %s ROWS',
+        'epilog' => ' %s',
+    ];
+
+    /**
+     * @inheritDoc
+     */
+    protected $_selectParts = [
+        'with', 'select', 'from', 'join', 'where', 'group', 'having', 'window', 'order',
+        'offset', 'limit', 'union', 'epilog',
+    ];
+
+    /**
+     * Helper function used to build the string representation of a `WITH` clause,
+     * it constructs the CTE definitions list without generating the `RECURSIVE`
+     * keyword that is neither required nor valid.
+     *
+     * @param array $parts List of CTEs to be transformed to string
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string
+     */
+    protected function _buildWithPart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        $expressions = [];
+        foreach ($parts as $cte) {
+            $expressions[] = $cte->sql($binder);
+        }
+
+        return sprintf('WITH %s ', implode(', ', $expressions));
+    }
+
+    /**
+     * Generates the INSERT part of a SQL query
+     *
+     * To better handle concurrency and low transaction isolation levels,
+     * we also include an OUTPUT clause so we can ensure we get the inserted
+     * row's data back.
+     *
+     * @param array $parts The parts to build
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string
+     */
+    protected function _buildInsertPart(array $parts, Query $query, ValueBinder $binder): string
+    {
+        if (!isset($parts[0])) {
+            throw new DatabaseException(
+                'Could not compile insert query. No table was specified. ' .
+                'Use `into()` to define a table.'
+            );
+        }
+        $table = $parts[0];
+        $columns = $this->_stringifyExpressions($parts[1], $binder);
+        $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $binder);
+
+        return sprintf(
+            'INSERT%s INTO %s (%s) OUTPUT INSERTED.*',
+            $modifiers,
+            $table,
+            implode(', ', $columns)
+        );
+    }
+
+    /**
+     * Generates the LIMIT part of a SQL query
+     *
+     * @param int $limit the limit clause
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @return string
+     */
+    protected function _buildLimitPart(int $limit, Query $query): string
+    {
+        if ($query->clause('offset') === null) {
+            return '';
+        }
+
+        return sprintf(' FETCH FIRST %d ROWS ONLY', $limit);
+    }
+
+    /**
+     * Helper function used to build the string representation of a HAVING clause,
+     * it constructs the field list taking care of aliasing and
+     * converting expression objects to string.
+     *
+     * @param array $parts list of fields to be transformed to string
+     * @param \Cake\Database\Query $query The query that is being compiled
+     * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder
+     * @return string
+     */
+    protected function _buildHavingPart($parts, $query, $binder)
+    {
+        $selectParts = $query->clause('select');
+
+        foreach ($selectParts as $selectKey => $selectPart) {
+            if (!$selectPart instanceof FunctionExpression) {
+                continue;
+            }
+            foreach ($parts as $k => $p) {
+                if (!is_string($p)) {
+                    continue;
+                }
+                preg_match_all(
+                    '/\b' . trim($selectKey, '[]') . '\b/i',
+                    $p,
+                    $matches
+                );
+
+                if (empty($matches[0])) {
+                    continue;
+                }
+
+                $parts[$k] = preg_replace(
+                    ['/\[|\]/', '/\b' . trim($selectKey, '[]') . '\b/i'],
+                    ['', $selectPart->sql($binder)],
+                    $p
+                );
+            }
+        }
+
+        return sprintf(' HAVING %s', implode(', ', $parts));
+    }
+}
diff --git a/vendor/cakephp/database/Statement/BufferResultsTrait.php b/vendor/cakephp/database/Statement/BufferResultsTrait.php
new file mode 100644
index 0000000..b6a5e57
--- /dev/null
+++ b/vendor/cakephp/database/Statement/BufferResultsTrait.php
@@ -0,0 +1,45 @@
+_bufferResults = $buffer;
+
+        return $this;
+    }
+}
diff --git a/vendor/cakephp/database/Statement/BufferedStatement.php b/vendor/cakephp/database/Statement/BufferedStatement.php
new file mode 100644
index 0000000..59aea0d
--- /dev/null
+++ b/vendor/cakephp/database/Statement/BufferedStatement.php
@@ -0,0 +1,361 @@
+
+ */
+class BufferedStatement implements Iterator, StatementInterface
+{
+    use TypeConverterTrait;
+
+    /**
+     * If true, all rows were fetched
+     *
+     * @var bool
+     */
+    protected $_allFetched = false;
+
+    /**
+     * The decorated statement
+     *
+     * @var \Cake\Database\StatementInterface
+     */
+    protected $statement;
+
+    /**
+     * The driver for the statement
+     *
+     * @var \Cake\Database\DriverInterface
+     */
+    protected $_driver;
+
+    /**
+     * The in-memory cache containing results from previous iterators
+     *
+     * @var array
+     */
+    protected $buffer = [];
+
+    /**
+     * Whether this statement has already been executed
+     *
+     * @var bool
+     */
+    protected $_hasExecuted = false;
+
+    /**
+     * The current iterator index.
+     *
+     * @var int
+     */
+    protected $index = 0;
+
+    /**
+     * Constructor
+     *
+     * @param \Cake\Database\StatementInterface $statement Statement implementation such as PDOStatement
+     * @param \Cake\Database\DriverInterface $driver Driver instance
+     */
+    public function __construct(StatementInterface $statement, DriverInterface $driver)
+    {
+        $this->statement = $statement;
+        $this->_driver = $driver;
+    }
+
+    /**
+     * Returns the connection driver.
+     *
+     * @return \Cake\Database\DriverInterface
+     */
+    protected function getDriver(): DriverInterface
+    {
+        return $this->_driver;
+    }
+
+    /**
+     * Magic getter to return $queryString as read-only.
+     *
+     * @param string $property internal property to get
+     * @return string|null
+     */
+    public function __get(string $property)
+    {
+        if ($property === 'queryString') {
+            /** @psalm-suppress NoInterfaceProperties */
+            return $this->statement->queryString;
+        }
+
+        return null;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function bindValue($column, $value, $type = 'string'): void
+    {
+        $this->statement->bindValue($column, $value, $type);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function closeCursor(): void
+    {
+        $this->statement->closeCursor();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function columnCount(): int
+    {
+        return $this->statement->columnCount();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function errorCode()
+    {
+        return $this->statement->errorCode();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function errorInfo(): array
+    {
+        return $this->statement->errorInfo();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function execute(?array $params = null): bool
+    {
+        $this->_reset();
+        $this->_hasExecuted = true;
+
+        return $this->statement->execute($params);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function fetchColumn(int $position)
+    {
+        $result = $this->fetch(static::FETCH_TYPE_NUM);
+        if ($result !== false && isset($result[$position])) {
+            return $result[$position];
+        }
+
+        return false;
+    }
+
+    /**
+     * Statements can be passed as argument for count() to return the number
+     * for affected rows from last execution.
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        return $this->rowCount();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function bind(array $params, array $types): void
+    {
+        $this->statement->bind($params, $types);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function lastInsertId(?string $table = null, ?string $column = null)
+    {
+        return $this->statement->lastInsertId($table, $column);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string|int $type The type to fetch.
+     * @return array|false
+     */
+    public function fetch($type = self::FETCH_TYPE_NUM)
+    {
+        if ($this->_allFetched) {
+            $row = false;
+            if (isset($this->buffer[$this->index])) {
+                $row = $this->buffer[$this->index];
+            }
+            $this->index += 1;
+
+            if ($row && $type === static::FETCH_TYPE_NUM) {
+                return array_values($row);
+            }
+
+            return $row;
+        }
+
+        $record = $this->statement->fetch($type);
+        if ($record === false) {
+            $this->_allFetched = true;
+            $this->statement->closeCursor();
+
+            return false;
+        }
+        $this->buffer[] = $record;
+
+        return $record;
+    }
+
+    /**
+     * @return array
+     */
+    public function fetchAssoc(): array
+    {
+        $result = $this->fetch(static::FETCH_TYPE_ASSOC);
+
+        return $result ?: [];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function fetchAll($type = self::FETCH_TYPE_NUM)
+    {
+        if ($this->_allFetched) {
+            return $this->buffer;
+        }
+        $results = $this->statement->fetchAll($type);
+        if ($results !== false) {
+            $this->buffer = array_merge($this->buffer, $results);
+        }
+        $this->_allFetched = true;
+        $this->statement->closeCursor();
+
+        return $this->buffer;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function rowCount(): int
+    {
+        if (!$this->_allFetched) {
+            $this->fetchAll(static::FETCH_TYPE_ASSOC);
+        }
+
+        return count($this->buffer);
+    }
+
+    /**
+     * Reset all properties
+     *
+     * @return void
+     */
+    protected function _reset(): void
+    {
+        $this->buffer = [];
+        $this->_allFetched = false;
+        $this->index = 0;
+    }
+
+    /**
+     * Returns the current key in the iterator
+     *
+     * @return mixed
+     */
+    #[\ReturnTypeWillChange]
+    public function key()
+    {
+        return $this->index;
+    }
+
+    /**
+     * Returns the current record in the iterator
+     *
+     * @return mixed
+     */
+    #[\ReturnTypeWillChange]
+    public function current()
+    {
+        return $this->buffer[$this->index];
+    }
+
+    /**
+     * Rewinds the collection
+     *
+     * @return void
+     */
+    public function rewind(): void
+    {
+        $this->index = 0;
+    }
+
+    /**
+     * Returns whether the iterator has more elements
+     *
+     * @return bool
+     */
+    public function valid(): bool
+    {
+        $old = $this->index;
+        $row = $this->fetch(self::FETCH_TYPE_ASSOC);
+
+        // Restore the index as fetch() increments during
+        // the cache scenario.
+        $this->index = $old;
+
+        return $row !== false;
+    }
+
+    /**
+     * Advances the iterator pointer to the next element
+     *
+     * @return void
+     */
+    public function next(): void
+    {
+        $this->index += 1;
+    }
+
+    /**
+     * Get the wrapped statement
+     *
+     * @return \Cake\Database\StatementInterface
+     */
+    public function getInnerStatement(): StatementInterface
+    {
+        return $this->statement;
+    }
+}
diff --git a/vendor/cakephp/database/Statement/CallbackStatement.php b/vendor/cakephp/database/Statement/CallbackStatement.php
new file mode 100644
index 0000000..a0b59c3
--- /dev/null
+++ b/vendor/cakephp/database/Statement/CallbackStatement.php
@@ -0,0 +1,77 @@
+_callback = $callback;
+    }
+
+    /**
+     * Fetch a row from the statement.
+     *
+     * The result will be processed by the callback when it is not `false`.
+     *
+     * @param string|int $type Either 'num' or 'assoc' to indicate the result format you would like.
+     * @return array|false
+     */
+    public function fetch($type = parent::FETCH_TYPE_NUM)
+    {
+        $callback = $this->_callback;
+        $row = $this->_statement->fetch($type);
+
+        return $row === false ? $row : $callback($row);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Each row in the result will be processed by the callback when it is not `false.
+     */
+    public function fetchAll($type = parent::FETCH_TYPE_NUM)
+    {
+        $results = $this->_statement->fetchAll($type);
+
+        return $results !== false ? array_map($this->_callback, $results) : false;
+    }
+}
diff --git a/vendor/cakephp/database/Statement/MysqlStatement.php b/vendor/cakephp/database/Statement/MysqlStatement.php
new file mode 100644
index 0000000..2ad4ec0
--- /dev/null
+++ b/vendor/cakephp/database/Statement/MysqlStatement.php
@@ -0,0 +1,46 @@
+_driver->getConnection();
+
+        try {
+            $connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, $this->_bufferResults);
+            $result = $this->_statement->execute($params);
+        } finally {
+            $connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
+        }
+
+        return $result;
+    }
+}
diff --git a/vendor/cakephp/database/Statement/PDOStatement.php b/vendor/cakephp/database/Statement/PDOStatement.php
new file mode 100644
index 0000000..6cbc827
--- /dev/null
+++ b/vendor/cakephp/database/Statement/PDOStatement.php
@@ -0,0 +1,176 @@
+_statement = $statement;
+        $this->_driver = $driver;
+    }
+
+    /**
+     * Magic getter to return PDOStatement::$queryString as read-only.
+     *
+     * @param string $property internal property to get
+     * @return string|null
+     */
+    public function __get(string $property)
+    {
+        if ($property === 'queryString' && isset($this->_statement->queryString)) {
+            return $this->_statement->queryString;
+        }
+
+        return null;
+    }
+
+    /**
+     * Assign a value to a positional or named variable in prepared query. If using
+     * positional variables you need to start with index one, if using named params then
+     * just use the name in any order.
+     *
+     * You can pass PDO compatible constants for binding values with a type or optionally
+     * any type name registered in the Type class. Any value will be converted to the valid type
+     * representation if needed.
+     *
+     * It is not allowed to combine positional and named variables in the same statement
+     *
+     * ### Examples:
+     *
+     * ```
+     * $statement->bindValue(1, 'a title');
+     * $statement->bindValue(2, 5, PDO::INT);
+     * $statement->bindValue('active', true, 'boolean');
+     * $statement->bindValue(5, new \DateTime(), 'date');
+     * ```
+     *
+     * @param string|int $column name or param position to be bound
+     * @param mixed $value The value to bind to variable in query
+     * @param string|int|null $type PDO type or name of configured Type class
+     * @return void
+     */
+    public function bindValue($column, $value, $type = 'string'): void
+    {
+        if ($type === null) {
+            $type = 'string';
+        }
+        if (!is_int($type)) {
+            [$value, $type] = $this->cast($value, $type);
+        }
+        $this->_statement->bindValue($column, $value, $type);
+    }
+
+    /**
+     * Returns the next row for the result set after executing this statement.
+     * Rows can be fetched to contain columns as names or positions. If no
+     * rows are left in result set, this method will return false
+     *
+     * ### Example:
+     *
+     * ```
+     *  $statement = $connection->prepare('SELECT id, title from articles');
+     *  $statement->execute();
+     *  print_r($statement->fetch('assoc')); // will show ['id' => 1, 'title' => 'a title']
+     * ```
+     *
+     * @param string|int $type 'num' for positional columns, assoc for named columns
+     * @return mixed Result array containing columns and values or false if no results
+     * are left
+     */
+    public function fetch($type = parent::FETCH_TYPE_NUM)
+    {
+        if ($type === static::FETCH_TYPE_NUM) {
+            return $this->_statement->fetch(PDO::FETCH_NUM);
+        }
+        if ($type === static::FETCH_TYPE_ASSOC) {
+            return $this->_statement->fetch(PDO::FETCH_ASSOC);
+        }
+        if ($type === static::FETCH_TYPE_OBJ) {
+            return $this->_statement->fetch(PDO::FETCH_OBJ);
+        }
+
+        if (!is_int($type)) {
+            throw new CakeException(sprintf(
+                'Fetch type for PDOStatement must be an integer, found `%s` instead',
+                getTypeName($type)
+            ));
+        }
+
+        return $this->_statement->fetch($type);
+    }
+
+    /**
+     * Returns an array with all rows resulting from executing this statement
+     *
+     * ### Example:
+     *
+     * ```
+     *  $statement = $connection->prepare('SELECT id, title from articles');
+     *  $statement->execute();
+     *  print_r($statement->fetchAll('assoc')); // will show [0 => ['id' => 1, 'title' => 'a title']]
+     * ```
+     *
+     * @param string|int $type num for fetching columns as positional keys or assoc for column names as keys
+     * @return array|false list of all results from database for this statement, false on failure
+     * @psalm-assert string $type
+     */
+    public function fetchAll($type = parent::FETCH_TYPE_NUM)
+    {
+        if ($type === static::FETCH_TYPE_NUM) {
+            return $this->_statement->fetchAll(PDO::FETCH_NUM);
+        }
+        if ($type === static::FETCH_TYPE_ASSOC) {
+            return $this->_statement->fetchAll(PDO::FETCH_ASSOC);
+        }
+        if ($type === static::FETCH_TYPE_OBJ) {
+            return $this->_statement->fetchAll(PDO::FETCH_OBJ);
+        }
+
+        if (!is_int($type)) {
+            throw new CakeException(sprintf(
+                'Fetch type for PDOStatement must be an integer, found `%s` instead',
+                getTypeName($type)
+            ));
+        }
+
+        return $this->_statement->fetchAll($type);
+    }
+}
diff --git a/vendor/cakephp/database/Statement/SqliteStatement.php b/vendor/cakephp/database/Statement/SqliteStatement.php
new file mode 100644
index 0000000..cc965a8
--- /dev/null
+++ b/vendor/cakephp/database/Statement/SqliteStatement.php
@@ -0,0 +1,70 @@
+_statement instanceof BufferedStatement) {
+            $this->_statement = $this->_statement->getInnerStatement();
+        }
+
+        if ($this->_bufferResults) {
+            $this->_statement = new BufferedStatement($this->_statement, $this->_driver);
+        }
+
+        return $this->_statement->execute($params);
+    }
+
+    /**
+     * Returns the number of rows returned of affected by last execution
+     *
+     * @return int
+     */
+    public function rowCount(): int
+    {
+        /** @psalm-suppress NoInterfaceProperties */
+        if (
+            $this->_statement->queryString &&
+            preg_match('/^(?:DELETE|UPDATE|INSERT)/i', $this->_statement->queryString)
+        ) {
+            $changes = $this->_driver->prepare('SELECT CHANGES()');
+            $changes->execute();
+            $row = $changes->fetch();
+            $changes->closeCursor();
+
+            if (!$row) {
+                return 0;
+            }
+
+            return (int)$row[0];
+        }
+
+        return parent::rowCount();
+    }
+}
diff --git a/vendor/cakephp/database/Statement/SqlserverStatement.php b/vendor/cakephp/database/Statement/SqlserverStatement.php
new file mode 100644
index 0000000..39f3be8
--- /dev/null
+++ b/vendor/cakephp/database/Statement/SqlserverStatement.php
@@ -0,0 +1,53 @@
+cast($value, $type);
+        }
+        if ($type === PDO::PARAM_LOB) {
+            $this->_statement->bindParam($column, $value, $type, 0, PDO::SQLSRV_ENCODING_BINARY);
+        } else {
+            $this->_statement->bindValue($column, $value, $type);
+        }
+    }
+}
diff --git a/vendor/cakephp/database/Statement/StatementDecorator.php b/vendor/cakephp/database/Statement/StatementDecorator.php
new file mode 100644
index 0000000..2c2f46b
--- /dev/null
+++ b/vendor/cakephp/database/Statement/StatementDecorator.php
@@ -0,0 +1,373 @@
+
+ */
+class StatementDecorator implements StatementInterface, Countable, IteratorAggregate
+{
+    use TypeConverterTrait;
+
+    /**
+     * Statement instance implementation, such as PDOStatement
+     * or any other custom implementation.
+     *
+     * @var \Cake\Database\StatementInterface
+     */
+    protected $_statement;
+
+    /**
+     * Reference to the driver object associated to this statement.
+     *
+     * @var \Cake\Database\DriverInterface
+     */
+    protected $_driver;
+
+    /**
+     * Whether this statement has already been executed
+     *
+     * @var bool
+     */
+    protected $_hasExecuted = false;
+
+    /**
+     * Constructor
+     *
+     * @param \Cake\Database\StatementInterface $statement Statement implementation
+     *  such as PDOStatement.
+     * @param \Cake\Database\DriverInterface $driver Driver instance
+     */
+    public function __construct(StatementInterface $statement, DriverInterface $driver)
+    {
+        $this->_statement = $statement;
+        $this->_driver = $driver;
+    }
+
+    /**
+     * Returns the connection driver.
+     *
+     * @return \Cake\Database\DriverInterface
+     */
+    protected function getDriver(): DriverInterface
+    {
+        return $this->_driver;
+    }
+
+    /**
+     * Magic getter to return $queryString as read-only.
+     *
+     * @param string $property internal property to get
+     * @return string|null
+     */
+    public function __get(string $property)
+    {
+        if ($property === 'queryString') {
+            /** @psalm-suppress NoInterfaceProperties */
+            return $this->_statement->queryString;
+        }
+
+        return null;
+    }
+
+    /**
+     * Assign a value to a positional or named variable in prepared query. If using
+     * positional variables you need to start with index one, if using named params then
+     * just use the name in any order.
+     *
+     * It is not allowed to combine positional and named variables in the same statement.
+     *
+     * ### Examples:
+     *
+     * ```
+     * $statement->bindValue(1, 'a title');
+     * $statement->bindValue('active', true, 'boolean');
+     * $statement->bindValue(5, new \DateTime(), 'date');
+     * ```
+     *
+     * @param string|int $column name or param position to be bound
+     * @param mixed $value The value to bind to variable in query
+     * @param string|int|null $type name of configured Type class
+     * @return void
+     */
+    public function bindValue($column, $value, $type = 'string'): void
+    {
+        $this->_statement->bindValue($column, $value, $type);
+    }
+
+    /**
+     * Closes a cursor in the database, freeing up any resources and memory
+     * allocated to it. In most cases you don't need to call this method, as it is
+     * automatically called after fetching all results from the result set.
+     *
+     * @return void
+     */
+    public function closeCursor(): void
+    {
+        $this->_statement->closeCursor();
+    }
+
+    /**
+     * Returns the number of columns this statement's results will contain.
+     *
+     * ### Example:
+     *
+     * ```
+     * $statement = $connection->prepare('SELECT id, title from articles');
+     * $statement->execute();
+     * echo $statement->columnCount(); // outputs 2
+     * ```
+     *
+     * @return int
+     */
+    public function columnCount(): int
+    {
+        return $this->_statement->columnCount();
+    }
+
+    /**
+     * Returns the error code for the last error that occurred when executing this statement.
+     *
+     * @return string|int
+     */
+    public function errorCode()
+    {
+        return $this->_statement->errorCode();
+    }
+
+    /**
+     * Returns the error information for the last error that occurred when executing
+     * this statement.
+     *
+     * @return array
+     */
+    public function errorInfo(): array
+    {
+        return $this->_statement->errorInfo();
+    }
+
+    /**
+     * Executes the statement by sending the SQL query to the database. It can optionally
+     * take an array or arguments to be bound to the query variables. Please note
+     * that binding parameters from this method will not perform any custom type conversion
+     * as it would normally happen when calling `bindValue`.
+     *
+     * @param array|null $params list of values to be bound to query
+     * @return bool true on success, false otherwise
+     */
+    public function execute(?array $params = null): bool
+    {
+        $this->_hasExecuted = true;
+
+        return $this->_statement->execute($params);
+    }
+
+    /**
+     * Returns the next row for the result set after executing this statement.
+     * Rows can be fetched to contain columns as names or positions. If no
+     * rows are left in result set, this method will return false.
+     *
+     * ### Example:
+     *
+     * ```
+     * $statement = $connection->prepare('SELECT id, title from articles');
+     * $statement->execute();
+     * print_r($statement->fetch('assoc')); // will show ['id' => 1, 'title' => 'a title']
+     * ```
+     *
+     * @param string|int $type 'num' for positional columns, assoc for named columns
+     * @return mixed Result array containing columns and values or false if no results
+     * are left
+     */
+    public function fetch($type = self::FETCH_TYPE_NUM)
+    {
+        return $this->_statement->fetch($type);
+    }
+
+    /**
+     * Returns the next row in a result set as an associative array. Calling this function is the same as calling
+     * $statement->fetch(StatementDecorator::FETCH_TYPE_ASSOC). If no results are found an empty array is returned.
+     *
+     * @return array
+     */
+    public function fetchAssoc(): array
+    {
+        $result = $this->fetch(static::FETCH_TYPE_ASSOC);
+
+        return $result ?: [];
+    }
+
+    /**
+     * Returns the value of the result at position.
+     *
+     * @param int $position The numeric position of the column to retrieve in the result
+     * @return mixed Returns the specific value of the column designated at $position
+     */
+    public function fetchColumn(int $position)
+    {
+        $result = $this->fetch(static::FETCH_TYPE_NUM);
+        if ($result && isset($result[$position])) {
+            return $result[$position];
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns an array with all rows resulting from executing this statement.
+     *
+     * ### Example:
+     *
+     * ```
+     * $statement = $connection->prepare('SELECT id, title from articles');
+     * $statement->execute();
+     * print_r($statement->fetchAll('assoc')); // will show [0 => ['id' => 1, 'title' => 'a title']]
+     * ```
+     *
+     * @param string|int $type num for fetching columns as positional keys or assoc for column names as keys
+     * @return array|false List of all results from database for this statement. False on failure.
+     */
+    public function fetchAll($type = self::FETCH_TYPE_NUM)
+    {
+        return $this->_statement->fetchAll($type);
+    }
+
+    /**
+     * Returns the number of rows affected by this SQL statement.
+     *
+     * ### Example:
+     *
+     * ```
+     * $statement = $connection->prepare('SELECT id, title from articles');
+     * $statement->execute();
+     * print_r($statement->rowCount()); // will show 1
+     * ```
+     *
+     * @return int
+     */
+    public function rowCount(): int
+    {
+        return $this->_statement->rowCount();
+    }
+
+    /**
+     * Statements are iterable as arrays, this method will return
+     * the iterator object for traversing all items in the result.
+     *
+     * ### Example:
+     *
+     * ```
+     * $statement = $connection->prepare('SELECT id, title from articles');
+     * foreach ($statement as $row) {
+     *   //do stuff
+     * }
+     * ```
+     *
+     * @return \Cake\Database\StatementInterface
+     * @psalm-suppress ImplementedReturnTypeMismatch
+     */
+    #[\ReturnTypeWillChange]
+    public function getIterator()
+    {
+        if (!$this->_hasExecuted) {
+            $this->execute();
+        }
+
+        return $this->_statement;
+    }
+
+    /**
+     * Statements can be passed as argument for count() to return the number
+     * for affected rows from last execution.
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        return $this->rowCount();
+    }
+
+    /**
+     * Binds a set of values to statement object with corresponding type.
+     *
+     * @param array $params list of values to be bound
+     * @param array $types list of types to be used, keys should match those in $params
+     * @return void
+     */
+    public function bind(array $params, array $types): void
+    {
+        if (empty($params)) {
+            return;
+        }
+
+        $anonymousParams = is_int(key($params));
+        $offset = 1;
+        foreach ($params as $index => $value) {
+            $type = $types[$index] ?? null;
+            if ($anonymousParams) {
+                /** @psalm-suppress InvalidOperand */
+                $index += $offset;
+            }
+            $this->bindValue($index, $value, $type);
+        }
+    }
+
+    /**
+     * Returns the latest primary inserted using this statement.
+     *
+     * @param string|null $table table name or sequence to get last insert value from
+     * @param string|null $column the name of the column representing the primary key
+     * @return string|int
+     */
+    public function lastInsertId(?string $table = null, ?string $column = null)
+    {
+        if ($column && $this->columnCount()) {
+            $row = $this->fetch(static::FETCH_TYPE_ASSOC);
+
+            if ($row && isset($row[$column])) {
+                return $row[$column];
+            }
+        }
+
+        return $this->_driver->lastInsertId($table, $column);
+    }
+
+    /**
+     * Returns the statement object that was decorated by this class.
+     *
+     * @return \Cake\Database\StatementInterface
+     */
+    public function getInnerStatement()
+    {
+        return $this->_statement;
+    }
+}
diff --git a/vendor/cakephp/database/StatementInterface.php b/vendor/cakephp/database/StatementInterface.php
new file mode 100644
index 0000000..dc5bbc6
--- /dev/null
+++ b/vendor/cakephp/database/StatementInterface.php
@@ -0,0 +1,203 @@
+bindValue(1, 'a title');
+     * $statement->bindValue('active', true, 'boolean');
+     * $statement->bindValue(5, new \DateTime(), 'date');
+     * ```
+     *
+     * @param string|int $column name or param position to be bound
+     * @param mixed $value The value to bind to variable in query
+     * @param string|int|null $type name of configured Type class, or PDO type constant.
+     * @return void
+     */
+    public function bindValue($column, $value, $type = 'string'): void;
+
+    /**
+     * Closes a cursor in the database, freeing up any resources and memory
+     * allocated to it. In most cases you don't need to call this method, as it is
+     * automatically called after fetching all results from the result set.
+     *
+     * @return void
+     */
+    public function closeCursor(): void;
+
+    /**
+     * Returns the number of columns this statement's results will contain
+     *
+     * ### Example:
+     *
+     * ```
+     *  $statement = $connection->prepare('SELECT id, title from articles');
+     *  $statement->execute();
+     *  echo $statement->columnCount(); // outputs 2
+     * ```
+     *
+     * @return int
+     */
+    public function columnCount(): int;
+
+    /**
+     * Returns the error code for the last error that occurred when executing this statement
+     *
+     * @return string|int
+     */
+    public function errorCode();
+
+    /**
+     * Returns the error information for the last error that occurred when executing
+     * this statement
+     *
+     * @return array
+     */
+    public function errorInfo(): array;
+
+    /**
+     * Executes the statement by sending the SQL query to the database. It can optionally
+     * take an array or arguments to be bound to the query variables. Please note
+     * that binding parameters from this method will not perform any custom type conversion
+     * as it would normally happen when calling `bindValue`
+     *
+     * @param array|null $params list of values to be bound to query
+     * @return bool true on success, false otherwise
+     */
+    public function execute(?array $params = null): bool;
+
+    /**
+     * Returns the next row for the result set after executing this statement.
+     * Rows can be fetched to contain columns as names or positions. If no
+     * rows are left in result set, this method will return false
+     *
+     * ### Example:
+     *
+     * ```
+     *  $statement = $connection->prepare('SELECT id, title from articles');
+     *  $statement->execute();
+     *  print_r($statement->fetch('assoc')); // will show ['id' => 1, 'title' => 'a title']
+     * ```
+     *
+     * @param string|int $type 'num' for positional columns, assoc for named columns, or PDO fetch mode constants.
+     * @return mixed Result array containing columns and values or false if no results
+     * are left
+     */
+    public function fetch($type = 'num');
+
+    /**
+     * Returns an array with all rows resulting from executing this statement
+     *
+     * ### Example:
+     *
+     * ```
+     *  $statement = $connection->prepare('SELECT id, title from articles');
+     *  $statement->execute();
+     *  print_r($statement->fetchAll('assoc')); // will show [0 => ['id' => 1, 'title' => 'a title']]
+     * ```
+     *
+     * @param string|int $type num for fetching columns as positional keys or assoc for column names as keys
+     * @return array|false list of all results from database for this statement or false on failure.
+     */
+    public function fetchAll($type = 'num');
+
+    /**
+     * Returns the value of the result at position.
+     *
+     * @param int $position The numeric position of the column to retrieve in the result
+     * @return mixed Returns the specific value of the column designated at $position
+     */
+    public function fetchColumn(int $position);
+
+    /**
+     * Returns the number of rows affected by this SQL statement
+     *
+     * ### Example:
+     *
+     * ```
+     *  $statement = $connection->prepare('SELECT id, title from articles');
+     *  $statement->execute();
+     *  print_r($statement->rowCount()); // will show 1
+     * ```
+     *
+     * @return int
+     */
+    public function rowCount(): int;
+
+    /**
+     * Statements can be passed as argument for count()
+     * to return the number for affected rows from last execution
+     *
+     * @return int
+     */
+    public function count(): int;
+
+    /**
+     * Binds a set of values to statement object with corresponding type
+     *
+     * @param array $params list of values to be bound
+     * @param array $types list of types to be used, keys should match those in $params
+     * @return void
+     */
+    public function bind(array $params, array $types): void;
+
+    /**
+     * Returns the latest primary inserted using this statement
+     *
+     * @param string|null $table table name or sequence to get last insert value from
+     * @param string|null $column the name of the column representing the primary key
+     * @return string|int
+     */
+    public function lastInsertId(?string $table = null, ?string $column = null);
+}
diff --git a/vendor/cakephp/database/Type.php b/vendor/cakephp/database/Type.php
new file mode 100644
index 0000000..164419c
--- /dev/null
+++ b/vendor/cakephp/database/Type.php
@@ -0,0 +1,9 @@
+_name = $name;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getName(): ?string
+    {
+        return $this->_name;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getBaseType(): ?string
+    {
+        return $this->_name;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function toStatement($value, DriverInterface $driver)
+    {
+        if ($value === null) {
+            return PDO::PARAM_NULL;
+        }
+
+        return PDO::PARAM_STR;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function newId()
+    {
+        return null;
+    }
+}
diff --git a/vendor/cakephp/database/Type/BatchCastingInterface.php b/vendor/cakephp/database/Type/BatchCastingInterface.php
new file mode 100644
index 0000000..8761f1c
--- /dev/null
+++ b/vendor/cakephp/database/Type/BatchCastingInterface.php
@@ -0,0 +1,37 @@
+ $fields The field keys to cast
+     * @param \Cake\Database\DriverInterface $driver Object from which database preferences and configuration will be extracted.
+     * @return array
+     */
+    public function manyToPHP(array $values, array $fields, DriverInterface $driver): array;
+}
diff --git a/vendor/cakephp/database/Type/BinaryType.php b/vendor/cakephp/database/Type/BinaryType.php
new file mode 100644
index 0000000..e570f53
--- /dev/null
+++ b/vendor/cakephp/database/Type/BinaryType.php
@@ -0,0 +1,92 @@
+convertStringToBinaryUuid($value);
+    }
+
+    /**
+     * Generate a new binary UUID
+     *
+     * @return string A new primary key value.
+     */
+    public function newId(): string
+    {
+        return Text::uuid();
+    }
+
+    /**
+     * Convert binary uuid into resource handles
+     *
+     * @param mixed $value The value to convert.
+     * @param \Cake\Database\DriverInterface $driver The driver instance to convert with.
+     * @return resource|string|null
+     * @throws \Cake\Core\Exception\CakeException
+     */
+    public function toPHP($value, DriverInterface $driver)
+    {
+        if ($value === null) {
+            return null;
+        }
+        if (is_string($value)) {
+            return $this->convertBinaryUuidToString($value);
+        }
+        if (is_resource($value)) {
+            return $value;
+        }
+
+        throw new CakeException(sprintf('Unable to convert %s into binary uuid.', gettype($value)));
+    }
+
+    /**
+     * Get the correct PDO binding type for Binary data.
+     *
+     * @param mixed $value The value being bound.
+     * @param \Cake\Database\DriverInterface $driver The driver.
+     * @return int
+     */
+    public function toStatement($value, DriverInterface $driver): int
+    {
+        return PDO::PARAM_LOB;
+    }
+
+    /**
+     * Marshals flat data into PHP objects.
+     *
+     * Most useful for converting request data into PHP objects
+     * that make sense for the rest of the ORM/Database layers.
+     *
+     * @param mixed $value The value to convert.
+     * @return mixed Converted value.
+     */
+    public function marshal($value)
+    {
+        return $value;
+    }
+
+    /**
+     * Converts a binary uuid to a string representation
+     *
+     * @param mixed $binary The value to convert.
+     * @return string Converted value.
+     */
+    protected function convertBinaryUuidToString($binary): string
+    {
+        $string = unpack('H*', $binary);
+
+        $string = preg_replace(
+            '/([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})/',
+            '$1-$2-$3-$4-$5',
+            $string
+        );
+
+        return $string[1];
+    }
+
+    /**
+     * Converts a string UUID (36 or 32 char) to a binary representation.
+     *
+     * @param string $string The value to convert.
+     * @return string Converted value.
+     */
+    protected function convertStringToBinaryUuid($string): string
+    {
+        $string = str_replace('-', '', $string);
+
+        return pack('H*', $string);
+    }
+}
diff --git a/vendor/cakephp/database/Type/BoolType.php b/vendor/cakephp/database/Type/BoolType.php
new file mode 100644
index 0000000..0bb8b0a
--- /dev/null
+++ b/vendor/cakephp/database/Type/BoolType.php
@@ -0,0 +1,126 @@
+|null Array of column information, or `null` in case the column isn't processed by this type.
+     */
+    public function convertColumnDefinition(array $definition, DriverInterface $driver): ?array;
+}
diff --git a/vendor/cakephp/database/Type/DateTimeFractionalType.php b/vendor/cakephp/database/Type/DateTimeFractionalType.php
new file mode 100644
index 0000000..3cc149d
--- /dev/null
+++ b/vendor/cakephp/database/Type/DateTimeFractionalType.php
@@ -0,0 +1,42 @@
+
+     */
+    protected $_marshalFormats = [
+        'Y-m-d H:i',
+        'Y-m-d H:i:s',
+        'Y-m-d\TH:i',
+        'Y-m-d\TH:i:s',
+        'Y-m-d\TH:i:sP',
+    ];
+
+    /**
+     * Whether `marshal()` should use locale-aware parser with `_localeMarshalFormat`.
+     *
+     * @var bool
+     */
+    protected $_useLocaleMarshal = false;
+
+    /**
+     * The locale-aware format `marshal()` uses when `_useLocaleParser` is true.
+     *
+     * See `Cake\I18n\Time::parseDateTime()` for accepted formats.
+     *
+     * @var array|string|int
+     */
+    protected $_localeMarshalFormat;
+
+    /**
+     * The classname to use when creating objects.
+     *
+     * @var string
+     * @psalm-var class-string<\DateTime>|class-string<\DateTimeImmutable>
+     */
+    protected $_className;
+
+    /**
+     * Database time zone.
+     *
+     * @var \DateTimeZone|null
+     */
+    protected $dbTimezone;
+
+    /**
+     * User time zone.
+     *
+     * @var \DateTimeZone|null
+     */
+    protected $userTimezone;
+
+    /**
+     * Default time zone.
+     *
+     * @var \DateTimeZone
+     */
+    protected $defaultTimezone;
+
+    /**
+     * Whether database time zone is kept when converting
+     *
+     * @var bool
+     */
+    protected $keepDatabaseTimezone = false;
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string|null $name The name identifying this type
+     */
+    public function __construct(?string $name = null)
+    {
+        parent::__construct($name);
+
+        $this->defaultTimezone = new DateTimeZone(date_default_timezone_get());
+        $this->_setClassName(FrozenTime::class, DateTimeImmutable::class);
+    }
+
+    /**
+     * Convert DateTime instance into strings.
+     *
+     * @param mixed $value The value to convert.
+     * @param \Cake\Database\DriverInterface $driver The driver instance to convert with.
+     * @return string|null
+     */
+    public function toDatabase($value, DriverInterface $driver): ?string
+    {
+        if ($value === null || is_string($value)) {
+            return $value;
+        }
+        if (is_int($value)) {
+            $class = $this->_className;
+            $value = new $class('@' . $value);
+        }
+
+        if (
+            $this->dbTimezone !== null
+            && $this->dbTimezone->getName() !== $value->getTimezone()->getName()
+        ) {
+            if (!$value instanceof DateTimeImmutable) {
+                $value = clone $value;
+            }
+            $value = $value->setTimezone($this->dbTimezone);
+        }
+
+        return $value->format($this->_format);
+    }
+
+    /**
+     * Alias for `setDatabaseTimezone()`.
+     *
+     * @param \DateTimeZone|string|null $timezone Database timezone.
+     * @return $this
+     * @deprecated 4.1.0 Use {@link setDatabaseTimezone()} instead.
+     */
+    public function setTimezone($timezone)
+    {
+        deprecationWarning('DateTimeType::setTimezone() is deprecated. Use setDatabaseTimezone() instead.');
+
+        return $this->setDatabaseTimezone($timezone);
+    }
+
+    /**
+     * Set database timezone.
+     *
+     * This is the time zone used when converting database strings to DateTime
+     * instances and converting DateTime instances to database strings.
+     *
+     * @see DateTimeType::setKeepDatabaseTimezone
+     * @param \DateTimeZone|string|null $timezone Database timezone.
+     * @return $this
+     */
+    public function setDatabaseTimezone($timezone)
+    {
+        if (is_string($timezone)) {
+            $timezone = new DateTimeZone($timezone);
+        }
+        $this->dbTimezone = $timezone;
+
+        return $this;
+    }
+
+    /**
+     * Set user timezone.
+     *
+     * This is the time zone used when marshalling strings to DateTime instances.
+     *
+     * @param \DateTimeZone|string|null $timezone User timezone.
+     * @return $this
+     */
+    public function setUserTimezone($timezone)
+    {
+        if (is_string($timezone)) {
+            $timezone = new DateTimeZone($timezone);
+        }
+        $this->userTimezone = $timezone;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param mixed $value Value to be converted to PHP equivalent
+     * @param \Cake\Database\DriverInterface $driver Object from which database preferences and configuration will be extracted
+     * @return \DateTimeInterface|null
+     */
+    public function toPHP($value, DriverInterface $driver)
+    {
+        if ($value === null) {
+            return null;
+        }
+
+        $class = $this->_className;
+        if (is_int($value)) {
+            $instance = new $class('@' . $value);
+        } elseif (strpos($value, '0000-00-00') === 0) {
+            return null;
+        } else {
+            $instance = new $class($value, $this->dbTimezone);
+        }
+
+        if (
+            !$this->keepDatabaseTimezone &&
+            $instance->getTimezone()->getName() !== $this->defaultTimezone->getName()
+        ) {
+            $instance = $instance->setTimezone($this->defaultTimezone);
+        }
+
+        if ($this->setToDateStart) {
+            $instance = $instance->setTime(0, 0, 0);
+        }
+
+        return $instance;
+    }
+
+    /**
+     * Set whether DateTime object created from database string is converted
+     * to default time zone.
+     *
+     * If your database date times are in a specific time zone that you want
+     * to keep in the DateTime instance then set this to true.
+     *
+     * When false, datetime timezones are converted to default time zone.
+     * This is default behavior.
+     *
+     * @param bool $keep If true, database time zone is kept when converting
+     *      to DateTime instances.
+     * @return $this
+     */
+    public function setKeepDatabaseTimezone(bool $keep)
+    {
+        $this->keepDatabaseTimezone = $keep;
+
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function manyToPHP(array $values, array $fields, DriverInterface $driver): array
+    {
+        foreach ($fields as $field) {
+            if (!isset($values[$field])) {
+                continue;
+            }
+
+            $value = $values[$field];
+
+            $class = $this->_className;
+            if (is_int($value)) {
+                $instance = new $class('@' . $value);
+            } elseif (strpos($value, '0000-00-00') === 0) {
+                $values[$field] = null;
+                continue;
+            } else {
+                $instance = new $class($value, $this->dbTimezone);
+            }
+
+            if (
+                !$this->keepDatabaseTimezone &&
+                $instance->getTimezone()->getName() !== $this->defaultTimezone->getName()
+            ) {
+                $instance = $instance->setTimezone($this->defaultTimezone);
+            }
+
+            if ($this->setToDateStart) {
+                $instance = $instance->setTime(0, 0, 0);
+            }
+
+            $values[$field] = $instance;
+        }
+
+        return $values;
+    }
+
+    /**
+     * Convert request data into a datetime object.
+     *
+     * @param mixed $value Request data
+     * @return \DateTimeInterface|null
+     */
+    public function marshal($value): ?DateTimeInterface
+    {
+        if ($value instanceof DateTimeInterface) {
+            if ($value instanceof DateTime) {
+                $value = clone $value;
+            }
+
+            if ($value instanceof ChronosDate) {
+                return $value;
+            }
+
+            /** @var \Datetime|\DateTimeImmutable $value */
+            return $value->setTimezone($this->defaultTimezone);
+        }
+
+        /** @var class-string<\DateTimeInterface> $class */
+        $class = $this->_className;
+        try {
+            if ($value === '' || $value === null || is_bool($value)) {
+                return null;
+            }
+
+            if (is_int($value) || (is_string($value) && ctype_digit($value))) {
+                /** @var \DateTime|\DateTimeImmutable $dateTime */
+                $dateTime = new $class('@' . $value);
+
+                return $dateTime->setTimezone($this->defaultTimezone);
+            }
+
+            if (is_string($value)) {
+                if ($this->_useLocaleMarshal) {
+                    $dateTime = $this->_parseLocaleValue($value);
+                } else {
+                    $dateTime = $this->_parseValue($value);
+                }
+
+                /** @var \DateTime|\DateTimeImmutable $dateTime */
+                if ($dateTime !== null) {
+                    $dateTime = $dateTime->setTimezone($this->defaultTimezone);
+                }
+
+                return $dateTime;
+            }
+        } catch (Exception $e) {
+            return null;
+        }
+
+        if (is_array($value) && implode('', $value) === '') {
+            return null;
+        }
+        $value += ['hour' => 0, 'minute' => 0, 'second' => 0, 'microsecond' => 0];
+
+        $format = '';
+        if (
+            isset($value['year'], $value['month'], $value['day']) &&
+            (
+                is_numeric($value['year']) &&
+                is_numeric($value['month']) &&
+                is_numeric($value['day'])
+            )
+        ) {
+            $format .= sprintf('%d-%02d-%02d', $value['year'], $value['month'], $value['day']);
+        }
+
+        if (isset($value['meridian']) && (int)$value['hour'] === 12) {
+            $value['hour'] = 0;
+        }
+        if (isset($value['meridian'])) {
+            $value['hour'] = strtolower($value['meridian']) === 'am' ? $value['hour'] : $value['hour'] + 12;
+        }
+        $format .= sprintf(
+            '%s%02d:%02d:%02d.%06d',
+            empty($format) ? '' : ' ',
+            $value['hour'],
+            $value['minute'],
+            $value['second'],
+            $value['microsecond']
+        );
+
+        /** @var \DateTime|\DateTimeImmutable $dateTime */
+        $dateTime = new $class($format, $value['timezone'] ?? $this->userTimezone);
+
+        return $dateTime->setTimezone($this->defaultTimezone);
+    }
+
+    /**
+     * Sets whether to parse strings passed to `marshal()` using
+     * the locale-aware format set by `setLocaleFormat()`.
+     *
+     * @param bool $enable Whether to enable
+     * @return $this
+     */
+    public function useLocaleParser(bool $enable = true)
+    {
+        if ($enable === false) {
+            $this->_useLocaleMarshal = $enable;
+
+            return $this;
+        }
+        if (is_subclass_of($this->_className, I18nDateTimeInterface::class)) {
+            $this->_useLocaleMarshal = $enable;
+
+            return $this;
+        }
+        throw new RuntimeException(
+            sprintf('Cannot use locale parsing with the %s class', $this->_className)
+        );
+    }
+
+    /**
+     * Sets the locale-aware format used by `marshal()` when parsing strings.
+     *
+     * See `Cake\I18n\Time::parseDateTime()` for accepted formats.
+     *
+     * @param array|string $format The locale-aware format
+     * @see \Cake\I18n\Time::parseDateTime()
+     * @return $this
+     */
+    public function setLocaleFormat($format)
+    {
+        $this->_localeMarshalFormat = $format;
+
+        return $this;
+    }
+
+    /**
+     * Change the preferred class name to the FrozenTime implementation.
+     *
+     * @return $this
+     * @deprecated 4.3.0 This method is no longer needed as using immutable datetime class is the default behavior.
+     */
+    public function useImmutable()
+    {
+        deprecationWarning(
+            'Configuring immutable or mutable classes is deprecated and immutable'
+            . ' classes will be the permanent configuration in 5.0. Calling `useImmutable()` is unnecessary.'
+        );
+
+        $this->_setClassName(FrozenTime::class, DateTimeImmutable::class);
+
+        return $this;
+    }
+
+    /**
+     * Set the classname to use when building objects.
+     *
+     * @param string $class The classname to use.
+     * @param string $fallback The classname to use when the preferred class does not exist.
+     * @return void
+     * @psalm-param class-string<\DateTime>|class-string<\DateTimeImmutable> $class
+     * @psalm-param class-string<\DateTime>|class-string<\DateTimeImmutable> $fallback
+     */
+    protected function _setClassName(string $class, string $fallback): void
+    {
+        if (!class_exists($class)) {
+            $class = $fallback;
+        }
+        $this->_className = $class;
+    }
+
+    /**
+     * Get the classname used for building objects.
+     *
+     * @return string
+     * @psalm-return class-string<\DateTime>|class-string<\DateTimeImmutable>
+     */
+    public function getDateTimeClassName(): string
+    {
+        return $this->_className;
+    }
+
+    /**
+     * Change the preferred class name to the mutable Time implementation.
+     *
+     * @return $this
+     * @deprecated 4.3.0 Using mutable datetime objects is deprecated.
+     */
+    public function useMutable()
+    {
+        deprecationWarning(
+            'Configuring immutable or mutable classes is deprecated and immutable'
+            . ' classes will be the permanent configuration in 5.0. Calling `useImmutable()` is unnecessary.'
+        );
+
+        $this->_setClassName(Time::class, DateTime::class);
+
+        return $this;
+    }
+
+    /**
+     * Converts a string into a DateTime object after parsing it using the locale
+     * aware parser with the format set by `setLocaleFormat()`.
+     *
+     * @param string $value The value to parse and convert to an object.
+     * @return \Cake\I18n\I18nDateTimeInterface|null
+     */
+    protected function _parseLocaleValue(string $value): ?I18nDateTimeInterface
+    {
+        /** @psalm-var class-string<\Cake\I18n\I18nDateTimeInterface> $class */
+        $class = $this->_className;
+
+        return $class::parseDateTime($value, $this->_localeMarshalFormat, $this->userTimezone);
+    }
+
+    /**
+     * Converts a string into a DateTime object after parsing it using the
+     * formats in `_marshalFormats`.
+     *
+     * @param string $value The value to parse and convert to an object.
+     * @return \DateTimeInterface|null
+     */
+    protected function _parseValue(string $value): ?DateTimeInterface
+    {
+        $class = $this->_className;
+
+        foreach ($this->_marshalFormats as $format) {
+            try {
+                $dateTime = $class::createFromFormat($format, $value, $this->userTimezone);
+                // Check for false in case DateTime is used directly
+                if ($dateTime !== false) {
+                    return $dateTime;
+                }
+            } catch (InvalidArgumentException $e) {
+                // Chronos wraps DateTime::createFromFormat and throws
+                // exception if parse fails.
+                continue;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Casts given value to Statement equivalent
+     *
+     * @param mixed $value value to be converted to PDO statement
+     * @param \Cake\Database\DriverInterface $driver object from which database preferences and configuration will be extracted
+     * @return mixed
+     */
+    public function toStatement($value, DriverInterface $driver)
+    {
+        return PDO::PARAM_STR;
+    }
+}
diff --git a/vendor/cakephp/database/Type/DateType.php b/vendor/cakephp/database/Type/DateType.php
new file mode 100644
index 0000000..0cd63cd
--- /dev/null
+++ b/vendor/cakephp/database/Type/DateType.php
@@ -0,0 +1,174 @@
+_setClassName(FrozenDate::class, DateTimeImmutable::class);
+    }
+
+    /**
+     * Change the preferred class name to the FrozenDate implementation.
+     *
+     * @return $this
+     * @deprecated 4.3.0 This method is no longer needed as using immutable datetime class is the default behavior.
+     */
+    public function useImmutable()
+    {
+        deprecationWarning(
+            'Configuring immutable or mutable classes is deprecated and immutable'
+            . ' classes will be the permanent configuration in 5.0. Calling `useImmutable()` is unnecessary.'
+        );
+
+        $this->_setClassName(FrozenDate::class, DateTimeImmutable::class);
+
+        return $this;
+    }
+
+    /**
+     * Change the preferred class name to the mutable Date implementation.
+     *
+     * @return $this
+     * @deprecated 4.3.0 Using mutable datetime objects is deprecated.
+     */
+    public function useMutable()
+    {
+        deprecationWarning(
+            'Configuring immutable or mutable classes is deprecated and immutable'
+            . ' classes will be the permanent configuration in 5.0. Calling `useImmutable()` is unnecessary.'
+        );
+
+        $this->_setClassName(Date::class, DateTime::class);
+
+        return $this;
+    }
+
+    /**
+     * Convert request data into a datetime object.
+     *
+     * @param mixed $value Request data
+     * @return \DateTimeInterface|null
+     */
+    public function marshal($value): ?DateTimeInterface
+    {
+        if ($value instanceof DateTimeInterface) {
+            return new FrozenDate($value);
+        }
+
+        /** @var class-string<\Cake\Chronos\ChronosDate> $class */
+        $class = $this->_className;
+        try {
+            if ($value === '' || $value === null || is_bool($value)) {
+                return null;
+            }
+
+            if (is_int($value) || (is_string($value) && ctype_digit($value))) {
+                /** @var \Cake\I18n\FrozenDate|\DateTimeImmutable $dateTime */
+                $dateTime = new $class('@' . $value);
+
+                return $dateTime;
+            }
+
+            if (is_string($value)) {
+                if ($this->_useLocaleMarshal) {
+                    $dateTime = $this->_parseLocaleValue($value);
+                } else {
+                    $dateTime = $this->_parseValue($value);
+                }
+
+                return $dateTime;
+            }
+        } catch (Exception $e) {
+            return null;
+        }
+
+        if (is_array($value) && implode('', $value) === '') {
+            return null;
+        }
+        $format = '';
+        if (
+            isset($value['year'], $value['month'], $value['day']) &&
+            (
+                is_numeric($value['year']) &&
+                is_numeric($value['month']) &&
+                is_numeric($value['day'])
+            )
+        ) {
+            $format .= sprintf('%d-%02d-%02d', $value['year'], $value['month'], $value['day']);
+        }
+
+        if (empty($format)) {
+            // Invalid array format.
+            return null;
+        }
+
+        /** @var \Cake\I18n\FrozenDate|\DateTimeImmutable $dateTime */
+        $dateTime = new $class($format);
+
+        return $dateTime;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function _parseLocaleValue(string $value): ?I18nDateTimeInterface
+    {
+        /** @psalm-var class-string<\Cake\I18n\I18nDateTimeInterface> $class */
+        $class = $this->_className;
+
+        return $class::parseDate($value, $this->_localeMarshalFormat);
+    }
+}
diff --git a/vendor/cakephp/database/Type/DecimalType.php b/vendor/cakephp/database/Type/DecimalType.php
new file mode 100644
index 0000000..f06f6d7
--- /dev/null
+++ b/vendor/cakephp/database/Type/DecimalType.php
@@ -0,0 +1,190 @@
+_useLocaleParser) {
+            return $this->_parseValue($value);
+        }
+        if (is_numeric($value)) {
+            return (string)$value;
+        }
+        if (is_string($value) && preg_match('/^[0-9,. ]+$/', $value)) {
+            return $value;
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets whether to parse numbers passed to the marshal() function
+     * by using a locale aware parser.
+     *
+     * @param bool $enable Whether to enable
+     * @return $this
+     * @throws \RuntimeException
+     */
+    public function useLocaleParser(bool $enable = true)
+    {
+        if ($enable === false) {
+            $this->_useLocaleParser = $enable;
+
+            return $this;
+        }
+        if (
+            static::$numberClass === Number::class ||
+            is_subclass_of(static::$numberClass, Number::class)
+        ) {
+            $this->_useLocaleParser = $enable;
+
+            return $this;
+        }
+        throw new RuntimeException(
+            sprintf('Cannot use locale parsing with the %s class', static::$numberClass)
+        );
+    }
+
+    /**
+     * Converts localized string into a decimal string after parsing it using
+     * the locale aware parser.
+     *
+     * @param string $value The value to parse and convert to an float.
+     * @return string
+     */
+    protected function _parseValue(string $value): string
+    {
+        /** @var \Cake\I18n\Number $class */
+        $class = static::$numberClass;
+
+        return (string)$class::parseFloat($value);
+    }
+}
diff --git a/vendor/cakephp/database/Type/ExpressionTypeCasterTrait.php b/vendor/cakephp/database/Type/ExpressionTypeCasterTrait.php
new file mode 100644
index 0000000..621bb9d
--- /dev/null
+++ b/vendor/cakephp/database/Type/ExpressionTypeCasterTrait.php
@@ -0,0 +1,80 @@
+toExpression($value);
+    }
+
+    /**
+     * Returns an array with the types that require values to
+     * be casted to expressions, out of the list of type names
+     * passed as parameter.
+     *
+     * @param array $types List of type names
+     * @return array
+     */
+    protected function _requiresToExpressionCasting(array $types): array
+    {
+        $result = [];
+        $types = array_filter($types);
+        foreach ($types as $k => $type) {
+            $object = TypeFactory::build($type);
+            if ($object instanceof ExpressionTypeInterface) {
+                $result[$k] = $object;
+            }
+        }
+
+        return $result;
+    }
+}
diff --git a/vendor/cakephp/database/Type/ExpressionTypeInterface.php b/vendor/cakephp/database/Type/ExpressionTypeInterface.php
new file mode 100644
index 0000000..7615cf9
--- /dev/null
+++ b/vendor/cakephp/database/Type/ExpressionTypeInterface.php
@@ -0,0 +1,36 @@
+_useLocaleParser) {
+            return $this->_parseValue($value);
+        }
+        if (is_numeric($value)) {
+            return (float)$value;
+        }
+        if (is_string($value) && preg_match('/^[0-9,. ]+$/', $value)) {
+            return $value;
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets whether to parse numbers passed to the marshal() function
+     * by using a locale aware parser.
+     *
+     * @param bool $enable Whether to enable
+     * @return $this
+     */
+    public function useLocaleParser(bool $enable = true)
+    {
+        if ($enable === false) {
+            $this->_useLocaleParser = $enable;
+
+            return $this;
+        }
+        if (
+            static::$numberClass === Number::class ||
+            is_subclass_of(static::$numberClass, Number::class)
+        ) {
+            $this->_useLocaleParser = $enable;
+
+            return $this;
+        }
+        throw new RuntimeException(
+            sprintf('Cannot use locale parsing with the %s class', static::$numberClass)
+        );
+    }
+
+    /**
+     * Converts a string into a float point after parsing it using the locale
+     * aware parser.
+     *
+     * @param string $value The value to parse and convert to an float.
+     * @return float
+     */
+    protected function _parseValue(string $value): float
+    {
+        $class = static::$numberClass;
+
+        return $class::parseFloat($value);
+    }
+}
diff --git a/vendor/cakephp/database/Type/IntegerType.php b/vendor/cakephp/database/Type/IntegerType.php
new file mode 100644
index 0000000..973d7ca
--- /dev/null
+++ b/vendor/cakephp/database/Type/IntegerType.php
@@ -0,0 +1,129 @@
+checkNumeric($value);
+
+        return (int)$value;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param mixed $value The value to convert.
+     * @param \Cake\Database\DriverInterface $driver The driver instance to convert with.
+     * @return int|null
+     */
+    public function toPHP($value, DriverInterface $driver): ?int
+    {
+        if ($value === null) {
+            return null;
+        }
+
+        return (int)$value;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function manyToPHP(array $values, array $fields, DriverInterface $driver): array
+    {
+        foreach ($fields as $field) {
+            if (!isset($values[$field])) {
+                continue;
+            }
+
+            $this->checkNumeric($values[$field]);
+
+            $values[$field] = (int)$values[$field];
+        }
+
+        return $values;
+    }
+
+    /**
+     * Get the correct PDO binding type for integer data.
+     *
+     * @param mixed $value The value being bound.
+     * @param \Cake\Database\DriverInterface $driver The driver.
+     * @return int
+     */
+    public function toStatement($value, DriverInterface $driver): int
+    {
+        return PDO::PARAM_INT;
+    }
+
+    /**
+     * Marshals request data into PHP integers.
+     *
+     * @param mixed $value The value to convert.
+     * @return int|null Converted value.
+     */
+    public function marshal($value): ?int
+    {
+        if ($value === null || $value === '') {
+            return null;
+        }
+        if (is_numeric($value)) {
+            return (int)$value;
+        }
+
+        return null;
+    }
+}
diff --git a/vendor/cakephp/database/Type/JsonType.php b/vendor/cakephp/database/Type/JsonType.php
new file mode 100644
index 0000000..5648d94
--- /dev/null
+++ b/vendor/cakephp/database/Type/JsonType.php
@@ -0,0 +1,124 @@
+_encodingOptions);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param mixed $value The value to convert.
+     * @param \Cake\Database\DriverInterface $driver The driver instance to convert with.
+     * @return array|string|null
+     */
+    public function toPHP($value, DriverInterface $driver)
+    {
+        if (!is_string($value)) {
+            return null;
+        }
+
+        return json_decode($value, true);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function manyToPHP(array $values, array $fields, DriverInterface $driver): array
+    {
+        foreach ($fields as $field) {
+            if (!isset($values[$field])) {
+                continue;
+            }
+
+            $values[$field] = json_decode($values[$field], true);
+        }
+
+        return $values;
+    }
+
+    /**
+     * Get the correct PDO binding type for string data.
+     *
+     * @param mixed $value The value being bound.
+     * @param \Cake\Database\DriverInterface $driver The driver.
+     * @return int
+     */
+    public function toStatement($value, DriverInterface $driver): int
+    {
+        return PDO::PARAM_STR;
+    }
+
+    /**
+     * Marshals request data into a JSON compatible structure.
+     *
+     * @param mixed $value The value to convert.
+     * @return mixed Converted value.
+     */
+    public function marshal($value)
+    {
+        return $value;
+    }
+
+    /**
+     * Set json_encode options.
+     *
+     * @param int $options Encoding flags. Use JSON_* flags. Set `0` to reset.
+     * @return $this
+     * @see https://www.php.net/manual/en/function.json-encode.php
+     */
+    public function setEncodingOptions(int $options)
+    {
+        $this->_encodingOptions = $options;
+
+        return $this;
+    }
+}
diff --git a/vendor/cakephp/database/Type/OptionalConvertInterface.php b/vendor/cakephp/database/Type/OptionalConvertInterface.php
new file mode 100644
index 0000000..512683b
--- /dev/null
+++ b/vendor/cakephp/database/Type/OptionalConvertInterface.php
@@ -0,0 +1,32 @@
+__toString();
+        }
+
+        if (is_scalar($value)) {
+            return (string)$value;
+        }
+
+        throw new InvalidArgumentException(sprintf(
+            'Cannot convert value of type `%s` to string',
+            getTypeName($value)
+        ));
+    }
+
+    /**
+     * Convert string values to PHP strings.
+     *
+     * @param mixed $value The value to convert.
+     * @param \Cake\Database\DriverInterface $driver The driver instance to convert with.
+     * @return string|null
+     */
+    public function toPHP($value, DriverInterface $driver): ?string
+    {
+        if ($value === null) {
+            return null;
+        }
+
+        return (string)$value;
+    }
+
+    /**
+     * Get the correct PDO binding type for string data.
+     *
+     * @param mixed $value The value being bound.
+     * @param \Cake\Database\DriverInterface $driver The driver.
+     * @return int
+     */
+    public function toStatement($value, DriverInterface $driver): int
+    {
+        return PDO::PARAM_STR;
+    }
+
+    /**
+     * Marshals request data into PHP strings.
+     *
+     * @param mixed $value The value to convert.
+     * @return string|null Converted value.
+     */
+    public function marshal($value): ?string
+    {
+        if ($value === null || is_array($value)) {
+            return null;
+        }
+
+        return (string)$value;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return bool False as database results are returned already as strings
+     */
+    public function requiresToPhpCast(): bool
+    {
+        return false;
+    }
+}
diff --git a/vendor/cakephp/database/Type/TimeType.php b/vendor/cakephp/database/Type/TimeType.php
new file mode 100644
index 0000000..4c021e2
--- /dev/null
+++ b/vendor/cakephp/database/Type/TimeType.php
@@ -0,0 +1,52 @@
+ $class */
+        $class = $this->_className;
+
+        /** @psalm-suppress PossiblyInvalidArgument */
+        return $class::parseTime($value, $this->_localeMarshalFormat);
+    }
+}
diff --git a/vendor/cakephp/database/Type/UuidType.php b/vendor/cakephp/database/Type/UuidType.php
new file mode 100644
index 0000000..ce7f754
--- /dev/null
+++ b/vendor/cakephp/database/Type/UuidType.php
@@ -0,0 +1,67 @@
+toDatabase($value, $this->getDriver());
+            $type = $type->toStatement($value, $this->getDriver());
+        }
+
+        return [$value, $type];
+    }
+
+    /**
+     * Matches columns to corresponding types
+     *
+     * Both $columns and $types should either be numeric based or string key based at
+     * the same time.
+     *
+     * @param array $columns list or associative array of columns and parameters to be bound with types
+     * @param array $types list or associative array of types
+     * @return array
+     */
+    public function matchTypes(array $columns, array $types): array
+    {
+        if (!is_int(key($types))) {
+            $positions = array_intersect_key(array_flip($columns), $types);
+            $types = array_intersect_key($types, $positions);
+            $types = array_combine($positions, $types);
+        }
+
+        return $types;
+    }
+}
diff --git a/vendor/cakephp/database/TypeFactory.php b/vendor/cakephp/database/TypeFactory.php
new file mode 100644
index 0000000..617bad6
--- /dev/null
+++ b/vendor/cakephp/database/TypeFactory.php
@@ -0,0 +1,171 @@
+
+     * @psalm-var array>
+     */
+    protected static $_types = [
+        'tinyinteger' => Type\IntegerType::class,
+        'smallinteger' => Type\IntegerType::class,
+        'integer' => Type\IntegerType::class,
+        'biginteger' => Type\IntegerType::class,
+        'binary' => Type\BinaryType::class,
+        'binaryuuid' => Type\BinaryUuidType::class,
+        'boolean' => Type\BoolType::class,
+        'date' => Type\DateType::class,
+        'datetime' => Type\DateTimeType::class,
+        'datetimefractional' => Type\DateTimeFractionalType::class,
+        'decimal' => Type\DecimalType::class,
+        'float' => Type\FloatType::class,
+        'json' => Type\JsonType::class,
+        'string' => Type\StringType::class,
+        'char' => Type\StringType::class,
+        'text' => Type\StringType::class,
+        'time' => Type\TimeType::class,
+        'timestamp' => Type\DateTimeType::class,
+        'timestampfractional' => Type\DateTimeFractionalType::class,
+        'timestamptimezone' => Type\DateTimeTimezoneType::class,
+        'uuid' => Type\UuidType::class,
+    ];
+
+    /**
+     * Contains a map of type object instances to be reused if needed.
+     *
+     * @var array<\Cake\Database\TypeInterface>
+     */
+    protected static $_builtTypes = [];
+
+    /**
+     * Returns a Type object capable of converting a type identified by name.
+     *
+     * @param string $name type identifier
+     * @throws \InvalidArgumentException If type identifier is unknown
+     * @return \Cake\Database\TypeInterface
+     */
+    public static function build(string $name): TypeInterface
+    {
+        if (isset(static::$_builtTypes[$name])) {
+            return static::$_builtTypes[$name];
+        }
+        if (!isset(static::$_types[$name])) {
+            throw new InvalidArgumentException(sprintf('Unknown type "%s"', $name));
+        }
+
+        return static::$_builtTypes[$name] = new static::$_types[$name]($name);
+    }
+
+    /**
+     * Returns an arrays with all the mapped type objects, indexed by name.
+     *
+     * @return array<\Cake\Database\TypeInterface>
+     */
+    public static function buildAll(): array
+    {
+        $result = [];
+        foreach (static::$_types as $name => $type) {
+            $result[$name] = static::$_builtTypes[$name] ?? static::build($name);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Set TypeInterface instance capable of converting a type identified by $name
+     *
+     * @param string $name The type identifier you want to set.
+     * @param \Cake\Database\TypeInterface $instance The type instance you want to set.
+     * @return void
+     */
+    public static function set(string $name, TypeInterface $instance): void
+    {
+        static::$_builtTypes[$name] = $instance;
+        static::$_types[$name] = get_class($instance);
+    }
+
+    /**
+     * Registers a new type identifier and maps it to a fully namespaced classname.
+     *
+     * @param string $type Name of type to map.
+     * @param string $className The classname to register.
+     * @return void
+     * @psalm-param class-string<\Cake\Database\TypeInterface> $className
+     */
+    public static function map(string $type, string $className): void
+    {
+        static::$_types[$type] = $className;
+        unset(static::$_builtTypes[$type]);
+    }
+
+    /**
+     * Set type to classname mapping.
+     *
+     * @param array $map List of types to be mapped.
+     * @return void
+     * @psalm-param array> $map
+     */
+    public static function setMap(array $map): void
+    {
+        static::$_types = $map;
+        static::$_builtTypes = [];
+    }
+
+    /**
+     * Get mapped class name for given type or map array.
+     *
+     * @param string|null $type Type name to get mapped class for or null to get map array.
+     * @return array|string|null Configured class name for given $type or map array.
+     */
+    public static function getMap(?string $type = null)
+    {
+        if ($type === null) {
+            return static::$_types;
+        }
+
+        return static::$_types[$type] ?? null;
+    }
+
+    /**
+     * Clears out all created instances and mapped types classes, useful for testing
+     *
+     * @return void
+     */
+    public static function clear(): void
+    {
+        static::$_types = [];
+        static::$_builtTypes = [];
+    }
+}
+
+// phpcs:disable
+class_alias(
+    'Cake\Database\TypeFactory',
+    'Cake\Database\Type'
+);
+// phpcs:enable
diff --git a/vendor/cakephp/database/TypeInterface.php b/vendor/cakephp/database/TypeInterface.php
new file mode 100644
index 0000000..fca0fdb
--- /dev/null
+++ b/vendor/cakephp/database/TypeInterface.php
@@ -0,0 +1,91 @@
+
+     */
+    protected $_defaults = [];
+
+    /**
+     * Array with the fields and the related types that override defaults this query might contain
+     *
+     * Used to avoid repetition when calling multiple functions inside this class that
+     * may require a custom type for a specific field.
+     *
+     * @var array
+     */
+    protected $_types = [];
+
+    /**
+     * Creates an instance with the given defaults
+     *
+     * @param array $defaults The defaults to use.
+     */
+    public function __construct(array $defaults = [])
+    {
+        $this->setDefaults($defaults);
+    }
+
+    /**
+     * Configures a map of fields and associated type.
+     *
+     * These values will be used as the default mapping of types for every function
+     * in this instance that supports a `$types` param.
+     *
+     * This method is useful when you want to avoid repeating type definitions
+     * as setting types overwrites the last set of types.
+     *
+     * ### Example
+     *
+     * ```
+     * $query->setDefaults(['created' => 'datetime', 'is_visible' => 'boolean']);
+     * ```
+     *
+     * This method will replace all the existing default mappings with the ones provided.
+     * To add into the mappings use `addDefaults()`.
+     *
+     * @param array $defaults Array where keys are field names / positions and values
+     * are the correspondent type.
+     * @return $this
+     */
+    public function setDefaults(array $defaults)
+    {
+        $this->_defaults = $defaults;
+
+        return $this;
+    }
+
+    /**
+     * Returns the currently configured types.
+     *
+     * @return array
+     */
+    public function getDefaults(): array
+    {
+        return $this->_defaults;
+    }
+
+    /**
+     * Add additional default types into the type map.
+     *
+     * If a key already exists it will not be overwritten.
+     *
+     * @param array $types The additional types to add.
+     * @return void
+     */
+    public function addDefaults(array $types): void
+    {
+        $this->_defaults += $types;
+    }
+
+    /**
+     * Sets a map of fields and their associated types for single-use.
+     *
+     * ### Example
+     *
+     * ```
+     * $query->setTypes(['created' => 'time']);
+     * ```
+     *
+     * This method will replace all the existing type maps with the ones provided.
+     *
+     * @param array $types Array where keys are field names / positions and values
+     * are the correspondent type.
+     * @return $this
+     */
+    public function setTypes(array $types)
+    {
+        $this->_types = $types;
+
+        return $this;
+    }
+
+    /**
+     * Gets a map of fields and their associated types for single-use.
+     *
+     * @return array
+     */
+    public function getTypes(): array
+    {
+        return $this->_types;
+    }
+
+    /**
+     * Returns the type of the given column. If there is no single use type is configured,
+     * the column type will be looked for inside the default mapping. If neither exist,
+     * null will be returned.
+     *
+     * @param string|int $column The type for a given column
+     * @return string|null
+     */
+    public function type($column): ?string
+    {
+        return $this->_types[$column] ?? $this->_defaults[$column] ?? null;
+    }
+
+    /**
+     * Returns an array of all types mapped types
+     *
+     * @return array
+     */
+    public function toArray(): array
+    {
+        return $this->_types + $this->_defaults;
+    }
+}
diff --git a/vendor/cakephp/database/TypeMapTrait.php b/vendor/cakephp/database/TypeMapTrait.php
new file mode 100644
index 0000000..402a9b5
--- /dev/null
+++ b/vendor/cakephp/database/TypeMapTrait.php
@@ -0,0 +1,89 @@
+_typeMap = is_array($typeMap) ? new TypeMap($typeMap) : $typeMap;
+
+        return $this;
+    }
+
+    /**
+     * Returns the existing type map.
+     *
+     * @return \Cake\Database\TypeMap
+     */
+    public function getTypeMap(): TypeMap
+    {
+        if ($this->_typeMap === null) {
+            $this->_typeMap = new TypeMap();
+        }
+
+        return $this->_typeMap;
+    }
+
+    /**
+     * Overwrite the default type mappings for fields
+     * in the implementing object.
+     *
+     * This method is useful if you need to set type mappings that are shared across
+     * multiple functions/expressions in a query.
+     *
+     * To add a default without overwriting existing ones
+     * use `getTypeMap()->addDefaults()`
+     *
+     * @param array $types The array of types to set.
+     * @return $this
+     * @see \Cake\Database\TypeMap::setDefaults()
+     */
+    public function setDefaultTypes(array $types)
+    {
+        $this->getTypeMap()->setDefaults($types);
+
+        return $this;
+    }
+
+    /**
+     * Gets default types of current type map.
+     *
+     * @return array
+     */
+    public function getDefaultTypes(): array
+    {
+        return $this->getTypeMap()->getDefaults();
+    }
+}
diff --git a/vendor/cakephp/database/TypedResultInterface.php b/vendor/cakephp/database/TypedResultInterface.php
new file mode 100644
index 0000000..434ff08
--- /dev/null
+++ b/vendor/cakephp/database/TypedResultInterface.php
@@ -0,0 +1,38 @@
+_returnType;
+    }
+
+    /**
+     * Sets the type of the value this object will generate.
+     *
+     * @param string $type The name of the type that is to be returned
+     * @return $this
+     */
+    public function setReturnType(string $type)
+    {
+        $this->_returnType = $type;
+
+        return $this;
+    }
+}
diff --git a/vendor/cakephp/database/ValueBinder.php b/vendor/cakephp/database/ValueBinder.php
new file mode 100644
index 0000000..0e5dd3d
--- /dev/null
+++ b/vendor/cakephp/database/ValueBinder.php
@@ -0,0 +1,163 @@
+_bindings[$param] = compact('value', 'type') + [
+            'placeholder' => is_int($param) ? $param : substr($param, 1),
+        ];
+    }
+
+    /**
+     * Creates a unique placeholder name if the token provided does not start with ":"
+     * otherwise, it will return the same string and internally increment the number
+     * of placeholders generated by this object.
+     *
+     * @param string $token string from which the placeholder will be derived from,
+     * if it starts with a colon, then the same string is returned
+     * @return string to be used as a placeholder in a query expression
+     */
+    public function placeholder(string $token): string
+    {
+        $number = $this->_bindingsCount++;
+        if ($token[0] !== ':' && $token !== '?') {
+            $token = sprintf(':%s%s', $token, $number);
+        }
+
+        return $token;
+    }
+
+    /**
+     * Creates unique named placeholders for each of the passed values
+     * and binds them with the specified type.
+     *
+     * @param iterable $values The list of values to be bound
+     * @param string|int|null $type The type with which all values will be bound
+     * @return array with the placeholders to insert in the query
+     */
+    public function generateManyNamed(iterable $values, $type = null): array
+    {
+        $placeholders = [];
+        foreach ($values as $k => $value) {
+            $param = $this->placeholder('c');
+            $this->_bindings[$param] = [
+                'value' => $value,
+                'type' => $type,
+                'placeholder' => substr($param, 1),
+            ];
+            $placeholders[$k] = $param;
+        }
+
+        return $placeholders;
+    }
+
+    /**
+     * Returns all values bound to this expression object at this nesting level.
+     * Subexpression bound values will not be returned with this function.
+     *
+     * @return array
+     */
+    public function bindings(): array
+    {
+        return $this->_bindings;
+    }
+
+    /**
+     * Clears any bindings that were previously registered
+     *
+     * @return void
+     */
+    public function reset(): void
+    {
+        $this->_bindings = [];
+        $this->_bindingsCount = 0;
+    }
+
+    /**
+     * Resets the bindings count without clearing previously bound values
+     *
+     * @return void
+     */
+    public function resetCount(): void
+    {
+        $this->_bindingsCount = 0;
+    }
+
+    /**
+     * Binds all the stored values in this object to the passed statement.
+     *
+     * @param \Cake\Database\StatementInterface $statement The statement to add parameters to.
+     * @return void
+     */
+    public function attachTo(StatementInterface $statement): void
+    {
+        $bindings = $this->bindings();
+        if (empty($bindings)) {
+            return;
+        }
+
+        foreach ($bindings as $b) {
+            $statement->bindValue($b['placeholder'], $b['value'], $b['type']);
+        }
+    }
+
+    /**
+     * Get verbose debugging data.
+     *
+     * @return array
+     */
+    public function __debugInfo(): array
+    {
+        return [
+            'bindings' => $this->bindings(),
+        ];
+    }
+}
diff --git a/vendor/cakephp/database/composer.json b/vendor/cakephp/database/composer.json
new file mode 100644
index 0000000..64c22e8
--- /dev/null
+++ b/vendor/cakephp/database/composer.json
@@ -0,0 +1,40 @@
+{
+    "name": "cakephp/database",
+    "description": "Flexible and powerful Database abstraction library with a familiar PDO-like API",
+    "type": "library",
+    "keywords": [
+        "cakephp",
+        "database",
+        "abstraction",
+        "database abstraction",
+        "pdo"
+    ],
+    "homepage": "https://cakephp.org",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "CakePHP Community",
+            "homepage": "https://github.com/cakephp/database/graphs/contributors"
+        }
+    ],
+    "support": {
+        "issues": "https://github.com/cakephp/cakephp/issues",
+        "forum": "https://stackoverflow.com/tags/cakephp",
+        "irc": "irc://irc.freenode.org/cakephp",
+        "source": "https://github.com/cakephp/database"
+    },
+    "require": {
+        "php": ">=7.4.0",
+        "cakephp/core": "^4.0",
+        "cakephp/datasource": "^4.0"
+    },
+    "suggest": {
+        "cakephp/i18n": "If you are using locale-aware datetime formats or Chronos types.",
+        "cakephp/log": "If you want to use query logging without providing a logger yourself."
+    },
+    "autoload": {
+        "psr-4": {
+            "Cake\\Database\\": "."
+        }
+    }
+}
diff --git a/vendor/cakephp/datasource/ConnectionInterface.php b/vendor/cakephp/datasource/ConnectionInterface.php
new file mode 100644
index 0000000..7efef43
--- /dev/null
+++ b/vendor/cakephp/datasource/ConnectionInterface.php
@@ -0,0 +1,154 @@
+
+     */
+    public function config(): array;
+
+    /**
+     * Executes a callable function inside a transaction, if any exception occurs
+     * while executing the passed callable, the transaction will be rolled back
+     * If the result of the callable function is `false`, the transaction will
+     * also be rolled back. Otherwise, the transaction is committed after executing
+     * the callback.
+     *
+     * The callback will receive the connection instance as its first argument.
+     *
+     * ### Example:
+     *
+     * ```
+     * $connection->transactional(function ($connection) {
+     *   $connection->newQuery()->delete('users')->execute();
+     * });
+     * ```
+     *
+     * @param callable $callback The callback to execute within a transaction.
+     * @return mixed The return value of the callback.
+     * @throws \Exception Will re-throw any exception raised in $callback after
+     *   rolling back the transaction.
+     */
+    public function transactional(callable $callback);
+
+    /**
+     * Run an operation with constraints disabled.
+     *
+     * Constraints should be re-enabled after the callback succeeds/fails.
+     *
+     * ### Example:
+     *
+     * ```
+     * $connection->disableConstraints(function ($connection) {
+     *   $connection->newQuery()->delete('users')->execute();
+     * });
+     * ```
+     *
+     * @param callable $callback The callback to execute within a transaction.
+     * @return mixed The return value of the callback.
+     * @throws \Exception Will re-throw any exception raised in $callback after
+     *   rolling back the transaction.
+     */
+    public function disableConstraints(callable $callback);
+
+    /**
+     * Enable/disable query logging
+     *
+     * @param bool $enable Enable/disable query logging
+     * @return $this
+     */
+    public function enableQueryLogging(bool $enable = true);
+
+    /**
+     * Disable query logging
+     *
+     * @return $this
+     */
+    public function disableQueryLogging();
+
+    /**
+     * Check if query logging is enabled.
+     *
+     * @return bool
+     */
+    public function isQueryLoggingEnabled(): bool;
+}
diff --git a/vendor/cakephp/datasource/ConnectionManager.php b/vendor/cakephp/datasource/ConnectionManager.php
new file mode 100644
index 0000000..8f244ea
--- /dev/null
+++ b/vendor/cakephp/datasource/ConnectionManager.php
@@ -0,0 +1,216 @@
+
+     */
+    protected static $_aliasMap = [];
+
+    /**
+     * An array mapping url schemes to fully qualified driver class names
+     *
+     * @var array
+     * @psalm-var array
+     */
+    protected static $_dsnClassMap = [
+        'mysql' => Mysql::class,
+        'postgres' => Postgres::class,
+        'sqlite' => Sqlite::class,
+        'sqlserver' => Sqlserver::class,
+    ];
+
+    /**
+     * The ConnectionRegistry used by the manager.
+     *
+     * @var \Cake\Datasource\ConnectionRegistry|null
+     */
+    protected static $_registry;
+
+    /**
+     * Configure a new connection object.
+     *
+     * The connection will not be constructed until it is first used.
+     *
+     * @param array|string $key The name of the connection config, or an array of multiple configs.
+     * @param array|null $config An array of name => config data for adapter.
+     * @return void
+     * @throws \Cake\Core\Exception\CakeException When trying to modify an existing config.
+     * @see \Cake\Core\StaticConfigTrait::config()
+     */
+    public static function setConfig($key, $config = null): void
+    {
+        if (is_array($config)) {
+            $config['name'] = $key;
+        }
+
+        static::_setConfig($key, $config);
+    }
+
+    /**
+     * Parses a DSN into a valid connection configuration
+     *
+     * This method allows setting a DSN using formatting similar to that used by PEAR::DB.
+     * The following is an example of its usage:
+     *
+     * ```
+     * $dsn = 'mysql://user:pass@localhost/database';
+     * $config = ConnectionManager::parseDsn($dsn);
+     *
+     * $dsn = 'Cake\Database\Driver\Mysql://localhost:3306/database?className=Cake\Database\Connection';
+     * $config = ConnectionManager::parseDsn($dsn);
+     *
+     * $dsn = 'Cake\Database\Connection://localhost:3306/database?driver=Cake\Database\Driver\Mysql';
+     * $config = ConnectionManager::parseDsn($dsn);
+     * ```
+     *
+     * For all classes, the value of `scheme` is set as the value of both the `className` and `driver`
+     * unless they have been otherwise specified.
+     *
+     * Note that query-string arguments are also parsed and set as values in the returned configuration.
+     *
+     * @param string $config The DSN string to convert to a configuration array
+     * @return array The configuration array to be stored after parsing the DSN
+     */
+    public static function parseDsn(string $config): array
+    {
+        $config = static::_parseDsn($config);
+
+        if (isset($config['path']) && empty($config['database'])) {
+            $config['database'] = substr($config['path'], 1);
+        }
+
+        if (empty($config['driver'])) {
+            $config['driver'] = $config['className'];
+            $config['className'] = Connection::class;
+        }
+
+        unset($config['path']);
+
+        return $config;
+    }
+
+    /**
+     * Set one or more connection aliases.
+     *
+     * Connection aliases allow you to rename active connections without overwriting
+     * the aliased connection. This is most useful in the test-suite for replacing
+     * connections with their test variant.
+     *
+     * Defined aliases will take precedence over normal connection names. For example,
+     * if you alias 'default' to 'test', fetching 'default' will always return the 'test'
+     * connection as long as the alias is defined.
+     *
+     * You can remove aliases with ConnectionManager::dropAlias().
+     *
+     * ### Usage
+     *
+     * ```
+     * // Make 'things' resolve to 'test_things' connection
+     * ConnectionManager::alias('test_things', 'things');
+     * ```
+     *
+     * @param string $source The existing connection to alias.
+     * @param string $alias The alias name that resolves to `$source`.
+     * @return void
+     */
+    public static function alias(string $source, string $alias): void
+    {
+        static::$_aliasMap[$alias] = $source;
+    }
+
+    /**
+     * Drop an alias.
+     *
+     * Removes an alias from ConnectionManager. Fetching the aliased
+     * connection may fail if there is no other connection with that name.
+     *
+     * @param string $alias The connection alias to drop
+     * @return void
+     */
+    public static function dropAlias(string $alias): void
+    {
+        unset(static::$_aliasMap[$alias]);
+    }
+
+    /**
+     * Returns the current connection aliases and what they alias.
+     *
+     * @return array
+     */
+    public static function aliases(): array
+    {
+        return static::$_aliasMap;
+    }
+
+    /**
+     * Get a connection.
+     *
+     * If the connection has not been constructed an instance will be added
+     * to the registry. This method will use any aliases that have been
+     * defined. If you want the original unaliased connections pass `false`
+     * as second parameter.
+     *
+     * @param string $name The connection name.
+     * @param bool $useAliases Whether connection aliases are used
+     * @return \Cake\Datasource\ConnectionInterface
+     * @throws \Cake\Datasource\Exception\MissingDatasourceConfigException When config
+     * data is missing.
+     */
+    public static function get(string $name, bool $useAliases = true)
+    {
+        if ($useAliases && isset(static::$_aliasMap[$name])) {
+            $name = static::$_aliasMap[$name];
+        }
+
+        if (!isset(static::$_config[$name])) {
+            throw new MissingDatasourceConfigException(['name' => $name]);
+        }
+
+        if (!isset(static::$_registry)) {
+            static::$_registry = new ConnectionRegistry();
+        }
+
+        return static::$_registry->{$name} ?? static::$_registry->load($name, static::$_config[$name]);
+    }
+}
diff --git a/vendor/cakephp/datasource/ConnectionRegistry.php b/vendor/cakephp/datasource/ConnectionRegistry.php
new file mode 100644
index 0000000..34a5aca
--- /dev/null
+++ b/vendor/cakephp/datasource/ConnectionRegistry.php
@@ -0,0 +1,104 @@
+
+ */
+class ConnectionRegistry extends ObjectRegistry
+{
+    /**
+     * Resolve a datasource classname.
+     *
+     * Part of the template method for Cake\Core\ObjectRegistry::load()
+     *
+     * @param string $class Partial classname to resolve.
+     * @return string|null Either the correct class name or null.
+     * @psalm-return class-string|null
+     */
+    protected function _resolveClassName(string $class): ?string
+    {
+        return App::className($class, 'Datasource');
+    }
+
+    /**
+     * Throws an exception when a datasource is missing
+     *
+     * Part of the template method for Cake\Core\ObjectRegistry::load()
+     *
+     * @param string $class The classname that is missing.
+     * @param string|null $plugin The plugin the datasource is missing in.
+     * @return void
+     * @throws \Cake\Datasource\Exception\MissingDatasourceException
+     */
+    protected function _throwMissingClassError(string $class, ?string $plugin): void
+    {
+        throw new MissingDatasourceException([
+            'class' => $class,
+            'plugin' => $plugin,
+        ]);
+    }
+
+    /**
+     * Create the connection object with the correct settings.
+     *
+     * Part of the template method for Cake\Core\ObjectRegistry::load()
+     *
+     * If a callable is passed as first argument, The returned value of this
+     * function will be the result of the callable.
+     *
+     * @param \Cake\Datasource\ConnectionInterface|callable|string $class The classname or object to make.
+     * @param string $alias The alias of the object.
+     * @param array $config An array of settings to use for the datasource.
+     * @return \Cake\Datasource\ConnectionInterface A connection with the correct settings.
+     */
+    protected function _create($class, string $alias, array $config)
+    {
+        if (is_callable($class)) {
+            return $class($alias);
+        }
+
+        if (is_object($class)) {
+            return $class;
+        }
+
+        unset($config['className']);
+
+        /** @var \Cake\Datasource\ConnectionInterface */
+        return new $class($config);
+    }
+
+    /**
+     * Remove a single adapter from the registry.
+     *
+     * @param string $name The adapter name.
+     * @return $this
+     */
+    public function unload(string $name)
+    {
+        unset($this->_loaded[$name]);
+
+        return $this;
+    }
+}
diff --git a/vendor/cakephp/datasource/EntityInterface.php b/vendor/cakephp/datasource/EntityInterface.php
new file mode 100644
index 0000000..5887670
--- /dev/null
+++ b/vendor/cakephp/datasource/EntityInterface.php
@@ -0,0 +1,288 @@
+
+ */
+interface EntityInterface extends ArrayAccess, JsonSerializable
+{
+    /**
+     * Sets hidden fields.
+     *
+     * @param array $fields An array of fields to hide from array exports.
+     * @param bool $merge Merge the new fields with the existing. By default false.
+     * @return $this
+     */
+    public function setHidden(array $fields, bool $merge = false);
+
+    /**
+     * Gets the hidden fields.
+     *
+     * @return array
+     */
+    public function getHidden(): array;
+
+    /**
+     * Sets the virtual fields on this entity.
+     *
+     * @param array $fields An array of fields to treat as virtual.
+     * @param bool $merge Merge the new fields with the existing. By default false.
+     * @return $this
+     */
+    public function setVirtual(array $fields, bool $merge = false);
+
+    /**
+     * Gets the virtual fields on this entity.
+     *
+     * @return array
+     */
+    public function getVirtual(): array;
+
+    /**
+     * Sets the dirty status of a single field.
+     *
+     * @param string $field the field to set or check status for
+     * @param bool $isDirty true means the field was changed, false means
+     * it was not changed. Default true.
+     * @return $this
+     */
+    public function setDirty(string $field, bool $isDirty = true);
+
+    /**
+     * Checks if the entity is dirty or if a single field of it is dirty.
+     *
+     * @param string|null $field The field to check the status for. Null for the whole entity.
+     * @return bool Whether the field was changed or not
+     */
+    public function isDirty(?string $field = null): bool;
+
+    /**
+     * Gets the dirty fields.
+     *
+     * @return array
+     */
+    public function getDirty(): array;
+
+    /**
+     * Returns whether this entity has errors.
+     *
+     * @param bool $includeNested true will check nested entities for hasErrors()
+     * @return bool
+     */
+    public function hasErrors(bool $includeNested = true): bool;
+
+    /**
+     * Returns all validation errors.
+     *
+     * @return array
+     */
+    public function getErrors(): array;
+
+    /**
+     * Returns validation errors of a field
+     *
+     * @param string $field Field name to get the errors from
+     * @return array
+     */
+    public function getError(string $field): array;
+
+    /**
+     * Sets error messages to the entity
+     *
+     * @param array $errors The array of errors to set.
+     * @param bool $overwrite Whether to overwrite pre-existing errors for $fields
+     * @return $this
+     */
+    public function setErrors(array $errors, bool $overwrite = false);
+
+    /**
+     * Sets errors for a single field
+     *
+     * @param string $field The field to get errors for, or the array of errors to set.
+     * @param array|string $errors The errors to be set for $field
+     * @param bool $overwrite Whether to overwrite pre-existing errors for $field
+     * @return $this
+     */
+    public function setError(string $field, $errors, bool $overwrite = false);
+
+    /**
+     * Stores whether a field value can be changed or set in this entity.
+     *
+     * @param array|string $field single or list of fields to change its accessibility
+     * @param bool $set true marks the field as accessible, false will
+     * mark it as protected.
+     * @return $this
+     */
+    public function setAccess($field, bool $set);
+
+    /**
+     * Checks if a field is accessible
+     *
+     * @param string $field Field name to check
+     * @return bool
+     */
+    public function isAccessible(string $field): bool;
+
+    /**
+     * Sets the source alias
+     *
+     * @param string $alias the alias of the repository
+     * @return $this
+     */
+    public function setSource(string $alias);
+
+    /**
+     * Returns the alias of the repository from which this entity came from.
+     *
+     * @return string
+     */
+    public function getSource(): string;
+
+    /**
+     * Returns an array with the requested original fields
+     * stored in this entity, indexed by field name.
+     *
+     * @param array $fields List of fields to be returned
+     * @return array
+     */
+    public function extractOriginal(array $fields): array;
+
+    /**
+     * Returns an array with only the original fields
+     * stored in this entity, indexed by field name.
+     *
+     * @param array $fields List of fields to be returned
+     * @return array
+     */
+    public function extractOriginalChanged(array $fields): array;
+
+    /**
+     * Sets one or multiple fields to the specified value
+     *
+     * @param array|string $field the name of field to set or a list of
+     * fields with their respective values
+     * @param mixed $value The value to set to the field or an array if the
+     * first argument is also an array, in which case will be treated as $options
+     * @param array $options Options to be used for setting the field. Allowed option
+     * keys are `setter` and `guard`
+     * @return $this
+     */
+    public function set($field, $value = null, array $options = []);
+
+    /**
+     * Returns the value of a field by name
+     *
+     * @param string $field the name of the field to retrieve
+     * @return mixed
+     */
+    public function &get(string $field);
+
+    /**
+     * Returns the original value of a field.
+     *
+     * @param string $field The name of the field.
+     * @return mixed
+     */
+    public function getOriginal(string $field);
+
+    /**
+     * Gets all original values of the entity.
+     *
+     * @return array
+     */
+    public function getOriginalValues(): array;
+
+    /**
+     * Returns whether this entity contains a field named $field
+     * and is not set to null.
+     *
+     * @param array|string $field The field to check.
+     * @return bool
+     */
+    public function has($field): bool;
+
+    /**
+     * Removes a field or list of fields from this entity
+     *
+     * @param array|string $field The field to unset.
+     * @return $this
+     */
+    public function unset($field);
+
+    /**
+     * Get the list of visible fields.
+     *
+     * @return array A list of fields that are 'visible' in all representations.
+     */
+    public function getVisible(): array;
+
+    /**
+     * Returns an array with all the visible fields set in this entity.
+     *
+     * *Note* hidden fields are not visible, and will not be output
+     * by toArray().
+     *
+     * @return array
+     */
+    public function toArray(): array;
+
+    /**
+     * Returns an array with the requested fields
+     * stored in this entity, indexed by field name
+     *
+     * @param array $fields list of fields to be returned
+     * @param bool $onlyDirty Return the requested field only if it is dirty
+     * @return array
+     */
+    public function extract(array $fields, bool $onlyDirty = false): array;
+
+    /**
+     * Sets the entire entity as clean, which means that it will appear as
+     * no fields being modified or added at all. This is an useful call
+     * for an initial object hydration
+     *
+     * @return void
+     */
+    public function clean(): void;
+
+    /**
+     * Set the status of this entity.
+     *
+     * Using `true` means that the entity has not been persisted in the database,
+     * `false` indicates that the entity has been persisted.
+     *
+     * @param bool $new Indicate whether this entity has been persisted.
+     * @return $this
+     */
+    public function setNew(bool $new);
+
+    /**
+     * Returns whether this entity has already been persisted.
+     *
+     * @return bool Whether the entity has been persisted.
+     */
+    public function isNew(): bool;
+}
diff --git a/vendor/cakephp/datasource/EntityTrait.php b/vendor/cakephp/datasource/EntityTrait.php
new file mode 100644
index 0000000..3cd54bb
--- /dev/null
+++ b/vendor/cakephp/datasource/EntityTrait.php
@@ -0,0 +1,1304 @@
+
+     */
+    protected $_fields = [];
+
+    /**
+     * Holds all fields that have been changed and their original values for this entity.
+     *
+     * @var array
+     */
+    protected $_original = [];
+
+    /**
+     * List of field names that should **not** be included in JSON or Array
+     * representations of this Entity.
+     *
+     * @var array
+     */
+    protected $_hidden = [];
+
+    /**
+     * List of computed or virtual fields that **should** be included in JSON or array
+     * representations of this Entity. If a field is present in both _hidden and _virtual
+     * the field will **not** be in the array/JSON versions of the entity.
+     *
+     * @var array
+     */
+    protected $_virtual = [];
+
+    /**
+     * Holds a list of the fields that were modified or added after this object
+     * was originally created.
+     *
+     * @var array
+     */
+    protected $_dirty = [];
+
+    /**
+     * Holds a cached list of getters/setters per class
+     *
+     * @var array>>
+     */
+    protected static $_accessors = [];
+
+    /**
+     * Indicates whether this entity is yet to be persisted.
+     * Entities default to assuming they are new. You can use Table::persisted()
+     * to set the new flag on an entity based on records in the database.
+     *
+     * @var bool
+     */
+    protected $_new = true;
+
+    /**
+     * List of errors per field as stored in this object.
+     *
+     * @var array
+     */
+    protected $_errors = [];
+
+    /**
+     * List of invalid fields and their data for errors upon validation/patching.
+     *
+     * @var array
+     */
+    protected $_invalid = [];
+
+    /**
+     * Map of fields in this entity that can be safely assigned, each
+     * field name points to a boolean indicating its status. An empty array
+     * means no fields are accessible
+     *
+     * The special field '\*' can also be mapped, meaning that any other field
+     * not defined in the map will take its value. For example, `'*' => true`
+     * means that any field not defined in the map will be accessible by default
+     *
+     * @var array
+     */
+    protected $_accessible = ['*' => true];
+
+    /**
+     * The alias of the repository this entity came from
+     *
+     * @var string
+     */
+    protected $_registryAlias = '';
+
+    /**
+     * Storing the current visitation status while recursing through entities getting errors.
+     *
+     * @var bool
+     */
+    protected $_hasBeenVisited = false;
+
+    /**
+     * Set to true in your entity's class definition or
+     * via application logic. When true. has() and related
+     * methods will use `array_key_exists` instead of `isset`
+     * to decide if fields are 'defined' in an entity.
+     *
+     * @var bool
+     */
+    protected $_hasAllowsNull = false;
+
+    /**
+     * Magic getter to access fields that have been set in this entity
+     *
+     * @param string $field Name of the field to access
+     * @return mixed
+     */
+    public function &__get(string $field)
+    {
+        return $this->get($field);
+    }
+
+    /**
+     * Magic setter to add or edit a field in this entity
+     *
+     * @param string $field The name of the field to set
+     * @param mixed $value The value to set to the field
+     * @return void
+     */
+    public function __set(string $field, $value): void
+    {
+        $this->set($field, $value);
+    }
+
+    /**
+     * Returns whether this entity contains a field named $field
+     * and is not set to null.
+     *
+     * @param string $field The field to check.
+     * @return bool
+     * @see \Cake\ORM\Entity::has()
+     */
+    public function __isset(string $field): bool
+    {
+        return $this->has($field);
+    }
+
+    /**
+     * Removes a field from this entity
+     *
+     * @param string $field The field to unset
+     * @return void
+     */
+    public function __unset(string $field): void
+    {
+        $this->unset($field);
+    }
+
+    /**
+     * Sets a single field inside this entity.
+     *
+     * ### Example:
+     *
+     * ```
+     * $entity->set('name', 'Andrew');
+     * ```
+     *
+     * It is also possible to mass-assign multiple fields to this entity
+     * with one call by passing a hashed array as fields in the form of
+     * field => value pairs
+     *
+     * ### Example:
+     *
+     * ```
+     * $entity->set(['name' => 'andrew', 'id' => 1]);
+     * echo $entity->name // prints andrew
+     * echo $entity->id // prints 1
+     * ```
+     *
+     * Some times it is handy to bypass setter functions in this entity when assigning
+     * fields. You can achieve this by disabling the `setter` option using the
+     * `$options` parameter:
+     *
+     * ```
+     * $entity->set('name', 'Andrew', ['setter' => false]);
+     * $entity->set(['name' => 'Andrew', 'id' => 1], ['setter' => false]);
+     * ```
+     *
+     * Mass assignment should be treated carefully when accepting user input, by default
+     * entities will guard all fields when fields are assigned in bulk. You can disable
+     * the guarding for a single set call with the `guard` option:
+     *
+     * ```
+     * $entity->set(['name' => 'Andrew', 'id' => 1], ['guard' => false]);
+     * ```
+     *
+     * You do not need to use the guard option when assigning fields individually:
+     *
+     * ```
+     * // No need to use the guard option.
+     * $entity->set('name', 'Andrew');
+     * ```
+     *
+     * @param array|string $field the name of field to set or a list of
+     * fields with their respective values
+     * @param mixed $value The value to set to the field or an array if the
+     * first argument is also an array, in which case will be treated as $options
+     * @param array $options Options to be used for setting the field. Allowed option
+     * keys are `setter` and `guard`
+     * @return $this
+     * @throws \InvalidArgumentException
+     */
+    public function set($field, $value = null, array $options = [])
+    {
+        if (is_string($field) && $field !== '') {
+            $guard = false;
+            $field = [$field => $value];
+        } else {
+            $guard = true;
+            $options = (array)$value;
+        }
+
+        if (!is_array($field)) {
+            throw new InvalidArgumentException('Cannot set an empty field');
+        }
+        $options += ['setter' => true, 'guard' => $guard];
+
+        foreach ($field as $name => $value) {
+            $name = (string)$name;
+            if ($options['guard'] === true && !$this->isAccessible($name)) {
+                continue;
+            }
+
+            $this->setDirty($name, true);
+
+            if (
+                !array_key_exists($name, $this->_original) &&
+                array_key_exists($name, $this->_fields) &&
+                $this->_fields[$name] !== $value
+            ) {
+                $this->_original[$name] = $this->_fields[$name];
+            }
+
+            if (!$options['setter']) {
+                $this->_fields[$name] = $value;
+                continue;
+            }
+
+            $setter = static::_accessor($name, 'set');
+            if ($setter) {
+                $value = $this->{$setter}($value);
+            }
+            $this->_fields[$name] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the value of a field by name
+     *
+     * @param string $field the name of the field to retrieve
+     * @return mixed
+     * @throws \InvalidArgumentException if an empty field name is passed
+     */
+    public function &get(string $field)
+    {
+        if ($field === '') {
+            throw new InvalidArgumentException('Cannot get an empty field');
+        }
+
+        $value = null;
+
+        if (isset($this->_fields[$field])) {
+            $value = &$this->_fields[$field];
+        }
+
+        $method = static::_accessor($field, 'get');
+        if ($method) {
+            $result = $this->{$method}($value);
+
+            return $result;
+        }
+
+        return $value;
+    }
+
+    /**
+     * Returns the value of an original field by name
+     *
+     * @param string $field the name of the field for which original value is retrieved.
+     * @return mixed
+     * @throws \InvalidArgumentException if an empty field name is passed.
+     */
+    public function getOriginal(string $field)
+    {
+        if ($field === '') {
+            throw new InvalidArgumentException('Cannot get an empty field');
+        }
+        if (array_key_exists($field, $this->_original)) {
+            return $this->_original[$field];
+        }
+
+        return $this->get($field);
+    }
+
+    /**
+     * Gets all original values of the entity.
+     *
+     * @return array
+     */
+    public function getOriginalValues(): array
+    {
+        $originals = $this->_original;
+        $originalKeys = array_keys($originals);
+        foreach ($this->_fields as $key => $value) {
+            if (!in_array($key, $originalKeys, true)) {
+                $originals[$key] = $value;
+            }
+        }
+
+        return $originals;
+    }
+
+    /**
+     * Returns whether this entity contains a field named $field
+     * that contains a non-null value.
+     *
+     * ### Example:
+     *
+     * ```
+     * $entity = new Entity(['id' => 1, 'name' => null]);
+     * $entity->has('id'); // true
+     * $entity->has('name'); // false
+     * $entity->has('last_name'); // false
+     * ```
+     *
+     * You can check multiple fields by passing an array:
+     *
+     * ```
+     * $entity->has(['name', 'last_name']);
+     * ```
+     *
+     * All fields must not be null to get a truthy result.
+     *
+     * When checking multiple fields. All fields must not be null
+     * in order for true to be returned.
+     *
+     * @param array|string $field The field or fields to check.
+     * @return bool
+     */
+    public function has($field): bool
+    {
+        foreach ((array)$field as $prop) {
+            if ($this->_hasAllowsNull) {
+                if (!array_key_exists($prop, $this->_fields) && !static::_accessor($prop, 'get')) {
+                    return false;
+                }
+            } elseif ($this->get($prop) === null) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks that a field is empty
+     *
+     * This is not working like the PHP `empty()` function. The method will
+     * return true for:
+     *
+     * - `''` (empty string)
+     * - `null`
+     * - `[]`
+     *
+     * and false in all other cases.
+     *
+     * @param string $field The field to check.
+     * @return bool
+     */
+    public function isEmpty(string $field): bool
+    {
+        $value = $this->get($field);
+        if (
+            $value === null ||
+            (
+                is_array($value) &&
+                empty($value) ||
+                (
+                    is_string($value) &&
+                    $value === ''
+                )
+            )
+        ) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks that a field has a value.
+     *
+     * This method will return true for
+     *
+     * - Non-empty strings
+     * - Non-empty arrays
+     * - Any object
+     * - Integer, even `0`
+     * - Float, even 0.0
+     *
+     * and false in all other cases.
+     *
+     * @param string $field The field to check.
+     * @return bool
+     */
+    public function hasValue(string $field): bool
+    {
+        return !$this->isEmpty($field);
+    }
+
+    /**
+     * Removes a field or list of fields from this entity
+     *
+     * ### Examples:
+     *
+     * ```
+     * $entity->unset('name');
+     * $entity->unset(['name', 'last_name']);
+     * ```
+     *
+     * @param array|string $field The field to unset.
+     * @return $this
+     */
+    public function unset($field)
+    {
+        $field = (array)$field;
+        foreach ($field as $p) {
+            unset($this->_fields[$p], $this->_original[$p], $this->_dirty[$p]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Removes a field or list of fields from this entity
+     *
+     * @deprecated 4.0.0 Use {@link unset()} instead. Will be removed in 5.0.
+     * @param array|string $field The field to unset.
+     * @return $this
+     */
+    public function unsetProperty($field)
+    {
+        deprecationWarning('EntityTrait::unsetProperty() is deprecated. Use unset() instead.');
+
+        return $this->unset($field);
+    }
+
+    /**
+     * Sets hidden fields.
+     *
+     * @param array $fields An array of fields to hide from array exports.
+     * @param bool $merge Merge the new fields with the existing. By default false.
+     * @return $this
+     */
+    public function setHidden(array $fields, bool $merge = false)
+    {
+        if ($merge === false) {
+            $this->_hidden = $fields;
+
+            return $this;
+        }
+
+        $fields = array_merge($this->_hidden, $fields);
+        $this->_hidden = array_unique($fields);
+
+        return $this;
+    }
+
+    /**
+     * Gets the hidden fields.
+     *
+     * @return array
+     */
+    public function getHidden(): array
+    {
+        return $this->_hidden;
+    }
+
+    /**
+     * Sets the virtual fields on this entity.
+     *
+     * @param array $fields An array of fields to treat as virtual.
+     * @param bool $merge Merge the new fields with the existing. By default false.
+     * @return $this
+     */
+    public function setVirtual(array $fields, bool $merge = false)
+    {
+        if ($merge === false) {
+            $this->_virtual = $fields;
+
+            return $this;
+        }
+
+        $fields = array_merge($this->_virtual, $fields);
+        $this->_virtual = array_unique($fields);
+
+        return $this;
+    }
+
+    /**
+     * Gets the virtual fields on this entity.
+     *
+     * @return array
+     */
+    public function getVirtual(): array
+    {
+        return $this->_virtual;
+    }
+
+    /**
+     * Gets the list of visible fields.
+     *
+     * The list of visible fields is all standard fields
+     * plus virtual fields minus hidden fields.
+     *
+     * @return array A list of fields that are 'visible' in all
+     *     representations.
+     */
+    public function getVisible(): array
+    {
+        $fields = array_keys($this->_fields);
+        $fields = array_merge($fields, $this->_virtual);
+
+        return array_diff($fields, $this->_hidden);
+    }
+
+    /**
+     * Returns an array with all the fields that have been set
+     * to this entity
+     *
+     * This method will recursively transform entities assigned to fields
+     * into arrays as well.
+     *
+     * @return array
+     */
+    public function toArray(): array
+    {
+        $result = [];
+        foreach ($this->getVisible() as $field) {
+            $value = $this->get($field);
+            if (is_array($value)) {
+                $result[$field] = [];
+                foreach ($value as $k => $entity) {
+                    if ($entity instanceof EntityInterface) {
+                        $result[$field][$k] = $entity->toArray();
+                    } else {
+                        $result[$field][$k] = $entity;
+                    }
+                }
+            } elseif ($value instanceof EntityInterface) {
+                $result[$field] = $value->toArray();
+            } else {
+                $result[$field] = $value;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns the fields that will be serialized as JSON
+     *
+     * @return array
+     */
+    public function jsonSerialize(): array
+    {
+        return $this->extract($this->getVisible());
+    }
+
+    /**
+     * Implements isset($entity);
+     *
+     * @param string $offset The offset to check.
+     * @return bool Success
+     */
+    public function offsetExists($offset): bool
+    {
+        return $this->has($offset);
+    }
+
+    /**
+     * Implements $entity[$offset];
+     *
+     * @param string $offset The offset to get.
+     * @return mixed
+     */
+    #[\ReturnTypeWillChange]
+    public function &offsetGet($offset)
+    {
+        return $this->get($offset);
+    }
+
+    /**
+     * Implements $entity[$offset] = $value;
+     *
+     * @param string $offset The offset to set.
+     * @param mixed $value The value to set.
+     * @return void
+     */
+    public function offsetSet($offset, $value): void
+    {
+        $this->set($offset, $value);
+    }
+
+    /**
+     * Implements unset($result[$offset]);
+     *
+     * @param string $offset The offset to remove.
+     * @return void
+     */
+    public function offsetUnset($offset): void
+    {
+        $this->unset($offset);
+    }
+
+    /**
+     * Fetch accessor method name
+     * Accessor methods (available or not) are cached in $_accessors
+     *
+     * @param string $property the field name to derive getter name from
+     * @param string $type the accessor type ('get' or 'set')
+     * @return string method name or empty string (no method available)
+     */
+    protected static function _accessor(string $property, string $type): string
+    {
+        $class = static::class;
+
+        if (isset(static::$_accessors[$class][$type][$property])) {
+            return static::$_accessors[$class][$type][$property];
+        }
+
+        if (!empty(static::$_accessors[$class])) {
+            return static::$_accessors[$class][$type][$property] = '';
+        }
+
+        if (static::class === Entity::class) {
+            return '';
+        }
+
+        foreach (get_class_methods($class) as $method) {
+            $prefix = substr($method, 1, 3);
+            if ($method[0] !== '_' || ($prefix !== 'get' && $prefix !== 'set')) {
+                continue;
+            }
+            $field = lcfirst(substr($method, 4));
+            $snakeField = Inflector::underscore($field);
+            $titleField = ucfirst($field);
+            static::$_accessors[$class][$prefix][$snakeField] = $method;
+            static::$_accessors[$class][$prefix][$field] = $method;
+            static::$_accessors[$class][$prefix][$titleField] = $method;
+        }
+
+        if (!isset(static::$_accessors[$class][$type][$property])) {
+            static::$_accessors[$class][$type][$property] = '';
+        }
+
+        return static::$_accessors[$class][$type][$property];
+    }
+
+    /**
+     * Returns an array with the requested fields
+     * stored in this entity, indexed by field name
+     *
+     * @param array $fields list of fields to be returned
+     * @param bool $onlyDirty Return the requested field only if it is dirty
+     * @return array
+     */
+    public function extract(array $fields, bool $onlyDirty = false): array
+    {
+        $result = [];
+        foreach ($fields as $field) {
+            if (!$onlyDirty || $this->isDirty($field)) {
+                $result[$field] = $this->get($field);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns an array with the requested original fields
+     * stored in this entity, indexed by field name.
+     *
+     * Fields that are unchanged from their original value will be included in the
+     * return of this method.
+     *
+     * @param array $fields List of fields to be returned
+     * @return array
+     */
+    public function extractOriginal(array $fields): array
+    {
+        $result = [];
+        foreach ($fields as $field) {
+            $result[$field] = $this->getOriginal($field);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns an array with only the original fields
+     * stored in this entity, indexed by field name.
+     *
+     * This method will only return fields that have been modified since
+     * the entity was built. Unchanged fields will be omitted.
+     *
+     * @param array $fields List of fields to be returned
+     * @return array
+     */
+    public function extractOriginalChanged(array $fields): array
+    {
+        $result = [];
+        foreach ($fields as $field) {
+            $original = $this->getOriginal($field);
+            if ($original !== $this->get($field)) {
+                $result[$field] = $original;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Sets the dirty status of a single field.
+     *
+     * @param string $field the field to set or check status for
+     * @param bool $isDirty true means the field was changed, false means
+     * it was not changed. Defaults to true.
+     * @return $this
+     */
+    public function setDirty(string $field, bool $isDirty = true)
+    {
+        if ($isDirty === false) {
+            unset($this->_dirty[$field]);
+
+            return $this;
+        }
+
+        $this->_dirty[$field] = true;
+        unset($this->_errors[$field], $this->_invalid[$field]);
+
+        return $this;
+    }
+
+    /**
+     * Checks if the entity is dirty or if a single field of it is dirty.
+     *
+     * @param string|null $field The field to check the status for. Null for the whole entity.
+     * @return bool Whether the field was changed or not
+     */
+    public function isDirty(?string $field = null): bool
+    {
+        if ($field === null) {
+            return !empty($this->_dirty);
+        }
+
+        return isset($this->_dirty[$field]);
+    }
+
+    /**
+     * Gets the dirty fields.
+     *
+     * @return array
+     */
+    public function getDirty(): array
+    {
+        return array_keys($this->_dirty);
+    }
+
+    /**
+     * Sets the entire entity as clean, which means that it will appear as
+     * no fields being modified or added at all. This is an useful call
+     * for an initial object hydration
+     *
+     * @return void
+     */
+    public function clean(): void
+    {
+        $this->_dirty = [];
+        $this->_errors = [];
+        $this->_invalid = [];
+        $this->_original = [];
+    }
+
+    /**
+     * Set the status of this entity.
+     *
+     * Using `true` means that the entity has not been persisted in the database,
+     * `false` that it already is.
+     *
+     * @param bool $new Indicate whether this entity has been persisted.
+     * @return $this
+     */
+    public function setNew(bool $new)
+    {
+        if ($new) {
+            foreach ($this->_fields as $k => $p) {
+                $this->_dirty[$k] = true;
+            }
+        }
+
+        $this->_new = $new;
+
+        return $this;
+    }
+
+    /**
+     * Returns whether this entity has already been persisted.
+     *
+     * @return bool Whether the entity has been persisted.
+     */
+    public function isNew(): bool
+    {
+        if (func_num_args()) {
+            deprecationWarning('Using isNew() as setter is deprecated. Use setNew() instead.');
+
+            $this->setNew(func_get_arg(0));
+        }
+
+        return $this->_new;
+    }
+
+    /**
+     * Returns whether this entity has errors.
+     *
+     * @param bool $includeNested true will check nested entities for hasErrors()
+     * @return bool
+     */
+    public function hasErrors(bool $includeNested = true): bool
+    {
+        if ($this->_hasBeenVisited) {
+            // While recursing through entities, each entity should only be visited once. See https://github.com/cakephp/cakephp/issues/17318
+            return false;
+        }
+
+        if (Hash::filter($this->_errors)) {
+            return true;
+        }
+
+        if ($includeNested === false) {
+            return false;
+        }
+
+        $this->_hasBeenVisited = true;
+        try {
+            foreach ($this->_fields as $field) {
+                if ($this->_readHasErrors($field)) {
+                    return true;
+                }
+            }
+        } finally {
+            $this->_hasBeenVisited = false;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns all validation errors.
+     *
+     * @return array
+     */
+    public function getErrors(): array
+    {
+        if ($this->_hasBeenVisited) {
+            // While recursing through entities, each entity should only be visited once. See https://github.com/cakephp/cakephp/issues/17318
+            return [];
+        }
+
+        $diff = array_diff_key($this->_fields, $this->_errors);
+
+        $this->_hasBeenVisited = true;
+        try {
+            $errors = $this->_errors + (new Collection($diff))
+                ->filter(function ($value) {
+                    return is_array($value) || $value instanceof EntityInterface;
+                })
+                ->map(function ($value) {
+                    return $this->_readError($value);
+                })
+                ->filter()
+                ->toArray();
+        } finally {
+            $this->_hasBeenVisited = false;
+        }
+
+        return $errors;
+    }
+
+    /**
+     * Returns validation errors of a field
+     *
+     * @param string $field Field name to get the errors from
+     * @return array
+     */
+    public function getError(string $field): array
+    {
+        $errors = $this->_errors[$field] ?? [];
+        if ($errors) {
+            return $errors;
+        }
+
+        return $this->_nestedErrors($field);
+    }
+
+    /**
+     * Sets error messages to the entity
+     *
+     * ## Example
+     *
+     * ```
+     * // Sets the error messages for multiple fields at once
+     * $entity->setErrors(['salary' => ['message'], 'name' => ['another message']]);
+     * ```
+     *
+     * @param array $errors The array of errors to set.
+     * @param bool $overwrite Whether to overwrite pre-existing errors for $fields
+     * @return $this
+     */
+    public function setErrors(array $errors, bool $overwrite = false)
+    {
+        if ($overwrite) {
+            foreach ($errors as $f => $error) {
+                $this->_errors[$f] = (array)$error;
+            }
+
+            return $this;
+        }
+
+        foreach ($errors as $f => $error) {
+            $this->_errors += [$f => []];
+
+            // String messages are appended to the list,
+            // while more complex error structures need their
+            // keys preserved for nested validator.
+            if (is_string($error)) {
+                $this->_errors[$f][] = $error;
+            } else {
+                foreach ($error as $k => $v) {
+                    $this->_errors[$f][$k] = $v;
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets errors for a single field
+     *
+     * ### Example
+     *
+     * ```
+     * // Sets the error messages for a single field
+     * $entity->setError('salary', ['must be numeric', 'must be a positive number']);
+     * ```
+     *
+     * @param string $field The field to get errors for, or the array of errors to set.
+     * @param array|string $errors The errors to be set for $field
+     * @param bool $overwrite Whether to overwrite pre-existing errors for $field
+     * @return $this
+     */
+    public function setError(string $field, $errors, bool $overwrite = false)
+    {
+        if (is_string($errors)) {
+            $errors = [$errors];
+        }
+
+        return $this->setErrors([$field => $errors], $overwrite);
+    }
+
+    /**
+     * Auxiliary method for getting errors in nested entities
+     *
+     * @param string $field the field in this entity to check for errors
+     * @return array errors in nested entity if any
+     */
+    protected function _nestedErrors(string $field): array
+    {
+        // Only one path element, check for nested entity with error.
+        if (strpos($field, '.') === false) {
+            return $this->_readError($this->get($field));
+        }
+        // Try reading the errors data with field as a simple path
+        $error = Hash::get($this->_errors, $field);
+        if ($error !== null) {
+            return $error;
+        }
+        $path = explode('.', $field);
+
+        // Traverse down the related entities/arrays for
+        // the relevant entity.
+        $entity = $this;
+        $len = count($path);
+        while ($len) {
+            $part = array_shift($path);
+            $len = count($path);
+            $val = null;
+            if ($entity instanceof EntityInterface) {
+                $val = $entity->get($part);
+            } elseif (is_array($entity)) {
+                $val = $entity[$part] ?? false;
+            }
+
+            if (
+                is_array($val) ||
+                $val instanceof Traversable ||
+                $val instanceof EntityInterface
+            ) {
+                $entity = $val;
+            } else {
+                $path[] = $part;
+                break;
+            }
+        }
+        if (count($path) <= 1) {
+            return $this->_readError($entity, array_pop($path));
+        }
+
+        return [];
+    }
+
+    /**
+     * Reads if there are errors for one or many objects.
+     *
+     * @param \Cake\Datasource\EntityInterface|array $object The object to read errors from.
+     * @return bool
+     */
+    protected function _readHasErrors($object): bool
+    {
+        if ($object instanceof EntityInterface && $object->hasErrors()) {
+            return true;
+        }
+
+        if (is_array($object)) {
+            foreach ($object as $value) {
+                if ($this->_readHasErrors($value)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Read the error(s) from one or many objects.
+     *
+     * @param \Cake\Datasource\EntityInterface|iterable $object The object to read errors from.
+     * @param string|null $path The field name for errors.
+     * @return array
+     */
+    protected function _readError($object, $path = null): array
+    {
+        if ($path !== null && $object instanceof EntityInterface) {
+            return $object->getError($path);
+        }
+        if ($object instanceof EntityInterface) {
+            return $object->getErrors();
+        }
+        if (is_iterable($object)) {
+            $array = array_map(function ($val) {
+                if ($val instanceof EntityInterface) {
+                    return $val->getErrors();
+                }
+
+                return null;
+            }, (array)$object);
+
+            return array_filter($array);
+        }
+
+        return [];
+    }
+
+    /**
+     * Get a list of invalid fields and their data for errors upon validation/patching
+     *
+     * @return array
+     */
+    public function getInvalid(): array
+    {
+        return $this->_invalid;
+    }
+
+    /**
+     * Get a single value of an invalid field. Returns null if not set.
+     *
+     * @param string $field The name of the field.
+     * @return mixed|null
+     */
+    public function getInvalidField(string $field)
+    {
+        return $this->_invalid[$field] ?? null;
+    }
+
+    /**
+     * Set fields as invalid and not patchable into the entity.
+     *
+     * This is useful for batch operations when one needs to get the original value for an error message after patching.
+     * This value could not be patched into the entity and is simply copied into the _invalid property for debugging
+     * purposes or to be able to log it away.
+     *
+     * @param array $fields The values to set.
+     * @param bool $overwrite Whether to overwrite pre-existing values for $field.
+     * @return $this
+     */
+    public function setInvalid(array $fields, bool $overwrite = false)
+    {
+        foreach ($fields as $field => $value) {
+            if ($overwrite === true) {
+                $this->_invalid[$field] = $value;
+                continue;
+            }
+            $this->_invalid += [$field => $value];
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets a field as invalid and not patchable into the entity.
+     *
+     * @param string $field The value to set.
+     * @param mixed $value The invalid value to be set for $field.
+     * @return $this
+     */
+    public function setInvalidField(string $field, $value)
+    {
+        $this->_invalid[$field] = $value;
+
+        return $this;
+    }
+
+    /**
+     * Stores whether a field value can be changed or set in this entity.
+     * The special field `*` can also be marked as accessible or protected, meaning
+     * that any other field specified before will take its value. For example
+     * `$entity->setAccess('*', true)` means that any field not specified already
+     * will be accessible by default.
+     *
+     * You can also call this method with an array of fields, in which case they
+     * will each take the accessibility value specified in the second argument.
+     *
+     * ### Example:
+     *
+     * ```
+     * $entity->setAccess('id', true); // Mark id as not protected
+     * $entity->setAccess('author_id', false); // Mark author_id as protected
+     * $entity->setAccess(['id', 'user_id'], true); // Mark both fields as accessible
+     * $entity->setAccess('*', false); // Mark all fields as protected
+     * ```
+     *
+     * @param array|string $field Single or list of fields to change its accessibility
+     * @param bool $set True marks the field as accessible, false will
+     * mark it as protected.
+     * @return $this
+     */
+    public function setAccess($field, bool $set)
+    {
+        if ($field === '*') {
+            $this->_accessible = array_map(function ($p) use ($set) {
+                return $set;
+            }, $this->_accessible);
+            $this->_accessible['*'] = $set;
+
+            return $this;
+        }
+
+        foreach ((array)$field as $prop) {
+            $this->_accessible[$prop] = $set;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the raw accessible configuration for this entity.
+     * The `*` wildcard refers to all fields.
+     *
+     * @return array
+     */
+    public function getAccessible(): array
+    {
+        return $this->_accessible;
+    }
+
+    /**
+     * Checks if a field is accessible
+     *
+     * ### Example:
+     *
+     * ```
+     * $entity->isAccessible('id'); // Returns whether it can be set or not
+     * ```
+     *
+     * @param string $field Field name to check
+     * @return bool
+     */
+    public function isAccessible(string $field): bool
+    {
+        $value = $this->_accessible[$field] ?? null;
+
+        return ($value === null && !empty($this->_accessible['*'])) || $value;
+    }
+
+    /**
+     * Returns the alias of the repository from which this entity came from.
+     *
+     * @return string
+     */
+    public function getSource(): string
+    {
+        return $this->_registryAlias;
+    }
+
+    /**
+     * Sets the source alias
+     *
+     * @param string $alias the alias of the repository
+     * @return $this
+     */
+    public function setSource(string $alias)
+    {
+        $this->_registryAlias = $alias;
+
+        return $this;
+    }
+
+    /**
+     * Returns a string representation of this object in a human readable format.
+     *
+     * @return string
+     */
+    public function __toString(): string
+    {
+        return (string)json_encode($this, JSON_PRETTY_PRINT);
+    }
+
+    /**
+     * Returns an array that can be used to describe the internal state of this
+     * object.
+     *
+     * @return array
+     */
+    public function __debugInfo(): array
+    {
+        $fields = $this->_fields;
+        foreach ($this->_virtual as $field) {
+            $fields[$field] = $this->$field;
+        }
+
+        return $fields + [
+            '[new]' => $this->isNew(),
+            '[accessible]' => $this->_accessible,
+            '[dirty]' => $this->_dirty,
+            '[original]' => $this->_original,
+            '[virtual]' => $this->_virtual,
+            '[hasErrors]' => $this->hasErrors(),
+            '[errors]' => $this->_errors,
+            '[invalid]' => $this->_invalid,
+            '[repository]' => $this->_registryAlias,
+        ];
+    }
+}
diff --git a/vendor/cakephp/datasource/Exception/InvalidPrimaryKeyException.php b/vendor/cakephp/datasource/Exception/InvalidPrimaryKeyException.php
new file mode 100644
index 0000000..ac7a72b
--- /dev/null
+++ b/vendor/cakephp/datasource/Exception/InvalidPrimaryKeyException.php
@@ -0,0 +1,26 @@
+
+     */
+    protected static $_modelFactories = [];
+
+    /**
+     * Register a callable to generate repositories of a given type.
+     *
+     * @param string $type The name of the repository type the factory function is for.
+     * @param \Cake\Datasource\Locator\LocatorInterface|callable $factory The factory function used to create instances.
+     * @return void
+     */
+    public static function add(string $type, $factory): void
+    {
+        if ($factory instanceof LocatorInterface) {
+            static::$_modelFactories[$type] = $factory;
+
+            return;
+        }
+
+        if (is_callable($factory)) {
+            deprecationWarning(
+                'Using a callable as a locator has been deprecated.'
+                . ' Use an instance of Cake\Datasource\Locator\LocatorInterface instead.'
+            );
+
+            static::$_modelFactories[$type] = $factory;
+
+            return;
+        }
+
+        throw new InvalidArgumentException(sprintf(
+            '`$factory` must be an instance of Cake\Datasource\Locator\LocatorInterface or a callable.'
+            . ' Got type `%s` instead.',
+            getTypeName($factory)
+        ));
+    }
+
+    /**
+     * Drop a model factory.
+     *
+     * @param string $type The name of the repository type to drop the factory for.
+     * @return void
+     */
+    public static function drop(string $type): void
+    {
+        unset(static::$_modelFactories[$type]);
+    }
+
+    /**
+     * Get the factory for the specified repository type.
+     *
+     * @param string $type The repository type to get the factory for.
+     * @throws \InvalidArgumentException If the specified repository type has no factory.
+     * @return \Cake\Datasource\Locator\LocatorInterface|callable The factory for the repository type.
+     */
+    public static function get(string $type)
+    {
+        if (!isset(static::$_modelFactories['Table'])) {
+            static::$_modelFactories['Table'] = new TableLocator();
+        }
+
+        if (!isset(static::$_modelFactories[$type])) {
+            throw new InvalidArgumentException(sprintf(
+                'Unknown repository type "%s". Make sure you register a type before trying to use it.',
+                $type
+            ));
+        }
+
+        return static::$_modelFactories[$type];
+    }
+}
diff --git a/vendor/cakephp/datasource/FixtureInterface.php b/vendor/cakephp/datasource/FixtureInterface.php
new file mode 100644
index 0000000..64c28e4
--- /dev/null
+++ b/vendor/cakephp/datasource/FixtureInterface.php
@@ -0,0 +1,73 @@
+ $fields The values to set.
+     * @param bool $overwrite Whether to overwrite pre-existing values for $field.
+     * @return $this
+     */
+    public function setInvalid(array $fields, bool $overwrite = false);
+
+    /**
+     * Get a single value of an invalid field. Returns null if not set.
+     *
+     * @param string $field The name of the field.
+     * @return mixed|null
+     */
+    public function getInvalidField(string $field);
+
+    /**
+     * Sets a field as invalid and not patchable into the entity.
+     *
+     * @param string $field The value to set.
+     * @param mixed $value The invalid value to be set for $field.
+     * @return $this
+     */
+    public function setInvalidField(string $field, $value);
+}
diff --git a/vendor/cakephp/datasource/LICENSE.txt b/vendor/cakephp/datasource/LICENSE.txt
new file mode 100644
index 0000000..b938c9e
--- /dev/null
+++ b/vendor/cakephp/datasource/LICENSE.txt
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org)
+Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org)
+
+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/cakephp/datasource/Locator/AbstractLocator.php b/vendor/cakephp/datasource/Locator/AbstractLocator.php
new file mode 100644
index 0000000..1d925c0
--- /dev/null
+++ b/vendor/cakephp/datasource/Locator/AbstractLocator.php
@@ -0,0 +1,115 @@
+
+     */
+    protected $instances = [];
+
+    /**
+     * Contains a list of options that were passed to get() method.
+     *
+     * @var array
+     */
+    protected $options = [];
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string $alias The alias name you want to get.
+     * @param array $options The options you want to build the table with.
+     * @return \Cake\Datasource\RepositoryInterface
+     * @throws \RuntimeException When trying to get alias for which instance
+     *   has already been created with different options.
+     */
+    public function get(string $alias, array $options = [])
+    {
+        $storeOptions = $options;
+        unset($storeOptions['allowFallbackClass']);
+
+        if (isset($this->instances[$alias])) {
+            if (!empty($storeOptions) && isset($this->options[$alias]) && $this->options[$alias] !== $storeOptions) {
+                throw new RuntimeException(sprintf(
+                    'You cannot configure "%s", it already exists in the registry.',
+                    $alias
+                ));
+            }
+
+            return $this->instances[$alias];
+        }
+
+        $this->options[$alias] = $storeOptions;
+
+        return $this->instances[$alias] = $this->createInstance($alias, $options);
+    }
+
+    /**
+     * Create an instance of a given classname.
+     *
+     * @param string $alias Repository alias.
+     * @param array $options The options you want to build the instance with.
+     * @return \Cake\Datasource\RepositoryInterface
+     */
+    abstract protected function createInstance(string $alias, array $options);
+
+    /**
+     * @inheritDoc
+     */
+    public function set(string $alias, RepositoryInterface $repository)
+    {
+        return $this->instances[$alias] = $repository;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function exists(string $alias): bool
+    {
+        return isset($this->instances[$alias]);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function remove(string $alias): void
+    {
+        unset(
+            $this->instances[$alias],
+            $this->options[$alias]
+        );
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function clear(): void
+    {
+        $this->instances = [];
+        $this->options = [];
+    }
+}
diff --git a/vendor/cakephp/datasource/Locator/LocatorInterface.php b/vendor/cakephp/datasource/Locator/LocatorInterface.php
new file mode 100644
index 0000000..256ae0a
--- /dev/null
+++ b/vendor/cakephp/datasource/Locator/LocatorInterface.php
@@ -0,0 +1,68 @@
+ $options The options you want to build the table with.
+     * @return \Cake\Datasource\RepositoryInterface
+     * @throws \RuntimeException When trying to get alias for which instance
+     *   has already been created with different options.
+     */
+    public function get(string $alias, array $options = []);
+
+    /**
+     * Set a repository instance.
+     *
+     * @param string $alias The alias to set.
+     * @param \Cake\Datasource\RepositoryInterface $repository The repository to set.
+     * @return \Cake\Datasource\RepositoryInterface
+     */
+    public function set(string $alias, RepositoryInterface $repository);
+
+    /**
+     * Check to see if an instance exists in the registry.
+     *
+     * @param string $alias The alias to check for.
+     * @return bool
+     */
+    public function exists(string $alias): bool;
+
+    /**
+     * Removes an repository instance from the registry.
+     *
+     * @param string $alias The alias to remove.
+     * @return void
+     */
+    public function remove(string $alias): void;
+
+    /**
+     * Clears the registry of configuration and instances.
+     *
+     * @return void
+     */
+    public function clear(): void;
+}
diff --git a/vendor/cakephp/datasource/ModelAwareTrait.php b/vendor/cakephp/datasource/ModelAwareTrait.php
new file mode 100644
index 0000000..cfc71c8
--- /dev/null
+++ b/vendor/cakephp/datasource/ModelAwareTrait.php
@@ -0,0 +1,241 @@
+
+     */
+    protected $_modelFactories = [];
+
+    /**
+     * The model type to use.
+     *
+     * @var string
+     */
+    protected $_modelType = 'Table';
+
+    /**
+     * Set the modelClass property based on conventions.
+     *
+     * If the property is already set it will not be overwritten
+     *
+     * @param string $name Class name.
+     * @return void
+     */
+    protected function _setModelClass(string $name): void
+    {
+        if ($this->modelClass === null) {
+            $this->modelClass = $name;
+        }
+    }
+
+    /**
+     * Fetch or construct a model and set it to a property on this object.
+     *
+     * Uses a modelFactory based on `$modelType` to fetch and construct a `RepositoryInterface`
+     * and set it as a property on the current object. The default `modelType`
+     * can be defined with `setModelType()`.
+     *
+     * If a repository provider does not return an object a MissingModelException will
+     * be thrown.
+     *
+     * @param string|null $modelClass Name of model class to load. Defaults to $this->modelClass.
+     *  The name can be an alias like `'Post'` or FQCN like `App\Model\Table\PostsTable::class`.
+     * @param string|null $modelType The type of repository to load. Defaults to the getModelType() value.
+     * @return \Cake\Datasource\RepositoryInterface The model instance created.
+     * @throws \Cake\Datasource\Exception\MissingModelException If the model class cannot be found.
+     * @throws \UnexpectedValueException If $modelClass argument is not provided
+     *   and ModelAwareTrait::$modelClass property value is empty.
+     * @deprecated 4.3.0 Prefer `LocatorAwareTrait::fetchTable()` or `ModelAwareTrait::fetchModel()` instead.
+     */
+    public function loadModel(?string $modelClass = null, ?string $modelType = null): RepositoryInterface
+    {
+        $modelClass = $modelClass ?? $this->modelClass;
+        if (empty($modelClass)) {
+            throw new UnexpectedValueException('Default modelClass is empty');
+        }
+        $modelType = $modelType ?? $this->getModelType();
+
+        $options = [];
+        if (strpos($modelClass, '\\') === false) {
+            [, $alias] = pluginSplit($modelClass, true);
+        } else {
+            $options['className'] = $modelClass;
+            /** @psalm-suppress PossiblyFalseOperand */
+            $alias = substr(
+                $modelClass,
+                strrpos($modelClass, '\\') + 1,
+                -strlen($modelType)
+            );
+            $modelClass = $alias;
+        }
+        if (!property_exists($this, $alias)) {
+            deprecationWarning(
+                '4.5.0 - Dynamic properties will be removed in PHP 8.2. ' .
+                "Add `public \${$alias} = null;` to your class definition or use `#[AllowDynamicProperties]` attribute."
+            );
+        }
+
+        if (isset($this->{$alias})) {
+            return $this->{$alias};
+        }
+
+        $factory = $this->_modelFactories[$modelType] ?? FactoryLocator::get($modelType);
+        if ($factory instanceof LocatorInterface) {
+            $this->{$alias} = $factory->get($modelClass, $options);
+        } else {
+            $this->{$alias} = $factory($modelClass, $options);
+        }
+
+        if (!$this->{$alias}) {
+            throw new MissingModelException([$modelClass, $modelType]);
+        }
+
+        return $this->{$alias};
+    }
+
+    /**
+     * Fetch or construct a model instance from a locator.
+     *
+     * Uses a modelFactory based on `$modelType` to fetch and construct a `RepositoryInterface`
+     * and return it. The default `modelType` can be defined with `setModelType()`.
+     *
+     * Unlike `loadModel()` this method will *not* set an object property.
+     *
+     * If a repository provider does not return an object a MissingModelException will
+     * be thrown.
+     *
+     * @param string|null $modelClass Name of model class to load. Defaults to $this->modelClass.
+     *  The name can be an alias like `'Post'` or FQCN like `App\Model\Table\PostsTable::class`.
+     * @param string|null $modelType The type of repository to load. Defaults to the getModelType() value.
+     * @return \Cake\Datasource\RepositoryInterface The model instance created.
+     * @throws \Cake\Datasource\Exception\MissingModelException If the model class cannot be found.
+     * @throws \UnexpectedValueException If $modelClass argument is not provided
+     *   and ModelAwareTrait::$modelClass property value is empty.
+     */
+    public function fetchModel(?string $modelClass = null, ?string $modelType = null): RepositoryInterface
+    {
+        $modelClass = $modelClass ?? $this->modelClass;
+        if (empty($modelClass)) {
+            throw new UnexpectedValueException('Default modelClass is empty');
+        }
+        $modelType = $modelType ?? $this->getModelType();
+
+        $options = [];
+        if (strpos($modelClass, '\\') === false) {
+            [, $alias] = pluginSplit($modelClass, true);
+        } else {
+            $options['className'] = $modelClass;
+            /** @psalm-suppress PossiblyFalseOperand */
+            $alias = substr(
+                $modelClass,
+                strrpos($modelClass, '\\') + 1,
+                -strlen($modelType)
+            );
+            $modelClass = $alias;
+        }
+
+        $factory = $this->_modelFactories[$modelType] ?? FactoryLocator::get($modelType);
+        if ($factory instanceof LocatorInterface) {
+            $instance = $factory->get($modelClass, $options);
+        } else {
+            $instance = $factory($modelClass, $options);
+        }
+        if ($instance) {
+            return $instance;
+        }
+
+        throw new MissingModelException([$modelClass, $modelType]);
+    }
+
+    /**
+     * Override a existing callable to generate repositories of a given type.
+     *
+     * @param string $type The name of the repository type the factory function is for.
+     * @param \Cake\Datasource\Locator\LocatorInterface|callable $factory The factory function used to create instances.
+     * @return void
+     */
+    public function modelFactory(string $type, $factory): void
+    {
+        if (!$factory instanceof LocatorInterface && !is_callable($factory)) {
+            throw new InvalidArgumentException(sprintf(
+                '`$factory` must be an instance of Cake\Datasource\Locator\LocatorInterface or a callable.'
+                . ' Got type `%s` instead.',
+                getTypeName($factory)
+            ));
+        }
+
+        $this->_modelFactories[$type] = $factory;
+    }
+
+    /**
+     * Get the model type to be used by this class
+     *
+     * @return string
+     */
+    public function getModelType(): string
+    {
+        return $this->_modelType;
+    }
+
+    /**
+     * Set the model type to be used by this class
+     *
+     * @param string $modelType The model type
+     * @return $this
+     */
+    public function setModelType(string $modelType)
+    {
+        $this->_modelType = $modelType;
+
+        return $this;
+    }
+}
diff --git a/vendor/cakephp/datasource/Paginator.php b/vendor/cakephp/datasource/Paginator.php
new file mode 100644
index 0000000..926b231
--- /dev/null
+++ b/vendor/cakephp/datasource/Paginator.php
@@ -0,0 +1,10 @@
+
+     */
+    protected $_defaultConfig = [
+        'page' => 1,
+        'limit' => 20,
+        'maxLimit' => 100,
+        'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
+        'sortableFields' => null,
+        'finder' => 'all',
+    ];
+
+    /**
+     * Paging params after pagination operation is done.
+     *
+     * @var array
+     */
+    protected $_pagingParams = [];
+
+    /**
+     * Handles automatic pagination of model records.
+     *
+     * ### Configuring pagination
+     *
+     * When calling `paginate()` you can use the $settings parameter to pass in
+     * pagination settings. These settings are used to build the queries made
+     * and control other pagination settings.
+     *
+     * If your settings contain a key with the current table's alias. The data
+     * inside that key will be used. Otherwise, the top level configuration will
+     * be used.
+     *
+     * ```
+     *  $settings = [
+     *    'limit' => 20,
+     *    'maxLimit' => 100
+     *  ];
+     *  $results = $paginator->paginate($table, $settings);
+     * ```
+     *
+     * The above settings will be used to paginate any repository. You can configure
+     * repository specific settings by keying the settings with the repository alias.
+     *
+     * ```
+     *  $settings = [
+     *    'Articles' => [
+     *      'limit' => 20,
+     *      'maxLimit' => 100
+     *    ],
+     *    'Comments' => [ ... ]
+     *  ];
+     *  $results = $paginator->paginate($table, $settings);
+     * ```
+     *
+     * This would allow you to have different pagination settings for
+     * `Articles` and `Comments` repositories.
+     *
+     * ### Controlling sort fields
+     *
+     * By default CakePHP will automatically allow sorting on any column on the
+     * repository object being paginated. Often times you will want to allow
+     * sorting on either associated columns or calculated fields. In these cases
+     * you will need to define an allowed list of all the columns you wish to allow
+     * sorting on. You can define the allowed sort fields in the `$settings` parameter:
+     *
+     * ```
+     * $settings = [
+     *   'Articles' => [
+     *     'finder' => 'custom',
+     *     'sortableFields' => ['title', 'author_id', 'comment_count'],
+     *   ]
+     * ];
+     * ```
+     *
+     * Passing an empty array as sortableFields disallows sorting altogether.
+     *
+     * ### Paginating with custom finders
+     *
+     * You can paginate with any find type defined on your table using the
+     * `finder` option.
+     *
+     * ```
+     *  $settings = [
+     *    'Articles' => [
+     *      'finder' => 'popular'
+     *    ]
+     *  ];
+     *  $results = $paginator->paginate($table, $settings);
+     * ```
+     *
+     * Would paginate using the `find('popular')` method.
+     *
+     * You can also pass an already created instance of a query to this method:
+     *
+     * ```
+     * $query = $this->Articles->find('popular')->matching('Tags', function ($q) {
+     *   return $q->where(['name' => 'CakePHP'])
+     * });
+     * $results = $paginator->paginate($query);
+     * ```
+     *
+     * ### Scoping Request parameters
+     *
+     * By using request parameter scopes you can paginate multiple queries in
+     * the same controller action:
+     *
+     * ```
+     * $articles = $paginator->paginate($articlesQuery, ['scope' => 'articles']);
+     * $tags = $paginator->paginate($tagsQuery, ['scope' => 'tags']);
+     * ```
+     *
+     * Each of the above queries will use different query string parameter sets
+     * for pagination data. An example URL paginating both results would be:
+     *
+     * ```
+     * /dashboard?articles[page]=1&tags[page]=2
+     * ```
+     *
+     * @param \Cake\Datasource\RepositoryInterface|\Cake\Datasource\QueryInterface $object The repository or query
+     *   to paginate.
+     * @param array $params Request params
+     * @param array $settings The settings/configuration used for pagination.
+     * @return \Cake\Datasource\ResultSetInterface Query results
+     * @throws \Cake\Datasource\Paging\Exception\PageOutOfBoundsException
+     */
+    public function paginate(object $object, array $params = [], array $settings = []): ResultSetInterface
+    {
+        $query = null;
+        if ($object instanceof QueryInterface) {
+            $query = $object;
+            $object = $query->getRepository();
+            if ($object === null) {
+                throw new CakeException('No repository set for query.');
+            }
+        }
+
+        $data = $this->extractData($object, $params, $settings);
+        $query = $this->getQuery($object, $query, $data);
+
+        $cleanQuery = clone $query;
+        $results = $query->all();
+        $data['numResults'] = count($results);
+        $data['count'] = $this->getCount($cleanQuery, $data);
+
+        $pagingParams = $this->buildParams($data);
+        $alias = $object->getAlias();
+        $this->_pagingParams = [$alias => $pagingParams];
+        if ($pagingParams['requestedPage'] > $pagingParams['page']) {
+            throw new PageOutOfBoundsException([
+                'requestedPage' => $pagingParams['requestedPage'],
+                'pagingParams' => $this->_pagingParams,
+            ]);
+        }
+
+        return $results;
+    }
+
+    /**
+     * Get query for fetching paginated results.
+     *
+     * @param \Cake\Datasource\RepositoryInterface $object Repository instance.
+     * @param \Cake\Datasource\QueryInterface|null $query Query Instance.
+     * @param array $data Pagination data.
+     * @return \Cake\Datasource\QueryInterface
+     */
+    protected function getQuery(RepositoryInterface $object, ?QueryInterface $query, array $data): QueryInterface
+    {
+        $options = $data['options'];
+        unset(
+            $options['scope'],
+            $options['sort'],
+            $options['direction'],
+        );
+
+        if ($query === null) {
+            $query = $object->find($data['finder'], $options);
+        } else {
+            $query->applyOptions($options);
+        }
+
+        return $query;
+    }
+
+    /**
+     * Get total count of records.
+     *
+     * @param \Cake\Datasource\QueryInterface $query Query instance.
+     * @param array $data Pagination data.
+     * @return int|null
+     */
+    protected function getCount(QueryInterface $query, array $data): ?int
+    {
+        return $query->count();
+    }
+
+    /**
+     * Extract pagination data needed
+     *
+     * @param \Cake\Datasource\RepositoryInterface $object The repository object.
+     * @param array $params Request params
+     * @param array $settings The settings/configuration used for pagination.
+     * @return array Array with keys 'defaults', 'options' and 'finder'
+     */
+    protected function extractData(RepositoryInterface $object, array $params, array $settings): array
+    {
+        $alias = $object->getAlias();
+        $defaults = $this->getDefaults($alias, $settings);
+
+        $validSettings = array_merge(
+            array_keys($this->_defaultConfig),
+            ['whitelist', 'sortWhitelist', 'order', 'scope']
+        );
+        $extraSettings = array_diff_key($defaults, array_flip($validSettings));
+        if ($extraSettings) {
+            deprecationWarning(
+                'Passing query options as paginator settings is deprecated.'
+                . ' Use a custom finder through `finder` config instead.'
+                . ' Extra keys found are: ' . implode(',', array_keys($extraSettings))
+            );
+        }
+
+        $options = $this->mergeOptions($params, $defaults);
+        $options = $this->validateSort($object, $options);
+        $options = $this->checkLimit($options);
+
+        $options += ['page' => 1, 'scope' => null];
+        $options['page'] = (int)$options['page'] < 1 ? 1 : (int)$options['page'];
+        [$finder, $options] = $this->_extractFinder($options);
+
+        return compact('defaults', 'options', 'finder');
+    }
+
+    /**
+     * Build pagination params.
+     *
+     * @param array $data Paginator data containing keys 'options',
+     *   'count', 'defaults', 'finder', 'numResults'.
+     * @return array Paging params.
+     */
+    protected function buildParams(array $data): array
+    {
+        $limit = $data['options']['limit'];
+
+        $paging = [
+            'count' => $data['count'],
+            'current' => $data['numResults'],
+            'perPage' => $limit,
+            'page' => $data['options']['page'],
+            'requestedPage' => $data['options']['page'],
+        ];
+
+        $paging = $this->addPageCountParams($paging, $data);
+        $paging = $this->addStartEndParams($paging, $data);
+        $paging = $this->addPrevNextParams($paging, $data);
+        $paging = $this->addSortingParams($paging, $data);
+
+        $paging += [
+            'limit' => $data['defaults']['limit'] != $limit ? $limit : null,
+            'scope' => $data['options']['scope'],
+            'finder' => $data['finder'],
+        ];
+
+        return $paging;
+    }
+
+    /**
+     * Add "page" and "pageCount" params.
+     *
+     * @param array $params Paging params.
+     * @param array $data Paginator data.
+     * @return array Updated params.
+     */
+    protected function addPageCountParams(array $params, array $data): array
+    {
+        $page = $params['page'];
+        $pageCount = 0;
+
+        if ($params['count'] !== null) {
+            $pageCount = max((int)ceil($params['count'] / $params['perPage']), 1);
+            $page = min($page, $pageCount);
+        } elseif ($params['current'] === 0 && $params['requestedPage'] > 1) {
+            $page = 1;
+        }
+
+        $params['page'] = $page;
+        $params['pageCount'] = $pageCount;
+
+        return $params;
+    }
+
+    /**
+     * Add "start" and "end" params.
+     *
+     * @param array $params Paging params.
+     * @param array $data Paginator data.
+     * @return array Updated params.
+     */
+    protected function addStartEndParams(array $params, array $data): array
+    {
+        $start = $end = 0;
+
+        if ($params['current'] > 0) {
+            $start = (($params['page'] - 1) * $params['perPage']) + 1;
+            $end = $start + $params['current'] - 1;
+        }
+
+        $params['start'] = $start;
+        $params['end'] = $end;
+
+        return $params;
+    }
+
+    /**
+     * Add "prevPage" and "nextPage" params.
+     *
+     * @param array $params Paginator params.
+     * @param array $data Paging data.
+     * @return array Updated params.
+     */
+    protected function addPrevNextParams(array $params, array $data): array
+    {
+        $params['prevPage'] = $params['page'] > 1;
+        if ($params['count'] === null) {
+            $params['nextPage'] = true;
+        } else {
+            $params['nextPage'] = $params['count'] > $params['page'] * $params['perPage'];
+        }
+
+        return $params;
+    }
+
+    /**
+     * Add sorting / ordering params.
+     *
+     * @param array $params Paginator params.
+     * @param array $data Paging data.
+     * @return array Updated params.
+     */
+    protected function addSortingParams(array $params, array $data): array
+    {
+        $defaults = $data['defaults'];
+        $order = (array)$data['options']['order'];
+        $sortDefault = $directionDefault = false;
+
+        if (!empty($defaults['order']) && count($defaults['order']) >= 1) {
+            $sortDefault = key($defaults['order']);
+            $directionDefault = current($defaults['order']);
+        }
+
+        $params += [
+            'sort' => $data['options']['sort'],
+            'direction' => isset($data['options']['sort']) && count($order) ? current($order) : null,
+            'sortDefault' => $sortDefault,
+            'directionDefault' => $directionDefault,
+            'completeSort' => $order,
+        ];
+
+        return $params;
+    }
+
+    /**
+     * Extracts the finder name and options out of the provided pagination options.
+     *
+     * @param array $options the pagination options.
+     * @return array An array containing in the first position the finder name
+     *   and in the second the options to be passed to it.
+     */
+    protected function _extractFinder(array $options): array
+    {
+        $type = !empty($options['finder']) ? $options['finder'] : 'all';
+        unset(
+            $options['finder'],
+            $options['maxLimit'],
+            $options['allowedParameters'],
+            $options['whitelist'],
+            $options['sortableFields'],
+            $options['sortWhitelist'],
+        );
+
+        if (is_array($type)) {
+            $options = (array)current($type) + $options;
+            $type = key($type);
+        }
+
+        return [$type, $options];
+    }
+
+    /**
+     * Get paging params after pagination operation.
+     *
+     * @return array
+     */
+    public function getPagingParams(): array
+    {
+        return $this->_pagingParams;
+    }
+
+    /**
+     * Shim method for reading the deprecated whitelist or allowedParameters options
+     *
+     * @return array
+     */
+    protected function getAllowedParameters(): array
+    {
+        $allowed = $this->getConfig('allowedParameters');
+        if (!$allowed) {
+            $allowed = [];
+        }
+        $whitelist = $this->getConfig('whitelist');
+        if ($whitelist) {
+            deprecationWarning('The `whitelist` option is deprecated. Use the `allowedParameters` option instead.');
+
+            return array_merge($allowed, $whitelist);
+        }
+
+        return $allowed;
+    }
+
+    /**
+     * Shim method for reading the deprecated sortWhitelist or sortableFields options.
+     *
+     * @param array $config The configuration data to coalesce and emit warnings on.
+     * @return array|null
+     */
+    protected function getSortableFields(array $config): ?array
+    {
+        $allowed = $config['sortableFields'] ?? null;
+        if ($allowed !== null) {
+            return $allowed;
+        }
+        $deprecated = $config['sortWhitelist'] ?? null;
+        if ($deprecated !== null) {
+            deprecationWarning('The `sortWhitelist` option is deprecated. Use `sortableFields` instead.');
+        }
+
+        return $deprecated;
+    }
+
+    /**
+     * Merges the various options that Paginator uses.
+     * Pulls settings together from the following places:
+     *
+     * - General pagination settings
+     * - Model specific settings.
+     * - Request parameters
+     *
+     * The result of this method is the aggregate of all the option sets
+     * combined together. You can change config value `allowedParameters` to modify
+     * which options/values can be set using request parameters.
+     *
+     * @param array $params Request params.
+     * @param array $settings The settings to merge with the request data.
+     * @return array Array of merged options.
+     */
+    public function mergeOptions(array $params, array $settings): array
+    {
+        if (!empty($settings['scope'])) {
+            $scope = $settings['scope'];
+            $params = !empty($params[$scope]) ? (array)$params[$scope] : [];
+        }
+
+        $allowed = $this->getAllowedParameters();
+        $params = array_intersect_key($params, array_flip($allowed));
+
+        return array_merge($settings, $params);
+    }
+
+    /**
+     * Get the settings for a $model. If there are no settings for a specific
+     * repository, the general settings will be used.
+     *
+     * @param string $alias Model name to get settings for.
+     * @param array $settings The settings which is used for combining.
+     * @return array An array of pagination settings for a model,
+     *   or the general settings.
+     */
+    public function getDefaults(string $alias, array $settings): array
+    {
+        if (isset($settings[$alias])) {
+            $settings = $settings[$alias];
+        }
+
+        $defaults = $this->getConfig();
+        $defaults['whitelist'] = $defaults['allowedParameters'] = $this->getAllowedParameters();
+
+        $maxLimit = $settings['maxLimit'] ?? $defaults['maxLimit'];
+        $limit = $settings['limit'] ?? $defaults['limit'];
+
+        if ($limit > $maxLimit) {
+            $limit = $maxLimit;
+        }
+
+        $settings['maxLimit'] = $maxLimit;
+        $settings['limit'] = $limit;
+
+        return $settings + $defaults;
+    }
+
+    /**
+     * Validate that the desired sorting can be performed on the $object.
+     *
+     * Only fields or virtualFields can be sorted on. The direction param will
+     * also be sanitized. Lastly sort + direction keys will be converted into
+     * the model friendly order key.
+     *
+     * You can use the allowedParameters option to control which columns/fields are
+     * available for sorting via URL parameters. This helps prevent users from ordering large
+     * result sets on un-indexed values.
+     *
+     * If you need to sort on associated columns or synthetic properties you
+     * will need to use the `sortableFields` option.
+     *
+     * Any columns listed in the allowed sort fields will be implicitly trusted.
+     * You can use this to sort on synthetic columns, or columns added in custom
+     * find operations that may not exist in the schema.
+     *
+     * The default order options provided to paginate() will be merged with the user's
+     * requested sorting field/direction.
+     *
+     * @param \Cake\Datasource\RepositoryInterface $object Repository object.
+     * @param array $options The pagination options being used for this request.
+     * @return array An array of options with sort + direction removed and
+     *   replaced with order if possible.
+     */
+    public function validateSort(RepositoryInterface $object, array $options): array
+    {
+        if (isset($options['sort'])) {
+            $direction = null;
+            if (isset($options['direction'])) {
+                $direction = strtolower($options['direction']);
+            }
+            if (!in_array($direction, ['asc', 'desc'], true)) {
+                $direction = 'asc';
+            }
+
+            $order = isset($options['order']) && is_array($options['order']) ? $options['order'] : [];
+            if ($order && $options['sort'] && strpos($options['sort'], '.') === false) {
+                $order = $this->_removeAliases($order, $object->getAlias());
+            }
+
+            $options['order'] = [$options['sort'] => $direction] + $order;
+        } else {
+            $options['sort'] = null;
+        }
+        unset($options['direction']);
+
+        if (empty($options['order'])) {
+            $options['order'] = [];
+        }
+        if (!is_array($options['order'])) {
+            return $options;
+        }
+
+        $sortAllowed = false;
+        $allowed = $this->getSortableFields($options);
+        if ($allowed !== null) {
+            $options['sortableFields'] = $options['sortWhitelist'] = $allowed;
+
+            $field = key($options['order']);
+            $sortAllowed = in_array($field, $allowed, true);
+            if (!$sortAllowed) {
+                $options['order'] = [];
+                $options['sort'] = null;
+
+                return $options;
+            }
+        }
+
+        if (
+            $options['sort'] === null
+            && count($options['order']) >= 1
+            && !is_numeric(key($options['order']))
+        ) {
+            $options['sort'] = key($options['order']);
+        }
+
+        $options['order'] = $this->_prefix($object, $options['order'], $sortAllowed);
+
+        return $options;
+    }
+
+    /**
+     * Remove alias if needed.
+     *
+     * @param array $fields Current fields
+     * @param string $model Current model alias
+     * @return array $fields Unaliased fields where applicable
+     */
+    protected function _removeAliases(array $fields, string $model): array
+    {
+        $result = [];
+        foreach ($fields as $field => $sort) {
+            if (is_int($field)) {
+                throw new CakeException(sprintf(
+                    'The `order` config must be an associative array. Found invalid value with numeric key: `%s`',
+                    $sort
+                ));
+            }
+
+            if (strpos($field, '.') === false) {
+                $result[$field] = $sort;
+                continue;
+            }
+
+            [$alias, $currentField] = explode('.', $field);
+
+            if ($alias === $model) {
+                $result[$currentField] = $sort;
+                continue;
+            }
+
+            $result[$field] = $sort;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Prefixes the field with the table alias if possible.
+     *
+     * @param \Cake\Datasource\RepositoryInterface $object Repository object.
+     * @param array $order Order array.
+     * @param bool $allowed Whether the field was allowed.
+     * @return array Final order array.
+     */
+    protected function _prefix(RepositoryInterface $object, array $order, bool $allowed = false): array
+    {
+        $tableAlias = $object->getAlias();
+        $tableOrder = [];
+        foreach ($order as $key => $value) {
+            if (is_numeric($key)) {
+                $tableOrder[] = $value;
+                continue;
+            }
+            $field = $key;
+            $alias = $tableAlias;
+
+            if (strpos($key, '.') !== false) {
+                [$alias, $field] = explode('.', $key);
+            }
+            $correctAlias = ($tableAlias === $alias);
+
+            if ($correctAlias && $allowed) {
+                // Disambiguate fields in schema. As id is quite common.
+                if ($object->hasField($field)) {
+                    $field = $alias . '.' . $field;
+                }
+                $tableOrder[$field] = $value;
+            } elseif ($correctAlias && $object->hasField($field)) {
+                $tableOrder[$tableAlias . '.' . $field] = $value;
+            } elseif (!$correctAlias && $allowed) {
+                $tableOrder[$alias . '.' . $field] = $value;
+            }
+        }
+
+        return $tableOrder;
+    }
+
+    /**
+     * Check the limit parameter and ensure it's within the maxLimit bounds.
+     *
+     * @param array $options An array of options with a limit key to be checked.
+     * @return array An array of options for pagination.
+     */
+    public function checkLimit(array $options): array
+    {
+        $options['limit'] = (int)$options['limit'];
+        if ($options['limit'] < 1) {
+            $options['limit'] = 1;
+        }
+        $options['limit'] = max(min($options['limit'], $options['maxLimit']), 1);
+
+        return $options;
+    }
+}
+
+// phpcs:disable
+class_alias(
+    'Cake\Datasource\Paging\NumericPaginator',
+    'Cake\Datasource\Paginator'
+);
+// phpcs:enable
diff --git a/vendor/cakephp/datasource/Paging/PaginatorInterface.php b/vendor/cakephp/datasource/Paging/PaginatorInterface.php
new file mode 100644
index 0000000..4d30597
--- /dev/null
+++ b/vendor/cakephp/datasource/Paging/PaginatorInterface.php
@@ -0,0 +1,50 @@
+_key = $key;
+
+        if (!is_string($config) && !($config instanceof CacheInterface)) {
+            throw new RuntimeException('Cache configs must be strings or \Psr\SimpleCache\CacheInterface instances.');
+        }
+        $this->_config = $config;
+    }
+
+    /**
+     * Load the cached results from the cache or run the query.
+     *
+     * @param object $query The query the cache read is for.
+     * @return mixed|null Either the cached results or null.
+     */
+    public function fetch(object $query)
+    {
+        $key = $this->_resolveKey($query);
+        $storage = $this->_resolveCacher();
+        $result = $storage->get($key);
+        if (empty($result)) {
+            return null;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Store the result set into the cache.
+     *
+     * @param object $query The query the cache read is for.
+     * @param \Traversable $results The result set to store.
+     * @return bool True if the data was successfully cached, false on failure
+     */
+    public function store(object $query, Traversable $results): bool
+    {
+        $key = $this->_resolveKey($query);
+        $storage = $this->_resolveCacher();
+
+        return $storage->set($key, $results);
+    }
+
+    /**
+     * Get/generate the cache key.
+     *
+     * @param object $query The query to generate a key for.
+     * @return string
+     * @throws \RuntimeException
+     */
+    protected function _resolveKey(object $query): string
+    {
+        if (is_string($this->_key)) {
+            return $this->_key;
+        }
+        $func = $this->_key;
+        $key = $func($query);
+        if (!is_string($key)) {
+            $msg = sprintf('Cache key functions must return a string. Got %s.', var_export($key, true));
+            throw new RuntimeException($msg);
+        }
+
+        return $key;
+    }
+
+    /**
+     * Get the cache engine.
+     *
+     * @return \Psr\SimpleCache\CacheInterface
+     */
+    protected function _resolveCacher()
+    {
+        if (is_string($this->_config)) {
+            return Cache::pool($this->_config);
+        }
+
+        return $this->_config;
+    }
+}
diff --git a/vendor/cakephp/datasource/QueryInterface.php b/vendor/cakephp/datasource/QueryInterface.php
new file mode 100644
index 0000000..fc7023a
--- /dev/null
+++ b/vendor/cakephp/datasource/QueryInterface.php
@@ -0,0 +1,406 @@
+ value array representing a single aliased field
+     * that can be passed directly to the select() method.
+     * The key will contain the alias and the value the actual field name.
+     *
+     * If the field is already aliased, then it will not be changed.
+     * If no $alias is passed, the default table for this query will be used.
+     *
+     * @param string $field The field to alias
+     * @param string|null $alias the alias used to prefix the field
+     * @return array
+     */
+    public function aliasField(string $field, ?string $alias = null): array;
+
+    /**
+     * Runs `aliasField()` for each field in the provided list and returns
+     * the result under a single array.
+     *
+     * @param array $fields The fields to alias
+     * @param string|null $defaultAlias The default alias
+     * @return array
+     */
+    public function aliasFields(array $fields, ?string $defaultAlias = null): array;
+
+    /**
+     * Fetch the results for this query.
+     *
+     * Will return either the results set through setResult(), or execute this query
+     * and return the ResultSetDecorator object ready for streaming of results.
+     *
+     * ResultSetDecorator is a traversable object that implements the methods found
+     * on Cake\Collection\Collection.
+     *
+     * @return \Cake\Datasource\ResultSetInterface
+     */
+    public function all(): ResultSetInterface;
+
+    /**
+     * Populates or adds parts to current query clauses using an array.
+     * This is handy for passing all query clauses at once. The option array accepts:
+     *
+     * - fields: Maps to the select method
+     * - conditions: Maps to the where method
+     * - limit: Maps to the limit method
+     * - order: Maps to the order method
+     * - offset: Maps to the offset method
+     * - group: Maps to the group method
+     * - having: Maps to the having method
+     * - contain: Maps to the contain options for eager loading
+     * - join: Maps to the join method
+     * - page: Maps to the page method
+     *
+     * ### Example:
+     *
+     * ```
+     * $query->applyOptions([
+     *   'fields' => ['id', 'name'],
+     *   'conditions' => [
+     *     'created >=' => '2013-01-01'
+     *   ],
+     *   'limit' => 10
+     * ]);
+     * ```
+     *
+     * Is equivalent to:
+     *
+     * ```
+     *  $query
+     *  ->select(['id', 'name'])
+     *  ->where(['created >=' => '2013-01-01'])
+     *  ->limit(10)
+     * ```
+     *
+     * @param array $options list of query clauses to apply new parts to.
+     * @return $this
+     */
+    public function applyOptions(array $options);
+
+    /**
+     * Apply custom finds to against an existing query object.
+     *
+     * Allows custom find methods to be combined and applied to each other.
+     *
+     * ```
+     * $repository->find('all')->find('recent');
+     * ```
+     *
+     * The above is an example of stacking multiple finder methods onto
+     * a single query.
+     *
+     * @param string $finder The finder method to use.
+     * @param array $options The options for the finder.
+     * @return static Returns a modified query.
+     */
+    public function find(string $finder, array $options = []);
+
+    /**
+     * Returns the first result out of executing this query, if the query has not been
+     * executed before, it will set the limit clause to 1 for performance reasons.
+     *
+     * ### Example:
+     *
+     * ```
+     * $singleUser = $query->select(['id', 'username'])->first();
+     * ```
+     *
+     * @return \Cake\Datasource\EntityInterface|array|null the first result from the ResultSet
+     */
+    public function first();
+
+    /**
+     * Returns the total amount of results for the query.
+     *
+     * @return int
+     */
+    public function count(): int;
+
+    /**
+     * Sets the number of records that should be retrieved from database,
+     * accepts an integer or an expression object that evaluates to an integer.
+     * In some databases, this operation might not be supported or will require
+     * the query to be transformed in order to limit the result set size.
+     *
+     * ### Examples
+     *
+     * ```
+     * $query->limit(10) // generates LIMIT 10
+     * $query->limit($query->newExpr()->add(['1 + 1'])); // LIMIT (1 + 1)
+     * ```
+     *
+     * @param \Cake\Database\ExpressionInterface|int|null $limit number of records to be returned
+     * @return $this
+     */
+    public function limit($limit);
+
+    /**
+     * Sets the number of records that should be skipped from the original result set
+     * This is commonly used for paginating large results. Accepts an integer or an
+     * expression object that evaluates to an integer.
+     *
+     * In some databases, this operation might not be supported or will require
+     * the query to be transformed in order to limit the result set size.
+     *
+     * ### Examples
+     *
+     * ```
+     *  $query->offset(10) // generates OFFSET 10
+     *  $query->offset($query->newExpr()->add(['1 + 1'])); // OFFSET (1 + 1)
+     * ```
+     *
+     * @param \Cake\Database\ExpressionInterface|int|null $offset number of records to be skipped
+     * @return $this
+     */
+    public function offset($offset);
+
+    /**
+     * Adds a single or multiple fields to be used in the ORDER clause for this query.
+     * Fields can be passed as an array of strings, array of expression
+     * objects, a single expression or a single string.
+     *
+     * If an array is passed, keys will be used as the field itself and the value will
+     * represent the order in which such field should be ordered. When called multiple
+     * times with the same fields as key, the last order definition will prevail over
+     * the others.
+     *
+     * By default this function will append any passed argument to the list of fields
+     * to be selected, unless the second argument is set to true.
+     *
+     * ### Examples:
+     *
+     * ```
+     * $query->order(['title' => 'DESC', 'author_id' => 'ASC']);
+     * ```
+     *
+     * Produces:
+     *
+     * `ORDER BY title DESC, author_id ASC`
+     *
+     * ```
+     * $query
+     *     ->order(['title' => $query->newExpr('DESC NULLS FIRST')])
+     *     ->order('author_id');
+     * ```
+     *
+     * Will generate:
+     *
+     * `ORDER BY title DESC NULLS FIRST, author_id`
+     *
+     * ```
+     * $expression = $query->newExpr()->add(['id % 2 = 0']);
+     * $query->order($expression)->order(['title' => 'ASC']);
+     * ```
+     *
+     * Will become:
+     *
+     * `ORDER BY (id %2 = 0), title ASC`
+     *
+     * If you need to set complex expressions as order conditions, you
+     * should use `orderAsc()` or `orderDesc()`.
+     *
+     * @param \Cake\Database\ExpressionInterface|\Closure|array|string $fields fields to be added to the list
+     * @param bool $overwrite whether to reset order with field list or not
+     * @return $this
+     */
+    public function order($fields, $overwrite = false);
+
+    /**
+     * Set the page of results you want.
+     *
+     * This method provides an easier to use interface to set the limit + offset
+     * in the record set you want as results. If empty the limit will default to
+     * the existing limit clause, and if that too is empty, then `25` will be used.
+     *
+     * Pages must start at 1.
+     *
+     * @param int $num The page number you want.
+     * @param int|null $limit The number of rows you want in the page. If null
+     *  the current limit clause will be used.
+     * @return $this
+     * @throws \InvalidArgumentException If page number < 1.
+     */
+    public function page(int $num, ?int $limit = null);
+
+    /**
+     * Returns an array representation of the results after executing the query.
+     *
+     * @return array
+     */
+    public function toArray(): array;
+
+    /**
+     * Set the default Table object that will be used by this query
+     * and form the `FROM` clause.
+     *
+     * @param \Cake\Datasource\RepositoryInterface $repository The default repository object to use
+     * @return $this
+     */
+    public function repository(RepositoryInterface $repository);
+
+    /**
+     * Returns the default repository object that will be used by this query,
+     * that is, the repository that will appear in the from clause.
+     *
+     * @return \Cake\Datasource\RepositoryInterface|null $repository The default repository object to use
+     */
+    public function getRepository(): ?RepositoryInterface;
+
+    /**
+     * Adds a condition or set of conditions to be used in the WHERE clause for this
+     * query. Conditions can be expressed as an array of fields as keys with
+     * comparison operators in it, the values for the array will be used for comparing
+     * the field to such literal. Finally, conditions can be expressed as a single
+     * string or an array of strings.
+     *
+     * When using arrays, each entry will be joined to the rest of the conditions using
+     * an AND operator. Consecutive calls to this function will also join the new
+     * conditions specified using the AND operator. Additionally, values can be
+     * expressed using expression objects which can include other query objects.
+     *
+     * Any conditions created with this methods can be used with any SELECT, UPDATE
+     * and DELETE type of queries.
+     *
+     * ### Conditions using operators:
+     *
+     * ```
+     *  $query->where([
+     *      'posted >=' => new DateTime('3 days ago'),
+     *      'title LIKE' => 'Hello W%',
+     *      'author_id' => 1,
+     *  ], ['posted' => 'datetime']);
+     * ```
+     *
+     * The previous example produces:
+     *
+     * `WHERE posted >= 2012-01-27 AND title LIKE 'Hello W%' AND author_id = 1`
+     *
+     * Second parameter is used to specify what type is expected for each passed
+     * key. Valid types can be used from the mapped with Database\Type class.
+     *
+     * ### Nesting conditions with conjunctions:
+     *
+     * ```
+     *  $query->where([
+     *      'author_id !=' => 1,
+     *      'OR' => ['published' => true, 'posted <' => new DateTime('now')],
+     *      'NOT' => ['title' => 'Hello']
+     *  ], ['published' => boolean, 'posted' => 'datetime']
+     * ```
+     *
+     * The previous example produces:
+     *
+     * `WHERE author_id = 1 AND (published = 1 OR posted < '2012-02-01') AND NOT (title = 'Hello')`
+     *
+     * You can nest conditions using conjunctions as much as you like. Sometimes, you
+     * may want to define 2 different options for the same key, in that case, you can
+     * wrap each condition inside a new array:
+     *
+     * `$query->where(['OR' => [['published' => false], ['published' => true]])`
+     *
+     * Keep in mind that every time you call where() with the third param set to false
+     * (default), it will join the passed conditions to the previous stored list using
+     * the AND operator. Also, using the same array key twice in consecutive calls to
+     * this method will not override the previous value.
+     *
+     * ### Using expressions objects:
+     *
+     * ```
+     *  $exp = $query->newExpr()->add(['id !=' => 100, 'author_id' != 1])->tieWith('OR');
+     *  $query->where(['published' => true], ['published' => 'boolean'])->where($exp);
+     * ```
+     *
+     * The previous example produces:
+     *
+     * `WHERE (id != 100 OR author_id != 1) AND published = 1`
+     *
+     * Other Query objects that be used as conditions for any field.
+     *
+     * ### Adding conditions in multiple steps:
+     *
+     * You can use callable functions to construct complex expressions, functions
+     * receive as first argument a new QueryExpression object and this query instance
+     * as second argument. Functions must return an expression object, that will be
+     * added the list of conditions for the query using the AND operator.
+     *
+     * ```
+     *  $query
+     *  ->where(['title !=' => 'Hello World'])
+     *  ->where(function ($exp, $query) {
+     *      $or = $exp->or(['id' => 1]);
+     *      $and = $exp->and(['id >' => 2, 'id <' => 10]);
+     *  return $or->add($and);
+     *  });
+     * ```
+     *
+     * * The previous example produces:
+     *
+     * `WHERE title != 'Hello World' AND (id = 1 OR (id > 2 AND id < 10))`
+     *
+     * ### Conditions as strings:
+     *
+     * ```
+     *  $query->where(['articles.author_id = authors.id', 'modified IS NULL']);
+     * ```
+     *
+     * The previous example produces:
+     *
+     * `WHERE articles.author_id = authors.id AND modified IS NULL`
+     *
+     * Please note that when using the array notation or the expression objects, all
+     * values will be correctly quoted and transformed to the correspondent database
+     * data type automatically for you, thus securing your application from SQL injections.
+     * If you use string conditions make sure that your values are correctly quoted.
+     * The safest thing you can do is to never use string conditions.
+     *
+     * @param \Closure|array|string|null $conditions The conditions to filter on.
+     * @param array $types Associative array of type names used to bind values to query
+     * @param bool $overwrite whether to reset conditions with passed list or not
+     * @return $this
+     */
+    public function where($conditions = null, array $types = [], bool $overwrite = false);
+}
diff --git a/vendor/cakephp/datasource/QueryTrait.php b/vendor/cakephp/datasource/QueryTrait.php
new file mode 100644
index 0000000..1552561
--- /dev/null
+++ b/vendor/cakephp/datasource/QueryTrait.php
@@ -0,0 +1,1465 @@
+
+     */
+    protected $_formatters = [];
+
+    /**
+     * A query cacher instance if this query has caching enabled.
+     *
+     * @var \Cake\Datasource\QueryCacher|null
+     */
+    protected $_cache;
+
+    /**
+     * Holds any custom options passed using applyOptions that could not be processed
+     * by any method in this class.
+     *
+     * @var array
+     */
+    protected $_options = [];
+
+    /**
+     * Whether the query is standalone or the product of an eager load operation.
+     *
+     * @var bool
+     */
+    protected $_eagerLoaded = false;
+
+    /**
+     * Set the default Table object that will be used by this query
+     * and form the `FROM` clause.
+     *
+     * @param \Cake\Datasource\RepositoryInterface|\Cake\ORM\Table $repository The default table object to use
+     * @return $this
+     * @deprecated 4.5.0 Use `setRepository()` instead.
+     */
+    public function repository(RepositoryInterface $repository)
+    {
+        deprecationWarning('`repository() method is deprecated. Use `setRepository()` instead.');
+
+        return $this->setRepository($repository);
+    }
+
+    /**
+     * Set the default Table object that will be used by this query
+     * and form the `FROM` clause.
+     *
+     * @param \Cake\Datasource\RepositoryInterface|\Cake\ORM\Table $repository The default table object to use
+     * @return $this
+     */
+    public function setRepository(RepositoryInterface $repository)
+    {
+        $this->_repository = $repository;
+
+        return $this;
+    }
+
+    /**
+     * Returns the default table object that will be used by this query,
+     * that is, the table that will appear in the from clause.
+     *
+     * @return \Cake\Datasource\RepositoryInterface
+     */
+    public function getRepository(): RepositoryInterface
+    {
+        return $this->_repository;
+    }
+
+    /**
+     * Set the result set for a query.
+     *
+     * Setting the resultset of a query will make execute() a no-op. Instead
+     * of executing the SQL query and fetching results, the ResultSet provided to this
+     * method will be returned.
+     *
+     * This method is most useful when combined with results stored in a persistent cache.
+     *
+     * @param iterable $results The results this query should return.
+     * @return $this
+     */
+    public function setResult(iterable $results)
+    {
+        $this->_results = $results;
+
+        return $this;
+    }
+
+    /**
+     * Executes this query and returns a results iterator. This function is required
+     * for implementing the IteratorAggregate interface and allows the query to be
+     * iterated without having to call execute() manually, thus making it look like
+     * a result set instead of the query itself.
+     *
+     * @return \Cake\Datasource\ResultSetInterface
+     * @psalm-suppress ImplementedReturnTypeMismatch
+     */
+    #[\ReturnTypeWillChange]
+    public function getIterator()
+    {
+        return $this->all();
+    }
+
+    /**
+     * Enable result caching for this query.
+     *
+     * If a query has caching enabled, it will do the following when executed:
+     *
+     * - Check the cache for $key. If there are results no SQL will be executed.
+     *   Instead the cached results will be returned.
+     * - When the cached data is stale/missing the result set will be cached as the query
+     *   is executed.
+     *
+     * ### Usage
+     *
+     * ```
+     * // Simple string key + config
+     * $query->cache('my_key', 'db_results');
+     *
+     * // Function to generate key.
+     * $query->cache(function ($q) {
+     *   $key = serialize($q->clause('select'));
+     *   $key .= serialize($q->clause('where'));
+     *   return md5($key);
+     * });
+     *
+     * // Using a pre-built cache engine.
+     * $query->cache('my_key', $engine);
+     *
+     * // Disable caching
+     * $query->cache(false);
+     * ```
+     *
+     * @param \Closure|string|false $key Either the cache key or a function to generate the cache key.
+     *   When using a function, this query instance will be supplied as an argument.
+     * @param \Psr\SimpleCache\CacheInterface|string $config Either the name of the cache config to use, or
+     *   a cache engine instance.
+     * @return $this
+     */
+    public function cache($key, $config = 'default')
+    {
+        if ($key === false) {
+            $this->_cache = null;
+
+            return $this;
+        }
+        $this->_cache = new QueryCacher($key, $config);
+
+        return $this;
+    }
+
+    /**
+     * Returns the current configured query `_eagerLoaded` value
+     *
+     * @return bool
+     */
+    public function isEagerLoaded(): bool
+    {
+        return $this->_eagerLoaded;
+    }
+
+    /**
+     * Sets the query instance to be an eager loaded query. If no argument is
+     * passed, the current configured query `_eagerLoaded` value is returned.
+     *
+     * @param bool $value Whether to eager load.
+     * @return $this
+     */
+    public function eagerLoaded(bool $value)
+    {
+        $this->_eagerLoaded = $value;
+
+        return $this;
+    }
+
+    /**
+     * Returns a key => value array representing a single aliased field
+     * that can be passed directly to the select() method.
+     * The key will contain the alias and the value the actual field name.
+     *
+     * If the field is already aliased, then it will not be changed.
+     * If no $alias is passed, the default table for this query will be used.
+     *
+     * @param string $field The field to alias
+     * @param string|null $alias the alias used to prefix the field
+     * @return array
+     */
+    public function aliasField(string $field, ?string $alias = null): array
+    {
+        if (strpos($field, '.') === false) {
+            $alias = $alias ?: $this->getRepository()->getAlias();
+            $aliasedField = $alias . '.' . $field;
+        } else {
+            $aliasedField = $field;
+            [$alias, $field] = explode('.', $field);
+        }
+
+        $key = sprintf('%s__%s', $alias, $field);
+
+        return [$key => $aliasedField];
+    }
+
+    /**
+     * Runs `aliasField()` for each field in the provided list and returns
+     * the result under a single array.
+     *
+     * @param array $fields The fields to alias
+     * @param string|null $defaultAlias The default alias
+     * @return array
+     */
+    public function aliasFields(array $fields, ?string $defaultAlias = null): array
+    {
+        $aliased = [];
+        foreach ($fields as $alias => $field) {
+            if (is_numeric($alias) && is_string($field)) {
+                $aliased += $this->aliasField($field, $defaultAlias);
+                continue;
+            }
+            $aliased[$alias] = $field;
+        }
+
+        return $aliased;
+    }
+
+    /**
+     * Fetch the results for this query.
+     *
+     * Will return either the results set through setResult(), or execute this query
+     * and return the ResultSetDecorator object ready for streaming of results.
+     *
+     * ResultSetDecorator is a traversable object that implements the methods found
+     * on Cake\Collection\Collection.
+     *
+     * @return \Cake\Datasource\ResultSetInterface
+     */
+    public function all(): ResultSetInterface
+    {
+        if ($this->_results !== null) {
+            return $this->_results;
+        }
+
+        $results = null;
+        if ($this->_cache) {
+            $results = $this->_cache->fetch($this);
+        }
+        if ($results === null) {
+            $results = $this->_decorateResults($this->_execute());
+            if ($this->_cache) {
+                $this->_cache->store($this, $results);
+            }
+        }
+        $this->_results = $results;
+
+        return $this->_results;
+    }
+
+    /**
+     * Returns an array representation of the results after executing the query.
+     *
+     * @return array
+     */
+    public function toArray(): array
+    {
+        return $this->all()->toArray();
+    }
+
+    /**
+     * Register a new MapReduce routine to be executed on top of the database results
+     * Both the mapper and caller callable should be invokable objects.
+     *
+     * The MapReduce routing will only be run when the query is executed and the first
+     * result is attempted to be fetched.
+     *
+     * If the third argument is set to true, it will erase previous map reducers
+     * and replace it with the arguments passed.
+     *
+     * @param callable|null $mapper The mapper callable.
+     * @param callable|null $reducer The reducing function.
+     * @param bool $overwrite Set to true to overwrite existing map + reduce functions.
+     * @return $this
+     * @see \Cake\Collection\Iterator\MapReduce for details on how to use emit data to the map reducer.
+     */
+    public function mapReduce(?callable $mapper = null, ?callable $reducer = null, bool $overwrite = false)
+    {
+        if ($overwrite) {
+            $this->_mapReduce = [];
+        }
+        if ($mapper === null) {
+            if (!$overwrite) {
+                throw new InvalidArgumentException('$mapper can be null only when $overwrite is true.');
+            }
+
+            return $this;
+        }
+        $this->_mapReduce[] = compact('mapper', 'reducer');
+
+        return $this;
+    }
+
+    /**
+     * Returns the list of previously registered map reduce routines.
+     *
+     * @return array
+     */
+    public function getMapReducers(): array
+    {
+        return $this->_mapReduce;
+    }
+
+    /**
+     * Registers a new formatter callback function that is to be executed when trying
+     * to fetch the results from the database.
+     *
+     * If the second argument is set to true, it will erase previous formatters
+     * and replace them with the passed first argument.
+     *
+     * Callbacks are required to return an iterator object, which will be used as
+     * the return value for this query's result. Formatter functions are applied
+     * after all the `MapReduce` routines for this query have been executed.
+     *
+     * Formatting callbacks will receive two arguments, the first one being an object
+     * implementing `\Cake\Collection\CollectionInterface`, that can be traversed and
+     * modified at will. The second one being the query instance on which the formatter
+     * callback is being applied.
+     *
+     * Usually the query instance received by the formatter callback is the same query
+     * instance on which the callback was attached to, except for in a joined
+     * association, in that case the callback will be invoked on the association source
+     * side query, and it will receive that query instance instead of the one on which
+     * the callback was originally attached to - see the examples below!
+     *
+     * ### Examples:
+     *
+     * Return all results from the table indexed by id:
+     *
+     * ```
+     * $query->select(['id', 'name'])->formatResults(function ($results) {
+     *     return $results->indexBy('id');
+     * });
+     * ```
+     *
+     * Add a new column to the ResultSet:
+     *
+     * ```
+     * $query->select(['name', 'birth_date'])->formatResults(function ($results) {
+     *     return $results->map(function ($row) {
+     *         $row['age'] = $row['birth_date']->diff(new DateTime)->y;
+     *
+     *         return $row;
+     *     });
+     * });
+     * ```
+     *
+     * Add a new column to the results with respect to the query's hydration configuration:
+     *
+     * ```
+     * $query->formatResults(function ($results, $query) {
+     *     return $results->map(function ($row) use ($query) {
+     *         $data = [
+     *             'bar' => 'baz',
+     *         ];
+     *
+     *         if ($query->isHydrationEnabled()) {
+     *             $row['foo'] = new Foo($data)
+     *         } else {
+     *             $row['foo'] = $data;
+     *         }
+     *
+     *         return $row;
+     *     });
+     * });
+     * ```
+     *
+     * Retaining access to the association target query instance of joined associations,
+     * by inheriting the contain callback's query argument:
+     *
+     * ```
+     * // Assuming a `Articles belongsTo Authors` association that uses the join strategy
+     *
+     * $articlesQuery->contain('Authors', function ($authorsQuery) {
+     *     return $authorsQuery->formatResults(function ($results, $query) use ($authorsQuery) {
+     *         // Here `$authorsQuery` will always be the instance
+     *         // where the callback was attached to.
+     *
+     *         // The instance passed to the callback in the second
+     *         // argument (`$query`), will be the one where the
+     *         // callback is actually being applied to, in this
+     *         // example that would be `$articlesQuery`.
+     *
+     *         // ...
+     *
+     *         return $results;
+     *     });
+     * });
+     * ```
+     *
+     * @param callable|null $formatter The formatting callable.
+     * @param int|bool $mode Whether to overwrite, append or prepend the formatter.
+     * @return $this
+     * @throws \InvalidArgumentException
+     */
+    public function formatResults(?callable $formatter = null, $mode = self::APPEND)
+    {
+        if ($mode === self::OVERWRITE) {
+            $this->_formatters = [];
+        }
+        if ($formatter === null) {
+            if ($mode !== self::OVERWRITE) {
+                throw new InvalidArgumentException('$formatter can be null only when $mode is overwrite.');
+            }
+
+            return $this;
+        }
+
+        if ($mode === self::PREPEND) {
+            array_unshift($this->_formatters, $formatter);
+
+            return $this;
+        }
+
+        $this->_formatters[] = $formatter;
+
+        return $this;
+    }
+
+    /**
+     * Returns the list of previously registered format routines.
+     *
+     * @return array
+     */
+    public function getResultFormatters(): array
+    {
+        return $this->_formatters;
+    }
+
+    /**
+     * Returns the first result out of executing this query, if the query has not been
+     * executed before, it will set the limit clause to 1 for performance reasons.
+     *
+     * ### Example:
+     *
+     * ```
+     * $singleUser = $query->select(['id', 'username'])->first();
+     * ```
+     *
+     * @return \Cake\Datasource\EntityInterface|array|null The first result from the ResultSet.
+     */
+    public function first()
+    {
+        if ($this->_dirty) {
+            $this->limit(1);
+        }
+
+        return $this->all()->first();
+    }
+
+    /**
+     * Get the first result from the executing query or raise an exception.
+     *
+     * @throws \Cake\Datasource\Exception\RecordNotFoundException When there is no first record.
+     * @return \Cake\Datasource\EntityInterface|array The first result from the ResultSet.
+     */
+    public function firstOrFail()
+    {
+        $entity = $this->first();
+        if (!$entity) {
+            $table = $this->getRepository();
+            throw new RecordNotFoundException(sprintf(
+                'Record not found in table "%s"',
+                $table->getTable()
+            ));
+        }
+
+        return $entity;
+    }
+
+    /**
+     * Returns an array with the custom options that were applied to this query
+     * and that were not already processed by another method in this class.
+     *
+     * ### Example:
+     *
+     * ```
+     *  $query->applyOptions(['doABarrelRoll' => true, 'fields' => ['id', 'name']);
+     *  $query->getOptions(); // Returns ['doABarrelRoll' => true]
+     * ```
+     *
+     * @see \Cake\Datasource\QueryInterface::applyOptions() to read about the options that will
+     * be processed by this class and not returned by this function
+     * @return array
+     * @see applyOptions()
+     */
+    public function getOptions(): array
+    {
+        return $this->_options;
+    }
+
+    /**
+     * Enables calling methods from the result set as if they were from this class
+     *
+     * @param string $method the method to call
+     * @param array $arguments list of arguments for the method to call
+     * @return mixed
+     * @throws \BadMethodCallException if no such method exists in result set
+     */
+    public function __call(string $method, array $arguments)
+    {
+        $resultSetClass = $this->_decoratorClass();
+        if (in_array($method, get_class_methods($resultSetClass), true)) {
+            deprecationWarning(sprintf(
+                'Calling `%s` methods, such as `%s()`, on queries is deprecated. ' .
+                'You must call `all()` first (for example, `all()->%s()`).',
+                ResultSetInterface::class,
+                $method,
+                $method,
+            ), 2);
+            $results = $this->all();
+
+            return $results->$method(...$arguments);
+        }
+        throw new BadMethodCallException(
+            sprintf('Unknown method "%s"', $method)
+        );
+    }
+
+    /**
+     * @param callable $callback The callback to apply
+     * @see \Cake\Collection\CollectionInterface::each()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function each(callable $callback): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling each() on a Query is deprecated. ' .
+            'Instead call `$query->all()->each(...)` instead.'
+        );
+
+        return $this->all()->each($callback);
+    }
+
+    /**
+     * @param ?callable $callback The callback to apply
+     * @see \Cake\Collection\CollectionInterface::filter()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function filter(?callable $callback = null): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling filter() on a Query is deprecated. ' .
+            'Instead call `$query->all()->filter(...)` instead.'
+        );
+
+        return $this->all()->filter($callback);
+    }
+
+    /**
+     * @param callable $callback The callback to apply
+     * @see \Cake\Collection\CollectionInterface::reject()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function reject(callable $callback): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling reject() on a Query is deprecated. ' .
+            'Instead call `$query->all()->reject(...)` instead.'
+        );
+
+        return $this->all()->reject($callback);
+    }
+
+    /**
+     * @param callable $callback The callback to apply
+     * @see \Cake\Collection\CollectionInterface::every()
+     * @return bool
+     * @deprecated
+     */
+    public function every(callable $callback): bool
+    {
+        deprecationWarning(
+            '4.3.0 - Calling every() on a Query is deprecated. ' .
+            'Instead call `$query->all()->every(...)` instead.'
+        );
+
+        return $this->all()->every($callback);
+    }
+
+    /**
+     * @param callable $callback The callback to apply
+     * @see \Cake\Collection\CollectionInterface::some()
+     * @return bool
+     * @deprecated
+     */
+    public function some(callable $callback): bool
+    {
+        deprecationWarning(
+            '4.3.0 - Calling some() on a Query is deprecated. ' .
+            'Instead call `$query->all()->some(...)` instead.'
+        );
+
+        return $this->all()->some($callback);
+    }
+
+    /**
+     * @param mixed $value The value to check.
+     * @see \Cake\Collection\CollectionInterface::contains()
+     * @return bool
+     * @deprecated
+     */
+    public function contains($value): bool
+    {
+        deprecationWarning(
+            '4.3.0 - Calling contains() on a Query is deprecated. ' .
+            'Instead call `$query->all()->contains(...)` instead.'
+        );
+
+        return $this->all()->contains($value);
+    }
+
+    /**
+     * @param callable $callback The callback to apply
+     * @see \Cake\Collection\CollectionInterface::map()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function map(callable $callback): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling map() on a Query is deprecated. ' .
+            'Instead call `$query->all()->map(...)` instead.'
+        );
+
+        return $this->all()->map($callback);
+    }
+
+    /**
+     * @param callable $callback The callback to apply
+     * @param mixed $initial The initial value
+     * @see \Cake\Collection\CollectionInterface::reduce()
+     * @return mixed
+     * @deprecated
+     */
+    public function reduce(callable $callback, $initial = null)
+    {
+        deprecationWarning(
+            '4.3.0 - Calling reduce() on a Query is deprecated. ' .
+            'Instead call `$query->all()->reduce(...)` instead.'
+        );
+
+        return $this->all()->reduce($callback, $initial);
+    }
+
+    /**
+     * @param callable|string $path The path to extract
+     * @see \Cake\Collection\CollectionInterface::extract()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function extract($path): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling extract() on a Query is deprecated. ' .
+            'Instead call `$query->all()->extract(...)` instead.'
+        );
+
+        return $this->all()->extract($path);
+    }
+
+    /**
+     * @param callable|string $path The path to max
+     * @param int $sort The SORT_ constant to order by.
+     * @see \Cake\Collection\CollectionInterface::max()
+     * @return mixed
+     * @deprecated
+     */
+    public function max($path, int $sort = \SORT_NUMERIC)
+    {
+        deprecationWarning(
+            '4.3.0 - Calling max() on a Query is deprecated. ' .
+            'Instead call `$query->all()->max(...)` instead.'
+        );
+
+        return $this->all()->max($path, $sort);
+    }
+
+    /**
+     * @param callable|string $path The path to max
+     * @param int $sort The SORT_ constant to order by.
+     * @see \Cake\Collection\CollectionInterface::min()
+     * @return mixed
+     * @deprecated
+     */
+    public function min($path, int $sort = \SORT_NUMERIC)
+    {
+        deprecationWarning(
+            '4.3.0 - Calling min() on a Query is deprecated. ' .
+            'Instead call `$query->all()->min(...)` instead.'
+        );
+
+        return $this->all()->min($path, $sort);
+    }
+
+    /**
+     * @param callable|string|null $path the path to average
+     * @see \Cake\Collection\CollectionInterface::avg()
+     * @return float|int|null
+     * @deprecated
+     */
+    public function avg($path = null)
+    {
+        deprecationwarning(
+            '4.3.0 - calling avg() on a query is deprecated. ' .
+            'instead call `$query->all()->avg(...)` instead.'
+        );
+
+        return $this->all()->avg($path);
+    }
+
+    /**
+     * @param callable|string|null $path the path to average
+     * @see \Cake\Collection\CollectionInterface::median()
+     * @return float|int|null
+     * @deprecated
+     */
+    public function median($path = null)
+    {
+        deprecationwarning(
+            '4.3.0 - calling median() on a query is deprecated. ' .
+            'instead call `$query->all()->median(...)` instead.'
+        );
+
+        return $this->all()->median($path);
+    }
+
+    /**
+     * @param callable|string $path the path to average
+     * @param int $order The \SORT_ constant for the direction you want results in.
+     * @param int $sort The \SORT_ method to use.
+     * @see \Cake\Collection\CollectionInterface::sortBy()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function sortBy($path, int $order = SORT_DESC, int $sort = \SORT_NUMERIC): CollectionInterface
+    {
+        deprecationwarning(
+            '4.3.0 - calling sortBy() on a query is deprecated. ' .
+            'instead call `$query->all()->sortBy(...)` instead.'
+        );
+
+        return $this->all()->sortBy($path, $order, $sort);
+    }
+
+    /**
+     * @param callable|string $path The path to group by
+     * @see \Cake\Collection\CollectionInterface::groupBy()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function groupBy($path): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling groupBy() on a Query is deprecated. ' .
+            'Instead call `$query->all()->groupBy(...)` instead.'
+        );
+
+        return $this->all()->groupBy($path);
+    }
+
+    /**
+     * @param string|callable $path The path to extract
+     * @see \Cake\Collection\CollectionInterface::indexBy()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function indexBy($path): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling indexBy() on a Query is deprecated. ' .
+            'Instead call `$query->all()->indexBy(...)` instead.'
+        );
+
+        return $this->all()->indexBy($path);
+    }
+
+    /**
+     * @param string|callable $path The path to count by
+     * @see \Cake\Collection\CollectionInterface::countBy()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function countBy($path): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling countBy() on a Query is deprecated. ' .
+            'Instead call `$query->all()->countBy(...)` instead.'
+        );
+
+        return $this->all()->countBy($path);
+    }
+
+    /**
+     * @param string|callable $path The path to sum
+     * @see \Cake\Collection\CollectionInterface::sumOf()
+     * @return int|float
+     * @deprecated
+     */
+    public function sumOf($path = null)
+    {
+        deprecationWarning(
+            '4.3.0 - Calling sumOf() on a Query is deprecated. ' .
+                'Instead call `$query->all()->sumOf(...)` instead.'
+        );
+
+        return $this->all()->sumOf($path);
+    }
+
+    /**
+     * @see \Cake\Collection\CollectionInterface::shuffle()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function shuffle(): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling shuffle() on a Query is deprecated. ' .
+            'Instead call `$query->all()->shuffle(...)` instead.'
+        );
+
+        return $this->all()->shuffle();
+    }
+
+    /**
+     * @param int $length The number of samples to select
+     * @see \Cake\Collection\CollectionInterface::sample()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function sample(int $length = 10): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling sample() on a Query is deprecated. ' .
+            'Instead call `$query->all()->sample(...)` instead.'
+        );
+
+        return $this->all()->sample($length);
+    }
+
+    /**
+     * @param int $length The number of elements to take
+     * @param int $offset The offset of the first element to take.
+     * @see \Cake\Collection\CollectionInterface::take()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function take(int $length = 1, int $offset = 0): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling take() on a Query is deprecated. ' .
+            'Instead call `$query->all()->take(...)` instead.'
+        );
+
+        return $this->all()->take($length, $offset);
+    }
+
+    /**
+     * @param int $length The number of items to take.
+     * @see \Cake\Collection\CollectionInterface::takeLast()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function takeLast(int $length): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling takeLast() on a Query is deprecated. ' .
+            'Instead call `$query->all()->takeLast(...)` instead.'
+        );
+
+        return $this->all()->takeLast($length);
+    }
+
+    /**
+     * @param int $length The number of items to skip
+     * @see \Cake\Collection\CollectionInterface::skip()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function skip(int $length): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling skip() on a Query is deprecated. ' .
+            'Instead call `$query->all()->skip(...)` instead.'
+        );
+
+        return $this->all()->skip($length);
+    }
+
+    /**
+     * @param array $conditions The conditions to use.
+     * @see \Cake\Collection\CollectionInterface::match()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function match(array $conditions): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling match() on a Query is deprecated. ' .
+            'Instead call `$query->all()->match(...)` instead.'
+        );
+
+        return $this->all()->match($conditions);
+    }
+
+    /**
+     * @param array $conditions The conditions to apply
+     * @see \Cake\Collection\CollectionInterface::firstMatch()
+     * @return mixed
+     * @deprecated
+     */
+    public function firstMatch(array $conditions)
+    {
+        deprecationWarning(
+            '4.3.0 - Calling firstMatch() on a Query is deprecated. ' .
+            'Instead call `$query->all()->firstMatch(...)` instead.'
+        );
+
+        return $this->all()->firstMatch($conditions);
+    }
+
+    /**
+     * @see \Cake\Collection\CollectionInterface::last()
+     * @deprecated
+     * @return mixed
+     */
+    public function last()
+    {
+        deprecationWarning(
+            '4.3.0 - Calling last() on a Query is deprecated. ' .
+            'Instead call `$query->all()->last(...)` instead.'
+        );
+
+        return $this->all()->last();
+    }
+
+    /**
+     * @param mixed $items The items to append
+     * @see \Cake\Collection\CollectionInterface::append()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function append($items): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling append() on a Query is deprecated. ' .
+            'Instead call `$query->all()->append(...)` instead.'
+        );
+
+        return $this->all()->append($items);
+    }
+
+    /**
+     * @param mixed $item The item to apply
+     * @param mixed $key The key to append with
+     * @see \Cake\Collection\CollectionInterface::appendItem()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function appendItem($item, $key = null): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling appendItem() on a Query is deprecated. ' .
+            'Instead call `$query->all()->appendItem(...)` instead.'
+        );
+
+        return $this->all()->appendItem($item, $key);
+    }
+
+    /**
+     * @param mixed $items The items to prepend.
+     * @see \Cake\Collection\CollectionInterface::prepend()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function prepend($items): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling prepend() on a Query is deprecated. ' .
+            'Instead call `$query->all()->prepend(...)` instead.'
+        );
+
+        return $this->all()->prepend($items);
+    }
+
+    /**
+     * @param mixed $item The item to prepend
+     * @param mixed $key The key to use.
+     * @see \Cake\Collection\CollectionInterface::prependItem()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function prependItem($item, $key = null): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling prependItem() on a Query is deprecated. ' .
+            'Instead call `$query->all()->prependItem(...)` instead.'
+        );
+
+        return $this->all()->prependItem($item, $key);
+    }
+
+    /**
+     * @param callable|string $keyPath The path for keys
+     * @param callable|string $valuePath The path for values
+     * @param callable|string|null $groupPath The path for grouping
+     * @see \Cake\Collection\CollectionInterface::combine()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function combine($keyPath, $valuePath, $groupPath = null): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling combine() on a Query is deprecated. ' .
+            'Instead call `$query->all()->combine(...)` instead.'
+        );
+
+        return $this->all()->combine($keyPath, $valuePath, $groupPath);
+    }
+
+    /**
+     * @param callable|string $idPath The path to ids
+     * @param callable|string $parentPath The path to parents
+     * @param string $nestingKey Key used for nesting children.
+     * @see \Cake\Collection\CollectionInterface::nest()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function nest($idPath, $parentPath, string $nestingKey = 'children'): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling nest() on a Query is deprecated. ' .
+            'Instead call `$query->all()->nest(...)` instead.'
+        );
+
+        return $this->all()->nest($idPath, $parentPath, $nestingKey);
+    }
+
+    /**
+     * @param string $path The path to insert on
+     * @param mixed $values The values to insert.
+     * @see \Cake\Collection\CollectionInterface::insert()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function insert(string $path, $values): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling insert() on a Query is deprecated. ' .
+            'Instead call `$query->all()->insert(...)` instead.'
+        );
+
+        return $this->all()->insert($path, $values);
+    }
+
+    /**
+     * @see \Cake\Collection\CollectionInterface::toList()
+     * @return array
+     * @deprecated
+     */
+    public function toList(): array
+    {
+        deprecationWarning(
+            '4.3.0 - Calling toList() on a Query is deprecated. ' .
+            'Instead call `$query->all()->toList(...)` instead.'
+        );
+
+        return $this->all()->toList();
+    }
+
+    /**
+     * @param bool $keepKeys Whether or not keys should be kept
+     * @see \Cake\Collection\CollectionInterface::compile()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function compile(bool $keepKeys = true): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling compile() on a Query is deprecated. ' .
+            'Instead call `$query->all()->compile(...)` instead.'
+        );
+
+        return $this->all()->compile($keepKeys);
+    }
+
+    /**
+     * @see \Cake\Collection\CollectionInterface::lazy()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function lazy(): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling lazy() on a Query is deprecated. ' .
+            'Instead call `$query->all()->lazy(...)` instead.'
+        );
+
+        return $this->all()->lazy();
+    }
+
+    /**
+     * @see \Cake\Collection\CollectionInterface::buffered()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function buffered(): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling buffered() on a Query is deprecated. ' .
+            'Instead call `$query->all()->buffered(...)` instead.'
+        );
+
+        return $this->all()->buffered();
+    }
+
+    /**
+     * @param string|int $order The order in which to return the elements
+     * @param callable|string $nestingKey The key name under which children are nested
+     * @see \Cake\Collection\CollectionInterface::listNested()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function listNested($order = 'desc', $nestingKey = 'children'): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling listNested() on a Query is deprecated. ' .
+            'Instead call `$query->all()->listNested(...)` instead.'
+        );
+
+        return $this->all()->listNested($order, $nestingKey);
+    }
+
+    /**
+     * @param callable|array $condition the method that will receive each of the elements and
+     *   returns true when the iteration should be stopped.
+     * @see \Cake\Collection\CollectionInterface::stopWhen()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function stopWhen($condition): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling stopWhen() on a Query is deprecated. ' .
+            'Instead call `$query->all()->stopWhen(...)` instead.'
+        );
+
+        return $this->all()->stopWhen($condition);
+    }
+
+    /**
+     * @param callable|null $callback A callable function that will receive each of
+     *  items in the collection.
+     * @see \Cake\Collection\CollectionInterface::unfold()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function unfold(?callable $callback = null): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling unfold() on a Query is deprecated. ' .
+            'Instead call `$query->all()->unfold(...)` instead.'
+        );
+
+        return $this->all()->unfold($callback);
+    }
+
+    /**
+     * @param callable $callback A callable function that will receive each of
+     *  items in the collection.
+     * @see \Cake\Collection\CollectionInterface::through()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function through(callable $callback): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling through() on a Query is deprecated. ' .
+            'Instead call `$query->all()->through(...)` instead.'
+        );
+
+        return $this->all()->through($callback);
+    }
+
+    /**
+     * @param iterable ...$items The collections to zip.
+     * @see \Cake\Collection\CollectionInterface::zip()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function zip(iterable $items): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling zip() on a Query is deprecated. ' .
+            'Instead call `$query->all()->zip(...)` instead.'
+        );
+
+        return $this->all()->zip($items);
+    }
+
+    /**
+     * @param iterable ...$items The collections to zip.
+     * @param callable $callback The function to use for zipping the elements together.
+     * @see \Cake\Collection\CollectionInterface::zipWith()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function zipWith(iterable $items, $callback): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling zipWith() on a Query is deprecated. ' .
+            'Instead call `$query->all()->zipWith(...)` instead.'
+        );
+
+        return $this->all()->zipWith($items, $callback);
+    }
+
+    /**
+     * @param int $chunkSize The maximum size for each chunk
+     * @see \Cake\Collection\CollectionInterface::chunk()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function chunk(int $chunkSize): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling chunk() on a Query is deprecated. ' .
+            'Instead call `$query->all()->chunk(...)` instead.'
+        );
+
+        return $this->all()->chunk($chunkSize);
+    }
+
+    /**
+     * @param int $chunkSize The maximum size for each chunk
+     * @param bool $keepKeys If the keys of the array should be kept
+     * @see \Cake\Collection\CollectionInterface::chunkWithKeys()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function chunkWithKeys(int $chunkSize, bool $keepKeys = true): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling chunkWithKeys() on a Query is deprecated. ' .
+            'Instead call `$query->all()->chunkWithKeys(...)` instead.'
+        );
+
+        return $this->all()->chunkWithKeys($chunkSize, $keepKeys);
+    }
+
+    /**
+     * @see \Cake\Collection\CollectionInterface::isEmpty()
+     * @return bool
+     * @deprecated
+     */
+    public function isEmpty(): bool
+    {
+        deprecationWarning(
+            '4.3.0 - Calling isEmpty() on a Query is deprecated. ' .
+            'Instead call `$query->all()->isEmpty(...)` instead.'
+        );
+
+        return $this->all()->isEmpty();
+    }
+
+    /**
+     * @see \Cake\Collection\CollectionInterface::unwrap()
+     * @return \Traversable
+     * @deprecated
+     */
+    public function unwrap(): Traversable
+    {
+        deprecationWarning(
+            '4.3.0 - Calling unwrap() on a Query is deprecated. ' .
+            'Instead call `$query->all()->unwrap(...)` instead.'
+        );
+
+        return $this->all()->unwrap();
+    }
+
+    /**
+     * @see \Cake\Collection\CollectionInterface::transpose()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function transpose(): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling transpose() on a Query is deprecated. ' .
+            'Instead call `$query->all()->transpose(...)` instead.'
+        );
+
+        return $this->all()->transpose();
+    }
+
+    /**
+     * @see \Cake\Collection\CollectionInterface::count()
+     * @return int
+     * @deprecated
+     */
+    public function count(): int
+    {
+        deprecationWarning(
+            '4.3.0 - Calling count() on a Query is deprecated. ' .
+            'Instead call `$query->all()->count(...)` instead.'
+        );
+
+        return $this->all()->count();
+    }
+
+    /**
+     * @see \Cake\Collection\CollectionInterface::countKeys()
+     * @return int
+     * @deprecated
+     */
+    public function countKeys(): int
+    {
+        deprecationWarning(
+            '4.3.0 - Calling countKeys() on a Query is deprecated. ' .
+            'Instead call `$query->all()->countKeys(...)` instead.'
+        );
+
+        return $this->all()->countKeys();
+    }
+
+    /**
+     * @param callable|null $operation A callable that allows you to customize the product result.
+     * @param callable|null $filter A filtering callback that must return true for a result to be part
+     *   of the final results.
+     * @see \Cake\Collection\CollectionInterface::cartesianProduct()
+     * @return \Cake\Collection\CollectionInterface
+     * @deprecated
+     */
+    public function cartesianProduct(?callable $operation = null, ?callable $filter = null): CollectionInterface
+    {
+        deprecationWarning(
+            '4.3.0 - Calling cartesianProduct() on a Query is deprecated. ' .
+            'Instead call `$query->all()->cartesianProduct(...)` instead.'
+        );
+
+        return $this->all()->cartesianProduct($operation, $filter);
+    }
+
+    /**
+     * Populates or adds parts to current query clauses using an array.
+     * This is handy for passing all query clauses at once.
+     *
+     * @param array $options the options to be applied
+     * @return $this
+     */
+    abstract public function applyOptions(array $options);
+
+    /**
+     * Executes this query and returns a traversable object containing the results
+     *
+     * @return \Cake\Datasource\ResultSetInterface
+     */
+    abstract protected function _execute(): ResultSetInterface;
+
+    /**
+     * Decorates the results iterator with MapReduce routines and formatters
+     *
+     * @param \Traversable $result Original results
+     * @return \Cake\Datasource\ResultSetInterface
+     */
+    protected function _decorateResults(Traversable $result): ResultSetInterface
+    {
+        $decorator = $this->_decoratorClass();
+        foreach ($this->_mapReduce as $functions) {
+            $result = new MapReduce($result, $functions['mapper'], $functions['reducer']);
+        }
+
+        if (!empty($this->_mapReduce)) {
+            $result = new $decorator($result);
+        }
+
+        foreach ($this->_formatters as $formatter) {
+            $result = $formatter($result, $this);
+        }
+
+        if (!empty($this->_formatters) && !($result instanceof $decorator)) {
+            $result = new $decorator($result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns the name of the class to be used for decorating results
+     *
+     * @return string
+     * @psalm-return class-string<\Cake\Datasource\ResultSetInterface>
+     */
+    protected function _decoratorClass(): string
+    {
+        return ResultSetDecorator::class;
+    }
+}
diff --git a/vendor/cakephp/datasource/README.md b/vendor/cakephp/datasource/README.md
new file mode 100644
index 0000000..38626ea
--- /dev/null
+++ b/vendor/cakephp/datasource/README.md
@@ -0,0 +1,82 @@
+[![Total Downloads](https://img.shields.io/packagist/dt/cakephp/datasource.svg?style=flat-square)](https://packagist.org/packages/cakephp/datasource)
+[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE.txt)
+
+# CakePHP Datasource Library
+
+This library contains interfaces for implementing Repositories and Entities using any data source,
+a class for managing connections to datasources and traits to help you quickly implement the
+interfaces provided by this package.
+
+## Repositories
+
+A repository is a class capable of interfacing with a data source using operations such as
+`find`, `save` and  `delete` by using intermediate query objects for expressing commands to
+the data store and returning Entities as the single result unit of such system.
+
+In the case of a Relational database, a Repository would be a `Table`, which can be return single
+or multiple `Entity` objects by using a `Query`.
+
+This library exposes the following interfaces for creating a system that implements the
+repository pattern and is compatible with the CakePHP framework:
+
+* `RepositoryInterface` - Describes the methods for a base repository class.
+* `EntityInterface` - Describes the methods for a single result object.
+* `ResultSetInterface` - Represents the idea of a collection of Entities as a result of a query.
+
+Additionally, this package provides a few traits and classes you can use in your own implementations:
+
+* `EntityTrait` - Contains the default implementation for the `EntityInterface`.
+* `QueryTrait` - Exposes the methods for creating a query object capable of returning decoratable collections.
+* `ResultSetDecorator` - Decorates any traversable object, so it complies with `ResultSetInterface`.
+
+
+## Connections
+
+This library contains a couple of utility classes meant to create and manage
+connection objects. Connections are typically used in repositories for
+interfacing with the actual data source system.
+
+The `ConnectionManager` class acts as a registry to access database connections
+your application has. It provides a place that other objects can get references
+to existing connections. Creating connections with the `ConnectionManager` is
+easy:
+
+```php
+use Cake\Datasource\ConnectionManager;
+
+ConnectionManager::config('connection-one', [
+    'className' => 'MyApp\Connections\CustomConnection',
+    'param1' => 'value',
+    'param2' => 'another value'
+]);
+
+ConnectionManager::config('connection-two', [
+    'className' => 'MyApp\Connections\CustomConnection',
+    'param1' => 'different value',
+    'param2' => 'another value'
+]);
+```
+
+When requested, the `ConnectionManager` will instantiate
+`MyApp\Connections\CustomConnection` by passing `param1` and `param2` inside an
+array as the first argument of the constructor.
+
+Once configured connections can be fetched using `ConnectionManager::get()`.
+This method will construct and load a connection if it has not been built
+before, or return the existing known connection:
+
+```php
+use Cake\Datasource\ConnectionManager;
+$conn = ConnectionManager::get('master');
+```
+
+It is also possible to store connection objects by passing the instance directly to the manager:
+
+```php
+use Cake\Datasource\ConnectionManager;
+$conn = ConnectionManager::config('other', $connectionInstance);
+```
+
+## Documentation
+
+Please make sure you check the [official API documentation](https://api.cakephp.org/4.x/namespace-Cake.Datasource.html)
diff --git a/vendor/cakephp/datasource/RepositoryInterface.php b/vendor/cakephp/datasource/RepositoryInterface.php
new file mode 100644
index 0000000..93a0041
--- /dev/null
+++ b/vendor/cakephp/datasource/RepositoryInterface.php
@@ -0,0 +1,252 @@
+ $options An array that will be passed to Query::applyOptions()
+     * @return \Cake\Datasource\QueryInterface
+     */
+    public function find(string $type = 'all', array $options = []);
+
+    /**
+     * Returns a single record after finding it by its primary key, if no record is
+     * found this method throws an exception.
+     *
+     * ### Example:
+     *
+     * ```
+     * $id = 10;
+     * $article = $articles->get($id);
+     *
+     * $article = $articles->get($id, ['contain' => ['Comments]]);
+     * ```
+     *
+     * @param mixed $primaryKey primary key value to find
+     * @param array $options options accepted by `Table::find()`
+     * @throws \Cake\Datasource\Exception\RecordNotFoundException if the record with such id
+     * could not be found
+     * @return \Cake\Datasource\EntityInterface
+     * @see \Cake\Datasource\RepositoryInterface::find()
+     */
+    public function get($primaryKey, array $options = []): EntityInterface;
+
+    /**
+     * Creates a new Query instance for this repository
+     *
+     * @return \Cake\Datasource\QueryInterface
+     */
+    public function query();
+
+    /**
+     * Update all matching records.
+     *
+     * Sets the $fields to the provided values based on $conditions.
+     * This method will *not* trigger beforeSave/afterSave events. If you need those
+     * first load a collection of records and update them.
+     *
+     * @param \Cake\Database\Expression\QueryExpression|\Closure|array|string $fields A hash of field => new value.
+     * @param mixed $conditions Conditions to be used, accepts anything Query::where()
+     * can take.
+     * @return int Count Returns the affected rows.
+     */
+    public function updateAll($fields, $conditions): int;
+
+    /**
+     * Deletes all records matching the provided conditions.
+     *
+     * This method will *not* trigger beforeDelete/afterDelete events. If you
+     * need those first load a collection of records and delete them.
+     *
+     * This method will *not* execute on associations' `cascade` attribute. You should
+     * use database foreign keys + ON CASCADE rules if you need cascading deletes combined
+     * with this method.
+     *
+     * @param mixed $conditions Conditions to be used, accepts anything Query::where()
+     * can take.
+     * @return int Returns the number of affected rows.
+     * @see \Cake\Datasource\RepositoryInterface::delete()
+     */
+    public function deleteAll($conditions): int;
+
+    /**
+     * Returns true if there is any record in this repository matching the specified
+     * conditions.
+     *
+     * @param array $conditions list of conditions to pass to the query
+     * @return bool
+     */
+    public function exists($conditions): bool;
+
+    /**
+     * Persists an entity based on the fields that are marked as dirty and
+     * returns the same entity after a successful save or false in case
+     * of any error.
+     *
+     * @param \Cake\Datasource\EntityInterface $entity the entity to be saved
+     * @param \ArrayAccess|array $options The options to use when saving.
+     * @return \Cake\Datasource\EntityInterface|false
+     */
+    public function save(EntityInterface $entity, $options = []);
+
+    /**
+     * Delete a single entity.
+     *
+     * Deletes an entity and possibly related associations from the database
+     * based on the 'dependent' option used when defining the association.
+     *
+     * @param \Cake\Datasource\EntityInterface $entity The entity to remove.
+     * @param \ArrayAccess|array $options The options for the delete.
+     * @return bool success
+     */
+    public function delete(EntityInterface $entity, $options = []): bool;
+
+    /**
+     * This creates a new entity object.
+     *
+     * Careful: This does not trigger any field validation.
+     * This entity can be persisted without validation error as empty record.
+     * Always patch in required fields before saving.
+     *
+     * @return \Cake\Datasource\EntityInterface
+     */
+    public function newEmptyEntity(): EntityInterface;
+
+    /**
+     * Create a new entity + associated entities from an array.
+     *
+     * This is most useful when hydrating request data back into entities.
+     * For example, in your controller code:
+     *
+     * ```
+     * $article = $this->Articles->newEntity($this->request->getData());
+     * ```
+     *
+     * The hydrated entity will correctly do an insert/update based
+     * on the primary key data existing in the database when the entity
+     * is saved. Until the entity is saved, it will be a detached record.
+     *
+     * @param array $data The data to build an entity with.
+     * @param array $options A list of options for the object hydration.
+     * @return \Cake\Datasource\EntityInterface
+     */
+    public function newEntity(array $data, array $options = []): EntityInterface;
+
+    /**
+     * Create a list of entities + associated entities from an array.
+     *
+     * This is most useful when hydrating request data back into entities.
+     * For example, in your controller code:
+     *
+     * ```
+     * $articles = $this->Articles->newEntities($this->request->getData());
+     * ```
+     *
+     * The hydrated entities can then be iterated and saved.
+     *
+     * @param array $data The data to build an entity with.
+     * @param array $options A list of options for the objects hydration.
+     * @return array<\Cake\Datasource\EntityInterface> An array of hydrated records.
+     */
+    public function newEntities(array $data, array $options = []): array;
+
+    /**
+     * Merges the passed `$data` into `$entity` respecting the accessible
+     * fields configured on the entity. Returns the same entity after being
+     * altered.
+     *
+     * This is most useful when editing an existing entity using request data:
+     *
+     * ```
+     * $article = $this->Articles->patchEntity($article, $this->request->getData());
+     * ```
+     *
+     * @param \Cake\Datasource\EntityInterface $entity the entity that will get the
+     * data merged in
+     * @param array $data key value list of fields to be merged into the entity
+     * @param array $options A list of options for the object hydration.
+     * @return \Cake\Datasource\EntityInterface
+     */
+    public function patchEntity(EntityInterface $entity, array $data, array $options = []): EntityInterface;
+
+    /**
+     * Merges each of the elements passed in `$data` into the entities
+     * found in `$entities` respecting the accessible fields configured on the entities.
+     * Merging is done by matching the primary key in each of the elements in `$data`
+     * and `$entities`.
+     *
+     * This is most useful when editing a list of existing entities using request data:
+     *
+     * ```
+     * $article = $this->Articles->patchEntities($articles, $this->request->getData());
+     * ```
+     *
+     * @param iterable<\Cake\Datasource\EntityInterface> $entities the entities that will get the
+     * data merged in
+     * @param array $data list of arrays to be merged into the entities
+     * @param array $options A list of options for the objects hydration.
+     * @return array<\Cake\Datasource\EntityInterface>
+     */
+    public function patchEntities(iterable $entities, array $data, array $options = []): array;
+}
diff --git a/vendor/cakephp/datasource/ResultSetDecorator.php b/vendor/cakephp/datasource/ResultSetDecorator.php
new file mode 100644
index 0000000..85d98b6
--- /dev/null
+++ b/vendor/cakephp/datasource/ResultSetDecorator.php
@@ -0,0 +1,61 @@
+
+ */
+class ResultSetDecorator extends Collection implements ResultSetInterface
+{
+    /**
+     * Make this object countable.
+     *
+     * Part of the Countable interface. Calling this method
+     * will convert the underlying traversable object into an array and
+     * get the count of the underlying data.
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        $iterator = $this->getInnerIterator();
+        if ($iterator instanceof Countable) {
+            return $iterator->count();
+        }
+
+        return count($this->toArray());
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function __debugInfo(): array
+    {
+        $parentInfo = parent::__debugInfo();
+        $limit = Configure::read('App.ResultSetDebugLimit', 10);
+
+        return array_merge($parentInfo, ['items' => $this->take($limit)->toArray()]);
+    }
+}
diff --git a/vendor/cakephp/datasource/ResultSetInterface.php b/vendor/cakephp/datasource/ResultSetInterface.php
new file mode 100644
index 0000000..c193fac
--- /dev/null
+++ b/vendor/cakephp/datasource/ResultSetInterface.php
@@ -0,0 +1,30 @@
+
+     */
+    protected $options = [];
+
+    /**
+     * Rule callable
+     *
+     * @var callable
+     */
+    protected $rule;
+
+    /**
+     * Constructor
+     *
+     * ### Options
+     *
+     * - `errorField` The field errors should be set onto.
+     * - `message` The error message.
+     *
+     * Individual rules may have additional options that can be
+     * set here. Any options will be passed into the rule as part of the
+     * rule $scope.
+     *
+     * @param callable $rule The rule to be invoked.
+     * @param ?string $name The name of the rule. Used in error messages.
+     * @param array $options The options for the rule. See above.
+     */
+    public function __construct(callable $rule, ?string $name, array $options = [])
+    {
+        $this->rule = $rule;
+        $this->name = $name;
+        $this->options = $options;
+    }
+
+    /**
+     * Set options for the rule invocation.
+     *
+     * Old options will be merged with the new ones.
+     *
+     * @param array $options The options to set.
+     * @return $this
+     */
+    public function setOptions(array $options)
+    {
+        $this->options = $options + $this->options;
+
+        return $this;
+    }
+
+    /**
+     * Set the rule name.
+     *
+     * Only truthy names will be set.
+     *
+     * @param string|null $name The name to set.
+     * @return $this
+     */
+    public function setName(?string $name)
+    {
+        if ($name) {
+            $this->name = $name;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Invoke the rule.
+     *
+     * @param \Cake\Datasource\EntityInterface $entity The entity the rule
+     *   should apply to.
+     * @param array $scope The rule's scope/options.
+     * @return bool Whether the rule passed.
+     */
+    public function __invoke(EntityInterface $entity, array $scope): bool
+    {
+        $rule = $this->rule;
+        $pass = $rule($entity, $this->options + $scope);
+        if ($pass === true || empty($this->options['errorField'])) {
+            return $pass === true;
+        }
+
+        $message = $this->options['message'] ?? 'invalid';
+        if (is_string($pass)) {
+            $message = $pass;
+        }
+        if ($this->name) {
+            $message = [$this->name => $message];
+        } else {
+            $message = [$message];
+        }
+        $errorField = $this->options['errorField'];
+        $entity->setError($errorField, $message);
+
+        if ($entity instanceof InvalidPropertyInterface && isset($entity->{$errorField})) {
+            $invalidValue = $entity->{$errorField};
+            $entity->setInvalidField($errorField, $invalidValue);
+        }
+
+        /** @phpstan-ignore-next-line */
+        return $pass === true;
+    }
+}
diff --git a/vendor/cakephp/datasource/RulesAwareTrait.php b/vendor/cakephp/datasource/RulesAwareTrait.php
new file mode 100644
index 0000000..5c01614
--- /dev/null
+++ b/vendor/cakephp/datasource/RulesAwareTrait.php
@@ -0,0 +1,120 @@
+rulesChecker();
+        $options = $options ?: new ArrayObject();
+        $options = is_array($options) ? new ArrayObject($options) : $options;
+        $hasEvents = ($this instanceof EventDispatcherInterface);
+
+        if ($hasEvents) {
+            $event = $this->dispatchEvent(
+                'Model.beforeRules',
+                compact('entity', 'options', 'operation')
+            );
+            if ($event->isStopped()) {
+                return $event->getResult();
+            }
+        }
+
+        $result = $rules->check($entity, $operation, $options->getArrayCopy());
+
+        if ($hasEvents) {
+            $event = $this->dispatchEvent(
+                'Model.afterRules',
+                compact('entity', 'options', 'result', 'operation')
+            );
+
+            if ($event->isStopped()) {
+                return $event->getResult();
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns the RulesChecker for this instance.
+     *
+     * A RulesChecker object is used to test an entity for validity
+     * on rules that may involve complex logic or data that
+     * needs to be fetched from relevant datasources.
+     *
+     * @see \Cake\Datasource\RulesChecker
+     * @return \Cake\Datasource\RulesChecker
+     */
+    public function rulesChecker(): RulesChecker
+    {
+        if ($this->_rulesChecker !== null) {
+            return $this->_rulesChecker;
+        }
+        /** @psalm-var class-string<\Cake\Datasource\RulesChecker> $class */
+        $class = defined('static::RULES_CLASS') ? static::RULES_CLASS : RulesChecker::class;
+        /** @psalm-suppress ArgumentTypeCoercion */
+        $this->_rulesChecker = $this->buildRules(new $class(['repository' => $this]));
+        $this->dispatchEvent('Model.buildRules', ['rules' => $this->_rulesChecker]);
+
+        return $this->_rulesChecker;
+    }
+
+    /**
+     * Returns a RulesChecker object after modifying the one that was supplied.
+     *
+     * Subclasses should override this method in order to initialize the rules to be applied to
+     * entities saved by this instance.
+     *
+     * @param \Cake\Datasource\RulesChecker $rules The rules object to be modified.
+     * @return \Cake\Datasource\RulesChecker
+     */
+    public function buildRules(RulesChecker $rules): RulesChecker
+    {
+        return $rules;
+    }
+}
diff --git a/vendor/cakephp/datasource/RulesChecker.php b/vendor/cakephp/datasource/RulesChecker.php
new file mode 100644
index 0000000..c523eba
--- /dev/null
+++ b/vendor/cakephp/datasource/RulesChecker.php
@@ -0,0 +1,330 @@
+
+     */
+    protected $_rules = [];
+
+    /**
+     * The list of rules to check during create operations
+     *
+     * @var array<\Cake\Datasource\RuleInvoker>
+     */
+    protected $_createRules = [];
+
+    /**
+     * The list of rules to check during update operations
+     *
+     * @var array<\Cake\Datasource\RuleInvoker>
+     */
+    protected $_updateRules = [];
+
+    /**
+     * The list of rules to check during delete operations
+     *
+     * @var array<\Cake\Datasource\RuleInvoker>
+     */
+    protected $_deleteRules = [];
+
+    /**
+     * List of options to pass to every callable rule
+     *
+     * @var array
+     */
+    protected $_options = [];
+
+    /**
+     * Whether to use I18n functions for translating default error messages
+     *
+     * @var bool
+     */
+    protected $_useI18n = false;
+
+    /**
+     * Constructor. Takes the options to be passed to all rules.
+     *
+     * @param array $options The options to pass to every rule
+     */
+    public function __construct(array $options = [])
+    {
+        $this->_options = $options;
+        $this->_useI18n = function_exists('\Cake\I18n\__d');
+    }
+
+    /**
+     * Adds a rule that will be applied to the entity both on create and update
+     * operations.
+     *
+     * ### Options
+     *
+     * The options array accept the following special keys:
+     *
+     * - `errorField`: The name of the entity field that will be marked as invalid
+     *    if the rule does not pass.
+     * - `message`: The error message to set to `errorField` if the rule does not pass.
+     *
+     * @param callable $rule A callable function or object that will return whether
+     * the entity is valid or not.
+     * @param array|string|null $name The alias for a rule, or an array of options.
+     * @param array $options List of extra options to pass to the rule callable as
+     * second argument.
+     * @return $this
+     */
+    public function add(callable $rule, $name = null, array $options = [])
+    {
+        $this->_rules[] = $this->_addError($rule, $name, $options);
+
+        return $this;
+    }
+
+    /**
+     * Adds a rule that will be applied to the entity on create operations.
+     *
+     * ### Options
+     *
+     * The options array accept the following special keys:
+     *
+     * - `errorField`: The name of the entity field that will be marked as invalid
+     *    if the rule does not pass.
+     * - `message`: The error message to set to `errorField` if the rule does not pass.
+     *
+     * @param callable $rule A callable function or object that will return whether
+     * the entity is valid or not.
+     * @param array|string|null $name The alias for a rule or an array of options.
+     * @param array $options List of extra options to pass to the rule callable as
+     * second argument.
+     * @return $this
+     */
+    public function addCreate(callable $rule, $name = null, array $options = [])
+    {
+        $this->_createRules[] = $this->_addError($rule, $name, $options);
+
+        return $this;
+    }
+
+    /**
+     * Adds a rule that will be applied to the entity on update operations.
+     *
+     * ### Options
+     *
+     * The options array accept the following special keys:
+     *
+     * - `errorField`: The name of the entity field that will be marked as invalid
+     *    if the rule does not pass.
+     * - `message`: The error message to set to `errorField` if the rule does not pass.
+     *
+     * @param callable $rule A callable function or object that will return whether
+     * the entity is valid or not.
+     * @param array|string|null $name The alias for a rule, or an array of options.
+     * @param array $options List of extra options to pass to the rule callable as
+     * second argument.
+     * @return $this
+     */
+    public function addUpdate(callable $rule, $name = null, array $options = [])
+    {
+        $this->_updateRules[] = $this->_addError($rule, $name, $options);
+
+        return $this;
+    }
+
+    /**
+     * Adds a rule that will be applied to the entity on delete operations.
+     *
+     * ### Options
+     *
+     * The options array accept the following special keys:
+     *
+     * - `errorField`: The name of the entity field that will be marked as invalid
+     *    if the rule does not pass.
+     * - `message`: The error message to set to `errorField` if the rule does not pass.
+     *
+     * @param callable $rule A callable function or object that will return whether
+     * the entity is valid or not.
+     * @param array|string|null $name The alias for a rule, or an array of options.
+     * @param array $options List of extra options to pass to the rule callable as
+     * second argument.
+     * @return $this
+     */
+    public function addDelete(callable $rule, $name = null, array $options = [])
+    {
+        $this->_deleteRules[] = $this->_addError($rule, $name, $options);
+
+        return $this;
+    }
+
+    /**
+     * Runs each of the rules by passing the provided entity and returns true if all
+     * of them pass. The rules to be applied are depended on the $mode parameter which
+     * can only be RulesChecker::CREATE, RulesChecker::UPDATE or RulesChecker::DELETE
+     *
+     * @param \Cake\Datasource\EntityInterface $entity The entity to check for validity.
+     * @param string $mode Either 'create, 'update' or 'delete'.
+     * @param array $options Extra options to pass to checker functions.
+     * @return bool
+     * @throws \InvalidArgumentException if an invalid mode is passed.
+     */
+    public function check(EntityInterface $entity, string $mode, array $options = []): bool
+    {
+        if ($mode === self::CREATE) {
+            return $this->checkCreate($entity, $options);
+        }
+
+        if ($mode === self::UPDATE) {
+            return $this->checkUpdate($entity, $options);
+        }
+
+        if ($mode === self::DELETE) {
+            return $this->checkDelete($entity, $options);
+        }
+
+        throw new InvalidArgumentException('Wrong checking mode: ' . $mode);
+    }
+
+    /**
+     * Runs each of the rules by passing the provided entity and returns true if all
+     * of them pass. The rules selected will be only those specified to be run on 'create'
+     *
+     * @param \Cake\Datasource\EntityInterface $entity The entity to check for validity.
+     * @param array $options Extra options to pass to checker functions.
+     * @return bool
+     */
+    public function checkCreate(EntityInterface $entity, array $options = []): bool
+    {
+        return $this->_checkRules($entity, $options, array_merge($this->_rules, $this->_createRules));
+    }
+
+    /**
+     * Runs each of the rules by passing the provided entity and returns true if all
+     * of them pass. The rules selected will be only those specified to be run on 'update'
+     *
+     * @param \Cake\Datasource\EntityInterface $entity The entity to check for validity.
+     * @param array $options Extra options to pass to checker functions.
+     * @return bool
+     */
+    public function checkUpdate(EntityInterface $entity, array $options = []): bool
+    {
+        return $this->_checkRules($entity, $options, array_merge($this->_rules, $this->_updateRules));
+    }
+
+    /**
+     * Runs each of the rules by passing the provided entity and returns true if all
+     * of them pass. The rules selected will be only those specified to be run on 'delete'
+     *
+     * @param \Cake\Datasource\EntityInterface $entity The entity to check for validity.
+     * @param array $options Extra options to pass to checker functions.
+     * @return bool
+     */
+    public function checkDelete(EntityInterface $entity, array $options = []): bool
+    {
+        return $this->_checkRules($entity, $options, $this->_deleteRules);
+    }
+
+    /**
+     * Used by top level functions checkDelete, checkCreate and checkUpdate, this function
+     * iterates an array containing the rules to be checked and checks them all.
+     *
+     * @param \Cake\Datasource\EntityInterface $entity The entity to check for validity.
+     * @param array $options Extra options to pass to checker functions.
+     * @param array<\Cake\Datasource\RuleInvoker> $rules The list of rules that must be checked.
+     * @return bool
+     */
+    protected function _checkRules(EntityInterface $entity, array $options = [], array $rules = []): bool
+    {
+        $success = true;
+        $options += $this->_options;
+        foreach ($rules as $rule) {
+            $success = $rule($entity, $options) && $success;
+        }
+
+        return $success;
+    }
+
+    /**
+     * Utility method for decorating any callable so that if it returns false, the correct
+     * property in the entity is marked as invalid.
+     *
+     * @param callable|\Cake\Datasource\RuleInvoker $rule The rule to decorate
+     * @param array|string|null $name The alias for a rule or an array of options
+     * @param array $options The options containing the error message and field.
+     * @return \Cake\Datasource\RuleInvoker
+     */
+    protected function _addError(callable $rule, $name = null, array $options = []): RuleInvoker
+    {
+        if (is_array($name)) {
+            $options = $name;
+            $name = null;
+        }
+
+        if (!($rule instanceof RuleInvoker)) {
+            $rule = new RuleInvoker($rule, $name, $options);
+        } else {
+            $rule->setOptions($options)->setName($name);
+        }
+
+        return $rule;
+    }
+}
diff --git a/vendor/cakephp/datasource/SchemaInterface.php b/vendor/cakephp/datasource/SchemaInterface.php
new file mode 100644
index 0000000..08ab8be
--- /dev/null
+++ b/vendor/cakephp/datasource/SchemaInterface.php
@@ -0,0 +1,166 @@
+|string $attrs The attributes for the column or the type name.
+     * @return $this
+     */
+    public function addColumn(string $name, $attrs);
+
+    /**
+     * Get column data in the table.
+     *
+     * @param string $name The column name.
+     * @return array|null Column data or null.
+     */
+    public function getColumn(string $name): ?array;
+
+    /**
+     * Returns true if a column exists in the schema.
+     *
+     * @param string $name Column name.
+     * @return bool
+     */
+    public function hasColumn(string $name): bool;
+
+    /**
+     * Remove a column from the table schema.
+     *
+     * If the column is not defined in the table, no error will be raised.
+     *
+     * @param string $name The name of the column
+     * @return $this
+     */
+    public function removeColumn(string $name);
+
+    /**
+     * Get the column names in the table.
+     *
+     * @return array
+     */
+    public function columns(): array;
+
+    /**
+     * Returns column type or null if a column does not exist.
+     *
+     * @param string $name The column to get the type of.
+     * @return string|null
+     */
+    public function getColumnType(string $name): ?string;
+
+    /**
+     * Sets the type of a column.
+     *
+     * @param string $name The column to set the type of.
+     * @param string $type The type to set the column to.
+     * @return $this
+     */
+    public function setColumnType(string $name, string $type);
+
+    /**
+     * Returns the base type name for the provided column.
+     * This represent the database type a more complex class is
+     * based upon.
+     *
+     * @param string $column The column name to get the base type from
+     * @return string|null The base type name
+     */
+    public function baseColumnType(string $column): ?string;
+
+    /**
+     * Check whether a field is nullable
+     *
+     * Missing columns are nullable.
+     *
+     * @param string $name The column to get the type of.
+     * @return bool Whether the field is nullable.
+     */
+    public function isNullable(string $name): bool;
+
+    /**
+     * Returns an array where the keys are the column names in the schema
+     * and the values the database type they have.
+     *
+     * @return array
+     */
+    public function typeMap(): array;
+
+    /**
+     * Get a hash of columns and their default values.
+     *
+     * @return array
+     */
+    public function defaultValues(): array;
+
+    /**
+     * Sets the options for a table.
+     *
+     * Table options allow you to set platform specific table level options.
+     * For example the engine type in MySQL.
+     *
+     * @param array $options The options to set, or null to read options.
+     * @return $this
+     */
+    public function setOptions(array $options);
+
+    /**
+     * Gets the options for a table.
+     *
+     * Table options allow you to set platform specific table level options.
+     * For example the engine type in MySQL.
+     *
+     * @return array An array of options.
+     */
+    public function getOptions(): array;
+}
diff --git a/vendor/cakephp/datasource/SimplePaginator.php b/vendor/cakephp/datasource/SimplePaginator.php
new file mode 100644
index 0000000..134b0bd
--- /dev/null
+++ b/vendor/cakephp/datasource/SimplePaginator.php
@@ -0,0 +1,10 @@
+=7.4.0",
+        "cakephp/core": "^4.0",
+        "psr/log": "^1.0 || ^2.0",
+        "psr/simple-cache": "^1.0 || ^2.0"
+    },
+    "suggest": {
+        "cakephp/utility": "If you decide to use EntityTrait.",
+        "cakephp/collection": "If you decide to use ResultSetInterface.",
+        "cakephp/cache": "If you decide to use Query caching."
+    },
+    "autoload": {
+        "psr-4": {
+            "Cake\\Datasource\\": "."
+        }
+    }
+}
diff --git a/vendor/cakephp/utility/CookieCryptTrait.php b/vendor/cakephp/utility/CookieCryptTrait.php
new file mode 100644
index 0000000..a57bfc0
--- /dev/null
+++ b/vendor/cakephp/utility/CookieCryptTrait.php
@@ -0,0 +1,192 @@
+
+     */
+    protected $_validCiphers = ['aes'];
+
+    /**
+     * Returns the encryption key to be used.
+     *
+     * @return string
+     */
+    abstract protected function _getCookieEncryptionKey(): string;
+
+    /**
+     * Encrypts $value using public $type method in Security class
+     *
+     * @param array|string $value Value to encrypt
+     * @param string|false $encrypt Encryption mode to use. False
+     *   disabled encryption.
+     * @param string|null $key Used as the security salt if specified.
+     * @return string Encoded values
+     */
+    protected function _encrypt($value, $encrypt, ?string $key = null): string
+    {
+        if (is_array($value)) {
+            $value = $this->_implode($value);
+        }
+        if ($encrypt === false) {
+            return $value;
+        }
+        $this->_checkCipher($encrypt);
+        $prefix = 'Q2FrZQ==.';
+        $cipher = '';
+        if ($key === null) {
+            $key = $this->_getCookieEncryptionKey();
+        }
+        if ($encrypt === 'aes') {
+            $cipher = Security::encrypt($value, $key);
+        }
+
+        return $prefix . base64_encode($cipher);
+    }
+
+    /**
+     * Helper method for validating encryption cipher names.
+     *
+     * @param string $encrypt The cipher name.
+     * @return void
+     * @throws \RuntimeException When an invalid cipher is provided.
+     */
+    protected function _checkCipher(string $encrypt): void
+    {
+        if (!in_array($encrypt, $this->_validCiphers, true)) {
+            $msg = sprintf(
+                'Invalid encryption cipher. Must be one of %s or false.',
+                implode(', ', $this->_validCiphers)
+            );
+            throw new RuntimeException($msg);
+        }
+    }
+
+    /**
+     * Decrypts $value using public $type method in Security class
+     *
+     * @param array|string $values Values to decrypt
+     * @param string|false $mode Encryption mode
+     * @param string|null $key Used as the security salt if specified.
+     * @return array|string Decrypted values
+     */
+    protected function _decrypt($values, $mode, ?string $key = null)
+    {
+        if (is_string($values)) {
+            return $this->_decode($values, $mode, $key);
+        }
+
+        $decrypted = [];
+        foreach ($values as $name => $value) {
+            $decrypted[$name] = $this->_decode($value, $mode, $key);
+        }
+
+        return $decrypted;
+    }
+
+    /**
+     * Decodes and decrypts a single value.
+     *
+     * @param string $value The value to decode & decrypt.
+     * @param string|false $encrypt The encryption cipher to use.
+     * @param string|null $key Used as the security salt if specified.
+     * @return array|string Decoded values.
+     */
+    protected function _decode(string $value, $encrypt, ?string $key)
+    {
+        if (!$encrypt) {
+            return $this->_explode($value);
+        }
+        $this->_checkCipher($encrypt);
+        $prefix = 'Q2FrZQ==.';
+        $prefixLength = strlen($prefix);
+
+        if (strncmp($value, $prefix, $prefixLength) !== 0) {
+            return '';
+        }
+
+        $value = base64_decode(substr($value, $prefixLength), true);
+
+        if ($value === false || $value === '') {
+            return '';
+        }
+
+        if ($key === null) {
+            $key = $this->_getCookieEncryptionKey();
+        }
+        if ($encrypt === 'aes') {
+            $value = Security::decrypt($value, $key);
+        }
+
+        if ($value === null) {
+            return '';
+        }
+
+        return $this->_explode($value);
+    }
+
+    /**
+     * Implode method to keep keys are multidimensional arrays
+     *
+     * @param array $array Map of key and values
+     * @return string A JSON encoded string.
+     */
+    protected function _implode(array $array): string
+    {
+        return json_encode($array);
+    }
+
+    /**
+     * Explode method to return array from string set in CookieComponent::_implode()
+     * Maintains reading backwards compatibility with 1.x CookieComponent::_implode().
+     *
+     * @param string $string A string containing JSON encoded data, or a bare string.
+     * @return array|string Map of key and values
+     */
+    protected function _explode(string $string)
+    {
+        $first = substr($string, 0, 1);
+        if ($first === '{' || $first === '[') {
+            $ret = json_decode($string, true);
+
+            return $ret ?? $string;
+        }
+        $array = [];
+        foreach (explode(',', $string) as $pair) {
+            $key = explode('|', $pair);
+            if (!isset($key[1])) {
+                return $key[0];
+            }
+            $array[$key[0]] = $key[1];
+        }
+
+        return $array;
+    }
+}
diff --git a/vendor/cakephp/utility/Crypto/OpenSsl.php b/vendor/cakephp/utility/Crypto/OpenSsl.php
new file mode 100644
index 0000000..99590d3
--- /dev/null
+++ b/vendor/cakephp/utility/Crypto/OpenSsl.php
@@ -0,0 +1,79 @@
+|string|int|null $path The path being searched for. Either a dot
+     *   separated string, or an array of path segments.
+     * @param mixed $default The return value when the path does not exist
+     * @throws \InvalidArgumentException
+     * @return mixed The value fetched from the array, or null.
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::get
+     */
+    public static function get($data, $path, $default = null)
+    {
+        if (!(is_array($data) || $data instanceof ArrayAccess)) {
+            throw new InvalidArgumentException(
+                'Invalid data type, must be an array or \ArrayAccess instance.'
+            );
+        }
+
+        if (empty($data) || $path === null) {
+            return $default;
+        }
+
+        if (is_string($path) || is_numeric($path)) {
+            $parts = explode('.', (string)$path);
+        } else {
+            if (!is_array($path)) {
+                throw new InvalidArgumentException(sprintf(
+                    'Invalid Parameter %s, should be dot separated path or array.',
+                    $path
+                ));
+            }
+
+            $parts = $path;
+        }
+
+        switch (count($parts)) {
+            case 1:
+                return $data[$parts[0]] ?? $default;
+            case 2:
+                return $data[$parts[0]][$parts[1]] ?? $default;
+            case 3:
+                return $data[$parts[0]][$parts[1]][$parts[2]] ?? $default;
+            default:
+                foreach ($parts as $key) {
+                    if ((is_array($data) || $data instanceof ArrayAccess) && isset($data[$key])) {
+                        $data = $data[$key];
+                    } else {
+                        return $default;
+                    }
+                }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Gets the values from an array matching the $path expression.
+     * The path expression is a dot separated expression, that can contain a set
+     * of patterns and expressions:
+     *
+     * - `{n}` Matches any numeric key, or integer.
+     * - `{s}` Matches any string key.
+     * - `{*}` Matches any value.
+     * - `Foo` Matches any key with the exact same value.
+     *
+     * There are a number of attribute operators:
+     *
+     *  - `=`, `!=` Equality.
+     *  - `>`, `<`, `>=`, `<=` Value comparison.
+     *  - `=/.../` Regular expression pattern match.
+     *
+     * Given a set of User array data, from a `$usersTable->find('all')` call:
+     *
+     * - `1.User.name` Get the name of the user at index 1.
+     * - `{n}.User.name` Get the name of every user in the set of users.
+     * - `{n}.User[id].name` Get the name of every user with an id key.
+     * - `{n}.User[id>=2].name` Get the name of every user with an id key greater than or equal to 2.
+     * - `{n}.User[username=/^paul/]` Get User elements with username matching `^paul`.
+     * - `{n}.User[id=1].name` Get the Users name with id matching `1`.
+     *
+     * @param \ArrayAccess|array $data The data to extract from.
+     * @param string $path The path to extract.
+     * @return \ArrayAccess|array An array of the extracted values. Returns an empty array
+     *   if there are no matches.
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::extract
+     */
+    public static function extract($data, string $path)
+    {
+        if (!(is_array($data) || $data instanceof ArrayAccess)) {
+            throw new InvalidArgumentException(
+                'Invalid data type, must be an array or \ArrayAccess instance.'
+            );
+        }
+
+        if (empty($path)) {
+            return $data;
+        }
+
+        // Simple paths.
+        if (!preg_match('/[{\[]/', $path)) {
+            $data = static::get($data, $path);
+            if ($data !== null && !(is_array($data) || $data instanceof ArrayAccess)) {
+                return [$data];
+            }
+
+            return $data !== null ? (array)$data : [];
+        }
+
+        if (strpos($path, '[') === false) {
+            $tokens = explode('.', $path);
+        } else {
+            $tokens = Text::tokenize($path, '.', '[', ']');
+        }
+
+        $_key = '__set_item__';
+
+        $context = [$_key => [$data]];
+
+        foreach ($tokens as $token) {
+            $next = [];
+
+            [$token, $conditions] = self::_splitConditions($token);
+
+            foreach ($context[$_key] as $item) {
+                if (is_object($item) && method_exists($item, 'toArray')) {
+                    /** @var \Cake\Datasource\EntityInterface $item */
+                    $item = $item->toArray();
+                }
+                foreach ((array)$item as $k => $v) {
+                    if (static::_matchToken($k, $token)) {
+                        $next[] = $v;
+                    }
+                }
+            }
+
+            // Filter for attributes.
+            if ($conditions) {
+                $filter = [];
+                foreach ($next as $item) {
+                    if (
+                        (
+                            is_array($item) ||
+                            $item instanceof ArrayAccess
+                        ) &&
+                        static::_matches($item, $conditions)
+                    ) {
+                        $filter[] = $item;
+                    }
+                }
+                $next = $filter;
+            }
+            $context = [$_key => $next];
+        }
+
+        return $context[$_key];
+    }
+
+    /**
+     * Split token conditions
+     *
+     * @param string $token the token being splitted.
+     * @return array [token, conditions] with token splitted
+     */
+    protected static function _splitConditions(string $token): array
+    {
+        $conditions = false;
+        $position = strpos($token, '[');
+        if ($position !== false) {
+            $conditions = substr($token, $position);
+            $token = substr($token, 0, $position);
+        }
+
+        return [$token, $conditions];
+    }
+
+    /**
+     * Check a key against a token.
+     *
+     * @param mixed $key The key in the array being searched.
+     * @param string $token The token being matched.
+     * @return bool
+     */
+    protected static function _matchToken($key, string $token): bool
+    {
+        switch ($token) {
+            case '{n}':
+                return is_numeric($key);
+            case '{s}':
+                return is_string($key);
+            case '{*}':
+                return true;
+            default:
+                return is_numeric($token) ? ($key == $token) : $key === $token;
+        }
+    }
+
+    /**
+     * Checks whether $data matches the attribute patterns
+     *
+     * @param \ArrayAccess|array $data Array of data to match.
+     * @param string $selector The patterns to match.
+     * @return bool Fitness of expression.
+     */
+    protected static function _matches($data, string $selector): bool
+    {
+        preg_match_all(
+            '/(\[ (?P[^=>[><]) \s* (?P(?:\/.*?\/ | [^\]]+)) )? \])/x',
+            $selector,
+            $conditions,
+            PREG_SET_ORDER
+        );
+
+        foreach ($conditions as $cond) {
+            $attr = $cond['attr'];
+            $op = $cond['op'] ?? null;
+            $val = $cond['val'] ?? null;
+
+            // Presence test.
+            if (empty($op) && empty($val) && !isset($data[$attr])) {
+                return false;
+            }
+
+            if (is_array($data)) {
+                $attrPresent = array_key_exists($attr, $data);
+            } else {
+                $attrPresent = $data->offsetExists($attr);
+            }
+            // Empty attribute = fail.
+            if (!$attrPresent) {
+                return false;
+            }
+
+            $prop = $data[$attr] ?? '';
+            $isBool = is_bool($prop);
+            if ($isBool && is_numeric($val)) {
+                $prop = $prop ? '1' : '0';
+            } elseif ($isBool) {
+                $prop = $prop ? 'true' : 'false';
+            } elseif (is_numeric($prop)) {
+                $prop = (string)$prop;
+            }
+
+            // Pattern matches and other operators.
+            if ($op === '=' && $val && $val[0] === '/') {
+                if (!preg_match($val, $prop)) {
+                    return false;
+                }
+                // phpcs:disable
+            } elseif (
+                ($op === '=' && $prop != $val) ||
+                ($op === '!=' && $prop == $val) ||
+                ($op === '>' && $prop <= $val) ||
+                ($op === '<' && $prop >= $val) ||
+                ($op === '>=' && $prop < $val) ||
+                ($op === '<=' && $prop > $val)
+                // phpcs:enable
+            ) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Insert $values into an array with the given $path. You can use
+     * `{n}` and `{s}` elements to insert $data multiple times.
+     *
+     * @param array $data The data to insert into.
+     * @param string $path The path to insert at.
+     * @param mixed $values The values to insert.
+     * @return array The data with $values inserted.
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::insert
+     */
+    public static function insert(array $data, string $path, $values = null): array
+    {
+        $noTokens = strpos($path, '[') === false;
+        if ($noTokens && strpos($path, '.') === false) {
+            $data[$path] = $values;
+
+            return $data;
+        }
+
+        if ($noTokens) {
+            $tokens = explode('.', $path);
+        } else {
+            $tokens = Text::tokenize($path, '.', '[', ']');
+        }
+
+        if ($noTokens && strpos($path, '{') === false) {
+            return static::_simpleOp('insert', $data, $tokens, $values);
+        }
+
+        $token = array_shift($tokens);
+        $nextPath = implode('.', $tokens);
+
+        [$token, $conditions] = static::_splitConditions($token);
+
+        foreach ($data as $k => $v) {
+            if (static::_matchToken($k, $token)) {
+                if (
+                    !$conditions ||
+                    ((is_array($v) || $v instanceof ArrayAccess) && static::_matches($v, $conditions))
+                ) {
+                    $data[$k] = $nextPath
+                        ? static::insert($v, $nextPath, $values)
+                        : array_merge($v, (array)$values);
+                }
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Perform a simple insert/remove operation.
+     *
+     * @param string $op The operation to do.
+     * @param array $data The data to operate on.
+     * @param array $path The path to work on.
+     * @param mixed $values The values to insert when doing inserts.
+     * @return array data.
+     */
+    protected static function _simpleOp(string $op, array $data, array $path, $values = null): array
+    {
+        $_list = &$data;
+
+        $count = count($path);
+        $last = $count - 1;
+        foreach ($path as $i => $key) {
+            if ($op === 'insert') {
+                if ($i === $last) {
+                    $_list[$key] = $values;
+
+                    return $data;
+                }
+                $_list[$key] = $_list[$key] ?? [];
+                $_list = &$_list[$key];
+                if (!is_array($_list)) {
+                    $_list = [];
+                }
+            } elseif ($op === 'remove') {
+                if ($i === $last) {
+                    if (is_array($_list)) {
+                        unset($_list[$key]);
+                    }
+
+                    return $data;
+                }
+                if (!isset($_list[$key])) {
+                    return $data;
+                }
+                $_list = &$_list[$key];
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Remove data matching $path from the $data array.
+     * You can use `{n}` and `{s}` to remove multiple elements
+     * from $data.
+     *
+     * @param array $data The data to operate on
+     * @param string $path A path expression to use to remove.
+     * @return array The modified array.
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::remove
+     */
+    public static function remove(array $data, string $path): array
+    {
+        $noTokens = strpos($path, '[') === false;
+        $noExpansion = strpos($path, '{') === false;
+
+        if ($noExpansion && $noTokens && strpos($path, '.') === false) {
+            unset($data[$path]);
+
+            return $data;
+        }
+
+        $tokens = $noTokens ? explode('.', $path) : Text::tokenize($path, '.', '[', ']');
+
+        if ($noExpansion && $noTokens) {
+            return static::_simpleOp('remove', $data, $tokens);
+        }
+
+        $token = array_shift($tokens);
+        $nextPath = implode('.', $tokens);
+
+        [$token, $conditions] = self::_splitConditions($token);
+
+        foreach ($data as $k => $v) {
+            $match = static::_matchToken($k, $token);
+            if ($match && is_array($v)) {
+                if ($conditions) {
+                    if (static::_matches($v, $conditions)) {
+                        if ($nextPath !== '') {
+                            $data[$k] = static::remove($v, $nextPath);
+                        } else {
+                            unset($data[$k]);
+                        }
+                    }
+                } else {
+                    $data[$k] = static::remove($v, $nextPath);
+                }
+                if (empty($data[$k])) {
+                    unset($data[$k]);
+                }
+            } elseif ($match && $nextPath === '') {
+                unset($data[$k]);
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Creates an associative array using `$keyPath` as the path to build its keys, and optionally
+     * `$valuePath` as path to get the values. If `$valuePath` is not specified, all values will be initialized
+     * to null (useful for Hash::merge). You can optionally group the values by what is obtained when
+     * following the path specified in `$groupPath`.
+     *
+     * @param array $data Array from where to extract keys and values
+     * @param array|string|null $keyPath A dot-separated string.
+     * @param array|string|null $valuePath A dot-separated string.
+     * @param string|null $groupPath A dot-separated string.
+     * @return array Combined array
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::combine
+     * @throws \RuntimeException When keys and values count is unequal.
+     */
+    public static function combine(array $data, $keyPath, $valuePath = null, ?string $groupPath = null): array
+    {
+        if (empty($data)) {
+            return [];
+        }
+
+        if (is_array($keyPath)) {
+            $format = array_shift($keyPath);
+            /** @var array $keys */
+            $keys = static::format($data, $keyPath, $format);
+        } elseif ($keyPath === null) {
+            $keys = $keyPath;
+        } else {
+            /** @var array $keys */
+            $keys = static::extract($data, $keyPath);
+        }
+        if ($keyPath !== null && empty($keys)) {
+            return [];
+        }
+
+        $vals = null;
+        if (!empty($valuePath) && is_array($valuePath)) {
+            $format = array_shift($valuePath);
+            /** @var array $vals */
+            $vals = static::format($data, $valuePath, $format);
+        } elseif (!empty($valuePath)) {
+            /** @var array $vals */
+            $vals = static::extract($data, $valuePath);
+        }
+        if (empty($vals)) {
+            $vals = array_fill(0, $keys === null ? count($data) : count($keys), null);
+        }
+
+        if (is_array($keys) && count($keys) !== count($vals)) {
+            throw new RuntimeException(
+                'Hash::combine() needs an equal number of keys + values.'
+            );
+        }
+
+        if ($groupPath !== null) {
+            $group = static::extract($data, $groupPath);
+            if (!empty($group)) {
+                $c = is_array($keys) ? count($keys) : count($vals);
+                $out = [];
+                for ($i = 0; $i < $c; $i++) {
+                    $group[$i] = $group[$i] ?? 0;
+                    $out[$group[$i]] = $out[$group[$i]] ?? [];
+                    if ($keys === null) {
+                        $out[$group[$i]][] = $vals[$i];
+                    } else {
+                        $out[$group[$i]][$keys[$i]] = $vals[$i];
+                    }
+                }
+
+                return $out;
+            }
+        }
+        if (empty($vals)) {
+            return [];
+        }
+
+        return array_combine($keys ?? range(0, count($vals) - 1), $vals);
+    }
+
+    /**
+     * Returns a formatted series of values extracted from `$data`, using
+     * `$format` as the format and `$paths` as the values to extract.
+     *
+     * Usage:
+     *
+     * ```
+     * $result = Hash::format($users, ['{n}.User.id', '{n}.User.name'], '%s : %s');
+     * ```
+     *
+     * The `$format` string can use any format options that `vsprintf()` and `sprintf()` do.
+     *
+     * @param array $data Source array from which to extract the data
+     * @param array $paths An array containing one or more Hash::extract()-style key paths
+     * @param string $format Format string into which values will be inserted, see sprintf()
+     * @return array|null An array of strings extracted from `$path` and formatted with `$format`
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::format
+     * @see sprintf()
+     * @see \Cake\Utility\Hash::extract()
+     */
+    public static function format(array $data, array $paths, string $format): ?array
+    {
+        $extracted = [];
+        $count = count($paths);
+
+        if (!$count) {
+            return null;
+        }
+
+        for ($i = 0; $i < $count; $i++) {
+            $extracted[] = static::extract($data, $paths[$i]);
+        }
+        $out = [];
+        /** @var array $data */
+        $data = $extracted;
+        $count = count($data[0]);
+
+        $countTwo = count($data);
+        for ($j = 0; $j < $count; $j++) {
+            $args = [];
+            for ($i = 0; $i < $countTwo; $i++) {
+                if (array_key_exists($j, $data[$i])) {
+                    $args[] = $data[$i][$j];
+                }
+            }
+            $out[] = vsprintf($format, $args);
+        }
+
+        return $out;
+    }
+
+    /**
+     * Determines if one array contains the exact keys and values of another.
+     *
+     * @param array $data The data to search through.
+     * @param array $needle The values to file in $data
+     * @return bool true If $data contains $needle, false otherwise
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::contains
+     */
+    public static function contains(array $data, array $needle): bool
+    {
+        if (empty($data) || empty($needle)) {
+            return false;
+        }
+        $stack = [];
+
+        while (!empty($needle)) {
+            $key = key($needle);
+            $val = $needle[$key];
+            unset($needle[$key]);
+
+            if (array_key_exists($key, $data) && is_array($val)) {
+                $next = $data[$key];
+                unset($data[$key]);
+
+                if (!empty($val)) {
+                    $stack[] = [$val, $next];
+                }
+            } elseif (!array_key_exists($key, $data) || $data[$key] != $val) {
+                return false;
+            }
+
+            if (empty($needle) && !empty($stack)) {
+                [$needle, $data] = array_pop($stack);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Test whether a given path exists in $data.
+     * This method uses the same path syntax as Hash::extract()
+     *
+     * Checking for paths that could target more than one element will
+     * make sure that at least one matching element exists.
+     *
+     * @param array $data The data to check.
+     * @param string $path The path to check for.
+     * @return bool Existence of path.
+     * @see \Cake\Utility\Hash::extract()
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::check
+     */
+    public static function check(array $data, string $path): bool
+    {
+        $results = static::extract($data, $path);
+        if (!is_array($results)) {
+            return false;
+        }
+
+        return count($results) > 0;
+    }
+
+    /**
+     * Recursively filters a data set.
+     *
+     * @param array $data Either an array to filter, or value when in callback
+     * @param callable|array $callback A function to filter the data with. Defaults to
+     *   `static::_filter()` Which strips out all non-zero empty values.
+     * @return array Filtered array
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::filter
+     */
+    public static function filter(array $data, $callback = [Hash::class, '_filter']): array
+    {
+        foreach ($data as $k => $v) {
+            if (is_array($v)) {
+                $data[$k] = static::filter($v, $callback);
+            }
+        }
+
+        return array_filter($data, $callback);
+    }
+
+    /**
+     * Callback function for filtering.
+     *
+     * @param mixed $var Array to filter.
+     * @return bool
+     */
+    protected static function _filter($var): bool
+    {
+        return $var === 0 || $var === 0.0 || $var === '0' || !empty($var);
+    }
+
+    /**
+     * Collapses a multi-dimensional array into a single dimension, using a delimited array path for
+     * each array element's key, i.e. [['Foo' => ['Bar' => 'Far']]] becomes
+     * ['0.Foo.Bar' => 'Far'].)
+     *
+     * @param array $data Array to flatten
+     * @param string $separator String used to separate array key elements in a path, defaults to '.'
+     * @return array
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::flatten
+     */
+    public static function flatten(array $data, string $separator = '.'): array
+    {
+        $result = [];
+        $stack = [];
+        $path = '';
+
+        reset($data);
+        while (!empty($data)) {
+            $key = key($data);
+            $element = $data[$key];
+            unset($data[$key]);
+
+            if (is_array($element) && !empty($element)) {
+                if (!empty($data)) {
+                    $stack[] = [$data, $path];
+                }
+                $data = $element;
+                reset($data);
+                $path .= $key . $separator;
+            } else {
+                $result[$path . $key] = $element;
+            }
+
+            if (empty($data) && !empty($stack)) {
+                [$data, $path] = array_pop($stack);
+                reset($data);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Expands a flat array to a nested array.
+     *
+     * For example, unflattens an array that was collapsed with `Hash::flatten()`
+     * into a multi-dimensional array. So, `['0.Foo.Bar' => 'Far']` becomes
+     * `[['Foo' => ['Bar' => 'Far']]]`.
+     *
+     * @phpstan-param non-empty-string $separator
+     * @param array $data Flattened array
+     * @param string $separator The delimiter used
+     * @return array
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::expand
+     */
+    public static function expand(array $data, string $separator = '.'): array
+    {
+        $hash = [];
+        foreach ($data as $path => $value) {
+            $keys = explode($separator, (string)$path);
+            if (count($keys) === 1) {
+                $hash[$path] = $value;
+                continue;
+            }
+
+            $valueKey = end($keys);
+            $keys = array_slice($keys, 0, -1);
+
+            $keyHash = &$hash;
+            foreach ($keys as $key) {
+                if (!array_key_exists($key, $keyHash)) {
+                    $keyHash[$key] = [];
+                }
+                $keyHash = &$keyHash[$key];
+            }
+            $keyHash[$valueKey] = $value;
+        }
+
+        return $hash;
+    }
+
+    /**
+     * This function can be thought of as a hybrid between PHP's `array_merge` and `array_merge_recursive`.
+     *
+     * The difference between this method and the built-in ones, is that if an array key contains another array, then
+     * Hash::merge() will behave in a recursive fashion (unlike `array_merge`). But it will not act recursively for
+     * keys that contain scalar values (unlike `array_merge_recursive`).
+     *
+     * This function will work with an unlimited amount of arguments and typecasts non-array parameters into arrays.
+     *
+     * @param array $data Array to be merged
+     * @param mixed $merge Array to merge with. The argument and all trailing arguments will be array cast when merged
+     * @return array Merged array
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::merge
+     */
+    public static function merge(array $data, $merge): array
+    {
+        $args = array_slice(func_get_args(), 1);
+        $return = $data;
+        $stack = [];
+
+        foreach ($args as &$curArg) {
+            $stack[] = [(array)$curArg, &$return];
+        }
+        unset($curArg);
+        static::_merge($stack, $return);
+
+        return $return;
+    }
+
+    /**
+     * Merge helper function to reduce duplicated code between merge() and expand().
+     *
+     * @param array $stack The stack of operations to work with.
+     * @param array $return The return value to operate on.
+     * @return void
+     */
+    protected static function _merge(array $stack, array &$return): void
+    {
+        while (!empty($stack)) {
+            foreach ($stack as $curKey => &$curMerge) {
+                foreach ($curMerge[0] as $key => &$val) {
+                    if (!is_array($curMerge[1])) {
+                        continue;
+                    }
+
+                    if (
+                        !empty($curMerge[1][$key])
+                        && (array)$curMerge[1][$key] === $curMerge[1][$key]
+                        && (array)$val === $val
+                    ) {
+                        // Recurse into the current merge data as it is an array.
+                        $stack[] = [&$val, &$curMerge[1][$key]];
+                    } elseif ((int)$key === $key && isset($curMerge[1][$key])) {
+                        $curMerge[1][] = $val;
+                    } else {
+                        $curMerge[1][$key] = $val;
+                    }
+                }
+                unset($stack[$curKey]);
+            }
+            unset($curMerge);
+        }
+    }
+
+    /**
+     * Checks to see if all the values in the array are numeric
+     *
+     * @param array $data The array to check.
+     * @return bool true if values are numeric, false otherwise
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::numeric
+     */
+    public static function numeric(array $data): bool
+    {
+        if (empty($data)) {
+            return false;
+        }
+
+        return $data === array_filter($data, 'is_numeric');
+    }
+
+    /**
+     * Counts the dimensions of an array.
+     * Only considers the dimension of the first element in the array.
+     *
+     * If you have an un-even or heterogeneous array, consider using Hash::maxDimensions()
+     * to get the dimensions of the array.
+     *
+     * @param array $data Array to count dimensions on
+     * @return int The number of dimensions in $data
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::dimensions
+     */
+    public static function dimensions(array $data): int
+    {
+        if (empty($data)) {
+            return 0;
+        }
+        reset($data);
+        $depth = 1;
+        while ($elem = array_shift($data)) {
+            if (is_array($elem)) {
+                $depth++;
+                $data = $elem;
+            } else {
+                break;
+            }
+        }
+
+        return $depth;
+    }
+
+    /**
+     * Counts the dimensions of *all* array elements. Useful for finding the maximum
+     * number of dimensions in a mixed array.
+     *
+     * @param array $data Array to count dimensions on
+     * @return int The maximum number of dimensions in $data
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::maxDimensions
+     */
+    public static function maxDimensions(array $data): int
+    {
+        $depth = [];
+        if (!empty($data)) {
+            foreach ($data as $value) {
+                if (is_array($value)) {
+                    $depth[] = static::maxDimensions($value) + 1;
+                } else {
+                    $depth[] = 1;
+                }
+            }
+        }
+
+        return empty($depth) ? 0 : max($depth);
+    }
+
+    /**
+     * Map a callback across all elements in a set.
+     * Can be provided a path to only modify slices of the set.
+     *
+     * @param array $data The data to map over, and extract data out of.
+     * @param string $path The path to extract for mapping over.
+     * @param callable $function The function to call on each extracted value.
+     * @return array An array of the modified values.
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::map
+     */
+    public static function map(array $data, string $path, callable $function): array
+    {
+        $values = (array)static::extract($data, $path);
+
+        return array_map($function, $values);
+    }
+
+    /**
+     * Reduce a set of extracted values using `$function`.
+     *
+     * @param array $data The data to reduce.
+     * @param string $path The path to extract from $data.
+     * @param callable $function The function to call on each extracted value.
+     * @return mixed The reduced value.
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::reduce
+     */
+    public static function reduce(array $data, string $path, callable $function)
+    {
+        $values = (array)static::extract($data, $path);
+
+        return array_reduce($values, $function);
+    }
+
+    /**
+     * Apply a callback to a set of extracted values using `$function`.
+     * The function will get the extracted values as the first argument.
+     *
+     * ### Example
+     *
+     * You can easily count the results of an extract using apply().
+     * For example to count the comments on an Article:
+     *
+     * ```
+     * $count = Hash::apply($data, 'Article.Comment.{n}', 'count');
+     * ```
+     *
+     * You could also use a function like `array_sum` to sum the results.
+     *
+     * ```
+     * $total = Hash::apply($data, '{n}.Item.price', 'array_sum');
+     * ```
+     *
+     * @param array $data The data to reduce.
+     * @param string $path The path to extract from $data.
+     * @param callable $function The function to call on each extracted value.
+     * @return mixed The results of the applied method.
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::apply
+     */
+    public static function apply(array $data, string $path, callable $function)
+    {
+        $values = (array)static::extract($data, $path);
+
+        return $function($values);
+    }
+
+    /**
+     * Sorts an array by any value, determined by a Set-compatible path
+     *
+     * ### Sort directions
+     *
+     * - `asc` or \SORT_ASC Sort ascending.
+     * - `desc` or \SORT_DESC Sort descending.
+     *
+     * ### Sort types
+     *
+     * - `regular` For regular sorting (don't change types)
+     * - `numeric` Compare values numerically
+     * - `string` Compare values as strings
+     * - `locale` Compare items as strings, based on the current locale
+     * - `natural` Compare items as strings using "natural ordering" in a human friendly way
+     *   Will sort foo10 below foo2 as an example.
+     *
+     * To do case insensitive sorting, pass the type as an array as follows:
+     *
+     * ```
+     * Hash::sort($data, 'some.attribute', 'asc', ['type' => 'regular', 'ignoreCase' => true]);
+     * ```
+     *
+     * When using the array form, `type` defaults to 'regular'. The `ignoreCase` option
+     * defaults to `false`.
+     *
+     * @param array $data An array of data to sort
+     * @param string $path A Set-compatible path to the array value
+     * @param string|int $dir See directions above. Defaults to 'asc'.
+     * @param array|string $type See direction types above. Defaults to 'regular'.
+     * @return array Sorted array of data
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::sort
+     */
+    public static function sort(array $data, string $path, $dir = 'asc', $type = 'regular'): array
+    {
+        if (empty($data)) {
+            return [];
+        }
+        $originalKeys = array_keys($data);
+        $numeric = is_numeric(implode('', $originalKeys));
+        if ($numeric) {
+            $data = array_values($data);
+        }
+        /** @var array $sortValues */
+        $sortValues = static::extract($data, $path);
+        $dataCount = count($data);
+
+        // Make sortValues match the data length, as some keys could be missing
+        // the sorted value path.
+        $missingData = count($sortValues) < $dataCount;
+        if ($missingData && $numeric) {
+            // Get the path without the leading '{n}.'
+            $itemPath = substr($path, 4);
+            foreach ($data as $key => $value) {
+                $sortValues[$key] = static::get($value, $itemPath);
+            }
+        } elseif ($missingData) {
+            $sortValues = array_pad($sortValues, $dataCount, null);
+        }
+        $result = static::_squash($sortValues);
+        /** @var array $keys */
+        $keys = static::extract($result, '{n}.id');
+        /** @var array $values */
+        $values = static::extract($result, '{n}.value');
+
+        if (is_string($dir)) {
+            $dir = strtolower($dir);
+        }
+        if (!in_array($dir, [\SORT_ASC, \SORT_DESC], true)) {
+            $dir = $dir === 'asc' ? \SORT_ASC : \SORT_DESC;
+        }
+
+        $ignoreCase = false;
+
+        // $type can be overloaded for case insensitive sort
+        if (is_array($type)) {
+            $type += ['ignoreCase' => false, 'type' => 'regular'];
+            $ignoreCase = $type['ignoreCase'];
+            $type = $type['type'];
+        }
+        $type = strtolower($type);
+
+        if ($type === 'numeric') {
+            $type = \SORT_NUMERIC;
+        } elseif ($type === 'string') {
+            $type = \SORT_STRING;
+        } elseif ($type === 'natural') {
+            $type = \SORT_NATURAL;
+        } elseif ($type === 'locale') {
+            $type = \SORT_LOCALE_STRING;
+        } else {
+            $type = \SORT_REGULAR;
+        }
+        if ($ignoreCase) {
+            $values = array_map('mb_strtolower', $values);
+        }
+        array_multisort($values, $dir, $type, $keys, $dir, $type);
+        $sorted = [];
+        $keys = array_unique($keys);
+
+        foreach ($keys as $k) {
+            if ($numeric) {
+                $sorted[] = $data[$k];
+                continue;
+            }
+            if (isset($originalKeys[$k])) {
+                $sorted[$originalKeys[$k]] = $data[$originalKeys[$k]];
+            } else {
+                $sorted[$k] = $data[$k];
+            }
+        }
+
+        return $sorted;
+    }
+
+    /**
+     * Helper method for sort()
+     * Squashes an array to a single hash so it can be sorted.
+     *
+     * @param array $data The data to squash.
+     * @param mixed $key The key for the data.
+     * @return array
+     */
+    protected static function _squash(array $data, $key = null): array
+    {
+        $stack = [];
+        foreach ($data as $k => $r) {
+            $id = $k;
+            if ($key !== null) {
+                $id = $key;
+            }
+            if (is_array($r) && !empty($r)) {
+                $stack = array_merge($stack, static::_squash($r, $id));
+            } else {
+                $stack[] = ['id' => $id, 'value' => $r];
+            }
+        }
+
+        return $stack;
+    }
+
+    /**
+     * Computes the difference between two complex arrays.
+     * This method differs from the built-in array_diff() in that it will preserve keys
+     * and work on multi-dimensional arrays.
+     *
+     * @param array $data First value
+     * @param array $compare Second value
+     * @return array Returns the key => value pairs that are not common in $data and $compare
+     *    The expression for this function is ($data - $compare) + ($compare - ($data - $compare))
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::diff
+     */
+    public static function diff(array $data, array $compare): array
+    {
+        if (empty($data)) {
+            return $compare;
+        }
+        if (empty($compare)) {
+            return $data;
+        }
+        $intersection = array_intersect_key($data, $compare);
+        while (($key = key($intersection)) !== null) {
+            if ($data[$key] == $compare[$key]) {
+                unset($data[$key], $compare[$key]);
+            }
+            next($intersection);
+        }
+
+        return $data + $compare;
+    }
+
+    /**
+     * Merges the difference between $data and $compare onto $data.
+     *
+     * @param array $data The data to append onto.
+     * @param array $compare The data to compare and append onto.
+     * @return array The merged array.
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::mergeDiff
+     */
+    public static function mergeDiff(array $data, array $compare): array
+    {
+        if (empty($data) && !empty($compare)) {
+            return $compare;
+        }
+        if (empty($compare)) {
+            return $data;
+        }
+        foreach ($compare as $key => $value) {
+            if (!array_key_exists($key, $data)) {
+                $data[$key] = $value;
+            } elseif (is_array($value) && is_array($data[$key])) {
+                $data[$key] = static::mergeDiff($data[$key], $value);
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Normalizes an array, and converts it to a standard format.
+     *
+     * @param array $data List to normalize
+     * @param bool $assoc If true, $data will be converted to an associative array.
+     * @param mixed $default The default value to use when a top level numeric key is converted to associative form.
+     * @return array
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::normalize
+     */
+    public static function normalize(array $data, bool $assoc = true, $default = null): array
+    {
+        $keys = array_keys($data);
+        $count = count($keys);
+        $numeric = true;
+
+        if (!$assoc) {
+            for ($i = 0; $i < $count; $i++) {
+                if (!is_int($keys[$i])) {
+                    $numeric = false;
+                    break;
+                }
+            }
+        }
+        if (!$numeric || $assoc) {
+            $newList = [];
+            for ($i = 0; $i < $count; $i++) {
+                if (is_int($keys[$i])) {
+                    $newList[$data[$keys[$i]]] = $default;
+                } else {
+                    $newList[$keys[$i]] = $data[$keys[$i]];
+                }
+            }
+            $data = $newList;
+        }
+
+        return $data;
+    }
+
+    /**
+     * Takes in a flat array and returns a nested array
+     *
+     * ### Options:
+     *
+     * - `children` The key name to use in the resultset for children.
+     * - `idPath` The path to a key that identifies each entry. Should be
+     *   compatible with Hash::extract(). Defaults to `{n}.$alias.id`
+     * - `parentPath` The path to a key that identifies the parent of each entry.
+     *   Should be compatible with Hash::extract(). Defaults to `{n}.$alias.parent_id`
+     * - `root` The id of the desired top-most result.
+     *
+     * @param array $data The data to nest.
+     * @param array $options Options are:
+     * @return array of results, nested
+     * @see \Cake\Utility\Hash::extract()
+     * @throws \InvalidArgumentException When providing invalid data.
+     * @link https://book.cakephp.org/4/en/core-libraries/hash.html#Cake\Utility\Hash::nest
+     */
+    public static function nest(array $data, array $options = []): array
+    {
+        if (!$data) {
+            return $data;
+        }
+
+        $alias = key(current($data));
+        $options += [
+            'idPath' => "{n}.$alias.id",
+            'parentPath' => "{n}.$alias.parent_id",
+            'children' => 'children',
+            'root' => null,
+        ];
+
+        $return = $idMap = [];
+        /** @var array $ids */
+        $ids = static::extract($data, $options['idPath']);
+
+        $idKeys = explode('.', $options['idPath']);
+        array_shift($idKeys);
+
+        $parentKeys = explode('.', $options['parentPath']);
+        array_shift($parentKeys);
+
+        foreach ($data as $result) {
+            $result[$options['children']] = [];
+
+            $id = static::get($result, $idKeys);
+            $parentId = static::get($result, $parentKeys);
+
+            if (isset($idMap[$id][$options['children']])) {
+                $idMap[$id] = array_merge($result, $idMap[$id]);
+            } else {
+                $idMap[$id] = array_merge($result, [$options['children'] => []]);
+            }
+            if (!$parentId || !in_array($parentId, $ids)) {
+                $return[] = &$idMap[$id];
+            } else {
+                $idMap[$parentId][$options['children']][] = &$idMap[$id];
+            }
+        }
+
+        if (!$return) {
+            throw new InvalidArgumentException('Invalid data array to nest.');
+        }
+
+        if ($options['root']) {
+            $root = $options['root'];
+        } else {
+            $root = static::get($return[0], $parentKeys);
+        }
+
+        foreach ($return as $i => $result) {
+            $id = static::get($result, $idKeys);
+            $parentId = static::get($result, $parentKeys);
+            if ($id !== $root && $parentId != $root) {
+                unset($return[$i]);
+            }
+        }
+
+        return array_values($return);
+    }
+}
diff --git a/vendor/cakephp/utility/Inflector.php b/vendor/cakephp/utility/Inflector.php
new file mode 100644
index 0000000..8abcd07
--- /dev/null
+++ b/vendor/cakephp/utility/Inflector.php
@@ -0,0 +1,524 @@
+
+     */
+    protected static $_plural = [
+        '/(s)tatus$/i' => '\1tatuses',
+        '/(quiz)$/i' => '\1zes',
+        '/^(ox)$/i' => '\1\2en',
+        '/([m|l])ouse$/i' => '\1ice',
+        '/(matr|vert)(ix|ex)$/i' => '\1ices',
+        '/(x|ch|ss|sh)$/i' => '\1es',
+        '/([^aeiouy]|qu)y$/i' => '\1ies',
+        '/(hive)$/i' => '\1s',
+        '/(chef)$/i' => '\1s',
+        '/(?:([^f])fe|([lre])f)$/i' => '\1\2ves',
+        '/sis$/i' => 'ses',
+        '/([ti])um$/i' => '\1a',
+        '/(p)erson$/i' => '\1eople',
+        '/(? '\1en',
+        '/(c)hild$/i' => '\1hildren',
+        '/(buffal|tomat)o$/i' => '\1\2oes',
+        '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin)us$/i' => '\1i',
+        '/us$/i' => 'uses',
+        '/(alias)$/i' => '\1es',
+        '/(ax|cris|test)is$/i' => '\1es',
+        '/s$/' => 's',
+        '/^$/' => '',
+        '/$/' => 's',
+    ];
+
+    /**
+     * Singular inflector rules
+     *
+     * @var array
+     */
+    protected static $_singular = [
+        '/(s)tatuses$/i' => '\1\2tatus',
+        '/^(.*)(menu)s$/i' => '\1\2',
+        '/(quiz)zes$/i' => '\\1',
+        '/(matr)ices$/i' => '\1ix',
+        '/(vert|ind)ices$/i' => '\1ex',
+        '/^(ox)en/i' => '\1',
+        '/(alias|lens)(es)*$/i' => '\1',
+        '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
+        '/([ftw]ax)es/i' => '\1',
+        '/(cris|ax|test)es$/i' => '\1is',
+        '/(shoe)s$/i' => '\1',
+        '/(o)es$/i' => '\1',
+        '/ouses$/' => 'ouse',
+        '/([^a])uses$/' => '\1us',
+        '/([m|l])ice$/i' => '\1ouse',
+        '/(x|ch|ss|sh)es$/i' => '\1',
+        '/(m)ovies$/i' => '\1\2ovie',
+        '/(s)eries$/i' => '\1\2eries',
+        '/(s)pecies$/i' => '\1\2pecies',
+        '/([^aeiouy]|qu)ies$/i' => '\1y',
+        '/(tive)s$/i' => '\1',
+        '/(hive)s$/i' => '\1',
+        '/(drive)s$/i' => '\1',
+        '/([le])ves$/i' => '\1f',
+        '/([^rfoa])ves$/i' => '\1fe',
+        '/(^analy)ses$/i' => '\1sis',
+        '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
+        '/([ti])a$/i' => '\1um',
+        '/(p)eople$/i' => '\1\2erson',
+        '/(m)en$/i' => '\1an',
+        '/(c)hildren$/i' => '\1\2hild',
+        '/(n)ews$/i' => '\1\2ews',
+        '/eaus$/' => 'eau',
+        '/^(.*us)$/' => '\\1',
+        '/s$/i' => '',
+    ];
+
+    /**
+     * Irregular rules
+     *
+     * @var array
+     */
+    protected static $_irregular = [
+        'atlas' => 'atlases',
+        'beef' => 'beefs',
+        'brief' => 'briefs',
+        'brother' => 'brothers',
+        'cafe' => 'cafes',
+        'child' => 'children',
+        'cookie' => 'cookies',
+        'corpus' => 'corpuses',
+        'cow' => 'cows',
+        'criterion' => 'criteria',
+        'ganglion' => 'ganglions',
+        'genie' => 'genies',
+        'genus' => 'genera',
+        'graffito' => 'graffiti',
+        'hoof' => 'hoofs',
+        'loaf' => 'loaves',
+        'man' => 'men',
+        'money' => 'monies',
+        'mongoose' => 'mongooses',
+        'move' => 'moves',
+        'mythos' => 'mythoi',
+        'niche' => 'niches',
+        'numen' => 'numina',
+        'occiput' => 'occiputs',
+        'octopus' => 'octopuses',
+        'opus' => 'opuses',
+        'ox' => 'oxen',
+        'penis' => 'penises',
+        'person' => 'people',
+        'sex' => 'sexes',
+        'soliloquy' => 'soliloquies',
+        'testis' => 'testes',
+        'trilby' => 'trilbys',
+        'turf' => 'turfs',
+        'potato' => 'potatoes',
+        'hero' => 'heroes',
+        'tooth' => 'teeth',
+        'goose' => 'geese',
+        'foot' => 'feet',
+        'foe' => 'foes',
+        'sieve' => 'sieves',
+        'cache' => 'caches',
+    ];
+
+    /**
+     * Words that should not be inflected
+     *
+     * @var array
+     */
+    protected static $_uninflected = [
+        '.*[nrlm]ese', '.*data', '.*deer', '.*fish', '.*measles', '.*ois',
+        '.*pox', '.*sheep', 'people', 'feedback', 'stadia', '.*?media',
+        'chassis', 'clippers', 'debris', 'diabetes', 'equipment', 'gallows',
+        'graffiti', 'headquarters', 'information', 'innings', 'news', 'nexus',
+        'pokemon', 'proceedings', 'research', 'sea[- ]bass', 'series', 'species', 'weather',
+    ];
+
+    /**
+     * Method cache array.
+     *
+     * @var array
+     */
+    protected static $_cache = [];
+
+    /**
+     * The initial state of Inflector so reset() works.
+     *
+     * @var array
+     */
+    protected static $_initialState = [];
+
+    /**
+     * Cache inflected values, and return if already available
+     *
+     * @param string $type Inflection type
+     * @param string $key Original value
+     * @param string|false $value Inflected value
+     * @return string|false Inflected value on cache hit or false on cache miss.
+     */
+    protected static function _cache(string $type, string $key, $value = false)
+    {
+        $key = '_' . $key;
+        $type = '_' . $type;
+        if ($value !== false) {
+            static::$_cache[$type][$key] = $value;
+
+            return $value;
+        }
+        if (!isset(static::$_cache[$type][$key])) {
+            return false;
+        }
+
+        return static::$_cache[$type][$key];
+    }
+
+    /**
+     * Clears Inflectors inflected value caches. And resets the inflection
+     * rules to the initial values.
+     *
+     * @return void
+     */
+    public static function reset(): void
+    {
+        if (empty(static::$_initialState)) {
+            static::$_initialState = get_class_vars(self::class);
+
+            return;
+        }
+        foreach (static::$_initialState as $key => $val) {
+            if ($key !== '_initialState') {
+                static::${$key} = $val;
+            }
+        }
+    }
+
+    /**
+     * Adds custom inflection $rules, of either 'plural', 'singular',
+     * 'uninflected' or 'irregular' $type.
+     *
+     * ### Usage:
+     *
+     * ```
+     * Inflector::rules('plural', ['/^(inflect)or$/i' => '\1ables']);
+     * Inflector::rules('irregular', ['red' => 'redlings']);
+     * Inflector::rules('uninflected', ['dontinflectme']);
+     * ```
+     *
+     * @param string $type The type of inflection, either 'plural', 'singular',
+     *    or 'uninflected'.
+     * @param array $rules Array of rules to be added.
+     * @param bool $reset If true, will unset default inflections for all
+     *        new rules that are being defined in $rules.
+     * @return void
+     */
+    public static function rules(string $type, array $rules, bool $reset = false): void
+    {
+        $var = '_' . $type;
+
+        if ($reset) {
+            static::${$var} = $rules;
+        } elseif ($type === 'uninflected') {
+            static::$_uninflected = array_merge(
+                $rules,
+                static::$_uninflected
+            );
+        } else {
+            static::${$var} = $rules + static::${$var};
+        }
+
+        static::$_cache = [];
+    }
+
+    /**
+     * Return $word in plural form.
+     *
+     * @param string $word Word in singular
+     * @return string Word in plural
+     * @link https://book.cakephp.org/4/en/core-libraries/inflector.html#creating-plural-singular-forms
+     */
+    public static function pluralize(string $word): string
+    {
+        if (isset(static::$_cache['pluralize'][$word])) {
+            return static::$_cache['pluralize'][$word];
+        }
+
+        if (!isset(static::$_cache['irregular']['pluralize'])) {
+            $words = array_keys(static::$_irregular);
+            static::$_cache['irregular']['pluralize'] = '/(.*?(?:\\b|_))(' . implode('|', $words) . ')$/i';
+
+            $upperWords = array_map('ucfirst', $words);
+            static::$_cache['irregular']['upperPluralize'] = '/(.*?(?:\\b|[a-z]))(' . implode('|', $upperWords) . ')$/';
+        }
+
+        if (
+            preg_match(static::$_cache['irregular']['pluralize'], $word, $regs) ||
+            preg_match(static::$_cache['irregular']['upperPluralize'], $word, $regs)
+        ) {
+            static::$_cache['pluralize'][$word] = $regs[1] . substr($regs[2], 0, 1) .
+                substr(static::$_irregular[strtolower($regs[2])], 1);
+
+            return static::$_cache['pluralize'][$word];
+        }
+
+        if (!isset(static::$_cache['uninflected'])) {
+            static::$_cache['uninflected'] = '/^(' . implode('|', static::$_uninflected) . ')$/i';
+        }
+
+        if (preg_match(static::$_cache['uninflected'], $word, $regs)) {
+            static::$_cache['pluralize'][$word] = $word;
+
+            return $word;
+        }
+
+        foreach (static::$_plural as $rule => $replacement) {
+            if (preg_match($rule, $word)) {
+                static::$_cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);
+
+                return static::$_cache['pluralize'][$word];
+            }
+        }
+
+        return $word;
+    }
+
+    /**
+     * Return $word in singular form.
+     *
+     * @param string $word Word in plural
+     * @return string Word in singular
+     * @link https://book.cakephp.org/4/en/core-libraries/inflector.html#creating-plural-singular-forms
+     */
+    public static function singularize(string $word): string
+    {
+        if (isset(static::$_cache['singularize'][$word])) {
+            return static::$_cache['singularize'][$word];
+        }
+
+        if (!isset(static::$_cache['irregular']['singular'])) {
+            $wordList = array_values(static::$_irregular);
+            static::$_cache['irregular']['singular'] = '/(.*?(?:\\b|_))(' . implode('|', $wordList) . ')$/i';
+
+            $upperWordList = array_map('ucfirst', $wordList);
+            static::$_cache['irregular']['singularUpper'] = '/(.*?(?:\\b|[a-z]))(' .
+                implode('|', $upperWordList) .
+                ')$/';
+        }
+
+        if (
+            preg_match(static::$_cache['irregular']['singular'], $word, $regs) ||
+            preg_match(static::$_cache['irregular']['singularUpper'], $word, $regs)
+        ) {
+            $suffix = array_search(strtolower($regs[2]), static::$_irregular, true);
+            $suffix = $suffix ? substr($suffix, 1) : '';
+            static::$_cache['singularize'][$word] = $regs[1] . substr($regs[2], 0, 1) . $suffix;
+
+            return static::$_cache['singularize'][$word];
+        }
+
+        if (!isset(static::$_cache['uninflected'])) {
+            static::$_cache['uninflected'] = '/^(' . implode('|', static::$_uninflected) . ')$/i';
+        }
+
+        if (preg_match(static::$_cache['uninflected'], $word, $regs)) {
+            static::$_cache['pluralize'][$word] = $word;
+
+            return $word;
+        }
+
+        foreach (static::$_singular as $rule => $replacement) {
+            if (preg_match($rule, $word)) {
+                static::$_cache['singularize'][$word] = preg_replace($rule, $replacement, $word);
+
+                return static::$_cache['singularize'][$word];
+            }
+        }
+        static::$_cache['singularize'][$word] = $word;
+
+        return $word;
+    }
+
+    /**
+     * Returns the input lower_case_delimited_string as a CamelCasedString.
+     *
+     * @param string $string String to camelize
+     * @param string $delimiter the delimiter in the input string
+     * @return string CamelizedStringLikeThis.
+     * @link https://book.cakephp.org/4/en/core-libraries/inflector.html#creating-camelcase-and-under-scored-forms
+     */
+    public static function camelize(string $string, string $delimiter = '_'): string
+    {
+        $cacheKey = __FUNCTION__ . $delimiter;
+
+        $result = static::_cache($cacheKey, $string);
+
+        if ($result === false) {
+            $result = str_replace(' ', '', static::humanize($string, $delimiter));
+            static::_cache($cacheKey, $string, $result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns the input CamelCasedString as an underscored_string.
+     *
+     * Also replaces dashes with underscores
+     *
+     * @param string $string CamelCasedString to be "underscorized"
+     * @return string underscore_version of the input string
+     * @link https://book.cakephp.org/4/en/core-libraries/inflector.html#creating-camelcase-and-under-scored-forms
+     */
+    public static function underscore(string $string): string
+    {
+        return static::delimit(str_replace('-', '_', $string), '_');
+    }
+
+    /**
+     * Returns the input CamelCasedString as an dashed-string.
+     *
+     * Also replaces underscores with dashes
+     *
+     * @param string $string The string to dasherize.
+     * @return string Dashed version of the input string
+     */
+    public static function dasherize(string $string): string
+    {
+        return static::delimit(str_replace('_', '-', $string), '-');
+    }
+
+    /**
+     * Returns the input lower_case_delimited_string as 'A Human Readable String'.
+     * (Underscores are replaced by spaces and capitalized following words.)
+     *
+     * @param string $string String to be humanized
+     * @param string $delimiter the character to replace with a space
+     * @return string Human-readable string
+     * @link https://book.cakephp.org/4/en/core-libraries/inflector.html#creating-human-readable-forms
+     */
+    public static function humanize(string $string, string $delimiter = '_'): string
+    {
+        $cacheKey = __FUNCTION__ . $delimiter;
+
+        $result = static::_cache($cacheKey, $string);
+
+        if ($result === false) {
+            $result = explode(' ', str_replace($delimiter, ' ', $string));
+            foreach ($result as &$word) {
+                $word = mb_strtoupper(mb_substr($word, 0, 1)) . mb_substr($word, 1);
+            }
+            $result = implode(' ', $result);
+            static::_cache($cacheKey, $string, $result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Expects a CamelCasedInputString, and produces a lower_case_delimited_string
+     *
+     * @param string $string String to delimit
+     * @param string $delimiter the character to use as a delimiter
+     * @return string delimited string
+     */
+    public static function delimit(string $string, string $delimiter = '_'): string
+    {
+        $cacheKey = __FUNCTION__ . $delimiter;
+
+        $result = static::_cache($cacheKey, $string);
+
+        if ($result === false) {
+            $result = mb_strtolower(preg_replace('/(?<=\\w)([A-Z])/', $delimiter . '\\1', $string));
+            static::_cache($cacheKey, $string, $result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns corresponding table name for given model $className. ("people" for the class name "Person").
+     *
+     * @param string $className Name of class to get database table name for
+     * @return string Name of the database table for given class
+     * @link https://book.cakephp.org/4/en/core-libraries/inflector.html#creating-table-and-class-name-forms
+     */
+    public static function tableize(string $className): string
+    {
+        $result = static::_cache(__FUNCTION__, $className);
+
+        if ($result === false) {
+            $result = static::pluralize(static::underscore($className));
+            static::_cache(__FUNCTION__, $className, $result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns a singular, CamelCase inflection for given database table. ("Person" for the table name "people")
+     *
+     * @param string $tableName Name of database table to get class name for
+     * @return string Class name
+     * @link https://book.cakephp.org/4/en/core-libraries/inflector.html#creating-table-and-class-name-forms
+     */
+    public static function classify(string $tableName): string
+    {
+        $result = static::_cache(__FUNCTION__, $tableName);
+
+        if ($result === false) {
+            $result = static::camelize(static::singularize($tableName));
+            static::_cache(__FUNCTION__, $tableName, $result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns camelBacked version of an underscored string.
+     *
+     * @param string $string String to convert.
+     * @return string in variable form
+     * @link https://book.cakephp.org/4/en/core-libraries/inflector.html#creating-variable-names
+     */
+    public static function variable(string $string): string
+    {
+        $result = static::_cache(__FUNCTION__, $string);
+
+        if ($result === false) {
+            $camelized = static::camelize(static::underscore($string));
+            $replace = strtolower(substr($camelized, 0, 1));
+            $result = $replace . substr($camelized, 1);
+            static::_cache(__FUNCTION__, $string, $result);
+        }
+
+        return $result;
+    }
+}
diff --git a/vendor/cakephp/utility/LICENSE.txt b/vendor/cakephp/utility/LICENSE.txt
new file mode 100644
index 0000000..b938c9e
--- /dev/null
+++ b/vendor/cakephp/utility/LICENSE.txt
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org)
+Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org)
+
+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/cakephp/utility/MergeVariablesTrait.php b/vendor/cakephp/utility/MergeVariablesTrait.php
new file mode 100644
index 0000000..39bafb8
--- /dev/null
+++ b/vendor/cakephp/utility/MergeVariablesTrait.php
@@ -0,0 +1,118 @@
+ $properties An array of properties and the merge strategy for them.
+     * @param array $options The options to use when merging properties.
+     * @return void
+     */
+    protected function _mergeVars(array $properties, array $options = []): void
+    {
+        $class = static::class;
+        $parents = [];
+        while (true) {
+            $parent = get_parent_class($class);
+            if (!$parent) {
+                break;
+            }
+            $parents[] = $parent;
+            $class = $parent;
+        }
+        foreach ($properties as $property) {
+            if (!property_exists($this, $property)) {
+                continue;
+            }
+            $thisValue = $this->{$property};
+            if ($thisValue === null || $thisValue === false) {
+                continue;
+            }
+            $this->_mergeProperty($property, $parents, $options);
+        }
+    }
+
+    /**
+     * Merge a single property with the values declared in all parent classes.
+     *
+     * @param string $property The name of the property being merged.
+     * @param array $parentClasses An array of classes you want to merge with.
+     * @param array $options Options for merging the property, see _mergeVars()
+     * @return void
+     */
+    protected function _mergeProperty(string $property, array $parentClasses, array $options): void
+    {
+        $thisValue = $this->{$property};
+        $isAssoc = false;
+        if (
+            isset($options['associative']) &&
+            in_array($property, (array)$options['associative'], true)
+        ) {
+            $isAssoc = true;
+        }
+
+        if ($isAssoc) {
+            $thisValue = Hash::normalize($thisValue);
+        }
+        foreach ($parentClasses as $class) {
+            $parentProperties = get_class_vars($class);
+            if (empty($parentProperties[$property])) {
+                continue;
+            }
+            $parentProperty = $parentProperties[$property];
+            if (!is_array($parentProperty)) {
+                continue;
+            }
+            $thisValue = $this->_mergePropertyData($thisValue, $parentProperty, $isAssoc);
+        }
+        $this->{$property} = $thisValue;
+    }
+
+    /**
+     * Merge each of the keys in a property together.
+     *
+     * @param array $current The current merged value.
+     * @param array $parent The parent class' value.
+     * @param bool $isAssoc Whether the merging should be done in associative mode.
+     * @return array The updated value.
+     */
+    protected function _mergePropertyData(array $current, array $parent, bool $isAssoc)
+    {
+        if (!$isAssoc) {
+            return array_merge($parent, $current);
+        }
+        $parent = Hash::normalize($parent);
+        foreach ($parent as $key => $value) {
+            if (!isset($current[$key])) {
+                $current[$key] = $value;
+            }
+        }
+
+        return $current;
+    }
+}
diff --git a/vendor/cakephp/utility/README.md b/vendor/cakephp/utility/README.md
new file mode 100644
index 0000000..45601b8
--- /dev/null
+++ b/vendor/cakephp/utility/README.md
@@ -0,0 +1,91 @@
+[![Total Downloads](https://img.shields.io/packagist/dt/cakephp/utility.svg?style=flat-square)](https://packagist.org/packages/cakephp/utility)
+[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE.txt)
+
+# CakePHP Utility Classes
+
+This library provides a range of utility classes that are used throughout the CakePHP framework
+
+## What's in the toolbox?
+
+### Hash
+
+A ``Hash`` (as in PHP arrays) class, capable of extracting data using an intuitive DSL:
+
+```php
+$things = [
+    ['name' => 'Mark', 'age' => 15],
+    ['name' => 'Susan', 'age' => 30],
+    ['name' => 'Lucy', 'age' => 25]
+];
+
+$bigPeople = Hash::extract($things, '{n}[age>21].name');
+
+// $bigPeople will contain ['Susan', 'Lucy']
+```
+
+Check the [official Hash class documentation](https://book.cakephp.org/4/en/core-libraries/hash.html)
+
+### Inflector
+
+The Inflector class takes a string and can manipulate it to handle word variations
+such as pluralizations or camelizing.
+
+```php
+echo Inflector::pluralize('Apple'); // echoes Apples
+
+echo Inflector::singularize('People'); // echoes Person
+```
+
+Check the [official Inflector class documentation](https://book.cakephp.org/4/en/core-libraries/inflector.html)
+
+### Text
+
+The Text class includes convenience methods for creating and manipulating strings.
+
+```php
+Text::insert(
+    'My name is :name and I am :age years old.',
+    ['name' => 'Bob', 'age' => '65']
+);
+// Returns: "My name is Bob and I am 65 years old."
+
+$text = 'This is the song that never ends.';
+$result = Text::wrap($text, 22);
+
+// Returns
+This is the song
+that never ends.
+```
+
+Check the [official Text class documentation](https://book.cakephp.org/4/en/core-libraries/text.html)
+
+### Security
+
+The security library handles basic security measures such as providing methods for hashing and encrypting data.
+
+```php
+$key = 'wt1U5MACWJFTXGenFoZoiLwQGrLgdbHA';
+$result = Security::encrypt($value, $key);
+
+Security::decrypt($result, $key);
+```
+
+Check the [official Security class documentation](https://book.cakephp.org/4/en/core-libraries/security.html)
+
+### Xml
+
+The Xml class allows you to easily transform arrays into SimpleXMLElement or DOMDocument objects
+and back into arrays again
+
+```php
+$data = [
+    'post' => [
+        'id' => 1,
+        'title' => 'Best post',
+        'body' => ' ... '
+    ]
+];
+$xml = Xml::build($data);
+```
+
+Check the [official Xml class documentation](https://book.cakephp.org/4/en/core-libraries/xml.html)
diff --git a/vendor/cakephp/utility/Security.php b/vendor/cakephp/utility/Security.php
new file mode 100644
index 0000000..a46d322
--- /dev/null
+++ b/vendor/cakephp/utility/Security.php
@@ -0,0 +1,309 @@
+encrypt($plain, $key);
+        $hmac = hash_hmac('sha256', $ciphertext, $key);
+
+        return $hmac . $ciphertext;
+    }
+
+    /**
+     * Check the encryption key for proper length.
+     *
+     * @param string $key Key to check.
+     * @param string $method The method the key is being checked for.
+     * @return void
+     * @throws \InvalidArgumentException When key length is not 256 bit/32 bytes
+     */
+    protected static function _checkKey(string $key, string $method): void
+    {
+        if (mb_strlen($key, '8bit') < 32) {
+            throw new InvalidArgumentException(
+                sprintf('Invalid key for %s, key must be at least 256 bits (32 bytes) long.', $method)
+            );
+        }
+    }
+
+    /**
+     * Decrypt a value using AES-256.
+     *
+     * @param string $cipher The ciphertext to decrypt.
+     * @param string $key The 256 bit/32 byte key to use as a cipher key.
+     * @param string|null $hmacSalt The salt to use for the HMAC process.
+     *   Leave null to use value of Security::getSalt().
+     * @return string|null Decrypted data. Any trailing null bytes will be removed.
+     * @throws \InvalidArgumentException On invalid data or key.
+     */
+    public static function decrypt(string $cipher, string $key, ?string $hmacSalt = null): ?string
+    {
+        self::_checkKey($key, 'decrypt()');
+        if (empty($cipher)) {
+            throw new InvalidArgumentException('The data to decrypt cannot be empty.');
+        }
+        if ($hmacSalt === null) {
+            $hmacSalt = static::getSalt();
+        }
+
+        // Generate the encryption and hmac key.
+        $key = mb_substr(hash('sha256', $key . $hmacSalt), 0, 32, '8bit');
+
+        // Split out hmac for comparison
+        $macSize = 64;
+        $hmac = mb_substr($cipher, 0, $macSize, '8bit');
+        $cipher = mb_substr($cipher, $macSize, null, '8bit');
+
+        $compareHmac = hash_hmac('sha256', $cipher, $key);
+        if (!static::constantEquals($hmac, $compareHmac)) {
+            return null;
+        }
+
+        $crypto = static::engine();
+
+        return $crypto->decrypt($cipher, $key);
+    }
+
+    /**
+     * A timing attack resistant comparison that prefers native PHP implementations.
+     *
+     * @param mixed $original The original value.
+     * @param mixed $compare The comparison value.
+     * @return bool
+     * @since 3.6.2
+     */
+    public static function constantEquals($original, $compare): bool
+    {
+        return is_string($original) && is_string($compare) && hash_equals($original, $compare);
+    }
+
+    /**
+     * Gets the HMAC salt to be used for encryption/decryption
+     * routines.
+     *
+     * @return string The currently configured salt
+     */
+    public static function getSalt(): string
+    {
+        if (static::$_salt === null) {
+            throw new RuntimeException(
+                'Salt not set. Use Security::setSalt() to set one, ideally in `config/bootstrap.php`.'
+            );
+        }
+
+        return static::$_salt;
+    }
+
+    /**
+     * Sets the HMAC salt to be used for encryption/decryption
+     * routines.
+     *
+     * @param string $salt The salt to use for encryption routines.
+     * @return void
+     */
+    public static function setSalt(string $salt): void
+    {
+        static::$_salt = $salt;
+    }
+}
diff --git a/vendor/cakephp/utility/Text.php b/vendor/cakephp/utility/Text.php
new file mode 100644
index 0000000..7116b0d
--- /dev/null
+++ b/vendor/cakephp/utility/Text.php
@@ -0,0 +1,1180 @@
+
+     */
+    protected static $_defaultHtmlNoCount = [
+        'style',
+        'script',
+    ];
+
+    /**
+     * Generate a random UUID version 4
+     *
+     * Warning: This method should not be used as a random seed for any cryptographic operations.
+     * Instead, you should use `Security::randomBytes()` or `Security::randomString()` instead.
+     *
+     * It should also not be used to create identifiers that have security implications, such as
+     * 'unguessable' URL identifiers. Instead, you should use {@link \Cake\Utility\Security::randomBytes()}` for that.
+     *
+     * @see https://www.ietf.org/rfc/rfc4122.txt
+     * @return string RFC 4122 UUID
+     * @copyright Matt Farina MIT License https://github.com/lootils/uuid/blob/master/LICENSE
+     */
+    public static function uuid(): string
+    {
+        return sprintf(
+            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+            // 32 bits for "time_low"
+            random_int(0, 65535),
+            random_int(0, 65535),
+            // 16 bits for "time_mid"
+            random_int(0, 65535),
+            // 12 bits before the 0100 of (version) 4 for "time_hi_and_version"
+            random_int(0, 4095) | 0x4000,
+            // 16 bits, 8 bits for "clk_seq_hi_res",
+            // 8 bits for "clk_seq_low",
+            // two most significant bits holds zero and one for variant DCE1.1
+            random_int(0, 0x3fff) | 0x8000,
+            // 48 bits for "node"
+            random_int(0, 65535),
+            random_int(0, 65535),
+            random_int(0, 65535)
+        );
+    }
+
+    /**
+     * Tokenizes a string using $separator, ignoring any instance of $separator that appears between
+     * $leftBound and $rightBound.
+     *
+     * @param string $data The data to tokenize.
+     * @param string $separator The token to split the data on.
+     * @param string $leftBound The left boundary to ignore separators in.
+     * @param string $rightBound The right boundary to ignore separators in.
+     * @return array Array of tokens in $data.
+     */
+    public static function tokenize(
+        string $data,
+        string $separator = ',',
+        string $leftBound = '(',
+        string $rightBound = ')'
+    ): array {
+        if (empty($data)) {
+            return [];
+        }
+
+        $depth = 0;
+        $offset = 0;
+        $buffer = '';
+        $results = [];
+        $length = mb_strlen($data);
+        $open = false;
+
+        while ($offset <= $length) {
+            $tmpOffset = -1;
+            $offsets = [
+                mb_strpos($data, $separator, $offset),
+                mb_strpos($data, $leftBound, $offset),
+                mb_strpos($data, $rightBound, $offset),
+            ];
+            for ($i = 0; $i < 3; $i++) {
+                if ($offsets[$i] !== false && ($offsets[$i] < $tmpOffset || $tmpOffset === -1)) {
+                    $tmpOffset = $offsets[$i];
+                }
+            }
+            if ($tmpOffset !== -1) {
+                $buffer .= mb_substr($data, $offset, $tmpOffset - $offset);
+                $char = mb_substr($data, $tmpOffset, 1);
+                if (!$depth && $char === $separator) {
+                    $results[] = $buffer;
+                    $buffer = '';
+                } else {
+                    $buffer .= $char;
+                }
+                if ($leftBound !== $rightBound) {
+                    if ($char === $leftBound) {
+                        $depth++;
+                    }
+                    if ($char === $rightBound) {
+                        $depth--;
+                    }
+                } else {
+                    if ($char === $leftBound) {
+                        if (!$open) {
+                            $depth++;
+                            $open = true;
+                        } else {
+                            $depth--;
+                            $open = false;
+                        }
+                    }
+                }
+                $tmpOffset += 1;
+                $offset = $tmpOffset;
+            } else {
+                $results[] = $buffer . mb_substr($data, $offset);
+                $offset = $length + 1;
+            }
+        }
+        if (empty($results) && !empty($buffer)) {
+            $results[] = $buffer;
+        }
+
+        if (!empty($results)) {
+            return array_map('trim', $results);
+        }
+
+        return [];
+    }
+
+    /**
+     * Replaces variable placeholders inside a $str with any given $data. Each key in the $data array
+     * corresponds to a variable placeholder name in $str.
+     * Example:
+     * ```
+     * Text::insert(':name is :age years old.', ['name' => 'Bob', 'age' => '65']);
+     * ```
+     * Returns: Bob is 65 years old.
+     *
+     * Available $options are:
+     *
+     * - before: The character or string in front of the name of the variable placeholder (Defaults to `:`)
+     * - after: The character or string after the name of the variable placeholder (Defaults to null)
+     * - escape: The character or string used to escape the before character / string (Defaults to `\`)
+     * - format: A regex to use for matching variable placeholders. Default is: `/(? val array where each key stands for a placeholder variable name
+     *     to be replaced with val
+     * @param array $options An array of options, see description above
+     * @return string
+     */
+    public static function insert(string $str, array $data, array $options = []): string
+    {
+        $defaults = [
+            'before' => ':', 'after' => '', 'escape' => '\\', 'format' => null, 'clean' => false,
+        ];
+        $options += $defaults;
+        if (empty($data)) {
+            return $options['clean'] ? static::cleanInsert($str, $options) : $str;
+        }
+
+        if (strpos($str, '?') !== false && is_numeric(key($data))) {
+            deprecationWarning(
+                'Using Text::insert() with `?` placeholders is deprecated. ' .
+                'Use sprintf() with `%s` placeholders instead.'
+            );
+
+            $offset = 0;
+            while (($pos = strpos($str, '?', $offset)) !== false) {
+                $val = array_shift($data);
+                $offset = $pos + strlen($val);
+                $str = substr_replace($str, $val, $pos, 1);
+            }
+
+            return $options['clean'] ? static::cleanInsert($str, $options) : $str;
+        }
+
+        $format = $options['format'];
+        if ($format === null) {
+            $format = sprintf(
+                '/(? $tempData */
+        $tempData = array_combine($dataKeys, $hashKeys);
+        krsort($tempData);
+
+        foreach ($tempData as $key => $hashVal) {
+            $key = sprintf($format, preg_quote($key, '/'));
+            $str = preg_replace($key, $hashVal, $str);
+        }
+        /** @var array $dataReplacements */
+        $dataReplacements = array_combine($hashKeys, array_values($data));
+        foreach ($dataReplacements as $tmpHash => $tmpValue) {
+            $tmpValue = is_array($tmpValue) ? '' : (string)$tmpValue;
+            $str = str_replace($tmpHash, $tmpValue, $str);
+        }
+
+        if (!isset($options['format']) && isset($options['before'])) {
+            $str = str_replace($options['escape'] . $options['before'], $options['before'], $str);
+        }
+
+        return $options['clean'] ? static::cleanInsert($str, $options) : $str;
+    }
+
+    /**
+     * Cleans up a Text::insert() formatted string with given $options depending on the 'clean' key in
+     * $options. The default method used is text but html is also available. The goal of this function
+     * is to replace all whitespace and unneeded markup around placeholders that did not get replaced
+     * by Text::insert().
+     *
+     * @param string $str String to clean.
+     * @param array $options Options list.
+     * @return string
+     * @see \Cake\Utility\Text::insert()
+     */
+    public static function cleanInsert(string $str, array $options): string
+    {
+        $clean = $options['clean'];
+        if (!$clean) {
+            return $str;
+        }
+        if ($clean === true) {
+            $clean = ['method' => 'text'];
+        }
+        if (!is_array($clean)) {
+            $clean = ['method' => $options['clean']];
+        }
+        switch ($clean['method']) {
+            case 'html':
+                $clean += [
+                    'word' => '[\w,.]+',
+                    'andText' => true,
+                    'replacement' => '',
+                ];
+                $kleenex = sprintf(
+                    '/[\s]*[a-z]+=(")(%s%s%s[\s]*)+\\1/i',
+                    preg_quote($options['before'], '/'),
+                    $clean['word'],
+                    preg_quote($options['after'], '/')
+                );
+                $str = preg_replace($kleenex, $clean['replacement'], $str);
+                if ($clean['andText']) {
+                    $options['clean'] = ['method' => 'text'];
+                    $str = static::cleanInsert($str, $options);
+                }
+                break;
+            case 'text':
+                $clean += [
+                    'word' => '[\w,.]+',
+                    'gap' => '[\s]*(?:(?:and|or)[\s]*)?',
+                    'replacement' => '',
+                ];
+
+                $kleenex = sprintf(
+                    '/(%s%s%s%s|%s%s%s%s)/',
+                    preg_quote($options['before'], '/'),
+                    $clean['word'],
+                    preg_quote($options['after'], '/'),
+                    $clean['gap'],
+                    $clean['gap'],
+                    preg_quote($options['before'], '/'),
+                    $clean['word'],
+                    preg_quote($options['after'], '/')
+                );
+                $str = preg_replace($kleenex, $clean['replacement'], $str);
+                break;
+        }
+
+        return $str;
+    }
+
+    /**
+     * Wraps text to a specific width, can optionally wrap at word breaks.
+     *
+     * ### Options
+     *
+     * - `width` The width to wrap to. Defaults to 72.
+     * - `wordWrap` Only wrap on words breaks (spaces) Defaults to true.
+     * - `indent` String to indent with. Defaults to null.
+     * - `indentAt` 0 based index to start indenting at. Defaults to 0.
+     *
+     * @param string $text The text to format.
+     * @param array|int $options Array of options to use, or an integer to wrap the text to.
+     * @return string Formatted text.
+     */
+    public static function wrap(string $text, $options = []): string
+    {
+        if (is_numeric($options)) {
+            $options = ['width' => $options];
+        }
+        $options += ['width' => 72, 'wordWrap' => true, 'indent' => null, 'indentAt' => 0];
+        if ($options['wordWrap']) {
+            $wrapped = self::wordWrap($text, $options['width'], "\n");
+        } else {
+            $wrapped = trim(chunk_split($text, $options['width'] - 1, "\n"));
+        }
+        if (!empty($options['indent'])) {
+            $chunks = explode("\n", $wrapped);
+            for ($i = $options['indentAt'], $len = count($chunks); $i < $len; $i++) {
+                $chunks[$i] = $options['indent'] . $chunks[$i];
+            }
+            $wrapped = implode("\n", $chunks);
+        }
+
+        return $wrapped;
+    }
+
+    /**
+     * Wraps a complete block of text to a specific width, can optionally wrap
+     * at word breaks.
+     *
+     * ### Options
+     *
+     * - `width` The width to wrap to. Defaults to 72.
+     * - `wordWrap` Only wrap on words breaks (spaces) Defaults to true.
+     * - `indent` String to indent with. Defaults to null.
+     * - `indentAt` 0 based index to start indenting at. Defaults to 0.
+     *
+     * @param string $text The text to format.
+     * @param array|int $options Array of options to use, or an integer to wrap the text to.
+     * @return string Formatted text.
+     */
+    public static function wrapBlock(string $text, $options = []): string
+    {
+        if (is_numeric($options)) {
+            $options = ['width' => $options];
+        }
+        $options += ['width' => 72, 'wordWrap' => true, 'indent' => null, 'indentAt' => 0];
+
+        $wrapped = self::wrap($text, $options);
+
+        if (!empty($options['indent'])) {
+            $indentationLength = mb_strlen($options['indent']);
+            $chunks = explode("\n", $wrapped);
+            $count = count($chunks);
+            if ($count < 2) {
+                return $wrapped;
+            }
+            $toRewrap = '';
+            for ($i = $options['indentAt']; $i < $count; $i++) {
+                $toRewrap .= mb_substr($chunks[$i], $indentationLength) . ' ';
+                unset($chunks[$i]);
+            }
+            $options['width'] -= $indentationLength;
+            $options['indentAt'] = 0;
+            $rewrapped = self::wrap($toRewrap, $options);
+            $newChunks = explode("\n", $rewrapped);
+
+            $chunks = array_merge($chunks, $newChunks);
+            $wrapped = implode("\n", $chunks);
+        }
+
+        return $wrapped;
+    }
+
+    /**
+     * Unicode and newline aware version of wordwrap.
+     *
+     * @phpstan-param non-empty-string $break
+     * @param string $text The text to format.
+     * @param int $width The width to wrap to. Defaults to 72.
+     * @param string $break The line is broken using the optional break parameter. Defaults to '\n'.
+     * @param bool $cut If the cut is set to true, the string is always wrapped at the specified width.
+     * @return string Formatted text.
+     */
+    public static function wordWrap(string $text, int $width = 72, string $break = "\n", bool $cut = false): string
+    {
+        $paragraphs = explode($break, $text);
+        foreach ($paragraphs as &$paragraph) {
+            $paragraph = static::_wordWrap($paragraph, $width, $break, $cut);
+        }
+
+        return implode($break, $paragraphs);
+    }
+
+    /**
+     * Unicode aware version of wordwrap as helper method.
+     *
+     * @param string $text The text to format.
+     * @param int $width The width to wrap to. Defaults to 72.
+     * @param string $break The line is broken using the optional break parameter. Defaults to '\n'.
+     * @param bool $cut If the cut is set to true, the string is always wrapped at the specified width.
+     * @return string Formatted text.
+     */
+    protected static function _wordWrap(string $text, int $width = 72, string $break = "\n", bool $cut = false): string
+    {
+        $parts = [];
+        if ($cut) {
+            while (mb_strlen($text) > 0) {
+                $part = mb_substr($text, 0, $width);
+                $parts[] = trim($part);
+                $text = trim(mb_substr($text, mb_strlen($part)));
+            }
+
+            return implode($break, $parts);
+        }
+
+        while (mb_strlen($text) > 0) {
+            if ($width >= mb_strlen($text)) {
+                $parts[] = trim($text);
+                break;
+            }
+
+            $part = mb_substr($text, 0, $width);
+            $nextChar = mb_substr($text, $width, 1);
+            if ($nextChar !== ' ') {
+                $breakAt = mb_strrpos($part, ' ');
+                if ($breakAt === false) {
+                    $breakAt = mb_strpos($text, ' ', $width);
+                }
+                if ($breakAt === false) {
+                    $parts[] = trim($text);
+                    break;
+                }
+                $part = mb_substr($text, 0, $breakAt);
+            }
+
+            $part = trim($part);
+            $parts[] = $part;
+            $text = trim(mb_substr($text, mb_strlen($part)));
+        }
+
+        return implode($break, $parts);
+    }
+
+    /**
+     * Highlights a given phrase in a text. You can specify any expression in highlighter that
+     * may include the \1 expression to include the $phrase found.
+     *
+     * ### Options:
+     *
+     * - `format` The piece of HTML with that the phrase will be highlighted
+     * - `html` If true, will ignore any HTML tags, ensuring that only the correct text is highlighted
+     * - `regex` A custom regex rule that is used to match words, default is '|$tag|iu'
+     * - `limit` A limit, optional, defaults to -1 (none)
+     *
+     * @param string $text Text to search the phrase in.
+     * @param array|string $phrase The phrase or phrases that will be searched.
+     * @param array $options An array of HTML attributes and options.
+     * @return string The highlighted text
+     * @link https://book.cakephp.org/4/en/core-libraries/text.html#highlighting-substrings
+     */
+    public static function highlight(string $text, $phrase, array $options = []): string
+    {
+        if (empty($phrase)) {
+            return $text;
+        }
+
+        $defaults = [
+            'format' => '\1',
+            'html' => false,
+            'regex' => '|%s|iu',
+            'limit' => -1,
+        ];
+        $options += $defaults;
+
+        if (is_array($phrase)) {
+            $replace = [];
+            $with = [];
+
+            foreach ($phrase as $key => $segment) {
+                $segment = '(' . preg_quote($segment, '|') . ')';
+                if ($options['html']) {
+                    $segment = "(?![^<]+>)$segment(?![^<]+>)";
+                }
+
+                $with[] = is_array($options['format']) ? $options['format'][$key] : $options['format'];
+                $replace[] = sprintf($options['regex'], $segment);
+            }
+
+            return preg_replace($replace, $with, $text, $options['limit']);
+        }
+
+        $phrase = '(' . preg_quote($phrase, '|') . ')';
+        if ($options['html']) {
+            $phrase = "(?![^<]+>)$phrase(?![^<]+>)";
+        }
+
+        return preg_replace(
+            sprintf($options['regex'], $phrase),
+            $options['format'],
+            $text,
+            $options['limit']
+        );
+    }
+
+    /**
+     * Truncates text starting from the end.
+     *
+     * Cuts a string to the length of $length and replaces the first characters
+     * with the ellipsis if the text is longer than length.
+     *
+     * ### Options:
+     *
+     * - `ellipsis` Will be used as beginning and prepended to the trimmed string
+     * - `exact` If false, $text will not be cut mid-word
+     *
+     * @param string $text String to truncate.
+     * @param int $length Length of returned string, including ellipsis.
+     * @param array $options An array of options.
+     * @return string Trimmed string.
+     */
+    public static function tail(string $text, int $length = 100, array $options = []): string
+    {
+        $default = [
+            'ellipsis' => '...', 'exact' => true,
+        ];
+        $options += $default;
+        $ellipsis = $options['ellipsis'];
+
+        if (mb_strlen($text) <= $length) {
+            return $text;
+        }
+
+        $truncate = mb_substr($text, mb_strlen($text) - $length + mb_strlen($ellipsis));
+        if (!$options['exact']) {
+            $spacepos = mb_strpos($truncate, ' ');
+            $truncate = $spacepos === false ? '' : trim(mb_substr($truncate, $spacepos));
+        }
+
+        return $ellipsis . $truncate;
+    }
+
+    /**
+     * Truncates text.
+     *
+     * Cuts a string to the length of $length and replaces the last characters
+     * with the ellipsis if the text is longer than length.
+     *
+     * ### Options:
+     *
+     * - `ellipsis` Will be used as ending and appended to the trimmed string
+     * - `exact` If false, $text will not be cut mid-word
+     * - `html` If true, HTML tags would be handled correctly
+     * - `trimWidth` If true, $text will be truncated with the width
+     *
+     * @param string $text String to truncate.
+     * @param int $length Length of returned string, including ellipsis.
+     * @param array $options An array of HTML attributes and options.
+     * @return string Trimmed string.
+     * @link https://book.cakephp.org/4/en/core-libraries/text.html#truncating-text
+     */
+    public static function truncate(string $text, int $length = 100, array $options = []): string
+    {
+        $default = [
+            'ellipsis' => '...', 'exact' => true, 'html' => false, 'trimWidth' => false,
+        ];
+        if (!empty($options['html']) && strtolower((string)mb_internal_encoding()) === 'utf-8') {
+            $default['ellipsis'] = "\xe2\x80\xa6";
+        }
+        $options += $default;
+
+        $prefix = '';
+        $suffix = $options['ellipsis'];
+
+        if ($options['html']) {
+            $ellipsisLength = self::_strlen(strip_tags($options['ellipsis']), $options);
+
+            $truncateLength = 0;
+            $totalLength = 0;
+            $openTags = [];
+            $truncate = '';
+
+            preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER);
+            foreach ($tags as $tag) {
+                $contentLength = 0;
+                if (!in_array($tag[2], static::$_defaultHtmlNoCount, true)) {
+                    $contentLength = self::_strlen($tag[3], $options);
+                }
+
+                if ($truncate === '') {
+                    if (
+                        !preg_match(
+                            '/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/i',
+                            $tag[2]
+                        )
+                    ) {
+                        if (preg_match('/<[\w]+[^>]*>/', $tag[0])) {
+                            array_unshift($openTags, $tag[2]);
+                        } elseif (preg_match('/<\/([\w]+)[^>]*>/', $tag[0], $closeTag)) {
+                            $pos = array_search($closeTag[1], $openTags, true);
+                            if ($pos !== false) {
+                                array_splice($openTags, $pos, 1);
+                            }
+                        }
+                    }
+
+                    $prefix .= $tag[1];
+
+                    if ($totalLength + $contentLength + $ellipsisLength > $length) {
+                        $truncate = $tag[3];
+                        $truncateLength = $length - $totalLength;
+                    } else {
+                        $prefix .= $tag[3];
+                    }
+                }
+
+                $totalLength += $contentLength;
+                if ($totalLength > $length) {
+                    break;
+                }
+            }
+
+            if ($totalLength <= $length) {
+                return $text;
+            }
+
+            $text = $truncate;
+            $length = $truncateLength;
+
+            foreach ($openTags as $tag) {
+                $suffix .= '';
+            }
+        } else {
+            if (self::_strlen($text, $options) <= $length) {
+                return $text;
+            }
+            $ellipsisLength = self::_strlen($options['ellipsis'], $options);
+        }
+
+        $result = self::_substr($text, 0, $length - $ellipsisLength, $options);
+
+        if (!$options['exact']) {
+            if (self::_substr($text, $length - $ellipsisLength, 1, $options) !== ' ') {
+                $result = self::_removeLastWord($result);
+            }
+
+            // If result is empty, then we don't need to count ellipsis in the cut.
+            if ($result === '') {
+                $result = self::_substr($text, 0, $length, $options);
+            }
+        }
+
+        return $prefix . $result . $suffix;
+    }
+
+    /**
+     * Truncate text with specified width.
+     *
+     * @param string $text String to truncate.
+     * @param int $length Length of returned string, including ellipsis.
+     * @param array $options An array of HTML attributes and options.
+     * @return string Trimmed string.
+     * @see \Cake\Utility\Text::truncate()
+     */
+    public static function truncateByWidth(string $text, int $length = 100, array $options = []): string
+    {
+        return static::truncate($text, $length, ['trimWidth' => true] + $options);
+    }
+
+    /**
+     * Get string length.
+     *
+     * ### Options:
+     *
+     * - `html` If true, HTML entities will be handled as decoded characters.
+     * - `trimWidth` If true, the width will return.
+     *
+     * @param string $text The string being checked for length
+     * @param array $options An array of options.
+     * @return int
+     */
+    protected static function _strlen(string $text, array $options): int
+    {
+        if (empty($options['trimWidth'])) {
+            $strlen = 'mb_strlen';
+        } else {
+            $strlen = 'mb_strwidth';
+        }
+
+        if (empty($options['html'])) {
+            return $strlen($text);
+        }
+
+        $pattern = '/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i';
+        $replace = preg_replace_callback(
+            $pattern,
+            function ($match) use ($strlen) {
+                $utf8 = html_entity_decode($match[0], ENT_HTML5 | ENT_QUOTES, 'UTF-8');
+
+                return str_repeat(' ', $strlen($utf8, 'UTF-8'));
+            },
+            $text
+        );
+
+        return $strlen($replace);
+    }
+
+    /**
+     * Return part of a string.
+     *
+     * ### Options:
+     *
+     * - `html` If true, HTML entities will be handled as decoded characters.
+     * - `trimWidth` If true, will be truncated with specified width.
+     *
+     * @param string $text The input string.
+     * @param int $start The position to begin extracting.
+     * @param int|null $length The desired length.
+     * @param array $options An array of options.
+     * @return string
+     */
+    protected static function _substr(string $text, int $start, ?int $length, array $options): string
+    {
+        if (empty($options['trimWidth'])) {
+            $substr = 'mb_substr';
+        } else {
+            $substr = 'mb_strimwidth';
+        }
+
+        $maxPosition = self::_strlen($text, ['trimWidth' => false] + $options);
+        if ($start < 0) {
+            $start += $maxPosition;
+            if ($start < 0) {
+                $start = 0;
+            }
+        }
+        if ($start >= $maxPosition) {
+            return '';
+        }
+
+        if ($length === null) {
+            $length = self::_strlen($text, $options);
+        }
+
+        if ($length < 0) {
+            $text = self::_substr($text, $start, null, $options);
+            $start = 0;
+            $length += self::_strlen($text, $options);
+        }
+
+        if ($length <= 0) {
+            return '';
+        }
+
+        if (empty($options['html'])) {
+            return (string)$substr($text, $start, $length);
+        }
+
+        $totalOffset = 0;
+        $totalLength = 0;
+        $result = '';
+
+        $pattern = '/(&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};)/i';
+        $parts = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+        foreach ($parts as $part) {
+            $offset = 0;
+
+            if ($totalOffset < $start) {
+                $len = self::_strlen($part, ['trimWidth' => false] + $options);
+                if ($totalOffset + $len <= $start) {
+                    $totalOffset += $len;
+                    continue;
+                }
+
+                $offset = $start - $totalOffset;
+                $totalOffset = $start;
+            }
+
+            $len = self::_strlen($part, $options);
+            if ($offset !== 0 || $totalLength + $len > $length) {
+                if (
+                    strpos($part, '&') === 0
+                    && preg_match($pattern, $part)
+                    && $part !== html_entity_decode($part, ENT_HTML5 | ENT_QUOTES, 'UTF-8')
+                ) {
+                    // Entities cannot be passed substr.
+                    continue;
+                }
+
+                $part = $substr($part, $offset, $length - $totalLength);
+                $len = self::_strlen($part, $options);
+            }
+
+            $result .= $part;
+            $totalLength += $len;
+            if ($totalLength >= $length) {
+                break;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Removes the last word from the input text.
+     *
+     * @param string $text The input text
+     * @return string
+     */
+    protected static function _removeLastWord(string $text): string
+    {
+        $spacepos = mb_strrpos($text, ' ');
+
+        if ($spacepos !== false) {
+            $lastWord = mb_substr($text, $spacepos);
+
+            // Some languages are written without word separation.
+            // We recognize a string as a word if it doesn't contain any full-width characters.
+            if (mb_strwidth($lastWord) === mb_strlen($lastWord)) {
+                $text = mb_substr($text, 0, $spacepos);
+            }
+
+            return $text;
+        }
+
+        return '';
+    }
+
+    /**
+     * Extracts an excerpt from the text surrounding the phrase with a number of characters on each side
+     * determined by radius.
+     *
+     * @param string $text String to search the phrase in
+     * @param string $phrase Phrase that will be searched for
+     * @param int $radius The amount of characters that will be returned on each side of the founded phrase
+     * @param string $ellipsis Ending that will be appended
+     * @return string Modified string
+     * @link https://book.cakephp.org/4/en/core-libraries/text.html#extracting-an-excerpt
+     */
+    public static function excerpt(string $text, string $phrase, int $radius = 100, string $ellipsis = '...'): string
+    {
+        if (empty($text) || empty($phrase)) {
+            return static::truncate($text, $radius * 2, ['ellipsis' => $ellipsis]);
+        }
+
+        $append = $prepend = $ellipsis;
+
+        $phraseLen = mb_strlen($phrase);
+        $textLen = mb_strlen($text);
+
+        $pos = mb_stripos($text, $phrase);
+        if ($pos === false) {
+            return mb_substr($text, 0, $radius) . $ellipsis;
+        }
+
+        $startPos = $pos - $radius;
+        if ($startPos <= 0) {
+            $startPos = 0;
+            $prepend = '';
+        }
+
+        $endPos = $pos + $phraseLen + $radius;
+        if ($endPos >= $textLen) {
+            $endPos = $textLen;
+            $append = '';
+        }
+
+        $excerpt = mb_substr($text, $startPos, $endPos - $startPos);
+
+        return $prepend . $excerpt . $append;
+    }
+
+    /**
+     * Creates a comma separated list where the last two items are joined with 'and', forming natural language.
+     *
+     * @param array $list The list to be joined.
+     * @param string|null $and The word used to join the last and second last items together with. Defaults to 'and'.
+     * @param string $separator The separator used to join all the other items together. Defaults to ', '.
+     * @return string The glued together string.
+     * @link https://book.cakephp.org/4/en/core-libraries/text.html#converting-an-array-to-sentence-form
+     */
+    public static function toList(array $list, ?string $and = null, string $separator = ', '): string
+    {
+        if ($and === null) {
+            $and = __d('cake', 'and');
+        }
+        if (count($list) > 1) {
+            return implode($separator, array_slice($list, 0, -1)) . ' ' . $and . ' ' . array_pop($list);
+        }
+
+        return (string)array_pop($list);
+    }
+
+    /**
+     * Check if the string contain multibyte characters
+     *
+     * @param string $string value to test
+     * @return bool
+     */
+    public static function isMultibyte(string $string): bool
+    {
+        $length = strlen($string);
+
+        for ($i = 0; $i < $length; $i++) {
+            $value = ord($string[$i]);
+            if ($value > 128) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Converts a multibyte character string
+     * to the decimal value of the character
+     *
+     * @param string $string String to convert.
+     * @return array
+     */
+    public static function utf8(string $string): array
+    {
+        $map = [];
+
+        $values = [];
+        $find = 1;
+        $length = strlen($string);
+
+        for ($i = 0; $i < $length; $i++) {
+            $value = ord($string[$i]);
+
+            if ($value < 128) {
+                $map[] = $value;
+            } else {
+                if (empty($values)) {
+                    $find = $value < 224 ? 2 : 3;
+                }
+                $values[] = $value;
+
+                if (count($values) === $find) {
+                    if ($find === 3) {
+                        $map[] = (($values[0] % 16) * 4096) + (($values[1] % 64) * 64) + ($values[2] % 64);
+                    } else {
+                        $map[] = (($values[0] % 32) * 64) + ($values[1] % 64);
+                    }
+                    $values = [];
+                    $find = 1;
+                }
+            }
+        }
+
+        return $map;
+    }
+
+    /**
+     * Converts the decimal value of a multibyte character string
+     * to a string
+     *
+     * @param array $array Array
+     * @return string
+     */
+    public static function ascii(array $array): string
+    {
+        $ascii = '';
+
+        foreach ($array as $utf8) {
+            if ($utf8 < 128) {
+                $ascii .= chr($utf8);
+            } elseif ($utf8 < 2048) {
+                $ascii .= chr(192 + (($utf8 - ($utf8 % 64)) / 64));
+                $ascii .= chr(128 + ($utf8 % 64));
+            } else {
+                $ascii .= chr(224 + (($utf8 - ($utf8 % 4096)) / 4096));
+                $ascii .= chr(128 + ((($utf8 % 4096) - ($utf8 % 64)) / 64));
+                $ascii .= chr(128 + ($utf8 % 64));
+            }
+        }
+
+        return $ascii;
+    }
+
+    /**
+     * Converts filesize from human readable string to bytes
+     *
+     * @param string $size Size in human readable string like '5MB', '5M', '500B', '50kb' etc.
+     * @param mixed $default Value to be returned when invalid size was used, for example 'Unknown type'
+     * @return mixed Number of bytes as integer on success, `$default` on failure if not false
+     * @throws \InvalidArgumentException On invalid Unit type.
+     * @link https://book.cakephp.org/4/en/core-libraries/text.html#Cake\Utility\Text::parseFileSize
+     */
+    public static function parseFileSize(string $size, $default = false)
+    {
+        if (ctype_digit($size)) {
+            return (int)$size;
+        }
+        $size = strtoupper($size);
+
+        $l = -2;
+        $i = array_search(substr($size, -2), ['KB', 'MB', 'GB', 'TB', 'PB'], true);
+        if ($i === false) {
+            $l = -1;
+            $i = array_search(substr($size, -1), ['K', 'M', 'G', 'T', 'P'], true);
+        }
+        if ($i !== false) {
+            $size = (float)substr($size, 0, $l);
+
+            return (int)($size * pow(1024, $i + 1));
+        }
+
+        if (substr($size, -1) === 'B' && ctype_digit(substr($size, 0, -1))) {
+            $size = substr($size, 0, -1);
+
+            return (int)$size;
+        }
+
+        if ($default !== false) {
+            return $default;
+        }
+        throw new InvalidArgumentException('No unit type.');
+    }
+
+    /**
+     * Get the default transliterator.
+     *
+     * @return \Transliterator|null Either a Transliterator instance, or `null`
+     *   in case no transliterator has been set yet.
+     */
+    public static function getTransliterator(): ?Transliterator
+    {
+        return static::$_defaultTransliterator;
+    }
+
+    /**
+     * Set the default transliterator.
+     *
+     * @param \Transliterator $transliterator A `Transliterator` instance.
+     * @return void
+     */
+    public static function setTransliterator(Transliterator $transliterator): void
+    {
+        static::$_defaultTransliterator = $transliterator;
+    }
+
+    /**
+     * Get default transliterator identifier string.
+     *
+     * @return string Transliterator identifier.
+     */
+    public static function getTransliteratorId(): string
+    {
+        return static::$_defaultTransliteratorId;
+    }
+
+    /**
+     * Set default transliterator identifier string.
+     *
+     * @param string $transliteratorId Transliterator identifier.
+     * @return void
+     */
+    public static function setTransliteratorId(string $transliteratorId): void
+    {
+        $transliterator = transliterator_create($transliteratorId);
+        if ($transliterator === null) {
+            throw new CakeException('Unable to create transliterator for id: ' . $transliteratorId);
+        }
+
+        static::setTransliterator($transliterator);
+        static::$_defaultTransliteratorId = $transliteratorId;
+    }
+
+    /**
+     * Transliterate string.
+     *
+     * @param string $string String to transliterate.
+     * @param \Transliterator|string|null $transliterator Either a Transliterator
+     *   instance, or a transliterator identifier string. If `null`, the default
+     *   transliterator (identifier) set via `setTransliteratorId()` or
+     *   `setTransliterator()` will be used.
+     * @return string
+     * @see https://secure.php.net/manual/en/transliterator.transliterate.php
+     */
+    public static function transliterate(string $string, $transliterator = null): string
+    {
+        if (empty($transliterator)) {
+            $transliterator = static::$_defaultTransliterator ?: static::$_defaultTransliteratorId;
+        }
+
+        $return = transliterator_transliterate($transliterator, $string);
+        if ($return === false) {
+            throw new CakeException(sprintf('Unable to transliterate string: %s', $string));
+        }
+
+        return $return;
+    }
+
+    /**
+     * Returns a string with all spaces converted to dashes (by default),
+     * characters transliterated to ASCII characters, and non word characters removed.
+     *
+     * ### Options:
+     *
+     * - `replacement`: Replacement string. Default '-'.
+     * - `transliteratorId`: A valid transliterator id string.
+     *   If `null` (default) the transliterator (identifier) set via
+     *   `setTransliteratorId()` or `setTransliterator()` will be used.
+     *   If `false` no transliteration will be done, only non words will be removed.
+     * - `preserve`: Specific non-word character to preserve. Default `null`.
+     *   For e.g. this option can be set to '.' to generate clean file names.
+     *
+     * @param string $string the string you want to slug
+     * @param array|string $options If string it will be use as replacement character
+     *   or an array of options.
+     * @return string
+     * @see setTransliterator()
+     * @see setTransliteratorId()
+     */
+    public static function slug(string $string, $options = []): string
+    {
+        if (is_string($options)) {
+            $options = ['replacement' => $options];
+        }
+        $options += [
+            'replacement' => '-',
+            'transliteratorId' => null,
+            'preserve' => null,
+        ];
+
+        if ($options['transliteratorId'] !== false) {
+            $string = static::transliterate($string, $options['transliteratorId']);
+        }
+
+        $regex = '^\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}';
+        if ($options['preserve']) {
+            $regex .= preg_quote($options['preserve'], '/');
+        }
+        $quotedReplacement = preg_quote((string)$options['replacement'], '/');
+        $map = [
+            '/[' . $regex . ']/mu' => $options['replacement'],
+            sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '',
+        ];
+        if (is_string($options['replacement']) && $options['replacement'] !== '') {
+            $map[sprintf('/[%s]+/mu', $quotedReplacement)] = $options['replacement'];
+        }
+
+        return preg_replace(array_keys($map), $map, $string);
+    }
+}
diff --git a/vendor/cakephp/utility/Xml.php b/vendor/cakephp/utility/Xml.php
new file mode 100644
index 0000000..53ddfbd
--- /dev/null
+++ b/vendor/cakephp/utility/Xml.php
@@ -0,0 +1,502 @@
+text');
+     * ```
+     *
+     * Building XML from string (output DOMDocument):
+     *
+     * ```
+     * $xml = Xml::build('text', ['return' => 'domdocument']);
+     * ```
+     *
+     * Building XML from a file path:
+     *
+     * ```
+     * $xml = Xml::build('/path/to/an/xml/file.xml', ['readFile' => true]);
+     * ```
+     *
+     * Building XML from a remote URL:
+     *
+     * ```
+     * use Cake\Http\Client;
+     *
+     * $http = new Client();
+     * $response = $http->get('http://example.com/example.xml');
+     * $xml = Xml::build($response->body());
+     * ```
+     *
+     * Building from an array:
+     *
+     * ```
+     *  $value = [
+     *      'tags' => [
+     *          'tag' => [
+     *              [
+     *                  'id' => '1',
+     *                  'name' => 'defect'
+     *              ],
+     *              [
+     *                  'id' => '2',
+     *                  'name' => 'enhancement'
+     *              ]
+     *          ]
+     *      ]
+     *  ];
+     * $xml = Xml::build($value);
+     * ```
+     *
+     * When building XML from an array ensure that there is only one top level element.
+     *
+     * ### Options
+     *
+     * - `return` Can be 'simplexml' to return object of SimpleXMLElement or 'domdocument' to return DOMDocument.
+     * - `loadEntities` Defaults to false. Set to true to enable loading of ` $options The options to use
+     * @return \SimpleXMLElement|\DOMDocument SimpleXMLElement or DOMDocument
+     * @throws \Cake\Utility\Exception\XmlException
+     */
+    public static function build($input, array $options = [])
+    {
+        $defaults = [
+            'return' => 'simplexml',
+            'loadEntities' => false,
+            'readFile' => false,
+            'parseHuge' => false,
+        ];
+        $options += $defaults;
+
+        if (is_array($input) || is_object($input)) {
+            return static::fromArray($input, $options);
+        }
+
+        if ($options['readFile'] && file_exists($input)) {
+            return static::_loadXml(file_get_contents($input), $options);
+        }
+
+        if (!is_string($input)) {
+            $type = gettype($input);
+            throw new XmlException("Invalid input. {$type} cannot be parsed as XML.");
+        }
+
+        if (strpos($input, '<') !== false) {
+            return static::_loadXml($input, $options);
+        }
+
+        throw new XmlException('XML cannot be read.');
+    }
+
+    /**
+     * Parse the input data and create either a SimpleXmlElement object or a DOMDocument.
+     *
+     * @param string $input The input to load.
+     * @param array $options The options to use. See Xml::build()
+     * @return \SimpleXMLElement|\DOMDocument
+     * @throws \Cake\Utility\Exception\XmlException
+     */
+    protected static function _loadXml(string $input, array $options)
+    {
+        return static::load(
+            $input,
+            $options,
+            function ($input, $options, $flags) {
+                if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
+                    $flags |= LIBXML_NOCDATA;
+                    $xml = new SimpleXMLElement($input, $flags);
+                } else {
+                    $xml = new DOMDocument();
+                    $xml->loadXML($input, $flags);
+                }
+
+                return $xml;
+            }
+        );
+    }
+
+    /**
+     * Parse the input html string and create either a SimpleXmlElement object or a DOMDocument.
+     *
+     * @param string $input The input html string to load.
+     * @param array $options The options to use. See Xml::build()
+     * @return \SimpleXMLElement|\DOMDocument
+     * @throws \Cake\Utility\Exception\XmlException
+     */
+    public static function loadHtml(string $input, array $options = [])
+    {
+        $defaults = [
+            'return' => 'simplexml',
+            'loadEntities' => false,
+        ];
+        $options += $defaults;
+
+        return static::load(
+            $input,
+            $options,
+            function ($input, $options, $flags) {
+                $xml = new DOMDocument();
+                $xml->loadHTML($input, $flags);
+
+                if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
+                    $xml = simplexml_import_dom($xml);
+                }
+
+                return $xml;
+            }
+        );
+    }
+
+    /**
+     * Parse the input data and create either a SimpleXmlElement object or a DOMDocument.
+     *
+     * @param string $input The input to load.
+     * @param array $options The options to use. See Xml::build()
+     * @param \Closure $callable Closure that should return SimpleXMLElement or DOMDocument instance.
+     * @return \SimpleXMLElement|\DOMDocument
+     * @throws \Cake\Utility\Exception\XmlException
+     */
+    protected static function load(string $input, array $options, Closure $callable)
+    {
+        $flags = 0;
+        if (!empty($options['parseHuge'])) {
+            $flags |= LIBXML_PARSEHUGE;
+        }
+
+        $internalErrors = libxml_use_internal_errors(true);
+        if (LIBXML_VERSION < 20900 && !$options['loadEntities']) {
+            $previousDisabledEntityLoader = libxml_disable_entity_loader(true);
+        } elseif ($options['loadEntities']) {
+            $flags |= LIBXML_NOENT;
+        }
+
+        try {
+            return $callable($input, $options, $flags);
+        } catch (Exception $e) {
+            throw new XmlException('Xml cannot be read. ' . $e->getMessage(), null, $e);
+        } finally {
+            if (isset($previousDisabledEntityLoader)) {
+                libxml_disable_entity_loader($previousDisabledEntityLoader);
+            }
+            libxml_use_internal_errors($internalErrors);
+        }
+    }
+
+    /**
+     * Transform an array into a SimpleXMLElement
+     *
+     * ### Options
+     *
+     * - `format` If create children ('tags') or attributes ('attributes').
+     * - `pretty` Returns formatted Xml when set to `true`. Defaults to `false`
+     * - `version` Version of XML document. Default is 1.0.
+     * - `encoding` Encoding of XML document. If null remove from XML header.
+     *    Defaults to the application's encoding
+     * - `return` If return object of SimpleXMLElement ('simplexml')
+     *   or DOMDocument ('domdocument'). Default is SimpleXMLElement.
+     *
+     * Using the following data:
+     *
+     * ```
+     * $value = [
+     *    'root' => [
+     *        'tag' => [
+     *            'id' => 1,
+     *            'value' => 'defect',
+     *            '@' => 'description'
+     *         ]
+     *     ]
+     * ];
+     * ```
+     *
+     * Calling `Xml::fromArray($value, 'tags');` Will generate:
+     *
+     * `1defectdescription`
+     *
+     * And calling `Xml::fromArray($value, 'attributes');` Will generate:
+     *
+     * `description`
+     *
+     * @param object|array $input Array with data or a collection instance.
+     * @param array $options The options to use.
+     * @return \SimpleXMLElement|\DOMDocument SimpleXMLElement or DOMDocument
+     * @throws \Cake\Utility\Exception\XmlException
+     */
+    public static function fromArray($input, array $options = [])
+    {
+        if (is_object($input) && method_exists($input, 'toArray') && is_callable([$input, 'toArray'])) {
+            $input = $input->toArray();
+        }
+        if (!is_array($input) || count($input) !== 1) {
+            throw new XmlException('Invalid input.');
+        }
+        $key = key($input);
+        if (is_int($key)) {
+            throw new XmlException('The key of input must be alphanumeric');
+        }
+
+        $defaults = [
+            'format' => 'tags',
+            'version' => '1.0',
+            'encoding' => mb_internal_encoding(),
+            'return' => 'simplexml',
+            'pretty' => false,
+        ];
+        $options += $defaults;
+
+        $dom = new DOMDocument($options['version'], $options['encoding']);
+        if ($options['pretty']) {
+            $dom->formatOutput = true;
+        }
+        self::_fromArray($dom, $dom, $input, $options['format']);
+
+        $options['return'] = strtolower($options['return']);
+        if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
+            return new SimpleXMLElement($dom->saveXML());
+        }
+
+        return $dom;
+    }
+
+    /**
+     * Recursive method to create children from array
+     *
+     * @param \DOMDocument $dom Handler to DOMDocument
+     * @param \DOMDocument|\DOMElement $node Handler to DOMElement (child)
+     * @param array $data Array of data to append to the $node.
+     * @param string $format Either 'attributes' or 'tags'. This determines where nested keys go.
+     * @return void
+     * @throws \Cake\Utility\Exception\XmlException
+     */
+    protected static function _fromArray(DOMDocument $dom, $node, &$data, $format): void
+    {
+        if (empty($data) || !is_array($data)) {
+            return;
+        }
+        foreach ($data as $key => $value) {
+            if (is_string($key)) {
+                if (is_object($value) && method_exists($value, 'toArray') && is_callable([$value, 'toArray'])) {
+                    $value = $value->toArray();
+                }
+
+                if (!is_array($value)) {
+                    if (is_bool($value)) {
+                        $value = (int)$value;
+                    } elseif ($value === null) {
+                        $value = '';
+                    }
+                    $isNamespace = strpos($key, 'xmlns:');
+                    if ($isNamespace !== false) {
+                        /** @psalm-suppress PossiblyUndefinedMethod */
+                        $node->setAttributeNS('http://www.w3.org/2000/xmlns/', $key, (string)$value);
+                        continue;
+                    }
+                    if ($key[0] !== '@' && $format === 'tags') {
+                        if (!is_numeric($value)) {
+                            // Escape special characters
+                            // https://www.w3.org/TR/REC-xml/#syntax
+                            // https://bugs.php.net/bug.php?id=36795
+                            $child = $dom->createElement($key, '');
+                            $child->appendChild(new DOMText((string)$value));
+                        } else {
+                            $child = $dom->createElement($key, (string)$value);
+                        }
+                        $node->appendChild($child);
+                    } else {
+                        if ($key[0] === '@') {
+                            $key = substr($key, 1);
+                        }
+                        $attribute = $dom->createAttribute($key);
+                        $attribute->appendChild($dom->createTextNode((string)$value));
+                        $node->appendChild($attribute);
+                    }
+                } else {
+                    if ($key[0] === '@') {
+                        throw new XmlException('Invalid array');
+                    }
+                    if (is_numeric(implode('', array_keys($value)))) {
+// List
+                        foreach ($value as $item) {
+                            $itemData = compact('dom', 'node', 'key', 'format');
+                            $itemData['value'] = $item;
+                            static::_createChild($itemData);
+                        }
+                    } else {
+// Struct
+                        static::_createChild(compact('dom', 'node', 'key', 'value', 'format'));
+                    }
+                }
+            } else {
+                throw new XmlException('Invalid array');
+            }
+        }
+    }
+
+    /**
+     * Helper to _fromArray(). It will create children of arrays
+     *
+     * @param array $data Array with information to create children
+     * @return void
+     */
+    protected static function _createChild(array $data): void
+    {
+        $data += [
+            'dom' => null,
+            'node' => null,
+            'key' => null,
+            'value' => null,
+            'format' => null,
+        ];
+
+        $value = $data['value'];
+        $dom = $data['dom'];
+        $key = $data['key'];
+        $format = $data['format'];
+        $node = $data['node'];
+
+        $childNS = $childValue = null;
+        if (is_object($value) && method_exists($value, 'toArray') && is_callable([$value, 'toArray'])) {
+            $value = $value->toArray();
+        }
+        if (is_array($value)) {
+            if (isset($value['@'])) {
+                $childValue = (string)$value['@'];
+                unset($value['@']);
+            }
+            if (isset($value['xmlns:'])) {
+                $childNS = $value['xmlns:'];
+                unset($value['xmlns:']);
+            }
+        } elseif (!empty($value) || $value === 0 || $value === '0') {
+            $childValue = (string)$value;
+        }
+
+        $child = $dom->createElement($key);
+        if ($childValue !== null) {
+            $child->appendChild($dom->createTextNode($childValue));
+        }
+        if ($childNS) {
+            $child->setAttribute('xmlns', $childNS);
+        }
+
+        static::_fromArray($dom, $child, $value, $format);
+        $node->appendChild($child);
+    }
+
+    /**
+     * Returns this XML structure as an array.
+     *
+     * @param \SimpleXMLElement|\DOMDocument|\DOMNode $obj SimpleXMLElement, DOMDocument or DOMNode instance
+     * @return array Array representation of the XML structure.
+     * @throws \Cake\Utility\Exception\XmlException
+     */
+    public static function toArray($obj): array
+    {
+        if ($obj instanceof DOMNode) {
+            $obj = simplexml_import_dom($obj);
+        }
+        if (!($obj instanceof SimpleXMLElement)) {
+            throw new XmlException('The input is not instance of SimpleXMLElement, DOMDocument or DOMNode.');
+        }
+        $result = [];
+        $namespaces = array_merge(['' => ''], $obj->getNamespaces(true));
+        static::_toArray($obj, $result, '', array_keys($namespaces));
+
+        return $result;
+    }
+
+    /**
+     * Recursive method to toArray
+     *
+     * @param \SimpleXMLElement $xml SimpleXMLElement object
+     * @param array $parentData Parent array with data
+     * @param string $ns Namespace of current child
+     * @param array $namespaces List of namespaces in XML
+     * @return void
+     */
+    protected static function _toArray(SimpleXMLElement $xml, array &$parentData, string $ns, array $namespaces): void
+    {
+        $data = [];
+
+        foreach ($namespaces as $namespace) {
+            /**
+             * @psalm-suppress PossiblyNullIterator
+             * @var string $key
+             */
+            foreach ($xml->attributes($namespace, true) as $key => $value) {
+                if (!empty($namespace)) {
+                    $key = $namespace . ':' . $key;
+                }
+                $data['@' . $key] = (string)$value;
+            }
+
+            foreach ($xml->children($namespace, true) as $child) {
+                static::_toArray($child, $data, $namespace, $namespaces);
+            }
+        }
+
+        $asString = trim((string)$xml);
+        if (empty($data)) {
+            $data = $asString;
+        } elseif ($asString !== '') {
+            $data['@'] = $asString;
+        }
+
+        if (!empty($ns)) {
+            $ns .= ':';
+        }
+        $name = $ns . $xml->getName();
+        if (isset($parentData[$name])) {
+            if (!is_array($parentData[$name]) || !isset($parentData[$name][0])) {
+                $parentData[$name] = [$parentData[$name]];
+            }
+            $parentData[$name][] = $data;
+        } else {
+            $parentData[$name] = $data;
+        }
+    }
+}
diff --git a/vendor/cakephp/utility/bootstrap.php b/vendor/cakephp/utility/bootstrap.php
new file mode 100644
index 0000000..d335b75
--- /dev/null
+++ b/vendor/cakephp/utility/bootstrap.php
@@ -0,0 +1,21 @@
+=7.4.0",
+        "cakephp/core": "^4.0"
+    },
+    "suggest": {
+        "ext-intl": "To use Text::transliterate() or Text::slug()",
+        "lib-ICU": "To use Text::transliterate() or Text::slug()"
+    },
+    "autoload": {
+        "psr-4": {
+            "Cake\\Utility\\": "."
+        },
+        "files": [
+            "bootstrap.php"
+        ]
+    }
+}
diff --git a/vendor/carbonphp/carbon-doctrine-types/LICENSE b/vendor/carbonphp/carbon-doctrine-types/LICENSE
new file mode 100644
index 0000000..2ee1671
--- /dev/null
+++ b/vendor/carbonphp/carbon-doctrine-types/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Carbon
+
+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/carbonphp/carbon-doctrine-types/README.md b/vendor/carbonphp/carbon-doctrine-types/README.md
new file mode 100644
index 0000000..5a18121
--- /dev/null
+++ b/vendor/carbonphp/carbon-doctrine-types/README.md
@@ -0,0 +1,14 @@
+# carbonphp/carbon-doctrine-types
+
+Types to use Carbon in Doctrine
+
+## Documentation
+
+[Check how to use in the official Carbon documentation](https://carbon.nesbot.com/symfony/)
+
+This package is an externalization of [src/Carbon/Doctrine](https://github.com/briannesbitt/Carbon/tree/2.71.0/src/Carbon/Doctrine)
+from `nestbot/carbon` package.
+
+Externalization allows to better deal with different versions of dbal. With
+version 4.0 of dbal, it no longer sustainable to be compatible with all version
+using a single code.
diff --git a/vendor/carbonphp/carbon-doctrine-types/composer.json b/vendor/carbonphp/carbon-doctrine-types/composer.json
new file mode 100644
index 0000000..b5fd8a5
--- /dev/null
+++ b/vendor/carbonphp/carbon-doctrine-types/composer.json
@@ -0,0 +1,36 @@
+{
+    "name": "carbonphp/carbon-doctrine-types",
+    "description": "Types to use Carbon in Doctrine",
+    "type": "library",
+    "keywords": [
+        "date",
+        "time",
+        "DateTime",
+        "Carbon",
+        "Doctrine"
+    ],
+    "require": {
+        "php": "^7.4 || ^8.0"
+    },
+    "require-dev": {
+        "doctrine/dbal": "^3.7.0",
+        "nesbot/carbon": "^2.71.0 || ^3.0.0",
+        "phpunit/phpunit": "^10.3"
+    },
+    "conflict": {
+        "doctrine/dbal": "<3.7.0 || >=4.0.0"
+    },
+    "license": "MIT",
+    "autoload": {
+        "psr-4": {
+            "Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
+        }
+    },
+    "authors": [
+        {
+            "name": "KyleKatarn",
+            "email": "kylekatarnls@gmail.com"
+        }
+    ],
+    "minimum-stability": "dev"
+}
diff --git a/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonDoctrineType.php b/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonDoctrineType.php
new file mode 100644
index 0000000..8c42dc0
--- /dev/null
+++ b/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonDoctrineType.php
@@ -0,0 +1,14 @@
+
+     */
+    protected function getCarbonClassName(): string
+    {
+        return Carbon::class;
+    }
+
+    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
+    {
+        $precision = min(
+            $fieldDeclaration['precision'] ?? DateTimeDefaultPrecision::get(),
+            $this->getMaximumPrecision($platform),
+        );
+
+        $type = parent::getSQLDeclaration($fieldDeclaration, $platform);
+
+        if (!$precision) {
+            return $type;
+        }
+
+        if (str_contains($type, '(')) {
+            return preg_replace('/\(\d+\)/', "($precision)", $type);
+        }
+
+        [$before, $after] = explode(' ', "$type ");
+
+        return trim("$before($precision) $after");
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     *
+     * @return T|null
+     */
+    public function convertToPHPValue($value, AbstractPlatform $platform)
+    {
+        $class = $this->getCarbonClassName();
+
+        if ($value === null || is_a($value, $class)) {
+            return $value;
+        }
+
+        if ($value instanceof DateTimeInterface) {
+            return $class::instance($value);
+        }
+
+        $date = null;
+        $error = null;
+
+        try {
+            $date = $class::parse($value);
+        } catch (Exception $exception) {
+            $error = $exception;
+        }
+
+        if (!$date) {
+            throw ConversionException::conversionFailedFormat(
+                $value,
+                $this->getTypeName(),
+                'Y-m-d H:i:s.u or any format supported by '.$class.'::parse()',
+                $error
+            );
+        }
+
+        return $date;
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
+    {
+        if ($value === null) {
+            return $value;
+        }
+
+        if ($value instanceof DateTimeInterface) {
+            return $value->format('Y-m-d H:i:s.u');
+        }
+
+        throw ConversionException::conversionFailedInvalidType(
+            $value,
+            $this->getTypeName(),
+            ['null', 'DateTime', 'Carbon']
+        );
+    }
+
+    private function getTypeName(): string
+    {
+        $chunks = explode('\\', static::class);
+        $type = preg_replace('/Type$/', '', end($chunks));
+
+        return strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $type));
+    }
+
+    private function getMaximumPrecision(AbstractPlatform $platform): int
+    {
+        if ($platform instanceof DB2Platform) {
+            return 12;
+        }
+
+        if ($platform instanceof OraclePlatform) {
+            return 9;
+        }
+
+        if ($platform instanceof SQLServerPlatform || $platform instanceof SqlitePlatform) {
+            return 3;
+        }
+
+        return 6;
+    }
+}
diff --git a/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeDefaultPrecision.php b/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeDefaultPrecision.php
new file mode 100644
index 0000000..3a9ff11
--- /dev/null
+++ b/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeDefaultPrecision.php
@@ -0,0 +1,28 @@
+ */
+    use CarbonTypeConverter;
+
+    /**
+     * @return class-string
+     */
+    protected function getCarbonClassName(): string
+    {
+        return CarbonImmutable::class;
+    }
+}
diff --git a/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeType.php b/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeType.php
new file mode 100644
index 0000000..3bf07e9
--- /dev/null
+++ b/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeType.php
@@ -0,0 +1,12 @@
+ */
+    use CarbonTypeConverter;
+}
diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php
new file mode 100644
index 0000000..7824d8f
--- /dev/null
+++ b/vendor/composer/ClassLoader.php
@@ -0,0 +1,579 @@
+
+ *     Jordi Boggiano 
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ *     $loader = new \Composer\Autoload\ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (eg. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier 
+ * @author Jordi Boggiano 
+ * @see    https://www.php-fig.org/psr/psr-0/
+ * @see    https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+    /** @var \Closure(string):void */
+    private static $includeFile;
+
+    /** @var string|null */
+    private $vendorDir;
+
+    // PSR-4
+    /**
+     * @var array>
+     */
+    private $prefixLengthsPsr4 = array();
+    /**
+     * @var array>
+     */
+    private $prefixDirsPsr4 = array();
+    /**
+     * @var list
+     */
+    private $fallbackDirsPsr4 = array();
+
+    // PSR-0
+    /**
+     * List of PSR-0 prefixes
+     *
+     * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
+     *
+     * @var array>>
+     */
+    private $prefixesPsr0 = array();
+    /**
+     * @var list
+     */
+    private $fallbackDirsPsr0 = array();
+
+    /** @var bool */
+    private $useIncludePath = false;
+
+    /**
+     * @var array
+     */
+    private $classMap = array();
+
+    /** @var bool */
+    private $classMapAuthoritative = false;
+
+    /**
+     * @var array
+     */
+    private $missingClasses = array();
+
+    /** @var string|null */
+    private $apcuPrefix;
+
+    /**
+     * @var array
+     */
+    private static $registeredLoaders = array();
+
+    /**
+     * @param string|null $vendorDir
+     */
+    public function __construct($vendorDir = null)
+    {
+        $this->vendorDir = $vendorDir;
+        self::initializeIncludeClosure();
+    }
+
+    /**
+     * @return array>
+     */
+    public function getPrefixes()
+    {
+        if (!empty($this->prefixesPsr0)) {
+            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+        }
+
+        return array();
+    }
+
+    /**
+     * @return array>
+     */
+    public function getPrefixesPsr4()
+    {
+        return $this->prefixDirsPsr4;
+    }
+
+    /**
+     * @return list
+     */
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirsPsr0;
+    }
+
+    /**
+     * @return list
+     */
+    public function getFallbackDirsPsr4()
+    {
+        return $this->fallbackDirsPsr4;
+    }
+
+    /**
+     * @return array Array of classname => path
+     */
+    public function getClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * @param array $classMap Class to filename map
+     *
+     * @return void
+     */
+    public function addClassMap(array $classMap)
+    {
+        if ($this->classMap) {
+            $this->classMap = array_merge($this->classMap, $classMap);
+        } else {
+            $this->classMap = $classMap;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix, either
+     * appending or prepending to the ones previously set for this prefix.
+     *
+     * @param string              $prefix  The prefix
+     * @param list|string $paths   The PSR-0 root directories
+     * @param bool                $prepend Whether to prepend the directories
+     *
+     * @return void
+     */
+    public function add($prefix, $paths, $prepend = false)
+    {
+        $paths = (array) $paths;
+        if (!$prefix) {
+            if ($prepend) {
+                $this->fallbackDirsPsr0 = array_merge(
+                    $paths,
+                    $this->fallbackDirsPsr0
+                );
+            } else {
+                $this->fallbackDirsPsr0 = array_merge(
+                    $this->fallbackDirsPsr0,
+                    $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset($this->prefixesPsr0[$first][$prefix])) {
+            $this->prefixesPsr0[$first][$prefix] = $paths;
+
+            return;
+        }
+        if ($prepend) {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                $paths,
+                $this->prefixesPsr0[$first][$prefix]
+            );
+        } else {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                $this->prefixesPsr0[$first][$prefix],
+                $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace, either
+     * appending or prepending to the ones previously set for this namespace.
+     *
+     * @param string              $prefix  The prefix/namespace, with trailing '\\'
+     * @param list|string $paths   The PSR-4 base directories
+     * @param bool                $prepend Whether to prepend the directories
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @return void
+     */
+    public function addPsr4($prefix, $paths, $prepend = false)
+    {
+        $paths = (array) $paths;
+        if (!$prefix) {
+            // Register directories for the root namespace.
+            if ($prepend) {
+                $this->fallbackDirsPsr4 = array_merge(
+                    $paths,
+                    $this->fallbackDirsPsr4
+                );
+            } else {
+                $this->fallbackDirsPsr4 = array_merge(
+                    $this->fallbackDirsPsr4,
+                    $paths
+                );
+            }
+        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+            // Register directories for a new namespace.
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = $paths;
+        } elseif ($prepend) {
+            // Prepend directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                $paths,
+                $this->prefixDirsPsr4[$prefix]
+            );
+        } else {
+            // Append directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                $this->prefixDirsPsr4[$prefix],
+                $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix,
+     * replacing any others previously set for this prefix.
+     *
+     * @param string              $prefix The prefix
+     * @param list|string $paths  The PSR-0 base directories
+     *
+     * @return void
+     */
+    public function set($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr0 = (array) $paths;
+        } else {
+            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace,
+     * replacing any others previously set for this namespace.
+     *
+     * @param string              $prefix The prefix/namespace, with trailing '\\'
+     * @param list|string $paths  The PSR-4 base directories
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @return void
+     */
+    public function setPsr4($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr4 = (array) $paths;
+        } else {
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Turns on searching the include path for class files.
+     *
+     * @param bool $useIncludePath
+     *
+     * @return void
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Turns off searching the prefix and fallback directories for classes
+     * that have not been registered with the class map.
+     *
+     * @param bool $classMapAuthoritative
+     *
+     * @return void
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative)
+    {
+        $this->classMapAuthoritative = $classMapAuthoritative;
+    }
+
+    /**
+     * Should class lookup fail if not found in the current class map?
+     *
+     * @return bool
+     */
+    public function isClassMapAuthoritative()
+    {
+        return $this->classMapAuthoritative;
+    }
+
+    /**
+     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+     *
+     * @param string|null $apcuPrefix
+     *
+     * @return void
+     */
+    public function setApcuPrefix($apcuPrefix)
+    {
+        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+    }
+
+    /**
+     * The APCu prefix in use, or null if APCu caching is not enabled.
+     *
+     * @return string|null
+     */
+    public function getApcuPrefix()
+    {
+        return $this->apcuPrefix;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     *
+     * @return void
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+        if (null === $this->vendorDir) {
+            return;
+        }
+
+        if ($prepend) {
+            self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+        } else {
+            unset(self::$registeredLoaders[$this->vendorDir]);
+            self::$registeredLoaders[$this->vendorDir] = $this;
+        }
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     *
+     * @return void
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+
+        if (null !== $this->vendorDir) {
+            unset(self::$registeredLoaders[$this->vendorDir]);
+        }
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param  string    $class The name of the class
+     * @return true|null True if loaded, null otherwise
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            $includeFile = self::$includeFile;
+            $includeFile($file);
+
+            return true;
+        }
+
+        return null;
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|false The path if found, false otherwise
+     */
+    public function findFile($class)
+    {
+        // class map lookup
+        if (isset($this->classMap[$class])) {
+            return $this->classMap[$class];
+        }
+        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+            return false;
+        }
+        if (null !== $this->apcuPrefix) {
+            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+            if ($hit) {
+                return $file;
+            }
+        }
+
+        $file = $this->findFileWithExtension($class, '.php');
+
+        // Search for Hack files if we are running on HHVM
+        if (false === $file && defined('HHVM_VERSION')) {
+            $file = $this->findFileWithExtension($class, '.hh');
+        }
+
+        if (null !== $this->apcuPrefix) {
+            apcu_add($this->apcuPrefix.$class, $file);
+        }
+
+        if (false === $file) {
+            // Remember that this class does not exist.
+            $this->missingClasses[$class] = true;
+        }
+
+        return $file;
+    }
+
+    /**
+     * Returns the currently registered loaders keyed by their corresponding vendor directories.
+     *
+     * @return array
+     */
+    public static function getRegisteredLoaders()
+    {
+        return self::$registeredLoaders;
+    }
+
+    /**
+     * @param  string       $class
+     * @param  string       $ext
+     * @return string|false
+     */
+    private function findFileWithExtension($class, $ext)
+    {
+        // PSR-4 lookup
+        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+        $first = $class[0];
+        if (isset($this->prefixLengthsPsr4[$first])) {
+            $subPath = $class;
+            while (false !== $lastPos = strrpos($subPath, '\\')) {
+                $subPath = substr($subPath, 0, $lastPos);
+                $search = $subPath . '\\';
+                if (isset($this->prefixDirsPsr4[$search])) {
+                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
+                        if (file_exists($file = $dir . $pathEnd)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-4 fallback dirs
+        foreach ($this->fallbackDirsPsr4 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 lookup
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+        } else {
+            // PEAR-like class name
+            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+        }
+
+        if (isset($this->prefixesPsr0[$first])) {
+            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-0 fallback dirs
+        foreach ($this->fallbackDirsPsr0 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 include paths.
+        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+            return $file;
+        }
+
+        return false;
+    }
+
+    /**
+     * @return void
+     */
+    private static function initializeIncludeClosure()
+    {
+        if (self::$includeFile !== null) {
+            return;
+        }
+
+        /**
+         * Scope isolated include.
+         *
+         * Prevents access to $this/self from included files.
+         *
+         * @param  string $file
+         * @return void
+         */
+        self::$includeFile = \Closure::bind(static function($file) {
+            include $file;
+        }, null, null);
+    }
+}
diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php
new file mode 100644
index 0000000..51e734a
--- /dev/null
+++ b/vendor/composer/InstalledVersions.php
@@ -0,0 +1,359 @@
+
+ *     Jordi Boggiano 
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer;
+
+use Composer\Autoload\ClassLoader;
+use Composer\Semver\VersionParser;
+
+/**
+ * This class is copied in every Composer installed project and available to all
+ *
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
+ *
+ * To require its presence, you can require `composer-runtime-api ^2.0`
+ *
+ * @final
+ */
+class InstalledVersions
+{
+    /**
+     * @var mixed[]|null
+     * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null
+     */
+    private static $installed;
+
+    /**
+     * @var bool|null
+     */
+    private static $canGetVendors;
+
+    /**
+     * @var array[]
+     * @psalm-var array}>
+     */
+    private static $installedByVendor = array();
+
+    /**
+     * Returns a list of all package names which are present, either by being installed, replaced or provided
+     *
+     * @return string[]
+     * @psalm-return list
+     */
+    public static function getInstalledPackages()
+    {
+        $packages = array();
+        foreach (self::getInstalled() as $installed) {
+            $packages[] = array_keys($installed['versions']);
+        }
+
+        if (1 === \count($packages)) {
+            return $packages[0];
+        }
+
+        return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+    }
+
+    /**
+     * Returns a list of all package names with a specific type e.g. 'library'
+     *
+     * @param  string   $type
+     * @return string[]
+     * @psalm-return list
+     */
+    public static function getInstalledPackagesByType($type)
+    {
+        $packagesByType = array();
+
+        foreach (self::getInstalled() as $installed) {
+            foreach ($installed['versions'] as $name => $package) {
+                if (isset($package['type']) && $package['type'] === $type) {
+                    $packagesByType[] = $name;
+                }
+            }
+        }
+
+        return $packagesByType;
+    }
+
+    /**
+     * Checks whether the given package is installed
+     *
+     * This also returns true if the package name is provided or replaced by another package
+     *
+     * @param  string $packageName
+     * @param  bool   $includeDevRequirements
+     * @return bool
+     */
+    public static function isInstalled($packageName, $includeDevRequirements = true)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (isset($installed['versions'][$packageName])) {
+                return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks whether the given package satisfies a version constraint
+     *
+     * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
+     *
+     *   Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
+     *
+     * @param  VersionParser $parser      Install composer/semver to have access to this class and functionality
+     * @param  string        $packageName
+     * @param  string|null   $constraint  A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
+     * @return bool
+     */
+    public static function satisfies(VersionParser $parser, $packageName, $constraint)
+    {
+        $constraint = $parser->parseConstraints((string) $constraint);
+        $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+        return $provided->matches($constraint);
+    }
+
+    /**
+     * Returns a version constraint representing all the range(s) which are installed for a given package
+     *
+     * It is easier to use this via isInstalled() with the $constraint argument if you need to check
+     * whether a given version of a package is installed, and not just whether it exists
+     *
+     * @param  string $packageName
+     * @return string Version constraint usable with composer/semver
+     */
+    public static function getVersionRanges($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            $ranges = array();
+            if (isset($installed['versions'][$packageName]['pretty_version'])) {
+                $ranges[] = $installed['versions'][$packageName]['pretty_version'];
+            }
+            if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+                $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+            }
+            if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+                $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+            }
+            if (array_key_exists('provided', $installed['versions'][$packageName])) {
+                $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+            }
+
+            return implode(' || ', $ranges);
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+     */
+    public static function getVersion($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            if (!isset($installed['versions'][$packageName]['version'])) {
+                return null;
+            }
+
+            return $installed['versions'][$packageName]['version'];
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+     */
+    public static function getPrettyVersion($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+                return null;
+            }
+
+            return $installed['versions'][$packageName]['pretty_version'];
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
+     */
+    public static function getReference($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            if (!isset($installed['versions'][$packageName]['reference'])) {
+                return null;
+            }
+
+            return $installed['versions'][$packageName]['reference'];
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
+     */
+    public static function getInstallPath($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @return array
+     * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
+     */
+    public static function getRootPackage()
+    {
+        $installed = self::getInstalled();
+
+        return $installed[0]['root'];
+    }
+
+    /**
+     * Returns the raw installed.php data for custom implementations
+     *
+     * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
+     * @return array[]
+     * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}
+     */
+    public static function getRawData()
+    {
+        @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+        if (null === self::$installed) {
+            // only require the installed.php file if this file is loaded from its dumped location,
+            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+            if (substr(__DIR__, -8, 1) !== 'C') {
+                self::$installed = include __DIR__ . '/installed.php';
+            } else {
+                self::$installed = array();
+            }
+        }
+
+        return self::$installed;
+    }
+
+    /**
+     * Returns the raw data of all installed.php which are currently loaded for custom implementations
+     *
+     * @return array[]
+     * @psalm-return list}>
+     */
+    public static function getAllRawData()
+    {
+        return self::getInstalled();
+    }
+
+    /**
+     * Lets you reload the static array from another file
+     *
+     * This is only useful for complex integrations in which a project needs to use
+     * this class but then also needs to execute another project's autoloader in process,
+     * and wants to ensure both projects have access to their version of installed.php.
+     *
+     * A typical case would be PHPUnit, where it would need to make sure it reads all
+     * the data it needs from this class, then call reload() with
+     * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
+     * the project in which it runs can then also use this class safely, without
+     * interference between PHPUnit's dependencies and the project's dependencies.
+     *
+     * @param  array[] $data A vendor/composer/installed.php data set
+     * @return void
+     *
+     * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data
+     */
+    public static function reload($data)
+    {
+        self::$installed = $data;
+        self::$installedByVendor = array();
+    }
+
+    /**
+     * @return array[]
+     * @psalm-return list}>
+     */
+    private static function getInstalled()
+    {
+        if (null === self::$canGetVendors) {
+            self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+        }
+
+        $installed = array();
+
+        if (self::$canGetVendors) {
+            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+                if (isset(self::$installedByVendor[$vendorDir])) {
+                    $installed[] = self::$installedByVendor[$vendorDir];
+                } elseif (is_file($vendorDir.'/composer/installed.php')) {
+                    /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */
+                    $required = require $vendorDir.'/composer/installed.php';
+                    $installed[] = self::$installedByVendor[$vendorDir] = $required;
+                    if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
+                        self::$installed = $installed[count($installed) - 1];
+                    }
+                }
+            }
+        }
+
+        if (null === self::$installed) {
+            // only require the installed.php file if this file is loaded from its dumped location,
+            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+            if (substr(__DIR__, -8, 1) !== 'C') {
+                /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */
+                $required = require __DIR__ . '/installed.php';
+                self::$installed = $required;
+            } else {
+                self::$installed = array();
+            }
+        }
+
+        if (self::$installed !== array()) {
+            $installed[] = self::$installed;
+        }
+
+        return $installed;
+    }
+}
diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE
new file mode 100644
index 0000000..f27399a
--- /dev/null
+++ b/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+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/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
new file mode 100644
index 0000000..afeafbd
--- /dev/null
+++ b/vendor/composer/autoload_classmap.php
@@ -0,0 +1,18 @@
+ $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
+    'CURLStringFile' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php',
+    'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
+    'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
+    'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
+    'ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php',
+    'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
+    'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
+    'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
+);
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
new file mode 100644
index 0000000..ae3f40a
--- /dev/null
+++ b/vendor/composer/autoload_files.php
@@ -0,0 +1,30 @@
+ $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
+    '60799491728b879e74601d83e38b2cad' => $vendorDir . '/illuminate/collections/helpers.php',
+    'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
+    'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
+    '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
+    '72579e7bd17821bb1321b87411366eae' => $vendorDir . '/illuminate/support/helpers.php',
+    '72142d7b40a3a0b14e91825290b5ad82' => $vendorDir . '/cakephp/core/functions.php',
+    '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
+    '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
+    'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
+    '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
+    'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
+    'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
+    '23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php',
+    '948ad5488880985ff1c06721a4e447fe' => $vendorDir . '/cakephp/utility/bootstrap.php',
+    '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
+    '253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php',
+    '2df68f9e79c919e2d88506611769ed2e' => $vendorDir . '/respect/stringifier/src/stringify.php',
+    'ef65a1626449d89d0811cf9befce46f0' => $vendorDir . '/illuminate/events/functions.php',
+    '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
+    'da5b71a9ad8465d48da441e2f36823b6' => $baseDir . '/support/helpers.php',
+);
diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php
new file mode 100644
index 0000000..de71277
--- /dev/null
+++ b/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,10 @@
+ array($vendorDir . '/jasongrimes/paginator/src'),
+);
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
new file mode 100644
index 0000000..178544f
--- /dev/null
+++ b/vendor/composer/autoload_psr4.php
@@ -0,0 +1,77 @@
+ array($vendorDir . '/yzh52521/easyhttp/src'),
+    'voku\\' => array($vendorDir . '/voku/portable-ascii/src/voku'),
+    'support\\' => array($vendorDir . '/workerman/webman-framework/src/support'),
+    'app\\View\\Components\\' => array($baseDir . '/app/view/components'),
+    'app\\' => array($baseDir . '/app'),
+    'Workerman\\Crontab\\' => array($vendorDir . '/workerman/crontab/src'),
+    'Workerman\\' => array($vendorDir . '/workerman/workerman'),
+    'Webman\\Event\\' => array($vendorDir . '/webman/event/src'),
+    'Webman\\Captcha\\' => array($vendorDir . '/webman/captcha/src'),
+    'Webman\\' => array($vendorDir . '/workerman/webman-framework/src'),
+    'Symfony\\Polyfill\\Php81\\' => array($vendorDir . '/symfony/polyfill-php81'),
+    'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
+    'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
+    'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
+    'Symfony\\Polyfill\\Intl\\Grapheme\\' => array($vendorDir . '/symfony/polyfill-intl-grapheme'),
+    'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
+    'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'),
+    'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'),
+    'Symfony\\Contracts\\Cache\\' => array($vendorDir . '/symfony/cache-contracts'),
+    'Symfony\\Component\\VarExporter\\' => array($vendorDir . '/symfony/var-exporter'),
+    'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
+    'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
+    'Symfony\\Component\\String\\' => array($vendorDir . '/symfony/string'),
+    'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'),
+    'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
+    'Symfony\\Component\\Config\\' => array($vendorDir . '/symfony/config'),
+    'Symfony\\Component\\Cache\\' => array($vendorDir . '/symfony/cache'),
+    'Support\\View\\' => array($vendorDir . '/workerman/webman-framework/src/support/view'),
+    'Support\\Exception\\' => array($vendorDir . '/workerman/webman-framework/src/support/exception'),
+    'Support\\Bootstrap\\' => array($vendorDir . '/workerman/webman-framework/src/support/bootstrap'),
+    'Support\\' => array($vendorDir . '/workerman/webman-framework/src/support'),
+    'Respect\\Validation\\' => array($vendorDir . '/workerman/validation/library'),
+    'Respect\\Stringifier\\' => array($vendorDir . '/respect/stringifier/src'),
+    'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
+    'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
+    'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src', $vendorDir . '/psr/http-factory/src'),
+    'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
+    'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
+    'Psr\\Clock\\' => array($vendorDir . '/psr/clock/src'),
+    'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
+    'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'),
+    'Phinx\\' => array($vendorDir . '/robmorgan/phinx/src/Phinx'),
+    'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
+    'Illuminate\\Support\\' => array($vendorDir . '/illuminate/macroable', $vendorDir . '/illuminate/conditionable', $vendorDir . '/illuminate/collections', $vendorDir . '/illuminate/support'),
+    'Illuminate\\Redis\\' => array($vendorDir . '/illuminate/redis'),
+    'Illuminate\\Pipeline\\' => array($vendorDir . '/illuminate/pipeline'),
+    'Illuminate\\Pagination\\' => array($vendorDir . '/illuminate/pagination'),
+    'Illuminate\\Events\\' => array($vendorDir . '/illuminate/events'),
+    'Illuminate\\Database\\' => array($vendorDir . '/illuminate/database'),
+    'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'),
+    'Illuminate\\Container\\' => array($vendorDir . '/illuminate/container'),
+    'Illuminate\\Bus\\' => array($vendorDir . '/illuminate/bus'),
+    'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
+    'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
+    'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
+    'GrahamCampbell\\ResultType\\' => array($vendorDir . '/graham-campbell/result-type/src'),
+    'FastRoute\\' => array($vendorDir . '/nikic/fast-route/src'),
+    'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
+    'Doctrine\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Inflector'),
+    'Carbon\\Doctrine\\' => array($vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine'),
+    'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
+    'Cake\\Utility\\' => array($vendorDir . '/cakephp/utility'),
+    'Cake\\Datasource\\' => array($vendorDir . '/cakephp/datasource'),
+    'Cake\\Database\\' => array($vendorDir . '/cakephp/database'),
+    'Cake\\Core\\' => array($vendorDir . '/cakephp/core'),
+    'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
+    'App\\' => array($baseDir . '/app'),
+    '' => array($baseDir . '/'),
+);
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
new file mode 100644
index 0000000..64d33d8
--- /dev/null
+++ b/vendor/composer/autoload_real.php
@@ -0,0 +1,50 @@
+register(true);
+
+        $filesToLoad = \Composer\Autoload\ComposerStaticInitd3b2156d13053c4f5c49b090883e3785::$files;
+        $requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
+            if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+                $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+
+                require $file;
+            }
+        }, null, null);
+        foreach ($filesToLoad as $fileIdentifier => $file) {
+            $requireFile($fileIdentifier, $file);
+        }
+
+        return $loader;
+    }
+}
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
new file mode 100644
index 0000000..6fbbb7c
--- /dev/null
+++ b/vendor/composer/autoload_static.php
@@ -0,0 +1,463 @@
+ __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
+        '60799491728b879e74601d83e38b2cad' => __DIR__ . '/..' . '/illuminate/collections/helpers.php',
+        'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
+        'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
+        '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
+        '72579e7bd17821bb1321b87411366eae' => __DIR__ . '/..' . '/illuminate/support/helpers.php',
+        '72142d7b40a3a0b14e91825290b5ad82' => __DIR__ . '/..' . '/cakephp/core/functions.php',
+        '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
+        '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
+        'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
+        '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
+        'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
+        'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
+        '23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php',
+        '948ad5488880985ff1c06721a4e447fe' => __DIR__ . '/..' . '/cakephp/utility/bootstrap.php',
+        '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
+        '253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php',
+        '2df68f9e79c919e2d88506611769ed2e' => __DIR__ . '/..' . '/respect/stringifier/src/stringify.php',
+        'ef65a1626449d89d0811cf9befce46f0' => __DIR__ . '/..' . '/illuminate/events/functions.php',
+        '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
+        'da5b71a9ad8465d48da441e2f36823b6' => __DIR__ . '/../..' . '/support/helpers.php',
+    );
+
+    public static $prefixLengthsPsr4 = array (
+        'y' => 
+        array (
+            'yzh52521\\EasyHttp\\' => 18,
+        ),
+        'v' => 
+        array (
+            'voku\\' => 5,
+        ),
+        's' => 
+        array (
+            'support\\' => 8,
+        ),
+        'a' => 
+        array (
+            'app\\View\\Components\\' => 20,
+            'app\\' => 4,
+        ),
+        'W' => 
+        array (
+            'Workerman\\Crontab\\' => 18,
+            'Workerman\\' => 10,
+            'Webman\\Event\\' => 13,
+            'Webman\\Captcha\\' => 15,
+            'Webman\\' => 7,
+        ),
+        'S' => 
+        array (
+            'Symfony\\Polyfill\\Php81\\' => 23,
+            'Symfony\\Polyfill\\Php80\\' => 23,
+            'Symfony\\Polyfill\\Mbstring\\' => 26,
+            'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
+            'Symfony\\Polyfill\\Intl\\Grapheme\\' => 31,
+            'Symfony\\Polyfill\\Ctype\\' => 23,
+            'Symfony\\Contracts\\Translation\\' => 30,
+            'Symfony\\Contracts\\Service\\' => 26,
+            'Symfony\\Contracts\\Cache\\' => 24,
+            'Symfony\\Component\\VarExporter\\' => 30,
+            'Symfony\\Component\\VarDumper\\' => 28,
+            'Symfony\\Component\\Translation\\' => 30,
+            'Symfony\\Component\\String\\' => 25,
+            'Symfony\\Component\\Filesystem\\' => 29,
+            'Symfony\\Component\\Console\\' => 26,
+            'Symfony\\Component\\Config\\' => 25,
+            'Symfony\\Component\\Cache\\' => 24,
+            'Support\\View\\' => 13,
+            'Support\\Exception\\' => 18,
+            'Support\\Bootstrap\\' => 18,
+            'Support\\' => 8,
+        ),
+        'R' => 
+        array (
+            'Respect\\Validation\\' => 19,
+            'Respect\\Stringifier\\' => 20,
+        ),
+        'P' => 
+        array (
+            'Psr\\SimpleCache\\' => 16,
+            'Psr\\Log\\' => 8,
+            'Psr\\Http\\Message\\' => 17,
+            'Psr\\Http\\Client\\' => 16,
+            'Psr\\Container\\' => 14,
+            'Psr\\Clock\\' => 10,
+            'Psr\\Cache\\' => 10,
+            'PhpOption\\' => 10,
+            'Phinx\\' => 6,
+        ),
+        'M' => 
+        array (
+            'Monolog\\' => 8,
+        ),
+        'I' => 
+        array (
+            'Illuminate\\Support\\' => 19,
+            'Illuminate\\Redis\\' => 17,
+            'Illuminate\\Pipeline\\' => 20,
+            'Illuminate\\Pagination\\' => 22,
+            'Illuminate\\Events\\' => 18,
+            'Illuminate\\Database\\' => 20,
+            'Illuminate\\Contracts\\' => 21,
+            'Illuminate\\Container\\' => 21,
+            'Illuminate\\Bus\\' => 15,
+        ),
+        'G' => 
+        array (
+            'GuzzleHttp\\Psr7\\' => 16,
+            'GuzzleHttp\\Promise\\' => 19,
+            'GuzzleHttp\\' => 11,
+            'GrahamCampbell\\ResultType\\' => 26,
+        ),
+        'F' => 
+        array (
+            'FastRoute\\' => 10,
+        ),
+        'D' => 
+        array (
+            'Dotenv\\' => 7,
+            'Doctrine\\Inflector\\' => 19,
+        ),
+        'C' => 
+        array (
+            'Carbon\\Doctrine\\' => 16,
+            'Carbon\\' => 7,
+            'Cake\\Utility\\' => 13,
+            'Cake\\Datasource\\' => 16,
+            'Cake\\Database\\' => 14,
+            'Cake\\Core\\' => 10,
+        ),
+        'B' => 
+        array (
+            'Brick\\Math\\' => 11,
+        ),
+        'A' => 
+        array (
+            'App\\' => 4,
+        ),
+    );
+
+    public static $prefixDirsPsr4 = array (
+        'yzh52521\\EasyHttp\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/yzh52521/easyhttp/src',
+        ),
+        'voku\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/voku/portable-ascii/src/voku',
+        ),
+        'support\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/workerman/webman-framework/src/support',
+        ),
+        'app\\View\\Components\\' => 
+        array (
+            0 => __DIR__ . '/../..' . '/app/view/components',
+        ),
+        'app\\' => 
+        array (
+            0 => __DIR__ . '/../..' . '/app',
+        ),
+        'Workerman\\Crontab\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/workerman/crontab/src',
+        ),
+        'Workerman\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/workerman/workerman',
+        ),
+        'Webman\\Event\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/webman/event/src',
+        ),
+        'Webman\\Captcha\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/webman/captcha/src',
+        ),
+        'Webman\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/workerman/webman-framework/src',
+        ),
+        'Symfony\\Polyfill\\Php81\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/polyfill-php81',
+        ),
+        'Symfony\\Polyfill\\Php80\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
+        ),
+        'Symfony\\Polyfill\\Mbstring\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
+        ),
+        'Symfony\\Polyfill\\Intl\\Normalizer\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer',
+        ),
+        'Symfony\\Polyfill\\Intl\\Grapheme\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme',
+        ),
+        'Symfony\\Polyfill\\Ctype\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
+        ),
+        'Symfony\\Contracts\\Translation\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/translation-contracts',
+        ),
+        'Symfony\\Contracts\\Service\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/service-contracts',
+        ),
+        'Symfony\\Contracts\\Cache\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/cache-contracts',
+        ),
+        'Symfony\\Component\\VarExporter\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/var-exporter',
+        ),
+        'Symfony\\Component\\VarDumper\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/var-dumper',
+        ),
+        'Symfony\\Component\\Translation\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/translation',
+        ),
+        'Symfony\\Component\\String\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/string',
+        ),
+        'Symfony\\Component\\Filesystem\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/filesystem',
+        ),
+        'Symfony\\Component\\Console\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/console',
+        ),
+        'Symfony\\Component\\Config\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/config',
+        ),
+        'Symfony\\Component\\Cache\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/cache',
+        ),
+        'Support\\View\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/workerman/webman-framework/src/support/view',
+        ),
+        'Support\\Exception\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/workerman/webman-framework/src/support/exception',
+        ),
+        'Support\\Bootstrap\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/workerman/webman-framework/src/support/bootstrap',
+        ),
+        'Support\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/workerman/webman-framework/src/support',
+        ),
+        'Respect\\Validation\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/workerman/validation/library',
+        ),
+        'Respect\\Stringifier\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/respect/stringifier/src',
+        ),
+        'Psr\\SimpleCache\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/simple-cache/src',
+        ),
+        'Psr\\Log\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/log/src',
+        ),
+        'Psr\\Http\\Message\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/http-message/src',
+            1 => __DIR__ . '/..' . '/psr/http-factory/src',
+        ),
+        'Psr\\Http\\Client\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/http-client/src',
+        ),
+        'Psr\\Container\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/container/src',
+        ),
+        'Psr\\Clock\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/clock/src',
+        ),
+        'Psr\\Cache\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/cache/src',
+        ),
+        'PhpOption\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/phpoption/phpoption/src/PhpOption',
+        ),
+        'Phinx\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx',
+        ),
+        'Monolog\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog',
+        ),
+        'Illuminate\\Support\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/macroable',
+            1 => __DIR__ . '/..' . '/illuminate/conditionable',
+            2 => __DIR__ . '/..' . '/illuminate/collections',
+            3 => __DIR__ . '/..' . '/illuminate/support',
+        ),
+        'Illuminate\\Redis\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/redis',
+        ),
+        'Illuminate\\Pipeline\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/pipeline',
+        ),
+        'Illuminate\\Pagination\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/pagination',
+        ),
+        'Illuminate\\Events\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/events',
+        ),
+        'Illuminate\\Database\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/database',
+        ),
+        'Illuminate\\Contracts\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/contracts',
+        ),
+        'Illuminate\\Container\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/container',
+        ),
+        'Illuminate\\Bus\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/bus',
+        ),
+        'GuzzleHttp\\Psr7\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
+        ),
+        'GuzzleHttp\\Promise\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/guzzlehttp/promises/src',
+        ),
+        'GuzzleHttp\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
+        ),
+        'GrahamCampbell\\ResultType\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/graham-campbell/result-type/src',
+        ),
+        'FastRoute\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/nikic/fast-route/src',
+        ),
+        'Dotenv\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/vlucas/phpdotenv/src',
+        ),
+        'Doctrine\\Inflector\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/doctrine/inflector/lib/Doctrine/Inflector',
+        ),
+        'Carbon\\Doctrine\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine',
+        ),
+        'Carbon\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon',
+        ),
+        'Cake\\Utility\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/cakephp/utility',
+        ),
+        'Cake\\Datasource\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/cakephp/datasource',
+        ),
+        'Cake\\Database\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/cakephp/database',
+        ),
+        'Cake\\Core\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/cakephp/core',
+        ),
+        'Brick\\Math\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/brick/math/src',
+        ),
+        'App\\' => 
+        array (
+            0 => __DIR__ . '/../..' . '/app',
+        ),
+    );
+
+    public static $fallbackDirsPsr4 = array (
+        0 => __DIR__ . '/../..' . '/',
+    );
+
+    public static $prefixesPsr0 = array (
+        'J' => 
+        array (
+            'JasonGrimes' => 
+            array (
+                0 => __DIR__ . '/..' . '/jasongrimes/paginator/src',
+            ),
+        ),
+    );
+
+    public static $classMap = array (
+        'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
+        'CURLStringFile' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php',
+        'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+        'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
+        'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
+        'ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php',
+        'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
+        'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
+        'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
+    );
+
+    public static function getInitializer(ClassLoader $loader)
+    {
+        return \Closure::bind(function () use ($loader) {
+            $loader->prefixLengthsPsr4 = ComposerStaticInitd3b2156d13053c4f5c49b090883e3785::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInitd3b2156d13053c4f5c49b090883e3785::$prefixDirsPsr4;
+            $loader->fallbackDirsPsr4 = ComposerStaticInitd3b2156d13053c4f5c49b090883e3785::$fallbackDirsPsr4;
+            $loader->prefixesPsr0 = ComposerStaticInitd3b2156d13053c4f5c49b090883e3785::$prefixesPsr0;
+            $loader->classMap = ComposerStaticInitd3b2156d13053c4f5c49b090883e3785::$classMap;
+
+        }, null, ClassLoader::class);
+    }
+}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
new file mode 100644
index 0000000..44d10b7
--- /dev/null
+++ b/vendor/composer/installed.json
@@ -0,0 +1,5046 @@
+{
+    "packages": [
+        {
+            "name": "brick/math",
+            "version": "0.11.0",
+            "version_normalized": "0.11.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/brick/math.git",
+                "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478",
+                "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^8.0"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.2",
+                "phpunit/phpunit": "^9.0",
+                "vimeo/psalm": "5.0.0"
+            },
+            "time": "2023-01-15T23:15:59+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Brick\\Math\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Arbitrary-precision arithmetic library",
+            "keywords": [
+                "Arbitrary-precision",
+                "BigInteger",
+                "BigRational",
+                "arithmetic",
+                "bigdecimal",
+                "bignum",
+                "brick",
+                "math"
+            ],
+            "support": {
+                "issues": "https://github.com/brick/math/issues",
+                "source": "https://github.com/brick/math/tree/0.11.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/BenMorel",
+                    "type": "github"
+                }
+            ],
+            "install-path": "../brick/math"
+        },
+        {
+            "name": "cakephp/core",
+            "version": "4.5.5",
+            "version_normalized": "4.5.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/cakephp/core.git",
+                "reference": "c2f4dff110d41e475d1041f2abe236f1c62d0cd0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/cakephp/core/zipball/c2f4dff110d41e475d1041f2abe236f1c62d0cd0",
+                "reference": "c2f4dff110d41e475d1041f2abe236f1c62d0cd0",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "cakephp/utility": "^4.0",
+                "php": ">=7.4.0"
+            },
+            "provide": {
+                "psr/container-implementation": "^1.0 || ^2.0"
+            },
+            "suggest": {
+                "cakephp/cache": "To use Configure::store() and restore().",
+                "cakephp/event": "To use PluginApplicationInterface or plugin applications.",
+                "league/container": "To use Container and ServiceProvider classes"
+            },
+            "time": "2023-10-21T13:30:46+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "functions.php"
+                ],
+                "psr-4": {
+                    "Cake\\Core\\": "."
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "CakePHP Community",
+                    "homepage": "https://github.com/cakephp/core/graphs/contributors"
+                }
+            ],
+            "description": "CakePHP Framework Core classes",
+            "homepage": "https://cakephp.org",
+            "keywords": [
+                "cakephp",
+                "core",
+                "framework"
+            ],
+            "support": {
+                "forum": "https://stackoverflow.com/tags/cakephp",
+                "irc": "irc://irc.freenode.org/cakephp",
+                "issues": "https://github.com/cakephp/cakephp/issues",
+                "source": "https://github.com/cakephp/core"
+            },
+            "install-path": "../cakephp/core"
+        },
+        {
+            "name": "cakephp/database",
+            "version": "4.5.6",
+            "version_normalized": "4.5.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/cakephp/database.git",
+                "reference": "317739cc32060ef19b6c19c87ac6b64848d78e27"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/cakephp/database/zipball/317739cc32060ef19b6c19c87ac6b64848d78e27",
+                "reference": "317739cc32060ef19b6c19c87ac6b64848d78e27",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "cakephp/core": "^4.0",
+                "cakephp/datasource": "^4.0",
+                "php": ">=7.4.0"
+            },
+            "suggest": {
+                "cakephp/i18n": "If you are using locale-aware datetime formats or Chronos types.",
+                "cakephp/log": "If you want to use query logging without providing a logger yourself."
+            },
+            "time": "2023-12-07T12:23:54+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Cake\\Database\\": "."
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "CakePHP Community",
+                    "homepage": "https://github.com/cakephp/database/graphs/contributors"
+                }
+            ],
+            "description": "Flexible and powerful Database abstraction library with a familiar PDO-like API",
+            "homepage": "https://cakephp.org",
+            "keywords": [
+                "abstraction",
+                "cakephp",
+                "database",
+                "database abstraction",
+                "pdo"
+            ],
+            "support": {
+                "forum": "https://stackoverflow.com/tags/cakephp",
+                "irc": "irc://irc.freenode.org/cakephp",
+                "issues": "https://github.com/cakephp/cakephp/issues",
+                "source": "https://github.com/cakephp/database"
+            },
+            "install-path": "../cakephp/database"
+        },
+        {
+            "name": "cakephp/datasource",
+            "version": "4.5.6",
+            "version_normalized": "4.5.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/cakephp/datasource.git",
+                "reference": "5d11a35ffc09dee744faaab7f758aeb42c17cfec"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/cakephp/datasource/zipball/5d11a35ffc09dee744faaab7f758aeb42c17cfec",
+                "reference": "5d11a35ffc09dee744faaab7f758aeb42c17cfec",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "cakephp/core": "^4.0",
+                "php": ">=7.4.0",
+                "psr/log": "^1.0 || ^2.0",
+                "psr/simple-cache": "^1.0 || ^2.0"
+            },
+            "suggest": {
+                "cakephp/cache": "If you decide to use Query caching.",
+                "cakephp/collection": "If you decide to use ResultSetInterface.",
+                "cakephp/utility": "If you decide to use EntityTrait."
+            },
+            "time": "2023-11-05T07:32:10+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Cake\\Datasource\\": "."
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "CakePHP Community",
+                    "homepage": "https://github.com/cakephp/datasource/graphs/contributors"
+                }
+            ],
+            "description": "Provides connection managing and traits for Entities and Queries that can be reused for different datastores",
+            "homepage": "https://cakephp.org",
+            "keywords": [
+                "cakephp",
+                "connection management",
+                "datasource",
+                "entity",
+                "query"
+            ],
+            "support": {
+                "forum": "https://stackoverflow.com/tags/cakephp",
+                "irc": "irc://irc.freenode.org/cakephp",
+                "issues": "https://github.com/cakephp/cakephp/issues",
+                "source": "https://github.com/cakephp/datasource"
+            },
+            "install-path": "../cakephp/datasource"
+        },
+        {
+            "name": "cakephp/utility",
+            "version": "4.5.6",
+            "version_normalized": "4.5.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/cakephp/utility.git",
+                "reference": "708929115e5b400e1b5b76d8120ca2e51e2de199"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/cakephp/utility/zipball/708929115e5b400e1b5b76d8120ca2e51e2de199",
+                "reference": "708929115e5b400e1b5b76d8120ca2e51e2de199",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "cakephp/core": "^4.0",
+                "php": ">=7.4.0"
+            },
+            "suggest": {
+                "ext-intl": "To use Text::transliterate() or Text::slug()",
+                "lib-ICU": "To use Text::transliterate() or Text::slug()"
+            },
+            "time": "2024-06-23T00:11:14+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Cake\\Utility\\": "."
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "CakePHP Community",
+                    "homepage": "https://github.com/cakephp/utility/graphs/contributors"
+                }
+            ],
+            "description": "CakePHP Utility classes such as Inflector, String, Hash, and Security",
+            "homepage": "https://cakephp.org",
+            "keywords": [
+                "cakephp",
+                "hash",
+                "inflector",
+                "security",
+                "string",
+                "utility"
+            ],
+            "support": {
+                "forum": "https://stackoverflow.com/tags/cakephp",
+                "irc": "irc://irc.freenode.org/cakephp",
+                "issues": "https://github.com/cakephp/cakephp/issues",
+                "source": "https://github.com/cakephp/utility"
+            },
+            "install-path": "../cakephp/utility"
+        },
+        {
+            "name": "carbonphp/carbon-doctrine-types",
+            "version": "2.1.0",
+            "version_normalized": "2.1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git",
+                "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/99f76ffa36cce3b70a4a6abce41dba15ca2e84cb",
+                "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^7.4 || ^8.0"
+            },
+            "conflict": {
+                "doctrine/dbal": "<3.7.0 || >=4.0.0"
+            },
+            "require-dev": {
+                "doctrine/dbal": "^3.7.0",
+                "nesbot/carbon": "^2.71.0 || ^3.0.0",
+                "phpunit/phpunit": "^10.3"
+            },
+            "time": "2023-12-11T17:09:12+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "KyleKatarn",
+                    "email": "kylekatarnls@gmail.com"
+                }
+            ],
+            "description": "Types to use Carbon in Doctrine",
+            "keywords": [
+                "carbon",
+                "date",
+                "datetime",
+                "doctrine",
+                "time"
+            ],
+            "support": {
+                "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues",
+                "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.1.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/kylekatarnls",
+                    "type": "github"
+                },
+                {
+                    "url": "https://opencollective.com/Carbon",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../carbonphp/carbon-doctrine-types"
+        },
+        {
+            "name": "doctrine/inflector",
+            "version": "2.0.10",
+            "version_normalized": "2.0.10.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/inflector.git",
+                "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc",
+                "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "doctrine/coding-standard": "^11.0",
+                "phpstan/phpstan": "^1.8",
+                "phpstan/phpstan-phpunit": "^1.1",
+                "phpstan/phpstan-strict-rules": "^1.3",
+                "phpunit/phpunit": "^8.5 || ^9.5",
+                "vimeo/psalm": "^4.25 || ^5.4"
+            },
+            "time": "2024-02-18T20:23:39+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+            "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+            "keywords": [
+                "inflection",
+                "inflector",
+                "lowercase",
+                "manipulation",
+                "php",
+                "plural",
+                "singular",
+                "strings",
+                "uppercase",
+                "words"
+            ],
+            "support": {
+                "issues": "https://github.com/doctrine/inflector/issues",
+                "source": "https://github.com/doctrine/inflector/tree/2.0.10"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../doctrine/inflector"
+        },
+        {
+            "name": "graham-campbell/result-type",
+            "version": "v1.1.2",
+            "version_normalized": "1.1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/GrahamCampbell/Result-Type.git",
+                "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862",
+                "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "phpoption/phpoption": "^1.9.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+            },
+            "time": "2023-11-12T22:16:48+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "GrahamCampbell\\ResultType\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                }
+            ],
+            "description": "An Implementation Of The Result Type",
+            "keywords": [
+                "Graham Campbell",
+                "GrahamCampbell",
+                "Result Type",
+                "Result-Type",
+                "result"
+            ],
+            "support": {
+                "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+                "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../graham-campbell/result-type"
+        },
+        {
+            "name": "guzzlehttp/guzzle",
+            "version": "7.9.2",
+            "version_normalized": "7.9.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle.git",
+                "reference": "d281ed313b989f213357e3be1a179f02196ac99b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
+                "reference": "d281ed313b989f213357e3be1a179f02196ac99b",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-json": "*",
+                "guzzlehttp/promises": "^1.5.3 || ^2.0.3",
+                "guzzlehttp/psr7": "^2.7.0",
+                "php": "^7.2.5 || ^8.0",
+                "psr/http-client": "^1.0",
+                "symfony/deprecation-contracts": "^2.2 || ^3.0"
+            },
+            "provide": {
+                "psr/http-client-implementation": "1.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "ext-curl": "*",
+                "guzzle/client-integration-tests": "3.0.2",
+                "php-http/message-factory": "^1.1",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20",
+                "psr/log": "^1.1 || ^2.0 || ^3.0"
+            },
+            "suggest": {
+                "ext-curl": "Required for CURL handler support",
+                "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+                "psr/log": "Required for using the Log middleware"
+            },
+            "time": "2024-07-24T11:22:20+00:00",
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "GuzzleHttp\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Jeremy Lindblom",
+                    "email": "jeremeamia@gmail.com",
+                    "homepage": "https://github.com/jeremeamia"
+                },
+                {
+                    "name": "George Mponos",
+                    "email": "gmponos@gmail.com",
+                    "homepage": "https://github.com/gmponos"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/Nyholm"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://github.com/sagikazarmark"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "email": "webmaster@tubo-world.de",
+                    "homepage": "https://github.com/Tobion"
+                }
+            ],
+            "description": "Guzzle is a PHP HTTP client library",
+            "keywords": [
+                "client",
+                "curl",
+                "framework",
+                "http",
+                "http client",
+                "psr-18",
+                "psr-7",
+                "rest",
+                "web service"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/guzzle/issues",
+                "source": "https://github.com/guzzle/guzzle/tree/7.9.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../guzzlehttp/guzzle"
+        },
+        {
+            "name": "guzzlehttp/promises",
+            "version": "1.5.3",
+            "version_normalized": "1.5.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/promises.git",
+                "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/67ab6e18aaa14d753cc148911d273f6e6cb6721e",
+                "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "^4.4 || ^5.1"
+            },
+            "time": "2023-05-21T12:31:43+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "GuzzleHttp\\Promise\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/Nyholm"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "email": "webmaster@tubo-world.de",
+                    "homepage": "https://github.com/Tobion"
+                }
+            ],
+            "description": "Guzzle promises library",
+            "keywords": [
+                "promise"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/promises/issues",
+                "source": "https://github.com/guzzle/promises/tree/1.5.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../guzzlehttp/promises"
+        },
+        {
+            "name": "guzzlehttp/psr7",
+            "version": "2.7.0",
+            "version_normalized": "2.7.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/psr7.git",
+                "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
+                "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "psr/http-factory": "^1.0",
+                "psr/http-message": "^1.1 || ^2.0",
+                "ralouphie/getallheaders": "^3.0"
+            },
+            "provide": {
+                "psr/http-factory-implementation": "1.0",
+                "psr/http-message-implementation": "1.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "http-interop/http-factory-tests": "0.9.0",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20"
+            },
+            "suggest": {
+                "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+            },
+            "time": "2024-07-18T11:15:46+00:00",
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Psr7\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "George Mponos",
+                    "email": "gmponos@gmail.com",
+                    "homepage": "https://github.com/gmponos"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/Nyholm"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://github.com/sagikazarmark"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "email": "webmaster@tubo-world.de",
+                    "homepage": "https://github.com/Tobion"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://sagikazarmark.hu"
+                }
+            ],
+            "description": "PSR-7 message implementation that also provides common utility methods",
+            "keywords": [
+                "http",
+                "message",
+                "psr-7",
+                "request",
+                "response",
+                "stream",
+                "uri",
+                "url"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/psr7/issues",
+                "source": "https://github.com/guzzle/psr7/tree/2.7.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../guzzlehttp/psr7"
+        },
+        {
+            "name": "illuminate/bus",
+            "version": "v9.52.16",
+            "version_normalized": "9.52.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/bus.git",
+                "reference": "4c719a19c3d8c34b2494a7206f8ffde3eff3f983"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/bus/zipball/4c719a19c3d8c34b2494a7206f8ffde3eff3f983",
+                "reference": "4c719a19c3d8c34b2494a7206f8ffde3eff3f983",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "illuminate/collections": "^9.0",
+                "illuminate/contracts": "^9.0",
+                "illuminate/pipeline": "^9.0",
+                "illuminate/support": "^9.0",
+                "php": "^8.0.2"
+            },
+            "suggest": {
+                "illuminate/queue": "Required to use closures when chaining jobs (^7.0)."
+            },
+            "time": "2023-04-18T13:42:14+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Bus\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Bus package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/bus"
+        },
+        {
+            "name": "illuminate/collections",
+            "version": "v9.52.16",
+            "version_normalized": "9.52.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/collections.git",
+                "reference": "d3710b0b244bfc62c288c1a87eaa62dd28352d1f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/collections/zipball/d3710b0b244bfc62c288c1a87eaa62dd28352d1f",
+                "reference": "d3710b0b244bfc62c288c1a87eaa62dd28352d1f",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "illuminate/conditionable": "^9.0",
+                "illuminate/contracts": "^9.0",
+                "illuminate/macroable": "^9.0",
+                "php": "^8.0.2"
+            },
+            "suggest": {
+                "symfony/var-dumper": "Required to use the dump method (^6.0)."
+            },
+            "time": "2023-06-11T21:17:10+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "helpers.php"
+                ],
+                "psr-4": {
+                    "Illuminate\\Support\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Collections package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/collections"
+        },
+        {
+            "name": "illuminate/conditionable",
+            "version": "v9.52.16",
+            "version_normalized": "9.52.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/conditionable.git",
+                "reference": "bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/conditionable/zipball/bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364",
+                "reference": "bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^8.0.2"
+            },
+            "time": "2023-02-01T21:42:32+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Support\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Conditionable package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/conditionable"
+        },
+        {
+            "name": "illuminate/container",
+            "version": "v9.52.16",
+            "version_normalized": "9.52.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/container.git",
+                "reference": "1641dda2d0750b68bb1264a3b37ff3973f2e6265"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/container/zipball/1641dda2d0750b68bb1264a3b37ff3973f2e6265",
+                "reference": "1641dda2d0750b68bb1264a3b37ff3973f2e6265",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "illuminate/contracts": "^9.0",
+                "php": "^8.0.2",
+                "psr/container": "^1.1.1|^2.0.1"
+            },
+            "provide": {
+                "psr/container-implementation": "1.1|2.0"
+            },
+            "time": "2023-01-24T16:54:18+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Container\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Container package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/container"
+        },
+        {
+            "name": "illuminate/contracts",
+            "version": "v9.52.16",
+            "version_normalized": "9.52.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/contracts.git",
+                "reference": "44f65d723b13823baa02ff69751a5948bde60c22"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/contracts/zipball/44f65d723b13823baa02ff69751a5948bde60c22",
+                "reference": "44f65d723b13823baa02ff69751a5948bde60c22",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^8.0.2",
+                "psr/container": "^1.1.1|^2.0.1",
+                "psr/simple-cache": "^1.0|^2.0|^3.0"
+            },
+            "time": "2023-02-08T14:36:30+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Contracts\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Contracts package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/contracts"
+        },
+        {
+            "name": "illuminate/database",
+            "version": "v9.52.16",
+            "version_normalized": "9.52.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/database.git",
+                "reference": "93cfc8e1f9ac147e6a2851ecabe8d8f21ad85182"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/database/zipball/93cfc8e1f9ac147e6a2851ecabe8d8f21ad85182",
+                "reference": "93cfc8e1f9ac147e6a2851ecabe8d8f21ad85182",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "brick/math": "^0.9.3|^0.10.2|^0.11",
+                "ext-pdo": "*",
+                "illuminate/collections": "^9.0",
+                "illuminate/container": "^9.0",
+                "illuminate/contracts": "^9.0",
+                "illuminate/macroable": "^9.0",
+                "illuminate/support": "^9.0",
+                "php": "^8.0.2",
+                "symfony/console": "^6.0.9"
+            },
+            "suggest": {
+                "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).",
+                "ext-filter": "Required to use the Postgres database driver.",
+                "fakerphp/faker": "Required to use the eloquent factory builder (^1.21).",
+                "illuminate/console": "Required to use the database commands (^9.0).",
+                "illuminate/events": "Required to use the observers with Eloquent (^9.0).",
+                "illuminate/filesystem": "Required to use the migrations (^9.0).",
+                "illuminate/pagination": "Required to paginate the result set (^9.0).",
+                "symfony/finder": "Required to use Eloquent model factories (^6.0)."
+            },
+            "time": "2023-06-11T21:17:10+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Database\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Database package.",
+            "homepage": "https://laravel.com",
+            "keywords": [
+                "database",
+                "laravel",
+                "orm",
+                "sql"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/database"
+        },
+        {
+            "name": "illuminate/events",
+            "version": "v9.52.16",
+            "version_normalized": "9.52.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/events.git",
+                "reference": "8e534676bac23bc17925f5c74c128f9c09b98f69"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/events/zipball/8e534676bac23bc17925f5c74c128f9c09b98f69",
+                "reference": "8e534676bac23bc17925f5c74c128f9c09b98f69",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "illuminate/bus": "^9.0",
+                "illuminate/collections": "^9.0",
+                "illuminate/container": "^9.0",
+                "illuminate/contracts": "^9.0",
+                "illuminate/macroable": "^9.0",
+                "illuminate/support": "^9.0",
+                "php": "^8.0.2"
+            },
+            "time": "2022-09-15T13:14:12+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "functions.php"
+                ],
+                "psr-4": {
+                    "Illuminate\\Events\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Events package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/events"
+        },
+        {
+            "name": "illuminate/macroable",
+            "version": "v9.52.16",
+            "version_normalized": "9.52.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/macroable.git",
+                "reference": "e3bfaf6401742a9c6abca61b9b10e998e5b6449a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/macroable/zipball/e3bfaf6401742a9c6abca61b9b10e998e5b6449a",
+                "reference": "e3bfaf6401742a9c6abca61b9b10e998e5b6449a",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^8.0.2"
+            },
+            "time": "2022-08-09T13:29:29+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Support\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Macroable package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/macroable"
+        },
+        {
+            "name": "illuminate/pagination",
+            "version": "v9.52.16",
+            "version_normalized": "9.52.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/pagination.git",
+                "reference": "0c913d6af303ae0060d94d74d68d537637f7e6d4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/pagination/zipball/0c913d6af303ae0060d94d74d68d537637f7e6d4",
+                "reference": "0c913d6af303ae0060d94d74d68d537637f7e6d4",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-filter": "*",
+                "illuminate/collections": "^9.0",
+                "illuminate/contracts": "^9.0",
+                "illuminate/support": "^9.0",
+                "php": "^8.0.2"
+            },
+            "time": "2023-02-06T02:52:41+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Pagination\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Pagination package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/pagination"
+        },
+        {
+            "name": "illuminate/pipeline",
+            "version": "v9.52.16",
+            "version_normalized": "9.52.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/pipeline.git",
+                "reference": "e0be3f3f79f8235ad7334919ca4094d5074e02f6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/pipeline/zipball/e0be3f3f79f8235ad7334919ca4094d5074e02f6",
+                "reference": "e0be3f3f79f8235ad7334919ca4094d5074e02f6",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "illuminate/contracts": "^9.0",
+                "illuminate/support": "^9.0",
+                "php": "^8.0.2"
+            },
+            "time": "2022-06-09T14:13:53+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Pipeline\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Pipeline package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/pipeline"
+        },
+        {
+            "name": "illuminate/redis",
+            "version": "v9.52.16",
+            "version_normalized": "9.52.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/redis.git",
+                "reference": "d4702b8d6a64a48cfab7259248ecbecf3b6135e2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/redis/zipball/d4702b8d6a64a48cfab7259248ecbecf3b6135e2",
+                "reference": "d4702b8d6a64a48cfab7259248ecbecf3b6135e2",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "illuminate/collections": "^9.0",
+                "illuminate/contracts": "^9.0",
+                "illuminate/macroable": "^9.0",
+                "illuminate/support": "^9.0",
+                "php": "^8.0.2"
+            },
+            "suggest": {
+                "ext-redis": "Required to use the phpredis connector (^4.0|^5.0).",
+                "predis/predis": "Required to use the predis connector (^1.1.9|^2.0.2)."
+            },
+            "time": "2023-07-31T15:01:27+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Redis\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Redis package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/redis"
+        },
+        {
+            "name": "illuminate/support",
+            "version": "v9.52.16",
+            "version_normalized": "9.52.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/support.git",
+                "reference": "223c608dbca27232df6213f776bfe7bdeec24874"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/support/zipball/223c608dbca27232df6213f776bfe7bdeec24874",
+                "reference": "223c608dbca27232df6213f776bfe7bdeec24874",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "doctrine/inflector": "^2.0",
+                "ext-ctype": "*",
+                "ext-filter": "*",
+                "ext-mbstring": "*",
+                "illuminate/collections": "^9.0",
+                "illuminate/conditionable": "^9.0",
+                "illuminate/contracts": "^9.0",
+                "illuminate/macroable": "^9.0",
+                "nesbot/carbon": "^2.62.1",
+                "php": "^8.0.2",
+                "voku/portable-ascii": "^2.0"
+            },
+            "conflict": {
+                "tightenco/collect": "<5.5.33"
+            },
+            "suggest": {
+                "illuminate/filesystem": "Required to use the composer class (^9.0).",
+                "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.0.2).",
+                "ramsey/uuid": "Required to use Str::uuid() (^4.7).",
+                "symfony/process": "Required to use the composer class (^6.0).",
+                "symfony/uid": "Required to use Str::ulid() (^6.0).",
+                "symfony/var-dumper": "Required to use the dd function (^6.0).",
+                "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)."
+            },
+            "time": "2023-06-11T21:11:53+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "helpers.php"
+                ],
+                "psr-4": {
+                    "Illuminate\\Support\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Support package.",
+            "homepage": "https://laravel.com",
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "install-path": "../illuminate/support"
+        },
+        {
+            "name": "jasongrimes/paginator",
+            "version": "1.0.3",
+            "version_normalized": "1.0.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/jasongrimes/php-paginator.git",
+                "reference": "3411e3cd0c6479a0b514f26e4358f0273552f221"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/jasongrimes/php-paginator/zipball/3411e3cd0c6479a0b514f26e4358f0273552f221",
+                "reference": "3411e3cd0c6479a0b514f26e4358f0273552f221",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.2"
+            },
+            "time": "2018-07-11T18:11:49+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-0": {
+                    "JasonGrimes": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jason Grimes",
+                    "email": "jason@grimesit.com"
+                }
+            ],
+            "description": "A lightweight PHP paginator, for generating pagination controls in the style of Stack Overflow and Flickr. The 'first' and 'last' page links are shown inline as page numbers, and excess page numbers are replaced by ellipses.",
+            "homepage": "http://github.com/jasongrimes/php-paginator",
+            "keywords": [
+                "pager",
+                "pagination",
+                "paginator"
+            ],
+            "support": {
+                "issues": "https://github.com/jasongrimes/php-paginator/issues",
+                "source": "https://github.com/jasongrimes/php-paginator/tree/master"
+            },
+            "install-path": "../jasongrimes/paginator"
+        },
+        {
+            "name": "monolog/monolog",
+            "version": "2.9.3",
+            "version_normalized": "2.9.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Seldaek/monolog.git",
+                "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/a30bfe2e142720dfa990d0a7e573997f5d884215",
+                "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.2",
+                "psr/log": "^1.0.1 || ^2.0 || ^3.0"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
+            },
+            "require-dev": {
+                "aws/aws-sdk-php": "^2.4.9 || ^3.0",
+                "doctrine/couchdb": "~1.0@dev",
+                "elasticsearch/elasticsearch": "^7 || ^8",
+                "ext-json": "*",
+                "graylog2/gelf-php": "^1.4.2 || ^2@dev",
+                "guzzlehttp/guzzle": "^7.4",
+                "guzzlehttp/psr7": "^2.2",
+                "mongodb/mongodb": "^1.8",
+                "php-amqplib/php-amqplib": "~2.4 || ^3",
+                "phpspec/prophecy": "^1.15",
+                "phpstan/phpstan": "^1.10",
+                "phpunit/phpunit": "^8.5.38 || ^9.6.19",
+                "predis/predis": "^1.1 || ^2.0",
+                "rollbar/rollbar": "^1.3 || ^2 || ^3",
+                "ruflin/elastica": "^7",
+                "swiftmailer/swiftmailer": "^5.3|^6.0",
+                "symfony/mailer": "^5.4 || ^6",
+                "symfony/mime": "^5.4 || ^6"
+            },
+            "suggest": {
+                "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+                "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+                "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
+                "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+                "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
+                "ext-mbstring": "Allow to work properly with unicode symbols",
+                "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
+                "ext-openssl": "Required to send log messages using SSL",
+                "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
+                "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+                "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
+                "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+                "rollbar/rollbar": "Allow sending log messages to Rollbar",
+                "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+            },
+            "time": "2024-04-12T20:52:51+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Monolog\\": "src/Monolog"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "https://seld.be"
+                }
+            ],
+            "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+            "homepage": "https://github.com/Seldaek/monolog",
+            "keywords": [
+                "log",
+                "logging",
+                "psr-3"
+            ],
+            "support": {
+                "issues": "https://github.com/Seldaek/monolog/issues",
+                "source": "https://github.com/Seldaek/monolog/tree/2.9.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/Seldaek",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../monolog/monolog"
+        },
+        {
+            "name": "nesbot/carbon",
+            "version": "2.72.5",
+            "version_normalized": "2.72.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/briannesbitt/Carbon.git",
+                "reference": "afd46589c216118ecd48ff2b95d77596af1e57ed"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/afd46589c216118ecd48ff2b95d77596af1e57ed",
+                "reference": "afd46589c216118ecd48ff2b95d77596af1e57ed",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "carbonphp/carbon-doctrine-types": "*",
+                "ext-json": "*",
+                "php": "^7.1.8 || ^8.0",
+                "psr/clock": "^1.0",
+                "symfony/polyfill-mbstring": "^1.0",
+                "symfony/polyfill-php80": "^1.16",
+                "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0"
+            },
+            "provide": {
+                "psr/clock-implementation": "1.0"
+            },
+            "require-dev": {
+                "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0",
+                "doctrine/orm": "^2.7 || ^3.0",
+                "friendsofphp/php-cs-fixer": "^3.0",
+                "kylekatarnls/multi-tester": "^2.0",
+                "ondrejmirtes/better-reflection": "*",
+                "phpmd/phpmd": "^2.9",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^0.12.99 || ^1.7.14",
+                "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6",
+                "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20",
+                "squizlabs/php_codesniffer": "^3.4"
+            },
+            "time": "2024-06-03T19:18:41+00:00",
+            "bin": [
+                "bin/carbon"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.x-dev",
+                    "dev-2.x": "2.x-dev"
+                },
+                "laravel": {
+                    "providers": [
+                        "Carbon\\Laravel\\ServiceProvider"
+                    ]
+                },
+                "phpstan": {
+                    "includes": [
+                        "extension.neon"
+                    ]
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Carbon\\": "src/Carbon/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Brian Nesbitt",
+                    "email": "brian@nesbot.com",
+                    "homepage": "https://markido.com"
+                },
+                {
+                    "name": "kylekatarnls",
+                    "homepage": "https://github.com/kylekatarnls"
+                }
+            ],
+            "description": "An API extension for DateTime that supports 281 different languages.",
+            "homepage": "https://carbon.nesbot.com",
+            "keywords": [
+                "date",
+                "datetime",
+                "time"
+            ],
+            "support": {
+                "docs": "https://carbon.nesbot.com/docs",
+                "issues": "https://github.com/briannesbitt/Carbon/issues",
+                "source": "https://github.com/briannesbitt/Carbon"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sponsors/kylekatarnls",
+                    "type": "github"
+                },
+                {
+                    "url": "https://opencollective.com/Carbon#sponsor",
+                    "type": "opencollective"
+                },
+                {
+                    "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../nesbot/carbon"
+        },
+        {
+            "name": "nikic/fast-route",
+            "version": "v1.3.0",
+            "version_normalized": "1.3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nikic/FastRoute.git",
+                "reference": "181d480e08d9476e61381e04a71b34dc0432e812"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
+                "reference": "181d480e08d9476e61381e04a71b34dc0432e812",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.35|~5.7"
+            },
+            "time": "2018-02-13T20:26:39+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "FastRoute\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Nikita Popov",
+                    "email": "nikic@php.net"
+                }
+            ],
+            "description": "Fast request router for PHP",
+            "keywords": [
+                "router",
+                "routing"
+            ],
+            "support": {
+                "issues": "https://github.com/nikic/FastRoute/issues",
+                "source": "https://github.com/nikic/FastRoute/tree/master"
+            },
+            "install-path": "../nikic/fast-route"
+        },
+        {
+            "name": "phpoption/phpoption",
+            "version": "1.9.3",
+            "version_normalized": "1.9.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/php-option.git",
+                "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54",
+                "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
+            },
+            "time": "2024-07-20T21:41:07+00:00",
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                },
+                "branch-alias": {
+                    "dev-master": "1.9-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "PhpOption\\": "src/PhpOption/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmittjoh@gmail.com",
+                    "homepage": "https://github.com/schmittjoh"
+                },
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                }
+            ],
+            "description": "Option Type for PHP",
+            "keywords": [
+                "language",
+                "option",
+                "php",
+                "type"
+            ],
+            "support": {
+                "issues": "https://github.com/schmittjoh/php-option/issues",
+                "source": "https://github.com/schmittjoh/php-option/tree/1.9.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../phpoption/phpoption"
+        },
+        {
+            "name": "psr/cache",
+            "version": "3.0.0",
+            "version_normalized": "3.0.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/cache.git",
+                "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
+                "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.0"
+            },
+            "time": "2021-02-03T23:26:27+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Cache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for caching libraries",
+            "keywords": [
+                "cache",
+                "psr",
+                "psr-6"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/cache/tree/3.0.0"
+            },
+            "install-path": "../psr/cache"
+        },
+        {
+            "name": "psr/clock",
+            "version": "1.0.0",
+            "version_normalized": "1.0.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/clock.git",
+                "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+                "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^7.0 || ^8.0"
+            },
+            "time": "2022-11-25T14:36:26+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Clock\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for reading the clock.",
+            "homepage": "https://github.com/php-fig/clock",
+            "keywords": [
+                "clock",
+                "now",
+                "psr",
+                "psr-20",
+                "time"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/clock/issues",
+                "source": "https://github.com/php-fig/clock/tree/1.0.0"
+            },
+            "install-path": "../psr/clock"
+        },
+        {
+            "name": "psr/container",
+            "version": "2.0.2",
+            "version_normalized": "2.0.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+                "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.4.0"
+            },
+            "time": "2021-11-05T16:47:00+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/container/issues",
+                "source": "https://github.com/php-fig/container/tree/2.0.2"
+            },
+            "install-path": "../psr/container"
+        },
+        {
+            "name": "psr/http-client",
+            "version": "1.0.3",
+            "version_normalized": "1.0.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-client.git",
+                "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+                "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^7.0 || ^8.0",
+                "psr/http-message": "^1.0 || ^2.0"
+            },
+            "time": "2023-09-23T14:17:50+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Client\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP clients",
+            "homepage": "https://github.com/php-fig/http-client",
+            "keywords": [
+                "http",
+                "http-client",
+                "psr",
+                "psr-18"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-client"
+            },
+            "install-path": "../psr/http-client"
+        },
+        {
+            "name": "psr/http-factory",
+            "version": "1.0.2",
+            "version_normalized": "1.0.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-factory.git",
+                "reference": "e616d01114759c4c489f93b099585439f795fe35"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
+                "reference": "e616d01114759c4c489f93b099585439f795fe35",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.0.0",
+                "psr/http-message": "^1.0 || ^2.0"
+            },
+            "time": "2023-04-10T20:10:41+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for PSR-7 HTTP message factories",
+            "keywords": [
+                "factory",
+                "http",
+                "message",
+                "psr",
+                "psr-17",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
+            },
+            "install-path": "../psr/http-factory"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "2.0",
+            "version_normalized": "2.0.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "time": "2023-04-04T09:54:51+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-message/tree/2.0"
+            },
+            "install-path": "../psr/http-message"
+        },
+        {
+            "name": "psr/log",
+            "version": "2.0.0",
+            "version_normalized": "2.0.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log.git",
+                "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376",
+                "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.0"
+            },
+            "time": "2021-07-14T16:41:46+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Log\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "homepage": "https://github.com/php-fig/log",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/log/tree/2.0.0"
+            },
+            "install-path": "../psr/log"
+        },
+        {
+            "name": "psr/simple-cache",
+            "version": "2.0.0",
+            "version_normalized": "2.0.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/simple-cache.git",
+                "reference": "8707bf3cea6f710bf6ef05491234e3ab06f6432a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/8707bf3cea6f710bf6ef05491234e3ab06f6432a",
+                "reference": "8707bf3cea6f710bf6ef05491234e3ab06f6432a",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.0"
+            },
+            "time": "2021-10-29T13:22:09+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\SimpleCache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for simple caching",
+            "keywords": [
+                "cache",
+                "caching",
+                "psr",
+                "psr-16",
+                "simple-cache"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/simple-cache/tree/2.0.0"
+            },
+            "install-path": "../psr/simple-cache"
+        },
+        {
+            "name": "ralouphie/getallheaders",
+            "version": "3.0.3",
+            "version_normalized": "3.0.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ralouphie/getallheaders.git",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.1",
+                "phpunit/phpunit": "^5 || ^6.5"
+            },
+            "time": "2019-03-08T08:55:37+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "src/getallheaders.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ralph Khattar",
+                    "email": "ralph.khattar@gmail.com"
+                }
+            ],
+            "description": "A polyfill for getallheaders.",
+            "support": {
+                "issues": "https://github.com/ralouphie/getallheaders/issues",
+                "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+            },
+            "install-path": "../ralouphie/getallheaders"
+        },
+        {
+            "name": "respect/stringifier",
+            "version": "0.2.0",
+            "version_normalized": "0.2.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Respect/Stringifier.git",
+                "reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Respect/Stringifier/zipball/e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59",
+                "reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^2.8",
+                "malukenho/docheader": "^0.1.7",
+                "phpunit/phpunit": "^6.4"
+            },
+            "time": "2017-12-29T19:39:25+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "src/stringify.php"
+                ],
+                "psr-4": {
+                    "Respect\\Stringifier\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Respect/Stringifier Contributors",
+                    "homepage": "https://github.com/Respect/Stringifier/graphs/contributors"
+                }
+            ],
+            "description": "Converts any value to a string",
+            "homepage": "http://respect.github.io/Stringifier/",
+            "keywords": [
+                "respect",
+                "stringifier",
+                "stringify"
+            ],
+            "support": {
+                "issues": "https://github.com/Respect/Stringifier/issues",
+                "source": "https://github.com/Respect/Stringifier/tree/0.2.0"
+            },
+            "install-path": "../respect/stringifier"
+        },
+        {
+            "name": "robmorgan/phinx",
+            "version": "0.14.0",
+            "version_normalized": "0.14.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/cakephp/phinx.git",
+                "reference": "7bc24bae664b2124f3d5b8d1e98fdb8abaf70e87"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/cakephp/phinx/zipball/7bc24bae664b2124f3d5b8d1e98fdb8abaf70e87",
+                "reference": "7bc24bae664b2124f3d5b8d1e98fdb8abaf70e87",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "cakephp/database": "^4.0",
+                "php-64bit": ">=7.3",
+                "psr/container": "^1.0 || ^2.0",
+                "symfony/config": "^3.4|^4.0|^5.0|^6.0",
+                "symfony/console": "^3.4|^4.0|^5.0|^6.0"
+            },
+            "require-dev": {
+                "cakephp/cakephp-codesniffer": "^4.0",
+                "ext-json": "*",
+                "ext-pdo": "*",
+                "phpunit/phpunit": "^9.5",
+                "sebastian/comparator": ">=1.2.3",
+                "symfony/yaml": "^3.4|^4.0|^5.0"
+            },
+            "suggest": {
+                "ext-json": "Install if using JSON configuration format",
+                "ext-pdo": "PDO extension is needed",
+                "symfony/yaml": "Install if using YAML configuration format"
+            },
+            "time": "2023-09-07T14:26:14+00:00",
+            "bin": [
+                "bin/phinx"
+            ],
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Phinx\\": "src/Phinx/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Rob Morgan",
+                    "email": "robbym@gmail.com",
+                    "homepage": "https://robmorgan.id.au",
+                    "role": "Lead Developer"
+                },
+                {
+                    "name": "Woody Gilk",
+                    "email": "woody.gilk@gmail.com",
+                    "homepage": "https://shadowhand.me",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Richard Quadling",
+                    "email": "rquadling@gmail.com",
+                    "role": "Developer"
+                },
+                {
+                    "name": "CakePHP Community",
+                    "homepage": "https://github.com/cakephp/phinx/graphs/contributors",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.",
+            "homepage": "https://phinx.org",
+            "keywords": [
+                "database",
+                "database migrations",
+                "db",
+                "migrations",
+                "phinx"
+            ],
+            "support": {
+                "issues": "https://github.com/cakephp/phinx/issues",
+                "source": "https://github.com/cakephp/phinx/tree/0.14.0"
+            },
+            "install-path": "../robmorgan/phinx"
+        },
+        {
+            "name": "symfony/cache",
+            "version": "v6.0.19",
+            "version_normalized": "6.0.19.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/cache.git",
+                "reference": "81ca309f056e836480928b20280ec52ce8369bb3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/cache/zipball/81ca309f056e836480928b20280ec52ce8369bb3",
+                "reference": "81ca309f056e836480928b20280ec52ce8369bb3",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "psr/cache": "^2.0|^3.0",
+                "psr/log": "^1.1|^2|^3",
+                "symfony/cache-contracts": "^1.1.7|^2|^3",
+                "symfony/service-contracts": "^1.1|^2|^3",
+                "symfony/var-exporter": "^5.4|^6.0"
+            },
+            "conflict": {
+                "doctrine/dbal": "<2.13.1",
+                "symfony/dependency-injection": "<5.4",
+                "symfony/http-kernel": "<5.4",
+                "symfony/var-dumper": "<5.4"
+            },
+            "provide": {
+                "psr/cache-implementation": "2.0|3.0",
+                "psr/simple-cache-implementation": "1.0|2.0|3.0",
+                "symfony/cache-implementation": "1.1|2.0|3.0"
+            },
+            "require-dev": {
+                "cache/integration-tests": "dev-master",
+                "doctrine/dbal": "^2.13.1|^3.0",
+                "predis/predis": "^1.1",
+                "psr/simple-cache": "^1.0|^2.0|^3.0",
+                "symfony/config": "^5.4|^6.0",
+                "symfony/dependency-injection": "^5.4|^6.0",
+                "symfony/filesystem": "^5.4|^6.0",
+                "symfony/http-kernel": "^5.4|^6.0",
+                "symfony/messenger": "^5.4|^6.0",
+                "symfony/var-dumper": "^5.4|^6.0"
+            },
+            "time": "2023-01-20T17:44:14+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Cache\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides extended PSR-6, PSR-16 (and tags) implementations",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "caching",
+                "psr6"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/cache/tree/v6.0.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/cache"
+        },
+        {
+            "name": "symfony/cache-contracts",
+            "version": "v3.0.2",
+            "version_normalized": "3.0.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/cache-contracts.git",
+                "reference": "1c0a181c9ee221afe4fa55b2d13fc63c5ae14348"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/1c0a181c9ee221afe4fa55b2d13fc63c5ae14348",
+                "reference": "1c0a181c9ee221afe4fa55b2d13fc63c5ae14348",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "psr/cache": "^3.0"
+            },
+            "suggest": {
+                "symfony/cache-implementation": ""
+            },
+            "time": "2022-01-02T09:55:41+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.0-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Cache\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to caching",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/cache-contracts/tree/v3.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/cache-contracts"
+        },
+        {
+            "name": "symfony/config",
+            "version": "v6.0.19",
+            "version_normalized": "6.0.19.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/config.git",
+                "reference": "db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/config/zipball/db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3",
+                "reference": "db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/deprecation-contracts": "^2.1|^3",
+                "symfony/filesystem": "^5.4|^6.0",
+                "symfony/polyfill-ctype": "~1.8",
+                "symfony/polyfill-php81": "^1.22"
+            },
+            "conflict": {
+                "symfony/finder": "<4.4"
+            },
+            "require-dev": {
+                "symfony/event-dispatcher": "^5.4|^6.0",
+                "symfony/finder": "^5.4|^6.0",
+                "symfony/messenger": "^5.4|^6.0",
+                "symfony/service-contracts": "^1.1|^2|^3",
+                "symfony/yaml": "^5.4|^6.0"
+            },
+            "suggest": {
+                "symfony/yaml": "To use the yaml reference dumper"
+            },
+            "time": "2023-01-09T04:36:00+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Config\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/config/tree/v6.0.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/config"
+        },
+        {
+            "name": "symfony/console",
+            "version": "v6.0.19",
+            "version_normalized": "6.0.19.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/console.git",
+                "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/console/zipball/c3ebc83d031b71c39da318ca8b7a07ecc67507ed",
+                "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/service-contracts": "^1.1|^2|^3",
+                "symfony/string": "^5.4|^6.0"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<5.4",
+                "symfony/dotenv": "<5.4",
+                "symfony/event-dispatcher": "<5.4",
+                "symfony/lock": "<5.4",
+                "symfony/process": "<5.4"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0|2.0|3.0"
+            },
+            "require-dev": {
+                "psr/log": "^1|^2|^3",
+                "symfony/config": "^5.4|^6.0",
+                "symfony/dependency-injection": "^5.4|^6.0",
+                "symfony/event-dispatcher": "^5.4|^6.0",
+                "symfony/lock": "^5.4|^6.0",
+                "symfony/process": "^5.4|^6.0",
+                "symfony/var-dumper": "^5.4|^6.0"
+            },
+            "suggest": {
+                "psr/log": "For using the console logger",
+                "symfony/event-dispatcher": "",
+                "symfony/lock": "",
+                "symfony/process": ""
+            },
+            "time": "2023-01-01T08:36:10+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Console\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Eases the creation of beautiful and testable command line interfaces",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "cli",
+                "command line",
+                "console",
+                "terminal"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/console/tree/v6.0.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/console"
+        },
+        {
+            "name": "symfony/deprecation-contracts",
+            "version": "v3.0.2",
+            "version_normalized": "3.0.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/deprecation-contracts.git",
+                "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
+                "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.2"
+            },
+            "time": "2022-01-02T09:55:41+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.0-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "function.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "A generic function and convention to trigger deprecation notices",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/deprecation-contracts"
+        },
+        {
+            "name": "symfony/filesystem",
+            "version": "v6.0.19",
+            "version_normalized": "6.0.19.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/filesystem.git",
+                "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/3d49eec03fda1f0fc19b7349fbbe55ebc1004214",
+                "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/polyfill-ctype": "~1.8",
+                "symfony/polyfill-mbstring": "~1.8"
+            },
+            "time": "2023-01-20T17:44:14+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Filesystem\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides basic utilities for the filesystem",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/filesystem/tree/v6.0.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/filesystem"
+        },
+        {
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.29.0",
+            "version_normalized": "1.29.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-ctype.git",
+                "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
+                "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "provide": {
+                "ext-ctype": "*"
+            },
+            "suggest": {
+                "ext-ctype": "For best performance"
+            },
+            "time": "2024-01-29T20:11:03+00:00",
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Ctype\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Gert de Pagter",
+                    "email": "BackEndTea@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for ctype functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "ctype",
+                "polyfill",
+                "portable"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/polyfill-ctype"
+        },
+        {
+            "name": "symfony/polyfill-intl-grapheme",
+            "version": "v1.30.0",
+            "version_normalized": "1.30.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+                "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a",
+                "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "time": "2024-05-31T15:07:36+00:00",
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's grapheme_* functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "grapheme",
+                "intl",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/polyfill-intl-grapheme"
+        },
+        {
+            "name": "symfony/polyfill-intl-normalizer",
+            "version": "v1.29.0",
+            "version_normalized": "1.29.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+                "reference": "bc45c394692b948b4d383a08d7753968bed9a83d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d",
+                "reference": "bc45c394692b948b4d383a08d7753968bed9a83d",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "time": "2024-01-29T20:11:03+00:00",
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's Normalizer class and related functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "intl",
+                "normalizer",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/polyfill-intl-normalizer"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.29.0",
+            "version_normalized": "1.29.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
+                "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "provide": {
+                "ext-mbstring": "*"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "time": "2024-01-29T20:11:03+00:00",
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/polyfill-mbstring"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.29.0",
+            "version_normalized": "1.29.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
+                "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "time": "2024-01-29T20:11:03+00:00",
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/polyfill-php80"
+        },
+        {
+            "name": "symfony/polyfill-php81",
+            "version": "v1.30.0",
+            "version_normalized": "1.30.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php81.git",
+                "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af",
+                "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "time": "2024-06-19T12:30:46+00:00",
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php81\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php81/tree/v1.30.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/polyfill-php81"
+        },
+        {
+            "name": "symfony/service-contracts",
+            "version": "v3.0.2",
+            "version_normalized": "3.0.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/service-contracts.git",
+                "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d78d39c1599bd1188b8e26bb341da52c3c6d8a66",
+                "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "psr/container": "^2.0"
+            },
+            "conflict": {
+                "ext-psr": "<1.1|>=2"
+            },
+            "suggest": {
+                "symfony/service-implementation": ""
+            },
+            "time": "2022-05-30T19:17:58+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.0-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Service\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to writing services",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/service-contracts/tree/v3.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/service-contracts"
+        },
+        {
+            "name": "symfony/string",
+            "version": "v6.0.19",
+            "version_normalized": "6.0.19.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/string.git",
+                "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/string/zipball/d9e72497367c23e08bf94176d2be45b00a9d232a",
+                "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/polyfill-ctype": "~1.8",
+                "symfony/polyfill-intl-grapheme": "~1.0",
+                "symfony/polyfill-intl-normalizer": "~1.0",
+                "symfony/polyfill-mbstring": "~1.0"
+            },
+            "conflict": {
+                "symfony/translation-contracts": "<2.0"
+            },
+            "require-dev": {
+                "symfony/error-handler": "^5.4|^6.0",
+                "symfony/http-client": "^5.4|^6.0",
+                "symfony/translation-contracts": "^2.0|^3.0",
+                "symfony/var-exporter": "^5.4|^6.0"
+            },
+            "time": "2023-01-01T08:36:10+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "Resources/functions.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\String\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "grapheme",
+                "i18n",
+                "string",
+                "unicode",
+                "utf-8",
+                "utf8"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/string/tree/v6.0.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/string"
+        },
+        {
+            "name": "symfony/translation",
+            "version": "v6.0.19",
+            "version_normalized": "6.0.19.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/translation.git",
+                "reference": "9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f",
+                "reference": "9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/translation-contracts": "^2.3|^3.0"
+            },
+            "conflict": {
+                "symfony/config": "<5.4",
+                "symfony/console": "<5.4",
+                "symfony/dependency-injection": "<5.4",
+                "symfony/http-kernel": "<5.4",
+                "symfony/twig-bundle": "<5.4",
+                "symfony/yaml": "<5.4"
+            },
+            "provide": {
+                "symfony/translation-implementation": "2.3|3.0"
+            },
+            "require-dev": {
+                "psr/log": "^1|^2|^3",
+                "symfony/config": "^5.4|^6.0",
+                "symfony/console": "^5.4|^6.0",
+                "symfony/dependency-injection": "^5.4|^6.0",
+                "symfony/finder": "^5.4|^6.0",
+                "symfony/http-client-contracts": "^1.1|^2.0|^3.0",
+                "symfony/http-kernel": "^5.4|^6.0",
+                "symfony/intl": "^5.4|^6.0",
+                "symfony/polyfill-intl-icu": "^1.21",
+                "symfony/service-contracts": "^1.1.2|^2|^3",
+                "symfony/yaml": "^5.4|^6.0"
+            },
+            "suggest": {
+                "psr/log-implementation": "To use logging capability in translator",
+                "symfony/config": "",
+                "symfony/yaml": ""
+            },
+            "time": "2023-01-01T08:36:10+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "Resources/functions.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\Translation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides tools to internationalize your application",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/translation/tree/v6.0.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/translation"
+        },
+        {
+            "name": "symfony/translation-contracts",
+            "version": "v3.0.2",
+            "version_normalized": "3.0.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/translation-contracts.git",
+                "reference": "acbfbb274e730e5a0236f619b6168d9dedb3e282"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/acbfbb274e730e5a0236f619b6168d9dedb3e282",
+                "reference": "acbfbb274e730e5a0236f619b6168d9dedb3e282",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.2"
+            },
+            "suggest": {
+                "symfony/translation-implementation": ""
+            },
+            "time": "2022-06-27T17:10:44+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.0-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Translation\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to translation",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/translation-contracts/tree/v3.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/translation-contracts"
+        },
+        {
+            "name": "symfony/var-dumper",
+            "version": "v6.0.19",
+            "version_normalized": "6.0.19.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/var-dumper.git",
+                "reference": "eb980457fa6899840fe1687e8627a03a7d8a3d52"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/eb980457fa6899840fe1687e8627a03a7d8a3d52",
+                "reference": "eb980457fa6899840fe1687e8627a03a7d8a3d52",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/polyfill-mbstring": "~1.0"
+            },
+            "conflict": {
+                "phpunit/phpunit": "<5.4.3",
+                "symfony/console": "<5.4"
+            },
+            "require-dev": {
+                "ext-iconv": "*",
+                "symfony/console": "^5.4|^6.0",
+                "symfony/process": "^5.4|^6.0",
+                "symfony/uid": "^5.4|^6.0",
+                "twig/twig": "^2.13|^3.0.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"
+            },
+            "time": "2023-01-20T17:44:14+00:00",
+            "bin": [
+                "Resources/bin/var-dump-server"
+            ],
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "Resources/functions/dump.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\VarDumper\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides mechanisms for walking through any arbitrary PHP variable",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "debug",
+                "dump"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/var-dumper/tree/v6.0.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/var-dumper"
+        },
+        {
+            "name": "symfony/var-exporter",
+            "version": "v6.0.19",
+            "version_normalized": "6.0.19.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/var-exporter.git",
+                "reference": "df56f53818c2d5d9f683f4ad2e365ba73a3b69d2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/df56f53818c2d5d9f683f4ad2e365ba73a3b69d2",
+                "reference": "df56f53818c2d5d9f683f4ad2e365ba73a3b69d2",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=8.0.2"
+            },
+            "require-dev": {
+                "symfony/var-dumper": "^5.4|^6.0"
+            },
+            "time": "2023-01-13T08:34:10+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\VarExporter\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Allows exporting any serializable PHP data structure to plain PHP code",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "clone",
+                "construct",
+                "export",
+                "hydrate",
+                "instantiate",
+                "serialize"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/var-exporter/tree/v6.0.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/var-exporter"
+        },
+        {
+            "name": "vlucas/phpdotenv",
+            "version": "v5.6.0",
+            "version_normalized": "5.6.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/vlucas/phpdotenv.git",
+                "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4",
+                "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-pcre": "*",
+                "graham-campbell/result-type": "^1.1.2",
+                "php": "^7.2.5 || ^8.0",
+                "phpoption/phpoption": "^1.9.2",
+                "symfony/polyfill-ctype": "^1.24",
+                "symfony/polyfill-mbstring": "^1.24",
+                "symfony/polyfill-php80": "^1.24"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "ext-filter": "*",
+                "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+            },
+            "suggest": {
+                "ext-filter": "Required to use the boolean validator."
+            },
+            "time": "2023-11-12T22:43:29+00:00",
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": true
+                },
+                "branch-alias": {
+                    "dev-master": "5.6-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Dotenv\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Vance Lucas",
+                    "email": "vance@vancelucas.com",
+                    "homepage": "https://github.com/vlucas"
+                }
+            ],
+            "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+            "keywords": [
+                "dotenv",
+                "env",
+                "environment"
+            ],
+            "support": {
+                "issues": "https://github.com/vlucas/phpdotenv/issues",
+                "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../vlucas/phpdotenv"
+        },
+        {
+            "name": "voku/portable-ascii",
+            "version": "2.0.1",
+            "version_normalized": "2.0.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/voku/portable-ascii.git",
+                "reference": "b56450eed252f6801410d810c8e1727224ae0743"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743",
+                "reference": "b56450eed252f6801410d810c8e1727224ae0743",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.0.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+            },
+            "suggest": {
+                "ext-intl": "Use Intl for transliterator_transliterate() support"
+            },
+            "time": "2022-03-08T17:03:00+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "voku\\": "src/voku/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Lars Moelleken",
+                    "homepage": "http://www.moelleken.org/"
+                }
+            ],
+            "description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
+            "homepage": "https://github.com/voku/portable-ascii",
+            "keywords": [
+                "ascii",
+                "clean",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/voku/portable-ascii/issues",
+                "source": "https://github.com/voku/portable-ascii/tree/2.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.me/moelleken",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/voku",
+                    "type": "github"
+                },
+                {
+                    "url": "https://opencollective.com/portable-ascii",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://www.patreon.com/voku",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../voku/portable-ascii"
+        },
+        {
+            "name": "webman/captcha",
+            "version": "v1.0.3",
+            "version_normalized": "1.0.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webman-php/captcha.git",
+                "reference": "e80f271af99cb337406118fa7d90c0699ee1f3bc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webman-php/captcha/zipball/e80f271af99cb337406118fa7d90c0699ee1f3bc",
+                "reference": "e80f271af99cb337406118fa7d90c0699ee1f3bc",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-gd": "*",
+                "ext-mbstring": "*",
+                "php": ">=5.6.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.4"
+            },
+            "time": "2024-03-28T07:54:04+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Webman\\Captcha\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "walkor",
+                    "email": "walkor@workerman.net"
+                },
+                {
+                    "name": "Grégoire Passault",
+                    "email": "g.passault@gmail.com",
+                    "homepage": "http://www.gregwar.com/"
+                },
+                {
+                    "name": "Jeremy Livingston",
+                    "email": "jeremy.j.livingston@gmail.com"
+                }
+            ],
+            "description": "Captcha generator",
+            "keywords": [
+                "bot",
+                "captcha",
+                "spam"
+            ],
+            "support": {
+                "source": "https://github.com/webman-php/captcha/tree/v1.0.3"
+            },
+            "install-path": "../webman/captcha"
+        },
+        {
+            "name": "webman/event",
+            "version": "v1.0.4",
+            "version_normalized": "1.0.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webman-php/event.git",
+                "reference": "f4478941c3b7efa4d7e9c063f2f6efd7ee3071a7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webman-php/event/zipball/f4478941c3b7efa4d7e9c063f2f6efd7ee3071a7",
+                "reference": "f4478941c3b7efa4d7e9c063f2f6efd7ee3071a7",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "time": "2023-03-28T04:01:23+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Webman\\Event\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Webman event plugin",
+            "support": {
+                "issues": "https://github.com/webman-php/event/issues",
+                "source": "https://github.com/webman-php/event/tree/v1.0.4"
+            },
+            "install-path": "../webman/event"
+        },
+        {
+            "name": "workerman/crontab",
+            "version": "v1.0.6",
+            "version_normalized": "1.0.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/walkor/crontab.git",
+                "reference": "b78f1556f2977715b9cb5653129e6d9cf160d966"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/walkor/crontab/zipball/b78f1556f2977715b9cb5653129e6d9cf160d966",
+                "reference": "b78f1556f2977715b9cb5653129e6d9cf160d966",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.0",
+                "workerman/workerman": ">=4.0.20"
+            },
+            "time": "2022-10-17T01:59:19+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Workerman\\Crontab\\": "./src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "walkor",
+                    "email": "walkor@workerman.net",
+                    "homepage": "http://www.workerman.net",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A crontab written in PHP based on workerman",
+            "homepage": "http://www.workerman.net",
+            "keywords": [
+                "crontab"
+            ],
+            "support": {
+                "email": "walkor@workerman.net",
+                "forum": "http://wenda.workerman.net/",
+                "issues": "https://github.com/walkor/workerman/issues",
+                "source": "https://github.com/walkor/crontab",
+                "wiki": "http://doc.workerman.net/"
+            },
+            "install-path": "../workerman/crontab"
+        },
+        {
+            "name": "workerman/validation",
+            "version": "v3.1.2",
+            "version_normalized": "3.1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/walkor/validation.git",
+                "reference": "a4e9896e76b2fac92aff9a9f784df55f615571a0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/walkor/validation/zipball/a4e9896e76b2fac92aff9a9f784df55f615571a0",
+                "reference": "a4e9896e76b2fac92aff9a9f784df55f615571a0",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^8.0 || ^8.1 || ^8.2",
+                "respect/stringifier": "^0.2.0",
+                "symfony/polyfill-mbstring": "^1.2"
+            },
+            "require-dev": {
+                "egulias/email-validator": "^3.0",
+                "giggsey/libphonenumber-for-php-lite": "^8.13",
+                "malukenho/docheader": "^1.0",
+                "mikey179/vfsstream": "^1.6",
+                "phpstan/phpstan": "^1.9",
+                "phpstan/phpstan-deprecation-rules": "^1.1",
+                "phpstan/phpstan-phpunit": "^1.3",
+                "phpunit/phpunit": "^9.6",
+                "psr/http-message": "^1.0",
+                "respect/coding-standard": "^3.0",
+                "squizlabs/php_codesniffer": "^3.7"
+            },
+            "suggest": {
+                "egulias/email-validator": "Improves the Email rule if available",
+                "ext-bcmath": "Arbitrary Precision Mathematics",
+                "ext-fileinfo": "File Information",
+                "ext-mbstring": "Multibyte String Functions",
+                "giggsey/libphonenumber-for-php-lite": "Enables the phone rule if available"
+            },
+            "time": "2023-12-07T06:19:04+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Respect\\Validation\\": "library/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Respect/Validation Contributors",
+                    "homepage": "https://github.com/Respect/Validation/graphs/contributors"
+                }
+            ],
+            "description": "The most awesome validation engine ever created for PHP. Respect/Validation 汉化版本",
+            "homepage": "http://respect.github.io/Validation/",
+            "keywords": [
+                "respect",
+                "validation",
+                "validator"
+            ],
+            "support": {
+                "source": "https://github.com/walkor/validation/tree/v3.1.2"
+            },
+            "install-path": "../workerman/validation"
+        },
+        {
+            "name": "workerman/webman-framework",
+            "version": "v1.5.19",
+            "version_normalized": "1.5.19.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/walkor/webman-framework.git",
+                "reference": "9ac7c136b0197a15a31f5092782366abff9a6e06"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/walkor/webman-framework/zipball/9ac7c136b0197a15a31f5092782366abff9a6e06",
+                "reference": "9ac7c136b0197a15a31f5092782366abff9a6e06",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-json": "*",
+                "nikic/fast-route": "^1.3",
+                "php": ">=7.2",
+                "psr/container": ">=1.0",
+                "workerman/workerman": "^4.0.4 || ^5.0.0"
+            },
+            "suggest": {
+                "ext-event": "For better performance. "
+            },
+            "time": "2024-06-17T01:51:40+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Webman\\": "./src",
+                    "Support\\": "./src/support",
+                    "support\\": "./src/support",
+                    "Support\\View\\": "./src/support/view",
+                    "Support\\Bootstrap\\": "./src/support/bootstrap",
+                    "Support\\Exception\\": "./src/support/exception"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "walkor",
+                    "email": "walkor@workerman.net",
+                    "homepage": "https://www.workerman.net",
+                    "role": "Developer"
+                }
+            ],
+            "description": "High performance HTTP Service Framework.",
+            "homepage": "https://www.workerman.net",
+            "keywords": [
+                "High Performance",
+                "http service"
+            ],
+            "support": {
+                "email": "walkor@workerman.net",
+                "forum": "https://wenda.workerman.net/",
+                "issues": "https://github.com/walkor/webman/issues",
+                "source": "https://github.com/walkor/webman-framework",
+                "wiki": "https://doc.workerman.net/"
+            },
+            "install-path": "../workerman/webman-framework"
+        },
+        {
+            "name": "workerman/workerman",
+            "version": "v4.1.15",
+            "version_normalized": "4.1.15.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/walkor/workerman.git",
+                "reference": "afc8242fc769ab7cf22eb4ac22b97cb59d465e4e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/walkor/workerman/zipball/afc8242fc769ab7cf22eb4ac22b97cb59d465e4e",
+                "reference": "afc8242fc769ab7cf22eb4ac22b97cb59d465e4e",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.0"
+            },
+            "suggest": {
+                "ext-event": "For better performance. "
+            },
+            "time": "2024-02-19T02:10:39+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Workerman\\": "./"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "walkor",
+                    "email": "walkor@workerman.net",
+                    "homepage": "http://www.workerman.net",
+                    "role": "Developer"
+                }
+            ],
+            "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
+            "homepage": "http://www.workerman.net",
+            "keywords": [
+                "asynchronous",
+                "event-loop"
+            ],
+            "support": {
+                "email": "walkor@workerman.net",
+                "forum": "http://wenda.workerman.net/",
+                "issues": "https://github.com/walkor/workerman/issues",
+                "source": "https://github.com/walkor/workerman",
+                "wiki": "http://doc.workerman.net/"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/workerman",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://www.patreon.com/walkor",
+                    "type": "patreon"
+                }
+            ],
+            "install-path": "../workerman/workerman"
+        },
+        {
+            "name": "yzh52521/easyhttp",
+            "version": "v1.1.3",
+            "version_normalized": "1.1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/yuanzhihai/easyhttp.git",
+                "reference": "02bcf47eaf723520fa3905d0e6f1852168fe646c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/yuanzhihai/easyhttp/zipball/02bcf47eaf723520fa3905d0e6f1852168fe646c",
+                "reference": "02bcf47eaf723520fa3905d0e6f1852168fe646c",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "guzzlehttp/guzzle": "^6.0|^7.0",
+                "php": ">=7.2.5",
+                "psr/log": "^1.0|^2.0|^3.0"
+            },
+            "time": "2023-11-14T05:49:02+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "yzh52521\\EasyHttp\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "yzh52521",
+                    "email": "396751927@qq.com"
+                }
+            ],
+            "description": "EasyHttp 是一个轻量级、语义化、对IDE友好的HTTP客户端,支持常见的HTTP请求、异步请求和并发请求,让你可以快速地使用 HTTP 请求与其他 Web 应用进行通信。",
+            "homepage": "https://github.com/yzh52521/easyhttp",
+            "keywords": [
+                "EasyHttp",
+                "curl",
+                "easy-http",
+                "http",
+                "php",
+                "php-http",
+                "phphttp"
+            ],
+            "support": {
+                "issues": "https://github.com/yuanzhihai/easyhttp/issues",
+                "source": "https://github.com/yuanzhihai/easyhttp/tree/v1.1.3"
+            },
+            "install-path": "../yzh52521/easyhttp"
+        }
+    ],
+    "dev": true,
+    "dev-package-names": []
+}
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
new file mode 100644
index 0000000..69a9f9b
--- /dev/null
+++ b/vendor/composer/installed.php
@@ -0,0 +1,679 @@
+ array(
+        'name' => 'workerman/webman',
+        'pretty_version' => '1.0.0+no-version-set',
+        'version' => '1.0.0.0',
+        'reference' => null,
+        'type' => 'project',
+        'install_path' => __DIR__ . '/../../',
+        'aliases' => array(),
+        'dev' => true,
+    ),
+    'versions' => array(
+        'brick/math' => array(
+            'pretty_version' => '0.11.0',
+            'version' => '0.11.0.0',
+            'reference' => '0ad82ce168c82ba30d1c01ec86116ab52f589478',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../brick/math',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'cakephp/core' => array(
+            'pretty_version' => '4.5.5',
+            'version' => '4.5.5.0',
+            'reference' => 'c2f4dff110d41e475d1041f2abe236f1c62d0cd0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../cakephp/core',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'cakephp/database' => array(
+            'pretty_version' => '4.5.6',
+            'version' => '4.5.6.0',
+            'reference' => '317739cc32060ef19b6c19c87ac6b64848d78e27',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../cakephp/database',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'cakephp/datasource' => array(
+            'pretty_version' => '4.5.6',
+            'version' => '4.5.6.0',
+            'reference' => '5d11a35ffc09dee744faaab7f758aeb42c17cfec',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../cakephp/datasource',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'cakephp/utility' => array(
+            'pretty_version' => '4.5.6',
+            'version' => '4.5.6.0',
+            'reference' => '708929115e5b400e1b5b76d8120ca2e51e2de199',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../cakephp/utility',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'carbonphp/carbon-doctrine-types' => array(
+            'pretty_version' => '2.1.0',
+            'version' => '2.1.0.0',
+            'reference' => '99f76ffa36cce3b70a4a6abce41dba15ca2e84cb',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../carbonphp/carbon-doctrine-types',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'doctrine/inflector' => array(
+            'pretty_version' => '2.0.10',
+            'version' => '2.0.10.0',
+            'reference' => '5817d0659c5b50c9b950feb9af7b9668e2c436bc',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../doctrine/inflector',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'graham-campbell/result-type' => array(
+            'pretty_version' => 'v1.1.2',
+            'version' => '1.1.2.0',
+            'reference' => 'fbd48bce38f73f8a4ec8583362e732e4095e5862',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../graham-campbell/result-type',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'guzzlehttp/guzzle' => array(
+            'pretty_version' => '7.9.2',
+            'version' => '7.9.2.0',
+            'reference' => 'd281ed313b989f213357e3be1a179f02196ac99b',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'guzzlehttp/promises' => array(
+            'pretty_version' => '1.5.3',
+            'version' => '1.5.3.0',
+            'reference' => '67ab6e18aaa14d753cc148911d273f6e6cb6721e',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../guzzlehttp/promises',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'guzzlehttp/psr7' => array(
+            'pretty_version' => '2.7.0',
+            'version' => '2.7.0.0',
+            'reference' => 'a70f5c95fb43bc83f07c9c948baa0dc1829bf201',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../guzzlehttp/psr7',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/bus' => array(
+            'pretty_version' => 'v9.52.16',
+            'version' => '9.52.16.0',
+            'reference' => '4c719a19c3d8c34b2494a7206f8ffde3eff3f983',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/bus',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/collections' => array(
+            'pretty_version' => 'v9.52.16',
+            'version' => '9.52.16.0',
+            'reference' => 'd3710b0b244bfc62c288c1a87eaa62dd28352d1f',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/collections',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/conditionable' => array(
+            'pretty_version' => 'v9.52.16',
+            'version' => '9.52.16.0',
+            'reference' => 'bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/conditionable',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/container' => array(
+            'pretty_version' => 'v9.52.16',
+            'version' => '9.52.16.0',
+            'reference' => '1641dda2d0750b68bb1264a3b37ff3973f2e6265',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/container',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/contracts' => array(
+            'pretty_version' => 'v9.52.16',
+            'version' => '9.52.16.0',
+            'reference' => '44f65d723b13823baa02ff69751a5948bde60c22',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/contracts',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/database' => array(
+            'pretty_version' => 'v9.52.16',
+            'version' => '9.52.16.0',
+            'reference' => '93cfc8e1f9ac147e6a2851ecabe8d8f21ad85182',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/database',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/events' => array(
+            'pretty_version' => 'v9.52.16',
+            'version' => '9.52.16.0',
+            'reference' => '8e534676bac23bc17925f5c74c128f9c09b98f69',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/events',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/macroable' => array(
+            'pretty_version' => 'v9.52.16',
+            'version' => '9.52.16.0',
+            'reference' => 'e3bfaf6401742a9c6abca61b9b10e998e5b6449a',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/macroable',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/pagination' => array(
+            'pretty_version' => 'v9.52.16',
+            'version' => '9.52.16.0',
+            'reference' => '0c913d6af303ae0060d94d74d68d537637f7e6d4',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/pagination',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/pipeline' => array(
+            'pretty_version' => 'v9.52.16',
+            'version' => '9.52.16.0',
+            'reference' => 'e0be3f3f79f8235ad7334919ca4094d5074e02f6',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/pipeline',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/redis' => array(
+            'pretty_version' => 'v9.52.16',
+            'version' => '9.52.16.0',
+            'reference' => 'd4702b8d6a64a48cfab7259248ecbecf3b6135e2',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/redis',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'illuminate/support' => array(
+            'pretty_version' => 'v9.52.16',
+            'version' => '9.52.16.0',
+            'reference' => '223c608dbca27232df6213f776bfe7bdeec24874',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../illuminate/support',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'jasongrimes/paginator' => array(
+            'pretty_version' => '1.0.3',
+            'version' => '1.0.3.0',
+            'reference' => '3411e3cd0c6479a0b514f26e4358f0273552f221',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../jasongrimes/paginator',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'monolog/monolog' => array(
+            'pretty_version' => '2.9.3',
+            'version' => '2.9.3.0',
+            'reference' => 'a30bfe2e142720dfa990d0a7e573997f5d884215',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../monolog/monolog',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'nesbot/carbon' => array(
+            'pretty_version' => '2.72.5',
+            'version' => '2.72.5.0',
+            'reference' => 'afd46589c216118ecd48ff2b95d77596af1e57ed',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../nesbot/carbon',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'nikic/fast-route' => array(
+            'pretty_version' => 'v1.3.0',
+            'version' => '1.3.0.0',
+            'reference' => '181d480e08d9476e61381e04a71b34dc0432e812',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../nikic/fast-route',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'phpoption/phpoption' => array(
+            'pretty_version' => '1.9.3',
+            'version' => '1.9.3.0',
+            'reference' => 'e3fac8b24f56113f7cb96af14958c0dd16330f54',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpoption/phpoption',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/cache' => array(
+            'pretty_version' => '3.0.0',
+            'version' => '3.0.0.0',
+            'reference' => 'aa5030cfa5405eccfdcb1083ce040c2cb8d253bf',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/cache',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/cache-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '2.0|3.0',
+            ),
+        ),
+        'psr/clock' => array(
+            'pretty_version' => '1.0.0',
+            'version' => '1.0.0.0',
+            'reference' => 'e41a24703d4560fd0acb709162f73b8adfc3aa0d',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/clock',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/clock-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.0',
+            ),
+        ),
+        'psr/container' => array(
+            'pretty_version' => '2.0.2',
+            'version' => '2.0.2.0',
+            'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/container',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/container-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '^1.0 || ^2.0',
+                1 => '1.1|2.0',
+            ),
+        ),
+        'psr/http-client' => array(
+            'pretty_version' => '1.0.3',
+            'version' => '1.0.3.0',
+            'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/http-client',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/http-client-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.0',
+            ),
+        ),
+        'psr/http-factory' => array(
+            'pretty_version' => '1.0.2',
+            'version' => '1.0.2.0',
+            'reference' => 'e616d01114759c4c489f93b099585439f795fe35',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/http-factory',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/http-factory-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.0',
+            ),
+        ),
+        'psr/http-message' => array(
+            'pretty_version' => '2.0',
+            'version' => '2.0.0.0',
+            'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/http-message',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/http-message-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.0',
+            ),
+        ),
+        'psr/log' => array(
+            'pretty_version' => '2.0.0',
+            'version' => '2.0.0.0',
+            'reference' => 'ef29f6d262798707a9edd554e2b82517ef3a9376',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/log',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/log-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.0|2.0|3.0',
+                1 => '1.0.0 || 2.0.0 || 3.0.0',
+            ),
+        ),
+        'psr/simple-cache' => array(
+            'pretty_version' => '2.0.0',
+            'version' => '2.0.0.0',
+            'reference' => '8707bf3cea6f710bf6ef05491234e3ab06f6432a',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/simple-cache',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/simple-cache-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.0|2.0|3.0',
+            ),
+        ),
+        'ralouphie/getallheaders' => array(
+            'pretty_version' => '3.0.3',
+            'version' => '3.0.3.0',
+            'reference' => '120b605dfeb996808c31b6477290a714d356e822',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../ralouphie/getallheaders',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'respect/stringifier' => array(
+            'pretty_version' => '0.2.0',
+            'version' => '0.2.0.0',
+            'reference' => 'e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../respect/stringifier',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'robmorgan/phinx' => array(
+            'pretty_version' => '0.14.0',
+            'version' => '0.14.0.0',
+            'reference' => '7bc24bae664b2124f3d5b8d1e98fdb8abaf70e87',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../robmorgan/phinx',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/cache' => array(
+            'pretty_version' => 'v6.0.19',
+            'version' => '6.0.19.0',
+            'reference' => '81ca309f056e836480928b20280ec52ce8369bb3',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/cache',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/cache-contracts' => array(
+            'pretty_version' => 'v3.0.2',
+            'version' => '3.0.2.0',
+            'reference' => '1c0a181c9ee221afe4fa55b2d13fc63c5ae14348',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/cache-contracts',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/cache-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.1|2.0|3.0',
+            ),
+        ),
+        'symfony/config' => array(
+            'pretty_version' => 'v6.0.19',
+            'version' => '6.0.19.0',
+            'reference' => 'db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/config',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/console' => array(
+            'pretty_version' => 'v6.0.19',
+            'version' => '6.0.19.0',
+            'reference' => 'c3ebc83d031b71c39da318ca8b7a07ecc67507ed',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/console',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/deprecation-contracts' => array(
+            'pretty_version' => 'v3.0.2',
+            'version' => '3.0.2.0',
+            'reference' => '26954b3d62a6c5fd0ea8a2a00c0353a14978d05c',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/filesystem' => array(
+            'pretty_version' => 'v6.0.19',
+            'version' => '6.0.19.0',
+            'reference' => '3d49eec03fda1f0fc19b7349fbbe55ebc1004214',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/filesystem',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-ctype' => array(
+            'pretty_version' => 'v1.29.0',
+            'version' => '1.29.0.0',
+            'reference' => 'ef4d7e442ca910c4764bce785146269b30cb5fc4',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-intl-grapheme' => array(
+            'pretty_version' => 'v1.30.0',
+            'version' => '1.30.0.0',
+            'reference' => '64647a7c30b2283f5d49b874d84a18fc22054b7a',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-intl-normalizer' => array(
+            'pretty_version' => 'v1.29.0',
+            'version' => '1.29.0.0',
+            'reference' => 'bc45c394692b948b4d383a08d7753968bed9a83d',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-mbstring' => array(
+            'pretty_version' => 'v1.29.0',
+            'version' => '1.29.0.0',
+            'reference' => '9773676c8a1bb1f8d4340a62efe641cf76eda7ec',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-php80' => array(
+            'pretty_version' => 'v1.29.0',
+            'version' => '1.29.0.0',
+            'reference' => '87b68208d5c1188808dd7839ee1e6c8ec3b02f1b',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-php80',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-php81' => array(
+            'pretty_version' => 'v1.30.0',
+            'version' => '1.30.0.0',
+            'reference' => '3fb075789fb91f9ad9af537c4012d523085bd5af',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-php81',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/service-contracts' => array(
+            'pretty_version' => 'v3.0.2',
+            'version' => '3.0.2.0',
+            'reference' => 'd78d39c1599bd1188b8e26bb341da52c3c6d8a66',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/service-contracts',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/string' => array(
+            'pretty_version' => 'v6.0.19',
+            'version' => '6.0.19.0',
+            'reference' => 'd9e72497367c23e08bf94176d2be45b00a9d232a',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/string',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/translation' => array(
+            'pretty_version' => 'v6.0.19',
+            'version' => '6.0.19.0',
+            'reference' => '9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/translation',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/translation-contracts' => array(
+            'pretty_version' => 'v3.0.2',
+            'version' => '3.0.2.0',
+            'reference' => 'acbfbb274e730e5a0236f619b6168d9dedb3e282',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/translation-contracts',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/translation-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '2.3|3.0',
+            ),
+        ),
+        'symfony/var-dumper' => array(
+            'pretty_version' => 'v6.0.19',
+            'version' => '6.0.19.0',
+            'reference' => 'eb980457fa6899840fe1687e8627a03a7d8a3d52',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/var-dumper',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/var-exporter' => array(
+            'pretty_version' => 'v6.0.19',
+            'version' => '6.0.19.0',
+            'reference' => 'df56f53818c2d5d9f683f4ad2e365ba73a3b69d2',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/var-exporter',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'vlucas/phpdotenv' => array(
+            'pretty_version' => 'v5.6.0',
+            'version' => '5.6.0.0',
+            'reference' => '2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../vlucas/phpdotenv',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'voku/portable-ascii' => array(
+            'pretty_version' => '2.0.1',
+            'version' => '2.0.1.0',
+            'reference' => 'b56450eed252f6801410d810c8e1727224ae0743',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../voku/portable-ascii',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'webman/captcha' => array(
+            'pretty_version' => 'v1.0.3',
+            'version' => '1.0.3.0',
+            'reference' => 'e80f271af99cb337406118fa7d90c0699ee1f3bc',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../webman/captcha',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'webman/event' => array(
+            'pretty_version' => 'v1.0.4',
+            'version' => '1.0.4.0',
+            'reference' => 'f4478941c3b7efa4d7e9c063f2f6efd7ee3071a7',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../webman/event',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'workerman/crontab' => array(
+            'pretty_version' => 'v1.0.6',
+            'version' => '1.0.6.0',
+            'reference' => 'b78f1556f2977715b9cb5653129e6d9cf160d966',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../workerman/crontab',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'workerman/validation' => array(
+            'pretty_version' => 'v3.1.2',
+            'version' => '3.1.2.0',
+            'reference' => 'a4e9896e76b2fac92aff9a9f784df55f615571a0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../workerman/validation',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'workerman/webman' => array(
+            'pretty_version' => '1.0.0+no-version-set',
+            'version' => '1.0.0.0',
+            'reference' => null,
+            'type' => 'project',
+            'install_path' => __DIR__ . '/../../',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'workerman/webman-framework' => array(
+            'pretty_version' => 'v1.5.19',
+            'version' => '1.5.19.0',
+            'reference' => '9ac7c136b0197a15a31f5092782366abff9a6e06',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../workerman/webman-framework',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'workerman/workerman' => array(
+            'pretty_version' => 'v4.1.15',
+            'version' => '4.1.15.0',
+            'reference' => 'afc8242fc769ab7cf22eb4ac22b97cb59d465e4e',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../workerman/workerman',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'yzh52521/easyhttp' => array(
+            'pretty_version' => 'v1.1.3',
+            'version' => '1.1.3.0',
+            'reference' => '02bcf47eaf723520fa3905d0e6f1852168fe646c',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../yzh52521/easyhttp',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+    ),
+);
diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php
new file mode 100644
index 0000000..f92607f
--- /dev/null
+++ b/vendor/composer/platform_check.php
@@ -0,0 +1,30 @@
+= 80002)) {
+    $issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.2". You are running ' . PHP_VERSION . '.';
+}
+
+if (PHP_INT_SIZE !== 8) {
+    $issues[] = 'Your Composer dependencies require a 64-bit build of PHP.';
+}
+
+if ($issues) {
+    if (!headers_sent()) {
+        header('HTTP/1.1 500 Internal Server Error');
+    }
+    if (!ini_get('display_errors')) {
+        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+            fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
+        } elseif (!headers_sent()) {
+            echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
+        }
+    }
+    trigger_error(
+        'Composer detected issues in your platform: ' . implode(' ', $issues),
+        E_USER_ERROR
+    );
+}
diff --git a/vendor/doctrine/inflector/LICENSE b/vendor/doctrine/inflector/LICENSE
new file mode 100644
index 0000000..8c38cc1
--- /dev/null
+++ b/vendor/doctrine/inflector/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2006-2015 Doctrine Project
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/doctrine/inflector/README.md b/vendor/doctrine/inflector/README.md
new file mode 100644
index 0000000..6e3a97f
--- /dev/null
+++ b/vendor/doctrine/inflector/README.md
@@ -0,0 +1,7 @@
+# Doctrine Inflector
+
+Doctrine Inflector is a small library that can perform string manipulations
+with regard to uppercase/lowercase and singular/plural forms of words.
+
+[![Build Status](https://github.com/doctrine/inflector/workflows/Continuous%20Integration/badge.svg)](https://github.com/doctrine/inflector/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.0.x)
+[![Code Coverage](https://codecov.io/gh/doctrine/inflector/branch/2.0.x/graph/badge.svg)](https://codecov.io/gh/doctrine/inflector/branch/2.0.x)
diff --git a/vendor/doctrine/inflector/composer.json b/vendor/doctrine/inflector/composer.json
new file mode 100644
index 0000000..91d7707
--- /dev/null
+++ b/vendor/doctrine/inflector/composer.json
@@ -0,0 +1,41 @@
+{
+    "name": "doctrine/inflector",
+    "type": "library",
+    "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+    "keywords": ["php", "strings", "words", "manipulation", "inflector", "inflection", "uppercase", "lowercase", "singular", "plural"],
+    "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+    "license": "MIT",
+    "authors": [
+        {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
+        {"name": "Roman Borschel", "email": "roman@code-factory.org"},
+        {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
+        {"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
+        {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
+    ],
+    "require": {
+        "php": "^7.2 || ^8.0"
+    },
+    "require-dev": {
+        "doctrine/coding-standard": "^11.0",
+        "phpstan/phpstan": "^1.8",
+        "phpstan/phpstan-phpunit": "^1.1",
+        "phpstan/phpstan-strict-rules": "^1.3",
+        "phpunit/phpunit": "^8.5 || ^9.5",
+        "vimeo/psalm": "^4.25 || ^5.4"
+    },
+    "autoload": {
+        "psr-4": {
+            "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Doctrine\\Tests\\Inflector\\": "tests/Doctrine/Tests/Inflector"
+        }
+    },
+    "config": {
+        "allow-plugins": {
+            "dealerdirect/phpcodesniffer-composer-installer": true
+        }
+    }
+}
diff --git a/vendor/doctrine/inflector/docs/en/index.rst b/vendor/doctrine/inflector/docs/en/index.rst
new file mode 100644
index 0000000..29866f4
--- /dev/null
+++ b/vendor/doctrine/inflector/docs/en/index.rst
@@ -0,0 +1,226 @@
+Introduction
+============
+
+The Doctrine Inflector has methods for inflecting text. The features include pluralization,
+singularization, converting between camelCase and under_score and capitalizing
+words.
+
+Installation
+============
+
+You can install the Inflector with composer:
+
+.. code-block:: console
+
+    $ composer require doctrine/inflector
+
+Usage
+=====
+
+Using the inflector is easy, you can create a new ``Doctrine\Inflector\Inflector`` instance by using
+the ``Doctrine\Inflector\InflectorFactory`` class:
+
+.. code-block:: php
+
+    use Doctrine\Inflector\InflectorFactory;
+
+    $inflector = InflectorFactory::create()->build();
+
+By default it will create an English inflector. If you want to use another language, just pass the language
+you want to create an inflector for to the ``createForLanguage()`` method:
+
+.. code-block:: php
+
+    use Doctrine\Inflector\InflectorFactory;
+    use Doctrine\Inflector\Language;
+
+    $inflector = InflectorFactory::createForLanguage(Language::SPANISH)->build();
+
+The supported languages are as follows:
+
+- ``Language::ENGLISH``
+- ``Language::FRENCH``
+- ``Language::NORWEGIAN_BOKMAL``
+- ``Language::PORTUGUESE``
+- ``Language::SPANISH``
+- ``Language::TURKISH``
+
+If you want to manually construct the inflector instead of using a factory, you can do so like this:
+
+.. code-block:: php
+
+    use Doctrine\Inflector\CachedWordInflector;
+    use Doctrine\Inflector\RulesetInflector;
+    use Doctrine\Inflector\Rules\English;
+
+    $inflector = new Inflector(
+        new CachedWordInflector(new RulesetInflector(
+            English\Rules::getSingularRuleset()
+        )),
+        new CachedWordInflector(new RulesetInflector(
+            English\Rules::getPluralRuleset()
+        ))
+    );
+
+Adding Languages
+----------------
+
+If you are interested in adding support for your language, take a look at the other languages defined in the
+``Doctrine\Inflector\Rules`` namespace and the tests located in ``Doctrine\Tests\Inflector\Rules``. You can copy
+one of the languages and update the rules for your language.
+
+Once you have done this, send a pull request to the ``doctrine/inflector`` repository with the additions.
+
+Custom Setup
+============
+
+If you want to setup custom singular and plural rules, you can configure these in the factory:
+
+.. code-block:: php
+
+    use Doctrine\Inflector\InflectorFactory;
+    use Doctrine\Inflector\Rules\Pattern;
+    use Doctrine\Inflector\Rules\Patterns;
+    use Doctrine\Inflector\Rules\Ruleset;
+    use Doctrine\Inflector\Rules\Substitution;
+    use Doctrine\Inflector\Rules\Substitutions;
+    use Doctrine\Inflector\Rules\Transformation;
+    use Doctrine\Inflector\Rules\Transformations;
+    use Doctrine\Inflector\Rules\Word;
+
+    $inflector = InflectorFactory::create()
+        ->withSingularRules(
+            new Ruleset(
+                new Transformations(
+                    new Transformation(new Pattern('/^(bil)er$/i'), '\1'),
+                    new Transformation(new Pattern('/^(inflec|contribu)tors$/i'), '\1ta')
+                ),
+                new Patterns(new Pattern('singulars')),
+                new Substitutions(new Substitution(new Word('spins'), new Word('spinor')))
+            )
+        )
+        ->withPluralRules(
+            new Ruleset(
+                new Transformations(
+                    new Transformation(new Pattern('^(bil)er$'), '\1'),
+                    new Transformation(new Pattern('^(inflec|contribu)tors$'), '\1ta')
+                ),
+                new Patterns(new Pattern('noflect'), new Pattern('abtuse')),
+                new Substitutions(
+                    new Substitution(new Word('amaze'), new Word('amazable')),
+                    new Substitution(new Word('phone'), new Word('phonezes'))
+                )
+            )
+        )
+        ->build();
+
+No operation inflector
+----------------------
+
+The ``Doctrine\Inflector\NoopWordInflector`` may be used to configure an inflector that doesn't perform any operation for
+pluralization and/or singularization. If will simply return the input as output.
+
+This is an implementation of the `Null Object design pattern `_.
+
+.. code-block:: php
+
+    use Doctrine\Inflector\Inflector;
+    use Doctrine\Inflector\NoopWordInflector;
+
+    $inflector = new Inflector(new NoopWordInflector(), new NoopWordInflector());
+
+Tableize
+========
+
+Converts ``ModelName`` to ``model_name``:
+
+.. code-block:: php
+
+    echo $inflector->tableize('ModelName'); // model_name
+
+Classify
+========
+
+Converts ``model_name`` to ``ModelName``:
+
+.. code-block:: php
+
+    echo $inflector->classify('model_name'); // ModelName
+
+Camelize
+========
+
+This method uses `Classify`_ and then converts the first character to lowercase:
+
+.. code-block:: php
+
+    echo $inflector->camelize('model_name'); // modelName
+
+Capitalize
+==========
+
+Takes a string and capitalizes all of the words, like PHP's built-in
+``ucwords`` function. This extends that behavior, however, by allowing the
+word delimiters to be configured, rather than only separating on
+whitespace.
+
+Here is an example:
+
+.. code-block:: php
+
+    $string = 'top-o-the-morning to all_of_you!';
+
+    echo $inflector->capitalize($string); // Top-O-The-Morning To All_of_you!
+
+    echo $inflector->capitalize($string, '-_ '); // Top-O-The-Morning To All_Of_You!
+
+Pluralize
+=========
+
+Returns a word in plural form.
+
+.. code-block:: php
+
+    echo $inflector->pluralize('browser'); // browsers
+
+Singularize
+===========
+
+Returns a word in singular form.
+
+.. code-block:: php
+
+    echo $inflector->singularize('browsers'); // browser
+
+Urlize
+======
+
+Generate a URL friendly string from a string of text:
+
+.. code-block:: php
+
+    echo $inflector->urlize('My first blog post'); // my-first-blog-post
+
+Unaccent
+========
+
+You can unaccent a string of text using the ``unaccent()`` method:
+
+.. code-block:: php
+
+    echo $inflector->unaccent('año'); // ano
+
+Legacy API
+==========
+
+The API present in Inflector 1.x is still available, but will be deprecated in a future release and dropped for 3.0.
+Support for languages other than English is available in the 2.0 API only.
+
+Acknowledgements
+================
+
+The language rules in this library have been adapted from several different sources, including but not limited to:
+
+- `Ruby On Rails Inflector `_
+- `ICanBoogie Inflector `_
+- `CakePHP Inflector `_
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/CachedWordInflector.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/CachedWordInflector.php
new file mode 100644
index 0000000..2d52908
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/CachedWordInflector.php
@@ -0,0 +1,24 @@
+wordInflector = $wordInflector;
+    }
+
+    public function inflect(string $word): string
+    {
+        return $this->cache[$word] ?? $this->cache[$word] = $this->wordInflector->inflect($word);
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/GenericLanguageInflectorFactory.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/GenericLanguageInflectorFactory.php
new file mode 100644
index 0000000..166061d
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/GenericLanguageInflectorFactory.php
@@ -0,0 +1,66 @@
+singularRulesets[] = $this->getSingularRuleset();
+        $this->pluralRulesets[]   = $this->getPluralRuleset();
+    }
+
+    final public function build(): Inflector
+    {
+        return new Inflector(
+            new CachedWordInflector(new RulesetInflector(
+                ...$this->singularRulesets
+            )),
+            new CachedWordInflector(new RulesetInflector(
+                ...$this->pluralRulesets
+            ))
+        );
+    }
+
+    final public function withSingularRules(?Ruleset $singularRules, bool $reset = false): LanguageInflectorFactory
+    {
+        if ($reset) {
+            $this->singularRulesets = [];
+        }
+
+        if ($singularRules instanceof Ruleset) {
+            array_unshift($this->singularRulesets, $singularRules);
+        }
+
+        return $this;
+    }
+
+    final public function withPluralRules(?Ruleset $pluralRules, bool $reset = false): LanguageInflectorFactory
+    {
+        if ($reset) {
+            $this->pluralRulesets = [];
+        }
+
+        if ($pluralRules instanceof Ruleset) {
+            array_unshift($this->pluralRulesets, $pluralRules);
+        }
+
+        return $this;
+    }
+
+    abstract protected function getSingularRuleset(): Ruleset;
+
+    abstract protected function getPluralRuleset(): Ruleset;
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Inflector.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Inflector.php
new file mode 100644
index 0000000..610a4cf
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Inflector.php
@@ -0,0 +1,507 @@
+ 'A',
+        'Á' => 'A',
+        'Â' => 'A',
+        'Ã' => 'A',
+        'Ä' => 'Ae',
+        'Æ' => 'Ae',
+        'Å' => 'Aa',
+        'æ' => 'a',
+        'Ç' => 'C',
+        'È' => 'E',
+        'É' => 'E',
+        'Ê' => 'E',
+        'Ë' => 'E',
+        'Ì' => 'I',
+        'Í' => 'I',
+        'Î' => 'I',
+        'Ï' => 'I',
+        'Ñ' => 'N',
+        'Ò' => 'O',
+        'Ó' => 'O',
+        'Ô' => 'O',
+        'Õ' => 'O',
+        'Ö' => 'Oe',
+        'Ù' => 'U',
+        'Ú' => 'U',
+        'Û' => 'U',
+        'Ü' => 'Ue',
+        'Ý' => 'Y',
+        'ß' => 'ss',
+        'à' => 'a',
+        'á' => 'a',
+        'â' => 'a',
+        'ã' => 'a',
+        'ä' => 'ae',
+        'å' => 'aa',
+        'ç' => 'c',
+        'è' => 'e',
+        'é' => 'e',
+        'ê' => 'e',
+        'ë' => 'e',
+        'ì' => 'i',
+        'í' => 'i',
+        'î' => 'i',
+        'ï' => 'i',
+        'ñ' => 'n',
+        'ò' => 'o',
+        'ó' => 'o',
+        'ô' => 'o',
+        'õ' => 'o',
+        'ö' => 'oe',
+        'ù' => 'u',
+        'ú' => 'u',
+        'û' => 'u',
+        'ü' => 'ue',
+        'ý' => 'y',
+        'ÿ' => 'y',
+        'Ā' => 'A',
+        'ā' => 'a',
+        'Ă' => 'A',
+        'ă' => 'a',
+        'Ą' => 'A',
+        'ą' => 'a',
+        'Ć' => 'C',
+        'ć' => 'c',
+        'Ĉ' => 'C',
+        'ĉ' => 'c',
+        'Ċ' => 'C',
+        'ċ' => 'c',
+        'Č' => 'C',
+        'č' => 'c',
+        'Ď' => 'D',
+        'ď' => 'd',
+        'Đ' => 'D',
+        'đ' => 'd',
+        'Ē' => 'E',
+        'ē' => 'e',
+        'Ĕ' => 'E',
+        'ĕ' => 'e',
+        'Ė' => 'E',
+        'ė' => 'e',
+        'Ę' => 'E',
+        'ę' => 'e',
+        'Ě' => 'E',
+        'ě' => 'e',
+        'Ĝ' => 'G',
+        'ĝ' => 'g',
+        'Ğ' => 'G',
+        'ğ' => 'g',
+        'Ġ' => 'G',
+        'ġ' => 'g',
+        'Ģ' => 'G',
+        'ģ' => 'g',
+        'Ĥ' => 'H',
+        'ĥ' => 'h',
+        'Ħ' => 'H',
+        'ħ' => 'h',
+        'Ĩ' => 'I',
+        'ĩ' => 'i',
+        'Ī' => 'I',
+        'ī' => 'i',
+        'Ĭ' => 'I',
+        'ĭ' => 'i',
+        'Į' => 'I',
+        'į' => 'i',
+        'İ' => 'I',
+        'ı' => 'i',
+        'IJ' => 'IJ',
+        'ij' => 'ij',
+        'Ĵ' => 'J',
+        'ĵ' => 'j',
+        'Ķ' => 'K',
+        'ķ' => 'k',
+        'ĸ' => 'k',
+        'Ĺ' => 'L',
+        'ĺ' => 'l',
+        'Ļ' => 'L',
+        'ļ' => 'l',
+        'Ľ' => 'L',
+        'ľ' => 'l',
+        'Ŀ' => 'L',
+        'ŀ' => 'l',
+        'Ł' => 'L',
+        'ł' => 'l',
+        'Ń' => 'N',
+        'ń' => 'n',
+        'Ņ' => 'N',
+        'ņ' => 'n',
+        'Ň' => 'N',
+        'ň' => 'n',
+        'ʼn' => 'N',
+        'Ŋ' => 'n',
+        'ŋ' => 'N',
+        'Ō' => 'O',
+        'ō' => 'o',
+        'Ŏ' => 'O',
+        'ŏ' => 'o',
+        'Ő' => 'O',
+        'ő' => 'o',
+        'Œ' => 'OE',
+        'œ' => 'oe',
+        'Ø' => 'O',
+        'ø' => 'o',
+        'Ŕ' => 'R',
+        'ŕ' => 'r',
+        'Ŗ' => 'R',
+        'ŗ' => 'r',
+        'Ř' => 'R',
+        'ř' => 'r',
+        'Ś' => 'S',
+        'ś' => 's',
+        'Ŝ' => 'S',
+        'ŝ' => 's',
+        'Ş' => 'S',
+        'ş' => 's',
+        'Š' => 'S',
+        'š' => 's',
+        'Ţ' => 'T',
+        'ţ' => 't',
+        'Ť' => 'T',
+        'ť' => 't',
+        'Ŧ' => 'T',
+        'ŧ' => 't',
+        'Ũ' => 'U',
+        'ũ' => 'u',
+        'Ū' => 'U',
+        'ū' => 'u',
+        'Ŭ' => 'U',
+        'ŭ' => 'u',
+        'Ů' => 'U',
+        'ů' => 'u',
+        'Ű' => 'U',
+        'ű' => 'u',
+        'Ų' => 'U',
+        'ų' => 'u',
+        'Ŵ' => 'W',
+        'ŵ' => 'w',
+        'Ŷ' => 'Y',
+        'ŷ' => 'y',
+        'Ÿ' => 'Y',
+        'Ź' => 'Z',
+        'ź' => 'z',
+        'Ż' => 'Z',
+        'ż' => 'z',
+        'Ž' => 'Z',
+        'ž' => 'z',
+        'ſ' => 's',
+        '€' => 'E',
+        '£' => '',
+    ];
+
+    /** @var WordInflector */
+    private $singularizer;
+
+    /** @var WordInflector */
+    private $pluralizer;
+
+    public function __construct(WordInflector $singularizer, WordInflector $pluralizer)
+    {
+        $this->singularizer = $singularizer;
+        $this->pluralizer   = $pluralizer;
+    }
+
+    /**
+     * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'.
+     */
+    public function tableize(string $word): string
+    {
+        $tableized = preg_replace('~(?<=\\w)([A-Z])~u', '_$1', $word);
+
+        if ($tableized === null) {
+            throw new RuntimeException(sprintf(
+                'preg_replace returned null for value "%s"',
+                $word
+            ));
+        }
+
+        return mb_strtolower($tableized);
+    }
+
+    /**
+     * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'.
+     */
+    public function classify(string $word): string
+    {
+        return str_replace([' ', '_', '-'], '', ucwords($word, ' _-'));
+    }
+
+    /**
+     * Camelizes a word. This uses the classify() method and turns the first character to lowercase.
+     */
+    public function camelize(string $word): string
+    {
+        return lcfirst($this->classify($word));
+    }
+
+    /**
+     * Uppercases words with configurable delimiters between words.
+     *
+     * Takes a string and capitalizes all of the words, like PHP's built-in
+     * ucwords function. This extends that behavior, however, by allowing the
+     * word delimiters to be configured, rather than only separating on
+     * whitespace.
+     *
+     * Here is an example:
+     * 
+     * capitalize($string);
+     * // Top-O-The-Morning To All_of_you!
+     *
+     * echo $inflector->capitalize($string, '-_ ');
+     * // Top-O-The-Morning To All_Of_You!
+     * ?>
+     * 
+     *
+     * @param string $string     The string to operate on.
+     * @param string $delimiters A list of word separators.
+     *
+     * @return string The string with all delimiter-separated words capitalized.
+     */
+    public function capitalize(string $string, string $delimiters = " \n\t\r\0\x0B-"): string
+    {
+        return ucwords($string, $delimiters);
+    }
+
+    /**
+     * Checks if the given string seems like it has utf8 characters in it.
+     *
+     * @param string $string The string to check for utf8 characters in.
+     */
+    public function seemsUtf8(string $string): bool
+    {
+        for ($i = 0; $i < strlen($string); $i++) {
+            if (ord($string[$i]) < 0x80) {
+                continue; // 0bbbbbbb
+            }
+
+            if ((ord($string[$i]) & 0xE0) === 0xC0) {
+                $n = 1; // 110bbbbb
+            } elseif ((ord($string[$i]) & 0xF0) === 0xE0) {
+                $n = 2; // 1110bbbb
+            } elseif ((ord($string[$i]) & 0xF8) === 0xF0) {
+                $n = 3; // 11110bbb
+            } elseif ((ord($string[$i]) & 0xFC) === 0xF8) {
+                $n = 4; // 111110bb
+            } elseif ((ord($string[$i]) & 0xFE) === 0xFC) {
+                $n = 5; // 1111110b
+            } else {
+                return false; // Does not match any model
+            }
+
+            for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
+                if (++$i === strlen($string) || ((ord($string[$i]) & 0xC0) !== 0x80)) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Remove any illegal characters, accents, etc.
+     *
+     * @param  string $string String to unaccent
+     *
+     * @return string Unaccented string
+     */
+    public function unaccent(string $string): string
+    {
+        if (preg_match('/[\x80-\xff]/', $string) === false) {
+            return $string;
+        }
+
+        if ($this->seemsUtf8($string)) {
+            $string = strtr($string, self::ACCENTED_CHARACTERS);
+        } else {
+            $characters = [];
+
+            // Assume ISO-8859-1 if not UTF-8
+            $characters['in'] =
+                  chr(128)
+                . chr(131)
+                . chr(138)
+                . chr(142)
+                . chr(154)
+                . chr(158)
+                . chr(159)
+                . chr(162)
+                . chr(165)
+                . chr(181)
+                . chr(192)
+                . chr(193)
+                . chr(194)
+                . chr(195)
+                . chr(196)
+                . chr(197)
+                . chr(199)
+                . chr(200)
+                . chr(201)
+                . chr(202)
+                . chr(203)
+                . chr(204)
+                . chr(205)
+                . chr(206)
+                . chr(207)
+                . chr(209)
+                . chr(210)
+                . chr(211)
+                . chr(212)
+                . chr(213)
+                . chr(214)
+                . chr(216)
+                . chr(217)
+                . chr(218)
+                . chr(219)
+                . chr(220)
+                . chr(221)
+                . chr(224)
+                . chr(225)
+                . chr(226)
+                . chr(227)
+                . chr(228)
+                . chr(229)
+                . chr(231)
+                . chr(232)
+                . chr(233)
+                . chr(234)
+                . chr(235)
+                . chr(236)
+                . chr(237)
+                . chr(238)
+                . chr(239)
+                . chr(241)
+                . chr(242)
+                . chr(243)
+                . chr(244)
+                . chr(245)
+                . chr(246)
+                . chr(248)
+                . chr(249)
+                . chr(250)
+                . chr(251)
+                . chr(252)
+                . chr(253)
+                . chr(255);
+
+            $characters['out'] = 'EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy';
+
+            $string = strtr($string, $characters['in'], $characters['out']);
+
+            $doubleChars = [];
+
+            $doubleChars['in'] = [
+                chr(140),
+                chr(156),
+                chr(198),
+                chr(208),
+                chr(222),
+                chr(223),
+                chr(230),
+                chr(240),
+                chr(254),
+            ];
+
+            $doubleChars['out'] = ['OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'];
+
+            $string = str_replace($doubleChars['in'], $doubleChars['out'], $string);
+        }
+
+        return $string;
+    }
+
+    /**
+     * Convert any passed string to a url friendly string.
+     * Converts 'My first blog post' to 'my-first-blog-post'
+     *
+     * @param  string $string String to urlize.
+     *
+     * @return string Urlized string.
+     */
+    public function urlize(string $string): string
+    {
+        // Remove all non url friendly characters with the unaccent function
+        $unaccented = $this->unaccent($string);
+
+        if (function_exists('mb_strtolower')) {
+            $lowered = mb_strtolower($unaccented);
+        } else {
+            $lowered = strtolower($unaccented);
+        }
+
+        $replacements = [
+            '/\W/' => ' ',
+            '/([A-Z]+)([A-Z][a-z])/' => '\1_\2',
+            '/([a-z\d])([A-Z])/' => '\1_\2',
+            '/[^A-Z^a-z^0-9^\/]+/' => '-',
+        ];
+
+        $urlized = $lowered;
+
+        foreach ($replacements as $pattern => $replacement) {
+            $replaced = preg_replace($pattern, $replacement, $urlized);
+
+            if ($replaced === null) {
+                throw new RuntimeException(sprintf(
+                    'preg_replace returned null for value "%s"',
+                    $urlized
+                ));
+            }
+
+            $urlized = $replaced;
+        }
+
+        return trim($urlized, '-');
+    }
+
+    /**
+     * Returns a word in singular form.
+     *
+     * @param string $word The word in plural form.
+     *
+     * @return string The word in singular form.
+     */
+    public function singularize(string $word): string
+    {
+        return $this->singularizer->inflect($word);
+    }
+
+    /**
+     * Returns a word in plural form.
+     *
+     * @param string $word The word in singular form.
+     *
+     * @return string The word in plural form.
+     */
+    public function pluralize(string $word): string
+    {
+        return $this->pluralizer->inflect($word);
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/InflectorFactory.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/InflectorFactory.php
new file mode 100644
index 0000000..a0740a7
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/InflectorFactory.php
@@ -0,0 +1,52 @@
+getFlippedSubstitutions()
+        );
+    }
+
+    public static function getPluralRuleset(): Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getPlural()),
+            new Patterns(...Uninflected::getPlural()),
+            new Substitutions(...Inflectible::getIrregular())
+        );
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Uninflected.php
new file mode 100644
index 0000000..02257de
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Uninflected.php
@@ -0,0 +1,189 @@
+getFlippedSubstitutions()
+        );
+    }
+
+    public static function getPluralRuleset(): Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getPlural()),
+            new Patterns(...Uninflected::getPlural()),
+            new Substitutions(...Inflectible::getIrregular())
+        );
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Uninflected.php
new file mode 100644
index 0000000..9747f91
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Uninflected.php
@@ -0,0 +1,28 @@
+getFlippedSubstitutions()
+        );
+    }
+
+    public static function getPluralRuleset(): Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getPlural()),
+            new Patterns(...Uninflected::getPlural()),
+            new Substitutions(...Inflectible::getIrregular())
+        );
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Uninflected.php
new file mode 100644
index 0000000..5d8d3b3
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Uninflected.php
@@ -0,0 +1,30 @@
+pattern = $pattern;
+
+        if (isset($this->pattern[0]) && $this->pattern[0] === '/') {
+            $this->regex = $this->pattern;
+        } else {
+            $this->regex = '/' . $this->pattern . '/i';
+        }
+    }
+
+    public function getPattern(): string
+    {
+        return $this->pattern;
+    }
+
+    public function getRegex(): string
+    {
+        return $this->regex;
+    }
+
+    public function matches(string $word): bool
+    {
+        return preg_match($this->getRegex(), $word) === 1;
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Patterns.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Patterns.php
new file mode 100644
index 0000000..e8d45cb
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Patterns.php
@@ -0,0 +1,34 @@
+patterns = $patterns;
+
+        $patterns = array_map(static function (Pattern $pattern): string {
+            return $pattern->getPattern();
+        }, $this->patterns);
+
+        $this->regex = '/^(?:' . implode('|', $patterns) . ')$/i';
+    }
+
+    public function matches(string $word): bool
+    {
+        return preg_match($this->regex, $word, $regs) === 1;
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Inflectible.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Inflectible.php
new file mode 100644
index 0000000..0d41fe7
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Inflectible.php
@@ -0,0 +1,98 @@
+getFlippedSubstitutions()
+        );
+    }
+
+    public static function getPluralRuleset(): Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getPlural()),
+            new Patterns(...Uninflected::getPlural()),
+            new Substitutions(...Inflectible::getIrregular())
+        );
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Uninflected.php
new file mode 100644
index 0000000..b8e988f
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Uninflected.php
@@ -0,0 +1,32 @@
+regular     = $regular;
+        $this->uninflected = $uninflected;
+        $this->irregular   = $irregular;
+    }
+
+    public function getRegular(): Transformations
+    {
+        return $this->regular;
+    }
+
+    public function getUninflected(): Patterns
+    {
+        return $this->uninflected;
+    }
+
+    public function getIrregular(): Substitutions
+    {
+        return $this->irregular;
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Inflectible.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Inflectible.php
new file mode 100644
index 0000000..9129460
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Inflectible.php
@@ -0,0 +1,47 @@
+getFlippedSubstitutions()
+        );
+    }
+
+    public static function getPluralRuleset(): Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getPlural()),
+            new Patterns(...Uninflected::getPlural()),
+            new Substitutions(...Inflectible::getIrregular())
+        );
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Uninflected.php
new file mode 100644
index 0000000..c26ebe9
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Uninflected.php
@@ -0,0 +1,30 @@
+from = $from;
+        $this->to   = $to;
+    }
+
+    public function getFrom(): Word
+    {
+        return $this->from;
+    }
+
+    public function getTo(): Word
+    {
+        return $this->to;
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Substitutions.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Substitutions.php
new file mode 100644
index 0000000..17ee296
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Substitutions.php
@@ -0,0 +1,57 @@
+substitutions[$substitution->getFrom()->getWord()] = $substitution;
+        }
+    }
+
+    public function getFlippedSubstitutions(): Substitutions
+    {
+        $substitutions = [];
+
+        foreach ($this->substitutions as $substitution) {
+            $substitutions[] = new Substitution(
+                $substitution->getTo(),
+                $substitution->getFrom()
+            );
+        }
+
+        return new Substitutions(...$substitutions);
+    }
+
+    public function inflect(string $word): string
+    {
+        $lowerWord = strtolower($word);
+
+        if (isset($this->substitutions[$lowerWord])) {
+            $firstLetterUppercase = $lowerWord[0] !== $word[0];
+
+            $toWord = $this->substitutions[$lowerWord]->getTo()->getWord();
+
+            if ($firstLetterUppercase) {
+                return strtoupper($toWord[0]) . substr($toWord, 1);
+            }
+
+            return $toWord;
+        }
+
+        return $word;
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformation.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformation.php
new file mode 100644
index 0000000..30dcd59
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformation.php
@@ -0,0 +1,39 @@
+pattern     = $pattern;
+        $this->replacement = $replacement;
+    }
+
+    public function getPattern(): Pattern
+    {
+        return $this->pattern;
+    }
+
+    public function getReplacement(): string
+    {
+        return $this->replacement;
+    }
+
+    public function inflect(string $word): string
+    {
+        return (string) preg_replace($this->pattern->getRegex(), $this->replacement, $word);
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformations.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformations.php
new file mode 100644
index 0000000..b6a48fa
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformations.php
@@ -0,0 +1,29 @@
+transformations = $transformations;
+    }
+
+    public function inflect(string $word): string
+    {
+        foreach ($this->transformations as $transformation) {
+            if ($transformation->getPattern()->matches($word)) {
+                return $transformation->inflect($word);
+            }
+        }
+
+        return $word;
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Inflectible.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Inflectible.php
new file mode 100644
index 0000000..a2bda0d
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Inflectible.php
@@ -0,0 +1,34 @@
+getFlippedSubstitutions()
+        );
+    }
+
+    public static function getPluralRuleset(): Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getPlural()),
+            new Patterns(...Uninflected::getPlural()),
+            new Substitutions(...Inflectible::getIrregular())
+        );
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Uninflected.php
new file mode 100644
index 0000000..ec1c37d
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Uninflected.php
@@ -0,0 +1,30 @@
+word = $word;
+    }
+
+    public function getWord(): string
+    {
+        return $this->word;
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/RulesetInflector.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/RulesetInflector.php
new file mode 100644
index 0000000..12b2ed5
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/RulesetInflector.php
@@ -0,0 +1,56 @@
+rulesets = array_merge([$ruleset], $rulesets);
+    }
+
+    public function inflect(string $word): string
+    {
+        if ($word === '') {
+            return '';
+        }
+
+        foreach ($this->rulesets as $ruleset) {
+            if ($ruleset->getUninflected()->matches($word)) {
+                return $word;
+            }
+
+            $inflected = $ruleset->getIrregular()->inflect($word);
+
+            if ($inflected !== $word) {
+                return $inflected;
+            }
+
+            $inflected = $ruleset->getRegular()->inflect($word);
+
+            if ($inflected !== $word) {
+                return $inflected;
+            }
+        }
+
+        return $word;
+    }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/WordInflector.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/WordInflector.php
new file mode 100644
index 0000000..b88b1d6
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/WordInflector.php
@@ -0,0 +1,10 @@
+
+
+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/graham-campbell/result-type/composer.json b/vendor/graham-campbell/result-type/composer.json
new file mode 100644
index 0000000..b1ba15a
--- /dev/null
+++ b/vendor/graham-campbell/result-type/composer.json
@@ -0,0 +1,33 @@
+{
+    "name": "graham-campbell/result-type",
+    "description": "An Implementation Of The Result Type",
+    "keywords": ["result", "result-type", "Result", "Result Type", "Result-Type", "Graham Campbell", "GrahamCampbell"],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Graham Campbell",
+            "email": "hello@gjcampbell.co.uk",
+            "homepage": "https://github.com/GrahamCampbell"
+        }
+    ],
+    "require": {
+        "php": "^7.2.5 || ^8.0",
+        "phpoption/phpoption": "^1.9.2"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+    },
+    "autoload": {
+        "psr-4": {
+            "GrahamCampbell\\ResultType\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "GrahamCampbell\\Tests\\ResultType\\": "tests/"
+        }
+    },
+    "config": {
+        "preferred-install": "dist"
+    }
+}
diff --git a/vendor/graham-campbell/result-type/src/Error.php b/vendor/graham-campbell/result-type/src/Error.php
new file mode 100644
index 0000000..2c37c3e
--- /dev/null
+++ b/vendor/graham-campbell/result-type/src/Error.php
@@ -0,0 +1,121 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace GrahamCampbell\ResultType;
+
+use PhpOption\None;
+use PhpOption\Some;
+
+/**
+ * @template T
+ * @template E
+ *
+ * @extends \GrahamCampbell\ResultType\Result
+ */
+final class Error extends Result
+{
+    /**
+     * @var E
+     */
+    private $value;
+
+    /**
+     * Internal constructor for an error value.
+     *
+     * @param E $value
+     *
+     * @return void
+     */
+    private function __construct($value)
+    {
+        $this->value = $value;
+    }
+
+    /**
+     * Create a new error value.
+     *
+     * @template F
+     *
+     * @param F $value
+     *
+     * @return \GrahamCampbell\ResultType\Result
+     */
+    public static function create($value)
+    {
+        return new self($value);
+    }
+
+    /**
+     * Get the success option value.
+     *
+     * @return \PhpOption\Option
+     */
+    public function success()
+    {
+        return None::create();
+    }
+
+    /**
+     * Map over the success value.
+     *
+     * @template S
+     *
+     * @param callable(T):S $f
+     *
+     * @return \GrahamCampbell\ResultType\Result
+     */
+    public function map(callable $f)
+    {
+        return self::create($this->value);
+    }
+
+    /**
+     * Flat map over the success value.
+     *
+     * @template S
+     * @template F
+     *
+     * @param callable(T):\GrahamCampbell\ResultType\Result $f
+     *
+     * @return \GrahamCampbell\ResultType\Result
+     */
+    public function flatMap(callable $f)
+    {
+        /** @var \GrahamCampbell\ResultType\Result */
+        return self::create($this->value);
+    }
+
+    /**
+     * Get the error option value.
+     *
+     * @return \PhpOption\Option
+     */
+    public function error()
+    {
+        return Some::create($this->value);
+    }
+
+    /**
+     * Map over the error value.
+     *
+     * @template F
+     *
+     * @param callable(E):F $f
+     *
+     * @return \GrahamCampbell\ResultType\Result
+     */
+    public function mapError(callable $f)
+    {
+        return self::create($f($this->value));
+    }
+}
diff --git a/vendor/graham-campbell/result-type/src/Result.php b/vendor/graham-campbell/result-type/src/Result.php
new file mode 100644
index 0000000..8c67bcd
--- /dev/null
+++ b/vendor/graham-campbell/result-type/src/Result.php
@@ -0,0 +1,69 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace GrahamCampbell\ResultType;
+
+/**
+ * @template T
+ * @template E
+ */
+abstract class Result
+{
+    /**
+     * Get the success option value.
+     *
+     * @return \PhpOption\Option
+     */
+    abstract public function success();
+
+    /**
+     * Map over the success value.
+     *
+     * @template S
+     *
+     * @param callable(T):S $f
+     *
+     * @return \GrahamCampbell\ResultType\Result
+     */
+    abstract public function map(callable $f);
+
+    /**
+     * Flat map over the success value.
+     *
+     * @template S
+     * @template F
+     *
+     * @param callable(T):\GrahamCampbell\ResultType\Result $f
+     *
+     * @return \GrahamCampbell\ResultType\Result
+     */
+    abstract public function flatMap(callable $f);
+
+    /**
+     * Get the error option value.
+     *
+     * @return \PhpOption\Option
+     */
+    abstract public function error();
+
+    /**
+     * Map over the error value.
+     *
+     * @template F
+     *
+     * @param callable(E):F $f
+     *
+     * @return \GrahamCampbell\ResultType\Result
+     */
+    abstract public function mapError(callable $f);
+}
diff --git a/vendor/graham-campbell/result-type/src/Success.php b/vendor/graham-campbell/result-type/src/Success.php
new file mode 100644
index 0000000..27cd85e
--- /dev/null
+++ b/vendor/graham-campbell/result-type/src/Success.php
@@ -0,0 +1,120 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace GrahamCampbell\ResultType;
+
+use PhpOption\None;
+use PhpOption\Some;
+
+/**
+ * @template T
+ * @template E
+ *
+ * @extends \GrahamCampbell\ResultType\Result
+ */
+final class Success extends Result
+{
+    /**
+     * @var T
+     */
+    private $value;
+
+    /**
+     * Internal constructor for a success value.
+     *
+     * @param T $value
+     *
+     * @return void
+     */
+    private function __construct($value)
+    {
+        $this->value = $value;
+    }
+
+    /**
+     * Create a new error value.
+     *
+     * @template S
+     *
+     * @param S $value
+     *
+     * @return \GrahamCampbell\ResultType\Result
+     */
+    public static function create($value)
+    {
+        return new self($value);
+    }
+
+    /**
+     * Get the success option value.
+     *
+     * @return \PhpOption\Option
+     */
+    public function success()
+    {
+        return Some::create($this->value);
+    }
+
+    /**
+     * Map over the success value.
+     *
+     * @template S
+     *
+     * @param callable(T):S $f
+     *
+     * @return \GrahamCampbell\ResultType\Result
+     */
+    public function map(callable $f)
+    {
+        return self::create($f($this->value));
+    }
+
+    /**
+     * Flat map over the success value.
+     *
+     * @template S
+     * @template F
+     *
+     * @param callable(T):\GrahamCampbell\ResultType\Result $f
+     *
+     * @return \GrahamCampbell\ResultType\Result
+     */
+    public function flatMap(callable $f)
+    {
+        return $f($this->value);
+    }
+
+    /**
+     * Get the error option value.
+     *
+     * @return \PhpOption\Option
+     */
+    public function error()
+    {
+        return None::create();
+    }
+
+    /**
+     * Map over the error value.
+     *
+     * @template F
+     *
+     * @param callable(E):F $f
+     *
+     * @return \GrahamCampbell\ResultType\Result
+     */
+    public function mapError(callable $f)
+    {
+        return self::create($this->value);
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/CHANGELOG.md b/vendor/guzzlehttp/guzzle/CHANGELOG.md
new file mode 100644
index 0000000..e0b6216
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/CHANGELOG.md
@@ -0,0 +1,1664 @@
+# Change Log
+
+Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version.
+
+
+## 7.9.2 - 2024-07-24
+
+### Fixed
+
+- Adjusted handler selection to use cURL if its version is 7.21.2 or higher, rather than 7.34.0
+
+
+## 7.9.1 - 2024-07-19
+
+### Fixed
+
+- Fix TLS 1.3 check for HTTP/2 requests
+
+
+## 7.9.0 - 2024-07-18
+
+### Changed
+
+- Improve protocol version checks to provide feedback around unsupported protocols
+- Only select the cURL handler by default if 7.34.0 or higher is linked
+- Improved `CurlMultiHandler` to avoid busy wait if possible
+- Dropped support for EOL `guzzlehttp/psr7` v1
+- Improved URI user info redaction in errors
+
+## 7.8.2 - 2024-07-18
+
+### Added
+
+- Support for PHP 8.4
+
+
+## 7.8.1 - 2023-12-03
+
+### Changed
+
+- Updated links in docs to their canonical versions
+- Replaced `call_user_func*` with native calls
+
+
+## 7.8.0 - 2023-08-27
+
+### Added
+
+- Support for PHP 8.3
+- Added automatic closing of handles on `CurlFactory` object destruction
+
+
+## 7.7.1 - 2023-08-27
+
+### Changed
+
+- Remove the need for `AllowDynamicProperties` in `CurlMultiHandler`
+
+
+## 7.7.0 - 2023-05-21
+
+### Added
+
+- Support `guzzlehttp/promises` v2
+
+
+## 7.6.1 - 2023-05-15
+
+### Fixed
+
+- Fix `SetCookie::fromString` MaxAge deprecation warning and skip invalid MaxAge values
+
+
+## 7.6.0 - 2023-05-14
+
+### Added
+
+- Support for setting the minimum TLS version in a unified way
+- Apply on request the version set in options parameters
+
+
+## 7.5.2 - 2023-05-14
+
+### Fixed
+
+- Fixed set cookie constructor validation
+- Fixed handling of files with `'0'` body
+
+### Changed
+
+- Corrected docs and default connect timeout value to 300 seconds
+
+
+## 7.5.1 - 2023-04-17
+
+### Fixed
+
+- Fixed `NO_PROXY` settings so that setting the `proxy` option to `no` overrides the env variable
+
+### Changed
+
+- Adjusted `guzzlehttp/psr7` version constraint to `^1.9.1 || ^2.4.5`
+
+
+## 7.5.0 - 2022-08-28
+
+### Added
+
+- Support PHP 8.2
+- Add request to delay closure params
+
+
+## 7.4.5 - 2022-06-20
+
+### Fixed
+
+* Fix change in port should be considered a change in origin
+* Fix `CURLOPT_HTTPAUTH` option not cleared on change of origin
+
+
+## 7.4.4 - 2022-06-09
+
+### Fixed
+
+* Fix failure to strip Authorization header on HTTP downgrade
+* Fix failure to strip the Cookie header on change in host or HTTP downgrade
+
+
+## 7.4.3 - 2022-05-25
+
+### Fixed
+
+* Fix cross-domain cookie leakage
+
+
+## 7.4.2 - 2022-03-20
+
+### Fixed
+
+- Remove curl auth on cross-domain redirects to align with the Authorization HTTP header
+- Reject non-HTTP schemes in StreamHandler
+- Set a default ssl.peer_name context in StreamHandler to allow `force_ip_resolve`
+
+
+## 7.4.1 - 2021-12-06
+
+### Changed
+
+- Replaced implicit URI to string coercion [#2946](https://github.com/guzzle/guzzle/pull/2946)
+- Allow `symfony/deprecation-contracts` version 3 [#2961](https://github.com/guzzle/guzzle/pull/2961)
+
+### Fixed
+
+- Only close curl handle if it's done [#2950](https://github.com/guzzle/guzzle/pull/2950)
+
+
+## 7.4.0 - 2021-10-18
+
+### Added
+
+- Support PHP 8.1 [#2929](https://github.com/guzzle/guzzle/pull/2929), [#2939](https://github.com/guzzle/guzzle/pull/2939)
+- Support `psr/log` version 2 and 3 [#2943](https://github.com/guzzle/guzzle/pull/2943)
+
+### Fixed
+
+- Make sure we always call `restore_error_handler()` [#2915](https://github.com/guzzle/guzzle/pull/2915)
+- Fix progress parameter type compatibility between the cURL and stream handlers [#2936](https://github.com/guzzle/guzzle/pull/2936)
+- Throw `InvalidArgumentException` when an incorrect `headers` array is provided [#2916](https://github.com/guzzle/guzzle/pull/2916), [#2942](https://github.com/guzzle/guzzle/pull/2942)
+
+### Changed
+
+- Be more strict with types [#2914](https://github.com/guzzle/guzzle/pull/2914), [#2917](https://github.com/guzzle/guzzle/pull/2917), [#2919](https://github.com/guzzle/guzzle/pull/2919), [#2945](https://github.com/guzzle/guzzle/pull/2945)
+
+
+## 7.3.0 - 2021-03-23
+
+### Added
+
+- Support for DER and P12 certificates [#2413](https://github.com/guzzle/guzzle/pull/2413)
+- Support the cURL (http://) scheme for StreamHandler proxies [#2850](https://github.com/guzzle/guzzle/pull/2850)
+- Support for `guzzlehttp/psr7:^2.0` [#2878](https://github.com/guzzle/guzzle/pull/2878)
+
+### Fixed
+
+- Handle exceptions on invalid header consistently between PHP versions and handlers [#2872](https://github.com/guzzle/guzzle/pull/2872)
+
+
+## 7.2.0 - 2020-10-10
+
+### Added
+
+- Support for PHP 8 [#2712](https://github.com/guzzle/guzzle/pull/2712), [#2715](https://github.com/guzzle/guzzle/pull/2715), [#2789](https://github.com/guzzle/guzzle/pull/2789)
+- Support passing a body summarizer to the http errors middleware [#2795](https://github.com/guzzle/guzzle/pull/2795)
+
+### Fixed
+
+- Handle exceptions during response creation [#2591](https://github.com/guzzle/guzzle/pull/2591)
+- Fix CURLOPT_ENCODING not to be overwritten [#2595](https://github.com/guzzle/guzzle/pull/2595)
+- Make sure the Request always has a body object [#2804](https://github.com/guzzle/guzzle/pull/2804)
+
+### Changed
+
+- The `TooManyRedirectsException` has a response [#2660](https://github.com/guzzle/guzzle/pull/2660)
+- Avoid "functions" from dependencies [#2712](https://github.com/guzzle/guzzle/pull/2712)
+
+### Deprecated
+
+- Using environment variable GUZZLE_CURL_SELECT_TIMEOUT [#2786](https://github.com/guzzle/guzzle/pull/2786)
+
+
+## 7.1.1 - 2020-09-30
+
+### Fixed
+
+- Incorrect EOF detection for response body streams on Windows.
+
+### Changed
+
+- We dont connect curl `sink` on HEAD requests.
+- Removed some PHP 5 workarounds
+
+
+## 7.1.0 - 2020-09-22
+
+### Added
+
+- `GuzzleHttp\MessageFormatterInterface`
+
+### Fixed
+
+- Fixed issue that caused cookies with no value not to be stored.
+- On redirects, we allow all safe methods like GET, HEAD and OPTIONS.
+- Fixed logging on empty responses.
+- Make sure MessageFormatter::format returns string
+
+### Deprecated
+
+- All functions in `GuzzleHttp` has been deprecated. Use static methods on `Utils` instead.
+- `ClientInterface::getConfig()`
+- `Client::getConfig()`
+- `Client::__call()`
+- `Utils::defaultCaBundle()`
+- `CurlFactory::LOW_CURL_VERSION_NUMBER`
+
+
+## 7.0.1 - 2020-06-27
+
+* Fix multiply defined functions fatal error [#2699](https://github.com/guzzle/guzzle/pull/2699)
+
+
+## 7.0.0 - 2020-06-27
+
+No changes since 7.0.0-rc1.
+
+
+## 7.0.0-rc1 - 2020-06-15
+
+### Changed
+
+* Use error level for logging errors in Middleware [#2629](https://github.com/guzzle/guzzle/pull/2629)
+* Disabled IDN support by default and require ext-intl to use it [#2675](https://github.com/guzzle/guzzle/pull/2675)
+
+
+## 7.0.0-beta2 - 2020-05-25
+
+### Added
+
+* Using `Utils` class instead of functions in the `GuzzleHttp` namespace. [#2546](https://github.com/guzzle/guzzle/pull/2546)
+* `ClientInterface::MAJOR_VERSION` [#2583](https://github.com/guzzle/guzzle/pull/2583)
+
+### Changed
+
+* Avoid the `getenv` function when unsafe [#2531](https://github.com/guzzle/guzzle/pull/2531)
+* Added real client methods [#2529](https://github.com/guzzle/guzzle/pull/2529)
+* Avoid functions due to global install conflicts [#2546](https://github.com/guzzle/guzzle/pull/2546)
+* Use Symfony intl-idn polyfill [#2550](https://github.com/guzzle/guzzle/pull/2550)
+* Adding methods for HTTP verbs like `Client::get()`, `Client::head()`, `Client::patch()` etc [#2529](https://github.com/guzzle/guzzle/pull/2529)
+* `ConnectException` extends `TransferException` [#2541](https://github.com/guzzle/guzzle/pull/2541)
+* Updated the default User Agent to "GuzzleHttp/7" [#2654](https://github.com/guzzle/guzzle/pull/2654)
+
+### Fixed
+
+* Various intl icu issues [#2626](https://github.com/guzzle/guzzle/pull/2626)
+
+### Removed
+
+* Pool option `pool_size` [#2528](https://github.com/guzzle/guzzle/pull/2528)
+
+
+## 7.0.0-beta1 - 2019-12-30
+
+The diff might look very big but 95% of Guzzle users will be able to upgrade without modification.
+Please see [the upgrade document](UPGRADING.md) that describes all BC breaking changes.
+
+### Added
+
+* Implement PSR-18 and dropped PHP 5 support [#2421](https://github.com/guzzle/guzzle/pull/2421) [#2474](https://github.com/guzzle/guzzle/pull/2474)
+* PHP 7 types [#2442](https://github.com/guzzle/guzzle/pull/2442) [#2449](https://github.com/guzzle/guzzle/pull/2449) [#2466](https://github.com/guzzle/guzzle/pull/2466) [#2497](https://github.com/guzzle/guzzle/pull/2497) [#2499](https://github.com/guzzle/guzzle/pull/2499)
+* IDN support for redirects [2424](https://github.com/guzzle/guzzle/pull/2424)
+
+### Changed
+
+* Dont allow passing null as third argument to `BadResponseException::__construct()` [#2427](https://github.com/guzzle/guzzle/pull/2427)
+* Use SAPI constant instead of method call [#2450](https://github.com/guzzle/guzzle/pull/2450)
+* Use native function invocation [#2444](https://github.com/guzzle/guzzle/pull/2444)
+* Better defaults for PHP installations with old ICU lib [2454](https://github.com/guzzle/guzzle/pull/2454)
+* Added visibility to all constants [#2462](https://github.com/guzzle/guzzle/pull/2462)
+* Dont allow passing `null` as URI to `Client::request()` and `Client::requestAsync()` [#2461](https://github.com/guzzle/guzzle/pull/2461)
+* Widen the exception argument to throwable [#2495](https://github.com/guzzle/guzzle/pull/2495)
+
+### Fixed
+
+* Logging when Promise rejected with a string [#2311](https://github.com/guzzle/guzzle/pull/2311)
+
+### Removed
+
+* Class `SeekException` [#2162](https://github.com/guzzle/guzzle/pull/2162)
+* `RequestException::getResponseBodySummary()` [#2425](https://github.com/guzzle/guzzle/pull/2425)
+* `CookieJar::getCookieValue()` [#2433](https://github.com/guzzle/guzzle/pull/2433)
+* `uri_template()` and `UriTemplate` [#2440](https://github.com/guzzle/guzzle/pull/2440)
+* Request options `save_to` and `exceptions` [#2464](https://github.com/guzzle/guzzle/pull/2464)
+
+
+## 6.5.2 - 2019-12-23
+
+* idn_to_ascii() fix for old PHP versions [#2489](https://github.com/guzzle/guzzle/pull/2489)
+
+
+## 6.5.1 - 2019-12-21
+
+* Better defaults for PHP installations with old ICU lib [#2454](https://github.com/guzzle/guzzle/pull/2454)
+* IDN support for redirects [#2424](https://github.com/guzzle/guzzle/pull/2424)
+
+
+## 6.5.0 - 2019-12-07
+
+* Improvement: Added support for reset internal queue in MockHandler. [#2143](https://github.com/guzzle/guzzle/pull/2143)
+* Improvement: Added support to pass arbitrary options to `curl_multi_init`. [#2287](https://github.com/guzzle/guzzle/pull/2287)
+* Fix: Gracefully handle passing `null` to the `header` option. [#2132](https://github.com/guzzle/guzzle/pull/2132)
+* Fix: `RetryMiddleware` did not do exponential delay between retires due unit mismatch. [#2132](https://github.com/guzzle/guzzle/pull/2132)
+* Fix: Prevent undefined offset when using array for ssl_key options. [#2348](https://github.com/guzzle/guzzle/pull/2348)
+* Deprecated `ClientInterface::VERSION`
+
+
+## 6.4.1 - 2019-10-23
+
+* No `guzzle.phar` was created in 6.4.0 due expired API token. This release will fix that
+* Added `parent::__construct()` to `FileCookieJar` and `SessionCookieJar`
+
+
+## 6.4.0 - 2019-10-23
+
+* Improvement: Improved error messages when using curl < 7.21.2 [#2108](https://github.com/guzzle/guzzle/pull/2108)
+* Fix: Test if response is readable before returning a summary in `RequestException::getResponseBodySummary()` [#2081](https://github.com/guzzle/guzzle/pull/2081)
+* Fix: Add support for GUZZLE_CURL_SELECT_TIMEOUT environment variable [#2161](https://github.com/guzzle/guzzle/pull/2161)
+* Improvement: Added `GuzzleHttp\Exception\InvalidArgumentException` [#2163](https://github.com/guzzle/guzzle/pull/2163)
+* Improvement: Added `GuzzleHttp\_current_time()` to use `hrtime()` if that function exists. [#2242](https://github.com/guzzle/guzzle/pull/2242)
+* Improvement: Added curl's `appconnect_time` in `TransferStats` [#2284](https://github.com/guzzle/guzzle/pull/2284)
+* Improvement: Make GuzzleException extend Throwable wherever it's available [#2273](https://github.com/guzzle/guzzle/pull/2273)
+* Fix: Prevent concurrent writes to file when saving `CookieJar` [#2335](https://github.com/guzzle/guzzle/pull/2335)
+* Improvement: Update `MockHandler` so we can test transfer time [#2362](https://github.com/guzzle/guzzle/pull/2362)
+
+
+## 6.3.3 - 2018-04-22
+
+* Fix: Default headers when decode_content is specified
+
+
+## 6.3.2 - 2018-03-26
+
+* Fix: Release process
+
+
+## 6.3.1 - 2018-03-26
+
+* Bug fix: Parsing 0 epoch expiry times in cookies [#2014](https://github.com/guzzle/guzzle/pull/2014)
+* Improvement: Better ConnectException detection [#2012](https://github.com/guzzle/guzzle/pull/2012)
+* Bug fix: Malformed domain that contains a "/" [#1999](https://github.com/guzzle/guzzle/pull/1999)
+* Bug fix: Undefined offset when a cookie has no first key-value pair [#1998](https://github.com/guzzle/guzzle/pull/1998)
+* Improvement: Support PHPUnit 6 [#1953](https://github.com/guzzle/guzzle/pull/1953)
+* Bug fix: Support empty headers [#1915](https://github.com/guzzle/guzzle/pull/1915)
+* Bug fix: Ignore case during header modifications [#1916](https://github.com/guzzle/guzzle/pull/1916)
+
++ Minor code cleanups, documentation fixes and clarifications.
+
+
+## 6.3.0 - 2017-06-22
+
+* Feature: force IP resolution (ipv4 or ipv6) [#1608](https://github.com/guzzle/guzzle/pull/1608), [#1659](https://github.com/guzzle/guzzle/pull/1659)
+* Improvement: Don't include summary in exception message when body is empty [#1621](https://github.com/guzzle/guzzle/pull/1621)
+* Improvement: Handle `on_headers` option in MockHandler [#1580](https://github.com/guzzle/guzzle/pull/1580)
+* Improvement: Added SUSE Linux CA path [#1609](https://github.com/guzzle/guzzle/issues/1609)
+* Improvement: Use class reference for getting the name of the class instead of using hardcoded strings [#1641](https://github.com/guzzle/guzzle/pull/1641)
+* Feature: Added `read_timeout` option [#1611](https://github.com/guzzle/guzzle/pull/1611)
+* Bug fix: PHP 7.x fixes [#1685](https://github.com/guzzle/guzzle/pull/1685), [#1686](https://github.com/guzzle/guzzle/pull/1686), [#1811](https://github.com/guzzle/guzzle/pull/1811)
+* Deprecation: BadResponseException instantiation without a response [#1642](https://github.com/guzzle/guzzle/pull/1642)
+* Feature: Added NTLM auth [#1569](https://github.com/guzzle/guzzle/pull/1569)
+* Feature: Track redirect HTTP status codes [#1711](https://github.com/guzzle/guzzle/pull/1711)
+* Improvement: Check handler type during construction [#1745](https://github.com/guzzle/guzzle/pull/1745)
+* Improvement: Always include the Content-Length if there's a body [#1721](https://github.com/guzzle/guzzle/pull/1721)
+* Feature: Added convenience method to access a cookie by name [#1318](https://github.com/guzzle/guzzle/pull/1318)
+* Bug fix: Fill `CURLOPT_CAPATH` and `CURLOPT_CAINFO` properly [#1684](https://github.com/guzzle/guzzle/pull/1684)
+* Improvement:  	Use `\GuzzleHttp\Promise\rejection_for` function instead of object init [#1827](https://github.com/guzzle/guzzle/pull/1827)
+
++ Minor code cleanups, documentation fixes and clarifications.
+
+
+## 6.2.3 - 2017-02-28
+
+* Fix deprecations with guzzle/psr7 version 1.4
+
+
+## 6.2.2 - 2016-10-08
+
+* Allow to pass nullable Response to delay callable
+* Only add scheme when host is present
+* Fix drain case where content-length is the literal string zero
+* Obfuscate in-URL credentials in exceptions
+
+
+## 6.2.1 - 2016-07-18
+
+* Address HTTP_PROXY security vulnerability, CVE-2016-5385:
+  https://httpoxy.org/
+* Fixing timeout bug with StreamHandler:
+  https://github.com/guzzle/guzzle/pull/1488
+* Only read up to `Content-Length` in PHP StreamHandler to avoid timeouts when
+  a server does not honor `Connection: close`.
+* Ignore URI fragment when sending requests.
+
+
+## 6.2.0 - 2016-03-21
+
+* Feature: added `GuzzleHttp\json_encode` and `GuzzleHttp\json_decode`.
+  https://github.com/guzzle/guzzle/pull/1389
+* Bug fix: Fix sleep calculation when waiting for delayed requests.
+  https://github.com/guzzle/guzzle/pull/1324
+* Feature: More flexible history containers.
+  https://github.com/guzzle/guzzle/pull/1373
+* Bug fix: defer sink stream opening in StreamHandler.
+  https://github.com/guzzle/guzzle/pull/1377
+* Bug fix: do not attempt to escape cookie values.
+  https://github.com/guzzle/guzzle/pull/1406
+* Feature: report original content encoding and length on decoded responses.
+  https://github.com/guzzle/guzzle/pull/1409
+* Bug fix: rewind seekable request bodies before dispatching to cURL.
+  https://github.com/guzzle/guzzle/pull/1422
+* Bug fix: provide an empty string to `http_build_query` for HHVM workaround.
+  https://github.com/guzzle/guzzle/pull/1367
+
+
+## 6.1.1 - 2015-11-22
+
+* Bug fix: Proxy::wrapSync() now correctly proxies to the appropriate handler
+  https://github.com/guzzle/guzzle/commit/911bcbc8b434adce64e223a6d1d14e9a8f63e4e4
+* Feature: HandlerStack is now more generic.
+  https://github.com/guzzle/guzzle/commit/f2102941331cda544745eedd97fc8fd46e1ee33e
+* Bug fix: setting verify to false in the StreamHandler now disables peer
+  verification. https://github.com/guzzle/guzzle/issues/1256
+* Feature: Middleware now uses an exception factory, including more error
+  context. https://github.com/guzzle/guzzle/pull/1282
+* Feature: better support for disabled functions.
+  https://github.com/guzzle/guzzle/pull/1287
+* Bug fix: fixed regression where MockHandler was not using `sink`.
+  https://github.com/guzzle/guzzle/pull/1292
+
+
+## 6.1.0 - 2015-09-08
+
+* Feature: Added the `on_stats` request option to provide access to transfer
+  statistics for requests. https://github.com/guzzle/guzzle/pull/1202
+* Feature: Added the ability to persist session cookies in CookieJars.
+  https://github.com/guzzle/guzzle/pull/1195
+* Feature: Some compatibility updates for Google APP Engine
+  https://github.com/guzzle/guzzle/pull/1216
+* Feature: Added support for NO_PROXY to prevent the use of a proxy based on
+  a simple set of rules. https://github.com/guzzle/guzzle/pull/1197
+* Feature: Cookies can now contain square brackets.
+  https://github.com/guzzle/guzzle/pull/1237
+* Bug fix: Now correctly parsing `=` inside of quotes in Cookies.
+  https://github.com/guzzle/guzzle/pull/1232
+* Bug fix: Cusotm cURL options now correctly override curl options of the
+  same name. https://github.com/guzzle/guzzle/pull/1221
+* Bug fix: Content-Type header is now added when using an explicitly provided
+  multipart body. https://github.com/guzzle/guzzle/pull/1218
+* Bug fix: Now ignoring Set-Cookie headers that have no name.
+* Bug fix: Reason phrase is no longer cast to an int in some cases in the
+  cURL handler. https://github.com/guzzle/guzzle/pull/1187
+* Bug fix: Remove the Authorization header when redirecting if the Host
+  header changes. https://github.com/guzzle/guzzle/pull/1207
+* Bug fix: Cookie path matching fixes
+  https://github.com/guzzle/guzzle/issues/1129
+* Bug fix: Fixing the cURL `body_as_string` setting
+  https://github.com/guzzle/guzzle/pull/1201
+* Bug fix: quotes are no longer stripped when parsing cookies.
+  https://github.com/guzzle/guzzle/issues/1172
+* Bug fix: `form_params` and `query` now always uses the `&` separator.
+  https://github.com/guzzle/guzzle/pull/1163
+* Bug fix: Adding a Content-Length to PHP stream wrapper requests if not set.
+  https://github.com/guzzle/guzzle/pull/1189
+
+
+## 6.0.2 - 2015-07-04
+
+* Fixed a memory leak in the curl handlers in which references to callbacks
+  were not being removed by `curl_reset`.
+* Cookies are now extracted properly before redirects.
+* Cookies now allow more character ranges.
+* Decoded Content-Encoding responses are now modified to correctly reflect
+  their state if the encoding was automatically removed by a handler. This
+  means that the `Content-Encoding` header may be removed an the
+  `Content-Length` modified to reflect the message size after removing the
+  encoding.
+* Added a more explicit error message when trying to use `form_params` and
+  `multipart` in the same request.
+* Several fixes for HHVM support.
+* Functions are now conditionally required using an additional level of
+  indirection to help with global Composer installations.
+
+
+## 6.0.1 - 2015-05-27
+
+* Fixed a bug with serializing the `query` request option where the `&`
+  separator was missing.
+* Added a better error message for when `body` is provided as an array. Please
+  use `form_params` or `multipart` instead.
+* Various doc fixes.
+
+
+## 6.0.0 - 2015-05-26
+
+* See the UPGRADING.md document for more information.
+* Added `multipart` and `form_params` request options.
+* Added `synchronous` request option.
+* Added the `on_headers` request option.
+* Fixed `expect` handling.
+* No longer adding default middlewares in the client ctor. These need to be
+  present on the provided handler in order to work.
+* Requests are no longer initiated when sending async requests with the
+  CurlMultiHandler. This prevents unexpected recursion from requests completing
+  while ticking the cURL loop.
+* Removed the semantics of setting `default` to `true`. This is no longer
+  required now that the cURL loop is not ticked for async requests.
+* Added request and response logging middleware.
+* No longer allowing self signed certificates when using the StreamHandler.
+* Ensuring that `sink` is valid if saving to a file.
+* Request exceptions now include a "handler context" which provides handler
+  specific contextual information.
+* Added `GuzzleHttp\RequestOptions` to allow request options to be applied
+  using constants.
+* `$maxHandles` has been removed from CurlMultiHandler.
+* `MultipartPostBody` is now part of the `guzzlehttp/psr7` package.
+
+
+## 5.3.0 - 2015-05-19
+
+* Mock now supports `save_to`
+* Marked `AbstractRequestEvent::getTransaction()` as public.
+* Fixed a bug in which multiple headers using different casing would overwrite
+  previous headers in the associative array.
+* Added `Utils::getDefaultHandler()`
+* Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated.
+* URL scheme is now always lowercased.
+
+
+## 6.0.0-beta.1
+
+* Requires PHP >= 5.5
+* Updated to use PSR-7
+  * Requires immutable messages, which basically means an event based system
+    owned by a request instance is no longer possible.
+  * Utilizing the [Guzzle PSR-7 package](https://github.com/guzzle/psr7).
+  * Removed the dependency on `guzzlehttp/streams`. These stream abstractions
+    are available in the `guzzlehttp/psr7` package under the `GuzzleHttp\Psr7`
+    namespace.
+* Added middleware and handler system
+  * Replaced the Guzzle event and subscriber system with a middleware system.
+  * No longer depends on RingPHP, but rather places the HTTP handlers directly
+    in Guzzle, operating on PSR-7 messages.
+  * Retry logic is now encapsulated in `GuzzleHttp\Middleware::retry`, which
+    means the `guzzlehttp/retry-subscriber` is now obsolete.
+  * Mocking responses is now handled using `GuzzleHttp\Handler\MockHandler`.
+* Asynchronous responses
+  * No longer supports the `future` request option to send an async request.
+    Instead, use one of the `*Async` methods of a client (e.g., `requestAsync`,
+    `getAsync`, etc.).
+  * Utilizing `GuzzleHttp\Promise` instead of React's promise library to avoid
+    recursion required by chaining and forwarding react promises. See
+    https://github.com/guzzle/promises
+  * Added `requestAsync` and `sendAsync` to send request asynchronously.
+  * Added magic methods for `getAsync()`, `postAsync()`, etc. to send requests
+    asynchronously.
+* Request options
+  * POST and form updates
+    * Added the `form_fields` and `form_files` request options.
+    * Removed the `GuzzleHttp\Post` namespace.
+    * The `body` request option no longer accepts an array for POST requests.
+  * The `exceptions` request option has been deprecated in favor of the
+    `http_errors` request options.
+  * The `save_to` request option has been deprecated in favor of `sink` request
+    option.
+* Clients no longer accept an array of URI template string and variables for
+  URI variables. You will need to expand URI templates before passing them
+  into a client constructor or request method.
+* Client methods `get()`, `post()`, `put()`, `patch()`, `options()`, etc. are
+  now magic methods that will send synchronous requests.
+* Replaced `Utils.php` with plain functions in `functions.php`.
+* Removed `GuzzleHttp\Collection`.
+* Removed `GuzzleHttp\BatchResults`. Batched pool results are now returned as
+  an array.
+* Removed `GuzzleHttp\Query`. Query string handling is now handled using an
+  associative array passed into the `query` request option. The query string
+  is serialized using PHP's `http_build_query`. If you need more control, you
+  can pass the query string in as a string.
+* `GuzzleHttp\QueryParser` has been replaced with the
+  `GuzzleHttp\Psr7\parse_query`.
+
+
+## 5.2.0 - 2015-01-27
+
+* Added `AppliesHeadersInterface` to make applying headers to a request based
+  on the body more generic and not specific to `PostBodyInterface`.
+* Reduced the number of stack frames needed to send requests.
+* Nested futures are now resolved in the client rather than the RequestFsm
+* Finishing state transitions is now handled in the RequestFsm rather than the
+  RingBridge.
+* Added a guard in the Pool class to not use recursion for request retries.
+
+
+## 5.1.0 - 2014-12-19
+
+* Pool class no longer uses recursion when a request is intercepted.
+* The size of a Pool can now be dynamically adjusted using a callback.
+  See https://github.com/guzzle/guzzle/pull/943.
+* Setting a request option to `null` when creating a request with a client will
+  ensure that the option is not set. This allows you to overwrite default
+  request options on a per-request basis.
+  See https://github.com/guzzle/guzzle/pull/937.
+* Added the ability to limit which protocols are allowed for redirects by
+  specifying a `protocols` array in the `allow_redirects` request option.
+* Nested futures due to retries are now resolved when waiting for synchronous
+  responses. See https://github.com/guzzle/guzzle/pull/947.
+* `"0"` is now an allowed URI path. See
+  https://github.com/guzzle/guzzle/pull/935.
+* `Query` no longer typehints on the `$query` argument in the constructor,
+  allowing for strings and arrays.
+* Exceptions thrown in the `end` event are now correctly wrapped with Guzzle
+  specific exceptions if necessary.
+
+
+## 5.0.3 - 2014-11-03
+
+This change updates query strings so that they are treated as un-encoded values
+by default where the value represents an un-encoded value to send over the
+wire. A Query object then encodes the value before sending over the wire. This
+means that even value query string values (e.g., ":") are url encoded. This
+makes the Query class match PHP's http_build_query function. However, if you
+want to send requests over the wire using valid query string characters that do
+not need to be encoded, then you can provide a string to Url::setQuery() and
+pass true as the second argument to specify that the query string is a raw
+string that should not be parsed or encoded (unless a call to getQuery() is
+subsequently made, forcing the query-string to be converted into a Query
+object).
+
+
+## 5.0.2 - 2014-10-30
+
+* Added a trailing `\r\n` to multipart/form-data payloads. See
+  https://github.com/guzzle/guzzle/pull/871
+* Added a `GuzzleHttp\Pool::send()` convenience method to match the docs.
+* Status codes are now returned as integers. See
+  https://github.com/guzzle/guzzle/issues/881
+* No longer overwriting an existing `application/x-www-form-urlencoded` header
+  when sending POST requests, allowing for customized headers. See
+  https://github.com/guzzle/guzzle/issues/877
+* Improved path URL serialization.
+
+  * No longer double percent-encoding characters in the path or query string if
+    they are already encoded.
+  * Now properly encoding the supplied path to a URL object, instead of only
+    encoding ' ' and '?'.
+  * Note: This has been changed in 5.0.3 to now encode query string values by
+    default unless the `rawString` argument is provided when setting the query
+    string on a URL: Now allowing many more characters to be present in the
+    query string without being percent encoded. See
+    https://datatracker.ietf.org/doc/html/rfc3986#appendix-A
+
+
+## 5.0.1 - 2014-10-16
+
+Bugfix release.
+
+* Fixed an issue where connection errors still returned response object in
+  error and end events event though the response is unusable. This has been
+  corrected so that a response is not returned in the `getResponse` method of
+  these events if the response did not complete. https://github.com/guzzle/guzzle/issues/867
+* Fixed an issue where transfer statistics were not being populated in the
+  RingBridge. https://github.com/guzzle/guzzle/issues/866
+
+
+## 5.0.0 - 2014-10-12
+
+Adding support for non-blocking responses and some minor API cleanup.
+
+### New Features
+
+* Added support for non-blocking responses based on `guzzlehttp/guzzle-ring`.
+* Added a public API for creating a default HTTP adapter.
+* Updated the redirect plugin to be non-blocking so that redirects are sent
+  concurrently. Other plugins like this can now be updated to be non-blocking.
+* Added a "progress" event so that you can get upload and download progress
+  events.
+* Added `GuzzleHttp\Pool` which implements FutureInterface and transfers
+  requests concurrently using a capped pool size as efficiently as possible.
+* Added `hasListeners()` to EmitterInterface.
+* Removed `GuzzleHttp\ClientInterface::sendAll` and marked
+  `GuzzleHttp\Client::sendAll` as deprecated (it's still there, just not the
+  recommended way).
+
+### Breaking changes
+
+The breaking changes in this release are relatively minor. The biggest thing to
+look out for is that request and response objects no longer implement fluent
+interfaces.
+
+* Removed the fluent interfaces (i.e., `return $this`) from requests,
+  responses, `GuzzleHttp\Collection`, `GuzzleHttp\Url`,
+  `GuzzleHttp\Query`, `GuzzleHttp\Post\PostBody`, and
+  `GuzzleHttp\Cookie\SetCookie`. This blog post provides a good outline of
+  why I did this: https://ocramius.github.io/blog/fluent-interfaces-are-evil/.
+  This also makes the Guzzle message interfaces compatible with the current
+  PSR-7 message proposal.
+* Removed "functions.php", so that Guzzle is truly PSR-4 compliant. Except
+  for the HTTP request functions from function.php, these functions are now
+  implemented in `GuzzleHttp\Utils` using camelCase. `GuzzleHttp\json_decode`
+  moved to `GuzzleHttp\Utils::jsonDecode`. `GuzzleHttp\get_path` moved to
+  `GuzzleHttp\Utils::getPath`. `GuzzleHttp\set_path` moved to
+  `GuzzleHttp\Utils::setPath`. `GuzzleHttp\batch` should now be
+  `GuzzleHttp\Pool::batch`, which returns an `objectStorage`. Using functions.php
+  caused problems for many users: they aren't PSR-4 compliant, require an
+  explicit include, and needed an if-guard to ensure that the functions are not
+  declared multiple times.
+* Rewrote adapter layer.
+    * Removing all classes from `GuzzleHttp\Adapter`, these are now
+      implemented as callables that are stored in `GuzzleHttp\Ring\Client`.
+    * Removed the concept of "parallel adapters". Sending requests serially or
+      concurrently is now handled using a single adapter.
+    * Moved `GuzzleHttp\Adapter\Transaction` to `GuzzleHttp\Transaction`. The
+      Transaction object now exposes the request, response, and client as public
+      properties. The getters and setters have been removed.
+* Removed the "headers" event. This event was only useful for changing the
+  body a response once the headers of the response were known. You can implement
+  a similar behavior in a number of ways. One example might be to use a
+  FnStream that has access to the transaction being sent. For example, when the
+  first byte is written, you could check if the response headers match your
+  expectations, and if so, change the actual stream body that is being
+  written to.
+* Removed the `asArray` parameter from
+  `GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
+  value as an array, then use the newly added `getHeaderAsArray()` method of
+  `MessageInterface`. This change makes the Guzzle interfaces compatible with
+  the PSR-7 interfaces.
+* `GuzzleHttp\Message\MessageFactory` no longer allows subclasses to add
+  custom request options using double-dispatch (this was an implementation
+  detail). Instead, you should now provide an associative array to the
+  constructor which is a mapping of the request option name mapping to a
+  function that applies the option value to a request.
+* Removed the concept of "throwImmediately" from exceptions and error events.
+  This control mechanism was used to stop a transfer of concurrent requests
+  from completing. This can now be handled by throwing the exception or by
+  cancelling a pool of requests or each outstanding future request individually.
+* Updated to "GuzzleHttp\Streams" 3.0.
+    * `GuzzleHttp\Stream\StreamInterface::getContents()` no longer accepts a
+      `maxLen` parameter. This update makes the Guzzle streams project
+      compatible with the current PSR-7 proposal.
+    * `GuzzleHttp\Stream\Stream::__construct`,
+      `GuzzleHttp\Stream\Stream::factory`, and
+      `GuzzleHttp\Stream\Utils::create` no longer accept a size in the second
+      argument. They now accept an associative array of options, including the
+      "size" key and "metadata" key which can be used to provide custom metadata.
+
+
+## 4.2.2 - 2014-09-08
+
+* Fixed a memory leak in the CurlAdapter when reusing cURL handles.
+* No longer using `request_fulluri` in stream adapter proxies.
+* Relative redirects are now based on the last response, not the first response.
+
+## 4.2.1 - 2014-08-19
+
+* Ensuring that the StreamAdapter does not always add a Content-Type header
+* Adding automated github releases with a phar and zip
+
+## 4.2.0 - 2014-08-17
+
+* Now merging in default options using a case-insensitive comparison.
+  Closes https://github.com/guzzle/guzzle/issues/767
+* Added the ability to automatically decode `Content-Encoding` response bodies
+  using the `decode_content` request option. This is set to `true` by default
+  to decode the response body if it comes over the wire with a
+  `Content-Encoding`. Set this value to `false` to disable decoding the
+  response content, and pass a string to provide a request `Accept-Encoding`
+  header and turn on automatic response decoding. This feature now allows you
+  to pass an `Accept-Encoding` header in the headers of a request but still
+  disable automatic response decoding.
+  Closes https://github.com/guzzle/guzzle/issues/764
+* Added the ability to throw an exception immediately when transferring
+  requests in parallel. Closes https://github.com/guzzle/guzzle/issues/760
+* Updating guzzlehttp/streams dependency to ~2.1
+* No longer utilizing the now deprecated namespaced methods from the stream
+  package.
+
+## 4.1.8 - 2014-08-14
+
+* Fixed an issue in the CurlFactory that caused setting the `stream=false`
+  request option to throw an exception.
+  See: https://github.com/guzzle/guzzle/issues/769
+* TransactionIterator now calls rewind on the inner iterator.
+  See: https://github.com/guzzle/guzzle/pull/765
+* You can now set the `Content-Type` header to `multipart/form-data`
+  when creating POST requests to force multipart bodies.
+  See https://github.com/guzzle/guzzle/issues/768
+
+## 4.1.7 - 2014-08-07
+
+* Fixed an error in the HistoryPlugin that caused the same request and response
+  to be logged multiple times when an HTTP protocol error occurs.
+* Ensuring that cURL does not add a default Content-Type when no Content-Type
+  has been supplied by the user. This prevents the adapter layer from modifying
+  the request that is sent over the wire after any listeners may have already
+  put the request in a desired state (e.g., signed the request).
+* Throwing an exception when you attempt to send requests that have the
+  "stream" set to true in parallel using the MultiAdapter.
+* Only calling curl_multi_select when there are active cURL handles. This was
+  previously changed and caused performance problems on some systems due to PHP
+  always selecting until the maximum select timeout.
+* Fixed a bug where multipart/form-data POST fields were not correctly
+  aggregated (e.g., values with "&").
+
+## 4.1.6 - 2014-08-03
+
+* Added helper methods to make it easier to represent messages as strings,
+  including getting the start line and getting headers as a string.
+
+## 4.1.5 - 2014-08-02
+
+* Automatically retrying cURL "Connection died, retrying a fresh connect"
+  errors when possible.
+* cURL implementation cleanup
+* Allowing multiple event subscriber listeners to be registered per event by
+  passing an array of arrays of listener configuration.
+
+## 4.1.4 - 2014-07-22
+
+* Fixed a bug that caused multi-part POST requests with more than one field to
+  serialize incorrectly.
+* Paths can now be set to "0"
+* `ResponseInterface::xml` now accepts a `libxml_options` option and added a
+  missing default argument that was required when parsing XML response bodies.
+* A `save_to` stream is now created lazily, which means that files are not
+  created on disk unless a request succeeds.
+
+## 4.1.3 - 2014-07-15
+
+* Various fixes to multipart/form-data POST uploads
+* Wrapping function.php in an if-statement to ensure Guzzle can be used
+  globally and in a Composer install
+* Fixed an issue with generating and merging in events to an event array
+* POST headers are only applied before sending a request to allow you to change
+  the query aggregator used before uploading
+* Added much more robust query string parsing
+* Fixed various parsing and normalization issues with URLs
+* Fixing an issue where multi-valued headers were not being utilized correctly
+  in the StreamAdapter
+
+## 4.1.2 - 2014-06-18
+
+* Added support for sending payloads with GET requests
+
+## 4.1.1 - 2014-06-08
+
+* Fixed an issue related to using custom message factory options in subclasses
+* Fixed an issue with nested form fields in a multi-part POST
+* Fixed an issue with using the `json` request option for POST requests
+* Added `ToArrayInterface` to `GuzzleHttp\Cookie\CookieJar`
+
+## 4.1.0 - 2014-05-27
+
+* Added a `json` request option to easily serialize JSON payloads.
+* Added a `GuzzleHttp\json_decode()` wrapper to safely parse JSON.
+* Added `setPort()` and `getPort()` to `GuzzleHttp\Message\RequestInterface`.
+* Added the ability to provide an emitter to a client in the client constructor.
+* Added the ability to persist a cookie session using $_SESSION.
+* Added a trait that can be used to add event listeners to an iterator.
+* Removed request method constants from RequestInterface.
+* Fixed warning when invalid request start-lines are received.
+* Updated MessageFactory to work with custom request option methods.
+* Updated cacert bundle to latest build.
+
+4.0.2 (2014-04-16)
+------------------
+
+* Proxy requests using the StreamAdapter now properly use request_fulluri (#632)
+* Added the ability to set scalars as POST fields (#628)
+
+## 4.0.1 - 2014-04-04
+
+* The HTTP status code of a response is now set as the exception code of
+  RequestException objects.
+* 303 redirects will now correctly switch from POST to GET requests.
+* The default parallel adapter of a client now correctly uses the MultiAdapter.
+* HasDataTrait now initializes the internal data array as an empty array so
+  that the toArray() method always returns an array.
+
+## 4.0.0 - 2014-03-29
+
+* For information on changes and upgrading, see:
+  https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40
+* Added `GuzzleHttp\batch()` as a convenience function for sending requests in
+  parallel without needing to write asynchronous code.
+* Restructured how events are added to `GuzzleHttp\ClientInterface::sendAll()`.
+  You can now pass a callable or an array of associative arrays where each
+  associative array contains the "fn", "priority", and "once" keys.
+
+## 4.0.0.rc-2 - 2014-03-25
+
+* Removed `getConfig()` and `setConfig()` from clients to avoid confusion
+  around whether things like base_url, message_factory, etc. should be able to
+  be retrieved or modified.
+* Added `getDefaultOption()` and `setDefaultOption()` to ClientInterface
+* functions.php functions were renamed using snake_case to match PHP idioms
+* Added support for `HTTP_PROXY`, `HTTPS_PROXY`, and
+  `GUZZLE_CURL_SELECT_TIMEOUT` environment variables
+* Added the ability to specify custom `sendAll()` event priorities
+* Added the ability to specify custom stream context options to the stream
+  adapter.
+* Added a functions.php function for `get_path()` and `set_path()`
+* CurlAdapter and MultiAdapter now use a callable to generate curl resources
+* MockAdapter now properly reads a body and emits a `headers` event
+* Updated Url class to check if a scheme and host are set before adding ":"
+  and "//". This allows empty Url (e.g., "") to be serialized as "".
+* Parsing invalid XML no longer emits warnings
+* Curl classes now properly throw AdapterExceptions
+* Various performance optimizations
+* Streams are created with the faster `Stream\create()` function
+* Marked deprecation_proxy() as internal
+* Test server is now a collection of static methods on a class
+
+## 4.0.0-rc.1 - 2014-03-15
+
+* See https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40
+
+## 3.8.1 - 2014-01-28
+
+* Bug: Always using GET requests when redirecting from a 303 response
+* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in
+  `Guzzle\Http\ClientInterface::setSslVerification()`
+* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL
+* Bug: The body of a request can now be set to `"0"`
+* Sending PHP stream requests no longer forces `HTTP/1.0`
+* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of
+  each sub-exception
+* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than
+  clobbering everything).
+* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators)
+* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`.
+  For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`.
+* Now properly escaping the regular expression delimiter when matching Cookie domains.
+* Network access is now disabled when loading XML documents
+
+## 3.8.0 - 2013-12-05
+
+* Added the ability to define a POST name for a file
+* JSON response parsing now properly walks additionalProperties
+* cURL error code 18 is now retried automatically in the BackoffPlugin
+* Fixed a cURL error when URLs contain fragments
+* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were
+  CurlExceptions
+* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e)
+* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS`
+* Fixed a bug that was encountered when parsing empty header parameters
+* UriTemplate now has a `setRegex()` method to match the docs
+* The `debug` request parameter now checks if it is truthy rather than if it exists
+* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin
+* Added the ability to combine URLs using strict RFC 3986 compliance
+* Command objects can now return the validation errors encountered by the command
+* Various fixes to cache revalidation (#437 and 29797e5)
+* Various fixes to the AsyncPlugin
+* Cleaned up build scripts
+
+## 3.7.4 - 2013-10-02
+
+* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430)
+* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp
+  (see https://github.com/aws/aws-sdk-php/issues/147)
+* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots
+* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420)
+* Updated the bundled cacert.pem (#419)
+* OauthPlugin now supports adding authentication to headers or query string (#425)
+
+## 3.7.3 - 2013-09-08
+
+* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and
+  `CommandTransferException`.
+* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description
+* Schemas are only injected into response models when explicitly configured.
+* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of
+  an EntityBody.
+* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator.
+* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`.
+* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody()
+* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin
+* Bug fix: Visiting XML attributes first before visiting XML children when serializing requests
+* Bug fix: Properly parsing headers that contain commas contained in quotes
+* Bug fix: mimetype guessing based on a filename is now case-insensitive
+
+## 3.7.2 - 2013-08-02
+
+* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander
+  See https://github.com/guzzle/guzzle/issues/371
+* Bug fix: Cookie domains are now matched correctly according to RFC 6265
+  See https://github.com/guzzle/guzzle/issues/377
+* Bug fix: GET parameters are now used when calculating an OAuth signature
+* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted
+* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched
+* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input.
+  See https://github.com/guzzle/guzzle/issues/379
+* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See
+  https://github.com/guzzle/guzzle/pull/380
+* cURL multi cleanup and optimizations
+
+## 3.7.1 - 2013-07-05
+
+* Bug fix: Setting default options on a client now works
+* Bug fix: Setting options on HEAD requests now works. See #352
+* Bug fix: Moving stream factory before send event to before building the stream. See #353
+* Bug fix: Cookies no longer match on IP addresses per RFC 6265
+* Bug fix: Correctly parsing header parameters that are in `<>` and quotes
+* Added `cert` and `ssl_key` as request options
+* `Host` header can now diverge from the host part of a URL if the header is set manually
+* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter
+* OAuth parameters are only added via the plugin if they aren't already set
+* Exceptions are now thrown when a URL cannot be parsed
+* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails
+* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin
+
+## 3.7.0 - 2013-06-10
+
+* See UPGRADING.md for more information on how to upgrade.
+* Requests now support the ability to specify an array of $options when creating a request to more easily modify a
+  request. You can pass a 'request.options' configuration setting to a client to apply default request options to
+  every request created by a client (e.g. default query string variables, headers, curl options, etc.).
+* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`.
+  See `Guzzle\Http\StaticClient::mount`.
+* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests
+      created by a command (e.g. custom headers, query string variables, timeout settings, etc.).
+* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the
+  headers of a response
+* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key
+  (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`)
+* ServiceBuilders now support storing and retrieving arbitrary data
+* CachePlugin can now purge all resources for a given URI
+* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource
+* CachePlugin now uses the Vary header to determine if a resource is a cache hit
+* `Guzzle\Http\Message\Response` now implements `\Serializable`
+* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters
+* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable
+* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()`
+* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size
+* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message
+* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older
+  Symfony users can still use the old version of Monolog.
+* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`.
+  Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`.
+* Several performance improvements to `Guzzle\Common\Collection`
+* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
+  createRequest, head, delete, put, patch, post, options, prepareRequest
+* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
+* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
+* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
+  `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
+  resource, string, or EntityBody into the $options parameter to specify the download location of the response.
+* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
+  default `array()`
+* Added `Guzzle\Stream\StreamInterface::isRepeatable`
+* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
+  $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
+  $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`.
+* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`.
+* Removed `Guzzle\Http\ClientInterface::expandTemplate()`
+* Removed `Guzzle\Http\ClientInterface::setRequestFactory()`
+* Removed `Guzzle\Http\ClientInterface::getCurlMulti()`
+* Removed `Guzzle\Http\Message\RequestInterface::canCache`
+* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`
+* Removed `Guzzle\Http\Message\RequestInterface::isRedirect`
+* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
+* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting
+  `Guzzle\Common\Version::$emitWarnings` to true.
+* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use
+      `$request->getResponseBody()->isRepeatable()` instead.
+* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use
+  `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use
+  `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
+* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
+* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
+* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand.
+  These will work through Guzzle 4.0
+* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params].
+* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
+* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`.
+* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`.
+* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
+* Marked `Guzzle\Common\Collection::inject()` as deprecated.
+* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');`
+* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
+  CacheStorageInterface. These two objects and interface will be removed in a future version.
+* Always setting X-cache headers on cached responses
+* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
+* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
+  $request, Response $response);`
+* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
+* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
+* Added `CacheStorageInterface::purge($url)`
+* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
+  $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
+  CanCacheStrategyInterface $canCache = null)`
+* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`
+
+## 3.6.0 - 2013-05-29
+
+* ServiceDescription now implements ToArrayInterface
+* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters
+* Guzzle can now correctly parse incomplete URLs
+* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
+* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
+* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
+* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
+  HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
+  CacheControl header implementation.
+* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
+* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
+* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
+  Guzzle\Http\Curl\RequestMediator
+* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
+* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
+* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
+* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
+* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
+* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
+* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
+  directly via interfaces
+* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
+  but are a no-op until removed.
+* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a
+  `Guzzle\Service\Command\ArrayCommandInterface`.
+* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
+  on a request while the request is still being transferred
+* The ability to case-insensitively search for header values
+* Guzzle\Http\Message\Header::hasExactHeader
+* Guzzle\Http\Message\Header::raw. Use getAll()
+* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
+  instead.
+* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
+* Added the ability to cast Model objects to a string to view debug information.
+
+## 3.5.0 - 2013-05-13
+
+* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times
+* Bug: Better cleanup of one-time events across the board (when an event is meant to fire once, it will now remove
+  itself from the EventDispatcher)
+* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values
+* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too
+* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a
+  non-existent key
+* Bug: All __call() method arguments are now required (helps with mocking frameworks)
+* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference
+  to help with refcount based garbage collection of resources created by sending a request
+* Deprecating ZF1 cache and log adapters. These will be removed in the next major version.
+* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it's deprecated). Use the
+  HistoryPlugin for a history.
+* Added a `responseBody` alias for the `response_body` location
+* Refactored internals to no longer rely on Response::getRequest()
+* HistoryPlugin can now be cast to a string
+* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests
+  and responses that are sent over the wire
+* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects
+
+## 3.4.3 - 2013-04-30
+
+* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response
+* Added a check to re-extract the temp cacert bundle from the phar before sending each request
+
+## 3.4.2 - 2013-04-29
+
+* Bug fix: Stream objects now work correctly with "a" and "a+" modes
+* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present
+* Bug fix: AsyncPlugin no longer forces HEAD requests
+* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter
+* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails
+* Setting a response on a request will write to the custom request body from the response body if one is specified
+* LogPlugin now writes to php://output when STDERR is undefined
+* Added the ability to set multiple POST files for the same key in a single call
+* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default
+* Added the ability to queue CurlExceptions to the MockPlugin
+* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send)
+* Configuration loading now allows remote files
+
+## 3.4.1 - 2013-04-16
+
+* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti
+  handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost.
+* Exceptions are now properly grouped when sending requests in parallel
+* Redirects are now properly aggregated when a multi transaction fails
+* Redirects now set the response on the original object even in the event of a failure
+* Bug fix: Model names are now properly set even when using $refs
+* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax
+* Added support for oauth_callback in OAuth signatures
+* Added support for oauth_verifier in OAuth signatures
+* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection
+
+## 3.4.0 - 2013-04-11
+
+* Bug fix: URLs are now resolved correctly based on https://datatracker.ietf.org/doc/html/rfc3986#section-5.2. #289
+* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289
+* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263
+* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264.
+* Bug fix: Added `number` type to service descriptions.
+* Bug fix: empty parameters are removed from an OAuth signature
+* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header
+* Bug fix: Fixed "array to string" error when validating a union of types in a service description
+* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream
+* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin.
+* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs.
+* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections.
+* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if
+  the Content-Type can be determined based on the entity body or the path of the request.
+* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder.
+* Added support for a PSR-3 LogAdapter.
+* Added a `command.after_prepare` event
+* Added `oauth_callback` parameter to the OauthPlugin
+* Added the ability to create a custom stream class when using a stream factory
+* Added a CachingEntityBody decorator
+* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized.
+* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar.
+* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies
+* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This
+  means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use
+  POST fields or files (the latter is only used when emulating a form POST in the browser).
+* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest
+
+## 3.3.1 - 2013-03-10
+
+* Added the ability to create PHP streaming responses from HTTP requests
+* Bug fix: Running any filters when parsing response headers with service descriptions
+* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing
+* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across
+  response location visitors.
+* Bug fix: Removed the possibility of creating configuration files with circular dependencies
+* RequestFactory::create() now uses the key of a POST file when setting the POST file name
+* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set
+
+## 3.3.0 - 2013-03-03
+
+* A large number of performance optimizations have been made
+* Bug fix: Added 'wb' as a valid write mode for streams
+* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned
+* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()`
+* BC: Removed `Guzzle\Http\Utils` class
+* BC: Setting a service description on a client will no longer modify the client's command factories.
+* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using
+  the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
+* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to
+  lowercase
+* Operation parameter objects are now lazy loaded internally
+* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses
+* Added support for instantiating responseType=class responseClass classes. Classes must implement
+  `Guzzle\Service\Command\ResponseClassInterface`
+* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These
+  additional properties also support locations and can be used to parse JSON responses where the outermost part of the
+  JSON is an array
+* Added support for nested renaming of JSON models (rename sentAs to name)
+* CachePlugin
+    * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error
+    * Debug headers can now added to cached response in the CachePlugin
+
+## 3.2.0 - 2013-02-14
+
+* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients.
+* URLs with no path no longer contain a "/" by default
+* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url.
+* BadResponseException no longer includes the full request and response message
+* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface
+* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface
+* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription
+* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list
+* xmlEncoding can now be customized for the XML declaration of a XML service description operation
+* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value
+  aggregation and no longer uses callbacks
+* The URL encoding implementation of Guzzle\Http\QueryString can now be customized
+* Bug fix: Filters were not always invoked for array service description parameters
+* Bug fix: Redirects now use a target response body rather than a temporary response body
+* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded
+* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives
+
+## 3.1.2 - 2013-01-27
+
+* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the
+  response body. For example, the XmlVisitor now parses the XML response into an array in the before() method.
+* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent
+* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444)
+* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse()
+* Setting default headers on a client after setting the user-agent will not erase the user-agent setting
+
+## 3.1.1 - 2013-01-20
+
+* Adding wildcard support to Guzzle\Common\Collection::getPath()
+* Adding alias support to ServiceBuilder configs
+* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface
+
+## 3.1.0 - 2013-01-12
+
+* BC: CurlException now extends from RequestException rather than BadResponseException
+* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse()
+* Added getData to ServiceDescriptionInterface
+* Added context array to RequestInterface::setState()
+* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http
+* Bug: Adding required content-type when JSON request visitor adds JSON to a command
+* Bug: Fixing the serialization of a service description with custom data
+* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing
+  an array of successful and failed responses
+* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection
+* Added Guzzle\Http\IoEmittingEntityBody
+* Moved command filtration from validators to location visitors
+* Added `extends` attributes to service description parameters
+* Added getModels to ServiceDescriptionInterface
+
+## 3.0.7 - 2012-12-19
+
+* Fixing phar detection when forcing a cacert to system if null or true
+* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()`
+* Cleaning up `Guzzle\Common\Collection::inject` method
+* Adding a response_body location to service descriptions
+
+## 3.0.6 - 2012-12-09
+
+* CurlMulti performance improvements
+* Adding setErrorResponses() to Operation
+* composer.json tweaks
+
+## 3.0.5 - 2012-11-18
+
+* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin
+* Bug: Response body can now be a string containing "0"
+* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert
+* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs
+* Added support for XML attributes in service description responses
+* DefaultRequestSerializer now supports array URI parameter values for URI template expansion
+* Added better mimetype guessing to requests and post files
+
+## 3.0.4 - 2012-11-11
+
+* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value
+* Bug: Cookies can now be added that have a name, domain, or value set to "0"
+* Bug: Using the system cacert bundle when using the Phar
+* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures
+* Enhanced cookie jar de-duplication
+* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added
+* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies
+* Added the ability to create any sort of hash for a stream rather than just an MD5 hash
+
+## 3.0.3 - 2012-11-04
+
+* Implementing redirects in PHP rather than cURL
+* Added PECL URI template extension and using as default parser if available
+* Bug: Fixed Content-Length parsing of Response factory
+* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams.
+* Adding ToArrayInterface throughout library
+* Fixing OauthPlugin to create unique nonce values per request
+
+## 3.0.2 - 2012-10-25
+
+* Magic methods are enabled by default on clients
+* Magic methods return the result of a command
+* Service clients no longer require a base_url option in the factory
+* Bug: Fixed an issue with URI templates where null template variables were being expanded
+
+## 3.0.1 - 2012-10-22
+
+* Models can now be used like regular collection objects by calling filter, map, etc.
+* Models no longer require a Parameter structure or initial data in the constructor
+* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator`
+
+## 3.0.0 - 2012-10-15
+
+* Rewrote service description format to be based on Swagger
+    * Now based on JSON schema
+    * Added nested input structures and nested response models
+    * Support for JSON and XML input and output models
+    * Renamed `commands` to `operations`
+    * Removed dot class notation
+    * Removed custom types
+* Broke the project into smaller top-level namespaces to be more component friendly
+* Removed support for XML configs and descriptions. Use arrays or JSON files.
+* Removed the Validation component and Inspector
+* Moved all cookie code to Guzzle\Plugin\Cookie
+* Magic methods on a Guzzle\Service\Client now return the command un-executed.
+* Calling getResult() or getResponse() on a command will lazily execute the command if needed.
+* Now shipping with cURL's CA certs and using it by default
+* Added previousResponse() method to response objects
+* No longer sending Accept and Accept-Encoding headers on every request
+* Only sending an Expect header by default when a payload is greater than 1MB
+* Added/moved client options:
+    * curl.blacklist to curl.option.blacklist
+    * Added ssl.certificate_authority
+* Added a Guzzle\Iterator component
+* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin
+* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin)
+* Added a more robust caching plugin
+* Added setBody to response objects
+* Updating LogPlugin to use a more flexible MessageFormatter
+* Added a completely revamped build process
+* Cleaning up Collection class and removing default values from the get method
+* Fixed ZF2 cache adapters
+
+## 2.8.8 - 2012-10-15
+
+* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did
+
+## 2.8.7 - 2012-09-30
+
+* Bug: Fixed config file aliases for JSON includes
+* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests
+* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload
+* Bug: Hardening request and response parsing to account for missing parts
+* Bug: Fixed PEAR packaging
+* Bug: Fixed Request::getInfo
+* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail
+* Adding the ability for the namespace Iterator factory to look in multiple directories
+* Added more getters/setters/removers from service descriptions
+* Added the ability to remove POST fields from OAuth signatures
+* OAuth plugin now supports 2-legged OAuth
+
+## 2.8.6 - 2012-09-05
+
+* Added the ability to modify and build service descriptions
+* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command
+* Added a `json` parameter location
+* Now allowing dot notation for classes in the CacheAdapterFactory
+* Using the union of two arrays rather than an array_merge when extending service builder services and service params
+* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references
+  in service builder config files.
+* Services defined in two different config files that include one another will by default replace the previously
+  defined service, but you can now create services that extend themselves and merge their settings over the previous
+* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like
+  '_default' with a default JSON configuration file.
+
+## 2.8.5 - 2012-08-29
+
+* Bug: Suppressed empty arrays from URI templates
+* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching
+* Added support for HTTP responses that do not contain a reason phrase in the start-line
+* AbstractCommand commands are now invokable
+* Added a way to get the data used when signing an Oauth request before a request is sent
+
+## 2.8.4 - 2012-08-15
+
+* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin
+* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable.
+* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream
+* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream
+* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5())
+* Added additional response status codes
+* Removed SSL information from the default User-Agent header
+* DELETE requests can now send an entity body
+* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries
+* Added the ability of the MockPlugin to consume mocked request bodies
+* LogPlugin now exposes request and response objects in the extras array
+
+## 2.8.3 - 2012-07-30
+
+* Bug: Fixed a case where empty POST requests were sent as GET requests
+* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body
+* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new
+* Added multiple inheritance to service description commands
+* Added an ApiCommandInterface and added `getParamNames()` and `hasParam()`
+* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything
+* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles
+
+## 2.8.2 - 2012-07-24
+
+* Bug: Query string values set to 0 are no longer dropped from the query string
+* Bug: A Collection object is no longer created each time a call is made to `Guzzle\Service\Command\AbstractCommand::getRequestHeaders()`
+* Bug: `+` is now treated as an encoded space when parsing query strings
+* QueryString and Collection performance improvements
+* Allowing dot notation for class paths in filters attribute of a service descriptions
+
+## 2.8.1 - 2012-07-16
+
+* Loosening Event Dispatcher dependency
+* POST redirects can now be customized using CURLOPT_POSTREDIR
+
+## 2.8.0 - 2012-07-15
+
+* BC: Guzzle\Http\Query
+    * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl)
+    * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding()
+    * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool)
+    * Changed the aggregation functions of QueryString to be static methods
+    * Can now use fromString() with querystrings that have a leading ?
+* cURL configuration values can be specified in service descriptions using `curl.` prefixed parameters
+* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body
+* Cookies are no longer URL decoded by default
+* Bug: URI template variables set to null are no longer expanded
+
+## 2.7.2 - 2012-07-02
+
+* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser.
+* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty()
+* CachePlugin now allows for a custom request parameter function to check if a request can be cached
+* Bug fix: CachePlugin now only caches GET and HEAD requests by default
+* Bug fix: Using header glue when transferring headers over the wire
+* Allowing deeply nested arrays for composite variables in URI templates
+* Batch divisors can now return iterators or arrays
+
+## 2.7.1 - 2012-06-26
+
+* Minor patch to update version number in UA string
+* Updating build process
+
+## 2.7.0 - 2012-06-25
+
+* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes.
+* BC: Removed magic setX methods from commands
+* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method
+* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable.
+* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity)
+* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace
+* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin
+* Added the ability to set POST fields and files in a service description
+* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method
+* Adding a command.before_prepare event to clients
+* Added BatchClosureTransfer and BatchClosureDivisor
+* BatchTransferException now includes references to the batch divisor and transfer strategies
+* Fixed some tests so that they pass more reliably
+* Added Guzzle\Common\Log\ArrayLogAdapter
+
+## 2.6.6 - 2012-06-10
+
+* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin
+* BC: Removing Guzzle\Service\Command\CommandSet
+* Adding generic batching system (replaces the batch queue plugin and command set)
+* Updating ZF cache and log adapters and now using ZF's composer repository
+* Bug: Setting the name of each ApiParam when creating through an ApiCommand
+* Adding result_type, result_doc, deprecated, and doc_url to service descriptions
+* Bug: Changed the default cookie header casing back to 'Cookie'
+
+## 2.6.5 - 2012-06-03
+
+* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource()
+* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from
+* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data
+* BC: Renaming methods in the CookieJarInterface
+* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations
+* Making the default glue for HTTP headers ';' instead of ','
+* Adding a removeValue to Guzzle\Http\Message\Header
+* Adding getCookies() to request interface.
+* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber()
+
+## 2.6.4 - 2012-05-30
+
+* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class.
+* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand
+* Bug: Fixing magic method command calls on clients
+* Bug: Email constraint only validates strings
+* Bug: Aggregate POST fields when POST files are present in curl handle
+* Bug: Fixing default User-Agent header
+* Bug: Only appending or prepending parameters in commands if they are specified
+* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes
+* Allowing the use of dot notation for class namespaces when using instance_of constraint
+* Added any_match validation constraint
+* Added an AsyncPlugin
+* Passing request object to the calculateWait method of the ExponentialBackoffPlugin
+* Allowing the result of a command object to be changed
+* Parsing location and type sub values when instantiating a service description rather than over and over at runtime
+
+## 2.6.3 - 2012-05-23
+
+* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options.
+* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields.
+* You can now use an array of data when creating PUT request bodies in the request factory.
+* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable.
+* [Http] Adding support for Content-Type in multipart POST uploads per upload
+* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1])
+* Adding more POST data operations for easier manipulation of POST data.
+* You can now set empty POST fields.
+* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files.
+* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate.
+* CS updates
+
+## 2.6.2 - 2012-05-19
+
+* [Http] Better handling of nested scope requests in CurlMulti.  Requests are now always prepares in the send() method rather than the addRequest() method.
+
+## 2.6.1 - 2012-05-19
+
+* [BC] Removing 'path' support in service descriptions.  Use 'uri'.
+* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache.
+* [BC] Removing Guzzle\Common\NullObject.  Use https://github.com/mtdowling/NullObject if you need it.
+* [BC] Removing Guzzle\Common\XmlElement.
+* All commands, both dynamic and concrete, have ApiCommand objects.
+* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits.
+* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored.
+* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible.
+
+## 2.6.0 - 2012-05-15
+
+* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder
+* [BC] Executing a Command returns the result of the command rather than the command
+* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed.
+* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args.
+* [BC] Moving ResourceIterator* to Guzzle\Service\Resource
+* [BC] Completely refactored ResourceIterators to iterate over a cloned command object
+* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate
+* [BC] Guzzle\Guzzle is now deprecated
+* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject
+* Adding Guzzle\Version class to give version information about Guzzle
+* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate()
+* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data
+* ServiceDescription and ServiceBuilder are now cacheable using similar configs
+* Changing the format of XML and JSON service builder configs.  Backwards compatible.
+* Cleaned up Cookie parsing
+* Trimming the default Guzzle User-Agent header
+* Adding a setOnComplete() method to Commands that is called when a command completes
+* Keeping track of requests that were mocked in the MockPlugin
+* Fixed a caching bug in the CacheAdapterFactory
+* Inspector objects can be injected into a Command object
+* Refactoring a lot of code and tests to be case insensitive when dealing with headers
+* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL
+* Adding the ability to set global option overrides to service builder configs
+* Adding the ability to include other service builder config files from within XML and JSON files
+* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method.
+
+## 2.5.0 - 2012-05-08
+
+* Major performance improvements
+* [BC] Simplifying Guzzle\Common\Collection.  Please check to see if you are using features that are now deprecated.
+* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component.
+* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates.  Use "{}"
+* Added the ability to passed parameters to all requests created by a client
+* Added callback functionality to the ExponentialBackoffPlugin
+* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies.
+* Rewinding request stream bodies when retrying requests
+* Exception is thrown when JSON response body cannot be decoded
+* Added configurable magic method calls to clients and commands.  This is off by default.
+* Fixed a defect that added a hash to every parsed URL part
+* Fixed duplicate none generation for OauthPlugin.
+* Emitting an event each time a client is generated by a ServiceBuilder
+* Using an ApiParams object instead of a Collection for parameters of an ApiCommand
+* cache.* request parameters should be renamed to params.cache.*
+* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc.). See CurlHandle.
+* Added the ability to disable type validation of service descriptions
+* ServiceDescriptions and ServiceBuilders are now Serializable
diff --git a/vendor/guzzlehttp/guzzle/LICENSE b/vendor/guzzlehttp/guzzle/LICENSE
new file mode 100644
index 0000000..fd2375d
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/LICENSE
@@ -0,0 +1,27 @@
+The MIT License (MIT)
+
+Copyright (c) 2011 Michael Dowling 
+Copyright (c) 2012 Jeremy Lindblom 
+Copyright (c) 2014 Graham Campbell 
+Copyright (c) 2015 Márk Sági-Kazár 
+Copyright (c) 2015 Tobias Schultze 
+Copyright (c) 2016 Tobias Nyholm 
+Copyright (c) 2016 George Mponos 
+
+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/guzzlehttp/guzzle/README.md b/vendor/guzzlehttp/guzzle/README.md
new file mode 100644
index 0000000..cdaebee
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/README.md
@@ -0,0 +1,94 @@
+![Guzzle](.github/logo.png?raw=true)
+
+# Guzzle, PHP HTTP client
+
+[![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases)
+[![Build Status](https://img.shields.io/github/actions/workflow/status/guzzle/guzzle/ci.yml?label=ci%20build&style=flat-square)](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI)
+[![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle)
+
+Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
+trivial to integrate with web services.
+
+- Simple interface for building query strings, POST requests, streaming large
+  uploads, streaming large downloads, using HTTP cookies, uploading JSON data,
+  etc...
+- Can send both synchronous and asynchronous requests using the same interface.
+- Uses PSR-7 interfaces for requests, responses, and streams. This allows you
+  to utilize other PSR-7 compatible libraries with Guzzle.
+- Supports PSR-18 allowing interoperability between other PSR-18 HTTP Clients.
+- Abstracts away the underlying HTTP transport, allowing you to write
+  environment and transport agnostic code; i.e., no hard dependency on cURL,
+  PHP streams, sockets, or non-blocking event loops.
+- Middleware system allows you to augment and compose client behavior.
+
+```php
+$client = new \GuzzleHttp\Client();
+$response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle');
+
+echo $response->getStatusCode(); // 200
+echo $response->getHeaderLine('content-type'); // 'application/json; charset=utf8'
+echo $response->getBody(); // '{"id": 1420053, "name": "guzzle", ...}'
+
+// Send an asynchronous request.
+$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
+$promise = $client->sendAsync($request)->then(function ($response) {
+    echo 'I completed! ' . $response->getBody();
+});
+
+$promise->wait();
+```
+
+## Help and docs
+
+We use GitHub issues only to discuss bugs and new features. For support please refer to:
+
+- [Documentation](https://docs.guzzlephp.org)
+- [Stack Overflow](https://stackoverflow.com/questions/tagged/guzzle)
+- [#guzzle](https://app.slack.com/client/T0D2S9JCT/CE6UAAKL4) channel on [PHP-HTTP Slack](https://slack.httplug.io/)
+- [Gitter](https://gitter.im/guzzle/guzzle)
+
+
+## Installing Guzzle
+
+The recommended way to install Guzzle is through
+[Composer](https://getcomposer.org/).
+
+```bash
+composer require guzzlehttp/guzzle
+```
+
+
+## Version Guidance
+
+| Version | Status              | Packagist           | Namespace    | Repo                | Docs                | PSR-7 | PHP Version  |
+|---------|---------------------|---------------------|--------------|---------------------|---------------------|-------|--------------|
+| 3.x     | EOL (2016-10-31)    | `guzzle/guzzle`     | `Guzzle`     | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No    | >=5.3.3,<7.0 |
+| 4.x     | EOL (2016-10-31)    | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A                 | No    | >=5.4,<7.0   |
+| 5.x     | EOL (2019-10-31)    | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No    | >=5.4,<7.4   |
+| 6.x     | EOL (2023-10-31)    | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes   | >=5.5,<8.0   |
+| 7.x     | Latest              | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes   | >=7.2.5,<8.5 |
+
+[guzzle-3-repo]: https://github.com/guzzle/guzzle3
+[guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x
+[guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3
+[guzzle-6-repo]: https://github.com/guzzle/guzzle/tree/6.5
+[guzzle-7-repo]: https://github.com/guzzle/guzzle
+[guzzle-3-docs]: https://guzzle3.readthedocs.io/
+[guzzle-5-docs]: https://docs.guzzlephp.org/en/5.3/
+[guzzle-6-docs]: https://docs.guzzlephp.org/en/6.5/
+[guzzle-7-docs]: https://docs.guzzlephp.org/en/latest/
+
+
+## Security
+
+If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/guzzle/security/policy) for more information.
+
+## License
+
+Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
+
+## For Enterprise
+
+Available as part of the Tidelift Subscription
+
+The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-guzzle?utm_source=packagist-guzzlehttp-guzzle&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
diff --git a/vendor/guzzlehttp/guzzle/UPGRADING.md b/vendor/guzzlehttp/guzzle/UPGRADING.md
new file mode 100644
index 0000000..4efbb59
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/UPGRADING.md
@@ -0,0 +1,1253 @@
+Guzzle Upgrade Guide
+====================
+
+6.0 to 7.0
+----------
+
+In order to take advantage of the new features of PHP, Guzzle dropped the support
+of PHP 5. The minimum supported PHP version is now PHP 7.2. Type hints and return
+types for functions and methods have been added wherever possible. 
+
+Please make sure:
+- You are calling a function or a method with the correct type.
+- If you extend a class of Guzzle; update all signatures on methods you override.
+
+#### Other backwards compatibility breaking changes
+
+- Class `GuzzleHttp\UriTemplate` is removed.
+- Class `GuzzleHttp\Exception\SeekException` is removed.
+- Classes `GuzzleHttp\Exception\BadResponseException`, `GuzzleHttp\Exception\ClientException`, 
+  `GuzzleHttp\Exception\ServerException` can no longer be initialized with an empty
+  Response as argument.
+- Class `GuzzleHttp\Exception\ConnectException` now extends `GuzzleHttp\Exception\TransferException`
+  instead of `GuzzleHttp\Exception\RequestException`.
+- Function `GuzzleHttp\Exception\ConnectException::getResponse()` is removed.
+- Function `GuzzleHttp\Exception\ConnectException::hasResponse()` is removed.
+- Constant `GuzzleHttp\ClientInterface::VERSION` is removed. Added `GuzzleHttp\ClientInterface::MAJOR_VERSION` instead.
+- Function `GuzzleHttp\Exception\RequestException::getResponseBodySummary` is removed.
+  Use `\GuzzleHttp\Psr7\get_message_body_summary` as an alternative.
+- Function `GuzzleHttp\Cookie\CookieJar::getCookieValue` is removed.
+- Request option `exceptions` is removed. Please use `http_errors`.
+- Request option `save_to` is removed. Please use `sink`.
+- Pool option `pool_size` is removed. Please use `concurrency`.
+- We now look for environment variables in the `$_SERVER` super global, due to thread safety issues with `getenv`. We continue to fallback to `getenv` in CLI environments, for maximum compatibility.
+- The `get`, `head`, `put`, `post`, `patch`, `delete`, `getAsync`, `headAsync`, `putAsync`, `postAsync`, `patchAsync`, and `deleteAsync` methods are now implemented as genuine methods on `GuzzleHttp\Client`, with strong typing. The original `__call` implementation remains unchanged for now, for maximum backwards compatibility, but won't be invoked under normal operation.
+- The `log` middleware will log the errors with level `error` instead of `notice` 
+- Support for international domain names (IDN) is now disabled by default, and enabling it requires installing ext-intl, linked against a modern version of the C library (ICU 4.6 or higher).
+
+#### Native functions calls
+
+All internal native functions calls of Guzzle are now prefixed with a slash. This
+change makes it impossible for method overloading by other libraries or applications.
+Example:
+
+```php
+// Before:
+curl_version();
+
+// After:
+\curl_version();
+```
+
+For the full diff you can check [here](https://github.com/guzzle/guzzle/compare/6.5.4..master).
+
+5.0 to 6.0
+----------
+
+Guzzle now uses [PSR-7](https://www.php-fig.org/psr/psr-7/) for HTTP messages.
+Due to the fact that these messages are immutable, this prompted a refactoring
+of Guzzle to use a middleware based system rather than an event system. Any
+HTTP message interaction (e.g., `GuzzleHttp\Message\Request`) need to be
+updated to work with the new immutable PSR-7 request and response objects. Any
+event listeners or subscribers need to be updated to become middleware
+functions that wrap handlers (or are injected into a
+`GuzzleHttp\HandlerStack`).
+
+- Removed `GuzzleHttp\BatchResults`
+- Removed `GuzzleHttp\Collection`
+- Removed `GuzzleHttp\HasDataTrait`
+- Removed `GuzzleHttp\ToArrayInterface`
+- The `guzzlehttp/streams` dependency has been removed. Stream functionality
+  is now present in the `GuzzleHttp\Psr7` namespace provided by the
+  `guzzlehttp/psr7` package.
+- Guzzle no longer uses ReactPHP promises and now uses the
+  `guzzlehttp/promises` library. We use a custom promise library for three
+  significant reasons:
+  1. React promises (at the time of writing this) are recursive. Promise
+     chaining and promise resolution will eventually blow the stack. Guzzle
+     promises are not recursive as they use a sort of trampolining technique.
+     Note: there has been movement in the React project to modify promises to
+     no longer utilize recursion.
+  2. Guzzle needs to have the ability to synchronously block on a promise to
+     wait for a result. Guzzle promises allows this functionality (and does
+     not require the use of recursion).
+  3. Because we need to be able to wait on a result, doing so using React
+     promises requires wrapping react promises with RingPHP futures. This
+     overhead is no longer needed, reducing stack sizes, reducing complexity,
+     and improving performance.
+- `GuzzleHttp\Mimetypes` has been moved to a function in
+  `GuzzleHttp\Psr7\mimetype_from_extension` and
+  `GuzzleHttp\Psr7\mimetype_from_filename`.
+- `GuzzleHttp\Query` and `GuzzleHttp\QueryParser` have been removed. Query
+  strings must now be passed into request objects as strings, or provided to
+  the `query` request option when creating requests with clients. The `query`
+  option uses PHP's `http_build_query` to convert an array to a string. If you
+  need a different serialization technique, you will need to pass the query
+  string in as a string. There are a couple helper functions that will make
+  working with query strings easier: `GuzzleHttp\Psr7\parse_query` and
+  `GuzzleHttp\Psr7\build_query`.
+- Guzzle no longer has a dependency on RingPHP. Due to the use of a middleware
+  system based on PSR-7, using RingPHP and it's middleware system as well adds
+  more complexity than the benefits it provides. All HTTP handlers that were
+  present in RingPHP have been modified to work directly with PSR-7 messages
+  and placed in the `GuzzleHttp\Handler` namespace. This significantly reduces
+  complexity in Guzzle, removes a dependency, and improves performance. RingPHP
+  will be maintained for Guzzle 5 support, but will no longer be a part of
+  Guzzle 6.
+- As Guzzle now uses a middleware based systems the event system and RingPHP
+  integration has been removed. Note: while the event system has been removed,
+  it is possible to add your own type of event system that is powered by the
+  middleware system.
+  - Removed the `Event` namespace.
+  - Removed the `Subscriber` namespace.
+  - Removed `Transaction` class
+  - Removed `RequestFsm`
+  - Removed `RingBridge`
+  - `GuzzleHttp\Subscriber\Cookie` is now provided by
+    `GuzzleHttp\Middleware::cookies`
+  - `GuzzleHttp\Subscriber\HttpError` is now provided by
+    `GuzzleHttp\Middleware::httpError`
+  - `GuzzleHttp\Subscriber\History` is now provided by
+    `GuzzleHttp\Middleware::history`
+  - `GuzzleHttp\Subscriber\Mock` is now provided by
+    `GuzzleHttp\Handler\MockHandler`
+  - `GuzzleHttp\Subscriber\Prepare` is now provided by
+    `GuzzleHttp\PrepareBodyMiddleware`
+  - `GuzzleHttp\Subscriber\Redirect` is now provided by
+    `GuzzleHttp\RedirectMiddleware`
+- Guzzle now uses `Psr\Http\Message\UriInterface` (implements in
+  `GuzzleHttp\Psr7\Uri`) for URI support. `GuzzleHttp\Url` is now gone.
+- Static functions in `GuzzleHttp\Utils` have been moved to namespaced
+  functions under the `GuzzleHttp` namespace. This requires either a Composer
+  based autoloader or you to include functions.php.
+- `GuzzleHttp\ClientInterface::getDefaultOption` has been renamed to
+  `GuzzleHttp\ClientInterface::getConfig`.
+- `GuzzleHttp\ClientInterface::setDefaultOption` has been removed.
+- The `json` and `xml` methods of response objects has been removed. With the
+  migration to strictly adhering to PSR-7 as the interface for Guzzle messages,
+  adding methods to message interfaces would actually require Guzzle messages
+  to extend from PSR-7 messages rather then work with them directly.
+
+## Migrating to middleware
+
+The change to PSR-7 unfortunately required significant refactoring to Guzzle
+due to the fact that PSR-7 messages are immutable. Guzzle 5 relied on an event
+system from plugins. The event system relied on mutability of HTTP messages and
+side effects in order to work. With immutable messages, you have to change your
+workflow to become more about either returning a value (e.g., functional
+middlewares) or setting a value on an object. Guzzle v6 has chosen the
+functional middleware approach.
+
+Instead of using the event system to listen for things like the `before` event,
+you now create a stack based middleware function that intercepts a request on
+the way in and the promise of the response on the way out. This is a much
+simpler and more predictable approach than the event system and works nicely
+with PSR-7 middleware. Due to the use of promises, the middleware system is
+also asynchronous.
+
+v5:
+
+```php
+use GuzzleHttp\Event\BeforeEvent;
+$client = new GuzzleHttp\Client();
+// Get the emitter and listen to the before event.
+$client->getEmitter()->on('before', function (BeforeEvent $e) {
+    // Guzzle v5 events relied on mutation
+    $e->getRequest()->setHeader('X-Foo', 'Bar');
+});
+```
+
+v6:
+
+In v6, you can modify the request before it is sent using the `mapRequest`
+middleware. The idiomatic way in v6 to modify the request/response lifecycle is
+to setup a handler middleware stack up front and inject the handler into a
+client.
+
+```php
+use GuzzleHttp\Middleware;
+// Create a handler stack that has all of the default middlewares attached
+$handler = GuzzleHttp\HandlerStack::create();
+// Push the handler onto the handler stack
+$handler->push(Middleware::mapRequest(function (RequestInterface $request) {
+    // Notice that we have to return a request object
+    return $request->withHeader('X-Foo', 'Bar');
+}));
+// Inject the handler into the client
+$client = new GuzzleHttp\Client(['handler' => $handler]);
+```
+
+## POST Requests
+
+This version added the [`form_params`](https://docs.guzzlephp.org/en/latest/request-options.html#form_params)
+and `multipart` request options. `form_params` is an associative array of
+strings or array of strings and is used to serialize an
+`application/x-www-form-urlencoded` POST request. The
+[`multipart`](https://docs.guzzlephp.org/en/latest/request-options.html#multipart)
+option is now used to send a multipart/form-data POST request.
+
+`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add
+POST files to a multipart/form-data request.
+
+The `body` option no longer accepts an array to send POST requests. Please use
+`multipart` or `form_params` instead.
+
+The `base_url` option has been renamed to `base_uri`.
+
+4.x to 5.0
+----------
+
+## Rewritten Adapter Layer
+
+Guzzle now uses [RingPHP](https://ringphp.readthedocs.org/en/latest) to send
+HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor
+is still supported, but it has now been renamed to `handler`. Instead of
+passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP
+`callable` that follows the RingPHP specification.
+
+## Removed Fluent Interfaces
+
+[Fluent interfaces were removed](https://ocramius.github.io/blog/fluent-interfaces-are-evil/)
+from the following classes:
+
+- `GuzzleHttp\Collection`
+- `GuzzleHttp\Url`
+- `GuzzleHttp\Query`
+- `GuzzleHttp\Post\PostBody`
+- `GuzzleHttp\Cookie\SetCookie`
+
+## Removed functions.php
+
+Removed "functions.php", so that Guzzle is truly PSR-4 compliant. The following
+functions can be used as replacements.
+
+- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode`
+- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath`
+- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path`
+- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however,
+  deprecated in favor of using `GuzzleHttp\Pool::batch()`.
+
+The "procedural" global client has been removed with no replacement (e.g.,
+`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client`
+object as a replacement.
+
+## `throwImmediately` has been removed
+
+The concept of "throwImmediately" has been removed from exceptions and error
+events. This control mechanism was used to stop a transfer of concurrent
+requests from completing. This can now be handled by throwing the exception or
+by cancelling a pool of requests or each outstanding future request
+individually.
+
+## headers event has been removed
+
+Removed the "headers" event. This event was only useful for changing the
+body a response once the headers of the response were known. You can implement
+a similar behavior in a number of ways. One example might be to use a
+FnStream that has access to the transaction being sent. For example, when the
+first byte is written, you could check if the response headers match your
+expectations, and if so, change the actual stream body that is being
+written to.
+
+## Updates to HTTP Messages
+
+Removed the `asArray` parameter from
+`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
+value as an array, then use the newly added `getHeaderAsArray()` method of
+`MessageInterface`. This change makes the Guzzle interfaces compatible with
+the PSR-7 interfaces.
+
+3.x to 4.0
+----------
+
+## Overarching changes:
+
+- Now requires PHP 5.4 or greater.
+- No longer requires cURL to send requests.
+- Guzzle no longer wraps every exception it throws. Only exceptions that are
+  recoverable are now wrapped by Guzzle.
+- Various namespaces have been removed or renamed.
+- No longer requiring the Symfony EventDispatcher. A custom event dispatcher
+  based on the Symfony EventDispatcher is
+  now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant
+  speed and functionality improvements).
+
+Changes per Guzzle 3.x namespace are described below.
+
+## Batch
+
+The `Guzzle\Batch` namespace has been removed. This is best left to
+third-parties to implement on top of Guzzle's core HTTP library.
+
+## Cache
+
+The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement
+has been implemented yet, but hoping to utilize a PSR cache interface).
+
+## Common
+
+- Removed all of the wrapped exceptions. It's better to use the standard PHP
+  library for unrecoverable exceptions.
+- `FromConfigInterface` has been removed.
+- `Guzzle\Common\Version` has been removed. The VERSION constant can be found
+  at `GuzzleHttp\ClientInterface::VERSION`.
+
+### Collection
+
+- `getAll` has been removed. Use `toArray` to convert a collection to an array.
+- `inject` has been removed.
+- `keySearch` has been removed.
+- `getPath` no longer supports wildcard expressions. Use something better like
+  JMESPath for this.
+- `setPath` now supports appending to an existing array via the `[]` notation.
+
+### Events
+
+Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses
+`GuzzleHttp\Event\Emitter`.
+
+- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by
+  `GuzzleHttp\Event\EmitterInterface`.
+- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by
+  `GuzzleHttp\Event\Emitter`.
+- `Symfony\Component\EventDispatcher\Event` is replaced by
+  `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in
+  `GuzzleHttp\Event\EventInterface`.
+- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and
+  `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the
+  event emitter of a request, client, etc. now uses the `getEmitter` method
+  rather than the `getDispatcher` method.
+
+#### Emitter
+
+- Use the `once()` method to add a listener that automatically removes itself
+  the first time it is invoked.
+- Use the `listeners()` method to retrieve a list of event listeners rather than
+  the `getListeners()` method.
+- Use `emit()` instead of `dispatch()` to emit an event from an emitter.
+- Use `attach()` instead of `addSubscriber()` and `detach()` instead of
+  `removeSubscriber()`.
+
+```php
+$mock = new Mock();
+// 3.x
+$request->getEventDispatcher()->addSubscriber($mock);
+$request->getEventDispatcher()->removeSubscriber($mock);
+// 4.x
+$request->getEmitter()->attach($mock);
+$request->getEmitter()->detach($mock);
+```
+
+Use the `on()` method to add a listener rather than the `addListener()` method.
+
+```php
+// 3.x
+$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } );
+// 4.x
+$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } );
+```
+
+## Http
+
+### General changes
+
+- The cacert.pem certificate has been moved to `src/cacert.pem`.
+- Added the concept of adapters that are used to transfer requests over the
+  wire.
+- Simplified the event system.
+- Sending requests in parallel is still possible, but batching is no longer a
+  concept of the HTTP layer. Instead, you must use the `complete` and `error`
+  events to asynchronously manage parallel request transfers.
+- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`.
+- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`.
+- QueryAggregators have been rewritten so that they are simply callable
+  functions.
+- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in
+  `functions.php` for an easy to use static client instance.
+- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from
+  `GuzzleHttp\Exception\TransferException`.
+
+### Client
+
+Calling methods like `get()`, `post()`, `head()`, etc. no longer create and
+return a request, but rather creates a request, sends the request, and returns
+the response.
+
+```php
+// 3.0
+$request = $client->get('/');
+$response = $request->send();
+
+// 4.0
+$response = $client->get('/');
+
+// or, to mirror the previous behavior
+$request = $client->createRequest('GET', '/');
+$response = $client->send($request);
+```
+
+`GuzzleHttp\ClientInterface` has changed.
+
+- The `send` method no longer accepts more than one request. Use `sendAll` to
+  send multiple requests in parallel.
+- `setUserAgent()` has been removed. Use a default request option instead. You
+  could, for example, do something like:
+  `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`.
+- `setSslVerification()` has been removed. Use default request options instead,
+  like `$client->setConfig('defaults/verify', true)`.
+
+`GuzzleHttp\Client` has changed.
+
+- The constructor now accepts only an associative array. You can include a
+  `base_url` string or array to use a URI template as the base URL of a client.
+  You can also specify a `defaults` key that is an associative array of default
+  request options. You can pass an `adapter` to use a custom adapter,
+  `batch_adapter` to use a custom adapter for sending requests in parallel, or
+  a `message_factory` to change the factory used to create HTTP requests and
+  responses.
+- The client no longer emits a `client.create_request` event.
+- Creating requests with a client no longer automatically utilize a URI
+  template. You must pass an array into a creational method (e.g.,
+  `createRequest`, `get`, `put`, etc.) in order to expand a URI template.
+
+### Messages
+
+Messages no longer have references to their counterparts (i.e., a request no
+longer has a reference to it's response, and a response no loger has a
+reference to its request). This association is now managed through a
+`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to
+these transaction objects using request events that are emitted over the
+lifecycle of a request.
+
+#### Requests with a body
+
+- `GuzzleHttp\Message\EntityEnclosingRequest` and
+  `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The
+  separation between requests that contain a body and requests that do not
+  contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface`
+  handles both use cases.
+- Any method that previously accepts a `GuzzleHttp\Response` object now accept a
+  `GuzzleHttp\Message\ResponseInterface`.
+- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to
+  `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create
+  both requests and responses and is implemented in
+  `GuzzleHttp\Message\MessageFactory`.
+- POST field and file methods have been removed from the request object. You
+  must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface`
+  to control the format of a POST body. Requests that are created using a
+  standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use
+  a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if
+  the method is POST and no body is provided.
+
+```php
+$request = $client->createRequest('POST', '/');
+$request->getBody()->setField('foo', 'bar');
+$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r')));
+```
+
+#### Headers
+
+- `GuzzleHttp\Message\Header` has been removed. Header values are now simply
+  represented by an array of values or as a string. Header values are returned
+  as a string by default when retrieving a header value from a message. You can
+  pass an optional argument of `true` to retrieve a header value as an array
+  of strings instead of a single concatenated string.
+- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to
+  `GuzzleHttp\Post`. This interface has been simplified and now allows the
+  addition of arbitrary headers.
+- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most
+  of the custom headers are now handled separately in specific
+  subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has
+  been updated to properly handle headers that contain parameters (like the
+  `Link` header).
+
+#### Responses
+
+- `GuzzleHttp\Message\Response::getInfo()` and
+  `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event
+  system to retrieve this type of information.
+- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed.
+- `GuzzleHttp\Message\Response::getMessage()` has been removed.
+- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific
+  methods have moved to the CacheSubscriber.
+- Header specific helper functions like `getContentMd5()` have been removed.
+  Just use `getHeader('Content-MD5')` instead.
+- `GuzzleHttp\Message\Response::setRequest()` and
+  `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event
+  system to work with request and response objects as a transaction.
+- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the
+  Redirect subscriber instead.
+- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have
+  been removed. Use `getStatusCode()` instead.
+
+#### Streaming responses
+
+Streaming requests can now be created by a client directly, returning a
+`GuzzleHttp\Message\ResponseInterface` object that contains a body stream
+referencing an open PHP HTTP stream.
+
+```php
+// 3.0
+use Guzzle\Stream\PhpStreamRequestFactory;
+$request = $client->get('/');
+$factory = new PhpStreamRequestFactory();
+$stream = $factory->fromRequest($request);
+$data = $stream->read(1024);
+
+// 4.0
+$response = $client->get('/', ['stream' => true]);
+// Read some data off of the stream in the response body
+$data = $response->getBody()->read(1024);
+```
+
+#### Redirects
+
+The `configureRedirects()` method has been removed in favor of a
+`allow_redirects` request option.
+
+```php
+// Standard redirects with a default of a max of 5 redirects
+$request = $client->createRequest('GET', '/', ['allow_redirects' => true]);
+
+// Strict redirects with a custom number of redirects
+$request = $client->createRequest('GET', '/', [
+    'allow_redirects' => ['max' => 5, 'strict' => true]
+]);
+```
+
+#### EntityBody
+
+EntityBody interfaces and classes have been removed or moved to
+`GuzzleHttp\Stream`. All classes and interfaces that once required
+`GuzzleHttp\EntityBodyInterface` now require
+`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no
+longer uses `GuzzleHttp\EntityBody::factory` but now uses
+`GuzzleHttp\Stream\Stream::factory` or even better:
+`GuzzleHttp\Stream\create()`.
+
+- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface`
+- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream`
+- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream`
+- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream`
+- `Guzzle\Http\IoEmittyinEntityBody` has been removed.
+
+#### Request lifecycle events
+
+Requests previously submitted a large number of requests. The number of events
+emitted over the lifecycle of a request has been significantly reduced to make
+it easier to understand how to extend the behavior of a request. All events
+emitted during the lifecycle of a request now emit a custom
+`GuzzleHttp\Event\EventInterface` object that contains context providing
+methods and a way in which to modify the transaction at that specific point in
+time (e.g., intercept the request and set a response on the transaction).
+
+- `request.before_send` has been renamed to `before` and now emits a
+  `GuzzleHttp\Event\BeforeEvent`
+- `request.complete` has been renamed to `complete` and now emits a
+  `GuzzleHttp\Event\CompleteEvent`.
+- `request.sent` has been removed. Use `complete`.
+- `request.success` has been removed. Use `complete`.
+- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`.
+- `request.exception` has been removed. Use `error`.
+- `request.receive.status_line` has been removed.
+- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to
+  maintain a status update.
+- `curl.callback.write` has been removed. Use a custom `StreamInterface` to
+  intercept writes.
+- `curl.callback.read` has been removed. Use a custom `StreamInterface` to
+  intercept reads.
+
+`headers` is a new event that is emitted after the response headers of a
+request have been received before the body of the response is downloaded. This
+event emits a `GuzzleHttp\Event\HeadersEvent`.
+
+You can intercept a request and inject a response using the `intercept()` event
+of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and
+`GuzzleHttp\Event\ErrorEvent` event.
+
+See: https://docs.guzzlephp.org/en/latest/events.html
+
+## Inflection
+
+The `Guzzle\Inflection` namespace has been removed. This is not a core concern
+of Guzzle.
+
+## Iterator
+
+The `Guzzle\Iterator` namespace has been removed.
+
+- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and
+  `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of
+  Guzzle itself.
+- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent
+  class is shipped with PHP 5.4.
+- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because
+  it's easier to just wrap an iterator in a generator that maps values.
+
+For a replacement of these iterators, see https://github.com/nikic/iter
+
+## Log
+
+The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The
+`Guzzle\Log` namespace has been removed. Guzzle now relies on
+`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been
+moved to `GuzzleHttp\Subscriber\Log\Formatter`.
+
+## Parser
+
+The `Guzzle\Parser` namespace has been removed. This was previously used to
+make it possible to plug in custom parsers for cookies, messages, URI
+templates, and URLs; however, this level of complexity is not needed in Guzzle
+so it has been removed.
+
+- Cookie: Cookie parsing logic has been moved to
+  `GuzzleHttp\Cookie\SetCookie::fromString`.
+- Message: Message parsing logic for both requests and responses has been moved
+  to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only
+  used in debugging or deserializing messages, so it doesn't make sense for
+  Guzzle as a library to add this level of complexity to parsing messages.
+- UriTemplate: URI template parsing has been moved to
+  `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL
+  URI template library if it is installed.
+- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously
+  it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary,
+  then developers are free to subclass `GuzzleHttp\Url`.
+
+## Plugin
+
+The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`.
+Several plugins are shipping with the core Guzzle library under this namespace.
+
+- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar
+  code has moved to `GuzzleHttp\Cookie`.
+- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin.
+- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is
+  received.
+- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin.
+- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before
+  sending. This subscriber is attached to all requests by default.
+- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin.
+
+The following plugins have been removed (third-parties are free to re-implement
+these if needed):
+
+- `GuzzleHttp\Plugin\Async` has been removed.
+- `GuzzleHttp\Plugin\CurlAuth` has been removed.
+- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This
+  functionality should instead be implemented with event listeners that occur
+  after normal response parsing occurs in the guzzle/command package.
+
+The following plugins are not part of the core Guzzle package, but are provided
+in separate repositories:
+
+- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be much simpler
+  to build custom retry policies using simple functions rather than various
+  chained classes. See: https://github.com/guzzle/retry-subscriber
+- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to
+  https://github.com/guzzle/cache-subscriber
+- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to
+  https://github.com/guzzle/log-subscriber
+- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to
+  https://github.com/guzzle/message-integrity-subscriber
+- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to
+  `GuzzleHttp\Subscriber\MockSubscriber`.
+- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to
+  https://github.com/guzzle/oauth-subscriber
+
+## Service
+
+The service description layer of Guzzle has moved into two separate packages:
+
+- https://github.com/guzzle/command Provides a high level abstraction over web
+  services by representing web service operations using commands.
+- https://github.com/guzzle/guzzle-services Provides an implementation of
+  guzzle/command that provides request serialization and response parsing using
+  Guzzle service descriptions.
+
+## Stream
+
+Stream have moved to a separate package available at
+https://github.com/guzzle/streams.
+
+`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take
+on the responsibilities of `Guzzle\Http\EntityBody` and
+`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number
+of methods implemented by the `StreamInterface` has been drastically reduced to
+allow developers to more easily extend and decorate stream behavior.
+
+## Removed methods from StreamInterface
+
+- `getStream` and `setStream` have been removed to better encapsulate streams.
+- `getMetadata` and `setMetadata` have been removed in favor of
+  `GuzzleHttp\Stream\MetadataStreamInterface`.
+- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been
+  removed. This data is accessible when
+  using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`.
+- `rewind` has been removed. Use `seek(0)` for a similar behavior.
+
+## Renamed methods
+
+- `detachStream` has been renamed to `detach`.
+- `feof` has been renamed to `eof`.
+- `ftell` has been renamed to `tell`.
+- `readLine` has moved from an instance method to a static class method of
+  `GuzzleHttp\Stream\Stream`.
+
+## Metadata streams
+
+`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams
+that contain additional metadata accessible via `getMetadata()`.
+`GuzzleHttp\Stream\StreamInterface::getMetadata` and
+`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed.
+
+## StreamRequestFactory
+
+The entire concept of the StreamRequestFactory has been removed. The way this
+was used in Guzzle 3 broke the actual interface of sending streaming requests
+(instead of getting back a Response, you got a StreamInterface). Streaming
+PHP requests are now implemented through the `GuzzleHttp\Adapter\StreamAdapter`.
+
+3.6 to 3.7
+----------
+
+### Deprecations
+
+- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.:
+
+```php
+\Guzzle\Common\Version::$emitWarnings = true;
+```
+
+The following APIs and options have been marked as deprecated:
+
+- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead.
+- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
+- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
+- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
+- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
+- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
+- Marked `Guzzle\Common\Collection::inject()` as deprecated.
+- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use
+  `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or
+  `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));`
+
+3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational
+request methods. When paired with a client's configuration settings, these options allow you to specify default settings
+for various aspects of a request. Because these options make other previous configuration options redundant, several
+configuration options and methods of a client and AbstractCommand have been deprecated.
+
+- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`.
+- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`.
+- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')`
+- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0
+
+        $command = $client->getCommand('foo', array(
+            'command.headers' => array('Test' => '123'),
+            'command.response_body' => '/path/to/file'
+        ));
+
+        // Should be changed to:
+
+        $command = $client->getCommand('foo', array(
+            'command.request_options' => array(
+                'headers' => array('Test' => '123'),
+                'save_as' => '/path/to/file'
+            )
+        ));
+
+### Interface changes
+
+Additions and changes (you will need to update any implementations or subclasses you may have created):
+
+- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
+  createRequest, head, delete, put, patch, post, options, prepareRequest
+- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
+- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
+- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
+  `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
+  resource, string, or EntityBody into the $options parameter to specify the download location of the response.
+- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
+  default `array()`
+- Added `Guzzle\Stream\StreamInterface::isRepeatable`
+- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
+
+The following methods were removed from interfaces. All of these methods are still available in the concrete classes
+that implement them, but you should update your code to use alternative methods:
+
+- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
+  `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
+  `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or
+  `$client->setDefaultOption('headers/{header_name}', 'value')`. or
+  `$client->setDefaultOption('headers', array('header_name' => 'value'))`.
+- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`.
+- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail.
+- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail.
+- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail.
+- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin.
+- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin.
+- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin.
+
+### Cache plugin breaking changes
+
+- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
+  CacheStorageInterface. These two objects and interface will be removed in a future version.
+- Always setting X-cache headers on cached responses
+- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
+- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
+  $request, Response $response);`
+- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
+- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
+- Added `CacheStorageInterface::purge($url)`
+- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
+  $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
+  CanCacheStrategyInterface $canCache = null)`
+- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`
+
+3.5 to 3.6
+----------
+
+* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
+* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
+* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
+  For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader().
+  Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request.
+* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
+  HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
+  CacheControl header implementation.
+* Moved getLinks() from Response to just be used on a Link header object.
+
+If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the
+HeaderInterface (e.g. toArray(), getAll(), etc.).
+
+### Interface changes
+
+* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
+* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
+* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
+  Guzzle\Http\Curl\RequestMediator
+* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
+* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
+* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
+
+### Removed deprecated functions
+
+* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
+* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
+
+### Deprecations
+
+* The ability to case-insensitively search for header values
+* Guzzle\Http\Message\Header::hasExactHeader
+* Guzzle\Http\Message\Header::raw. Use getAll()
+* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
+  instead.
+
+### Other changes
+
+* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
+* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
+  directly via interfaces
+* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
+  but are a no-op until removed.
+* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a
+  `Guzzle\Service\Command\ArrayCommandInterface`.
+* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
+  on a request while the request is still being transferred
+* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
+
+3.3 to 3.4
+----------
+
+Base URLs of a client now follow the rules of https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2 when merging URLs.
+
+3.2 to 3.3
+----------
+
+### Response::getEtag() quote stripping removed
+
+`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header
+
+### Removed `Guzzle\Http\Utils`
+
+The `Guzzle\Http\Utils` class was removed. This class was only used for testing.
+
+### Stream wrapper and type
+
+`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase.
+
+### curl.emit_io became emit_io
+
+Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the
+'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
+
+3.1 to 3.2
+----------
+
+### CurlMulti is no longer reused globally
+
+Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added
+to a single client can pollute requests dispatched from other clients.
+
+If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the
+ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is
+created.
+
+```php
+$multi = new Guzzle\Http\Curl\CurlMulti();
+$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json');
+$builder->addListener('service_builder.create_client', function ($event) use ($multi) {
+    $event['client']->setCurlMulti($multi);
+}
+});
+```
+
+### No default path
+
+URLs no longer have a default path value of '/' if no path was specified.
+
+Before:
+
+```php
+$request = $client->get('http://www.foo.com');
+echo $request->getUrl();
+// >> http://www.foo.com/
+```
+
+After:
+
+```php
+$request = $client->get('http://www.foo.com');
+echo $request->getUrl();
+// >> http://www.foo.com
+```
+
+### Less verbose BadResponseException
+
+The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and
+response information. You can, however, get access to the request and response object by calling `getRequest()` or
+`getResponse()` on the exception object.
+
+### Query parameter aggregation
+
+Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a
+setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is
+responsible for handling the aggregation of multi-valued query string variables into a flattened hash.
+
+2.8 to 3.x
+----------
+
+### Guzzle\Service\Inspector
+
+Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig`
+
+**Before**
+
+```php
+use Guzzle\Service\Inspector;
+
+class YourClient extends \Guzzle\Service\Client
+{
+    public static function factory($config = array())
+    {
+        $default = array();
+        $required = array('base_url', 'username', 'api_key');
+        $config = Inspector::fromConfig($config, $default, $required);
+
+        $client = new self(
+            $config->get('base_url'),
+            $config->get('username'),
+            $config->get('api_key')
+        );
+        $client->setConfig($config);
+
+        $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
+
+        return $client;
+    }
+```
+
+**After**
+
+```php
+use Guzzle\Common\Collection;
+
+class YourClient extends \Guzzle\Service\Client
+{
+    public static function factory($config = array())
+    {
+        $default = array();
+        $required = array('base_url', 'username', 'api_key');
+        $config = Collection::fromConfig($config, $default, $required);
+
+        $client = new self(
+            $config->get('base_url'),
+            $config->get('username'),
+            $config->get('api_key')
+        );
+        $client->setConfig($config);
+
+        $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
+
+        return $client;
+    }
+```
+
+### Convert XML Service Descriptions to JSON
+
+**Before**
+
+```xml
+
+
+    
+        
+        
+            Get a list of groups
+        
+        
+            Uses a search query to get a list of groups
+            
+        
+        
+            Create a group
+            
+            
+        
+        
+            Delete a group by ID
+            
+        
+        
+            
+        
+        
+            Update a group
+            
+            
+            
+        
+    
+
+```
+
+**After**
+
+```json
+{
+    "name":       "Zendesk REST API v2",
+    "apiVersion": "2012-12-31",
+    "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users",
+    "operations": {
+        "list_groups":  {
+            "httpMethod":"GET",
+            "uri":       "groups.json",
+            "summary":   "Get a list of groups"
+        },
+        "search_groups":{
+            "httpMethod":"GET",
+            "uri":       "search.json?query=\"{query} type:group\"",
+            "summary":   "Uses a search query to get a list of groups",
+            "parameters":{
+                "query":{
+                    "location":   "uri",
+                    "description":"Zendesk Search Query",
+                    "type":       "string",
+                    "required":   true
+                }
+            }
+        },
+        "create_group": {
+            "httpMethod":"POST",
+            "uri":       "groups.json",
+            "summary":   "Create a group",
+            "parameters":{
+                "data":        {
+                    "type":       "array",
+                    "location":   "body",
+                    "description":"Group JSON",
+                    "filters":    "json_encode",
+                    "required":   true
+                },
+                "Content-Type":{
+                    "type":    "string",
+                    "location":"header",
+                    "static":  "application/json"
+                }
+            }
+        },
+        "delete_group": {
+            "httpMethod":"DELETE",
+            "uri":       "groups/{id}.json",
+            "summary":   "Delete a group",
+            "parameters":{
+                "id":{
+                    "location":   "uri",
+                    "description":"Group to delete by ID",
+                    "type":       "integer",
+                    "required":   true
+                }
+            }
+        },
+        "get_group":    {
+            "httpMethod":"GET",
+            "uri":       "groups/{id}.json",
+            "summary":   "Get a ticket",
+            "parameters":{
+                "id":{
+                    "location":   "uri",
+                    "description":"Group to get by ID",
+                    "type":       "integer",
+                    "required":   true
+                }
+            }
+        },
+        "update_group": {
+            "httpMethod":"PUT",
+            "uri":       "groups/{id}.json",
+            "summary":   "Update a group",
+            "parameters":{
+                "id":          {
+                    "location":   "uri",
+                    "description":"Group to update by ID",
+                    "type":       "integer",
+                    "required":   true
+                },
+                "data":        {
+                    "type":       "array",
+                    "location":   "body",
+                    "description":"Group JSON",
+                    "filters":    "json_encode",
+                    "required":   true
+                },
+                "Content-Type":{
+                    "type":    "string",
+                    "location":"header",
+                    "static":  "application/json"
+                }
+            }
+        }
+}
+```
+
+### Guzzle\Service\Description\ServiceDescription
+
+Commands are now called Operations
+
+**Before**
+
+```php
+use Guzzle\Service\Description\ServiceDescription;
+
+$sd = new ServiceDescription();
+$sd->getCommands();     // @returns ApiCommandInterface[]
+$sd->hasCommand($name);
+$sd->getCommand($name); // @returns ApiCommandInterface|null
+$sd->addCommand($command); // @param ApiCommandInterface $command
+```
+
+**After**
+
+```php
+use Guzzle\Service\Description\ServiceDescription;
+
+$sd = new ServiceDescription();
+$sd->getOperations();           // @returns OperationInterface[]
+$sd->hasOperation($name);
+$sd->getOperation($name);       // @returns OperationInterface|null
+$sd->addOperation($operation);  // @param OperationInterface $operation
+```
+
+### Guzzle\Common\Inflection\Inflector
+
+Namespace is now `Guzzle\Inflection\Inflector`
+
+### Guzzle\Http\Plugin
+
+Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below.
+
+### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log
+
+Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively.
+
+**Before**
+
+```php
+use Guzzle\Common\Log\ClosureLogAdapter;
+use Guzzle\Http\Plugin\LogPlugin;
+
+/** @var \Guzzle\Http\Client */
+$client;
+
+// $verbosity is an integer indicating desired message verbosity level
+$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE);
+```
+
+**After**
+
+```php
+use Guzzle\Log\ClosureLogAdapter;
+use Guzzle\Log\MessageFormatter;
+use Guzzle\Plugin\Log\LogPlugin;
+
+/** @var \Guzzle\Http\Client */
+$client;
+
+// $format is a string indicating desired message format -- @see MessageFormatter
+$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT);
+```
+
+### Guzzle\Http\Plugin\CurlAuthPlugin
+
+Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`.
+
+### Guzzle\Http\Plugin\ExponentialBackoffPlugin
+
+Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes.
+
+**Before**
+
+```php
+use Guzzle\Http\Plugin\ExponentialBackoffPlugin;
+
+$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge(
+        ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429)
+    ));
+
+$client->addSubscriber($backoffPlugin);
+```
+
+**After**
+
+```php
+use Guzzle\Plugin\Backoff\BackoffPlugin;
+use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
+
+// Use convenient factory method instead -- see implementation for ideas of what
+// you can do with chaining backoff strategies
+$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge(
+        HttpBackoffStrategy::getDefaultFailureCodes(), array(429)
+    ));
+$client->addSubscriber($backoffPlugin);
+```
+
+### Known Issues
+
+#### [BUG] Accept-Encoding header behavior changed unintentionally.
+
+(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e)
+
+In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to
+properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen.
+See issue #217 for a workaround, or use a version containing the fix.
diff --git a/vendor/guzzlehttp/guzzle/composer.json b/vendor/guzzlehttp/guzzle/composer.json
new file mode 100644
index 0000000..cbede14
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/composer.json
@@ -0,0 +1,131 @@
+{
+    "name": "guzzlehttp/guzzle",
+    "description": "Guzzle is a PHP HTTP client library",
+    "keywords": [
+        "framework",
+        "http",
+        "rest",
+        "web service",
+        "curl",
+        "client",
+        "HTTP client",
+        "PSR-7",
+        "PSR-18"
+    ],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Graham Campbell",
+            "email": "hello@gjcampbell.co.uk",
+            "homepage": "https://github.com/GrahamCampbell"
+        },
+        {
+            "name": "Michael Dowling",
+            "email": "mtdowling@gmail.com",
+            "homepage": "https://github.com/mtdowling"
+        },
+        {
+            "name": "Jeremy Lindblom",
+            "email": "jeremeamia@gmail.com",
+            "homepage": "https://github.com/jeremeamia"
+        },
+        {
+            "name": "George Mponos",
+            "email": "gmponos@gmail.com",
+            "homepage": "https://github.com/gmponos"
+        },
+        {
+            "name": "Tobias Nyholm",
+            "email": "tobias.nyholm@gmail.com",
+            "homepage": "https://github.com/Nyholm"
+        },
+        {
+            "name": "Márk Sági-Kazár",
+            "email": "mark.sagikazar@gmail.com",
+            "homepage": "https://github.com/sagikazarmark"
+        },
+        {
+            "name": "Tobias Schultze",
+            "email": "webmaster@tubo-world.de",
+            "homepage": "https://github.com/Tobion"
+        }
+    ],
+    "repositories": [
+        {
+            "type": "package",
+            "package": {
+                "name": "guzzle/client-integration-tests",
+                "version": "v3.0.2",
+                "dist": {
+                    "url": "https://codeload.github.com/guzzle/client-integration-tests/zip/2c025848417c1135031fdf9c728ee53d0a7ceaee",
+                    "type": "zip"
+                },
+                "require": {
+                    "php": "^7.2.5 || ^8.0",
+                    "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.11",
+                    "php-http/message": "^1.0 || ^2.0",
+                    "guzzlehttp/psr7": "^1.7 || ^2.0",
+                    "th3n3rd/cartesian-product": "^0.3"
+                },
+                "autoload": {
+                    "psr-4": {
+                        "Http\\Client\\Tests\\": "src/"
+                    }
+                },
+                "bin": [
+                    "bin/http_test_server"
+                ]
+            }
+        }
+    ],
+    "require": {
+        "php": "^7.2.5 || ^8.0",
+        "ext-json": "*",
+        "guzzlehttp/promises": "^1.5.3 || ^2.0.3",
+        "guzzlehttp/psr7": "^2.7.0",
+        "psr/http-client": "^1.0",
+        "symfony/deprecation-contracts": "^2.2 || ^3.0"
+    },
+    "provide": {
+        "psr/http-client-implementation": "1.0"
+    },
+    "require-dev": {
+        "ext-curl": "*",
+        "bamarni/composer-bin-plugin": "^1.8.2",
+        "guzzle/client-integration-tests": "3.0.2",
+        "php-http/message-factory": "^1.1",
+        "phpunit/phpunit": "^8.5.39 || ^9.6.20",
+        "psr/log": "^1.1 || ^2.0 || ^3.0"
+    },
+    "suggest": {
+        "ext-curl": "Required for CURL handler support",
+        "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+        "psr/log": "Required for using the Log middleware"
+    },
+    "config": {
+        "allow-plugins": {
+            "bamarni/composer-bin-plugin": true
+        },
+        "preferred-install": "dist",
+        "sort-packages": true
+    },
+    "extra": {
+        "bamarni-bin": {
+            "bin-links": true,
+            "forward-command": false
+        }
+    },
+    "autoload": {
+        "psr-4": {
+            "GuzzleHttp\\": "src/"
+        },
+        "files": [
+            "src/functions_include.php"
+        ]
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "GuzzleHttp\\Tests\\": "tests/"
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/BodySummarizer.php b/vendor/guzzlehttp/guzzle/src/BodySummarizer.php
new file mode 100644
index 0000000..761506d
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/BodySummarizer.php
@@ -0,0 +1,28 @@
+truncateAt = $truncateAt;
+    }
+
+    /**
+     * Returns a summarized message body.
+     */
+    public function summarize(MessageInterface $message): ?string
+    {
+        return $this->truncateAt === null
+            ? Psr7\Message::bodySummary($message)
+            : Psr7\Message::bodySummary($message, $this->truncateAt);
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/BodySummarizerInterface.php b/vendor/guzzlehttp/guzzle/src/BodySummarizerInterface.php
new file mode 100644
index 0000000..3e02e03
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/BodySummarizerInterface.php
@@ -0,0 +1,13 @@
+ 'http://www.foo.com/1.0/',
+     *         'timeout'         => 0,
+     *         'allow_redirects' => false,
+     *         'proxy'           => '192.168.16.1:10'
+     *     ]);
+     *
+     * Client configuration settings include the following options:
+     *
+     * - handler: (callable) Function that transfers HTTP requests over the
+     *   wire. The function is called with a Psr7\Http\Message\RequestInterface
+     *   and array of transfer options, and must return a
+     *   GuzzleHttp\Promise\PromiseInterface that is fulfilled with a
+     *   Psr7\Http\Message\ResponseInterface on success.
+     *   If no handler is provided, a default handler will be created
+     *   that enables all of the request options below by attaching all of the
+     *   default middleware to the handler.
+     * - base_uri: (string|UriInterface) Base URI of the client that is merged
+     *   into relative URIs. Can be a string or instance of UriInterface.
+     * - **: any request option
+     *
+     * @param array $config Client configuration settings.
+     *
+     * @see RequestOptions for a list of available request options.
+     */
+    public function __construct(array $config = [])
+    {
+        if (!isset($config['handler'])) {
+            $config['handler'] = HandlerStack::create();
+        } elseif (!\is_callable($config['handler'])) {
+            throw new InvalidArgumentException('handler must be a callable');
+        }
+
+        // Convert the base_uri to a UriInterface
+        if (isset($config['base_uri'])) {
+            $config['base_uri'] = Psr7\Utils::uriFor($config['base_uri']);
+        }
+
+        $this->configureDefaults($config);
+    }
+
+    /**
+     * @param string $method
+     * @param array  $args
+     *
+     * @return PromiseInterface|ResponseInterface
+     *
+     * @deprecated Client::__call will be removed in guzzlehttp/guzzle:8.0.
+     */
+    public function __call($method, $args)
+    {
+        if (\count($args) < 1) {
+            throw new InvalidArgumentException('Magic request methods require a URI and optional options array');
+        }
+
+        $uri = $args[0];
+        $opts = $args[1] ?? [];
+
+        return \substr($method, -5) === 'Async'
+            ? $this->requestAsync(\substr($method, 0, -5), $uri, $opts)
+            : $this->request($method, $uri, $opts);
+    }
+
+    /**
+     * Asynchronously send an HTTP request.
+     *
+     * @param array $options Request options to apply to the given
+     *                       request and to the transfer. See \GuzzleHttp\RequestOptions.
+     */
+    public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface
+    {
+        // Merge the base URI into the request URI if needed.
+        $options = $this->prepareDefaults($options);
+
+        return $this->transfer(
+            $request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')),
+            $options
+        );
+    }
+
+    /**
+     * Send an HTTP request.
+     *
+     * @param array $options Request options to apply to the given
+     *                       request and to the transfer. See \GuzzleHttp\RequestOptions.
+     *
+     * @throws GuzzleException
+     */
+    public function send(RequestInterface $request, array $options = []): ResponseInterface
+    {
+        $options[RequestOptions::SYNCHRONOUS] = true;
+
+        return $this->sendAsync($request, $options)->wait();
+    }
+
+    /**
+     * The HttpClient PSR (PSR-18) specify this method.
+     *
+     * {@inheritDoc}
+     */
+    public function sendRequest(RequestInterface $request): ResponseInterface
+    {
+        $options[RequestOptions::SYNCHRONOUS] = true;
+        $options[RequestOptions::ALLOW_REDIRECTS] = false;
+        $options[RequestOptions::HTTP_ERRORS] = false;
+
+        return $this->sendAsync($request, $options)->wait();
+    }
+
+    /**
+     * Create and send an asynchronous HTTP request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well. Use an array to provide a URL
+     * template and additional variables to use in the URL template expansion.
+     *
+     * @param string              $method  HTTP method
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply. See \GuzzleHttp\RequestOptions.
+     */
+    public function requestAsync(string $method, $uri = '', array $options = []): PromiseInterface
+    {
+        $options = $this->prepareDefaults($options);
+        // Remove request modifying parameter because it can be done up-front.
+        $headers = $options['headers'] ?? [];
+        $body = $options['body'] ?? null;
+        $version = $options['version'] ?? '1.1';
+        // Merge the URI into the base URI.
+        $uri = $this->buildUri(Psr7\Utils::uriFor($uri), $options);
+        if (\is_array($body)) {
+            throw $this->invalidBody();
+        }
+        $request = new Psr7\Request($method, $uri, $headers, $body, $version);
+        // Remove the option so that they are not doubly-applied.
+        unset($options['headers'], $options['body'], $options['version']);
+
+        return $this->transfer($request, $options);
+    }
+
+    /**
+     * Create and send an HTTP request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well.
+     *
+     * @param string              $method  HTTP method.
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply. See \GuzzleHttp\RequestOptions.
+     *
+     * @throws GuzzleException
+     */
+    public function request(string $method, $uri = '', array $options = []): ResponseInterface
+    {
+        $options[RequestOptions::SYNCHRONOUS] = true;
+
+        return $this->requestAsync($method, $uri, $options)->wait();
+    }
+
+    /**
+     * Get a client configuration option.
+     *
+     * These options include default request options of the client, a "handler"
+     * (if utilized by the concrete client), and a "base_uri" if utilized by
+     * the concrete client.
+     *
+     * @param string|null $option The config option to retrieve.
+     *
+     * @return mixed
+     *
+     * @deprecated Client::getConfig will be removed in guzzlehttp/guzzle:8.0.
+     */
+    public function getConfig(?string $option = null)
+    {
+        return $option === null
+            ? $this->config
+            : ($this->config[$option] ?? null);
+    }
+
+    private function buildUri(UriInterface $uri, array $config): UriInterface
+    {
+        if (isset($config['base_uri'])) {
+            $uri = Psr7\UriResolver::resolve(Psr7\Utils::uriFor($config['base_uri']), $uri);
+        }
+
+        if (isset($config['idn_conversion']) && ($config['idn_conversion'] !== false)) {
+            $idnOptions = ($config['idn_conversion'] === true) ? \IDNA_DEFAULT : $config['idn_conversion'];
+            $uri = Utils::idnUriConvert($uri, $idnOptions);
+        }
+
+        return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;
+    }
+
+    /**
+     * Configures the default options for a client.
+     */
+    private function configureDefaults(array $config): void
+    {
+        $defaults = [
+            'allow_redirects' => RedirectMiddleware::$defaultSettings,
+            'http_errors' => true,
+            'decode_content' => true,
+            'verify' => true,
+            'cookies' => false,
+            'idn_conversion' => false,
+        ];
+
+        // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
+
+        // We can only trust the HTTP_PROXY environment variable in a CLI
+        // process due to the fact that PHP has no reliable mechanism to
+        // get environment variables that start with "HTTP_".
+        if (\PHP_SAPI === 'cli' && ($proxy = Utils::getenv('HTTP_PROXY'))) {
+            $defaults['proxy']['http'] = $proxy;
+        }
+
+        if ($proxy = Utils::getenv('HTTPS_PROXY')) {
+            $defaults['proxy']['https'] = $proxy;
+        }
+
+        if ($noProxy = Utils::getenv('NO_PROXY')) {
+            $cleanedNoProxy = \str_replace(' ', '', $noProxy);
+            $defaults['proxy']['no'] = \explode(',', $cleanedNoProxy);
+        }
+
+        $this->config = $config + $defaults;
+
+        if (!empty($config['cookies']) && $config['cookies'] === true) {
+            $this->config['cookies'] = new CookieJar();
+        }
+
+        // Add the default user-agent header.
+        if (!isset($this->config['headers'])) {
+            $this->config['headers'] = ['User-Agent' => Utils::defaultUserAgent()];
+        } else {
+            // Add the User-Agent header if one was not already set.
+            foreach (\array_keys($this->config['headers']) as $name) {
+                if (\strtolower($name) === 'user-agent') {
+                    return;
+                }
+            }
+            $this->config['headers']['User-Agent'] = Utils::defaultUserAgent();
+        }
+    }
+
+    /**
+     * Merges default options into the array.
+     *
+     * @param array $options Options to modify by reference
+     */
+    private function prepareDefaults(array $options): array
+    {
+        $defaults = $this->config;
+
+        if (!empty($defaults['headers'])) {
+            // Default headers are only added if they are not present.
+            $defaults['_conditional'] = $defaults['headers'];
+            unset($defaults['headers']);
+        }
+
+        // Special handling for headers is required as they are added as
+        // conditional headers and as headers passed to a request ctor.
+        if (\array_key_exists('headers', $options)) {
+            // Allows default headers to be unset.
+            if ($options['headers'] === null) {
+                $defaults['_conditional'] = [];
+                unset($options['headers']);
+            } elseif (!\is_array($options['headers'])) {
+                throw new InvalidArgumentException('headers must be an array');
+            }
+        }
+
+        // Shallow merge defaults underneath options.
+        $result = $options + $defaults;
+
+        // Remove null values.
+        foreach ($result as $k => $v) {
+            if ($v === null) {
+                unset($result[$k]);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Transfers the given request and applies request options.
+     *
+     * The URI of the request is not modified and the request options are used
+     * as-is without merging in default options.
+     *
+     * @param array $options See \GuzzleHttp\RequestOptions.
+     */
+    private function transfer(RequestInterface $request, array $options): PromiseInterface
+    {
+        $request = $this->applyOptions($request, $options);
+        /** @var HandlerStack $handler */
+        $handler = $options['handler'];
+
+        try {
+            return P\Create::promiseFor($handler($request, $options));
+        } catch (\Exception $e) {
+            return P\Create::rejectionFor($e);
+        }
+    }
+
+    /**
+     * Applies the array of request options to a request.
+     */
+    private function applyOptions(RequestInterface $request, array &$options): RequestInterface
+    {
+        $modify = [
+            'set_headers' => [],
+        ];
+
+        if (isset($options['headers'])) {
+            if (array_keys($options['headers']) === range(0, count($options['headers']) - 1)) {
+                throw new InvalidArgumentException('The headers array must have header name as keys.');
+            }
+            $modify['set_headers'] = $options['headers'];
+            unset($options['headers']);
+        }
+
+        if (isset($options['form_params'])) {
+            if (isset($options['multipart'])) {
+                throw new InvalidArgumentException('You cannot use '
+                    .'form_params and multipart at the same time. Use the '
+                    .'form_params option if you want to send application/'
+                    .'x-www-form-urlencoded requests, and the multipart '
+                    .'option to send multipart/form-data requests.');
+            }
+            $options['body'] = \http_build_query($options['form_params'], '', '&');
+            unset($options['form_params']);
+            // Ensure that we don't have the header in different case and set the new value.
+            $options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
+            $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
+        }
+
+        if (isset($options['multipart'])) {
+            $options['body'] = new Psr7\MultipartStream($options['multipart']);
+            unset($options['multipart']);
+        }
+
+        if (isset($options['json'])) {
+            $options['body'] = Utils::jsonEncode($options['json']);
+            unset($options['json']);
+            // Ensure that we don't have the header in different case and set the new value.
+            $options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
+            $options['_conditional']['Content-Type'] = 'application/json';
+        }
+
+        if (!empty($options['decode_content'])
+            && $options['decode_content'] !== true
+        ) {
+            // Ensure that we don't have the header in different case and set the new value.
+            $options['_conditional'] = Psr7\Utils::caselessRemove(['Accept-Encoding'], $options['_conditional']);
+            $modify['set_headers']['Accept-Encoding'] = $options['decode_content'];
+        }
+
+        if (isset($options['body'])) {
+            if (\is_array($options['body'])) {
+                throw $this->invalidBody();
+            }
+            $modify['body'] = Psr7\Utils::streamFor($options['body']);
+            unset($options['body']);
+        }
+
+        if (!empty($options['auth']) && \is_array($options['auth'])) {
+            $value = $options['auth'];
+            $type = isset($value[2]) ? \strtolower($value[2]) : 'basic';
+            switch ($type) {
+                case 'basic':
+                    // Ensure that we don't have the header in different case and set the new value.
+                    $modify['set_headers'] = Psr7\Utils::caselessRemove(['Authorization'], $modify['set_headers']);
+                    $modify['set_headers']['Authorization'] = 'Basic '
+                        .\base64_encode("$value[0]:$value[1]");
+                    break;
+                case 'digest':
+                    // @todo: Do not rely on curl
+                    $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_DIGEST;
+                    $options['curl'][\CURLOPT_USERPWD] = "$value[0]:$value[1]";
+                    break;
+                case 'ntlm':
+                    $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_NTLM;
+                    $options['curl'][\CURLOPT_USERPWD] = "$value[0]:$value[1]";
+                    break;
+            }
+        }
+
+        if (isset($options['query'])) {
+            $value = $options['query'];
+            if (\is_array($value)) {
+                $value = \http_build_query($value, '', '&', \PHP_QUERY_RFC3986);
+            }
+            if (!\is_string($value)) {
+                throw new InvalidArgumentException('query must be a string or array');
+            }
+            $modify['query'] = $value;
+            unset($options['query']);
+        }
+
+        // Ensure that sink is not an invalid value.
+        if (isset($options['sink'])) {
+            // TODO: Add more sink validation?
+            if (\is_bool($options['sink'])) {
+                throw new InvalidArgumentException('sink must not be a boolean');
+            }
+        }
+
+        if (isset($options['version'])) {
+            $modify['version'] = $options['version'];
+        }
+
+        $request = Psr7\Utils::modifyRequest($request, $modify);
+        if ($request->getBody() instanceof Psr7\MultipartStream) {
+            // Use a multipart/form-data POST if a Content-Type is not set.
+            // Ensure that we don't have the header in different case and set the new value.
+            $options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
+            $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
+                .$request->getBody()->getBoundary();
+        }
+
+        // Merge in conditional headers if they are not present.
+        if (isset($options['_conditional'])) {
+            // Build up the changes so it's in a single clone of the message.
+            $modify = [];
+            foreach ($options['_conditional'] as $k => $v) {
+                if (!$request->hasHeader($k)) {
+                    $modify['set_headers'][$k] = $v;
+                }
+            }
+            $request = Psr7\Utils::modifyRequest($request, $modify);
+            // Don't pass this internal value along to middleware/handlers.
+            unset($options['_conditional']);
+        }
+
+        return $request;
+    }
+
+    /**
+     * Return an InvalidArgumentException with pre-set message.
+     */
+    private function invalidBody(): InvalidArgumentException
+    {
+        return new InvalidArgumentException('Passing in the "body" request '
+            .'option as an array to send a request is not supported. '
+            .'Please use the "form_params" request option to send a '
+            .'application/x-www-form-urlencoded request, or the "multipart" '
+            .'request option to send a multipart/form-data request.');
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/ClientInterface.php b/vendor/guzzlehttp/guzzle/src/ClientInterface.php
new file mode 100644
index 0000000..6aaee61
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/ClientInterface.php
@@ -0,0 +1,84 @@
+request('GET', $uri, $options);
+    }
+
+    /**
+     * Create and send an HTTP HEAD request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well.
+     *
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     *
+     * @throws GuzzleException
+     */
+    public function head($uri, array $options = []): ResponseInterface
+    {
+        return $this->request('HEAD', $uri, $options);
+    }
+
+    /**
+     * Create and send an HTTP PUT request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well.
+     *
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     *
+     * @throws GuzzleException
+     */
+    public function put($uri, array $options = []): ResponseInterface
+    {
+        return $this->request('PUT', $uri, $options);
+    }
+
+    /**
+     * Create and send an HTTP POST request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well.
+     *
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     *
+     * @throws GuzzleException
+     */
+    public function post($uri, array $options = []): ResponseInterface
+    {
+        return $this->request('POST', $uri, $options);
+    }
+
+    /**
+     * Create and send an HTTP PATCH request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well.
+     *
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     *
+     * @throws GuzzleException
+     */
+    public function patch($uri, array $options = []): ResponseInterface
+    {
+        return $this->request('PATCH', $uri, $options);
+    }
+
+    /**
+     * Create and send an HTTP DELETE request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well.
+     *
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     *
+     * @throws GuzzleException
+     */
+    public function delete($uri, array $options = []): ResponseInterface
+    {
+        return $this->request('DELETE', $uri, $options);
+    }
+
+    /**
+     * Create and send an asynchronous HTTP request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well. Use an array to provide a URL
+     * template and additional variables to use in the URL template expansion.
+     *
+     * @param string              $method  HTTP method
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     */
+    abstract public function requestAsync(string $method, $uri, array $options = []): PromiseInterface;
+
+    /**
+     * Create and send an asynchronous HTTP GET request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well. Use an array to provide a URL
+     * template and additional variables to use in the URL template expansion.
+     *
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     */
+    public function getAsync($uri, array $options = []): PromiseInterface
+    {
+        return $this->requestAsync('GET', $uri, $options);
+    }
+
+    /**
+     * Create and send an asynchronous HTTP HEAD request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well. Use an array to provide a URL
+     * template and additional variables to use in the URL template expansion.
+     *
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     */
+    public function headAsync($uri, array $options = []): PromiseInterface
+    {
+        return $this->requestAsync('HEAD', $uri, $options);
+    }
+
+    /**
+     * Create and send an asynchronous HTTP PUT request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well. Use an array to provide a URL
+     * template and additional variables to use in the URL template expansion.
+     *
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     */
+    public function putAsync($uri, array $options = []): PromiseInterface
+    {
+        return $this->requestAsync('PUT', $uri, $options);
+    }
+
+    /**
+     * Create and send an asynchronous HTTP POST request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well. Use an array to provide a URL
+     * template and additional variables to use in the URL template expansion.
+     *
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     */
+    public function postAsync($uri, array $options = []): PromiseInterface
+    {
+        return $this->requestAsync('POST', $uri, $options);
+    }
+
+    /**
+     * Create and send an asynchronous HTTP PATCH request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well. Use an array to provide a URL
+     * template and additional variables to use in the URL template expansion.
+     *
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     */
+    public function patchAsync($uri, array $options = []): PromiseInterface
+    {
+        return $this->requestAsync('PATCH', $uri, $options);
+    }
+
+    /**
+     * Create and send an asynchronous HTTP DELETE request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well. Use an array to provide a URL
+     * template and additional variables to use in the URL template expansion.
+     *
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     */
+    public function deleteAsync($uri, array $options = []): PromiseInterface
+    {
+        return $this->requestAsync('DELETE', $uri, $options);
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
new file mode 100644
index 0000000..b616cf2
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
@@ -0,0 +1,307 @@
+strictMode = $strictMode;
+
+        foreach ($cookieArray as $cookie) {
+            if (!($cookie instanceof SetCookie)) {
+                $cookie = new SetCookie($cookie);
+            }
+            $this->setCookie($cookie);
+        }
+    }
+
+    /**
+     * Create a new Cookie jar from an associative array and domain.
+     *
+     * @param array  $cookies Cookies to create the jar from
+     * @param string $domain  Domain to set the cookies to
+     */
+    public static function fromArray(array $cookies, string $domain): self
+    {
+        $cookieJar = new self();
+        foreach ($cookies as $name => $value) {
+            $cookieJar->setCookie(new SetCookie([
+                'Domain' => $domain,
+                'Name' => $name,
+                'Value' => $value,
+                'Discard' => true,
+            ]));
+        }
+
+        return $cookieJar;
+    }
+
+    /**
+     * Evaluate if this cookie should be persisted to storage
+     * that survives between requests.
+     *
+     * @param SetCookie $cookie              Being evaluated.
+     * @param bool      $allowSessionCookies If we should persist session cookies
+     */
+    public static function shouldPersist(SetCookie $cookie, bool $allowSessionCookies = false): bool
+    {
+        if ($cookie->getExpires() || $allowSessionCookies) {
+            if (!$cookie->getDiscard()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Finds and returns the cookie based on the name
+     *
+     * @param string $name cookie name to search for
+     *
+     * @return SetCookie|null cookie that was found or null if not found
+     */
+    public function getCookieByName(string $name): ?SetCookie
+    {
+        foreach ($this->cookies as $cookie) {
+            if ($cookie->getName() !== null && \strcasecmp($cookie->getName(), $name) === 0) {
+                return $cookie;
+            }
+        }
+
+        return null;
+    }
+
+    public function toArray(): array
+    {
+        return \array_map(static function (SetCookie $cookie): array {
+            return $cookie->toArray();
+        }, $this->getIterator()->getArrayCopy());
+    }
+
+    public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void
+    {
+        if (!$domain) {
+            $this->cookies = [];
+
+            return;
+        } elseif (!$path) {
+            $this->cookies = \array_filter(
+                $this->cookies,
+                static function (SetCookie $cookie) use ($domain): bool {
+                    return !$cookie->matchesDomain($domain);
+                }
+            );
+        } elseif (!$name) {
+            $this->cookies = \array_filter(
+                $this->cookies,
+                static function (SetCookie $cookie) use ($path, $domain): bool {
+                    return !($cookie->matchesPath($path)
+                        && $cookie->matchesDomain($domain));
+                }
+            );
+        } else {
+            $this->cookies = \array_filter(
+                $this->cookies,
+                static function (SetCookie $cookie) use ($path, $domain, $name) {
+                    return !($cookie->getName() == $name
+                        && $cookie->matchesPath($path)
+                        && $cookie->matchesDomain($domain));
+                }
+            );
+        }
+    }
+
+    public function clearSessionCookies(): void
+    {
+        $this->cookies = \array_filter(
+            $this->cookies,
+            static function (SetCookie $cookie): bool {
+                return !$cookie->getDiscard() && $cookie->getExpires();
+            }
+        );
+    }
+
+    public function setCookie(SetCookie $cookie): bool
+    {
+        // If the name string is empty (but not 0), ignore the set-cookie
+        // string entirely.
+        $name = $cookie->getName();
+        if (!$name && $name !== '0') {
+            return false;
+        }
+
+        // Only allow cookies with set and valid domain, name, value
+        $result = $cookie->validate();
+        if ($result !== true) {
+            if ($this->strictMode) {
+                throw new \RuntimeException('Invalid cookie: '.$result);
+            }
+            $this->removeCookieIfEmpty($cookie);
+
+            return false;
+        }
+
+        // Resolve conflicts with previously set cookies
+        foreach ($this->cookies as $i => $c) {
+            // Two cookies are identical, when their path, and domain are
+            // identical.
+            if ($c->getPath() != $cookie->getPath()
+                || $c->getDomain() != $cookie->getDomain()
+                || $c->getName() != $cookie->getName()
+            ) {
+                continue;
+            }
+
+            // The previously set cookie is a discard cookie and this one is
+            // not so allow the new cookie to be set
+            if (!$cookie->getDiscard() && $c->getDiscard()) {
+                unset($this->cookies[$i]);
+                continue;
+            }
+
+            // If the new cookie's expiration is further into the future, then
+            // replace the old cookie
+            if ($cookie->getExpires() > $c->getExpires()) {
+                unset($this->cookies[$i]);
+                continue;
+            }
+
+            // If the value has changed, we better change it
+            if ($cookie->getValue() !== $c->getValue()) {
+                unset($this->cookies[$i]);
+                continue;
+            }
+
+            // The cookie exists, so no need to continue
+            return false;
+        }
+
+        $this->cookies[] = $cookie;
+
+        return true;
+    }
+
+    public function count(): int
+    {
+        return \count($this->cookies);
+    }
+
+    /**
+     * @return \ArrayIterator
+     */
+    public function getIterator(): \ArrayIterator
+    {
+        return new \ArrayIterator(\array_values($this->cookies));
+    }
+
+    public function extractCookies(RequestInterface $request, ResponseInterface $response): void
+    {
+        if ($cookieHeader = $response->getHeader('Set-Cookie')) {
+            foreach ($cookieHeader as $cookie) {
+                $sc = SetCookie::fromString($cookie);
+                if (!$sc->getDomain()) {
+                    $sc->setDomain($request->getUri()->getHost());
+                }
+                if (0 !== \strpos($sc->getPath(), '/')) {
+                    $sc->setPath($this->getCookiePathFromRequest($request));
+                }
+                if (!$sc->matchesDomain($request->getUri()->getHost())) {
+                    continue;
+                }
+                // Note: At this point `$sc->getDomain()` being a public suffix should
+                // be rejected, but we don't want to pull in the full PSL dependency.
+                $this->setCookie($sc);
+            }
+        }
+    }
+
+    /**
+     * Computes cookie path following RFC 6265 section 5.1.4
+     *
+     * @see https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4
+     */
+    private function getCookiePathFromRequest(RequestInterface $request): string
+    {
+        $uriPath = $request->getUri()->getPath();
+        if ('' === $uriPath) {
+            return '/';
+        }
+        if (0 !== \strpos($uriPath, '/')) {
+            return '/';
+        }
+        if ('/' === $uriPath) {
+            return '/';
+        }
+        $lastSlashPos = \strrpos($uriPath, '/');
+        if (0 === $lastSlashPos || false === $lastSlashPos) {
+            return '/';
+        }
+
+        return \substr($uriPath, 0, $lastSlashPos);
+    }
+
+    public function withCookieHeader(RequestInterface $request): RequestInterface
+    {
+        $values = [];
+        $uri = $request->getUri();
+        $scheme = $uri->getScheme();
+        $host = $uri->getHost();
+        $path = $uri->getPath() ?: '/';
+
+        foreach ($this->cookies as $cookie) {
+            if ($cookie->matchesPath($path)
+                && $cookie->matchesDomain($host)
+                && !$cookie->isExpired()
+                && (!$cookie->getSecure() || $scheme === 'https')
+            ) {
+                $values[] = $cookie->getName().'='
+                    .$cookie->getValue();
+            }
+        }
+
+        return $values
+            ? $request->withHeader('Cookie', \implode('; ', $values))
+            : $request;
+    }
+
+    /**
+     * If a cookie already exists and the server asks to set it again with a
+     * null value, the cookie must be deleted.
+     */
+    private function removeCookieIfEmpty(SetCookie $cookie): void
+    {
+        $cookieValue = $cookie->getValue();
+        if ($cookieValue === null || $cookieValue === '') {
+            $this->clear(
+                $cookie->getDomain(),
+                $cookie->getPath(),
+                $cookie->getName()
+            );
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php
new file mode 100644
index 0000000..93ada58
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php
@@ -0,0 +1,80 @@
+
+ */
+interface CookieJarInterface extends \Countable, \IteratorAggregate
+{
+    /**
+     * Create a request with added cookie headers.
+     *
+     * If no matching cookies are found in the cookie jar, then no Cookie
+     * header is added to the request and the same request is returned.
+     *
+     * @param RequestInterface $request Request object to modify.
+     *
+     * @return RequestInterface returns the modified request.
+     */
+    public function withCookieHeader(RequestInterface $request): RequestInterface;
+
+    /**
+     * Extract cookies from an HTTP response and store them in the CookieJar.
+     *
+     * @param RequestInterface  $request  Request that was sent
+     * @param ResponseInterface $response Response that was received
+     */
+    public function extractCookies(RequestInterface $request, ResponseInterface $response): void;
+
+    /**
+     * Sets a cookie in the cookie jar.
+     *
+     * @param SetCookie $cookie Cookie to set.
+     *
+     * @return bool Returns true on success or false on failure
+     */
+    public function setCookie(SetCookie $cookie): bool;
+
+    /**
+     * Remove cookies currently held in the cookie jar.
+     *
+     * Invoking this method without arguments will empty the whole cookie jar.
+     * If given a $domain argument only cookies belonging to that domain will
+     * be removed. If given a $domain and $path argument, cookies belonging to
+     * the specified path within that domain are removed. If given all three
+     * arguments, then the cookie with the specified name, path and domain is
+     * removed.
+     *
+     * @param string|null $domain Clears cookies matching a domain
+     * @param string|null $path   Clears cookies matching a domain and path
+     * @param string|null $name   Clears cookies matching a domain, path, and name
+     */
+    public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void;
+
+    /**
+     * Discard all sessions cookies.
+     *
+     * Removes cookies that don't have an expire field or a have a discard
+     * field set to true. To be called when the user agent shuts down according
+     * to RFC 2965.
+     */
+    public function clearSessionCookies(): void;
+
+    /**
+     * Converts the cookie jar to an array.
+     */
+    public function toArray(): array;
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php
new file mode 100644
index 0000000..290236d
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php
@@ -0,0 +1,101 @@
+filename = $cookieFile;
+        $this->storeSessionCookies = $storeSessionCookies;
+
+        if (\file_exists($cookieFile)) {
+            $this->load($cookieFile);
+        }
+    }
+
+    /**
+     * Saves the file when shutting down
+     */
+    public function __destruct()
+    {
+        $this->save($this->filename);
+    }
+
+    /**
+     * Saves the cookies to a file.
+     *
+     * @param string $filename File to save
+     *
+     * @throws \RuntimeException if the file cannot be found or created
+     */
+    public function save(string $filename): void
+    {
+        $json = [];
+        /** @var SetCookie $cookie */
+        foreach ($this as $cookie) {
+            if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
+                $json[] = $cookie->toArray();
+            }
+        }
+
+        $jsonStr = Utils::jsonEncode($json);
+        if (false === \file_put_contents($filename, $jsonStr, \LOCK_EX)) {
+            throw new \RuntimeException("Unable to save file {$filename}");
+        }
+    }
+
+    /**
+     * Load cookies from a JSON formatted file.
+     *
+     * Old cookies are kept unless overwritten by newly loaded ones.
+     *
+     * @param string $filename Cookie file to load.
+     *
+     * @throws \RuntimeException if the file cannot be loaded.
+     */
+    public function load(string $filename): void
+    {
+        $json = \file_get_contents($filename);
+        if (false === $json) {
+            throw new \RuntimeException("Unable to load file {$filename}");
+        }
+        if ($json === '') {
+            return;
+        }
+
+        $data = Utils::jsonDecode($json, true);
+        if (\is_array($data)) {
+            foreach ($data as $cookie) {
+                $this->setCookie(new SetCookie($cookie));
+            }
+        } elseif (\is_scalar($data) && !empty($data)) {
+            throw new \RuntimeException("Invalid cookie file: {$filename}");
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php
new file mode 100644
index 0000000..cb3e67c
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php
@@ -0,0 +1,77 @@
+sessionKey = $sessionKey;
+        $this->storeSessionCookies = $storeSessionCookies;
+        $this->load();
+    }
+
+    /**
+     * Saves cookies to session when shutting down
+     */
+    public function __destruct()
+    {
+        $this->save();
+    }
+
+    /**
+     * Save cookies to the client session
+     */
+    public function save(): void
+    {
+        $json = [];
+        /** @var SetCookie $cookie */
+        foreach ($this as $cookie) {
+            if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
+                $json[] = $cookie->toArray();
+            }
+        }
+
+        $_SESSION[$this->sessionKey] = \json_encode($json);
+    }
+
+    /**
+     * Load the contents of the client session into the data array
+     */
+    protected function load(): void
+    {
+        if (!isset($_SESSION[$this->sessionKey])) {
+            return;
+        }
+        $data = \json_decode($_SESSION[$this->sessionKey], true);
+        if (\is_array($data)) {
+            foreach ($data as $cookie) {
+                $this->setCookie(new SetCookie($cookie));
+            }
+        } elseif (\strlen($data)) {
+            throw new \RuntimeException('Invalid cookie data');
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
new file mode 100644
index 0000000..c9806da
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
@@ -0,0 +1,488 @@
+ null,
+        'Value' => null,
+        'Domain' => null,
+        'Path' => '/',
+        'Max-Age' => null,
+        'Expires' => null,
+        'Secure' => false,
+        'Discard' => false,
+        'HttpOnly' => false,
+    ];
+
+    /**
+     * @var array Cookie data
+     */
+    private $data;
+
+    /**
+     * Create a new SetCookie object from a string.
+     *
+     * @param string $cookie Set-Cookie header string
+     */
+    public static function fromString(string $cookie): self
+    {
+        // Create the default return array
+        $data = self::$defaults;
+        // Explode the cookie string using a series of semicolons
+        $pieces = \array_filter(\array_map('trim', \explode(';', $cookie)));
+        // The name of the cookie (first kvp) must exist and include an equal sign.
+        if (!isset($pieces[0]) || \strpos($pieces[0], '=') === false) {
+            return new self($data);
+        }
+
+        // Add the cookie pieces into the parsed data array
+        foreach ($pieces as $part) {
+            $cookieParts = \explode('=', $part, 2);
+            $key = \trim($cookieParts[0]);
+            $value = isset($cookieParts[1])
+                ? \trim($cookieParts[1], " \n\r\t\0\x0B")
+                : true;
+
+            // Only check for non-cookies when cookies have been found
+            if (!isset($data['Name'])) {
+                $data['Name'] = $key;
+                $data['Value'] = $value;
+            } else {
+                foreach (\array_keys(self::$defaults) as $search) {
+                    if (!\strcasecmp($search, $key)) {
+                        if ($search === 'Max-Age') {
+                            if (is_numeric($value)) {
+                                $data[$search] = (int) $value;
+                            }
+                        } else {
+                            $data[$search] = $value;
+                        }
+                        continue 2;
+                    }
+                }
+                $data[$key] = $value;
+            }
+        }
+
+        return new self($data);
+    }
+
+    /**
+     * @param array $data Array of cookie data provided by a Cookie parser
+     */
+    public function __construct(array $data = [])
+    {
+        $this->data = self::$defaults;
+
+        if (isset($data['Name'])) {
+            $this->setName($data['Name']);
+        }
+
+        if (isset($data['Value'])) {
+            $this->setValue($data['Value']);
+        }
+
+        if (isset($data['Domain'])) {
+            $this->setDomain($data['Domain']);
+        }
+
+        if (isset($data['Path'])) {
+            $this->setPath($data['Path']);
+        }
+
+        if (isset($data['Max-Age'])) {
+            $this->setMaxAge($data['Max-Age']);
+        }
+
+        if (isset($data['Expires'])) {
+            $this->setExpires($data['Expires']);
+        }
+
+        if (isset($data['Secure'])) {
+            $this->setSecure($data['Secure']);
+        }
+
+        if (isset($data['Discard'])) {
+            $this->setDiscard($data['Discard']);
+        }
+
+        if (isset($data['HttpOnly'])) {
+            $this->setHttpOnly($data['HttpOnly']);
+        }
+
+        // Set the remaining values that don't have extra validation logic
+        foreach (array_diff(array_keys($data), array_keys(self::$defaults)) as $key) {
+            $this->data[$key] = $data[$key];
+        }
+
+        // Extract the Expires value and turn it into a UNIX timestamp if needed
+        if (!$this->getExpires() && $this->getMaxAge()) {
+            // Calculate the Expires date
+            $this->setExpires(\time() + $this->getMaxAge());
+        } elseif (null !== ($expires = $this->getExpires()) && !\is_numeric($expires)) {
+            $this->setExpires($expires);
+        }
+    }
+
+    public function __toString()
+    {
+        $str = $this->data['Name'].'='.($this->data['Value'] ?? '').'; ';
+        foreach ($this->data as $k => $v) {
+            if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) {
+                if ($k === 'Expires') {
+                    $str .= 'Expires='.\gmdate('D, d M Y H:i:s \G\M\T', $v).'; ';
+                } else {
+                    $str .= ($v === true ? $k : "{$k}={$v}").'; ';
+                }
+            }
+        }
+
+        return \rtrim($str, '; ');
+    }
+
+    public function toArray(): array
+    {
+        return $this->data;
+    }
+
+    /**
+     * Get the cookie name.
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->data['Name'];
+    }
+
+    /**
+     * Set the cookie name.
+     *
+     * @param string $name Cookie name
+     */
+    public function setName($name): void
+    {
+        if (!is_string($name)) {
+            trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__);
+        }
+
+        $this->data['Name'] = (string) $name;
+    }
+
+    /**
+     * Get the cookie value.
+     *
+     * @return string|null
+     */
+    public function getValue()
+    {
+        return $this->data['Value'];
+    }
+
+    /**
+     * Set the cookie value.
+     *
+     * @param string $value Cookie value
+     */
+    public function setValue($value): void
+    {
+        if (!is_string($value)) {
+            trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__);
+        }
+
+        $this->data['Value'] = (string) $value;
+    }
+
+    /**
+     * Get the domain.
+     *
+     * @return string|null
+     */
+    public function getDomain()
+    {
+        return $this->data['Domain'];
+    }
+
+    /**
+     * Set the domain of the cookie.
+     *
+     * @param string|null $domain
+     */
+    public function setDomain($domain): void
+    {
+        if (!is_string($domain) && null !== $domain) {
+            trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__);
+        }
+
+        $this->data['Domain'] = null === $domain ? null : (string) $domain;
+    }
+
+    /**
+     * Get the path.
+     *
+     * @return string
+     */
+    public function getPath()
+    {
+        return $this->data['Path'];
+    }
+
+    /**
+     * Set the path of the cookie.
+     *
+     * @param string $path Path of the cookie
+     */
+    public function setPath($path): void
+    {
+        if (!is_string($path)) {
+            trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__);
+        }
+
+        $this->data['Path'] = (string) $path;
+    }
+
+    /**
+     * Maximum lifetime of the cookie in seconds.
+     *
+     * @return int|null
+     */
+    public function getMaxAge()
+    {
+        return null === $this->data['Max-Age'] ? null : (int) $this->data['Max-Age'];
+    }
+
+    /**
+     * Set the max-age of the cookie.
+     *
+     * @param int|null $maxAge Max age of the cookie in seconds
+     */
+    public function setMaxAge($maxAge): void
+    {
+        if (!is_int($maxAge) && null !== $maxAge) {
+            trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an int or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__);
+        }
+
+        $this->data['Max-Age'] = $maxAge === null ? null : (int) $maxAge;
+    }
+
+    /**
+     * The UNIX timestamp when the cookie Expires.
+     *
+     * @return string|int|null
+     */
+    public function getExpires()
+    {
+        return $this->data['Expires'];
+    }
+
+    /**
+     * Set the unix timestamp for which the cookie will expire.
+     *
+     * @param int|string|null $timestamp Unix timestamp or any English textual datetime description.
+     */
+    public function setExpires($timestamp): void
+    {
+        if (!is_int($timestamp) && !is_string($timestamp) && null !== $timestamp) {
+            trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an int, string or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__);
+        }
+
+        $this->data['Expires'] = null === $timestamp ? null : (\is_numeric($timestamp) ? (int) $timestamp : \strtotime((string) $timestamp));
+    }
+
+    /**
+     * Get whether or not this is a secure cookie.
+     *
+     * @return bool
+     */
+    public function getSecure()
+    {
+        return $this->data['Secure'];
+    }
+
+    /**
+     * Set whether or not the cookie is secure.
+     *
+     * @param bool $secure Set to true or false if secure
+     */
+    public function setSecure($secure): void
+    {
+        if (!is_bool($secure)) {
+            trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__);
+        }
+
+        $this->data['Secure'] = (bool) $secure;
+    }
+
+    /**
+     * Get whether or not this is a session cookie.
+     *
+     * @return bool|null
+     */
+    public function getDiscard()
+    {
+        return $this->data['Discard'];
+    }
+
+    /**
+     * Set whether or not this is a session cookie.
+     *
+     * @param bool $discard Set to true or false if this is a session cookie
+     */
+    public function setDiscard($discard): void
+    {
+        if (!is_bool($discard)) {
+            trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__);
+        }
+
+        $this->data['Discard'] = (bool) $discard;
+    }
+
+    /**
+     * Get whether or not this is an HTTP only cookie.
+     *
+     * @return bool
+     */
+    public function getHttpOnly()
+    {
+        return $this->data['HttpOnly'];
+    }
+
+    /**
+     * Set whether or not this is an HTTP only cookie.
+     *
+     * @param bool $httpOnly Set to true or false if this is HTTP only
+     */
+    public function setHttpOnly($httpOnly): void
+    {
+        if (!is_bool($httpOnly)) {
+            trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__);
+        }
+
+        $this->data['HttpOnly'] = (bool) $httpOnly;
+    }
+
+    /**
+     * Check if the cookie matches a path value.
+     *
+     * A request-path path-matches a given cookie-path if at least one of
+     * the following conditions holds:
+     *
+     * - The cookie-path and the request-path are identical.
+     * - The cookie-path is a prefix of the request-path, and the last
+     *   character of the cookie-path is %x2F ("/").
+     * - The cookie-path is a prefix of the request-path, and the first
+     *   character of the request-path that is not included in the cookie-
+     *   path is a %x2F ("/") character.
+     *
+     * @param string $requestPath Path to check against
+     */
+    public function matchesPath(string $requestPath): bool
+    {
+        $cookiePath = $this->getPath();
+
+        // Match on exact matches or when path is the default empty "/"
+        if ($cookiePath === '/' || $cookiePath == $requestPath) {
+            return true;
+        }
+
+        // Ensure that the cookie-path is a prefix of the request path.
+        if (0 !== \strpos($requestPath, $cookiePath)) {
+            return false;
+        }
+
+        // Match if the last character of the cookie-path is "/"
+        if (\substr($cookiePath, -1, 1) === '/') {
+            return true;
+        }
+
+        // Match if the first character not included in cookie path is "/"
+        return \substr($requestPath, \strlen($cookiePath), 1) === '/';
+    }
+
+    /**
+     * Check if the cookie matches a domain value.
+     *
+     * @param string $domain Domain to check against
+     */
+    public function matchesDomain(string $domain): bool
+    {
+        $cookieDomain = $this->getDomain();
+        if (null === $cookieDomain) {
+            return true;
+        }
+
+        // Remove the leading '.' as per spec in RFC 6265.
+        // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.3
+        $cookieDomain = \ltrim(\strtolower($cookieDomain), '.');
+
+        $domain = \strtolower($domain);
+
+        // Domain not set or exact match.
+        if ('' === $cookieDomain || $domain === $cookieDomain) {
+            return true;
+        }
+
+        // Matching the subdomain according to RFC 6265.
+        // https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3
+        if (\filter_var($domain, \FILTER_VALIDATE_IP)) {
+            return false;
+        }
+
+        return (bool) \preg_match('/\.'.\preg_quote($cookieDomain, '/').'$/', $domain);
+    }
+
+    /**
+     * Check if the cookie is expired.
+     */
+    public function isExpired(): bool
+    {
+        return $this->getExpires() !== null && \time() > $this->getExpires();
+    }
+
+    /**
+     * Check if the cookie is valid according to RFC 6265.
+     *
+     * @return bool|string Returns true if valid or an error message if invalid
+     */
+    public function validate()
+    {
+        $name = $this->getName();
+        if ($name === '') {
+            return 'The cookie name must not be empty';
+        }
+
+        // Check if any of the invalid characters are present in the cookie name
+        if (\preg_match(
+            '/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/',
+            $name
+        )) {
+            return 'Cookie name must not contain invalid characters: ASCII '
+                .'Control characters (0-31;127), space, tab and the '
+                .'following characters: ()<>@,;:\"/?={}';
+        }
+
+        // Value must not be null. 0 and empty string are valid. Empty strings
+        // are technically against RFC 6265, but known to happen in the wild.
+        $value = $this->getValue();
+        if ($value === null) {
+            return 'The cookie value must not be empty';
+        }
+
+        // Domains must not be empty, but can be 0. "0" is not a valid internet
+        // domain, but may be used as server name in a private network.
+        $domain = $this->getDomain();
+        if ($domain === null || $domain === '') {
+            return 'The cookie domain must not be empty';
+        }
+
+        return true;
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php b/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php
new file mode 100644
index 0000000..ba67ad4
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php
@@ -0,0 +1,39 @@
+request = $request;
+        $this->handlerContext = $handlerContext;
+    }
+
+    /**
+     * Get the request that caused the exception
+     */
+    public function getRequest(): RequestInterface
+    {
+        return $this->request;
+    }
+
+    /**
+     * Get contextual information about the error from the underlying handler.
+     *
+     * The contents of this array will vary depending on which handler you are
+     * using. It may also be just an empty array. Relying on this data will
+     * couple you to a specific handler, but can give more debug information
+     * when needed.
+     */
+    public function getHandlerContext(): array
+    {
+        return $this->handlerContext;
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php b/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php
new file mode 100644
index 0000000..fa3ed69
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php
@@ -0,0 +1,9 @@
+getStatusCode() : 0;
+        parent::__construct($message, $code, $previous);
+        $this->request = $request;
+        $this->response = $response;
+        $this->handlerContext = $handlerContext;
+    }
+
+    /**
+     * Wrap non-RequestExceptions with a RequestException
+     */
+    public static function wrapException(RequestInterface $request, \Throwable $e): RequestException
+    {
+        return $e instanceof RequestException ? $e : new RequestException($e->getMessage(), $request, null, $e);
+    }
+
+    /**
+     * Factory method to create a new exception with a normalized error message
+     *
+     * @param RequestInterface             $request        Request sent
+     * @param ResponseInterface            $response       Response received
+     * @param \Throwable|null              $previous       Previous exception
+     * @param array                        $handlerContext Optional handler context
+     * @param BodySummarizerInterface|null $bodySummarizer Optional body summarizer
+     */
+    public static function create(
+        RequestInterface $request,
+        ?ResponseInterface $response = null,
+        ?\Throwable $previous = null,
+        array $handlerContext = [],
+        ?BodySummarizerInterface $bodySummarizer = null
+    ): self {
+        if (!$response) {
+            return new self(
+                'Error completing request',
+                $request,
+                null,
+                $previous,
+                $handlerContext
+            );
+        }
+
+        $level = (int) \floor($response->getStatusCode() / 100);
+        if ($level === 4) {
+            $label = 'Client error';
+            $className = ClientException::class;
+        } elseif ($level === 5) {
+            $label = 'Server error';
+            $className = ServerException::class;
+        } else {
+            $label = 'Unsuccessful request';
+            $className = __CLASS__;
+        }
+
+        $uri = \GuzzleHttp\Psr7\Utils::redactUserInfo($request->getUri());
+
+        // Client Error: `GET /` resulted in a `404 Not Found` response:
+        //  ... (truncated)
+        $message = \sprintf(
+            '%s: `%s %s` resulted in a `%s %s` response',
+            $label,
+            $request->getMethod(),
+            $uri->__toString(),
+            $response->getStatusCode(),
+            $response->getReasonPhrase()
+        );
+
+        $summary = ($bodySummarizer ?? new BodySummarizer())->summarize($response);
+
+        if ($summary !== null) {
+            $message .= ":\n{$summary}\n";
+        }
+
+        return new $className($message, $request, $response, $previous, $handlerContext);
+    }
+
+    /**
+     * Get the request that caused the exception
+     */
+    public function getRequest(): RequestInterface
+    {
+        return $this->request;
+    }
+
+    /**
+     * Get the associated response
+     */
+    public function getResponse(): ?ResponseInterface
+    {
+        return $this->response;
+    }
+
+    /**
+     * Check if a response was received
+     */
+    public function hasResponse(): bool
+    {
+        return $this->response !== null;
+    }
+
+    /**
+     * Get contextual information about the error from the underlying handler.
+     *
+     * The contents of this array will vary depending on which handler you are
+     * using. It may also be just an empty array. Relying on this data will
+     * couple you to a specific handler, but can give more debug information
+     * when needed.
+     */
+    public function getHandlerContext(): array
+    {
+        return $this->handlerContext;
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php b/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php
new file mode 100644
index 0000000..8055e06
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php
@@ -0,0 +1,10 @@
+maxHandles = $maxHandles;
+    }
+
+    public function create(RequestInterface $request, array $options): EasyHandle
+    {
+        $protocolVersion = $request->getProtocolVersion();
+
+        if ('2' === $protocolVersion || '2.0' === $protocolVersion) {
+            if (!self::supportsHttp2()) {
+                throw new ConnectException('HTTP/2 is supported by the cURL handler, however libcurl is built without HTTP/2 support.', $request);
+            }
+        } elseif ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) {
+            throw new ConnectException(sprintf('HTTP/%s is not supported by the cURL handler.', $protocolVersion), $request);
+        }
+
+        if (isset($options['curl']['body_as_string'])) {
+            $options['_body_as_string'] = $options['curl']['body_as_string'];
+            unset($options['curl']['body_as_string']);
+        }
+
+        $easy = new EasyHandle();
+        $easy->request = $request;
+        $easy->options = $options;
+        $conf = $this->getDefaultConf($easy);
+        $this->applyMethod($easy, $conf);
+        $this->applyHandlerOptions($easy, $conf);
+        $this->applyHeaders($easy, $conf);
+        unset($conf['_headers']);
+
+        // Add handler options from the request configuration options
+        if (isset($options['curl'])) {
+            $conf = \array_replace($conf, $options['curl']);
+        }
+
+        $conf[\CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy);
+        $easy->handle = $this->handles ? \array_pop($this->handles) : \curl_init();
+        curl_setopt_array($easy->handle, $conf);
+
+        return $easy;
+    }
+
+    private static function supportsHttp2(): bool
+    {
+        static $supportsHttp2 = null;
+
+        if (null === $supportsHttp2) {
+            $supportsHttp2 = self::supportsTls12()
+                && defined('CURL_VERSION_HTTP2')
+                && (\CURL_VERSION_HTTP2 & \curl_version()['features']);
+        }
+
+        return $supportsHttp2;
+    }
+
+    private static function supportsTls12(): bool
+    {
+        static $supportsTls12 = null;
+
+        if (null === $supportsTls12) {
+            $supportsTls12 = \CURL_SSLVERSION_TLSv1_2 & \curl_version()['features'];
+        }
+
+        return $supportsTls12;
+    }
+
+    private static function supportsTls13(): bool
+    {
+        static $supportsTls13 = null;
+
+        if (null === $supportsTls13) {
+            $supportsTls13 = defined('CURL_SSLVERSION_TLSv1_3')
+                && (\CURL_SSLVERSION_TLSv1_3 & \curl_version()['features']);
+        }
+
+        return $supportsTls13;
+    }
+
+    public function release(EasyHandle $easy): void
+    {
+        $resource = $easy->handle;
+        unset($easy->handle);
+
+        if (\count($this->handles) >= $this->maxHandles) {
+            \curl_close($resource);
+        } else {
+            // Remove all callback functions as they can hold onto references
+            // and are not cleaned up by curl_reset. Using curl_setopt_array
+            // does not work for some reason, so removing each one
+            // individually.
+            \curl_setopt($resource, \CURLOPT_HEADERFUNCTION, null);
+            \curl_setopt($resource, \CURLOPT_READFUNCTION, null);
+            \curl_setopt($resource, \CURLOPT_WRITEFUNCTION, null);
+            \curl_setopt($resource, \CURLOPT_PROGRESSFUNCTION, null);
+            \curl_reset($resource);
+            $this->handles[] = $resource;
+        }
+    }
+
+    /**
+     * Completes a cURL transaction, either returning a response promise or a
+     * rejected promise.
+     *
+     * @param callable(RequestInterface, array): PromiseInterface $handler
+     * @param CurlFactoryInterface                                $factory Dictates how the handle is released
+     */
+    public static function finish(callable $handler, EasyHandle $easy, CurlFactoryInterface $factory): PromiseInterface
+    {
+        if (isset($easy->options['on_stats'])) {
+            self::invokeStats($easy);
+        }
+
+        if (!$easy->response || $easy->errno) {
+            return self::finishError($handler, $easy, $factory);
+        }
+
+        // Return the response if it is present and there is no error.
+        $factory->release($easy);
+
+        // Rewind the body of the response if possible.
+        $body = $easy->response->getBody();
+        if ($body->isSeekable()) {
+            $body->rewind();
+        }
+
+        return new FulfilledPromise($easy->response);
+    }
+
+    private static function invokeStats(EasyHandle $easy): void
+    {
+        $curlStats = \curl_getinfo($easy->handle);
+        $curlStats['appconnect_time'] = \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME);
+        $stats = new TransferStats(
+            $easy->request,
+            $easy->response,
+            $curlStats['total_time'],
+            $easy->errno,
+            $curlStats
+        );
+        ($easy->options['on_stats'])($stats);
+    }
+
+    /**
+     * @param callable(RequestInterface, array): PromiseInterface $handler
+     */
+    private static function finishError(callable $handler, EasyHandle $easy, CurlFactoryInterface $factory): PromiseInterface
+    {
+        // Get error information and release the handle to the factory.
+        $ctx = [
+            'errno' => $easy->errno,
+            'error' => \curl_error($easy->handle),
+            'appconnect_time' => \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME),
+        ] + \curl_getinfo($easy->handle);
+        $ctx[self::CURL_VERSION_STR] = self::getCurlVersion();
+        $factory->release($easy);
+
+        // Retry when nothing is present or when curl failed to rewind.
+        if (empty($easy->options['_err_message']) && (!$easy->errno || $easy->errno == 65)) {
+            return self::retryFailedRewind($handler, $easy, $ctx);
+        }
+
+        return self::createRejection($easy, $ctx);
+    }
+
+    private static function getCurlVersion(): string
+    {
+        static $curlVersion = null;
+
+        if (null === $curlVersion) {
+            $curlVersion = \curl_version()['version'];
+        }
+
+        return $curlVersion;
+    }
+
+    private static function createRejection(EasyHandle $easy, array $ctx): PromiseInterface
+    {
+        static $connectionErrors = [
+            \CURLE_OPERATION_TIMEOUTED => true,
+            \CURLE_COULDNT_RESOLVE_HOST => true,
+            \CURLE_COULDNT_CONNECT => true,
+            \CURLE_SSL_CONNECT_ERROR => true,
+            \CURLE_GOT_NOTHING => true,
+        ];
+
+        if ($easy->createResponseException) {
+            return P\Create::rejectionFor(
+                new RequestException(
+                    'An error was encountered while creating the response',
+                    $easy->request,
+                    $easy->response,
+                    $easy->createResponseException,
+                    $ctx
+                )
+            );
+        }
+
+        // If an exception was encountered during the onHeaders event, then
+        // return a rejected promise that wraps that exception.
+        if ($easy->onHeadersException) {
+            return P\Create::rejectionFor(
+                new RequestException(
+                    'An error was encountered during the on_headers event',
+                    $easy->request,
+                    $easy->response,
+                    $easy->onHeadersException,
+                    $ctx
+                )
+            );
+        }
+
+        $uri = $easy->request->getUri();
+
+        $sanitizedError = self::sanitizeCurlError($ctx['error'] ?? '', $uri);
+
+        $message = \sprintf(
+            'cURL error %s: %s (%s)',
+            $ctx['errno'],
+            $sanitizedError,
+            'see https://curl.haxx.se/libcurl/c/libcurl-errors.html'
+        );
+
+        if ('' !== $sanitizedError) {
+            $redactedUriString = \GuzzleHttp\Psr7\Utils::redactUserInfo($uri)->__toString();
+            if ($redactedUriString !== '' && false === \strpos($sanitizedError, $redactedUriString)) {
+                $message .= \sprintf(' for %s', $redactedUriString);
+            }
+        }
+
+        // Create a connection exception if it was a specific error code.
+        $error = isset($connectionErrors[$easy->errno])
+            ? new ConnectException($message, $easy->request, null, $ctx)
+            : new RequestException($message, $easy->request, $easy->response, null, $ctx);
+
+        return P\Create::rejectionFor($error);
+    }
+
+    private static function sanitizeCurlError(string $error, UriInterface $uri): string
+    {
+        if ('' === $error) {
+            return $error;
+        }
+
+        $baseUri = $uri->withQuery('')->withFragment('');
+        $baseUriString = $baseUri->__toString();
+
+        if ('' === $baseUriString) {
+            return $error;
+        }
+
+        $redactedUriString = \GuzzleHttp\Psr7\Utils::redactUserInfo($baseUri)->__toString();
+
+        return str_replace($baseUriString, $redactedUriString, $error);
+    }
+
+    /**
+     * @return array
+     */
+    private function getDefaultConf(EasyHandle $easy): array
+    {
+        $conf = [
+            '_headers' => $easy->request->getHeaders(),
+            \CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(),
+            \CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''),
+            \CURLOPT_RETURNTRANSFER => false,
+            \CURLOPT_HEADER => false,
+            \CURLOPT_CONNECTTIMEOUT => 300,
+        ];
+
+        if (\defined('CURLOPT_PROTOCOLS')) {
+            $conf[\CURLOPT_PROTOCOLS] = \CURLPROTO_HTTP | \CURLPROTO_HTTPS;
+        }
+
+        $version = $easy->request->getProtocolVersion();
+
+        if ('2' === $version || '2.0' === $version) {
+            $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_2_0;
+        } elseif ('1.1' === $version) {
+            $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1;
+        } else {
+            $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_0;
+        }
+
+        return $conf;
+    }
+
+    private function applyMethod(EasyHandle $easy, array &$conf): void
+    {
+        $body = $easy->request->getBody();
+        $size = $body->getSize();
+
+        if ($size === null || $size > 0) {
+            $this->applyBody($easy->request, $easy->options, $conf);
+
+            return;
+        }
+
+        $method = $easy->request->getMethod();
+        if ($method === 'PUT' || $method === 'POST') {
+            // See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
+            if (!$easy->request->hasHeader('Content-Length')) {
+                $conf[\CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
+            }
+        } elseif ($method === 'HEAD') {
+            $conf[\CURLOPT_NOBODY] = true;
+            unset(
+                $conf[\CURLOPT_WRITEFUNCTION],
+                $conf[\CURLOPT_READFUNCTION],
+                $conf[\CURLOPT_FILE],
+                $conf[\CURLOPT_INFILE]
+            );
+        }
+    }
+
+    private function applyBody(RequestInterface $request, array $options, array &$conf): void
+    {
+        $size = $request->hasHeader('Content-Length')
+            ? (int) $request->getHeaderLine('Content-Length')
+            : null;
+
+        // Send the body as a string if the size is less than 1MB OR if the
+        // [curl][body_as_string] request value is set.
+        if (($size !== null && $size < 1000000) || !empty($options['_body_as_string'])) {
+            $conf[\CURLOPT_POSTFIELDS] = (string) $request->getBody();
+            // Don't duplicate the Content-Length header
+            $this->removeHeader('Content-Length', $conf);
+            $this->removeHeader('Transfer-Encoding', $conf);
+        } else {
+            $conf[\CURLOPT_UPLOAD] = true;
+            if ($size !== null) {
+                $conf[\CURLOPT_INFILESIZE] = $size;
+                $this->removeHeader('Content-Length', $conf);
+            }
+            $body = $request->getBody();
+            if ($body->isSeekable()) {
+                $body->rewind();
+            }
+            $conf[\CURLOPT_READFUNCTION] = static function ($ch, $fd, $length) use ($body) {
+                return $body->read($length);
+            };
+        }
+
+        // If the Expect header is not present, prevent curl from adding it
+        if (!$request->hasHeader('Expect')) {
+            $conf[\CURLOPT_HTTPHEADER][] = 'Expect:';
+        }
+
+        // cURL sometimes adds a content-type by default. Prevent this.
+        if (!$request->hasHeader('Content-Type')) {
+            $conf[\CURLOPT_HTTPHEADER][] = 'Content-Type:';
+        }
+    }
+
+    private function applyHeaders(EasyHandle $easy, array &$conf): void
+    {
+        foreach ($conf['_headers'] as $name => $values) {
+            foreach ($values as $value) {
+                $value = (string) $value;
+                if ($value === '') {
+                    // cURL requires a special format for empty headers.
+                    // See https://github.com/guzzle/guzzle/issues/1882 for more details.
+                    $conf[\CURLOPT_HTTPHEADER][] = "$name;";
+                } else {
+                    $conf[\CURLOPT_HTTPHEADER][] = "$name: $value";
+                }
+            }
+        }
+
+        // Remove the Accept header if one was not set
+        if (!$easy->request->hasHeader('Accept')) {
+            $conf[\CURLOPT_HTTPHEADER][] = 'Accept:';
+        }
+    }
+
+    /**
+     * Remove a header from the options array.
+     *
+     * @param string $name    Case-insensitive header to remove
+     * @param array  $options Array of options to modify
+     */
+    private function removeHeader(string $name, array &$options): void
+    {
+        foreach (\array_keys($options['_headers']) as $key) {
+            if (!\strcasecmp($key, $name)) {
+                unset($options['_headers'][$key]);
+
+                return;
+            }
+        }
+    }
+
+    private function applyHandlerOptions(EasyHandle $easy, array &$conf): void
+    {
+        $options = $easy->options;
+        if (isset($options['verify'])) {
+            if ($options['verify'] === false) {
+                unset($conf[\CURLOPT_CAINFO]);
+                $conf[\CURLOPT_SSL_VERIFYHOST] = 0;
+                $conf[\CURLOPT_SSL_VERIFYPEER] = false;
+            } else {
+                $conf[\CURLOPT_SSL_VERIFYHOST] = 2;
+                $conf[\CURLOPT_SSL_VERIFYPEER] = true;
+                if (\is_string($options['verify'])) {
+                    // Throw an error if the file/folder/link path is not valid or doesn't exist.
+                    if (!\file_exists($options['verify'])) {
+                        throw new \InvalidArgumentException("SSL CA bundle not found: {$options['verify']}");
+                    }
+                    // If it's a directory or a link to a directory use CURLOPT_CAPATH.
+                    // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
+                    if (
+                        \is_dir($options['verify'])
+                        || (
+                            \is_link($options['verify']) === true
+                            && ($verifyLink = \readlink($options['verify'])) !== false
+                            && \is_dir($verifyLink)
+                        )
+                    ) {
+                        $conf[\CURLOPT_CAPATH] = $options['verify'];
+                    } else {
+                        $conf[\CURLOPT_CAINFO] = $options['verify'];
+                    }
+                }
+            }
+        }
+
+        if (!isset($options['curl'][\CURLOPT_ENCODING]) && !empty($options['decode_content'])) {
+            $accept = $easy->request->getHeaderLine('Accept-Encoding');
+            if ($accept) {
+                $conf[\CURLOPT_ENCODING] = $accept;
+            } else {
+                // The empty string enables all available decoders and implicitly
+                // sets a matching 'Accept-Encoding' header.
+                $conf[\CURLOPT_ENCODING] = '';
+                // But as the user did not specify any encoding preference,
+                // let's leave it up to server by preventing curl from sending
+                // the header, which will be interpreted as 'Accept-Encoding: *'.
+                // https://www.rfc-editor.org/rfc/rfc9110#field.accept-encoding
+                $conf[\CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
+            }
+        }
+
+        if (!isset($options['sink'])) {
+            // Use a default temp stream if no sink was set.
+            $options['sink'] = \GuzzleHttp\Psr7\Utils::tryFopen('php://temp', 'w+');
+        }
+        $sink = $options['sink'];
+        if (!\is_string($sink)) {
+            $sink = \GuzzleHttp\Psr7\Utils::streamFor($sink);
+        } elseif (!\is_dir(\dirname($sink))) {
+            // Ensure that the directory exists before failing in curl.
+            throw new \RuntimeException(\sprintf('Directory %s does not exist for sink value of %s', \dirname($sink), $sink));
+        } else {
+            $sink = new LazyOpenStream($sink, 'w+');
+        }
+        $easy->sink = $sink;
+        $conf[\CURLOPT_WRITEFUNCTION] = static function ($ch, $write) use ($sink): int {
+            return $sink->write($write);
+        };
+
+        $timeoutRequiresNoSignal = false;
+        if (isset($options['timeout'])) {
+            $timeoutRequiresNoSignal |= $options['timeout'] < 1;
+            $conf[\CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
+        }
+
+        // CURL default value is CURL_IPRESOLVE_WHATEVER
+        if (isset($options['force_ip_resolve'])) {
+            if ('v4' === $options['force_ip_resolve']) {
+                $conf[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V4;
+            } elseif ('v6' === $options['force_ip_resolve']) {
+                $conf[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V6;
+            }
+        }
+
+        if (isset($options['connect_timeout'])) {
+            $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1;
+            $conf[\CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000;
+        }
+
+        if ($timeoutRequiresNoSignal && \strtoupper(\substr(\PHP_OS, 0, 3)) !== 'WIN') {
+            $conf[\CURLOPT_NOSIGNAL] = true;
+        }
+
+        if (isset($options['proxy'])) {
+            if (!\is_array($options['proxy'])) {
+                $conf[\CURLOPT_PROXY] = $options['proxy'];
+            } else {
+                $scheme = $easy->request->getUri()->getScheme();
+                if (isset($options['proxy'][$scheme])) {
+                    $host = $easy->request->getUri()->getHost();
+                    if (isset($options['proxy']['no']) && Utils::isHostInNoProxy($host, $options['proxy']['no'])) {
+                        unset($conf[\CURLOPT_PROXY]);
+                    } else {
+                        $conf[\CURLOPT_PROXY] = $options['proxy'][$scheme];
+                    }
+                }
+            }
+        }
+
+        if (isset($options['crypto_method'])) {
+            $protocolVersion = $easy->request->getProtocolVersion();
+
+            // If HTTP/2, upgrade TLS 1.0 and 1.1 to 1.2
+            if ('2' === $protocolVersion || '2.0' === $protocolVersion) {
+                if (
+                    \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']
+                    || \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']
+                    || \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']
+                ) {
+                    $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2;
+                } elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) {
+                    if (!self::supportsTls13()) {
+                        throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL');
+                    }
+                    $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3;
+                } else {
+                    throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
+                }
+            } elseif (\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']) {
+                $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_0;
+            } elseif (\STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']) {
+                $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_1;
+            } elseif (\STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']) {
+                if (!self::supportsTls12()) {
+                    throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.2 not supported by your version of cURL');
+                }
+                $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2;
+            } elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) {
+                if (!self::supportsTls13()) {
+                    throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL');
+                }
+                $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3;
+            } else {
+                throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
+            }
+        }
+
+        if (isset($options['cert'])) {
+            $cert = $options['cert'];
+            if (\is_array($cert)) {
+                $conf[\CURLOPT_SSLCERTPASSWD] = $cert[1];
+                $cert = $cert[0];
+            }
+            if (!\file_exists($cert)) {
+                throw new \InvalidArgumentException("SSL certificate not found: {$cert}");
+            }
+            // OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files.
+            // see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html
+            $ext = pathinfo($cert, \PATHINFO_EXTENSION);
+            if (preg_match('#^(der|p12)$#i', $ext)) {
+                $conf[\CURLOPT_SSLCERTTYPE] = strtoupper($ext);
+            }
+            $conf[\CURLOPT_SSLCERT] = $cert;
+        }
+
+        if (isset($options['ssl_key'])) {
+            if (\is_array($options['ssl_key'])) {
+                if (\count($options['ssl_key']) === 2) {
+                    [$sslKey, $conf[\CURLOPT_SSLKEYPASSWD]] = $options['ssl_key'];
+                } else {
+                    [$sslKey] = $options['ssl_key'];
+                }
+            }
+
+            $sslKey = $sslKey ?? $options['ssl_key'];
+
+            if (!\file_exists($sslKey)) {
+                throw new \InvalidArgumentException("SSL private key not found: {$sslKey}");
+            }
+            $conf[\CURLOPT_SSLKEY] = $sslKey;
+        }
+
+        if (isset($options['progress'])) {
+            $progress = $options['progress'];
+            if (!\is_callable($progress)) {
+                throw new \InvalidArgumentException('progress client option must be callable');
+            }
+            $conf[\CURLOPT_NOPROGRESS] = false;
+            $conf[\CURLOPT_PROGRESSFUNCTION] = static function ($resource, int $downloadSize, int $downloaded, int $uploadSize, int $uploaded) use ($progress) {
+                $progress($downloadSize, $downloaded, $uploadSize, $uploaded);
+            };
+        }
+
+        if (!empty($options['debug'])) {
+            $conf[\CURLOPT_STDERR] = Utils::debugResource($options['debug']);
+            $conf[\CURLOPT_VERBOSE] = true;
+        }
+    }
+
+    /**
+     * This function ensures that a response was set on a transaction. If one
+     * was not set, then the request is retried if possible. This error
+     * typically means you are sending a payload, curl encountered a
+     * "Connection died, retrying a fresh connect" error, tried to rewind the
+     * stream, and then encountered a "necessary data rewind wasn't possible"
+     * error, causing the request to be sent through curl_multi_info_read()
+     * without an error status.
+     *
+     * @param callable(RequestInterface, array): PromiseInterface $handler
+     */
+    private static function retryFailedRewind(callable $handler, EasyHandle $easy, array $ctx): PromiseInterface
+    {
+        try {
+            // Only rewind if the body has been read from.
+            $body = $easy->request->getBody();
+            if ($body->tell() > 0) {
+                $body->rewind();
+            }
+        } catch (\RuntimeException $e) {
+            $ctx['error'] = 'The connection unexpectedly failed without '
+                .'providing an error. The request would have been retried, '
+                .'but attempting to rewind the request body failed. '
+                .'Exception: '.$e;
+
+            return self::createRejection($easy, $ctx);
+        }
+
+        // Retry no more than 3 times before giving up.
+        if (!isset($easy->options['_curl_retries'])) {
+            $easy->options['_curl_retries'] = 1;
+        } elseif ($easy->options['_curl_retries'] == 2) {
+            $ctx['error'] = 'The cURL request was retried 3 times '
+                .'and did not succeed. The most likely reason for the failure '
+                .'is that cURL was unable to rewind the body of the request '
+                .'and subsequent retries resulted in the same error. Turn on '
+                .'the debug option to see what went wrong. See '
+                .'https://bugs.php.net/bug.php?id=47204 for more information.';
+
+            return self::createRejection($easy, $ctx);
+        } else {
+            ++$easy->options['_curl_retries'];
+        }
+
+        return $handler($easy->request, $easy->options);
+    }
+
+    private function createHeaderFn(EasyHandle $easy): callable
+    {
+        if (isset($easy->options['on_headers'])) {
+            $onHeaders = $easy->options['on_headers'];
+
+            if (!\is_callable($onHeaders)) {
+                throw new \InvalidArgumentException('on_headers must be callable');
+            }
+        } else {
+            $onHeaders = null;
+        }
+
+        return static function ($ch, $h) use (
+            $onHeaders,
+            $easy,
+            &$startingResponse
+        ) {
+            $value = \trim($h);
+            if ($value === '') {
+                $startingResponse = true;
+                try {
+                    $easy->createResponse();
+                } catch (\Exception $e) {
+                    $easy->createResponseException = $e;
+
+                    return -1;
+                }
+                if ($onHeaders !== null) {
+                    try {
+                        $onHeaders($easy->response);
+                    } catch (\Exception $e) {
+                        // Associate the exception with the handle and trigger
+                        // a curl header write error by returning 0.
+                        $easy->onHeadersException = $e;
+
+                        return -1;
+                    }
+                }
+            } elseif ($startingResponse) {
+                $startingResponse = false;
+                $easy->headers = [$value];
+            } else {
+                $easy->headers[] = $value;
+            }
+
+            return \strlen($h);
+        };
+    }
+
+    public function __destruct()
+    {
+        foreach ($this->handles as $id => $handle) {
+            \curl_close($handle);
+            unset($this->handles[$id]);
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php
new file mode 100644
index 0000000..fe57ed5
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php
@@ -0,0 +1,25 @@
+factory = $options['handle_factory']
+            ?? new CurlFactory(3);
+    }
+
+    public function __invoke(RequestInterface $request, array $options): PromiseInterface
+    {
+        if (isset($options['delay'])) {
+            \usleep($options['delay'] * 1000);
+        }
+
+        $easy = $this->factory->create($request, $options);
+        \curl_exec($easy->handle);
+        $easy->errno = \curl_errno($easy->handle);
+
+        return CurlFactory::finish($this, $easy, $this->factory);
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php
new file mode 100644
index 0000000..73a6abe
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php
@@ -0,0 +1,284 @@
+ An array of delay times, indexed by handle id in `addRequest`.
+     *
+     * @see CurlMultiHandler::addRequest
+     */
+    private $delays = [];
+
+    /**
+     * @var array An associative array of CURLMOPT_* options and corresponding values for curl_multi_setopt()
+     */
+    private $options = [];
+
+    /** @var resource|\CurlMultiHandle */
+    private $_mh;
+
+    /**
+     * This handler accepts the following options:
+     *
+     * - handle_factory: An optional factory  used to create curl handles
+     * - select_timeout: Optional timeout (in seconds) to block before timing
+     *   out while selecting curl handles. Defaults to 1 second.
+     * - options: An associative array of CURLMOPT_* options and
+     *   corresponding values for curl_multi_setopt()
+     */
+    public function __construct(array $options = [])
+    {
+        $this->factory = $options['handle_factory'] ?? new CurlFactory(50);
+
+        if (isset($options['select_timeout'])) {
+            $this->selectTimeout = $options['select_timeout'];
+        } elseif ($selectTimeout = Utils::getenv('GUZZLE_CURL_SELECT_TIMEOUT')) {
+            @trigger_error('Since guzzlehttp/guzzle 7.2.0: Using environment variable GUZZLE_CURL_SELECT_TIMEOUT is deprecated. Use option "select_timeout" instead.', \E_USER_DEPRECATED);
+            $this->selectTimeout = (int) $selectTimeout;
+        } else {
+            $this->selectTimeout = 1;
+        }
+
+        $this->options = $options['options'] ?? [];
+
+        // unsetting the property forces the first access to go through
+        // __get().
+        unset($this->_mh);
+    }
+
+    /**
+     * @param string $name
+     *
+     * @return resource|\CurlMultiHandle
+     *
+     * @throws \BadMethodCallException when another field as `_mh` will be gotten
+     * @throws \RuntimeException       when curl can not initialize a multi handle
+     */
+    public function __get($name)
+    {
+        if ($name !== '_mh') {
+            throw new \BadMethodCallException("Can not get other property as '_mh'.");
+        }
+
+        $multiHandle = \curl_multi_init();
+
+        if (false === $multiHandle) {
+            throw new \RuntimeException('Can not initialize curl multi handle.');
+        }
+
+        $this->_mh = $multiHandle;
+
+        foreach ($this->options as $option => $value) {
+            // A warning is raised in case of a wrong option.
+            curl_multi_setopt($this->_mh, $option, $value);
+        }
+
+        return $this->_mh;
+    }
+
+    public function __destruct()
+    {
+        if (isset($this->_mh)) {
+            \curl_multi_close($this->_mh);
+            unset($this->_mh);
+        }
+    }
+
+    public function __invoke(RequestInterface $request, array $options): PromiseInterface
+    {
+        $easy = $this->factory->create($request, $options);
+        $id = (int) $easy->handle;
+
+        $promise = new Promise(
+            [$this, 'execute'],
+            function () use ($id) {
+                return $this->cancel($id);
+            }
+        );
+
+        $this->addRequest(['easy' => $easy, 'deferred' => $promise]);
+
+        return $promise;
+    }
+
+    /**
+     * Ticks the curl event loop.
+     */
+    public function tick(): void
+    {
+        // Add any delayed handles if needed.
+        if ($this->delays) {
+            $currentTime = Utils::currentTime();
+            foreach ($this->delays as $id => $delay) {
+                if ($currentTime >= $delay) {
+                    unset($this->delays[$id]);
+                    \curl_multi_add_handle(
+                        $this->_mh,
+                        $this->handles[$id]['easy']->handle
+                    );
+                }
+            }
+        }
+
+        // Run curl_multi_exec in the queue to enable other async tasks to run
+        P\Utils::queue()->add(Closure::fromCallable([$this, 'tickInQueue']));
+
+        // Step through the task queue which may add additional requests.
+        P\Utils::queue()->run();
+
+        if ($this->active && \curl_multi_select($this->_mh, $this->selectTimeout) === -1) {
+            // Perform a usleep if a select returns -1.
+            // See: https://bugs.php.net/bug.php?id=61141
+            \usleep(250);
+        }
+
+        while (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM) {
+            // Prevent busy looping for slow HTTP requests.
+            \curl_multi_select($this->_mh, $this->selectTimeout);
+        }
+
+        $this->processMessages();
+    }
+
+    /**
+     * Runs \curl_multi_exec() inside the event loop, to prevent busy looping
+     */
+    private function tickInQueue(): void
+    {
+        if (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM) {
+            \curl_multi_select($this->_mh, 0);
+            P\Utils::queue()->add(Closure::fromCallable([$this, 'tickInQueue']));
+        }
+    }
+
+    /**
+     * Runs until all outstanding connections have completed.
+     */
+    public function execute(): void
+    {
+        $queue = P\Utils::queue();
+
+        while ($this->handles || !$queue->isEmpty()) {
+            // If there are no transfers, then sleep for the next delay
+            if (!$this->active && $this->delays) {
+                \usleep($this->timeToNext());
+            }
+            $this->tick();
+        }
+    }
+
+    private function addRequest(array $entry): void
+    {
+        $easy = $entry['easy'];
+        $id = (int) $easy->handle;
+        $this->handles[$id] = $entry;
+        if (empty($easy->options['delay'])) {
+            \curl_multi_add_handle($this->_mh, $easy->handle);
+        } else {
+            $this->delays[$id] = Utils::currentTime() + ($easy->options['delay'] / 1000);
+        }
+    }
+
+    /**
+     * Cancels a handle from sending and removes references to it.
+     *
+     * @param int $id Handle ID to cancel and remove.
+     *
+     * @return bool True on success, false on failure.
+     */
+    private function cancel($id): bool
+    {
+        if (!is_int($id)) {
+            trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an integer to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__);
+        }
+
+        // Cannot cancel if it has been processed.
+        if (!isset($this->handles[$id])) {
+            return false;
+        }
+
+        $handle = $this->handles[$id]['easy']->handle;
+        unset($this->delays[$id], $this->handles[$id]);
+        \curl_multi_remove_handle($this->_mh, $handle);
+        \curl_close($handle);
+
+        return true;
+    }
+
+    private function processMessages(): void
+    {
+        while ($done = \curl_multi_info_read($this->_mh)) {
+            if ($done['msg'] !== \CURLMSG_DONE) {
+                // if it's not done, then it would be premature to remove the handle. ref https://github.com/guzzle/guzzle/pull/2892#issuecomment-945150216
+                continue;
+            }
+            $id = (int) $done['handle'];
+            \curl_multi_remove_handle($this->_mh, $done['handle']);
+
+            if (!isset($this->handles[$id])) {
+                // Probably was cancelled.
+                continue;
+            }
+
+            $entry = $this->handles[$id];
+            unset($this->handles[$id], $this->delays[$id]);
+            $entry['easy']->errno = $done['result'];
+            $entry['deferred']->resolve(
+                CurlFactory::finish($this, $entry['easy'], $this->factory)
+            );
+        }
+    }
+
+    private function timeToNext(): int
+    {
+        $currentTime = Utils::currentTime();
+        $nextTime = \PHP_INT_MAX;
+        foreach ($this->delays as $time) {
+            if ($time < $nextTime) {
+                $nextTime = $time;
+            }
+        }
+
+        return ((int) \max(0, $nextTime - $currentTime)) * 1000000;
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php b/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php
new file mode 100644
index 0000000..1bc39f4
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php
@@ -0,0 +1,112 @@
+headers);
+
+        $normalizedKeys = Utils::normalizeHeaderKeys($headers);
+
+        if (!empty($this->options['decode_content']) && isset($normalizedKeys['content-encoding'])) {
+            $headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']];
+            unset($headers[$normalizedKeys['content-encoding']]);
+            if (isset($normalizedKeys['content-length'])) {
+                $headers['x-encoded-content-length'] = $headers[$normalizedKeys['content-length']];
+
+                $bodyLength = (int) $this->sink->getSize();
+                if ($bodyLength) {
+                    $headers[$normalizedKeys['content-length']] = $bodyLength;
+                } else {
+                    unset($headers[$normalizedKeys['content-length']]);
+                }
+            }
+        }
+
+        // Attach a response to the easy handle with the parsed headers.
+        $this->response = new Response(
+            $status,
+            $headers,
+            $this->sink,
+            $ver,
+            $reason
+        );
+    }
+
+    /**
+     * @param string $name
+     *
+     * @return void
+     *
+     * @throws \BadMethodCallException
+     */
+    public function __get($name)
+    {
+        $msg = $name === 'handle' ? 'The EasyHandle has been released' : 'Invalid property: '.$name;
+        throw new \BadMethodCallException($msg);
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php b/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php
new file mode 100644
index 0000000..5554b8f
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php
@@ -0,0 +1,42 @@
+|null $queue       The parameters to be passed to the append function, as an indexed array.
+     * @param callable|null          $onFulfilled Callback to invoke when the return value is fulfilled.
+     * @param callable|null          $onRejected  Callback to invoke when the return value is rejected.
+     */
+    public function __construct(?array $queue = null, ?callable $onFulfilled = null, ?callable $onRejected = null)
+    {
+        $this->onFulfilled = $onFulfilled;
+        $this->onRejected = $onRejected;
+
+        if ($queue) {
+            // array_values included for BC
+            $this->append(...array_values($queue));
+        }
+    }
+
+    public function __invoke(RequestInterface $request, array $options): PromiseInterface
+    {
+        if (!$this->queue) {
+            throw new \OutOfBoundsException('Mock queue is empty');
+        }
+
+        if (isset($options['delay']) && \is_numeric($options['delay'])) {
+            \usleep((int) $options['delay'] * 1000);
+        }
+
+        $this->lastRequest = $request;
+        $this->lastOptions = $options;
+        $response = \array_shift($this->queue);
+
+        if (isset($options['on_headers'])) {
+            if (!\is_callable($options['on_headers'])) {
+                throw new \InvalidArgumentException('on_headers must be callable');
+            }
+            try {
+                $options['on_headers']($response);
+            } catch (\Exception $e) {
+                $msg = 'An error was encountered during the on_headers event';
+                $response = new RequestException($msg, $request, $response, $e);
+            }
+        }
+
+        if (\is_callable($response)) {
+            $response = $response($request, $options);
+        }
+
+        $response = $response instanceof \Throwable
+            ? P\Create::rejectionFor($response)
+            : P\Create::promiseFor($response);
+
+        return $response->then(
+            function (?ResponseInterface $value) use ($request, $options) {
+                $this->invokeStats($request, $options, $value);
+                if ($this->onFulfilled) {
+                    ($this->onFulfilled)($value);
+                }
+
+                if ($value !== null && isset($options['sink'])) {
+                    $contents = (string) $value->getBody();
+                    $sink = $options['sink'];
+
+                    if (\is_resource($sink)) {
+                        \fwrite($sink, $contents);
+                    } elseif (\is_string($sink)) {
+                        \file_put_contents($sink, $contents);
+                    } elseif ($sink instanceof StreamInterface) {
+                        $sink->write($contents);
+                    }
+                }
+
+                return $value;
+            },
+            function ($reason) use ($request, $options) {
+                $this->invokeStats($request, $options, null, $reason);
+                if ($this->onRejected) {
+                    ($this->onRejected)($reason);
+                }
+
+                return P\Create::rejectionFor($reason);
+            }
+        );
+    }
+
+    /**
+     * Adds one or more variadic requests, exceptions, callables, or promises
+     * to the queue.
+     *
+     * @param mixed ...$values
+     */
+    public function append(...$values): void
+    {
+        foreach ($values as $value) {
+            if ($value instanceof ResponseInterface
+                || $value instanceof \Throwable
+                || $value instanceof PromiseInterface
+                || \is_callable($value)
+            ) {
+                $this->queue[] = $value;
+            } else {
+                throw new \TypeError('Expected a Response, Promise, Throwable or callable. Found '.Utils::describeType($value));
+            }
+        }
+    }
+
+    /**
+     * Get the last received request.
+     */
+    public function getLastRequest(): ?RequestInterface
+    {
+        return $this->lastRequest;
+    }
+
+    /**
+     * Get the last received request options.
+     */
+    public function getLastOptions(): array
+    {
+        return $this->lastOptions;
+    }
+
+    /**
+     * Returns the number of remaining items in the queue.
+     */
+    public function count(): int
+    {
+        return \count($this->queue);
+    }
+
+    public function reset(): void
+    {
+        $this->queue = [];
+    }
+
+    /**
+     * @param mixed $reason Promise or reason.
+     */
+    private function invokeStats(
+        RequestInterface $request,
+        array $options,
+        ?ResponseInterface $response = null,
+        $reason = null
+    ): void {
+        if (isset($options['on_stats'])) {
+            $transferTime = $options['transfer_time'] ?? 0;
+            $stats = new TransferStats($request, $response, $transferTime, $reason);
+            ($options['on_stats'])($stats);
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php b/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php
new file mode 100644
index 0000000..f045b52
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php
@@ -0,0 +1,51 @@
+getProtocolVersion();
+
+        if ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) {
+            throw new ConnectException(sprintf('HTTP/%s is not supported by the stream handler.', $protocolVersion), $request);
+        }
+
+        $startTime = isset($options['on_stats']) ? Utils::currentTime() : null;
+
+        try {
+            // Does not support the expect header.
+            $request = $request->withoutHeader('Expect');
+
+            // Append a content-length header if body size is zero to match
+            // cURL's behavior.
+            if (0 === $request->getBody()->getSize()) {
+                $request = $request->withHeader('Content-Length', '0');
+            }
+
+            return $this->createResponse(
+                $request,
+                $options,
+                $this->createStream($request, $options),
+                $startTime
+            );
+        } catch (\InvalidArgumentException $e) {
+            throw $e;
+        } catch (\Exception $e) {
+            // Determine if the error was a networking error.
+            $message = $e->getMessage();
+            // This list can probably get more comprehensive.
+            if (false !== \strpos($message, 'getaddrinfo') // DNS lookup failed
+                || false !== \strpos($message, 'Connection refused')
+                || false !== \strpos($message, "couldn't connect to host") // error on HHVM
+                || false !== \strpos($message, 'connection attempt failed')
+            ) {
+                $e = new ConnectException($e->getMessage(), $request, $e);
+            } else {
+                $e = RequestException::wrapException($request, $e);
+            }
+            $this->invokeStats($options, $request, $startTime, null, $e);
+
+            return P\Create::rejectionFor($e);
+        }
+    }
+
+    private function invokeStats(
+        array $options,
+        RequestInterface $request,
+        ?float $startTime,
+        ?ResponseInterface $response = null,
+        ?\Throwable $error = null
+    ): void {
+        if (isset($options['on_stats'])) {
+            $stats = new TransferStats($request, $response, Utils::currentTime() - $startTime, $error, []);
+            ($options['on_stats'])($stats);
+        }
+    }
+
+    /**
+     * @param resource $stream
+     */
+    private function createResponse(RequestInterface $request, array $options, $stream, ?float $startTime): PromiseInterface
+    {
+        $hdrs = $this->lastHeaders;
+        $this->lastHeaders = [];
+
+        try {
+            [$ver, $status, $reason, $headers] = HeaderProcessor::parseHeaders($hdrs);
+        } catch (\Exception $e) {
+            return P\Create::rejectionFor(
+                new RequestException('An error was encountered while creating the response', $request, null, $e)
+            );
+        }
+
+        [$stream, $headers] = $this->checkDecode($options, $headers, $stream);
+        $stream = Psr7\Utils::streamFor($stream);
+        $sink = $stream;
+
+        if (\strcasecmp('HEAD', $request->getMethod())) {
+            $sink = $this->createSink($stream, $options);
+        }
+
+        try {
+            $response = new Psr7\Response($status, $headers, $sink, $ver, $reason);
+        } catch (\Exception $e) {
+            return P\Create::rejectionFor(
+                new RequestException('An error was encountered while creating the response', $request, null, $e)
+            );
+        }
+
+        if (isset($options['on_headers'])) {
+            try {
+                $options['on_headers']($response);
+            } catch (\Exception $e) {
+                return P\Create::rejectionFor(
+                    new RequestException('An error was encountered during the on_headers event', $request, $response, $e)
+                );
+            }
+        }
+
+        // Do not drain when the request is a HEAD request because they have
+        // no body.
+        if ($sink !== $stream) {
+            $this->drain($stream, $sink, $response->getHeaderLine('Content-Length'));
+        }
+
+        $this->invokeStats($options, $request, $startTime, $response, null);
+
+        return new FulfilledPromise($response);
+    }
+
+    private function createSink(StreamInterface $stream, array $options): StreamInterface
+    {
+        if (!empty($options['stream'])) {
+            return $stream;
+        }
+
+        $sink = $options['sink'] ?? Psr7\Utils::tryFopen('php://temp', 'r+');
+
+        return \is_string($sink) ? new Psr7\LazyOpenStream($sink, 'w+') : Psr7\Utils::streamFor($sink);
+    }
+
+    /**
+     * @param resource $stream
+     */
+    private function checkDecode(array $options, array $headers, $stream): array
+    {
+        // Automatically decode responses when instructed.
+        if (!empty($options['decode_content'])) {
+            $normalizedKeys = Utils::normalizeHeaderKeys($headers);
+            if (isset($normalizedKeys['content-encoding'])) {
+                $encoding = $headers[$normalizedKeys['content-encoding']];
+                if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') {
+                    $stream = new Psr7\InflateStream(Psr7\Utils::streamFor($stream));
+                    $headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']];
+
+                    // Remove content-encoding header
+                    unset($headers[$normalizedKeys['content-encoding']]);
+
+                    // Fix content-length header
+                    if (isset($normalizedKeys['content-length'])) {
+                        $headers['x-encoded-content-length'] = $headers[$normalizedKeys['content-length']];
+                        $length = (int) $stream->getSize();
+                        if ($length === 0) {
+                            unset($headers[$normalizedKeys['content-length']]);
+                        } else {
+                            $headers[$normalizedKeys['content-length']] = [$length];
+                        }
+                    }
+                }
+            }
+        }
+
+        return [$stream, $headers];
+    }
+
+    /**
+     * Drains the source stream into the "sink" client option.
+     *
+     * @param string $contentLength Header specifying the amount of
+     *                              data to read.
+     *
+     * @throws \RuntimeException when the sink option is invalid.
+     */
+    private function drain(StreamInterface $source, StreamInterface $sink, string $contentLength): StreamInterface
+    {
+        // If a content-length header is provided, then stop reading once
+        // that number of bytes has been read. This can prevent infinitely
+        // reading from a stream when dealing with servers that do not honor
+        // Connection: Close headers.
+        Psr7\Utils::copyToStream(
+            $source,
+            $sink,
+            (\strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1
+        );
+
+        $sink->seek(0);
+        $source->close();
+
+        return $sink;
+    }
+
+    /**
+     * Create a resource and check to ensure it was created successfully
+     *
+     * @param callable $callback Callable that returns stream resource
+     *
+     * @return resource
+     *
+     * @throws \RuntimeException on error
+     */
+    private function createResource(callable $callback)
+    {
+        $errors = [];
+        \set_error_handler(static function ($_, $msg, $file, $line) use (&$errors): bool {
+            $errors[] = [
+                'message' => $msg,
+                'file' => $file,
+                'line' => $line,
+            ];
+
+            return true;
+        });
+
+        try {
+            $resource = $callback();
+        } finally {
+            \restore_error_handler();
+        }
+
+        if (!$resource) {
+            $message = 'Error creating resource: ';
+            foreach ($errors as $err) {
+                foreach ($err as $key => $value) {
+                    $message .= "[$key] $value".\PHP_EOL;
+                }
+            }
+            throw new \RuntimeException(\trim($message));
+        }
+
+        return $resource;
+    }
+
+    /**
+     * @return resource
+     */
+    private function createStream(RequestInterface $request, array $options)
+    {
+        static $methods;
+        if (!$methods) {
+            $methods = \array_flip(\get_class_methods(__CLASS__));
+        }
+
+        if (!\in_array($request->getUri()->getScheme(), ['http', 'https'])) {
+            throw new RequestException(\sprintf("The scheme '%s' is not supported.", $request->getUri()->getScheme()), $request);
+        }
+
+        // HTTP/1.1 streams using the PHP stream wrapper require a
+        // Connection: close header
+        if ($request->getProtocolVersion() === '1.1'
+            && !$request->hasHeader('Connection')
+        ) {
+            $request = $request->withHeader('Connection', 'close');
+        }
+
+        // Ensure SSL is verified by default
+        if (!isset($options['verify'])) {
+            $options['verify'] = true;
+        }
+
+        $params = [];
+        $context = $this->getDefaultContext($request);
+
+        if (isset($options['on_headers']) && !\is_callable($options['on_headers'])) {
+            throw new \InvalidArgumentException('on_headers must be callable');
+        }
+
+        if (!empty($options)) {
+            foreach ($options as $key => $value) {
+                $method = "add_{$key}";
+                if (isset($methods[$method])) {
+                    $this->{$method}($request, $context, $value, $params);
+                }
+            }
+        }
+
+        if (isset($options['stream_context'])) {
+            if (!\is_array($options['stream_context'])) {
+                throw new \InvalidArgumentException('stream_context must be an array');
+            }
+            $context = \array_replace_recursive($context, $options['stream_context']);
+        }
+
+        // Microsoft NTLM authentication only supported with curl handler
+        if (isset($options['auth'][2]) && 'ntlm' === $options['auth'][2]) {
+            throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler');
+        }
+
+        $uri = $this->resolveHost($request, $options);
+
+        $contextResource = $this->createResource(
+            static function () use ($context, $params) {
+                return \stream_context_create($context, $params);
+            }
+        );
+
+        return $this->createResource(
+            function () use ($uri, &$http_response_header, $contextResource, $context, $options, $request) {
+                $resource = @\fopen((string) $uri, 'r', false, $contextResource);
+                $this->lastHeaders = $http_response_header ?? [];
+
+                if (false === $resource) {
+                    throw new ConnectException(sprintf('Connection refused for URI %s', $uri), $request, null, $context);
+                }
+
+                if (isset($options['read_timeout'])) {
+                    $readTimeout = $options['read_timeout'];
+                    $sec = (int) $readTimeout;
+                    $usec = ($readTimeout - $sec) * 100000;
+                    \stream_set_timeout($resource, $sec, $usec);
+                }
+
+                return $resource;
+            }
+        );
+    }
+
+    private function resolveHost(RequestInterface $request, array $options): UriInterface
+    {
+        $uri = $request->getUri();
+
+        if (isset($options['force_ip_resolve']) && !\filter_var($uri->getHost(), \FILTER_VALIDATE_IP)) {
+            if ('v4' === $options['force_ip_resolve']) {
+                $records = \dns_get_record($uri->getHost(), \DNS_A);
+                if (false === $records || !isset($records[0]['ip'])) {
+                    throw new ConnectException(\sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request);
+                }
+
+                return $uri->withHost($records[0]['ip']);
+            }
+            if ('v6' === $options['force_ip_resolve']) {
+                $records = \dns_get_record($uri->getHost(), \DNS_AAAA);
+                if (false === $records || !isset($records[0]['ipv6'])) {
+                    throw new ConnectException(\sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request);
+                }
+
+                return $uri->withHost('['.$records[0]['ipv6'].']');
+            }
+        }
+
+        return $uri;
+    }
+
+    private function getDefaultContext(RequestInterface $request): array
+    {
+        $headers = '';
+        foreach ($request->getHeaders() as $name => $value) {
+            foreach ($value as $val) {
+                $headers .= "$name: $val\r\n";
+            }
+        }
+
+        $context = [
+            'http' => [
+                'method' => $request->getMethod(),
+                'header' => $headers,
+                'protocol_version' => $request->getProtocolVersion(),
+                'ignore_errors' => true,
+                'follow_location' => 0,
+            ],
+            'ssl' => [
+                'peer_name' => $request->getUri()->getHost(),
+            ],
+        ];
+
+        $body = (string) $request->getBody();
+
+        if ('' !== $body) {
+            $context['http']['content'] = $body;
+            // Prevent the HTTP handler from adding a Content-Type header.
+            if (!$request->hasHeader('Content-Type')) {
+                $context['http']['header'] .= "Content-Type:\r\n";
+            }
+        }
+
+        $context['http']['header'] = \rtrim($context['http']['header']);
+
+        return $context;
+    }
+
+    /**
+     * @param mixed $value as passed via Request transfer options.
+     */
+    private function add_proxy(RequestInterface $request, array &$options, $value, array &$params): void
+    {
+        $uri = null;
+
+        if (!\is_array($value)) {
+            $uri = $value;
+        } else {
+            $scheme = $request->getUri()->getScheme();
+            if (isset($value[$scheme])) {
+                if (!isset($value['no']) || !Utils::isHostInNoProxy($request->getUri()->getHost(), $value['no'])) {
+                    $uri = $value[$scheme];
+                }
+            }
+        }
+
+        if (!$uri) {
+            return;
+        }
+
+        $parsed = $this->parse_proxy($uri);
+        $options['http']['proxy'] = $parsed['proxy'];
+
+        if ($parsed['auth']) {
+            if (!isset($options['http']['header'])) {
+                $options['http']['header'] = [];
+            }
+            $options['http']['header'] .= "\r\nProxy-Authorization: {$parsed['auth']}";
+        }
+    }
+
+    /**
+     * Parses the given proxy URL to make it compatible with the format PHP's stream context expects.
+     */
+    private function parse_proxy(string $url): array
+    {
+        $parsed = \parse_url($url);
+
+        if ($parsed !== false && isset($parsed['scheme']) && $parsed['scheme'] === 'http') {
+            if (isset($parsed['host']) && isset($parsed['port'])) {
+                $auth = null;
+                if (isset($parsed['user']) && isset($parsed['pass'])) {
+                    $auth = \base64_encode("{$parsed['user']}:{$parsed['pass']}");
+                }
+
+                return [
+                    'proxy' => "tcp://{$parsed['host']}:{$parsed['port']}",
+                    'auth' => $auth ? "Basic {$auth}" : null,
+                ];
+            }
+        }
+
+        // Return proxy as-is.
+        return [
+            'proxy' => $url,
+            'auth' => null,
+        ];
+    }
+
+    /**
+     * @param mixed $value as passed via Request transfer options.
+     */
+    private function add_timeout(RequestInterface $request, array &$options, $value, array &$params): void
+    {
+        if ($value > 0) {
+            $options['http']['timeout'] = $value;
+        }
+    }
+
+    /**
+     * @param mixed $value as passed via Request transfer options.
+     */
+    private function add_crypto_method(RequestInterface $request, array &$options, $value, array &$params): void
+    {
+        if (
+            $value === \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT
+            || $value === \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT
+            || $value === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
+            || (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && $value === \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT)
+        ) {
+            $options['http']['crypto_method'] = $value;
+
+            return;
+        }
+
+        throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
+    }
+
+    /**
+     * @param mixed $value as passed via Request transfer options.
+     */
+    private function add_verify(RequestInterface $request, array &$options, $value, array &$params): void
+    {
+        if ($value === false) {
+            $options['ssl']['verify_peer'] = false;
+            $options['ssl']['verify_peer_name'] = false;
+
+            return;
+        }
+
+        if (\is_string($value)) {
+            $options['ssl']['cafile'] = $value;
+            if (!\file_exists($value)) {
+                throw new \RuntimeException("SSL CA bundle not found: $value");
+            }
+        } elseif ($value !== true) {
+            throw new \InvalidArgumentException('Invalid verify request option');
+        }
+
+        $options['ssl']['verify_peer'] = true;
+        $options['ssl']['verify_peer_name'] = true;
+        $options['ssl']['allow_self_signed'] = false;
+    }
+
+    /**
+     * @param mixed $value as passed via Request transfer options.
+     */
+    private function add_cert(RequestInterface $request, array &$options, $value, array &$params): void
+    {
+        if (\is_array($value)) {
+            $options['ssl']['passphrase'] = $value[1];
+            $value = $value[0];
+        }
+
+        if (!\file_exists($value)) {
+            throw new \RuntimeException("SSL certificate not found: {$value}");
+        }
+
+        $options['ssl']['local_cert'] = $value;
+    }
+
+    /**
+     * @param mixed $value as passed via Request transfer options.
+     */
+    private function add_progress(RequestInterface $request, array &$options, $value, array &$params): void
+    {
+        self::addNotification(
+            $params,
+            static function ($code, $a, $b, $c, $transferred, $total) use ($value) {
+                if ($code == \STREAM_NOTIFY_PROGRESS) {
+                    // The upload progress cannot be determined. Use 0 for cURL compatibility:
+                    // https://curl.se/libcurl/c/CURLOPT_PROGRESSFUNCTION.html
+                    $value($total, $transferred, 0, 0);
+                }
+            }
+        );
+    }
+
+    /**
+     * @param mixed $value as passed via Request transfer options.
+     */
+    private function add_debug(RequestInterface $request, array &$options, $value, array &$params): void
+    {
+        if ($value === false) {
+            return;
+        }
+
+        static $map = [
+            \STREAM_NOTIFY_CONNECT => 'CONNECT',
+            \STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
+            \STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
+            \STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
+            \STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
+            \STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
+            \STREAM_NOTIFY_PROGRESS => 'PROGRESS',
+            \STREAM_NOTIFY_FAILURE => 'FAILURE',
+            \STREAM_NOTIFY_COMPLETED => 'COMPLETED',
+            \STREAM_NOTIFY_RESOLVE => 'RESOLVE',
+        ];
+        static $args = ['severity', 'message', 'message_code', 'bytes_transferred', 'bytes_max'];
+
+        $value = Utils::debugResource($value);
+        $ident = $request->getMethod().' '.$request->getUri()->withFragment('');
+        self::addNotification(
+            $params,
+            static function (int $code, ...$passed) use ($ident, $value, $map, $args): void {
+                \fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
+                foreach (\array_filter($passed) as $i => $v) {
+                    \fwrite($value, $args[$i].': "'.$v.'" ');
+                }
+                \fwrite($value, "\n");
+            }
+        );
+    }
+
+    private static function addNotification(array &$params, callable $notify): void
+    {
+        // Wrap the existing function if needed.
+        if (!isset($params['notification'])) {
+            $params['notification'] = $notify;
+        } else {
+            $params['notification'] = self::callArray([
+                $params['notification'],
+                $notify,
+            ]);
+        }
+    }
+
+    private static function callArray(array $functions): callable
+    {
+        return static function (...$args) use ($functions) {
+            foreach ($functions as $fn) {
+                $fn(...$args);
+            }
+        };
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/HandlerStack.php b/vendor/guzzlehttp/guzzle/src/HandlerStack.php
new file mode 100644
index 0000000..03f9a18
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/HandlerStack.php
@@ -0,0 +1,275 @@
+push(Middleware::httpErrors(), 'http_errors');
+        $stack->push(Middleware::redirect(), 'allow_redirects');
+        $stack->push(Middleware::cookies(), 'cookies');
+        $stack->push(Middleware::prepareBody(), 'prepare_body');
+
+        return $stack;
+    }
+
+    /**
+     * @param (callable(RequestInterface, array): PromiseInterface)|null $handler Underlying HTTP handler.
+     */
+    public function __construct(?callable $handler = null)
+    {
+        $this->handler = $handler;
+    }
+
+    /**
+     * Invokes the handler stack as a composed handler
+     *
+     * @return ResponseInterface|PromiseInterface
+     */
+    public function __invoke(RequestInterface $request, array $options)
+    {
+        $handler = $this->resolve();
+
+        return $handler($request, $options);
+    }
+
+    /**
+     * Dumps a string representation of the stack.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        $depth = 0;
+        $stack = [];
+
+        if ($this->handler !== null) {
+            $stack[] = '0) Handler: '.$this->debugCallable($this->handler);
+        }
+
+        $result = '';
+        foreach (\array_reverse($this->stack) as $tuple) {
+            ++$depth;
+            $str = "{$depth}) Name: '{$tuple[1]}', ";
+            $str .= 'Function: '.$this->debugCallable($tuple[0]);
+            $result = "> {$str}\n{$result}";
+            $stack[] = $str;
+        }
+
+        foreach (\array_keys($stack) as $k) {
+            $result .= "< {$stack[$k]}\n";
+        }
+
+        return $result;
+    }
+
+    /**
+     * Set the HTTP handler that actually returns a promise.
+     *
+     * @param callable(RequestInterface, array): PromiseInterface $handler Accepts a request and array of options and
+     *                                                                     returns a Promise.
+     */
+    public function setHandler(callable $handler): void
+    {
+        $this->handler = $handler;
+        $this->cached = null;
+    }
+
+    /**
+     * Returns true if the builder has a handler.
+     */
+    public function hasHandler(): bool
+    {
+        return $this->handler !== null;
+    }
+
+    /**
+     * Unshift a middleware to the bottom of the stack.
+     *
+     * @param callable(callable): callable $middleware Middleware function
+     * @param string                       $name       Name to register for this middleware.
+     */
+    public function unshift(callable $middleware, ?string $name = null): void
+    {
+        \array_unshift($this->stack, [$middleware, $name]);
+        $this->cached = null;
+    }
+
+    /**
+     * Push a middleware to the top of the stack.
+     *
+     * @param callable(callable): callable $middleware Middleware function
+     * @param string                       $name       Name to register for this middleware.
+     */
+    public function push(callable $middleware, string $name = ''): void
+    {
+        $this->stack[] = [$middleware, $name];
+        $this->cached = null;
+    }
+
+    /**
+     * Add a middleware before another middleware by name.
+     *
+     * @param string                       $findName   Middleware to find
+     * @param callable(callable): callable $middleware Middleware function
+     * @param string                       $withName   Name to register for this middleware.
+     */
+    public function before(string $findName, callable $middleware, string $withName = ''): void
+    {
+        $this->splice($findName, $withName, $middleware, true);
+    }
+
+    /**
+     * Add a middleware after another middleware by name.
+     *
+     * @param string                       $findName   Middleware to find
+     * @param callable(callable): callable $middleware Middleware function
+     * @param string                       $withName   Name to register for this middleware.
+     */
+    public function after(string $findName, callable $middleware, string $withName = ''): void
+    {
+        $this->splice($findName, $withName, $middleware, false);
+    }
+
+    /**
+     * Remove a middleware by instance or name from the stack.
+     *
+     * @param callable|string $remove Middleware to remove by instance or name.
+     */
+    public function remove($remove): void
+    {
+        if (!is_string($remove) && !is_callable($remove)) {
+            trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a callable or string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__);
+        }
+
+        $this->cached = null;
+        $idx = \is_callable($remove) ? 0 : 1;
+        $this->stack = \array_values(\array_filter(
+            $this->stack,
+            static function ($tuple) use ($idx, $remove) {
+                return $tuple[$idx] !== $remove;
+            }
+        ));
+    }
+
+    /**
+     * Compose the middleware and handler into a single callable function.
+     *
+     * @return callable(RequestInterface, array): PromiseInterface
+     */
+    public function resolve(): callable
+    {
+        if ($this->cached === null) {
+            if (($prev = $this->handler) === null) {
+                throw new \LogicException('No handler has been specified');
+            }
+
+            foreach (\array_reverse($this->stack) as $fn) {
+                /** @var callable(RequestInterface, array): PromiseInterface $prev */
+                $prev = $fn[0]($prev);
+            }
+
+            $this->cached = $prev;
+        }
+
+        return $this->cached;
+    }
+
+    private function findByName(string $name): int
+    {
+        foreach ($this->stack as $k => $v) {
+            if ($v[1] === $name) {
+                return $k;
+            }
+        }
+
+        throw new \InvalidArgumentException("Middleware not found: $name");
+    }
+
+    /**
+     * Splices a function into the middleware list at a specific position.
+     */
+    private function splice(string $findName, string $withName, callable $middleware, bool $before): void
+    {
+        $this->cached = null;
+        $idx = $this->findByName($findName);
+        $tuple = [$middleware, $withName];
+
+        if ($before) {
+            if ($idx === 0) {
+                \array_unshift($this->stack, $tuple);
+            } else {
+                $replacement = [$tuple, $this->stack[$idx]];
+                \array_splice($this->stack, $idx, 1, $replacement);
+            }
+        } elseif ($idx === \count($this->stack) - 1) {
+            $this->stack[] = $tuple;
+        } else {
+            $replacement = [$this->stack[$idx], $tuple];
+            \array_splice($this->stack, $idx, 1, $replacement);
+        }
+    }
+
+    /**
+     * Provides a debug string for a given callable.
+     *
+     * @param callable|string $fn Function to write as a string.
+     */
+    private function debugCallable($fn): string
+    {
+        if (\is_string($fn)) {
+            return "callable({$fn})";
+        }
+
+        if (\is_array($fn)) {
+            return \is_string($fn[0])
+                ? "callable({$fn[0]}::{$fn[1]})"
+                : "callable(['".\get_class($fn[0])."', '{$fn[1]}'])";
+        }
+
+        /** @var object $fn */
+        return 'callable('.\spl_object_hash($fn).')';
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/MessageFormatter.php b/vendor/guzzlehttp/guzzle/src/MessageFormatter.php
new file mode 100644
index 0000000..9b77eee
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/MessageFormatter.php
@@ -0,0 +1,199 @@
+>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
+    public const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
+
+    /**
+     * @var string Template used to format log messages
+     */
+    private $template;
+
+    /**
+     * @param string $template Log message template
+     */
+    public function __construct(?string $template = self::CLF)
+    {
+        $this->template = $template ?: self::CLF;
+    }
+
+    /**
+     * Returns a formatted message string.
+     *
+     * @param RequestInterface       $request  Request that was sent
+     * @param ResponseInterface|null $response Response that was received
+     * @param \Throwable|null        $error    Exception that was received
+     */
+    public function format(RequestInterface $request, ?ResponseInterface $response = null, ?\Throwable $error = null): string
+    {
+        $cache = [];
+
+        /** @var string */
+        return \preg_replace_callback(
+            '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
+            function (array $matches) use ($request, $response, $error, &$cache) {
+                if (isset($cache[$matches[1]])) {
+                    return $cache[$matches[1]];
+                }
+
+                $result = '';
+                switch ($matches[1]) {
+                    case 'request':
+                        $result = Psr7\Message::toString($request);
+                        break;
+                    case 'response':
+                        $result = $response ? Psr7\Message::toString($response) : '';
+                        break;
+                    case 'req_headers':
+                        $result = \trim($request->getMethod()
+                                .' '.$request->getRequestTarget())
+                            .' HTTP/'.$request->getProtocolVersion()."\r\n"
+                            .$this->headers($request);
+                        break;
+                    case 'res_headers':
+                        $result = $response ?
+                            \sprintf(
+                                'HTTP/%s %d %s',
+                                $response->getProtocolVersion(),
+                                $response->getStatusCode(),
+                                $response->getReasonPhrase()
+                            )."\r\n".$this->headers($response)
+                            : 'NULL';
+                        break;
+                    case 'req_body':
+                        $result = $request->getBody()->__toString();
+                        break;
+                    case 'res_body':
+                        if (!$response instanceof ResponseInterface) {
+                            $result = 'NULL';
+                            break;
+                        }
+
+                        $body = $response->getBody();
+
+                        if (!$body->isSeekable()) {
+                            $result = 'RESPONSE_NOT_LOGGEABLE';
+                            break;
+                        }
+
+                        $result = $response->getBody()->__toString();
+                        break;
+                    case 'ts':
+                    case 'date_iso_8601':
+                        $result = \gmdate('c');
+                        break;
+                    case 'date_common_log':
+                        $result = \date('d/M/Y:H:i:s O');
+                        break;
+                    case 'method':
+                        $result = $request->getMethod();
+                        break;
+                    case 'version':
+                        $result = $request->getProtocolVersion();
+                        break;
+                    case 'uri':
+                    case 'url':
+                        $result = $request->getUri()->__toString();
+                        break;
+                    case 'target':
+                        $result = $request->getRequestTarget();
+                        break;
+                    case 'req_version':
+                        $result = $request->getProtocolVersion();
+                        break;
+                    case 'res_version':
+                        $result = $response
+                            ? $response->getProtocolVersion()
+                            : 'NULL';
+                        break;
+                    case 'host':
+                        $result = $request->getHeaderLine('Host');
+                        break;
+                    case 'hostname':
+                        $result = \gethostname();
+                        break;
+                    case 'code':
+                        $result = $response ? $response->getStatusCode() : 'NULL';
+                        break;
+                    case 'phrase':
+                        $result = $response ? $response->getReasonPhrase() : 'NULL';
+                        break;
+                    case 'error':
+                        $result = $error ? $error->getMessage() : 'NULL';
+                        break;
+                    default:
+                        // handle prefixed dynamic headers
+                        if (\strpos($matches[1], 'req_header_') === 0) {
+                            $result = $request->getHeaderLine(\substr($matches[1], 11));
+                        } elseif (\strpos($matches[1], 'res_header_') === 0) {
+                            $result = $response
+                                ? $response->getHeaderLine(\substr($matches[1], 11))
+                                : 'NULL';
+                        }
+                }
+
+                $cache[$matches[1]] = $result;
+
+                return $result;
+            },
+            $this->template
+        );
+    }
+
+    /**
+     * Get headers from message as string
+     */
+    private function headers(MessageInterface $message): string
+    {
+        $result = '';
+        foreach ($message->getHeaders() as $name => $values) {
+            $result .= $name.': '.\implode(', ', $values)."\r\n";
+        }
+
+        return \trim($result);
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php b/vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php
new file mode 100644
index 0000000..a39ac24
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php
@@ -0,0 +1,18 @@
+withCookieHeader($request);
+
+                return $handler($request, $options)
+                    ->then(
+                        static function (ResponseInterface $response) use ($cookieJar, $request): ResponseInterface {
+                            $cookieJar->extractCookies($request, $response);
+
+                            return $response;
+                        }
+                    );
+            };
+        };
+    }
+
+    /**
+     * Middleware that throws exceptions for 4xx or 5xx responses when the
+     * "http_errors" request option is set to true.
+     *
+     * @param BodySummarizerInterface|null $bodySummarizer The body summarizer to use in exception messages.
+     *
+     * @return callable(callable): callable Returns a function that accepts the next handler.
+     */
+    public static function httpErrors(?BodySummarizerInterface $bodySummarizer = null): callable
+    {
+        return static function (callable $handler) use ($bodySummarizer): callable {
+            return static function ($request, array $options) use ($handler, $bodySummarizer) {
+                if (empty($options['http_errors'])) {
+                    return $handler($request, $options);
+                }
+
+                return $handler($request, $options)->then(
+                    static function (ResponseInterface $response) use ($request, $bodySummarizer) {
+                        $code = $response->getStatusCode();
+                        if ($code < 400) {
+                            return $response;
+                        }
+                        throw RequestException::create($request, $response, null, [], $bodySummarizer);
+                    }
+                );
+            };
+        };
+    }
+
+    /**
+     * Middleware that pushes history data to an ArrayAccess container.
+     *
+     * @param array|\ArrayAccess $container Container to hold the history (by reference).
+     *
+     * @return callable(callable): callable Returns a function that accepts the next handler.
+     *
+     * @throws \InvalidArgumentException if container is not an array or ArrayAccess.
+     */
+    public static function history(&$container): callable
+    {
+        if (!\is_array($container) && !$container instanceof \ArrayAccess) {
+            throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess');
+        }
+
+        return static function (callable $handler) use (&$container): callable {
+            return static function (RequestInterface $request, array $options) use ($handler, &$container) {
+                return $handler($request, $options)->then(
+                    static function ($value) use ($request, &$container, $options) {
+                        $container[] = [
+                            'request' => $request,
+                            'response' => $value,
+                            'error' => null,
+                            'options' => $options,
+                        ];
+
+                        return $value;
+                    },
+                    static function ($reason) use ($request, &$container, $options) {
+                        $container[] = [
+                            'request' => $request,
+                            'response' => null,
+                            'error' => $reason,
+                            'options' => $options,
+                        ];
+
+                        return P\Create::rejectionFor($reason);
+                    }
+                );
+            };
+        };
+    }
+
+    /**
+     * Middleware that invokes a callback before and after sending a request.
+     *
+     * The provided listener cannot modify or alter the response. It simply
+     * "taps" into the chain to be notified before returning the promise. The
+     * before listener accepts a request and options array, and the after
+     * listener accepts a request, options array, and response promise.
+     *
+     * @param callable $before Function to invoke before forwarding the request.
+     * @param callable $after  Function invoked after forwarding.
+     *
+     * @return callable Returns a function that accepts the next handler.
+     */
+    public static function tap(?callable $before = null, ?callable $after = null): callable
+    {
+        return static function (callable $handler) use ($before, $after): callable {
+            return static function (RequestInterface $request, array $options) use ($handler, $before, $after) {
+                if ($before) {
+                    $before($request, $options);
+                }
+                $response = $handler($request, $options);
+                if ($after) {
+                    $after($request, $options, $response);
+                }
+
+                return $response;
+            };
+        };
+    }
+
+    /**
+     * Middleware that handles request redirects.
+     *
+     * @return callable Returns a function that accepts the next handler.
+     */
+    public static function redirect(): callable
+    {
+        return static function (callable $handler): RedirectMiddleware {
+            return new RedirectMiddleware($handler);
+        };
+    }
+
+    /**
+     * Middleware that retries requests based on the boolean result of
+     * invoking the provided "decider" function.
+     *
+     * If no delay function is provided, a simple implementation of exponential
+     * backoff will be utilized.
+     *
+     * @param callable $decider Function that accepts the number of retries,
+     *                          a request, [response], and [exception] and
+     *                          returns true if the request is to be retried.
+     * @param callable $delay   Function that accepts the number of retries and
+     *                          returns the number of milliseconds to delay.
+     *
+     * @return callable Returns a function that accepts the next handler.
+     */
+    public static function retry(callable $decider, ?callable $delay = null): callable
+    {
+        return static function (callable $handler) use ($decider, $delay): RetryMiddleware {
+            return new RetryMiddleware($decider, $handler, $delay);
+        };
+    }
+
+    /**
+     * Middleware that logs requests, responses, and errors using a message
+     * formatter.
+     *
+     * @phpstan-param \Psr\Log\LogLevel::* $logLevel  Level at which to log requests.
+     *
+     * @param LoggerInterface                            $logger    Logs messages.
+     * @param MessageFormatterInterface|MessageFormatter $formatter Formatter used to create message strings.
+     * @param string                                     $logLevel  Level at which to log requests.
+     *
+     * @return callable Returns a function that accepts the next handler.
+     */
+    public static function log(LoggerInterface $logger, $formatter, string $logLevel = 'info'): callable
+    {
+        // To be compatible with Guzzle 7.1.x we need to allow users to pass a MessageFormatter
+        if (!$formatter instanceof MessageFormatter && !$formatter instanceof MessageFormatterInterface) {
+            throw new \LogicException(sprintf('Argument 2 to %s::log() must be of type %s', self::class, MessageFormatterInterface::class));
+        }
+
+        return static function (callable $handler) use ($logger, $formatter, $logLevel): callable {
+            return static function (RequestInterface $request, array $options = []) use ($handler, $logger, $formatter, $logLevel) {
+                return $handler($request, $options)->then(
+                    static function ($response) use ($logger, $request, $formatter, $logLevel): ResponseInterface {
+                        $message = $formatter->format($request, $response);
+                        $logger->log($logLevel, $message);
+
+                        return $response;
+                    },
+                    static function ($reason) use ($logger, $request, $formatter): PromiseInterface {
+                        $response = $reason instanceof RequestException ? $reason->getResponse() : null;
+                        $message = $formatter->format($request, $response, P\Create::exceptionFor($reason));
+                        $logger->error($message);
+
+                        return P\Create::rejectionFor($reason);
+                    }
+                );
+            };
+        };
+    }
+
+    /**
+     * This middleware adds a default content-type if possible, a default
+     * content-length or transfer-encoding header, and the expect header.
+     */
+    public static function prepareBody(): callable
+    {
+        return static function (callable $handler): PrepareBodyMiddleware {
+            return new PrepareBodyMiddleware($handler);
+        };
+    }
+
+    /**
+     * Middleware that applies a map function to the request before passing to
+     * the next handler.
+     *
+     * @param callable $fn Function that accepts a RequestInterface and returns
+     *                     a RequestInterface.
+     */
+    public static function mapRequest(callable $fn): callable
+    {
+        return static function (callable $handler) use ($fn): callable {
+            return static function (RequestInterface $request, array $options) use ($handler, $fn) {
+                return $handler($fn($request), $options);
+            };
+        };
+    }
+
+    /**
+     * Middleware that applies a map function to the resolved promise's
+     * response.
+     *
+     * @param callable $fn Function that accepts a ResponseInterface and
+     *                     returns a ResponseInterface.
+     */
+    public static function mapResponse(callable $fn): callable
+    {
+        return static function (callable $handler) use ($fn): callable {
+            return static function (RequestInterface $request, array $options) use ($handler, $fn) {
+                return $handler($request, $options)->then($fn);
+            };
+        };
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Pool.php b/vendor/guzzlehttp/guzzle/src/Pool.php
new file mode 100644
index 0000000..6277c61
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Pool.php
@@ -0,0 +1,125 @@
+ $rfn) {
+                if ($rfn instanceof RequestInterface) {
+                    yield $key => $client->sendAsync($rfn, $opts);
+                } elseif (\is_callable($rfn)) {
+                    yield $key => $rfn($opts);
+                } else {
+                    throw new \InvalidArgumentException('Each value yielded by the iterator must be a Psr7\Http\Message\RequestInterface or a callable that returns a promise that fulfills with a Psr7\Message\Http\ResponseInterface object.');
+                }
+            }
+        };
+
+        $this->each = new EachPromise($requests(), $config);
+    }
+
+    /**
+     * Get promise
+     */
+    public function promise(): PromiseInterface
+    {
+        return $this->each->promise();
+    }
+
+    /**
+     * Sends multiple requests concurrently and returns an array of responses
+     * and exceptions that uses the same ordering as the provided requests.
+     *
+     * IMPORTANT: This method keeps every request and response in memory, and
+     * as such, is NOT recommended when sending a large number or an
+     * indeterminate number of requests concurrently.
+     *
+     * @param ClientInterface $client   Client used to send the requests
+     * @param array|\Iterator $requests Requests to send concurrently.
+     * @param array           $options  Passes through the options available in
+     *                                  {@see \GuzzleHttp\Pool::__construct}
+     *
+     * @return array Returns an array containing the response or an exception
+     *               in the same order that the requests were sent.
+     *
+     * @throws \InvalidArgumentException if the event format is incorrect.
+     */
+    public static function batch(ClientInterface $client, $requests, array $options = []): array
+    {
+        $res = [];
+        self::cmpCallback($options, 'fulfilled', $res);
+        self::cmpCallback($options, 'rejected', $res);
+        $pool = new static($client, $requests, $options);
+        $pool->promise()->wait();
+        \ksort($res);
+
+        return $res;
+    }
+
+    /**
+     * Execute callback(s)
+     */
+    private static function cmpCallback(array &$options, string $name, array &$results): void
+    {
+        if (!isset($options[$name])) {
+            $options[$name] = static function ($v, $k) use (&$results) {
+                $results[$k] = $v;
+            };
+        } else {
+            $currentFn = $options[$name];
+            $options[$name] = static function ($v, $k) use (&$results, $currentFn) {
+                $currentFn($v, $k);
+                $results[$k] = $v;
+            };
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php b/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php
new file mode 100644
index 0000000..7dde6c5
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php
@@ -0,0 +1,105 @@
+nextHandler = $nextHandler;
+    }
+
+    public function __invoke(RequestInterface $request, array $options): PromiseInterface
+    {
+        $fn = $this->nextHandler;
+
+        // Don't do anything if the request has no body.
+        if ($request->getBody()->getSize() === 0) {
+            return $fn($request, $options);
+        }
+
+        $modify = [];
+
+        // Add a default content-type if possible.
+        if (!$request->hasHeader('Content-Type')) {
+            if ($uri = $request->getBody()->getMetadata('uri')) {
+                if (is_string($uri) && $type = Psr7\MimeType::fromFilename($uri)) {
+                    $modify['set_headers']['Content-Type'] = $type;
+                }
+            }
+        }
+
+        // Add a default content-length or transfer-encoding header.
+        if (!$request->hasHeader('Content-Length')
+            && !$request->hasHeader('Transfer-Encoding')
+        ) {
+            $size = $request->getBody()->getSize();
+            if ($size !== null) {
+                $modify['set_headers']['Content-Length'] = $size;
+            } else {
+                $modify['set_headers']['Transfer-Encoding'] = 'chunked';
+            }
+        }
+
+        // Add the expect header if needed.
+        $this->addExpectHeader($request, $options, $modify);
+
+        return $fn(Psr7\Utils::modifyRequest($request, $modify), $options);
+    }
+
+    /**
+     * Add expect header
+     */
+    private function addExpectHeader(RequestInterface $request, array $options, array &$modify): void
+    {
+        // Determine if the Expect header should be used
+        if ($request->hasHeader('Expect')) {
+            return;
+        }
+
+        $expect = $options['expect'] ?? null;
+
+        // Return if disabled or using HTTP/1.0
+        if ($expect === false || $request->getProtocolVersion() === '1.0') {
+            return;
+        }
+
+        // The expect header is unconditionally enabled
+        if ($expect === true) {
+            $modify['set_headers']['Expect'] = '100-Continue';
+
+            return;
+        }
+
+        // By default, send the expect header when the payload is > 1mb
+        if ($expect === null) {
+            $expect = 1048576;
+        }
+
+        // Always add if the body cannot be rewound, the size cannot be
+        // determined, or the size is greater than the cutoff threshold
+        $body = $request->getBody();
+        $size = $body->getSize();
+
+        if ($size === null || $size >= (int) $expect || !$body->isSeekable()) {
+            $modify['set_headers']['Expect'] = '100-Continue';
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php b/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php
new file mode 100644
index 0000000..7aa21a6
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php
@@ -0,0 +1,228 @@
+ 5,
+        'protocols' => ['http', 'https'],
+        'strict' => false,
+        'referer' => false,
+        'track_redirects' => false,
+    ];
+
+    /**
+     * @var callable(RequestInterface, array): PromiseInterface
+     */
+    private $nextHandler;
+
+    /**
+     * @param callable(RequestInterface, array): PromiseInterface $nextHandler Next handler to invoke.
+     */
+    public function __construct(callable $nextHandler)
+    {
+        $this->nextHandler = $nextHandler;
+    }
+
+    public function __invoke(RequestInterface $request, array $options): PromiseInterface
+    {
+        $fn = $this->nextHandler;
+
+        if (empty($options['allow_redirects'])) {
+            return $fn($request, $options);
+        }
+
+        if ($options['allow_redirects'] === true) {
+            $options['allow_redirects'] = self::$defaultSettings;
+        } elseif (!\is_array($options['allow_redirects'])) {
+            throw new \InvalidArgumentException('allow_redirects must be true, false, or array');
+        } else {
+            // Merge the default settings with the provided settings
+            $options['allow_redirects'] += self::$defaultSettings;
+        }
+
+        if (empty($options['allow_redirects']['max'])) {
+            return $fn($request, $options);
+        }
+
+        return $fn($request, $options)
+            ->then(function (ResponseInterface $response) use ($request, $options) {
+                return $this->checkRedirect($request, $options, $response);
+            });
+    }
+
+    /**
+     * @return ResponseInterface|PromiseInterface
+     */
+    public function checkRedirect(RequestInterface $request, array $options, ResponseInterface $response)
+    {
+        if (\strpos((string) $response->getStatusCode(), '3') !== 0
+            || !$response->hasHeader('Location')
+        ) {
+            return $response;
+        }
+
+        $this->guardMax($request, $response, $options);
+        $nextRequest = $this->modifyRequest($request, $options, $response);
+
+        // If authorization is handled by curl, unset it if URI is cross-origin.
+        if (Psr7\UriComparator::isCrossOrigin($request->getUri(), $nextRequest->getUri()) && defined('\CURLOPT_HTTPAUTH')) {
+            unset(
+                $options['curl'][\CURLOPT_HTTPAUTH],
+                $options['curl'][\CURLOPT_USERPWD]
+            );
+        }
+
+        if (isset($options['allow_redirects']['on_redirect'])) {
+            ($options['allow_redirects']['on_redirect'])(
+                $request,
+                $response,
+                $nextRequest->getUri()
+            );
+        }
+
+        $promise = $this($nextRequest, $options);
+
+        // Add headers to be able to track history of redirects.
+        if (!empty($options['allow_redirects']['track_redirects'])) {
+            return $this->withTracking(
+                $promise,
+                (string) $nextRequest->getUri(),
+                $response->getStatusCode()
+            );
+        }
+
+        return $promise;
+    }
+
+    /**
+     * Enable tracking on promise.
+     */
+    private function withTracking(PromiseInterface $promise, string $uri, int $statusCode): PromiseInterface
+    {
+        return $promise->then(
+            static function (ResponseInterface $response) use ($uri, $statusCode) {
+                // Note that we are pushing to the front of the list as this
+                // would be an earlier response than what is currently present
+                // in the history header.
+                $historyHeader = $response->getHeader(self::HISTORY_HEADER);
+                $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER);
+                \array_unshift($historyHeader, $uri);
+                \array_unshift($statusHeader, (string) $statusCode);
+
+                return $response->withHeader(self::HISTORY_HEADER, $historyHeader)
+                                ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader);
+            }
+        );
+    }
+
+    /**
+     * Check for too many redirects.
+     *
+     * @throws TooManyRedirectsException Too many redirects.
+     */
+    private function guardMax(RequestInterface $request, ResponseInterface $response, array &$options): void
+    {
+        $current = $options['__redirect_count']
+            ?? 0;
+        $options['__redirect_count'] = $current + 1;
+        $max = $options['allow_redirects']['max'];
+
+        if ($options['__redirect_count'] > $max) {
+            throw new TooManyRedirectsException("Will not follow more than {$max} redirects", $request, $response);
+        }
+    }
+
+    public function modifyRequest(RequestInterface $request, array $options, ResponseInterface $response): RequestInterface
+    {
+        // Request modifications to apply.
+        $modify = [];
+        $protocols = $options['allow_redirects']['protocols'];
+
+        // Use a GET request if this is an entity enclosing request and we are
+        // not forcing RFC compliance, but rather emulating what all browsers
+        // would do.
+        $statusCode = $response->getStatusCode();
+        if ($statusCode == 303
+            || ($statusCode <= 302 && !$options['allow_redirects']['strict'])
+        ) {
+            $safeMethods = ['GET', 'HEAD', 'OPTIONS'];
+            $requestMethod = $request->getMethod();
+
+            $modify['method'] = in_array($requestMethod, $safeMethods) ? $requestMethod : 'GET';
+            $modify['body'] = '';
+        }
+
+        $uri = self::redirectUri($request, $response, $protocols);
+        if (isset($options['idn_conversion']) && ($options['idn_conversion'] !== false)) {
+            $idnOptions = ($options['idn_conversion'] === true) ? \IDNA_DEFAULT : $options['idn_conversion'];
+            $uri = Utils::idnUriConvert($uri, $idnOptions);
+        }
+
+        $modify['uri'] = $uri;
+        Psr7\Message::rewindBody($request);
+
+        // Add the Referer header if it is told to do so and only
+        // add the header if we are not redirecting from https to http.
+        if ($options['allow_redirects']['referer']
+            && $modify['uri']->getScheme() === $request->getUri()->getScheme()
+        ) {
+            $uri = $request->getUri()->withUserInfo('');
+            $modify['set_headers']['Referer'] = (string) $uri;
+        } else {
+            $modify['remove_headers'][] = 'Referer';
+        }
+
+        // Remove Authorization and Cookie headers if URI is cross-origin.
+        if (Psr7\UriComparator::isCrossOrigin($request->getUri(), $modify['uri'])) {
+            $modify['remove_headers'][] = 'Authorization';
+            $modify['remove_headers'][] = 'Cookie';
+        }
+
+        return Psr7\Utils::modifyRequest($request, $modify);
+    }
+
+    /**
+     * Set the appropriate URL on the request based on the location header.
+     */
+    private static function redirectUri(
+        RequestInterface $request,
+        ResponseInterface $response,
+        array $protocols
+    ): UriInterface {
+        $location = Psr7\UriResolver::resolve(
+            $request->getUri(),
+            new Psr7\Uri($response->getHeaderLine('Location'))
+        );
+
+        // Ensure that the redirect URI is allowed based on the protocols.
+        if (!\in_array($location->getScheme(), $protocols)) {
+            throw new BadResponseException(\sprintf('Redirect URI, %s, does not use one of the allowed redirect protocols: %s', $location, \implode(', ', $protocols)), $request, $response);
+        }
+
+        return $location;
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/RequestOptions.php b/vendor/guzzlehttp/guzzle/src/RequestOptions.php
new file mode 100644
index 0000000..84a3500
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/RequestOptions.php
@@ -0,0 +1,274 @@
+decider = $decider;
+        $this->nextHandler = $nextHandler;
+        $this->delay = $delay ?: __CLASS__.'::exponentialDelay';
+    }
+
+    /**
+     * Default exponential backoff delay function.
+     *
+     * @return int milliseconds.
+     */
+    public static function exponentialDelay(int $retries): int
+    {
+        return (int) 2 ** ($retries - 1) * 1000;
+    }
+
+    public function __invoke(RequestInterface $request, array $options): PromiseInterface
+    {
+        if (!isset($options['retries'])) {
+            $options['retries'] = 0;
+        }
+
+        $fn = $this->nextHandler;
+
+        return $fn($request, $options)
+            ->then(
+                $this->onFulfilled($request, $options),
+                $this->onRejected($request, $options)
+            );
+    }
+
+    /**
+     * Execute fulfilled closure
+     */
+    private function onFulfilled(RequestInterface $request, array $options): callable
+    {
+        return function ($value) use ($request, $options) {
+            if (!($this->decider)(
+                $options['retries'],
+                $request,
+                $value,
+                null
+            )) {
+                return $value;
+            }
+
+            return $this->doRetry($request, $options, $value);
+        };
+    }
+
+    /**
+     * Execute rejected closure
+     */
+    private function onRejected(RequestInterface $req, array $options): callable
+    {
+        return function ($reason) use ($req, $options) {
+            if (!($this->decider)(
+                $options['retries'],
+                $req,
+                null,
+                $reason
+            )) {
+                return P\Create::rejectionFor($reason);
+            }
+
+            return $this->doRetry($req, $options);
+        };
+    }
+
+    private function doRetry(RequestInterface $request, array $options, ?ResponseInterface $response = null): PromiseInterface
+    {
+        $options['delay'] = ($this->delay)(++$options['retries'], $response, $request);
+
+        return $this($request, $options);
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/TransferStats.php b/vendor/guzzlehttp/guzzle/src/TransferStats.php
new file mode 100644
index 0000000..93fa334
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/TransferStats.php
@@ -0,0 +1,133 @@
+request = $request;
+        $this->response = $response;
+        $this->transferTime = $transferTime;
+        $this->handlerErrorData = $handlerErrorData;
+        $this->handlerStats = $handlerStats;
+    }
+
+    public function getRequest(): RequestInterface
+    {
+        return $this->request;
+    }
+
+    /**
+     * Returns the response that was received (if any).
+     */
+    public function getResponse(): ?ResponseInterface
+    {
+        return $this->response;
+    }
+
+    /**
+     * Returns true if a response was received.
+     */
+    public function hasResponse(): bool
+    {
+        return $this->response !== null;
+    }
+
+    /**
+     * Gets handler specific error data.
+     *
+     * This might be an exception, a integer representing an error code, or
+     * anything else. Relying on this value assumes that you know what handler
+     * you are using.
+     *
+     * @return mixed
+     */
+    public function getHandlerErrorData()
+    {
+        return $this->handlerErrorData;
+    }
+
+    /**
+     * Get the effective URI the request was sent to.
+     */
+    public function getEffectiveUri(): UriInterface
+    {
+        return $this->request->getUri();
+    }
+
+    /**
+     * Get the estimated time the request was being transferred by the handler.
+     *
+     * @return float|null Time in seconds.
+     */
+    public function getTransferTime(): ?float
+    {
+        return $this->transferTime;
+    }
+
+    /**
+     * Gets an array of all of the handler specific transfer data.
+     */
+    public function getHandlerStats(): array
+    {
+        return $this->handlerStats;
+    }
+
+    /**
+     * Get a specific handler statistic from the handler by name.
+     *
+     * @param string $stat Handler specific transfer stat to retrieve.
+     *
+     * @return mixed|null
+     */
+    public function getHandlerStat(string $stat)
+    {
+        return $this->handlerStats[$stat] ?? null;
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/Utils.php b/vendor/guzzlehttp/guzzle/src/Utils.php
new file mode 100644
index 0000000..df52927
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/Utils.php
@@ -0,0 +1,384 @@
+= 0) {
+            if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) {
+                $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler());
+            } elseif (\function_exists('curl_exec')) {
+                $handler = new CurlHandler();
+            } elseif (\function_exists('curl_multi_exec')) {
+                $handler = new CurlMultiHandler();
+            }
+        }
+
+        if (\ini_get('allow_url_fopen')) {
+            $handler = $handler
+                ? Proxy::wrapStreaming($handler, new StreamHandler())
+                : new StreamHandler();
+        } elseif (!$handler) {
+            throw new \RuntimeException('GuzzleHttp requires cURL, the allow_url_fopen ini setting, or a custom HTTP handler.');
+        }
+
+        return $handler;
+    }
+
+    /**
+     * Get the default User-Agent string to use with Guzzle.
+     */
+    public static function defaultUserAgent(): string
+    {
+        return sprintf('GuzzleHttp/%d', ClientInterface::MAJOR_VERSION);
+    }
+
+    /**
+     * Returns the default cacert bundle for the current system.
+     *
+     * First, the openssl.cafile and curl.cainfo php.ini settings are checked.
+     * If those settings are not configured, then the common locations for
+     * bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X
+     * and Windows are checked. If any of these file locations are found on
+     * disk, they will be utilized.
+     *
+     * Note: the result of this function is cached for subsequent calls.
+     *
+     * @throws \RuntimeException if no bundle can be found.
+     *
+     * @deprecated Utils::defaultCaBundle will be removed in guzzlehttp/guzzle:8.0. This method is not needed in PHP 5.6+.
+     */
+    public static function defaultCaBundle(): string
+    {
+        static $cached = null;
+        static $cafiles = [
+            // Red Hat, CentOS, Fedora (provided by the ca-certificates package)
+            '/etc/pki/tls/certs/ca-bundle.crt',
+            // Ubuntu, Debian (provided by the ca-certificates package)
+            '/etc/ssl/certs/ca-certificates.crt',
+            // FreeBSD (provided by the ca_root_nss package)
+            '/usr/local/share/certs/ca-root-nss.crt',
+            // SLES 12 (provided by the ca-certificates package)
+            '/var/lib/ca-certificates/ca-bundle.pem',
+            // OS X provided by homebrew (using the default path)
+            '/usr/local/etc/openssl/cert.pem',
+            // Google app engine
+            '/etc/ca-certificates.crt',
+            // Windows?
+            'C:\\windows\\system32\\curl-ca-bundle.crt',
+            'C:\\windows\\curl-ca-bundle.crt',
+        ];
+
+        if ($cached) {
+            return $cached;
+        }
+
+        if ($ca = \ini_get('openssl.cafile')) {
+            return $cached = $ca;
+        }
+
+        if ($ca = \ini_get('curl.cainfo')) {
+            return $cached = $ca;
+        }
+
+        foreach ($cafiles as $filename) {
+            if (\file_exists($filename)) {
+                return $cached = $filename;
+            }
+        }
+
+        throw new \RuntimeException(
+            <<< EOT
+No system CA bundle could be found in any of the the common system locations.
+PHP versions earlier than 5.6 are not properly configured to use the system's
+CA bundle by default. In order to verify peer certificates, you will need to
+supply the path on disk to a certificate bundle to the 'verify' request
+option: https://docs.guzzlephp.org/en/latest/request-options.html#verify. If
+you do not need a specific certificate bundle, then Mozilla provides a commonly
+used CA bundle which can be downloaded here (provided by the maintainer of
+cURL): https://curl.haxx.se/ca/cacert.pem. Once you have a CA bundle available
+on disk, you can set the 'openssl.cafile' PHP ini setting to point to the path
+to the file, allowing you to omit the 'verify' request option. See
+https://curl.haxx.se/docs/sslcerts.html for more information.
+EOT
+        );
+    }
+
+    /**
+     * Creates an associative array of lowercase header names to the actual
+     * header casing.
+     */
+    public static function normalizeHeaderKeys(array $headers): array
+    {
+        $result = [];
+        foreach (\array_keys($headers) as $key) {
+            $result[\strtolower($key)] = $key;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns true if the provided host matches any of the no proxy areas.
+     *
+     * This method will strip a port from the host if it is present. Each pattern
+     * can be matched with an exact match (e.g., "foo.com" == "foo.com") or a
+     * partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" ==
+     * "baz.foo.com", but ".foo.com" != "foo.com").
+     *
+     * Areas are matched in the following cases:
+     * 1. "*" (without quotes) always matches any hosts.
+     * 2. An exact match.
+     * 3. The area starts with "." and the area is the last part of the host. e.g.
+     *    '.mit.edu' will match any host that ends with '.mit.edu'.
+     *
+     * @param string   $host         Host to check against the patterns.
+     * @param string[] $noProxyArray An array of host patterns.
+     *
+     * @throws InvalidArgumentException
+     */
+    public static function isHostInNoProxy(string $host, array $noProxyArray): bool
+    {
+        if (\strlen($host) === 0) {
+            throw new InvalidArgumentException('Empty host provided');
+        }
+
+        // Strip port if present.
+        [$host] = \explode(':', $host, 2);
+
+        foreach ($noProxyArray as $area) {
+            // Always match on wildcards.
+            if ($area === '*') {
+                return true;
+            }
+
+            if (empty($area)) {
+                // Don't match on empty values.
+                continue;
+            }
+
+            if ($area === $host) {
+                // Exact matches.
+                return true;
+            }
+            // Special match if the area when prefixed with ".". Remove any
+            // existing leading "." and add a new leading ".".
+            $area = '.'.\ltrim($area, '.');
+            if (\substr($host, -\strlen($area)) === $area) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Wrapper for json_decode that throws when an error occurs.
+     *
+     * @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.
+     * @param int    $options Bitmask of JSON decode options.
+     *
+     * @return object|array|string|int|float|bool|null
+     *
+     * @throws InvalidArgumentException if the JSON cannot be decoded.
+     *
+     * @see https://www.php.net/manual/en/function.json-decode.php
+     */
+    public static function jsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
+    {
+        $data = \json_decode($json, $assoc, $depth, $options);
+        if (\JSON_ERROR_NONE !== \json_last_error()) {
+            throw new InvalidArgumentException('json_decode error: '.\json_last_error_msg());
+        }
+
+        return $data;
+    }
+
+    /**
+     * Wrapper for JSON encoding that throws when an error occurs.
+     *
+     * @param mixed $value   The value being encoded
+     * @param int   $options JSON encode option bitmask
+     * @param int   $depth   Set the maximum depth. Must be greater than zero.
+     *
+     * @throws InvalidArgumentException if the JSON cannot be encoded.
+     *
+     * @see https://www.php.net/manual/en/function.json-encode.php
+     */
+    public static function jsonEncode($value, int $options = 0, int $depth = 512): string
+    {
+        $json = \json_encode($value, $options, $depth);
+        if (\JSON_ERROR_NONE !== \json_last_error()) {
+            throw new InvalidArgumentException('json_encode error: '.\json_last_error_msg());
+        }
+
+        /** @var string */
+        return $json;
+    }
+
+    /**
+     * Wrapper for the hrtime() or microtime() functions
+     * (depending on the PHP version, one of the two is used)
+     *
+     * @return float UNIX timestamp
+     *
+     * @internal
+     */
+    public static function currentTime(): float
+    {
+        return (float) \function_exists('hrtime') ? \hrtime(true) / 1e9 : \microtime(true);
+    }
+
+    /**
+     * @throws InvalidArgumentException
+     *
+     * @internal
+     */
+    public static function idnUriConvert(UriInterface $uri, int $options = 0): UriInterface
+    {
+        if ($uri->getHost()) {
+            $asciiHost = self::idnToAsci($uri->getHost(), $options, $info);
+            if ($asciiHost === false) {
+                $errorBitSet = $info['errors'] ?? 0;
+
+                $errorConstants = array_filter(array_keys(get_defined_constants()), static function (string $name): bool {
+                    return substr($name, 0, 11) === 'IDNA_ERROR_';
+                });
+
+                $errors = [];
+                foreach ($errorConstants as $errorConstant) {
+                    if ($errorBitSet & constant($errorConstant)) {
+                        $errors[] = $errorConstant;
+                    }
+                }
+
+                $errorMessage = 'IDN conversion failed';
+                if ($errors) {
+                    $errorMessage .= ' (errors: '.implode(', ', $errors).')';
+                }
+
+                throw new InvalidArgumentException($errorMessage);
+            }
+            if ($uri->getHost() !== $asciiHost) {
+                // Replace URI only if the ASCII version is different
+                $uri = $uri->withHost($asciiHost);
+            }
+        }
+
+        return $uri;
+    }
+
+    /**
+     * @internal
+     */
+    public static function getenv(string $name): ?string
+    {
+        if (isset($_SERVER[$name])) {
+            return (string) $_SERVER[$name];
+        }
+
+        if (\PHP_SAPI === 'cli' && ($value = \getenv($name)) !== false && $value !== null) {
+            return (string) $value;
+        }
+
+        return null;
+    }
+
+    /**
+     * @return string|false
+     */
+    private static function idnToAsci(string $domain, int $options, ?array &$info = [])
+    {
+        if (\function_exists('idn_to_ascii') && \defined('INTL_IDNA_VARIANT_UTS46')) {
+            return \idn_to_ascii($domain, $options, \INTL_IDNA_VARIANT_UTS46, $info);
+        }
+
+        throw new \Error('ext-idn or symfony/polyfill-intl-idn not loaded or too old');
+    }
+}
diff --git a/vendor/guzzlehttp/guzzle/src/functions.php b/vendor/guzzlehttp/guzzle/src/functions.php
new file mode 100644
index 0000000..5edc66a
--- /dev/null
+++ b/vendor/guzzlehttp/guzzle/src/functions.php
@@ -0,0 +1,167 @@
+
+Copyright (c) 2015 Graham Campbell 
+Copyright (c) 2017 Tobias Schultze 
+Copyright (c) 2020 Tobias Nyholm 
+
+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/guzzlehttp/promises/README.md b/vendor/guzzlehttp/promises/README.md
new file mode 100644
index 0000000..1ea667a
--- /dev/null
+++ b/vendor/guzzlehttp/promises/README.md
@@ -0,0 +1,546 @@
+# Guzzle Promises
+
+[Promises/A+](https://promisesaplus.com/) implementation that handles promise
+chaining and resolution iteratively, allowing for "infinite" promise chaining
+while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
+for a general introduction to promises.
+
+- [Features](#features)
+- [Quick start](#quick-start)
+- [Synchronous wait](#synchronous-wait)
+- [Cancellation](#cancellation)
+- [API](#api)
+  - [Promise](#promise)
+  - [FulfilledPromise](#fulfilledpromise)
+  - [RejectedPromise](#rejectedpromise)
+- [Promise interop](#promise-interop)
+- [Implementation notes](#implementation-notes)
+
+
+## Features
+
+- [Promises/A+](https://promisesaplus.com/) implementation.
+- Promise resolution and chaining is handled iteratively, allowing for
+  "infinite" promise chaining.
+- Promises have a synchronous `wait` method.
+- Promises can be cancelled.
+- Works with any object that has a `then` function.
+- C# style async/await coroutine promises using
+  `GuzzleHttp\Promise\Coroutine::of()`.
+
+
+## Quick Start
+
+A *promise* represents the eventual result of an asynchronous operation. The
+primary way of interacting with a promise is through its `then` method, which
+registers callbacks to receive either a promise's eventual value or the reason
+why the promise cannot be fulfilled.
+
+### Callbacks
+
+Callbacks are registered with the `then` method by providing an optional 
+`$onFulfilled` followed by an optional `$onRejected` function.
+
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise->then(
+    // $onFulfilled
+    function ($value) {
+        echo 'The promise was fulfilled.';
+    },
+    // $onRejected
+    function ($reason) {
+        echo 'The promise was rejected.';
+    }
+);
+```
+
+*Resolving* a promise means that you either fulfill a promise with a *value* or
+reject a promise with a *reason*. Resolving a promise triggers callbacks
+registered with the promise's `then` method. These callbacks are triggered
+only once and in the order in which they were added.
+
+### Resolving a Promise
+
+Promises are fulfilled using the `resolve($value)` method. Resolving a promise
+with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
+all of the onFulfilled callbacks (resolving a promise with a rejected promise
+will reject the promise and trigger the `$onRejected` callbacks).
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise
+    ->then(function ($value) {
+        // Return a value and don't break the chain
+        return "Hello, " . $value;
+    })
+    // This then is executed after the first then and receives the value
+    // returned from the first then.
+    ->then(function ($value) {
+        echo $value;
+    });
+
+// Resolving the promise triggers the $onFulfilled callbacks and outputs
+// "Hello, reader."
+$promise->resolve('reader.');
+```
+
+### Promise Forwarding
+
+Promises can be chained one after the other. Each then in the chain is a new
+promise. The return value of a promise is what's forwarded to the next
+promise in the chain. Returning a promise in a `then` callback will cause the
+subsequent promises in the chain to only be fulfilled when the returned promise
+has been fulfilled. The next promise in the chain will be invoked with the
+resolved value of the promise.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$nextPromise = new Promise();
+
+$promise
+    ->then(function ($value) use ($nextPromise) {
+        echo $value;
+        return $nextPromise;
+    })
+    ->then(function ($value) {
+        echo $value;
+    });
+
+// Triggers the first callback and outputs "A"
+$promise->resolve('A');
+// Triggers the second callback and outputs "B"
+$nextPromise->resolve('B');
+```
+
+### Promise Rejection
+
+When a promise is rejected, the `$onRejected` callbacks are invoked with the
+rejection reason.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise->then(null, function ($reason) {
+    echo $reason;
+});
+
+$promise->reject('Error!');
+// Outputs "Error!"
+```
+
+### Rejection Forwarding
+
+If an exception is thrown in an `$onRejected` callback, subsequent
+`$onRejected` callbacks are invoked with the thrown exception as the reason.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise->then(null, function ($reason) {
+    throw new Exception($reason);
+})->then(null, function ($reason) {
+    assert($reason->getMessage() === 'Error!');
+});
+
+$promise->reject('Error!');
+```
+
+You can also forward a rejection down the promise chain by returning a
+`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
+`$onRejected` callback.
+
+```php
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\RejectedPromise;
+
+$promise = new Promise();
+$promise->then(null, function ($reason) {
+    return new RejectedPromise($reason);
+})->then(null, function ($reason) {
+    assert($reason === 'Error!');
+});
+
+$promise->reject('Error!');
+```
+
+If an exception is not thrown in a `$onRejected` callback and the callback
+does not return a rejected promise, downstream `$onFulfilled` callbacks are
+invoked using the value returned from the `$onRejected` callback.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise
+    ->then(null, function ($reason) {
+        return "It's ok";
+    })
+    ->then(function ($value) {
+        assert($value === "It's ok");
+    });
+
+$promise->reject('Error!');
+```
+
+
+## Synchronous Wait
+
+You can synchronously force promises to complete using a promise's `wait`
+method. When creating a promise, you can provide a wait function that is used
+to synchronously force a promise to complete. When a wait function is invoked
+it is expected to deliver a value to the promise or reject the promise. If the
+wait function does not deliver a value, then an exception is thrown. The wait
+function provided to a promise constructor is invoked when the `wait` function
+of the promise is called.
+
+```php
+$promise = new Promise(function () use (&$promise) {
+    $promise->resolve('foo');
+});
+
+// Calling wait will return the value of the promise.
+echo $promise->wait(); // outputs "foo"
+```
+
+If an exception is encountered while invoking the wait function of a promise,
+the promise is rejected with the exception and the exception is thrown.
+
+```php
+$promise = new Promise(function () use (&$promise) {
+    throw new Exception('foo');
+});
+
+$promise->wait(); // throws the exception.
+```
+
+Calling `wait` on a promise that has been fulfilled will not trigger the wait
+function. It will simply return the previously resolved value.
+
+```php
+$promise = new Promise(function () { die('this is not called!'); });
+$promise->resolve('foo');
+echo $promise->wait(); // outputs "foo"
+```
+
+Calling `wait` on a promise that has been rejected will throw an exception. If
+the rejection reason is an instance of `\Exception` the reason is thrown.
+Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
+can be obtained by calling the `getReason` method of the exception.
+
+```php
+$promise = new Promise();
+$promise->reject('foo');
+$promise->wait();
+```
+
+> PHP Fatal error:  Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'
+
+### Unwrapping a Promise
+
+When synchronously waiting on a promise, you are joining the state of the
+promise into the current state of execution (i.e., return the value of the
+promise if it was fulfilled or throw an exception if it was rejected). This is
+called "unwrapping" the promise. Waiting on a promise will by default unwrap
+the promise state.
+
+You can force a promise to resolve and *not* unwrap the state of the promise
+by passing `false` to the first argument of the `wait` function:
+
+```php
+$promise = new Promise();
+$promise->reject('foo');
+// This will not throw an exception. It simply ensures the promise has
+// been resolved.
+$promise->wait(false);
+```
+
+When unwrapping a promise, the resolved value of the promise will be waited
+upon until the unwrapped value is not a promise. This means that if you resolve
+promise A with a promise B and unwrap promise A, the value returned by the
+wait function will be the value delivered to promise B.
+
+**Note**: when you do not unwrap the promise, no value is returned.
+
+
+## Cancellation
+
+You can cancel a promise that has not yet been fulfilled using the `cancel()`
+method of a promise. When creating a promise you can provide an optional
+cancel function that when invoked cancels the action of computing a resolution
+of the promise.
+
+
+## API
+
+### Promise
+
+When creating a promise object, you can provide an optional `$waitFn` and
+`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
+expected to resolve the promise. `$cancelFn` is a function with no arguments
+that is expected to cancel the computation of a promise. It is invoked when the
+`cancel()` method of a promise is called.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise(
+    function () use (&$promise) {
+        $promise->resolve('waited');
+    },
+    function () {
+        // do something that will cancel the promise computation (e.g., close
+        // a socket, cancel a database query, etc...)
+    }
+);
+
+assert('waited' === $promise->wait());
+```
+
+A promise has the following methods:
+
+- `then(callable $onFulfilled, callable $onRejected) : PromiseInterface`
+  
+  Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.
+
+- `otherwise(callable $onRejected) : PromiseInterface`
+  
+  Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled.
+
+- `wait($unwrap = true) : mixed`
+
+  Synchronously waits on the promise to complete.
+  
+  `$unwrap` controls whether or not the value of the promise is returned for a
+  fulfilled promise or if an exception is thrown if the promise is rejected.
+  This is set to `true` by default.
+
+- `cancel()`
+
+  Attempts to cancel the promise if possible. The promise being cancelled and
+  the parent most ancestor that has not yet been resolved will also be
+  cancelled. Any promises waiting on the cancelled promise to resolve will also
+  be cancelled.
+
+- `getState() : string`
+
+  Returns the state of the promise. One of `pending`, `fulfilled`, or
+  `rejected`.
+
+- `resolve($value)`
+
+  Fulfills the promise with the given `$value`.
+
+- `reject($reason)`
+
+  Rejects the promise with the given `$reason`.
+
+
+### FulfilledPromise
+
+A fulfilled promise can be created to represent a promise that has been
+fulfilled.
+
+```php
+use GuzzleHttp\Promise\FulfilledPromise;
+
+$promise = new FulfilledPromise('value');
+
+// Fulfilled callbacks are immediately invoked.
+$promise->then(function ($value) {
+    echo $value;
+});
+```
+
+
+### RejectedPromise
+
+A rejected promise can be created to represent a promise that has been
+rejected.
+
+```php
+use GuzzleHttp\Promise\RejectedPromise;
+
+$promise = new RejectedPromise('Error');
+
+// Rejected callbacks are immediately invoked.
+$promise->then(null, function ($reason) {
+    echo $reason;
+});
+```
+
+
+## Promise Interoperability
+
+This library works with foreign promises that have a `then` method. This means
+you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
+for example. When a foreign promise is returned inside of a then method
+callback, promise resolution will occur recursively.
+
+```php
+// Create a React promise
+$deferred = new React\Promise\Deferred();
+$reactPromise = $deferred->promise();
+
+// Create a Guzzle promise that is fulfilled with a React promise.
+$guzzlePromise = new GuzzleHttp\Promise\Promise();
+$guzzlePromise->then(function ($value) use ($reactPromise) {
+    // Do something something with the value...
+    // Return the React promise
+    return $reactPromise;
+});
+```
+
+Please note that wait and cancel chaining is no longer possible when forwarding
+a foreign promise. You will need to wrap a third-party promise with a Guzzle
+promise in order to utilize wait and cancel functions with foreign promises.
+
+
+### Event Loop Integration
+
+In order to keep the stack size constant, Guzzle promises are resolved
+asynchronously using a task queue. When waiting on promises synchronously, the
+task queue will be automatically run to ensure that the blocking promise and
+any forwarded promises are resolved. When using promises asynchronously in an
+event loop, you will need to run the task queue on each tick of the loop. If
+you do not run the task queue, then promises will not be resolved.
+
+You can run the task queue using the `run()` method of the global task queue
+instance.
+
+```php
+// Get the global task queue
+$queue = GuzzleHttp\Promise\Utils::queue();
+$queue->run();
+```
+
+For example, you could use Guzzle promises with React using a periodic timer:
+
+```php
+$loop = React\EventLoop\Factory::create();
+$loop->addPeriodicTimer(0, [$queue, 'run']);
+```
+
+*TODO*: Perhaps adding a `futureTick()` on each tick would be faster?
+
+
+## Implementation Notes
+
+### Promise Resolution and Chaining is Handled Iteratively
+
+By shuffling pending handlers from one owner to another, promises are
+resolved iteratively, allowing for "infinite" then chaining.
+
+```php
+then(function ($v) {
+        // The stack size remains constant (a good thing)
+        echo xdebug_get_stack_depth() . ', ';
+        return $v + 1;
+    });
+}
+
+$parent->resolve(0);
+var_dump($p->wait()); // int(1000)
+
+```
+
+When a promise is fulfilled or rejected with a non-promise value, the promise
+then takes ownership of the handlers of each child promise and delivers values
+down the chain without using recursion.
+
+When a promise is resolved with another promise, the original promise transfers
+all of its pending handlers to the new promise. When the new promise is
+eventually resolved, all of the pending handlers are delivered the forwarded
+value.
+
+### A Promise is the Deferred
+
+Some promise libraries implement promises using a deferred object to represent
+a computation and a promise object to represent the delivery of the result of
+the computation. This is a nice separation of computation and delivery because
+consumers of the promise cannot modify the value that will be eventually
+delivered.
+
+One side effect of being able to implement promise resolution and chaining
+iteratively is that you need to be able for one promise to reach into the state
+of another promise to shuffle around ownership of handlers. In order to achieve
+this without making the handlers of a promise publicly mutable, a promise is
+also the deferred value, allowing promises of the same parent class to reach
+into and modify the private properties of promises of the same type. While this
+does allow consumers of the value to modify the resolution or rejection of the
+deferred, it is a small price to pay for keeping the stack size constant.
+
+```php
+$promise = new Promise();
+$promise->then(function ($value) { echo $value; });
+// The promise is the deferred value, so you can deliver a value to it.
+$promise->resolve('foo');
+// prints "foo"
+```
+
+
+## Upgrading from Function API
+
+A static API was first introduced in 1.4.0, in order to mitigate problems with
+functions conflicting between global and local copies of the package. The
+function API will be removed in 2.0.0. A migration table has been provided here
+for your convenience:
+
+| Original Function | Replacement Method |
+|----------------|----------------|
+| `queue` | `Utils::queue` |
+| `task` | `Utils::task` |
+| `promise_for` | `Create::promiseFor` |
+| `rejection_for` | `Create::rejectionFor` |
+| `exception_for` | `Create::exceptionFor` |
+| `iter_for` | `Create::iterFor` |
+| `inspect` | `Utils::inspect` |
+| `inspect_all` | `Utils::inspectAll` |
+| `unwrap` | `Utils::unwrap` |
+| `all` | `Utils::all` |
+| `some` | `Utils::some` |
+| `any` | `Utils::any` |
+| `settle` | `Utils::settle` |
+| `each` | `Each::of` |
+| `each_limit` | `Each::ofLimit` |
+| `each_limit_all` | `Each::ofLimitAll` |
+| `!is_fulfilled` | `Is::pending` |
+| `is_fulfilled` | `Is::fulfilled` |
+| `is_rejected` | `Is::rejected` |
+| `is_settled` | `Is::settled` |
+| `coroutine` | `Coroutine::of` |
+
+
+## Security
+
+If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/promises/security/policy) for more information.
+
+
+## License
+
+Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
+
+
+## For Enterprise
+
+Available as part of the Tidelift Subscription
+
+The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-promises?utm_source=packagist-guzzlehttp-promises&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
diff --git a/vendor/guzzlehttp/promises/composer.json b/vendor/guzzlehttp/promises/composer.json
new file mode 100644
index 0000000..966e3e3
--- /dev/null
+++ b/vendor/guzzlehttp/promises/composer.json
@@ -0,0 +1,53 @@
+{
+    "name": "guzzlehttp/promises",
+    "description": "Guzzle promises library",
+    "keywords": ["promise"],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Graham Campbell",
+            "email": "hello@gjcampbell.co.uk",
+            "homepage": "https://github.com/GrahamCampbell"
+        },
+        {
+            "name": "Michael Dowling",
+            "email": "mtdowling@gmail.com",
+            "homepage": "https://github.com/mtdowling"
+        },
+        {
+            "name": "Tobias Nyholm",
+            "email": "tobias.nyholm@gmail.com",
+            "homepage": "https://github.com/Nyholm"
+        },
+        {
+            "name": "Tobias Schultze",
+            "email": "webmaster@tubo-world.de",
+            "homepage": "https://github.com/Tobion"
+        }
+    ],
+    "require": {
+        "php": ">=5.5"
+    },
+    "require-dev": {
+        "symfony/phpunit-bridge": "^4.4 || ^5.1"
+    },
+    "autoload": {
+        "psr-4": {
+            "GuzzleHttp\\Promise\\": "src/"
+        },
+        "files": ["src/functions_include.php"]
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "GuzzleHttp\\Promise\\Tests\\": "tests/"
+        }
+    },
+    "scripts": {
+        "test": "vendor/bin/simple-phpunit",
+        "test-ci": "vendor/bin/simple-phpunit --coverage-text"
+    },
+    "config": {
+        "preferred-install": "dist",
+        "sort-packages": true
+    }
+}
diff --git a/vendor/guzzlehttp/promises/src/AggregateException.php b/vendor/guzzlehttp/promises/src/AggregateException.php
new file mode 100644
index 0000000..d2b5712
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/AggregateException.php
@@ -0,0 +1,17 @@
+then(function ($v) { echo $v; });
+ *
+ * @param callable $generatorFn Generator function to wrap into a promise.
+ *
+ * @return Promise
+ *
+ * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
+ */
+final class Coroutine implements PromiseInterface
+{
+    /**
+     * @var PromiseInterface|null
+     */
+    private $currentPromise;
+
+    /**
+     * @var Generator
+     */
+    private $generator;
+
+    /**
+     * @var Promise
+     */
+    private $result;
+
+    public function __construct(callable $generatorFn)
+    {
+        $this->generator = $generatorFn();
+        $this->result = new Promise(function () {
+            while (isset($this->currentPromise)) {
+                $this->currentPromise->wait();
+            }
+        });
+        try {
+            $this->nextCoroutine($this->generator->current());
+        } catch (\Exception $exception) {
+            $this->result->reject($exception);
+        } catch (Throwable $throwable) {
+            $this->result->reject($throwable);
+        }
+    }
+
+    /**
+     * Create a new coroutine.
+     *
+     * @return self
+     */
+    public static function of(callable $generatorFn)
+    {
+        return new self($generatorFn);
+    }
+
+    public function then(
+        callable $onFulfilled = null,
+        callable $onRejected = null
+    ) {
+        return $this->result->then($onFulfilled, $onRejected);
+    }
+
+    public function otherwise(callable $onRejected)
+    {
+        return $this->result->otherwise($onRejected);
+    }
+
+    public function wait($unwrap = true)
+    {
+        return $this->result->wait($unwrap);
+    }
+
+    public function getState()
+    {
+        return $this->result->getState();
+    }
+
+    public function resolve($value)
+    {
+        $this->result->resolve($value);
+    }
+
+    public function reject($reason)
+    {
+        $this->result->reject($reason);
+    }
+
+    public function cancel()
+    {
+        $this->currentPromise->cancel();
+        $this->result->cancel();
+    }
+
+    private function nextCoroutine($yielded)
+    {
+        $this->currentPromise = Create::promiseFor($yielded)
+            ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
+    }
+
+    /**
+     * @internal
+     */
+    public function _handleSuccess($value)
+    {
+        unset($this->currentPromise);
+        try {
+            $next = $this->generator->send($value);
+            if ($this->generator->valid()) {
+                $this->nextCoroutine($next);
+            } else {
+                $this->result->resolve($value);
+            }
+        } catch (Exception $exception) {
+            $this->result->reject($exception);
+        } catch (Throwable $throwable) {
+            $this->result->reject($throwable);
+        }
+    }
+
+    /**
+     * @internal
+     */
+    public function _handleFailure($reason)
+    {
+        unset($this->currentPromise);
+        try {
+            $nextYield = $this->generator->throw(Create::exceptionFor($reason));
+            // The throw was caught, so keep iterating on the coroutine
+            $this->nextCoroutine($nextYield);
+        } catch (Exception $exception) {
+            $this->result->reject($exception);
+        } catch (Throwable $throwable) {
+            $this->result->reject($throwable);
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/promises/src/Create.php b/vendor/guzzlehttp/promises/src/Create.php
new file mode 100644
index 0000000..8d038e9
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/Create.php
@@ -0,0 +1,84 @@
+then([$promise, 'resolve'], [$promise, 'reject']);
+            return $promise;
+        }
+
+        return new FulfilledPromise($value);
+    }
+
+    /**
+     * Creates a rejected promise for a reason if the reason is not a promise.
+     * If the provided reason is a promise, then it is returned as-is.
+     *
+     * @param mixed $reason Promise or reason.
+     *
+     * @return PromiseInterface
+     */
+    public static function rejectionFor($reason)
+    {
+        if ($reason instanceof PromiseInterface) {
+            return $reason;
+        }
+
+        return new RejectedPromise($reason);
+    }
+
+    /**
+     * Create an exception for a rejected promise value.
+     *
+     * @param mixed $reason
+     *
+     * @return \Exception|\Throwable
+     */
+    public static function exceptionFor($reason)
+    {
+        if ($reason instanceof \Exception || $reason instanceof \Throwable) {
+            return $reason;
+        }
+
+        return new RejectionException($reason);
+    }
+
+    /**
+     * Returns an iterator for the given value.
+     *
+     * @param mixed $value
+     *
+     * @return \Iterator
+     */
+    public static function iterFor($value)
+    {
+        if ($value instanceof \Iterator) {
+            return $value;
+        }
+
+        if (is_array($value)) {
+            return new \ArrayIterator($value);
+        }
+
+        return new \ArrayIterator([$value]);
+    }
+}
diff --git a/vendor/guzzlehttp/promises/src/Each.php b/vendor/guzzlehttp/promises/src/Each.php
new file mode 100644
index 0000000..ff8efd7
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/Each.php
@@ -0,0 +1,90 @@
+ $onFulfilled,
+            'rejected'  => $onRejected
+        ]))->promise();
+    }
+
+    /**
+     * Like of, but only allows a certain number of outstanding promises at any
+     * given time.
+     *
+     * $concurrency may be an integer or a function that accepts the number of
+     * pending promises and returns a numeric concurrency limit value to allow
+     * for dynamic a concurrency size.
+     *
+     * @param mixed        $iterable
+     * @param int|callable $concurrency
+     * @param callable     $onFulfilled
+     * @param callable     $onRejected
+     *
+     * @return PromiseInterface
+     */
+    public static function ofLimit(
+        $iterable,
+        $concurrency,
+        callable $onFulfilled = null,
+        callable $onRejected = null
+    ) {
+        return (new EachPromise($iterable, [
+            'fulfilled'   => $onFulfilled,
+            'rejected'    => $onRejected,
+            'concurrency' => $concurrency
+        ]))->promise();
+    }
+
+    /**
+     * Like limit, but ensures that no promise in the given $iterable argument
+     * is rejected. If any promise is rejected, then the aggregate promise is
+     * rejected with the encountered rejection.
+     *
+     * @param mixed        $iterable
+     * @param int|callable $concurrency
+     * @param callable     $onFulfilled
+     *
+     * @return PromiseInterface
+     */
+    public static function ofLimitAll(
+        $iterable,
+        $concurrency,
+        callable $onFulfilled = null
+    ) {
+        return self::ofLimit(
+            $iterable,
+            $concurrency,
+            $onFulfilled,
+            function ($reason, $idx, PromiseInterface $aggregate) {
+                $aggregate->reject($reason);
+            }
+        );
+    }
+}
diff --git a/vendor/guzzlehttp/promises/src/EachPromise.php b/vendor/guzzlehttp/promises/src/EachPromise.php
new file mode 100644
index 0000000..280d799
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/EachPromise.php
@@ -0,0 +1,247 @@
+iterable = Create::iterFor($iterable);
+
+        if (isset($config['concurrency'])) {
+            $this->concurrency = $config['concurrency'];
+        }
+
+        if (isset($config['fulfilled'])) {
+            $this->onFulfilled = $config['fulfilled'];
+        }
+
+        if (isset($config['rejected'])) {
+            $this->onRejected = $config['rejected'];
+        }
+    }
+
+    /** @psalm-suppress InvalidNullableReturnType */
+    public function promise()
+    {
+        if ($this->aggregate) {
+            return $this->aggregate;
+        }
+
+        try {
+            $this->createPromise();
+            /** @psalm-assert Promise $this->aggregate */
+            $this->iterable->rewind();
+            $this->refillPending();
+        } catch (\Throwable $e) {
+            $this->aggregate->reject($e);
+        } catch (\Exception $e) {
+            $this->aggregate->reject($e);
+        }
+
+        /**
+         * @psalm-suppress NullableReturnStatement
+         * @phpstan-ignore-next-line
+         */
+        return $this->aggregate;
+    }
+
+    private function createPromise()
+    {
+        $this->mutex = false;
+        $this->aggregate = new Promise(function () {
+            if ($this->checkIfFinished()) {
+                return;
+            }
+            reset($this->pending);
+            // Consume a potentially fluctuating list of promises while
+            // ensuring that indexes are maintained (precluding array_shift).
+            while ($promise = current($this->pending)) {
+                next($this->pending);
+                $promise->wait();
+                if (Is::settled($this->aggregate)) {
+                    return;
+                }
+            }
+        });
+
+        // Clear the references when the promise is resolved.
+        $clearFn = function () {
+            $this->iterable = $this->concurrency = $this->pending = null;
+            $this->onFulfilled = $this->onRejected = null;
+            $this->nextPendingIndex = 0;
+        };
+
+        $this->aggregate->then($clearFn, $clearFn);
+    }
+
+    private function refillPending()
+    {
+        if (!$this->concurrency) {
+            // Add all pending promises.
+            while ($this->addPending() && $this->advanceIterator());
+            return;
+        }
+
+        // Add only up to N pending promises.
+        $concurrency = is_callable($this->concurrency)
+            ? call_user_func($this->concurrency, count($this->pending))
+            : $this->concurrency;
+        $concurrency = max($concurrency - count($this->pending), 0);
+        // Concurrency may be set to 0 to disallow new promises.
+        if (!$concurrency) {
+            return;
+        }
+        // Add the first pending promise.
+        $this->addPending();
+        // Note this is special handling for concurrency=1 so that we do
+        // not advance the iterator after adding the first promise. This
+        // helps work around issues with generators that might not have the
+        // next value to yield until promise callbacks are called.
+        while (--$concurrency
+            && $this->advanceIterator()
+            && $this->addPending());
+    }
+
+    private function addPending()
+    {
+        if (!$this->iterable || !$this->iterable->valid()) {
+            return false;
+        }
+
+        $promise = Create::promiseFor($this->iterable->current());
+        $key = $this->iterable->key();
+
+        // Iterable keys may not be unique, so we use a counter to
+        // guarantee uniqueness
+        $idx = $this->nextPendingIndex++;
+
+        $this->pending[$idx] = $promise->then(
+            function ($value) use ($idx, $key) {
+                if ($this->onFulfilled) {
+                    call_user_func(
+                        $this->onFulfilled,
+                        $value,
+                        $key,
+                        $this->aggregate
+                    );
+                }
+                $this->step($idx);
+            },
+            function ($reason) use ($idx, $key) {
+                if ($this->onRejected) {
+                    call_user_func(
+                        $this->onRejected,
+                        $reason,
+                        $key,
+                        $this->aggregate
+                    );
+                }
+                $this->step($idx);
+            }
+        );
+
+        return true;
+    }
+
+    private function advanceIterator()
+    {
+        // Place a lock on the iterator so that we ensure to not recurse,
+        // preventing fatal generator errors.
+        if ($this->mutex) {
+            return false;
+        }
+
+        $this->mutex = true;
+
+        try {
+            $this->iterable->next();
+            $this->mutex = false;
+            return true;
+        } catch (\Throwable $e) {
+            $this->aggregate->reject($e);
+            $this->mutex = false;
+            return false;
+        } catch (\Exception $e) {
+            $this->aggregate->reject($e);
+            $this->mutex = false;
+            return false;
+        }
+    }
+
+    private function step($idx)
+    {
+        // If the promise was already resolved, then ignore this step.
+        if (Is::settled($this->aggregate)) {
+            return;
+        }
+
+        unset($this->pending[$idx]);
+
+        // Only refill pending promises if we are not locked, preventing the
+        // EachPromise to recursively invoke the provided iterator, which
+        // cause a fatal error: "Cannot resume an already running generator"
+        if ($this->advanceIterator() && !$this->checkIfFinished()) {
+            // Add more pending promises if possible.
+            $this->refillPending();
+        }
+    }
+
+    private function checkIfFinished()
+    {
+        if (!$this->pending && !$this->iterable->valid()) {
+            // Resolve the promise if there's nothing left to do.
+            $this->aggregate->resolve(null);
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/vendor/guzzlehttp/promises/src/FulfilledPromise.php b/vendor/guzzlehttp/promises/src/FulfilledPromise.php
new file mode 100644
index 0000000..98f72a6
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/FulfilledPromise.php
@@ -0,0 +1,84 @@
+value = $value;
+    }
+
+    public function then(
+        callable $onFulfilled = null,
+        callable $onRejected = null
+    ) {
+        // Return itself if there is no onFulfilled function.
+        if (!$onFulfilled) {
+            return $this;
+        }
+
+        $queue = Utils::queue();
+        $p = new Promise([$queue, 'run']);
+        $value = $this->value;
+        $queue->add(static function () use ($p, $value, $onFulfilled) {
+            if (Is::pending($p)) {
+                try {
+                    $p->resolve($onFulfilled($value));
+                } catch (\Throwable $e) {
+                    $p->reject($e);
+                } catch (\Exception $e) {
+                    $p->reject($e);
+                }
+            }
+        });
+
+        return $p;
+    }
+
+    public function otherwise(callable $onRejected)
+    {
+        return $this->then(null, $onRejected);
+    }
+
+    public function wait($unwrap = true, $defaultDelivery = null)
+    {
+        return $unwrap ? $this->value : null;
+    }
+
+    public function getState()
+    {
+        return self::FULFILLED;
+    }
+
+    public function resolve($value)
+    {
+        if ($value !== $this->value) {
+            throw new \LogicException("Cannot resolve a fulfilled promise");
+        }
+    }
+
+    public function reject($reason)
+    {
+        throw new \LogicException("Cannot reject a fulfilled promise");
+    }
+
+    public function cancel()
+    {
+        // pass
+    }
+}
diff --git a/vendor/guzzlehttp/promises/src/Is.php b/vendor/guzzlehttp/promises/src/Is.php
new file mode 100644
index 0000000..c3ed8d0
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/Is.php
@@ -0,0 +1,46 @@
+getState() === PromiseInterface::PENDING;
+    }
+
+    /**
+     * Returns true if a promise is fulfilled or rejected.
+     *
+     * @return bool
+     */
+    public static function settled(PromiseInterface $promise)
+    {
+        return $promise->getState() !== PromiseInterface::PENDING;
+    }
+
+    /**
+     * Returns true if a promise is fulfilled.
+     *
+     * @return bool
+     */
+    public static function fulfilled(PromiseInterface $promise)
+    {
+        return $promise->getState() === PromiseInterface::FULFILLED;
+    }
+
+    /**
+     * Returns true if a promise is rejected.
+     *
+     * @return bool
+     */
+    public static function rejected(PromiseInterface $promise)
+    {
+        return $promise->getState() === PromiseInterface::REJECTED;
+    }
+}
diff --git a/vendor/guzzlehttp/promises/src/Promise.php b/vendor/guzzlehttp/promises/src/Promise.php
new file mode 100644
index 0000000..7593905
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/Promise.php
@@ -0,0 +1,278 @@
+waitFn = $waitFn;
+        $this->cancelFn = $cancelFn;
+    }
+
+    public function then(
+        callable $onFulfilled = null,
+        callable $onRejected = null
+    ) {
+        if ($this->state === self::PENDING) {
+            $p = new Promise(null, [$this, 'cancel']);
+            $this->handlers[] = [$p, $onFulfilled, $onRejected];
+            $p->waitList = $this->waitList;
+            $p->waitList[] = $this;
+            return $p;
+        }
+
+        // Return a fulfilled promise and immediately invoke any callbacks.
+        if ($this->state === self::FULFILLED) {
+            $promise = Create::promiseFor($this->result);
+            return $onFulfilled ? $promise->then($onFulfilled) : $promise;
+        }
+
+        // It's either cancelled or rejected, so return a rejected promise
+        // and immediately invoke any callbacks.
+        $rejection = Create::rejectionFor($this->result);
+        return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
+    }
+
+    public function otherwise(callable $onRejected)
+    {
+        return $this->then(null, $onRejected);
+    }
+
+    public function wait($unwrap = true)
+    {
+        $this->waitIfPending();
+
+        if ($this->result instanceof PromiseInterface) {
+            return $this->result->wait($unwrap);
+        }
+        if ($unwrap) {
+            if ($this->state === self::FULFILLED) {
+                return $this->result;
+            }
+            // It's rejected so "unwrap" and throw an exception.
+            throw Create::exceptionFor($this->result);
+        }
+    }
+
+    public function getState()
+    {
+        return $this->state;
+    }
+
+    public function cancel()
+    {
+        if ($this->state !== self::PENDING) {
+            return;
+        }
+
+        $this->waitFn = $this->waitList = null;
+
+        if ($this->cancelFn) {
+            $fn = $this->cancelFn;
+            $this->cancelFn = null;
+            try {
+                $fn();
+            } catch (\Throwable $e) {
+                $this->reject($e);
+            } catch (\Exception $e) {
+                $this->reject($e);
+            }
+        }
+
+        // Reject the promise only if it wasn't rejected in a then callback.
+        /** @psalm-suppress RedundantCondition */
+        if ($this->state === self::PENDING) {
+            $this->reject(new CancellationException('Promise has been cancelled'));
+        }
+    }
+
+    public function resolve($value)
+    {
+        $this->settle(self::FULFILLED, $value);
+    }
+
+    public function reject($reason)
+    {
+        $this->settle(self::REJECTED, $reason);
+    }
+
+    private function settle($state, $value)
+    {
+        if ($this->state !== self::PENDING) {
+            // Ignore calls with the same resolution.
+            if ($state === $this->state && $value === $this->result) {
+                return;
+            }
+            throw $this->state === $state
+                ? new \LogicException("The promise is already {$state}.")
+                : new \LogicException("Cannot change a {$this->state} promise to {$state}");
+        }
+
+        if ($value === $this) {
+            throw new \LogicException('Cannot fulfill or reject a promise with itself');
+        }
+
+        // Clear out the state of the promise but stash the handlers.
+        $this->state = $state;
+        $this->result = $value;
+        $handlers = $this->handlers;
+        $this->handlers = null;
+        $this->waitList = $this->waitFn = null;
+        $this->cancelFn = null;
+
+        if (!$handlers) {
+            return;
+        }
+
+        // If the value was not a settled promise or a thenable, then resolve
+        // it in the task queue using the correct ID.
+        if (!is_object($value) || !method_exists($value, 'then')) {
+            $id = $state === self::FULFILLED ? 1 : 2;
+            // It's a success, so resolve the handlers in the queue.
+            Utils::queue()->add(static function () use ($id, $value, $handlers) {
+                foreach ($handlers as $handler) {
+                    self::callHandler($id, $value, $handler);
+                }
+            });
+        } elseif ($value instanceof Promise && Is::pending($value)) {
+            // We can just merge our handlers onto the next promise.
+            $value->handlers = array_merge($value->handlers, $handlers);
+        } else {
+            // Resolve the handlers when the forwarded promise is resolved.
+            $value->then(
+                static function ($value) use ($handlers) {
+                    foreach ($handlers as $handler) {
+                        self::callHandler(1, $value, $handler);
+                    }
+                },
+                static function ($reason) use ($handlers) {
+                    foreach ($handlers as $handler) {
+                        self::callHandler(2, $reason, $handler);
+                    }
+                }
+            );
+        }
+    }
+
+    /**
+     * Call a stack of handlers using a specific callback index and value.
+     *
+     * @param int   $index   1 (resolve) or 2 (reject).
+     * @param mixed $value   Value to pass to the callback.
+     * @param array $handler Array of handler data (promise and callbacks).
+     */
+    private static function callHandler($index, $value, array $handler)
+    {
+        /** @var PromiseInterface $promise */
+        $promise = $handler[0];
+
+        // The promise may have been cancelled or resolved before placing
+        // this thunk in the queue.
+        if (Is::settled($promise)) {
+            return;
+        }
+
+        try {
+            if (isset($handler[$index])) {
+                /*
+                 * If $f throws an exception, then $handler will be in the exception
+                 * stack trace. Since $handler contains a reference to the callable
+                 * itself we get a circular reference. We clear the $handler
+                 * here to avoid that memory leak.
+                 */
+                $f = $handler[$index];
+                unset($handler);
+                $promise->resolve($f($value));
+            } elseif ($index === 1) {
+                // Forward resolution values as-is.
+                $promise->resolve($value);
+            } else {
+                // Forward rejections down the chain.
+                $promise->reject($value);
+            }
+        } catch (\Throwable $reason) {
+            $promise->reject($reason);
+        } catch (\Exception $reason) {
+            $promise->reject($reason);
+        }
+    }
+
+    private function waitIfPending()
+    {
+        if ($this->state !== self::PENDING) {
+            return;
+        } elseif ($this->waitFn) {
+            $this->invokeWaitFn();
+        } elseif ($this->waitList) {
+            $this->invokeWaitList();
+        } else {
+            // If there's no wait function, then reject the promise.
+            $this->reject('Cannot wait on a promise that has '
+                . 'no internal wait function. You must provide a wait '
+                . 'function when constructing the promise to be able to '
+                . 'wait on a promise.');
+        }
+
+        Utils::queue()->run();
+
+        /** @psalm-suppress RedundantCondition */
+        if ($this->state === self::PENDING) {
+            $this->reject('Invoking the wait callback did not resolve the promise');
+        }
+    }
+
+    private function invokeWaitFn()
+    {
+        try {
+            $wfn = $this->waitFn;
+            $this->waitFn = null;
+            $wfn(true);
+        } catch (\Exception $reason) {
+            if ($this->state === self::PENDING) {
+                // The promise has not been resolved yet, so reject the promise
+                // with the exception.
+                $this->reject($reason);
+            } else {
+                // The promise was already resolved, so there's a problem in
+                // the application.
+                throw $reason;
+            }
+        }
+    }
+
+    private function invokeWaitList()
+    {
+        $waitList = $this->waitList;
+        $this->waitList = null;
+
+        foreach ($waitList as $result) {
+            do {
+                $result->waitIfPending();
+                $result = $result->result;
+            } while ($result instanceof Promise);
+
+            if ($result instanceof PromiseInterface) {
+                $result->wait(false);
+            }
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/promises/src/PromiseInterface.php b/vendor/guzzlehttp/promises/src/PromiseInterface.php
new file mode 100644
index 0000000..e598331
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/PromiseInterface.php
@@ -0,0 +1,97 @@
+reason = $reason;
+    }
+
+    public function then(
+        callable $onFulfilled = null,
+        callable $onRejected = null
+    ) {
+        // If there's no onRejected callback then just return self.
+        if (!$onRejected) {
+            return $this;
+        }
+
+        $queue = Utils::queue();
+        $reason = $this->reason;
+        $p = new Promise([$queue, 'run']);
+        $queue->add(static function () use ($p, $reason, $onRejected) {
+            if (Is::pending($p)) {
+                try {
+                    // Return a resolved promise if onRejected does not throw.
+                    $p->resolve($onRejected($reason));
+                } catch (\Throwable $e) {
+                    // onRejected threw, so return a rejected promise.
+                    $p->reject($e);
+                } catch (\Exception $e) {
+                    // onRejected threw, so return a rejected promise.
+                    $p->reject($e);
+                }
+            }
+        });
+
+        return $p;
+    }
+
+    public function otherwise(callable $onRejected)
+    {
+        return $this->then(null, $onRejected);
+    }
+
+    public function wait($unwrap = true, $defaultDelivery = null)
+    {
+        if ($unwrap) {
+            throw Create::exceptionFor($this->reason);
+        }
+
+        return null;
+    }
+
+    public function getState()
+    {
+        return self::REJECTED;
+    }
+
+    public function resolve($value)
+    {
+        throw new \LogicException("Cannot resolve a rejected promise");
+    }
+
+    public function reject($reason)
+    {
+        if ($reason !== $this->reason) {
+            throw new \LogicException("Cannot reject a rejected promise");
+        }
+    }
+
+    public function cancel()
+    {
+        // pass
+    }
+}
diff --git a/vendor/guzzlehttp/promises/src/RejectionException.php b/vendor/guzzlehttp/promises/src/RejectionException.php
new file mode 100644
index 0000000..e2f1377
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/RejectionException.php
@@ -0,0 +1,48 @@
+reason = $reason;
+
+        $message = 'The promise was rejected';
+
+        if ($description) {
+            $message .= ' with reason: ' . $description;
+        } elseif (is_string($reason)
+            || (is_object($reason) && method_exists($reason, '__toString'))
+        ) {
+            $message .= ' with reason: ' . $this->reason;
+        } elseif ($reason instanceof \JsonSerializable) {
+            $message .= ' with reason: '
+                . json_encode($this->reason, JSON_PRETTY_PRINT);
+        }
+
+        parent::__construct($message);
+    }
+
+    /**
+     * Returns the rejection reason.
+     *
+     * @return mixed
+     */
+    public function getReason()
+    {
+        return $this->reason;
+    }
+}
diff --git a/vendor/guzzlehttp/promises/src/TaskQueue.php b/vendor/guzzlehttp/promises/src/TaskQueue.php
new file mode 100644
index 0000000..f0fba2c
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/TaskQueue.php
@@ -0,0 +1,67 @@
+run();
+ */
+class TaskQueue implements TaskQueueInterface
+{
+    private $enableShutdown = true;
+    private $queue = [];
+
+    public function __construct($withShutdown = true)
+    {
+        if ($withShutdown) {
+            register_shutdown_function(function () {
+                if ($this->enableShutdown) {
+                    // Only run the tasks if an E_ERROR didn't occur.
+                    $err = error_get_last();
+                    if (!$err || ($err['type'] ^ E_ERROR)) {
+                        $this->run();
+                    }
+                }
+            });
+        }
+    }
+
+    public function isEmpty()
+    {
+        return !$this->queue;
+    }
+
+    public function add(callable $task)
+    {
+        $this->queue[] = $task;
+    }
+
+    public function run()
+    {
+        while ($task = array_shift($this->queue)) {
+            /** @var callable $task */
+            $task();
+        }
+    }
+
+    /**
+     * The task queue will be run and exhausted by default when the process
+     * exits IFF the exit is not the result of a PHP E_ERROR error.
+     *
+     * You can disable running the automatic shutdown of the queue by calling
+     * this function. If you disable the task queue shutdown process, then you
+     * MUST either run the task queue (as a result of running your event loop
+     * or manually using the run() method) or wait on each outstanding promise.
+     *
+     * Note: This shutdown will occur before any destructors are triggered.
+     */
+    public function disableShutdown()
+    {
+        $this->enableShutdown = false;
+    }
+}
diff --git a/vendor/guzzlehttp/promises/src/TaskQueueInterface.php b/vendor/guzzlehttp/promises/src/TaskQueueInterface.php
new file mode 100644
index 0000000..723d4d5
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/TaskQueueInterface.php
@@ -0,0 +1,24 @@
+
+     * while ($eventLoop->isRunning()) {
+     *     GuzzleHttp\Promise\Utils::queue()->run();
+     * }
+     * 
+     *
+     * @param TaskQueueInterface $assign Optionally specify a new queue instance.
+     *
+     * @return TaskQueueInterface
+     */
+    public static function queue(TaskQueueInterface $assign = null)
+    {
+        static $queue;
+
+        if ($assign) {
+            $queue = $assign;
+        } elseif (!$queue) {
+            $queue = new TaskQueue();
+        }
+
+        return $queue;
+    }
+
+    /**
+     * Adds a function to run in the task queue when it is next `run()` and
+     * returns a promise that is fulfilled or rejected with the result.
+     *
+     * @param callable $task Task function to run.
+     *
+     * @return PromiseInterface
+     */
+    public static function task(callable $task)
+    {
+        $queue = self::queue();
+        $promise = new Promise([$queue, 'run']);
+        $queue->add(function () use ($task, $promise) {
+            try {
+                if (Is::pending($promise)) {
+                    $promise->resolve($task());
+                }
+            } catch (\Throwable $e) {
+                $promise->reject($e);
+            } catch (\Exception $e) {
+                $promise->reject($e);
+            }
+        });
+
+        return $promise;
+    }
+
+    /**
+     * Synchronously waits on a promise to resolve and returns an inspection
+     * state array.
+     *
+     * Returns a state associative array containing a "state" key mapping to a
+     * valid promise state. If the state of the promise is "fulfilled", the
+     * array will contain a "value" key mapping to the fulfilled value of the
+     * promise. If the promise is rejected, the array will contain a "reason"
+     * key mapping to the rejection reason of the promise.
+     *
+     * @param PromiseInterface $promise Promise or value.
+     *
+     * @return array
+     */
+    public static function inspect(PromiseInterface $promise)
+    {
+        try {
+            return [
+                'state' => PromiseInterface::FULFILLED,
+                'value' => $promise->wait()
+            ];
+        } catch (RejectionException $e) {
+            return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
+        } catch (\Throwable $e) {
+            return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
+        } catch (\Exception $e) {
+            return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
+        }
+    }
+
+    /**
+     * Waits on all of the provided promises, but does not unwrap rejected
+     * promises as thrown exception.
+     *
+     * Returns an array of inspection state arrays.
+     *
+     * @see inspect for the inspection state array format.
+     *
+     * @param PromiseInterface[] $promises Traversable of promises to wait upon.
+     *
+     * @return array
+     */
+    public static function inspectAll($promises)
+    {
+        $results = [];
+        foreach ($promises as $key => $promise) {
+            $results[$key] = self::inspect($promise);
+        }
+
+        return $results;
+    }
+
+    /**
+     * Waits on all of the provided promises and returns the fulfilled values.
+     *
+     * Returns an array that contains the value of each promise (in the same
+     * order the promises were provided). An exception is thrown if any of the
+     * promises are rejected.
+     *
+     * @param iterable $promises Iterable of PromiseInterface objects to wait on.
+     *
+     * @return array
+     *
+     * @throws \Exception on error
+     * @throws \Throwable on error in PHP >=7
+     */
+    public static function unwrap($promises)
+    {
+        $results = [];
+        foreach ($promises as $key => $promise) {
+            $results[$key] = $promise->wait();
+        }
+
+        return $results;
+    }
+
+    /**
+     * Given an array of promises, return a promise that is fulfilled when all
+     * the items in the array are fulfilled.
+     *
+     * The promise's fulfillment value is an array with fulfillment values at
+     * respective positions to the original array. If any promise in the array
+     * rejects, the returned promise is rejected with the rejection reason.
+     *
+     * @param mixed $promises  Promises or values.
+     * @param bool  $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
+     *
+     * @return PromiseInterface
+     */
+    public static function all($promises, $recursive = false)
+    {
+        $results = [];
+        $promise = Each::of(
+            $promises,
+            function ($value, $idx) use (&$results) {
+                $results[$idx] = $value;
+            },
+            function ($reason, $idx, Promise $aggregate) {
+                $aggregate->reject($reason);
+            }
+        )->then(function () use (&$results) {
+            ksort($results);
+            return $results;
+        });
+
+        if (true === $recursive) {
+            $promise = $promise->then(function ($results) use ($recursive, &$promises) {
+                foreach ($promises as $promise) {
+                    if (Is::pending($promise)) {
+                        return self::all($promises, $recursive);
+                    }
+                }
+                return $results;
+            });
+        }
+
+        return $promise;
+    }
+
+    /**
+     * Initiate a competitive race between multiple promises or values (values
+     * will become immediately fulfilled promises).
+     *
+     * When count amount of promises have been fulfilled, the returned promise
+     * is fulfilled with an array that contains the fulfillment values of the
+     * winners in order of resolution.
+     *
+     * This promise is rejected with a {@see AggregateException} if the number
+     * of fulfilled promises is less than the desired $count.
+     *
+     * @param int   $count    Total number of promises.
+     * @param mixed $promises Promises or values.
+     *
+     * @return PromiseInterface
+     */
+    public static function some($count, $promises)
+    {
+        $results = [];
+        $rejections = [];
+
+        return Each::of(
+            $promises,
+            function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
+                if (Is::settled($p)) {
+                    return;
+                }
+                $results[$idx] = $value;
+                if (count($results) >= $count) {
+                    $p->resolve(null);
+                }
+            },
+            function ($reason) use (&$rejections) {
+                $rejections[] = $reason;
+            }
+        )->then(
+            function () use (&$results, &$rejections, $count) {
+                if (count($results) !== $count) {
+                    throw new AggregateException(
+                        'Not enough promises to fulfill count',
+                        $rejections
+                    );
+                }
+                ksort($results);
+                return array_values($results);
+            }
+        );
+    }
+
+    /**
+     * Like some(), with 1 as count. However, if the promise fulfills, the
+     * fulfillment value is not an array of 1 but the value directly.
+     *
+     * @param mixed $promises Promises or values.
+     *
+     * @return PromiseInterface
+     */
+    public static function any($promises)
+    {
+        return self::some(1, $promises)->then(function ($values) {
+            return $values[0];
+        });
+    }
+
+    /**
+     * Returns a promise that is fulfilled when all of the provided promises have
+     * been fulfilled or rejected.
+     *
+     * The returned promise is fulfilled with an array of inspection state arrays.
+     *
+     * @see inspect for the inspection state array format.
+     *
+     * @param mixed $promises Promises or values.
+     *
+     * @return PromiseInterface
+     */
+    public static function settle($promises)
+    {
+        $results = [];
+
+        return Each::of(
+            $promises,
+            function ($value, $idx) use (&$results) {
+                $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
+            },
+            function ($reason, $idx) use (&$results) {
+                $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
+            }
+        )->then(function () use (&$results) {
+            ksort($results);
+            return $results;
+        });
+    }
+}
diff --git a/vendor/guzzlehttp/promises/src/functions.php b/vendor/guzzlehttp/promises/src/functions.php
new file mode 100644
index 0000000..c03d39d
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/functions.php
@@ -0,0 +1,363 @@
+
+ * while ($eventLoop->isRunning()) {
+ *     GuzzleHttp\Promise\queue()->run();
+ * }
+ * 
+ *
+ * @param TaskQueueInterface $assign Optionally specify a new queue instance.
+ *
+ * @return TaskQueueInterface
+ *
+ * @deprecated queue will be removed in guzzlehttp/promises:2.0. Use Utils::queue instead.
+ */
+function queue(TaskQueueInterface $assign = null)
+{
+    return Utils::queue($assign);
+}
+
+/**
+ * Adds a function to run in the task queue when it is next `run()` and returns
+ * a promise that is fulfilled or rejected with the result.
+ *
+ * @param callable $task Task function to run.
+ *
+ * @return PromiseInterface
+ *
+ * @deprecated task will be removed in guzzlehttp/promises:2.0. Use Utils::task instead.
+ */
+function task(callable $task)
+{
+    return Utils::task($task);
+}
+
+/**
+ * Creates a promise for a value if the value is not a promise.
+ *
+ * @param mixed $value Promise or value.
+ *
+ * @return PromiseInterface
+ *
+ * @deprecated promise_for will be removed in guzzlehttp/promises:2.0. Use Create::promiseFor instead.
+ */
+function promise_for($value)
+{
+    return Create::promiseFor($value);
+}
+
+/**
+ * Creates a rejected promise for a reason if the reason is not a promise. If
+ * the provided reason is a promise, then it is returned as-is.
+ *
+ * @param mixed $reason Promise or reason.
+ *
+ * @return PromiseInterface
+ *
+ * @deprecated rejection_for will be removed in guzzlehttp/promises:2.0. Use Create::rejectionFor instead.
+ */
+function rejection_for($reason)
+{
+    return Create::rejectionFor($reason);
+}
+
+/**
+ * Create an exception for a rejected promise value.
+ *
+ * @param mixed $reason
+ *
+ * @return \Exception|\Throwable
+ *
+ * @deprecated exception_for will be removed in guzzlehttp/promises:2.0. Use Create::exceptionFor instead.
+ */
+function exception_for($reason)
+{
+    return Create::exceptionFor($reason);
+}
+
+/**
+ * Returns an iterator for the given value.
+ *
+ * @param mixed $value
+ *
+ * @return \Iterator
+ *
+ * @deprecated iter_for will be removed in guzzlehttp/promises:2.0. Use Create::iterFor instead.
+ */
+function iter_for($value)
+{
+    return Create::iterFor($value);
+}
+
+/**
+ * Synchronously waits on a promise to resolve and returns an inspection state
+ * array.
+ *
+ * Returns a state associative array containing a "state" key mapping to a
+ * valid promise state. If the state of the promise is "fulfilled", the array
+ * will contain a "value" key mapping to the fulfilled value of the promise. If
+ * the promise is rejected, the array will contain a "reason" key mapping to
+ * the rejection reason of the promise.
+ *
+ * @param PromiseInterface $promise Promise or value.
+ *
+ * @return array
+ *
+ * @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspect instead.
+ */
+function inspect(PromiseInterface $promise)
+{
+    return Utils::inspect($promise);
+}
+
+/**
+ * Waits on all of the provided promises, but does not unwrap rejected promises
+ * as thrown exception.
+ *
+ * Returns an array of inspection state arrays.
+ *
+ * @see inspect for the inspection state array format.
+ *
+ * @param PromiseInterface[] $promises Traversable of promises to wait upon.
+ *
+ * @return array
+ *
+ * @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspectAll instead.
+ */
+function inspect_all($promises)
+{
+    return Utils::inspectAll($promises);
+}
+
+/**
+ * Waits on all of the provided promises and returns the fulfilled values.
+ *
+ * Returns an array that contains the value of each promise (in the same order
+ * the promises were provided). An exception is thrown if any of the promises
+ * are rejected.
+ *
+ * @param iterable $promises Iterable of PromiseInterface objects to wait on.
+ *
+ * @return array
+ *
+ * @throws \Exception on error
+ * @throws \Throwable on error in PHP >=7
+ *
+ * @deprecated unwrap will be removed in guzzlehttp/promises:2.0. Use Utils::unwrap instead.
+ */
+function unwrap($promises)
+{
+    return Utils::unwrap($promises);
+}
+
+/**
+ * Given an array of promises, return a promise that is fulfilled when all the
+ * items in the array are fulfilled.
+ *
+ * The promise's fulfillment value is an array with fulfillment values at
+ * respective positions to the original array. If any promise in the array
+ * rejects, the returned promise is rejected with the rejection reason.
+ *
+ * @param mixed $promises  Promises or values.
+ * @param bool  $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
+ *
+ * @return PromiseInterface
+ *
+ * @deprecated all will be removed in guzzlehttp/promises:2.0. Use Utils::all instead.
+ */
+function all($promises, $recursive = false)
+{
+    return Utils::all($promises, $recursive);
+}
+
+/**
+ * Initiate a competitive race between multiple promises or values (values will
+ * become immediately fulfilled promises).
+ *
+ * When count amount of promises have been fulfilled, the returned promise is
+ * fulfilled with an array that contains the fulfillment values of the winners
+ * in order of resolution.
+ *
+ * This promise is rejected with a {@see AggregateException} if the number of
+ * fulfilled promises is less than the desired $count.
+ *
+ * @param int   $count    Total number of promises.
+ * @param mixed $promises Promises or values.
+ *
+ * @return PromiseInterface
+ *
+ * @deprecated some will be removed in guzzlehttp/promises:2.0. Use Utils::some instead.
+ */
+function some($count, $promises)
+{
+    return Utils::some($count, $promises);
+}
+
+/**
+ * Like some(), with 1 as count. However, if the promise fulfills, the
+ * fulfillment value is not an array of 1 but the value directly.
+ *
+ * @param mixed $promises Promises or values.
+ *
+ * @return PromiseInterface
+ *
+ * @deprecated any will be removed in guzzlehttp/promises:2.0. Use Utils::any instead.
+ */
+function any($promises)
+{
+    return Utils::any($promises);
+}
+
+/**
+ * Returns a promise that is fulfilled when all of the provided promises have
+ * been fulfilled or rejected.
+ *
+ * The returned promise is fulfilled with an array of inspection state arrays.
+ *
+ * @see inspect for the inspection state array format.
+ *
+ * @param mixed $promises Promises or values.
+ *
+ * @return PromiseInterface
+ *
+ * @deprecated settle will be removed in guzzlehttp/promises:2.0. Use Utils::settle instead.
+ */
+function settle($promises)
+{
+    return Utils::settle($promises);
+}
+
+/**
+ * Given an iterator that yields promises or values, returns a promise that is
+ * fulfilled with a null value when the iterator has been consumed or the
+ * aggregate promise has been fulfilled or rejected.
+ *
+ * $onFulfilled is a function that accepts the fulfilled value, iterator index,
+ * and the aggregate promise. The callback can invoke any necessary side
+ * effects and choose to resolve or reject the aggregate if needed.
+ *
+ * $onRejected is a function that accepts the rejection reason, iterator index,
+ * and the aggregate promise. The callback can invoke any necessary side
+ * effects and choose to resolve or reject the aggregate if needed.
+ *
+ * @param mixed    $iterable    Iterator or array to iterate over.
+ * @param callable $onFulfilled
+ * @param callable $onRejected
+ *
+ * @return PromiseInterface
+ *
+ * @deprecated each will be removed in guzzlehttp/promises:2.0. Use Each::of instead.
+ */
+function each(
+    $iterable,
+    callable $onFulfilled = null,
+    callable $onRejected = null
+) {
+    return Each::of($iterable, $onFulfilled, $onRejected);
+}
+
+/**
+ * Like each, but only allows a certain number of outstanding promises at any
+ * given time.
+ *
+ * $concurrency may be an integer or a function that accepts the number of
+ * pending promises and returns a numeric concurrency limit value to allow for
+ * dynamic a concurrency size.
+ *
+ * @param mixed        $iterable
+ * @param int|callable $concurrency
+ * @param callable     $onFulfilled
+ * @param callable     $onRejected
+ *
+ * @return PromiseInterface
+ *
+ * @deprecated each_limit will be removed in guzzlehttp/promises:2.0. Use Each::ofLimit instead.
+ */
+function each_limit(
+    $iterable,
+    $concurrency,
+    callable $onFulfilled = null,
+    callable $onRejected = null
+) {
+    return Each::ofLimit($iterable, $concurrency, $onFulfilled, $onRejected);
+}
+
+/**
+ * Like each_limit, but ensures that no promise in the given $iterable argument
+ * is rejected. If any promise is rejected, then the aggregate promise is
+ * rejected with the encountered rejection.
+ *
+ * @param mixed        $iterable
+ * @param int|callable $concurrency
+ * @param callable     $onFulfilled
+ *
+ * @return PromiseInterface
+ *
+ * @deprecated each_limit_all will be removed in guzzlehttp/promises:2.0. Use Each::ofLimitAll instead.
+ */
+function each_limit_all(
+    $iterable,
+    $concurrency,
+    callable $onFulfilled = null
+) {
+    return Each::ofLimitAll($iterable, $concurrency, $onFulfilled);
+}
+
+/**
+ * Returns true if a promise is fulfilled.
+ *
+ * @return bool
+ *
+ * @deprecated is_fulfilled will be removed in guzzlehttp/promises:2.0. Use Is::fulfilled instead.
+ */
+function is_fulfilled(PromiseInterface $promise)
+{
+    return Is::fulfilled($promise);
+}
+
+/**
+ * Returns true if a promise is rejected.
+ *
+ * @return bool
+ *
+ * @deprecated is_rejected will be removed in guzzlehttp/promises:2.0. Use Is::rejected instead.
+ */
+function is_rejected(PromiseInterface $promise)
+{
+    return Is::rejected($promise);
+}
+
+/**
+ * Returns true if a promise is fulfilled or rejected.
+ *
+ * @return bool
+ *
+ * @deprecated is_settled will be removed in guzzlehttp/promises:2.0. Use Is::settled instead.
+ */
+function is_settled(PromiseInterface $promise)
+{
+    return Is::settled($promise);
+}
+
+/**
+ * Create a new coroutine.
+ *
+ * @see Coroutine
+ *
+ * @return PromiseInterface
+ *
+ * @deprecated coroutine will be removed in guzzlehttp/promises:2.0. Use Coroutine::of instead.
+ */
+function coroutine(callable $generatorFn)
+{
+    return Coroutine::of($generatorFn);
+}
diff --git a/vendor/guzzlehttp/promises/src/functions_include.php b/vendor/guzzlehttp/promises/src/functions_include.php
new file mode 100644
index 0000000..34cd171
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/functions_include.php
@@ -0,0 +1,6 @@
+withPath('foo')->withHost('example.com')` will throw an exception
+    because the path of a URI with an authority must start with a slash "/" or be empty
+  - `(new Uri())->withScheme('http')` will return `'http://localhost'`
+
+### Deprecated
+
+- `Uri::resolve` in favor of `UriResolver::resolve`
+- `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments`
+
+### Fixed
+
+- `Stream::read` when length parameter <= 0.
+- `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory.
+- `ServerRequest::getUriFromGlobals` when `Host` header contains port.
+- Compatibility of URIs with `file` scheme and empty host.
+
+
+## [1.3.1] - 2016-06-25
+
+### Fixed
+
+- `Uri::__toString` for network path references, e.g. `//example.org`.
+- Missing lowercase normalization for host.
+- Handling of URI components in case they are `'0'` in a lot of places,
+  e.g. as a user info password.
+- `Uri::withAddedHeader` to correctly merge headers with different case.
+- Trimming of header values in `Uri::withAddedHeader`. Header values may
+  be surrounded by whitespace which should be ignored according to RFC 7230
+  Section 3.2.4. This does not apply to header names.
+- `Uri::withAddedHeader` with an array of header values.
+- `Uri::resolve` when base path has no slash and handling of fragment.
+- Handling of encoding in `Uri::with(out)QueryValue` so one can pass the
+  key/value both in encoded as well as decoded form to those methods. This is
+  consistent with withPath, withQuery etc.
+- `ServerRequest::withoutAttribute` when attribute value is null.
+
+
+## [1.3.0] - 2016-04-13
+
+### Added
+
+- Remaining interfaces needed for full PSR7 compatibility
+  (ServerRequestInterface, UploadedFileInterface, etc.).
+- Support for stream_for from scalars.
+
+### Changed
+
+- Can now extend Uri.
+
+### Fixed
+- A bug in validating request methods by making it more permissive.
+
+
+## [1.2.3] - 2016-02-18
+
+### Fixed
+
+- Support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote
+  streams, which can sometimes return fewer bytes than requested with `fread`.
+- Handling of gzipped responses with FNAME headers.
+
+
+## [1.2.2] - 2016-01-22
+
+### Added
+
+- Support for URIs without any authority.
+- Support for HTTP 451 'Unavailable For Legal Reasons.'
+- Support for using '0' as a filename.
+- Support for including non-standard ports in Host headers.
+
+
+## [1.2.1] - 2015-11-02
+
+### Changes
+
+- Now supporting negative offsets when seeking to SEEK_END.
+
+
+## [1.2.0] - 2015-08-15
+
+### Changed
+
+- Body as `"0"` is now properly added to a response.
+- Now allowing forward seeking in CachingStream.
+- Now properly parsing HTTP requests that contain proxy targets in
+  `parse_request`.
+- functions.php is now conditionally required.
+- user-info is no longer dropped when resolving URIs.
+
+
+## [1.1.0] - 2015-06-24
+
+### Changed
+
+- URIs can now be relative.
+- `multipart/form-data` headers are now overridden case-insensitively.
+- URI paths no longer encode the following characters because they are allowed
+  in URIs: "(", ")", "*", "!", "'"
+- A port is no longer added to a URI when the scheme is missing and no port is
+  present.
+
+
+## 1.0.0 - 2015-05-19
+
+Initial release.
+
+Currently unsupported:
+
+- `Psr\Http\Message\ServerRequestInterface`
+- `Psr\Http\Message\UploadedFileInterface`
+
+
+
+[1.6.0]: https://github.com/guzzle/psr7/compare/1.5.2...1.6.0
+[1.5.2]: https://github.com/guzzle/psr7/compare/1.5.1...1.5.2
+[1.5.1]: https://github.com/guzzle/psr7/compare/1.5.0...1.5.1
+[1.5.0]: https://github.com/guzzle/psr7/compare/1.4.2...1.5.0
+[1.4.2]: https://github.com/guzzle/psr7/compare/1.4.1...1.4.2
+[1.4.1]: https://github.com/guzzle/psr7/compare/1.4.0...1.4.1
+[1.4.0]: https://github.com/guzzle/psr7/compare/1.3.1...1.4.0
+[1.3.1]: https://github.com/guzzle/psr7/compare/1.3.0...1.3.1
+[1.3.0]: https://github.com/guzzle/psr7/compare/1.2.3...1.3.0
+[1.2.3]: https://github.com/guzzle/psr7/compare/1.2.2...1.2.3
+[1.2.2]: https://github.com/guzzle/psr7/compare/1.2.1...1.2.2
+[1.2.1]: https://github.com/guzzle/psr7/compare/1.2.0...1.2.1
+[1.2.0]: https://github.com/guzzle/psr7/compare/1.1.0...1.2.0
+[1.1.0]: https://github.com/guzzle/psr7/compare/1.0.0...1.1.0
diff --git a/vendor/guzzlehttp/psr7/LICENSE b/vendor/guzzlehttp/psr7/LICENSE
new file mode 100644
index 0000000..51c7ec8
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/LICENSE
@@ -0,0 +1,26 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Michael Dowling 
+Copyright (c) 2015 Márk Sági-Kazár 
+Copyright (c) 2015 Graham Campbell 
+Copyright (c) 2016 Tobias Schultze 
+Copyright (c) 2016 George Mponos 
+Copyright (c) 2018 Tobias Nyholm 
+
+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/guzzlehttp/psr7/README.md b/vendor/guzzlehttp/psr7/README.md
new file mode 100644
index 0000000..2e9bb0b
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/README.md
@@ -0,0 +1,887 @@
+# PSR-7 Message Implementation
+
+This repository contains a full [PSR-7](https://www.php-fig.org/psr/psr-7/)
+message implementation, several stream decorators, and some helpful
+functionality like query string parsing.
+
+![CI](https://github.com/guzzle/psr7/workflows/CI/badge.svg)
+![Static analysis](https://github.com/guzzle/psr7/workflows/Static%20analysis/badge.svg)
+
+
+## Features
+
+This package comes with a number of stream implementations and stream
+decorators.
+
+
+## Installation
+
+```shell
+composer require guzzlehttp/psr7
+```
+
+## Version Guidance
+
+| Version | Status              | PHP Version  |
+|---------|---------------------|--------------|
+| 1.x     | EOL (2024-06-30)    | >=5.4,<8.2   |
+| 2.x     | Latest              | >=7.2.5,<8.5 |
+
+
+## AppendStream
+
+`GuzzleHttp\Psr7\AppendStream`
+
+Reads from multiple streams, one after the other.
+
+```php
+use GuzzleHttp\Psr7;
+
+$a = Psr7\Utils::streamFor('abc, ');
+$b = Psr7\Utils::streamFor('123.');
+$composed = new Psr7\AppendStream([$a, $b]);
+
+$composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));
+
+echo $composed; // abc, 123. Above all listen to me.
+```
+
+
+## BufferStream
+
+`GuzzleHttp\Psr7\BufferStream`
+
+Provides a buffer stream that can be written to fill a buffer, and read
+from to remove bytes from the buffer.
+
+This stream returns a "hwm" metadata value that tells upstream consumers
+what the configured high water mark of the stream is, or the maximum
+preferred size of the buffer.
+
+```php
+use GuzzleHttp\Psr7;
+
+// When more than 1024 bytes are in the buffer, it will begin returning
+// false to writes. This is an indication that writers should slow down.
+$buffer = new Psr7\BufferStream(1024);
+```
+
+
+## CachingStream
+
+The CachingStream is used to allow seeking over previously read bytes on
+non-seekable streams. This can be useful when transferring a non-seekable
+entity body fails due to needing to rewind the stream (for example, resulting
+from a redirect). Data that is read from the remote stream will be buffered in
+a PHP temp stream so that previously read bytes are cached first in memory,
+then on disk.
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r'));
+$stream = new Psr7\CachingStream($original);
+
+$stream->read(1024);
+echo $stream->tell();
+// 1024
+
+$stream->seek(0);
+echo $stream->tell();
+// 0
+```
+
+
+## DroppingStream
+
+`GuzzleHttp\Psr7\DroppingStream`
+
+Stream decorator that begins dropping data once the size of the underlying
+stream becomes too full.
+
+```php
+use GuzzleHttp\Psr7;
+
+// Create an empty stream
+$stream = Psr7\Utils::streamFor();
+
+// Start dropping data when the stream has more than 10 bytes
+$dropping = new Psr7\DroppingStream($stream, 10);
+
+$dropping->write('01234567890123456789');
+echo $stream; // 0123456789
+```
+
+
+## FnStream
+
+`GuzzleHttp\Psr7\FnStream`
+
+Compose stream implementations based on a hash of functions.
+
+Allows for easy testing and extension of a provided stream without needing
+to create a concrete class for a simple extension point.
+
+```php
+
+use GuzzleHttp\Psr7;
+
+$stream = Psr7\Utils::streamFor('hi');
+$fnStream = Psr7\FnStream::decorate($stream, [
+    'rewind' => function () use ($stream) {
+        echo 'About to rewind - ';
+        $stream->rewind();
+        echo 'rewound!';
+    }
+]);
+
+$fnStream->rewind();
+// Outputs: About to rewind - rewound!
+```
+
+
+## InflateStream
+
+`GuzzleHttp\Psr7\InflateStream`
+
+Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content.
+
+This stream decorator converts the provided stream to a PHP stream resource,
+then appends the zlib.inflate filter. The stream is then converted back
+to a Guzzle stream resource to be used as a Guzzle stream.
+
+
+## LazyOpenStream
+
+`GuzzleHttp\Psr7\LazyOpenStream`
+
+Lazily reads or writes to a file that is opened only after an IO operation
+take place on the stream.
+
+```php
+use GuzzleHttp\Psr7;
+
+$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
+// The file has not yet been opened...
+
+echo $stream->read(10);
+// The file is opened and read from only when needed.
+```
+
+
+## LimitStream
+
+`GuzzleHttp\Psr7\LimitStream`
+
+LimitStream can be used to read a subset or slice of an existing stream object.
+This can be useful for breaking a large file into smaller pieces to be sent in
+chunks (e.g. Amazon S3's multipart upload API).
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+'));
+echo $original->getSize();
+// >>> 1048576
+
+// Limit the size of the body to 1024 bytes and start reading from byte 2048
+$stream = new Psr7\LimitStream($original, 1024, 2048);
+echo $stream->getSize();
+// >>> 1024
+echo $stream->tell();
+// >>> 0
+```
+
+
+## MultipartStream
+
+`GuzzleHttp\Psr7\MultipartStream`
+
+Stream that when read returns bytes for a streaming multipart or
+multipart/form-data stream.
+
+
+## NoSeekStream
+
+`GuzzleHttp\Psr7\NoSeekStream`
+
+NoSeekStream wraps a stream and does not allow seeking.
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\Utils::streamFor('foo');
+$noSeek = new Psr7\NoSeekStream($original);
+
+echo $noSeek->read(3);
+// foo
+var_export($noSeek->isSeekable());
+// false
+$noSeek->seek(0);
+var_export($noSeek->read(3));
+// NULL
+```
+
+
+## PumpStream
+
+`GuzzleHttp\Psr7\PumpStream`
+
+Provides a read only stream that pumps data from a PHP callable.
+
+When invoking the provided callable, the PumpStream will pass the amount of
+data requested to read to the callable. The callable can choose to ignore
+this value and return fewer or more bytes than requested. Any extra data
+returned by the provided callable is buffered internally until drained using
+the read() function of the PumpStream. The provided callable MUST return
+false when there is no more data to read.
+
+
+## Implementing stream decorators
+
+Creating a stream decorator is very easy thanks to the
+`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
+implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
+stream. Just `use` the `StreamDecoratorTrait` and implement your custom
+methods.
+
+For example, let's say we wanted to call a specific function each time the last
+byte is read from a stream. This could be implemented by overriding the
+`read()` method.
+
+```php
+use Psr\Http\Message\StreamInterface;
+use GuzzleHttp\Psr7\StreamDecoratorTrait;
+
+class EofCallbackStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    private $callback;
+
+    private $stream;
+
+    public function __construct(StreamInterface $stream, callable $cb)
+    {
+        $this->stream = $stream;
+        $this->callback = $cb;
+    }
+
+    public function read($length)
+    {
+        $result = $this->stream->read($length);
+
+        // Invoke the callback when EOF is hit.
+        if ($this->eof()) {
+            ($this->callback)();
+        }
+
+        return $result;
+    }
+}
+```
+
+This decorator could be added to any existing stream and used like so:
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\Utils::streamFor('foo');
+
+$eofStream = new EofCallbackStream($original, function () {
+    echo 'EOF!';
+});
+
+$eofStream->read(2);
+$eofStream->read(1);
+// echoes "EOF!"
+$eofStream->seek(0);
+$eofStream->read(3);
+// echoes "EOF!"
+```
+
+
+## PHP StreamWrapper
+
+You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
+PSR-7 stream as a PHP stream resource.
+
+Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
+stream from a PSR-7 stream.
+
+```php
+use GuzzleHttp\Psr7\StreamWrapper;
+
+$stream = GuzzleHttp\Psr7\Utils::streamFor('hello!');
+$resource = StreamWrapper::getResource($stream);
+echo fread($resource, 6); // outputs hello!
+```
+
+
+# Static API
+
+There are various static methods available under the `GuzzleHttp\Psr7` namespace.
+
+
+## `GuzzleHttp\Psr7\Message::toString`
+
+`public static function toString(MessageInterface $message): string`
+
+Returns the string representation of an HTTP message.
+
+```php
+$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
+echo GuzzleHttp\Psr7\Message::toString($request);
+```
+
+
+## `GuzzleHttp\Psr7\Message::bodySummary`
+
+`public static function bodySummary(MessageInterface $message, int $truncateAt = 120): string|null`
+
+Get a short summary of the message body.
+
+Will return `null` if the response is not printable.
+
+
+## `GuzzleHttp\Psr7\Message::rewindBody`
+
+`public static function rewindBody(MessageInterface $message): void`
+
+Attempts to rewind a message body and throws an exception on failure.
+
+The body of the message will only be rewound if a call to `tell()`
+returns a value other than `0`.
+
+
+## `GuzzleHttp\Psr7\Message::parseMessage`
+
+`public static function parseMessage(string $message): array`
+
+Parses an HTTP message into an associative array.
+
+The array contains the "start-line" key containing the start line of
+the message, "headers" key containing an associative array of header
+array values, and a "body" key containing the body of the message.
+
+
+## `GuzzleHttp\Psr7\Message::parseRequestUri`
+
+`public static function parseRequestUri(string $path, array $headers): string`
+
+Constructs a URI for an HTTP request message.
+
+
+## `GuzzleHttp\Psr7\Message::parseRequest`
+
+`public static function parseRequest(string $message): Request`
+
+Parses a request message string into a request object.
+
+
+## `GuzzleHttp\Psr7\Message::parseResponse`
+
+`public static function parseResponse(string $message): Response`
+
+Parses a response message string into a response object.
+
+
+## `GuzzleHttp\Psr7\Header::parse`
+
+`public static function parse(string|array $header): array`
+
+Parse an array of header values containing ";" separated data into an
+array of associative arrays representing the header key value pair data
+of the header. When a parameter does not contain a value, but just
+contains a key, this function will inject a key with a '' string value.
+
+
+## `GuzzleHttp\Psr7\Header::splitList`
+
+`public static function splitList(string|string[] $header): string[]`
+
+Splits a HTTP header defined to contain a comma-separated list into
+each individual value:
+
+```
+$knownEtags = Header::splitList($request->getHeader('if-none-match'));
+```
+
+Example headers include `accept`, `cache-control` and `if-none-match`.
+
+
+## `GuzzleHttp\Psr7\Header::normalize` (deprecated)
+
+`public static function normalize(string|array $header): array`
+
+`Header::normalize()` is deprecated in favor of [`Header::splitList()`](README.md#guzzlehttppsr7headersplitlist)
+which performs the same operation with a cleaned up API and improved
+documentation.
+
+Converts an array of header values that may contain comma separated
+headers into an array of headers with no comma separated values.
+
+
+## `GuzzleHttp\Psr7\Query::parse`
+
+`public static function parse(string $str, int|bool $urlEncoding = true): array`
+
+Parse a query string into an associative array.
+
+If multiple values are found for the same key, the value of that key
+value pair will become an array. This function does not parse nested
+PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
+will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
+
+
+## `GuzzleHttp\Psr7\Query::build`
+
+`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986, bool $treatBoolsAsInts = true): string`
+
+Build a query string from an array of key value pairs.
+
+This function can use the return value of `parse()` to build a query
+string. This function does not modify the provided keys when an array is
+encountered (like `http_build_query()` would).
+
+
+## `GuzzleHttp\Psr7\Utils::caselessRemove`
+
+`public static function caselessRemove(iterable $keys, $keys, array $data): array`
+
+Remove the items given by the keys, case insensitively from the data.
+
+
+## `GuzzleHttp\Psr7\Utils::copyToStream`
+
+`public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void`
+
+Copy the contents of a stream into another stream until the given number
+of bytes have been read.
+
+
+## `GuzzleHttp\Psr7\Utils::copyToString`
+
+`public static function copyToString(StreamInterface $stream, int $maxLen = -1): string`
+
+Copy the contents of a stream into a string until the given number of
+bytes have been read.
+
+
+## `GuzzleHttp\Psr7\Utils::hash`
+
+`public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string`
+
+Calculate a hash of a stream.
+
+This method reads the entire stream to calculate a rolling hash, based on
+PHP's `hash_init` functions.
+
+
+## `GuzzleHttp\Psr7\Utils::modifyRequest`
+
+`public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface`
+
+Clone and modify a request with the given changes.
+
+This method is useful for reducing the number of clones needed to mutate
+a message.
+
+- method: (string) Changes the HTTP method.
+- set_headers: (array) Sets the given headers.
+- remove_headers: (array) Remove the given headers.
+- body: (mixed) Sets the given body.
+- uri: (UriInterface) Set the URI.
+- query: (string) Set the query string value of the URI.
+- version: (string) Set the protocol version.
+
+
+## `GuzzleHttp\Psr7\Utils::readLine`
+
+`public static function readLine(StreamInterface $stream, ?int $maxLength = null): string`
+
+Read a line from the stream up to the maximum allowed buffer length.
+
+
+## `GuzzleHttp\Psr7\Utils::redactUserInfo`
+
+`public static function redactUserInfo(UriInterface $uri): UriInterface`
+
+Redact the password in the user info part of a URI.
+
+
+## `GuzzleHttp\Psr7\Utils::streamFor`
+
+`public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface`
+
+Create a new stream based on the input type.
+
+Options is an associative array that can contain the following keys:
+
+- metadata: Array of custom metadata.
+- size: Size of the stream.
+
+This method accepts the following `$resource` types:
+
+- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
+- `string`: Creates a stream object that uses the given string as the contents.
+- `resource`: Creates a stream object that wraps the given PHP stream resource.
+- `Iterator`: If the provided value implements `Iterator`, then a read-only
+  stream object will be created that wraps the given iterable. Each time the
+  stream is read from, data from the iterator will fill a buffer and will be
+  continuously called until the buffer is equal to the requested read size.
+  Subsequent read calls will first read from the buffer and then call `next`
+  on the underlying iterator until it is exhausted.
+- `object` with `__toString()`: If the object has the `__toString()` method,
+  the object will be cast to a string and then a stream will be returned that
+  uses the string value.
+- `NULL`: When `null` is passed, an empty stream object is returned.
+- `callable` When a callable is passed, a read-only stream object will be
+  created that invokes the given callable. The callable is invoked with the
+  number of suggested bytes to read. The callable can return any number of
+  bytes, but MUST return `false` when there is no more data to return. The
+  stream object that wraps the callable will invoke the callable until the
+  number of requested bytes are available. Any additional bytes will be
+  buffered and used in subsequent reads.
+
+```php
+$stream = GuzzleHttp\Psr7\Utils::streamFor('foo');
+$stream = GuzzleHttp\Psr7\Utils::streamFor(fopen('/path/to/file', 'r'));
+
+$generator = function ($bytes) {
+    for ($i = 0; $i < $bytes; $i++) {
+        yield ' ';
+    }
+}
+
+$stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100));
+```
+
+
+## `GuzzleHttp\Psr7\Utils::tryFopen`
+
+`public static function tryFopen(string $filename, string $mode): resource`
+
+Safely opens a PHP stream resource using a filename.
+
+When fopen fails, PHP normally raises a warning. This function adds an
+error handler that checks for errors and throws an exception instead.
+
+
+## `GuzzleHttp\Psr7\Utils::tryGetContents`
+
+`public static function tryGetContents(resource $stream): string`
+
+Safely gets the contents of a given stream.
+
+When stream_get_contents fails, PHP normally raises a warning. This
+function adds an error handler that checks for errors and throws an
+exception instead.
+
+
+## `GuzzleHttp\Psr7\Utils::uriFor`
+
+`public static function uriFor(string|UriInterface $uri): UriInterface`
+
+Returns a UriInterface for the given value.
+
+This function accepts a string or UriInterface and returns a
+UriInterface for the given value. If the value is already a
+UriInterface, it is returned as-is.
+
+
+## `GuzzleHttp\Psr7\MimeType::fromFilename`
+
+`public static function fromFilename(string $filename): string|null`
+
+Determines the mimetype of a file by looking at its extension.
+
+
+## `GuzzleHttp\Psr7\MimeType::fromExtension`
+
+`public static function fromExtension(string $extension): string|null`
+
+Maps a file extensions to a mimetype.
+
+
+## Upgrading from Function API
+
+The static API was first introduced in 1.7.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API was removed in 2.0.0. A migration table has been provided here for your convenience:
+
+| Original Function | Replacement Method |
+|----------------|----------------|
+| `str` | `Message::toString` |
+| `uri_for` | `Utils::uriFor` |
+| `stream_for` | `Utils::streamFor` |
+| `parse_header` | `Header::parse` |
+| `normalize_header` | `Header::normalize` |
+| `modify_request` | `Utils::modifyRequest` |
+| `rewind_body` | `Message::rewindBody` |
+| `try_fopen` | `Utils::tryFopen` |
+| `copy_to_string` | `Utils::copyToString` |
+| `copy_to_stream` | `Utils::copyToStream` |
+| `hash` | `Utils::hash` |
+| `readline` | `Utils::readLine` |
+| `parse_request` | `Message::parseRequest` |
+| `parse_response` | `Message::parseResponse` |
+| `parse_query` | `Query::parse` |
+| `build_query` | `Query::build` |
+| `mimetype_from_filename` | `MimeType::fromFilename` |
+| `mimetype_from_extension` | `MimeType::fromExtension` |
+| `_parse_message` | `Message::parseMessage` |
+| `_parse_request_uri` | `Message::parseRequestUri` |
+| `get_message_body_summary` | `Message::bodySummary` |
+| `_caseless_remove` | `Utils::caselessRemove` |
+
+
+# Additional URI Methods
+
+Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
+this library also provides additional functionality when working with URIs as static methods.
+
+## URI Types
+
+An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
+An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
+the base URI. Relative references can be divided into several forms according to
+[RFC 3986 Section 4.2](https://datatracker.ietf.org/doc/html/rfc3986#section-4.2):
+
+- network-path references, e.g. `//example.com/path`
+- absolute-path references, e.g. `/path`
+- relative-path references, e.g. `subpath`
+
+The following methods can be used to identify the type of the URI.
+
+### `GuzzleHttp\Psr7\Uri::isAbsolute`
+
+`public static function isAbsolute(UriInterface $uri): bool`
+
+Whether the URI is absolute, i.e. it has a scheme.
+
+### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`
+
+`public static function isNetworkPathReference(UriInterface $uri): bool`
+
+Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
+termed an network-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`
+
+`public static function isAbsolutePathReference(UriInterface $uri): bool`
+
+Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
+termed an absolute-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isRelativePathReference`
+
+`public static function isRelativePathReference(UriInterface $uri): bool`
+
+Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
+termed a relative-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
+
+`public static function isSameDocumentReference(UriInterface $uri, ?UriInterface $base = null): bool`
+
+Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
+fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
+(apart from its fragment) is considered a same-document reference.
+
+## URI Components
+
+Additional methods to work with URI components.
+
+### `GuzzleHttp\Psr7\Uri::isDefaultPort`
+
+`public static function isDefaultPort(UriInterface $uri): bool`
+
+Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
+or the standard port. This method can be used independently of the implementation.
+
+### `GuzzleHttp\Psr7\Uri::composeComponents`
+
+`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
+
+Composes a URI reference string from its various components according to
+[RFC 3986 Section 5.3](https://datatracker.ietf.org/doc/html/rfc3986#section-5.3). Usually this method does not need
+to be called manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
+
+### `GuzzleHttp\Psr7\Uri::fromParts`
+
+`public static function fromParts(array $parts): UriInterface`
+
+Creates a URI from a hash of [`parse_url`](https://www.php.net/manual/en/function.parse-url.php) components.
+
+
+### `GuzzleHttp\Psr7\Uri::withQueryValue`
+
+`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`
+
+Creates a new URI with a specific query string value. Any existing query string values that exactly match the
+provided key are removed and replaced with the given key value pair. A value of null will set the query string
+key without a value, e.g. "key" instead of "key=value".
+
+### `GuzzleHttp\Psr7\Uri::withQueryValues`
+
+`public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface`
+
+Creates a new URI with multiple query string values. It has the same behavior as `withQueryValue()` but for an
+associative array of key => value.
+
+### `GuzzleHttp\Psr7\Uri::withoutQueryValue`
+
+`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`
+
+Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
+provided key are removed.
+
+## Cross-Origin Detection
+
+`GuzzleHttp\Psr7\UriComparator` provides methods to determine if a modified URL should be considered cross-origin.
+
+### `GuzzleHttp\Psr7\UriComparator::isCrossOrigin`
+
+`public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool`
+
+Determines if a modified URL should be considered cross-origin with respect to an original URL.
+
+## Reference Resolution
+
+`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
+to [RFC 3986 Section 5](https://datatracker.ietf.org/doc/html/rfc3986#section-5). This is for example also what web
+browsers do when resolving a link in a website based on the current request URI.
+
+### `GuzzleHttp\Psr7\UriResolver::resolve`
+
+`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`
+
+Converts the relative URI into a new URI that is resolved against the base URI.
+
+### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`
+
+`public static function removeDotSegments(string $path): string`
+
+Removes dot segments from a path and returns the new path according to
+[RFC 3986 Section 5.2.4](https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4).
+
+### `GuzzleHttp\Psr7\UriResolver::relativize`
+
+`public static function relativize(UriInterface $base, UriInterface $target): UriInterface`
+
+Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():
+
+```php
+(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
+```
+
+One use-case is to use the current request URI as base URI and then generate relative links in your documents
+to reduce the document size or offer self-contained downloadable document archives.
+
+```php
+$base = new Uri('http://example.com/a/b/');
+echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c'));  // prints 'c'.
+echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y'));  // prints '../x/y'.
+echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
+echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // prints '//example.org/a/b/'.
+```
+
+## Normalization and Comparison
+
+`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
+[RFC 3986 Section 6](https://datatracker.ietf.org/doc/html/rfc3986#section-6).
+
+### `GuzzleHttp\Psr7\UriNormalizer::normalize`
+
+`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`
+
+Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
+This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
+of normalizations to apply. The following normalizations are available:
+
+- `UriNormalizer::PRESERVING_NORMALIZATIONS`
+
+    Default normalizations which only include the ones that preserve semantics.
+
+- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`
+
+    All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
+
+    Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`
+
+- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`
+
+    Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
+    ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
+    not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
+    characters by URI normalizers.
+
+    Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`
+
+- `UriNormalizer::CONVERT_EMPTY_PATH`
+
+    Converts the empty path to "/" for http and https URIs.
+
+    Example: `http://example.org` → `http://example.org/`
+
+- `UriNormalizer::REMOVE_DEFAULT_HOST`
+
+    Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
+    "localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
+    RFC 3986.
+
+    Example: `file://localhost/myfile` → `file:///myfile`
+
+- `UriNormalizer::REMOVE_DEFAULT_PORT`
+
+    Removes the default port of the given URI scheme from the URI.
+
+    Example: `http://example.org:80/` → `http://example.org/`
+
+- `UriNormalizer::REMOVE_DOT_SEGMENTS`
+
+    Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
+    change the semantics of the URI reference.
+
+    Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`
+
+- `UriNormalizer::REMOVE_DUPLICATE_SLASHES`
+
+    Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
+    and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
+    may change the semantics. Encoded slashes (%2F) are not removed.
+
+    Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`
+
+- `UriNormalizer::SORT_QUERY_PARAMETERS`
+
+    Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
+    significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
+    of the URI.
+
+    Example: `?lang=en&article=fred` → `?article=fred&lang=en`
+
+### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`
+
+`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`
+
+Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
+`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
+This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
+equivalence or difference of relative references does not mean anything.
+
+
+## Security
+
+If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/psr7/security/policy) for more information.
+
+
+## License
+
+Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
+
+
+## For Enterprise
+
+Available as part of the Tidelift Subscription
+
+The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-psr7?utm_source=packagist-guzzlehttp-psr7&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
diff --git a/vendor/guzzlehttp/psr7/composer.json b/vendor/guzzlehttp/psr7/composer.json
new file mode 100644
index 0000000..28d15f5
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/composer.json
@@ -0,0 +1,93 @@
+{
+    "name": "guzzlehttp/psr7",
+    "description": "PSR-7 message implementation that also provides common utility methods",
+    "keywords": [
+        "request",
+        "response",
+        "message",
+        "stream",
+        "http",
+        "uri",
+        "url",
+        "psr-7"
+    ],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Graham Campbell",
+            "email": "hello@gjcampbell.co.uk",
+            "homepage": "https://github.com/GrahamCampbell"
+        },
+        {
+            "name": "Michael Dowling",
+            "email": "mtdowling@gmail.com",
+            "homepage": "https://github.com/mtdowling"
+        },
+        {
+            "name": "George Mponos",
+            "email": "gmponos@gmail.com",
+            "homepage": "https://github.com/gmponos"
+        },
+        {
+            "name": "Tobias Nyholm",
+            "email": "tobias.nyholm@gmail.com",
+            "homepage": "https://github.com/Nyholm"
+        },
+        {
+            "name": "Márk Sági-Kazár",
+            "email": "mark.sagikazar@gmail.com",
+            "homepage": "https://github.com/sagikazarmark"
+        },
+        {
+            "name": "Tobias Schultze",
+            "email": "webmaster@tubo-world.de",
+            "homepage": "https://github.com/Tobion"
+        },
+        {
+            "name": "Márk Sági-Kazár",
+            "email": "mark.sagikazar@gmail.com",
+            "homepage": "https://sagikazarmark.hu"
+        }
+    ],
+    "require": {
+        "php": "^7.2.5 || ^8.0",
+        "psr/http-factory": "^1.0",
+        "psr/http-message": "^1.1 || ^2.0",
+        "ralouphie/getallheaders": "^3.0"
+    },
+    "provide": {
+        "psr/http-factory-implementation": "1.0",
+        "psr/http-message-implementation": "1.0"
+    },
+    "require-dev": {
+        "bamarni/composer-bin-plugin": "^1.8.2",
+        "http-interop/http-factory-tests": "0.9.0",
+        "phpunit/phpunit": "^8.5.39 || ^9.6.20"
+    },
+    "suggest": {
+        "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+    },
+    "autoload": {
+        "psr-4": {
+            "GuzzleHttp\\Psr7\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "GuzzleHttp\\Tests\\Psr7\\": "tests/"
+        }
+    },
+    "extra": {
+        "bamarni-bin": {
+            "bin-links": true,
+            "forward-command": false
+        }
+    },
+    "config": {
+        "allow-plugins": {
+            "bamarni/composer-bin-plugin": true
+        },
+        "preferred-install": "dist",
+        "sort-packages": true
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/AppendStream.php b/vendor/guzzlehttp/psr7/src/AppendStream.php
new file mode 100644
index 0000000..ee8f378
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/AppendStream.php
@@ -0,0 +1,248 @@
+addStream($stream);
+        }
+    }
+
+    public function __toString(): string
+    {
+        try {
+            $this->rewind();
+
+            return $this->getContents();
+        } catch (\Throwable $e) {
+            if (\PHP_VERSION_ID >= 70400) {
+                throw $e;
+            }
+            trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
+            return '';
+        }
+    }
+
+    /**
+     * Add a stream to the AppendStream
+     *
+     * @param StreamInterface $stream Stream to append. Must be readable.
+     *
+     * @throws \InvalidArgumentException if the stream is not readable
+     */
+    public function addStream(StreamInterface $stream): void
+    {
+        if (!$stream->isReadable()) {
+            throw new \InvalidArgumentException('Each stream must be readable');
+        }
+
+        // The stream is only seekable if all streams are seekable
+        if (!$stream->isSeekable()) {
+            $this->seekable = false;
+        }
+
+        $this->streams[] = $stream;
+    }
+
+    public function getContents(): string
+    {
+        return Utils::copyToString($this);
+    }
+
+    /**
+     * Closes each attached stream.
+     */
+    public function close(): void
+    {
+        $this->pos = $this->current = 0;
+        $this->seekable = true;
+
+        foreach ($this->streams as $stream) {
+            $stream->close();
+        }
+
+        $this->streams = [];
+    }
+
+    /**
+     * Detaches each attached stream.
+     *
+     * Returns null as it's not clear which underlying stream resource to return.
+     */
+    public function detach()
+    {
+        $this->pos = $this->current = 0;
+        $this->seekable = true;
+
+        foreach ($this->streams as $stream) {
+            $stream->detach();
+        }
+
+        $this->streams = [];
+
+        return null;
+    }
+
+    public function tell(): int
+    {
+        return $this->pos;
+    }
+
+    /**
+     * Tries to calculate the size by adding the size of each stream.
+     *
+     * If any of the streams do not return a valid number, then the size of the
+     * append stream cannot be determined and null is returned.
+     */
+    public function getSize(): ?int
+    {
+        $size = 0;
+
+        foreach ($this->streams as $stream) {
+            $s = $stream->getSize();
+            if ($s === null) {
+                return null;
+            }
+            $size += $s;
+        }
+
+        return $size;
+    }
+
+    public function eof(): bool
+    {
+        return !$this->streams
+            || ($this->current >= count($this->streams) - 1
+             && $this->streams[$this->current]->eof());
+    }
+
+    public function rewind(): void
+    {
+        $this->seek(0);
+    }
+
+    /**
+     * Attempts to seek to the given position. Only supports SEEK_SET.
+     */
+    public function seek($offset, $whence = SEEK_SET): void
+    {
+        if (!$this->seekable) {
+            throw new \RuntimeException('This AppendStream is not seekable');
+        } elseif ($whence !== SEEK_SET) {
+            throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
+        }
+
+        $this->pos = $this->current = 0;
+
+        // Rewind each stream
+        foreach ($this->streams as $i => $stream) {
+            try {
+                $stream->rewind();
+            } catch (\Exception $e) {
+                throw new \RuntimeException('Unable to seek stream '
+                    .$i.' of the AppendStream', 0, $e);
+            }
+        }
+
+        // Seek to the actual position by reading from each stream
+        while ($this->pos < $offset && !$this->eof()) {
+            $result = $this->read(min(8096, $offset - $this->pos));
+            if ($result === '') {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Reads from all of the appended streams until the length is met or EOF.
+     */
+    public function read($length): string
+    {
+        $buffer = '';
+        $total = count($this->streams) - 1;
+        $remaining = $length;
+        $progressToNext = false;
+
+        while ($remaining > 0) {
+            // Progress to the next stream if needed.
+            if ($progressToNext || $this->streams[$this->current]->eof()) {
+                $progressToNext = false;
+                if ($this->current === $total) {
+                    break;
+                }
+                ++$this->current;
+            }
+
+            $result = $this->streams[$this->current]->read($remaining);
+
+            if ($result === '') {
+                $progressToNext = true;
+                continue;
+            }
+
+            $buffer .= $result;
+            $remaining = $length - strlen($buffer);
+        }
+
+        $this->pos += strlen($buffer);
+
+        return $buffer;
+    }
+
+    public function isReadable(): bool
+    {
+        return true;
+    }
+
+    public function isWritable(): bool
+    {
+        return false;
+    }
+
+    public function isSeekable(): bool
+    {
+        return $this->seekable;
+    }
+
+    public function write($string): int
+    {
+        throw new \RuntimeException('Cannot write to an AppendStream');
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getMetadata($key = null)
+    {
+        return $key ? null : [];
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/BufferStream.php b/vendor/guzzlehttp/psr7/src/BufferStream.php
new file mode 100644
index 0000000..2b0eb77
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/BufferStream.php
@@ -0,0 +1,147 @@
+hwm = $hwm;
+    }
+
+    public function __toString(): string
+    {
+        return $this->getContents();
+    }
+
+    public function getContents(): string
+    {
+        $buffer = $this->buffer;
+        $this->buffer = '';
+
+        return $buffer;
+    }
+
+    public function close(): void
+    {
+        $this->buffer = '';
+    }
+
+    public function detach()
+    {
+        $this->close();
+
+        return null;
+    }
+
+    public function getSize(): ?int
+    {
+        return strlen($this->buffer);
+    }
+
+    public function isReadable(): bool
+    {
+        return true;
+    }
+
+    public function isWritable(): bool
+    {
+        return true;
+    }
+
+    public function isSeekable(): bool
+    {
+        return false;
+    }
+
+    public function rewind(): void
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET): void
+    {
+        throw new \RuntimeException('Cannot seek a BufferStream');
+    }
+
+    public function eof(): bool
+    {
+        return strlen($this->buffer) === 0;
+    }
+
+    public function tell(): int
+    {
+        throw new \RuntimeException('Cannot determine the position of a BufferStream');
+    }
+
+    /**
+     * Reads data from the buffer.
+     */
+    public function read($length): string
+    {
+        $currentLength = strlen($this->buffer);
+
+        if ($length >= $currentLength) {
+            // No need to slice the buffer because we don't have enough data.
+            $result = $this->buffer;
+            $this->buffer = '';
+        } else {
+            // Slice up the result to provide a subset of the buffer.
+            $result = substr($this->buffer, 0, $length);
+            $this->buffer = substr($this->buffer, $length);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Writes data to the buffer.
+     */
+    public function write($string): int
+    {
+        $this->buffer .= $string;
+
+        if (strlen($this->buffer) >= $this->hwm) {
+            return 0;
+        }
+
+        return strlen($string);
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getMetadata($key = null)
+    {
+        if ($key === 'hwm') {
+            return $this->hwm;
+        }
+
+        return $key ? null : [];
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/CachingStream.php b/vendor/guzzlehttp/psr7/src/CachingStream.php
new file mode 100644
index 0000000..7e4554d
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/CachingStream.php
@@ -0,0 +1,153 @@
+remoteStream = $stream;
+        $this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+'));
+    }
+
+    public function getSize(): ?int
+    {
+        $remoteSize = $this->remoteStream->getSize();
+
+        if (null === $remoteSize) {
+            return null;
+        }
+
+        return max($this->stream->getSize(), $remoteSize);
+    }
+
+    public function rewind(): void
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET): void
+    {
+        if ($whence === SEEK_SET) {
+            $byte = $offset;
+        } elseif ($whence === SEEK_CUR) {
+            $byte = $offset + $this->tell();
+        } elseif ($whence === SEEK_END) {
+            $size = $this->remoteStream->getSize();
+            if ($size === null) {
+                $size = $this->cacheEntireStream();
+            }
+            $byte = $size + $offset;
+        } else {
+            throw new \InvalidArgumentException('Invalid whence');
+        }
+
+        $diff = $byte - $this->stream->getSize();
+
+        if ($diff > 0) {
+            // Read the remoteStream until we have read in at least the amount
+            // of bytes requested, or we reach the end of the file.
+            while ($diff > 0 && !$this->remoteStream->eof()) {
+                $this->read($diff);
+                $diff = $byte - $this->stream->getSize();
+            }
+        } else {
+            // We can just do a normal seek since we've already seen this byte.
+            $this->stream->seek($byte);
+        }
+    }
+
+    public function read($length): string
+    {
+        // Perform a regular read on any previously read data from the buffer
+        $data = $this->stream->read($length);
+        $remaining = $length - strlen($data);
+
+        // More data was requested so read from the remote stream
+        if ($remaining) {
+            // If data was written to the buffer in a position that would have
+            // been filled from the remote stream, then we must skip bytes on
+            // the remote stream to emulate overwriting bytes from that
+            // position. This mimics the behavior of other PHP stream wrappers.
+            $remoteData = $this->remoteStream->read(
+                $remaining + $this->skipReadBytes
+            );
+
+            if ($this->skipReadBytes) {
+                $len = strlen($remoteData);
+                $remoteData = substr($remoteData, $this->skipReadBytes);
+                $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
+            }
+
+            $data .= $remoteData;
+            $this->stream->write($remoteData);
+        }
+
+        return $data;
+    }
+
+    public function write($string): int
+    {
+        // When appending to the end of the currently read stream, you'll want
+        // to skip bytes from being read from the remote stream to emulate
+        // other stream wrappers. Basically replacing bytes of data of a fixed
+        // length.
+        $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell();
+        if ($overflow > 0) {
+            $this->skipReadBytes += $overflow;
+        }
+
+        return $this->stream->write($string);
+    }
+
+    public function eof(): bool
+    {
+        return $this->stream->eof() && $this->remoteStream->eof();
+    }
+
+    /**
+     * Close both the remote stream and buffer stream
+     */
+    public function close(): void
+    {
+        $this->remoteStream->close();
+        $this->stream->close();
+    }
+
+    private function cacheEntireStream(): int
+    {
+        $target = new FnStream(['write' => 'strlen']);
+        Utils::copyToStream($this, $target);
+
+        return $this->tell();
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/DroppingStream.php b/vendor/guzzlehttp/psr7/src/DroppingStream.php
new file mode 100644
index 0000000..6e3d209
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/DroppingStream.php
@@ -0,0 +1,49 @@
+stream = $stream;
+        $this->maxLength = $maxLength;
+    }
+
+    public function write($string): int
+    {
+        $diff = $this->maxLength - $this->stream->getSize();
+
+        // Begin returning 0 when the underlying stream is too large.
+        if ($diff <= 0) {
+            return 0;
+        }
+
+        // Write the stream or a subset of the stream if needed.
+        if (strlen($string) < $diff) {
+            return $this->stream->write($string);
+        }
+
+        return $this->stream->write(substr($string, 0, $diff));
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php b/vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php
new file mode 100644
index 0000000..3a08477
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php
@@ -0,0 +1,14 @@
+ */
+    private $methods;
+
+    /**
+     * @param array $methods Hash of method name to a callable.
+     */
+    public function __construct(array $methods)
+    {
+        $this->methods = $methods;
+
+        // Create the functions on the class
+        foreach ($methods as $name => $fn) {
+            $this->{'_fn_'.$name} = $fn;
+        }
+    }
+
+    /**
+     * Lazily determine which methods are not implemented.
+     *
+     * @throws \BadMethodCallException
+     */
+    public function __get(string $name): void
+    {
+        throw new \BadMethodCallException(str_replace('_fn_', '', $name)
+            .'() is not implemented in the FnStream');
+    }
+
+    /**
+     * The close method is called on the underlying stream only if possible.
+     */
+    public function __destruct()
+    {
+        if (isset($this->_fn_close)) {
+            ($this->_fn_close)();
+        }
+    }
+
+    /**
+     * An unserialize would allow the __destruct to run when the unserialized value goes out of scope.
+     *
+     * @throws \LogicException
+     */
+    public function __wakeup(): void
+    {
+        throw new \LogicException('FnStream should never be unserialized');
+    }
+
+    /**
+     * Adds custom functionality to an underlying stream by intercepting
+     * specific method calls.
+     *
+     * @param StreamInterface         $stream  Stream to decorate
+     * @param array $methods Hash of method name to a closure
+     *
+     * @return FnStream
+     */
+    public static function decorate(StreamInterface $stream, array $methods)
+    {
+        // If any of the required methods were not provided, then simply
+        // proxy to the decorated stream.
+        foreach (array_diff(self::SLOTS, array_keys($methods)) as $diff) {
+            /** @var callable $callable */
+            $callable = [$stream, $diff];
+            $methods[$diff] = $callable;
+        }
+
+        return new self($methods);
+    }
+
+    public function __toString(): string
+    {
+        try {
+            /** @var string */
+            return ($this->_fn___toString)();
+        } catch (\Throwable $e) {
+            if (\PHP_VERSION_ID >= 70400) {
+                throw $e;
+            }
+            trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
+            return '';
+        }
+    }
+
+    public function close(): void
+    {
+        ($this->_fn_close)();
+    }
+
+    public function detach()
+    {
+        return ($this->_fn_detach)();
+    }
+
+    public function getSize(): ?int
+    {
+        return ($this->_fn_getSize)();
+    }
+
+    public function tell(): int
+    {
+        return ($this->_fn_tell)();
+    }
+
+    public function eof(): bool
+    {
+        return ($this->_fn_eof)();
+    }
+
+    public function isSeekable(): bool
+    {
+        return ($this->_fn_isSeekable)();
+    }
+
+    public function rewind(): void
+    {
+        ($this->_fn_rewind)();
+    }
+
+    public function seek($offset, $whence = SEEK_SET): void
+    {
+        ($this->_fn_seek)($offset, $whence);
+    }
+
+    public function isWritable(): bool
+    {
+        return ($this->_fn_isWritable)();
+    }
+
+    public function write($string): int
+    {
+        return ($this->_fn_write)($string);
+    }
+
+    public function isReadable(): bool
+    {
+        return ($this->_fn_isReadable)();
+    }
+
+    public function read($length): string
+    {
+        return ($this->_fn_read)($length);
+    }
+
+    public function getContents(): string
+    {
+        return ($this->_fn_getContents)();
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getMetadata($key = null)
+    {
+        return ($this->_fn_getMetadata)($key);
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Header.php b/vendor/guzzlehttp/psr7/src/Header.php
new file mode 100644
index 0000000..bbce8b0
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Header.php
@@ -0,0 +1,134 @@
+]+>|[^=]+/', $kvp, $matches)) {
+                        $m = $matches[0];
+                        if (isset($m[1])) {
+                            $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
+                        } else {
+                            $part[] = trim($m[0], $trimmed);
+                        }
+                    }
+                }
+                if ($part) {
+                    $params[] = $part;
+                }
+            }
+        }
+
+        return $params;
+    }
+
+    /**
+     * Converts an array of header values that may contain comma separated
+     * headers into an array of headers with no comma separated values.
+     *
+     * @param string|array $header Header to normalize.
+     *
+     * @deprecated Use self::splitList() instead.
+     */
+    public static function normalize($header): array
+    {
+        $result = [];
+        foreach ((array) $header as $value) {
+            foreach (self::splitList($value) as $parsed) {
+                $result[] = $parsed;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Splits a HTTP header defined to contain a comma-separated list into
+     * each individual value. Empty values will be removed.
+     *
+     * Example headers include 'accept', 'cache-control' and 'if-none-match'.
+     *
+     * This method must not be used to parse headers that are not defined as
+     * a list, such as 'user-agent' or 'set-cookie'.
+     *
+     * @param string|string[] $values Header value as returned by MessageInterface::getHeader()
+     *
+     * @return string[]
+     */
+    public static function splitList($values): array
+    {
+        if (!\is_array($values)) {
+            $values = [$values];
+        }
+
+        $result = [];
+        foreach ($values as $value) {
+            if (!\is_string($value)) {
+                throw new \TypeError('$header must either be a string or an array containing strings.');
+            }
+
+            $v = '';
+            $isQuoted = false;
+            $isEscaped = false;
+            for ($i = 0, $max = \strlen($value); $i < $max; ++$i) {
+                if ($isEscaped) {
+                    $v .= $value[$i];
+                    $isEscaped = false;
+
+                    continue;
+                }
+
+                if (!$isQuoted && $value[$i] === ',') {
+                    $v = \trim($v);
+                    if ($v !== '') {
+                        $result[] = $v;
+                    }
+
+                    $v = '';
+                    continue;
+                }
+
+                if ($isQuoted && $value[$i] === '\\') {
+                    $isEscaped = true;
+                    $v .= $value[$i];
+
+                    continue;
+                }
+                if ($value[$i] === '"') {
+                    $isQuoted = !$isQuoted;
+                    $v .= $value[$i];
+
+                    continue;
+                }
+
+                $v .= $value[$i];
+            }
+
+            $v = \trim($v);
+            if ($v !== '') {
+                $result[] = $v;
+            }
+        }
+
+        return $result;
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/HttpFactory.php b/vendor/guzzlehttp/psr7/src/HttpFactory.php
new file mode 100644
index 0000000..3ef1510
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/HttpFactory.php
@@ -0,0 +1,94 @@
+getSize();
+        }
+
+        return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType);
+    }
+
+    public function createStream(string $content = ''): StreamInterface
+    {
+        return Utils::streamFor($content);
+    }
+
+    public function createStreamFromFile(string $file, string $mode = 'r'): StreamInterface
+    {
+        try {
+            $resource = Utils::tryFopen($file, $mode);
+        } catch (\RuntimeException $e) {
+            if ('' === $mode || false === \in_array($mode[0], ['r', 'w', 'a', 'x', 'c'], true)) {
+                throw new \InvalidArgumentException(sprintf('Invalid file opening mode "%s"', $mode), 0, $e);
+            }
+
+            throw $e;
+        }
+
+        return Utils::streamFor($resource);
+    }
+
+    public function createStreamFromResource($resource): StreamInterface
+    {
+        return Utils::streamFor($resource);
+    }
+
+    public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
+    {
+        if (empty($method)) {
+            if (!empty($serverParams['REQUEST_METHOD'])) {
+                $method = $serverParams['REQUEST_METHOD'];
+            } else {
+                throw new \InvalidArgumentException('Cannot determine HTTP method');
+            }
+        }
+
+        return new ServerRequest($method, $uri, [], null, '1.1', $serverParams);
+    }
+
+    public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
+    {
+        return new Response($code, [], null, '1.1', $reasonPhrase);
+    }
+
+    public function createRequest(string $method, $uri): RequestInterface
+    {
+        return new Request($method, $uri);
+    }
+
+    public function createUri(string $uri = ''): UriInterface
+    {
+        return new Uri($uri);
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/InflateStream.php b/vendor/guzzlehttp/psr7/src/InflateStream.php
new file mode 100644
index 0000000..e674c9a
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/InflateStream.php
@@ -0,0 +1,37 @@
+ 15 + 32]);
+        $this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource));
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/LazyOpenStream.php b/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
new file mode 100644
index 0000000..f6c8490
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
@@ -0,0 +1,49 @@
+filename = $filename;
+        $this->mode = $mode;
+
+        // unsetting the property forces the first access to go through
+        // __get().
+        unset($this->stream);
+    }
+
+    /**
+     * Creates the underlying stream lazily when required.
+     */
+    protected function createStream(): StreamInterface
+    {
+        return Utils::streamFor(Utils::tryFopen($this->filename, $this->mode));
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/LimitStream.php b/vendor/guzzlehttp/psr7/src/LimitStream.php
new file mode 100644
index 0000000..fb22325
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/LimitStream.php
@@ -0,0 +1,157 @@
+stream = $stream;
+        $this->setLimit($limit);
+        $this->setOffset($offset);
+    }
+
+    public function eof(): bool
+    {
+        // Always return true if the underlying stream is EOF
+        if ($this->stream->eof()) {
+            return true;
+        }
+
+        // No limit and the underlying stream is not at EOF
+        if ($this->limit === -1) {
+            return false;
+        }
+
+        return $this->stream->tell() >= $this->offset + $this->limit;
+    }
+
+    /**
+     * Returns the size of the limited subset of data
+     */
+    public function getSize(): ?int
+    {
+        if (null === ($length = $this->stream->getSize())) {
+            return null;
+        } elseif ($this->limit === -1) {
+            return $length - $this->offset;
+        } else {
+            return min($this->limit, $length - $this->offset);
+        }
+    }
+
+    /**
+     * Allow for a bounded seek on the read limited stream
+     */
+    public function seek($offset, $whence = SEEK_SET): void
+    {
+        if ($whence !== SEEK_SET || $offset < 0) {
+            throw new \RuntimeException(sprintf(
+                'Cannot seek to offset %s with whence %s',
+                $offset,
+                $whence
+            ));
+        }
+
+        $offset += $this->offset;
+
+        if ($this->limit !== -1) {
+            if ($offset > $this->offset + $this->limit) {
+                $offset = $this->offset + $this->limit;
+            }
+        }
+
+        $this->stream->seek($offset);
+    }
+
+    /**
+     * Give a relative tell()
+     */
+    public function tell(): int
+    {
+        return $this->stream->tell() - $this->offset;
+    }
+
+    /**
+     * Set the offset to start limiting from
+     *
+     * @param int $offset Offset to seek to and begin byte limiting from
+     *
+     * @throws \RuntimeException if the stream cannot be seeked.
+     */
+    public function setOffset(int $offset): void
+    {
+        $current = $this->stream->tell();
+
+        if ($current !== $offset) {
+            // If the stream cannot seek to the offset position, then read to it
+            if ($this->stream->isSeekable()) {
+                $this->stream->seek($offset);
+            } elseif ($current > $offset) {
+                throw new \RuntimeException("Could not seek to stream offset $offset");
+            } else {
+                $this->stream->read($offset - $current);
+            }
+        }
+
+        $this->offset = $offset;
+    }
+
+    /**
+     * Set the limit of bytes that the decorator allows to be read from the
+     * stream.
+     *
+     * @param int $limit Number of bytes to allow to be read from the stream.
+     *                   Use -1 for no limit.
+     */
+    public function setLimit(int $limit): void
+    {
+        $this->limit = $limit;
+    }
+
+    public function read($length): string
+    {
+        if ($this->limit === -1) {
+            return $this->stream->read($length);
+        }
+
+        // Check if the current position is less than the total allowed
+        // bytes + original offset
+        $remaining = ($this->offset + $this->limit) - $this->stream->tell();
+        if ($remaining > 0) {
+            // Only return the amount of requested data, ensuring that the byte
+            // limit is not exceeded
+            return $this->stream->read(min($remaining, $length));
+        }
+
+        return '';
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Message.php b/vendor/guzzlehttp/psr7/src/Message.php
new file mode 100644
index 0000000..5561a51
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Message.php
@@ -0,0 +1,246 @@
+getMethod().' '
+                    .$message->getRequestTarget())
+                .' HTTP/'.$message->getProtocolVersion();
+            if (!$message->hasHeader('host')) {
+                $msg .= "\r\nHost: ".$message->getUri()->getHost();
+            }
+        } elseif ($message instanceof ResponseInterface) {
+            $msg = 'HTTP/'.$message->getProtocolVersion().' '
+                .$message->getStatusCode().' '
+                .$message->getReasonPhrase();
+        } else {
+            throw new \InvalidArgumentException('Unknown message type');
+        }
+
+        foreach ($message->getHeaders() as $name => $values) {
+            if (is_string($name) && strtolower($name) === 'set-cookie') {
+                foreach ($values as $value) {
+                    $msg .= "\r\n{$name}: ".$value;
+                }
+            } else {
+                $msg .= "\r\n{$name}: ".implode(', ', $values);
+            }
+        }
+
+        return "{$msg}\r\n\r\n".$message->getBody();
+    }
+
+    /**
+     * Get a short summary of the message body.
+     *
+     * Will return `null` if the response is not printable.
+     *
+     * @param MessageInterface $message    The message to get the body summary
+     * @param int              $truncateAt The maximum allowed size of the summary
+     */
+    public static function bodySummary(MessageInterface $message, int $truncateAt = 120): ?string
+    {
+        $body = $message->getBody();
+
+        if (!$body->isSeekable() || !$body->isReadable()) {
+            return null;
+        }
+
+        $size = $body->getSize();
+
+        if ($size === 0) {
+            return null;
+        }
+
+        $body->rewind();
+        $summary = $body->read($truncateAt);
+        $body->rewind();
+
+        if ($size > $truncateAt) {
+            $summary .= ' (truncated...)';
+        }
+
+        // Matches any printable character, including unicode characters:
+        // letters, marks, numbers, punctuation, spacing, and separators.
+        if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary) !== 0) {
+            return null;
+        }
+
+        return $summary;
+    }
+
+    /**
+     * Attempts to rewind a message body and throws an exception on failure.
+     *
+     * The body of the message will only be rewound if a call to `tell()`
+     * returns a value other than `0`.
+     *
+     * @param MessageInterface $message Message to rewind
+     *
+     * @throws \RuntimeException
+     */
+    public static function rewindBody(MessageInterface $message): void
+    {
+        $body = $message->getBody();
+
+        if ($body->tell()) {
+            $body->rewind();
+        }
+    }
+
+    /**
+     * Parses an HTTP message into an associative array.
+     *
+     * The array contains the "start-line" key containing the start line of
+     * the message, "headers" key containing an associative array of header
+     * array values, and a "body" key containing the body of the message.
+     *
+     * @param string $message HTTP request or response to parse.
+     */
+    public static function parseMessage(string $message): array
+    {
+        if (!$message) {
+            throw new \InvalidArgumentException('Invalid message');
+        }
+
+        $message = ltrim($message, "\r\n");
+
+        $messageParts = preg_split("/\r?\n\r?\n/", $message, 2);
+
+        if ($messageParts === false || count($messageParts) !== 2) {
+            throw new \InvalidArgumentException('Invalid message: Missing header delimiter');
+        }
+
+        [$rawHeaders, $body] = $messageParts;
+        $rawHeaders .= "\r\n"; // Put back the delimiter we split previously
+        $headerParts = preg_split("/\r?\n/", $rawHeaders, 2);
+
+        if ($headerParts === false || count($headerParts) !== 2) {
+            throw new \InvalidArgumentException('Invalid message: Missing status line');
+        }
+
+        [$startLine, $rawHeaders] = $headerParts;
+
+        if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') {
+            // Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0
+            $rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders);
+        }
+
+        /** @var array[] $headerLines */
+        $count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER);
+
+        // If these aren't the same, then one line didn't match and there's an invalid header.
+        if ($count !== substr_count($rawHeaders, "\n")) {
+            // Folding is deprecated, see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
+            if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
+                throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
+            }
+
+            throw new \InvalidArgumentException('Invalid header syntax');
+        }
+
+        $headers = [];
+
+        foreach ($headerLines as $headerLine) {
+            $headers[$headerLine[1]][] = $headerLine[2];
+        }
+
+        return [
+            'start-line' => $startLine,
+            'headers' => $headers,
+            'body' => $body,
+        ];
+    }
+
+    /**
+     * Constructs a URI for an HTTP request message.
+     *
+     * @param string $path    Path from the start-line
+     * @param array  $headers Array of headers (each value an array).
+     */
+    public static function parseRequestUri(string $path, array $headers): string
+    {
+        $hostKey = array_filter(array_keys($headers), function ($k) {
+            // Numeric array keys are converted to int by PHP.
+            $k = (string) $k;
+
+            return strtolower($k) === 'host';
+        });
+
+        // If no host is found, then a full URI cannot be constructed.
+        if (!$hostKey) {
+            return $path;
+        }
+
+        $host = $headers[reset($hostKey)][0];
+        $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
+
+        return $scheme.'://'.$host.'/'.ltrim($path, '/');
+    }
+
+    /**
+     * Parses a request message string into a request object.
+     *
+     * @param string $message Request message string.
+     */
+    public static function parseRequest(string $message): RequestInterface
+    {
+        $data = self::parseMessage($message);
+        $matches = [];
+        if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
+            throw new \InvalidArgumentException('Invalid request string');
+        }
+        $parts = explode(' ', $data['start-line'], 3);
+        $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1';
+
+        $request = new Request(
+            $parts[0],
+            $matches[1] === '/' ? self::parseRequestUri($parts[1], $data['headers']) : $parts[1],
+            $data['headers'],
+            $data['body'],
+            $version
+        );
+
+        return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
+    }
+
+    /**
+     * Parses a response message string into a response object.
+     *
+     * @param string $message Response message string.
+     */
+    public static function parseResponse(string $message): ResponseInterface
+    {
+        $data = self::parseMessage($message);
+        // According to https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
+        // the space between status-code and reason-phrase is required. But
+        // browsers accept responses without space and reason as well.
+        if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
+            throw new \InvalidArgumentException('Invalid response string: '.$data['start-line']);
+        }
+        $parts = explode(' ', $data['start-line'], 3);
+
+        return new Response(
+            (int) $parts[1],
+            $data['headers'],
+            $data['body'],
+            explode('/', $parts[0])[1],
+            $parts[2] ?? null
+        );
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/MessageTrait.php b/vendor/guzzlehttp/psr7/src/MessageTrait.php
new file mode 100644
index 0000000..65dbc4b
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/MessageTrait.php
@@ -0,0 +1,265 @@
+ array of values */
+    private $headers = [];
+
+    /** @var string[] Map of lowercase header name => original name at registration */
+    private $headerNames = [];
+
+    /** @var string */
+    private $protocol = '1.1';
+
+    /** @var StreamInterface|null */
+    private $stream;
+
+    public function getProtocolVersion(): string
+    {
+        return $this->protocol;
+    }
+
+    public function withProtocolVersion($version): MessageInterface
+    {
+        if ($this->protocol === $version) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->protocol = $version;
+
+        return $new;
+    }
+
+    public function getHeaders(): array
+    {
+        return $this->headers;
+    }
+
+    public function hasHeader($header): bool
+    {
+        return isset($this->headerNames[strtolower($header)]);
+    }
+
+    public function getHeader($header): array
+    {
+        $header = strtolower($header);
+
+        if (!isset($this->headerNames[$header])) {
+            return [];
+        }
+
+        $header = $this->headerNames[$header];
+
+        return $this->headers[$header];
+    }
+
+    public function getHeaderLine($header): string
+    {
+        return implode(', ', $this->getHeader($header));
+    }
+
+    public function withHeader($header, $value): MessageInterface
+    {
+        $this->assertHeader($header);
+        $value = $this->normalizeHeaderValue($value);
+        $normalized = strtolower($header);
+
+        $new = clone $this;
+        if (isset($new->headerNames[$normalized])) {
+            unset($new->headers[$new->headerNames[$normalized]]);
+        }
+        $new->headerNames[$normalized] = $header;
+        $new->headers[$header] = $value;
+
+        return $new;
+    }
+
+    public function withAddedHeader($header, $value): MessageInterface
+    {
+        $this->assertHeader($header);
+        $value = $this->normalizeHeaderValue($value);
+        $normalized = strtolower($header);
+
+        $new = clone $this;
+        if (isset($new->headerNames[$normalized])) {
+            $header = $this->headerNames[$normalized];
+            $new->headers[$header] = array_merge($this->headers[$header], $value);
+        } else {
+            $new->headerNames[$normalized] = $header;
+            $new->headers[$header] = $value;
+        }
+
+        return $new;
+    }
+
+    public function withoutHeader($header): MessageInterface
+    {
+        $normalized = strtolower($header);
+
+        if (!isset($this->headerNames[$normalized])) {
+            return $this;
+        }
+
+        $header = $this->headerNames[$normalized];
+
+        $new = clone $this;
+        unset($new->headers[$header], $new->headerNames[$normalized]);
+
+        return $new;
+    }
+
+    public function getBody(): StreamInterface
+    {
+        if (!$this->stream) {
+            $this->stream = Utils::streamFor('');
+        }
+
+        return $this->stream;
+    }
+
+    public function withBody(StreamInterface $body): MessageInterface
+    {
+        if ($body === $this->stream) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->stream = $body;
+
+        return $new;
+    }
+
+    /**
+     * @param (string|string[])[] $headers
+     */
+    private function setHeaders(array $headers): void
+    {
+        $this->headerNames = $this->headers = [];
+        foreach ($headers as $header => $value) {
+            // Numeric array keys are converted to int by PHP.
+            $header = (string) $header;
+
+            $this->assertHeader($header);
+            $value = $this->normalizeHeaderValue($value);
+            $normalized = strtolower($header);
+            if (isset($this->headerNames[$normalized])) {
+                $header = $this->headerNames[$normalized];
+                $this->headers[$header] = array_merge($this->headers[$header], $value);
+            } else {
+                $this->headerNames[$normalized] = $header;
+                $this->headers[$header] = $value;
+            }
+        }
+    }
+
+    /**
+     * @param mixed $value
+     *
+     * @return string[]
+     */
+    private function normalizeHeaderValue($value): array
+    {
+        if (!is_array($value)) {
+            return $this->trimAndValidateHeaderValues([$value]);
+        }
+
+        if (count($value) === 0) {
+            throw new \InvalidArgumentException('Header value can not be an empty array.');
+        }
+
+        return $this->trimAndValidateHeaderValues($value);
+    }
+
+    /**
+     * Trims whitespace from the header values.
+     *
+     * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field.
+     *
+     * header-field = field-name ":" OWS field-value OWS
+     * OWS          = *( SP / HTAB )
+     *
+     * @param mixed[] $values Header values
+     *
+     * @return string[] Trimmed header values
+     *
+     * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
+     */
+    private function trimAndValidateHeaderValues(array $values): array
+    {
+        return array_map(function ($value) {
+            if (!is_scalar($value) && null !== $value) {
+                throw new \InvalidArgumentException(sprintf(
+                    'Header value must be scalar or null but %s provided.',
+                    is_object($value) ? get_class($value) : gettype($value)
+                ));
+            }
+
+            $trimmed = trim((string) $value, " \t");
+            $this->assertValue($trimmed);
+
+            return $trimmed;
+        }, array_values($values));
+    }
+
+    /**
+     * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
+     *
+     * @param mixed $header
+     */
+    private function assertHeader($header): void
+    {
+        if (!is_string($header)) {
+            throw new \InvalidArgumentException(sprintf(
+                'Header name must be a string but %s provided.',
+                is_object($header) ? get_class($header) : gettype($header)
+            ));
+        }
+
+        if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $header)) {
+            throw new \InvalidArgumentException(
+                sprintf('"%s" is not valid header name.', $header)
+            );
+        }
+    }
+
+    /**
+     * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
+     *
+     * field-value    = *( field-content / obs-fold )
+     * field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
+     * field-vchar    = VCHAR / obs-text
+     * VCHAR          = %x21-7E
+     * obs-text       = %x80-FF
+     * obs-fold       = CRLF 1*( SP / HTAB )
+     */
+    private function assertValue(string $value): void
+    {
+        // The regular expression intentionally does not support the obs-fold production, because as
+        // per RFC 7230#3.2.4:
+        //
+        // A sender MUST NOT generate a message that includes
+        // line folding (i.e., that has any field-value that contains a match to
+        // the obs-fold rule) unless the message is intended for packaging
+        // within the message/http media type.
+        //
+        // Clients must not send a request with line folding and a server sending folded headers is
+        // likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting
+        // folding is not likely to break any legitimate use case.
+        if (!preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/D', $value)) {
+            throw new \InvalidArgumentException(
+                sprintf('"%s" is not valid header value.', $value)
+            );
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/MimeType.php b/vendor/guzzlehttp/psr7/src/MimeType.php
new file mode 100644
index 0000000..b131bdb
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/MimeType.php
@@ -0,0 +1,1259 @@
+ 'application/vnd.1000minds.decision-model+xml',
+        '3dml' => 'text/vnd.in3d.3dml',
+        '3ds' => 'image/x-3ds',
+        '3g2' => 'video/3gpp2',
+        '3gp' => 'video/3gp',
+        '3gpp' => 'video/3gpp',
+        '3mf' => 'model/3mf',
+        '7z' => 'application/x-7z-compressed',
+        '7zip' => 'application/x-7z-compressed',
+        '123' => 'application/vnd.lotus-1-2-3',
+        'aab' => 'application/x-authorware-bin',
+        'aac' => 'audio/aac',
+        'aam' => 'application/x-authorware-map',
+        'aas' => 'application/x-authorware-seg',
+        'abw' => 'application/x-abiword',
+        'ac' => 'application/vnd.nokia.n-gage.ac+xml',
+        'ac3' => 'audio/ac3',
+        'acc' => 'application/vnd.americandynamics.acc',
+        'ace' => 'application/x-ace-compressed',
+        'acu' => 'application/vnd.acucobol',
+        'acutc' => 'application/vnd.acucorp',
+        'adp' => 'audio/adpcm',
+        'adts' => 'audio/aac',
+        'aep' => 'application/vnd.audiograph',
+        'afm' => 'application/x-font-type1',
+        'afp' => 'application/vnd.ibm.modcap',
+        'age' => 'application/vnd.age',
+        'ahead' => 'application/vnd.ahead.space',
+        'ai' => 'application/pdf',
+        'aif' => 'audio/x-aiff',
+        'aifc' => 'audio/x-aiff',
+        'aiff' => 'audio/x-aiff',
+        'air' => 'application/vnd.adobe.air-application-installer-package+zip',
+        'ait' => 'application/vnd.dvb.ait',
+        'ami' => 'application/vnd.amiga.ami',
+        'aml' => 'application/automationml-aml+xml',
+        'amlx' => 'application/automationml-amlx+zip',
+        'amr' => 'audio/amr',
+        'apk' => 'application/vnd.android.package-archive',
+        'apng' => 'image/apng',
+        'appcache' => 'text/cache-manifest',
+        'appinstaller' => 'application/appinstaller',
+        'application' => 'application/x-ms-application',
+        'appx' => 'application/appx',
+        'appxbundle' => 'application/appxbundle',
+        'apr' => 'application/vnd.lotus-approach',
+        'arc' => 'application/x-freearc',
+        'arj' => 'application/x-arj',
+        'asc' => 'application/pgp-signature',
+        'asf' => 'video/x-ms-asf',
+        'asm' => 'text/x-asm',
+        'aso' => 'application/vnd.accpac.simply.aso',
+        'asx' => 'video/x-ms-asf',
+        'atc' => 'application/vnd.acucorp',
+        'atom' => 'application/atom+xml',
+        'atomcat' => 'application/atomcat+xml',
+        'atomdeleted' => 'application/atomdeleted+xml',
+        'atomsvc' => 'application/atomsvc+xml',
+        'atx' => 'application/vnd.antix.game-component',
+        'au' => 'audio/x-au',
+        'avci' => 'image/avci',
+        'avcs' => 'image/avcs',
+        'avi' => 'video/x-msvideo',
+        'avif' => 'image/avif',
+        'aw' => 'application/applixware',
+        'azf' => 'application/vnd.airzip.filesecure.azf',
+        'azs' => 'application/vnd.airzip.filesecure.azs',
+        'azv' => 'image/vnd.airzip.accelerator.azv',
+        'azw' => 'application/vnd.amazon.ebook',
+        'b16' => 'image/vnd.pco.b16',
+        'bat' => 'application/x-msdownload',
+        'bcpio' => 'application/x-bcpio',
+        'bdf' => 'application/x-font-bdf',
+        'bdm' => 'application/vnd.syncml.dm+wbxml',
+        'bdoc' => 'application/x-bdoc',
+        'bed' => 'application/vnd.realvnc.bed',
+        'bh2' => 'application/vnd.fujitsu.oasysprs',
+        'bin' => 'application/octet-stream',
+        'blb' => 'application/x-blorb',
+        'blorb' => 'application/x-blorb',
+        'bmi' => 'application/vnd.bmi',
+        'bmml' => 'application/vnd.balsamiq.bmml+xml',
+        'bmp' => 'image/bmp',
+        'book' => 'application/vnd.framemaker',
+        'box' => 'application/vnd.previewsystems.box',
+        'boz' => 'application/x-bzip2',
+        'bpk' => 'application/octet-stream',
+        'bpmn' => 'application/octet-stream',
+        'bsp' => 'model/vnd.valve.source.compiled-map',
+        'btf' => 'image/prs.btif',
+        'btif' => 'image/prs.btif',
+        'buffer' => 'application/octet-stream',
+        'bz' => 'application/x-bzip',
+        'bz2' => 'application/x-bzip2',
+        'c' => 'text/x-c',
+        'c4d' => 'application/vnd.clonk.c4group',
+        'c4f' => 'application/vnd.clonk.c4group',
+        'c4g' => 'application/vnd.clonk.c4group',
+        'c4p' => 'application/vnd.clonk.c4group',
+        'c4u' => 'application/vnd.clonk.c4group',
+        'c11amc' => 'application/vnd.cluetrust.cartomobile-config',
+        'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg',
+        'cab' => 'application/vnd.ms-cab-compressed',
+        'caf' => 'audio/x-caf',
+        'cap' => 'application/vnd.tcpdump.pcap',
+        'car' => 'application/vnd.curl.car',
+        'cat' => 'application/vnd.ms-pki.seccat',
+        'cb7' => 'application/x-cbr',
+        'cba' => 'application/x-cbr',
+        'cbr' => 'application/x-cbr',
+        'cbt' => 'application/x-cbr',
+        'cbz' => 'application/x-cbr',
+        'cc' => 'text/x-c',
+        'cco' => 'application/x-cocoa',
+        'cct' => 'application/x-director',
+        'ccxml' => 'application/ccxml+xml',
+        'cdbcmsg' => 'application/vnd.contact.cmsg',
+        'cdf' => 'application/x-netcdf',
+        'cdfx' => 'application/cdfx+xml',
+        'cdkey' => 'application/vnd.mediastation.cdkey',
+        'cdmia' => 'application/cdmi-capability',
+        'cdmic' => 'application/cdmi-container',
+        'cdmid' => 'application/cdmi-domain',
+        'cdmio' => 'application/cdmi-object',
+        'cdmiq' => 'application/cdmi-queue',
+        'cdr' => 'application/cdr',
+        'cdx' => 'chemical/x-cdx',
+        'cdxml' => 'application/vnd.chemdraw+xml',
+        'cdy' => 'application/vnd.cinderella',
+        'cer' => 'application/pkix-cert',
+        'cfs' => 'application/x-cfs-compressed',
+        'cgm' => 'image/cgm',
+        'chat' => 'application/x-chat',
+        'chm' => 'application/vnd.ms-htmlhelp',
+        'chrt' => 'application/vnd.kde.kchart',
+        'cif' => 'chemical/x-cif',
+        'cii' => 'application/vnd.anser-web-certificate-issue-initiation',
+        'cil' => 'application/vnd.ms-artgalry',
+        'cjs' => 'application/node',
+        'cla' => 'application/vnd.claymore',
+        'class' => 'application/octet-stream',
+        'cld' => 'model/vnd.cld',
+        'clkk' => 'application/vnd.crick.clicker.keyboard',
+        'clkp' => 'application/vnd.crick.clicker.palette',
+        'clkt' => 'application/vnd.crick.clicker.template',
+        'clkw' => 'application/vnd.crick.clicker.wordbank',
+        'clkx' => 'application/vnd.crick.clicker',
+        'clp' => 'application/x-msclip',
+        'cmc' => 'application/vnd.cosmocaller',
+        'cmdf' => 'chemical/x-cmdf',
+        'cml' => 'chemical/x-cml',
+        'cmp' => 'application/vnd.yellowriver-custom-menu',
+        'cmx' => 'image/x-cmx',
+        'cod' => 'application/vnd.rim.cod',
+        'coffee' => 'text/coffeescript',
+        'com' => 'application/x-msdownload',
+        'conf' => 'text/plain',
+        'cpio' => 'application/x-cpio',
+        'cpl' => 'application/cpl+xml',
+        'cpp' => 'text/x-c',
+        'cpt' => 'application/mac-compactpro',
+        'crd' => 'application/x-mscardfile',
+        'crl' => 'application/pkix-crl',
+        'crt' => 'application/x-x509-ca-cert',
+        'crx' => 'application/x-chrome-extension',
+        'cryptonote' => 'application/vnd.rig.cryptonote',
+        'csh' => 'application/x-csh',
+        'csl' => 'application/vnd.citationstyles.style+xml',
+        'csml' => 'chemical/x-csml',
+        'csp' => 'application/vnd.commonspace',
+        'csr' => 'application/octet-stream',
+        'css' => 'text/css',
+        'cst' => 'application/x-director',
+        'csv' => 'text/csv',
+        'cu' => 'application/cu-seeme',
+        'curl' => 'text/vnd.curl',
+        'cwl' => 'application/cwl',
+        'cww' => 'application/prs.cww',
+        'cxt' => 'application/x-director',
+        'cxx' => 'text/x-c',
+        'dae' => 'model/vnd.collada+xml',
+        'daf' => 'application/vnd.mobius.daf',
+        'dart' => 'application/vnd.dart',
+        'dataless' => 'application/vnd.fdsn.seed',
+        'davmount' => 'application/davmount+xml',
+        'dbf' => 'application/vnd.dbf',
+        'dbk' => 'application/docbook+xml',
+        'dcr' => 'application/x-director',
+        'dcurl' => 'text/vnd.curl.dcurl',
+        'dd2' => 'application/vnd.oma.dd2+xml',
+        'ddd' => 'application/vnd.fujixerox.ddd',
+        'ddf' => 'application/vnd.syncml.dmddf+xml',
+        'dds' => 'image/vnd.ms-dds',
+        'deb' => 'application/x-debian-package',
+        'def' => 'text/plain',
+        'deploy' => 'application/octet-stream',
+        'der' => 'application/x-x509-ca-cert',
+        'dfac' => 'application/vnd.dreamfactory',
+        'dgc' => 'application/x-dgc-compressed',
+        'dib' => 'image/bmp',
+        'dic' => 'text/x-c',
+        'dir' => 'application/x-director',
+        'dis' => 'application/vnd.mobius.dis',
+        'disposition-notification' => 'message/disposition-notification',
+        'dist' => 'application/octet-stream',
+        'distz' => 'application/octet-stream',
+        'djv' => 'image/vnd.djvu',
+        'djvu' => 'image/vnd.djvu',
+        'dll' => 'application/octet-stream',
+        'dmg' => 'application/x-apple-diskimage',
+        'dmn' => 'application/octet-stream',
+        'dmp' => 'application/vnd.tcpdump.pcap',
+        'dms' => 'application/octet-stream',
+        'dna' => 'application/vnd.dna',
+        'doc' => 'application/msword',
+        'docm' => 'application/vnd.ms-word.template.macroEnabled.12',
+        'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+        'dot' => 'application/msword',
+        'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
+        'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+        'dp' => 'application/vnd.osgi.dp',
+        'dpg' => 'application/vnd.dpgraph',
+        'dpx' => 'image/dpx',
+        'dra' => 'audio/vnd.dra',
+        'drle' => 'image/dicom-rle',
+        'dsc' => 'text/prs.lines.tag',
+        'dssc' => 'application/dssc+der',
+        'dtb' => 'application/x-dtbook+xml',
+        'dtd' => 'application/xml-dtd',
+        'dts' => 'audio/vnd.dts',
+        'dtshd' => 'audio/vnd.dts.hd',
+        'dump' => 'application/octet-stream',
+        'dvb' => 'video/vnd.dvb.file',
+        'dvi' => 'application/x-dvi',
+        'dwd' => 'application/atsc-dwd+xml',
+        'dwf' => 'model/vnd.dwf',
+        'dwg' => 'image/vnd.dwg',
+        'dxf' => 'image/vnd.dxf',
+        'dxp' => 'application/vnd.spotfire.dxp',
+        'dxr' => 'application/x-director',
+        'ear' => 'application/java-archive',
+        'ecelp4800' => 'audio/vnd.nuera.ecelp4800',
+        'ecelp7470' => 'audio/vnd.nuera.ecelp7470',
+        'ecelp9600' => 'audio/vnd.nuera.ecelp9600',
+        'ecma' => 'application/ecmascript',
+        'edm' => 'application/vnd.novadigm.edm',
+        'edx' => 'application/vnd.novadigm.edx',
+        'efif' => 'application/vnd.picsel',
+        'ei6' => 'application/vnd.pg.osasli',
+        'elc' => 'application/octet-stream',
+        'emf' => 'image/emf',
+        'eml' => 'message/rfc822',
+        'emma' => 'application/emma+xml',
+        'emotionml' => 'application/emotionml+xml',
+        'emz' => 'application/x-msmetafile',
+        'eol' => 'audio/vnd.digital-winds',
+        'eot' => 'application/vnd.ms-fontobject',
+        'eps' => 'application/postscript',
+        'epub' => 'application/epub+zip',
+        'es3' => 'application/vnd.eszigno3+xml',
+        'esa' => 'application/vnd.osgi.subsystem',
+        'esf' => 'application/vnd.epson.esf',
+        'et3' => 'application/vnd.eszigno3+xml',
+        'etx' => 'text/x-setext',
+        'eva' => 'application/x-eva',
+        'evy' => 'application/x-envoy',
+        'exe' => 'application/octet-stream',
+        'exi' => 'application/exi',
+        'exp' => 'application/express',
+        'exr' => 'image/aces',
+        'ext' => 'application/vnd.novadigm.ext',
+        'ez' => 'application/andrew-inset',
+        'ez2' => 'application/vnd.ezpix-album',
+        'ez3' => 'application/vnd.ezpix-package',
+        'f' => 'text/x-fortran',
+        'f4v' => 'video/mp4',
+        'f77' => 'text/x-fortran',
+        'f90' => 'text/x-fortran',
+        'fbs' => 'image/vnd.fastbidsheet',
+        'fcdt' => 'application/vnd.adobe.formscentral.fcdt',
+        'fcs' => 'application/vnd.isac.fcs',
+        'fdf' => 'application/vnd.fdf',
+        'fdt' => 'application/fdt+xml',
+        'fe_launch' => 'application/vnd.denovo.fcselayout-link',
+        'fg5' => 'application/vnd.fujitsu.oasysgp',
+        'fgd' => 'application/x-director',
+        'fh' => 'image/x-freehand',
+        'fh4' => 'image/x-freehand',
+        'fh5' => 'image/x-freehand',
+        'fh7' => 'image/x-freehand',
+        'fhc' => 'image/x-freehand',
+        'fig' => 'application/x-xfig',
+        'fits' => 'image/fits',
+        'flac' => 'audio/x-flac',
+        'fli' => 'video/x-fli',
+        'flo' => 'application/vnd.micrografx.flo',
+        'flv' => 'video/x-flv',
+        'flw' => 'application/vnd.kde.kivio',
+        'flx' => 'text/vnd.fmi.flexstor',
+        'fly' => 'text/vnd.fly',
+        'fm' => 'application/vnd.framemaker',
+        'fnc' => 'application/vnd.frogans.fnc',
+        'fo' => 'application/vnd.software602.filler.form+xml',
+        'for' => 'text/x-fortran',
+        'fpx' => 'image/vnd.fpx',
+        'frame' => 'application/vnd.framemaker',
+        'fsc' => 'application/vnd.fsc.weblaunch',
+        'fst' => 'image/vnd.fst',
+        'ftc' => 'application/vnd.fluxtime.clip',
+        'fti' => 'application/vnd.anser-web-funds-transfer-initiation',
+        'fvt' => 'video/vnd.fvt',
+        'fxp' => 'application/vnd.adobe.fxp',
+        'fxpl' => 'application/vnd.adobe.fxp',
+        'fzs' => 'application/vnd.fuzzysheet',
+        'g2w' => 'application/vnd.geoplan',
+        'g3' => 'image/g3fax',
+        'g3w' => 'application/vnd.geospace',
+        'gac' => 'application/vnd.groove-account',
+        'gam' => 'application/x-tads',
+        'gbr' => 'application/rpki-ghostbusters',
+        'gca' => 'application/x-gca-compressed',
+        'gdl' => 'model/vnd.gdl',
+        'gdoc' => 'application/vnd.google-apps.document',
+        'ged' => 'text/vnd.familysearch.gedcom',
+        'geo' => 'application/vnd.dynageo',
+        'geojson' => 'application/geo+json',
+        'gex' => 'application/vnd.geometry-explorer',
+        'ggb' => 'application/vnd.geogebra.file',
+        'ggt' => 'application/vnd.geogebra.tool',
+        'ghf' => 'application/vnd.groove-help',
+        'gif' => 'image/gif',
+        'gim' => 'application/vnd.groove-identity-message',
+        'glb' => 'model/gltf-binary',
+        'gltf' => 'model/gltf+json',
+        'gml' => 'application/gml+xml',
+        'gmx' => 'application/vnd.gmx',
+        'gnumeric' => 'application/x-gnumeric',
+        'gpg' => 'application/gpg-keys',
+        'gph' => 'application/vnd.flographit',
+        'gpx' => 'application/gpx+xml',
+        'gqf' => 'application/vnd.grafeq',
+        'gqs' => 'application/vnd.grafeq',
+        'gram' => 'application/srgs',
+        'gramps' => 'application/x-gramps-xml',
+        'gre' => 'application/vnd.geometry-explorer',
+        'grv' => 'application/vnd.groove-injector',
+        'grxml' => 'application/srgs+xml',
+        'gsf' => 'application/x-font-ghostscript',
+        'gsheet' => 'application/vnd.google-apps.spreadsheet',
+        'gslides' => 'application/vnd.google-apps.presentation',
+        'gtar' => 'application/x-gtar',
+        'gtm' => 'application/vnd.groove-tool-message',
+        'gtw' => 'model/vnd.gtw',
+        'gv' => 'text/vnd.graphviz',
+        'gxf' => 'application/gxf',
+        'gxt' => 'application/vnd.geonext',
+        'gz' => 'application/gzip',
+        'gzip' => 'application/gzip',
+        'h' => 'text/x-c',
+        'h261' => 'video/h261',
+        'h263' => 'video/h263',
+        'h264' => 'video/h264',
+        'hal' => 'application/vnd.hal+xml',
+        'hbci' => 'application/vnd.hbci',
+        'hbs' => 'text/x-handlebars-template',
+        'hdd' => 'application/x-virtualbox-hdd',
+        'hdf' => 'application/x-hdf',
+        'heic' => 'image/heic',
+        'heics' => 'image/heic-sequence',
+        'heif' => 'image/heif',
+        'heifs' => 'image/heif-sequence',
+        'hej2' => 'image/hej2k',
+        'held' => 'application/atsc-held+xml',
+        'hh' => 'text/x-c',
+        'hjson' => 'application/hjson',
+        'hlp' => 'application/winhlp',
+        'hpgl' => 'application/vnd.hp-hpgl',
+        'hpid' => 'application/vnd.hp-hpid',
+        'hps' => 'application/vnd.hp-hps',
+        'hqx' => 'application/mac-binhex40',
+        'hsj2' => 'image/hsj2',
+        'htc' => 'text/x-component',
+        'htke' => 'application/vnd.kenameaapp',
+        'htm' => 'text/html',
+        'html' => 'text/html',
+        'hvd' => 'application/vnd.yamaha.hv-dic',
+        'hvp' => 'application/vnd.yamaha.hv-voice',
+        'hvs' => 'application/vnd.yamaha.hv-script',
+        'i2g' => 'application/vnd.intergeo',
+        'icc' => 'application/vnd.iccprofile',
+        'ice' => 'x-conference/x-cooltalk',
+        'icm' => 'application/vnd.iccprofile',
+        'ico' => 'image/x-icon',
+        'ics' => 'text/calendar',
+        'ief' => 'image/ief',
+        'ifb' => 'text/calendar',
+        'ifm' => 'application/vnd.shana.informed.formdata',
+        'iges' => 'model/iges',
+        'igl' => 'application/vnd.igloader',
+        'igm' => 'application/vnd.insors.igm',
+        'igs' => 'model/iges',
+        'igx' => 'application/vnd.micrografx.igx',
+        'iif' => 'application/vnd.shana.informed.interchange',
+        'img' => 'application/octet-stream',
+        'imp' => 'application/vnd.accpac.simply.imp',
+        'ims' => 'application/vnd.ms-ims',
+        'in' => 'text/plain',
+        'ini' => 'text/plain',
+        'ink' => 'application/inkml+xml',
+        'inkml' => 'application/inkml+xml',
+        'install' => 'application/x-install-instructions',
+        'iota' => 'application/vnd.astraea-software.iota',
+        'ipfix' => 'application/ipfix',
+        'ipk' => 'application/vnd.shana.informed.package',
+        'irm' => 'application/vnd.ibm.rights-management',
+        'irp' => 'application/vnd.irepository.package+xml',
+        'iso' => 'application/x-iso9660-image',
+        'itp' => 'application/vnd.shana.informed.formtemplate',
+        'its' => 'application/its+xml',
+        'ivp' => 'application/vnd.immervision-ivp',
+        'ivu' => 'application/vnd.immervision-ivu',
+        'jad' => 'text/vnd.sun.j2me.app-descriptor',
+        'jade' => 'text/jade',
+        'jam' => 'application/vnd.jam',
+        'jar' => 'application/java-archive',
+        'jardiff' => 'application/x-java-archive-diff',
+        'java' => 'text/x-java-source',
+        'jhc' => 'image/jphc',
+        'jisp' => 'application/vnd.jisp',
+        'jls' => 'image/jls',
+        'jlt' => 'application/vnd.hp-jlyt',
+        'jng' => 'image/x-jng',
+        'jnlp' => 'application/x-java-jnlp-file',
+        'joda' => 'application/vnd.joost.joda-archive',
+        'jp2' => 'image/jp2',
+        'jpe' => 'image/jpeg',
+        'jpeg' => 'image/jpeg',
+        'jpf' => 'image/jpx',
+        'jpg' => 'image/jpeg',
+        'jpg2' => 'image/jp2',
+        'jpgm' => 'video/jpm',
+        'jpgv' => 'video/jpeg',
+        'jph' => 'image/jph',
+        'jpm' => 'video/jpm',
+        'jpx' => 'image/jpx',
+        'js' => 'application/javascript',
+        'json' => 'application/json',
+        'json5' => 'application/json5',
+        'jsonld' => 'application/ld+json',
+        'jsonml' => 'application/jsonml+json',
+        'jsx' => 'text/jsx',
+        'jt' => 'model/jt',
+        'jxr' => 'image/jxr',
+        'jxra' => 'image/jxra',
+        'jxrs' => 'image/jxrs',
+        'jxs' => 'image/jxs',
+        'jxsc' => 'image/jxsc',
+        'jxsi' => 'image/jxsi',
+        'jxss' => 'image/jxss',
+        'kar' => 'audio/midi',
+        'karbon' => 'application/vnd.kde.karbon',
+        'kdb' => 'application/octet-stream',
+        'kdbx' => 'application/x-keepass2',
+        'key' => 'application/x-iwork-keynote-sffkey',
+        'kfo' => 'application/vnd.kde.kformula',
+        'kia' => 'application/vnd.kidspiration',
+        'kml' => 'application/vnd.google-earth.kml+xml',
+        'kmz' => 'application/vnd.google-earth.kmz',
+        'kne' => 'application/vnd.kinar',
+        'knp' => 'application/vnd.kinar',
+        'kon' => 'application/vnd.kde.kontour',
+        'kpr' => 'application/vnd.kde.kpresenter',
+        'kpt' => 'application/vnd.kde.kpresenter',
+        'kpxx' => 'application/vnd.ds-keypoint',
+        'ksp' => 'application/vnd.kde.kspread',
+        'ktr' => 'application/vnd.kahootz',
+        'ktx' => 'image/ktx',
+        'ktx2' => 'image/ktx2',
+        'ktz' => 'application/vnd.kahootz',
+        'kwd' => 'application/vnd.kde.kword',
+        'kwt' => 'application/vnd.kde.kword',
+        'lasxml' => 'application/vnd.las.las+xml',
+        'latex' => 'application/x-latex',
+        'lbd' => 'application/vnd.llamagraphics.life-balance.desktop',
+        'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml',
+        'les' => 'application/vnd.hhe.lesson-player',
+        'less' => 'text/less',
+        'lgr' => 'application/lgr+xml',
+        'lha' => 'application/octet-stream',
+        'link66' => 'application/vnd.route66.link66+xml',
+        'list' => 'text/plain',
+        'list3820' => 'application/vnd.ibm.modcap',
+        'listafp' => 'application/vnd.ibm.modcap',
+        'litcoffee' => 'text/coffeescript',
+        'lnk' => 'application/x-ms-shortcut',
+        'log' => 'text/plain',
+        'lostxml' => 'application/lost+xml',
+        'lrf' => 'application/octet-stream',
+        'lrm' => 'application/vnd.ms-lrm',
+        'ltf' => 'application/vnd.frogans.ltf',
+        'lua' => 'text/x-lua',
+        'luac' => 'application/x-lua-bytecode',
+        'lvp' => 'audio/vnd.lucent.voice',
+        'lwp' => 'application/vnd.lotus-wordpro',
+        'lzh' => 'application/octet-stream',
+        'm1v' => 'video/mpeg',
+        'm2a' => 'audio/mpeg',
+        'm2v' => 'video/mpeg',
+        'm3a' => 'audio/mpeg',
+        'm3u' => 'text/plain',
+        'm3u8' => 'application/vnd.apple.mpegurl',
+        'm4a' => 'audio/x-m4a',
+        'm4p' => 'application/mp4',
+        'm4s' => 'video/iso.segment',
+        'm4u' => 'application/vnd.mpegurl',
+        'm4v' => 'video/x-m4v',
+        'm13' => 'application/x-msmediaview',
+        'm14' => 'application/x-msmediaview',
+        'm21' => 'application/mp21',
+        'ma' => 'application/mathematica',
+        'mads' => 'application/mads+xml',
+        'maei' => 'application/mmt-aei+xml',
+        'mag' => 'application/vnd.ecowin.chart',
+        'maker' => 'application/vnd.framemaker',
+        'man' => 'text/troff',
+        'manifest' => 'text/cache-manifest',
+        'map' => 'application/json',
+        'mar' => 'application/octet-stream',
+        'markdown' => 'text/markdown',
+        'mathml' => 'application/mathml+xml',
+        'mb' => 'application/mathematica',
+        'mbk' => 'application/vnd.mobius.mbk',
+        'mbox' => 'application/mbox',
+        'mc1' => 'application/vnd.medcalcdata',
+        'mcd' => 'application/vnd.mcd',
+        'mcurl' => 'text/vnd.curl.mcurl',
+        'md' => 'text/markdown',
+        'mdb' => 'application/x-msaccess',
+        'mdi' => 'image/vnd.ms-modi',
+        'mdx' => 'text/mdx',
+        'me' => 'text/troff',
+        'mesh' => 'model/mesh',
+        'meta4' => 'application/metalink4+xml',
+        'metalink' => 'application/metalink+xml',
+        'mets' => 'application/mets+xml',
+        'mfm' => 'application/vnd.mfmp',
+        'mft' => 'application/rpki-manifest',
+        'mgp' => 'application/vnd.osgeo.mapguide.package',
+        'mgz' => 'application/vnd.proteus.magazine',
+        'mid' => 'audio/midi',
+        'midi' => 'audio/midi',
+        'mie' => 'application/x-mie',
+        'mif' => 'application/vnd.mif',
+        'mime' => 'message/rfc822',
+        'mj2' => 'video/mj2',
+        'mjp2' => 'video/mj2',
+        'mjs' => 'text/javascript',
+        'mk3d' => 'video/x-matroska',
+        'mka' => 'audio/x-matroska',
+        'mkd' => 'text/x-markdown',
+        'mks' => 'video/x-matroska',
+        'mkv' => 'video/x-matroska',
+        'mlp' => 'application/vnd.dolby.mlp',
+        'mmd' => 'application/vnd.chipnuts.karaoke-mmd',
+        'mmf' => 'application/vnd.smaf',
+        'mml' => 'text/mathml',
+        'mmr' => 'image/vnd.fujixerox.edmics-mmr',
+        'mng' => 'video/x-mng',
+        'mny' => 'application/x-msmoney',
+        'mobi' => 'application/x-mobipocket-ebook',
+        'mods' => 'application/mods+xml',
+        'mov' => 'video/quicktime',
+        'movie' => 'video/x-sgi-movie',
+        'mp2' => 'audio/mpeg',
+        'mp2a' => 'audio/mpeg',
+        'mp3' => 'audio/mpeg',
+        'mp4' => 'video/mp4',
+        'mp4a' => 'audio/mp4',
+        'mp4s' => 'application/mp4',
+        'mp4v' => 'video/mp4',
+        'mp21' => 'application/mp21',
+        'mpc' => 'application/vnd.mophun.certificate',
+        'mpd' => 'application/dash+xml',
+        'mpe' => 'video/mpeg',
+        'mpeg' => 'video/mpeg',
+        'mpf' => 'application/media-policy-dataset+xml',
+        'mpg' => 'video/mpeg',
+        'mpg4' => 'video/mp4',
+        'mpga' => 'audio/mpeg',
+        'mpkg' => 'application/vnd.apple.installer+xml',
+        'mpm' => 'application/vnd.blueice.multipass',
+        'mpn' => 'application/vnd.mophun.application',
+        'mpp' => 'application/vnd.ms-project',
+        'mpt' => 'application/vnd.ms-project',
+        'mpy' => 'application/vnd.ibm.minipay',
+        'mqy' => 'application/vnd.mobius.mqy',
+        'mrc' => 'application/marc',
+        'mrcx' => 'application/marcxml+xml',
+        'ms' => 'text/troff',
+        'mscml' => 'application/mediaservercontrol+xml',
+        'mseed' => 'application/vnd.fdsn.mseed',
+        'mseq' => 'application/vnd.mseq',
+        'msf' => 'application/vnd.epson.msf',
+        'msg' => 'application/vnd.ms-outlook',
+        'msh' => 'model/mesh',
+        'msi' => 'application/x-msdownload',
+        'msix' => 'application/msix',
+        'msixbundle' => 'application/msixbundle',
+        'msl' => 'application/vnd.mobius.msl',
+        'msm' => 'application/octet-stream',
+        'msp' => 'application/octet-stream',
+        'msty' => 'application/vnd.muvee.style',
+        'mtl' => 'model/mtl',
+        'mts' => 'model/vnd.mts',
+        'mus' => 'application/vnd.musician',
+        'musd' => 'application/mmt-usd+xml',
+        'musicxml' => 'application/vnd.recordare.musicxml+xml',
+        'mvb' => 'application/x-msmediaview',
+        'mvt' => 'application/vnd.mapbox-vector-tile',
+        'mwf' => 'application/vnd.mfer',
+        'mxf' => 'application/mxf',
+        'mxl' => 'application/vnd.recordare.musicxml',
+        'mxmf' => 'audio/mobile-xmf',
+        'mxml' => 'application/xv+xml',
+        'mxs' => 'application/vnd.triscape.mxs',
+        'mxu' => 'video/vnd.mpegurl',
+        'n-gage' => 'application/vnd.nokia.n-gage.symbian.install',
+        'n3' => 'text/n3',
+        'nb' => 'application/mathematica',
+        'nbp' => 'application/vnd.wolfram.player',
+        'nc' => 'application/x-netcdf',
+        'ncx' => 'application/x-dtbncx+xml',
+        'nfo' => 'text/x-nfo',
+        'ngdat' => 'application/vnd.nokia.n-gage.data',
+        'nitf' => 'application/vnd.nitf',
+        'nlu' => 'application/vnd.neurolanguage.nlu',
+        'nml' => 'application/vnd.enliven',
+        'nnd' => 'application/vnd.noblenet-directory',
+        'nns' => 'application/vnd.noblenet-sealer',
+        'nnw' => 'application/vnd.noblenet-web',
+        'npx' => 'image/vnd.net-fpx',
+        'nq' => 'application/n-quads',
+        'nsc' => 'application/x-conference',
+        'nsf' => 'application/vnd.lotus-notes',
+        'nt' => 'application/n-triples',
+        'ntf' => 'application/vnd.nitf',
+        'numbers' => 'application/x-iwork-numbers-sffnumbers',
+        'nzb' => 'application/x-nzb',
+        'oa2' => 'application/vnd.fujitsu.oasys2',
+        'oa3' => 'application/vnd.fujitsu.oasys3',
+        'oas' => 'application/vnd.fujitsu.oasys',
+        'obd' => 'application/x-msbinder',
+        'obgx' => 'application/vnd.openblox.game+xml',
+        'obj' => 'model/obj',
+        'oda' => 'application/oda',
+        'odb' => 'application/vnd.oasis.opendocument.database',
+        'odc' => 'application/vnd.oasis.opendocument.chart',
+        'odf' => 'application/vnd.oasis.opendocument.formula',
+        'odft' => 'application/vnd.oasis.opendocument.formula-template',
+        'odg' => 'application/vnd.oasis.opendocument.graphics',
+        'odi' => 'application/vnd.oasis.opendocument.image',
+        'odm' => 'application/vnd.oasis.opendocument.text-master',
+        'odp' => 'application/vnd.oasis.opendocument.presentation',
+        'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+        'odt' => 'application/vnd.oasis.opendocument.text',
+        'oga' => 'audio/ogg',
+        'ogex' => 'model/vnd.opengex',
+        'ogg' => 'audio/ogg',
+        'ogv' => 'video/ogg',
+        'ogx' => 'application/ogg',
+        'omdoc' => 'application/omdoc+xml',
+        'onepkg' => 'application/onenote',
+        'onetmp' => 'application/onenote',
+        'onetoc' => 'application/onenote',
+        'onetoc2' => 'application/onenote',
+        'opf' => 'application/oebps-package+xml',
+        'opml' => 'text/x-opml',
+        'oprc' => 'application/vnd.palm',
+        'opus' => 'audio/ogg',
+        'org' => 'text/x-org',
+        'osf' => 'application/vnd.yamaha.openscoreformat',
+        'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml',
+        'osm' => 'application/vnd.openstreetmap.data+xml',
+        'otc' => 'application/vnd.oasis.opendocument.chart-template',
+        'otf' => 'font/otf',
+        'otg' => 'application/vnd.oasis.opendocument.graphics-template',
+        'oth' => 'application/vnd.oasis.opendocument.text-web',
+        'oti' => 'application/vnd.oasis.opendocument.image-template',
+        'otp' => 'application/vnd.oasis.opendocument.presentation-template',
+        'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
+        'ott' => 'application/vnd.oasis.opendocument.text-template',
+        'ova' => 'application/x-virtualbox-ova',
+        'ovf' => 'application/x-virtualbox-ovf',
+        'owl' => 'application/rdf+xml',
+        'oxps' => 'application/oxps',
+        'oxt' => 'application/vnd.openofficeorg.extension',
+        'p' => 'text/x-pascal',
+        'p7a' => 'application/x-pkcs7-signature',
+        'p7b' => 'application/x-pkcs7-certificates',
+        'p7c' => 'application/pkcs7-mime',
+        'p7m' => 'application/pkcs7-mime',
+        'p7r' => 'application/x-pkcs7-certreqresp',
+        'p7s' => 'application/pkcs7-signature',
+        'p8' => 'application/pkcs8',
+        'p10' => 'application/x-pkcs10',
+        'p12' => 'application/x-pkcs12',
+        'pac' => 'application/x-ns-proxy-autoconfig',
+        'pages' => 'application/x-iwork-pages-sffpages',
+        'pas' => 'text/x-pascal',
+        'paw' => 'application/vnd.pawaafile',
+        'pbd' => 'application/vnd.powerbuilder6',
+        'pbm' => 'image/x-portable-bitmap',
+        'pcap' => 'application/vnd.tcpdump.pcap',
+        'pcf' => 'application/x-font-pcf',
+        'pcl' => 'application/vnd.hp-pcl',
+        'pclxl' => 'application/vnd.hp-pclxl',
+        'pct' => 'image/x-pict',
+        'pcurl' => 'application/vnd.curl.pcurl',
+        'pcx' => 'image/x-pcx',
+        'pdb' => 'application/x-pilot',
+        'pde' => 'text/x-processing',
+        'pdf' => 'application/pdf',
+        'pem' => 'application/x-x509-user-cert',
+        'pfa' => 'application/x-font-type1',
+        'pfb' => 'application/x-font-type1',
+        'pfm' => 'application/x-font-type1',
+        'pfr' => 'application/font-tdpfr',
+        'pfx' => 'application/x-pkcs12',
+        'pgm' => 'image/x-portable-graymap',
+        'pgn' => 'application/x-chess-pgn',
+        'pgp' => 'application/pgp',
+        'phar' => 'application/octet-stream',
+        'php' => 'application/x-httpd-php',
+        'php3' => 'application/x-httpd-php',
+        'php4' => 'application/x-httpd-php',
+        'phps' => 'application/x-httpd-php-source',
+        'phtml' => 'application/x-httpd-php',
+        'pic' => 'image/x-pict',
+        'pkg' => 'application/octet-stream',
+        'pki' => 'application/pkixcmp',
+        'pkipath' => 'application/pkix-pkipath',
+        'pkpass' => 'application/vnd.apple.pkpass',
+        'pl' => 'application/x-perl',
+        'plb' => 'application/vnd.3gpp.pic-bw-large',
+        'plc' => 'application/vnd.mobius.plc',
+        'plf' => 'application/vnd.pocketlearn',
+        'pls' => 'application/pls+xml',
+        'pm' => 'application/x-perl',
+        'pml' => 'application/vnd.ctc-posml',
+        'png' => 'image/png',
+        'pnm' => 'image/x-portable-anymap',
+        'portpkg' => 'application/vnd.macports.portpkg',
+        'pot' => 'application/vnd.ms-powerpoint',
+        'potm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
+        'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
+        'ppa' => 'application/vnd.ms-powerpoint',
+        'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
+        'ppd' => 'application/vnd.cups-ppd',
+        'ppm' => 'image/x-portable-pixmap',
+        'pps' => 'application/vnd.ms-powerpoint',
+        'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
+        'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+        'ppt' => 'application/powerpoint',
+        'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
+        'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+        'pqa' => 'application/vnd.palm',
+        'prc' => 'model/prc',
+        'pre' => 'application/vnd.lotus-freelance',
+        'prf' => 'application/pics-rules',
+        'provx' => 'application/provenance+xml',
+        'ps' => 'application/postscript',
+        'psb' => 'application/vnd.3gpp.pic-bw-small',
+        'psd' => 'application/x-photoshop',
+        'psf' => 'application/x-font-linux-psf',
+        'pskcxml' => 'application/pskc+xml',
+        'pti' => 'image/prs.pti',
+        'ptid' => 'application/vnd.pvi.ptid1',
+        'pub' => 'application/x-mspublisher',
+        'pvb' => 'application/vnd.3gpp.pic-bw-var',
+        'pwn' => 'application/vnd.3m.post-it-notes',
+        'pya' => 'audio/vnd.ms-playready.media.pya',
+        'pyo' => 'model/vnd.pytha.pyox',
+        'pyox' => 'model/vnd.pytha.pyox',
+        'pyv' => 'video/vnd.ms-playready.media.pyv',
+        'qam' => 'application/vnd.epson.quickanime',
+        'qbo' => 'application/vnd.intu.qbo',
+        'qfx' => 'application/vnd.intu.qfx',
+        'qps' => 'application/vnd.publishare-delta-tree',
+        'qt' => 'video/quicktime',
+        'qwd' => 'application/vnd.quark.quarkxpress',
+        'qwt' => 'application/vnd.quark.quarkxpress',
+        'qxb' => 'application/vnd.quark.quarkxpress',
+        'qxd' => 'application/vnd.quark.quarkxpress',
+        'qxl' => 'application/vnd.quark.quarkxpress',
+        'qxt' => 'application/vnd.quark.quarkxpress',
+        'ra' => 'audio/x-realaudio',
+        'ram' => 'audio/x-pn-realaudio',
+        'raml' => 'application/raml+yaml',
+        'rapd' => 'application/route-apd+xml',
+        'rar' => 'application/x-rar',
+        'ras' => 'image/x-cmu-raster',
+        'rcprofile' => 'application/vnd.ipunplugged.rcprofile',
+        'rdf' => 'application/rdf+xml',
+        'rdz' => 'application/vnd.data-vision.rdz',
+        'relo' => 'application/p2p-overlay+xml',
+        'rep' => 'application/vnd.businessobjects',
+        'res' => 'application/x-dtbresource+xml',
+        'rgb' => 'image/x-rgb',
+        'rif' => 'application/reginfo+xml',
+        'rip' => 'audio/vnd.rip',
+        'ris' => 'application/x-research-info-systems',
+        'rl' => 'application/resource-lists+xml',
+        'rlc' => 'image/vnd.fujixerox.edmics-rlc',
+        'rld' => 'application/resource-lists-diff+xml',
+        'rm' => 'audio/x-pn-realaudio',
+        'rmi' => 'audio/midi',
+        'rmp' => 'audio/x-pn-realaudio-plugin',
+        'rms' => 'application/vnd.jcp.javame.midlet-rms',
+        'rmvb' => 'application/vnd.rn-realmedia-vbr',
+        'rnc' => 'application/relax-ng-compact-syntax',
+        'rng' => 'application/xml',
+        'roa' => 'application/rpki-roa',
+        'roff' => 'text/troff',
+        'rp9' => 'application/vnd.cloanto.rp9',
+        'rpm' => 'audio/x-pn-realaudio-plugin',
+        'rpss' => 'application/vnd.nokia.radio-presets',
+        'rpst' => 'application/vnd.nokia.radio-preset',
+        'rq' => 'application/sparql-query',
+        'rs' => 'application/rls-services+xml',
+        'rsa' => 'application/x-pkcs7',
+        'rsat' => 'application/atsc-rsat+xml',
+        'rsd' => 'application/rsd+xml',
+        'rsheet' => 'application/urc-ressheet+xml',
+        'rss' => 'application/rss+xml',
+        'rtf' => 'text/rtf',
+        'rtx' => 'text/richtext',
+        'run' => 'application/x-makeself',
+        'rusd' => 'application/route-usd+xml',
+        'rv' => 'video/vnd.rn-realvideo',
+        's' => 'text/x-asm',
+        's3m' => 'audio/s3m',
+        'saf' => 'application/vnd.yamaha.smaf-audio',
+        'sass' => 'text/x-sass',
+        'sbml' => 'application/sbml+xml',
+        'sc' => 'application/vnd.ibm.secure-container',
+        'scd' => 'application/x-msschedule',
+        'scm' => 'application/vnd.lotus-screencam',
+        'scq' => 'application/scvp-cv-request',
+        'scs' => 'application/scvp-cv-response',
+        'scss' => 'text/x-scss',
+        'scurl' => 'text/vnd.curl.scurl',
+        'sda' => 'application/vnd.stardivision.draw',
+        'sdc' => 'application/vnd.stardivision.calc',
+        'sdd' => 'application/vnd.stardivision.impress',
+        'sdkd' => 'application/vnd.solent.sdkm+xml',
+        'sdkm' => 'application/vnd.solent.sdkm+xml',
+        'sdp' => 'application/sdp',
+        'sdw' => 'application/vnd.stardivision.writer',
+        'sea' => 'application/octet-stream',
+        'see' => 'application/vnd.seemail',
+        'seed' => 'application/vnd.fdsn.seed',
+        'sema' => 'application/vnd.sema',
+        'semd' => 'application/vnd.semd',
+        'semf' => 'application/vnd.semf',
+        'senmlx' => 'application/senml+xml',
+        'sensmlx' => 'application/sensml+xml',
+        'ser' => 'application/java-serialized-object',
+        'setpay' => 'application/set-payment-initiation',
+        'setreg' => 'application/set-registration-initiation',
+        'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data',
+        'sfs' => 'application/vnd.spotfire.sfs',
+        'sfv' => 'text/x-sfv',
+        'sgi' => 'image/sgi',
+        'sgl' => 'application/vnd.stardivision.writer-global',
+        'sgm' => 'text/sgml',
+        'sgml' => 'text/sgml',
+        'sh' => 'application/x-sh',
+        'shar' => 'application/x-shar',
+        'shex' => 'text/shex',
+        'shf' => 'application/shf+xml',
+        'shtml' => 'text/html',
+        'sid' => 'image/x-mrsid-image',
+        'sieve' => 'application/sieve',
+        'sig' => 'application/pgp-signature',
+        'sil' => 'audio/silk',
+        'silo' => 'model/mesh',
+        'sis' => 'application/vnd.symbian.install',
+        'sisx' => 'application/vnd.symbian.install',
+        'sit' => 'application/x-stuffit',
+        'sitx' => 'application/x-stuffitx',
+        'siv' => 'application/sieve',
+        'skd' => 'application/vnd.koan',
+        'skm' => 'application/vnd.koan',
+        'skp' => 'application/vnd.koan',
+        'skt' => 'application/vnd.koan',
+        'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12',
+        'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
+        'slim' => 'text/slim',
+        'slm' => 'text/slim',
+        'sls' => 'application/route-s-tsid+xml',
+        'slt' => 'application/vnd.epson.salt',
+        'sm' => 'application/vnd.stepmania.stepchart',
+        'smf' => 'application/vnd.stardivision.math',
+        'smi' => 'application/smil',
+        'smil' => 'application/smil',
+        'smv' => 'video/x-smv',
+        'smzip' => 'application/vnd.stepmania.package',
+        'snd' => 'audio/basic',
+        'snf' => 'application/x-font-snf',
+        'so' => 'application/octet-stream',
+        'spc' => 'application/x-pkcs7-certificates',
+        'spdx' => 'text/spdx',
+        'spf' => 'application/vnd.yamaha.smaf-phrase',
+        'spl' => 'application/x-futuresplash',
+        'spot' => 'text/vnd.in3d.spot',
+        'spp' => 'application/scvp-vp-response',
+        'spq' => 'application/scvp-vp-request',
+        'spx' => 'audio/ogg',
+        'sql' => 'application/x-sql',
+        'src' => 'application/x-wais-source',
+        'srt' => 'application/x-subrip',
+        'sru' => 'application/sru+xml',
+        'srx' => 'application/sparql-results+xml',
+        'ssdl' => 'application/ssdl+xml',
+        'sse' => 'application/vnd.kodak-descriptor',
+        'ssf' => 'application/vnd.epson.ssf',
+        'ssml' => 'application/ssml+xml',
+        'sst' => 'application/octet-stream',
+        'st' => 'application/vnd.sailingtracker.track',
+        'stc' => 'application/vnd.sun.xml.calc.template',
+        'std' => 'application/vnd.sun.xml.draw.template',
+        'step' => 'application/STEP',
+        'stf' => 'application/vnd.wt.stf',
+        'sti' => 'application/vnd.sun.xml.impress.template',
+        'stk' => 'application/hyperstudio',
+        'stl' => 'model/stl',
+        'stp' => 'application/STEP',
+        'stpx' => 'model/step+xml',
+        'stpxz' => 'model/step-xml+zip',
+        'stpz' => 'model/step+zip',
+        'str' => 'application/vnd.pg.format',
+        'stw' => 'application/vnd.sun.xml.writer.template',
+        'styl' => 'text/stylus',
+        'stylus' => 'text/stylus',
+        'sub' => 'text/vnd.dvb.subtitle',
+        'sus' => 'application/vnd.sus-calendar',
+        'susp' => 'application/vnd.sus-calendar',
+        'sv4cpio' => 'application/x-sv4cpio',
+        'sv4crc' => 'application/x-sv4crc',
+        'svc' => 'application/vnd.dvb.service',
+        'svd' => 'application/vnd.svd',
+        'svg' => 'image/svg+xml',
+        'svgz' => 'image/svg+xml',
+        'swa' => 'application/x-director',
+        'swf' => 'application/x-shockwave-flash',
+        'swi' => 'application/vnd.aristanetworks.swi',
+        'swidtag' => 'application/swid+xml',
+        'sxc' => 'application/vnd.sun.xml.calc',
+        'sxd' => 'application/vnd.sun.xml.draw',
+        'sxg' => 'application/vnd.sun.xml.writer.global',
+        'sxi' => 'application/vnd.sun.xml.impress',
+        'sxm' => 'application/vnd.sun.xml.math',
+        'sxw' => 'application/vnd.sun.xml.writer',
+        't' => 'text/troff',
+        't3' => 'application/x-t3vm-image',
+        't38' => 'image/t38',
+        'taglet' => 'application/vnd.mynfc',
+        'tao' => 'application/vnd.tao.intent-module-archive',
+        'tap' => 'image/vnd.tencent.tap',
+        'tar' => 'application/x-tar',
+        'tcap' => 'application/vnd.3gpp2.tcap',
+        'tcl' => 'application/x-tcl',
+        'td' => 'application/urc-targetdesc+xml',
+        'teacher' => 'application/vnd.smart.teacher',
+        'tei' => 'application/tei+xml',
+        'teicorpus' => 'application/tei+xml',
+        'tex' => 'application/x-tex',
+        'texi' => 'application/x-texinfo',
+        'texinfo' => 'application/x-texinfo',
+        'text' => 'text/plain',
+        'tfi' => 'application/thraud+xml',
+        'tfm' => 'application/x-tex-tfm',
+        'tfx' => 'image/tiff-fx',
+        'tga' => 'image/x-tga',
+        'tgz' => 'application/x-tar',
+        'thmx' => 'application/vnd.ms-officetheme',
+        'tif' => 'image/tiff',
+        'tiff' => 'image/tiff',
+        'tk' => 'application/x-tcl',
+        'tmo' => 'application/vnd.tmobile-livetv',
+        'toml' => 'application/toml',
+        'torrent' => 'application/x-bittorrent',
+        'tpl' => 'application/vnd.groove-tool-template',
+        'tpt' => 'application/vnd.trid.tpt',
+        'tr' => 'text/troff',
+        'tra' => 'application/vnd.trueapp',
+        'trig' => 'application/trig',
+        'trm' => 'application/x-msterminal',
+        'ts' => 'video/mp2t',
+        'tsd' => 'application/timestamped-data',
+        'tsv' => 'text/tab-separated-values',
+        'ttc' => 'font/collection',
+        'ttf' => 'font/ttf',
+        'ttl' => 'text/turtle',
+        'ttml' => 'application/ttml+xml',
+        'twd' => 'application/vnd.simtech-mindmapper',
+        'twds' => 'application/vnd.simtech-mindmapper',
+        'txd' => 'application/vnd.genomatix.tuxedo',
+        'txf' => 'application/vnd.mobius.txf',
+        'txt' => 'text/plain',
+        'u3d' => 'model/u3d',
+        'u8dsn' => 'message/global-delivery-status',
+        'u8hdr' => 'message/global-headers',
+        'u8mdn' => 'message/global-disposition-notification',
+        'u8msg' => 'message/global',
+        'u32' => 'application/x-authorware-bin',
+        'ubj' => 'application/ubjson',
+        'udeb' => 'application/x-debian-package',
+        'ufd' => 'application/vnd.ufdl',
+        'ufdl' => 'application/vnd.ufdl',
+        'ulx' => 'application/x-glulx',
+        'umj' => 'application/vnd.umajin',
+        'unityweb' => 'application/vnd.unity',
+        'uo' => 'application/vnd.uoml+xml',
+        'uoml' => 'application/vnd.uoml+xml',
+        'uri' => 'text/uri-list',
+        'uris' => 'text/uri-list',
+        'urls' => 'text/uri-list',
+        'usda' => 'model/vnd.usda',
+        'usdz' => 'model/vnd.usdz+zip',
+        'ustar' => 'application/x-ustar',
+        'utz' => 'application/vnd.uiq.theme',
+        'uu' => 'text/x-uuencode',
+        'uva' => 'audio/vnd.dece.audio',
+        'uvd' => 'application/vnd.dece.data',
+        'uvf' => 'application/vnd.dece.data',
+        'uvg' => 'image/vnd.dece.graphic',
+        'uvh' => 'video/vnd.dece.hd',
+        'uvi' => 'image/vnd.dece.graphic',
+        'uvm' => 'video/vnd.dece.mobile',
+        'uvp' => 'video/vnd.dece.pd',
+        'uvs' => 'video/vnd.dece.sd',
+        'uvt' => 'application/vnd.dece.ttml+xml',
+        'uvu' => 'video/vnd.uvvu.mp4',
+        'uvv' => 'video/vnd.dece.video',
+        'uvva' => 'audio/vnd.dece.audio',
+        'uvvd' => 'application/vnd.dece.data',
+        'uvvf' => 'application/vnd.dece.data',
+        'uvvg' => 'image/vnd.dece.graphic',
+        'uvvh' => 'video/vnd.dece.hd',
+        'uvvi' => 'image/vnd.dece.graphic',
+        'uvvm' => 'video/vnd.dece.mobile',
+        'uvvp' => 'video/vnd.dece.pd',
+        'uvvs' => 'video/vnd.dece.sd',
+        'uvvt' => 'application/vnd.dece.ttml+xml',
+        'uvvu' => 'video/vnd.uvvu.mp4',
+        'uvvv' => 'video/vnd.dece.video',
+        'uvvx' => 'application/vnd.dece.unspecified',
+        'uvvz' => 'application/vnd.dece.zip',
+        'uvx' => 'application/vnd.dece.unspecified',
+        'uvz' => 'application/vnd.dece.zip',
+        'vbox' => 'application/x-virtualbox-vbox',
+        'vbox-extpack' => 'application/x-virtualbox-vbox-extpack',
+        'vcard' => 'text/vcard',
+        'vcd' => 'application/x-cdlink',
+        'vcf' => 'text/x-vcard',
+        'vcg' => 'application/vnd.groove-vcard',
+        'vcs' => 'text/x-vcalendar',
+        'vcx' => 'application/vnd.vcx',
+        'vdi' => 'application/x-virtualbox-vdi',
+        'vds' => 'model/vnd.sap.vds',
+        'vhd' => 'application/x-virtualbox-vhd',
+        'vis' => 'application/vnd.visionary',
+        'viv' => 'video/vnd.vivo',
+        'vlc' => 'application/videolan',
+        'vmdk' => 'application/x-virtualbox-vmdk',
+        'vob' => 'video/x-ms-vob',
+        'vor' => 'application/vnd.stardivision.writer',
+        'vox' => 'application/x-authorware-bin',
+        'vrml' => 'model/vrml',
+        'vsd' => 'application/vnd.visio',
+        'vsf' => 'application/vnd.vsf',
+        'vss' => 'application/vnd.visio',
+        'vst' => 'application/vnd.visio',
+        'vsw' => 'application/vnd.visio',
+        'vtf' => 'image/vnd.valve.source.texture',
+        'vtt' => 'text/vtt',
+        'vtu' => 'model/vnd.vtu',
+        'vxml' => 'application/voicexml+xml',
+        'w3d' => 'application/x-director',
+        'wad' => 'application/x-doom',
+        'wadl' => 'application/vnd.sun.wadl+xml',
+        'war' => 'application/java-archive',
+        'wasm' => 'application/wasm',
+        'wav' => 'audio/x-wav',
+        'wax' => 'audio/x-ms-wax',
+        'wbmp' => 'image/vnd.wap.wbmp',
+        'wbs' => 'application/vnd.criticaltools.wbs+xml',
+        'wbxml' => 'application/wbxml',
+        'wcm' => 'application/vnd.ms-works',
+        'wdb' => 'application/vnd.ms-works',
+        'wdp' => 'image/vnd.ms-photo',
+        'weba' => 'audio/webm',
+        'webapp' => 'application/x-web-app-manifest+json',
+        'webm' => 'video/webm',
+        'webmanifest' => 'application/manifest+json',
+        'webp' => 'image/webp',
+        'wg' => 'application/vnd.pmi.widget',
+        'wgsl' => 'text/wgsl',
+        'wgt' => 'application/widget',
+        'wif' => 'application/watcherinfo+xml',
+        'wks' => 'application/vnd.ms-works',
+        'wm' => 'video/x-ms-wm',
+        'wma' => 'audio/x-ms-wma',
+        'wmd' => 'application/x-ms-wmd',
+        'wmf' => 'image/wmf',
+        'wml' => 'text/vnd.wap.wml',
+        'wmlc' => 'application/wmlc',
+        'wmls' => 'text/vnd.wap.wmlscript',
+        'wmlsc' => 'application/vnd.wap.wmlscriptc',
+        'wmv' => 'video/x-ms-wmv',
+        'wmx' => 'video/x-ms-wmx',
+        'wmz' => 'application/x-msmetafile',
+        'woff' => 'font/woff',
+        'woff2' => 'font/woff2',
+        'word' => 'application/msword',
+        'wpd' => 'application/vnd.wordperfect',
+        'wpl' => 'application/vnd.ms-wpl',
+        'wps' => 'application/vnd.ms-works',
+        'wqd' => 'application/vnd.wqd',
+        'wri' => 'application/x-mswrite',
+        'wrl' => 'model/vrml',
+        'wsc' => 'message/vnd.wfa.wsc',
+        'wsdl' => 'application/wsdl+xml',
+        'wspolicy' => 'application/wspolicy+xml',
+        'wtb' => 'application/vnd.webturbo',
+        'wvx' => 'video/x-ms-wvx',
+        'x3d' => 'model/x3d+xml',
+        'x3db' => 'model/x3d+fastinfoset',
+        'x3dbz' => 'model/x3d+binary',
+        'x3dv' => 'model/x3d-vrml',
+        'x3dvz' => 'model/x3d+vrml',
+        'x3dz' => 'model/x3d+xml',
+        'x32' => 'application/x-authorware-bin',
+        'x_b' => 'model/vnd.parasolid.transmit.binary',
+        'x_t' => 'model/vnd.parasolid.transmit.text',
+        'xaml' => 'application/xaml+xml',
+        'xap' => 'application/x-silverlight-app',
+        'xar' => 'application/vnd.xara',
+        'xav' => 'application/xcap-att+xml',
+        'xbap' => 'application/x-ms-xbap',
+        'xbd' => 'application/vnd.fujixerox.docuworks.binder',
+        'xbm' => 'image/x-xbitmap',
+        'xca' => 'application/xcap-caps+xml',
+        'xcs' => 'application/calendar+xml',
+        'xdf' => 'application/xcap-diff+xml',
+        'xdm' => 'application/vnd.syncml.dm+xml',
+        'xdp' => 'application/vnd.adobe.xdp+xml',
+        'xdssc' => 'application/dssc+xml',
+        'xdw' => 'application/vnd.fujixerox.docuworks',
+        'xel' => 'application/xcap-el+xml',
+        'xenc' => 'application/xenc+xml',
+        'xer' => 'application/patch-ops-error+xml',
+        'xfdf' => 'application/xfdf',
+        'xfdl' => 'application/vnd.xfdl',
+        'xht' => 'application/xhtml+xml',
+        'xhtm' => 'application/vnd.pwg-xhtml-print+xml',
+        'xhtml' => 'application/xhtml+xml',
+        'xhvml' => 'application/xv+xml',
+        'xif' => 'image/vnd.xiff',
+        'xl' => 'application/excel',
+        'xla' => 'application/vnd.ms-excel',
+        'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
+        'xlc' => 'application/vnd.ms-excel',
+        'xlf' => 'application/xliff+xml',
+        'xlm' => 'application/vnd.ms-excel',
+        'xls' => 'application/vnd.ms-excel',
+        'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
+        'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
+        'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+        'xlt' => 'application/vnd.ms-excel',
+        'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
+        'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+        'xlw' => 'application/vnd.ms-excel',
+        'xm' => 'audio/xm',
+        'xml' => 'application/xml',
+        'xns' => 'application/xcap-ns+xml',
+        'xo' => 'application/vnd.olpc-sugar',
+        'xop' => 'application/xop+xml',
+        'xpi' => 'application/x-xpinstall',
+        'xpl' => 'application/xproc+xml',
+        'xpm' => 'image/x-xpixmap',
+        'xpr' => 'application/vnd.is-xpr',
+        'xps' => 'application/vnd.ms-xpsdocument',
+        'xpw' => 'application/vnd.intercon.formnet',
+        'xpx' => 'application/vnd.intercon.formnet',
+        'xsd' => 'application/xml',
+        'xsf' => 'application/prs.xsf+xml',
+        'xsl' => 'application/xml',
+        'xslt' => 'application/xslt+xml',
+        'xsm' => 'application/vnd.syncml+xml',
+        'xspf' => 'application/xspf+xml',
+        'xul' => 'application/vnd.mozilla.xul+xml',
+        'xvm' => 'application/xv+xml',
+        'xvml' => 'application/xv+xml',
+        'xwd' => 'image/x-xwindowdump',
+        'xyz' => 'chemical/x-xyz',
+        'xz' => 'application/x-xz',
+        'yaml' => 'text/yaml',
+        'yang' => 'application/yang',
+        'yin' => 'application/yin+xml',
+        'yml' => 'text/yaml',
+        'ymp' => 'text/x-suse-ymp',
+        'z' => 'application/x-compress',
+        'z1' => 'application/x-zmachine',
+        'z2' => 'application/x-zmachine',
+        'z3' => 'application/x-zmachine',
+        'z4' => 'application/x-zmachine',
+        'z5' => 'application/x-zmachine',
+        'z6' => 'application/x-zmachine',
+        'z7' => 'application/x-zmachine',
+        'z8' => 'application/x-zmachine',
+        'zaz' => 'application/vnd.zzazz.deck+xml',
+        'zip' => 'application/zip',
+        'zir' => 'application/vnd.zul',
+        'zirz' => 'application/vnd.zul',
+        'zmm' => 'application/vnd.handheld-entertainment+xml',
+        'zsh' => 'text/x-scriptzsh',
+    ];
+
+    /**
+     * Determines the mimetype of a file by looking at its extension.
+     *
+     * @see https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
+     */
+    public static function fromFilename(string $filename): ?string
+    {
+        return self::fromExtension(pathinfo($filename, PATHINFO_EXTENSION));
+    }
+
+    /**
+     * Maps a file extensions to a mimetype.
+     *
+     * @see https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
+     */
+    public static function fromExtension(string $extension): ?string
+    {
+        return self::MIME_TYPES[strtolower($extension)] ?? null;
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/MultipartStream.php b/vendor/guzzlehttp/psr7/src/MultipartStream.php
new file mode 100644
index 0000000..43d718f
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/MultipartStream.php
@@ -0,0 +1,165 @@
+boundary = $boundary ?: bin2hex(random_bytes(20));
+        $this->stream = $this->createStream($elements);
+    }
+
+    public function getBoundary(): string
+    {
+        return $this->boundary;
+    }
+
+    public function isWritable(): bool
+    {
+        return false;
+    }
+
+    /**
+     * Get the headers needed before transferring the content of a POST file
+     *
+     * @param string[] $headers
+     */
+    private function getHeaders(array $headers): string
+    {
+        $str = '';
+        foreach ($headers as $key => $value) {
+            $str .= "{$key}: {$value}\r\n";
+        }
+
+        return "--{$this->boundary}\r\n".trim($str)."\r\n\r\n";
+    }
+
+    /**
+     * Create the aggregate stream that will be used to upload the POST data
+     */
+    protected function createStream(array $elements = []): StreamInterface
+    {
+        $stream = new AppendStream();
+
+        foreach ($elements as $element) {
+            if (!is_array($element)) {
+                throw new \UnexpectedValueException('An array is expected');
+            }
+            $this->addElement($stream, $element);
+        }
+
+        // Add the trailing boundary with CRLF
+        $stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n"));
+
+        return $stream;
+    }
+
+    private function addElement(AppendStream $stream, array $element): void
+    {
+        foreach (['contents', 'name'] as $key) {
+            if (!array_key_exists($key, $element)) {
+                throw new \InvalidArgumentException("A '{$key}' key is required");
+            }
+        }
+
+        $element['contents'] = Utils::streamFor($element['contents']);
+
+        if (empty($element['filename'])) {
+            $uri = $element['contents']->getMetadata('uri');
+            if ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') {
+                $element['filename'] = $uri;
+            }
+        }
+
+        [$body, $headers] = $this->createElement(
+            $element['name'],
+            $element['contents'],
+            $element['filename'] ?? null,
+            $element['headers'] ?? []
+        );
+
+        $stream->addStream(Utils::streamFor($this->getHeaders($headers)));
+        $stream->addStream($body);
+        $stream->addStream(Utils::streamFor("\r\n"));
+    }
+
+    /**
+     * @param string[] $headers
+     *
+     * @return array{0: StreamInterface, 1: string[]}
+     */
+    private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array
+    {
+        // Set a default content-disposition header if one was no provided
+        $disposition = self::getHeader($headers, 'content-disposition');
+        if (!$disposition) {
+            $headers['Content-Disposition'] = ($filename === '0' || $filename)
+                ? sprintf(
+                    'form-data; name="%s"; filename="%s"',
+                    $name,
+                    basename($filename)
+                )
+                : "form-data; name=\"{$name}\"";
+        }
+
+        // Set a default content-length header if one was no provided
+        $length = self::getHeader($headers, 'content-length');
+        if (!$length) {
+            if ($length = $stream->getSize()) {
+                $headers['Content-Length'] = (string) $length;
+            }
+        }
+
+        // Set a default Content-Type if one was not supplied
+        $type = self::getHeader($headers, 'content-type');
+        if (!$type && ($filename === '0' || $filename)) {
+            $headers['Content-Type'] = MimeType::fromFilename($filename) ?? 'application/octet-stream';
+        }
+
+        return [$stream, $headers];
+    }
+
+    /**
+     * @param string[] $headers
+     */
+    private static function getHeader(array $headers, string $key): ?string
+    {
+        $lowercaseHeader = strtolower($key);
+        foreach ($headers as $k => $v) {
+            if (strtolower((string) $k) === $lowercaseHeader) {
+                return $v;
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/NoSeekStream.php b/vendor/guzzlehttp/psr7/src/NoSeekStream.php
new file mode 100644
index 0000000..161a224
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/NoSeekStream.php
@@ -0,0 +1,28 @@
+source = $source;
+        $this->size = $options['size'] ?? null;
+        $this->metadata = $options['metadata'] ?? [];
+        $this->buffer = new BufferStream();
+    }
+
+    public function __toString(): string
+    {
+        try {
+            return Utils::copyToString($this);
+        } catch (\Throwable $e) {
+            if (\PHP_VERSION_ID >= 70400) {
+                throw $e;
+            }
+            trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
+            return '';
+        }
+    }
+
+    public function close(): void
+    {
+        $this->detach();
+    }
+
+    public function detach()
+    {
+        $this->tellPos = 0;
+        $this->source = null;
+
+        return null;
+    }
+
+    public function getSize(): ?int
+    {
+        return $this->size;
+    }
+
+    public function tell(): int
+    {
+        return $this->tellPos;
+    }
+
+    public function eof(): bool
+    {
+        return $this->source === null;
+    }
+
+    public function isSeekable(): bool
+    {
+        return false;
+    }
+
+    public function rewind(): void
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET): void
+    {
+        throw new \RuntimeException('Cannot seek a PumpStream');
+    }
+
+    public function isWritable(): bool
+    {
+        return false;
+    }
+
+    public function write($string): int
+    {
+        throw new \RuntimeException('Cannot write to a PumpStream');
+    }
+
+    public function isReadable(): bool
+    {
+        return true;
+    }
+
+    public function read($length): string
+    {
+        $data = $this->buffer->read($length);
+        $readLen = strlen($data);
+        $this->tellPos += $readLen;
+        $remaining = $length - $readLen;
+
+        if ($remaining) {
+            $this->pump($remaining);
+            $data .= $this->buffer->read($remaining);
+            $this->tellPos += strlen($data) - $readLen;
+        }
+
+        return $data;
+    }
+
+    public function getContents(): string
+    {
+        $result = '';
+        while (!$this->eof()) {
+            $result .= $this->read(1000000);
+        }
+
+        return $result;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getMetadata($key = null)
+    {
+        if (!$key) {
+            return $this->metadata;
+        }
+
+        return $this->metadata[$key] ?? null;
+    }
+
+    private function pump(int $length): void
+    {
+        if ($this->source !== null) {
+            do {
+                $data = ($this->source)($length);
+                if ($data === false || $data === null) {
+                    $this->source = null;
+
+                    return;
+                }
+                $this->buffer->write($data);
+                $length -= strlen($data);
+            } while ($length > 0);
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Query.php b/vendor/guzzlehttp/psr7/src/Query.php
new file mode 100644
index 0000000..ccf867a
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Query.php
@@ -0,0 +1,118 @@
+ '1', 'foo[b]' => '2'])`.
+     *
+     * @param string   $str         Query string to parse
+     * @param int|bool $urlEncoding How the query string is encoded
+     */
+    public static function parse(string $str, $urlEncoding = true): array
+    {
+        $result = [];
+
+        if ($str === '') {
+            return $result;
+        }
+
+        if ($urlEncoding === true) {
+            $decoder = function ($value) {
+                return rawurldecode(str_replace('+', ' ', (string) $value));
+            };
+        } elseif ($urlEncoding === PHP_QUERY_RFC3986) {
+            $decoder = 'rawurldecode';
+        } elseif ($urlEncoding === PHP_QUERY_RFC1738) {
+            $decoder = 'urldecode';
+        } else {
+            $decoder = function ($str) {
+                return $str;
+            };
+        }
+
+        foreach (explode('&', $str) as $kvp) {
+            $parts = explode('=', $kvp, 2);
+            $key = $decoder($parts[0]);
+            $value = isset($parts[1]) ? $decoder($parts[1]) : null;
+            if (!array_key_exists($key, $result)) {
+                $result[$key] = $value;
+            } else {
+                if (!is_array($result[$key])) {
+                    $result[$key] = [$result[$key]];
+                }
+                $result[$key][] = $value;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Build a query string from an array of key value pairs.
+     *
+     * This function can use the return value of `parse()` to build a query
+     * string. This function does not modify the provided keys when an array is
+     * encountered (like `http_build_query()` would).
+     *
+     * @param array     $params           Query string parameters.
+     * @param int|false $encoding         Set to false to not encode,
+     *                                    PHP_QUERY_RFC3986 to encode using
+     *                                    RFC3986, or PHP_QUERY_RFC1738 to
+     *                                    encode using RFC1738.
+     * @param bool      $treatBoolsAsInts Set to true to encode as 0/1, and
+     *                                    false as false/true.
+     */
+    public static function build(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBoolsAsInts = true): string
+    {
+        if (!$params) {
+            return '';
+        }
+
+        if ($encoding === false) {
+            $encoder = function (string $str): string {
+                return $str;
+            };
+        } elseif ($encoding === PHP_QUERY_RFC3986) {
+            $encoder = 'rawurlencode';
+        } elseif ($encoding === PHP_QUERY_RFC1738) {
+            $encoder = 'urlencode';
+        } else {
+            throw new \InvalidArgumentException('Invalid type');
+        }
+
+        $castBool = $treatBoolsAsInts ? static function ($v) { return (int) $v; } : static function ($v) { return $v ? 'true' : 'false'; };
+
+        $qs = '';
+        foreach ($params as $k => $v) {
+            $k = $encoder((string) $k);
+            if (!is_array($v)) {
+                $qs .= $k;
+                $v = is_bool($v) ? $castBool($v) : $v;
+                if ($v !== null) {
+                    $qs .= '='.$encoder((string) $v);
+                }
+                $qs .= '&';
+            } else {
+                foreach ($v as $vv) {
+                    $qs .= $k;
+                    $vv = is_bool($vv) ? $castBool($vv) : $vv;
+                    if ($vv !== null) {
+                        $qs .= '='.$encoder((string) $vv);
+                    }
+                    $qs .= '&';
+                }
+            }
+        }
+
+        return $qs ? (string) substr($qs, 0, -1) : '';
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Request.php b/vendor/guzzlehttp/psr7/src/Request.php
new file mode 100644
index 0000000..faafe1a
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Request.php
@@ -0,0 +1,159 @@
+assertMethod($method);
+        if (!($uri instanceof UriInterface)) {
+            $uri = new Uri($uri);
+        }
+
+        $this->method = strtoupper($method);
+        $this->uri = $uri;
+        $this->setHeaders($headers);
+        $this->protocol = $version;
+
+        if (!isset($this->headerNames['host'])) {
+            $this->updateHostFromUri();
+        }
+
+        if ($body !== '' && $body !== null) {
+            $this->stream = Utils::streamFor($body);
+        }
+    }
+
+    public function getRequestTarget(): string
+    {
+        if ($this->requestTarget !== null) {
+            return $this->requestTarget;
+        }
+
+        $target = $this->uri->getPath();
+        if ($target === '') {
+            $target = '/';
+        }
+        if ($this->uri->getQuery() != '') {
+            $target .= '?'.$this->uri->getQuery();
+        }
+
+        return $target;
+    }
+
+    public function withRequestTarget($requestTarget): RequestInterface
+    {
+        if (preg_match('#\s#', $requestTarget)) {
+            throw new InvalidArgumentException(
+                'Invalid request target provided; cannot contain whitespace'
+            );
+        }
+
+        $new = clone $this;
+        $new->requestTarget = $requestTarget;
+
+        return $new;
+    }
+
+    public function getMethod(): string
+    {
+        return $this->method;
+    }
+
+    public function withMethod($method): RequestInterface
+    {
+        $this->assertMethod($method);
+        $new = clone $this;
+        $new->method = strtoupper($method);
+
+        return $new;
+    }
+
+    public function getUri(): UriInterface
+    {
+        return $this->uri;
+    }
+
+    public function withUri(UriInterface $uri, $preserveHost = false): RequestInterface
+    {
+        if ($uri === $this->uri) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->uri = $uri;
+
+        if (!$preserveHost || !isset($this->headerNames['host'])) {
+            $new->updateHostFromUri();
+        }
+
+        return $new;
+    }
+
+    private function updateHostFromUri(): void
+    {
+        $host = $this->uri->getHost();
+
+        if ($host == '') {
+            return;
+        }
+
+        if (($port = $this->uri->getPort()) !== null) {
+            $host .= ':'.$port;
+        }
+
+        if (isset($this->headerNames['host'])) {
+            $header = $this->headerNames['host'];
+        } else {
+            $header = 'Host';
+            $this->headerNames['host'] = 'Host';
+        }
+        // Ensure Host is the first header.
+        // See: https://datatracker.ietf.org/doc/html/rfc7230#section-5.4
+        $this->headers = [$header => [$host]] + $this->headers;
+    }
+
+    /**
+     * @param mixed $method
+     */
+    private function assertMethod($method): void
+    {
+        if (!is_string($method) || $method === '') {
+            throw new InvalidArgumentException('Method must be a non-empty string.');
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Response.php b/vendor/guzzlehttp/psr7/src/Response.php
new file mode 100644
index 0000000..34e612f
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Response.php
@@ -0,0 +1,161 @@
+ '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',
+        300 => 'Multiple Choices',
+        301 => 'Moved Permanently',
+        302 => 'Found',
+        303 => 'See Other',
+        304 => 'Not Modified',
+        305 => 'Use Proxy',
+        306 => 'Switch 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 Time-out',
+        409 => 'Conflict',
+        410 => 'Gone',
+        411 => 'Length Required',
+        412 => 'Precondition Failed',
+        413 => 'Request Entity Too Large',
+        414 => 'Request-URI Too Large',
+        415 => 'Unsupported Media Type',
+        416 => 'Requested range not satisfiable',
+        417 => 'Expectation Failed',
+        418 => 'I\'m a teapot',
+        422 => 'Unprocessable Entity',
+        423 => 'Locked',
+        424 => 'Failed Dependency',
+        425 => 'Unordered Collection',
+        426 => 'Upgrade Required',
+        428 => 'Precondition Required',
+        429 => 'Too Many Requests',
+        431 => 'Request Header Fields Too Large',
+        451 => 'Unavailable For Legal Reasons',
+        500 => 'Internal Server Error',
+        501 => 'Not Implemented',
+        502 => 'Bad Gateway',
+        503 => 'Service Unavailable',
+        504 => 'Gateway Time-out',
+        505 => 'HTTP Version not supported',
+        506 => 'Variant Also Negotiates',
+        507 => 'Insufficient Storage',
+        508 => 'Loop Detected',
+        510 => 'Not Extended',
+        511 => 'Network Authentication Required',
+    ];
+
+    /** @var string */
+    private $reasonPhrase;
+
+    /** @var int */
+    private $statusCode;
+
+    /**
+     * @param int                                  $status  Status code
+     * @param (string|string[])[]                  $headers Response headers
+     * @param string|resource|StreamInterface|null $body    Response body
+     * @param string                               $version Protocol version
+     * @param string|null                          $reason  Reason phrase (when empty a default will be used based on the status code)
+     */
+    public function __construct(
+        int $status = 200,
+        array $headers = [],
+        $body = null,
+        string $version = '1.1',
+        ?string $reason = null
+    ) {
+        $this->assertStatusCodeRange($status);
+
+        $this->statusCode = $status;
+
+        if ($body !== '' && $body !== null) {
+            $this->stream = Utils::streamFor($body);
+        }
+
+        $this->setHeaders($headers);
+        if ($reason == '' && isset(self::PHRASES[$this->statusCode])) {
+            $this->reasonPhrase = self::PHRASES[$this->statusCode];
+        } else {
+            $this->reasonPhrase = (string) $reason;
+        }
+
+        $this->protocol = $version;
+    }
+
+    public function getStatusCode(): int
+    {
+        return $this->statusCode;
+    }
+
+    public function getReasonPhrase(): string
+    {
+        return $this->reasonPhrase;
+    }
+
+    public function withStatus($code, $reasonPhrase = ''): ResponseInterface
+    {
+        $this->assertStatusCodeIsInteger($code);
+        $code = (int) $code;
+        $this->assertStatusCodeRange($code);
+
+        $new = clone $this;
+        $new->statusCode = $code;
+        if ($reasonPhrase == '' && isset(self::PHRASES[$new->statusCode])) {
+            $reasonPhrase = self::PHRASES[$new->statusCode];
+        }
+        $new->reasonPhrase = (string) $reasonPhrase;
+
+        return $new;
+    }
+
+    /**
+     * @param mixed $statusCode
+     */
+    private function assertStatusCodeIsInteger($statusCode): void
+    {
+        if (filter_var($statusCode, FILTER_VALIDATE_INT) === false) {
+            throw new \InvalidArgumentException('Status code must be an integer value.');
+        }
+    }
+
+    private function assertStatusCodeRange(int $statusCode): void
+    {
+        if ($statusCode < 100 || $statusCode >= 600) {
+            throw new \InvalidArgumentException('Status code must be an integer value between 1xx and 5xx.');
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Rfc7230.php b/vendor/guzzlehttp/psr7/src/Rfc7230.php
new file mode 100644
index 0000000..8219dba
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Rfc7230.php
@@ -0,0 +1,23 @@
+@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m";
+    public const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)";
+}
diff --git a/vendor/guzzlehttp/psr7/src/ServerRequest.php b/vendor/guzzlehttp/psr7/src/ServerRequest.php
new file mode 100644
index 0000000..3cc9534
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/ServerRequest.php
@@ -0,0 +1,340 @@
+serverParams = $serverParams;
+
+        parent::__construct($method, $uri, $headers, $body, $version);
+    }
+
+    /**
+     * Return an UploadedFile instance array.
+     *
+     * @param array $files An array which respect $_FILES structure
+     *
+     * @throws InvalidArgumentException for unrecognized values
+     */
+    public static function normalizeFiles(array $files): array
+    {
+        $normalized = [];
+
+        foreach ($files as $key => $value) {
+            if ($value instanceof UploadedFileInterface) {
+                $normalized[$key] = $value;
+            } elseif (is_array($value) && isset($value['tmp_name'])) {
+                $normalized[$key] = self::createUploadedFileFromSpec($value);
+            } elseif (is_array($value)) {
+                $normalized[$key] = self::normalizeFiles($value);
+                continue;
+            } else {
+                throw new InvalidArgumentException('Invalid value in files specification');
+            }
+        }
+
+        return $normalized;
+    }
+
+    /**
+     * Create and return an UploadedFile instance from a $_FILES specification.
+     *
+     * If the specification represents an array of values, this method will
+     * delegate to normalizeNestedFileSpec() and return that return value.
+     *
+     * @param array $value $_FILES struct
+     *
+     * @return UploadedFileInterface|UploadedFileInterface[]
+     */
+    private static function createUploadedFileFromSpec(array $value)
+    {
+        if (is_array($value['tmp_name'])) {
+            return self::normalizeNestedFileSpec($value);
+        }
+
+        return new UploadedFile(
+            $value['tmp_name'],
+            (int) $value['size'],
+            (int) $value['error'],
+            $value['name'],
+            $value['type']
+        );
+    }
+
+    /**
+     * Normalize an array of file specifications.
+     *
+     * Loops through all nested files and returns a normalized array of
+     * UploadedFileInterface instances.
+     *
+     * @return UploadedFileInterface[]
+     */
+    private static function normalizeNestedFileSpec(array $files = []): array
+    {
+        $normalizedFiles = [];
+
+        foreach (array_keys($files['tmp_name']) as $key) {
+            $spec = [
+                'tmp_name' => $files['tmp_name'][$key],
+                'size' => $files['size'][$key] ?? null,
+                'error' => $files['error'][$key] ?? null,
+                'name' => $files['name'][$key] ?? null,
+                'type' => $files['type'][$key] ?? null,
+            ];
+            $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
+        }
+
+        return $normalizedFiles;
+    }
+
+    /**
+     * Return a ServerRequest populated with superglobals:
+     * $_GET
+     * $_POST
+     * $_COOKIE
+     * $_FILES
+     * $_SERVER
+     */
+    public static function fromGlobals(): ServerRequestInterface
+    {
+        $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
+        $headers = getallheaders();
+        $uri = self::getUriFromGlobals();
+        $body = new CachingStream(new LazyOpenStream('php://input', 'r+'));
+        $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';
+
+        $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);
+
+        return $serverRequest
+            ->withCookieParams($_COOKIE)
+            ->withQueryParams($_GET)
+            ->withParsedBody($_POST)
+            ->withUploadedFiles(self::normalizeFiles($_FILES));
+    }
+
+    private static function extractHostAndPortFromAuthority(string $authority): array
+    {
+        $uri = 'http://'.$authority;
+        $parts = parse_url($uri);
+        if (false === $parts) {
+            return [null, null];
+        }
+
+        $host = $parts['host'] ?? null;
+        $port = $parts['port'] ?? null;
+
+        return [$host, $port];
+    }
+
+    /**
+     * Get a Uri populated with values from $_SERVER.
+     */
+    public static function getUriFromGlobals(): UriInterface
+    {
+        $uri = new Uri('');
+
+        $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
+
+        $hasPort = false;
+        if (isset($_SERVER['HTTP_HOST'])) {
+            [$host, $port] = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']);
+            if ($host !== null) {
+                $uri = $uri->withHost($host);
+            }
+
+            if ($port !== null) {
+                $hasPort = true;
+                $uri = $uri->withPort($port);
+            }
+        } elseif (isset($_SERVER['SERVER_NAME'])) {
+            $uri = $uri->withHost($_SERVER['SERVER_NAME']);
+        } elseif (isset($_SERVER['SERVER_ADDR'])) {
+            $uri = $uri->withHost($_SERVER['SERVER_ADDR']);
+        }
+
+        if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
+            $uri = $uri->withPort($_SERVER['SERVER_PORT']);
+        }
+
+        $hasQuery = false;
+        if (isset($_SERVER['REQUEST_URI'])) {
+            $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2);
+            $uri = $uri->withPath($requestUriParts[0]);
+            if (isset($requestUriParts[1])) {
+                $hasQuery = true;
+                $uri = $uri->withQuery($requestUriParts[1]);
+            }
+        }
+
+        if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
+            $uri = $uri->withQuery($_SERVER['QUERY_STRING']);
+        }
+
+        return $uri;
+    }
+
+    public function getServerParams(): array
+    {
+        return $this->serverParams;
+    }
+
+    public function getUploadedFiles(): array
+    {
+        return $this->uploadedFiles;
+    }
+
+    public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
+    {
+        $new = clone $this;
+        $new->uploadedFiles = $uploadedFiles;
+
+        return $new;
+    }
+
+    public function getCookieParams(): array
+    {
+        return $this->cookieParams;
+    }
+
+    public function withCookieParams(array $cookies): ServerRequestInterface
+    {
+        $new = clone $this;
+        $new->cookieParams = $cookies;
+
+        return $new;
+    }
+
+    public function getQueryParams(): array
+    {
+        return $this->queryParams;
+    }
+
+    public function withQueryParams(array $query): ServerRequestInterface
+    {
+        $new = clone $this;
+        $new->queryParams = $query;
+
+        return $new;
+    }
+
+    /**
+     * @return array|object|null
+     */
+    public function getParsedBody()
+    {
+        return $this->parsedBody;
+    }
+
+    public function withParsedBody($data): ServerRequestInterface
+    {
+        $new = clone $this;
+        $new->parsedBody = $data;
+
+        return $new;
+    }
+
+    public function getAttributes(): array
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getAttribute($attribute, $default = null)
+    {
+        if (false === array_key_exists($attribute, $this->attributes)) {
+            return $default;
+        }
+
+        return $this->attributes[$attribute];
+    }
+
+    public function withAttribute($attribute, $value): ServerRequestInterface
+    {
+        $new = clone $this;
+        $new->attributes[$attribute] = $value;
+
+        return $new;
+    }
+
+    public function withoutAttribute($attribute): ServerRequestInterface
+    {
+        if (false === array_key_exists($attribute, $this->attributes)) {
+            return $this;
+        }
+
+        $new = clone $this;
+        unset($new->attributes[$attribute]);
+
+        return $new;
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Stream.php b/vendor/guzzlehttp/psr7/src/Stream.php
new file mode 100644
index 0000000..0aff9b2
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Stream.php
@@ -0,0 +1,283 @@
+size = $options['size'];
+        }
+
+        $this->customMetadata = $options['metadata'] ?? [];
+        $this->stream = $stream;
+        $meta = stream_get_meta_data($this->stream);
+        $this->seekable = $meta['seekable'];
+        $this->readable = (bool) preg_match(self::READABLE_MODES, $meta['mode']);
+        $this->writable = (bool) preg_match(self::WRITABLE_MODES, $meta['mode']);
+        $this->uri = $this->getMetadata('uri');
+    }
+
+    /**
+     * Closes the stream when the destructed
+     */
+    public function __destruct()
+    {
+        $this->close();
+    }
+
+    public function __toString(): string
+    {
+        try {
+            if ($this->isSeekable()) {
+                $this->seek(0);
+            }
+
+            return $this->getContents();
+        } catch (\Throwable $e) {
+            if (\PHP_VERSION_ID >= 70400) {
+                throw $e;
+            }
+            trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
+            return '';
+        }
+    }
+
+    public function getContents(): string
+    {
+        if (!isset($this->stream)) {
+            throw new \RuntimeException('Stream is detached');
+        }
+
+        if (!$this->readable) {
+            throw new \RuntimeException('Cannot read from non-readable stream');
+        }
+
+        return Utils::tryGetContents($this->stream);
+    }
+
+    public function close(): void
+    {
+        if (isset($this->stream)) {
+            if (is_resource($this->stream)) {
+                fclose($this->stream);
+            }
+            $this->detach();
+        }
+    }
+
+    public function detach()
+    {
+        if (!isset($this->stream)) {
+            return null;
+        }
+
+        $result = $this->stream;
+        unset($this->stream);
+        $this->size = $this->uri = null;
+        $this->readable = $this->writable = $this->seekable = false;
+
+        return $result;
+    }
+
+    public function getSize(): ?int
+    {
+        if ($this->size !== null) {
+            return $this->size;
+        }
+
+        if (!isset($this->stream)) {
+            return null;
+        }
+
+        // Clear the stat cache if the stream has a URI
+        if ($this->uri) {
+            clearstatcache(true, $this->uri);
+        }
+
+        $stats = fstat($this->stream);
+        if (is_array($stats) && isset($stats['size'])) {
+            $this->size = $stats['size'];
+
+            return $this->size;
+        }
+
+        return null;
+    }
+
+    public function isReadable(): bool
+    {
+        return $this->readable;
+    }
+
+    public function isWritable(): bool
+    {
+        return $this->writable;
+    }
+
+    public function isSeekable(): bool
+    {
+        return $this->seekable;
+    }
+
+    public function eof(): bool
+    {
+        if (!isset($this->stream)) {
+            throw new \RuntimeException('Stream is detached');
+        }
+
+        return feof($this->stream);
+    }
+
+    public function tell(): int
+    {
+        if (!isset($this->stream)) {
+            throw new \RuntimeException('Stream is detached');
+        }
+
+        $result = ftell($this->stream);
+
+        if ($result === false) {
+            throw new \RuntimeException('Unable to determine stream position');
+        }
+
+        return $result;
+    }
+
+    public function rewind(): void
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET): void
+    {
+        $whence = (int) $whence;
+
+        if (!isset($this->stream)) {
+            throw new \RuntimeException('Stream is detached');
+        }
+        if (!$this->seekable) {
+            throw new \RuntimeException('Stream is not seekable');
+        }
+        if (fseek($this->stream, $offset, $whence) === -1) {
+            throw new \RuntimeException('Unable to seek to stream position '
+                .$offset.' with whence '.var_export($whence, true));
+        }
+    }
+
+    public function read($length): string
+    {
+        if (!isset($this->stream)) {
+            throw new \RuntimeException('Stream is detached');
+        }
+        if (!$this->readable) {
+            throw new \RuntimeException('Cannot read from non-readable stream');
+        }
+        if ($length < 0) {
+            throw new \RuntimeException('Length parameter cannot be negative');
+        }
+
+        if (0 === $length) {
+            return '';
+        }
+
+        try {
+            $string = fread($this->stream, $length);
+        } catch (\Exception $e) {
+            throw new \RuntimeException('Unable to read from stream', 0, $e);
+        }
+
+        if (false === $string) {
+            throw new \RuntimeException('Unable to read from stream');
+        }
+
+        return $string;
+    }
+
+    public function write($string): int
+    {
+        if (!isset($this->stream)) {
+            throw new \RuntimeException('Stream is detached');
+        }
+        if (!$this->writable) {
+            throw new \RuntimeException('Cannot write to a non-writable stream');
+        }
+
+        // We can't know the size after writing anything
+        $this->size = null;
+        $result = fwrite($this->stream, $string);
+
+        if ($result === false) {
+            throw new \RuntimeException('Unable to write to stream');
+        }
+
+        return $result;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getMetadata($key = null)
+    {
+        if (!isset($this->stream)) {
+            return $key ? null : [];
+        } elseif (!$key) {
+            return $this->customMetadata + stream_get_meta_data($this->stream);
+        } elseif (isset($this->customMetadata[$key])) {
+            return $this->customMetadata[$key];
+        }
+
+        $meta = stream_get_meta_data($this->stream);
+
+        return $meta[$key] ?? null;
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php b/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
new file mode 100644
index 0000000..601c13a
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
@@ -0,0 +1,156 @@
+stream = $stream;
+    }
+
+    /**
+     * Magic method used to create a new stream if streams are not added in
+     * the constructor of a decorator (e.g., LazyOpenStream).
+     *
+     * @return StreamInterface
+     */
+    public function __get(string $name)
+    {
+        if ($name === 'stream') {
+            $this->stream = $this->createStream();
+
+            return $this->stream;
+        }
+
+        throw new \UnexpectedValueException("$name not found on class");
+    }
+
+    public function __toString(): string
+    {
+        try {
+            if ($this->isSeekable()) {
+                $this->seek(0);
+            }
+
+            return $this->getContents();
+        } catch (\Throwable $e) {
+            if (\PHP_VERSION_ID >= 70400) {
+                throw $e;
+            }
+            trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
+            return '';
+        }
+    }
+
+    public function getContents(): string
+    {
+        return Utils::copyToString($this);
+    }
+
+    /**
+     * Allow decorators to implement custom methods
+     *
+     * @return mixed
+     */
+    public function __call(string $method, array $args)
+    {
+        /** @var callable $callable */
+        $callable = [$this->stream, $method];
+        $result = ($callable)(...$args);
+
+        // Always return the wrapped object if the result is a return $this
+        return $result === $this->stream ? $this : $result;
+    }
+
+    public function close(): void
+    {
+        $this->stream->close();
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getMetadata($key = null)
+    {
+        return $this->stream->getMetadata($key);
+    }
+
+    public function detach()
+    {
+        return $this->stream->detach();
+    }
+
+    public function getSize(): ?int
+    {
+        return $this->stream->getSize();
+    }
+
+    public function eof(): bool
+    {
+        return $this->stream->eof();
+    }
+
+    public function tell(): int
+    {
+        return $this->stream->tell();
+    }
+
+    public function isReadable(): bool
+    {
+        return $this->stream->isReadable();
+    }
+
+    public function isWritable(): bool
+    {
+        return $this->stream->isWritable();
+    }
+
+    public function isSeekable(): bool
+    {
+        return $this->stream->isSeekable();
+    }
+
+    public function rewind(): void
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET): void
+    {
+        $this->stream->seek($offset, $whence);
+    }
+
+    public function read($length): string
+    {
+        return $this->stream->read($length);
+    }
+
+    public function write($string): int
+    {
+        return $this->stream->write($string);
+    }
+
+    /**
+     * Implement in subclasses to dynamically create streams when requested.
+     *
+     * @throws \BadMethodCallException
+     */
+    protected function createStream(): StreamInterface
+    {
+        throw new \BadMethodCallException('Not implemented');
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/StreamWrapper.php b/vendor/guzzlehttp/psr7/src/StreamWrapper.php
new file mode 100644
index 0000000..77b04d7
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/StreamWrapper.php
@@ -0,0 +1,207 @@
+isReadable()) {
+            $mode = $stream->isWritable() ? 'r+' : 'r';
+        } elseif ($stream->isWritable()) {
+            $mode = 'w';
+        } else {
+            throw new \InvalidArgumentException('The stream must be readable, '
+                .'writable, or both.');
+        }
+
+        return fopen('guzzle://stream', $mode, false, self::createStreamContext($stream));
+    }
+
+    /**
+     * Creates a stream context that can be used to open a stream as a php stream resource.
+     *
+     * @return resource
+     */
+    public static function createStreamContext(StreamInterface $stream)
+    {
+        return stream_context_create([
+            'guzzle' => ['stream' => $stream],
+        ]);
+    }
+
+    /**
+     * Registers the stream wrapper if needed
+     */
+    public static function register(): void
+    {
+        if (!in_array('guzzle', stream_get_wrappers())) {
+            stream_wrapper_register('guzzle', __CLASS__);
+        }
+    }
+
+    public function stream_open(string $path, string $mode, int $options, ?string &$opened_path = null): bool
+    {
+        $options = stream_context_get_options($this->context);
+
+        if (!isset($options['guzzle']['stream'])) {
+            return false;
+        }
+
+        $this->mode = $mode;
+        $this->stream = $options['guzzle']['stream'];
+
+        return true;
+    }
+
+    public function stream_read(int $count): string
+    {
+        return $this->stream->read($count);
+    }
+
+    public function stream_write(string $data): int
+    {
+        return $this->stream->write($data);
+    }
+
+    public function stream_tell(): int
+    {
+        return $this->stream->tell();
+    }
+
+    public function stream_eof(): bool
+    {
+        return $this->stream->eof();
+    }
+
+    public function stream_seek(int $offset, int $whence): bool
+    {
+        $this->stream->seek($offset, $whence);
+
+        return true;
+    }
+
+    /**
+     * @return resource|false
+     */
+    public function stream_cast(int $cast_as)
+    {
+        $stream = clone $this->stream;
+        $resource = $stream->detach();
+
+        return $resource ?? false;
+    }
+
+    /**
+     * @return array{
+     *   dev: int,
+     *   ino: int,
+     *   mode: int,
+     *   nlink: int,
+     *   uid: int,
+     *   gid: int,
+     *   rdev: int,
+     *   size: int,
+     *   atime: int,
+     *   mtime: int,
+     *   ctime: int,
+     *   blksize: int,
+     *   blocks: int
+     * }|false
+     */
+    public function stream_stat()
+    {
+        if ($this->stream->getSize() === null) {
+            return false;
+        }
+
+        static $modeMap = [
+            'r' => 33060,
+            'rb' => 33060,
+            'r+' => 33206,
+            'w' => 33188,
+            'wb' => 33188,
+        ];
+
+        return [
+            'dev' => 0,
+            'ino' => 0,
+            'mode' => $modeMap[$this->mode],
+            'nlink' => 0,
+            'uid' => 0,
+            'gid' => 0,
+            'rdev' => 0,
+            'size' => $this->stream->getSize() ?: 0,
+            'atime' => 0,
+            'mtime' => 0,
+            'ctime' => 0,
+            'blksize' => 0,
+            'blocks' => 0,
+        ];
+    }
+
+    /**
+     * @return array{
+     *   dev: int,
+     *   ino: int,
+     *   mode: int,
+     *   nlink: int,
+     *   uid: int,
+     *   gid: int,
+     *   rdev: int,
+     *   size: int,
+     *   atime: int,
+     *   mtime: int,
+     *   ctime: int,
+     *   blksize: int,
+     *   blocks: int
+     * }
+     */
+    public function url_stat(string $path, int $flags): array
+    {
+        return [
+            'dev' => 0,
+            'ino' => 0,
+            'mode' => 0,
+            'nlink' => 0,
+            'uid' => 0,
+            'gid' => 0,
+            'rdev' => 0,
+            'size' => 0,
+            'atime' => 0,
+            'mtime' => 0,
+            'ctime' => 0,
+            'blksize' => 0,
+            'blocks' => 0,
+        ];
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/UploadedFile.php b/vendor/guzzlehttp/psr7/src/UploadedFile.php
new file mode 100644
index 0000000..9c9ea49
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/UploadedFile.php
@@ -0,0 +1,211 @@
+setError($errorStatus);
+        $this->size = $size;
+        $this->clientFilename = $clientFilename;
+        $this->clientMediaType = $clientMediaType;
+
+        if ($this->isOk()) {
+            $this->setStreamOrFile($streamOrFile);
+        }
+    }
+
+    /**
+     * Depending on the value set file or stream variable
+     *
+     * @param StreamInterface|string|resource $streamOrFile
+     *
+     * @throws InvalidArgumentException
+     */
+    private function setStreamOrFile($streamOrFile): void
+    {
+        if (is_string($streamOrFile)) {
+            $this->file = $streamOrFile;
+        } elseif (is_resource($streamOrFile)) {
+            $this->stream = new Stream($streamOrFile);
+        } elseif ($streamOrFile instanceof StreamInterface) {
+            $this->stream = $streamOrFile;
+        } else {
+            throw new InvalidArgumentException(
+                'Invalid stream or file provided for UploadedFile'
+            );
+        }
+    }
+
+    /**
+     * @throws InvalidArgumentException
+     */
+    private function setError(int $error): void
+    {
+        if (false === in_array($error, UploadedFile::ERRORS, true)) {
+            throw new InvalidArgumentException(
+                'Invalid error status for UploadedFile'
+            );
+        }
+
+        $this->error = $error;
+    }
+
+    private static function isStringNotEmpty($param): bool
+    {
+        return is_string($param) && false === empty($param);
+    }
+
+    /**
+     * Return true if there is no upload error
+     */
+    private function isOk(): bool
+    {
+        return $this->error === UPLOAD_ERR_OK;
+    }
+
+    public function isMoved(): bool
+    {
+        return $this->moved;
+    }
+
+    /**
+     * @throws RuntimeException if is moved or not ok
+     */
+    private function validateActive(): void
+    {
+        if (false === $this->isOk()) {
+            throw new RuntimeException('Cannot retrieve stream due to upload error');
+        }
+
+        if ($this->isMoved()) {
+            throw new RuntimeException('Cannot retrieve stream after it has already been moved');
+        }
+    }
+
+    public function getStream(): StreamInterface
+    {
+        $this->validateActive();
+
+        if ($this->stream instanceof StreamInterface) {
+            return $this->stream;
+        }
+
+        /** @var string $file */
+        $file = $this->file;
+
+        return new LazyOpenStream($file, 'r+');
+    }
+
+    public function moveTo($targetPath): void
+    {
+        $this->validateActive();
+
+        if (false === self::isStringNotEmpty($targetPath)) {
+            throw new InvalidArgumentException(
+                'Invalid path provided for move operation; must be a non-empty string'
+            );
+        }
+
+        if ($this->file) {
+            $this->moved = PHP_SAPI === 'cli'
+                ? rename($this->file, $targetPath)
+                : move_uploaded_file($this->file, $targetPath);
+        } else {
+            Utils::copyToStream(
+                $this->getStream(),
+                new LazyOpenStream($targetPath, 'w')
+            );
+
+            $this->moved = true;
+        }
+
+        if (false === $this->moved) {
+            throw new RuntimeException(
+                sprintf('Uploaded file could not be moved to %s', $targetPath)
+            );
+        }
+    }
+
+    public function getSize(): ?int
+    {
+        return $this->size;
+    }
+
+    public function getError(): int
+    {
+        return $this->error;
+    }
+
+    public function getClientFilename(): ?string
+    {
+        return $this->clientFilename;
+    }
+
+    public function getClientMediaType(): ?string
+    {
+        return $this->clientMediaType;
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Uri.php b/vendor/guzzlehttp/psr7/src/Uri.php
new file mode 100644
index 0000000..481dfca
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Uri.php
@@ -0,0 +1,743 @@
+ 80,
+        'https' => 443,
+        'ftp' => 21,
+        'gopher' => 70,
+        'nntp' => 119,
+        'news' => 119,
+        'telnet' => 23,
+        'tn3270' => 23,
+        'imap' => 143,
+        'pop' => 110,
+        'ldap' => 389,
+    ];
+
+    /**
+     * Unreserved characters for use in a regex.
+     *
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
+     */
+    private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
+
+    /**
+     * Sub-delims for use in a regex.
+     *
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
+     */
+    private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
+    private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26'];
+
+    /** @var string Uri scheme. */
+    private $scheme = '';
+
+    /** @var string Uri user info. */
+    private $userInfo = '';
+
+    /** @var string Uri host. */
+    private $host = '';
+
+    /** @var int|null Uri port. */
+    private $port;
+
+    /** @var string Uri path. */
+    private $path = '';
+
+    /** @var string Uri query string. */
+    private $query = '';
+
+    /** @var string Uri fragment. */
+    private $fragment = '';
+
+    /** @var string|null String representation */
+    private $composedComponents;
+
+    public function __construct(string $uri = '')
+    {
+        if ($uri !== '') {
+            $parts = self::parse($uri);
+            if ($parts === false) {
+                throw new MalformedUriException("Unable to parse URI: $uri");
+            }
+            $this->applyParts($parts);
+        }
+    }
+
+    /**
+     * UTF-8 aware \parse_url() replacement.
+     *
+     * The internal function produces broken output for non ASCII domain names
+     * (IDN) when used with locales other than "C".
+     *
+     * On the other hand, cURL understands IDN correctly only when UTF-8 locale
+     * is configured ("C.UTF-8", "en_US.UTF-8", etc.).
+     *
+     * @see https://bugs.php.net/bug.php?id=52923
+     * @see https://www.php.net/manual/en/function.parse-url.php#114817
+     * @see https://curl.haxx.se/libcurl/c/CURLOPT_URL.html#ENCODING
+     *
+     * @return array|false
+     */
+    private static function parse(string $url)
+    {
+        // If IPv6
+        $prefix = '';
+        if (preg_match('%^(.*://\[[0-9:a-f]+\])(.*?)$%', $url, $matches)) {
+            /** @var array{0:string, 1:string, 2:string} $matches */
+            $prefix = $matches[1];
+            $url = $matches[2];
+        }
+
+        /** @var string */
+        $encodedUrl = preg_replace_callback(
+            '%[^:/@?&=#]+%usD',
+            static function ($matches) {
+                return urlencode($matches[0]);
+            },
+            $url
+        );
+
+        $result = parse_url($prefix.$encodedUrl);
+
+        if ($result === false) {
+            return false;
+        }
+
+        return array_map('urldecode', $result);
+    }
+
+    public function __toString(): string
+    {
+        if ($this->composedComponents === null) {
+            $this->composedComponents = self::composeComponents(
+                $this->scheme,
+                $this->getAuthority(),
+                $this->path,
+                $this->query,
+                $this->fragment
+            );
+        }
+
+        return $this->composedComponents;
+    }
+
+    /**
+     * Composes a URI reference string from its various components.
+     *
+     * Usually this method does not need to be called manually but instead is used indirectly via
+     * `Psr\Http\Message\UriInterface::__toString`.
+     *
+     * PSR-7 UriInterface treats an empty component the same as a missing component as
+     * getQuery(), getFragment() etc. always return a string. This explains the slight
+     * difference to RFC 3986 Section 5.3.
+     *
+     * Another adjustment is that the authority separator is added even when the authority is missing/empty
+     * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with
+     * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But
+     * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to
+     * that format).
+     *
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.3
+     */
+    public static function composeComponents(?string $scheme, ?string $authority, string $path, ?string $query, ?string $fragment): string
+    {
+        $uri = '';
+
+        // weak type checks to also accept null until we can add scalar type hints
+        if ($scheme != '') {
+            $uri .= $scheme.':';
+        }
+
+        if ($authority != '' || $scheme === 'file') {
+            $uri .= '//'.$authority;
+        }
+
+        if ($authority != '' && $path != '' && $path[0] != '/') {
+            $path = '/'.$path;
+        }
+
+        $uri .= $path;
+
+        if ($query != '') {
+            $uri .= '?'.$query;
+        }
+
+        if ($fragment != '') {
+            $uri .= '#'.$fragment;
+        }
+
+        return $uri;
+    }
+
+    /**
+     * Whether the URI has the default port of the current scheme.
+     *
+     * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used
+     * independently of the implementation.
+     */
+    public static function isDefaultPort(UriInterface $uri): bool
+    {
+        return $uri->getPort() === null
+            || (isset(self::DEFAULT_PORTS[$uri->getScheme()]) && $uri->getPort() === self::DEFAULT_PORTS[$uri->getScheme()]);
+    }
+
+    /**
+     * Whether the URI is absolute, i.e. it has a scheme.
+     *
+     * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true
+     * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative
+     * to another URI, the base URI. Relative references can be divided into several forms:
+     * - network-path references, e.g. '//example.com/path'
+     * - absolute-path references, e.g. '/path'
+     * - relative-path references, e.g. 'subpath'
+     *
+     * @see Uri::isNetworkPathReference
+     * @see Uri::isAbsolutePathReference
+     * @see Uri::isRelativePathReference
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4
+     */
+    public static function isAbsolute(UriInterface $uri): bool
+    {
+        return $uri->getScheme() !== '';
+    }
+
+    /**
+     * Whether the URI is a network-path reference.
+     *
+     * A relative reference that begins with two slash characters is termed an network-path reference.
+     *
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
+     */
+    public static function isNetworkPathReference(UriInterface $uri): bool
+    {
+        return $uri->getScheme() === '' && $uri->getAuthority() !== '';
+    }
+
+    /**
+     * Whether the URI is a absolute-path reference.
+     *
+     * A relative reference that begins with a single slash character is termed an absolute-path reference.
+     *
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
+     */
+    public static function isAbsolutePathReference(UriInterface $uri): bool
+    {
+        return $uri->getScheme() === ''
+            && $uri->getAuthority() === ''
+            && isset($uri->getPath()[0])
+            && $uri->getPath()[0] === '/';
+    }
+
+    /**
+     * Whether the URI is a relative-path reference.
+     *
+     * A relative reference that does not begin with a slash character is termed a relative-path reference.
+     *
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
+     */
+    public static function isRelativePathReference(UriInterface $uri): bool
+    {
+        return $uri->getScheme() === ''
+            && $uri->getAuthority() === ''
+            && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/');
+    }
+
+    /**
+     * Whether the URI is a same-document reference.
+     *
+     * A same-document reference refers to a URI that is, aside from its fragment
+     * component, identical to the base URI. When no base URI is given, only an empty
+     * URI reference (apart from its fragment) is considered a same-document reference.
+     *
+     * @param UriInterface      $uri  The URI to check
+     * @param UriInterface|null $base An optional base URI to compare against
+     *
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.4
+     */
+    public static function isSameDocumentReference(UriInterface $uri, ?UriInterface $base = null): bool
+    {
+        if ($base !== null) {
+            $uri = UriResolver::resolve($base, $uri);
+
+            return ($uri->getScheme() === $base->getScheme())
+                && ($uri->getAuthority() === $base->getAuthority())
+                && ($uri->getPath() === $base->getPath())
+                && ($uri->getQuery() === $base->getQuery());
+        }
+
+        return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === '';
+    }
+
+    /**
+     * Creates a new URI with a specific query string value removed.
+     *
+     * Any existing query string values that exactly match the provided key are
+     * removed.
+     *
+     * @param UriInterface $uri URI to use as a base.
+     * @param string       $key Query string key to remove.
+     */
+    public static function withoutQueryValue(UriInterface $uri, string $key): UriInterface
+    {
+        $result = self::getFilteredQueryString($uri, [$key]);
+
+        return $uri->withQuery(implode('&', $result));
+    }
+
+    /**
+     * Creates a new URI with a specific query string value.
+     *
+     * Any existing query string values that exactly match the provided key are
+     * removed and replaced with the given key value pair.
+     *
+     * A value of null will set the query string key without a value, e.g. "key"
+     * instead of "key=value".
+     *
+     * @param UriInterface $uri   URI to use as a base.
+     * @param string       $key   Key to set.
+     * @param string|null  $value Value to set
+     */
+    public static function withQueryValue(UriInterface $uri, string $key, ?string $value): UriInterface
+    {
+        $result = self::getFilteredQueryString($uri, [$key]);
+
+        $result[] = self::generateQueryString($key, $value);
+
+        return $uri->withQuery(implode('&', $result));
+    }
+
+    /**
+     * Creates a new URI with multiple specific query string values.
+     *
+     * It has the same behavior as withQueryValue() but for an associative array of key => value.
+     *
+     * @param UriInterface    $uri           URI to use as a base.
+     * @param (string|null)[] $keyValueArray Associative array of key and values
+     */
+    public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface
+    {
+        $result = self::getFilteredQueryString($uri, array_keys($keyValueArray));
+
+        foreach ($keyValueArray as $key => $value) {
+            $result[] = self::generateQueryString((string) $key, $value !== null ? (string) $value : null);
+        }
+
+        return $uri->withQuery(implode('&', $result));
+    }
+
+    /**
+     * Creates a URI from a hash of `parse_url` components.
+     *
+     * @see https://www.php.net/manual/en/function.parse-url.php
+     *
+     * @throws MalformedUriException If the components do not form a valid URI.
+     */
+    public static function fromParts(array $parts): UriInterface
+    {
+        $uri = new self();
+        $uri->applyParts($parts);
+        $uri->validateState();
+
+        return $uri;
+    }
+
+    public function getScheme(): string
+    {
+        return $this->scheme;
+    }
+
+    public function getAuthority(): string
+    {
+        $authority = $this->host;
+        if ($this->userInfo !== '') {
+            $authority = $this->userInfo.'@'.$authority;
+        }
+
+        if ($this->port !== null) {
+            $authority .= ':'.$this->port;
+        }
+
+        return $authority;
+    }
+
+    public function getUserInfo(): string
+    {
+        return $this->userInfo;
+    }
+
+    public function getHost(): string
+    {
+        return $this->host;
+    }
+
+    public function getPort(): ?int
+    {
+        return $this->port;
+    }
+
+    public function getPath(): string
+    {
+        return $this->path;
+    }
+
+    public function getQuery(): string
+    {
+        return $this->query;
+    }
+
+    public function getFragment(): string
+    {
+        return $this->fragment;
+    }
+
+    public function withScheme($scheme): UriInterface
+    {
+        $scheme = $this->filterScheme($scheme);
+
+        if ($this->scheme === $scheme) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->scheme = $scheme;
+        $new->composedComponents = null;
+        $new->removeDefaultPort();
+        $new->validateState();
+
+        return $new;
+    }
+
+    public function withUserInfo($user, $password = null): UriInterface
+    {
+        $info = $this->filterUserInfoComponent($user);
+        if ($password !== null) {
+            $info .= ':'.$this->filterUserInfoComponent($password);
+        }
+
+        if ($this->userInfo === $info) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->userInfo = $info;
+        $new->composedComponents = null;
+        $new->validateState();
+
+        return $new;
+    }
+
+    public function withHost($host): UriInterface
+    {
+        $host = $this->filterHost($host);
+
+        if ($this->host === $host) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->host = $host;
+        $new->composedComponents = null;
+        $new->validateState();
+
+        return $new;
+    }
+
+    public function withPort($port): UriInterface
+    {
+        $port = $this->filterPort($port);
+
+        if ($this->port === $port) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->port = $port;
+        $new->composedComponents = null;
+        $new->removeDefaultPort();
+        $new->validateState();
+
+        return $new;
+    }
+
+    public function withPath($path): UriInterface
+    {
+        $path = $this->filterPath($path);
+
+        if ($this->path === $path) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->path = $path;
+        $new->composedComponents = null;
+        $new->validateState();
+
+        return $new;
+    }
+
+    public function withQuery($query): UriInterface
+    {
+        $query = $this->filterQueryAndFragment($query);
+
+        if ($this->query === $query) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->query = $query;
+        $new->composedComponents = null;
+
+        return $new;
+    }
+
+    public function withFragment($fragment): UriInterface
+    {
+        $fragment = $this->filterQueryAndFragment($fragment);
+
+        if ($this->fragment === $fragment) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->fragment = $fragment;
+        $new->composedComponents = null;
+
+        return $new;
+    }
+
+    public function jsonSerialize(): string
+    {
+        return $this->__toString();
+    }
+
+    /**
+     * Apply parse_url parts to a URI.
+     *
+     * @param array $parts Array of parse_url parts to apply.
+     */
+    private function applyParts(array $parts): void
+    {
+        $this->scheme = isset($parts['scheme'])
+            ? $this->filterScheme($parts['scheme'])
+            : '';
+        $this->userInfo = isset($parts['user'])
+            ? $this->filterUserInfoComponent($parts['user'])
+            : '';
+        $this->host = isset($parts['host'])
+            ? $this->filterHost($parts['host'])
+            : '';
+        $this->port = isset($parts['port'])
+            ? $this->filterPort($parts['port'])
+            : null;
+        $this->path = isset($parts['path'])
+            ? $this->filterPath($parts['path'])
+            : '';
+        $this->query = isset($parts['query'])
+            ? $this->filterQueryAndFragment($parts['query'])
+            : '';
+        $this->fragment = isset($parts['fragment'])
+            ? $this->filterQueryAndFragment($parts['fragment'])
+            : '';
+        if (isset($parts['pass'])) {
+            $this->userInfo .= ':'.$this->filterUserInfoComponent($parts['pass']);
+        }
+
+        $this->removeDefaultPort();
+    }
+
+    /**
+     * @param mixed $scheme
+     *
+     * @throws \InvalidArgumentException If the scheme is invalid.
+     */
+    private function filterScheme($scheme): string
+    {
+        if (!is_string($scheme)) {
+            throw new \InvalidArgumentException('Scheme must be a string');
+        }
+
+        return \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
+    }
+
+    /**
+     * @param mixed $component
+     *
+     * @throws \InvalidArgumentException If the user info is invalid.
+     */
+    private function filterUserInfoComponent($component): string
+    {
+        if (!is_string($component)) {
+            throw new \InvalidArgumentException('User info must be a string');
+        }
+
+        return preg_replace_callback(
+            '/(?:[^%'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.']+|%(?![A-Fa-f0-9]{2}))/',
+            [$this, 'rawurlencodeMatchZero'],
+            $component
+        );
+    }
+
+    /**
+     * @param mixed $host
+     *
+     * @throws \InvalidArgumentException If the host is invalid.
+     */
+    private function filterHost($host): string
+    {
+        if (!is_string($host)) {
+            throw new \InvalidArgumentException('Host must be a string');
+        }
+
+        return \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
+    }
+
+    /**
+     * @param mixed $port
+     *
+     * @throws \InvalidArgumentException If the port is invalid.
+     */
+    private function filterPort($port): ?int
+    {
+        if ($port === null) {
+            return null;
+        }
+
+        $port = (int) $port;
+        if (0 > $port || 0xFFFF < $port) {
+            throw new \InvalidArgumentException(
+                sprintf('Invalid port: %d. Must be between 0 and 65535', $port)
+            );
+        }
+
+        return $port;
+    }
+
+    /**
+     * @param (string|int)[] $keys
+     *
+     * @return string[]
+     */
+    private static function getFilteredQueryString(UriInterface $uri, array $keys): array
+    {
+        $current = $uri->getQuery();
+
+        if ($current === '') {
+            return [];
+        }
+
+        $decodedKeys = array_map(function ($k): string {
+            return rawurldecode((string) $k);
+        }, $keys);
+
+        return array_filter(explode('&', $current), function ($part) use ($decodedKeys) {
+            return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true);
+        });
+    }
+
+    private static function generateQueryString(string $key, ?string $value): string
+    {
+        // Query string separators ("=", "&") within the key or value need to be encoded
+        // (while preventing double-encoding) before setting the query string. All other
+        // chars that need percent-encoding will be encoded by withQuery().
+        $queryString = strtr($key, self::QUERY_SEPARATORS_REPLACEMENT);
+
+        if ($value !== null) {
+            $queryString .= '='.strtr($value, self::QUERY_SEPARATORS_REPLACEMENT);
+        }
+
+        return $queryString;
+    }
+
+    private function removeDefaultPort(): void
+    {
+        if ($this->port !== null && self::isDefaultPort($this)) {
+            $this->port = null;
+        }
+    }
+
+    /**
+     * Filters the path of a URI
+     *
+     * @param mixed $path
+     *
+     * @throws \InvalidArgumentException If the path is invalid.
+     */
+    private function filterPath($path): string
+    {
+        if (!is_string($path)) {
+            throw new \InvalidArgumentException('Path must be a string');
+        }
+
+        return preg_replace_callback(
+            '/(?:[^'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.'%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
+            [$this, 'rawurlencodeMatchZero'],
+            $path
+        );
+    }
+
+    /**
+     * Filters the query string or fragment of a URI.
+     *
+     * @param mixed $str
+     *
+     * @throws \InvalidArgumentException If the query or fragment is invalid.
+     */
+    private function filterQueryAndFragment($str): string
+    {
+        if (!is_string($str)) {
+            throw new \InvalidArgumentException('Query and fragment must be a string');
+        }
+
+        return preg_replace_callback(
+            '/(?:[^'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.'%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
+            [$this, 'rawurlencodeMatchZero'],
+            $str
+        );
+    }
+
+    private function rawurlencodeMatchZero(array $match): string
+    {
+        return rawurlencode($match[0]);
+    }
+
+    private function validateState(): void
+    {
+        if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
+            $this->host = self::HTTP_DEFAULT_HOST;
+        }
+
+        if ($this->getAuthority() === '') {
+            if (0 === strpos($this->path, '//')) {
+                throw new MalformedUriException('The path of a URI without an authority must not start with two slashes "//"');
+            }
+            if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) {
+                throw new MalformedUriException('A relative URI must not have a path beginning with a segment containing a colon');
+            }
+        }
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/UriComparator.php b/vendor/guzzlehttp/psr7/src/UriComparator.php
new file mode 100644
index 0000000..70c582a
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/UriComparator.php
@@ -0,0 +1,52 @@
+getHost(), $modified->getHost()) !== 0) {
+            return true;
+        }
+
+        if ($original->getScheme() !== $modified->getScheme()) {
+            return true;
+        }
+
+        if (self::computePort($original) !== self::computePort($modified)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private static function computePort(UriInterface $uri): int
+    {
+        $port = $uri->getPort();
+
+        if (null !== $port) {
+            return $port;
+        }
+
+        return 'https' === $uri->getScheme() ? 443 : 80;
+    }
+
+    private function __construct()
+    {
+        // cannot be instantiated
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/UriNormalizer.php b/vendor/guzzlehttp/psr7/src/UriNormalizer.php
new file mode 100644
index 0000000..e174557
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/UriNormalizer.php
@@ -0,0 +1,220 @@
+getPath() === ''
+            && ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')
+        ) {
+            $uri = $uri->withPath('/');
+        }
+
+        if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') {
+            $uri = $uri->withHost('');
+        }
+
+        if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && Uri::isDefaultPort($uri)) {
+            $uri = $uri->withPort(null);
+        }
+
+        if ($flags & self::REMOVE_DOT_SEGMENTS && !Uri::isRelativePathReference($uri)) {
+            $uri = $uri->withPath(UriResolver::removeDotSegments($uri->getPath()));
+        }
+
+        if ($flags & self::REMOVE_DUPLICATE_SLASHES) {
+            $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath()));
+        }
+
+        if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') {
+            $queryKeyValues = explode('&', $uri->getQuery());
+            sort($queryKeyValues);
+            $uri = $uri->withQuery(implode('&', $queryKeyValues));
+        }
+
+        return $uri;
+    }
+
+    /**
+     * Whether two URIs can be considered equivalent.
+     *
+     * Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also
+     * accepts relative URI references and returns true when they are equivalent. This of course assumes they will be
+     * resolved against the same base URI. If this is not the case, determination of equivalence or difference of
+     * relative references does not mean anything.
+     *
+     * @param UriInterface $uri1           An URI to compare
+     * @param UriInterface $uri2           An URI to compare
+     * @param int          $normalizations A bitmask of normalizations to apply, see constants
+     *
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.1
+     */
+    public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, int $normalizations = self::PRESERVING_NORMALIZATIONS): bool
+    {
+        return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations);
+    }
+
+    private static function capitalizePercentEncoding(UriInterface $uri): UriInterface
+    {
+        $regex = '/(?:%[A-Fa-f0-9]{2})++/';
+
+        $callback = function (array $match): string {
+            return strtoupper($match[0]);
+        };
+
+        return
+            $uri->withPath(
+                preg_replace_callback($regex, $callback, $uri->getPath())
+            )->withQuery(
+                preg_replace_callback($regex, $callback, $uri->getQuery())
+            );
+    }
+
+    private static function decodeUnreservedCharacters(UriInterface $uri): UriInterface
+    {
+        $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i';
+
+        $callback = function (array $match): string {
+            return rawurldecode($match[0]);
+        };
+
+        return
+            $uri->withPath(
+                preg_replace_callback($regex, $callback, $uri->getPath())
+            )->withQuery(
+                preg_replace_callback($regex, $callback, $uri->getQuery())
+            );
+    }
+
+    private function __construct()
+    {
+        // cannot be instantiated
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/UriResolver.php b/vendor/guzzlehttp/psr7/src/UriResolver.php
new file mode 100644
index 0000000..3737be1
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/UriResolver.php
@@ -0,0 +1,211 @@
+getScheme() != '') {
+            return $rel->withPath(self::removeDotSegments($rel->getPath()));
+        }
+
+        if ($rel->getAuthority() != '') {
+            $targetAuthority = $rel->getAuthority();
+            $targetPath = self::removeDotSegments($rel->getPath());
+            $targetQuery = $rel->getQuery();
+        } else {
+            $targetAuthority = $base->getAuthority();
+            if ($rel->getPath() === '') {
+                $targetPath = $base->getPath();
+                $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery();
+            } else {
+                if ($rel->getPath()[0] === '/') {
+                    $targetPath = $rel->getPath();
+                } else {
+                    if ($targetAuthority != '' && $base->getPath() === '') {
+                        $targetPath = '/'.$rel->getPath();
+                    } else {
+                        $lastSlashPos = strrpos($base->getPath(), '/');
+                        if ($lastSlashPos === false) {
+                            $targetPath = $rel->getPath();
+                        } else {
+                            $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1).$rel->getPath();
+                        }
+                    }
+                }
+                $targetPath = self::removeDotSegments($targetPath);
+                $targetQuery = $rel->getQuery();
+            }
+        }
+
+        return new Uri(Uri::composeComponents(
+            $base->getScheme(),
+            $targetAuthority,
+            $targetPath,
+            $targetQuery,
+            $rel->getFragment()
+        ));
+    }
+
+    /**
+     * Returns the target URI as a relative reference from the base URI.
+     *
+     * This method is the counterpart to resolve():
+     *
+     *    (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
+     *
+     * One use-case is to use the current request URI as base URI and then generate relative links in your documents
+     * to reduce the document size or offer self-contained downloadable document archives.
+     *
+     *    $base = new Uri('http://example.com/a/b/');
+     *    echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c'));  // prints 'c'.
+     *    echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y'));  // prints '../x/y'.
+     *    echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
+     *    echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // prints '//example.org/a/b/'.
+     *
+     * This method also accepts a target that is already relative and will try to relativize it further. Only a
+     * relative-path reference will be returned as-is.
+     *
+     *    echo UriResolver::relativize($base, new Uri('/a/b/c'));  // prints 'c' as well
+     */
+    public static function relativize(UriInterface $base, UriInterface $target): UriInterface
+    {
+        if ($target->getScheme() !== ''
+            && ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '')
+        ) {
+            return $target;
+        }
+
+        if (Uri::isRelativePathReference($target)) {
+            // As the target is already highly relative we return it as-is. It would be possible to resolve
+            // the target with `$target = self::resolve($base, $target);` and then try make it more relative
+            // by removing a duplicate query. But let's not do that automatically.
+            return $target;
+        }
+
+        if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) {
+            return $target->withScheme('');
+        }
+
+        // We must remove the path before removing the authority because if the path starts with two slashes, the URI
+        // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also
+        // invalid.
+        $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost('');
+
+        if ($base->getPath() !== $target->getPath()) {
+            return $emptyPathUri->withPath(self::getRelativePath($base, $target));
+        }
+
+        if ($base->getQuery() === $target->getQuery()) {
+            // Only the target fragment is left. And it must be returned even if base and target fragment are the same.
+            return $emptyPathUri->withQuery('');
+        }
+
+        // If the base URI has a query but the target has none, we cannot return an empty path reference as it would
+        // inherit the base query component when resolving.
+        if ($target->getQuery() === '') {
+            $segments = explode('/', $target->getPath());
+            /** @var string $lastSegment */
+            $lastSegment = end($segments);
+
+            return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment);
+        }
+
+        return $emptyPathUri;
+    }
+
+    private static function getRelativePath(UriInterface $base, UriInterface $target): string
+    {
+        $sourceSegments = explode('/', $base->getPath());
+        $targetSegments = explode('/', $target->getPath());
+        array_pop($sourceSegments);
+        $targetLastSegment = array_pop($targetSegments);
+        foreach ($sourceSegments as $i => $segment) {
+            if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) {
+                unset($sourceSegments[$i], $targetSegments[$i]);
+            } else {
+                break;
+            }
+        }
+        $targetSegments[] = $targetLastSegment;
+        $relativePath = str_repeat('../', count($sourceSegments)).implode('/', $targetSegments);
+
+        // A reference to am empty last segment or an empty first sub-segment 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.
+        if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) {
+            $relativePath = "./$relativePath";
+        } elseif ('/' === $relativePath[0]) {
+            if ($base->getAuthority() != '' && $base->getPath() === '') {
+                // In this case an extra slash is added by resolve() automatically. So we must not add one here.
+                $relativePath = ".$relativePath";
+            } else {
+                $relativePath = "./$relativePath";
+            }
+        }
+
+        return $relativePath;
+    }
+
+    private function __construct()
+    {
+        // cannot be instantiated
+    }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Utils.php b/vendor/guzzlehttp/psr7/src/Utils.php
new file mode 100644
index 0000000..7682d2c
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Utils.php
@@ -0,0 +1,477 @@
+ $v) {
+            if (!in_array(strtolower((string) $k), $keys)) {
+                $result[$k] = $v;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Copy the contents of a stream into another stream until the given number
+     * of bytes have been read.
+     *
+     * @param StreamInterface $source Stream to read from
+     * @param StreamInterface $dest   Stream to write to
+     * @param int             $maxLen Maximum number of bytes to read. Pass -1
+     *                                to read the entire stream.
+     *
+     * @throws \RuntimeException on error.
+     */
+    public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void
+    {
+        $bufferSize = 8192;
+
+        if ($maxLen === -1) {
+            while (!$source->eof()) {
+                if (!$dest->write($source->read($bufferSize))) {
+                    break;
+                }
+            }
+        } else {
+            $remaining = $maxLen;
+            while ($remaining > 0 && !$source->eof()) {
+                $buf = $source->read(min($bufferSize, $remaining));
+                $len = strlen($buf);
+                if (!$len) {
+                    break;
+                }
+                $remaining -= $len;
+                $dest->write($buf);
+            }
+        }
+    }
+
+    /**
+     * Copy the contents of a stream into a string until the given number of
+     * bytes have been read.
+     *
+     * @param StreamInterface $stream Stream to read
+     * @param int             $maxLen Maximum number of bytes to read. Pass -1
+     *                                to read the entire stream.
+     *
+     * @throws \RuntimeException on error.
+     */
+    public static function copyToString(StreamInterface $stream, int $maxLen = -1): string
+    {
+        $buffer = '';
+
+        if ($maxLen === -1) {
+            while (!$stream->eof()) {
+                $buf = $stream->read(1048576);
+                if ($buf === '') {
+                    break;
+                }
+                $buffer .= $buf;
+            }
+
+            return $buffer;
+        }
+
+        $len = 0;
+        while (!$stream->eof() && $len < $maxLen) {
+            $buf = $stream->read($maxLen - $len);
+            if ($buf === '') {
+                break;
+            }
+            $buffer .= $buf;
+            $len = strlen($buffer);
+        }
+
+        return $buffer;
+    }
+
+    /**
+     * Calculate a hash of a stream.
+     *
+     * This method reads the entire stream to calculate a rolling hash, based
+     * on PHP's `hash_init` functions.
+     *
+     * @param StreamInterface $stream    Stream to calculate the hash for
+     * @param string          $algo      Hash algorithm (e.g. md5, crc32, etc)
+     * @param bool            $rawOutput Whether or not to use raw output
+     *
+     * @throws \RuntimeException on error.
+     */
+    public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string
+    {
+        $pos = $stream->tell();
+
+        if ($pos > 0) {
+            $stream->rewind();
+        }
+
+        $ctx = hash_init($algo);
+        while (!$stream->eof()) {
+            hash_update($ctx, $stream->read(1048576));
+        }
+
+        $out = hash_final($ctx, $rawOutput);
+        $stream->seek($pos);
+
+        return $out;
+    }
+
+    /**
+     * Clone and modify a request with the given changes.
+     *
+     * This method is useful for reducing the number of clones needed to mutate
+     * a message.
+     *
+     * The changes can be one of:
+     * - method: (string) Changes the HTTP method.
+     * - set_headers: (array) Sets the given headers.
+     * - remove_headers: (array) Remove the given headers.
+     * - body: (mixed) Sets the given body.
+     * - uri: (UriInterface) Set the URI.
+     * - query: (string) Set the query string value of the URI.
+     * - version: (string) Set the protocol version.
+     *
+     * @param RequestInterface $request Request to clone and modify.
+     * @param array            $changes Changes to apply.
+     */
+    public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface
+    {
+        if (!$changes) {
+            return $request;
+        }
+
+        $headers = $request->getHeaders();
+
+        if (!isset($changes['uri'])) {
+            $uri = $request->getUri();
+        } else {
+            // Remove the host header if one is on the URI
+            if ($host = $changes['uri']->getHost()) {
+                $changes['set_headers']['Host'] = $host;
+
+                if ($port = $changes['uri']->getPort()) {
+                    $standardPorts = ['http' => 80, 'https' => 443];
+                    $scheme = $changes['uri']->getScheme();
+                    if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
+                        $changes['set_headers']['Host'] .= ':'.$port;
+                    }
+                }
+            }
+            $uri = $changes['uri'];
+        }
+
+        if (!empty($changes['remove_headers'])) {
+            $headers = self::caselessRemove($changes['remove_headers'], $headers);
+        }
+
+        if (!empty($changes['set_headers'])) {
+            $headers = self::caselessRemove(array_keys($changes['set_headers']), $headers);
+            $headers = $changes['set_headers'] + $headers;
+        }
+
+        if (isset($changes['query'])) {
+            $uri = $uri->withQuery($changes['query']);
+        }
+
+        if ($request instanceof ServerRequestInterface) {
+            $new = (new ServerRequest(
+                $changes['method'] ?? $request->getMethod(),
+                $uri,
+                $headers,
+                $changes['body'] ?? $request->getBody(),
+                $changes['version'] ?? $request->getProtocolVersion(),
+                $request->getServerParams()
+            ))
+            ->withParsedBody($request->getParsedBody())
+            ->withQueryParams($request->getQueryParams())
+            ->withCookieParams($request->getCookieParams())
+            ->withUploadedFiles($request->getUploadedFiles());
+
+            foreach ($request->getAttributes() as $key => $value) {
+                $new = $new->withAttribute($key, $value);
+            }
+
+            return $new;
+        }
+
+        return new Request(
+            $changes['method'] ?? $request->getMethod(),
+            $uri,
+            $headers,
+            $changes['body'] ?? $request->getBody(),
+            $changes['version'] ?? $request->getProtocolVersion()
+        );
+    }
+
+    /**
+     * Read a line from the stream up to the maximum allowed buffer length.
+     *
+     * @param StreamInterface $stream    Stream to read from
+     * @param int|null        $maxLength Maximum buffer length
+     */
+    public static function readLine(StreamInterface $stream, ?int $maxLength = null): string
+    {
+        $buffer = '';
+        $size = 0;
+
+        while (!$stream->eof()) {
+            if ('' === ($byte = $stream->read(1))) {
+                return $buffer;
+            }
+            $buffer .= $byte;
+            // Break when a new line is found or the max length - 1 is reached
+            if ($byte === "\n" || ++$size === $maxLength - 1) {
+                break;
+            }
+        }
+
+        return $buffer;
+    }
+
+    /**
+     * Redact the password in the user info part of a URI.
+     */
+    public static function redactUserInfo(UriInterface $uri): UriInterface
+    {
+        $userInfo = $uri->getUserInfo();
+
+        if (false !== ($pos = \strpos($userInfo, ':'))) {
+            return $uri->withUserInfo(\substr($userInfo, 0, $pos), '***');
+        }
+
+        return $uri;
+    }
+
+    /**
+     * Create a new stream based on the input type.
+     *
+     * Options is an associative array that can contain the following keys:
+     * - metadata: Array of custom metadata.
+     * - size: Size of the stream.
+     *
+     * This method accepts the following `$resource` types:
+     * - `Psr\Http\Message\StreamInterface`: Returns the value as-is.
+     * - `string`: Creates a stream object that uses the given string as the contents.
+     * - `resource`: Creates a stream object that wraps the given PHP stream resource.
+     * - `Iterator`: If the provided value implements `Iterator`, then a read-only
+     *   stream object will be created that wraps the given iterable. Each time the
+     *   stream is read from, data from the iterator will fill a buffer and will be
+     *   continuously called until the buffer is equal to the requested read size.
+     *   Subsequent read calls will first read from the buffer and then call `next`
+     *   on the underlying iterator until it is exhausted.
+     * - `object` with `__toString()`: If the object has the `__toString()` method,
+     *   the object will be cast to a string and then a stream will be returned that
+     *   uses the string value.
+     * - `NULL`: When `null` is passed, an empty stream object is returned.
+     * - `callable` When a callable is passed, a read-only stream object will be
+     *   created that invokes the given callable. The callable is invoked with the
+     *   number of suggested bytes to read. The callable can return any number of
+     *   bytes, but MUST return `false` when there is no more data to return. The
+     *   stream object that wraps the callable will invoke the callable until the
+     *   number of requested bytes are available. Any additional bytes will be
+     *   buffered and used in subsequent reads.
+     *
+     * @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data
+     * @param array{size?: int, metadata?: array}                                    $options  Additional options
+     *
+     * @throws \InvalidArgumentException if the $resource arg is not valid.
+     */
+    public static function streamFor($resource = '', array $options = []): StreamInterface
+    {
+        if (is_scalar($resource)) {
+            $stream = self::tryFopen('php://temp', 'r+');
+            if ($resource !== '') {
+                fwrite($stream, (string) $resource);
+                fseek($stream, 0);
+            }
+
+            return new Stream($stream, $options);
+        }
+
+        switch (gettype($resource)) {
+            case 'resource':
+                /*
+                 * The 'php://input' is a special stream with quirks and inconsistencies.
+                 * We avoid using that stream by reading it into php://temp
+                 */
+
+                /** @var resource $resource */
+                if ((\stream_get_meta_data($resource)['uri'] ?? '') === 'php://input') {
+                    $stream = self::tryFopen('php://temp', 'w+');
+                    stream_copy_to_stream($resource, $stream);
+                    fseek($stream, 0);
+                    $resource = $stream;
+                }
+
+                return new Stream($resource, $options);
+            case 'object':
+                /** @var object $resource */
+                if ($resource instanceof StreamInterface) {
+                    return $resource;
+                } elseif ($resource instanceof \Iterator) {
+                    return new PumpStream(function () use ($resource) {
+                        if (!$resource->valid()) {
+                            return false;
+                        }
+                        $result = $resource->current();
+                        $resource->next();
+
+                        return $result;
+                    }, $options);
+                } elseif (method_exists($resource, '__toString')) {
+                    return self::streamFor((string) $resource, $options);
+                }
+                break;
+            case 'NULL':
+                return new Stream(self::tryFopen('php://temp', 'r+'), $options);
+        }
+
+        if (is_callable($resource)) {
+            return new PumpStream($resource, $options);
+        }
+
+        throw new \InvalidArgumentException('Invalid resource type: '.gettype($resource));
+    }
+
+    /**
+     * Safely opens a PHP stream resource using a filename.
+     *
+     * When fopen fails, PHP normally raises a warning. This function adds an
+     * error handler that checks for errors and throws an exception instead.
+     *
+     * @param string $filename File to open
+     * @param string $mode     Mode used to open the file
+     *
+     * @return resource
+     *
+     * @throws \RuntimeException if the file cannot be opened
+     */
+    public static function tryFopen(string $filename, string $mode)
+    {
+        $ex = null;
+        set_error_handler(static function (int $errno, string $errstr) use ($filename, $mode, &$ex): bool {
+            $ex = new \RuntimeException(sprintf(
+                'Unable to open "%s" using mode "%s": %s',
+                $filename,
+                $mode,
+                $errstr
+            ));
+
+            return true;
+        });
+
+        try {
+            /** @var resource $handle */
+            $handle = fopen($filename, $mode);
+        } catch (\Throwable $e) {
+            $ex = new \RuntimeException(sprintf(
+                'Unable to open "%s" using mode "%s": %s',
+                $filename,
+                $mode,
+                $e->getMessage()
+            ), 0, $e);
+        }
+
+        restore_error_handler();
+
+        if ($ex) {
+            /** @var $ex \RuntimeException */
+            throw $ex;
+        }
+
+        return $handle;
+    }
+
+    /**
+     * Safely gets the contents of a given stream.
+     *
+     * When stream_get_contents fails, PHP normally raises a warning. This
+     * function adds an error handler that checks for errors and throws an
+     * exception instead.
+     *
+     * @param resource $stream
+     *
+     * @throws \RuntimeException if the stream cannot be read
+     */
+    public static function tryGetContents($stream): string
+    {
+        $ex = null;
+        set_error_handler(static function (int $errno, string $errstr) use (&$ex): bool {
+            $ex = new \RuntimeException(sprintf(
+                'Unable to read stream contents: %s',
+                $errstr
+            ));
+
+            return true;
+        });
+
+        try {
+            /** @var string|false $contents */
+            $contents = stream_get_contents($stream);
+
+            if ($contents === false) {
+                $ex = new \RuntimeException('Unable to read stream contents');
+            }
+        } catch (\Throwable $e) {
+            $ex = new \RuntimeException(sprintf(
+                'Unable to read stream contents: %s',
+                $e->getMessage()
+            ), 0, $e);
+        }
+
+        restore_error_handler();
+
+        if ($ex) {
+            /** @var $ex \RuntimeException */
+            throw $ex;
+        }
+
+        return $contents;
+    }
+
+    /**
+     * Returns a UriInterface for the given value.
+     *
+     * This function accepts a string or UriInterface and returns a
+     * UriInterface for the given value. If the value is already a
+     * UriInterface, it is returned as-is.
+     *
+     * @param string|UriInterface $uri
+     *
+     * @throws \InvalidArgumentException
+     */
+    public static function uriFor($uri): UriInterface
+    {
+        if ($uri instanceof UriInterface) {
+            return $uri;
+        }
+
+        if (is_string($uri)) {
+            return new Uri($uri);
+        }
+
+        throw new \InvalidArgumentException('URI must be a string or UriInterface');
+    }
+}
diff --git a/vendor/illuminate/bus/Batch.php b/vendor/illuminate/bus/Batch.php
new file mode 100644
index 0000000..644bfbc
--- /dev/null
+++ b/vendor/illuminate/bus/Batch.php
@@ -0,0 +1,480 @@
+queue = $queue;
+        $this->repository = $repository;
+        $this->id = $id;
+        $this->name = $name;
+        $this->totalJobs = $totalJobs;
+        $this->pendingJobs = $pendingJobs;
+        $this->failedJobs = $failedJobs;
+        $this->failedJobIds = $failedJobIds;
+        $this->options = $options;
+        $this->createdAt = $createdAt;
+        $this->cancelledAt = $cancelledAt;
+        $this->finishedAt = $finishedAt;
+    }
+
+    /**
+     * Get a fresh instance of the batch represented by this ID.
+     *
+     * @return self
+     */
+    public function fresh()
+    {
+        return $this->repository->find($this->id);
+    }
+
+    /**
+     * Add additional jobs to the batch.
+     *
+     * @param  \Illuminate\Support\Enumerable|object|array  $jobs
+     * @return self
+     */
+    public function add($jobs)
+    {
+        $count = 0;
+
+        $jobs = Collection::wrap($jobs)->map(function ($job) use (&$count) {
+            $job = $job instanceof Closure ? CallQueuedClosure::create($job) : $job;
+
+            if (is_array($job)) {
+                $count += count($job);
+
+                return with($this->prepareBatchedChain($job), function ($chain) {
+                    return $chain->first()
+                            ->allOnQueue($this->options['queue'] ?? null)
+                            ->allOnConnection($this->options['connection'] ?? null)
+                            ->chain($chain->slice(1)->values()->all());
+                });
+            } else {
+                $job->withBatchId($this->id);
+
+                $count++;
+            }
+
+            return $job;
+        });
+
+        $this->repository->transaction(function () use ($jobs, $count) {
+            $this->repository->incrementTotalJobs($this->id, $count);
+
+            $this->queue->connection($this->options['connection'] ?? null)->bulk(
+                $jobs->all(),
+                $data = '',
+                $this->options['queue'] ?? null
+            );
+        });
+
+        return $this->fresh();
+    }
+
+    /**
+     * Prepare a chain that exists within the jobs being added.
+     *
+     * @param  array  $chain
+     * @return \Illuminate\Support\Collection
+     */
+    protected function prepareBatchedChain(array $chain)
+    {
+        return collect($chain)->map(function ($job) {
+            $job = $job instanceof Closure ? CallQueuedClosure::create($job) : $job;
+
+            return $job->withBatchId($this->id);
+        });
+    }
+
+    /**
+     * Get the total number of jobs that have been processed by the batch thus far.
+     *
+     * @return int
+     */
+    public function processedJobs()
+    {
+        return $this->totalJobs - $this->pendingJobs;
+    }
+
+    /**
+     * Get the percentage of jobs that have been processed (between 0-100).
+     *
+     * @return int
+     */
+    public function progress()
+    {
+        return $this->totalJobs > 0 ? round(($this->processedJobs() / $this->totalJobs) * 100) : 0;
+    }
+
+    /**
+     * Record that a job within the batch finished successfully, executing any callbacks if necessary.
+     *
+     * @param  string  $jobId
+     * @return void
+     */
+    public function recordSuccessfulJob(string $jobId)
+    {
+        $counts = $this->decrementPendingJobs($jobId);
+
+        if ($counts->pendingJobs === 0) {
+            $this->repository->markAsFinished($this->id);
+        }
+
+        if ($counts->pendingJobs === 0 && $this->hasThenCallbacks()) {
+            $batch = $this->fresh();
+
+            collect($this->options['then'])->each(function ($handler) use ($batch) {
+                $this->invokeHandlerCallback($handler, $batch);
+            });
+        }
+
+        if ($counts->allJobsHaveRanExactlyOnce() && $this->hasFinallyCallbacks()) {
+            $batch = $this->fresh();
+
+            collect($this->options['finally'])->each(function ($handler) use ($batch) {
+                $this->invokeHandlerCallback($handler, $batch);
+            });
+        }
+    }
+
+    /**
+     * Decrement the pending jobs for the batch.
+     *
+     * @param  string  $jobId
+     * @return \Illuminate\Bus\UpdatedBatchJobCounts
+     */
+    public function decrementPendingJobs(string $jobId)
+    {
+        return $this->repository->decrementPendingJobs($this->id, $jobId);
+    }
+
+    /**
+     * Determine if the batch has finished executing.
+     *
+     * @return bool
+     */
+    public function finished()
+    {
+        return ! is_null($this->finishedAt);
+    }
+
+    /**
+     * Determine if the batch has "success" callbacks.
+     *
+     * @return bool
+     */
+    public function hasThenCallbacks()
+    {
+        return isset($this->options['then']) && ! empty($this->options['then']);
+    }
+
+    /**
+     * Determine if the batch allows jobs to fail without cancelling the batch.
+     *
+     * @return bool
+     */
+    public function allowsFailures()
+    {
+        return Arr::get($this->options, 'allowFailures', false) === true;
+    }
+
+    /**
+     * Determine if the batch has job failures.
+     *
+     * @return bool
+     */
+    public function hasFailures()
+    {
+        return $this->failedJobs > 0;
+    }
+
+    /**
+     * Record that a job within the batch failed to finish successfully, executing any callbacks if necessary.
+     *
+     * @param  string  $jobId
+     * @param  \Throwable  $e
+     * @return void
+     */
+    public function recordFailedJob(string $jobId, $e)
+    {
+        $counts = $this->incrementFailedJobs($jobId);
+
+        if ($counts->failedJobs === 1 && ! $this->allowsFailures()) {
+            $this->cancel();
+        }
+
+        if ($counts->failedJobs === 1 && $this->hasCatchCallbacks()) {
+            $batch = $this->fresh();
+
+            collect($this->options['catch'])->each(function ($handler) use ($batch, $e) {
+                $this->invokeHandlerCallback($handler, $batch, $e);
+            });
+        }
+
+        if ($counts->allJobsHaveRanExactlyOnce() && $this->hasFinallyCallbacks()) {
+            $batch = $this->fresh();
+
+            collect($this->options['finally'])->each(function ($handler) use ($batch, $e) {
+                $this->invokeHandlerCallback($handler, $batch, $e);
+            });
+        }
+    }
+
+    /**
+     * Increment the failed jobs for the batch.
+     *
+     * @param  string  $jobId
+     * @return \Illuminate\Bus\UpdatedBatchJobCounts
+     */
+    public function incrementFailedJobs(string $jobId)
+    {
+        return $this->repository->incrementFailedJobs($this->id, $jobId);
+    }
+
+    /**
+     * Determine if the batch has "catch" callbacks.
+     *
+     * @return bool
+     */
+    public function hasCatchCallbacks()
+    {
+        return isset($this->options['catch']) && ! empty($this->options['catch']);
+    }
+
+    /**
+     * Determine if the batch has "finally" callbacks.
+     *
+     * @return bool
+     */
+    public function hasFinallyCallbacks()
+    {
+        return isset($this->options['finally']) && ! empty($this->options['finally']);
+    }
+
+    /**
+     * Cancel the batch.
+     *
+     * @return void
+     */
+    public function cancel()
+    {
+        $this->repository->cancel($this->id);
+    }
+
+    /**
+     * Determine if the batch has been cancelled.
+     *
+     * @return bool
+     */
+    public function canceled()
+    {
+        return $this->cancelled();
+    }
+
+    /**
+     * Determine if the batch has been cancelled.
+     *
+     * @return bool
+     */
+    public function cancelled()
+    {
+        return ! is_null($this->cancelledAt);
+    }
+
+    /**
+     * Delete the batch from storage.
+     *
+     * @return void
+     */
+    public function delete()
+    {
+        $this->repository->delete($this->id);
+    }
+
+    /**
+     * Invoke a batch callback handler.
+     *
+     * @param  callable  $handler
+     * @param  \Illuminate\Bus\Batch  $batch
+     * @param  \Throwable|null  $e
+     * @return void
+     */
+    protected function invokeHandlerCallback($handler, Batch $batch, Throwable $e = null)
+    {
+        try {
+            return $handler($batch, $e);
+        } catch (Throwable $e) {
+            if (function_exists('report')) {
+                report($e);
+            }
+        }
+    }
+
+    /**
+     * Convert the batch to an array.
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return [
+            'id' => $this->id,
+            'name' => $this->name,
+            'totalJobs' => $this->totalJobs,
+            'pendingJobs' => $this->pendingJobs,
+            'processedJobs' => $this->processedJobs(),
+            'progress' => $this->progress(),
+            'failedJobs' => $this->failedJobs,
+            'options' => $this->options,
+            'createdAt' => $this->createdAt,
+            'cancelledAt' => $this->cancelledAt,
+            'finishedAt' => $this->finishedAt,
+        ];
+    }
+
+    /**
+     * Get the JSON serializable representation of the object.
+     *
+     * @return array
+     */
+    public function jsonSerialize(): array
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * Dynamically access the batch's "options" via properties.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function __get($key)
+    {
+        return $this->options[$key] ?? null;
+    }
+}
diff --git a/vendor/illuminate/bus/BatchFactory.php b/vendor/illuminate/bus/BatchFactory.php
new file mode 100644
index 0000000..2c3a4e9
--- /dev/null
+++ b/vendor/illuminate/bus/BatchFactory.php
@@ -0,0 +1,58 @@
+queue = $queue;
+    }
+
+    /**
+     * Create a new batch instance.
+     *
+     * @param  \Illuminate\Bus\BatchRepository  $repository
+     * @param  string  $id
+     * @param  string  $name
+     * @param  int  $totalJobs
+     * @param  int  $pendingJobs
+     * @param  int  $failedJobs
+     * @param  array  $failedJobIds
+     * @param  array  $options
+     * @param  \Carbon\CarbonImmutable  $createdAt
+     * @param  \Carbon\CarbonImmutable|null  $cancelledAt
+     * @param  \Carbon\CarbonImmutable|null  $finishedAt
+     * @return \Illuminate\Bus\Batch
+     */
+    public function make(BatchRepository $repository,
+                         string $id,
+                         string $name,
+                         int $totalJobs,
+                         int $pendingJobs,
+                         int $failedJobs,
+                         array $failedJobIds,
+                         array $options,
+                         CarbonImmutable $createdAt,
+                         ?CarbonImmutable $cancelledAt,
+                         ?CarbonImmutable $finishedAt)
+    {
+        return new Batch($this->queue, $repository, $id, $name, $totalJobs, $pendingJobs, $failedJobs, $failedJobIds, $options, $createdAt, $cancelledAt, $finishedAt);
+    }
+}
diff --git a/vendor/illuminate/bus/BatchRepository.php b/vendor/illuminate/bus/BatchRepository.php
new file mode 100644
index 0000000..098ccef
--- /dev/null
+++ b/vendor/illuminate/bus/BatchRepository.php
@@ -0,0 +1,92 @@
+fakeBatch) {
+            return $this->fakeBatch;
+        }
+
+        if ($this->batchId) {
+            return Container::getInstance()->make(BatchRepository::class)->find($this->batchId);
+        }
+    }
+
+    /**
+     * Determine if the batch is still active and processing.
+     *
+     * @return bool
+     */
+    public function batching()
+    {
+        $batch = $this->batch();
+
+        return $batch && ! $batch->cancelled();
+    }
+
+    /**
+     * Set the batch ID on the job.
+     *
+     * @param  string  $batchId
+     * @return $this
+     */
+    public function withBatchId(string $batchId)
+    {
+        $this->batchId = $batchId;
+
+        return $this;
+    }
+
+    /**
+     * Indicate that the job should use a fake batch.
+     *
+     * @param  string  $id
+     * @param  string  $name
+     * @param  int  $totalJobs
+     * @param  int  $pendingJobs
+     * @param  int  $failedJobs
+     * @param  array  $failedJobIds
+     * @param  array  $options
+     * @param  \Carbon\CarbonImmutable  $createdAt
+     * @param  \Carbon\CarbonImmutable|null  $cancelledAt
+     * @param  \Carbon\CarbonImmutable|null  $finishedAt
+     * @return array{0: $this, 1: \Illuminate\Support\Testing\Fakes\BatchFake}
+     */
+    public function withFakeBatch(string $id = '',
+                                  string $name = '',
+                                  int $totalJobs = 0,
+                                  int $pendingJobs = 0,
+                                  int $failedJobs = 0,
+                                  array $failedJobIds = [],
+                                  array $options = [],
+                                  CarbonImmutable $createdAt = null,
+                                  ?CarbonImmutable $cancelledAt = null,
+                                  ?CarbonImmutable $finishedAt = null)
+    {
+        $this->fakeBatch = new BatchFake(
+            empty($id) ? (string) Str::uuid() : $id,
+            $name,
+            $totalJobs,
+            $pendingJobs,
+            $failedJobs,
+            $failedJobIds,
+            $options,
+            $createdAt ?? CarbonImmutable::now(),
+            $cancelledAt,
+            $finishedAt,
+        );
+
+        return [$this, $this->fakeBatch];
+    }
+}
diff --git a/vendor/illuminate/bus/BusServiceProvider.php b/vendor/illuminate/bus/BusServiceProvider.php
new file mode 100644
index 0000000..bd6192d
--- /dev/null
+++ b/vendor/illuminate/bus/BusServiceProvider.php
@@ -0,0 +1,70 @@
+app->singleton(Dispatcher::class, function ($app) {
+            return new Dispatcher($app, function ($connection = null) use ($app) {
+                return $app[QueueFactoryContract::class]->connection($connection);
+            });
+        });
+
+        $this->registerBatchServices();
+
+        $this->app->alias(
+            Dispatcher::class, DispatcherContract::class
+        );
+
+        $this->app->alias(
+            Dispatcher::class, QueueingDispatcherContract::class
+        );
+    }
+
+    /**
+     * Register the batch handling services.
+     *
+     * @return void
+     */
+    protected function registerBatchServices()
+    {
+        $this->app->singleton(BatchRepository::class, DatabaseBatchRepository::class);
+
+        $this->app->singleton(DatabaseBatchRepository::class, function ($app) {
+            return new DatabaseBatchRepository(
+                $app->make(BatchFactory::class),
+                $app->make('db')->connection($app->config->get('queue.batching.database')),
+                $app->config->get('queue.batching.table', 'job_batches')
+            );
+        });
+    }
+
+    /**
+     * Get the services provided by the provider.
+     *
+     * @return array
+     */
+    public function provides()
+    {
+        return [
+            Dispatcher::class,
+            DispatcherContract::class,
+            QueueingDispatcherContract::class,
+            BatchRepository::class,
+            DatabaseBatchRepository::class,
+        ];
+    }
+}
diff --git a/vendor/illuminate/bus/DatabaseBatchRepository.php b/vendor/illuminate/bus/DatabaseBatchRepository.php
new file mode 100644
index 0000000..624da19
--- /dev/null
+++ b/vendor/illuminate/bus/DatabaseBatchRepository.php
@@ -0,0 +1,388 @@
+factory = $factory;
+        $this->connection = $connection;
+        $this->table = $table;
+    }
+
+    /**
+     * Retrieve a list of batches.
+     *
+     * @param  int  $limit
+     * @param  mixed  $before
+     * @return \Illuminate\Bus\Batch[]
+     */
+    public function get($limit = 50, $before = null)
+    {
+        return $this->connection->table($this->table)
+                            ->orderByDesc('id')
+                            ->take($limit)
+                            ->when($before, fn ($q) => $q->where('id', '<', $before))
+                            ->get()
+                            ->map(function ($batch) {
+                                return $this->toBatch($batch);
+                            })
+                            ->all();
+    }
+
+    /**
+     * Retrieve information about an existing batch.
+     *
+     * @param  string  $batchId
+     * @return \Illuminate\Bus\Batch|null
+     */
+    public function find(string $batchId)
+    {
+        $batch = $this->connection->table($this->table)
+                            ->useWritePdo()
+                            ->where('id', $batchId)
+                            ->first();
+
+        if ($batch) {
+            return $this->toBatch($batch);
+        }
+    }
+
+    /**
+     * Store a new pending batch.
+     *
+     * @param  \Illuminate\Bus\PendingBatch  $batch
+     * @return \Illuminate\Bus\Batch
+     */
+    public function store(PendingBatch $batch)
+    {
+        $id = (string) Str::orderedUuid();
+
+        $this->connection->table($this->table)->insert([
+            'id' => $id,
+            'name' => $batch->name,
+            'total_jobs' => 0,
+            'pending_jobs' => 0,
+            'failed_jobs' => 0,
+            'failed_job_ids' => '[]',
+            'options' => $this->serialize($batch->options),
+            'created_at' => time(),
+            'cancelled_at' => null,
+            'finished_at' => null,
+        ]);
+
+        return $this->find($id);
+    }
+
+    /**
+     * Increment the total number of jobs within the batch.
+     *
+     * @param  string  $batchId
+     * @param  int  $amount
+     * @return void
+     */
+    public function incrementTotalJobs(string $batchId, int $amount)
+    {
+        $this->connection->table($this->table)->where('id', $batchId)->update([
+            'total_jobs' => new Expression('total_jobs + '.$amount),
+            'pending_jobs' => new Expression('pending_jobs + '.$amount),
+            'finished_at' => null,
+        ]);
+    }
+
+    /**
+     * Decrement the total number of pending jobs for the batch.
+     *
+     * @param  string  $batchId
+     * @param  string  $jobId
+     * @return \Illuminate\Bus\UpdatedBatchJobCounts
+     */
+    public function decrementPendingJobs(string $batchId, string $jobId)
+    {
+        $values = $this->updateAtomicValues($batchId, function ($batch) use ($jobId) {
+            return [
+                'pending_jobs' => $batch->pending_jobs - 1,
+                'failed_jobs' => $batch->failed_jobs,
+                'failed_job_ids' => json_encode(array_values(array_diff(json_decode($batch->failed_job_ids, true), [$jobId]))),
+            ];
+        });
+
+        return new UpdatedBatchJobCounts(
+            $values['pending_jobs'],
+            $values['failed_jobs']
+        );
+    }
+
+    /**
+     * Increment the total number of failed jobs for the batch.
+     *
+     * @param  string  $batchId
+     * @param  string  $jobId
+     * @return \Illuminate\Bus\UpdatedBatchJobCounts
+     */
+    public function incrementFailedJobs(string $batchId, string $jobId)
+    {
+        $values = $this->updateAtomicValues($batchId, function ($batch) use ($jobId) {
+            return [
+                'pending_jobs' => $batch->pending_jobs,
+                'failed_jobs' => $batch->failed_jobs + 1,
+                'failed_job_ids' => json_encode(array_values(array_unique(array_merge(json_decode($batch->failed_job_ids, true), [$jobId])))),
+            ];
+        });
+
+        return new UpdatedBatchJobCounts(
+            $values['pending_jobs'],
+            $values['failed_jobs']
+        );
+    }
+
+    /**
+     * Update an atomic value within the batch.
+     *
+     * @param  string  $batchId
+     * @param  \Closure  $callback
+     * @return int|null
+     */
+    protected function updateAtomicValues(string $batchId, Closure $callback)
+    {
+        return $this->connection->transaction(function () use ($batchId, $callback) {
+            $batch = $this->connection->table($this->table)->where('id', $batchId)
+                        ->lockForUpdate()
+                        ->first();
+
+            return is_null($batch) ? [] : tap($callback($batch), function ($values) use ($batchId) {
+                $this->connection->table($this->table)->where('id', $batchId)->update($values);
+            });
+        });
+    }
+
+    /**
+     * Mark the batch that has the given ID as finished.
+     *
+     * @param  string  $batchId
+     * @return void
+     */
+    public function markAsFinished(string $batchId)
+    {
+        $this->connection->table($this->table)->where('id', $batchId)->update([
+            'finished_at' => time(),
+        ]);
+    }
+
+    /**
+     * Cancel the batch that has the given ID.
+     *
+     * @param  string  $batchId
+     * @return void
+     */
+    public function cancel(string $batchId)
+    {
+        $this->connection->table($this->table)->where('id', $batchId)->update([
+            'cancelled_at' => time(),
+            'finished_at' => time(),
+        ]);
+    }
+
+    /**
+     * Delete the batch that has the given ID.
+     *
+     * @param  string  $batchId
+     * @return void
+     */
+    public function delete(string $batchId)
+    {
+        $this->connection->table($this->table)->where('id', $batchId)->delete();
+    }
+
+    /**
+     * Prune all of the entries older than the given date.
+     *
+     * @param  \DateTimeInterface  $before
+     * @return int
+     */
+    public function prune(DateTimeInterface $before)
+    {
+        $query = $this->connection->table($this->table)
+            ->whereNotNull('finished_at')
+            ->where('finished_at', '<', $before->getTimestamp());
+
+        $totalDeleted = 0;
+
+        do {
+            $deleted = $query->take(1000)->delete();
+
+            $totalDeleted += $deleted;
+        } while ($deleted !== 0);
+
+        return $totalDeleted;
+    }
+
+    /**
+     * Prune all of the unfinished entries older than the given date.
+     *
+     * @param  \DateTimeInterface  $before
+     * @return int
+     */
+    public function pruneUnfinished(DateTimeInterface $before)
+    {
+        $query = $this->connection->table($this->table)
+            ->whereNull('finished_at')
+            ->where('created_at', '<', $before->getTimestamp());
+
+        $totalDeleted = 0;
+
+        do {
+            $deleted = $query->take(1000)->delete();
+
+            $totalDeleted += $deleted;
+        } while ($deleted !== 0);
+
+        return $totalDeleted;
+    }
+
+    /**
+     * Prune all of the cancelled entries older than the given date.
+     *
+     * @param  \DateTimeInterface  $before
+     * @return int
+     */
+    public function pruneCancelled(DateTimeInterface $before)
+    {
+        $query = $this->connection->table($this->table)
+            ->whereNotNull('cancelled_at')
+            ->where('created_at', '<', $before->getTimestamp());
+
+        $totalDeleted = 0;
+
+        do {
+            $deleted = $query->take(1000)->delete();
+
+            $totalDeleted += $deleted;
+        } while ($deleted !== 0);
+
+        return $totalDeleted;
+    }
+
+    /**
+     * Execute the given Closure within a storage specific transaction.
+     *
+     * @param  \Closure  $callback
+     * @return mixed
+     */
+    public function transaction(Closure $callback)
+    {
+        return $this->connection->transaction(fn () => $callback());
+    }
+
+    /**
+     * Serialize the given value.
+     *
+     * @param  mixed  $value
+     * @return string
+     */
+    protected function serialize($value)
+    {
+        $serialized = serialize($value);
+
+        return $this->connection instanceof PostgresConnection
+            ? base64_encode($serialized)
+            : $serialized;
+    }
+
+    /**
+     * Unserialize the given value.
+     *
+     * @param  string  $serialized
+     * @return mixed
+     */
+    protected function unserialize($serialized)
+    {
+        if ($this->connection instanceof PostgresConnection &&
+            ! Str::contains($serialized, [':', ';'])) {
+            $serialized = base64_decode($serialized);
+        }
+
+        return unserialize($serialized);
+    }
+
+    /**
+     * Convert the given raw batch to a Batch object.
+     *
+     * @param  object  $batch
+     * @return \Illuminate\Bus\Batch
+     */
+    protected function toBatch($batch)
+    {
+        return $this->factory->make(
+            $this,
+            $batch->id,
+            $batch->name,
+            (int) $batch->total_jobs,
+            (int) $batch->pending_jobs,
+            (int) $batch->failed_jobs,
+            json_decode($batch->failed_job_ids, true),
+            $this->unserialize($batch->options),
+            CarbonImmutable::createFromTimestamp($batch->created_at),
+            $batch->cancelled_at ? CarbonImmutable::createFromTimestamp($batch->cancelled_at) : $batch->cancelled_at,
+            $batch->finished_at ? CarbonImmutable::createFromTimestamp($batch->finished_at) : $batch->finished_at
+        );
+    }
+
+    /**
+     * Get the underlying database connection.
+     *
+     * @return \Illuminate\Database\Connection
+     */
+    public function getConnection()
+    {
+        return $this->connection;
+    }
+
+    /**
+     * Set the underlying database connection.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return void
+     */
+    public function setConnection(Connection $connection)
+    {
+        $this->connection = $connection;
+    }
+}
diff --git a/vendor/illuminate/bus/Dispatcher.php b/vendor/illuminate/bus/Dispatcher.php
new file mode 100644
index 0000000..8ed3a21
--- /dev/null
+++ b/vendor/illuminate/bus/Dispatcher.php
@@ -0,0 +1,295 @@
+container = $container;
+        $this->queueResolver = $queueResolver;
+        $this->pipeline = new Pipeline($container);
+    }
+
+    /**
+     * Dispatch a command to its appropriate handler.
+     *
+     * @param  mixed  $command
+     * @return mixed
+     */
+    public function dispatch($command)
+    {
+        return $this->queueResolver && $this->commandShouldBeQueued($command)
+                        ? $this->dispatchToQueue($command)
+                        : $this->dispatchNow($command);
+    }
+
+    /**
+     * Dispatch a command to its appropriate handler in the current process.
+     *
+     * Queueable jobs will be dispatched to the "sync" queue.
+     *
+     * @param  mixed  $command
+     * @param  mixed  $handler
+     * @return mixed
+     */
+    public function dispatchSync($command, $handler = null)
+    {
+        if ($this->queueResolver &&
+            $this->commandShouldBeQueued($command) &&
+            method_exists($command, 'onConnection')) {
+            return $this->dispatchToQueue($command->onConnection('sync'));
+        }
+
+        return $this->dispatchNow($command, $handler);
+    }
+
+    /**
+     * Dispatch a command to its appropriate handler in the current process without using the synchronous queue.
+     *
+     * @param  mixed  $command
+     * @param  mixed  $handler
+     * @return mixed
+     */
+    public function dispatchNow($command, $handler = null)
+    {
+        $uses = class_uses_recursive($command);
+
+        if (in_array(InteractsWithQueue::class, $uses) &&
+            in_array(Queueable::class, $uses) &&
+            ! $command->job) {
+            $command->setJob(new SyncJob($this->container, json_encode([]), 'sync', 'sync'));
+        }
+
+        if ($handler || $handler = $this->getCommandHandler($command)) {
+            $callback = function ($command) use ($handler) {
+                $method = method_exists($handler, 'handle') ? 'handle' : '__invoke';
+
+                return $handler->{$method}($command);
+            };
+        } else {
+            $callback = function ($command) {
+                $method = method_exists($command, 'handle') ? 'handle' : '__invoke';
+
+                return $this->container->call([$command, $method]);
+            };
+        }
+
+        return $this->pipeline->send($command)->through($this->pipes)->then($callback);
+    }
+
+    /**
+     * Attempt to find the batch with the given ID.
+     *
+     * @param  string  $batchId
+     * @return \Illuminate\Bus\Batch|null
+     */
+    public function findBatch(string $batchId)
+    {
+        return $this->container->make(BatchRepository::class)->find($batchId);
+    }
+
+    /**
+     * Create a new batch of queueable jobs.
+     *
+     * @param  \Illuminate\Support\Collection|array|mixed  $jobs
+     * @return \Illuminate\Bus\PendingBatch
+     */
+    public function batch($jobs)
+    {
+        return new PendingBatch($this->container, Collection::wrap($jobs));
+    }
+
+    /**
+     * Create a new chain of queueable jobs.
+     *
+     * @param  \Illuminate\Support\Collection|array  $jobs
+     * @return \Illuminate\Foundation\Bus\PendingChain
+     */
+    public function chain($jobs)
+    {
+        $jobs = Collection::wrap($jobs);
+
+        return new PendingChain($jobs->shift(), $jobs->toArray());
+    }
+
+    /**
+     * Determine if the given command has a handler.
+     *
+     * @param  mixed  $command
+     * @return bool
+     */
+    public function hasCommandHandler($command)
+    {
+        return array_key_exists(get_class($command), $this->handlers);
+    }
+
+    /**
+     * Retrieve the handler for a command.
+     *
+     * @param  mixed  $command
+     * @return bool|mixed
+     */
+    public function getCommandHandler($command)
+    {
+        if ($this->hasCommandHandler($command)) {
+            return $this->container->make($this->handlers[get_class($command)]);
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine if the given command should be queued.
+     *
+     * @param  mixed  $command
+     * @return bool
+     */
+    protected function commandShouldBeQueued($command)
+    {
+        return $command instanceof ShouldQueue;
+    }
+
+    /**
+     * Dispatch a command to its appropriate handler behind a queue.
+     *
+     * @param  mixed  $command
+     * @return mixed
+     *
+     * @throws \RuntimeException
+     */
+    public function dispatchToQueue($command)
+    {
+        $connection = $command->connection ?? null;
+
+        $queue = call_user_func($this->queueResolver, $connection);
+
+        if (! $queue instanceof Queue) {
+            throw new RuntimeException('Queue resolver did not return a Queue implementation.');
+        }
+
+        if (method_exists($command, 'queue')) {
+            return $command->queue($queue, $command);
+        }
+
+        return $this->pushCommandToQueue($queue, $command);
+    }
+
+    /**
+     * Push the command onto the given queue instance.
+     *
+     * @param  \Illuminate\Contracts\Queue\Queue  $queue
+     * @param  mixed  $command
+     * @return mixed
+     */
+    protected function pushCommandToQueue($queue, $command)
+    {
+        if (isset($command->queue, $command->delay)) {
+            return $queue->laterOn($command->queue, $command->delay, $command);
+        }
+
+        if (isset($command->queue)) {
+            return $queue->pushOn($command->queue, $command);
+        }
+
+        if (isset($command->delay)) {
+            return $queue->later($command->delay, $command);
+        }
+
+        return $queue->push($command);
+    }
+
+    /**
+     * Dispatch a command to its appropriate handler after the current process.
+     *
+     * @param  mixed  $command
+     * @param  mixed  $handler
+     * @return void
+     */
+    public function dispatchAfterResponse($command, $handler = null)
+    {
+        $this->container->terminating(function () use ($command, $handler) {
+            $this->dispatchSync($command, $handler);
+        });
+    }
+
+    /**
+     * Set the pipes through which commands should be piped before dispatching.
+     *
+     * @param  array  $pipes
+     * @return $this
+     */
+    public function pipeThrough(array $pipes)
+    {
+        $this->pipes = $pipes;
+
+        return $this;
+    }
+
+    /**
+     * Map a command to a handler.
+     *
+     * @param  array  $map
+     * @return $this
+     */
+    public function map(array $map)
+    {
+        $this->handlers = array_merge($this->handlers, $map);
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/bus/Events/BatchDispatched.php b/vendor/illuminate/bus/Events/BatchDispatched.php
new file mode 100644
index 0000000..b9a161a
--- /dev/null
+++ b/vendor/illuminate/bus/Events/BatchDispatched.php
@@ -0,0 +1,26 @@
+batch = $batch;
+    }
+}
diff --git a/vendor/illuminate/bus/LICENSE.md b/vendor/illuminate/bus/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/illuminate/bus/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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/illuminate/bus/PendingBatch.php b/vendor/illuminate/bus/PendingBatch.php
new file mode 100644
index 0000000..6cd5183
--- /dev/null
+++ b/vendor/illuminate/bus/PendingBatch.php
@@ -0,0 +1,319 @@
+container = $container;
+        $this->jobs = $jobs;
+    }
+
+    /**
+     * Add jobs to the batch.
+     *
+     * @param  iterable|object|array  $jobs
+     * @return $this
+     */
+    public function add($jobs)
+    {
+        $jobs = is_iterable($jobs) ? $jobs : Arr::wrap($jobs);
+
+        foreach ($jobs as $job) {
+            $this->jobs->push($job);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a callback to be executed after all jobs in the batch have executed successfully.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function then($callback)
+    {
+        $this->options['then'][] = $callback instanceof Closure
+                        ? new SerializableClosure($callback)
+                        : $callback;
+
+        return $this;
+    }
+
+    /**
+     * Get the "then" callbacks that have been registered with the pending batch.
+     *
+     * @return array
+     */
+    public function thenCallbacks()
+    {
+        return $this->options['then'] ?? [];
+    }
+
+    /**
+     * Add a callback to be executed after the first failing job in the batch.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function catch($callback)
+    {
+        $this->options['catch'][] = $callback instanceof Closure
+                    ? new SerializableClosure($callback)
+                    : $callback;
+
+        return $this;
+    }
+
+    /**
+     * Get the "catch" callbacks that have been registered with the pending batch.
+     *
+     * @return array
+     */
+    public function catchCallbacks()
+    {
+        return $this->options['catch'] ?? [];
+    }
+
+    /**
+     * Add a callback to be executed after the batch has finished executing.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function finally($callback)
+    {
+        $this->options['finally'][] = $callback instanceof Closure
+                    ? new SerializableClosure($callback)
+                    : $callback;
+
+        return $this;
+    }
+
+    /**
+     * Get the "finally" callbacks that have been registered with the pending batch.
+     *
+     * @return array
+     */
+    public function finallyCallbacks()
+    {
+        return $this->options['finally'] ?? [];
+    }
+
+    /**
+     * Indicate that the batch should not be cancelled when a job within the batch fails.
+     *
+     * @param  bool  $allowFailures
+     * @return $this
+     */
+    public function allowFailures($allowFailures = true)
+    {
+        $this->options['allowFailures'] = $allowFailures;
+
+        return $this;
+    }
+
+    /**
+     * Determine if the pending batch allows jobs to fail without cancelling the batch.
+     *
+     * @return bool
+     */
+    public function allowsFailures()
+    {
+        return Arr::get($this->options, 'allowFailures', false) === true;
+    }
+
+    /**
+     * Set the name for the batch.
+     *
+     * @param  string  $name
+     * @return $this
+     */
+    public function name(string $name)
+    {
+        $this->name = $name;
+
+        return $this;
+    }
+
+    /**
+     * Specify the queue connection that the batched jobs should run on.
+     *
+     * @param  string  $connection
+     * @return $this
+     */
+    public function onConnection(string $connection)
+    {
+        $this->options['connection'] = $connection;
+
+        return $this;
+    }
+
+    /**
+     * Get the connection used by the pending batch.
+     *
+     * @return string|null
+     */
+    public function connection()
+    {
+        return $this->options['connection'] ?? null;
+    }
+
+    /**
+     * Specify the queue that the batched jobs should run on.
+     *
+     * @param  string  $queue
+     * @return $this
+     */
+    public function onQueue(string $queue)
+    {
+        $this->options['queue'] = $queue;
+
+        return $this;
+    }
+
+    /**
+     * Get the queue used by the pending batch.
+     *
+     * @return string|null
+     */
+    public function queue()
+    {
+        return $this->options['queue'] ?? null;
+    }
+
+    /**
+     * Add additional data into the batch's options array.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function withOption(string $key, $value)
+    {
+        $this->options[$key] = $value;
+
+        return $this;
+    }
+
+    /**
+     * Dispatch the batch.
+     *
+     * @return \Illuminate\Bus\Batch
+     *
+     * @throws \Throwable
+     */
+    public function dispatch()
+    {
+        $repository = $this->container->make(BatchRepository::class);
+
+        try {
+            $batch = $repository->store($this);
+
+            $batch = $batch->add($this->jobs);
+        } catch (Throwable $e) {
+            if (isset($batch)) {
+                $repository->delete($batch->id);
+            }
+
+            throw $e;
+        }
+
+        $this->container->make(EventDispatcher::class)->dispatch(
+            new BatchDispatched($batch)
+        );
+
+        return $batch;
+    }
+
+    /**
+     * Dispatch the batch after the response is sent to the browser.
+     *
+     * @return \Illuminate\Bus\Batch
+     */
+    public function dispatchAfterResponse()
+    {
+        $repository = $this->container->make(BatchRepository::class);
+
+        $batch = $repository->store($this);
+
+        if ($batch) {
+            $this->container->terminating(function () use ($batch) {
+                $this->dispatchExistingBatch($batch);
+            });
+        }
+
+        return $batch;
+    }
+
+    /**
+     * Dispatch an existing batch.
+     *
+     * @param  \Illuminate\Bus\Batch  $batch
+     * @return void
+     *
+     * @throws \Throwable
+     */
+    protected function dispatchExistingBatch($batch)
+    {
+        try {
+            $batch = $batch->add($this->jobs);
+        } catch (Throwable $e) {
+            if (isset($batch)) {
+                $batch->delete();
+            }
+
+            throw $e;
+        }
+
+        $this->container->make(EventDispatcher::class)->dispatch(
+            new BatchDispatched($batch)
+        );
+    }
+}
diff --git a/vendor/illuminate/bus/PrunableBatchRepository.php b/vendor/illuminate/bus/PrunableBatchRepository.php
new file mode 100644
index 0000000..3f97255
--- /dev/null
+++ b/vendor/illuminate/bus/PrunableBatchRepository.php
@@ -0,0 +1,16 @@
+connection = $connection;
+
+        return $this;
+    }
+
+    /**
+     * Set the desired queue for the job.
+     *
+     * @param  string|null  $queue
+     * @return $this
+     */
+    public function onQueue($queue)
+    {
+        $this->queue = $queue;
+
+        return $this;
+    }
+
+    /**
+     * Set the desired connection for the chain.
+     *
+     * @param  string|null  $connection
+     * @return $this
+     */
+    public function allOnConnection($connection)
+    {
+        $this->chainConnection = $connection;
+        $this->connection = $connection;
+
+        return $this;
+    }
+
+    /**
+     * Set the desired queue for the chain.
+     *
+     * @param  string|null  $queue
+     * @return $this
+     */
+    public function allOnQueue($queue)
+    {
+        $this->chainQueue = $queue;
+        $this->queue = $queue;
+
+        return $this;
+    }
+
+    /**
+     * Set the desired delay in seconds for the job.
+     *
+     * @param  \DateTimeInterface|\DateInterval|array|int|null  $delay
+     * @return $this
+     */
+    public function delay($delay)
+    {
+        $this->delay = $delay;
+
+        return $this;
+    }
+
+    /**
+     * Indicate that the job should be dispatched after all database transactions have committed.
+     *
+     * @return $this
+     */
+    public function afterCommit()
+    {
+        $this->afterCommit = true;
+
+        return $this;
+    }
+
+    /**
+     * Indicate that the job should not wait until database transactions have been committed before dispatching.
+     *
+     * @return $this
+     */
+    public function beforeCommit()
+    {
+        $this->afterCommit = false;
+
+        return $this;
+    }
+
+    /**
+     * Specify the middleware the job should be dispatched through.
+     *
+     * @param  array|object  $middleware
+     * @return $this
+     */
+    public function through($middleware)
+    {
+        $this->middleware = Arr::wrap($middleware);
+
+        return $this;
+    }
+
+    /**
+     * Set the jobs that should run if this job is successful.
+     *
+     * @param  array  $chain
+     * @return $this
+     */
+    public function chain($chain)
+    {
+        $this->chained = collect($chain)->map(function ($job) {
+            return $this->serializeJob($job);
+        })->all();
+
+        return $this;
+    }
+
+    /**
+     * Prepend a job to the current chain so that it is run after the currently running job.
+     *
+     * @param  mixed  $job
+     * @return $this
+     */
+    public function prependToChain($job)
+    {
+        $this->chained = Arr::prepend($this->chained, $this->serializeJob($job));
+
+        return $this;
+    }
+
+    /**
+     * Append a job to the end of the current chain.
+     *
+     * @param  mixed  $job
+     * @return $this
+     */
+    public function appendToChain($job)
+    {
+        $this->chained = array_merge($this->chained, [$this->serializeJob($job)]);
+
+        return $this;
+    }
+
+    /**
+     * Serialize a job for queuing.
+     *
+     * @param  mixed  $job
+     * @return string
+     *
+     * @throws \RuntimeException
+     */
+    protected function serializeJob($job)
+    {
+        if ($job instanceof Closure) {
+            if (! class_exists(CallQueuedClosure::class)) {
+                throw new RuntimeException(
+                    'To enable support for closure jobs, please install the illuminate/queue package.'
+                );
+            }
+
+            $job = CallQueuedClosure::create($job);
+        }
+
+        return serialize($job);
+    }
+
+    /**
+     * Dispatch the next job on the chain.
+     *
+     * @return void
+     */
+    public function dispatchNextJobInChain()
+    {
+        if (! empty($this->chained)) {
+            dispatch(tap(unserialize(array_shift($this->chained)), function ($next) {
+                $next->chained = $this->chained;
+
+                $next->onConnection($next->connection ?: $this->chainConnection);
+                $next->onQueue($next->queue ?: $this->chainQueue);
+
+                $next->chainConnection = $this->chainConnection;
+                $next->chainQueue = $this->chainQueue;
+                $next->chainCatchCallbacks = $this->chainCatchCallbacks;
+            }));
+        }
+    }
+
+    /**
+     * Invoke all of the chain's failed job callbacks.
+     *
+     * @param  \Throwable  $e
+     * @return void
+     */
+    public function invokeChainCatchCallbacks($e)
+    {
+        collect($this->chainCatchCallbacks)->each(function ($callback) use ($e) {
+            $callback($e);
+        });
+    }
+}
diff --git a/vendor/illuminate/bus/UniqueLock.php b/vendor/illuminate/bus/UniqueLock.php
new file mode 100644
index 0000000..a4066b7
--- /dev/null
+++ b/vendor/illuminate/bus/UniqueLock.php
@@ -0,0 +1,75 @@
+cache = $cache;
+    }
+
+    /**
+     * Attempt to acquire a lock for the given job.
+     *
+     * @param  mixed  $job
+     * @return bool
+     */
+    public function acquire($job)
+    {
+        $uniqueFor = method_exists($job, 'uniqueFor')
+                    ? $job->uniqueFor()
+                    : ($job->uniqueFor ?? 0);
+
+        $cache = method_exists($job, 'uniqueVia')
+                    ? $job->uniqueVia()
+                    : $this->cache;
+
+        return (bool) $cache->lock($this->getKey($job), $uniqueFor)->get();
+    }
+
+    /**
+     * Release the lock for the given job.
+     *
+     * @param  mixed  $job
+     * @return void
+     */
+    public function release($job)
+    {
+        $cache = method_exists($job, 'uniqueVia')
+                    ? $job->uniqueVia()
+                    : $this->cache;
+
+        $cache->lock($this->getKey($job))->forceRelease();
+    }
+
+    /**
+     * Generate the lock key for the given job.
+     *
+     * @param  mixed  $job
+     * @return string
+     */
+    protected function getKey($job)
+    {
+        $uniqueId = method_exists($job, 'uniqueId')
+                    ? $job->uniqueId()
+                    : ($job->uniqueId ?? '');
+
+        return 'laravel_unique_job:'.get_class($job).$uniqueId;
+    }
+}
diff --git a/vendor/illuminate/bus/UpdatedBatchJobCounts.php b/vendor/illuminate/bus/UpdatedBatchJobCounts.php
new file mode 100644
index 0000000..83d33a4
--- /dev/null
+++ b/vendor/illuminate/bus/UpdatedBatchJobCounts.php
@@ -0,0 +1,43 @@
+pendingJobs = $pendingJobs;
+        $this->failedJobs = $failedJobs;
+    }
+
+    /**
+     * Determine if all jobs have run exactly once.
+     *
+     * @return bool
+     */
+    public function allJobsHaveRanExactlyOnce()
+    {
+        return ($this->pendingJobs - $this->failedJobs) === 0;
+    }
+}
diff --git a/vendor/illuminate/bus/composer.json b/vendor/illuminate/bus/composer.json
new file mode 100644
index 0000000..44e795a
--- /dev/null
+++ b/vendor/illuminate/bus/composer.json
@@ -0,0 +1,40 @@
+{
+    "name": "illuminate/bus",
+    "description": "The Illuminate Bus package.",
+    "license": "MIT",
+    "homepage": "https://laravel.com",
+    "support": {
+        "issues": "https://github.com/laravel/framework/issues",
+        "source": "https://github.com/laravel/framework"
+    },
+    "authors": [
+        {
+            "name": "Taylor Otwell",
+            "email": "taylor@laravel.com"
+        }
+    ],
+    "require": {
+        "php": "^8.0.2",
+        "illuminate/collections": "^9.0",
+        "illuminate/contracts": "^9.0",
+        "illuminate/pipeline": "^9.0",
+        "illuminate/support": "^9.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Illuminate\\Bus\\": ""
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "9.x-dev"
+        }
+    },
+    "suggest": {
+        "illuminate/queue": "Required to use closures when chaining jobs (^7.0)."
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/collections/Arr.php b/vendor/illuminate/collections/Arr.php
new file mode 100644
index 0000000..69220d5
--- /dev/null
+++ b/vendor/illuminate/collections/Arr.php
@@ -0,0 +1,858 @@
+all();
+            } elseif (! is_array($values)) {
+                continue;
+            }
+
+            $results[] = $values;
+        }
+
+        return array_merge([], ...$results);
+    }
+
+    /**
+     * Cross join the given arrays, returning all possible permutations.
+     *
+     * @param  iterable  ...$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  iterable  $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;
+    }
+
+    /**
+     * Convert a flatten "dot" notation array into an expanded array.
+     *
+     * @param  iterable  $array
+     * @return array
+     */
+    public static function undot($array)
+    {
+        $results = [];
+
+        foreach ($array as $key => $value) {
+            static::set($results, $key, $value);
+        }
+
+        return $results;
+    }
+
+    /**
+     * Get all of the given array except for a specified array of keys.
+     *
+     * @param  array  $array
+     * @param  array|string|int|float  $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 Enumerable) {
+            return $array->has($key);
+        }
+
+        if ($array instanceof ArrayAccess) {
+            return $array->offsetExists($key);
+        }
+
+        if (is_float($key)) {
+            $key = (string) $key;
+        }
+
+        return array_key_exists($key, $array);
+    }
+
+    /**
+     * Return the first element in an array passing a given truth test.
+     *
+     * @param  iterable  $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 ($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  iterable  $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;
+            } else {
+                $values = $depth === 1
+                    ? array_values($item)
+                    : static::flatten($item, $depth - 1);
+
+                foreach ($values as $value) {
+                    $result[] = $value;
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Remove one or many array items from a given array using "dot" notation.
+     *
+     * @param  array  $array
+     * @param  array|string|int|float  $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]) && static::accessible($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|int|null  $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 (! str_contains($key, '.')) {
+            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;
+    }
+
+    /**
+     * Determine if any of the keys exist in an array using "dot" notation.
+     *
+     * @param  \ArrayAccess|array  $array
+     * @param  string|array  $keys
+     * @return bool
+     */
+    public static function hasAny($array, $keys)
+    {
+        if (is_null($keys)) {
+            return false;
+        }
+
+        $keys = (array) $keys;
+
+        if (! $array) {
+            return false;
+        }
+
+        if ($keys === []) {
+            return false;
+        }
+
+        foreach ($keys as $key) {
+            if (static::has($array, $key)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * Determines if an array is a list.
+     *
+     * An array is a "list" if all array keys are sequential integers starting from 0 with no gaps in between.
+     *
+     * @param  array  $array
+     * @return bool
+     */
+    public static function isList($array)
+    {
+        return ! self::isAssoc($array);
+    }
+
+    /**
+     * Join all items using a string. The final items can use a separate glue string.
+     *
+     * @param  array  $array
+     * @param  string  $glue
+     * @param  string  $finalGlue
+     * @return string
+     */
+    public static function join($array, $glue, $finalGlue = '')
+    {
+        if ($finalGlue === '') {
+            return implode($glue, $array);
+        }
+
+        if (count($array) === 0) {
+            return '';
+        }
+
+        if (count($array) === 1) {
+            return end($array);
+        }
+
+        $finalItem = array_pop($array);
+
+        return implode($glue, $array).$finalGlue.$finalItem;
+    }
+
+    /**
+     * Key an associative array by a field or using a callback.
+     *
+     * @param  array  $array
+     * @param  callable|array|string  $keyBy
+     * @return array
+     */
+    public static function keyBy($array, $keyBy)
+    {
+        return Collection::make($array)->keyBy($keyBy)->all();
+    }
+
+    /**
+     * Prepend the key names of an associative array.
+     *
+     * @param  array  $array
+     * @param  string  $prependWith
+     * @return array
+     */
+    public static function prependKeysWith($array, $prependWith)
+    {
+        return Collection::make($array)->mapWithKeys(function ($item, $key) use ($prependWith) {
+            return [$prependWith.$key => $item];
+        })->all();
+    }
+
+    /**
+     * 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  iterable  $array
+     * @param  string|array|int|null  $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];
+    }
+
+    /**
+     * Run a map over each of the items in the array.
+     *
+     * @param  array  $array
+     * @param  callable  $callback
+     * @return array
+     */
+    public static function map(array $array, callable $callback)
+    {
+        $keys = array_keys($array);
+
+        try {
+            $items = array_map($callback, $array, $keys);
+        } catch (ArgumentCountError) {
+            $items = array_map($callback, $array);
+        }
+
+        return array_combine($keys, $items);
+    }
+
+    /**
+     * 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 (func_num_args() == 2) {
+            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|int  $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;
+    }
+
+    /**
+     * Convert the array into a query string.
+     *
+     * @param  array  $array
+     * @return string
+     */
+    public static function query($array)
+    {
+        return http_build_query($array, '', '&', PHP_QUERY_RFC3986);
+    }
+
+    /**
+     * Get one or a specified number of random values from an array.
+     *
+     * @param  array  $array
+     * @param  int|null  $number
+     * @param  bool  $preserveKeys
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    public static function random($array, $number = null, $preserveKeys = false)
+    {
+        $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 = [];
+
+        if ($preserveKeys) {
+            foreach ((array) $keys as $key) {
+                $results[$key] = $array[$key];
+            }
+        } else {
+            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|int|null  $key
+     * @param  mixed  $value
+     * @return array
+     */
+    public static function set(&$array, $key, $value)
+    {
+        if (is_null($key)) {
+            return $array = $value;
+        }
+
+        $keys = explode('.', $key);
+
+        foreach ($keys as $i => $key) {
+            if (count($keys) === 1) {
+                break;
+            }
+
+            unset($keys[$i]);
+
+            // 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 {
+            mt_srand($seed);
+            shuffle($array);
+            mt_srand();
+        }
+
+        return $array;
+    }
+
+    /**
+     * Sort the array using the given callback or "dot" notation.
+     *
+     * @param  array  $array
+     * @param  callable|array|string|null  $callback
+     * @return array
+     */
+    public static function sort($array, $callback = null)
+    {
+        return Collection::make($array)->sortBy($callback)->all();
+    }
+
+    /**
+     * Sort the array in descending order using the given callback or "dot" notation.
+     *
+     * @param  array  $array
+     * @param  callable|array|string|null  $callback
+     * @return array
+     */
+    public static function sortDesc($array, $callback = null)
+    {
+        return Collection::make($array)->sortByDesc($callback)->all();
+    }
+
+    /**
+     * Recursively sort an array by keys and values.
+     *
+     * @param  array  $array
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return array
+     */
+    public static function sortRecursive($array, $options = SORT_REGULAR, $descending = false)
+    {
+        foreach ($array as &$value) {
+            if (is_array($value)) {
+                $value = static::sortRecursive($value, $options, $descending);
+            }
+        }
+
+        if (static::isAssoc($array)) {
+            $descending
+                    ? krsort($array, $options)
+                    : ksort($array, $options);
+        } else {
+            $descending
+                    ? rsort($array, $options)
+                    : sort($array, $options);
+        }
+
+        return $array;
+    }
+
+    /**
+     * Conditionally compile classes from an array into a CSS class list.
+     *
+     * @param  array  $array
+     * @return string
+     */
+    public static function toCssClasses($array)
+    {
+        $classList = static::wrap($array);
+
+        $classes = [];
+
+        foreach ($classList as $class => $constraint) {
+            if (is_numeric($class)) {
+                $classes[] = $constraint;
+            } elseif ($constraint) {
+                $classes[] = $class;
+            }
+        }
+
+        return implode(' ', $classes);
+    }
+
+    /**
+     * Conditionally compile styles from an array into a style list.
+     *
+     * @param  array  $array
+     * @return string
+     */
+    public static function toCssStyles($array)
+    {
+        $styleList = static::wrap($array);
+
+        $styles = [];
+
+        foreach ($styleList as $class => $constraint) {
+            if (is_numeric($class)) {
+                $styles[] = Str::finish($constraint, ';');
+            } elseif ($constraint) {
+                $styles[] = Str::finish($class, ';');
+            }
+        }
+
+        return implode(' ', $styles);
+    }
+
+    /**
+     * 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);
+    }
+
+    /**
+     * Filter items where the value is not null.
+     *
+     * @param  array  $array
+     * @return array
+     */
+    public static function whereNotNull($array)
+    {
+        return static::where($array, fn ($value) => ! is_null($value));
+    }
+
+    /**
+     * 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];
+    }
+}
diff --git a/vendor/illuminate/collections/Collection.php b/vendor/illuminate/collections/Collection.php
new file mode 100644
index 0000000..23bc80d
--- /dev/null
+++ b/vendor/illuminate/collections/Collection.php
@@ -0,0 +1,1753 @@
+
+ * @implements \Illuminate\Support\Enumerable
+ */
+class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable
+{
+    /**
+     * @use \Illuminate\Support\Traits\EnumeratesValues
+     */
+    use EnumeratesValues, Macroable;
+
+    /**
+     * The items contained in the collection.
+     *
+     * @var array
+     */
+    protected $items = [];
+
+    /**
+     * Create a new collection.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable|null  $items
+     * @return void
+     */
+    public function __construct($items = [])
+    {
+        $this->items = $this->getArrayableItems($items);
+    }
+
+    /**
+     * Create a collection with the given range.
+     *
+     * @param  int  $from
+     * @param  int  $to
+     * @return static
+     */
+    public static function range($from, $to)
+    {
+        return new static(range($from, $to));
+    }
+
+    /**
+     * Get all of the items in the collection.
+     *
+     * @return array
+     */
+    public function all()
+    {
+        return $this->items;
+    }
+
+    /**
+     * Get a lazy collection for the items in this collection.
+     *
+     * @return \Illuminate\Support\LazyCollection
+     */
+    public function lazy()
+    {
+        return new LazyCollection($this->items);
+    }
+
+    /**
+     * Get the average value of a given key.
+     *
+     * @param  (callable(TValue): float|int)|string|null  $callback
+     * @return float|int|null
+     */
+    public function avg($callback = null)
+    {
+        $callback = $this->valueRetriever($callback);
+
+        $items = $this
+            ->map(fn ($value) => $callback($value))
+            ->filter(fn ($value) => ! is_null($value));
+
+        if ($count = $items->count()) {
+            return $items->sum() / $count;
+        }
+    }
+
+    /**
+     * Get the median of a given key.
+     *
+     * @param  string|array|null  $key
+     * @return float|int|null
+     */
+    public function median($key = null)
+    {
+        $values = (isset($key) ? $this->pluck($key) : $this)
+            ->filter(fn ($item) => ! is_null($item))
+            ->sort()->values();
+
+        $count = $values->count();
+
+        if ($count === 0) {
+            return;
+        }
+
+        $middle = (int) ($count / 2);
+
+        if ($count % 2) {
+            return $values->get($middle);
+        }
+
+        return (new static([
+            $values->get($middle - 1), $values->get($middle),
+        ]))->average();
+    }
+
+    /**
+     * Get the mode of a given key.
+     *
+     * @param  string|array|null  $key
+     * @return array|null
+     */
+    public function mode($key = null)
+    {
+        if ($this->count() === 0) {
+            return;
+        }
+
+        $collection = isset($key) ? $this->pluck($key) : $this;
+
+        $counts = new static;
+
+        $collection->each(fn ($value) => $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1);
+
+        $sorted = $counts->sort();
+
+        $highestValue = $sorted->last();
+
+        return $sorted->filter(fn ($value) => $value == $highestValue)
+            ->sort()->keys()->all();
+    }
+
+    /**
+     * Collapse the collection of items into a single array.
+     *
+     * @return static
+     */
+    public function collapse()
+    {
+        return new static(Arr::collapse($this->items));
+    }
+
+    /**
+     * Determine if an item exists in the collection.
+     *
+     * @param  (callable(TValue, TKey): bool)|TValue|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function contains($key, $operator = null, $value = null)
+    {
+        if (func_num_args() === 1) {
+            if ($this->useAsCallable($key)) {
+                $placeholder = new stdClass;
+
+                return $this->first($key, $placeholder) !== $placeholder;
+            }
+
+            return in_array($key, $this->items);
+        }
+
+        return $this->contains($this->operatorForWhere(...func_get_args()));
+    }
+
+    /**
+     * Determine if an item exists, using strict comparison.
+     *
+     * @param  (callable(TValue): bool)|TValue|array-key  $key
+     * @param  TValue|null  $value
+     * @return bool
+     */
+    public function containsStrict($key, $value = null)
+    {
+        if (func_num_args() === 2) {
+            return $this->contains(fn ($item) => data_get($item, $key) === $value);
+        }
+
+        if ($this->useAsCallable($key)) {
+            return ! is_null($this->first($key));
+        }
+
+        return in_array($key, $this->items, true);
+    }
+
+    /**
+     * Determine if an item is not contained in the collection.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function doesntContain($key, $operator = null, $value = null)
+    {
+        return ! $this->contains(...func_get_args());
+    }
+
+    /**
+     * Cross join with the given lists, returning all possible permutations.
+     *
+     * @template TCrossJoinKey
+     * @template TCrossJoinValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  ...$lists
+     * @return static>
+     */
+    public function crossJoin(...$lists)
+    {
+        return new static(Arr::crossJoin(
+            $this->items, ...array_map([$this, 'getArrayableItems'], $lists)
+        ));
+    }
+
+    /**
+     * Get the items in the collection that are not present in the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function diff($items)
+    {
+        return new static(array_diff($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Get the items in the collection that are not present in the given items, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TValue, TValue): int  $callback
+     * @return static
+     */
+    public function diffUsing($items, callable $callback)
+    {
+        return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback));
+    }
+
+    /**
+     * Get the items in the collection whose keys and values are not present in the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function diffAssoc($items)
+    {
+        return new static(array_diff_assoc($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Get the items in the collection whose keys and values are not present in the given items, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TKey, TKey): int  $callback
+     * @return static
+     */
+    public function diffAssocUsing($items, callable $callback)
+    {
+        return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback));
+    }
+
+    /**
+     * Get the items in the collection whose keys are not present in the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function diffKeys($items)
+    {
+        return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Get the items in the collection whose keys are not present in the given items, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TKey, TKey): int  $callback
+     * @return static
+     */
+    public function diffKeysUsing($items, callable $callback)
+    {
+        return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback));
+    }
+
+    /**
+     * Retrieve duplicate items from the collection.
+     *
+     * @param  (callable(TValue): bool)|string|null  $callback
+     * @param  bool  $strict
+     * @return static
+     */
+    public function duplicates($callback = null, $strict = false)
+    {
+        $items = $this->map($this->valueRetriever($callback));
+
+        $uniqueItems = $items->unique(null, $strict);
+
+        $compare = $this->duplicateComparator($strict);
+
+        $duplicates = new static;
+
+        foreach ($items as $key => $value) {
+            if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) {
+                $uniqueItems->shift();
+            } else {
+                $duplicates[$key] = $value;
+            }
+        }
+
+        return $duplicates;
+    }
+
+    /**
+     * Retrieve duplicate items from the collection using strict comparison.
+     *
+     * @param  (callable(TValue): bool)|string|null  $callback
+     * @return static
+     */
+    public function duplicatesStrict($callback = null)
+    {
+        return $this->duplicates($callback, true);
+    }
+
+    /**
+     * Get the comparison function to detect duplicates.
+     *
+     * @param  bool  $strict
+     * @return callable(TValue, TValue): bool
+     */
+    protected function duplicateComparator($strict)
+    {
+        if ($strict) {
+            return fn ($a, $b) => $a === $b;
+        }
+
+        return fn ($a, $b) => $a == $b;
+    }
+
+    /**
+     * Get all items except for those with the specified keys.
+     *
+     * @param  \Illuminate\Support\Enumerable|array  $keys
+     * @return static
+     */
+    public function except($keys)
+    {
+        if ($keys instanceof Enumerable) {
+            $keys = $keys->all();
+        } elseif (! is_array($keys)) {
+            $keys = func_get_args();
+        }
+
+        return new static(Arr::except($this->items, $keys));
+    }
+
+    /**
+     * Run a filter over each of the items.
+     *
+     * @param  (callable(TValue, TKey): bool)|null  $callback
+     * @return static
+     */
+    public function filter(callable $callback = null)
+    {
+        if ($callback) {
+            return new static(Arr::where($this->items, $callback));
+        }
+
+        return new static(array_filter($this->items));
+    }
+
+    /**
+     * Get the first item from the collection passing the given truth test.
+     *
+     * @template TFirstDefault
+     *
+     * @param  (callable(TValue, TKey): bool)|null  $callback
+     * @param  TFirstDefault|(\Closure(): TFirstDefault)  $default
+     * @return TValue|TFirstDefault
+     */
+    public function first(callable $callback = null, $default = null)
+    {
+        return Arr::first($this->items, $callback, $default);
+    }
+
+    /**
+     * Get a flattened array of the items in the collection.
+     *
+     * @param  int  $depth
+     * @return static
+     */
+    public function flatten($depth = INF)
+    {
+        return new static(Arr::flatten($this->items, $depth));
+    }
+
+    /**
+     * Flip the items in the collection.
+     *
+     * @return static
+     */
+    public function flip()
+    {
+        return new static(array_flip($this->items));
+    }
+
+    /**
+     * Remove an item from the collection by key.
+     *
+     * @param  TKey|array  $keys
+     * @return $this
+     */
+    public function forget($keys)
+    {
+        foreach ((array) $keys as $key) {
+            $this->offsetUnset($key);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get an item from the collection by key.
+     *
+     * @template TGetDefault
+     *
+     * @param  TKey  $key
+     * @param  TGetDefault|(\Closure(): TGetDefault)  $default
+     * @return TValue|TGetDefault
+     */
+    public function get($key, $default = null)
+    {
+        if (array_key_exists($key, $this->items)) {
+            return $this->items[$key];
+        }
+
+        return value($default);
+    }
+
+    /**
+     * Get an item from the collection by key or add it to collection if it does not exist.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    public function getOrPut($key, $value)
+    {
+        if (array_key_exists($key, $this->items)) {
+            return $this->items[$key];
+        }
+
+        $this->offsetSet($key, $value = value($value));
+
+        return $value;
+    }
+
+    /**
+     * Group an associative array by a field or using a callback.
+     *
+     * @param  (callable(TValue, TKey): array-key)|array|string  $groupBy
+     * @param  bool  $preserveKeys
+     * @return static>
+     */
+    public function groupBy($groupBy, $preserveKeys = false)
+    {
+        if (! $this->useAsCallable($groupBy) && is_array($groupBy)) {
+            $nextGroups = $groupBy;
+
+            $groupBy = array_shift($nextGroups);
+        }
+
+        $groupBy = $this->valueRetriever($groupBy);
+
+        $results = [];
+
+        foreach ($this->items as $key => $value) {
+            $groupKeys = $groupBy($value, $key);
+
+            if (! is_array($groupKeys)) {
+                $groupKeys = [$groupKeys];
+            }
+
+            foreach ($groupKeys as $groupKey) {
+                $groupKey = match (true) {
+                    is_bool($groupKey) => (int) $groupKey,
+                    $groupKey instanceof \Stringable => (string) $groupKey,
+                    default => $groupKey,
+                };
+
+                if (! array_key_exists($groupKey, $results)) {
+                    $results[$groupKey] = new static;
+                }
+
+                $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
+            }
+        }
+
+        $result = new static($results);
+
+        if (! empty($nextGroups)) {
+            return $result->map->groupBy($nextGroups, $preserveKeys);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Key an associative array by a field or using a callback.
+     *
+     * @param  (callable(TValue, TKey): array-key)|array|string  $keyBy
+     * @return static
+     */
+    public function keyBy($keyBy)
+    {
+        $keyBy = $this->valueRetriever($keyBy);
+
+        $results = [];
+
+        foreach ($this->items as $key => $item) {
+            $resolvedKey = $keyBy($item, $key);
+
+            if (is_object($resolvedKey)) {
+                $resolvedKey = (string) $resolvedKey;
+            }
+
+            $results[$resolvedKey] = $item;
+        }
+
+        return new static($results);
+    }
+
+    /**
+     * Determine if an item exists in the collection by key.
+     *
+     * @param  TKey|array  $key
+     * @return bool
+     */
+    public function has($key)
+    {
+        $keys = is_array($key) ? $key : func_get_args();
+
+        foreach ($keys as $value) {
+            if (! array_key_exists($value, $this->items)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Determine if any of the keys exist in the collection.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function hasAny($key)
+    {
+        if ($this->isEmpty()) {
+            return false;
+        }
+
+        $keys = is_array($key) ? $key : func_get_args();
+
+        foreach ($keys as $value) {
+            if ($this->has($value)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Concatenate values of a given key as a string.
+     *
+     * @param  callable|string  $value
+     * @param  string|null  $glue
+     * @return string
+     */
+    public function implode($value, $glue = null)
+    {
+        if ($this->useAsCallable($value)) {
+            return implode($glue ?? '', $this->map($value)->all());
+        }
+
+        $first = $this->first();
+
+        if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) {
+            return implode($glue ?? '', $this->pluck($value)->all());
+        }
+
+        return implode($value ?? '', $this->items);
+    }
+
+    /**
+     * Intersect the collection with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function intersect($items)
+    {
+        return new static(array_intersect($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Intersect the collection with the given items, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TValue, TValue): int  $callback
+     * @return static
+     */
+    public function intersectUsing($items, callable $callback)
+    {
+        return new static(array_uintersect($this->items, $this->getArrayableItems($items), $callback));
+    }
+
+    /**
+     * Intersect the collection with the given items with additional index check.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function intersectAssoc($items)
+    {
+        return new static(array_intersect_assoc($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Intersect the collection with the given items with additional index check, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TValue, TValue): int  $callback
+     * @return static
+     */
+    public function intersectAssocUsing($items, callable $callback)
+    {
+        return new static(array_intersect_uassoc($this->items, $this->getArrayableItems($items), $callback));
+    }
+
+    /**
+     * Intersect the collection with the given items by key.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function intersectByKeys($items)
+    {
+        return new static(array_intersect_key(
+            $this->items, $this->getArrayableItems($items)
+        ));
+    }
+
+    /**
+     * Determine if the collection is empty or not.
+     *
+     * @return bool
+     */
+    public function isEmpty()
+    {
+        return empty($this->items);
+    }
+
+    /**
+     * Determine if the collection contains a single item.
+     *
+     * @return bool
+     */
+    public function containsOneItem()
+    {
+        return $this->count() === 1;
+    }
+
+    /**
+     * Join all items from the collection using a string. The final items can use a separate glue string.
+     *
+     * @param  string  $glue
+     * @param  string  $finalGlue
+     * @return string
+     */
+    public function join($glue, $finalGlue = '')
+    {
+        if ($finalGlue === '') {
+            return $this->implode($glue);
+        }
+
+        $count = $this->count();
+
+        if ($count === 0) {
+            return '';
+        }
+
+        if ($count === 1) {
+            return $this->last();
+        }
+
+        $collection = new static($this->items);
+
+        $finalItem = $collection->pop();
+
+        return $collection->implode($glue).$finalGlue.$finalItem;
+    }
+
+    /**
+     * Get the keys of the collection items.
+     *
+     * @return static
+     */
+    public function keys()
+    {
+        return new static(array_keys($this->items));
+    }
+
+    /**
+     * Get the last item from the collection.
+     *
+     * @template TLastDefault
+     *
+     * @param  (callable(TValue, TKey): bool)|null  $callback
+     * @param  TLastDefault|(\Closure(): TLastDefault)  $default
+     * @return TValue|TLastDefault
+     */
+    public function last(callable $callback = null, $default = null)
+    {
+        return Arr::last($this->items, $callback, $default);
+    }
+
+    /**
+     * Get the values of a given key.
+     *
+     * @param  string|int|array  $value
+     * @param  string|null  $key
+     * @return static
+     */
+    public function pluck($value, $key = null)
+    {
+        return new static(Arr::pluck($this->items, $value, $key));
+    }
+
+    /**
+     * Run a map over each of the items.
+     *
+     * @template TMapValue
+     *
+     * @param  callable(TValue, TKey): TMapValue  $callback
+     * @return static
+     */
+    public function map(callable $callback)
+    {
+        return new static(Arr::map($this->items, $callback));
+    }
+
+    /**
+     * Run a dictionary map over the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @template TMapToDictionaryKey of array-key
+     * @template TMapToDictionaryValue
+     *
+     * @param  callable(TValue, TKey): array  $callback
+     * @return static>
+     */
+    public function mapToDictionary(callable $callback)
+    {
+        $dictionary = [];
+
+        foreach ($this->items as $key => $item) {
+            $pair = $callback($item, $key);
+
+            $key = key($pair);
+
+            $value = reset($pair);
+
+            if (! isset($dictionary[$key])) {
+                $dictionary[$key] = [];
+            }
+
+            $dictionary[$key][] = $value;
+        }
+
+        return new static($dictionary);
+    }
+
+    /**
+     * Run an associative map over each of the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @template TMapWithKeysKey of array-key
+     * @template TMapWithKeysValue
+     *
+     * @param  callable(TValue, TKey): array  $callback
+     * @return static
+     */
+    public function mapWithKeys(callable $callback)
+    {
+        $result = [];
+
+        foreach ($this->items as $key => $value) {
+            $assoc = $callback($value, $key);
+
+            foreach ($assoc as $mapKey => $mapValue) {
+                $result[$mapKey] = $mapValue;
+            }
+        }
+
+        return new static($result);
+    }
+
+    /**
+     * Merge the collection with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function merge($items)
+    {
+        return new static(array_merge($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Recursively merge the collection with the given items.
+     *
+     * @template TMergeRecursiveValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function mergeRecursive($items)
+    {
+        return new static(array_merge_recursive($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Create a collection by using this collection for keys and another for its values.
+     *
+     * @template TCombineValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @return static
+     */
+    public function combine($values)
+    {
+        return new static(array_combine($this->all(), $this->getArrayableItems($values)));
+    }
+
+    /**
+     * Union the collection with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function union($items)
+    {
+        return new static($this->items + $this->getArrayableItems($items));
+    }
+
+    /**
+     * Create a new collection consisting of every n-th element.
+     *
+     * @param  int  $step
+     * @param  int  $offset
+     * @return static
+     */
+    public function nth($step, $offset = 0)
+    {
+        $new = [];
+
+        $position = 0;
+
+        foreach ($this->slice($offset)->items as $item) {
+            if ($position % $step === 0) {
+                $new[] = $item;
+            }
+
+            $position++;
+        }
+
+        return new static($new);
+    }
+
+    /**
+     * Get the items with the specified keys.
+     *
+     * @param  \Illuminate\Support\Enumerable|array|string|null  $keys
+     * @return static
+     */
+    public function only($keys)
+    {
+        if (is_null($keys)) {
+            return new static($this->items);
+        }
+
+        if ($keys instanceof Enumerable) {
+            $keys = $keys->all();
+        }
+
+        $keys = is_array($keys) ? $keys : func_get_args();
+
+        return new static(Arr::only($this->items, $keys));
+    }
+
+    /**
+     * Get and remove the last N items from the collection.
+     *
+     * @param  int  $count
+     * @return static|TValue|null
+     */
+    public function pop($count = 1)
+    {
+        if ($count === 1) {
+            return array_pop($this->items);
+        }
+
+        if ($this->isEmpty()) {
+            return new static;
+        }
+
+        $results = [];
+
+        $collectionCount = $this->count();
+
+        foreach (range(1, min($count, $collectionCount)) as $item) {
+            array_push($results, array_pop($this->items));
+        }
+
+        return new static($results);
+    }
+
+    /**
+     * Push an item onto the beginning of the collection.
+     *
+     * @param  TValue  $value
+     * @param  TKey  $key
+     * @return $this
+     */
+    public function prepend($value, $key = null)
+    {
+        $this->items = Arr::prepend($this->items, ...func_get_args());
+
+        return $this;
+    }
+
+    /**
+     * Push one or more items onto the end of the collection.
+     *
+     * @param  TValue  ...$values
+     * @return $this
+     */
+    public function push(...$values)
+    {
+        foreach ($values as $value) {
+            $this->items[] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Push all of the given items onto the collection.
+     *
+     * @param  iterable  $source
+     * @return static
+     */
+    public function concat($source)
+    {
+        $result = new static($this);
+
+        foreach ($source as $item) {
+            $result->push($item);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Get and remove an item from the collection.
+     *
+     * @template TPullDefault
+     *
+     * @param  TKey  $key
+     * @param  TPullDefault|(\Closure(): TPullDefault)  $default
+     * @return TValue|TPullDefault
+     */
+    public function pull($key, $default = null)
+    {
+        return Arr::pull($this->items, $key, $default);
+    }
+
+    /**
+     * Put an item in the collection by key.
+     *
+     * @param  TKey  $key
+     * @param  TValue  $value
+     * @return $this
+     */
+    public function put($key, $value)
+    {
+        $this->offsetSet($key, $value);
+
+        return $this;
+    }
+
+    /**
+     * Get one or a specified number of items randomly from the collection.
+     *
+     * @param  (callable(self): int)|int|null  $number
+     * @return static|TValue
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function random($number = null)
+    {
+        if (is_null($number)) {
+            return Arr::random($this->items);
+        }
+
+        if (is_callable($number)) {
+            return new static(Arr::random($this->items, $number($this)));
+        }
+
+        return new static(Arr::random($this->items, $number));
+    }
+
+    /**
+     * Replace the collection items with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function replace($items)
+    {
+        return new static(array_replace($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Recursively replace the collection items with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function replaceRecursive($items)
+    {
+        return new static(array_replace_recursive($this->items, $this->getArrayableItems($items)));
+    }
+
+    /**
+     * Reverse items order.
+     *
+     * @return static
+     */
+    public function reverse()
+    {
+        return new static(array_reverse($this->items, true));
+    }
+
+    /**
+     * Search the collection for a given value and return the corresponding key if successful.
+     *
+     * @param  TValue|(callable(TValue,TKey): bool)  $value
+     * @param  bool  $strict
+     * @return TKey|bool
+     */
+    public function search($value, $strict = false)
+    {
+        if (! $this->useAsCallable($value)) {
+            return array_search($value, $this->items, $strict);
+        }
+
+        foreach ($this->items as $key => $item) {
+            if ($value($item, $key)) {
+                return $key;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get and remove the first N items from the collection.
+     *
+     * @param  int  $count
+     * @return static|TValue|null
+     */
+    public function shift($count = 1)
+    {
+        if ($count === 1) {
+            return array_shift($this->items);
+        }
+
+        if ($this->isEmpty()) {
+            return new static;
+        }
+
+        $results = [];
+
+        $collectionCount = $this->count();
+
+        foreach (range(1, min($count, $collectionCount)) as $item) {
+            array_push($results, array_shift($this->items));
+        }
+
+        return new static($results);
+    }
+
+    /**
+     * Shuffle the items in the collection.
+     *
+     * @param  int|null  $seed
+     * @return static
+     */
+    public function shuffle($seed = null)
+    {
+        return new static(Arr::shuffle($this->items, $seed));
+    }
+
+    /**
+     * Create chunks representing a "sliding window" view of the items in the collection.
+     *
+     * @param  int  $size
+     * @param  int  $step
+     * @return static
+     */
+    public function sliding($size = 2, $step = 1)
+    {
+        $chunks = floor(($this->count() - $size) / $step) + 1;
+
+        return static::times($chunks, function ($number) use ($size, $step) {
+            return $this->slice(($number - 1) * $step, $size);
+        });
+    }
+
+    /**
+     * Skip the first {$count} items.
+     *
+     * @param  int  $count
+     * @return static
+     */
+    public function skip($count)
+    {
+        return $this->slice($count);
+    }
+
+    /**
+     * Skip items in the collection until the given condition is met.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @return static
+     */
+    public function skipUntil($value)
+    {
+        return new static($this->lazy()->skipUntil($value)->all());
+    }
+
+    /**
+     * Skip items in the collection while the given condition is met.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @return static
+     */
+    public function skipWhile($value)
+    {
+        return new static($this->lazy()->skipWhile($value)->all());
+    }
+
+    /**
+     * Slice the underlying collection array.
+     *
+     * @param  int  $offset
+     * @param  int|null  $length
+     * @return static
+     */
+    public function slice($offset, $length = null)
+    {
+        return new static(array_slice($this->items, $offset, $length, true));
+    }
+
+    /**
+     * Split a collection into a certain number of groups.
+     *
+     * @param  int  $numberOfGroups
+     * @return static
+     */
+    public function split($numberOfGroups)
+    {
+        if ($this->isEmpty()) {
+            return new static;
+        }
+
+        $groups = new static;
+
+        $groupSize = floor($this->count() / $numberOfGroups);
+
+        $remain = $this->count() % $numberOfGroups;
+
+        $start = 0;
+
+        for ($i = 0; $i < $numberOfGroups; $i++) {
+            $size = $groupSize;
+
+            if ($i < $remain) {
+                $size++;
+            }
+
+            if ($size) {
+                $groups->push(new static(array_slice($this->items, $start, $size)));
+
+                $start += $size;
+            }
+        }
+
+        return $groups;
+    }
+
+    /**
+     * Split a collection into a certain number of groups, and fill the first groups completely.
+     *
+     * @param  int  $numberOfGroups
+     * @return static
+     */
+    public function splitIn($numberOfGroups)
+    {
+        return $this->chunk(ceil($this->count() / $numberOfGroups));
+    }
+
+    /**
+     * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
+     *
+     * @param  (callable(TValue, TKey): bool)|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return TValue
+     *
+     * @throws \Illuminate\Support\ItemNotFoundException
+     * @throws \Illuminate\Support\MultipleItemsFoundException
+     */
+    public function sole($key = null, $operator = null, $value = null)
+    {
+        $filter = func_num_args() > 1
+            ? $this->operatorForWhere(...func_get_args())
+            : $key;
+
+        $items = $this->unless($filter == null)->filter($filter);
+
+        $count = $items->count();
+
+        if ($count === 0) {
+            throw new ItemNotFoundException;
+        }
+
+        if ($count > 1) {
+            throw new MultipleItemsFoundException($count);
+        }
+
+        return $items->first();
+    }
+
+    /**
+     * Get the first item in the collection but throw an exception if no matching items exist.
+     *
+     * @param  (callable(TValue, TKey): bool)|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return TValue
+     *
+     * @throws \Illuminate\Support\ItemNotFoundException
+     */
+    public function firstOrFail($key = null, $operator = null, $value = null)
+    {
+        $filter = func_num_args() > 1
+            ? $this->operatorForWhere(...func_get_args())
+            : $key;
+
+        $placeholder = new stdClass();
+
+        $item = $this->first($filter, $placeholder);
+
+        if ($item === $placeholder) {
+            throw new ItemNotFoundException;
+        }
+
+        return $item;
+    }
+
+    /**
+     * Chunk the collection into chunks of the given size.
+     *
+     * @param  int  $size
+     * @return static
+     */
+    public function chunk($size)
+    {
+        if ($size <= 0) {
+            return new static;
+        }
+
+        $chunks = [];
+
+        foreach (array_chunk($this->items, $size, true) as $chunk) {
+            $chunks[] = new static($chunk);
+        }
+
+        return new static($chunks);
+    }
+
+    /**
+     * Chunk the collection into chunks with a callback.
+     *
+     * @param  callable(TValue, TKey, static): bool  $callback
+     * @return static>
+     */
+    public function chunkWhile(callable $callback)
+    {
+        return new static(
+            $this->lazy()->chunkWhile($callback)->mapInto(static::class)
+        );
+    }
+
+    /**
+     * Sort through each item with a callback.
+     *
+     * @param  (callable(TValue, TValue): int)|null|int  $callback
+     * @return static
+     */
+    public function sort($callback = null)
+    {
+        $items = $this->items;
+
+        $callback && is_callable($callback)
+            ? uasort($items, $callback)
+            : asort($items, $callback ?? SORT_REGULAR);
+
+        return new static($items);
+    }
+
+    /**
+     * Sort items in descending order.
+     *
+     * @param  int  $options
+     * @return static
+     */
+    public function sortDesc($options = SORT_REGULAR)
+    {
+        $items = $this->items;
+
+        arsort($items, $options);
+
+        return new static($items);
+    }
+
+    /**
+     * Sort the collection using the given callback.
+     *
+     * @param  array|(callable(TValue, TKey): mixed)|string  $callback
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return static
+     */
+    public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
+    {
+        if (is_array($callback) && ! is_callable($callback)) {
+            return $this->sortByMany($callback);
+        }
+
+        $results = [];
+
+        $callback = $this->valueRetriever($callback);
+
+        // First we will loop through the items and get the comparator from a callback
+        // function which we were given. Then, we will sort the returned values and
+        // grab all the corresponding values for the sorted keys from this array.
+        foreach ($this->items as $key => $value) {
+            $results[$key] = $callback($value, $key);
+        }
+
+        $descending ? arsort($results, $options)
+            : asort($results, $options);
+
+        // Once we have sorted all of the keys in the array, we will loop through them
+        // and grab the corresponding model so we can set the underlying items list
+        // to the sorted version. Then we'll just return the collection instance.
+        foreach (array_keys($results) as $key) {
+            $results[$key] = $this->items[$key];
+        }
+
+        return new static($results);
+    }
+
+    /**
+     * Sort the collection using multiple comparisons.
+     *
+     * @param  array  $comparisons
+     * @return static
+     */
+    protected function sortByMany(array $comparisons = [])
+    {
+        $items = $this->items;
+
+        uasort($items, function ($a, $b) use ($comparisons) {
+            foreach ($comparisons as $comparison) {
+                $comparison = Arr::wrap($comparison);
+
+                $prop = $comparison[0];
+
+                $ascending = Arr::get($comparison, 1, true) === true ||
+                             Arr::get($comparison, 1, true) === 'asc';
+
+                if (! is_string($prop) && is_callable($prop)) {
+                    $result = $prop($a, $b);
+                } else {
+                    $values = [data_get($a, $prop), data_get($b, $prop)];
+
+                    if (! $ascending) {
+                        $values = array_reverse($values);
+                    }
+
+                    $result = $values[0] <=> $values[1];
+                }
+
+                if ($result === 0) {
+                    continue;
+                }
+
+                return $result;
+            }
+        });
+
+        return new static($items);
+    }
+
+    /**
+     * Sort the collection in descending order using the given callback.
+     *
+     * @param  array|(callable(TValue, TKey): mixed)|string  $callback
+     * @param  int  $options
+     * @return static
+     */
+    public function sortByDesc($callback, $options = SORT_REGULAR)
+    {
+        return $this->sortBy($callback, $options, true);
+    }
+
+    /**
+     * Sort the collection keys.
+     *
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return static
+     */
+    public function sortKeys($options = SORT_REGULAR, $descending = false)
+    {
+        $items = $this->items;
+
+        $descending ? krsort($items, $options) : ksort($items, $options);
+
+        return new static($items);
+    }
+
+    /**
+     * Sort the collection keys in descending order.
+     *
+     * @param  int  $options
+     * @return static
+     */
+    public function sortKeysDesc($options = SORT_REGULAR)
+    {
+        return $this->sortKeys($options, true);
+    }
+
+    /**
+     * Sort the collection keys using a callback.
+     *
+     * @param  callable(TKey, TKey): int  $callback
+     * @return static
+     */
+    public function sortKeysUsing(callable $callback)
+    {
+        $items = $this->items;
+
+        uksort($items, $callback);
+
+        return new static($items);
+    }
+
+    /**
+     * Splice a portion of the underlying collection array.
+     *
+     * @param  int  $offset
+     * @param  int|null  $length
+     * @param  array  $replacement
+     * @return static
+     */
+    public function splice($offset, $length = null, $replacement = [])
+    {
+        if (func_num_args() === 1) {
+            return new static(array_splice($this->items, $offset));
+        }
+
+        return new static(array_splice($this->items, $offset, $length, $this->getArrayableItems($replacement)));
+    }
+
+    /**
+     * Take the first or last {$limit} items.
+     *
+     * @param  int  $limit
+     * @return static
+     */
+    public function take($limit)
+    {
+        if ($limit < 0) {
+            return $this->slice($limit, abs($limit));
+        }
+
+        return $this->slice(0, $limit);
+    }
+
+    /**
+     * Take items in the collection until the given condition is met.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @return static
+     */
+    public function takeUntil($value)
+    {
+        return new static($this->lazy()->takeUntil($value)->all());
+    }
+
+    /**
+     * Take items in the collection while the given condition is met.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @return static
+     */
+    public function takeWhile($value)
+    {
+        return new static($this->lazy()->takeWhile($value)->all());
+    }
+
+    /**
+     * Transform each item in the collection using a callback.
+     *
+     * @param  callable(TValue, TKey): TValue  $callback
+     * @return $this
+     */
+    public function transform(callable $callback)
+    {
+        $this->items = $this->map($callback)->all();
+
+        return $this;
+    }
+
+    /**
+     * Convert a flatten "dot" notation array into an expanded array.
+     *
+     * @return static
+     */
+    public function undot()
+    {
+        return new static(Arr::undot($this->all()));
+    }
+
+    /**
+     * Return only unique items from the collection array.
+     *
+     * @param  (callable(TValue, TKey): mixed)|string|null  $key
+     * @param  bool  $strict
+     * @return static
+     */
+    public function unique($key = null, $strict = false)
+    {
+        if (is_null($key) && $strict === false) {
+            return new static(array_unique($this->items, SORT_REGULAR));
+        }
+
+        $callback = $this->valueRetriever($key);
+
+        $exists = [];
+
+        return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
+            if (in_array($id = $callback($item, $key), $exists, $strict)) {
+                return true;
+            }
+
+            $exists[] = $id;
+        });
+    }
+
+    /**
+     * Reset the keys on the underlying array.
+     *
+     * @return static
+     */
+    public function values()
+    {
+        return new static(array_values($this->items));
+    }
+
+    /**
+     * Zip the collection together with one or more arrays.
+     *
+     * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
+     *      => [[1, 4], [2, 5], [3, 6]]
+     *
+     * @template TZipValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  ...$items
+     * @return static>
+     */
+    public function zip($items)
+    {
+        $arrayableItems = array_map(fn ($items) => $this->getArrayableItems($items), func_get_args());
+
+        $params = array_merge([fn () => new static(func_get_args()), $this->items], $arrayableItems);
+
+        return new static(array_map(...$params));
+    }
+
+    /**
+     * Pad collection to the specified length with a value.
+     *
+     * @template TPadValue
+     *
+     * @param  int  $size
+     * @param  TPadValue  $value
+     * @return static
+     */
+    public function pad($size, $value)
+    {
+        return new static(array_pad($this->items, $size, $value));
+    }
+
+    /**
+     * Get an iterator for the items.
+     *
+     * @return \ArrayIterator
+     */
+    public function getIterator(): Traversable
+    {
+        return new ArrayIterator($this->items);
+    }
+
+    /**
+     * Count the number of items in the collection.
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        return count($this->items);
+    }
+
+    /**
+     * Count the number of items in the collection by a field or using a callback.
+     *
+     * @param  (callable(TValue, TKey): array-key)|string|null  $countBy
+     * @return static
+     */
+    public function countBy($countBy = null)
+    {
+        return new static($this->lazy()->countBy($countBy)->all());
+    }
+
+    /**
+     * Add an item to the collection.
+     *
+     * @param  TValue  $item
+     * @return $this
+     */
+    public function add($item)
+    {
+        $this->items[] = $item;
+
+        return $this;
+    }
+
+    /**
+     * Get a base Support collection instance from this collection.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function toBase()
+    {
+        return new self($this);
+    }
+
+    /**
+     * Determine if an item exists at an offset.
+     *
+     * @param  TKey  $key
+     * @return bool
+     */
+    public function offsetExists($key): bool
+    {
+        return isset($this->items[$key]);
+    }
+
+    /**
+     * Get an item at a given offset.
+     *
+     * @param  TKey  $key
+     * @return TValue
+     */
+    public function offsetGet($key): mixed
+    {
+        return $this->items[$key];
+    }
+
+    /**
+     * Set the item at a given offset.
+     *
+     * @param  TKey|null  $key
+     * @param  TValue  $value
+     * @return void
+     */
+    public function offsetSet($key, $value): void
+    {
+        if (is_null($key)) {
+            $this->items[] = $value;
+        } else {
+            $this->items[$key] = $value;
+        }
+    }
+
+    /**
+     * Unset the item at a given offset.
+     *
+     * @param  TKey  $key
+     * @return void
+     */
+    public function offsetUnset($key): void
+    {
+        unset($this->items[$key]);
+    }
+}
diff --git a/vendor/illuminate/collections/Enumerable.php b/vendor/illuminate/collections/Enumerable.php
new file mode 100644
index 0000000..070b565
--- /dev/null
+++ b/vendor/illuminate/collections/Enumerable.php
@@ -0,0 +1,1257 @@
+
+ * @extends \IteratorAggregate
+ */
+interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable
+{
+    /**
+     * Create a new collection instance if the value isn't one already.
+     *
+     * @template TMakeKey of array-key
+     * @template TMakeValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable|null  $items
+     * @return static
+     */
+    public static function make($items = []);
+
+    /**
+     * Create a new instance by invoking the callback a given amount of times.
+     *
+     * @param  int  $number
+     * @param  callable|null  $callback
+     * @return static
+     */
+    public static function times($number, callable $callback = null);
+
+    /**
+     * Create a collection with the given range.
+     *
+     * @param  int  $from
+     * @param  int  $to
+     * @return static
+     */
+    public static function range($from, $to);
+
+    /**
+     * Wrap the given value in a collection if applicable.
+     *
+     * @template TWrapValue
+     *
+     * @param  iterable|TWrapValue  $value
+     * @return static
+     */
+    public static function wrap($value);
+
+    /**
+     * Get the underlying items from the given collection if applicable.
+     *
+     * @template TUnwrapKey of array-key
+     * @template TUnwrapValue
+     *
+     * @param  array|static  $value
+     * @return array
+     */
+    public static function unwrap($value);
+
+    /**
+     * Create a new instance with no items.
+     *
+     * @return static
+     */
+    public static function empty();
+
+    /**
+     * Get all items in the enumerable.
+     *
+     * @return array
+     */
+    public function all();
+
+    /**
+     * Alias for the "avg" method.
+     *
+     * @param  (callable(TValue): float|int)|string|null  $callback
+     * @return float|int|null
+     */
+    public function average($callback = null);
+
+    /**
+     * Get the median of a given key.
+     *
+     * @param  string|array|null  $key
+     * @return float|int|null
+     */
+    public function median($key = null);
+
+    /**
+     * Get the mode of a given key.
+     *
+     * @param  string|array|null  $key
+     * @return array|null
+     */
+    public function mode($key = null);
+
+    /**
+     * Collapse the items into a single enumerable.
+     *
+     * @return static
+     */
+    public function collapse();
+
+    /**
+     * Alias for the "contains" method.
+     *
+     * @param  (callable(TValue, TKey): bool)|TValue|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function some($key, $operator = null, $value = null);
+
+    /**
+     * Determine if an item exists, using strict comparison.
+     *
+     * @param  (callable(TValue): bool)|TValue|array-key  $key
+     * @param  TValue|null  $value
+     * @return bool
+     */
+    public function containsStrict($key, $value = null);
+
+    /**
+     * Get the average value of a given key.
+     *
+     * @param  (callable(TValue): float|int)|string|null  $callback
+     * @return float|int|null
+     */
+    public function avg($callback = null);
+
+    /**
+     * Determine if an item exists in the enumerable.
+     *
+     * @param  (callable(TValue, TKey): bool)|TValue|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function contains($key, $operator = null, $value = null);
+
+    /**
+     * Determine if an item is not contained in the collection.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function doesntContain($key, $operator = null, $value = null);
+
+    /**
+     * Cross join with the given lists, returning all possible permutations.
+     *
+     * @template TCrossJoinKey
+     * @template TCrossJoinValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  ...$lists
+     * @return static>
+     */
+    public function crossJoin(...$lists);
+
+    /**
+     * Dump the collection and end the script.
+     *
+     * @param  mixed  ...$args
+     * @return never
+     */
+    public function dd(...$args);
+
+    /**
+     * Dump the collection.
+     *
+     * @return $this
+     */
+    public function dump();
+
+    /**
+     * Get the items that are not present in the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function diff($items);
+
+    /**
+     * Get the items that are not present in the given items, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TValue, TValue): int  $callback
+     * @return static
+     */
+    public function diffUsing($items, callable $callback);
+
+    /**
+     * Get the items whose keys and values are not present in the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function diffAssoc($items);
+
+    /**
+     * Get the items whose keys and values are not present in the given items, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TKey, TKey): int  $callback
+     * @return static
+     */
+    public function diffAssocUsing($items, callable $callback);
+
+    /**
+     * Get the items whose keys are not present in the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function diffKeys($items);
+
+    /**
+     * Get the items whose keys are not present in the given items, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TKey, TKey): int  $callback
+     * @return static
+     */
+    public function diffKeysUsing($items, callable $callback);
+
+    /**
+     * Retrieve duplicate items.
+     *
+     * @param  (callable(TValue): bool)|string|null  $callback
+     * @param  bool  $strict
+     * @return static
+     */
+    public function duplicates($callback = null, $strict = false);
+
+    /**
+     * Retrieve duplicate items using strict comparison.
+     *
+     * @param  (callable(TValue): bool)|string|null  $callback
+     * @return static
+     */
+    public function duplicatesStrict($callback = null);
+
+    /**
+     * Execute a callback over each item.
+     *
+     * @param  callable(TValue, TKey): mixed  $callback
+     * @return $this
+     */
+    public function each(callable $callback);
+
+    /**
+     * Execute a callback over each nested chunk of items.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function eachSpread(callable $callback);
+
+    /**
+     * Determine if all items pass the given truth test.
+     *
+     * @param  (callable(TValue, TKey): bool)|TValue|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function every($key, $operator = null, $value = null);
+
+    /**
+     * Get all items except for those with the specified keys.
+     *
+     * @param  \Illuminate\Support\Enumerable|array  $keys
+     * @return static
+     */
+    public function except($keys);
+
+    /**
+     * Run a filter over each of the items.
+     *
+     * @param  (callable(TValue): bool)|null  $callback
+     * @return static
+     */
+    public function filter(callable $callback = null);
+
+    /**
+     * Apply the callback if the given "value" is (or resolves to) truthy.
+     *
+     * @template TWhenReturnType as null
+     *
+     * @param  bool  $value
+     * @param  (callable($this): TWhenReturnType)|null  $callback
+     * @param  (callable($this): TWhenReturnType)|null  $default
+     * @return $this|TWhenReturnType
+     */
+    public function when($value, callable $callback = null, callable $default = null);
+
+    /**
+     * Apply the callback if the collection is empty.
+     *
+     * @template TWhenEmptyReturnType
+     *
+     * @param  (callable($this): TWhenEmptyReturnType)  $callback
+     * @param  (callable($this): TWhenEmptyReturnType)|null  $default
+     * @return $this|TWhenEmptyReturnType
+     */
+    public function whenEmpty(callable $callback, callable $default = null);
+
+    /**
+     * Apply the callback if the collection is not empty.
+     *
+     * @template TWhenNotEmptyReturnType
+     *
+     * @param  callable($this): TWhenNotEmptyReturnType  $callback
+     * @param  (callable($this): TWhenNotEmptyReturnType)|null  $default
+     * @return $this|TWhenNotEmptyReturnType
+     */
+    public function whenNotEmpty(callable $callback, callable $default = null);
+
+    /**
+     * Apply the callback if the given "value" is (or resolves to) truthy.
+     *
+     * @template TUnlessReturnType
+     *
+     * @param  bool  $value
+     * @param  (callable($this): TUnlessReturnType)  $callback
+     * @param  (callable($this): TUnlessReturnType)|null  $default
+     * @return $this|TUnlessReturnType
+     */
+    public function unless($value, callable $callback, callable $default = null);
+
+    /**
+     * Apply the callback unless the collection is empty.
+     *
+     * @template TUnlessEmptyReturnType
+     *
+     * @param  callable($this): TUnlessEmptyReturnType  $callback
+     * @param  (callable($this): TUnlessEmptyReturnType)|null  $default
+     * @return $this|TUnlessEmptyReturnType
+     */
+    public function unlessEmpty(callable $callback, callable $default = null);
+
+    /**
+     * Apply the callback unless the collection is not empty.
+     *
+     * @template TUnlessNotEmptyReturnType
+     *
+     * @param  callable($this): TUnlessNotEmptyReturnType  $callback
+     * @param  (callable($this): TUnlessNotEmptyReturnType)|null  $default
+     * @return $this|TUnlessNotEmptyReturnType
+     */
+    public function unlessNotEmpty(callable $callback, callable $default = null);
+
+    /**
+     * Filter items by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return static
+     */
+    public function where($key, $operator = null, $value = null);
+
+    /**
+     * Filter items where the value for the given key is null.
+     *
+     * @param  string|null  $key
+     * @return static
+     */
+    public function whereNull($key = null);
+
+    /**
+     * Filter items where the value for the given key is not null.
+     *
+     * @param  string|null  $key
+     * @return static
+     */
+    public function whereNotNull($key = null);
+
+    /**
+     * Filter items by the given key value pair using strict comparison.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return static
+     */
+    public function whereStrict($key, $value);
+
+    /**
+     * Filter items by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @param  bool  $strict
+     * @return static
+     */
+    public function whereIn($key, $values, $strict = false);
+
+    /**
+     * Filter items by the given key value pair using strict comparison.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @return static
+     */
+    public function whereInStrict($key, $values);
+
+    /**
+     * Filter items such that the value of the given key is between the given values.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @return static
+     */
+    public function whereBetween($key, $values);
+
+    /**
+     * Filter items such that the value of the given key is not between the given values.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @return static
+     */
+    public function whereNotBetween($key, $values);
+
+    /**
+     * Filter items by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @param  bool  $strict
+     * @return static
+     */
+    public function whereNotIn($key, $values, $strict = false);
+
+    /**
+     * Filter items by the given key value pair using strict comparison.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @return static
+     */
+    public function whereNotInStrict($key, $values);
+
+    /**
+     * Filter the items, removing any items that don't match the given type(s).
+     *
+     * @template TWhereInstanceOf
+     *
+     * @param  class-string|array>  $type
+     * @return static
+     */
+    public function whereInstanceOf($type);
+
+    /**
+     * Get the first item from the enumerable passing the given truth test.
+     *
+     * @template TFirstDefault
+     *
+     * @param  (callable(TValue,TKey): bool)|null  $callback
+     * @param  TFirstDefault|(\Closure(): TFirstDefault)  $default
+     * @return TValue|TFirstDefault
+     */
+    public function first(callable $callback = null, $default = null);
+
+    /**
+     * Get the first item by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return TValue|null
+     */
+    public function firstWhere($key, $operator = null, $value = null);
+
+    /**
+     * Get a flattened array of the items in the collection.
+     *
+     * @param  int  $depth
+     * @return static
+     */
+    public function flatten($depth = INF);
+
+    /**
+     * Flip the values with their keys.
+     *
+     * @return static
+     */
+    public function flip();
+
+    /**
+     * Get an item from the collection by key.
+     *
+     * @template TGetDefault
+     *
+     * @param  TKey  $key
+     * @param  TGetDefault|(\Closure(): TGetDefault)  $default
+     * @return TValue|TGetDefault
+     */
+    public function get($key, $default = null);
+
+    /**
+     * Group an associative array by a field or using a callback.
+     *
+     * @param  (callable(TValue, TKey): array-key)|array|string  $groupBy
+     * @param  bool  $preserveKeys
+     * @return static>
+     */
+    public function groupBy($groupBy, $preserveKeys = false);
+
+    /**
+     * Key an associative array by a field or using a callback.
+     *
+     * @param  (callable(TValue, TKey): array-key)|array|string  $keyBy
+     * @return static
+     */
+    public function keyBy($keyBy);
+
+    /**
+     * Determine if an item exists in the collection by key.
+     *
+     * @param  TKey|array  $key
+     * @return bool
+     */
+    public function has($key);
+
+    /**
+     * Determine if any of the keys exist in the collection.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function hasAny($key);
+
+    /**
+     * Concatenate values of a given key as a string.
+     *
+     * @param  string  $value
+     * @param  string|null  $glue
+     * @return string
+     */
+    public function implode($value, $glue = null);
+
+    /**
+     * Intersect the collection with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function intersect($items);
+
+    /**
+     * Intersect the collection with the given items by key.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function intersectByKeys($items);
+
+    /**
+     * Determine if the collection is empty or not.
+     *
+     * @return bool
+     */
+    public function isEmpty();
+
+    /**
+     * Determine if the collection is not empty.
+     *
+     * @return bool
+     */
+    public function isNotEmpty();
+
+    /**
+     * Determine if the collection contains a single item.
+     *
+     * @return bool
+     */
+    public function containsOneItem();
+
+    /**
+     * Join all items from the collection using a string. The final items can use a separate glue string.
+     *
+     * @param  string  $glue
+     * @param  string  $finalGlue
+     * @return string
+     */
+    public function join($glue, $finalGlue = '');
+
+    /**
+     * Get the keys of the collection items.
+     *
+     * @return static
+     */
+    public function keys();
+
+    /**
+     * Get the last item from the collection.
+     *
+     * @template TLastDefault
+     *
+     * @param  (callable(TValue, TKey): bool)|null  $callback
+     * @param  TLastDefault|(\Closure(): TLastDefault)  $default
+     * @return TValue|TLastDefault
+     */
+    public function last(callable $callback = null, $default = null);
+
+    /**
+     * Run a map over each of the items.
+     *
+     * @template TMapValue
+     *
+     * @param  callable(TValue, TKey): TMapValue  $callback
+     * @return static
+     */
+    public function map(callable $callback);
+
+    /**
+     * Run a map over each nested chunk of items.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function mapSpread(callable $callback);
+
+    /**
+     * Run a dictionary map over the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @template TMapToDictionaryKey of array-key
+     * @template TMapToDictionaryValue
+     *
+     * @param  callable(TValue, TKey): array  $callback
+     * @return static>
+     */
+    public function mapToDictionary(callable $callback);
+
+    /**
+     * Run a grouping map over the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @template TMapToGroupsKey of array-key
+     * @template TMapToGroupsValue
+     *
+     * @param  callable(TValue, TKey): array  $callback
+     * @return static>
+     */
+    public function mapToGroups(callable $callback);
+
+    /**
+     * Run an associative map over each of the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @template TMapWithKeysKey of array-key
+     * @template TMapWithKeysValue
+     *
+     * @param  callable(TValue, TKey): array  $callback
+     * @return static
+     */
+    public function mapWithKeys(callable $callback);
+
+    /**
+     * Map a collection and flatten the result by a single level.
+     *
+     * @template TFlatMapKey of array-key
+     * @template TFlatMapValue
+     *
+     * @param  callable(TValue, TKey): (\Illuminate\Support\Collection|array)  $callback
+     * @return static
+     */
+    public function flatMap(callable $callback);
+
+    /**
+     * Map the values into a new class.
+     *
+     * @template TMapIntoValue
+     *
+     * @param  class-string  $class
+     * @return static
+     */
+    public function mapInto($class);
+
+    /**
+     * Merge the collection with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function merge($items);
+
+    /**
+     * Recursively merge the collection with the given items.
+     *
+     * @template TMergeRecursiveValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function mergeRecursive($items);
+
+    /**
+     * Create a collection by using this collection for keys and another for its values.
+     *
+     * @template TCombineValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @return static
+     */
+    public function combine($values);
+
+    /**
+     * Union the collection with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function union($items);
+
+    /**
+     * Get the min value of a given key.
+     *
+     * @param  (callable(TValue):mixed)|string|null  $callback
+     * @return mixed
+     */
+    public function min($callback = null);
+
+    /**
+     * Get the max value of a given key.
+     *
+     * @param  (callable(TValue):mixed)|string|null  $callback
+     * @return mixed
+     */
+    public function max($callback = null);
+
+    /**
+     * Create a new collection consisting of every n-th element.
+     *
+     * @param  int  $step
+     * @param  int  $offset
+     * @return static
+     */
+    public function nth($step, $offset = 0);
+
+    /**
+     * Get the items with the specified keys.
+     *
+     * @param  \Illuminate\Support\Enumerable|array|string  $keys
+     * @return static
+     */
+    public function only($keys);
+
+    /**
+     * "Paginate" the collection by slicing it into a smaller collection.
+     *
+     * @param  int  $page
+     * @param  int  $perPage
+     * @return static
+     */
+    public function forPage($page, $perPage);
+
+    /**
+     * Partition the collection into two arrays using the given callback or key.
+     *
+     * @param  (callable(TValue, TKey): bool)|TValue|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return static, static>
+     */
+    public function partition($key, $operator = null, $value = null);
+
+    /**
+     * Push all of the given items onto the collection.
+     *
+     * @param  iterable  $source
+     * @return static
+     */
+    public function concat($source);
+
+    /**
+     * Get one or a specified number of items randomly from the collection.
+     *
+     * @param  int|null  $number
+     * @return static|TValue
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function random($number = null);
+
+    /**
+     * Reduce the collection to a single value.
+     *
+     * @template TReduceInitial
+     * @template TReduceReturnType
+     *
+     * @param  callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType  $callback
+     * @param  TReduceInitial  $initial
+     * @return TReduceReturnType
+     */
+    public function reduce(callable $callback, $initial = null);
+
+    /**
+     * Reduce the collection to multiple aggregate values.
+     *
+     * @param  callable  $callback
+     * @param  mixed  ...$initial
+     * @return array
+     *
+     * @throws \UnexpectedValueException
+     */
+    public function reduceSpread(callable $callback, ...$initial);
+
+    /**
+     * Replace the collection items with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function replace($items);
+
+    /**
+     * Recursively replace the collection items with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function replaceRecursive($items);
+
+    /**
+     * Reverse items order.
+     *
+     * @return static
+     */
+    public function reverse();
+
+    /**
+     * Search the collection for a given value and return the corresponding key if successful.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @param  bool  $strict
+     * @return TKey|bool
+     */
+    public function search($value, $strict = false);
+
+    /**
+     * Shuffle the items in the collection.
+     *
+     * @param  int|null  $seed
+     * @return static
+     */
+    public function shuffle($seed = null);
+
+    /**
+     * Create chunks representing a "sliding window" view of the items in the collection.
+     *
+     * @param  int  $size
+     * @param  int  $step
+     * @return static
+     */
+    public function sliding($size = 2, $step = 1);
+
+    /**
+     * Skip the first {$count} items.
+     *
+     * @param  int  $count
+     * @return static
+     */
+    public function skip($count);
+
+    /**
+     * Skip items in the collection until the given condition is met.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @return static
+     */
+    public function skipUntil($value);
+
+    /**
+     * Skip items in the collection while the given condition is met.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @return static
+     */
+    public function skipWhile($value);
+
+    /**
+     * Get a slice of items from the enumerable.
+     *
+     * @param  int  $offset
+     * @param  int|null  $length
+     * @return static
+     */
+    public function slice($offset, $length = null);
+
+    /**
+     * Split a collection into a certain number of groups.
+     *
+     * @param  int  $numberOfGroups
+     * @return static
+     */
+    public function split($numberOfGroups);
+
+    /**
+     * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
+     *
+     * @param  (callable(TValue, TKey): bool)|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return TValue
+     *
+     * @throws \Illuminate\Support\ItemNotFoundException
+     * @throws \Illuminate\Support\MultipleItemsFoundException
+     */
+    public function sole($key = null, $operator = null, $value = null);
+
+    /**
+     * Get the first item in the collection but throw an exception if no matching items exist.
+     *
+     * @param  (callable(TValue, TKey): bool)|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return TValue
+     *
+     * @throws \Illuminate\Support\ItemNotFoundException
+     */
+    public function firstOrFail($key = null, $operator = null, $value = null);
+
+    /**
+     * Chunk the collection into chunks of the given size.
+     *
+     * @param  int  $size
+     * @return static
+     */
+    public function chunk($size);
+
+    /**
+     * Chunk the collection into chunks with a callback.
+     *
+     * @param  callable(TValue, TKey, static): bool  $callback
+     * @return static>
+     */
+    public function chunkWhile(callable $callback);
+
+    /**
+     * Split a collection into a certain number of groups, and fill the first groups completely.
+     *
+     * @param  int  $numberOfGroups
+     * @return static
+     */
+    public function splitIn($numberOfGroups);
+
+    /**
+     * Sort through each item with a callback.
+     *
+     * @param  (callable(TValue, TValue): int)|null|int  $callback
+     * @return static
+     */
+    public function sort($callback = null);
+
+    /**
+     * Sort items in descending order.
+     *
+     * @param  int  $options
+     * @return static
+     */
+    public function sortDesc($options = SORT_REGULAR);
+
+    /**
+     * Sort the collection using the given callback.
+     *
+     * @param  array|(callable(TValue, TKey): mixed)|string  $callback
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return static
+     */
+    public function sortBy($callback, $options = SORT_REGULAR, $descending = false);
+
+    /**
+     * Sort the collection in descending order using the given callback.
+     *
+     * @param  array|(callable(TValue, TKey): mixed)|string  $callback
+     * @param  int  $options
+     * @return static
+     */
+    public function sortByDesc($callback, $options = SORT_REGULAR);
+
+    /**
+     * Sort the collection keys.
+     *
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return static
+     */
+    public function sortKeys($options = SORT_REGULAR, $descending = false);
+
+    /**
+     * Sort the collection keys in descending order.
+     *
+     * @param  int  $options
+     * @return static
+     */
+    public function sortKeysDesc($options = SORT_REGULAR);
+
+    /**
+     * Sort the collection keys using a callback.
+     *
+     * @param  callable(TKey, TKey): int  $callback
+     * @return static
+     */
+    public function sortKeysUsing(callable $callback);
+
+    /**
+     * Get the sum of the given values.
+     *
+     * @param  (callable(TValue): mixed)|string|null  $callback
+     * @return mixed
+     */
+    public function sum($callback = null);
+
+    /**
+     * Take the first or last {$limit} items.
+     *
+     * @param  int  $limit
+     * @return static
+     */
+    public function take($limit);
+
+    /**
+     * Take items in the collection until the given condition is met.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @return static
+     */
+    public function takeUntil($value);
+
+    /**
+     * Take items in the collection while the given condition is met.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @return static
+     */
+    public function takeWhile($value);
+
+    /**
+     * Pass the collection to the given callback and then return it.
+     *
+     * @param  callable(TValue): mixed  $callback
+     * @return $this
+     */
+    public function tap(callable $callback);
+
+    /**
+     * Pass the enumerable to the given callback and return the result.
+     *
+     * @template TPipeReturnType
+     *
+     * @param  callable($this): TPipeReturnType  $callback
+     * @return TPipeReturnType
+     */
+    public function pipe(callable $callback);
+
+    /**
+     * Pass the collection into a new class.
+     *
+     * @param  class-string  $class
+     * @return mixed
+     */
+    public function pipeInto($class);
+
+    /**
+     * Pass the collection through a series of callable pipes and return the result.
+     *
+     * @param  array  $pipes
+     * @return mixed
+     */
+    public function pipeThrough($pipes);
+
+    /**
+     * Get the values of a given key.
+     *
+     * @param  string|array  $value
+     * @param  string|null  $key
+     * @return static
+     */
+    public function pluck($value, $key = null);
+
+    /**
+     * Create a collection of all elements that do not pass a given truth test.
+     *
+     * @param  (callable(TValue, TKey): bool)|bool|TValue  $callback
+     * @return static
+     */
+    public function reject($callback = true);
+
+    /**
+     * Convert a flatten "dot" notation array into an expanded array.
+     *
+     * @return static
+     */
+    public function undot();
+
+    /**
+     * Return only unique items from the collection array.
+     *
+     * @param  (callable(TValue, TKey): mixed)|string|null  $key
+     * @param  bool  $strict
+     * @return static
+     */
+    public function unique($key = null, $strict = false);
+
+    /**
+     * Return only unique items from the collection array using strict comparison.
+     *
+     * @param  (callable(TValue, TKey): mixed)|string|null  $key
+     * @return static
+     */
+    public function uniqueStrict($key = null);
+
+    /**
+     * Reset the keys on the underlying array.
+     *
+     * @return static
+     */
+    public function values();
+
+    /**
+     * Pad collection to the specified length with a value.
+     *
+     * @template TPadValue
+     *
+     * @param  int  $size
+     * @param  TPadValue  $value
+     * @return static
+     */
+    public function pad($size, $value);
+
+    /**
+     * Get the values iterator.
+     *
+     * @return \Traversable
+     */
+    public function getIterator(): Traversable;
+
+    /**
+     * Count the number of items in the collection.
+     *
+     * @return int
+     */
+    public function count(): int;
+
+    /**
+     * Count the number of items in the collection by a field or using a callback.
+     *
+     * @param  (callable(TValue, TKey): array-key)|string|null  $countBy
+     * @return static
+     */
+    public function countBy($countBy = null);
+
+    /**
+     * Zip the collection together with one or more arrays.
+     *
+     * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
+     *      => [[1, 4], [2, 5], [3, 6]]
+     *
+     * @template TZipValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  ...$items
+     * @return static>
+     */
+    public function zip($items);
+
+    /**
+     * Collect the values into a collection.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function collect();
+
+    /**
+     * Get the collection of items as a plain array.
+     *
+     * @return array
+     */
+    public function toArray();
+
+    /**
+     * Convert the object into something JSON serializable.
+     *
+     * @return mixed
+     */
+    public function jsonSerialize(): mixed;
+
+    /**
+     * Get the collection of items as JSON.
+     *
+     * @param  int  $options
+     * @return string
+     */
+    public function toJson($options = 0);
+
+    /**
+     * Get a CachingIterator instance.
+     *
+     * @param  int  $flags
+     * @return \CachingIterator
+     */
+    public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING);
+
+    /**
+     * Convert the collection to its string representation.
+     *
+     * @return string
+     */
+    public function __toString();
+
+    /**
+     * Indicate that the model's string representation should be escaped when __toString is invoked.
+     *
+     * @param  bool  $escape
+     * @return $this
+     */
+    public function escapeWhenCastingToString($escape = true);
+
+    /**
+     * Add a method to the list of proxied methods.
+     *
+     * @param  string  $method
+     * @return void
+     */
+    public static function proxy($method);
+
+    /**
+     * Dynamically access collection proxies.
+     *
+     * @param  string  $key
+     * @return mixed
+     *
+     * @throws \Exception
+     */
+    public function __get($key);
+}
diff --git a/vendor/illuminate/collections/HigherOrderCollectionProxy.php b/vendor/illuminate/collections/HigherOrderCollectionProxy.php
new file mode 100644
index 0000000..106356c
--- /dev/null
+++ b/vendor/illuminate/collections/HigherOrderCollectionProxy.php
@@ -0,0 +1,63 @@
+method = $method;
+        $this->collection = $collection;
+    }
+
+    /**
+     * Proxy accessing an attribute onto the collection items.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function __get($key)
+    {
+        return $this->collection->{$this->method}(function ($value) use ($key) {
+            return is_array($value) ? $value[$key] : $value->{$key};
+        });
+    }
+
+    /**
+     * Proxy a method call onto the collection items.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->collection->{$this->method}(function ($value) use ($method, $parameters) {
+            return $value->{$method}(...$parameters);
+        });
+    }
+}
diff --git a/vendor/illuminate/collections/ItemNotFoundException.php b/vendor/illuminate/collections/ItemNotFoundException.php
new file mode 100644
index 0000000..05a51d9
--- /dev/null
+++ b/vendor/illuminate/collections/ItemNotFoundException.php
@@ -0,0 +1,9 @@
+
+ */
+class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable
+{
+    /**
+     * @use \Illuminate\Support\Traits\EnumeratesValues
+     */
+    use EnumeratesValues, Macroable;
+
+    /**
+     * The source from which to generate items.
+     *
+     * @var (Closure(): \Generator)|static|array
+     */
+    public $source;
+
+    /**
+     * Create a new lazy collection instance.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null  $source
+     * @return void
+     */
+    public function __construct($source = null)
+    {
+        if ($source instanceof Closure || $source instanceof self) {
+            $this->source = $source;
+        } elseif (is_null($source)) {
+            $this->source = static::empty();
+        } elseif ($source instanceof Generator) {
+            throw new InvalidArgumentException(
+                'Generators should not be passed directly to LazyCollection. Instead, pass a generator function.'
+            );
+        } else {
+            $this->source = $this->getArrayableItems($source);
+        }
+    }
+
+    /**
+     * Create a new collection instance if the value isn't one already.
+     *
+     * @template TMakeKey of array-key
+     * @template TMakeValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null  $items
+     * @return static
+     */
+    public static function make($items = [])
+    {
+        return new static($items);
+    }
+
+    /**
+     * Create a collection with the given range.
+     *
+     * @param  int  $from
+     * @param  int  $to
+     * @return static
+     */
+    public static function range($from, $to)
+    {
+        return new static(function () use ($from, $to) {
+            if ($from <= $to) {
+                for (; $from <= $to; $from++) {
+                    yield $from;
+                }
+            } else {
+                for (; $from >= $to; $from--) {
+                    yield $from;
+                }
+            }
+        });
+    }
+
+    /**
+     * Get all items in the enumerable.
+     *
+     * @return array
+     */
+    public function all()
+    {
+        if (is_array($this->source)) {
+            return $this->source;
+        }
+
+        return iterator_to_array($this->getIterator());
+    }
+
+    /**
+     * Eager load all items into a new lazy collection backed by an array.
+     *
+     * @return static
+     */
+    public function eager()
+    {
+        return new static($this->all());
+    }
+
+    /**
+     * Cache values as they're enumerated.
+     *
+     * @return static
+     */
+    public function remember()
+    {
+        $iterator = $this->getIterator();
+
+        $iteratorIndex = 0;
+
+        $cache = [];
+
+        return new static(function () use ($iterator, &$iteratorIndex, &$cache) {
+            for ($index = 0; true; $index++) {
+                if (array_key_exists($index, $cache)) {
+                    yield $cache[$index][0] => $cache[$index][1];
+
+                    continue;
+                }
+
+                if ($iteratorIndex < $index) {
+                    $iterator->next();
+
+                    $iteratorIndex++;
+                }
+
+                if (! $iterator->valid()) {
+                    break;
+                }
+
+                $cache[$index] = [$iterator->key(), $iterator->current()];
+
+                yield $cache[$index][0] => $cache[$index][1];
+            }
+        });
+    }
+
+    /**
+     * Get the average value of a given key.
+     *
+     * @param  (callable(TValue): float|int)|string|null  $callback
+     * @return float|int|null
+     */
+    public function avg($callback = null)
+    {
+        return $this->collect()->avg($callback);
+    }
+
+    /**
+     * Get the median of a given key.
+     *
+     * @param  string|array|null  $key
+     * @return float|int|null
+     */
+    public function median($key = null)
+    {
+        return $this->collect()->median($key);
+    }
+
+    /**
+     * Get the mode of a given key.
+     *
+     * @param  string|array|null  $key
+     * @return array|null
+     */
+    public function mode($key = null)
+    {
+        return $this->collect()->mode($key);
+    }
+
+    /**
+     * Collapse the collection of items into a single array.
+     *
+     * @return static
+     */
+    public function collapse()
+    {
+        return new static(function () {
+            foreach ($this as $values) {
+                if (is_array($values) || $values instanceof Enumerable) {
+                    foreach ($values as $value) {
+                        yield $value;
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Determine if an item exists in the enumerable.
+     *
+     * @param  (callable(TValue, TKey): bool)|TValue|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function contains($key, $operator = null, $value = null)
+    {
+        if (func_num_args() === 1 && $this->useAsCallable($key)) {
+            $placeholder = new stdClass;
+
+            /** @var callable $key */
+            return $this->first($key, $placeholder) !== $placeholder;
+        }
+
+        if (func_num_args() === 1) {
+            $needle = $key;
+
+            foreach ($this as $value) {
+                if ($value == $needle) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        return $this->contains($this->operatorForWhere(...func_get_args()));
+    }
+
+    /**
+     * Determine if an item exists, using strict comparison.
+     *
+     * @param  (callable(TValue): bool)|TValue|array-key  $key
+     * @param  TValue|null  $value
+     * @return bool
+     */
+    public function containsStrict($key, $value = null)
+    {
+        if (func_num_args() === 2) {
+            return $this->contains(fn ($item) => data_get($item, $key) === $value);
+        }
+
+        if ($this->useAsCallable($key)) {
+            return ! is_null($this->first($key));
+        }
+
+        foreach ($this as $item) {
+            if ($item === $key) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine if an item is not contained in the enumerable.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function doesntContain($key, $operator = null, $value = null)
+    {
+        return ! $this->contains(...func_get_args());
+    }
+
+    /**
+     * Cross join the given iterables, returning all possible permutations.
+     *
+     * @template TCrossJoinKey
+     * @template TCrossJoinValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  ...$arrays
+     * @return static>
+     */
+    public function crossJoin(...$arrays)
+    {
+        return $this->passthru('crossJoin', func_get_args());
+    }
+
+    /**
+     * Count the number of items in the collection by a field or using a callback.
+     *
+     * @param  (callable(TValue, TKey): array-key)|string|null  $countBy
+     * @return static
+     */
+    public function countBy($countBy = null)
+    {
+        $countBy = is_null($countBy)
+            ? $this->identity()
+            : $this->valueRetriever($countBy);
+
+        return new static(function () use ($countBy) {
+            $counts = [];
+
+            foreach ($this as $key => $value) {
+                $group = $countBy($value, $key);
+
+                if (empty($counts[$group])) {
+                    $counts[$group] = 0;
+                }
+
+                $counts[$group]++;
+            }
+
+            yield from $counts;
+        });
+    }
+
+    /**
+     * Get the items that are not present in the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function diff($items)
+    {
+        return $this->passthru('diff', func_get_args());
+    }
+
+    /**
+     * Get the items that are not present in the given items, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TValue, TValue): int  $callback
+     * @return static
+     */
+    public function diffUsing($items, callable $callback)
+    {
+        return $this->passthru('diffUsing', func_get_args());
+    }
+
+    /**
+     * Get the items whose keys and values are not present in the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function diffAssoc($items)
+    {
+        return $this->passthru('diffAssoc', func_get_args());
+    }
+
+    /**
+     * Get the items whose keys and values are not present in the given items, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TKey, TKey): int  $callback
+     * @return static
+     */
+    public function diffAssocUsing($items, callable $callback)
+    {
+        return $this->passthru('diffAssocUsing', func_get_args());
+    }
+
+    /**
+     * Get the items whose keys are not present in the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function diffKeys($items)
+    {
+        return $this->passthru('diffKeys', func_get_args());
+    }
+
+    /**
+     * Get the items whose keys are not present in the given items, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TKey, TKey): int  $callback
+     * @return static
+     */
+    public function diffKeysUsing($items, callable $callback)
+    {
+        return $this->passthru('diffKeysUsing', func_get_args());
+    }
+
+    /**
+     * Retrieve duplicate items.
+     *
+     * @param  (callable(TValue): bool)|string|null  $callback
+     * @param  bool  $strict
+     * @return static
+     */
+    public function duplicates($callback = null, $strict = false)
+    {
+        return $this->passthru('duplicates', func_get_args());
+    }
+
+    /**
+     * Retrieve duplicate items using strict comparison.
+     *
+     * @param  (callable(TValue): bool)|string|null  $callback
+     * @return static
+     */
+    public function duplicatesStrict($callback = null)
+    {
+        return $this->passthru('duplicatesStrict', func_get_args());
+    }
+
+    /**
+     * Get all items except for those with the specified keys.
+     *
+     * @param  \Illuminate\Support\Enumerable|array  $keys
+     * @return static
+     */
+    public function except($keys)
+    {
+        return $this->passthru('except', func_get_args());
+    }
+
+    /**
+     * Run a filter over each of the items.
+     *
+     * @param  (callable(TValue, TKey): bool)|null  $callback
+     * @return static
+     */
+    public function filter(callable $callback = null)
+    {
+        if (is_null($callback)) {
+            $callback = fn ($value) => (bool) $value;
+        }
+
+        return new static(function () use ($callback) {
+            foreach ($this as $key => $value) {
+                if ($callback($value, $key)) {
+                    yield $key => $value;
+                }
+            }
+        });
+    }
+
+    /**
+     * Get the first item from the enumerable passing the given truth test.
+     *
+     * @template TFirstDefault
+     *
+     * @param  (callable(TValue): bool)|null  $callback
+     * @param  TFirstDefault|(\Closure(): TFirstDefault)  $default
+     * @return TValue|TFirstDefault
+     */
+    public function first(callable $callback = null, $default = null)
+    {
+        $iterator = $this->getIterator();
+
+        if (is_null($callback)) {
+            if (! $iterator->valid()) {
+                return value($default);
+            }
+
+            return $iterator->current();
+        }
+
+        foreach ($iterator as $key => $value) {
+            if ($callback($value, $key)) {
+                return $value;
+            }
+        }
+
+        return value($default);
+    }
+
+    /**
+     * Get a flattened list of the items in the collection.
+     *
+     * @param  int  $depth
+     * @return static
+     */
+    public function flatten($depth = INF)
+    {
+        $instance = new static(function () use ($depth) {
+            foreach ($this as $item) {
+                if (! is_array($item) && ! $item instanceof Enumerable) {
+                    yield $item;
+                } elseif ($depth === 1) {
+                    yield from $item;
+                } else {
+                    yield from (new static($item))->flatten($depth - 1);
+                }
+            }
+        });
+
+        return $instance->values();
+    }
+
+    /**
+     * Flip the items in the collection.
+     *
+     * @return static
+     */
+    public function flip()
+    {
+        return new static(function () {
+            foreach ($this as $key => $value) {
+                yield $value => $key;
+            }
+        });
+    }
+
+    /**
+     * Get an item by key.
+     *
+     * @template TGetDefault
+     *
+     * @param  TKey|null  $key
+     * @param  TGetDefault|(\Closure(): TGetDefault)  $default
+     * @return TValue|TGetDefault
+     */
+    public function get($key, $default = null)
+    {
+        if (is_null($key)) {
+            return;
+        }
+
+        foreach ($this as $outerKey => $outerValue) {
+            if ($outerKey == $key) {
+                return $outerValue;
+            }
+        }
+
+        return value($default);
+    }
+
+    /**
+     * Group an associative array by a field or using a callback.
+     *
+     * @param  (callable(TValue, TKey): array-key)|array|string  $groupBy
+     * @param  bool  $preserveKeys
+     * @return static>
+     */
+    public function groupBy($groupBy, $preserveKeys = false)
+    {
+        return $this->passthru('groupBy', func_get_args());
+    }
+
+    /**
+     * Key an associative array by a field or using a callback.
+     *
+     * @param  (callable(TValue, TKey): array-key)|array|string  $keyBy
+     * @return static
+     */
+    public function keyBy($keyBy)
+    {
+        return new static(function () use ($keyBy) {
+            $keyBy = $this->valueRetriever($keyBy);
+
+            foreach ($this as $key => $item) {
+                $resolvedKey = $keyBy($item, $key);
+
+                if (is_object($resolvedKey)) {
+                    $resolvedKey = (string) $resolvedKey;
+                }
+
+                yield $resolvedKey => $item;
+            }
+        });
+    }
+
+    /**
+     * Determine if an item exists in the collection by key.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function has($key)
+    {
+        $keys = array_flip(is_array($key) ? $key : func_get_args());
+        $count = count($keys);
+
+        foreach ($this as $key => $value) {
+            if (array_key_exists($key, $keys) && --$count == 0) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine if any of the keys exist in the collection.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function hasAny($key)
+    {
+        $keys = array_flip(is_array($key) ? $key : func_get_args());
+
+        foreach ($this as $key => $value) {
+            if (array_key_exists($key, $keys)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Concatenate values of a given key as a string.
+     *
+     * @param  callable|string  $value
+     * @param  string|null  $glue
+     * @return string
+     */
+    public function implode($value, $glue = null)
+    {
+        return $this->collect()->implode(...func_get_args());
+    }
+
+    /**
+     * Intersect the collection with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function intersect($items)
+    {
+        return $this->passthru('intersect', func_get_args());
+    }
+
+    /**
+     * Intersect the collection with the given items, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TValue, TValue): int  $callback
+     * @return static
+     */
+    public function intersectUsing()
+    {
+        return $this->passthru('intersectUsing', func_get_args());
+    }
+
+    /**
+     * Intersect the collection with the given items with additional index check.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function intersectAssoc($items)
+    {
+        return $this->passthru('intersectAssoc', func_get_args());
+    }
+
+    /**
+     * Intersect the collection with the given items with additional index check, using the callback.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @param  callable(TValue, TValue): int  $callback
+     * @return static
+     */
+    public function intersectAssocUsing($items, callable $callback)
+    {
+        return $this->passthru('intersectAssocUsing', func_get_args());
+    }
+
+    /**
+     * Intersect the collection with the given items by key.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function intersectByKeys($items)
+    {
+        return $this->passthru('intersectByKeys', func_get_args());
+    }
+
+    /**
+     * Determine if the items are empty or not.
+     *
+     * @return bool
+     */
+    public function isEmpty()
+    {
+        return ! $this->getIterator()->valid();
+    }
+
+    /**
+     * Determine if the collection contains a single item.
+     *
+     * @return bool
+     */
+    public function containsOneItem()
+    {
+        return $this->take(2)->count() === 1;
+    }
+
+    /**
+     * Join all items from the collection using a string. The final items can use a separate glue string.
+     *
+     * @param  string  $glue
+     * @param  string  $finalGlue
+     * @return string
+     */
+    public function join($glue, $finalGlue = '')
+    {
+        return $this->collect()->join(...func_get_args());
+    }
+
+    /**
+     * Get the keys of the collection items.
+     *
+     * @return static
+     */
+    public function keys()
+    {
+        return new static(function () {
+            foreach ($this as $key => $value) {
+                yield $key;
+            }
+        });
+    }
+
+    /**
+     * Get the last item from the collection.
+     *
+     * @template TLastDefault
+     *
+     * @param  (callable(TValue, TKey): bool)|null  $callback
+     * @param  TLastDefault|(\Closure(): TLastDefault)  $default
+     * @return TValue|TLastDefault
+     */
+    public function last(callable $callback = null, $default = null)
+    {
+        $needle = $placeholder = new stdClass;
+
+        foreach ($this as $key => $value) {
+            if (is_null($callback) || $callback($value, $key)) {
+                $needle = $value;
+            }
+        }
+
+        return $needle === $placeholder ? value($default) : $needle;
+    }
+
+    /**
+     * Get the values of a given key.
+     *
+     * @param  string|array  $value
+     * @param  string|null  $key
+     * @return static
+     */
+    public function pluck($value, $key = null)
+    {
+        return new static(function () use ($value, $key) {
+            [$value, $key] = $this->explodePluckParameters($value, $key);
+
+            foreach ($this as $item) {
+                $itemValue = data_get($item, $value);
+
+                if (is_null($key)) {
+                    yield $itemValue;
+                } else {
+                    $itemKey = data_get($item, $key);
+
+                    if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
+                        $itemKey = (string) $itemKey;
+                    }
+
+                    yield $itemKey => $itemValue;
+                }
+            }
+        });
+    }
+
+    /**
+     * Run a map over each of the items.
+     *
+     * @template TMapValue
+     *
+     * @param  callable(TValue, TKey): TMapValue  $callback
+     * @return static
+     */
+    public function map(callable $callback)
+    {
+        return new static(function () use ($callback) {
+            foreach ($this as $key => $value) {
+                yield $key => $callback($value, $key);
+            }
+        });
+    }
+
+    /**
+     * Run a dictionary map over the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @template TMapToDictionaryKey of array-key
+     * @template TMapToDictionaryValue
+     *
+     * @param  callable(TValue, TKey): array  $callback
+     * @return static>
+     */
+    public function mapToDictionary(callable $callback)
+    {
+        return $this->passthru('mapToDictionary', func_get_args());
+    }
+
+    /**
+     * Run an associative map over each of the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @template TMapWithKeysKey of array-key
+     * @template TMapWithKeysValue
+     *
+     * @param  callable(TValue, TKey): array  $callback
+     * @return static
+     */
+    public function mapWithKeys(callable $callback)
+    {
+        return new static(function () use ($callback) {
+            foreach ($this as $key => $value) {
+                yield from $callback($value, $key);
+            }
+        });
+    }
+
+    /**
+     * Merge the collection with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function merge($items)
+    {
+        return $this->passthru('merge', func_get_args());
+    }
+
+    /**
+     * Recursively merge the collection with the given items.
+     *
+     * @template TMergeRecursiveValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function mergeRecursive($items)
+    {
+        return $this->passthru('mergeRecursive', func_get_args());
+    }
+
+    /**
+     * Create a collection by using this collection for keys and another for its values.
+     *
+     * @template TCombineValue
+     *
+     * @param  \IteratorAggregate|array|(callable(): \Generator)  $values
+     * @return static
+     */
+    public function combine($values)
+    {
+        return new static(function () use ($values) {
+            $values = $this->makeIterator($values);
+
+            $errorMessage = 'Both parameters should have an equal number of elements';
+
+            foreach ($this as $key) {
+                if (! $values->valid()) {
+                    trigger_error($errorMessage, E_USER_WARNING);
+
+                    break;
+                }
+
+                yield $key => $values->current();
+
+                $values->next();
+            }
+
+            if ($values->valid()) {
+                trigger_error($errorMessage, E_USER_WARNING);
+            }
+        });
+    }
+
+    /**
+     * Union the collection with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function union($items)
+    {
+        return $this->passthru('union', func_get_args());
+    }
+
+    /**
+     * Create a new collection consisting of every n-th element.
+     *
+     * @param  int  $step
+     * @param  int  $offset
+     * @return static
+     */
+    public function nth($step, $offset = 0)
+    {
+        return new static(function () use ($step, $offset) {
+            $position = 0;
+
+            foreach ($this->slice($offset) as $item) {
+                if ($position % $step === 0) {
+                    yield $item;
+                }
+
+                $position++;
+            }
+        });
+    }
+
+    /**
+     * Get the items with the specified keys.
+     *
+     * @param  \Illuminate\Support\Enumerable|array|string  $keys
+     * @return static
+     */
+    public function only($keys)
+    {
+        if ($keys instanceof Enumerable) {
+            $keys = $keys->all();
+        } elseif (! is_null($keys)) {
+            $keys = is_array($keys) ? $keys : func_get_args();
+        }
+
+        return new static(function () use ($keys) {
+            if (is_null($keys)) {
+                yield from $this;
+            } else {
+                $keys = array_flip($keys);
+
+                foreach ($this as $key => $value) {
+                    if (array_key_exists($key, $keys)) {
+                        yield $key => $value;
+
+                        unset($keys[$key]);
+
+                        if (empty($keys)) {
+                            break;
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Push all of the given items onto the collection.
+     *
+     * @param  iterable  $source
+     * @return static
+     */
+    public function concat($source)
+    {
+        return (new static(function () use ($source) {
+            yield from $this;
+            yield from $source;
+        }))->values();
+    }
+
+    /**
+     * Get one or a specified number of items randomly from the collection.
+     *
+     * @param  int|null  $number
+     * @return static|TValue
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function random($number = null)
+    {
+        $result = $this->collect()->random(...func_get_args());
+
+        return is_null($number) ? $result : new static($result);
+    }
+
+    /**
+     * Replace the collection items with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function replace($items)
+    {
+        return new static(function () use ($items) {
+            $items = $this->getArrayableItems($items);
+
+            foreach ($this as $key => $value) {
+                if (array_key_exists($key, $items)) {
+                    yield $key => $items[$key];
+
+                    unset($items[$key]);
+                } else {
+                    yield $key => $value;
+                }
+            }
+
+            foreach ($items as $key => $value) {
+                yield $key => $value;
+            }
+        });
+    }
+
+    /**
+     * Recursively replace the collection items with the given items.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $items
+     * @return static
+     */
+    public function replaceRecursive($items)
+    {
+        return $this->passthru('replaceRecursive', func_get_args());
+    }
+
+    /**
+     * Reverse items order.
+     *
+     * @return static
+     */
+    public function reverse()
+    {
+        return $this->passthru('reverse', func_get_args());
+    }
+
+    /**
+     * Search the collection for a given value and return the corresponding key if successful.
+     *
+     * @param  TValue|(callable(TValue,TKey): bool)  $value
+     * @param  bool  $strict
+     * @return TKey|bool
+     */
+    public function search($value, $strict = false)
+    {
+        /** @var (callable(TValue,TKey): bool) $predicate */
+        $predicate = $this->useAsCallable($value)
+            ? $value
+            : function ($item) use ($value, $strict) {
+                return $strict ? $item === $value : $item == $value;
+            };
+
+        foreach ($this as $key => $item) {
+            if ($predicate($item, $key)) {
+                return $key;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Shuffle the items in the collection.
+     *
+     * @param  int|null  $seed
+     * @return static
+     */
+    public function shuffle($seed = null)
+    {
+        return $this->passthru('shuffle', func_get_args());
+    }
+
+    /**
+     * Create chunks representing a "sliding window" view of the items in the collection.
+     *
+     * @param  int  $size
+     * @param  int  $step
+     * @return static
+     */
+    public function sliding($size = 2, $step = 1)
+    {
+        return new static(function () use ($size, $step) {
+            $iterator = $this->getIterator();
+
+            $chunk = [];
+
+            while ($iterator->valid()) {
+                $chunk[$iterator->key()] = $iterator->current();
+
+                if (count($chunk) == $size) {
+                    yield (new static($chunk))->tap(function () use (&$chunk, $step) {
+                        $chunk = array_slice($chunk, $step, null, true);
+                    });
+
+                    // If the $step between chunks is bigger than each chunk's $size
+                    // we will skip the extra items (which should never be in any
+                    // chunk) before we continue to the next chunk in the loop.
+                    if ($step > $size) {
+                        $skip = $step - $size;
+
+                        for ($i = 0; $i < $skip && $iterator->valid(); $i++) {
+                            $iterator->next();
+                        }
+                    }
+                }
+
+                $iterator->next();
+            }
+        });
+    }
+
+    /**
+     * Skip the first {$count} items.
+     *
+     * @param  int  $count
+     * @return static
+     */
+    public function skip($count)
+    {
+        return new static(function () use ($count) {
+            $iterator = $this->getIterator();
+
+            while ($iterator->valid() && $count--) {
+                $iterator->next();
+            }
+
+            while ($iterator->valid()) {
+                yield $iterator->key() => $iterator->current();
+
+                $iterator->next();
+            }
+        });
+    }
+
+    /**
+     * Skip items in the collection until the given condition is met.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @return static
+     */
+    public function skipUntil($value)
+    {
+        $callback = $this->useAsCallable($value) ? $value : $this->equality($value);
+
+        return $this->skipWhile($this->negate($callback));
+    }
+
+    /**
+     * Skip items in the collection while the given condition is met.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @return static
+     */
+    public function skipWhile($value)
+    {
+        $callback = $this->useAsCallable($value) ? $value : $this->equality($value);
+
+        return new static(function () use ($callback) {
+            $iterator = $this->getIterator();
+
+            while ($iterator->valid() && $callback($iterator->current(), $iterator->key())) {
+                $iterator->next();
+            }
+
+            while ($iterator->valid()) {
+                yield $iterator->key() => $iterator->current();
+
+                $iterator->next();
+            }
+        });
+    }
+
+    /**
+     * Get a slice of items from the enumerable.
+     *
+     * @param  int  $offset
+     * @param  int|null  $length
+     * @return static
+     */
+    public function slice($offset, $length = null)
+    {
+        if ($offset < 0 || $length < 0) {
+            return $this->passthru('slice', func_get_args());
+        }
+
+        $instance = $this->skip($offset);
+
+        return is_null($length) ? $instance : $instance->take($length);
+    }
+
+    /**
+     * Split a collection into a certain number of groups.
+     *
+     * @param  int  $numberOfGroups
+     * @return static
+     */
+    public function split($numberOfGroups)
+    {
+        return $this->passthru('split', func_get_args());
+    }
+
+    /**
+     * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
+     *
+     * @param  (callable(TValue, TKey): bool)|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return TValue
+     *
+     * @throws \Illuminate\Support\ItemNotFoundException
+     * @throws \Illuminate\Support\MultipleItemsFoundException
+     */
+    public function sole($key = null, $operator = null, $value = null)
+    {
+        $filter = func_num_args() > 1
+            ? $this->operatorForWhere(...func_get_args())
+            : $key;
+
+        return $this
+            ->unless($filter == null)
+            ->filter($filter)
+            ->take(2)
+            ->collect()
+            ->sole();
+    }
+
+    /**
+     * Get the first item in the collection but throw an exception if no matching items exist.
+     *
+     * @param  (callable(TValue, TKey): bool)|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return TValue
+     *
+     * @throws \Illuminate\Support\ItemNotFoundException
+     */
+    public function firstOrFail($key = null, $operator = null, $value = null)
+    {
+        $filter = func_num_args() > 1
+            ? $this->operatorForWhere(...func_get_args())
+            : $key;
+
+        return $this
+            ->unless($filter == null)
+            ->filter($filter)
+            ->take(1)
+            ->collect()
+            ->firstOrFail();
+    }
+
+    /**
+     * Chunk the collection into chunks of the given size.
+     *
+     * @param  int  $size
+     * @return static
+     */
+    public function chunk($size)
+    {
+        if ($size <= 0) {
+            return static::empty();
+        }
+
+        return new static(function () use ($size) {
+            $iterator = $this->getIterator();
+
+            while ($iterator->valid()) {
+                $chunk = [];
+
+                while (true) {
+                    $chunk[$iterator->key()] = $iterator->current();
+
+                    if (count($chunk) < $size) {
+                        $iterator->next();
+
+                        if (! $iterator->valid()) {
+                            break;
+                        }
+                    } else {
+                        break;
+                    }
+                }
+
+                yield new static($chunk);
+
+                $iterator->next();
+            }
+        });
+    }
+
+    /**
+     * Split a collection into a certain number of groups, and fill the first groups completely.
+     *
+     * @param  int  $numberOfGroups
+     * @return static
+     */
+    public function splitIn($numberOfGroups)
+    {
+        return $this->chunk(ceil($this->count() / $numberOfGroups));
+    }
+
+    /**
+     * Chunk the collection into chunks with a callback.
+     *
+     * @param  callable(TValue, TKey, Collection): bool  $callback
+     * @return static>
+     */
+    public function chunkWhile(callable $callback)
+    {
+        return new static(function () use ($callback) {
+            $iterator = $this->getIterator();
+
+            $chunk = new Collection;
+
+            if ($iterator->valid()) {
+                $chunk[$iterator->key()] = $iterator->current();
+
+                $iterator->next();
+            }
+
+            while ($iterator->valid()) {
+                if (! $callback($iterator->current(), $iterator->key(), $chunk)) {
+                    yield new static($chunk);
+
+                    $chunk = new Collection;
+                }
+
+                $chunk[$iterator->key()] = $iterator->current();
+
+                $iterator->next();
+            }
+
+            if ($chunk->isNotEmpty()) {
+                yield new static($chunk);
+            }
+        });
+    }
+
+    /**
+     * Sort through each item with a callback.
+     *
+     * @param  (callable(TValue, TValue): int)|null|int  $callback
+     * @return static
+     */
+    public function sort($callback = null)
+    {
+        return $this->passthru('sort', func_get_args());
+    }
+
+    /**
+     * Sort items in descending order.
+     *
+     * @param  int  $options
+     * @return static
+     */
+    public function sortDesc($options = SORT_REGULAR)
+    {
+        return $this->passthru('sortDesc', func_get_args());
+    }
+
+    /**
+     * Sort the collection using the given callback.
+     *
+     * @param  array|(callable(TValue, TKey): mixed)|string  $callback
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return static
+     */
+    public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
+    {
+        return $this->passthru('sortBy', func_get_args());
+    }
+
+    /**
+     * Sort the collection in descending order using the given callback.
+     *
+     * @param  array|(callable(TValue, TKey): mixed)|string  $callback
+     * @param  int  $options
+     * @return static
+     */
+    public function sortByDesc($callback, $options = SORT_REGULAR)
+    {
+        return $this->passthru('sortByDesc', func_get_args());
+    }
+
+    /**
+     * Sort the collection keys.
+     *
+     * @param  int  $options
+     * @param  bool  $descending
+     * @return static
+     */
+    public function sortKeys($options = SORT_REGULAR, $descending = false)
+    {
+        return $this->passthru('sortKeys', func_get_args());
+    }
+
+    /**
+     * Sort the collection keys in descending order.
+     *
+     * @param  int  $options
+     * @return static
+     */
+    public function sortKeysDesc($options = SORT_REGULAR)
+    {
+        return $this->passthru('sortKeysDesc', func_get_args());
+    }
+
+    /**
+     * Sort the collection keys using a callback.
+     *
+     * @param  callable(TKey, TKey): int  $callback
+     * @return static
+     */
+    public function sortKeysUsing(callable $callback)
+    {
+        return $this->passthru('sortKeysUsing', func_get_args());
+    }
+
+    /**
+     * Take the first or last {$limit} items.
+     *
+     * @param  int  $limit
+     * @return static
+     */
+    public function take($limit)
+    {
+        if ($limit < 0) {
+            return $this->passthru('take', func_get_args());
+        }
+
+        return new static(function () use ($limit) {
+            $iterator = $this->getIterator();
+
+            while ($limit--) {
+                if (! $iterator->valid()) {
+                    break;
+                }
+
+                yield $iterator->key() => $iterator->current();
+
+                if ($limit) {
+                    $iterator->next();
+                }
+            }
+        });
+    }
+
+    /**
+     * Take items in the collection until the given condition is met.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @return static
+     */
+    public function takeUntil($value)
+    {
+        /** @var callable(TValue, TKey): bool $callback */
+        $callback = $this->useAsCallable($value) ? $value : $this->equality($value);
+
+        return new static(function () use ($callback) {
+            foreach ($this as $key => $item) {
+                if ($callback($item, $key)) {
+                    break;
+                }
+
+                yield $key => $item;
+            }
+        });
+    }
+
+    /**
+     * Take items in the collection until a given point in time.
+     *
+     * @param  \DateTimeInterface  $timeout
+     * @return static
+     */
+    public function takeUntilTimeout(DateTimeInterface $timeout)
+    {
+        $timeout = $timeout->getTimestamp();
+
+        return new static(function () use ($timeout) {
+            if ($this->now() >= $timeout) {
+                return;
+            }
+
+            foreach ($this as $key => $value) {
+                yield $key => $value;
+
+                if ($this->now() >= $timeout) {
+                    break;
+                }
+            }
+        });
+    }
+
+    /**
+     * Take items in the collection while the given condition is met.
+     *
+     * @param  TValue|callable(TValue,TKey): bool  $value
+     * @return static
+     */
+    public function takeWhile($value)
+    {
+        /** @var callable(TValue, TKey): bool $callback */
+        $callback = $this->useAsCallable($value) ? $value : $this->equality($value);
+
+        return $this->takeUntil(fn ($item, $key) => ! $callback($item, $key));
+    }
+
+    /**
+     * Pass each item in the collection to the given callback, lazily.
+     *
+     * @param  callable(TValue, TKey): mixed  $callback
+     * @return static
+     */
+    public function tapEach(callable $callback)
+    {
+        return new static(function () use ($callback) {
+            foreach ($this as $key => $value) {
+                $callback($value, $key);
+
+                yield $key => $value;
+            }
+        });
+    }
+
+    /**
+     * Convert a flatten "dot" notation array into an expanded array.
+     *
+     * @return static
+     */
+    public function undot()
+    {
+        return $this->passthru('undot', []);
+    }
+
+    /**
+     * Return only unique items from the collection array.
+     *
+     * @param  (callable(TValue, TKey): mixed)|string|null  $key
+     * @param  bool  $strict
+     * @return static
+     */
+    public function unique($key = null, $strict = false)
+    {
+        $callback = $this->valueRetriever($key);
+
+        return new static(function () use ($callback, $strict) {
+            $exists = [];
+
+            foreach ($this as $key => $item) {
+                if (! in_array($id = $callback($item, $key), $exists, $strict)) {
+                    yield $key => $item;
+
+                    $exists[] = $id;
+                }
+            }
+        });
+    }
+
+    /**
+     * Reset the keys on the underlying array.
+     *
+     * @return static
+     */
+    public function values()
+    {
+        return new static(function () {
+            foreach ($this as $item) {
+                yield $item;
+            }
+        });
+    }
+
+    /**
+     * Zip the collection together with one or more arrays.
+     *
+     * e.g. new LazyCollection([1, 2, 3])->zip([4, 5, 6]);
+     *      => [[1, 4], [2, 5], [3, 6]]
+     *
+     * @template TZipValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  ...$items
+     * @return static>
+     */
+    public function zip($items)
+    {
+        $iterables = func_get_args();
+
+        return new static(function () use ($iterables) {
+            $iterators = Collection::make($iterables)->map(function ($iterable) {
+                return $this->makeIterator($iterable);
+            })->prepend($this->getIterator());
+
+            while ($iterators->contains->valid()) {
+                yield new static($iterators->map->current());
+
+                $iterators->each->next();
+            }
+        });
+    }
+
+    /**
+     * Pad collection to the specified length with a value.
+     *
+     * @template TPadValue
+     *
+     * @param  int  $size
+     * @param  TPadValue  $value
+     * @return static
+     */
+    public function pad($size, $value)
+    {
+        if ($size < 0) {
+            return $this->passthru('pad', func_get_args());
+        }
+
+        return new static(function () use ($size, $value) {
+            $yielded = 0;
+
+            foreach ($this as $index => $item) {
+                yield $index => $item;
+
+                $yielded++;
+            }
+
+            while ($yielded++ < $size) {
+                yield $value;
+            }
+        });
+    }
+
+    /**
+     * Get the values iterator.
+     *
+     * @return \Traversable
+     */
+    public function getIterator(): Traversable
+    {
+        return $this->makeIterator($this->source);
+    }
+
+    /**
+     * Count the number of items in the collection.
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        if (is_array($this->source)) {
+            return count($this->source);
+        }
+
+        return iterator_count($this->getIterator());
+    }
+
+    /**
+     * Make an iterator from the given source.
+     *
+     * @template TIteratorKey of array-key
+     * @template TIteratorValue
+     *
+     * @param  \IteratorAggregate|array|(callable(): \Generator)  $source
+     * @return \Traversable
+     */
+    protected function makeIterator($source)
+    {
+        if ($source instanceof IteratorAggregate) {
+            return $source->getIterator();
+        }
+
+        if (is_array($source)) {
+            return new ArrayIterator($source);
+        }
+
+        if (is_callable($source)) {
+            $maybeTraversable = $source();
+
+            return $maybeTraversable instanceof Traversable
+                ? $maybeTraversable
+                : new ArrayIterator(Arr::wrap($maybeTraversable));
+        }
+
+        return new ArrayIterator((array) $source);
+    }
+
+    /**
+     * Explode the "value" and "key" arguments passed to "pluck".
+     *
+     * @param  string|string[]  $value
+     * @param  string|string[]|null  $key
+     * @return array{string[],string[]|null}
+     */
+    protected function explodePluckParameters($value, $key)
+    {
+        $value = is_string($value) ? explode('.', $value) : $value;
+
+        $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
+
+        return [$value, $key];
+    }
+
+    /**
+     * Pass this lazy collection through a method on the collection class.
+     *
+     * @param  string  $method
+     * @param  array  $params
+     * @return static
+     */
+    protected function passthru($method, array $params)
+    {
+        return new static(function () use ($method, $params) {
+            yield from $this->collect()->$method(...$params);
+        });
+    }
+
+    /**
+     * Get the current time.
+     *
+     * @return int
+     */
+    protected function now()
+    {
+        return time();
+    }
+}
diff --git a/vendor/illuminate/collections/MultipleItemsFoundException.php b/vendor/illuminate/collections/MultipleItemsFoundException.php
new file mode 100644
index 0000000..d90d835
--- /dev/null
+++ b/vendor/illuminate/collections/MultipleItemsFoundException.php
@@ -0,0 +1,40 @@
+count = $count;
+
+        parent::__construct("$count items were found.", $code, $previous);
+    }
+
+    /**
+     * Get the number of items found.
+     *
+     * @return int
+     */
+    public function getCount()
+    {
+        return $this->count;
+    }
+}
diff --git a/vendor/illuminate/collections/Traits/EnumeratesValues.php b/vendor/illuminate/collections/Traits/EnumeratesValues.php
new file mode 100644
index 0000000..2ffbe8d
--- /dev/null
+++ b/vendor/illuminate/collections/Traits/EnumeratesValues.php
@@ -0,0 +1,1083 @@
+
+     */
+    protected static $proxies = [
+        'average',
+        'avg',
+        'contains',
+        'doesntContain',
+        'each',
+        'every',
+        'filter',
+        'first',
+        'flatMap',
+        'groupBy',
+        'keyBy',
+        'map',
+        'max',
+        'min',
+        'partition',
+        'reject',
+        'skipUntil',
+        'skipWhile',
+        'some',
+        'sortBy',
+        'sortByDesc',
+        'sum',
+        'takeUntil',
+        'takeWhile',
+        'unique',
+        'unless',
+        'until',
+        'when',
+    ];
+
+    /**
+     * Create a new collection instance if the value isn't one already.
+     *
+     * @template TMakeKey of array-key
+     * @template TMakeValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable|null  $items
+     * @return static
+     */
+    public static function make($items = [])
+    {
+        return new static($items);
+    }
+
+    /**
+     * Wrap the given value in a collection if applicable.
+     *
+     * @template TWrapValue
+     *
+     * @param  iterable|TWrapValue  $value
+     * @return static
+     */
+    public static function wrap($value)
+    {
+        return $value instanceof Enumerable
+            ? new static($value)
+            : new static(Arr::wrap($value));
+    }
+
+    /**
+     * Get the underlying items from the given collection if applicable.
+     *
+     * @template TUnwrapKey of array-key
+     * @template TUnwrapValue
+     *
+     * @param  array|static  $value
+     * @return array
+     */
+    public static function unwrap($value)
+    {
+        return $value instanceof Enumerable ? $value->all() : $value;
+    }
+
+    /**
+     * Create a new instance with no items.
+     *
+     * @return static
+     */
+    public static function empty()
+    {
+        return new static([]);
+    }
+
+    /**
+     * Create a new collection by invoking the callback a given amount of times.
+     *
+     * @template TTimesValue
+     *
+     * @param  int  $number
+     * @param  (callable(int): TTimesValue)|null  $callback
+     * @return static
+     */
+    public static function times($number, callable $callback = null)
+    {
+        if ($number < 1) {
+            return new static;
+        }
+
+        return static::range(1, $number)
+            ->unless($callback == null)
+            ->map($callback);
+    }
+
+    /**
+     * Alias for the "avg" method.
+     *
+     * @param  (callable(TValue): float|int)|string|null  $callback
+     * @return float|int|null
+     */
+    public function average($callback = null)
+    {
+        return $this->avg($callback);
+    }
+
+    /**
+     * Alias for the "contains" method.
+     *
+     * @param  (callable(TValue, TKey): bool)|TValue|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function some($key, $operator = null, $value = null)
+    {
+        return $this->contains(...func_get_args());
+    }
+
+    /**
+     * Dump the items and end the script.
+     *
+     * @param  mixed  ...$args
+     * @return never
+     */
+    public function dd(...$args)
+    {
+        $this->dump(...$args);
+
+        exit(1);
+    }
+
+    /**
+     * Dump the items.
+     *
+     * @return $this
+     */
+    public function dump()
+    {
+        (new Collection(func_get_args()))
+            ->push($this->all())
+            ->each(function ($item) {
+                VarDumper::dump($item);
+            });
+
+        return $this;
+    }
+
+    /**
+     * Execute a callback over each item.
+     *
+     * @param  callable(TValue, TKey): mixed  $callback
+     * @return $this
+     */
+    public function each(callable $callback)
+    {
+        foreach ($this as $key => $item) {
+            if ($callback($item, $key) === false) {
+                break;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Execute a callback over each nested chunk of items.
+     *
+     * @param  callable(...mixed): mixed  $callback
+     * @return static
+     */
+    public function eachSpread(callable $callback)
+    {
+        return $this->each(function ($chunk, $key) use ($callback) {
+            $chunk[] = $key;
+
+            return $callback(...$chunk);
+        });
+    }
+
+    /**
+     * Determine if all items pass the given truth test.
+     *
+     * @param  (callable(TValue, TKey): bool)|TValue|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function every($key, $operator = null, $value = null)
+    {
+        if (func_num_args() === 1) {
+            $callback = $this->valueRetriever($key);
+
+            foreach ($this as $k => $v) {
+                if (! $callback($v, $k)) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        return $this->every($this->operatorForWhere(...func_get_args()));
+    }
+
+    /**
+     * Get the first item by the given key value pair.
+     *
+     * @param  callable|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return TValue|null
+     */
+    public function firstWhere($key, $operator = null, $value = null)
+    {
+        return $this->first($this->operatorForWhere(...func_get_args()));
+    }
+
+    /**
+     * Get a single key's value from the first matching item in the collection.
+     *
+     * @param  string  $key
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public function value($key, $default = null)
+    {
+        if ($value = $this->firstWhere($key)) {
+            return data_get($value, $key, $default);
+        }
+
+        return value($default);
+    }
+
+    /**
+     * Determine if the collection is not empty.
+     *
+     * @return bool
+     */
+    public function isNotEmpty()
+    {
+        return ! $this->isEmpty();
+    }
+
+    /**
+     * Run a map over each nested chunk of items.
+     *
+     * @template TMapSpreadValue
+     *
+     * @param  callable(mixed): TMapSpreadValue  $callback
+     * @return static
+     */
+    public function mapSpread(callable $callback)
+    {
+        return $this->map(function ($chunk, $key) use ($callback) {
+            $chunk[] = $key;
+
+            return $callback(...$chunk);
+        });
+    }
+
+    /**
+     * Run a grouping map over the items.
+     *
+     * The callback should return an associative array with a single key/value pair.
+     *
+     * @template TMapToGroupsKey of array-key
+     * @template TMapToGroupsValue
+     *
+     * @param  callable(TValue, TKey): array  $callback
+     * @return static>
+     */
+    public function mapToGroups(callable $callback)
+    {
+        $groups = $this->mapToDictionary($callback);
+
+        return $groups->map([$this, 'make']);
+    }
+
+    /**
+     * Map a collection and flatten the result by a single level.
+     *
+     * @template TFlatMapKey of array-key
+     * @template TFlatMapValue
+     *
+     * @param  callable(TValue, TKey): (\Illuminate\Support\Collection|array)  $callback
+     * @return static
+     */
+    public function flatMap(callable $callback)
+    {
+        return $this->map($callback)->collapse();
+    }
+
+    /**
+     * Map the values into a new class.
+     *
+     * @template TMapIntoValue
+     *
+     * @param  class-string  $class
+     * @return static
+     */
+    public function mapInto($class)
+    {
+        return $this->map(fn ($value, $key) => new $class($value, $key));
+    }
+
+    /**
+     * Get the min value of a given key.
+     *
+     * @param  (callable(TValue):mixed)|string|null  $callback
+     * @return mixed
+     */
+    public function min($callback = null)
+    {
+        $callback = $this->valueRetriever($callback);
+
+        return $this->map(fn ($value) => $callback($value))
+            ->filter(fn ($value) => ! is_null($value))
+            ->reduce(fn ($result, $value) => is_null($result) || $value < $result ? $value : $result);
+    }
+
+    /**
+     * Get the max value of a given key.
+     *
+     * @param  (callable(TValue):mixed)|string|null  $callback
+     * @return mixed
+     */
+    public function max($callback = null)
+    {
+        $callback = $this->valueRetriever($callback);
+
+        return $this->filter(fn ($value) => ! is_null($value))->reduce(function ($result, $item) use ($callback) {
+            $value = $callback($item);
+
+            return is_null($result) || $value > $result ? $value : $result;
+        });
+    }
+
+    /**
+     * "Paginate" the collection by slicing it into a smaller collection.
+     *
+     * @param  int  $page
+     * @param  int  $perPage
+     * @return static
+     */
+    public function forPage($page, $perPage)
+    {
+        $offset = max(0, ($page - 1) * $perPage);
+
+        return $this->slice($offset, $perPage);
+    }
+
+    /**
+     * Partition the collection into two arrays using the given callback or key.
+     *
+     * @param  (callable(TValue, TKey): bool)|TValue|string  $key
+     * @param  TValue|string|null  $operator
+     * @param  TValue|null  $value
+     * @return static, static>
+     */
+    public function partition($key, $operator = null, $value = null)
+    {
+        $passed = [];
+        $failed = [];
+
+        $callback = func_num_args() === 1
+                ? $this->valueRetriever($key)
+                : $this->operatorForWhere(...func_get_args());
+
+        foreach ($this as $key => $item) {
+            if ($callback($item, $key)) {
+                $passed[$key] = $item;
+            } else {
+                $failed[$key] = $item;
+            }
+        }
+
+        return new static([new static($passed), new static($failed)]);
+    }
+
+    /**
+     * Get the sum of the given values.
+     *
+     * @param  (callable(TValue): mixed)|string|null  $callback
+     * @return mixed
+     */
+    public function sum($callback = null)
+    {
+        $callback = is_null($callback)
+            ? $this->identity()
+            : $this->valueRetriever($callback);
+
+        return $this->reduce(fn ($result, $item) => $result + $callback($item), 0);
+    }
+
+    /**
+     * Apply the callback if the collection is empty.
+     *
+     * @template TWhenEmptyReturnType
+     *
+     * @param  (callable($this): TWhenEmptyReturnType)  $callback
+     * @param  (callable($this): TWhenEmptyReturnType)|null  $default
+     * @return $this|TWhenEmptyReturnType
+     */
+    public function whenEmpty(callable $callback, callable $default = null)
+    {
+        return $this->when($this->isEmpty(), $callback, $default);
+    }
+
+    /**
+     * Apply the callback if the collection is not empty.
+     *
+     * @template TWhenNotEmptyReturnType
+     *
+     * @param  callable($this): TWhenNotEmptyReturnType  $callback
+     * @param  (callable($this): TWhenNotEmptyReturnType)|null  $default
+     * @return $this|TWhenNotEmptyReturnType
+     */
+    public function whenNotEmpty(callable $callback, callable $default = null)
+    {
+        return $this->when($this->isNotEmpty(), $callback, $default);
+    }
+
+    /**
+     * Apply the callback unless the collection is empty.
+     *
+     * @template TUnlessEmptyReturnType
+     *
+     * @param  callable($this): TUnlessEmptyReturnType  $callback
+     * @param  (callable($this): TUnlessEmptyReturnType)|null  $default
+     * @return $this|TUnlessEmptyReturnType
+     */
+    public function unlessEmpty(callable $callback, callable $default = null)
+    {
+        return $this->whenNotEmpty($callback, $default);
+    }
+
+    /**
+     * Apply the callback unless the collection is not empty.
+     *
+     * @template TUnlessNotEmptyReturnType
+     *
+     * @param  callable($this): TUnlessNotEmptyReturnType  $callback
+     * @param  (callable($this): TUnlessNotEmptyReturnType)|null  $default
+     * @return $this|TUnlessNotEmptyReturnType
+     */
+    public function unlessNotEmpty(callable $callback, callable $default = null)
+    {
+        return $this->whenEmpty($callback, $default);
+    }
+
+    /**
+     * Filter items by the given key value pair.
+     *
+     * @param  callable|string  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return static
+     */
+    public function where($key, $operator = null, $value = null)
+    {
+        return $this->filter($this->operatorForWhere(...func_get_args()));
+    }
+
+    /**
+     * Filter items where the value for the given key is null.
+     *
+     * @param  string|null  $key
+     * @return static
+     */
+    public function whereNull($key = null)
+    {
+        return $this->whereStrict($key, null);
+    }
+
+    /**
+     * Filter items where the value for the given key is not null.
+     *
+     * @param  string|null  $key
+     * @return static
+     */
+    public function whereNotNull($key = null)
+    {
+        return $this->where($key, '!==', null);
+    }
+
+    /**
+     * Filter items by the given key value pair using strict comparison.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return static
+     */
+    public function whereStrict($key, $value)
+    {
+        return $this->where($key, '===', $value);
+    }
+
+    /**
+     * Filter items by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @param  bool  $strict
+     * @return static
+     */
+    public function whereIn($key, $values, $strict = false)
+    {
+        $values = $this->getArrayableItems($values);
+
+        return $this->filter(fn ($item) => in_array(data_get($item, $key), $values, $strict));
+    }
+
+    /**
+     * Filter items by the given key value pair using strict comparison.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @return static
+     */
+    public function whereInStrict($key, $values)
+    {
+        return $this->whereIn($key, $values, true);
+    }
+
+    /**
+     * Filter items such that the value of the given key is between the given values.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @return static
+     */
+    public function whereBetween($key, $values)
+    {
+        return $this->where($key, '>=', reset($values))->where($key, '<=', end($values));
+    }
+
+    /**
+     * Filter items such that the value of the given key is not between the given values.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @return static
+     */
+    public function whereNotBetween($key, $values)
+    {
+        return $this->filter(
+            fn ($item) => data_get($item, $key) < reset($values) || data_get($item, $key) > end($values)
+        );
+    }
+
+    /**
+     * Filter items by the given key value pair.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @param  bool  $strict
+     * @return static
+     */
+    public function whereNotIn($key, $values, $strict = false)
+    {
+        $values = $this->getArrayableItems($values);
+
+        return $this->reject(fn ($item) => in_array(data_get($item, $key), $values, $strict));
+    }
+
+    /**
+     * Filter items by the given key value pair using strict comparison.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  $values
+     * @return static
+     */
+    public function whereNotInStrict($key, $values)
+    {
+        return $this->whereNotIn($key, $values, true);
+    }
+
+    /**
+     * Filter the items, removing any items that don't match the given type(s).
+     *
+     * @template TWhereInstanceOf
+     *
+     * @param  class-string|array>  $type
+     * @return static
+     */
+    public function whereInstanceOf($type)
+    {
+        return $this->filter(function ($value) use ($type) {
+            if (is_array($type)) {
+                foreach ($type as $classType) {
+                    if ($value instanceof $classType) {
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+
+            return $value instanceof $type;
+        });
+    }
+
+    /**
+     * Pass the collection to the given callback and return the result.
+     *
+     * @template TPipeReturnType
+     *
+     * @param  callable($this): TPipeReturnType  $callback
+     * @return TPipeReturnType
+     */
+    public function pipe(callable $callback)
+    {
+        return $callback($this);
+    }
+
+    /**
+     * Pass the collection into a new class.
+     *
+     * @param  class-string  $class
+     * @return mixed
+     */
+    public function pipeInto($class)
+    {
+        return new $class($this);
+    }
+
+    /**
+     * Pass the collection through a series of callable pipes and return the result.
+     *
+     * @param  array  $callbacks
+     * @return mixed
+     */
+    public function pipeThrough($callbacks)
+    {
+        return Collection::make($callbacks)->reduce(
+            fn ($carry, $callback) => $callback($carry),
+            $this,
+        );
+    }
+
+    /**
+     * Reduce the collection to a single value.
+     *
+     * @template TReduceInitial
+     * @template TReduceReturnType
+     *
+     * @param  callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType  $callback
+     * @param  TReduceInitial  $initial
+     * @return TReduceReturnType
+     */
+    public function reduce(callable $callback, $initial = null)
+    {
+        $result = $initial;
+
+        foreach ($this as $key => $value) {
+            $result = $callback($result, $value, $key);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Reduce the collection to multiple aggregate values.
+     *
+     * @param  callable  $callback
+     * @param  mixed  ...$initial
+     * @return array
+     *
+     * @throws \UnexpectedValueException
+     */
+    public function reduceSpread(callable $callback, ...$initial)
+    {
+        $result = $initial;
+
+        foreach ($this as $key => $value) {
+            $result = call_user_func_array($callback, array_merge($result, [$value, $key]));
+
+            if (! is_array($result)) {
+                throw new UnexpectedValueException(sprintf(
+                    "%s::reduceSpread expects reducer to return an array, but got a '%s' instead.",
+                    class_basename(static::class), gettype($result)
+                ));
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Create a collection of all elements that do not pass a given truth test.
+     *
+     * @param  (callable(TValue, TKey): bool)|bool|TValue  $callback
+     * @return static
+     */
+    public function reject($callback = true)
+    {
+        $useAsCallable = $this->useAsCallable($callback);
+
+        return $this->filter(function ($value, $key) use ($callback, $useAsCallable) {
+            return $useAsCallable
+                ? ! $callback($value, $key)
+                : $value != $callback;
+        });
+    }
+
+    /**
+     * Pass the collection to the given callback and then return it.
+     *
+     * @param  callable($this): mixed  $callback
+     * @return $this
+     */
+    public function tap(callable $callback)
+    {
+        $callback($this);
+
+        return $this;
+    }
+
+    /**
+     * Return only unique items from the collection array.
+     *
+     * @param  (callable(TValue, TKey): mixed)|string|null  $key
+     * @param  bool  $strict
+     * @return static
+     */
+    public function unique($key = null, $strict = false)
+    {
+        $callback = $this->valueRetriever($key);
+
+        $exists = [];
+
+        return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
+            if (in_array($id = $callback($item, $key), $exists, $strict)) {
+                return true;
+            }
+
+            $exists[] = $id;
+        });
+    }
+
+    /**
+     * Return only unique items from the collection array using strict comparison.
+     *
+     * @param  (callable(TValue, TKey): mixed)|string|null  $key
+     * @return static
+     */
+    public function uniqueStrict($key = null)
+    {
+        return $this->unique($key, true);
+    }
+
+    /**
+     * Collect the values into a collection.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function collect()
+    {
+        return new Collection($this->all());
+    }
+
+    /**
+     * Get the collection of items as a plain array.
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return $this->map(fn ($value) => $value instanceof Arrayable ? $value->toArray() : $value)->all();
+    }
+
+    /**
+     * Convert the object into something JSON serializable.
+     *
+     * @return array
+     */
+    public function jsonSerialize(): array
+    {
+        return array_map(function ($value) {
+            if ($value instanceof JsonSerializable) {
+                return $value->jsonSerialize();
+            } elseif ($value instanceof Jsonable) {
+                return json_decode($value->toJson(), true);
+            } elseif ($value instanceof Arrayable) {
+                return $value->toArray();
+            }
+
+            return $value;
+        }, $this->all());
+    }
+
+    /**
+     * Get the collection of items as JSON.
+     *
+     * @param  int  $options
+     * @return string
+     */
+    public function toJson($options = 0)
+    {
+        return json_encode($this->jsonSerialize(), $options);
+    }
+
+    /**
+     * Get a CachingIterator instance.
+     *
+     * @param  int  $flags
+     * @return \CachingIterator
+     */
+    public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
+    {
+        return new CachingIterator($this->getIterator(), $flags);
+    }
+
+    /**
+     * Convert the collection to its string representation.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->escapeWhenCastingToString
+                    ? e($this->toJson())
+                    : $this->toJson();
+    }
+
+    /**
+     * Indicate that the model's string representation should be escaped when __toString is invoked.
+     *
+     * @param  bool  $escape
+     * @return $this
+     */
+    public function escapeWhenCastingToString($escape = true)
+    {
+        $this->escapeWhenCastingToString = $escape;
+
+        return $this;
+    }
+
+    /**
+     * Add a method to the list of proxied methods.
+     *
+     * @param  string  $method
+     * @return void
+     */
+    public static function proxy($method)
+    {
+        static::$proxies[] = $method;
+    }
+
+    /**
+     * Dynamically access collection proxies.
+     *
+     * @param  string  $key
+     * @return mixed
+     *
+     * @throws \Exception
+     */
+    public function __get($key)
+    {
+        if (! in_array($key, static::$proxies)) {
+            throw new Exception("Property [{$key}] does not exist on this collection instance.");
+        }
+
+        return new HigherOrderCollectionProxy($this, $key);
+    }
+
+    /**
+     * Results array of items from Collection or Arrayable.
+     *
+     * @param  mixed  $items
+     * @return array
+     */
+    protected function getArrayableItems($items)
+    {
+        if (is_array($items)) {
+            return $items;
+        } elseif ($items instanceof Enumerable) {
+            return $items->all();
+        } elseif ($items instanceof Arrayable) {
+            return $items->toArray();
+        } elseif ($items instanceof Traversable) {
+            return iterator_to_array($items);
+        } elseif ($items instanceof Jsonable) {
+            return json_decode($items->toJson(), true);
+        } elseif ($items instanceof JsonSerializable) {
+            return (array) $items->jsonSerialize();
+        } elseif ($items instanceof UnitEnum) {
+            return [$items];
+        }
+
+        return (array) $items;
+    }
+
+    /**
+     * Get an operator checker callback.
+     *
+     * @param  callable|string  $key
+     * @param  string|null  $operator
+     * @param  mixed  $value
+     * @return \Closure
+     */
+    protected function operatorForWhere($key, $operator = null, $value = null)
+    {
+        if ($this->useAsCallable($key)) {
+            return $key;
+        }
+
+        if (func_num_args() === 1) {
+            $value = true;
+
+            $operator = '=';
+        }
+
+        if (func_num_args() === 2) {
+            $value = $operator;
+
+            $operator = '=';
+        }
+
+        return function ($item) use ($key, $operator, $value) {
+            $retrieved = data_get($item, $key);
+
+            $strings = array_filter([$retrieved, $value], function ($value) {
+                return is_string($value) || (is_object($value) && method_exists($value, '__toString'));
+            });
+
+            if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) {
+                return in_array($operator, ['!=', '<>', '!==']);
+            }
+
+            switch ($operator) {
+                default:
+                case '=':
+                case '==':  return $retrieved == $value;
+                case '!=':
+                case '<>':  return $retrieved != $value;
+                case '<':   return $retrieved < $value;
+                case '>':   return $retrieved > $value;
+                case '<=':  return $retrieved <= $value;
+                case '>=':  return $retrieved >= $value;
+                case '===': return $retrieved === $value;
+                case '!==': return $retrieved !== $value;
+                case '<=>': return $retrieved <=> $value;
+            }
+        };
+    }
+
+    /**
+     * Determine if the given value is callable, but not a string.
+     *
+     * @param  mixed  $value
+     * @return bool
+     */
+    protected function useAsCallable($value)
+    {
+        return ! is_string($value) && is_callable($value);
+    }
+
+    /**
+     * Get a value retrieving callback.
+     *
+     * @param  callable|string|null  $value
+     * @return callable
+     */
+    protected function valueRetriever($value)
+    {
+        if ($this->useAsCallable($value)) {
+            return $value;
+        }
+
+        return fn ($item) => data_get($item, $value);
+    }
+
+    /**
+     * Make a function to check an item's equality.
+     *
+     * @param  mixed  $value
+     * @return \Closure(mixed): bool
+     */
+    protected function equality($value)
+    {
+        return fn ($item) => $item === $value;
+    }
+
+    /**
+     * Make a function using another function, by negating its result.
+     *
+     * @param  \Closure  $callback
+     * @return \Closure
+     */
+    protected function negate(Closure $callback)
+    {
+        return fn (...$params) => ! $callback(...$params);
+    }
+
+    /**
+     * Make a function that returns what's passed to it.
+     *
+     * @return \Closure(TValue): TValue
+     */
+    protected function identity()
+    {
+        return fn ($value) => $value;
+    }
+}
diff --git a/vendor/illuminate/collections/composer.json b/vendor/illuminate/collections/composer.json
new file mode 100644
index 0000000..cc9aad3
--- /dev/null
+++ b/vendor/illuminate/collections/composer.json
@@ -0,0 +1,42 @@
+{
+    "name": "illuminate/collections",
+    "description": "The Illuminate Collections package.",
+    "license": "MIT",
+    "homepage": "https://laravel.com",
+    "support": {
+        "issues": "https://github.com/laravel/framework/issues",
+        "source": "https://github.com/laravel/framework"
+    },
+    "authors": [
+        {
+            "name": "Taylor Otwell",
+            "email": "taylor@laravel.com"
+        }
+    ],
+    "require": {
+        "php": "^8.0.2",
+        "illuminate/conditionable": "^9.0",
+        "illuminate/contracts": "^9.0",
+        "illuminate/macroable": "^9.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Illuminate\\Support\\": ""
+        },
+        "files": [
+            "helpers.php"
+        ]
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "9.x-dev"
+        }
+    },
+    "suggest": {
+        "symfony/var-dumper": "Required to use the dump method (^6.0)."
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/collections/helpers.php b/vendor/illuminate/collections/helpers.php
new file mode 100644
index 0000000..9babf4e
--- /dev/null
+++ b/vendor/illuminate/collections/helpers.php
@@ -0,0 +1,190 @@
+|iterable|null  $value
+     * @return \Illuminate\Support\Collection
+     */
+    function collect($value = [])
+    {
+        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|null  $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);
+
+        foreach ($key as $i => $segment) {
+            unset($key[$i]);
+
+            if (is_null($segment)) {
+                return $target;
+            }
+
+            if ($segment === '*') {
+                if ($target instanceof Collection) {
+                    $target = $target->all();
+                } elseif (! is_iterable($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('head')) {
+    /**
+     * Get the first element of an array. Useful for method chaining.
+     *
+     * @param  array  $array
+     * @return mixed
+     */
+    function head($array)
+    {
+        return reset($array);
+    }
+}
+
+if (! function_exists('last')) {
+    /**
+     * Get the last element from an array.
+     *
+     * @param  array  $array
+     * @return mixed
+     */
+    function last($array)
+    {
+        return end($array);
+    }
+}
+
+if (! function_exists('value')) {
+    /**
+     * Return the default value of the given value.
+     *
+     * @param  mixed  $value
+     * @param  mixed  ...$args
+     * @return mixed
+     */
+    function value($value, ...$args)
+    {
+        return $value instanceof Closure ? $value(...$args) : $value;
+    }
+}
diff --git a/vendor/illuminate/conditionable/HigherOrderWhenProxy.php b/vendor/illuminate/conditionable/HigherOrderWhenProxy.php
new file mode 100644
index 0000000..579114c
--- /dev/null
+++ b/vendor/illuminate/conditionable/HigherOrderWhenProxy.php
@@ -0,0 +1,109 @@
+target = $target;
+    }
+
+    /**
+     * Set the condition on the proxy.
+     *
+     * @param  bool  $condition
+     * @return $this
+     */
+    public function condition($condition)
+    {
+        [$this->condition, $this->hasCondition] = [$condition, true];
+
+        return $this;
+    }
+
+    /**
+     * Indicate that the condition should be negated.
+     *
+     * @return $this
+     */
+    public function negateConditionOnCapture()
+    {
+        $this->negateConditionOnCapture = true;
+
+        return $this;
+    }
+
+    /**
+     * Proxy accessing an attribute onto the target.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function __get($key)
+    {
+        if (! $this->hasCondition) {
+            $condition = $this->target->{$key};
+
+            return $this->condition($this->negateConditionOnCapture ? ! $condition : $condition);
+        }
+
+        return $this->condition
+            ? $this->target->{$key}
+            : $this->target;
+    }
+
+    /**
+     * Proxy a method call on the target.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        if (! $this->hasCondition) {
+            $condition = $this->target->{$method}(...$parameters);
+
+            return $this->condition($this->negateConditionOnCapture ? ! $condition : $condition);
+        }
+
+        return $this->condition
+            ? $this->target->{$method}(...$parameters)
+            : $this->target;
+    }
+}
diff --git a/vendor/illuminate/conditionable/LICENSE.md b/vendor/illuminate/conditionable/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/illuminate/conditionable/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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/illuminate/conditionable/Traits/Conditionable.php b/vendor/illuminate/conditionable/Traits/Conditionable.php
new file mode 100644
index 0000000..1930743
--- /dev/null
+++ b/vendor/illuminate/conditionable/Traits/Conditionable.php
@@ -0,0 +1,73 @@
+condition($value);
+        }
+
+        if ($value) {
+            return $callback($this, $value) ?? $this;
+        } elseif ($default) {
+            return $default($this, $value) ?? $this;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Apply the callback if the given "value" is (or resolves to) falsy.
+     *
+     * @template TUnlessParameter
+     * @template TUnlessReturnType
+     *
+     * @param  (\Closure($this): TUnlessParameter)|TUnlessParameter|null  $value
+     * @param  (callable($this, TUnlessParameter): TUnlessReturnType)|null  $callback
+     * @param  (callable($this, TUnlessParameter): TUnlessReturnType)|null  $default
+     * @return $this|TUnlessReturnType
+     */
+    public function unless($value = null, callable $callback = null, callable $default = null)
+    {
+        $value = $value instanceof Closure ? $value($this) : $value;
+
+        if (func_num_args() === 0) {
+            return (new HigherOrderWhenProxy($this))->negateConditionOnCapture();
+        }
+
+        if (func_num_args() === 1) {
+            return (new HigherOrderWhenProxy($this))->condition(! $value);
+        }
+
+        if (! $value) {
+            return $callback($this, $value) ?? $this;
+        } elseif ($default) {
+            return $default($this, $value) ?? $this;
+        }
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/conditionable/composer.json b/vendor/illuminate/conditionable/composer.json
new file mode 100644
index 0000000..a423aad
--- /dev/null
+++ b/vendor/illuminate/conditionable/composer.json
@@ -0,0 +1,33 @@
+{
+    "name": "illuminate/conditionable",
+    "description": "The Illuminate Conditionable package.",
+    "license": "MIT",
+    "homepage": "https://laravel.com",
+    "support": {
+        "issues": "https://github.com/laravel/framework/issues",
+        "source": "https://github.com/laravel/framework"
+    },
+    "authors": [
+        {
+            "name": "Taylor Otwell",
+            "email": "taylor@laravel.com"
+        }
+    ],
+    "require": {
+        "php": "^8.0.2"
+    },
+    "autoload": {
+        "psr-4": {
+            "Illuminate\\Support\\": ""
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "9.x-dev"
+        }
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/container/BoundMethod.php b/vendor/illuminate/container/BoundMethod.php
new file mode 100644
index 0000000..0bac7fa
--- /dev/null
+++ b/vendor/illuminate/container/BoundMethod.php
@@ -0,0 +1,202 @@
+make($segments[0]), $method], $parameters
+        );
+    }
+
+    /**
+     * Call a method that has been bound to the container.
+     *
+     * @param  \Illuminate\Container\Container  $container
+     * @param  callable  $callback
+     * @param  mixed  $default
+     * @return mixed
+     */
+    protected static function callBoundMethod($container, $callback, $default)
+    {
+        if (! is_array($callback)) {
+            return Util::unwrapIfClosure($default);
+        }
+
+        // Here we need to turn the array callable into a Class@method string we can use to
+        // examine the container and see if there are any method bindings for this given
+        // method. If there are, we can call this method binding callback immediately.
+        $method = static::normalizeMethod($callback);
+
+        if ($container->hasMethodBinding($method)) {
+            return $container->callMethodBinding($method, $callback[0]);
+        }
+
+        return Util::unwrapIfClosure($default);
+    }
+
+    /**
+     * Normalize the given callback into a Class@method string.
+     *
+     * @param  callable  $callback
+     * @return string
+     */
+    protected static function normalizeMethod($callback)
+    {
+        $class = is_string($callback[0]) ? $callback[0] : get_class($callback[0]);
+
+        return "{$class}@{$callback[1]}";
+    }
+
+    /**
+     * Get all dependencies for a given method.
+     *
+     * @param  \Illuminate\Container\Container  $container
+     * @param  callable|string  $callback
+     * @param  array  $parameters
+     * @return array
+     *
+     * @throws \ReflectionException
+     */
+    protected static function getMethodDependencies($container, $callback, array $parameters = [])
+    {
+        $dependencies = [];
+
+        foreach (static::getCallReflector($callback)->getParameters() as $parameter) {
+            static::addDependencyForCallParameter($container, $parameter, $parameters, $dependencies);
+        }
+
+        return array_merge($dependencies, array_values($parameters));
+    }
+
+    /**
+     * Get the proper reflection instance for the given callback.
+     *
+     * @param  callable|string  $callback
+     * @return \ReflectionFunctionAbstract
+     *
+     * @throws \ReflectionException
+     */
+    protected static function getCallReflector($callback)
+    {
+        if (is_string($callback) && str_contains($callback, '::')) {
+            $callback = explode('::', $callback);
+        } elseif (is_object($callback) && ! $callback instanceof Closure) {
+            $callback = [$callback, '__invoke'];
+        }
+
+        return is_array($callback)
+                        ? new ReflectionMethod($callback[0], $callback[1])
+                        : new ReflectionFunction($callback);
+    }
+
+    /**
+     * Get the dependency for the given call parameter.
+     *
+     * @param  \Illuminate\Container\Container  $container
+     * @param  \ReflectionParameter  $parameter
+     * @param  array  $parameters
+     * @param  array  $dependencies
+     * @return void
+     *
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    protected static function addDependencyForCallParameter($container, $parameter,
+                                                            array &$parameters, &$dependencies)
+    {
+        if (array_key_exists($paramName = $parameter->getName(), $parameters)) {
+            $dependencies[] = $parameters[$paramName];
+
+            unset($parameters[$paramName]);
+        } elseif (! is_null($className = Util::getParameterClassName($parameter))) {
+            if (array_key_exists($className, $parameters)) {
+                $dependencies[] = $parameters[$className];
+
+                unset($parameters[$className]);
+            } elseif ($parameter->isVariadic()) {
+                $variadicDependencies = $container->make($className);
+
+                $dependencies = array_merge($dependencies, is_array($variadicDependencies)
+                            ? $variadicDependencies
+                            : [$variadicDependencies]);
+            } else {
+                $dependencies[] = $container->make($className);
+            }
+        } elseif ($parameter->isDefaultValueAvailable()) {
+            $dependencies[] = $parameter->getDefaultValue();
+        } elseif (! $parameter->isOptional() && ! array_key_exists($paramName, $parameters)) {
+            $message = "Unable to resolve dependency [{$parameter}] in class {$parameter->getDeclaringClass()->getName()}";
+
+            throw new BindingResolutionException($message);
+        }
+    }
+
+    /**
+     * Determine if the given string is in Class@method syntax.
+     *
+     * @param  mixed  $callback
+     * @return bool
+     */
+    protected static function isCallableWithAtSign($callback)
+    {
+        return is_string($callback) && str_contains($callback, '@');
+    }
+}
diff --git a/vendor/illuminate/container/Container.php b/vendor/illuminate/container/Container.php
new file mode 100755
index 0000000..519fcdf
--- /dev/null
+++ b/vendor/illuminate/container/Container.php
@@ -0,0 +1,1479 @@
+getAlias($c);
+        }
+
+        return new ContextualBindingBuilder($this, $aliases);
+    }
+
+    /**
+     * Determine if the given abstract type has been bound.
+     *
+     * @param  string  $abstract
+     * @return bool
+     */
+    public function bound($abstract)
+    {
+        return isset($this->bindings[$abstract]) ||
+               isset($this->instances[$abstract]) ||
+               $this->isAlias($abstract);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return bool
+     */
+    public function has(string $id): bool
+    {
+        return $this->bound($id);
+    }
+
+    /**
+     * Determine if the given abstract type has been resolved.
+     *
+     * @param  string  $abstract
+     * @return bool
+     */
+    public function resolved($abstract)
+    {
+        if ($this->isAlias($abstract)) {
+            $abstract = $this->getAlias($abstract);
+        }
+
+        return isset($this->resolved[$abstract]) ||
+               isset($this->instances[$abstract]);
+    }
+
+    /**
+     * Determine if a given type is shared.
+     *
+     * @param  string  $abstract
+     * @return bool
+     */
+    public function isShared($abstract)
+    {
+        return isset($this->instances[$abstract]) ||
+               (isset($this->bindings[$abstract]['shared']) &&
+               $this->bindings[$abstract]['shared'] === true);
+    }
+
+    /**
+     * Determine if a given string is an alias.
+     *
+     * @param  string  $name
+     * @return bool
+     */
+    public function isAlias($name)
+    {
+        return isset($this->aliases[$name]);
+    }
+
+    /**
+     * Register a binding with the container.
+     *
+     * @param  string  $abstract
+     * @param  \Closure|string|null  $concrete
+     * @param  bool  $shared
+     * @return void
+     *
+     * @throws \TypeError
+     */
+    public function bind($abstract, $concrete = null, $shared = false)
+    {
+        $this->dropStaleInstances($abstract);
+
+        // If no concrete type was given, we will simply set the concrete type to the
+        // abstract type. After that, the concrete type to be registered as shared
+        // without being forced to state their classes in both of the parameters.
+        if (is_null($concrete)) {
+            $concrete = $abstract;
+        }
+
+        // If the factory is not a Closure, it means it is just a class name which is
+        // bound into this container to the abstract type and we will just wrap it
+        // up inside its own Closure to give us more convenience when extending.
+        if (! $concrete instanceof Closure) {
+            if (! is_string($concrete)) {
+                throw new TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null');
+            }
+
+            $concrete = $this->getClosure($abstract, $concrete);
+        }
+
+        $this->bindings[$abstract] = compact('concrete', 'shared');
+
+        // If the abstract type was already resolved in this container we'll fire the
+        // rebound listener so that any objects which have already gotten resolved
+        // can have their copy of the object updated via the listener callbacks.
+        if ($this->resolved($abstract)) {
+            $this->rebound($abstract);
+        }
+    }
+
+    /**
+     * Get the Closure to be used when building a type.
+     *
+     * @param  string  $abstract
+     * @param  string  $concrete
+     * @return \Closure
+     */
+    protected function getClosure($abstract, $concrete)
+    {
+        return function ($container, $parameters = []) use ($abstract, $concrete) {
+            if ($abstract == $concrete) {
+                return $container->build($concrete);
+            }
+
+            return $container->resolve(
+                $concrete, $parameters, $raiseEvents = false
+            );
+        };
+    }
+
+    /**
+     * Determine if the container has a method binding.
+     *
+     * @param  string  $method
+     * @return bool
+     */
+    public function hasMethodBinding($method)
+    {
+        return isset($this->methodBindings[$method]);
+    }
+
+    /**
+     * Bind a callback to resolve with Container::call.
+     *
+     * @param  array|string  $method
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function bindMethod($method, $callback)
+    {
+        $this->methodBindings[$this->parseBindMethod($method)] = $callback;
+    }
+
+    /**
+     * Get the method to be bound in class@method format.
+     *
+     * @param  array|string  $method
+     * @return string
+     */
+    protected function parseBindMethod($method)
+    {
+        if (is_array($method)) {
+            return $method[0].'@'.$method[1];
+        }
+
+        return $method;
+    }
+
+    /**
+     * Get the method binding for the given method.
+     *
+     * @param  string  $method
+     * @param  mixed  $instance
+     * @return mixed
+     */
+    public function callMethodBinding($method, $instance)
+    {
+        return call_user_func($this->methodBindings[$method], $instance, $this);
+    }
+
+    /**
+     * Add a contextual binding to the container.
+     *
+     * @param  string  $concrete
+     * @param  string  $abstract
+     * @param  \Closure|string  $implementation
+     * @return void
+     */
+    public function addContextualBinding($concrete, $abstract, $implementation)
+    {
+        $this->contextual[$concrete][$this->getAlias($abstract)] = $implementation;
+    }
+
+    /**
+     * Register a binding if it hasn't already been registered.
+     *
+     * @param  string  $abstract
+     * @param  \Closure|string|null  $concrete
+     * @param  bool  $shared
+     * @return void
+     */
+    public function bindIf($abstract, $concrete = null, $shared = false)
+    {
+        if (! $this->bound($abstract)) {
+            $this->bind($abstract, $concrete, $shared);
+        }
+    }
+
+    /**
+     * Register a shared binding in the container.
+     *
+     * @param  string  $abstract
+     * @param  \Closure|string|null  $concrete
+     * @return void
+     */
+    public function singleton($abstract, $concrete = null)
+    {
+        $this->bind($abstract, $concrete, true);
+    }
+
+    /**
+     * Register a shared binding if it hasn't already been registered.
+     *
+     * @param  string  $abstract
+     * @param  \Closure|string|null  $concrete
+     * @return void
+     */
+    public function singletonIf($abstract, $concrete = null)
+    {
+        if (! $this->bound($abstract)) {
+            $this->singleton($abstract, $concrete);
+        }
+    }
+
+    /**
+     * Register a scoped binding in the container.
+     *
+     * @param  string  $abstract
+     * @param  \Closure|string|null  $concrete
+     * @return void
+     */
+    public function scoped($abstract, $concrete = null)
+    {
+        $this->scopedInstances[] = $abstract;
+
+        $this->singleton($abstract, $concrete);
+    }
+
+    /**
+     * Register a scoped binding if it hasn't already been registered.
+     *
+     * @param  string  $abstract
+     * @param  \Closure|string|null  $concrete
+     * @return void
+     */
+    public function scopedIf($abstract, $concrete = null)
+    {
+        if (! $this->bound($abstract)) {
+            $this->scoped($abstract, $concrete);
+        }
+    }
+
+    /**
+     * "Extend" an abstract type in the container.
+     *
+     * @param  string  $abstract
+     * @param  \Closure  $closure
+     * @return void
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function extend($abstract, Closure $closure)
+    {
+        $abstract = $this->getAlias($abstract);
+
+        if (isset($this->instances[$abstract])) {
+            $this->instances[$abstract] = $closure($this->instances[$abstract], $this);
+
+            $this->rebound($abstract);
+        } else {
+            $this->extenders[$abstract][] = $closure;
+
+            if ($this->resolved($abstract)) {
+                $this->rebound($abstract);
+            }
+        }
+    }
+
+    /**
+     * Register an existing instance as shared in the container.
+     *
+     * @param  string  $abstract
+     * @param  mixed  $instance
+     * @return mixed
+     */
+    public function instance($abstract, $instance)
+    {
+        $this->removeAbstractAlias($abstract);
+
+        $isBound = $this->bound($abstract);
+
+        unset($this->aliases[$abstract]);
+
+        // We'll check to determine if this type has been bound before, and if it has
+        // we will fire the rebound callbacks registered with the container and it
+        // can be updated with consuming classes that have gotten resolved here.
+        $this->instances[$abstract] = $instance;
+
+        if ($isBound) {
+            $this->rebound($abstract);
+        }
+
+        return $instance;
+    }
+
+    /**
+     * Remove an alias from the contextual binding alias cache.
+     *
+     * @param  string  $searched
+     * @return void
+     */
+    protected function removeAbstractAlias($searched)
+    {
+        if (! isset($this->aliases[$searched])) {
+            return;
+        }
+
+        foreach ($this->abstractAliases as $abstract => $aliases) {
+            foreach ($aliases as $index => $alias) {
+                if ($alias == $searched) {
+                    unset($this->abstractAliases[$abstract][$index]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Assign a set of tags to a given binding.
+     *
+     * @param  array|string  $abstracts
+     * @param  array|mixed  ...$tags
+     * @return void
+     */
+    public function tag($abstracts, $tags)
+    {
+        $tags = is_array($tags) ? $tags : array_slice(func_get_args(), 1);
+
+        foreach ($tags as $tag) {
+            if (! isset($this->tags[$tag])) {
+                $this->tags[$tag] = [];
+            }
+
+            foreach ((array) $abstracts as $abstract) {
+                $this->tags[$tag][] = $abstract;
+            }
+        }
+    }
+
+    /**
+     * Resolve all of the bindings for a given tag.
+     *
+     * @param  string  $tag
+     * @return iterable
+     */
+    public function tagged($tag)
+    {
+        if (! isset($this->tags[$tag])) {
+            return [];
+        }
+
+        return new RewindableGenerator(function () use ($tag) {
+            foreach ($this->tags[$tag] as $abstract) {
+                yield $this->make($abstract);
+            }
+        }, count($this->tags[$tag]));
+    }
+
+    /**
+     * Alias a type to a different name.
+     *
+     * @param  string  $abstract
+     * @param  string  $alias
+     * @return void
+     *
+     * @throws \LogicException
+     */
+    public function alias($abstract, $alias)
+    {
+        if ($alias === $abstract) {
+            throw new LogicException("[{$abstract}] is aliased to itself.");
+        }
+
+        $this->aliases[$alias] = $abstract;
+
+        $this->abstractAliases[$abstract][] = $alias;
+    }
+
+    /**
+     * Bind a new callback to an abstract's rebind event.
+     *
+     * @param  string  $abstract
+     * @param  \Closure  $callback
+     * @return mixed
+     */
+    public function rebinding($abstract, Closure $callback)
+    {
+        $this->reboundCallbacks[$abstract = $this->getAlias($abstract)][] = $callback;
+
+        if ($this->bound($abstract)) {
+            return $this->make($abstract);
+        }
+    }
+
+    /**
+     * Refresh an instance on the given target and method.
+     *
+     * @param  string  $abstract
+     * @param  mixed  $target
+     * @param  string  $method
+     * @return mixed
+     */
+    public function refresh($abstract, $target, $method)
+    {
+        return $this->rebinding($abstract, function ($app, $instance) use ($target, $method) {
+            $target->{$method}($instance);
+        });
+    }
+
+    /**
+     * Fire the "rebound" callbacks for the given abstract type.
+     *
+     * @param  string  $abstract
+     * @return void
+     */
+    protected function rebound($abstract)
+    {
+        $instance = $this->make($abstract);
+
+        foreach ($this->getReboundCallbacks($abstract) as $callback) {
+            $callback($this, $instance);
+        }
+    }
+
+    /**
+     * Get the rebound callbacks for a given type.
+     *
+     * @param  string  $abstract
+     * @return array
+     */
+    protected function getReboundCallbacks($abstract)
+    {
+        return $this->reboundCallbacks[$abstract] ?? [];
+    }
+
+    /**
+     * Wrap the given closure such that its dependencies will be injected when executed.
+     *
+     * @param  \Closure  $callback
+     * @param  array  $parameters
+     * @return \Closure
+     */
+    public function wrap(Closure $callback, array $parameters = [])
+    {
+        return fn () => $this->call($callback, $parameters);
+    }
+
+    /**
+     * Call the given Closure / class@method and inject its dependencies.
+     *
+     * @param  callable|string  $callback
+     * @param  array  $parameters
+     * @param  string|null  $defaultMethod
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function call($callback, array $parameters = [], $defaultMethod = null)
+    {
+        $pushedToBuildStack = false;
+
+        if (is_array($callback) && ! in_array(
+            $className = (is_string($callback[0]) ? $callback[0] : get_class($callback[0])),
+            $this->buildStack,
+            true
+        )) {
+            $this->buildStack[] = $className;
+
+            $pushedToBuildStack = true;
+        }
+
+        $result = BoundMethod::call($this, $callback, $parameters, $defaultMethod);
+
+        if ($pushedToBuildStack) {
+            array_pop($this->buildStack);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Get a closure to resolve the given type from the container.
+     *
+     * @param  string  $abstract
+     * @return \Closure
+     */
+    public function factory($abstract)
+    {
+        return fn () => $this->make($abstract);
+    }
+
+    /**
+     * An alias function name for make().
+     *
+     * @param  string|callable  $abstract
+     * @param  array  $parameters
+     * @return mixed
+     *
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function makeWith($abstract, array $parameters = [])
+    {
+        return $this->make($abstract, $parameters);
+    }
+
+    /**
+     * Resolve the given type from the container.
+     *
+     * @param  string|callable  $abstract
+     * @param  array  $parameters
+     * @return mixed
+     *
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function make($abstract, array $parameters = [])
+    {
+        return $this->resolve($abstract, $parameters);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return mixed
+     */
+    public function get(string $id)
+    {
+        try {
+            return $this->resolve($id);
+        } catch (Exception $e) {
+            if ($this->has($id) || $e instanceof CircularDependencyException) {
+                throw $e;
+            }
+
+            throw new EntryNotFoundException($id, is_int($e->getCode()) ? $e->getCode() : 0, $e);
+        }
+    }
+
+    /**
+     * Resolve the given type from the container.
+     *
+     * @param  string|callable  $abstract
+     * @param  array  $parameters
+     * @param  bool  $raiseEvents
+     * @return mixed
+     *
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     * @throws \Illuminate\Contracts\Container\CircularDependencyException
+     */
+    protected function resolve($abstract, $parameters = [], $raiseEvents = true)
+    {
+        $abstract = $this->getAlias($abstract);
+
+        // First we'll fire any event handlers which handle the "before" resolving of
+        // specific types. This gives some hooks the chance to add various extends
+        // calls to change the resolution of objects that they're interested in.
+        if ($raiseEvents) {
+            $this->fireBeforeResolvingCallbacks($abstract, $parameters);
+        }
+
+        $concrete = $this->getContextualConcrete($abstract);
+
+        $needsContextualBuild = ! empty($parameters) || ! is_null($concrete);
+
+        // If an instance of the type is currently being managed as a singleton we'll
+        // just return an existing instance instead of instantiating new instances
+        // so the developer can keep using the same objects instance every time.
+        if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
+            return $this->instances[$abstract];
+        }
+
+        $this->with[] = $parameters;
+
+        if (is_null($concrete)) {
+            $concrete = $this->getConcrete($abstract);
+        }
+
+        // We're ready to instantiate an instance of the concrete type registered for
+        // the binding. This will instantiate the types, as well as resolve any of
+        // its "nested" dependencies recursively until all have gotten resolved.
+        if ($this->isBuildable($concrete, $abstract)) {
+            $object = $this->build($concrete);
+        } else {
+            $object = $this->make($concrete);
+        }
+
+        // If we defined any extenders for this type, we'll need to spin through them
+        // and apply them to the object being built. This allows for the extension
+        // of services, such as changing configuration or decorating the object.
+        foreach ($this->getExtenders($abstract) as $extender) {
+            $object = $extender($object, $this);
+        }
+
+        // If the requested type is registered as a singleton we'll want to cache off
+        // the instances in "memory" so we can return it later without creating an
+        // entirely new instance of an object on each subsequent request for it.
+        if ($this->isShared($abstract) && ! $needsContextualBuild) {
+            $this->instances[$abstract] = $object;
+        }
+
+        if ($raiseEvents) {
+            $this->fireResolvingCallbacks($abstract, $object);
+        }
+
+        // Before returning, we will also set the resolved flag to "true" and pop off
+        // the parameter overrides for this build. After those two things are done
+        // we will be ready to return back the fully constructed class instance.
+        $this->resolved[$abstract] = true;
+
+        array_pop($this->with);
+
+        return $object;
+    }
+
+    /**
+     * Get the concrete type for a given abstract.
+     *
+     * @param  string|callable  $abstract
+     * @return mixed
+     */
+    protected function getConcrete($abstract)
+    {
+        // If we don't have a registered resolver or concrete for the type, we'll just
+        // assume each type is a concrete name and will attempt to resolve it as is
+        // since the container should be able to resolve concretes automatically.
+        if (isset($this->bindings[$abstract])) {
+            return $this->bindings[$abstract]['concrete'];
+        }
+
+        return $abstract;
+    }
+
+    /**
+     * Get the contextual concrete binding for the given abstract.
+     *
+     * @param  string|callable  $abstract
+     * @return \Closure|string|array|null
+     */
+    protected function getContextualConcrete($abstract)
+    {
+        if (! is_null($binding = $this->findInContextualBindings($abstract))) {
+            return $binding;
+        }
+
+        // Next we need to see if a contextual binding might be bound under an alias of the
+        // given abstract type. So, we will need to check if any aliases exist with this
+        // type and then spin through them and check for contextual bindings on these.
+        if (empty($this->abstractAliases[$abstract])) {
+            return;
+        }
+
+        foreach ($this->abstractAliases[$abstract] as $alias) {
+            if (! is_null($binding = $this->findInContextualBindings($alias))) {
+                return $binding;
+            }
+        }
+    }
+
+    /**
+     * Find the concrete binding for the given abstract in the contextual binding array.
+     *
+     * @param  string|callable  $abstract
+     * @return \Closure|string|null
+     */
+    protected function findInContextualBindings($abstract)
+    {
+        return $this->contextual[end($this->buildStack)][$abstract] ?? null;
+    }
+
+    /**
+     * Determine if the given concrete is buildable.
+     *
+     * @param  mixed  $concrete
+     * @param  string  $abstract
+     * @return bool
+     */
+    protected function isBuildable($concrete, $abstract)
+    {
+        return $concrete === $abstract || $concrete instanceof Closure;
+    }
+
+    /**
+     * Instantiate a concrete instance of the given type.
+     *
+     * @param  \Closure|string  $concrete
+     * @return mixed
+     *
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     * @throws \Illuminate\Contracts\Container\CircularDependencyException
+     */
+    public function build($concrete)
+    {
+        // If the concrete type is actually a Closure, we will just execute it and
+        // hand back the results of the functions, which allows functions to be
+        // used as resolvers for more fine-tuned resolution of these objects.
+        if ($concrete instanceof Closure) {
+            return $concrete($this, $this->getLastParameterOverride());
+        }
+
+        try {
+            $reflector = new ReflectionClass($concrete);
+        } catch (ReflectionException $e) {
+            throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
+        }
+
+        // If the type is not instantiable, the developer is attempting to resolve
+        // an abstract type such as an Interface or Abstract Class and there is
+        // no binding registered for the abstractions so we need to bail out.
+        if (! $reflector->isInstantiable()) {
+            return $this->notInstantiable($concrete);
+        }
+
+        $this->buildStack[] = $concrete;
+
+        $constructor = $reflector->getConstructor();
+
+        // If there are no constructors, that means there are no dependencies then
+        // we can just resolve the instances of the objects right away, without
+        // resolving any other types or dependencies out of these containers.
+        if (is_null($constructor)) {
+            array_pop($this->buildStack);
+
+            return new $concrete;
+        }
+
+        $dependencies = $constructor->getParameters();
+
+        // Once we have all the constructor's parameters we can create each of the
+        // dependency instances and then use the reflection instances to make a
+        // new instance of this class, injecting the created dependencies in.
+        try {
+            $instances = $this->resolveDependencies($dependencies);
+        } catch (BindingResolutionException $e) {
+            array_pop($this->buildStack);
+
+            throw $e;
+        }
+
+        array_pop($this->buildStack);
+
+        return $reflector->newInstanceArgs($instances);
+    }
+
+    /**
+     * Resolve all of the dependencies from the ReflectionParameters.
+     *
+     * @param  \ReflectionParameter[]  $dependencies
+     * @return array
+     *
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    protected function resolveDependencies(array $dependencies)
+    {
+        $results = [];
+
+        foreach ($dependencies as $dependency) {
+            // If the dependency has an override for this particular build we will use
+            // that instead as the value. Otherwise, we will continue with this run
+            // of resolutions and let reflection attempt to determine the result.
+            if ($this->hasParameterOverride($dependency)) {
+                $results[] = $this->getParameterOverride($dependency);
+
+                continue;
+            }
+
+            // If the class is null, it means the dependency is a string or some other
+            // primitive type which we can not resolve since it is not a class and
+            // we will just bomb out with an error since we have no-where to go.
+            $result = is_null(Util::getParameterClassName($dependency))
+                            ? $this->resolvePrimitive($dependency)
+                            : $this->resolveClass($dependency);
+
+            if ($dependency->isVariadic()) {
+                $results = array_merge($results, $result);
+            } else {
+                $results[] = $result;
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Determine if the given dependency has a parameter override.
+     *
+     * @param  \ReflectionParameter  $dependency
+     * @return bool
+     */
+    protected function hasParameterOverride($dependency)
+    {
+        return array_key_exists(
+            $dependency->name, $this->getLastParameterOverride()
+        );
+    }
+
+    /**
+     * Get a parameter override for a dependency.
+     *
+     * @param  \ReflectionParameter  $dependency
+     * @return mixed
+     */
+    protected function getParameterOverride($dependency)
+    {
+        return $this->getLastParameterOverride()[$dependency->name];
+    }
+
+    /**
+     * Get the last parameter override.
+     *
+     * @return array
+     */
+    protected function getLastParameterOverride()
+    {
+        return count($this->with) ? end($this->with) : [];
+    }
+
+    /**
+     * Resolve a non-class hinted primitive dependency.
+     *
+     * @param  \ReflectionParameter  $parameter
+     * @return mixed
+     *
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    protected function resolvePrimitive(ReflectionParameter $parameter)
+    {
+        if (! is_null($concrete = $this->getContextualConcrete('$'.$parameter->getName()))) {
+            return Util::unwrapIfClosure($concrete, $this);
+        }
+
+        if ($parameter->isDefaultValueAvailable()) {
+            return $parameter->getDefaultValue();
+        }
+
+        if ($parameter->isVariadic()) {
+            return [];
+        }
+
+        $this->unresolvablePrimitive($parameter);
+    }
+
+    /**
+     * Resolve a class based dependency from the container.
+     *
+     * @param  \ReflectionParameter  $parameter
+     * @return mixed
+     *
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    protected function resolveClass(ReflectionParameter $parameter)
+    {
+        try {
+            return $parameter->isVariadic()
+                        ? $this->resolveVariadicClass($parameter)
+                        : $this->make(Util::getParameterClassName($parameter));
+        }
+
+        // If we can not resolve the class instance, we will check to see if the value
+        // is optional, and if it is we will return the optional parameter value as
+        // the value of the dependency, similarly to how we do this with scalars.
+        catch (BindingResolutionException $e) {
+            if ($parameter->isDefaultValueAvailable()) {
+                array_pop($this->with);
+
+                return $parameter->getDefaultValue();
+            }
+
+            if ($parameter->isVariadic()) {
+                array_pop($this->with);
+
+                return [];
+            }
+
+            throw $e;
+        }
+    }
+
+    /**
+     * Resolve a class based variadic dependency from the container.
+     *
+     * @param  \ReflectionParameter  $parameter
+     * @return mixed
+     */
+    protected function resolveVariadicClass(ReflectionParameter $parameter)
+    {
+        $className = Util::getParameterClassName($parameter);
+
+        $abstract = $this->getAlias($className);
+
+        if (! is_array($concrete = $this->getContextualConcrete($abstract))) {
+            return $this->make($className);
+        }
+
+        return array_map(fn ($abstract) => $this->resolve($abstract), $concrete);
+    }
+
+    /**
+     * Throw an exception that the concrete is not instantiable.
+     *
+     * @param  string  $concrete
+     * @return void
+     *
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    protected function notInstantiable($concrete)
+    {
+        if (! empty($this->buildStack)) {
+            $previous = implode(', ', $this->buildStack);
+
+            $message = "Target [$concrete] is not instantiable while building [$previous].";
+        } else {
+            $message = "Target [$concrete] is not instantiable.";
+        }
+
+        throw new BindingResolutionException($message);
+    }
+
+    /**
+     * Throw an exception for an unresolvable primitive.
+     *
+     * @param  \ReflectionParameter  $parameter
+     * @return void
+     *
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    protected function unresolvablePrimitive(ReflectionParameter $parameter)
+    {
+        $message = "Unresolvable dependency resolving [$parameter] in class {$parameter->getDeclaringClass()->getName()}";
+
+        throw new BindingResolutionException($message);
+    }
+
+    /**
+     * Register a new before resolving callback for all types.
+     *
+     * @param  \Closure|string  $abstract
+     * @param  \Closure|null  $callback
+     * @return void
+     */
+    public function beforeResolving($abstract, Closure $callback = null)
+    {
+        if (is_string($abstract)) {
+            $abstract = $this->getAlias($abstract);
+        }
+
+        if ($abstract instanceof Closure && is_null($callback)) {
+            $this->globalBeforeResolvingCallbacks[] = $abstract;
+        } else {
+            $this->beforeResolvingCallbacks[$abstract][] = $callback;
+        }
+    }
+
+    /**
+     * Register a new resolving callback.
+     *
+     * @param  \Closure|string  $abstract
+     * @param  \Closure|null  $callback
+     * @return void
+     */
+    public function resolving($abstract, Closure $callback = null)
+    {
+        if (is_string($abstract)) {
+            $abstract = $this->getAlias($abstract);
+        }
+
+        if (is_null($callback) && $abstract instanceof Closure) {
+            $this->globalResolvingCallbacks[] = $abstract;
+        } else {
+            $this->resolvingCallbacks[$abstract][] = $callback;
+        }
+    }
+
+    /**
+     * Register a new after resolving callback for all types.
+     *
+     * @param  \Closure|string  $abstract
+     * @param  \Closure|null  $callback
+     * @return void
+     */
+    public function afterResolving($abstract, Closure $callback = null)
+    {
+        if (is_string($abstract)) {
+            $abstract = $this->getAlias($abstract);
+        }
+
+        if ($abstract instanceof Closure && is_null($callback)) {
+            $this->globalAfterResolvingCallbacks[] = $abstract;
+        } else {
+            $this->afterResolvingCallbacks[$abstract][] = $callback;
+        }
+    }
+
+    /**
+     * Fire all of the before resolving callbacks.
+     *
+     * @param  string  $abstract
+     * @param  array  $parameters
+     * @return void
+     */
+    protected function fireBeforeResolvingCallbacks($abstract, $parameters = [])
+    {
+        $this->fireBeforeCallbackArray($abstract, $parameters, $this->globalBeforeResolvingCallbacks);
+
+        foreach ($this->beforeResolvingCallbacks as $type => $callbacks) {
+            if ($type === $abstract || is_subclass_of($abstract, $type)) {
+                $this->fireBeforeCallbackArray($abstract, $parameters, $callbacks);
+            }
+        }
+    }
+
+    /**
+     * Fire an array of callbacks with an object.
+     *
+     * @param  string  $abstract
+     * @param  array  $parameters
+     * @param  array  $callbacks
+     * @return void
+     */
+    protected function fireBeforeCallbackArray($abstract, $parameters, array $callbacks)
+    {
+        foreach ($callbacks as $callback) {
+            $callback($abstract, $parameters, $this);
+        }
+    }
+
+    /**
+     * Fire all of the resolving callbacks.
+     *
+     * @param  string  $abstract
+     * @param  mixed  $object
+     * @return void
+     */
+    protected function fireResolvingCallbacks($abstract, $object)
+    {
+        $this->fireCallbackArray($object, $this->globalResolvingCallbacks);
+
+        $this->fireCallbackArray(
+            $object, $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks)
+        );
+
+        $this->fireAfterResolvingCallbacks($abstract, $object);
+    }
+
+    /**
+     * Fire all of the after resolving callbacks.
+     *
+     * @param  string  $abstract
+     * @param  mixed  $object
+     * @return void
+     */
+    protected function fireAfterResolvingCallbacks($abstract, $object)
+    {
+        $this->fireCallbackArray($object, $this->globalAfterResolvingCallbacks);
+
+        $this->fireCallbackArray(
+            $object, $this->getCallbacksForType($abstract, $object, $this->afterResolvingCallbacks)
+        );
+    }
+
+    /**
+     * Get all callbacks for a given type.
+     *
+     * @param  string  $abstract
+     * @param  object  $object
+     * @param  array  $callbacksPerType
+     * @return array
+     */
+    protected function getCallbacksForType($abstract, $object, array $callbacksPerType)
+    {
+        $results = [];
+
+        foreach ($callbacksPerType as $type => $callbacks) {
+            if ($type === $abstract || $object instanceof $type) {
+                $results = array_merge($results, $callbacks);
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Fire an array of callbacks with an object.
+     *
+     * @param  mixed  $object
+     * @param  array  $callbacks
+     * @return void
+     */
+    protected function fireCallbackArray($object, array $callbacks)
+    {
+        foreach ($callbacks as $callback) {
+            $callback($object, $this);
+        }
+    }
+
+    /**
+     * Get the container's bindings.
+     *
+     * @return array
+     */
+    public function getBindings()
+    {
+        return $this->bindings;
+    }
+
+    /**
+     * Get the alias for an abstract if available.
+     *
+     * @param  string  $abstract
+     * @return string
+     */
+    public function getAlias($abstract)
+    {
+        return isset($this->aliases[$abstract])
+                    ? $this->getAlias($this->aliases[$abstract])
+                    : $abstract;
+    }
+
+    /**
+     * Get the extender callbacks for a given type.
+     *
+     * @param  string  $abstract
+     * @return array
+     */
+    protected function getExtenders($abstract)
+    {
+        return $this->extenders[$this->getAlias($abstract)] ?? [];
+    }
+
+    /**
+     * Remove all of the extender callbacks for a given type.
+     *
+     * @param  string  $abstract
+     * @return void
+     */
+    public function forgetExtenders($abstract)
+    {
+        unset($this->extenders[$this->getAlias($abstract)]);
+    }
+
+    /**
+     * Drop all of the stale instances and aliases.
+     *
+     * @param  string  $abstract
+     * @return void
+     */
+    protected function dropStaleInstances($abstract)
+    {
+        unset($this->instances[$abstract], $this->aliases[$abstract]);
+    }
+
+    /**
+     * Remove a resolved instance from the instance cache.
+     *
+     * @param  string  $abstract
+     * @return void
+     */
+    public function forgetInstance($abstract)
+    {
+        unset($this->instances[$abstract]);
+    }
+
+    /**
+     * Clear all of the instances from the container.
+     *
+     * @return void
+     */
+    public function forgetInstances()
+    {
+        $this->instances = [];
+    }
+
+    /**
+     * Clear all of the scoped instances from the container.
+     *
+     * @return void
+     */
+    public function forgetScopedInstances()
+    {
+        foreach ($this->scopedInstances as $scoped) {
+            unset($this->instances[$scoped]);
+        }
+    }
+
+    /**
+     * Flush the container of all bindings and resolved instances.
+     *
+     * @return void
+     */
+    public function flush()
+    {
+        $this->aliases = [];
+        $this->resolved = [];
+        $this->bindings = [];
+        $this->instances = [];
+        $this->abstractAliases = [];
+        $this->scopedInstances = [];
+    }
+
+    /**
+     * Get the globally available instance of the container.
+     *
+     * @return static
+     */
+    public static function getInstance()
+    {
+        if (is_null(static::$instance)) {
+            static::$instance = new static;
+        }
+
+        return static::$instance;
+    }
+
+    /**
+     * Set the shared instance of the container.
+     *
+     * @param  \Illuminate\Contracts\Container\Container|null  $container
+     * @return \Illuminate\Contracts\Container\Container|static
+     */
+    public static function setInstance(ContainerContract $container = null)
+    {
+        return static::$instance = $container;
+    }
+
+    /**
+     * Determine if a given offset exists.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    public function offsetExists($key): bool
+    {
+        return $this->bound($key);
+    }
+
+    /**
+     * Get the value at a given offset.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function offsetGet($key): mixed
+    {
+        return $this->make($key);
+    }
+
+    /**
+     * Set the value at a given offset.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return void
+     */
+    public function offsetSet($key, $value): void
+    {
+        $this->bind($key, $value instanceof Closure ? $value : fn () => $value);
+    }
+
+    /**
+     * Unset the value at a given offset.
+     *
+     * @param  string  $key
+     * @return void
+     */
+    public function offsetUnset($key): void
+    {
+        unset($this->bindings[$key], $this->instances[$key], $this->resolved[$key]);
+    }
+
+    /**
+     * Dynamically access container services.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function __get($key)
+    {
+        return $this[$key];
+    }
+
+    /**
+     * Dynamically set container services.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return void
+     */
+    public function __set($key, $value)
+    {
+        $this[$key] = $value;
+    }
+}
diff --git a/vendor/illuminate/container/ContextualBindingBuilder.php b/vendor/illuminate/container/ContextualBindingBuilder.php
new file mode 100644
index 0000000..707b74c
--- /dev/null
+++ b/vendor/illuminate/container/ContextualBindingBuilder.php
@@ -0,0 +1,96 @@
+concrete = $concrete;
+        $this->container = $container;
+    }
+
+    /**
+     * Define the abstract target that depends on the context.
+     *
+     * @param  string  $abstract
+     * @return $this
+     */
+    public function needs($abstract)
+    {
+        $this->needs = $abstract;
+
+        return $this;
+    }
+
+    /**
+     * Define the implementation for the contextual binding.
+     *
+     * @param  \Closure|string|array  $implementation
+     * @return void
+     */
+    public function give($implementation)
+    {
+        foreach (Util::arrayWrap($this->concrete) as $concrete) {
+            $this->container->addContextualBinding($concrete, $this->needs, $implementation);
+        }
+    }
+
+    /**
+     * Define tagged services to be used as the implementation for the contextual binding.
+     *
+     * @param  string  $tag
+     * @return void
+     */
+    public function giveTagged($tag)
+    {
+        $this->give(function ($container) use ($tag) {
+            $taggedServices = $container->tagged($tag);
+
+            return is_array($taggedServices) ? $taggedServices : iterator_to_array($taggedServices);
+        });
+    }
+
+    /**
+     * Specify the configuration item to bind as a primitive.
+     *
+     * @param  string  $key
+     * @param  mixed  $default
+     * @return void
+     */
+    public function giveConfig($key, $default = null)
+    {
+        $this->give(fn ($container) => $container->get('config')->get($key, $default));
+    }
+}
diff --git a/vendor/illuminate/container/EntryNotFoundException.php b/vendor/illuminate/container/EntryNotFoundException.php
new file mode 100644
index 0000000..4266921
--- /dev/null
+++ b/vendor/illuminate/container/EntryNotFoundException.php
@@ -0,0 +1,11 @@
+count = $count;
+        $this->generator = $generator;
+    }
+
+    /**
+     * Get an iterator from the generator.
+     *
+     * @return \Traversable
+     */
+    public function getIterator(): Traversable
+    {
+        return ($this->generator)();
+    }
+
+    /**
+     * Get the total number of tagged services.
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        if (is_callable($count = $this->count)) {
+            $this->count = $count();
+        }
+
+        return $this->count;
+    }
+}
diff --git a/vendor/illuminate/container/Util.php b/vendor/illuminate/container/Util.php
new file mode 100644
index 0000000..8d5023b
--- /dev/null
+++ b/vendor/illuminate/container/Util.php
@@ -0,0 +1,74 @@
+getType();
+
+        if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) {
+            return null;
+        }
+
+        $name = $type->getName();
+
+        if (! is_null($class = $parameter->getDeclaringClass())) {
+            if ($name === 'self') {
+                return $class->getName();
+            }
+
+            if ($name === 'parent' && $parent = $class->getParentClass()) {
+                return $parent->getName();
+            }
+        }
+
+        return $name;
+    }
+}
diff --git a/vendor/illuminate/container/composer.json b/vendor/illuminate/container/composer.json
new file mode 100755
index 0000000..8944b5b
--- /dev/null
+++ b/vendor/illuminate/container/composer.json
@@ -0,0 +1,38 @@
+{
+    "name": "illuminate/container",
+    "description": "The Illuminate Container package.",
+    "license": "MIT",
+    "homepage": "https://laravel.com",
+    "support": {
+        "issues": "https://github.com/laravel/framework/issues",
+        "source": "https://github.com/laravel/framework"
+    },
+    "authors": [
+        {
+            "name": "Taylor Otwell",
+            "email": "taylor@laravel.com"
+        }
+    ],
+    "require": {
+        "php": "^8.0.2",
+        "illuminate/contracts": "^9.0",
+        "psr/container": "^1.1.1|^2.0.1"
+    },
+    "provide": {
+        "psr/container-implementation": "1.1|2.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Illuminate\\Container\\": ""
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "9.x-dev"
+        }
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/contracts/Auth/Access/Authorizable.php b/vendor/illuminate/contracts/Auth/Access/Authorizable.php
new file mode 100644
index 0000000..cedeb6e
--- /dev/null
+++ b/vendor/illuminate/contracts/Auth/Access/Authorizable.php
@@ -0,0 +1,15 @@
+|CastsAttributes|CastsInboundAttributes
+     */
+    public static function castUsing(array $arguments);
+}
diff --git a/vendor/illuminate/contracts/Database/Eloquent/CastsAttributes.php b/vendor/illuminate/contracts/Database/Eloquent/CastsAttributes.php
new file mode 100644
index 0000000..878169c
--- /dev/null
+++ b/vendor/illuminate/contracts/Database/Eloquent/CastsAttributes.php
@@ -0,0 +1,32 @@
+id = $id;
+        $this->class = $class;
+        $this->relations = $relations;
+        $this->connection = $connection;
+    }
+
+    /**
+     * Specify the collection class that should be used when serializing / restoring collections.
+     *
+     * @param  string|null  $collectionClass
+     * @return $this
+     */
+    public function useCollectionClass(?string $collectionClass)
+    {
+        $this->collectionClass = $collectionClass;
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/contracts/Database/Query/Builder.php b/vendor/illuminate/contracts/Database/Query/Builder.php
new file mode 100644
index 0000000..e116ebf
--- /dev/null
+++ b/vendor/illuminate/contracts/Database/Query/Builder.php
@@ -0,0 +1,12 @@
+
+     */
+    public function getQueueableIds();
+
+    /**
+     * Get the relationships of the entities being queued.
+     *
+     * @return array
+     */
+    public function getQueueableRelations();
+
+    /**
+     * Get the connection of the entities being queued.
+     *
+     * @return string|null
+     */
+    public function getQueueableConnection();
+}
diff --git a/vendor/illuminate/contracts/Queue/QueueableEntity.php b/vendor/illuminate/contracts/Queue/QueueableEntity.php
new file mode 100644
index 0000000..366f0c8
--- /dev/null
+++ b/vendor/illuminate/contracts/Queue/QueueableEntity.php
@@ -0,0 +1,27 @@
+
+     */
+    public function toArray();
+}
diff --git a/vendor/illuminate/contracts/Support/CanBeEscapedWhenCastToString.php b/vendor/illuminate/contracts/Support/CanBeEscapedWhenCastToString.php
new file mode 100644
index 0000000..e1be6fe
--- /dev/null
+++ b/vendor/illuminate/contracts/Support/CanBeEscapedWhenCastToString.php
@@ -0,0 +1,14 @@
+setupContainer($container ?: new Container);
+
+        // Once we have the container setup, we will setup the default configuration
+        // options in the container "config" binding. This will make the database
+        // manager work correctly out of the box without extreme configuration.
+        $this->setupDefaultConfiguration();
+
+        $this->setupManager();
+    }
+
+    /**
+     * Setup the default database configuration options.
+     *
+     * @return void
+     */
+    protected function setupDefaultConfiguration()
+    {
+        $this->container['config']['database.fetch'] = PDO::FETCH_OBJ;
+
+        $this->container['config']['database.default'] = 'default';
+    }
+
+    /**
+     * Build the database manager instance.
+     *
+     * @return void
+     */
+    protected function setupManager()
+    {
+        $factory = new ConnectionFactory($this->container);
+
+        $this->manager = new DatabaseManager($this->container, $factory);
+    }
+
+    /**
+     * Get a connection instance from the global manager.
+     *
+     * @param  string|null  $connection
+     * @return \Illuminate\Database\Connection
+     */
+    public static function connection($connection = null)
+    {
+        return static::$instance->getConnection($connection);
+    }
+
+    /**
+     * Get a fluent query builder instance.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|string  $table
+     * @param  string|null  $as
+     * @param  string|null  $connection
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public static function table($table, $as = null, $connection = null)
+    {
+        return static::$instance->connection($connection)->table($table, $as);
+    }
+
+    /**
+     * Get a schema builder instance.
+     *
+     * @param  string|null  $connection
+     * @return \Illuminate\Database\Schema\Builder
+     */
+    public static function schema($connection = null)
+    {
+        return static::$instance->connection($connection)->getSchemaBuilder();
+    }
+
+    /**
+     * Get a registered connection instance.
+     *
+     * @param  string|null  $name
+     * @return \Illuminate\Database\Connection
+     */
+    public function getConnection($name = null)
+    {
+        return $this->manager->connection($name);
+    }
+
+    /**
+     * Register a connection with the manager.
+     *
+     * @param  array  $config
+     * @param  string  $name
+     * @return void
+     */
+    public function addConnection(array $config, $name = 'default')
+    {
+        $connections = $this->container['config']['database.connections'];
+
+        $connections[$name] = $config;
+
+        $this->container['config']['database.connections'] = $connections;
+    }
+
+    /**
+     * Bootstrap Eloquent so it is ready for usage.
+     *
+     * @return void
+     */
+    public function bootEloquent()
+    {
+        Eloquent::setConnectionResolver($this->manager);
+
+        // If we have an event dispatcher instance, we will go ahead and register it
+        // with the Eloquent ORM, allowing for model callbacks while creating and
+        // updating "model" instances; however, it is not necessary to operate.
+        if ($dispatcher = $this->getEventDispatcher()) {
+            Eloquent::setEventDispatcher($dispatcher);
+        }
+    }
+
+    /**
+     * Set the fetch mode for the database connections.
+     *
+     * @param  int  $fetchMode
+     * @return $this
+     */
+    public function setFetchMode($fetchMode)
+    {
+        $this->container['config']['database.fetch'] = $fetchMode;
+
+        return $this;
+    }
+
+    /**
+     * Get the database manager instance.
+     *
+     * @return \Illuminate\Database\DatabaseManager
+     */
+    public function getDatabaseManager()
+    {
+        return $this->manager;
+    }
+
+    /**
+     * Get the current event dispatcher instance.
+     *
+     * @return \Illuminate\Contracts\Events\Dispatcher|null
+     */
+    public function getEventDispatcher()
+    {
+        if ($this->container->bound('events')) {
+            return $this->container['events'];
+        }
+    }
+
+    /**
+     * Set the event dispatcher instance to be used by connections.
+     *
+     * @param  \Illuminate\Contracts\Events\Dispatcher  $dispatcher
+     * @return void
+     */
+    public function setEventDispatcher(Dispatcher $dispatcher)
+    {
+        $this->container->instance('events', $dispatcher);
+    }
+
+    /**
+     * Dynamically pass methods to the default connection.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public static function __callStatic($method, $parameters)
+    {
+        return static::connection()->$method(...$parameters);
+    }
+}
diff --git a/vendor/illuminate/database/ClassMorphViolationException.php b/vendor/illuminate/database/ClassMorphViolationException.php
new file mode 100644
index 0000000..6594d2d
--- /dev/null
+++ b/vendor/illuminate/database/ClassMorphViolationException.php
@@ -0,0 +1,29 @@
+model = $class;
+    }
+}
diff --git a/vendor/illuminate/database/Concerns/BuildsQueries.php b/vendor/illuminate/database/Concerns/BuildsQueries.php
new file mode 100644
index 0000000..16dc024
--- /dev/null
+++ b/vendor/illuminate/database/Concerns/BuildsQueries.php
@@ -0,0 +1,506 @@
+enforceOrderBy();
+
+        $page = 1;
+
+        do {
+            // We'll execute the query for the given page and get the results. If there are
+            // no results we can just break and return from here. When there are results
+            // we will call the callback with the current chunk of these results here.
+            $results = $this->forPage($page, $count)->get();
+
+            $countResults = $results->count();
+
+            if ($countResults == 0) {
+                break;
+            }
+
+            // On each chunk result set, we will pass them to the callback and then let the
+            // developer take care of everything within the callback, which allows us to
+            // keep the memory low for spinning through large result sets for working.
+            if ($callback($results, $page) === false) {
+                return false;
+            }
+
+            unset($results);
+
+            $page++;
+        } while ($countResults == $count);
+
+        return true;
+    }
+
+    /**
+     * Run a map over each item while chunking.
+     *
+     * @param  callable  $callback
+     * @param  int  $count
+     * @return \Illuminate\Support\Collection
+     */
+    public function chunkMap(callable $callback, $count = 1000)
+    {
+        $collection = Collection::make();
+
+        $this->chunk($count, function ($items) use ($collection, $callback) {
+            $items->each(function ($item) use ($collection, $callback) {
+                $collection->push($callback($item));
+            });
+        });
+
+        return $collection;
+    }
+
+    /**
+     * Execute a callback over each item while chunking.
+     *
+     * @param  callable  $callback
+     * @param  int  $count
+     * @return bool
+     *
+     * @throws \RuntimeException
+     */
+    public function each(callable $callback, $count = 1000)
+    {
+        return $this->chunk($count, function ($results) use ($callback) {
+            foreach ($results as $key => $value) {
+                if ($callback($value, $key) === false) {
+                    return false;
+                }
+            }
+        });
+    }
+
+    /**
+     * Chunk the results of a query by comparing IDs.
+     *
+     * @param  int  $count
+     * @param  callable  $callback
+     * @param  string|null  $column
+     * @param  string|null  $alias
+     * @return bool
+     */
+    public function chunkById($count, callable $callback, $column = null, $alias = null)
+    {
+        $column ??= $this->defaultKeyName();
+
+        $alias ??= $column;
+
+        $lastId = null;
+
+        $page = 1;
+
+        do {
+            $clone = clone $this;
+
+            // We'll execute the query for the given page and get the results. If there are
+            // no results we can just break and return from here. When there are results
+            // we will call the callback with the current chunk of these results here.
+            $results = $clone->forPageAfterId($count, $lastId, $column)->get();
+
+            $countResults = $results->count();
+
+            if ($countResults == 0) {
+                break;
+            }
+
+            // On each chunk result set, we will pass them to the callback and then let the
+            // developer take care of everything within the callback, which allows us to
+            // keep the memory low for spinning through large result sets for working.
+            if ($callback($results, $page) === false) {
+                return false;
+            }
+
+            $lastId = data_get($results->last(), $alias);
+
+            if ($lastId === null) {
+                throw new RuntimeException("The chunkById operation was aborted because the [{$alias}] column is not present in the query result.");
+            }
+
+            unset($results);
+
+            $page++;
+        } while ($countResults == $count);
+
+        return true;
+    }
+
+    /**
+     * Execute a callback over each item while chunking by ID.
+     *
+     * @param  callable  $callback
+     * @param  int  $count
+     * @param  string|null  $column
+     * @param  string|null  $alias
+     * @return bool
+     */
+    public function eachById(callable $callback, $count = 1000, $column = null, $alias = null)
+    {
+        return $this->chunkById($count, function ($results, $page) use ($callback, $count) {
+            foreach ($results as $key => $value) {
+                if ($callback($value, (($page - 1) * $count) + $key) === false) {
+                    return false;
+                }
+            }
+        }, $column, $alias);
+    }
+
+    /**
+     * Query lazily, by chunks of the given size.
+     *
+     * @param  int  $chunkSize
+     * @return \Illuminate\Support\LazyCollection
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function lazy($chunkSize = 1000)
+    {
+        if ($chunkSize < 1) {
+            throw new InvalidArgumentException('The chunk size should be at least 1');
+        }
+
+        $this->enforceOrderBy();
+
+        return LazyCollection::make(function () use ($chunkSize) {
+            $page = 1;
+
+            while (true) {
+                $results = $this->forPage($page++, $chunkSize)->get();
+
+                foreach ($results as $result) {
+                    yield $result;
+                }
+
+                if ($results->count() < $chunkSize) {
+                    return;
+                }
+            }
+        });
+    }
+
+    /**
+     * Query lazily, by chunking the results of a query by comparing IDs.
+     *
+     * @param  int  $chunkSize
+     * @param  string|null  $column
+     * @param  string|null  $alias
+     * @return \Illuminate\Support\LazyCollection
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function lazyById($chunkSize = 1000, $column = null, $alias = null)
+    {
+        return $this->orderedLazyById($chunkSize, $column, $alias);
+    }
+
+    /**
+     * Query lazily, by chunking the results of a query by comparing IDs in descending order.
+     *
+     * @param  int  $chunkSize
+     * @param  string|null  $column
+     * @param  string|null  $alias
+     * @return \Illuminate\Support\LazyCollection
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function lazyByIdDesc($chunkSize = 1000, $column = null, $alias = null)
+    {
+        return $this->orderedLazyById($chunkSize, $column, $alias, true);
+    }
+
+    /**
+     * Query lazily, by chunking the results of a query by comparing IDs in a given order.
+     *
+     * @param  int  $chunkSize
+     * @param  string|null  $column
+     * @param  string|null  $alias
+     * @param  bool  $descending
+     * @return \Illuminate\Support\LazyCollection
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function orderedLazyById($chunkSize = 1000, $column = null, $alias = null, $descending = false)
+    {
+        if ($chunkSize < 1) {
+            throw new InvalidArgumentException('The chunk size should be at least 1');
+        }
+
+        $column ??= $this->defaultKeyName();
+
+        $alias ??= $column;
+
+        return LazyCollection::make(function () use ($chunkSize, $column, $alias, $descending) {
+            $lastId = null;
+
+            while (true) {
+                $clone = clone $this;
+
+                if ($descending) {
+                    $results = $clone->forPageBeforeId($chunkSize, $lastId, $column)->get();
+                } else {
+                    $results = $clone->forPageAfterId($chunkSize, $lastId, $column)->get();
+                }
+
+                foreach ($results as $result) {
+                    yield $result;
+                }
+
+                if ($results->count() < $chunkSize) {
+                    return;
+                }
+
+                $lastId = $results->last()->{$alias};
+            }
+        });
+    }
+
+    /**
+     * Execute the query and get the first result.
+     *
+     * @param  array|string  $columns
+     * @return \Illuminate\Database\Eloquent\Model|object|static|null
+     */
+    public function first($columns = ['*'])
+    {
+        return $this->take(1)->get($columns)->first();
+    }
+
+    /**
+     * Execute the query and get the first result if it's the sole matching record.
+     *
+     * @param  array|string  $columns
+     * @return \Illuminate\Database\Eloquent\Model|object|static|null
+     *
+     * @throws \Illuminate\Database\RecordsNotFoundException
+     * @throws \Illuminate\Database\MultipleRecordsFoundException
+     */
+    public function sole($columns = ['*'])
+    {
+        $result = $this->take(2)->get($columns);
+
+        $count = $result->count();
+
+        if ($count === 0) {
+            throw new RecordsNotFoundException;
+        }
+
+        if ($count > 1) {
+            throw new MultipleRecordsFoundException($count);
+        }
+
+        return $result->first();
+    }
+
+    /**
+     * Paginate the given query using a cursor paginator.
+     *
+     * @param  int  $perPage
+     * @param  array|string  $columns
+     * @param  string  $cursorName
+     * @param  \Illuminate\Pagination\Cursor|string|null  $cursor
+     * @return \Illuminate\Contracts\Pagination\CursorPaginator
+     */
+    protected function paginateUsingCursor($perPage, $columns = ['*'], $cursorName = 'cursor', $cursor = null)
+    {
+        if (! $cursor instanceof Cursor) {
+            $cursor = is_string($cursor)
+                ? Cursor::fromEncoded($cursor)
+                : CursorPaginator::resolveCurrentCursor($cursorName, $cursor);
+        }
+
+        $orders = $this->ensureOrderForCursorPagination(! is_null($cursor) && $cursor->pointsToPreviousItems());
+
+        if (! is_null($cursor)) {
+            $addCursorConditions = function (self $builder, $previousColumn, $i) use (&$addCursorConditions, $cursor, $orders) {
+                $unionBuilders = isset($builder->unions) ? collect($builder->unions)->pluck('query') : collect();
+
+                if (! is_null($previousColumn)) {
+                    $originalColumn = $this->getOriginalColumnNameForCursorPagination($this, $previousColumn);
+
+                    $builder->where(
+                        Str::contains($originalColumn, ['(', ')']) ? new Expression($originalColumn) : $originalColumn,
+                        '=',
+                        $cursor->parameter($previousColumn)
+                    );
+
+                    $unionBuilders->each(function ($unionBuilder) use ($previousColumn, $cursor) {
+                        $unionBuilder->where(
+                            $this->getOriginalColumnNameForCursorPagination($this, $previousColumn),
+                            '=',
+                            $cursor->parameter($previousColumn)
+                        );
+
+                        $this->addBinding($unionBuilder->getRawBindings()['where'], 'union');
+                    });
+                }
+
+                $builder->where(function (self $builder) use ($addCursorConditions, $cursor, $orders, $i, $unionBuilders) {
+                    ['column' => $column, 'direction' => $direction] = $orders[$i];
+
+                    $originalColumn = $this->getOriginalColumnNameForCursorPagination($this, $column);
+
+                    $builder->where(
+                        Str::contains($originalColumn, ['(', ')']) ? new Expression($originalColumn) : $originalColumn,
+                        $direction === 'asc' ? '>' : '<',
+                        $cursor->parameter($column)
+                    );
+
+                    if ($i < $orders->count() - 1) {
+                        $builder->orWhere(function (self $builder) use ($addCursorConditions, $column, $i) {
+                            $addCursorConditions($builder, $column, $i + 1);
+                        });
+                    }
+
+                    $unionBuilders->each(function ($unionBuilder) use ($column, $direction, $cursor, $i, $orders, $addCursorConditions) {
+                        $unionBuilder->where(function ($unionBuilder) use ($column, $direction, $cursor, $i, $orders, $addCursorConditions) {
+                            $unionBuilder->where(
+                                $this->getOriginalColumnNameForCursorPagination($this, $column),
+                                $direction === 'asc' ? '>' : '<',
+                                $cursor->parameter($column)
+                            );
+
+                            if ($i < $orders->count() - 1) {
+                                $unionBuilder->orWhere(function (self $builder) use ($addCursorConditions, $column, $i) {
+                                    $addCursorConditions($builder, $column, $i + 1);
+                                });
+                            }
+
+                            $this->addBinding($unionBuilder->getRawBindings()['where'], 'union');
+                        });
+                    });
+                });
+            };
+
+            $addCursorConditions($this, null, 0);
+        }
+
+        $this->limit($perPage + 1);
+
+        return $this->cursorPaginator($this->get($columns), $perPage, $cursor, [
+            'path' => Paginator::resolveCurrentPath(),
+            'cursorName' => $cursorName,
+            'parameters' => $orders->pluck('column')->toArray(),
+        ]);
+    }
+
+    /**
+     * Get the original column name of the given column, without any aliasing.
+     *
+     * @param  \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder  $builder
+     * @param  string  $parameter
+     * @return string
+     */
+    protected function getOriginalColumnNameForCursorPagination($builder, string $parameter)
+    {
+        $columns = $builder instanceof Builder ? $builder->getQuery()->columns : $builder->columns;
+
+        if (! is_null($columns)) {
+            foreach ($columns as $column) {
+                if (($position = strripos($column, ' as ')) !== false) {
+                    $original = substr($column, 0, $position);
+
+                    $alias = substr($column, $position + 4);
+
+                    if ($parameter === $alias || $builder->getGrammar()->wrap($parameter) === $alias) {
+                        return $original;
+                    }
+                }
+            }
+        }
+
+        return $parameter;
+    }
+
+    /**
+     * Create a new length-aware paginator instance.
+     *
+     * @param  \Illuminate\Support\Collection  $items
+     * @param  int  $total
+     * @param  int  $perPage
+     * @param  int  $currentPage
+     * @param  array  $options
+     * @return \Illuminate\Pagination\LengthAwarePaginator
+     */
+    protected function paginator($items, $total, $perPage, $currentPage, $options)
+    {
+        return Container::getInstance()->makeWith(LengthAwarePaginator::class, compact(
+            'items', 'total', 'perPage', 'currentPage', 'options'
+        ));
+    }
+
+    /**
+     * Create a new simple paginator instance.
+     *
+     * @param  \Illuminate\Support\Collection  $items
+     * @param  int  $perPage
+     * @param  int  $currentPage
+     * @param  array  $options
+     * @return \Illuminate\Pagination\Paginator
+     */
+    protected function simplePaginator($items, $perPage, $currentPage, $options)
+    {
+        return Container::getInstance()->makeWith(Paginator::class, compact(
+            'items', 'perPage', 'currentPage', 'options'
+        ));
+    }
+
+    /**
+     * Create a new cursor paginator instance.
+     *
+     * @param  \Illuminate\Support\Collection  $items
+     * @param  int  $perPage
+     * @param  \Illuminate\Pagination\Cursor  $cursor
+     * @param  array  $options
+     * @return \Illuminate\Pagination\CursorPaginator
+     */
+    protected function cursorPaginator($items, $perPage, $cursor, $options)
+    {
+        return Container::getInstance()->makeWith(CursorPaginator::class, compact(
+            'items', 'perPage', 'cursor', 'options'
+        ));
+    }
+
+    /**
+     * Pass the query to a given callback.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function tap($callback)
+    {
+        $callback($this);
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/database/Concerns/CompilesJsonPaths.php b/vendor/illuminate/database/Concerns/CompilesJsonPaths.php
new file mode 100644
index 0000000..ade5461
--- /dev/null
+++ b/vendor/illuminate/database/Concerns/CompilesJsonPaths.php
@@ -0,0 +1,64 @@
+', $column, 2);
+
+        $field = $this->wrap($parts[0]);
+
+        $path = count($parts) > 1 ? ', '.$this->wrapJsonPath($parts[1], '->') : '';
+
+        return [$field, $path];
+    }
+
+    /**
+     * Wrap the given JSON path.
+     *
+     * @param  string  $value
+     * @param  string  $delimiter
+     * @return string
+     */
+    protected function wrapJsonPath($value, $delimiter = '->')
+    {
+        $value = preg_replace("/([\\\\]+)?\\'/", "''", $value);
+
+        $jsonPath = collect(explode($delimiter, $value))
+            ->map(fn ($segment) => $this->wrapJsonPathSegment($segment))
+            ->join('.');
+
+        return "'$".(str_starts_with($jsonPath, '[') ? '' : '.').$jsonPath."'";
+    }
+
+    /**
+     * Wrap the given JSON path segment.
+     *
+     * @param  string  $segment
+     * @return string
+     */
+    protected function wrapJsonPathSegment($segment)
+    {
+        if (preg_match('/(\[[^\]]+\])+$/', $segment, $parts)) {
+            $key = Str::beforeLast($segment, $parts[0]);
+
+            if (! empty($key)) {
+                return '"'.$key.'"'.$parts[0];
+            }
+
+            return $parts[0];
+        }
+
+        return '"'.$segment.'"';
+    }
+}
diff --git a/vendor/illuminate/database/Concerns/ExplainsQueries.php b/vendor/illuminate/database/Concerns/ExplainsQueries.php
new file mode 100644
index 0000000..7168de1
--- /dev/null
+++ b/vendor/illuminate/database/Concerns/ExplainsQueries.php
@@ -0,0 +1,24 @@
+toSql();
+
+        $bindings = $this->getBindings();
+
+        $explanation = $this->getConnection()->select('EXPLAIN '.$sql, $bindings);
+
+        return new Collection($explanation);
+    }
+}
diff --git a/vendor/illuminate/database/Concerns/ManagesTransactions.php b/vendor/illuminate/database/Concerns/ManagesTransactions.php
new file mode 100644
index 0000000..14661cc
--- /dev/null
+++ b/vendor/illuminate/database/Concerns/ManagesTransactions.php
@@ -0,0 +1,352 @@
+beginTransaction();
+
+            // We'll simply execute the given callback within a try / catch block and if we
+            // catch any exception we can rollback this transaction so that none of this
+            // gets actually persisted to a database or stored in a permanent fashion.
+            try {
+                $callbackResult = $callback($this);
+            }
+
+            // If we catch an exception we'll rollback this transaction and try again if we
+            // are not out of attempts. If we are out of attempts we will just throw the
+            // exception back out, and let the developer handle an uncaught exception.
+            catch (Throwable $e) {
+                $this->handleTransactionException(
+                    $e, $currentAttempt, $attempts
+                );
+
+                continue;
+            }
+
+            try {
+                if ($this->transactions == 1) {
+                    $this->fireConnectionEvent('committing');
+                    $this->getPdo()->commit();
+                }
+
+                $this->transactions = max(0, $this->transactions - 1);
+
+                if ($this->afterCommitCallbacksShouldBeExecuted()) {
+                    $this->transactionsManager?->commit($this->getName());
+                }
+            } catch (Throwable $e) {
+                $this->handleCommitTransactionException(
+                    $e, $currentAttempt, $attempts
+                );
+
+                continue;
+            }
+
+            $this->fireConnectionEvent('committed');
+
+            return $callbackResult;
+        }
+    }
+
+    /**
+     * Handle an exception encountered when running a transacted statement.
+     *
+     * @param  \Throwable  $e
+     * @param  int  $currentAttempt
+     * @param  int  $maxAttempts
+     * @return void
+     *
+     * @throws \Throwable
+     */
+    protected function handleTransactionException(Throwable $e, $currentAttempt, $maxAttempts)
+    {
+        // On a deadlock, MySQL rolls back the entire transaction so we can't just
+        // retry the query. We have to throw this exception all the way out and
+        // let the developer handle it in another way. We will decrement too.
+        if ($this->causedByConcurrencyError($e) &&
+            $this->transactions > 1) {
+            $this->transactions--;
+
+            $this->transactionsManager?->rollback(
+                $this->getName(), $this->transactions
+            );
+
+            throw new DeadlockException($e->getMessage(), is_int($e->getCode()) ? $e->getCode() : 0, $e);
+        }
+
+        // If there was an exception we will rollback this transaction and then we
+        // can check if we have exceeded the maximum attempt count for this and
+        // if we haven't we will return and try this query again in our loop.
+        $this->rollBack();
+
+        if ($this->causedByConcurrencyError($e) &&
+            $currentAttempt < $maxAttempts) {
+            return;
+        }
+
+        throw $e;
+    }
+
+    /**
+     * Start a new database transaction.
+     *
+     * @return void
+     *
+     * @throws \Throwable
+     */
+    public function beginTransaction()
+    {
+        $this->createTransaction();
+
+        $this->transactions++;
+
+        $this->transactionsManager?->begin(
+            $this->getName(), $this->transactions
+        );
+
+        $this->fireConnectionEvent('beganTransaction');
+    }
+
+    /**
+     * Create a transaction within the database.
+     *
+     * @return void
+     *
+     * @throws \Throwable
+     */
+    protected function createTransaction()
+    {
+        if ($this->transactions == 0) {
+            $this->reconnectIfMissingConnection();
+
+            try {
+                $this->getPdo()->beginTransaction();
+            } catch (Throwable $e) {
+                $this->handleBeginTransactionException($e);
+            }
+        } elseif ($this->transactions >= 1 && $this->queryGrammar->supportsSavepoints()) {
+            $this->createSavepoint();
+        }
+    }
+
+    /**
+     * Create a save point within the database.
+     *
+     * @return void
+     *
+     * @throws \Throwable
+     */
+    protected function createSavepoint()
+    {
+        $this->getPdo()->exec(
+            $this->queryGrammar->compileSavepoint('trans'.($this->transactions + 1))
+        );
+    }
+
+    /**
+     * Handle an exception from a transaction beginning.
+     *
+     * @param  \Throwable  $e
+     * @return void
+     *
+     * @throws \Throwable
+     */
+    protected function handleBeginTransactionException(Throwable $e)
+    {
+        if ($this->causedByLostConnection($e)) {
+            $this->reconnect();
+
+            $this->getPdo()->beginTransaction();
+        } else {
+            throw $e;
+        }
+    }
+
+    /**
+     * Commit the active database transaction.
+     *
+     * @return void
+     *
+     * @throws \Throwable
+     */
+    public function commit()
+    {
+        if ($this->transactionLevel() == 1) {
+            $this->fireConnectionEvent('committing');
+            $this->getPdo()->commit();
+        }
+
+        $this->transactions = max(0, $this->transactions - 1);
+
+        if ($this->afterCommitCallbacksShouldBeExecuted()) {
+            $this->transactionsManager?->commit($this->getName());
+        }
+
+        $this->fireConnectionEvent('committed');
+    }
+
+    /**
+     * Determine if after commit callbacks should be executed.
+     *
+     * @return bool
+     */
+    protected function afterCommitCallbacksShouldBeExecuted()
+    {
+        return $this->transactions == 0 ||
+            ($this->transactionsManager &&
+             $this->transactionsManager->callbackApplicableTransactions()->count() === 1);
+    }
+
+    /**
+     * Handle an exception encountered when committing a transaction.
+     *
+     * @param  \Throwable  $e
+     * @param  int  $currentAttempt
+     * @param  int  $maxAttempts
+     * @return void
+     *
+     * @throws \Throwable
+     */
+    protected function handleCommitTransactionException(Throwable $e, $currentAttempt, $maxAttempts)
+    {
+        $this->transactions = max(0, $this->transactions - 1);
+
+        if ($this->causedByConcurrencyError($e) && $currentAttempt < $maxAttempts) {
+            return;
+        }
+
+        if ($this->causedByLostConnection($e)) {
+            $this->transactions = 0;
+        }
+
+        throw $e;
+    }
+
+    /**
+     * Rollback the active database transaction.
+     *
+     * @param  int|null  $toLevel
+     * @return void
+     *
+     * @throws \Throwable
+     */
+    public function rollBack($toLevel = null)
+    {
+        // We allow developers to rollback to a certain transaction level. We will verify
+        // that this given transaction level is valid before attempting to rollback to
+        // that level. If it's not we will just return out and not attempt anything.
+        $toLevel = is_null($toLevel)
+                    ? $this->transactions - 1
+                    : $toLevel;
+
+        if ($toLevel < 0 || $toLevel >= $this->transactions) {
+            return;
+        }
+
+        // Next, we will actually perform this rollback within this database and fire the
+        // rollback event. We will also set the current transaction level to the given
+        // level that was passed into this method so it will be right from here out.
+        try {
+            $this->performRollBack($toLevel);
+        } catch (Throwable $e) {
+            $this->handleRollBackException($e);
+        }
+
+        $this->transactions = $toLevel;
+
+        $this->transactionsManager?->rollback(
+            $this->getName(), $this->transactions
+        );
+
+        $this->fireConnectionEvent('rollingBack');
+    }
+
+    /**
+     * Perform a rollback within the database.
+     *
+     * @param  int  $toLevel
+     * @return void
+     *
+     * @throws \Throwable
+     */
+    protected function performRollBack($toLevel)
+    {
+        if ($toLevel == 0) {
+            $pdo = $this->getPdo();
+
+            if ($pdo->inTransaction()) {
+                $pdo->rollBack();
+            }
+        } elseif ($this->queryGrammar->supportsSavepoints()) {
+            $this->getPdo()->exec(
+                $this->queryGrammar->compileSavepointRollBack('trans'.($toLevel + 1))
+            );
+        }
+    }
+
+    /**
+     * Handle an exception from a rollback.
+     *
+     * @param  \Throwable  $e
+     * @return void
+     *
+     * @throws \Throwable
+     */
+    protected function handleRollBackException(Throwable $e)
+    {
+        if ($this->causedByLostConnection($e)) {
+            $this->transactions = 0;
+
+            $this->transactionsManager?->rollback(
+                $this->getName(), $this->transactions
+            );
+        }
+
+        throw $e;
+    }
+
+    /**
+     * Get the number of active transactions.
+     *
+     * @return int
+     */
+    public function transactionLevel()
+    {
+        return $this->transactions;
+    }
+
+    /**
+     * Execute the callback after a transaction commits.
+     *
+     * @param  callable  $callback
+     * @return void
+     *
+     * @throws \RuntimeException
+     */
+    public function afterCommit($callback)
+    {
+        if ($this->transactionsManager) {
+            return $this->transactionsManager->addCallback($callback);
+        }
+
+        throw new RuntimeException('Transactions Manager has not been set.');
+    }
+}
diff --git a/vendor/illuminate/database/Concerns/ParsesSearchPath.php b/vendor/illuminate/database/Concerns/ParsesSearchPath.php
new file mode 100644
index 0000000..e822c72
--- /dev/null
+++ b/vendor/illuminate/database/Concerns/ParsesSearchPath.php
@@ -0,0 +1,25 @@
+
+     */
+    protected $doctrineTypeMappings = [];
+
+    /**
+     * The connection resolvers.
+     *
+     * @var \Closure[]
+     */
+    protected static $resolvers = [];
+
+    /**
+     * Create a new database connection instance.
+     *
+     * @param  \PDO|\Closure  $pdo
+     * @param  string  $database
+     * @param  string  $tablePrefix
+     * @param  array  $config
+     * @return void
+     */
+    public function __construct($pdo, $database = '', $tablePrefix = '', array $config = [])
+    {
+        $this->pdo = $pdo;
+
+        // First we will setup the default properties. We keep track of the DB
+        // name we are connected to since it is needed when some reflective
+        // type commands are run such as checking whether a table exists.
+        $this->database = $database;
+
+        $this->tablePrefix = $tablePrefix;
+
+        $this->config = $config;
+
+        // We need to initialize a query grammar and the query post processors
+        // which are both very important parts of the database abstractions
+        // so we initialize these to their default values while starting.
+        $this->useDefaultQueryGrammar();
+
+        $this->useDefaultPostProcessor();
+    }
+
+    /**
+     * Set the query grammar to the default implementation.
+     *
+     * @return void
+     */
+    public function useDefaultQueryGrammar()
+    {
+        $this->queryGrammar = $this->getDefaultQueryGrammar();
+    }
+
+    /**
+     * Get the default query grammar instance.
+     *
+     * @return \Illuminate\Database\Query\Grammars\Grammar
+     */
+    protected function getDefaultQueryGrammar()
+    {
+        return new QueryGrammar;
+    }
+
+    /**
+     * Set the schema grammar to the default implementation.
+     *
+     * @return void
+     */
+    public function useDefaultSchemaGrammar()
+    {
+        $this->schemaGrammar = $this->getDefaultSchemaGrammar();
+    }
+
+    /**
+     * Get the default schema grammar instance.
+     *
+     * @return \Illuminate\Database\Schema\Grammars\Grammar
+     */
+    protected function getDefaultSchemaGrammar()
+    {
+        //
+    }
+
+    /**
+     * Set the query post processor to the default implementation.
+     *
+     * @return void
+     */
+    public function useDefaultPostProcessor()
+    {
+        $this->postProcessor = $this->getDefaultPostProcessor();
+    }
+
+    /**
+     * Get the default post processor instance.
+     *
+     * @return \Illuminate\Database\Query\Processors\Processor
+     */
+    protected function getDefaultPostProcessor()
+    {
+        return new Processor;
+    }
+
+    /**
+     * Get a schema builder instance for the connection.
+     *
+     * @return \Illuminate\Database\Schema\Builder
+     */
+    public function getSchemaBuilder()
+    {
+        if (is_null($this->schemaGrammar)) {
+            $this->useDefaultSchemaGrammar();
+        }
+
+        return new SchemaBuilder($this);
+    }
+
+    /**
+     * Begin a fluent query against a database table.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|string  $table
+     * @param  string|null  $as
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function table($table, $as = null)
+    {
+        return $this->query()->from($table, $as);
+    }
+
+    /**
+     * Get a new query builder instance.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function query()
+    {
+        return new QueryBuilder(
+            $this, $this->getQueryGrammar(), $this->getPostProcessor()
+        );
+    }
+
+    /**
+     * Run a select statement and return a single result.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @param  bool  $useReadPdo
+     * @return mixed
+     */
+    public function selectOne($query, $bindings = [], $useReadPdo = true)
+    {
+        $records = $this->select($query, $bindings, $useReadPdo);
+
+        return array_shift($records);
+    }
+
+    /**
+     * Run a select statement and return the first column of the first row.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @param  bool  $useReadPdo
+     * @return mixed
+     *
+     * @throws \Illuminate\Database\MultipleColumnsSelectedException
+     */
+    public function scalar($query, $bindings = [], $useReadPdo = true)
+    {
+        $record = $this->selectOne($query, $bindings, $useReadPdo);
+
+        if (is_null($record)) {
+            return null;
+        }
+
+        $record = (array) $record;
+
+        if (count($record) > 1) {
+            throw new MultipleColumnsSelectedException;
+        }
+
+        return reset($record);
+    }
+
+    /**
+     * Run a select statement against the database.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @return array
+     */
+    public function selectFromWriteConnection($query, $bindings = [])
+    {
+        return $this->select($query, $bindings, false);
+    }
+
+    /**
+     * Run a select statement against the database.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @param  bool  $useReadPdo
+     * @return array
+     */
+    public function select($query, $bindings = [], $useReadPdo = true)
+    {
+        return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
+            if ($this->pretending()) {
+                return [];
+            }
+
+            // For select statements, we'll simply execute the query and return an array
+            // of the database result set. Each element in the array will be a single
+            // row from the database table, and will either be an array or objects.
+            $statement = $this->prepared(
+                $this->getPdoForSelect($useReadPdo)->prepare($query)
+            );
+
+            $this->bindValues($statement, $this->prepareBindings($bindings));
+
+            $statement->execute();
+
+            return $statement->fetchAll();
+        });
+    }
+
+    /**
+     * Run a select statement against the database and returns a generator.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @param  bool  $useReadPdo
+     * @return \Generator
+     */
+    public function cursor($query, $bindings = [], $useReadPdo = true)
+    {
+        $statement = $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
+            if ($this->pretending()) {
+                return [];
+            }
+
+            // First we will create a statement for the query. Then, we will set the fetch
+            // mode and prepare the bindings for the query. Once that's done we will be
+            // ready to execute the query against the database and return the cursor.
+            $statement = $this->prepared($this->getPdoForSelect($useReadPdo)
+                              ->prepare($query));
+
+            $this->bindValues(
+                $statement, $this->prepareBindings($bindings)
+            );
+
+            // Next, we'll execute the query against the database and return the statement
+            // so we can return the cursor. The cursor will use a PHP generator to give
+            // back one row at a time without using a bunch of memory to render them.
+            $statement->execute();
+
+            return $statement;
+        });
+
+        while ($record = $statement->fetch()) {
+            yield $record;
+        }
+    }
+
+    /**
+     * Configure the PDO prepared statement.
+     *
+     * @param  \PDOStatement  $statement
+     * @return \PDOStatement
+     */
+    protected function prepared(PDOStatement $statement)
+    {
+        $statement->setFetchMode($this->fetchMode);
+
+        $this->event(new StatementPrepared($this, $statement));
+
+        return $statement;
+    }
+
+    /**
+     * Get the PDO connection to use for a select query.
+     *
+     * @param  bool  $useReadPdo
+     * @return \PDO
+     */
+    protected function getPdoForSelect($useReadPdo = true)
+    {
+        return $useReadPdo ? $this->getReadPdo() : $this->getPdo();
+    }
+
+    /**
+     * Run an insert statement against the database.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @return bool
+     */
+    public function insert($query, $bindings = [])
+    {
+        return $this->statement($query, $bindings);
+    }
+
+    /**
+     * Run an update statement against the database.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @return int
+     */
+    public function update($query, $bindings = [])
+    {
+        return $this->affectingStatement($query, $bindings);
+    }
+
+    /**
+     * Run a delete statement against the database.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @return int
+     */
+    public function delete($query, $bindings = [])
+    {
+        return $this->affectingStatement($query, $bindings);
+    }
+
+    /**
+     * Execute an SQL statement and return the boolean result.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @return bool
+     */
+    public function statement($query, $bindings = [])
+    {
+        return $this->run($query, $bindings, function ($query, $bindings) {
+            if ($this->pretending()) {
+                return true;
+            }
+
+            $statement = $this->getPdo()->prepare($query);
+
+            $this->bindValues($statement, $this->prepareBindings($bindings));
+
+            $this->recordsHaveBeenModified();
+
+            return $statement->execute();
+        });
+    }
+
+    /**
+     * Run an SQL statement and get the number of rows affected.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @return int
+     */
+    public function affectingStatement($query, $bindings = [])
+    {
+        return $this->run($query, $bindings, function ($query, $bindings) {
+            if ($this->pretending()) {
+                return 0;
+            }
+
+            // For update or delete statements, we want to get the number of rows affected
+            // by the statement and return that back to the developer. We'll first need
+            // to execute the statement and then we'll use PDO to fetch the affected.
+            $statement = $this->getPdo()->prepare($query);
+
+            $this->bindValues($statement, $this->prepareBindings($bindings));
+
+            $statement->execute();
+
+            $this->recordsHaveBeenModified(
+                ($count = $statement->rowCount()) > 0
+            );
+
+            return $count;
+        });
+    }
+
+    /**
+     * Run a raw, unprepared query against the PDO connection.
+     *
+     * @param  string  $query
+     * @return bool
+     */
+    public function unprepared($query)
+    {
+        return $this->run($query, [], function ($query) {
+            if ($this->pretending()) {
+                return true;
+            }
+
+            $this->recordsHaveBeenModified(
+                $change = $this->getPdo()->exec($query) !== false
+            );
+
+            return $change;
+        });
+    }
+
+    /**
+     * Execute the given callback in "dry run" mode.
+     *
+     * @param  \Closure  $callback
+     * @return array
+     */
+    public function pretend(Closure $callback)
+    {
+        return $this->withFreshQueryLog(function () use ($callback) {
+            $this->pretending = true;
+
+            // Basically to make the database connection "pretend", we will just return
+            // the default values for all the query methods, then we will return an
+            // array of queries that were "executed" within the Closure callback.
+            $callback($this);
+
+            $this->pretending = false;
+
+            return $this->queryLog;
+        });
+    }
+
+    /**
+     * Execute the given callback in "dry run" mode.
+     *
+     * @param  \Closure  $callback
+     * @return array
+     */
+    protected function withFreshQueryLog($callback)
+    {
+        $loggingQueries = $this->loggingQueries;
+
+        // First we will back up the value of the logging queries property and then
+        // we'll be ready to run callbacks. This query log will also get cleared
+        // so we will have a new log of all the queries that are executed now.
+        $this->enableQueryLog();
+
+        $this->queryLog = [];
+
+        // Now we'll execute this callback and capture the result. Once it has been
+        // executed we will restore the value of query logging and give back the
+        // value of the callback so the original callers can have the results.
+        $result = $callback();
+
+        $this->loggingQueries = $loggingQueries;
+
+        return $result;
+    }
+
+    /**
+     * Bind values to their parameters in the given statement.
+     *
+     * @param  \PDOStatement  $statement
+     * @param  array  $bindings
+     * @return void
+     */
+    public function bindValues($statement, $bindings)
+    {
+        foreach ($bindings as $key => $value) {
+            $statement->bindValue(
+                is_string($key) ? $key : $key + 1,
+                $value,
+                match (true) {
+                    is_int($value) => PDO::PARAM_INT,
+                    is_resource($value) => PDO::PARAM_LOB,
+                    default => PDO::PARAM_STR
+                },
+            );
+        }
+    }
+
+    /**
+     * Prepare the query bindings for execution.
+     *
+     * @param  array  $bindings
+     * @return array
+     */
+    public function prepareBindings(array $bindings)
+    {
+        $grammar = $this->getQueryGrammar();
+
+        foreach ($bindings as $key => $value) {
+            // We need to transform all instances of DateTimeInterface into the actual
+            // date string. Each query grammar maintains its own date string format
+            // so we'll just ask the grammar for the format to get from the date.
+            if ($value instanceof DateTimeInterface) {
+                $bindings[$key] = $value->format($grammar->getDateFormat());
+            } elseif (is_bool($value)) {
+                $bindings[$key] = (int) $value;
+            }
+        }
+
+        return $bindings;
+    }
+
+    /**
+     * Run a SQL statement and log its execution context.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @param  \Closure  $callback
+     * @return mixed
+     *
+     * @throws \Illuminate\Database\QueryException
+     */
+    protected function run($query, $bindings, Closure $callback)
+    {
+        foreach ($this->beforeExecutingCallbacks as $beforeExecutingCallback) {
+            $beforeExecutingCallback($query, $bindings, $this);
+        }
+
+        $this->reconnectIfMissingConnection();
+
+        $start = microtime(true);
+
+        // Here we will run this query. If an exception occurs we'll determine if it was
+        // caused by a connection that has been lost. If that is the cause, we'll try
+        // to re-establish connection and re-run the query with a fresh connection.
+        try {
+            $result = $this->runQueryCallback($query, $bindings, $callback);
+        } catch (QueryException $e) {
+            $result = $this->handleQueryException(
+                $e, $query, $bindings, $callback
+            );
+        }
+
+        // Once we have run the query we will calculate the time that it took to run and
+        // then log the query, bindings, and execution time so we will report them on
+        // the event that the developer needs them. We'll log time in milliseconds.
+        $this->logQuery(
+            $query, $bindings, $this->getElapsedTime($start)
+        );
+
+        return $result;
+    }
+
+    /**
+     * Run a SQL statement.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @param  \Closure  $callback
+     * @return mixed
+     *
+     * @throws \Illuminate\Database\QueryException
+     */
+    protected function runQueryCallback($query, $bindings, Closure $callback)
+    {
+        // To execute the statement, we'll simply call the callback, which will actually
+        // run the SQL against the PDO connection. Then we can calculate the time it
+        // took to execute and log the query SQL, bindings and time in our memory.
+        try {
+            return $callback($query, $bindings);
+        }
+
+        // If an exception occurs when attempting to run a query, we'll format the error
+        // message to include the bindings with SQL, which will make this exception a
+        // lot more helpful to the developer instead of just the database's errors.
+        catch (Exception $e) {
+            throw new QueryException(
+                $query, $this->prepareBindings($bindings), $e
+            );
+        }
+    }
+
+    /**
+     * Log a query in the connection's query log.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @param  float|null  $time
+     * @return void
+     */
+    public function logQuery($query, $bindings, $time = null)
+    {
+        $this->totalQueryDuration += $time ?? 0.0;
+
+        $this->event(new QueryExecuted($query, $bindings, $time, $this));
+
+        if ($this->loggingQueries) {
+            $this->queryLog[] = compact('query', 'bindings', 'time');
+        }
+    }
+
+    /**
+     * Get the elapsed time since a given starting point.
+     *
+     * @param  int  $start
+     * @return float
+     */
+    protected function getElapsedTime($start)
+    {
+        return round((microtime(true) - $start) * 1000, 2);
+    }
+
+    /**
+     * Register a callback to be invoked when the connection queries for longer than a given amount of time.
+     *
+     * @param  \DateTimeInterface|\Carbon\CarbonInterval|float|int  $threshold
+     * @param  callable  $handler
+     * @return void
+     */
+    public function whenQueryingForLongerThan($threshold, $handler)
+    {
+        $threshold = $threshold instanceof DateTimeInterface
+            ? $this->secondsUntil($threshold) * 1000
+            : $threshold;
+
+        $threshold = $threshold instanceof CarbonInterval
+            ? $threshold->totalMilliseconds
+            : $threshold;
+
+        $this->queryDurationHandlers[] = [
+            'has_run' => false,
+            'handler' => $handler,
+        ];
+
+        $key = count($this->queryDurationHandlers) - 1;
+
+        $this->listen(function ($event) use ($threshold, $handler, $key) {
+            if (! $this->queryDurationHandlers[$key]['has_run'] && $this->totalQueryDuration() > $threshold) {
+                $handler($this, $event);
+
+                $this->queryDurationHandlers[$key]['has_run'] = true;
+            }
+        });
+    }
+
+    /**
+     * Allow all the query duration handlers to run again, even if they have already run.
+     *
+     * @return void
+     */
+    public function allowQueryDurationHandlersToRunAgain()
+    {
+        foreach ($this->queryDurationHandlers as $key => $queryDurationHandler) {
+            $this->queryDurationHandlers[$key]['has_run'] = false;
+        }
+    }
+
+    /**
+     * Get the duration of all run queries in milliseconds.
+     *
+     * @return float
+     */
+    public function totalQueryDuration()
+    {
+        return $this->totalQueryDuration;
+    }
+
+    /**
+     * Reset the duration of all run queries.
+     *
+     * @return void
+     */
+    public function resetTotalQueryDuration()
+    {
+        $this->totalQueryDuration = 0.0;
+    }
+
+    /**
+     * Handle a query exception.
+     *
+     * @param  \Illuminate\Database\QueryException  $e
+     * @param  string  $query
+     * @param  array  $bindings
+     * @param  \Closure  $callback
+     * @return mixed
+     *
+     * @throws \Illuminate\Database\QueryException
+     */
+    protected function handleQueryException(QueryException $e, $query, $bindings, Closure $callback)
+    {
+        if ($this->transactions >= 1) {
+            throw $e;
+        }
+
+        return $this->tryAgainIfCausedByLostConnection(
+            $e, $query, $bindings, $callback
+        );
+    }
+
+    /**
+     * Handle a query exception that occurred during query execution.
+     *
+     * @param  \Illuminate\Database\QueryException  $e
+     * @param  string  $query
+     * @param  array  $bindings
+     * @param  \Closure  $callback
+     * @return mixed
+     *
+     * @throws \Illuminate\Database\QueryException
+     */
+    protected function tryAgainIfCausedByLostConnection(QueryException $e, $query, $bindings, Closure $callback)
+    {
+        if ($this->causedByLostConnection($e->getPrevious())) {
+            $this->reconnect();
+
+            return $this->runQueryCallback($query, $bindings, $callback);
+        }
+
+        throw $e;
+    }
+
+    /**
+     * Reconnect to the database.
+     *
+     * @return mixed|false
+     *
+     * @throws \Illuminate\Database\LostConnectionException
+     */
+    public function reconnect()
+    {
+        if (is_callable($this->reconnector)) {
+            $this->doctrineConnection = null;
+
+            return call_user_func($this->reconnector, $this);
+        }
+
+        throw new LostConnectionException('Lost connection and no reconnector available.');
+    }
+
+    /**
+     * Reconnect to the database if a PDO connection is missing.
+     *
+     * @return void
+     */
+    protected function reconnectIfMissingConnection()
+    {
+        if (is_null($this->pdo)) {
+            $this->reconnect();
+        }
+    }
+
+    /**
+     * Disconnect from the underlying PDO connection.
+     *
+     * @return void
+     */
+    public function disconnect()
+    {
+        $this->setPdo(null)->setReadPdo(null);
+
+        $this->doctrineConnection = null;
+    }
+
+    /**
+     * Register a hook to be run just before a database query is executed.
+     *
+     * @param  \Closure  $callback
+     * @return $this
+     */
+    public function beforeExecuting(Closure $callback)
+    {
+        $this->beforeExecutingCallbacks[] = $callback;
+
+        return $this;
+    }
+
+    /**
+     * Register a database query listener with the connection.
+     *
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function listen(Closure $callback)
+    {
+        $this->events?->listen(Events\QueryExecuted::class, $callback);
+    }
+
+    /**
+     * Fire an event for this connection.
+     *
+     * @param  string  $event
+     * @return array|null
+     */
+    protected function fireConnectionEvent($event)
+    {
+        return $this->events?->dispatch(match ($event) {
+            'beganTransaction' => new TransactionBeginning($this),
+            'committed' => new TransactionCommitted($this),
+            'committing' => new TransactionCommitting($this),
+            'rollingBack' => new TransactionRolledBack($this),
+            default => null,
+        });
+    }
+
+    /**
+     * Fire the given event if possible.
+     *
+     * @param  mixed  $event
+     * @return void
+     */
+    protected function event($event)
+    {
+        $this->events?->dispatch($event);
+    }
+
+    /**
+     * Get a new raw query expression.
+     *
+     * @param  mixed  $value
+     * @return \Illuminate\Database\Query\Expression
+     */
+    public function raw($value)
+    {
+        return new Expression($value);
+    }
+
+    /**
+     * Determine if the database connection has modified any database records.
+     *
+     * @return bool
+     */
+    public function hasModifiedRecords()
+    {
+        return $this->recordsModified;
+    }
+
+    /**
+     * Indicate if any records have been modified.
+     *
+     * @param  bool  $value
+     * @return void
+     */
+    public function recordsHaveBeenModified($value = true)
+    {
+        if (! $this->recordsModified) {
+            $this->recordsModified = $value;
+        }
+    }
+
+    /**
+     * Set the record modification state.
+     *
+     * @param  bool  $value
+     * @return $this
+     */
+    public function setRecordModificationState(bool $value)
+    {
+        $this->recordsModified = $value;
+
+        return $this;
+    }
+
+    /**
+     * Reset the record modification state.
+     *
+     * @return void
+     */
+    public function forgetRecordModificationState()
+    {
+        $this->recordsModified = false;
+    }
+
+    /**
+     * Indicate that the connection should use the write PDO connection for reads.
+     *
+     * @param  bool  $value
+     * @return $this
+     */
+    public function useWriteConnectionWhenReading($value = true)
+    {
+        $this->readOnWriteConnection = $value;
+
+        return $this;
+    }
+
+    /**
+     * Is Doctrine available?
+     *
+     * @return bool
+     */
+    public function isDoctrineAvailable()
+    {
+        return class_exists('Doctrine\DBAL\Connection');
+    }
+
+    /**
+     * Indicates whether native alter operations will be used when dropping or renaming columns, even if Doctrine DBAL is installed.
+     *
+     * @return bool
+     */
+    public function usingNativeSchemaOperations()
+    {
+        return ! $this->isDoctrineAvailable() || SchemaBuilder::$alwaysUsesNativeSchemaOperationsIfPossible;
+    }
+
+    /**
+     * Get a Doctrine Schema Column instance.
+     *
+     * @param  string  $table
+     * @param  string  $column
+     * @return \Doctrine\DBAL\Schema\Column
+     */
+    public function getDoctrineColumn($table, $column)
+    {
+        $schema = $this->getDoctrineSchemaManager();
+
+        return $schema->listTableDetails($table)->getColumn($column);
+    }
+
+    /**
+     * Get the Doctrine DBAL schema manager for the connection.
+     *
+     * @return \Doctrine\DBAL\Schema\AbstractSchemaManager
+     */
+    public function getDoctrineSchemaManager()
+    {
+        $connection = $this->getDoctrineConnection();
+
+        // Doctrine v2 expects one parameter while v3 expects two. 2nd will be ignored on v2...
+        return $this->getDoctrineDriver()->getSchemaManager(
+            $connection,
+            $connection->getDatabasePlatform()
+        );
+    }
+
+    /**
+     * Get the Doctrine DBAL database connection instance.
+     *
+     * @return \Doctrine\DBAL\Connection
+     */
+    public function getDoctrineConnection()
+    {
+        if (is_null($this->doctrineConnection)) {
+            $driver = $this->getDoctrineDriver();
+
+            $this->doctrineConnection = new DoctrineConnection(array_filter([
+                'pdo' => $this->getPdo(),
+                'dbname' => $this->getDatabaseName(),
+                'driver' => $driver->getName(),
+                'serverVersion' => $this->getConfig('server_version'),
+            ]), $driver);
+
+            foreach ($this->doctrineTypeMappings as $name => $type) {
+                $this->doctrineConnection
+                    ->getDatabasePlatform()
+                    ->registerDoctrineTypeMapping($type, $name);
+            }
+        }
+
+        return $this->doctrineConnection;
+    }
+
+    /**
+     * Register a custom Doctrine mapping type.
+     *
+     * @param  Type|class-string  $class
+     * @param  string  $name
+     * @param  string  $type
+     * @return void
+     *
+     * @throws \Doctrine\DBAL\DBALException
+     * @throws \RuntimeException
+     */
+    public function registerDoctrineType(Type|string $class, string $name, string $type): void
+    {
+        if (! $this->isDoctrineAvailable()) {
+            throw new RuntimeException(
+                'Registering a custom Doctrine type requires Doctrine DBAL (doctrine/dbal).'
+            );
+        }
+
+        if (! Type::hasType($name)) {
+            Type::getTypeRegistry()
+                ->register($name, is_string($class) ? new $class() : $class);
+        }
+
+        $this->doctrineTypeMappings[$name] = $type;
+    }
+
+    /**
+     * Get the current PDO connection.
+     *
+     * @return \PDO
+     */
+    public function getPdo()
+    {
+        if ($this->pdo instanceof Closure) {
+            return $this->pdo = call_user_func($this->pdo);
+        }
+
+        return $this->pdo;
+    }
+
+    /**
+     * Get the current PDO connection parameter without executing any reconnect logic.
+     *
+     * @return \PDO|\Closure|null
+     */
+    public function getRawPdo()
+    {
+        return $this->pdo;
+    }
+
+    /**
+     * Get the current PDO connection used for reading.
+     *
+     * @return \PDO
+     */
+    public function getReadPdo()
+    {
+        if ($this->transactions > 0) {
+            return $this->getPdo();
+        }
+
+        if ($this->readOnWriteConnection ||
+            ($this->recordsModified && $this->getConfig('sticky'))) {
+            return $this->getPdo();
+        }
+
+        if ($this->readPdo instanceof Closure) {
+            return $this->readPdo = call_user_func($this->readPdo);
+        }
+
+        return $this->readPdo ?: $this->getPdo();
+    }
+
+    /**
+     * Get the current read PDO connection parameter without executing any reconnect logic.
+     *
+     * @return \PDO|\Closure|null
+     */
+    public function getRawReadPdo()
+    {
+        return $this->readPdo;
+    }
+
+    /**
+     * Set the PDO connection.
+     *
+     * @param  \PDO|\Closure|null  $pdo
+     * @return $this
+     */
+    public function setPdo($pdo)
+    {
+        $this->transactions = 0;
+
+        $this->pdo = $pdo;
+
+        return $this;
+    }
+
+    /**
+     * Set the PDO connection used for reading.
+     *
+     * @param  \PDO|\Closure|null  $pdo
+     * @return $this
+     */
+    public function setReadPdo($pdo)
+    {
+        $this->readPdo = $pdo;
+
+        return $this;
+    }
+
+    /**
+     * Set the reconnect instance on the connection.
+     *
+     * @param  callable  $reconnector
+     * @return $this
+     */
+    public function setReconnector(callable $reconnector)
+    {
+        $this->reconnector = $reconnector;
+
+        return $this;
+    }
+
+    /**
+     * Get the database connection name.
+     *
+     * @return string|null
+     */
+    public function getName()
+    {
+        return $this->getConfig('name');
+    }
+
+    /**
+     * Get the database connection full name.
+     *
+     * @return string|null
+     */
+    public function getNameWithReadWriteType()
+    {
+        return $this->getName().($this->readWriteType ? '::'.$this->readWriteType : '');
+    }
+
+    /**
+     * Get an option from the configuration options.
+     *
+     * @param  string|null  $option
+     * @return mixed
+     */
+    public function getConfig($option = null)
+    {
+        return Arr::get($this->config, $option);
+    }
+
+    /**
+     * Get the PDO driver name.
+     *
+     * @return string
+     */
+    public function getDriverName()
+    {
+        return $this->getConfig('driver');
+    }
+
+    /**
+     * Get the query grammar used by the connection.
+     *
+     * @return \Illuminate\Database\Query\Grammars\Grammar
+     */
+    public function getQueryGrammar()
+    {
+        return $this->queryGrammar;
+    }
+
+    /**
+     * Set the query grammar used by the connection.
+     *
+     * @param  \Illuminate\Database\Query\Grammars\Grammar  $grammar
+     * @return $this
+     */
+    public function setQueryGrammar(Query\Grammars\Grammar $grammar)
+    {
+        $this->queryGrammar = $grammar;
+
+        return $this;
+    }
+
+    /**
+     * Get the schema grammar used by the connection.
+     *
+     * @return \Illuminate\Database\Schema\Grammars\Grammar
+     */
+    public function getSchemaGrammar()
+    {
+        return $this->schemaGrammar;
+    }
+
+    /**
+     * Set the schema grammar used by the connection.
+     *
+     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
+     * @return $this
+     */
+    public function setSchemaGrammar(Schema\Grammars\Grammar $grammar)
+    {
+        $this->schemaGrammar = $grammar;
+
+        return $this;
+    }
+
+    /**
+     * Get the query post processor used by the connection.
+     *
+     * @return \Illuminate\Database\Query\Processors\Processor
+     */
+    public function getPostProcessor()
+    {
+        return $this->postProcessor;
+    }
+
+    /**
+     * Set the query post processor used by the connection.
+     *
+     * @param  \Illuminate\Database\Query\Processors\Processor  $processor
+     * @return $this
+     */
+    public function setPostProcessor(Processor $processor)
+    {
+        $this->postProcessor = $processor;
+
+        return $this;
+    }
+
+    /**
+     * Get the event dispatcher used by the connection.
+     *
+     * @return \Illuminate\Contracts\Events\Dispatcher
+     */
+    public function getEventDispatcher()
+    {
+        return $this->events;
+    }
+
+    /**
+     * Set the event dispatcher instance on the connection.
+     *
+     * @param  \Illuminate\Contracts\Events\Dispatcher  $events
+     * @return $this
+     */
+    public function setEventDispatcher(Dispatcher $events)
+    {
+        $this->events = $events;
+
+        return $this;
+    }
+
+    /**
+     * Unset the event dispatcher for this connection.
+     *
+     * @return void
+     */
+    public function unsetEventDispatcher()
+    {
+        $this->events = null;
+    }
+
+    /**
+     * Set the transaction manager instance on the connection.
+     *
+     * @param  \Illuminate\Database\DatabaseTransactionsManager  $manager
+     * @return $this
+     */
+    public function setTransactionManager($manager)
+    {
+        $this->transactionsManager = $manager;
+
+        return $this;
+    }
+
+    /**
+     * Unset the transaction manager for this connection.
+     *
+     * @return void
+     */
+    public function unsetTransactionManager()
+    {
+        $this->transactionsManager = null;
+    }
+
+    /**
+     * Determine if the connection is in a "dry run".
+     *
+     * @return bool
+     */
+    public function pretending()
+    {
+        return $this->pretending === true;
+    }
+
+    /**
+     * Get the connection query log.
+     *
+     * @return array
+     */
+    public function getQueryLog()
+    {
+        return $this->queryLog;
+    }
+
+    /**
+     * Clear the query log.
+     *
+     * @return void
+     */
+    public function flushQueryLog()
+    {
+        $this->queryLog = [];
+    }
+
+    /**
+     * Enable the query log on the connection.
+     *
+     * @return void
+     */
+    public function enableQueryLog()
+    {
+        $this->loggingQueries = true;
+    }
+
+    /**
+     * Disable the query log on the connection.
+     *
+     * @return void
+     */
+    public function disableQueryLog()
+    {
+        $this->loggingQueries = false;
+    }
+
+    /**
+     * Determine whether we're logging queries.
+     *
+     * @return bool
+     */
+    public function logging()
+    {
+        return $this->loggingQueries;
+    }
+
+    /**
+     * Get the name of the connected database.
+     *
+     * @return string
+     */
+    public function getDatabaseName()
+    {
+        return $this->database;
+    }
+
+    /**
+     * Set the name of the connected database.
+     *
+     * @param  string  $database
+     * @return $this
+     */
+    public function setDatabaseName($database)
+    {
+        $this->database = $database;
+
+        return $this;
+    }
+
+    /**
+     * Set the read / write type of the connection.
+     *
+     * @param  string|null  $readWriteType
+     * @return $this
+     */
+    public function setReadWriteType($readWriteType)
+    {
+        $this->readWriteType = $readWriteType;
+
+        return $this;
+    }
+
+    /**
+     * Get the table prefix for the connection.
+     *
+     * @return string
+     */
+    public function getTablePrefix()
+    {
+        return $this->tablePrefix;
+    }
+
+    /**
+     * Set the table prefix in use by the connection.
+     *
+     * @param  string  $prefix
+     * @return $this
+     */
+    public function setTablePrefix($prefix)
+    {
+        $this->tablePrefix = $prefix;
+
+        $this->getQueryGrammar()->setTablePrefix($prefix);
+
+        return $this;
+    }
+
+    /**
+     * Set the table prefix and return the grammar.
+     *
+     * @param  \Illuminate\Database\Grammar  $grammar
+     * @return \Illuminate\Database\Grammar
+     */
+    public function withTablePrefix(Grammar $grammar)
+    {
+        $grammar->setTablePrefix($this->tablePrefix);
+
+        return $grammar;
+    }
+
+    /**
+     * Register a connection resolver.
+     *
+     * @param  string  $driver
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public static function resolverFor($driver, Closure $callback)
+    {
+        static::$resolvers[$driver] = $callback;
+    }
+
+    /**
+     * Get the connection resolver for the given driver.
+     *
+     * @param  string  $driver
+     * @return mixed
+     */
+    public static function getResolver($driver)
+    {
+        return static::$resolvers[$driver] ?? null;
+    }
+}
diff --git a/vendor/illuminate/database/ConnectionInterface.php b/vendor/illuminate/database/ConnectionInterface.php
new file mode 100755
index 0000000..00b2395
--- /dev/null
+++ b/vendor/illuminate/database/ConnectionInterface.php
@@ -0,0 +1,170 @@
+  $connections
+     * @return void
+     */
+    public function __construct(array $connections = [])
+    {
+        foreach ($connections as $name => $connection) {
+            $this->addConnection($name, $connection);
+        }
+    }
+
+    /**
+     * Get a database connection instance.
+     *
+     * @param  string|null  $name
+     * @return \Illuminate\Database\ConnectionInterface
+     */
+    public function connection($name = null)
+    {
+        if (is_null($name)) {
+            $name = $this->getDefaultConnection();
+        }
+
+        return $this->connections[$name];
+    }
+
+    /**
+     * Add a connection to the resolver.
+     *
+     * @param  string  $name
+     * @param  \Illuminate\Database\ConnectionInterface  $connection
+     * @return void
+     */
+    public function addConnection($name, ConnectionInterface $connection)
+    {
+        $this->connections[$name] = $connection;
+    }
+
+    /**
+     * Check if a connection has been registered.
+     *
+     * @param  string  $name
+     * @return bool
+     */
+    public function hasConnection($name)
+    {
+        return isset($this->connections[$name]);
+    }
+
+    /**
+     * Get the default connection name.
+     *
+     * @return string
+     */
+    public function getDefaultConnection()
+    {
+        return $this->default;
+    }
+
+    /**
+     * Set the default connection name.
+     *
+     * @param  string  $name
+     * @return void
+     */
+    public function setDefaultConnection($name)
+    {
+        $this->default = $name;
+    }
+}
diff --git a/vendor/illuminate/database/ConnectionResolverInterface.php b/vendor/illuminate/database/ConnectionResolverInterface.php
new file mode 100755
index 0000000..b31e5a7
--- /dev/null
+++ b/vendor/illuminate/database/ConnectionResolverInterface.php
@@ -0,0 +1,29 @@
+container = $container;
+    }
+
+    /**
+     * Establish a PDO connection based on the configuration.
+     *
+     * @param  array  $config
+     * @param  string|null  $name
+     * @return \Illuminate\Database\Connection
+     */
+    public function make(array $config, $name = null)
+    {
+        $config = $this->parseConfig($config, $name);
+
+        if (isset($config['read'])) {
+            return $this->createReadWriteConnection($config);
+        }
+
+        return $this->createSingleConnection($config);
+    }
+
+    /**
+     * Parse and prepare the database configuration.
+     *
+     * @param  array  $config
+     * @param  string  $name
+     * @return array
+     */
+    protected function parseConfig(array $config, $name)
+    {
+        return Arr::add(Arr::add($config, 'prefix', ''), 'name', $name);
+    }
+
+    /**
+     * Create a single database connection instance.
+     *
+     * @param  array  $config
+     * @return \Illuminate\Database\Connection
+     */
+    protected function createSingleConnection(array $config)
+    {
+        $pdo = $this->createPdoResolver($config);
+
+        return $this->createConnection(
+            $config['driver'], $pdo, $config['database'], $config['prefix'], $config
+        );
+    }
+
+    /**
+     * Create a read / write database connection instance.
+     *
+     * @param  array  $config
+     * @return \Illuminate\Database\Connection
+     */
+    protected function createReadWriteConnection(array $config)
+    {
+        $connection = $this->createSingleConnection($this->getWriteConfig($config));
+
+        return $connection->setReadPdo($this->createReadPdo($config));
+    }
+
+    /**
+     * Create a new PDO instance for reading.
+     *
+     * @param  array  $config
+     * @return \Closure
+     */
+    protected function createReadPdo(array $config)
+    {
+        return $this->createPdoResolver($this->getReadConfig($config));
+    }
+
+    /**
+     * Get the read configuration for a read / write connection.
+     *
+     * @param  array  $config
+     * @return array
+     */
+    protected function getReadConfig(array $config)
+    {
+        return $this->mergeReadWriteConfig(
+            $config, $this->getReadWriteConfig($config, 'read')
+        );
+    }
+
+    /**
+     * Get the write configuration for a read / write connection.
+     *
+     * @param  array  $config
+     * @return array
+     */
+    protected function getWriteConfig(array $config)
+    {
+        return $this->mergeReadWriteConfig(
+            $config, $this->getReadWriteConfig($config, 'write')
+        );
+    }
+
+    /**
+     * Get a read / write level configuration.
+     *
+     * @param  array  $config
+     * @param  string  $type
+     * @return array
+     */
+    protected function getReadWriteConfig(array $config, $type)
+    {
+        return isset($config[$type][0])
+                        ? Arr::random($config[$type])
+                        : $config[$type];
+    }
+
+    /**
+     * Merge a configuration for a read / write connection.
+     *
+     * @param  array  $config
+     * @param  array  $merge
+     * @return array
+     */
+    protected function mergeReadWriteConfig(array $config, array $merge)
+    {
+        return Arr::except(array_merge($config, $merge), ['read', 'write']);
+    }
+
+    /**
+     * Create a new Closure that resolves to a PDO instance.
+     *
+     * @param  array  $config
+     * @return \Closure
+     */
+    protected function createPdoResolver(array $config)
+    {
+        return array_key_exists('host', $config)
+                            ? $this->createPdoResolverWithHosts($config)
+                            : $this->createPdoResolverWithoutHosts($config);
+    }
+
+    /**
+     * Create a new Closure that resolves to a PDO instance with a specific host or an array of hosts.
+     *
+     * @param  array  $config
+     * @return \Closure
+     *
+     * @throws \PDOException
+     */
+    protected function createPdoResolverWithHosts(array $config)
+    {
+        return function () use ($config) {
+            foreach (Arr::shuffle($this->parseHosts($config)) as $host) {
+                $config['host'] = $host;
+
+                try {
+                    return $this->createConnector($config)->connect($config);
+                } catch (PDOException $e) {
+                    continue;
+                }
+            }
+
+            throw $e;
+        };
+    }
+
+    /**
+     * Parse the hosts configuration item into an array.
+     *
+     * @param  array  $config
+     * @return array
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function parseHosts(array $config)
+    {
+        $hosts = Arr::wrap($config['host']);
+
+        if (empty($hosts)) {
+            throw new InvalidArgumentException('Database hosts array is empty.');
+        }
+
+        return $hosts;
+    }
+
+    /**
+     * Create a new Closure that resolves to a PDO instance where there is no configured host.
+     *
+     * @param  array  $config
+     * @return \Closure
+     */
+    protected function createPdoResolverWithoutHosts(array $config)
+    {
+        return fn () => $this->createConnector($config)->connect($config);
+    }
+
+    /**
+     * Create a connector instance based on the configuration.
+     *
+     * @param  array  $config
+     * @return \Illuminate\Database\Connectors\ConnectorInterface
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function createConnector(array $config)
+    {
+        if (! isset($config['driver'])) {
+            throw new InvalidArgumentException('A driver must be specified.');
+        }
+
+        if ($this->container->bound($key = "db.connector.{$config['driver']}")) {
+            return $this->container->make($key);
+        }
+
+        return match ($config['driver']) {
+            'mysql' => new MySqlConnector,
+            'pgsql' => new PostgresConnector,
+            'sqlite' => new SQLiteConnector,
+            'sqlsrv' => new SqlServerConnector,
+            default => throw new InvalidArgumentException("Unsupported driver [{$config['driver']}]."),
+        };
+    }
+
+    /**
+     * Create a new connection instance.
+     *
+     * @param  string  $driver
+     * @param  \PDO|\Closure  $connection
+     * @param  string  $database
+     * @param  string  $prefix
+     * @param  array  $config
+     * @return \Illuminate\Database\Connection
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function createConnection($driver, $connection, $database, $prefix = '', array $config = [])
+    {
+        if ($resolver = Connection::getResolver($driver)) {
+            return $resolver($connection, $database, $prefix, $config);
+        }
+
+        return match ($driver) {
+            'mysql' => new MySqlConnection($connection, $database, $prefix, $config),
+            'pgsql' => new PostgresConnection($connection, $database, $prefix, $config),
+            'sqlite' => new SQLiteConnection($connection, $database, $prefix, $config),
+            'sqlsrv' => new SqlServerConnection($connection, $database, $prefix, $config),
+            default => throw new InvalidArgumentException("Unsupported driver [{$driver}]."),
+        };
+    }
+}
diff --git a/vendor/illuminate/database/Connectors/Connector.php b/vendor/illuminate/database/Connectors/Connector.php
new file mode 100755
index 0000000..0fecfb5
--- /dev/null
+++ b/vendor/illuminate/database/Connectors/Connector.php
@@ -0,0 +1,139 @@
+ 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,
+    ];
+
+    /**
+     * Create a new PDO connection.
+     *
+     * @param  string  $dsn
+     * @param  array  $config
+     * @param  array  $options
+     * @return \PDO
+     *
+     * @throws \Exception
+     */
+    public function createConnection($dsn, array $config, array $options)
+    {
+        [$username, $password] = [
+            $config['username'] ?? null, $config['password'] ?? null,
+        ];
+
+        try {
+            return $this->createPdoConnection(
+                $dsn, $username, $password, $options
+            );
+        } catch (Exception $e) {
+            return $this->tryAgainIfCausedByLostConnection(
+                $e, $dsn, $username, $password, $options
+            );
+        }
+    }
+
+    /**
+     * Create a new PDO connection instance.
+     *
+     * @param  string  $dsn
+     * @param  string  $username
+     * @param  string  $password
+     * @param  array  $options
+     * @return \PDO
+     */
+    protected function createPdoConnection($dsn, $username, $password, $options)
+    {
+        if (class_exists(PDOConnection::class) && ! $this->isPersistentConnection($options)) {
+            return new PDOConnection($dsn, $username, $password, $options);
+        }
+
+        return new PDO($dsn, $username, $password, $options);
+    }
+
+    /**
+     * Determine if the connection is persistent.
+     *
+     * @param  array  $options
+     * @return bool
+     */
+    protected function isPersistentConnection($options)
+    {
+        return isset($options[PDO::ATTR_PERSISTENT]) &&
+               $options[PDO::ATTR_PERSISTENT];
+    }
+
+    /**
+     * Handle an exception that occurred during connect execution.
+     *
+     * @param  \Throwable  $e
+     * @param  string  $dsn
+     * @param  string  $username
+     * @param  string  $password
+     * @param  array  $options
+     * @return \PDO
+     *
+     * @throws \Exception
+     */
+    protected function tryAgainIfCausedByLostConnection(Throwable $e, $dsn, $username, $password, $options)
+    {
+        if ($this->causedByLostConnection($e)) {
+            return $this->createPdoConnection($dsn, $username, $password, $options);
+        }
+
+        throw $e;
+    }
+
+    /**
+     * Get the PDO options based on the configuration.
+     *
+     * @param  array  $config
+     * @return array
+     */
+    public function getOptions(array $config)
+    {
+        $options = $config['options'] ?? [];
+
+        return array_diff_key($this->options, $options) + $options;
+    }
+
+    /**
+     * Get the default PDO connection options.
+     *
+     * @return array
+     */
+    public function getDefaultOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * Set the default PDO connection options.
+     *
+     * @param  array  $options
+     * @return void
+     */
+    public function setDefaultOptions(array $options)
+    {
+        $this->options = $options;
+    }
+}
diff --git a/vendor/illuminate/database/Connectors/ConnectorInterface.php b/vendor/illuminate/database/Connectors/ConnectorInterface.php
new file mode 100755
index 0000000..08597ac
--- /dev/null
+++ b/vendor/illuminate/database/Connectors/ConnectorInterface.php
@@ -0,0 +1,14 @@
+getDsn($config);
+
+        $options = $this->getOptions($config);
+
+        // We need to grab the PDO options that should be used while making the brand
+        // new connection instance. The PDO options control various aspects of the
+        // connection's behavior, and some might be specified by the developers.
+        $connection = $this->createConnection($dsn, $config, $options);
+
+        if (! empty($config['database'])) {
+            $connection->exec("use `{$config['database']}`;");
+        }
+
+        $this->configureIsolationLevel($connection, $config);
+
+        $this->configureEncoding($connection, $config);
+
+        // Next, we will check to see if a timezone has been specified in this config
+        // and if it has we will issue a statement to modify the timezone with the
+        // database. Setting this DB timezone is an optional configuration item.
+        $this->configureTimezone($connection, $config);
+
+        $this->setModes($connection, $config);
+
+        return $connection;
+    }
+
+    /**
+     * Set the connection transaction isolation level.
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return void
+     */
+    protected function configureIsolationLevel($connection, array $config)
+    {
+        if (! isset($config['isolation_level'])) {
+            return;
+        }
+
+        $connection->prepare(
+            "SET SESSION TRANSACTION ISOLATION LEVEL {$config['isolation_level']}"
+        )->execute();
+    }
+
+    /**
+     * Set the connection character set and collation.
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return void|\PDO
+     */
+    protected function configureEncoding($connection, array $config)
+    {
+        if (! isset($config['charset'])) {
+            return $connection;
+        }
+
+        $connection->prepare(
+            "set names '{$config['charset']}'".$this->getCollation($config)
+        )->execute();
+    }
+
+    /**
+     * Get the collation for the configuration.
+     *
+     * @param  array  $config
+     * @return string
+     */
+    protected function getCollation(array $config)
+    {
+        return isset($config['collation']) ? " collate '{$config['collation']}'" : '';
+    }
+
+    /**
+     * Set the timezone on the connection.
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return void
+     */
+    protected function configureTimezone($connection, array $config)
+    {
+        if (isset($config['timezone'])) {
+            $connection->prepare('set time_zone="'.$config['timezone'].'"')->execute();
+        }
+    }
+
+    /**
+     * Create a DSN string from a configuration.
+     *
+     * Chooses socket or host/port based on the 'unix_socket' config value.
+     *
+     * @param  array  $config
+     * @return string
+     */
+    protected function getDsn(array $config)
+    {
+        return $this->hasSocket($config)
+                            ? $this->getSocketDsn($config)
+                            : $this->getHostDsn($config);
+    }
+
+    /**
+     * Determine if the given configuration array has a UNIX socket value.
+     *
+     * @param  array  $config
+     * @return bool
+     */
+    protected function hasSocket(array $config)
+    {
+        return isset($config['unix_socket']) && ! empty($config['unix_socket']);
+    }
+
+    /**
+     * Get the DSN string for a socket configuration.
+     *
+     * @param  array  $config
+     * @return string
+     */
+    protected function getSocketDsn(array $config)
+    {
+        return "mysql:unix_socket={$config['unix_socket']};dbname={$config['database']}";
+    }
+
+    /**
+     * Get the DSN string for a host / port configuration.
+     *
+     * @param  array  $config
+     * @return string
+     */
+    protected function getHostDsn(array $config)
+    {
+        extract($config, EXTR_SKIP);
+
+        return isset($port)
+                    ? "mysql:host={$host};port={$port};dbname={$database}"
+                    : "mysql:host={$host};dbname={$database}";
+    }
+
+    /**
+     * Set the modes for the connection.
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return void
+     */
+    protected function setModes(PDO $connection, array $config)
+    {
+        if (isset($config['modes'])) {
+            $this->setCustomModes($connection, $config);
+        } elseif (isset($config['strict'])) {
+            if ($config['strict']) {
+                $connection->prepare($this->strictMode($connection, $config))->execute();
+            } else {
+                $connection->prepare("set session sql_mode='NO_ENGINE_SUBSTITUTION'")->execute();
+            }
+        }
+    }
+
+    /**
+     * Set the custom modes on the connection.
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return void
+     */
+    protected function setCustomModes(PDO $connection, array $config)
+    {
+        $modes = implode(',', $config['modes']);
+
+        $connection->prepare("set session sql_mode='{$modes}'")->execute();
+    }
+
+    /**
+     * Get the query to enable strict mode.
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return string
+     */
+    protected function strictMode(PDO $connection, $config)
+    {
+        $version = $config['version'] ?? $connection->getAttribute(PDO::ATTR_SERVER_VERSION);
+
+        if (version_compare($version, '8.0.11') >= 0) {
+            return "set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'";
+        }
+
+        return "set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'";
+    }
+}
diff --git a/vendor/illuminate/database/Connectors/PostgresConnector.php b/vendor/illuminate/database/Connectors/PostgresConnector.php
new file mode 100755
index 0000000..c54163f
--- /dev/null
+++ b/vendor/illuminate/database/Connectors/PostgresConnector.php
@@ -0,0 +1,216 @@
+ PDO::CASE_NATURAL,
+        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+        PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
+        PDO::ATTR_STRINGIFY_FETCHES => false,
+    ];
+
+    /**
+     * Establish a database connection.
+     *
+     * @param  array  $config
+     * @return \PDO
+     */
+    public function connect(array $config)
+    {
+        // First we'll create the basic DSN and connection instance connecting to the
+        // using the configuration option specified by the developer. We will also
+        // set the default character set on the connections to UTF-8 by default.
+        $connection = $this->createConnection(
+            $this->getDsn($config), $config, $this->getOptions($config)
+        );
+
+        $this->configureIsolationLevel($connection, $config);
+
+        $this->configureEncoding($connection, $config);
+
+        // Next, we will check to see if a timezone has been specified in this config
+        // and if it has we will issue a statement to modify the timezone with the
+        // database. Setting this DB timezone is an optional configuration item.
+        $this->configureTimezone($connection, $config);
+
+        $this->configureSearchPath($connection, $config);
+
+        // Postgres allows an application_name to be set by the user and this name is
+        // used to when monitoring the application with pg_stat_activity. So we'll
+        // determine if the option has been specified and run a statement if so.
+        $this->configureApplicationName($connection, $config);
+
+        $this->configureSynchronousCommit($connection, $config);
+
+        return $connection;
+    }
+
+    /**
+     * Set the connection transaction isolation level.
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return void
+     */
+    protected function configureIsolationLevel($connection, array $config)
+    {
+        if (isset($config['isolation_level'])) {
+            $connection->prepare("set session characteristics as transaction isolation level {$config['isolation_level']}")->execute();
+        }
+    }
+
+    /**
+     * Set the connection character set and collation.
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return void
+     */
+    protected function configureEncoding($connection, $config)
+    {
+        if (! isset($config['charset'])) {
+            return;
+        }
+
+        $connection->prepare("set names '{$config['charset']}'")->execute();
+    }
+
+    /**
+     * Set the timezone on the connection.
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return void
+     */
+    protected function configureTimezone($connection, array $config)
+    {
+        if (isset($config['timezone'])) {
+            $timezone = $config['timezone'];
+
+            $connection->prepare("set time zone '{$timezone}'")->execute();
+        }
+    }
+
+    /**
+     * Set the "search_path" on the database connection.
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return void
+     */
+    protected function configureSearchPath($connection, $config)
+    {
+        if (isset($config['search_path']) || isset($config['schema'])) {
+            $searchPath = $this->quoteSearchPath(
+                $this->parseSearchPath($config['search_path'] ?? $config['schema'])
+            );
+
+            $connection->prepare("set search_path to {$searchPath}")->execute();
+        }
+    }
+
+    /**
+     * Format the search path for the DSN.
+     *
+     * @param  array  $searchPath
+     * @return string
+     */
+    protected function quoteSearchPath($searchPath)
+    {
+        return count($searchPath) === 1 ? '"'.$searchPath[0].'"' : '"'.implode('", "', $searchPath).'"';
+    }
+
+    /**
+     * Set the application name on the connection.
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return void
+     */
+    protected function configureApplicationName($connection, $config)
+    {
+        if (isset($config['application_name'])) {
+            $applicationName = $config['application_name'];
+
+            $connection->prepare("set application_name to '$applicationName'")->execute();
+        }
+    }
+
+    /**
+     * Create a DSN string from a configuration.
+     *
+     * @param  array  $config
+     * @return string
+     */
+    protected function getDsn(array $config)
+    {
+        // First we will create the basic DSN setup as well as the port if it is in
+        // in the configuration options. This will give us the basic DSN we will
+        // need to establish the PDO connections and return them back for use.
+        extract($config, EXTR_SKIP);
+
+        $host = isset($host) ? "host={$host};" : '';
+
+        // Sometimes - users may need to connect to a database that has a different
+        // name than the database used for "information_schema" queries. This is
+        // typically the case if using "pgbouncer" type software when pooling.
+        $database = $connect_via_database ?? $database;
+
+        $dsn = "pgsql:{$host}dbname='{$database}'";
+
+        // If a port was specified, we will add it to this Postgres DSN connections
+        // format. Once we have done that we are ready to return this connection
+        // string back out for usage, as this has been fully constructed here.
+        if (isset($config['port'])) {
+            $dsn .= ";port={$port}";
+        }
+
+        return $this->addSslOptions($dsn, $config);
+    }
+
+    /**
+     * Add the SSL options to the DSN.
+     *
+     * @param  string  $dsn
+     * @param  array  $config
+     * @return string
+     */
+    protected function addSslOptions($dsn, array $config)
+    {
+        foreach (['sslmode', 'sslcert', 'sslkey', 'sslrootcert'] as $option) {
+            if (isset($config[$option])) {
+                $dsn .= ";{$option}={$config[$option]}";
+            }
+        }
+
+        return $dsn;
+    }
+
+    /**
+     * Configure the synchronous_commit setting.
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return void
+     */
+    protected function configureSynchronousCommit($connection, array $config)
+    {
+        if (! isset($config['synchronous_commit'])) {
+            return;
+        }
+
+        $connection->prepare("set synchronous_commit to '{$config['synchronous_commit']}'")->execute();
+    }
+}
diff --git a/vendor/illuminate/database/Connectors/SQLiteConnector.php b/vendor/illuminate/database/Connectors/SQLiteConnector.php
new file mode 100755
index 0000000..ddedfbf
--- /dev/null
+++ b/vendor/illuminate/database/Connectors/SQLiteConnector.php
@@ -0,0 +1,39 @@
+getOptions($config);
+
+        // SQLite supports "in-memory" databases that only last as long as the owning
+        // connection does. These are useful for tests or for short lifetime store
+        // querying. In-memory databases may only have a single open connection.
+        if ($config['database'] === ':memory:') {
+            return $this->createConnection('sqlite::memory:', $config, $options);
+        }
+
+        $path = realpath($config['database']);
+
+        // Here we'll verify that the SQLite database exists before going any further
+        // as the developer probably wants to know if the database exists and this
+        // SQLite driver will not throw any exception if it does not by default.
+        if ($path === false) {
+            throw new SQLiteDatabaseDoesNotExistException($config['database']);
+        }
+
+        return $this->createConnection("sqlite:{$path}", $config, $options);
+    }
+}
diff --git a/vendor/illuminate/database/Connectors/SqlServerConnector.php b/vendor/illuminate/database/Connectors/SqlServerConnector.php
new file mode 100755
index 0000000..b6ed47d
--- /dev/null
+++ b/vendor/illuminate/database/Connectors/SqlServerConnector.php
@@ -0,0 +1,233 @@
+ PDO::CASE_NATURAL,
+        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+        PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
+        PDO::ATTR_STRINGIFY_FETCHES => false,
+    ];
+
+    /**
+     * Establish a database connection.
+     *
+     * @param  array  $config
+     * @return \PDO
+     */
+    public function connect(array $config)
+    {
+        $options = $this->getOptions($config);
+
+        $connection = $this->createConnection($this->getDsn($config), $config, $options);
+
+        $this->configureIsolationLevel($connection, $config);
+
+        return $connection;
+    }
+
+    /**
+     * Set the connection transaction isolation level.
+     *
+     * https://learn.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql
+     *
+     * @param  \PDO  $connection
+     * @param  array  $config
+     * @return void
+     */
+    protected function configureIsolationLevel($connection, array $config)
+    {
+        if (! isset($config['isolation_level'])) {
+            return;
+        }
+
+        $connection->prepare(
+            "SET TRANSACTION ISOLATION LEVEL {$config['isolation_level']}"
+        )->execute();
+    }
+
+    /**
+     * Create a DSN string from a configuration.
+     *
+     * @param  array  $config
+     * @return string
+     */
+    protected function getDsn(array $config)
+    {
+        // First we will create the basic DSN setup as well as the port if it is in
+        // in the configuration options. This will give us the basic DSN we will
+        // need to establish the PDO connections and return them back for use.
+        if ($this->prefersOdbc($config)) {
+            return $this->getOdbcDsn($config);
+        }
+
+        if (in_array('sqlsrv', $this->getAvailableDrivers())) {
+            return $this->getSqlSrvDsn($config);
+        } else {
+            return $this->getDblibDsn($config);
+        }
+    }
+
+    /**
+     * Determine if the database configuration prefers ODBC.
+     *
+     * @param  array  $config
+     * @return bool
+     */
+    protected function prefersOdbc(array $config)
+    {
+        return in_array('odbc', $this->getAvailableDrivers()) &&
+               ($config['odbc'] ?? null) === true;
+    }
+
+    /**
+     * Get the DSN string for a DbLib connection.
+     *
+     * @param  array  $config
+     * @return string
+     */
+    protected function getDblibDsn(array $config)
+    {
+        return $this->buildConnectString('dblib', array_merge([
+            'host' => $this->buildHostString($config, ':'),
+            'dbname' => $config['database'],
+        ], Arr::only($config, ['appname', 'charset', 'version'])));
+    }
+
+    /**
+     * Get the DSN string for an ODBC connection.
+     *
+     * @param  array  $config
+     * @return string
+     */
+    protected function getOdbcDsn(array $config)
+    {
+        return isset($config['odbc_datasource_name'])
+                    ? 'odbc:'.$config['odbc_datasource_name'] : '';
+    }
+
+    /**
+     * Get the DSN string for a SqlSrv connection.
+     *
+     * @param  array  $config
+     * @return string
+     */
+    protected function getSqlSrvDsn(array $config)
+    {
+        $arguments = [
+            'Server' => $this->buildHostString($config, ','),
+        ];
+
+        if (isset($config['database'])) {
+            $arguments['Database'] = $config['database'];
+        }
+
+        if (isset($config['readonly'])) {
+            $arguments['ApplicationIntent'] = 'ReadOnly';
+        }
+
+        if (isset($config['pooling']) && $config['pooling'] === false) {
+            $arguments['ConnectionPooling'] = '0';
+        }
+
+        if (isset($config['appname'])) {
+            $arguments['APP'] = $config['appname'];
+        }
+
+        if (isset($config['encrypt'])) {
+            $arguments['Encrypt'] = $config['encrypt'];
+        }
+
+        if (isset($config['trust_server_certificate'])) {
+            $arguments['TrustServerCertificate'] = $config['trust_server_certificate'];
+        }
+
+        if (isset($config['multiple_active_result_sets']) && $config['multiple_active_result_sets'] === false) {
+            $arguments['MultipleActiveResultSets'] = 'false';
+        }
+
+        if (isset($config['transaction_isolation'])) {
+            $arguments['TransactionIsolation'] = $config['transaction_isolation'];
+        }
+
+        if (isset($config['multi_subnet_failover'])) {
+            $arguments['MultiSubnetFailover'] = $config['multi_subnet_failover'];
+        }
+
+        if (isset($config['column_encryption'])) {
+            $arguments['ColumnEncryption'] = $config['column_encryption'];
+        }
+
+        if (isset($config['key_store_authentication'])) {
+            $arguments['KeyStoreAuthentication'] = $config['key_store_authentication'];
+        }
+
+        if (isset($config['key_store_principal_id'])) {
+            $arguments['KeyStorePrincipalId'] = $config['key_store_principal_id'];
+        }
+
+        if (isset($config['key_store_secret'])) {
+            $arguments['KeyStoreSecret'] = $config['key_store_secret'];
+        }
+
+        if (isset($config['login_timeout'])) {
+            $arguments['LoginTimeout'] = $config['login_timeout'];
+        }
+
+        if (isset($config['authentication'])) {
+            $arguments['Authentication'] = $config['authentication'];
+        }
+
+        return $this->buildConnectString('sqlsrv', $arguments);
+    }
+
+    /**
+     * Build a connection string from the given arguments.
+     *
+     * @param  string  $driver
+     * @param  array  $arguments
+     * @return string
+     */
+    protected function buildConnectString($driver, array $arguments)
+    {
+        return $driver.':'.implode(';', array_map(function ($key) use ($arguments) {
+            return sprintf('%s=%s', $key, $arguments[$key]);
+        }, array_keys($arguments)));
+    }
+
+    /**
+     * Build a host string from the given configuration.
+     *
+     * @param  array  $config
+     * @param  string  $separator
+     * @return string
+     */
+    protected function buildHostString(array $config, $separator)
+    {
+        if (empty($config['port'])) {
+            return $config['host'];
+        }
+
+        return $config['host'].$separator.$config['port'];
+    }
+
+    /**
+     * Get the available PDO drivers.
+     *
+     * @return array
+     */
+    protected function getAvailableDrivers()
+    {
+        return PDO::getAvailableDrivers();
+    }
+}
diff --git a/vendor/illuminate/database/Console/DatabaseInspectionCommand.php b/vendor/illuminate/database/Console/DatabaseInspectionCommand.php
new file mode 100644
index 0000000..e3391a0
--- /dev/null
+++ b/vendor/illuminate/database/Console/DatabaseInspectionCommand.php
@@ -0,0 +1,246 @@
+ 'string',
+        'citext' => 'string',
+        'enum' => 'string',
+        'geometry' => 'string',
+        'geomcollection' => 'string',
+        'linestring' => 'string',
+        'ltree' => 'string',
+        'multilinestring' => 'string',
+        'multipoint' => 'string',
+        'multipolygon' => 'string',
+        'point' => 'string',
+        'polygon' => 'string',
+        'sysname' => 'string',
+    ];
+
+    /**
+     * The Composer instance.
+     *
+     * @var \Illuminate\Support\Composer
+     */
+    protected $composer;
+
+    /**
+     * Create a new command instance.
+     *
+     * @param  \Illuminate\Support\Composer|null  $composer
+     * @return void
+     */
+    public function __construct(Composer $composer = null)
+    {
+        parent::__construct();
+
+        $this->composer = $composer ?? $this->laravel->make(Composer::class);
+    }
+
+    /**
+     * Register the custom Doctrine type mappings for inspection commands.
+     *
+     * @param  \Doctrine\DBAL\Platforms\AbstractPlatform  $platform
+     * @return void
+     */
+    protected function registerTypeMappings(AbstractPlatform $platform)
+    {
+        foreach ($this->typeMappings as $type => $value) {
+            $platform->registerDoctrineTypeMapping($type, $value);
+        }
+    }
+
+    /**
+     * Get a human-readable platform name for the given platform.
+     *
+     * @param  \Doctrine\DBAL\Platforms\AbstractPlatform  $platform
+     * @param  string  $database
+     * @return string
+     */
+    protected function getPlatformName(AbstractPlatform $platform, $database)
+    {
+        return match (class_basename($platform)) {
+            'MySQLPlatform' => 'MySQL <= 5',
+            'MySQL57Platform' => 'MySQL 5.7',
+            'MySQL80Platform' => 'MySQL 8',
+            'PostgreSQL100Platform', 'PostgreSQLPlatform' => 'Postgres',
+            'SqlitePlatform' => 'SQLite',
+            'SQLServerPlatform' => 'SQL Server',
+            'SQLServer2012Platform' => 'SQL Server 2012',
+            default => $database,
+        };
+    }
+
+    /**
+     * Get the size of a table in bytes.
+     *
+     * @param  \Illuminate\Database\ConnectionInterface  $connection
+     * @param  string  $table
+     * @return int|null
+     */
+    protected function getTableSize(ConnectionInterface $connection, string $table)
+    {
+        return match (true) {
+            $connection instanceof MySqlConnection => $this->getMySQLTableSize($connection, $table),
+            $connection instanceof PostgresConnection => $this->getPostgresTableSize($connection, $table),
+            $connection instanceof SQLiteConnection => $this->getSqliteTableSize($connection, $table),
+            default => null,
+        };
+    }
+
+    /**
+     * Get the size of a MySQL table in bytes.
+     *
+     * @param  \Illuminate\Database\ConnectionInterface  $connection
+     * @param  string  $table
+     * @return mixed
+     */
+    protected function getMySQLTableSize(ConnectionInterface $connection, string $table)
+    {
+        $result = $connection->selectOne('SELECT (data_length + index_length) AS size FROM information_schema.TABLES WHERE table_schema = ? AND table_name = ?', [
+            $connection->getDatabaseName(),
+            $table,
+        ]);
+
+        return Arr::wrap((array) $result)['size'];
+    }
+
+    /**
+     * Get the size of a Postgres table in bytes.
+     *
+     * @param  \Illuminate\Database\ConnectionInterface  $connection
+     * @param  string  $table
+     * @return mixed
+     */
+    protected function getPostgresTableSize(ConnectionInterface $connection, string $table)
+    {
+        $result = $connection->selectOne('SELECT pg_total_relation_size(?) AS size;', [
+            $table,
+        ]);
+
+        return Arr::wrap((array) $result)['size'];
+    }
+
+    /**
+     * Get the size of a SQLite table in bytes.
+     *
+     * @param  \Illuminate\Database\ConnectionInterface  $connection
+     * @param  string  $table
+     * @return mixed
+     */
+    protected function getSqliteTableSize(ConnectionInterface $connection, string $table)
+    {
+        try {
+            $result = $connection->selectOne('SELECT SUM(pgsize) AS size FROM dbstat WHERE name=?', [
+                $table,
+            ]);
+
+            return Arr::wrap((array) $result)['size'];
+        } catch (QueryException $e) {
+            return null;
+        }
+    }
+
+    /**
+     * Get the number of open connections for a database.
+     *
+     * @param  \Illuminate\Database\ConnectionInterface  $connection
+     * @return int|null
+     */
+    protected function getConnectionCount(ConnectionInterface $connection)
+    {
+        $result = match (true) {
+            $connection instanceof MySqlConnection => $connection->selectOne('show status where variable_name = "threads_connected"'),
+            $connection instanceof PostgresConnection => $connection->selectOne('select count(*) AS "Value" from pg_stat_activity'),
+            $connection instanceof SqlServerConnection => $connection->selectOne('SELECT COUNT(*) Value FROM sys.dm_exec_sessions WHERE status = ?', ['running']),
+            default => null,
+        };
+
+        if (! $result) {
+            return null;
+        }
+
+        return Arr::wrap((array) $result)['Value'];
+    }
+
+    /**
+     * Get the connection configuration details for the given connection.
+     *
+     * @param  string  $database
+     * @return array
+     */
+    protected function getConfigFromDatabase($database)
+    {
+        $database ??= config('database.default');
+
+        return Arr::except(config('database.connections.'.$database), ['password']);
+    }
+
+    /**
+     * Ensure the dependencies for the database commands are available.
+     *
+     * @return bool
+     */
+    protected function ensureDependenciesExist()
+    {
+        return tap(interface_exists('Doctrine\DBAL\Driver'), function ($dependenciesExist) {
+            if (! $dependenciesExist && $this->components->confirm('Inspecting database information requires the Doctrine DBAL (doctrine/dbal) package. Would you like to install it?')) {
+                $this->installDependencies();
+            }
+        });
+    }
+
+    /**
+     * Install the command's dependencies.
+     *
+     * @return void
+     *
+     * @throws \Symfony\Component\Process\Exception\ProcessSignaledException
+     */
+    protected function installDependencies()
+    {
+        $command = collect($this->composer->findComposer())
+            ->push('require doctrine/dbal')
+            ->implode(' ');
+
+        $process = Process::fromShellCommandline($command, null, null, null, null);
+
+        if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) {
+            try {
+                $process->setTty(true);
+            } catch (RuntimeException $e) {
+                $this->components->warn($e->getMessage());
+            }
+        }
+
+        try {
+            $process->run(fn ($type, $line) => $this->output->write($line));
+        } catch (ProcessSignaledException $e) {
+            if (extension_loaded('pcntl') && $e->getSignal() !== SIGINT) {
+                throw $e;
+            }
+        }
+    }
+}
diff --git a/vendor/illuminate/database/Console/DbCommand.php b/vendor/illuminate/database/Console/DbCommand.php
new file mode 100644
index 0000000..caecafe
--- /dev/null
+++ b/vendor/illuminate/database/Console/DbCommand.php
@@ -0,0 +1,227 @@
+getConnection();
+
+        if (! isset($connection['host']) && $connection['driver'] !== 'sqlite') {
+            $this->components->error('No host specified for this database connection.');
+            $this->line('  Use the [--read] and [--write] options to specify a read or write connection.');
+            $this->newLine();
+
+            return Command::FAILURE;
+        }
+
+        (new Process(
+            array_merge([$this->getCommand($connection)], $this->commandArguments($connection)),
+            null,
+            $this->commandEnvironment($connection)
+        ))->setTimeout(null)->setTty(true)->mustRun(function ($type, $buffer) {
+            $this->output->write($buffer);
+        });
+
+        return 0;
+    }
+
+    /**
+     * Get the database connection configuration.
+     *
+     * @return array
+     *
+     * @throws \UnexpectedValueException
+     */
+    public function getConnection()
+    {
+        $connection = $this->laravel['config']['database.connections.'.
+            (($db = $this->argument('connection')) ?? $this->laravel['config']['database.default'])
+        ];
+
+        if (empty($connection)) {
+            throw new UnexpectedValueException("Invalid database connection [{$db}].");
+        }
+
+        if (! empty($connection['url'])) {
+            $connection = (new ConfigurationUrlParser)->parseConfiguration($connection);
+        }
+
+        if ($this->option('read')) {
+            if (is_array($connection['read']['host'])) {
+                $connection['read']['host'] = $connection['read']['host'][0];
+            }
+
+            $connection = array_merge($connection, $connection['read']);
+        } elseif ($this->option('write')) {
+            if (is_array($connection['write']['host'])) {
+                $connection['write']['host'] = $connection['write']['host'][0];
+            }
+
+            $connection = array_merge($connection, $connection['write']);
+        }
+
+        return $connection;
+    }
+
+    /**
+     * Get the arguments for the database client command.
+     *
+     * @param  array  $connection
+     * @return array
+     */
+    public function commandArguments(array $connection)
+    {
+        $driver = ucfirst($connection['driver']);
+
+        return $this->{"get{$driver}Arguments"}($connection);
+    }
+
+    /**
+     * Get the environment variables for the database client command.
+     *
+     * @param  array  $connection
+     * @return array|null
+     */
+    public function commandEnvironment(array $connection)
+    {
+        $driver = ucfirst($connection['driver']);
+
+        if (method_exists($this, "get{$driver}Environment")) {
+            return $this->{"get{$driver}Environment"}($connection);
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the database client command to run.
+     *
+     * @param  array  $connection
+     * @return string
+     */
+    public function getCommand(array $connection)
+    {
+        return [
+            'mysql' => 'mysql',
+            'pgsql' => 'psql',
+            'sqlite' => 'sqlite3',
+            'sqlsrv' => 'sqlcmd',
+        ][$connection['driver']];
+    }
+
+    /**
+     * Get the arguments for the MySQL CLI.
+     *
+     * @param  array  $connection
+     * @return array
+     */
+    protected function getMysqlArguments(array $connection)
+    {
+        return array_merge([
+            '--host='.$connection['host'],
+            '--port='.$connection['port'],
+            '--user='.$connection['username'],
+        ], $this->getOptionalArguments([
+            'password' => '--password='.$connection['password'],
+            'unix_socket' => '--socket='.($connection['unix_socket'] ?? ''),
+            'charset' => '--default-character-set='.($connection['charset'] ?? ''),
+        ], $connection), [$connection['database']]);
+    }
+
+    /**
+     * Get the arguments for the Postgres CLI.
+     *
+     * @param  array  $connection
+     * @return array
+     */
+    protected function getPgsqlArguments(array $connection)
+    {
+        return [$connection['database']];
+    }
+
+    /**
+     * Get the arguments for the SQLite CLI.
+     *
+     * @param  array  $connection
+     * @return array
+     */
+    protected function getSqliteArguments(array $connection)
+    {
+        return [$connection['database']];
+    }
+
+    /**
+     * Get the arguments for the SQL Server CLI.
+     *
+     * @param  array  $connection
+     * @return array
+     */
+    protected function getSqlsrvArguments(array $connection)
+    {
+        return array_merge(...$this->getOptionalArguments([
+            'database' => ['-d', $connection['database']],
+            'username' => ['-U', $connection['username']],
+            'password' => ['-P', $connection['password']],
+            'host' => ['-S', 'tcp:'.$connection['host']
+                        .($connection['port'] ? ','.$connection['port'] : ''), ],
+        ], $connection));
+    }
+
+    /**
+     * Get the environment variables for the Postgres CLI.
+     *
+     * @param  array  $connection
+     * @return array|null
+     */
+    protected function getPgsqlEnvironment(array $connection)
+    {
+        return array_merge(...$this->getOptionalArguments([
+            'username' => ['PGUSER' => $connection['username']],
+            'host' => ['PGHOST' => $connection['host']],
+            'port' => ['PGPORT' => $connection['port']],
+            'password' => ['PGPASSWORD' => $connection['password']],
+        ], $connection));
+    }
+
+    /**
+     * Get the optional arguments based on the connection configuration.
+     *
+     * @param  array  $args
+     * @param  array  $connection
+     * @return array
+     */
+    protected function getOptionalArguments(array $args, array $connection)
+    {
+        return array_values(array_filter($args, function ($key) use ($connection) {
+            return ! empty($connection[$key]);
+        }, ARRAY_FILTER_USE_KEY));
+    }
+}
diff --git a/vendor/illuminate/database/Console/DumpCommand.php b/vendor/illuminate/database/Console/DumpCommand.php
new file mode 100644
index 0000000..3f21aaf
--- /dev/null
+++ b/vendor/illuminate/database/Console/DumpCommand.php
@@ -0,0 +1,101 @@
+connection($database = $this->input->getOption('database'));
+
+        $this->schemaState($connection)->dump(
+            $connection, $path = $this->path($connection)
+        );
+
+        $dispatcher->dispatch(new SchemaDumped($connection, $path));
+
+        $info = 'Database schema dumped';
+
+        if ($this->option('prune')) {
+            (new Filesystem)->deleteDirectory(
+                database_path('migrations'), $preserve = false
+            );
+
+            $info .= ' and pruned';
+        }
+
+        $this->components->info($info.' successfully.');
+    }
+
+    /**
+     * Create a schema state instance for the given connection.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return mixed
+     */
+    protected function schemaState(Connection $connection)
+    {
+        return $connection->getSchemaState()
+                ->withMigrationTable($connection->getTablePrefix().Config::get('database.migrations', 'migrations'))
+                ->handleOutputUsing(function ($type, $buffer) {
+                    $this->output->write($buffer);
+                });
+    }
+
+    /**
+     * Get the path that the dump should be written to.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     */
+    protected function path(Connection $connection)
+    {
+        return tap($this->option('path') ?: database_path('schema/'.$connection->getName().'-schema.sql'), function ($path) {
+            (new Filesystem)->ensureDirectoryExists(dirname($path));
+        });
+    }
+}
diff --git a/vendor/illuminate/database/Console/Factories/FactoryMakeCommand.php b/vendor/illuminate/database/Console/Factories/FactoryMakeCommand.php
new file mode 100644
index 0000000..48c4375
--- /dev/null
+++ b/vendor/illuminate/database/Console/Factories/FactoryMakeCommand.php
@@ -0,0 +1,154 @@
+resolveStubPath('/stubs/factory.stub');
+    }
+
+    /**
+     * Resolve the fully-qualified path to the stub.
+     *
+     * @param  string  $stub
+     * @return string
+     */
+    protected function resolveStubPath($stub)
+    {
+        return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
+            ? $customPath
+            : __DIR__.$stub;
+    }
+
+    /**
+     * Build the class with the given name.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    protected function buildClass($name)
+    {
+        $factory = class_basename(Str::ucfirst(str_replace('Factory', '', $name)));
+
+        $namespaceModel = $this->option('model')
+                        ? $this->qualifyModel($this->option('model'))
+                        : $this->qualifyModel($this->guessModelName($name));
+
+        $model = class_basename($namespaceModel);
+
+        $namespace = $this->getNamespace(
+            Str::replaceFirst($this->rootNamespace(), 'Database\\Factories\\', $this->qualifyClass($this->getNameInput()))
+        );
+
+        $replace = [
+            '{{ factoryNamespace }}' => $namespace,
+            'NamespacedDummyModel' => $namespaceModel,
+            '{{ namespacedModel }}' => $namespaceModel,
+            '{{namespacedModel}}' => $namespaceModel,
+            'DummyModel' => $model,
+            '{{ model }}' => $model,
+            '{{model}}' => $model,
+            '{{ factory }}' => $factory,
+            '{{factory}}' => $factory,
+        ];
+
+        return str_replace(
+            array_keys($replace), array_values($replace), parent::buildClass($name)
+        );
+    }
+
+    /**
+     * Get the destination class path.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    protected function getPath($name)
+    {
+        $name = (string) Str::of($name)->replaceFirst($this->rootNamespace(), '')->finish('Factory');
+
+        return $this->laravel->databasePath().'/factories/'.str_replace('\\', '/', $name).'.php';
+    }
+
+    /**
+     * Guess the model name from the Factory name or return a default model name.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    protected function guessModelName($name)
+    {
+        if (str_ends_with($name, 'Factory')) {
+            $name = substr($name, 0, -7);
+        }
+
+        $modelName = $this->qualifyModel(Str::after($name, $this->rootNamespace()));
+
+        if (class_exists($modelName)) {
+            return $modelName;
+        }
+
+        if (is_dir(app_path('Models/'))) {
+            return $this->rootNamespace().'Models\Model';
+        }
+
+        return $this->rootNamespace().'Model';
+    }
+
+    /**
+     * Get the console command options.
+     *
+     * @return array
+     */
+    protected function getOptions()
+    {
+        return [
+            ['model', 'm', InputOption::VALUE_OPTIONAL, 'The name of the model'],
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/Console/Factories/stubs/factory.stub b/vendor/illuminate/database/Console/Factories/stubs/factory.stub
new file mode 100644
index 0000000..0759b5d
--- /dev/null
+++ b/vendor/illuminate/database/Console/Factories/stubs/factory.stub
@@ -0,0 +1,23 @@
+
+ */
+class {{ factory }}Factory extends Factory
+{
+    /**
+     * Define the model's default state.
+     *
+     * @return array
+     */
+    public function definition()
+    {
+        return [
+            //
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/Console/Migrations/BaseCommand.php b/vendor/illuminate/database/Console/Migrations/BaseCommand.php
new file mode 100755
index 0000000..6c4f255
--- /dev/null
+++ b/vendor/illuminate/database/Console/Migrations/BaseCommand.php
@@ -0,0 +1,51 @@
+input->hasOption('path') && $this->option('path')) {
+            return collect($this->option('path'))->map(function ($path) {
+                return ! $this->usingRealPath()
+                                ? $this->laravel->basePath().'/'.$path
+                                : $path;
+            })->all();
+        }
+
+        return array_merge(
+            $this->migrator->paths(), [$this->getMigrationPath()]
+        );
+    }
+
+    /**
+     * Determine if the given path(s) are pre-resolved "real" paths.
+     *
+     * @return bool
+     */
+    protected function usingRealPath()
+    {
+        return $this->input->hasOption('realpath') && $this->option('realpath');
+    }
+
+    /**
+     * Get the path to the migration directory.
+     *
+     * @return string
+     */
+    protected function getMigrationPath()
+    {
+        return $this->laravel->databasePath().DIRECTORY_SEPARATOR.'migrations';
+    }
+}
diff --git a/vendor/illuminate/database/Console/Migrations/FreshCommand.php b/vendor/illuminate/database/Console/Migrations/FreshCommand.php
new file mode 100644
index 0000000..e319e74
--- /dev/null
+++ b/vendor/illuminate/database/Console/Migrations/FreshCommand.php
@@ -0,0 +1,120 @@
+confirmToProceed()) {
+            return 1;
+        }
+
+        $database = $this->input->getOption('database');
+
+        $this->newLine();
+
+        $this->components->task('Dropping all tables', fn () => $this->callSilent('db:wipe', array_filter([
+            '--database' => $database,
+            '--drop-views' => $this->option('drop-views'),
+            '--drop-types' => $this->option('drop-types'),
+            '--force' => true,
+        ])) == 0);
+
+        $this->newLine();
+
+        $this->call('migrate', array_filter([
+            '--database' => $database,
+            '--path' => $this->input->getOption('path'),
+            '--realpath' => $this->input->getOption('realpath'),
+            '--schema-path' => $this->input->getOption('schema-path'),
+            '--force' => true,
+            '--step' => $this->option('step'),
+        ]));
+
+        if ($this->laravel->bound(Dispatcher::class)) {
+            $this->laravel[Dispatcher::class]->dispatch(
+                new DatabaseRefreshed
+            );
+        }
+
+        if ($this->needsSeeding()) {
+            $this->runSeeder($database);
+        }
+
+        return 0;
+    }
+
+    /**
+     * Determine if the developer has requested database seeding.
+     *
+     * @return bool
+     */
+    protected function needsSeeding()
+    {
+        return $this->option('seed') || $this->option('seeder');
+    }
+
+    /**
+     * Run the database seeder command.
+     *
+     * @param  string  $database
+     * @return void
+     */
+    protected function runSeeder($database)
+    {
+        $this->call('db:seed', array_filter([
+            '--database' => $database,
+            '--class' => $this->option('seeder') ?: 'Database\\Seeders\\DatabaseSeeder',
+            '--force' => true,
+        ]));
+    }
+
+    /**
+     * Get the console command options.
+     *
+     * @return array
+     */
+    protected function getOptions()
+    {
+        return [
+            ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
+            ['drop-views', null, InputOption::VALUE_NONE, 'Drop all tables and views'],
+            ['drop-types', null, InputOption::VALUE_NONE, 'Drop all tables and types (Postgres only)'],
+            ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
+            ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) to the migrations files to be executed'],
+            ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'],
+            ['schema-path', null, InputOption::VALUE_OPTIONAL, 'The path to a schema dump file'],
+            ['seed', null, InputOption::VALUE_NONE, 'Indicates if the seed task should be re-run'],
+            ['seeder', null, InputOption::VALUE_OPTIONAL, 'The class name of the root seeder'],
+            ['step', null, InputOption::VALUE_NONE, 'Force the migrations to be run so they can be rolled back individually'],
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/Console/Migrations/InstallCommand.php b/vendor/illuminate/database/Console/Migrations/InstallCommand.php
new file mode 100755
index 0000000..901a83b
--- /dev/null
+++ b/vendor/illuminate/database/Console/Migrations/InstallCommand.php
@@ -0,0 +1,70 @@
+repository = $repository;
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        $this->repository->setSource($this->input->getOption('database'));
+
+        $this->repository->createRepository();
+
+        $this->components->info('Migration table created successfully.');
+    }
+
+    /**
+     * Get the console command options.
+     *
+     * @return array
+     */
+    protected function getOptions()
+    {
+        return [
+            ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/Console/Migrations/MigrateCommand.php b/vendor/illuminate/database/Console/Migrations/MigrateCommand.php
new file mode 100755
index 0000000..fc43bf5
--- /dev/null
+++ b/vendor/illuminate/database/Console/Migrations/MigrateCommand.php
@@ -0,0 +1,281 @@
+migrator = $migrator;
+        $this->dispatcher = $dispatcher;
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return int
+     */
+    public function handle()
+    {
+        if (! $this->confirmToProceed()) {
+            return 1;
+        }
+
+        $this->migrator->usingConnection($this->option('database'), function () {
+            $this->prepareDatabase();
+
+            // Next, we will check to see if a path option has been defined. If it has
+            // we will use the path relative to the root of this installation folder
+            // so that migrations may be run for any path within the applications.
+            $migrations = $this->migrator->setOutput($this->output)
+                    ->run($this->getMigrationPaths(), [
+                        'pretend' => $this->option('pretend'),
+                        'step' => $this->option('step'),
+                    ]);
+
+            // Finally, if the "seed" option has been given, we will re-run the database
+            // seed task to re-populate the database, which is convenient when adding
+            // a migration and a seed at the same time, as it is only this command.
+            if ($this->option('seed') && ! $this->option('pretend')) {
+                $this->call('db:seed', [
+                    '--class' => $this->option('seeder') ?: 'Database\\Seeders\\DatabaseSeeder',
+                    '--force' => true,
+                ]);
+            }
+        });
+
+        return 0;
+    }
+
+    /**
+     * Prepare the migration database for running.
+     *
+     * @return void
+     */
+    protected function prepareDatabase()
+    {
+        if (! $this->repositoryExists()) {
+            $this->components->info('Preparing database.');
+
+            $this->components->task('Creating migration table', function () {
+                return $this->callSilent('migrate:install', array_filter([
+                    '--database' => $this->option('database'),
+                ])) == 0;
+            });
+
+            $this->newLine();
+        }
+
+        if (! $this->migrator->hasRunAnyMigrations() && ! $this->option('pretend')) {
+            $this->loadSchemaState();
+        }
+    }
+
+    /**
+     * Determine if the migrator repository exists.
+     *
+     * @return bool
+     */
+    protected function repositoryExists()
+    {
+        return retry(2, fn () => $this->migrator->repositoryExists(), 0, function ($e) {
+            try {
+                if ($e->getPrevious() instanceof SQLiteDatabaseDoesNotExistException) {
+                    return $this->createMissingSqliteDatbase($e->getPrevious()->path);
+                }
+
+                $connection = $this->migrator->resolveConnection($this->option('database'));
+
+                if (
+                    $e->getPrevious() instanceof PDOException &&
+                    $e->getPrevious()->getCode() === 1049 &&
+                    $connection->getDriverName() === 'mysql') {
+                    return $this->createMissingMysqlDatabase($connection);
+                }
+
+                return false;
+            } catch (Throwable) {
+                return false;
+            }
+        });
+    }
+
+    /**
+     * Create a missing SQLite database.
+     *
+     * @param  string  $path
+     * @return bool
+     */
+    protected function createMissingSqliteDatbase($path)
+    {
+        if ($this->option('force')) {
+            return touch($path);
+        }
+
+        if ($this->option('no-interaction')) {
+            return false;
+        }
+
+        $this->components->warn('The SQLite database does not exist: '.$path);
+
+        if (! $this->components->confirm('Would you like to create it?')) {
+            return false;
+        }
+
+        return touch($path);
+    }
+
+    /**
+     * Create a missing MySQL database.
+     *
+     * @return bool
+     */
+    protected function createMissingMysqlDatabase($connection)
+    {
+        if ($this->laravel['config']->get("database.connections.{$connection->getName()}.database") !== $connection->getDatabaseName()) {
+            return false;
+        }
+
+        if (! $this->option('force') && $this->option('no-interaction')) {
+            return false;
+        }
+
+        if (! $this->option('force') && ! $this->option('no-interaction')) {
+            $this->components->warn("The database '{$connection->getDatabaseName()}' does not exist on the '{$connection->getName()}' connection.");
+
+            if (! $this->components->confirm('Would you like to create it?')) {
+                return false;
+            }
+        }
+
+        try {
+            $this->laravel['config']->set("database.connections.{$connection->getName()}.database", null);
+
+            $this->laravel['db']->purge();
+
+            $freshConnection = $this->migrator->resolveConnection($this->option('database'));
+
+            return tap($freshConnection->unprepared("CREATE DATABASE IF NOT EXISTS `{$connection->getDatabaseName()}`"), function () {
+                $this->laravel['db']->purge();
+            });
+        } finally {
+            $this->laravel['config']->set("database.connections.{$connection->getName()}.database", $connection->getDatabaseName());
+        }
+    }
+
+    /**
+     * Load the schema state to seed the initial database schema structure.
+     *
+     * @return void
+     */
+    protected function loadSchemaState()
+    {
+        $connection = $this->migrator->resolveConnection($this->option('database'));
+
+        // First, we will make sure that the connection supports schema loading and that
+        // the schema file exists before we proceed any further. If not, we will just
+        // continue with the standard migration operation as normal without errors.
+        if ($connection instanceof SqlServerConnection ||
+            ! is_file($path = $this->schemaPath($connection))) {
+            return;
+        }
+
+        $this->components->info('Loading stored database schemas.');
+
+        $this->components->task($path, function () use ($connection, $path) {
+            // Since the schema file will create the "migrations" table and reload it to its
+            // proper state, we need to delete it here so we don't get an error that this
+            // table already exists when the stored database schema file gets executed.
+            $this->migrator->deleteRepository();
+
+            $connection->getSchemaState()->handleOutputUsing(function ($type, $buffer) {
+                $this->output->write($buffer);
+            })->load($path);
+        });
+
+        $this->newLine();
+
+        // Finally, we will fire an event that this schema has been loaded so developers
+        // can perform any post schema load tasks that are necessary in listeners for
+        // this event, which may seed the database tables with some necessary data.
+        $this->dispatcher->dispatch(
+            new SchemaLoaded($connection, $path)
+        );
+    }
+
+    /**
+     * Get the path to the stored schema for the given connection.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return string
+     */
+    protected function schemaPath($connection)
+    {
+        if ($this->option('schema-path')) {
+            return $this->option('schema-path');
+        }
+
+        if (file_exists($path = database_path('schema/'.$connection->getName().'-schema.dump'))) {
+            return $path;
+        }
+
+        return database_path('schema/'.$connection->getName().'-schema.sql');
+    }
+}
diff --git a/vendor/illuminate/database/Console/Migrations/MigrateMakeCommand.php b/vendor/illuminate/database/Console/Migrations/MigrateMakeCommand.php
new file mode 100644
index 0000000..75c0634
--- /dev/null
+++ b/vendor/illuminate/database/Console/Migrations/MigrateMakeCommand.php
@@ -0,0 +1,144 @@
+creator = $creator;
+        $this->composer = $composer;
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        // It's possible for the developer to specify the tables to modify in this
+        // schema operation. The developer may also specify if this table needs
+        // to be freshly created so we can create the appropriate migrations.
+        $name = Str::snake(trim($this->input->getArgument('name')));
+
+        $table = $this->input->getOption('table');
+
+        $create = $this->input->getOption('create') ?: false;
+
+        // If no table was given as an option but a create option is given then we
+        // will use the "create" option as the table name. This allows the devs
+        // to pass a table name into this option as a short-cut for creating.
+        if (! $table && is_string($create)) {
+            $table = $create;
+
+            $create = true;
+        }
+
+        // Next, we will attempt to guess the table name if this the migration has
+        // "create" in the name. This will allow us to provide a convenient way
+        // of creating migrations that create new tables for the application.
+        if (! $table) {
+            [$table, $create] = TableGuesser::guess($name);
+        }
+
+        // Now we are ready to write the migration out to disk. Once we've written
+        // the migration out, we will dump-autoload for the entire framework to
+        // make sure that the migrations are registered by the class loaders.
+        $this->writeMigration($name, $table, $create);
+
+        $this->composer->dumpAutoloads();
+    }
+
+    /**
+     * Write the migration file to disk.
+     *
+     * @param  string  $name
+     * @param  string  $table
+     * @param  bool  $create
+     * @return string
+     */
+    protected function writeMigration($name, $table, $create)
+    {
+        $file = $this->creator->create(
+            $name, $this->getMigrationPath(), $table, $create
+        );
+
+        $this->components->info(sprintf('Migration [%s] created successfully.', $file));
+    }
+
+    /**
+     * Get migration path (either specified by '--path' option or default location).
+     *
+     * @return string
+     */
+    protected function getMigrationPath()
+    {
+        if (! is_null($targetPath = $this->input->getOption('path'))) {
+            return ! $this->usingRealPath()
+                            ? $this->laravel->basePath().'/'.$targetPath
+                            : $targetPath;
+        }
+
+        return parent::getMigrationPath();
+    }
+
+    /**
+     * Prompt for missing input arguments using the returned questions.
+     *
+     * @return array
+     */
+    protected function promptForMissingArgumentsUsing()
+    {
+        return [
+            'name' => 'What should the migration be named?',
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/Console/Migrations/RefreshCommand.php b/vendor/illuminate/database/Console/Migrations/RefreshCommand.php
new file mode 100755
index 0000000..2073cd9
--- /dev/null
+++ b/vendor/illuminate/database/Console/Migrations/RefreshCommand.php
@@ -0,0 +1,159 @@
+confirmToProceed()) {
+            return 1;
+        }
+
+        // Next we'll gather some of the options so that we can have the right options
+        // to pass to the commands. This includes options such as which database to
+        // use and the path to use for the migration. Then we'll run the command.
+        $database = $this->input->getOption('database');
+
+        $path = $this->input->getOption('path');
+
+        // If the "step" option is specified it means we only want to rollback a small
+        // number of migrations before migrating again. For example, the user might
+        // only rollback and remigrate the latest four migrations instead of all.
+        $step = $this->input->getOption('step') ?: 0;
+
+        if ($step > 0) {
+            $this->runRollback($database, $path, $step);
+        } else {
+            $this->runReset($database, $path);
+        }
+
+        // The refresh command is essentially just a brief aggregate of a few other of
+        // the migration commands and just provides a convenient wrapper to execute
+        // them in succession. We'll also see if we need to re-seed the database.
+        $this->call('migrate', array_filter([
+            '--database' => $database,
+            '--path' => $path,
+            '--realpath' => $this->input->getOption('realpath'),
+            '--force' => true,
+        ]));
+
+        if ($this->laravel->bound(Dispatcher::class)) {
+            $this->laravel[Dispatcher::class]->dispatch(
+                new DatabaseRefreshed
+            );
+        }
+
+        if ($this->needsSeeding()) {
+            $this->runSeeder($database);
+        }
+
+        return 0;
+    }
+
+    /**
+     * Run the rollback command.
+     *
+     * @param  string  $database
+     * @param  string  $path
+     * @param  int  $step
+     * @return void
+     */
+    protected function runRollback($database, $path, $step)
+    {
+        $this->call('migrate:rollback', array_filter([
+            '--database' => $database,
+            '--path' => $path,
+            '--realpath' => $this->input->getOption('realpath'),
+            '--step' => $step,
+            '--force' => true,
+        ]));
+    }
+
+    /**
+     * Run the reset command.
+     *
+     * @param  string  $database
+     * @param  string  $path
+     * @return void
+     */
+    protected function runReset($database, $path)
+    {
+        $this->call('migrate:reset', array_filter([
+            '--database' => $database,
+            '--path' => $path,
+            '--realpath' => $this->input->getOption('realpath'),
+            '--force' => true,
+        ]));
+    }
+
+    /**
+     * Determine if the developer has requested database seeding.
+     *
+     * @return bool
+     */
+    protected function needsSeeding()
+    {
+        return $this->option('seed') || $this->option('seeder');
+    }
+
+    /**
+     * Run the database seeder command.
+     *
+     * @param  string  $database
+     * @return void
+     */
+    protected function runSeeder($database)
+    {
+        $this->call('db:seed', array_filter([
+            '--database' => $database,
+            '--class' => $this->option('seeder') ?: 'Database\\Seeders\\DatabaseSeeder',
+            '--force' => true,
+        ]));
+    }
+
+    /**
+     * Get the console command options.
+     *
+     * @return array
+     */
+    protected function getOptions()
+    {
+        return [
+            ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
+            ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
+            ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) to the migrations files to be executed'],
+            ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'],
+            ['seed', null, InputOption::VALUE_NONE, 'Indicates if the seed task should be re-run'],
+            ['seeder', null, InputOption::VALUE_OPTIONAL, 'The class name of the root seeder'],
+            ['step', null, InputOption::VALUE_OPTIONAL, 'The number of migrations to be reverted & re-run'],
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/Console/Migrations/ResetCommand.php b/vendor/illuminate/database/Console/Migrations/ResetCommand.php
new file mode 100755
index 0000000..c5952fa
--- /dev/null
+++ b/vendor/illuminate/database/Console/Migrations/ResetCommand.php
@@ -0,0 +1,91 @@
+migrator = $migrator;
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return int
+     */
+    public function handle()
+    {
+        if (! $this->confirmToProceed()) {
+            return 1;
+        }
+
+        return $this->migrator->usingConnection($this->option('database'), function () {
+            // First, we'll make sure that the migration table actually exists before we
+            // start trying to rollback and re-run all of the migrations. If it's not
+            // present we'll just bail out with an info message for the developers.
+            if (! $this->migrator->repositoryExists()) {
+                return $this->components->warn('Migration table not found.');
+            }
+
+            $this->migrator->setOutput($this->output)->reset(
+                $this->getMigrationPaths(), $this->option('pretend')
+            );
+        });
+    }
+
+    /**
+     * Get the console command options.
+     *
+     * @return array
+     */
+    protected function getOptions()
+    {
+        return [
+            ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
+
+            ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
+
+            ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) to the migrations files to be executed'],
+
+            ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'],
+
+            ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run'],
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/Console/Migrations/RollbackCommand.php b/vendor/illuminate/database/Console/Migrations/RollbackCommand.php
new file mode 100755
index 0000000..c851360
--- /dev/null
+++ b/vendor/illuminate/database/Console/Migrations/RollbackCommand.php
@@ -0,0 +1,91 @@
+migrator = $migrator;
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return int
+     */
+    public function handle()
+    {
+        if (! $this->confirmToProceed()) {
+            return 1;
+        }
+
+        $this->migrator->usingConnection($this->option('database'), function () {
+            $this->migrator->setOutput($this->output)->rollback(
+                $this->getMigrationPaths(), [
+                    'pretend' => $this->option('pretend'),
+                    'step' => (int) $this->option('step'),
+                ]
+            );
+        });
+
+        return 0;
+    }
+
+    /**
+     * Get the console command options.
+     *
+     * @return array
+     */
+    protected function getOptions()
+    {
+        return [
+            ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
+
+            ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
+
+            ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) to the migrations files to be executed'],
+
+            ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'],
+
+            ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run'],
+
+            ['step', null, InputOption::VALUE_OPTIONAL, 'The number of migrations to be reverted'],
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/Console/Migrations/StatusCommand.php b/vendor/illuminate/database/Console/Migrations/StatusCommand.php
new file mode 100644
index 0000000..aa01f07
--- /dev/null
+++ b/vendor/illuminate/database/Console/Migrations/StatusCommand.php
@@ -0,0 +1,132 @@
+migrator = $migrator;
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return int|null
+     */
+    public function handle()
+    {
+        return $this->migrator->usingConnection($this->option('database'), function () {
+            if (! $this->migrator->repositoryExists()) {
+                $this->components->error('Migration table not found.');
+
+                return 1;
+            }
+
+            $ran = $this->migrator->getRepository()->getRan();
+
+            $batches = $this->migrator->getRepository()->getMigrationBatches();
+
+            if (count($migrations = $this->getStatusFor($ran, $batches)) > 0) {
+                $this->newLine();
+
+                $this->components->twoColumnDetail('Migration name', 'Batch / Status');
+
+                $migrations
+                    ->when($this->option('pending'), fn ($collection) => $collection->filter(function ($migration) {
+                        return str($migration[1])->contains('Pending');
+                    }))
+                    ->each(
+                        fn ($migration) => $this->components->twoColumnDetail($migration[0], $migration[1])
+                    );
+
+                $this->newLine();
+            } else {
+                $this->components->info('No migrations found');
+            }
+        });
+    }
+
+    /**
+     * Get the status for the given run migrations.
+     *
+     * @param  array  $ran
+     * @param  array  $batches
+     * @return \Illuminate\Support\Collection
+     */
+    protected function getStatusFor(array $ran, array $batches)
+    {
+        return Collection::make($this->getAllMigrationFiles())
+                    ->map(function ($migration) use ($ran, $batches) {
+                        $migrationName = $this->migrator->getMigrationName($migration);
+
+                        $status = in_array($migrationName, $ran)
+                            ? 'Ran'
+                            : 'Pending';
+
+                        if (in_array($migrationName, $ran)) {
+                            $status = '['.$batches[$migrationName].'] '.$status;
+                        }
+
+                        return [$migrationName, $status];
+                    });
+    }
+
+    /**
+     * Get an array of all of the migration files.
+     *
+     * @return array
+     */
+    protected function getAllMigrationFiles()
+    {
+        return $this->migrator->getMigrationFiles($this->getMigrationPaths());
+    }
+
+    /**
+     * Get the console command options.
+     *
+     * @return array
+     */
+    protected function getOptions()
+    {
+        return [
+            ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
+            ['pending', null, InputOption::VALUE_NONE, 'Only list pending migrations'],
+            ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) to the migrations files to use'],
+            ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'],
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/Console/Migrations/TableGuesser.php b/vendor/illuminate/database/Console/Migrations/TableGuesser.php
new file mode 100644
index 0000000..82dfbdd
--- /dev/null
+++ b/vendor/illuminate/database/Console/Migrations/TableGuesser.php
@@ -0,0 +1,37 @@
+connection = $connection;
+        $this->events = $events;
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        $databases = $this->parseDatabases($this->option('databases'));
+
+        $this->displayConnections($databases);
+
+        if ($this->option('max')) {
+            $this->dispatchEvents($databases);
+        }
+    }
+
+    /**
+     * Parse the database into an array of the connections.
+     *
+     * @param  string  $databases
+     * @return \Illuminate\Support\Collection
+     */
+    protected function parseDatabases($databases)
+    {
+        return collect(explode(',', $databases))->map(function ($database) {
+            if (! $database) {
+                $database = $this->laravel['config']['database.default'];
+            }
+
+            $maxConnections = $this->option('max');
+
+            return [
+                'database' => $database,
+                'connections' => $connections = $this->getConnectionCount($this->connection->connection($database)),
+                'status' => $maxConnections && $connections >= $maxConnections ? 'ALERT' : 'OK',
+            ];
+        });
+    }
+
+    /**
+     * Display the databases and their connection counts in the console.
+     *
+     * @param  \Illuminate\Support\Collection  $databases
+     * @return void
+     */
+    protected function displayConnections($databases)
+    {
+        $this->newLine();
+
+        $this->components->twoColumnDetail('Database name', 'Connections');
+
+        $databases->each(function ($database) {
+            $status = '['.$database['connections'].'] '.$database['status'];
+
+            $this->components->twoColumnDetail($database['database'], $status);
+        });
+
+        $this->newLine();
+    }
+
+    /**
+     * Dispatch the database monitoring events.
+     *
+     * @param  \Illuminate\Support\Collection  $databases
+     * @return void
+     */
+    protected function dispatchEvents($databases)
+    {
+        $databases->each(function ($database) {
+            if ($database['status'] === 'OK') {
+                return;
+            }
+
+            $this->events->dispatch(
+                new DatabaseBusy(
+                    $database['database'],
+                    $database['connections']
+                )
+            );
+        });
+    }
+}
diff --git a/vendor/illuminate/database/Console/PruneCommand.php b/vendor/illuminate/database/Console/PruneCommand.php
new file mode 100644
index 0000000..7ea6cec
--- /dev/null
+++ b/vendor/illuminate/database/Console/PruneCommand.php
@@ -0,0 +1,186 @@
+models();
+
+        if ($models->isEmpty()) {
+            $this->components->info('No prunable models found.');
+
+            return;
+        }
+
+        if ($this->option('pretend')) {
+            $models->each(function ($model) {
+                $this->pretendToPrune($model);
+            });
+
+            return;
+        }
+
+        $pruning = [];
+
+        $events->listen(ModelsPruned::class, function ($event) use (&$pruning) {
+            if (! in_array($event->model, $pruning)) {
+                $pruning[] = $event->model;
+
+                $this->newLine();
+
+                $this->components->info(sprintf('Pruning [%s] records.', $event->model));
+            }
+
+            $this->components->twoColumnDetail($event->model, "{$event->count} records");
+        });
+
+        $models->each(function ($model) {
+            $this->pruneModel($model);
+        });
+
+        $events->forget(ModelsPruned::class);
+    }
+
+    /**
+     * Prune the given model.
+     *
+     * @param  string  $model
+     * @return void
+     */
+    protected function pruneModel(string $model)
+    {
+        $instance = new $model;
+
+        $chunkSize = property_exists($instance, 'prunableChunkSize')
+            ? $instance->prunableChunkSize
+            : $this->option('chunk');
+
+        $total = $this->isPrunable($model)
+            ? $instance->pruneAll($chunkSize)
+            : 0;
+
+        if ($total == 0) {
+            $this->components->info("No prunable [$model] records found.");
+        }
+    }
+
+    /**
+     * Determine the models that should be pruned.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    protected function models()
+    {
+        if (! empty($models = $this->option('model'))) {
+            return collect($models)->filter(function ($model) {
+                return class_exists($model);
+            })->values();
+        }
+
+        $except = $this->option('except');
+
+        if (! empty($models) && ! empty($except)) {
+            throw new InvalidArgumentException('The --models and --except options cannot be combined.');
+        }
+
+        return collect((new Finder)->in($this->getDefaultPath())->files()->name('*.php'))
+            ->map(function ($model) {
+                $namespace = $this->laravel->getNamespace();
+
+                return $namespace.str_replace(
+                    ['/', '.php'],
+                    ['\\', ''],
+                    Str::after($model->getRealPath(), realpath(app_path()).DIRECTORY_SEPARATOR)
+                );
+            })->when(! empty($except), function ($models) use ($except) {
+                return $models->reject(function ($model) use ($except) {
+                    return in_array($model, $except);
+                });
+            })->filter(function ($model) {
+                return $this->isPrunable($model);
+            })->filter(function ($model) {
+                return class_exists($model);
+            })->values();
+    }
+
+    /**
+     * Get the default path where models are located.
+     *
+     * @return string|string[]
+     */
+    protected function getDefaultPath()
+    {
+        return app_path('Models');
+    }
+
+    /**
+     * Determine if the given model class is prunable.
+     *
+     * @param  string  $model
+     * @return bool
+     */
+    protected function isPrunable($model)
+    {
+        $uses = class_uses_recursive($model);
+
+        return in_array(Prunable::class, $uses) || in_array(MassPrunable::class, $uses);
+    }
+
+    /**
+     * Display how many models will be pruned.
+     *
+     * @param  string  $model
+     * @return void
+     */
+    protected function pretendToPrune($model)
+    {
+        $instance = new $model;
+
+        $count = $instance->prunable()
+            ->when(in_array(SoftDeletes::class, class_uses_recursive(get_class($instance))), function ($query) {
+                $query->withTrashed();
+            })->count();
+
+        if ($count === 0) {
+            $this->components->info("No prunable [$model] records found.");
+        } else {
+            $this->components->info("{$count} [{$model}] records will be pruned.");
+        }
+    }
+}
diff --git a/vendor/illuminate/database/Console/Seeds/SeedCommand.php b/vendor/illuminate/database/Console/Seeds/SeedCommand.php
new file mode 100644
index 0000000..2359586
--- /dev/null
+++ b/vendor/illuminate/database/Console/Seeds/SeedCommand.php
@@ -0,0 +1,151 @@
+resolver = $resolver;
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return int
+     */
+    public function handle()
+    {
+        if (! $this->confirmToProceed()) {
+            return 1;
+        }
+
+        $this->components->info('Seeding database.');
+
+        $previousConnection = $this->resolver->getDefaultConnection();
+
+        $this->resolver->setDefaultConnection($this->getDatabase());
+
+        Model::unguarded(function () {
+            $this->getSeeder()->__invoke();
+        });
+
+        if ($previousConnection) {
+            $this->resolver->setDefaultConnection($previousConnection);
+        }
+
+        return 0;
+    }
+
+    /**
+     * Get a seeder instance from the container.
+     *
+     * @return \Illuminate\Database\Seeder
+     */
+    protected function getSeeder()
+    {
+        $class = $this->input->getArgument('class') ?? $this->input->getOption('class');
+
+        if (! str_contains($class, '\\')) {
+            $class = 'Database\\Seeders\\'.$class;
+        }
+
+        if ($class === 'Database\\Seeders\\DatabaseSeeder' &&
+            ! class_exists($class)) {
+            $class = 'DatabaseSeeder';
+        }
+
+        return $this->laravel->make($class)
+                        ->setContainer($this->laravel)
+                        ->setCommand($this);
+    }
+
+    /**
+     * Get the name of the database connection to use.
+     *
+     * @return string
+     */
+    protected function getDatabase()
+    {
+        $database = $this->input->getOption('database');
+
+        return $database ?: $this->laravel['config']['database.default'];
+    }
+
+    /**
+     * Get the console command arguments.
+     *
+     * @return array
+     */
+    protected function getArguments()
+    {
+        return [
+            ['class', InputArgument::OPTIONAL, 'The class name of the root seeder', null],
+        ];
+    }
+
+    /**
+     * Get the console command options.
+     *
+     * @return array
+     */
+    protected function getOptions()
+    {
+        return [
+            ['class', null, InputOption::VALUE_OPTIONAL, 'The class name of the root seeder', 'Database\\Seeders\\DatabaseSeeder'],
+            ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to seed'],
+            ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/Console/Seeds/SeederMakeCommand.php b/vendor/illuminate/database/Console/Seeds/SeederMakeCommand.php
new file mode 100644
index 0000000..8ba01cb
--- /dev/null
+++ b/vendor/illuminate/database/Console/Seeds/SeederMakeCommand.php
@@ -0,0 +1,103 @@
+resolveStubPath('/stubs/seeder.stub');
+    }
+
+    /**
+     * Resolve the fully-qualified path to the stub.
+     *
+     * @param  string  $stub
+     * @return string
+     */
+    protected function resolveStubPath($stub)
+    {
+        return is_file($customPath = $this->laravel->basePath(trim($stub, '/')))
+            ? $customPath
+            : __DIR__.$stub;
+    }
+
+    /**
+     * Get the destination class path.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    protected function getPath($name)
+    {
+        $name = str_replace('\\', '/', Str::replaceFirst($this->rootNamespace(), '', $name));
+
+        if (is_dir($this->laravel->databasePath().'/seeds')) {
+            return $this->laravel->databasePath().'/seeds/'.$name.'.php';
+        }
+
+        return $this->laravel->databasePath().'/seeders/'.$name.'.php';
+    }
+
+    /**
+     * Get the root namespace for the class.
+     *
+     * @return string
+     */
+    protected function rootNamespace()
+    {
+        return 'Database\Seeders\\';
+    }
+}
diff --git a/vendor/illuminate/database/Console/Seeds/WithoutModelEvents.php b/vendor/illuminate/database/Console/Seeds/WithoutModelEvents.php
new file mode 100644
index 0000000..acd9ec3
--- /dev/null
+++ b/vendor/illuminate/database/Console/Seeds/WithoutModelEvents.php
@@ -0,0 +1,19 @@
+ Model::withoutEvents($callback);
+    }
+}
diff --git a/vendor/illuminate/database/Console/Seeds/stubs/seeder.stub b/vendor/illuminate/database/Console/Seeds/stubs/seeder.stub
new file mode 100644
index 0000000..19ae5f5
--- /dev/null
+++ b/vendor/illuminate/database/Console/Seeds/stubs/seeder.stub
@@ -0,0 +1,19 @@
+ Note: This can be slow on large databases };
+                {--views : Show the database views  Note: This can be slow on large databases }';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Display information about the given database';
+
+    /**
+     * Execute the console command.
+     *
+     * @param  \Illuminate\Database\ConnectionResolverInterface  $connections
+     * @return int
+     */
+    public function handle(ConnectionResolverInterface $connections)
+    {
+        if (! $this->ensureDependenciesExist()) {
+            return 1;
+        }
+
+        $connection = $connections->connection($database = $this->input->getOption('database'));
+
+        $schema = $connection->getDoctrineSchemaManager();
+
+        $this->registerTypeMappings($schema->getDatabasePlatform());
+
+        $data = [
+            'platform' => [
+                'config' => $this->getConfigFromDatabase($database),
+                'name' => $this->getPlatformName($schema->getDatabasePlatform(), $database),
+                'open_connections' => $this->getConnectionCount($connection),
+            ],
+            'tables' => $this->tables($connection, $schema),
+        ];
+
+        if ($this->option('views')) {
+            $data['views'] = $this->collectViews($connection, $schema);
+        }
+
+        $this->display($data);
+
+        return 0;
+    }
+
+    /**
+     * Get information regarding the tables within the database.
+     *
+     * @param  \Illuminate\Database\ConnectionInterface  $connection
+     * @param  \Doctrine\DBAL\Schema\AbstractSchemaManager  $schema
+     * @return \Illuminate\Support\Collection
+     */
+    protected function tables(ConnectionInterface $connection, AbstractSchemaManager $schema)
+    {
+        return collect($schema->listTables())->map(fn (Table $table, $index) => [
+            'table' => $table->getName(),
+            'size' => $this->getTableSize($connection, $table->getName()),
+            'rows' => $this->option('counts') ? $connection->table($table->getName())->count() : null,
+            'engine' => rescue(fn () => $table->getOption('engine'), null, false),
+            'comment' => $table->getComment(),
+        ]);
+    }
+
+    /**
+     * Get information regarding the views within the database.
+     *
+     * @param  \Illuminate\Database\ConnectionInterface  $connection
+     * @param  \Doctrine\DBAL\Schema\AbstractSchemaManager  $schema
+     * @return \Illuminate\Support\Collection
+     */
+    protected function collectViews(ConnectionInterface $connection, AbstractSchemaManager $schema)
+    {
+        return collect($schema->listViews())
+            ->reject(fn (View $view) => str($view->getName())
+                ->startsWith(['pg_catalog', 'information_schema', 'spt_']))
+            ->map(fn (View $view) => [
+                'view' => $view->getName(),
+                'rows' => $connection->table($view->getName())->count(),
+            ]);
+    }
+
+    /**
+     * Render the database information.
+     *
+     * @param  array  $data
+     * @return void
+     */
+    protected function display(array $data)
+    {
+        $this->option('json') ? $this->displayJson($data) : $this->displayForCli($data);
+    }
+
+    /**
+     * Render the database information as JSON.
+     *
+     * @param  array  $data
+     * @return void
+     */
+    protected function displayJson(array $data)
+    {
+        $this->output->writeln(json_encode($data));
+    }
+
+    /**
+     * Render the database information formatted for the CLI.
+     *
+     * @param  array  $data
+     * @return void
+     */
+    protected function displayForCli(array $data)
+    {
+        $platform = $data['platform'];
+        $tables = $data['tables'];
+        $views = $data['views'] ?? null;
+
+        $this->newLine();
+
+        $this->components->twoColumnDetail(''.$platform['name'].'');
+        $this->components->twoColumnDetail('Database', Arr::get($platform['config'], 'database'));
+        $this->components->twoColumnDetail('Host', Arr::get($platform['config'], 'host'));
+        $this->components->twoColumnDetail('Port', Arr::get($platform['config'], 'port'));
+        $this->components->twoColumnDetail('Username', Arr::get($platform['config'], 'username'));
+        $this->components->twoColumnDetail('URL', Arr::get($platform['config'], 'url'));
+        $this->components->twoColumnDetail('Open Connections', $platform['open_connections']);
+        $this->components->twoColumnDetail('Tables', $tables->count());
+
+        if ($tableSizeSum = $tables->sum('size')) {
+            $this->components->twoColumnDetail('Total Size', number_format($tableSizeSum / 1024 / 1024, 2).'MiB');
+        }
+
+        $this->newLine();
+
+        if ($tables->isNotEmpty()) {
+            $this->components->twoColumnDetail('Table', 'Size (MiB)'.($this->option('counts') ? ' / Rows' : ''));
+
+            $tables->each(function ($table) {
+                if ($tableSize = $table['size']) {
+                    $tableSize = number_format($tableSize / 1024 / 1024, 2);
+                }
+
+                $this->components->twoColumnDetail(
+                    $table['table'].($this->output->isVerbose() ? ' '.$table['engine'].'' : null),
+                    ($tableSize ? $tableSize : '—').($this->option('counts') ? ' / '.number_format($table['rows']).'' : '')
+                );
+
+                if ($this->output->isVerbose()) {
+                    if ($table['comment']) {
+                        $this->components->bulletList([
+                            $table['comment'],
+                        ]);
+                    }
+                }
+            });
+
+            $this->newLine();
+        }
+
+        if ($views && $views->isNotEmpty()) {
+            $this->components->twoColumnDetail('View', 'Rows');
+
+            $views->each(fn ($view) => $this->components->twoColumnDetail($view['view'], number_format($view['rows'])));
+
+            $this->newLine();
+        }
+    }
+}
diff --git a/vendor/illuminate/database/Console/TableCommand.php b/vendor/illuminate/database/Console/TableCommand.php
new file mode 100644
index 0000000..3b08bde
--- /dev/null
+++ b/vendor/illuminate/database/Console/TableCommand.php
@@ -0,0 +1,246 @@
+ensureDependenciesExist()) {
+            return 1;
+        }
+
+        $connection = $connections->connection($this->input->getOption('database'));
+
+        $schema = $connection->getDoctrineSchemaManager();
+
+        $this->registerTypeMappings($schema->getDatabasePlatform());
+
+        $table = $this->argument('table') ?: $this->components->choice(
+            'Which table would you like to inspect?',
+            collect($schema->listTables())->flatMap(fn (Table $table) => [$table->getName()])->toArray()
+        );
+
+        if (! $schema->tablesExist([$table])) {
+            return $this->components->warn("Table [{$table}] doesn't exist.");
+        }
+
+        $table = $schema->listTableDetails($table);
+
+        $columns = $this->columns($table);
+        $indexes = $this->indexes($table);
+        $foreignKeys = $this->foreignKeys($table);
+
+        $data = [
+            'table' => [
+                'name' => $table->getName(),
+                'columns' => $columns->count(),
+                'size' => $this->getTableSize($connection, $table->getName()),
+            ],
+            'columns' => $columns,
+            'indexes' => $indexes,
+            'foreign_keys' => $foreignKeys,
+        ];
+
+        $this->display($data);
+
+        return 0;
+    }
+
+    /**
+     * Get the information regarding the table's columns.
+     *
+     * @param  \Doctrine\DBAL\Schema\Table  $table
+     * @return \Illuminate\Support\Collection
+     */
+    protected function columns(Table $table)
+    {
+        return collect($table->getColumns())->map(fn (Column $column) => [
+            'column' => $column->getName(),
+            'attributes' => $this->getAttributesForColumn($column),
+            'default' => $column->getDefault(),
+            'type' => $column->getType()->getName(),
+        ]);
+    }
+
+    /**
+     * Get the attributes for a table column.
+     *
+     * @param  \Doctrine\DBAL\Schema\Column  $column
+     * @return \Illuminate\Support\Collection
+     */
+    protected function getAttributesForColumn(Column $column)
+    {
+        return collect([
+            $column->getAutoincrement() ? 'autoincrement' : null,
+            'type' => $column->getType()->getName(),
+            $column->getUnsigned() ? 'unsigned' : null,
+            ! $column->getNotNull() ? 'nullable' : null,
+        ])->filter();
+    }
+
+    /**
+     * Get the information regarding the table's indexes.
+     *
+     * @param  \Doctrine\DBAL\Schema\Table  $table
+     * @return \Illuminate\Support\Collection
+     */
+    protected function indexes(Table $table)
+    {
+        return collect($table->getIndexes())->map(fn (Index $index) => [
+            'name' => $index->getName(),
+            'columns' => collect($index->getColumns()),
+            'attributes' => $this->getAttributesForIndex($index),
+        ]);
+    }
+
+    /**
+     * Get the attributes for a table index.
+     *
+     * @param  \Doctrine\DBAL\Schema\Index  $index
+     * @return \Illuminate\Support\Collection
+     */
+    protected function getAttributesForIndex(Index $index)
+    {
+        return collect([
+            'compound' => count($index->getColumns()) > 1,
+            'unique' => $index->isUnique(),
+            'primary' => $index->isPrimary(),
+        ])->filter()->keys()->map(fn ($attribute) => Str::lower($attribute));
+    }
+
+    /**
+     * Get the information regarding the table's foreign keys.
+     *
+     * @param  \Doctrine\DBAL\Schema\Table  $table
+     * @return \Illuminate\Support\Collection
+     */
+    protected function foreignKeys(Table $table)
+    {
+        return collect($table->getForeignKeys())->map(fn (ForeignKeyConstraint $foreignKey) => [
+            'name' => $foreignKey->getName(),
+            'local_table' => $table->getName(),
+            'local_columns' => collect($foreignKey->getLocalColumns()),
+            'foreign_table' => $foreignKey->getForeignTableName(),
+            'foreign_columns' => collect($foreignKey->getForeignColumns()),
+            'on_update' => Str::lower(rescue(fn () => $foreignKey->getOption('onUpdate'), 'N/A')),
+            'on_delete' => Str::lower(rescue(fn () => $foreignKey->getOption('onDelete'), 'N/A')),
+        ]);
+    }
+
+    /**
+     * Render the table information.
+     *
+     * @param  array  $data
+     * @return void
+     */
+    protected function display(array $data)
+    {
+        $this->option('json') ? $this->displayJson($data) : $this->displayForCli($data);
+    }
+
+    /**
+     * Render the table information as JSON.
+     *
+     * @param  array  $data
+     * @return void
+     */
+    protected function displayJson(array $data)
+    {
+        $this->output->writeln(json_encode($data));
+    }
+
+    /**
+     * Render the table information formatted for the CLI.
+     *
+     * @param  array  $data
+     * @return void
+     */
+    protected function displayForCli(array $data)
+    {
+        [$table, $columns, $indexes, $foreignKeys] = [
+            $data['table'], $data['columns'], $data['indexes'], $data['foreign_keys'],
+        ];
+
+        $this->newLine();
+
+        $this->components->twoColumnDetail(''.$table['name'].'');
+        $this->components->twoColumnDetail('Columns', $table['columns']);
+
+        if ($size = $table['size']) {
+            $this->components->twoColumnDetail('Size', number_format($size / 1024 / 1024, 2).'MiB');
+        }
+
+        $this->newLine();
+
+        if ($columns->isNotEmpty()) {
+            $this->components->twoColumnDetail('Column', 'Type');
+
+            $columns->each(function ($column) {
+                $this->components->twoColumnDetail(
+                    $column['column'].' '.$column['attributes']->implode(', ').'',
+                    ($column['default'] ? ''.$column['default'].' ' : '').''.$column['type'].''
+                );
+            });
+
+            $this->newLine();
+        }
+
+        if ($indexes->isNotEmpty()) {
+            $this->components->twoColumnDetail('Index');
+
+            $indexes->each(function ($index) {
+                $this->components->twoColumnDetail(
+                    $index['name'].' '.$index['columns']->implode(', ').'',
+                    $index['attributes']->implode(', ')
+                );
+            });
+
+            $this->newLine();
+        }
+
+        if ($foreignKeys->isNotEmpty()) {
+            $this->components->twoColumnDetail('Foreign Key', 'On Update / On Delete');
+
+            $foreignKeys->each(function ($foreignKey) {
+                $this->components->twoColumnDetail(
+                    $foreignKey['name'].' '.$foreignKey['local_columns']->implode(', ').' references '.$foreignKey['foreign_columns']->implode(', ').' on '.$foreignKey['foreign_table'].'',
+                    $foreignKey['on_update'].' / '.$foreignKey['on_delete'],
+                );
+            });
+
+            $this->newLine();
+        }
+    }
+}
diff --git a/vendor/illuminate/database/Console/WipeCommand.php b/vendor/illuminate/database/Console/WipeCommand.php
new file mode 100644
index 0000000..cb26922
--- /dev/null
+++ b/vendor/illuminate/database/Console/WipeCommand.php
@@ -0,0 +1,125 @@
+confirmToProceed()) {
+            return 1;
+        }
+
+        $database = $this->input->getOption('database');
+
+        if ($this->option('drop-views')) {
+            $this->dropAllViews($database);
+
+            $this->components->info('Dropped all views successfully.');
+        }
+
+        $this->dropAllTables($database);
+
+        $this->components->info('Dropped all tables successfully.');
+
+        if ($this->option('drop-types')) {
+            $this->dropAllTypes($database);
+
+            $this->components->info('Dropped all types successfully.');
+        }
+
+        return 0;
+    }
+
+    /**
+     * Drop all of the database tables.
+     *
+     * @param  string  $database
+     * @return void
+     */
+    protected function dropAllTables($database)
+    {
+        $this->laravel['db']->connection($database)
+                    ->getSchemaBuilder()
+                    ->dropAllTables();
+    }
+
+    /**
+     * Drop all of the database views.
+     *
+     * @param  string  $database
+     * @return void
+     */
+    protected function dropAllViews($database)
+    {
+        $this->laravel['db']->connection($database)
+                    ->getSchemaBuilder()
+                    ->dropAllViews();
+    }
+
+    /**
+     * Drop all of the database types.
+     *
+     * @param  string  $database
+     * @return void
+     */
+    protected function dropAllTypes($database)
+    {
+        $this->laravel['db']->connection($database)
+                    ->getSchemaBuilder()
+                    ->dropAllTypes();
+    }
+
+    /**
+     * Get the console command options.
+     *
+     * @return array
+     */
+    protected function getOptions()
+    {
+        return [
+            ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
+            ['drop-views', null, InputOption::VALUE_NONE, 'Drop all tables and views'],
+            ['drop-types', null, InputOption::VALUE_NONE, 'Drop all tables and types (Postgres only)'],
+            ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/DBAL/TimestampType.php b/vendor/illuminate/database/DBAL/TimestampType.php
new file mode 100644
index 0000000..4a863bc
--- /dev/null
+++ b/vendor/illuminate/database/DBAL/TimestampType.php
@@ -0,0 +1,99 @@
+getName()) {
+            'mysql',
+            'mysql2' => $this->getMySqlPlatformSQLDeclaration($fieldDeclaration),
+            'postgresql',
+            'pgsql',
+            'postgres' => $this->getPostgresPlatformSQLDeclaration($fieldDeclaration),
+            'mssql' => $this->getSqlServerPlatformSQLDeclaration($fieldDeclaration),
+            'sqlite',
+            'sqlite3' => $this->getSQLitePlatformSQLDeclaration($fieldDeclaration),
+            default => throw new DBALException('Invalid platform: '.$name),
+        };
+    }
+
+    /**
+     * Get the SQL declaration for MySQL.
+     *
+     * @param  array  $fieldDeclaration
+     * @return string
+     */
+    protected function getMySqlPlatformSQLDeclaration(array $fieldDeclaration)
+    {
+        $columnType = 'TIMESTAMP';
+
+        if ($fieldDeclaration['precision']) {
+            $columnType = 'TIMESTAMP('.$fieldDeclaration['precision'].')';
+        }
+
+        $notNull = $fieldDeclaration['notnull'] ?? false;
+
+        if (! $notNull) {
+            return $columnType.' NULL';
+        }
+
+        return $columnType;
+    }
+
+    /**
+     * Get the SQL declaration for PostgreSQL.
+     *
+     * @param  array  $fieldDeclaration
+     * @return string
+     */
+    protected function getPostgresPlatformSQLDeclaration(array $fieldDeclaration)
+    {
+        return 'TIMESTAMP('.(int) $fieldDeclaration['precision'].')';
+    }
+
+    /**
+     * Get the SQL declaration for SQL Server.
+     *
+     * @param  array  $fieldDeclaration
+     * @return string
+     */
+    protected function getSqlServerPlatformSQLDeclaration(array $fieldDeclaration)
+    {
+        return $fieldDeclaration['precision'] ?? false
+                    ? 'DATETIME2('.$fieldDeclaration['precision'].')'
+                    : 'DATETIME';
+    }
+
+    /**
+     * Get the SQL declaration for SQLite.
+     *
+     * @param  array  $fieldDeclaration
+     * @return string
+     */
+    protected function getSQLitePlatformSQLDeclaration(array $fieldDeclaration)
+    {
+        return 'DATETIME';
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return 'timestamp';
+    }
+}
diff --git a/vendor/illuminate/database/DatabaseManager.php b/vendor/illuminate/database/DatabaseManager.php
new file mode 100755
index 0000000..fc81353
--- /dev/null
+++ b/vendor/illuminate/database/DatabaseManager.php
@@ -0,0 +1,471 @@
+
+     */
+    protected $connections = [];
+
+    /**
+     * The custom connection resolvers.
+     *
+     * @var array
+     */
+    protected $extensions = [];
+
+    /**
+     * The callback to be executed to reconnect to a database.
+     *
+     * @var callable
+     */
+    protected $reconnector;
+
+    /**
+     * The custom Doctrine column types.
+     *
+     * @var array
+     */
+    protected $doctrineTypes = [];
+
+    /**
+     * Create a new database manager instance.
+     *
+     * @param  \Illuminate\Contracts\Foundation\Application  $app
+     * @param  \Illuminate\Database\Connectors\ConnectionFactory  $factory
+     * @return void
+     */
+    public function __construct($app, ConnectionFactory $factory)
+    {
+        $this->app = $app;
+        $this->factory = $factory;
+
+        $this->reconnector = function ($connection) {
+            $this->reconnect($connection->getNameWithReadWriteType());
+        };
+    }
+
+    /**
+     * Get a database connection instance.
+     *
+     * @param  string|null  $name
+     * @return \Illuminate\Database\Connection
+     */
+    public function connection($name = null)
+    {
+        [$database, $type] = $this->parseConnectionName($name);
+
+        $name = $name ?: $database;
+
+        // If we haven't created this connection, we'll create it based on the config
+        // provided in the application. Once we've created the connections we will
+        // set the "fetch mode" for PDO which determines the query return types.
+        if (! isset($this->connections[$name])) {
+            $this->connections[$name] = $this->configure(
+                $this->makeConnection($database), $type
+            );
+
+            if ($this->app->bound('events')) {
+                $this->app['events']->dispatch(
+                    new ConnectionEstablished($this->connections[$name])
+                );
+            }
+        }
+
+        return $this->connections[$name];
+    }
+
+    /**
+     * Parse the connection into an array of the name and read / write type.
+     *
+     * @param  string  $name
+     * @return array
+     */
+    protected function parseConnectionName($name)
+    {
+        $name = $name ?: $this->getDefaultConnection();
+
+        return Str::endsWith($name, ['::read', '::write'])
+                            ? explode('::', $name, 2) : [$name, null];
+    }
+
+    /**
+     * Make the database connection instance.
+     *
+     * @param  string  $name
+     * @return \Illuminate\Database\Connection
+     */
+    protected function makeConnection($name)
+    {
+        $config = $this->configuration($name);
+
+        // First we will check by the connection name to see if an extension has been
+        // registered specifically for that connection. If it has we will call the
+        // Closure and pass it the config allowing it to resolve the connection.
+        if (isset($this->extensions[$name])) {
+            return call_user_func($this->extensions[$name], $config, $name);
+        }
+
+        // Next we will check to see if an extension has been registered for a driver
+        // and will call the Closure if so, which allows us to have a more generic
+        // resolver for the drivers themselves which applies to all connections.
+        if (isset($this->extensions[$driver = $config['driver']])) {
+            return call_user_func($this->extensions[$driver], $config, $name);
+        }
+
+        return $this->factory->make($config, $name);
+    }
+
+    /**
+     * Get the configuration for a connection.
+     *
+     * @param  string  $name
+     * @return array
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function configuration($name)
+    {
+        $name = $name ?: $this->getDefaultConnection();
+
+        // To get the database connection configuration, we will just pull each of the
+        // connection configurations and get the configurations for the given name.
+        // If the configuration doesn't exist, we'll throw an exception and bail.
+        $connections = $this->app['config']['database.connections'];
+
+        if (is_null($config = Arr::get($connections, $name))) {
+            throw new InvalidArgumentException("Database connection [{$name}] not configured.");
+        }
+
+        return (new ConfigurationUrlParser)
+                    ->parseConfiguration($config);
+    }
+
+    /**
+     * Prepare the database connection instance.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @param  string  $type
+     * @return \Illuminate\Database\Connection
+     */
+    protected function configure(Connection $connection, $type)
+    {
+        $connection = $this->setPdoForType($connection, $type)->setReadWriteType($type);
+
+        // First we'll set the fetch mode and a few other dependencies of the database
+        // connection. This method basically just configures and prepares it to get
+        // used by the application. Once we're finished we'll return it back out.
+        if ($this->app->bound('events')) {
+            $connection->setEventDispatcher($this->app['events']);
+        }
+
+        if ($this->app->bound('db.transactions')) {
+            $connection->setTransactionManager($this->app['db.transactions']);
+        }
+
+        // Here we'll set a reconnector callback. This reconnector can be any callable
+        // so we will set a Closure to reconnect from this manager with the name of
+        // the connection, which will allow us to reconnect from the connections.
+        $connection->setReconnector($this->reconnector);
+
+        $this->registerConfiguredDoctrineTypes($connection);
+
+        return $connection;
+    }
+
+    /**
+     * Prepare the read / write mode for database connection instance.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @param  string|null  $type
+     * @return \Illuminate\Database\Connection
+     */
+    protected function setPdoForType(Connection $connection, $type = null)
+    {
+        if ($type === 'read') {
+            $connection->setPdo($connection->getReadPdo());
+        } elseif ($type === 'write') {
+            $connection->setReadPdo($connection->getPdo());
+        }
+
+        return $connection;
+    }
+
+    /**
+     * Register custom Doctrine types with the connection.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return void
+     */
+    protected function registerConfiguredDoctrineTypes(Connection $connection): void
+    {
+        foreach ($this->app['config']->get('database.dbal.types', []) as $name => $class) {
+            $this->registerDoctrineType($class, $name, $name);
+        }
+
+        foreach ($this->doctrineTypes as $name => [$type, $class]) {
+            $connection->registerDoctrineType($class, $name, $type);
+        }
+    }
+
+    /**
+     * Register a custom Doctrine type.
+     *
+     * @param  string  $class
+     * @param  string  $name
+     * @param  string  $type
+     * @return void
+     *
+     * @throws \Doctrine\DBAL\DBALException
+     * @throws \RuntimeException
+     */
+    public function registerDoctrineType(string $class, string $name, string $type): void
+    {
+        if (! class_exists('Doctrine\DBAL\Connection')) {
+            throw new RuntimeException(
+                'Registering a custom Doctrine type requires Doctrine DBAL (doctrine/dbal).'
+            );
+        }
+
+        if (! Type::hasType($name)) {
+            Type::addType($name, $class);
+        }
+
+        $this->doctrineTypes[$name] = [$type, $class];
+    }
+
+    /**
+     * Disconnect from the given database and remove from local cache.
+     *
+     * @param  string|null  $name
+     * @return void
+     */
+    public function purge($name = null)
+    {
+        $name = $name ?: $this->getDefaultConnection();
+
+        $this->disconnect($name);
+
+        unset($this->connections[$name]);
+    }
+
+    /**
+     * Disconnect from the given database.
+     *
+     * @param  string|null  $name
+     * @return void
+     */
+    public function disconnect($name = null)
+    {
+        if (isset($this->connections[$name = $name ?: $this->getDefaultConnection()])) {
+            $this->connections[$name]->disconnect();
+        }
+    }
+
+    /**
+     * Reconnect to the given database.
+     *
+     * @param  string|null  $name
+     * @return \Illuminate\Database\Connection
+     */
+    public function reconnect($name = null)
+    {
+        $this->disconnect($name = $name ?: $this->getDefaultConnection());
+
+        if (! isset($this->connections[$name])) {
+            return $this->connection($name);
+        }
+
+        return $this->refreshPdoConnections($name);
+    }
+
+    /**
+     * Set the default database connection for the callback execution.
+     *
+     * @param  string  $name
+     * @param  callable  $callback
+     * @return mixed
+     */
+    public function usingConnection($name, callable $callback)
+    {
+        $previousName = $this->getDefaultConnection();
+
+        $this->setDefaultConnection($name);
+
+        return tap($callback(), function () use ($previousName) {
+            $this->setDefaultConnection($previousName);
+        });
+    }
+
+    /**
+     * Refresh the PDO connections on a given connection.
+     *
+     * @param  string  $name
+     * @return \Illuminate\Database\Connection
+     */
+    protected function refreshPdoConnections($name)
+    {
+        [$database, $type] = $this->parseConnectionName($name);
+
+        $fresh = $this->configure(
+            $this->makeConnection($database), $type
+        );
+
+        return $this->connections[$name]
+                    ->setPdo($fresh->getRawPdo())
+                    ->setReadPdo($fresh->getRawReadPdo());
+    }
+
+    /**
+     * Get the default connection name.
+     *
+     * @return string
+     */
+    public function getDefaultConnection()
+    {
+        return $this->app['config']['database.default'];
+    }
+
+    /**
+     * Set the default connection name.
+     *
+     * @param  string  $name
+     * @return void
+     */
+    public function setDefaultConnection($name)
+    {
+        $this->app['config']['database.default'] = $name;
+    }
+
+    /**
+     * Get all of the support drivers.
+     *
+     * @return string[]
+     */
+    public function supportedDrivers()
+    {
+        return ['mysql', 'pgsql', 'sqlite', 'sqlsrv'];
+    }
+
+    /**
+     * Get all of the drivers that are actually available.
+     *
+     * @return string[]
+     */
+    public function availableDrivers()
+    {
+        return array_intersect(
+            $this->supportedDrivers(),
+            str_replace('dblib', 'sqlsrv', PDO::getAvailableDrivers())
+        );
+    }
+
+    /**
+     * Register an extension connection resolver.
+     *
+     * @param  string  $name
+     * @param  callable  $resolver
+     * @return void
+     */
+    public function extend($name, callable $resolver)
+    {
+        $this->extensions[$name] = $resolver;
+    }
+
+    /**
+     * Remove an extension connection resolver.
+     *
+     * @param  string  $name
+     * @return void
+     */
+    public function forgetExtension($name)
+    {
+        unset($this->extensions[$name]);
+    }
+
+    /**
+     * Return all of the created connections.
+     *
+     * @return array
+     */
+    public function getConnections()
+    {
+        return $this->connections;
+    }
+
+    /**
+     * Set the database reconnector callback.
+     *
+     * @param  callable  $reconnector
+     * @return void
+     */
+    public function setReconnector(callable $reconnector)
+    {
+        $this->reconnector = $reconnector;
+    }
+
+    /**
+     * Set the application instance used by the manager.
+     *
+     * @param  \Illuminate\Contracts\Foundation\Application  $app
+     * @return $this
+     */
+    public function setApplication($app)
+    {
+        $this->app = $app;
+
+        return $this;
+    }
+
+    /**
+     * Dynamically pass methods to the default connection.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        if (static::hasMacro($method)) {
+            return $this->macroCall($method, $parameters);
+        }
+
+        return $this->connection()->$method(...$parameters);
+    }
+}
diff --git a/vendor/illuminate/database/DatabaseServiceProvider.php b/vendor/illuminate/database/DatabaseServiceProvider.php
new file mode 100755
index 0000000..9a2f47d
--- /dev/null
+++ b/vendor/illuminate/database/DatabaseServiceProvider.php
@@ -0,0 +1,113 @@
+app['db']);
+
+        Model::setEventDispatcher($this->app['events']);
+    }
+
+    /**
+     * Register the service provider.
+     *
+     * @return void
+     */
+    public function register()
+    {
+        Model::clearBootedModels();
+
+        $this->registerConnectionServices();
+        $this->registerEloquentFactory();
+        $this->registerQueueableEntityResolver();
+    }
+
+    /**
+     * Register the primary database bindings.
+     *
+     * @return void
+     */
+    protected function registerConnectionServices()
+    {
+        // The connection factory is used to create the actual connection instances on
+        // the database. We will inject the factory into the manager so that it may
+        // make the connections while they are actually needed and not of before.
+        $this->app->singleton('db.factory', function ($app) {
+            return new ConnectionFactory($app);
+        });
+
+        // The database manager is used to resolve various connections, since multiple
+        // connections might be managed. It also implements the connection resolver
+        // interface which may be used by other components requiring connections.
+        $this->app->singleton('db', function ($app) {
+            return new DatabaseManager($app, $app['db.factory']);
+        });
+
+        $this->app->bind('db.connection', function ($app) {
+            return $app['db']->connection();
+        });
+
+        $this->app->bind('db.schema', function ($app) {
+            return $app['db']->connection()->getSchemaBuilder();
+        });
+
+        $this->app->singleton('db.transactions', function ($app) {
+            return new DatabaseTransactionsManager;
+        });
+    }
+
+    /**
+     * Register the Eloquent factory instance in the container.
+     *
+     * @return void
+     */
+    protected function registerEloquentFactory()
+    {
+        $this->app->singleton(FakerGenerator::class, function ($app, $parameters) {
+            $locale = $parameters['locale'] ?? $app['config']->get('app.faker_locale', 'en_US');
+
+            if (! isset(static::$fakers[$locale])) {
+                static::$fakers[$locale] = FakerFactory::create($locale);
+            }
+
+            static::$fakers[$locale]->unique(true);
+
+            return static::$fakers[$locale];
+        });
+    }
+
+    /**
+     * Register the queueable entity resolver implementation.
+     *
+     * @return void
+     */
+    protected function registerQueueableEntityResolver()
+    {
+        $this->app->singleton(EntityResolver::class, function () {
+            return new QueueEntityResolver;
+        });
+    }
+}
diff --git a/vendor/illuminate/database/DatabaseTransactionRecord.php b/vendor/illuminate/database/DatabaseTransactionRecord.php
new file mode 100755
index 0000000..4736ee9
--- /dev/null
+++ b/vendor/illuminate/database/DatabaseTransactionRecord.php
@@ -0,0 +1,73 @@
+connection = $connection;
+        $this->level = $level;
+    }
+
+    /**
+     * Register a callback to be executed after committing.
+     *
+     * @param  callable  $callback
+     * @return void
+     */
+    public function addCallback($callback)
+    {
+        $this->callbacks[] = $callback;
+    }
+
+    /**
+     * Execute all of the callbacks.
+     *
+     * @return void
+     */
+    public function executeCallbacks()
+    {
+        foreach ($this->callbacks as $callback) {
+            $callback();
+        }
+    }
+
+    /**
+     * Get all of the callbacks.
+     *
+     * @return array
+     */
+    public function getCallbacks()
+    {
+        return $this->callbacks;
+    }
+}
diff --git a/vendor/illuminate/database/DatabaseTransactionsManager.php b/vendor/illuminate/database/DatabaseTransactionsManager.php
new file mode 100755
index 0000000..8d14518
--- /dev/null
+++ b/vendor/illuminate/database/DatabaseTransactionsManager.php
@@ -0,0 +1,133 @@
+transactions = collect();
+    }
+
+    /**
+     * Start a new database transaction.
+     *
+     * @param  string  $connection
+     * @param  int  $level
+     * @return void
+     */
+    public function begin($connection, $level)
+    {
+        $this->transactions->push(
+            new DatabaseTransactionRecord($connection, $level)
+        );
+    }
+
+    /**
+     * Rollback the active database transaction.
+     *
+     * @param  string  $connection
+     * @param  int  $level
+     * @return void
+     */
+    public function rollback($connection, $level)
+    {
+        $this->transactions = $this->transactions->reject(
+            fn ($transaction) => $transaction->connection == $connection && $transaction->level > $level
+        )->values();
+
+        if ($this->transactions->isEmpty()) {
+            $this->callbacksShouldIgnore = null;
+        }
+    }
+
+    /**
+     * Commit the active database transaction.
+     *
+     * @param  string  $connection
+     * @return void
+     */
+    public function commit($connection)
+    {
+        [$forThisConnection, $forOtherConnections] = $this->transactions->partition(
+            fn ($transaction) => $transaction->connection == $connection
+        );
+
+        $this->transactions = $forOtherConnections->values();
+
+        $forThisConnection->map->executeCallbacks();
+
+        if ($this->transactions->isEmpty()) {
+            $this->callbacksShouldIgnore = null;
+        }
+    }
+
+    /**
+     * Register a transaction callback.
+     *
+     * @param  callable  $callback
+     * @return void
+     */
+    public function addCallback($callback)
+    {
+        if ($current = $this->callbackApplicableTransactions()->last()) {
+            return $current->addCallback($callback);
+        }
+
+        $callback();
+    }
+
+    /**
+     * Specify that callbacks should ignore the given transaction when determining if they should be executed.
+     *
+     * @param  \Illuminate\Database\DatabaseTransactionRecord  $transaction
+     * @return $this
+     */
+    public function callbacksShouldIgnore(DatabaseTransactionRecord $transaction)
+    {
+        $this->callbacksShouldIgnore = $transaction;
+
+        return $this;
+    }
+
+    /**
+     * Get the transactions that are applicable to callbacks.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function callbackApplicableTransactions()
+    {
+        return $this->transactions->reject(function ($transaction) {
+            return $transaction === $this->callbacksShouldIgnore;
+        })->values();
+    }
+
+    /**
+     * Get all the transactions.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function getTransactions()
+    {
+        return $this->transactions;
+    }
+}
diff --git a/vendor/illuminate/database/DeadlockException.php b/vendor/illuminate/database/DeadlockException.php
new file mode 100644
index 0000000..375a39b
--- /dev/null
+++ b/vendor/illuminate/database/DeadlockException.php
@@ -0,0 +1,10 @@
+getCode() === 40001 || $e->getCode() === '40001')) {
+            return true;
+        }
+
+        $message = $e->getMessage();
+
+        return Str::contains($message, [
+            'Deadlock found when trying to get lock',
+            'deadlock detected',
+            'The database file is locked',
+            'database is locked',
+            'database table is locked',
+            'A table in the database is locked',
+            'has been chosen as the deadlock victim',
+            'Lock wait timeout exceeded; try restarting transaction',
+            'WSREP detected deadlock/conflict and aborted the transaction. Try restarting the transaction',
+        ]);
+    }
+}
diff --git a/vendor/illuminate/database/DetectsLostConnections.php b/vendor/illuminate/database/DetectsLostConnections.php
new file mode 100644
index 0000000..f0c216f
--- /dev/null
+++ b/vendor/illuminate/database/DetectsLostConnections.php
@@ -0,0 +1,66 @@
+getMessage();
+
+        return Str::contains($message, [
+            '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',
+            'Transaction() on null',
+            'child connection forced to terminate due to client_idle_limit',
+            'query_wait_timeout',
+            'reset by peer',
+            'Physical connection is not usable',
+            'TCP Provider: Error code 0x68',
+            'ORA-03114',
+            'Packets out of order. Expected',
+            'Adaptive Server connection failed',
+            'Communication link failure',
+            'connection is no longer usable',
+            'Login timeout expired',
+            'SQLSTATE[HY000] [2002] Connection refused',
+            'running with the --read-only option so it cannot execute this statement',
+            'The connection is broken and recovery is not possible. The connection is marked by the client driver as unrecoverable. No attempt was made to restore the connection.',
+            'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Try again',
+            'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known',
+            'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo for',
+            'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: EOF detected',
+            'SQLSTATE[HY000] [2002] Connection timed out',
+            'SSL: Connection timed out',
+            'SQLSTATE[HY000]: General error: 1105 The last transaction was aborted due to Seamless Scaling. Please retry.',
+            'Temporary failure in name resolution',
+            'SSL: Broken pipe',
+            'SQLSTATE[08S01]: Communication link failure',
+            'SQLSTATE[08006] [7] could not connect to server: Connection refused Is the server running on host',
+            'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: No route to host',
+            'The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.',
+            'SQLSTATE[08006] [7] could not translate host name',
+            'TCP Provider: Error code 0x274C',
+            'SQLSTATE[HY000] [2002] No such file or directory',
+            'SSL: Operation timed out',
+            'Reason: Server is in script upgrade mode. Only administrator can connect at this time.',
+        ]);
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/BroadcastableModelEventOccurred.php b/vendor/illuminate/database/Eloquent/BroadcastableModelEventOccurred.php
new file mode 100644
index 0000000..249b183
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/BroadcastableModelEventOccurred.php
@@ -0,0 +1,144 @@
+model = $model;
+        $this->event = $event;
+    }
+
+    /**
+     * The channels the event should broadcast on.
+     *
+     * @return array
+     */
+    public function broadcastOn()
+    {
+        $channels = empty($this->channels)
+                ? ($this->model->broadcastOn($this->event) ?: [])
+                : $this->channels;
+
+        return collect($channels)->map(function ($channel) {
+            return $channel instanceof Model ? new PrivateChannel($channel) : $channel;
+        })->all();
+    }
+
+    /**
+     * The name the event should broadcast as.
+     *
+     * @return string
+     */
+    public function broadcastAs()
+    {
+        $default = class_basename($this->model).ucfirst($this->event);
+
+        return method_exists($this->model, 'broadcastAs')
+                ? ($this->model->broadcastAs($this->event) ?: $default)
+                : $default;
+    }
+
+    /**
+     * Get the data that should be sent with the broadcasted event.
+     *
+     * @return array|null
+     */
+    public function broadcastWith()
+    {
+        return method_exists($this->model, 'broadcastWith')
+            ? $this->model->broadcastWith($this->event)
+            : null;
+    }
+
+    /**
+     * Manually specify the channels the event should broadcast on.
+     *
+     * @param  array  $channels
+     * @return $this
+     */
+    public function onChannels(array $channels)
+    {
+        $this->channels = $channels;
+
+        return $this;
+    }
+
+    /**
+     * Determine if the event should be broadcast synchronously.
+     *
+     * @return bool
+     */
+    public function shouldBroadcastNow()
+    {
+        return $this->event === 'deleted' &&
+               ! method_exists($this->model, 'bootSoftDeletes');
+    }
+
+    /**
+     * Get the event name.
+     *
+     * @return string
+     */
+    public function event()
+    {
+        return $this->event;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/BroadcastsEvents.php b/vendor/illuminate/database/Eloquent/BroadcastsEvents.php
new file mode 100644
index 0000000..79dc02d
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/BroadcastsEvents.php
@@ -0,0 +1,197 @@
+broadcastCreated();
+        });
+
+        static::updated(function ($model) {
+            $model->broadcastUpdated();
+        });
+
+        if (method_exists(static::class, 'bootSoftDeletes')) {
+            static::softDeleted(function ($model) {
+                $model->broadcastTrashed();
+            });
+
+            static::restored(function ($model) {
+                $model->broadcastRestored();
+            });
+        }
+
+        static::deleted(function ($model) {
+            $model->broadcastDeleted();
+        });
+    }
+
+    /**
+     * Broadcast that the model was created.
+     *
+     * @param  \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null  $channels
+     * @return \Illuminate\Broadcasting\PendingBroadcast
+     */
+    public function broadcastCreated($channels = null)
+    {
+        return $this->broadcastIfBroadcastChannelsExistForEvent(
+            $this->newBroadcastableModelEvent('created'), 'created', $channels
+        );
+    }
+
+    /**
+     * Broadcast that the model was updated.
+     *
+     * @param  \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null  $channels
+     * @return \Illuminate\Broadcasting\PendingBroadcast
+     */
+    public function broadcastUpdated($channels = null)
+    {
+        return $this->broadcastIfBroadcastChannelsExistForEvent(
+            $this->newBroadcastableModelEvent('updated'), 'updated', $channels
+        );
+    }
+
+    /**
+     * Broadcast that the model was trashed.
+     *
+     * @param  \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null  $channels
+     * @return \Illuminate\Broadcasting\PendingBroadcast
+     */
+    public function broadcastTrashed($channels = null)
+    {
+        return $this->broadcastIfBroadcastChannelsExistForEvent(
+            $this->newBroadcastableModelEvent('trashed'), 'trashed', $channels
+        );
+    }
+
+    /**
+     * Broadcast that the model was restored.
+     *
+     * @param  \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null  $channels
+     * @return \Illuminate\Broadcasting\PendingBroadcast
+     */
+    public function broadcastRestored($channels = null)
+    {
+        return $this->broadcastIfBroadcastChannelsExistForEvent(
+            $this->newBroadcastableModelEvent('restored'), 'restored', $channels
+        );
+    }
+
+    /**
+     * Broadcast that the model was deleted.
+     *
+     * @param  \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null  $channels
+     * @return \Illuminate\Broadcasting\PendingBroadcast
+     */
+    public function broadcastDeleted($channels = null)
+    {
+        return $this->broadcastIfBroadcastChannelsExistForEvent(
+            $this->newBroadcastableModelEvent('deleted'), 'deleted', $channels
+        );
+    }
+
+    /**
+     * Broadcast the given event instance if channels are configured for the model event.
+     *
+     * @param  mixed  $instance
+     * @param  string  $event
+     * @param  mixed  $channels
+     * @return \Illuminate\Broadcasting\PendingBroadcast|null
+     */
+    protected function broadcastIfBroadcastChannelsExistForEvent($instance, $event, $channels = null)
+    {
+        if (! static::$isBroadcasting) {
+            return;
+        }
+
+        if (! empty($this->broadcastOn($event)) || ! empty($channels)) {
+            return broadcast($instance->onChannels(Arr::wrap($channels)));
+        }
+    }
+
+    /**
+     * Create a new broadcastable model event event.
+     *
+     * @param  string  $event
+     * @return mixed
+     */
+    public function newBroadcastableModelEvent($event)
+    {
+        return tap($this->newBroadcastableEvent($event), function ($event) {
+            $event->connection = property_exists($this, 'broadcastConnection')
+                            ? $this->broadcastConnection
+                            : $this->broadcastConnection();
+
+            $event->queue = property_exists($this, 'broadcastQueue')
+                            ? $this->broadcastQueue
+                            : $this->broadcastQueue();
+
+            $event->afterCommit = property_exists($this, 'broadcastAfterCommit')
+                            ? $this->broadcastAfterCommit
+                            : $this->broadcastAfterCommit();
+        });
+    }
+
+    /**
+     * Create a new broadcastable model event for the model.
+     *
+     * @param  string  $event
+     * @return \Illuminate\Database\Eloquent\BroadcastableModelEventOccurred
+     */
+    protected function newBroadcastableEvent($event)
+    {
+        return new BroadcastableModelEventOccurred($this, $event);
+    }
+
+    /**
+     * Get the channels that model events should broadcast on.
+     *
+     * @param  string  $event
+     * @return \Illuminate\Broadcasting\Channel|array
+     */
+    public function broadcastOn($event)
+    {
+        return [$this];
+    }
+
+    /**
+     * Get the queue connection that should be used to broadcast model events.
+     *
+     * @return string|null
+     */
+    public function broadcastConnection()
+    {
+        //
+    }
+
+    /**
+     * Get the queue that should be used to broadcast model events.
+     *
+     * @return string|null
+     */
+    public function broadcastQueue()
+    {
+        //
+    }
+
+    /**
+     * Determine if the model event broadcast queued job should be dispatched after all transactions are committed.
+     *
+     * @return bool
+     */
+    public function broadcastAfterCommit()
+    {
+        return false;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Builder.php b/vendor/illuminate/database/Eloquent/Builder.php
new file mode 100755
index 0000000..4387bc8
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Builder.php
@@ -0,0 +1,1952 @@
+query = $query;
+    }
+
+    /**
+     * Create and return an un-saved model instance.
+     *
+     * @param  array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model|static
+     */
+    public function make(array $attributes = [])
+    {
+        return $this->newModelInstance($attributes);
+    }
+
+    /**
+     * Register a new global scope.
+     *
+     * @param  string  $identifier
+     * @param  \Illuminate\Database\Eloquent\Scope|\Closure  $scope
+     * @return $this
+     */
+    public function withGlobalScope($identifier, $scope)
+    {
+        $this->scopes[$identifier] = $scope;
+
+        if (method_exists($scope, 'extend')) {
+            $scope->extend($this);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Remove a registered global scope.
+     *
+     * @param  \Illuminate\Database\Eloquent\Scope|string  $scope
+     * @return $this
+     */
+    public function withoutGlobalScope($scope)
+    {
+        if (! is_string($scope)) {
+            $scope = get_class($scope);
+        }
+
+        unset($this->scopes[$scope]);
+
+        $this->removedScopes[] = $scope;
+
+        return $this;
+    }
+
+    /**
+     * Remove all or passed registered global scopes.
+     *
+     * @param  array|null  $scopes
+     * @return $this
+     */
+    public function withoutGlobalScopes(array $scopes = null)
+    {
+        if (! is_array($scopes)) {
+            $scopes = array_keys($this->scopes);
+        }
+
+        foreach ($scopes as $scope) {
+            $this->withoutGlobalScope($scope);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get an array of global scopes that were removed from the query.
+     *
+     * @return array
+     */
+    public function removedScopes()
+    {
+        return $this->removedScopes;
+    }
+
+    /**
+     * Add a where clause on the primary key to the query.
+     *
+     * @param  mixed  $id
+     * @return $this
+     */
+    public function whereKey($id)
+    {
+        if ($id instanceof Model) {
+            $id = $id->getKey();
+        }
+
+        if (is_array($id) || $id instanceof Arrayable) {
+            if (in_array($this->model->getKeyType(), ['int', 'integer'])) {
+                $this->query->whereIntegerInRaw($this->model->getQualifiedKeyName(), $id);
+            } else {
+                $this->query->whereIn($this->model->getQualifiedKeyName(), $id);
+            }
+
+            return $this;
+        }
+
+        if ($id !== null && $this->model->getKeyType() === 'string') {
+            $id = (string) $id;
+        }
+
+        return $this->where($this->model->getQualifiedKeyName(), '=', $id);
+    }
+
+    /**
+     * Add a where clause on the primary key to the query.
+     *
+     * @param  mixed  $id
+     * @return $this
+     */
+    public function whereKeyNot($id)
+    {
+        if ($id instanceof Model) {
+            $id = $id->getKey();
+        }
+
+        if (is_array($id) || $id instanceof Arrayable) {
+            if (in_array($this->model->getKeyType(), ['int', 'integer'])) {
+                $this->query->whereIntegerNotInRaw($this->model->getQualifiedKeyName(), $id);
+            } else {
+                $this->query->whereNotIn($this->model->getQualifiedKeyName(), $id);
+            }
+
+            return $this;
+        }
+
+        if ($id !== null && $this->model->getKeyType() === 'string') {
+            $id = (string) $id;
+        }
+
+        return $this->where($this->model->getQualifiedKeyName(), '!=', $id);
+    }
+
+    /**
+     * Add a basic where clause to the query.
+     *
+     * @param  \Closure|string|array|\Illuminate\Database\Query\Expression  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function where($column, $operator = null, $value = null, $boolean = 'and')
+    {
+        if ($column instanceof Closure && is_null($operator)) {
+            $column($query = $this->model->newQueryWithoutRelationships());
+
+            $this->query->addNestedWhereQuery($query->getQuery(), $boolean);
+        } else {
+            $this->query->where(...func_get_args());
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a basic where clause to the query, and return the first result.
+     *
+     * @param  \Closure|string|array|\Illuminate\Database\Query\Expression  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @param  string  $boolean
+     * @return \Illuminate\Database\Eloquent\Model|static|null
+     */
+    public function firstWhere($column, $operator = null, $value = null, $boolean = 'and')
+    {
+        return $this->where(...func_get_args())->first();
+    }
+
+    /**
+     * Add an "or where" clause to the query.
+     *
+     * @param  \Closure|array|string|\Illuminate\Database\Query\Expression  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function orWhere($column, $operator = null, $value = null)
+    {
+        [$value, $operator] = $this->query->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        return $this->where($column, $operator, $value, 'or');
+    }
+
+    /**
+     * Add a basic "where not" clause to the query.
+     *
+     * @param  \Closure|string|array|\Illuminate\Database\Query\Expression  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereNot($column, $operator = null, $value = null, $boolean = 'and')
+    {
+        return $this->where($column, $operator, $value, $boolean.' not');
+    }
+
+    /**
+     * Add an "or where not" clause to the query.
+     *
+     * @param  \Closure|array|string|\Illuminate\Database\Query\Expression  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function orWhereNot($column, $operator = null, $value = null)
+    {
+        return $this->whereNot($column, $operator, $value, 'or');
+    }
+
+    /**
+     * Add an "order by" clause for a timestamp to the query.
+     *
+     * @param  string|\Illuminate\Database\Query\Expression  $column
+     * @return $this
+     */
+    public function latest($column = null)
+    {
+        if (is_null($column)) {
+            $column = $this->model->getCreatedAtColumn() ?? 'created_at';
+        }
+
+        $this->query->latest($column);
+
+        return $this;
+    }
+
+    /**
+     * Add an "order by" clause for a timestamp to the query.
+     *
+     * @param  string|\Illuminate\Database\Query\Expression  $column
+     * @return $this
+     */
+    public function oldest($column = null)
+    {
+        if (is_null($column)) {
+            $column = $this->model->getCreatedAtColumn() ?? 'created_at';
+        }
+
+        $this->query->oldest($column);
+
+        return $this;
+    }
+
+    /**
+     * Create a collection of models from plain arrays.
+     *
+     * @param  array  $items
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function hydrate(array $items)
+    {
+        $instance = $this->newModelInstance();
+
+        return $instance->newCollection(array_map(function ($item) use ($items, $instance) {
+            $model = $instance->newFromBuilder($item);
+
+            if (count($items) > 1) {
+                $model->preventsLazyLoading = Model::preventsLazyLoading();
+            }
+
+            return $model;
+        }, $items));
+    }
+
+    /**
+     * Create a collection of models from a raw query.
+     *
+     * @param  string  $query
+     * @param  array  $bindings
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function fromQuery($query, $bindings = [])
+    {
+        return $this->hydrate(
+            $this->query->getConnection()->select($query, $bindings)
+        );
+    }
+
+    /**
+     * Find a model by its primary key.
+     *
+     * @param  mixed  $id
+     * @param  array|string  $columns
+     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|null
+     */
+    public function find($id, $columns = ['*'])
+    {
+        if (is_array($id) || $id instanceof Arrayable) {
+            return $this->findMany($id, $columns);
+        }
+
+        return $this->whereKey($id)->first($columns);
+    }
+
+    /**
+     * Find multiple models by their primary keys.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|array  $ids
+     * @param  array|string  $columns
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function findMany($ids, $columns = ['*'])
+    {
+        $ids = $ids instanceof Arrayable ? $ids->toArray() : $ids;
+
+        if (empty($ids)) {
+            return $this->model->newCollection();
+        }
+
+        return $this->whereKey($ids)->get($columns);
+    }
+
+    /**
+     * Find a model by its primary key or throw an exception.
+     *
+     * @param  mixed  $id
+     * @param  array|string  $columns
+     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static|static[]
+     *
+     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
+     */
+    public function findOrFail($id, $columns = ['*'])
+    {
+        $result = $this->find($id, $columns);
+
+        $id = $id instanceof Arrayable ? $id->toArray() : $id;
+
+        if (is_array($id)) {
+            if (count($result) !== count(array_unique($id))) {
+                throw (new ModelNotFoundException)->setModel(
+                    get_class($this->model), array_diff($id, $result->modelKeys())
+                );
+            }
+
+            return $result;
+        }
+
+        if (is_null($result)) {
+            throw (new ModelNotFoundException)->setModel(
+                get_class($this->model), $id
+            );
+        }
+
+        return $result;
+    }
+
+    /**
+     * Find a model by its primary key or return fresh model instance.
+     *
+     * @param  mixed  $id
+     * @param  array|string  $columns
+     * @return \Illuminate\Database\Eloquent\Model|static
+     */
+    public function findOrNew($id, $columns = ['*'])
+    {
+        if (! is_null($model = $this->find($id, $columns))) {
+            return $model;
+        }
+
+        return $this->newModelInstance();
+    }
+
+    /**
+     * Find a model by its primary key or call a callback.
+     *
+     * @param  mixed  $id
+     * @param  \Closure|array|string  $columns
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|mixed
+     */
+    public function findOr($id, $columns = ['*'], Closure $callback = null)
+    {
+        if ($columns instanceof Closure) {
+            $callback = $columns;
+
+            $columns = ['*'];
+        }
+
+        if (! is_null($model = $this->find($id, $columns))) {
+            return $model;
+        }
+
+        return $callback();
+    }
+
+    /**
+     * Get the first record matching the attributes or instantiate it.
+     *
+     * @param  array  $attributes
+     * @param  array  $values
+     * @return \Illuminate\Database\Eloquent\Model|static
+     */
+    public function firstOrNew(array $attributes = [], array $values = [])
+    {
+        if (! is_null($instance = $this->where($attributes)->first())) {
+            return $instance;
+        }
+
+        return $this->newModelInstance(array_merge($attributes, $values));
+    }
+
+    /**
+     * Get the first record matching the attributes or create it.
+     *
+     * @param  array  $attributes
+     * @param  array  $values
+     * @return \Illuminate\Database\Eloquent\Model|static
+     */
+    public function firstOrCreate(array $attributes = [], array $values = [])
+    {
+        if (! is_null($instance = $this->where($attributes)->first())) {
+            return $instance;
+        }
+
+        return tap($this->newModelInstance(array_merge($attributes, $values)), function ($instance) {
+            $instance->save();
+        });
+    }
+
+    /**
+     * Create or update a record matching the attributes, and fill it with values.
+     *
+     * @param  array  $attributes
+     * @param  array  $values
+     * @return \Illuminate\Database\Eloquent\Model|static
+     */
+    public function updateOrCreate(array $attributes, array $values = [])
+    {
+        return tap($this->firstOrNew($attributes), function ($instance) use ($values) {
+            $instance->fill($values)->save();
+        });
+    }
+
+    /**
+     * Execute the query and get the first result or throw an exception.
+     *
+     * @param  array|string  $columns
+     * @return \Illuminate\Database\Eloquent\Model|static
+     *
+     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
+     */
+    public function firstOrFail($columns = ['*'])
+    {
+        if (! is_null($model = $this->first($columns))) {
+            return $model;
+        }
+
+        throw (new ModelNotFoundException)->setModel(get_class($this->model));
+    }
+
+    /**
+     * Execute the query and get the first result or call a callback.
+     *
+     * @param  \Closure|array|string  $columns
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Model|static|mixed
+     */
+    public function firstOr($columns = ['*'], Closure $callback = null)
+    {
+        if ($columns instanceof Closure) {
+            $callback = $columns;
+
+            $columns = ['*'];
+        }
+
+        if (! is_null($model = $this->first($columns))) {
+            return $model;
+        }
+
+        return $callback();
+    }
+
+    /**
+     * Execute the query and get the first result if it's the sole matching record.
+     *
+     * @param  array|string  $columns
+     * @return \Illuminate\Database\Eloquent\Model
+     *
+     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
+     * @throws \Illuminate\Database\MultipleRecordsFoundException
+     */
+    public function sole($columns = ['*'])
+    {
+        try {
+            return $this->baseSole($columns);
+        } catch (RecordsNotFoundException $exception) {
+            throw (new ModelNotFoundException)->setModel(get_class($this->model));
+        }
+    }
+
+    /**
+     * Get a single column's value from the first result of a query.
+     *
+     * @param  string|\Illuminate\Database\Query\Expression  $column
+     * @return mixed
+     */
+    public function value($column)
+    {
+        if ($result = $this->first([$column])) {
+            return $result->{Str::afterLast($column, '.')};
+        }
+    }
+
+    /**
+     * Get a single column's value from the first result of a query if it's the sole matching record.
+     *
+     * @param  string|\Illuminate\Database\Query\Expression  $column
+     * @return mixed
+     *
+     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
+     * @throws \Illuminate\Database\MultipleRecordsFoundException
+     */
+    public function soleValue($column)
+    {
+        return $this->sole([$column])->{Str::afterLast($column, '.')};
+    }
+
+    /**
+     * Get a single column's value from the first result of the query or throw an exception.
+     *
+     * @param  string|\Illuminate\Database\Query\Expression  $column
+     * @return mixed
+     *
+     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
+     */
+    public function valueOrFail($column)
+    {
+        return $this->firstOrFail([$column])->{Str::afterLast($column, '.')};
+    }
+
+    /**
+     * Execute the query as a "select" statement.
+     *
+     * @param  array|string  $columns
+     * @return \Illuminate\Database\Eloquent\Collection|static[]
+     */
+    public function get($columns = ['*'])
+    {
+        $builder = $this->applyScopes();
+
+        // If we actually found models we will also eager load any relationships that
+        // have been specified as needing to be eager loaded, which will solve the
+        // n+1 query issue for the developers to avoid running a lot of queries.
+        if (count($models = $builder->getModels($columns)) > 0) {
+            $models = $builder->eagerLoadRelations($models);
+        }
+
+        return $builder->getModel()->newCollection($models);
+    }
+
+    /**
+     * Get the hydrated models without eager loading.
+     *
+     * @param  array|string  $columns
+     * @return \Illuminate\Database\Eloquent\Model[]|static[]
+     */
+    public function getModels($columns = ['*'])
+    {
+        return $this->model->hydrate(
+            $this->query->get($columns)->all()
+        )->all();
+    }
+
+    /**
+     * Eager load the relationships for the models.
+     *
+     * @param  array  $models
+     * @return array
+     */
+    public function eagerLoadRelations(array $models)
+    {
+        foreach ($this->eagerLoad as $name => $constraints) {
+            // For nested eager loads we'll skip loading them here and they will be set as an
+            // eager load on the query to retrieve the relation so that they will be eager
+            // loaded on that query, because that is where they get hydrated as models.
+            if (! str_contains($name, '.')) {
+                $models = $this->eagerLoadRelation($models, $name, $constraints);
+            }
+        }
+
+        return $models;
+    }
+
+    /**
+     * Eagerly load the relationship on a set of models.
+     *
+     * @param  array  $models
+     * @param  string  $name
+     * @param  \Closure  $constraints
+     * @return array
+     */
+    protected function eagerLoadRelation(array $models, $name, Closure $constraints)
+    {
+        // First we will "back up" the existing where conditions on the query so we can
+        // add our eager constraints. Then we will merge the wheres that were on the
+        // query back to it in order that any where conditions might be specified.
+        $relation = $this->getRelation($name);
+
+        $relation->addEagerConstraints($models);
+
+        $constraints($relation);
+
+        // Once we have the results, we just match those back up to their parent models
+        // using the relationship instance. Then we just return the finished arrays
+        // of models which have been eagerly hydrated and are readied for return.
+        return $relation->match(
+            $relation->initRelation($models, $name),
+            $relation->getEager(), $name
+        );
+    }
+
+    /**
+     * Get the relation instance for the given relation name.
+     *
+     * @param  string  $name
+     * @return \Illuminate\Database\Eloquent\Relations\Relation
+     */
+    public function getRelation($name)
+    {
+        // We want to run a relationship query without any constrains so that we will
+        // not have to remove these where clauses manually which gets really hacky
+        // and error prone. We don't want constraints because we add eager ones.
+        $relation = Relation::noConstraints(function () use ($name) {
+            try {
+                return $this->getModel()->newInstance()->$name();
+            } catch (BadMethodCallException $e) {
+                throw RelationNotFoundException::make($this->getModel(), $name);
+            }
+        });
+
+        $nested = $this->relationsNestedUnder($name);
+
+        // If there are nested relationships set on the query, we will put those onto
+        // the query instances so that they can be handled after this relationship
+        // is loaded. In this way they will all trickle down as they are loaded.
+        if (count($nested) > 0) {
+            $relation->getQuery()->with($nested);
+        }
+
+        return $relation;
+    }
+
+    /**
+     * Get the deeply nested relations for a given top-level relation.
+     *
+     * @param  string  $relation
+     * @return array
+     */
+    protected function relationsNestedUnder($relation)
+    {
+        $nested = [];
+
+        // We are basically looking for any relationships that are nested deeper than
+        // the given top-level relationship. We will just check for any relations
+        // that start with the given top relations and adds them to our arrays.
+        foreach ($this->eagerLoad as $name => $constraints) {
+            if ($this->isNestedUnder($relation, $name)) {
+                $nested[substr($name, strlen($relation.'.'))] = $constraints;
+            }
+        }
+
+        return $nested;
+    }
+
+    /**
+     * Determine if the relationship is nested.
+     *
+     * @param  string  $relation
+     * @param  string  $name
+     * @return bool
+     */
+    protected function isNestedUnder($relation, $name)
+    {
+        return str_contains($name, '.') && str_starts_with($name, $relation.'.');
+    }
+
+    /**
+     * Get a lazy collection for the given query.
+     *
+     * @return \Illuminate\Support\LazyCollection
+     */
+    public function cursor()
+    {
+        return $this->applyScopes()->query->cursor()->map(function ($record) {
+            return $this->newModelInstance()->newFromBuilder($record);
+        });
+    }
+
+    /**
+     * Add a generic "order by" clause if the query doesn't already have one.
+     *
+     * @return void
+     */
+    protected function enforceOrderBy()
+    {
+        if (empty($this->query->orders) && empty($this->query->unionOrders)) {
+            $this->orderBy($this->model->getQualifiedKeyName(), 'asc');
+        }
+    }
+
+    /**
+     * Get a collection with the values of a given column.
+     *
+     * @param  string|\Illuminate\Database\Query\Expression  $column
+     * @param  string|null  $key
+     * @return \Illuminate\Support\Collection
+     */
+    public function pluck($column, $key = null)
+    {
+        $results = $this->toBase()->pluck($column, $key);
+
+        // If the model has a mutator for the requested column, we will spin through
+        // the results and mutate the values so that the mutated version of these
+        // columns are returned as you would expect from these Eloquent models.
+        if (! $this->model->hasGetMutator($column) &&
+            ! $this->model->hasCast($column) &&
+            ! in_array($column, $this->model->getDates())) {
+            return $results;
+        }
+
+        return $results->map(function ($value) use ($column) {
+            return $this->model->newFromBuilder([$column => $value])->{$column};
+        });
+    }
+
+    /**
+     * Paginate the given query.
+     *
+     * @param  int|null|\Closure  $perPage
+     * @param  array|string  $columns
+     * @param  string  $pageName
+     * @param  int|null  $page
+     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
+    {
+        $page = $page ?: Paginator::resolveCurrentPage($pageName);
+
+        $total = $this->toBase()->getCountForPagination();
+
+        $perPage = ($perPage instanceof Closure
+            ? $perPage($total)
+            : $perPage
+        ) ?: $this->model->getPerPage();
+
+        $results = $total
+            ? $this->forPage($page, $perPage)->get($columns)
+            : $this->model->newCollection();
+
+        return $this->paginator($results, $total, $perPage, $page, [
+            'path' => Paginator::resolveCurrentPath(),
+            'pageName' => $pageName,
+        ]);
+    }
+
+    /**
+     * Paginate the given query into a simple paginator.
+     *
+     * @param  int|null  $perPage
+     * @param  array|string  $columns
+     * @param  string  $pageName
+     * @param  int|null  $page
+     * @return \Illuminate\Contracts\Pagination\Paginator
+     */
+    public function simplePaginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
+    {
+        $page = $page ?: Paginator::resolveCurrentPage($pageName);
+
+        $perPage = $perPage ?: $this->model->getPerPage();
+
+        // Next we will set the limit and offset for this query so that when we get the
+        // results we get the proper section of results. Then, we'll create the full
+        // paginator instances for these results with the given page and per page.
+        $this->skip(($page - 1) * $perPage)->take($perPage + 1);
+
+        return $this->simplePaginator($this->get($columns), $perPage, $page, [
+            'path' => Paginator::resolveCurrentPath(),
+            'pageName' => $pageName,
+        ]);
+    }
+
+    /**
+     * Paginate the given query into a cursor paginator.
+     *
+     * @param  int|null  $perPage
+     * @param  array|string  $columns
+     * @param  string  $cursorName
+     * @param  \Illuminate\Pagination\Cursor|string|null  $cursor
+     * @return \Illuminate\Contracts\Pagination\CursorPaginator
+     */
+    public function cursorPaginate($perPage = null, $columns = ['*'], $cursorName = 'cursor', $cursor = null)
+    {
+        $perPage = $perPage ?: $this->model->getPerPage();
+
+        return $this->paginateUsingCursor($perPage, $columns, $cursorName, $cursor);
+    }
+
+    /**
+     * Ensure the proper order by required for cursor pagination.
+     *
+     * @param  bool  $shouldReverse
+     * @return \Illuminate\Support\Collection
+     */
+    protected function ensureOrderForCursorPagination($shouldReverse = false)
+    {
+        if (empty($this->query->orders) && empty($this->query->unionOrders)) {
+            $this->enforceOrderBy();
+        }
+
+        if ($shouldReverse) {
+            $this->query->orders = collect($this->query->orders)->map(function ($order) {
+                $order['direction'] = $order['direction'] === 'asc' ? 'desc' : 'asc';
+
+                return $order;
+            })->toArray();
+        }
+
+        if ($this->query->unionOrders) {
+            return collect($this->query->unionOrders);
+        }
+
+        return collect($this->query->orders);
+    }
+
+    /**
+     * Save a new model and return the instance.
+     *
+     * @param  array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model|$this
+     */
+    public function create(array $attributes = [])
+    {
+        return tap($this->newModelInstance($attributes), function ($instance) {
+            $instance->save();
+        });
+    }
+
+    /**
+     * Save a new model and return the instance. Allow mass-assignment.
+     *
+     * @param  array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model|$this
+     */
+    public function forceCreate(array $attributes)
+    {
+        return $this->model->unguarded(function () use ($attributes) {
+            return $this->newModelInstance()->create($attributes);
+        });
+    }
+
+    /**
+     * Update records in the database.
+     *
+     * @param  array  $values
+     * @return int
+     */
+    public function update(array $values)
+    {
+        return $this->toBase()->update($this->addUpdatedAtColumn($values));
+    }
+
+    /**
+     * Insert new records or update the existing ones.
+     *
+     * @param  array  $values
+     * @param  array|string  $uniqueBy
+     * @param  array|null  $update
+     * @return int
+     */
+    public function upsert(array $values, $uniqueBy, $update = null)
+    {
+        if (empty($values)) {
+            return 0;
+        }
+
+        if (! is_array(reset($values))) {
+            $values = [$values];
+        }
+
+        if (is_null($update)) {
+            $update = array_keys(reset($values));
+        }
+
+        return $this->toBase()->upsert(
+            $this->addTimestampsToUpsertValues($values),
+            $uniqueBy,
+            $this->addUpdatedAtToUpsertColumns($update)
+        );
+    }
+
+    /**
+     * Update the column's update timestamp.
+     *
+     * @param  string|null  $column
+     * @return int|false
+     */
+    public function touch($column = null)
+    {
+        $time = $this->model->freshTimestamp();
+
+        if ($column) {
+            return $this->toBase()->update([$column => $time]);
+        }
+
+        $column = $this->model->getUpdatedAtColumn();
+
+        if (! $this->model->usesTimestamps() || is_null($column)) {
+            return false;
+        }
+
+        return $this->toBase()->update([$column => $time]);
+    }
+
+    /**
+     * Increment a column's value by a given amount.
+     *
+     * @param  string|\Illuminate\Database\Query\Expression  $column
+     * @param  float|int  $amount
+     * @param  array  $extra
+     * @return int
+     */
+    public function increment($column, $amount = 1, array $extra = [])
+    {
+        return $this->toBase()->increment(
+            $column, $amount, $this->addUpdatedAtColumn($extra)
+        );
+    }
+
+    /**
+     * Decrement a column's value by a given amount.
+     *
+     * @param  string|\Illuminate\Database\Query\Expression  $column
+     * @param  float|int  $amount
+     * @param  array  $extra
+     * @return int
+     */
+    public function decrement($column, $amount = 1, array $extra = [])
+    {
+        return $this->toBase()->decrement(
+            $column, $amount, $this->addUpdatedAtColumn($extra)
+        );
+    }
+
+    /**
+     * Add the "updated at" column to an array of values.
+     *
+     * @param  array  $values
+     * @return array
+     */
+    protected function addUpdatedAtColumn(array $values)
+    {
+        if (! $this->model->usesTimestamps() ||
+            is_null($this->model->getUpdatedAtColumn())) {
+            return $values;
+        }
+
+        $column = $this->model->getUpdatedAtColumn();
+
+        $values = array_merge(
+            [$column => $this->model->freshTimestampString()],
+            $values
+        );
+
+        $segments = preg_split('/\s+as\s+/i', $this->query->from);
+
+        $qualifiedColumn = end($segments).'.'.$column;
+
+        $values[$qualifiedColumn] = Arr::get($values, $qualifiedColumn, $values[$column]);
+
+        unset($values[$column]);
+
+        return $values;
+    }
+
+    /**
+     * Add timestamps to the inserted values.
+     *
+     * @param  array  $values
+     * @return array
+     */
+    protected function addTimestampsToUpsertValues(array $values)
+    {
+        if (! $this->model->usesTimestamps()) {
+            return $values;
+        }
+
+        $timestamp = $this->model->freshTimestampString();
+
+        $columns = array_filter([
+            $this->model->getCreatedAtColumn(),
+            $this->model->getUpdatedAtColumn(),
+        ]);
+
+        foreach ($columns as $column) {
+            foreach ($values as &$row) {
+                $row = array_merge([$column => $timestamp], $row);
+            }
+        }
+
+        return $values;
+    }
+
+    /**
+     * Add the "updated at" column to the updated columns.
+     *
+     * @param  array  $update
+     * @return array
+     */
+    protected function addUpdatedAtToUpsertColumns(array $update)
+    {
+        if (! $this->model->usesTimestamps()) {
+            return $update;
+        }
+
+        $column = $this->model->getUpdatedAtColumn();
+
+        if (! is_null($column) &&
+            ! array_key_exists($column, $update) &&
+            ! in_array($column, $update)) {
+            $update[] = $column;
+        }
+
+        return $update;
+    }
+
+    /**
+     * Delete records from the database.
+     *
+     * @return mixed
+     */
+    public function delete()
+    {
+        if (isset($this->onDelete)) {
+            return call_user_func($this->onDelete, $this);
+        }
+
+        return $this->toBase()->delete();
+    }
+
+    /**
+     * Run the default delete function on the builder.
+     *
+     * Since we do not apply scopes here, the row will actually be deleted.
+     *
+     * @return mixed
+     */
+    public function forceDelete()
+    {
+        return $this->query->delete();
+    }
+
+    /**
+     * Register a replacement for the default delete function.
+     *
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function onDelete(Closure $callback)
+    {
+        $this->onDelete = $callback;
+    }
+
+    /**
+     * Determine if the given model has a scope.
+     *
+     * @param  string  $scope
+     * @return bool
+     */
+    public function hasNamedScope($scope)
+    {
+        return $this->model && $this->model->hasNamedScope($scope);
+    }
+
+    /**
+     * Call the given local model scopes.
+     *
+     * @param  array|string  $scopes
+     * @return static|mixed
+     */
+    public function scopes($scopes)
+    {
+        $builder = $this;
+
+        foreach (Arr::wrap($scopes) as $scope => $parameters) {
+            // If the scope key is an integer, then the scope was passed as the value and
+            // the parameter list is empty, so we will format the scope name and these
+            // parameters here. Then, we'll be ready to call the scope on the model.
+            if (is_int($scope)) {
+                [$scope, $parameters] = [$parameters, []];
+            }
+
+            // Next we'll pass the scope callback to the callScope method which will take
+            // care of grouping the "wheres" properly so the logical order doesn't get
+            // messed up when adding scopes. Then we'll return back out the builder.
+            $builder = $builder->callNamedScope(
+                $scope, Arr::wrap($parameters)
+            );
+        }
+
+        return $builder;
+    }
+
+    /**
+     * Apply the scopes to the Eloquent builder instance and return it.
+     *
+     * @return static
+     */
+    public function applyScopes()
+    {
+        if (! $this->scopes) {
+            return $this;
+        }
+
+        $builder = clone $this;
+
+        foreach ($this->scopes as $identifier => $scope) {
+            if (! isset($builder->scopes[$identifier])) {
+                continue;
+            }
+
+            $builder->callScope(function (self $builder) use ($scope) {
+                // If the scope is a Closure we will just go ahead and call the scope with the
+                // builder instance. The "callScope" method will properly group the clauses
+                // that are added to this query so "where" clauses maintain proper logic.
+                if ($scope instanceof Closure) {
+                    $scope($builder);
+                }
+
+                // If the scope is a scope object, we will call the apply method on this scope
+                // passing in the builder and the model instance. After we run all of these
+                // scopes we will return back the builder instance to the outside caller.
+                if ($scope instanceof Scope) {
+                    $scope->apply($builder, $this->getModel());
+                }
+            });
+        }
+
+        return $builder;
+    }
+
+    /**
+     * Apply the given scope on the current builder instance.
+     *
+     * @param  callable  $scope
+     * @param  array  $parameters
+     * @return mixed
+     */
+    protected function callScope(callable $scope, array $parameters = [])
+    {
+        array_unshift($parameters, $this);
+
+        $query = $this->getQuery();
+
+        // We will keep track of how many wheres are on the query before running the
+        // scope so that we can properly group the added scope constraints in the
+        // query as their own isolated nested where statement and avoid issues.
+        $originalWhereCount = is_null($query->wheres)
+                    ? 0 : count($query->wheres);
+
+        $result = $scope(...$parameters) ?? $this;
+
+        if (count((array) $query->wheres) > $originalWhereCount) {
+            $this->addNewWheresWithinGroup($query, $originalWhereCount);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Apply the given named scope on the current builder instance.
+     *
+     * @param  string  $scope
+     * @param  array  $parameters
+     * @return mixed
+     */
+    protected function callNamedScope($scope, array $parameters = [])
+    {
+        return $this->callScope(function (...$parameters) use ($scope) {
+            return $this->model->callNamedScope($scope, $parameters);
+        }, $parameters);
+    }
+
+    /**
+     * Nest where conditions by slicing them at the given where count.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  int  $originalWhereCount
+     * @return void
+     */
+    protected function addNewWheresWithinGroup(QueryBuilder $query, $originalWhereCount)
+    {
+        // Here, we totally remove all of the where clauses since we are going to
+        // rebuild them as nested queries by slicing the groups of wheres into
+        // their own sections. This is to prevent any confusing logic order.
+        $allWheres = $query->wheres;
+
+        $query->wheres = [];
+
+        $this->groupWhereSliceForScope(
+            $query, array_slice($allWheres, 0, $originalWhereCount)
+        );
+
+        $this->groupWhereSliceForScope(
+            $query, array_slice($allWheres, $originalWhereCount)
+        );
+    }
+
+    /**
+     * Slice where conditions at the given offset and add them to the query as a nested condition.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $whereSlice
+     * @return void
+     */
+    protected function groupWhereSliceForScope(QueryBuilder $query, $whereSlice)
+    {
+        $whereBooleans = collect($whereSlice)->pluck('boolean');
+
+        // Here we'll check if the given subset of where clauses contains any "or"
+        // booleans and in this case create a nested where expression. That way
+        // we don't add any unnecessary nesting thus keeping the query clean.
+        if ($whereBooleans->contains('or')) {
+            $query->wheres[] = $this->createNestedWhere(
+                $whereSlice, $whereBooleans->first()
+            );
+        } else {
+            $query->wheres = array_merge($query->wheres, $whereSlice);
+        }
+    }
+
+    /**
+     * Create a where array with nested where conditions.
+     *
+     * @param  array  $whereSlice
+     * @param  string  $boolean
+     * @return array
+     */
+    protected function createNestedWhere($whereSlice, $boolean = 'and')
+    {
+        $whereGroup = $this->getQuery()->forNestedWhere();
+
+        $whereGroup->wheres = $whereSlice;
+
+        return ['type' => 'Nested', 'query' => $whereGroup, 'boolean' => $boolean];
+    }
+
+    /**
+     * Set the relationships that should be eager loaded.
+     *
+     * @param  string|array  $relations
+     * @param  string|\Closure|null  $callback
+     * @return $this
+     */
+    public function with($relations, $callback = null)
+    {
+        if ($callback instanceof Closure) {
+            $eagerLoad = $this->parseWithRelations([$relations => $callback]);
+        } else {
+            $eagerLoad = $this->parseWithRelations(is_string($relations) ? func_get_args() : $relations);
+        }
+
+        $this->eagerLoad = array_merge($this->eagerLoad, $eagerLoad);
+
+        return $this;
+    }
+
+    /**
+     * Prevent the specified relations from being eager loaded.
+     *
+     * @param  mixed  $relations
+     * @return $this
+     */
+    public function without($relations)
+    {
+        $this->eagerLoad = array_diff_key($this->eagerLoad, array_flip(
+            is_string($relations) ? func_get_args() : $relations
+        ));
+
+        return $this;
+    }
+
+    /**
+     * Set the relationships that should be eager loaded while removing any previously added eager loading specifications.
+     *
+     * @param  mixed  $relations
+     * @return $this
+     */
+    public function withOnly($relations)
+    {
+        $this->eagerLoad = [];
+
+        return $this->with($relations);
+    }
+
+    /**
+     * Create a new instance of the model being queried.
+     *
+     * @param  array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model|static
+     */
+    public function newModelInstance($attributes = [])
+    {
+        return $this->model->newInstance($attributes)->setConnection(
+            $this->query->getConnection()->getName()
+        );
+    }
+
+    /**
+     * Parse a list of relations into individuals.
+     *
+     * @param  array  $relations
+     * @return array
+     */
+    protected function parseWithRelations(array $relations)
+    {
+        if ($relations === []) {
+            return [];
+        }
+
+        $results = [];
+
+        foreach ($this->prepareNestedWithRelationships($relations) as $name => $constraints) {
+            // We need to separate out any nested includes, which allows the developers
+            // to load deep relationships using "dots" without stating each level of
+            // the relationship with its own key in the array of eager-load names.
+            $results = $this->addNestedWiths($name, $results);
+
+            $results[$name] = $constraints;
+        }
+
+        return $results;
+    }
+
+    /**
+     * Prepare nested with relationships.
+     *
+     * @param  array  $relations
+     * @param  string  $prefix
+     * @return array
+     */
+    protected function prepareNestedWithRelationships($relations, $prefix = '')
+    {
+        $preparedRelationships = [];
+
+        if ($prefix !== '') {
+            $prefix .= '.';
+        }
+
+        // If any of the relationships are formatted with the [$attribute => array()]
+        // syntax, we shall loop over the nested relations and prepend each key of
+        // this array while flattening into the traditional dot notation format.
+        foreach ($relations as $key => $value) {
+            if (! is_string($key) || ! is_array($value)) {
+                continue;
+            }
+
+            [$attribute, $attributeSelectConstraint] = $this->parseNameAndAttributeSelectionConstraint($key);
+
+            $preparedRelationships = array_merge(
+                $preparedRelationships,
+                ["{$prefix}{$attribute}" => $attributeSelectConstraint],
+                $this->prepareNestedWithRelationships($value, "{$prefix}{$attribute}"),
+            );
+
+            unset($relations[$key]);
+        }
+
+        // We now know that the remaining relationships are in a dot notation format
+        // and may be a string or Closure. We'll loop over them and ensure all of
+        // the present Closures are merged + strings are made into constraints.
+        foreach ($relations as $key => $value) {
+            if (is_numeric($key) && is_string($value)) {
+                [$key, $value] = $this->parseNameAndAttributeSelectionConstraint($value);
+            }
+
+            $preparedRelationships[$prefix.$key] = $this->combineConstraints([
+                $value,
+                $preparedRelationships[$prefix.$key] ?? static function () {
+                    //
+                },
+            ]);
+        }
+
+        return $preparedRelationships;
+    }
+
+    /**
+     * Combine an array of constraints into a single constraint.
+     *
+     * @param  array  $constraints
+     * @return \Closure
+     */
+    protected function combineConstraints(array $constraints)
+    {
+        return function ($builder) use ($constraints) {
+            foreach ($constraints as $constraint) {
+                $builder = $constraint($builder) ?? $builder;
+            }
+
+            return $builder;
+        };
+    }
+
+    /**
+     * Parse the attribute select constraints from the name.
+     *
+     * @param  string  $name
+     * @return array
+     */
+    protected function parseNameAndAttributeSelectionConstraint($name)
+    {
+        return str_contains($name, ':')
+            ? $this->createSelectWithConstraint($name)
+            : [$name, static function () {
+                //
+            }];
+    }
+
+    /**
+     * Create a constraint to select the given columns for the relation.
+     *
+     * @param  string  $name
+     * @return array
+     */
+    protected function createSelectWithConstraint($name)
+    {
+        return [explode(':', $name)[0], static function ($query) use ($name) {
+            $query->select(array_map(static function ($column) use ($query) {
+                if (str_contains($column, '.')) {
+                    return $column;
+                }
+
+                return $query instanceof BelongsToMany
+                        ? $query->getRelated()->getTable().'.'.$column
+                        : $column;
+            }, explode(',', explode(':', $name)[1])));
+        }];
+    }
+
+    /**
+     * Parse the nested relationships in a relation.
+     *
+     * @param  string  $name
+     * @param  array  $results
+     * @return array
+     */
+    protected function addNestedWiths($name, $results)
+    {
+        $progress = [];
+
+        // If the relation has already been set on the result array, we will not set it
+        // again, since that would override any constraints that were already placed
+        // on the relationships. We will only set the ones that are not specified.
+        foreach (explode('.', $name) as $segment) {
+            $progress[] = $segment;
+
+            if (! isset($results[$last = implode('.', $progress)])) {
+                $results[$last] = static function () {
+                    //
+                };
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Apply query-time casts to the model instance.
+     *
+     * @param  array  $casts
+     * @return $this
+     */
+    public function withCasts($casts)
+    {
+        $this->model->mergeCasts($casts);
+
+        return $this;
+    }
+
+    /**
+     * Get the underlying query builder instance.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function getQuery()
+    {
+        return $this->query;
+    }
+
+    /**
+     * Set the underlying query builder instance.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return $this
+     */
+    public function setQuery($query)
+    {
+        $this->query = $query;
+
+        return $this;
+    }
+
+    /**
+     * Get a base query builder instance.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function toBase()
+    {
+        return $this->applyScopes()->getQuery();
+    }
+
+    /**
+     * Get the relationships being eagerly loaded.
+     *
+     * @return array
+     */
+    public function getEagerLoads()
+    {
+        return $this->eagerLoad;
+    }
+
+    /**
+     * Set the relationships being eagerly loaded.
+     *
+     * @param  array  $eagerLoad
+     * @return $this
+     */
+    public function setEagerLoads(array $eagerLoad)
+    {
+        $this->eagerLoad = $eagerLoad;
+
+        return $this;
+    }
+
+    /**
+     * Indicate that the given relationships should not be eagerly loaded.
+     *
+     * @param  array  $relations
+     * @return $this
+     */
+    public function withoutEagerLoad(array $relations)
+    {
+        $relations = array_diff(array_keys($this->model->getRelations()), $relations);
+
+        return $this->with($relations);
+    }
+
+    /**
+     * Flush the relationships being eagerly loaded.
+     *
+     * @return $this
+     */
+    public function withoutEagerLoads()
+    {
+        return $this->setEagerLoads([]);
+    }
+
+    /**
+     * Get the default key name of the table.
+     *
+     * @return string
+     */
+    protected function defaultKeyName()
+    {
+        return $this->getModel()->getKeyName();
+    }
+
+    /**
+     * Get the model instance being queried.
+     *
+     * @return \Illuminate\Database\Eloquent\Model|static
+     */
+    public function getModel()
+    {
+        return $this->model;
+    }
+
+    /**
+     * Set a model instance for the model being queried.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return $this
+     */
+    public function setModel(Model $model)
+    {
+        $this->model = $model;
+
+        $this->query->from($model->getTable());
+
+        return $this;
+    }
+
+    /**
+     * Qualify the given column name by the model's table.
+     *
+     * @param  string|\Illuminate\Database\Query\Expression  $column
+     * @return string
+     */
+    public function qualifyColumn($column)
+    {
+        return $this->model->qualifyColumn($column);
+    }
+
+    /**
+     * Qualify the given columns with the model's table.
+     *
+     * @param  array|\Illuminate\Database\Query\Expression  $columns
+     * @return array
+     */
+    public function qualifyColumns($columns)
+    {
+        return $this->model->qualifyColumns($columns);
+    }
+
+    /**
+     * Get the given macro by name.
+     *
+     * @param  string  $name
+     * @return \Closure
+     */
+    public function getMacro($name)
+    {
+        return Arr::get($this->localMacros, $name);
+    }
+
+    /**
+     * Checks if a macro is registered.
+     *
+     * @param  string  $name
+     * @return bool
+     */
+    public function hasMacro($name)
+    {
+        return isset($this->localMacros[$name]);
+    }
+
+    /**
+     * Get the given global macro by name.
+     *
+     * @param  string  $name
+     * @return \Closure
+     */
+    public static function getGlobalMacro($name)
+    {
+        return Arr::get(static::$macros, $name);
+    }
+
+    /**
+     * Checks if a global macro is registered.
+     *
+     * @param  string  $name
+     * @return bool
+     */
+    public static function hasGlobalMacro($name)
+    {
+        return isset(static::$macros[$name]);
+    }
+
+    /**
+     * Dynamically access builder proxies.
+     *
+     * @param  string  $key
+     * @return mixed
+     *
+     * @throws \Exception
+     */
+    public function __get($key)
+    {
+        if (in_array($key, ['orWhere', 'whereNot', 'orWhereNot'])) {
+            return new HigherOrderBuilderProxy($this, $key);
+        }
+
+        if (in_array($key, $this->propertyPassthru)) {
+            return $this->toBase()->{$key};
+        }
+
+        throw new Exception("Property [{$key}] does not exist on the Eloquent builder instance.");
+    }
+
+    /**
+     * Dynamically handle calls into the query instance.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        if ($method === 'macro') {
+            $this->localMacros[$parameters[0]] = $parameters[1];
+
+            return;
+        }
+
+        if ($this->hasMacro($method)) {
+            array_unshift($parameters, $this);
+
+            return $this->localMacros[$method](...$parameters);
+        }
+
+        if (static::hasGlobalMacro($method)) {
+            $callable = static::$macros[$method];
+
+            if ($callable instanceof Closure) {
+                $callable = $callable->bindTo($this, static::class);
+            }
+
+            return $callable(...$parameters);
+        }
+
+        if ($this->hasNamedScope($method)) {
+            return $this->callNamedScope($method, $parameters);
+        }
+
+        if (in_array($method, $this->passthru)) {
+            return $this->toBase()->{$method}(...$parameters);
+        }
+
+        $this->forwardCallTo($this->query, $method, $parameters);
+
+        return $this;
+    }
+
+    /**
+     * Dynamically handle calls into the query instance.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     *
+     * @throws \BadMethodCallException
+     */
+    public static function __callStatic($method, $parameters)
+    {
+        if ($method === 'macro') {
+            static::$macros[$parameters[0]] = $parameters[1];
+
+            return;
+        }
+
+        if ($method === 'mixin') {
+            return static::registerMixin($parameters[0], $parameters[1] ?? true);
+        }
+
+        if (! static::hasGlobalMacro($method)) {
+            static::throwBadMethodCallException($method);
+        }
+
+        $callable = static::$macros[$method];
+
+        if ($callable instanceof Closure) {
+            $callable = $callable->bindTo(null, static::class);
+        }
+
+        return $callable(...$parameters);
+    }
+
+    /**
+     * Register the given mixin with the builder.
+     *
+     * @param  string  $mixin
+     * @param  bool  $replace
+     * @return void
+     */
+    protected static function registerMixin($mixin, $replace)
+    {
+        $methods = (new ReflectionClass($mixin))->getMethods(
+            ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
+        );
+
+        foreach ($methods as $method) {
+            if ($replace || ! static::hasGlobalMacro($method->name)) {
+                $method->setAccessible(true);
+
+                static::macro($method->name, $method->invoke($mixin));
+            }
+        }
+    }
+
+    /**
+     * Clone the Eloquent query builder.
+     *
+     * @return static
+     */
+    public function clone()
+    {
+        return clone $this;
+    }
+
+    /**
+     * Force a clone of the underlying query builder when cloning.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        $this->query = clone $this->query;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Casts/ArrayObject.php b/vendor/illuminate/database/Eloquent/Casts/ArrayObject.php
new file mode 100644
index 0000000..176b733
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Casts/ArrayObject.php
@@ -0,0 +1,46 @@
+
+ */
+class ArrayObject extends BaseArrayObject implements Arrayable, JsonSerializable
+{
+    /**
+     * Get a collection containing the underlying array.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function collect()
+    {
+        return collect($this->getArrayCopy());
+    }
+
+    /**
+     * Get the instance as an array.
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return $this->getArrayCopy();
+    }
+
+    /**
+     * Get the array that should be JSON serialized.
+     *
+     * @return array
+     */
+    public function jsonSerialize(): array
+    {
+        return $this->getArrayCopy();
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Casts/AsArrayObject.php b/vendor/illuminate/database/Eloquent/Casts/AsArrayObject.php
new file mode 100644
index 0000000..23543ba
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Casts/AsArrayObject.php
@@ -0,0 +1,42 @@
+, iterable>
+     */
+    public static function castUsing(array $arguments)
+    {
+        return new class implements CastsAttributes
+        {
+            public function get($model, $key, $value, $attributes)
+            {
+                if (! isset($attributes[$key])) {
+                    return;
+                }
+
+                $data = json_decode($attributes[$key], true);
+
+                return is_array($data) ? new ArrayObject($data) : null;
+            }
+
+            public function set($model, $key, $value, $attributes)
+            {
+                return [$key => json_encode($value)];
+            }
+
+            public function serialize($model, string $key, $value, array $attributes)
+            {
+                return $value->getArrayCopy();
+            }
+        };
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Casts/AsCollection.php b/vendor/illuminate/database/Eloquent/Casts/AsCollection.php
new file mode 100644
index 0000000..1a0dd83
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Casts/AsCollection.php
@@ -0,0 +1,38 @@
+, iterable>
+     */
+    public static function castUsing(array $arguments)
+    {
+        return new class implements CastsAttributes
+        {
+            public function get($model, $key, $value, $attributes)
+            {
+                if (! isset($attributes[$key])) {
+                    return;
+                }
+
+                $data = json_decode($attributes[$key], true);
+
+                return is_array($data) ? new Collection($data) : null;
+            }
+
+            public function set($model, $key, $value, $attributes)
+            {
+                return [$key => json_encode($value)];
+            }
+        };
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Casts/AsEncryptedArrayObject.php b/vendor/illuminate/database/Eloquent/Casts/AsEncryptedArrayObject.php
new file mode 100644
index 0000000..ce2b663
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Casts/AsEncryptedArrayObject.php
@@ -0,0 +1,45 @@
+, iterable>
+     */
+    public static function castUsing(array $arguments)
+    {
+        return new class implements CastsAttributes
+        {
+            public function get($model, $key, $value, $attributes)
+            {
+                if (isset($attributes[$key])) {
+                    return new ArrayObject(json_decode(Crypt::decryptString($attributes[$key]), true));
+                }
+
+                return null;
+            }
+
+            public function set($model, $key, $value, $attributes)
+            {
+                if (! is_null($value)) {
+                    return [$key => Crypt::encryptString(json_encode($value))];
+                }
+
+                return null;
+            }
+
+            public function serialize($model, string $key, $value, array $attributes)
+            {
+                return ! is_null($value) ? $value->getArrayCopy() : null;
+            }
+        };
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Casts/AsEncryptedCollection.php b/vendor/illuminate/database/Eloquent/Casts/AsEncryptedCollection.php
new file mode 100644
index 0000000..64cdf00
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Casts/AsEncryptedCollection.php
@@ -0,0 +1,41 @@
+, iterable>
+     */
+    public static function castUsing(array $arguments)
+    {
+        return new class implements CastsAttributes
+        {
+            public function get($model, $key, $value, $attributes)
+            {
+                if (isset($attributes[$key])) {
+                    return new Collection(json_decode(Crypt::decryptString($attributes[$key]), true));
+                }
+
+                return null;
+            }
+
+            public function set($model, $key, $value, $attributes)
+            {
+                if (! is_null($value)) {
+                    return [$key => Crypt::encryptString(json_encode($value))];
+                }
+
+                return null;
+            }
+        };
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Casts/AsEnumArrayObject.php b/vendor/illuminate/database/Eloquent/Casts/AsEnumArrayObject.php
new file mode 100644
index 0000000..5b47785
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Casts/AsEnumArrayObject.php
@@ -0,0 +1,84 @@
+}  $arguments
+     * @return CastsAttributes, iterable>
+     */
+    public static function castUsing(array $arguments)
+    {
+        return new class($arguments) implements CastsAttributes
+        {
+            protected $arguments;
+
+            public function __construct(array $arguments)
+            {
+                $this->arguments = $arguments;
+            }
+
+            public function get($model, $key, $value, $attributes)
+            {
+                if (! isset($attributes[$key]) || is_null($attributes[$key])) {
+                    return;
+                }
+
+                $data = json_decode($attributes[$key], true);
+
+                if (! is_array($data)) {
+                    return;
+                }
+
+                $enumClass = $this->arguments[0];
+
+                return new ArrayObject((new Collection($data))->map(function ($value) use ($enumClass) {
+                    return is_subclass_of($enumClass, BackedEnum::class)
+                        ? $enumClass::from($value)
+                        : constant($enumClass.'::'.$value);
+                })->toArray());
+            }
+
+            public function set($model, $key, $value, $attributes)
+            {
+                if ($value === null) {
+                    return [$key => null];
+                }
+
+                $storable = [];
+
+                foreach ($value as $enum) {
+                    $storable[] = $this->getStorableEnumValue($enum);
+                }
+
+                return [$key => json_encode($storable)];
+            }
+
+            public function serialize($model, string $key, $value, array $attributes)
+            {
+                return (new Collection($value->getArrayCopy()))->map(function ($enum) {
+                    return $this->getStorableEnumValue($enum);
+                })->toArray();
+            }
+
+            protected function getStorableEnumValue($enum)
+            {
+                if (is_string($enum) || is_int($enum)) {
+                    return $enum;
+                }
+
+                return $enum instanceof BackedEnum ? $enum->value : $enum->name;
+            }
+        };
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Casts/AsEnumCollection.php b/vendor/illuminate/database/Eloquent/Casts/AsEnumCollection.php
new file mode 100644
index 0000000..ca1feb5
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Casts/AsEnumCollection.php
@@ -0,0 +1,80 @@
+}  $arguments
+     * @return CastsAttributes, iterable>
+     */
+    public static function castUsing(array $arguments)
+    {
+        return new class($arguments) implements CastsAttributes
+        {
+            protected $arguments;
+
+            public function __construct(array $arguments)
+            {
+                $this->arguments = $arguments;
+            }
+
+            public function get($model, $key, $value, $attributes)
+            {
+                if (! isset($attributes[$key]) || is_null($attributes[$key])) {
+                    return;
+                }
+
+                $data = json_decode($attributes[$key], true);
+
+                if (! is_array($data)) {
+                    return;
+                }
+
+                $enumClass = $this->arguments[0];
+
+                return (new Collection($data))->map(function ($value) use ($enumClass) {
+                    return is_subclass_of($enumClass, BackedEnum::class)
+                        ? $enumClass::from($value)
+                        : constant($enumClass.'::'.$value);
+                });
+            }
+
+            public function set($model, $key, $value, $attributes)
+            {
+                $value = $value !== null
+                    ? (new Collection($value))->map(function ($enum) {
+                        return $this->getStorableEnumValue($enum);
+                    })->toJson()
+                    : null;
+
+                return [$key => $value];
+            }
+
+            public function serialize($model, string $key, $value, array $attributes)
+            {
+                return (new Collection($value))->map(function ($enum) {
+                    return $this->getStorableEnumValue($enum);
+                })->toArray();
+            }
+
+            protected function getStorableEnumValue($enum)
+            {
+                if (is_string($enum) || is_int($enum)) {
+                    return $enum;
+                }
+
+                return $enum instanceof BackedEnum ? $enum->value : $enum->name;
+            }
+        };
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Casts/AsStringable.php b/vendor/illuminate/database/Eloquent/Casts/AsStringable.php
new file mode 100644
index 0000000..c2927d2
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Casts/AsStringable.php
@@ -0,0 +1,32 @@
+
+     */
+    public static function castUsing(array $arguments)
+    {
+        return new class implements CastsAttributes
+        {
+            public function get($model, $key, $value, $attributes)
+            {
+                return isset($value) ? Str::of($value) : null;
+            }
+
+            public function set($model, $key, $value, $attributes)
+            {
+                return isset($value) ? (string) $value : null;
+            }
+        };
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Casts/Attribute.php b/vendor/illuminate/database/Eloquent/Casts/Attribute.php
new file mode 100644
index 0000000..3f9fd19
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Casts/Attribute.php
@@ -0,0 +1,105 @@
+get = $get;
+        $this->set = $set;
+    }
+
+    /**
+     * Create a new attribute accessor / mutator.
+     *
+     * @param  callable|null  $get
+     * @param  callable|null  $set
+     * @return static
+     */
+    public static function make(callable $get = null, callable $set = null): static
+    {
+        return new static($get, $set);
+    }
+
+    /**
+     * Create a new attribute accessor.
+     *
+     * @param  callable  $get
+     * @return static
+     */
+    public static function get(callable $get)
+    {
+        return new static($get);
+    }
+
+    /**
+     * Create a new attribute mutator.
+     *
+     * @param  callable  $set
+     * @return static
+     */
+    public static function set(callable $set)
+    {
+        return new static(null, $set);
+    }
+
+    /**
+     * Disable object caching for the attribute.
+     *
+     * @return static
+     */
+    public function withoutObjectCaching()
+    {
+        $this->withObjectCaching = false;
+
+        return $this;
+    }
+
+    /**
+     * Enable caching for the attribute.
+     *
+     * @return static
+     */
+    public function shouldCache()
+    {
+        $this->withCaching = true;
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Collection.php b/vendor/illuminate/database/Eloquent/Collection.php
new file mode 100755
index 0000000..0904b8e
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Collection.php
@@ -0,0 +1,782 @@
+
+ */
+class Collection extends BaseCollection implements QueueableCollection
+{
+    /**
+     * Find a model in the collection by key.
+     *
+     * @template TFindDefault
+     *
+     * @param  mixed  $key
+     * @param  TFindDefault  $default
+     * @return static|TModel|TFindDefault
+     */
+    public function find($key, $default = null)
+    {
+        if ($key instanceof Model) {
+            $key = $key->getKey();
+        }
+
+        if ($key instanceof Arrayable) {
+            $key = $key->toArray();
+        }
+
+        if (is_array($key)) {
+            if ($this->isEmpty()) {
+                return new static;
+            }
+
+            return $this->whereIn($this->first()->getKeyName(), $key);
+        }
+
+        return Arr::first($this->items, fn ($model) => $model->getKey() == $key, $default);
+    }
+
+    /**
+     * Load a set of relationships onto the collection.
+     *
+     * @param  array|string  $relations
+     * @return $this
+     */
+    public function load($relations)
+    {
+        if ($this->isNotEmpty()) {
+            if (is_string($relations)) {
+                $relations = func_get_args();
+            }
+
+            $query = $this->first()->newQueryWithoutRelationships()->with($relations);
+
+            $this->items = $query->eagerLoadRelations($this->items);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Load a set of aggregations over relationship's column onto the collection.
+     *
+     * @param  array|string  $relations
+     * @param  string  $column
+     * @param  string|null  $function
+     * @return $this
+     */
+    public function loadAggregate($relations, $column, $function = null)
+    {
+        if ($this->isEmpty()) {
+            return $this;
+        }
+
+        $models = $this->first()->newModelQuery()
+            ->whereKey($this->modelKeys())
+            ->select($this->first()->getKeyName())
+            ->withAggregate($relations, $column, $function)
+            ->get()
+            ->keyBy($this->first()->getKeyName());
+
+        $attributes = Arr::except(
+            array_keys($models->first()->getAttributes()),
+            $models->first()->getKeyName()
+        );
+
+        $this->each(function ($model) use ($models, $attributes) {
+            $extraAttributes = Arr::only($models->get($model->getKey())->getAttributes(), $attributes);
+
+            $model->forceFill($extraAttributes)
+                ->syncOriginalAttributes($attributes)
+                ->mergeCasts($models->get($model->getKey())->getCasts());
+        });
+
+        return $this;
+    }
+
+    /**
+     * Load a set of relationship counts onto the collection.
+     *
+     * @param  array|string  $relations
+     * @return $this
+     */
+    public function loadCount($relations)
+    {
+        return $this->loadAggregate($relations, '*', 'count');
+    }
+
+    /**
+     * Load a set of relationship's max column values onto the collection.
+     *
+     * @param  array|string  $relations
+     * @param  string  $column
+     * @return $this
+     */
+    public function loadMax($relations, $column)
+    {
+        return $this->loadAggregate($relations, $column, 'max');
+    }
+
+    /**
+     * Load a set of relationship's min column values onto the collection.
+     *
+     * @param  array|string  $relations
+     * @param  string  $column
+     * @return $this
+     */
+    public function loadMin($relations, $column)
+    {
+        return $this->loadAggregate($relations, $column, 'min');
+    }
+
+    /**
+     * Load a set of relationship's column summations onto the collection.
+     *
+     * @param  array|string  $relations
+     * @param  string  $column
+     * @return $this
+     */
+    public function loadSum($relations, $column)
+    {
+        return $this->loadAggregate($relations, $column, 'sum');
+    }
+
+    /**
+     * Load a set of relationship's average column values onto the collection.
+     *
+     * @param  array|string  $relations
+     * @param  string  $column
+     * @return $this
+     */
+    public function loadAvg($relations, $column)
+    {
+        return $this->loadAggregate($relations, $column, 'avg');
+    }
+
+    /**
+     * Load a set of related existences onto the collection.
+     *
+     * @param  array|string  $relations
+     * @return $this
+     */
+    public function loadExists($relations)
+    {
+        return $this->loadAggregate($relations, '*', 'exists');
+    }
+
+    /**
+     * Load a set of relationships onto the collection if they are not already eager loaded.
+     *
+     * @param  array|string  $relations
+     * @return $this
+     */
+    public function loadMissing($relations)
+    {
+        if (is_string($relations)) {
+            $relations = func_get_args();
+        }
+
+        foreach ($relations as $key => $value) {
+            if (is_numeric($key)) {
+                $key = $value;
+            }
+
+            $segments = explode('.', explode(':', $key)[0]);
+
+            if (str_contains($key, ':')) {
+                $segments[count($segments) - 1] .= ':'.explode(':', $key)[1];
+            }
+
+            $path = [];
+
+            foreach ($segments as $segment) {
+                $path[] = [$segment => $segment];
+            }
+
+            if (is_callable($value)) {
+                $path[count($segments) - 1][end($segments)] = $value;
+            }
+
+            $this->loadMissingRelation($this, $path);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Load a relationship path if it is not already eager loaded.
+     *
+     * @param  \Illuminate\Database\Eloquent\Collection  $models
+     * @param  array  $path
+     * @return void
+     */
+    protected function loadMissingRelation(self $models, array $path)
+    {
+        $relation = array_shift($path);
+
+        $name = explode(':', key($relation))[0];
+
+        if (is_string(reset($relation))) {
+            $relation = reset($relation);
+        }
+
+        $models->filter(fn ($model) => ! is_null($model) && ! $model->relationLoaded($name))->load($relation);
+
+        if (empty($path)) {
+            return;
+        }
+
+        $models = $models->pluck($name)->whereNotNull();
+
+        if ($models->first() instanceof BaseCollection) {
+            $models = $models->collapse();
+        }
+
+        $this->loadMissingRelation(new static($models), $path);
+    }
+
+    /**
+     * Load a set of relationships onto the mixed relationship collection.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @return $this
+     */
+    public function loadMorph($relation, $relations)
+    {
+        $this->pluck($relation)
+            ->filter()
+            ->groupBy(fn ($model) => get_class($model))
+            ->each(fn ($models, $className) => static::make($models)->load($relations[$className] ?? []));
+
+        return $this;
+    }
+
+    /**
+     * Load a set of relationship counts onto the mixed relationship collection.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @return $this
+     */
+    public function loadMorphCount($relation, $relations)
+    {
+        $this->pluck($relation)
+            ->filter()
+            ->groupBy(fn ($model) => get_class($model))
+            ->each(fn ($models, $className) => static::make($models)->loadCount($relations[$className] ?? []));
+
+        return $this;
+    }
+
+    /**
+     * Determine if a key exists in the collection.
+     *
+     * @param  (callable(TModel, TKey): bool)|TModel|string|int  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function contains($key, $operator = null, $value = null)
+    {
+        if (func_num_args() > 1 || $this->useAsCallable($key)) {
+            return parent::contains(...func_get_args());
+        }
+
+        if ($key instanceof Model) {
+            return parent::contains(fn ($model) => $model->is($key));
+        }
+
+        return parent::contains(fn ($model) => $model->getKey() == $key);
+    }
+
+    /**
+     * Get the array of primary keys.
+     *
+     * @return array
+     */
+    public function modelKeys()
+    {
+        return array_map(fn ($model) => $model->getKey(), $this->items);
+    }
+
+    /**
+     * Merge the collection with the given items.
+     *
+     * @param  iterable  $items
+     * @return static
+     */
+    public function merge($items)
+    {
+        $dictionary = $this->getDictionary();
+
+        foreach ($items as $item) {
+            $dictionary[$item->getKey()] = $item;
+        }
+
+        return new static(array_values($dictionary));
+    }
+
+    /**
+     * Run a map over each of the items.
+     *
+     * @template TMapValue
+     *
+     * @param  callable(TModel, TKey): TMapValue  $callback
+     * @return \Illuminate\Support\Collection|static
+     */
+    public function map(callable $callback)
+    {
+        $result = parent::map($callback);
+
+        return $result->contains(fn ($item) => ! $item instanceof Model) ? $result->toBase() : $result;
+    }
+
+    /**
+     * Run an associative map over each of the items.
+     *
+     * The callback should return an associative array with a single key / value pair.
+     *
+     * @template TMapWithKeysKey of array-key
+     * @template TMapWithKeysValue
+     *
+     * @param  callable(TModel, TKey): array  $callback
+     * @return \Illuminate\Support\Collection|static
+     */
+    public function mapWithKeys(callable $callback)
+    {
+        $result = parent::mapWithKeys($callback);
+
+        return $result->contains(fn ($item) => ! $item instanceof Model) ? $result->toBase() : $result;
+    }
+
+    /**
+     * Reload a fresh model instance from the database for all the entities.
+     *
+     * @param  array|string  $with
+     * @return static
+     */
+    public function fresh($with = [])
+    {
+        if ($this->isEmpty()) {
+            return new static;
+        }
+
+        $model = $this->first();
+
+        $freshModels = $model->newQueryWithoutScopes()
+            ->with(is_string($with) ? func_get_args() : $with)
+            ->whereIn($model->getKeyName(), $this->modelKeys())
+            ->get()
+            ->getDictionary();
+
+        return $this->filter(fn ($model) => $model->exists && isset($freshModels[$model->getKey()]))
+            ->map(fn ($model) => $freshModels[$model->getKey()]);
+    }
+
+    /**
+     * Diff the collection with the given items.
+     *
+     * @param  iterable  $items
+     * @return static
+     */
+    public function diff($items)
+    {
+        $diff = new static;
+
+        $dictionary = $this->getDictionary($items);
+
+        foreach ($this->items as $item) {
+            if (! isset($dictionary[$item->getKey()])) {
+                $diff->add($item);
+            }
+        }
+
+        return $diff;
+    }
+
+    /**
+     * Intersect the collection with the given items.
+     *
+     * @param  iterable  $items
+     * @return static
+     */
+    public function intersect($items)
+    {
+        $intersect = new static;
+
+        if (empty($items)) {
+            return $intersect;
+        }
+
+        $dictionary = $this->getDictionary($items);
+
+        foreach ($this->items as $item) {
+            if (isset($dictionary[$item->getKey()])) {
+                $intersect->add($item);
+            }
+        }
+
+        return $intersect;
+    }
+
+    /**
+     * Return only unique items from the collection.
+     *
+     * @param  (callable(TModel, TKey): mixed)|string|null  $key
+     * @param  bool  $strict
+     * @return static
+     */
+    public function unique($key = null, $strict = false)
+    {
+        if (! is_null($key)) {
+            return parent::unique($key, $strict);
+        }
+
+        return new static(array_values($this->getDictionary()));
+    }
+
+    /**
+     * Returns only the models from the collection with the specified keys.
+     *
+     * @param  array|null  $keys
+     * @return static
+     */
+    public function only($keys)
+    {
+        if (is_null($keys)) {
+            return new static($this->items);
+        }
+
+        $dictionary = Arr::only($this->getDictionary(), $keys);
+
+        return new static(array_values($dictionary));
+    }
+
+    /**
+     * Returns all models in the collection except the models with specified keys.
+     *
+     * @param  array|null  $keys
+     * @return static
+     */
+    public function except($keys)
+    {
+        $dictionary = Arr::except($this->getDictionary(), $keys);
+
+        return new static(array_values($dictionary));
+    }
+
+    /**
+     * Make the given, typically visible, attributes hidden across the entire collection.
+     *
+     * @param  array|string  $attributes
+     * @return $this
+     */
+    public function makeHidden($attributes)
+    {
+        return $this->each->makeHidden($attributes);
+    }
+
+    /**
+     * Make the given, typically hidden, attributes visible across the entire collection.
+     *
+     * @param  array|string  $attributes
+     * @return $this
+     */
+    public function makeVisible($attributes)
+    {
+        return $this->each->makeVisible($attributes);
+    }
+
+    /**
+     * Set the visible attributes across the entire collection.
+     *
+     * @param  array  $visible
+     * @return $this
+     */
+    public function setVisible($visible)
+    {
+        return $this->each->setVisible($visible);
+    }
+
+    /**
+     * Set the hidden attributes across the entire collection.
+     *
+     * @param  array  $hidden
+     * @return $this
+     */
+    public function setHidden($hidden)
+    {
+        return $this->each->setHidden($hidden);
+    }
+
+    /**
+     * Append an attribute across the entire collection.
+     *
+     * @param  array|string  $attributes
+     * @return $this
+     */
+    public function append($attributes)
+    {
+        return $this->each->append($attributes);
+    }
+
+    /**
+     * Get a dictionary keyed by primary keys.
+     *
+     * @param  iterable|null  $items
+     * @return array
+     */
+    public function getDictionary($items = null)
+    {
+        $items = is_null($items) ? $this->items : $items;
+
+        $dictionary = [];
+
+        foreach ($items as $value) {
+            $dictionary[$value->getKey()] = $value;
+        }
+
+        return $dictionary;
+    }
+
+    /**
+     * The following methods are intercepted to always return base collections.
+     */
+
+    /**
+     * Count the number of items in the collection by a field or using a callback.
+     *
+     * @param  (callable(TModel, TKey): array-key)|string|null  $countBy
+     * @return \Illuminate\Support\Collection
+     */
+    public function countBy($countBy = null)
+    {
+        return $this->toBase()->countBy($countBy);
+    }
+
+    /**
+     * Collapse the collection of items into a single array.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function collapse()
+    {
+        return $this->toBase()->collapse();
+    }
+
+    /**
+     * Get a flattened array of the items in the collection.
+     *
+     * @param  int  $depth
+     * @return \Illuminate\Support\Collection
+     */
+    public function flatten($depth = INF)
+    {
+        return $this->toBase()->flatten($depth);
+    }
+
+    /**
+     * Flip the items in the collection.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function flip()
+    {
+        return $this->toBase()->flip();
+    }
+
+    /**
+     * Get the keys of the collection items.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function keys()
+    {
+        return $this->toBase()->keys();
+    }
+
+    /**
+     * Pad collection to the specified length with a value.
+     *
+     * @template TPadValue
+     *
+     * @param  int  $size
+     * @param  TPadValue  $value
+     * @return \Illuminate\Support\Collection
+     */
+    public function pad($size, $value)
+    {
+        return $this->toBase()->pad($size, $value);
+    }
+
+    /**
+     * Get an array with the values of a given key.
+     *
+     * @param  string|array  $value
+     * @param  string|null  $key
+     * @return \Illuminate\Support\Collection
+     */
+    public function pluck($value, $key = null)
+    {
+        return $this->toBase()->pluck($value, $key);
+    }
+
+    /**
+     * Zip the collection together with one or more arrays.
+     *
+     * @template TZipValue
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|iterable  ...$items
+     * @return \Illuminate\Support\Collection>
+     */
+    public function zip($items)
+    {
+        return $this->toBase()->zip(...func_get_args());
+    }
+
+    /**
+     * Get the comparison function to detect duplicates.
+     *
+     * @param  bool  $strict
+     * @return callable(TModel, TModel): bool
+     */
+    protected function duplicateComparator($strict)
+    {
+        return fn ($a, $b) => $a->is($b);
+    }
+
+    /**
+     * Get the type of the entities being queued.
+     *
+     * @return string|null
+     *
+     * @throws \LogicException
+     */
+    public function getQueueableClass()
+    {
+        if ($this->isEmpty()) {
+            return;
+        }
+
+        $class = $this->getQueueableModelClass($this->first());
+
+        $this->each(function ($model) use ($class) {
+            if ($this->getQueueableModelClass($model) !== $class) {
+                throw new LogicException('Queueing collections with multiple model types is not supported.');
+            }
+        });
+
+        return $class;
+    }
+
+    /**
+     * Get the queueable class name for the given model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return string
+     */
+    protected function getQueueableModelClass($model)
+    {
+        return method_exists($model, 'getQueueableClassName')
+                ? $model->getQueueableClassName()
+                : get_class($model);
+    }
+
+    /**
+     * Get the identifiers for all of the entities.
+     *
+     * @return array
+     */
+    public function getQueueableIds()
+    {
+        if ($this->isEmpty()) {
+            return [];
+        }
+
+        return $this->first() instanceof QueueableEntity
+                    ? $this->map->getQueueableId()->all()
+                    : $this->modelKeys();
+    }
+
+    /**
+     * Get the relationships of the entities being queued.
+     *
+     * @return array
+     */
+    public function getQueueableRelations()
+    {
+        if ($this->isEmpty()) {
+            return [];
+        }
+
+        $relations = $this->map->getQueueableRelations()->all();
+
+        if (count($relations) === 0 || $relations === [[]]) {
+            return [];
+        } elseif (count($relations) === 1) {
+            return reset($relations);
+        } else {
+            return array_intersect(...array_values($relations));
+        }
+    }
+
+    /**
+     * Get the connection of the entities being queued.
+     *
+     * @return string|null
+     *
+     * @throws \LogicException
+     */
+    public function getQueueableConnection()
+    {
+        if ($this->isEmpty()) {
+            return;
+        }
+
+        $connection = $this->first()->getConnectionName();
+
+        $this->each(function ($model) use ($connection) {
+            if ($model->getConnectionName() !== $connection) {
+                throw new LogicException('Queueing collections with multiple model connections is not supported.');
+            }
+        });
+
+        return $connection;
+    }
+
+    /**
+     * Get the Eloquent query builder from the collection.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder
+     *
+     * @throws \LogicException
+     */
+    public function toQuery()
+    {
+        $model = $this->first();
+
+        if (! $model) {
+            throw new LogicException('Unable to create query for empty collection.');
+        }
+
+        $class = get_class($model);
+
+        if ($this->filter(fn ($model) => ! $model instanceof $class)->isNotEmpty()) {
+            throw new LogicException('Unable to create query for collection with mixed types.');
+        }
+
+        return $model->newModelQuery()->whereKey($this->modelKeys());
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Concerns/GuardsAttributes.php b/vendor/illuminate/database/Eloquent/Concerns/GuardsAttributes.php
new file mode 100644
index 0000000..bfb6775
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Concerns/GuardsAttributes.php
@@ -0,0 +1,255 @@
+
+     */
+    protected $fillable = [];
+
+    /**
+     * The attributes that aren't mass assignable.
+     *
+     * @var array|bool
+     */
+    protected $guarded = ['*'];
+
+    /**
+     * Indicates if all mass assignment is enabled.
+     *
+     * @var bool
+     */
+    protected static $unguarded = false;
+
+    /**
+     * The actual columns that exist on the database and can be guarded.
+     *
+     * @var array
+     */
+    protected static $guardableColumns = [];
+
+    /**
+     * Get the fillable attributes for the model.
+     *
+     * @return array
+     */
+    public function getFillable()
+    {
+        return $this->fillable;
+    }
+
+    /**
+     * Set the fillable attributes for the model.
+     *
+     * @param  array  $fillable
+     * @return $this
+     */
+    public function fillable(array $fillable)
+    {
+        $this->fillable = $fillable;
+
+        return $this;
+    }
+
+    /**
+     * Merge new fillable attributes with existing fillable attributes on the model.
+     *
+     * @param  array  $fillable
+     * @return $this
+     */
+    public function mergeFillable(array $fillable)
+    {
+        $this->fillable = array_merge($this->fillable, $fillable);
+
+        return $this;
+    }
+
+    /**
+     * Get the guarded attributes for the model.
+     *
+     * @return array
+     */
+    public function getGuarded()
+    {
+        return $this->guarded === false
+                    ? []
+                    : $this->guarded;
+    }
+
+    /**
+     * Set the guarded attributes for the model.
+     *
+     * @param  array  $guarded
+     * @return $this
+     */
+    public function guard(array $guarded)
+    {
+        $this->guarded = $guarded;
+
+        return $this;
+    }
+
+    /**
+     * Merge new guarded attributes with existing guarded attributes on the model.
+     *
+     * @param  array  $guarded
+     * @return $this
+     */
+    public function mergeGuarded(array $guarded)
+    {
+        $this->guarded = array_merge($this->guarded, $guarded);
+
+        return $this;
+    }
+
+    /**
+     * Disable all mass assignable restrictions.
+     *
+     * @param  bool  $state
+     * @return void
+     */
+    public static function unguard($state = true)
+    {
+        static::$unguarded = $state;
+    }
+
+    /**
+     * Enable the mass assignment restrictions.
+     *
+     * @return void
+     */
+    public static function reguard()
+    {
+        static::$unguarded = false;
+    }
+
+    /**
+     * Determine if the current state is "unguarded".
+     *
+     * @return bool
+     */
+    public static function isUnguarded()
+    {
+        return static::$unguarded;
+    }
+
+    /**
+     * Run the given callable while being unguarded.
+     *
+     * @param  callable  $callback
+     * @return mixed
+     */
+    public static function unguarded(callable $callback)
+    {
+        if (static::$unguarded) {
+            return $callback();
+        }
+
+        static::unguard();
+
+        try {
+            return $callback();
+        } finally {
+            static::reguard();
+        }
+    }
+
+    /**
+     * Determine if the given attribute may be mass assigned.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    public function isFillable($key)
+    {
+        if (static::$unguarded) {
+            return true;
+        }
+
+        // If the key is in the "fillable" array, we can of course assume that it's
+        // a fillable attribute. Otherwise, we will check the guarded array when
+        // we need to determine if the attribute is black-listed on the model.
+        if (in_array($key, $this->getFillable())) {
+            return true;
+        }
+
+        // If the attribute is explicitly listed in the "guarded" array then we can
+        // return false immediately. This means this attribute is definitely not
+        // fillable and there is no point in going any further in this method.
+        if ($this->isGuarded($key)) {
+            return false;
+        }
+
+        return empty($this->getFillable()) &&
+            ! str_contains($key, '.') &&
+            ! str_starts_with($key, '_');
+    }
+
+    /**
+     * Determine if the given key is guarded.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    public function isGuarded($key)
+    {
+        if (empty($this->getGuarded())) {
+            return false;
+        }
+
+        return $this->getGuarded() == ['*'] ||
+               ! empty(preg_grep('/^'.preg_quote($key, '/').'$/i', $this->getGuarded())) ||
+               ! $this->isGuardableColumn($key);
+    }
+
+    /**
+     * Determine if the given column is a valid, guardable column.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    protected function isGuardableColumn($key)
+    {
+        if (! isset(static::$guardableColumns[get_class($this)])) {
+            $columns = $this->getConnection()
+                        ->getSchemaBuilder()
+                        ->getColumnListing($this->getTable());
+
+            if (empty($columns)) {
+                return true;
+            }
+            static::$guardableColumns[get_class($this)] = $columns;
+        }
+
+        return in_array($key, static::$guardableColumns[get_class($this)]);
+    }
+
+    /**
+     * Determine if the model is totally guarded.
+     *
+     * @return bool
+     */
+    public function totallyGuarded()
+    {
+        return count($this->getFillable()) === 0 && $this->getGuarded() == ['*'];
+    }
+
+    /**
+     * Get the fillable attributes of a given array.
+     *
+     * @param  array  $attributes
+     * @return array
+     */
+    protected function fillableFromArray(array $attributes)
+    {
+        if (count($this->getFillable()) > 0 && ! static::$unguarded) {
+            return array_intersect_key($attributes, array_flip($this->getFillable()));
+        }
+
+        return $attributes;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Concerns/HasAttributes.php b/vendor/illuminate/database/Eloquent/Concerns/HasAttributes.php
new file mode 100644
index 0000000..2b1b7ca
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Concerns/HasAttributes.php
@@ -0,0 +1,2232 @@
+addDateAttributesToArray(
+            $attributes = $this->getArrayableAttributes()
+        );
+
+        $attributes = $this->addMutatedAttributesToArray(
+            $attributes, $mutatedAttributes = $this->getMutatedAttributes()
+        );
+
+        // Next we will handle any casts that have been setup for this model and cast
+        // the values to their appropriate type. If the attribute has a mutator we
+        // will not perform the cast on those attributes to avoid any confusion.
+        $attributes = $this->addCastAttributesToArray(
+            $attributes, $mutatedAttributes
+        );
+
+        // Here we will grab all of the appended, calculated attributes to this model
+        // as these attributes are not really in the attributes array, but are run
+        // when we need to array or JSON the model for convenience to the coder.
+        foreach ($this->getArrayableAppends() as $key) {
+            $attributes[$key] = $this->mutateAttributeForArray($key, null);
+        }
+
+        return $attributes;
+    }
+
+    /**
+     * Add the date attributes to the attributes array.
+     *
+     * @param  array  $attributes
+     * @return array
+     */
+    protected function addDateAttributesToArray(array $attributes)
+    {
+        foreach ($this->getDates() as $key) {
+            if (! isset($attributes[$key])) {
+                continue;
+            }
+
+            $attributes[$key] = $this->serializeDate(
+                $this->asDateTime($attributes[$key])
+            );
+        }
+
+        return $attributes;
+    }
+
+    /**
+     * Add the mutated attributes to the attributes array.
+     *
+     * @param  array  $attributes
+     * @param  array  $mutatedAttributes
+     * @return array
+     */
+    protected function addMutatedAttributesToArray(array $attributes, array $mutatedAttributes)
+    {
+        foreach ($mutatedAttributes as $key) {
+            // We want to spin through all the mutated attributes for this model and call
+            // the mutator for the attribute. We cache off every mutated attributes so
+            // we don't have to constantly check on attributes that actually change.
+            if (! array_key_exists($key, $attributes)) {
+                continue;
+            }
+
+            // Next, we will call the mutator for this attribute so that we can get these
+            // mutated attribute's actual values. After we finish mutating each of the
+            // attributes we will return this final array of the mutated attributes.
+            $attributes[$key] = $this->mutateAttributeForArray(
+                $key, $attributes[$key]
+            );
+        }
+
+        return $attributes;
+    }
+
+    /**
+     * Add the casted attributes to the attributes array.
+     *
+     * @param  array  $attributes
+     * @param  array  $mutatedAttributes
+     * @return array
+     */
+    protected function addCastAttributesToArray(array $attributes, array $mutatedAttributes)
+    {
+        foreach ($this->getCasts() as $key => $value) {
+            if (! array_key_exists($key, $attributes) ||
+                in_array($key, $mutatedAttributes)) {
+                continue;
+            }
+
+            // Here we will cast the attribute. Then, if the cast is a date or datetime cast
+            // then we will serialize the date for the array. This will convert the dates
+            // to strings based on the date format specified for these Eloquent models.
+            $attributes[$key] = $this->castAttribute(
+                $key, $attributes[$key]
+            );
+
+            // If the attribute cast was a date or a datetime, we will serialize the date as
+            // a string. This allows the developers to customize how dates are serialized
+            // into an array without affecting how they are persisted into the storage.
+            if (isset($attributes[$key]) && in_array($value, ['date', 'datetime', 'immutable_date', 'immutable_datetime'])) {
+                $attributes[$key] = $this->serializeDate($attributes[$key]);
+            }
+
+            if (isset($attributes[$key]) && ($this->isCustomDateTimeCast($value) ||
+                $this->isImmutableCustomDateTimeCast($value))) {
+                $attributes[$key] = $attributes[$key]->format(explode(':', $value, 2)[1]);
+            }
+
+            if ($attributes[$key] instanceof DateTimeInterface &&
+                $this->isClassCastable($key)) {
+                $attributes[$key] = $this->serializeDate($attributes[$key]);
+            }
+
+            if (isset($attributes[$key]) && $this->isClassSerializable($key)) {
+                $attributes[$key] = $this->serializeClassCastableAttribute($key, $attributes[$key]);
+            }
+
+            if ($this->isEnumCastable($key) && (! ($attributes[$key] ?? null) instanceof Arrayable)) {
+                $attributes[$key] = isset($attributes[$key]) ? $this->getStorableEnumValue($attributes[$key]) : null;
+            }
+
+            if ($attributes[$key] instanceof Arrayable) {
+                $attributes[$key] = $attributes[$key]->toArray();
+            }
+        }
+
+        return $attributes;
+    }
+
+    /**
+     * Get an attribute array of all arrayable attributes.
+     *
+     * @return array
+     */
+    protected function getArrayableAttributes()
+    {
+        return $this->getArrayableItems($this->getAttributes());
+    }
+
+    /**
+     * Get all of the appendable values that are arrayable.
+     *
+     * @return array
+     */
+    protected function getArrayableAppends()
+    {
+        if (! count($this->appends)) {
+            return [];
+        }
+
+        return $this->getArrayableItems(
+            array_combine($this->appends, $this->appends)
+        );
+    }
+
+    /**
+     * Get the model's relationships in array form.
+     *
+     * @return array
+     */
+    public function relationsToArray()
+    {
+        $attributes = [];
+
+        foreach ($this->getArrayableRelations() as $key => $value) {
+            // If the values implement the Arrayable interface we can just call this
+            // toArray method on the instances which will convert both models and
+            // collections to their proper array form and we'll set the values.
+            if ($value instanceof Arrayable) {
+                $relation = $value->toArray();
+            }
+
+            // If the value is null, we'll still go ahead and set it in this list of
+            // attributes, since null is used to represent empty relationships if
+            // it has a has one or belongs to type relationships on the models.
+            elseif (is_null($value)) {
+                $relation = $value;
+            }
+
+            // If the relationships snake-casing is enabled, we will snake case this
+            // key so that the relation attribute is snake cased in this returned
+            // array to the developers, making this consistent with attributes.
+            if (static::$snakeAttributes) {
+                $key = Str::snake($key);
+            }
+
+            // If the relation value has been set, we will set it on this attributes
+            // list for returning. If it was not arrayable or null, we'll not set
+            // the value on the array because it is some type of invalid value.
+            if (isset($relation) || is_null($value)) {
+                $attributes[$key] = $relation;
+            }
+
+            unset($relation);
+        }
+
+        return $attributes;
+    }
+
+    /**
+     * Get an attribute array of all arrayable relations.
+     *
+     * @return array
+     */
+    protected function getArrayableRelations()
+    {
+        return $this->getArrayableItems($this->relations);
+    }
+
+    /**
+     * Get an attribute array of all arrayable values.
+     *
+     * @param  array  $values
+     * @return array
+     */
+    protected function getArrayableItems(array $values)
+    {
+        if (count($this->getVisible()) > 0) {
+            $values = array_intersect_key($values, array_flip($this->getVisible()));
+        }
+
+        if (count($this->getHidden()) > 0) {
+            $values = array_diff_key($values, array_flip($this->getHidden()));
+        }
+
+        return $values;
+    }
+
+    /**
+     * Get an attribute from the model.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function getAttribute($key)
+    {
+        if (! $key) {
+            return;
+        }
+
+        // If the attribute exists in the attribute array or has a "get" mutator we will
+        // get the attribute's value. Otherwise, we will proceed as if the developers
+        // are asking for a relationship's value. This covers both types of values.
+        if (array_key_exists($key, $this->attributes) ||
+            array_key_exists($key, $this->casts) ||
+            $this->hasGetMutator($key) ||
+            $this->hasAttributeMutator($key) ||
+            $this->isClassCastable($key)) {
+            return $this->getAttributeValue($key);
+        }
+
+        // Here we will determine if the model base class itself contains this given key
+        // since we don't want to treat any of those methods as relationships because
+        // they are all intended as helper methods and none of these are relations.
+        if (method_exists(self::class, $key)) {
+            return $this->throwMissingAttributeExceptionIfApplicable($key);
+        }
+
+        return $this->isRelation($key) || $this->relationLoaded($key)
+                    ? $this->getRelationValue($key)
+                    : $this->throwMissingAttributeExceptionIfApplicable($key);
+    }
+
+    /**
+     * Either throw a missing attribute exception or return null depending on Eloquent's configuration.
+     *
+     * @param  string  $key
+     * @return null
+     *
+     * @throws \Illuminate\Database\Eloquent\MissingAttributeException
+     */
+    protected function throwMissingAttributeExceptionIfApplicable($key)
+    {
+        if ($this->exists &&
+            ! $this->wasRecentlyCreated &&
+            static::preventsAccessingMissingAttributes()) {
+            if (isset(static::$missingAttributeViolationCallback)) {
+                return call_user_func(static::$missingAttributeViolationCallback, $this, $key);
+            }
+
+            throw new MissingAttributeException($this, $key);
+        }
+
+        return null;
+    }
+
+    /**
+     * Get a plain attribute (not a relationship).
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function getAttributeValue($key)
+    {
+        return $this->transformModelValue($key, $this->getAttributeFromArray($key));
+    }
+
+    /**
+     * Get an attribute from the $attributes array.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    protected function getAttributeFromArray($key)
+    {
+        return $this->getAttributes()[$key] ?? null;
+    }
+
+    /**
+     * Get a relationship.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function getRelationValue($key)
+    {
+        // If the key already exists in the relationships array, it just means the
+        // relationship has already been loaded, so we'll just return it out of
+        // here because there is no need to query within the relations twice.
+        if ($this->relationLoaded($key)) {
+            return $this->relations[$key];
+        }
+
+        if (! $this->isRelation($key)) {
+            return;
+        }
+
+        if ($this->preventsLazyLoading) {
+            $this->handleLazyLoadingViolation($key);
+        }
+
+        // If the "attribute" exists as a method on the model, we will just assume
+        // it is a relationship and will load and return results from the query
+        // and hydrate the relationship's value on the "relationships" array.
+        return $this->getRelationshipFromMethod($key);
+    }
+
+    /**
+     * Determine if the given key is a relationship method on the model.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    public function isRelation($key)
+    {
+        if ($this->hasAttributeMutator($key)) {
+            return false;
+        }
+
+        return method_exists($this, $key) ||
+               $this->relationResolver(static::class, $key);
+    }
+
+    /**
+     * Handle a lazy loading violation.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    protected function handleLazyLoadingViolation($key)
+    {
+        if (isset(static::$lazyLoadingViolationCallback)) {
+            return call_user_func(static::$lazyLoadingViolationCallback, $this, $key);
+        }
+
+        if (! $this->exists || $this->wasRecentlyCreated) {
+            return;
+        }
+
+        throw new LazyLoadingViolationException($this, $key);
+    }
+
+    /**
+     * Get a relationship value from a method.
+     *
+     * @param  string  $method
+     * @return mixed
+     *
+     * @throws \LogicException
+     */
+    protected function getRelationshipFromMethod($method)
+    {
+        $relation = $this->$method();
+
+        if (! $relation instanceof Relation) {
+            if (is_null($relation)) {
+                throw new LogicException(sprintf(
+                    '%s::%s must return a relationship instance, but "null" was returned. Was the "return" keyword used?', static::class, $method
+                ));
+            }
+
+            throw new LogicException(sprintf(
+                '%s::%s must return a relationship instance.', static::class, $method
+            ));
+        }
+
+        return tap($relation->getResults(), function ($results) use ($method) {
+            $this->setRelation($method, $results);
+        });
+    }
+
+    /**
+     * Determine if a get mutator exists for an attribute.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    public function hasGetMutator($key)
+    {
+        return method_exists($this, 'get'.Str::studly($key).'Attribute');
+    }
+
+    /**
+     * Determine if a "Attribute" return type marked mutator exists for an attribute.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    public function hasAttributeMutator($key)
+    {
+        if (isset(static::$attributeMutatorCache[get_class($this)][$key])) {
+            return static::$attributeMutatorCache[get_class($this)][$key];
+        }
+
+        if (! method_exists($this, $method = Str::camel($key))) {
+            return static::$attributeMutatorCache[get_class($this)][$key] = false;
+        }
+
+        $returnType = (new ReflectionMethod($this, $method))->getReturnType();
+
+        return static::$attributeMutatorCache[get_class($this)][$key] =
+                    $returnType instanceof ReflectionNamedType &&
+                    $returnType->getName() === Attribute::class;
+    }
+
+    /**
+     * Determine if a "Attribute" return type marked get mutator exists for an attribute.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    public function hasAttributeGetMutator($key)
+    {
+        if (isset(static::$getAttributeMutatorCache[get_class($this)][$key])) {
+            return static::$getAttributeMutatorCache[get_class($this)][$key];
+        }
+
+        if (! $this->hasAttributeMutator($key)) {
+            return static::$getAttributeMutatorCache[get_class($this)][$key] = false;
+        }
+
+        return static::$getAttributeMutatorCache[get_class($this)][$key] = is_callable($this->{Str::camel($key)}()->get);
+    }
+
+    /**
+     * Get the value of an attribute using its mutator.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function mutateAttribute($key, $value)
+    {
+        return $this->{'get'.Str::studly($key).'Attribute'}($value);
+    }
+
+    /**
+     * Get the value of an "Attribute" return type marked attribute using its mutator.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function mutateAttributeMarkedAttribute($key, $value)
+    {
+        if (array_key_exists($key, $this->attributeCastCache)) {
+            return $this->attributeCastCache[$key];
+        }
+
+        $attribute = $this->{Str::camel($key)}();
+
+        $value = call_user_func($attribute->get ?: function ($value) {
+            return $value;
+        }, $value, $this->attributes);
+
+        if ($attribute->withCaching || (is_object($value) && $attribute->withObjectCaching)) {
+            $this->attributeCastCache[$key] = $value;
+        } else {
+            unset($this->attributeCastCache[$key]);
+        }
+
+        return $value;
+    }
+
+    /**
+     * Get the value of an attribute using its mutator for array conversion.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function mutateAttributeForArray($key, $value)
+    {
+        if ($this->isClassCastable($key)) {
+            $value = $this->getClassCastableAttributeValue($key, $value);
+        } elseif (isset(static::$getAttributeMutatorCache[get_class($this)][$key]) &&
+                  static::$getAttributeMutatorCache[get_class($this)][$key] === true) {
+            $value = $this->mutateAttributeMarkedAttribute($key, $value);
+
+            $value = $value instanceof DateTimeInterface
+                        ? $this->serializeDate($value)
+                        : $value;
+        } else {
+            $value = $this->mutateAttribute($key, $value);
+        }
+
+        return $value instanceof Arrayable ? $value->toArray() : $value;
+    }
+
+    /**
+     * Merge new casts with existing casts on the model.
+     *
+     * @param  array  $casts
+     * @return $this
+     */
+    public function mergeCasts($casts)
+    {
+        $this->casts = array_merge($this->casts, $casts);
+
+        return $this;
+    }
+
+    /**
+     * Cast an attribute to a native PHP type.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function castAttribute($key, $value)
+    {
+        $castType = $this->getCastType($key);
+
+        if (is_null($value) && in_array($castType, static::$primitiveCastTypes)) {
+            return $value;
+        }
+
+        // If the key is one of the encrypted castable types, we'll first decrypt
+        // the value and update the cast type so we may leverage the following
+        // logic for casting this value to any additionally specified types.
+        if ($this->isEncryptedCastable($key)) {
+            $value = $this->fromEncryptedString($value);
+
+            $castType = Str::after($castType, 'encrypted:');
+        }
+
+        switch ($castType) {
+            case 'int':
+            case 'integer':
+                return (int) $value;
+            case 'real':
+            case 'float':
+            case 'double':
+                return $this->fromFloat($value);
+            case 'decimal':
+                return $this->asDecimal($value, explode(':', $this->getCasts()[$key], 2)[1]);
+            case 'string':
+                return (string) $value;
+            case 'bool':
+            case 'boolean':
+                return (bool) $value;
+            case 'object':
+                return $this->fromJson($value, true);
+            case 'array':
+            case 'json':
+                return $this->fromJson($value);
+            case 'collection':
+                return new BaseCollection($this->fromJson($value));
+            case 'date':
+                return $this->asDate($value);
+            case 'datetime':
+            case 'custom_datetime':
+                return $this->asDateTime($value);
+            case 'immutable_date':
+                return $this->asDate($value)->toImmutable();
+            case 'immutable_custom_datetime':
+            case 'immutable_datetime':
+                return $this->asDateTime($value)->toImmutable();
+            case 'timestamp':
+                return $this->asTimestamp($value);
+        }
+
+        if ($this->isEnumCastable($key)) {
+            return $this->getEnumCastableAttributeValue($key, $value);
+        }
+
+        if ($this->isClassCastable($key)) {
+            return $this->getClassCastableAttributeValue($key, $value);
+        }
+
+        return $value;
+    }
+
+    /**
+     * Cast the given attribute using a custom cast class.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function getClassCastableAttributeValue($key, $value)
+    {
+        if (isset($this->classCastCache[$key])) {
+            return $this->classCastCache[$key];
+        } else {
+            $caster = $this->resolveCasterClass($key);
+
+            $value = $caster instanceof CastsInboundAttributes
+                ? $value
+                : $caster->get($this, $key, $value, $this->attributes);
+
+            if ($caster instanceof CastsInboundAttributes || ! is_object($value)) {
+                unset($this->classCastCache[$key]);
+            } else {
+                $this->classCastCache[$key] = $value;
+            }
+
+            return $value;
+        }
+    }
+
+    /**
+     * Cast the given attribute to an enum.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function getEnumCastableAttributeValue($key, $value)
+    {
+        if (is_null($value)) {
+            return;
+        }
+
+        $castType = $this->getCasts()[$key];
+
+        if ($value instanceof $castType) {
+            return $value;
+        }
+
+        return $this->getEnumCaseFromValue($castType, $value);
+    }
+
+    /**
+     * Get the type of cast for a model attribute.
+     *
+     * @param  string  $key
+     * @return string
+     */
+    protected function getCastType($key)
+    {
+        $castType = $this->getCasts()[$key];
+
+        if (isset(static::$castTypeCache[$castType])) {
+            return static::$castTypeCache[$castType];
+        }
+
+        if ($this->isCustomDateTimeCast($castType)) {
+            $convertedCastType = 'custom_datetime';
+        } elseif ($this->isImmutableCustomDateTimeCast($castType)) {
+            $convertedCastType = 'immutable_custom_datetime';
+        } elseif ($this->isDecimalCast($castType)) {
+            $convertedCastType = 'decimal';
+        } else {
+            $convertedCastType = trim(strtolower($castType));
+        }
+
+        return static::$castTypeCache[$castType] = $convertedCastType;
+    }
+
+    /**
+     * Increment or decrement the given attribute using the custom cast class.
+     *
+     * @param  string  $method
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function deviateClassCastableAttribute($method, $key, $value)
+    {
+        return $this->resolveCasterClass($key)->{$method}(
+            $this, $key, $value, $this->attributes
+        );
+    }
+
+    /**
+     * Serialize the given attribute using the custom cast class.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function serializeClassCastableAttribute($key, $value)
+    {
+        return $this->resolveCasterClass($key)->serialize(
+            $this, $key, $value, $this->attributes
+        );
+    }
+
+    /**
+     * Determine if the cast type is a custom date time cast.
+     *
+     * @param  string  $cast
+     * @return bool
+     */
+    protected function isCustomDateTimeCast($cast)
+    {
+        return str_starts_with($cast, 'date:') ||
+                str_starts_with($cast, 'datetime:');
+    }
+
+    /**
+     * Determine if the cast type is an immutable custom date time cast.
+     *
+     * @param  string  $cast
+     * @return bool
+     */
+    protected function isImmutableCustomDateTimeCast($cast)
+    {
+        return str_starts_with($cast, 'immutable_date:') ||
+                str_starts_with($cast, 'immutable_datetime:');
+    }
+
+    /**
+     * Determine if the cast type is a decimal cast.
+     *
+     * @param  string  $cast
+     * @return bool
+     */
+    protected function isDecimalCast($cast)
+    {
+        return str_starts_with($cast, 'decimal:');
+    }
+
+    /**
+     * Set a given attribute on the model.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    public function setAttribute($key, $value)
+    {
+        // First we will check for the presence of a mutator for the set operation
+        // which simply lets the developers tweak the attribute as it is set on
+        // this model, such as "json_encoding" a listing of data for storage.
+        if ($this->hasSetMutator($key)) {
+            return $this->setMutatedAttributeValue($key, $value);
+        } elseif ($this->hasAttributeSetMutator($key)) {
+            return $this->setAttributeMarkedMutatedAttributeValue($key, $value);
+        }
+
+        // If an attribute is listed as a "date", we'll convert it from a DateTime
+        // instance into a form proper for storage on the database tables using
+        // the connection grammar's date format. We will auto set the values.
+        elseif (! is_null($value) && $this->isDateAttribute($key)) {
+            $value = $this->fromDateTime($value);
+        }
+
+        if ($this->isEnumCastable($key)) {
+            $this->setEnumCastableAttribute($key, $value);
+
+            return $this;
+        }
+
+        if ($this->isClassCastable($key)) {
+            $this->setClassCastableAttribute($key, $value);
+
+            return $this;
+        }
+
+        if (! is_null($value) && $this->isJsonCastable($key)) {
+            $value = $this->castAttributeAsJson($key, $value);
+        }
+
+        // If this attribute contains a JSON ->, we'll set the proper value in the
+        // attribute's underlying array. This takes care of properly nesting an
+        // attribute in the array's value in the case of deeply nested items.
+        if (str_contains($key, '->')) {
+            return $this->fillJsonAttribute($key, $value);
+        }
+
+        if (! is_null($value) && $this->isEncryptedCastable($key)) {
+            $value = $this->castAttributeAsEncryptedString($key, $value);
+        }
+
+        $this->attributes[$key] = $value;
+
+        return $this;
+    }
+
+    /**
+     * Determine if a set mutator exists for an attribute.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    public function hasSetMutator($key)
+    {
+        return method_exists($this, 'set'.Str::studly($key).'Attribute');
+    }
+
+    /**
+     * Determine if an "Attribute" return type marked set mutator exists for an attribute.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    public function hasAttributeSetMutator($key)
+    {
+        $class = get_class($this);
+
+        if (isset(static::$setAttributeMutatorCache[$class][$key])) {
+            return static::$setAttributeMutatorCache[$class][$key];
+        }
+
+        if (! method_exists($this, $method = Str::camel($key))) {
+            return static::$setAttributeMutatorCache[$class][$key] = false;
+        }
+
+        $returnType = (new ReflectionMethod($this, $method))->getReturnType();
+
+        return static::$setAttributeMutatorCache[$class][$key] =
+                    $returnType instanceof ReflectionNamedType &&
+                    $returnType->getName() === Attribute::class &&
+                    is_callable($this->{$method}()->set);
+    }
+
+    /**
+     * Set the value of an attribute using its mutator.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function setMutatedAttributeValue($key, $value)
+    {
+        return $this->{'set'.Str::studly($key).'Attribute'}($value);
+    }
+
+    /**
+     * Set the value of a "Attribute" return type marked attribute using its mutator.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function setAttributeMarkedMutatedAttributeValue($key, $value)
+    {
+        $attribute = $this->{Str::camel($key)}();
+
+        $callback = $attribute->set ?: function ($value) use ($key) {
+            $this->attributes[$key] = $value;
+        };
+
+        $this->attributes = array_merge(
+            $this->attributes,
+            $this->normalizeCastClassResponse(
+                $key, $callback($value, $this->attributes)
+            )
+        );
+
+        if ($attribute->withCaching || (is_object($value) && $attribute->withObjectCaching)) {
+            $this->attributeCastCache[$key] = $value;
+        } else {
+            unset($this->attributeCastCache[$key]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Determine if the given attribute is a date or date castable.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    protected function isDateAttribute($key)
+    {
+        return in_array($key, $this->getDates(), true) ||
+            $this->isDateCastable($key);
+    }
+
+    /**
+     * Set a given JSON attribute on the model.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function fillJsonAttribute($key, $value)
+    {
+        [$key, $path] = explode('->', $key, 2);
+
+        $value = $this->asJson($this->getArrayAttributeWithValue(
+            $path, $key, $value
+        ));
+
+        $this->attributes[$key] = $this->isEncryptedCastable($key)
+            ? $this->castAttributeAsEncryptedString($key, $value)
+            : $value;
+
+        if ($this->isClassCastable($key)) {
+            unset($this->classCastCache[$key]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set the value of a class castable attribute.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return void
+     */
+    protected function setClassCastableAttribute($key, $value)
+    {
+        $caster = $this->resolveCasterClass($key);
+
+        $this->attributes = array_replace(
+            $this->attributes,
+            $this->normalizeCastClassResponse($key, $caster->set(
+                $this, $key, $value, $this->attributes
+            ))
+        );
+
+        if ($caster instanceof CastsInboundAttributes || ! is_object($value)) {
+            unset($this->classCastCache[$key]);
+        } else {
+            $this->classCastCache[$key] = $value;
+        }
+    }
+
+    /**
+     * Set the value of an enum castable attribute.
+     *
+     * @param  string  $key
+     * @param  \UnitEnum|string|int  $value
+     * @return void
+     */
+    protected function setEnumCastableAttribute($key, $value)
+    {
+        $enumClass = $this->getCasts()[$key];
+
+        if (! isset($value)) {
+            $this->attributes[$key] = null;
+        } elseif (is_object($value)) {
+            $this->attributes[$key] = $this->getStorableEnumValue($value);
+        } else {
+            $this->attributes[$key] = $this->getStorableEnumValue(
+                $this->getEnumCaseFromValue($enumClass, $value)
+            );
+        }
+    }
+
+    /**
+     * Get an enum case instance from a given class and value.
+     *
+     * @param  string  $enumClass
+     * @param  string|int  $value
+     * @return \UnitEnum|\BackedEnum
+     */
+    protected function getEnumCaseFromValue($enumClass, $value)
+    {
+        return is_subclass_of($enumClass, BackedEnum::class)
+                ? $enumClass::from($value)
+                : constant($enumClass.'::'.$value);
+    }
+
+    /**
+     * Get the storable value from the given enum.
+     *
+     * @param  \UnitEnum|\BackedEnum  $value
+     * @return string|int
+     */
+    protected function getStorableEnumValue($value)
+    {
+        return $value instanceof BackedEnum
+                ? $value->value
+                : $value->name;
+    }
+
+    /**
+     * Get an array attribute with the given key and value set.
+     *
+     * @param  string  $path
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return $this
+     */
+    protected function getArrayAttributeWithValue($path, $key, $value)
+    {
+        return tap($this->getArrayAttributeByKey($key), function (&$array) use ($path, $value) {
+            Arr::set($array, str_replace('->', '.', $path), $value);
+        });
+    }
+
+    /**
+     * Get an array attribute or return an empty array if it is not set.
+     *
+     * @param  string  $key
+     * @return array
+     */
+    protected function getArrayAttributeByKey($key)
+    {
+        if (! isset($this->attributes[$key])) {
+            return [];
+        }
+
+        return $this->fromJson(
+            $this->isEncryptedCastable($key)
+                ? $this->fromEncryptedString($this->attributes[$key])
+                : $this->attributes[$key]
+        );
+    }
+
+    /**
+     * Cast the given attribute to JSON.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return string
+     */
+    protected function castAttributeAsJson($key, $value)
+    {
+        $value = $this->asJson($value);
+
+        if ($value === false) {
+            throw JsonEncodingException::forAttribute(
+                $this, $key, json_last_error_msg()
+            );
+        }
+
+        return $value;
+    }
+
+    /**
+     * Encode the given value as JSON.
+     *
+     * @param  mixed  $value
+     * @return string
+     */
+    protected function asJson($value)
+    {
+        return json_encode($value);
+    }
+
+    /**
+     * Decode the given JSON back into an array or object.
+     *
+     * @param  string  $value
+     * @param  bool  $asObject
+     * @return mixed
+     */
+    public function fromJson($value, $asObject = false)
+    {
+        return json_decode($value ?? '', ! $asObject);
+    }
+
+    /**
+     * Decrypt the given encrypted string.
+     *
+     * @param  string  $value
+     * @return mixed
+     */
+    public function fromEncryptedString($value)
+    {
+        return (static::$encrypter ?? Crypt::getFacadeRoot())->decrypt($value, false);
+    }
+
+    /**
+     * Cast the given attribute to an encrypted string.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return string
+     */
+    protected function castAttributeAsEncryptedString($key, $value)
+    {
+        return (static::$encrypter ?? Crypt::getFacadeRoot())->encrypt($value, false);
+    }
+
+    /**
+     * Set the encrypter instance that will be used to encrypt attributes.
+     *
+     * @param  \Illuminate\Contracts\Encryption\Encrypter|null  $encrypter
+     * @return void
+     */
+    public static function encryptUsing($encrypter)
+    {
+        static::$encrypter = $encrypter;
+    }
+
+    /**
+     * Decode the given float.
+     *
+     * @param  mixed  $value
+     * @return mixed
+     */
+    public function fromFloat($value)
+    {
+        return match ((string) $value) {
+            'Infinity' => INF,
+            '-Infinity' => -INF,
+            'NaN' => NAN,
+            default => (float) $value,
+        };
+    }
+
+    /**
+     * Return a decimal as string.
+     *
+     * @param  float|string  $value
+     * @param  int  $decimals
+     * @return string
+     */
+    protected function asDecimal($value, $decimals)
+    {
+        try {
+            return (string) BigDecimal::of($value)->toScale($decimals, RoundingMode::HALF_UP);
+        } catch (BrickMathException $e) {
+            throw new MathException('Unable to cast value to a decimal.', previous: $e);
+        }
+    }
+
+    /**
+     * Return a timestamp as DateTime object with time set to 00:00:00.
+     *
+     * @param  mixed  $value
+     * @return \Illuminate\Support\Carbon
+     */
+    protected function asDate($value)
+    {
+        return $this->asDateTime($value)->startOfDay();
+    }
+
+    /**
+     * Return a timestamp as DateTime object.
+     *
+     * @param  mixed  $value
+     * @return \Illuminate\Support\Carbon
+     */
+    protected function asDateTime($value)
+    {
+        // If this value is already a Carbon instance, we shall just return it as is.
+        // This prevents us having to re-instantiate a Carbon instance when we know
+        // it already is one, which wouldn't be fulfilled by the DateTime check.
+        if ($value instanceof CarbonInterface) {
+            return Date::instance($value);
+        }
+
+        // If the value is already a DateTime instance, we will just skip the rest of
+        // these checks since they will be a waste of time, and hinder performance
+        // when checking the field. We will just return the DateTime right away.
+        if ($value instanceof DateTimeInterface) {
+            return Date::parse(
+                $value->format('Y-m-d H:i:s.u'), $value->getTimezone()
+            );
+        }
+
+        // If this value is an integer, we will assume it is a UNIX timestamp's value
+        // and format a Carbon object from this timestamp. This allows flexibility
+        // when defining your date fields as they might be UNIX timestamps here.
+        if (is_numeric($value)) {
+            return Date::createFromTimestamp($value);
+        }
+
+        // If the value is in simply year, month, day format, we will instantiate the
+        // Carbon instances from that format. Again, this provides for simple date
+        // fields on the database, while still supporting Carbonized conversion.
+        if ($this->isStandardDateFormat($value)) {
+            return Date::instance(Carbon::createFromFormat('Y-m-d', $value)->startOfDay());
+        }
+
+        $format = $this->getDateFormat();
+
+        // Finally, we will just assume this date is in the format used by default on
+        // the database connection and use that format to create the Carbon object
+        // that is returned back out to the developers after we convert it here.
+        try {
+            $date = Date::createFromFormat($format, $value);
+        } catch (InvalidArgumentException $e) {
+            $date = false;
+        }
+
+        return $date ?: Date::parse($value);
+    }
+
+    /**
+     * Determine if the given value is a standard date format.
+     *
+     * @param  string  $value
+     * @return bool
+     */
+    protected function isStandardDateFormat($value)
+    {
+        return preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})$/', $value);
+    }
+
+    /**
+     * Convert a DateTime to a storable string.
+     *
+     * @param  mixed  $value
+     * @return string|null
+     */
+    public function fromDateTime($value)
+    {
+        return empty($value) ? $value : $this->asDateTime($value)->format(
+            $this->getDateFormat()
+        );
+    }
+
+    /**
+     * Return a timestamp as unix timestamp.
+     *
+     * @param  mixed  $value
+     * @return int
+     */
+    protected function asTimestamp($value)
+    {
+        return $this->asDateTime($value)->getTimestamp();
+    }
+
+    /**
+     * Prepare a date for array / JSON serialization.
+     *
+     * @param  \DateTimeInterface  $date
+     * @return string
+     */
+    protected function serializeDate(DateTimeInterface $date)
+    {
+        return $date instanceof DateTimeImmutable ?
+            CarbonImmutable::instance($date)->toJSON() :
+            Carbon::instance($date)->toJSON();
+    }
+
+    /**
+     * Get the attributes that should be converted to dates.
+     *
+     * @return array
+     */
+    public function getDates()
+    {
+        if (! $this->usesTimestamps()) {
+            return $this->dates;
+        }
+
+        $defaults = [
+            $this->getCreatedAtColumn(),
+            $this->getUpdatedAtColumn(),
+        ];
+
+        return array_unique(array_merge($this->dates, $defaults));
+    }
+
+    /**
+     * Get the format for database stored dates.
+     *
+     * @return string
+     */
+    public function getDateFormat()
+    {
+        return $this->dateFormat ?: $this->getConnection()->getQueryGrammar()->getDateFormat();
+    }
+
+    /**
+     * Set the date format used by the model.
+     *
+     * @param  string  $format
+     * @return $this
+     */
+    public function setDateFormat($format)
+    {
+        $this->dateFormat = $format;
+
+        return $this;
+    }
+
+    /**
+     * Determine whether an attribute should be cast to a native type.
+     *
+     * @param  string  $key
+     * @param  array|string|null  $types
+     * @return bool
+     */
+    public function hasCast($key, $types = null)
+    {
+        if (array_key_exists($key, $this->getCasts())) {
+            return $types ? in_array($this->getCastType($key), (array) $types, true) : true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the casts array.
+     *
+     * @return array
+     */
+    public function getCasts()
+    {
+        if ($this->getIncrementing()) {
+            return array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts);
+        }
+
+        return $this->casts;
+    }
+
+    /**
+     * Determine whether a value is Date / DateTime castable for inbound manipulation.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    protected function isDateCastable($key)
+    {
+        return $this->hasCast($key, ['date', 'datetime', 'immutable_date', 'immutable_datetime']);
+    }
+
+    /**
+     * Determine whether a value is Date / DateTime custom-castable for inbound manipulation.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    protected function isDateCastableWithCustomFormat($key)
+    {
+        return $this->hasCast($key, ['custom_datetime', 'immutable_custom_datetime']);
+    }
+
+    /**
+     * Determine whether a value is JSON castable for inbound manipulation.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    protected function isJsonCastable($key)
+    {
+        return $this->hasCast($key, ['array', 'json', 'object', 'collection', 'encrypted:array', 'encrypted:collection', 'encrypted:json', 'encrypted:object']);
+    }
+
+    /**
+     * Determine whether a value is an encrypted castable for inbound manipulation.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    protected function isEncryptedCastable($key)
+    {
+        return $this->hasCast($key, ['encrypted', 'encrypted:array', 'encrypted:collection', 'encrypted:json', 'encrypted:object']);
+    }
+
+    /**
+     * Determine if the given key is cast using a custom class.
+     *
+     * @param  string  $key
+     * @return bool
+     *
+     * @throws \Illuminate\Database\Eloquent\InvalidCastException
+     */
+    protected function isClassCastable($key)
+    {
+        $casts = $this->getCasts();
+
+        if (! array_key_exists($key, $casts)) {
+            return false;
+        }
+
+        $castType = $this->parseCasterClass($casts[$key]);
+
+        if (in_array($castType, static::$primitiveCastTypes)) {
+            return false;
+        }
+
+        if (class_exists($castType)) {
+            return true;
+        }
+
+        throw new InvalidCastException($this->getModel(), $key, $castType);
+    }
+
+    /**
+     * Determine if the given key is cast using an enum.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    protected function isEnumCastable($key)
+    {
+        $casts = $this->getCasts();
+
+        if (! array_key_exists($key, $casts)) {
+            return false;
+        }
+
+        $castType = $casts[$key];
+
+        if (in_array($castType, static::$primitiveCastTypes)) {
+            return false;
+        }
+
+        if (function_exists('enum_exists') && enum_exists($castType)) {
+            return true;
+        }
+    }
+
+    /**
+     * Determine if the key is deviable using a custom class.
+     *
+     * @param  string  $key
+     * @return bool
+     *
+     * @throws \Illuminate\Database\Eloquent\InvalidCastException
+     */
+    protected function isClassDeviable($key)
+    {
+        if (! $this->isClassCastable($key)) {
+            return false;
+        }
+
+        $castType = $this->resolveCasterClass($key);
+
+        return method_exists($castType::class, 'increment') && method_exists($castType::class, 'decrement');
+    }
+
+    /**
+     * Determine if the key is serializable using a custom class.
+     *
+     * @param  string  $key
+     * @return bool
+     *
+     * @throws \Illuminate\Database\Eloquent\InvalidCastException
+     */
+    protected function isClassSerializable($key)
+    {
+        return ! $this->isEnumCastable($key) &&
+            $this->isClassCastable($key) &&
+            method_exists($this->resolveCasterClass($key), 'serialize');
+    }
+
+    /**
+     * Resolve the custom caster class for a given key.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    protected function resolveCasterClass($key)
+    {
+        $castType = $this->getCasts()[$key];
+
+        $arguments = [];
+
+        if (is_string($castType) && str_contains($castType, ':')) {
+            $segments = explode(':', $castType, 2);
+
+            $castType = $segments[0];
+            $arguments = explode(',', $segments[1]);
+        }
+
+        if (is_subclass_of($castType, Castable::class)) {
+            $castType = $castType::castUsing($arguments);
+        }
+
+        if (is_object($castType)) {
+            return $castType;
+        }
+
+        return new $castType(...$arguments);
+    }
+
+    /**
+     * Parse the given caster class, removing any arguments.
+     *
+     * @param  string  $class
+     * @return string
+     */
+    protected function parseCasterClass($class)
+    {
+        return ! str_contains($class, ':')
+            ? $class
+            : explode(':', $class, 2)[0];
+    }
+
+    /**
+     * Merge the cast class and attribute cast attributes back into the model.
+     *
+     * @return void
+     */
+    protected function mergeAttributesFromCachedCasts()
+    {
+        $this->mergeAttributesFromClassCasts();
+        $this->mergeAttributesFromAttributeCasts();
+    }
+
+    /**
+     * Merge the cast class attributes back into the model.
+     *
+     * @return void
+     */
+    protected function mergeAttributesFromClassCasts()
+    {
+        foreach ($this->classCastCache as $key => $value) {
+            $caster = $this->resolveCasterClass($key);
+
+            $this->attributes = array_merge(
+                $this->attributes,
+                $caster instanceof CastsInboundAttributes
+                    ? [$key => $value]
+                    : $this->normalizeCastClassResponse($key, $caster->set($this, $key, $value, $this->attributes))
+            );
+        }
+    }
+
+    /**
+     * Merge the cast class attributes back into the model.
+     *
+     * @return void
+     */
+    protected function mergeAttributesFromAttributeCasts()
+    {
+        foreach ($this->attributeCastCache as $key => $value) {
+            $attribute = $this->{Str::camel($key)}();
+
+            if ($attribute->get && ! $attribute->set) {
+                continue;
+            }
+
+            $callback = $attribute->set ?: function ($value) use ($key) {
+                $this->attributes[$key] = $value;
+            };
+
+            $this->attributes = array_merge(
+                $this->attributes,
+                $this->normalizeCastClassResponse(
+                    $key, $callback($value, $this->attributes)
+                )
+            );
+        }
+    }
+
+    /**
+     * Normalize the response from a custom class caster.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return array
+     */
+    protected function normalizeCastClassResponse($key, $value)
+    {
+        return is_array($value) ? $value : [$key => $value];
+    }
+
+    /**
+     * Get all of the current attributes on the model.
+     *
+     * @return array
+     */
+    public function getAttributes()
+    {
+        $this->mergeAttributesFromCachedCasts();
+
+        return $this->attributes;
+    }
+
+    /**
+     * Get all of the current attributes on the model for an insert operation.
+     *
+     * @return array
+     */
+    protected function getAttributesForInsert()
+    {
+        return $this->getAttributes();
+    }
+
+    /**
+     * Set the array of model attributes. No checking is done.
+     *
+     * @param  array  $attributes
+     * @param  bool  $sync
+     * @return $this
+     */
+    public function setRawAttributes(array $attributes, $sync = false)
+    {
+        $this->attributes = $attributes;
+
+        if ($sync) {
+            $this->syncOriginal();
+        }
+
+        $this->classCastCache = [];
+        $this->attributeCastCache = [];
+
+        return $this;
+    }
+
+    /**
+     * Get the model's original attribute values.
+     *
+     * @param  string|null  $key
+     * @param  mixed  $default
+     * @return mixed|array
+     */
+    public function getOriginal($key = null, $default = null)
+    {
+        return (new static)->setRawAttributes(
+            $this->original, $sync = true
+        )->getOriginalWithoutRewindingModel($key, $default);
+    }
+
+    /**
+     * Get the model's original attribute values.
+     *
+     * @param  string|null  $key
+     * @param  mixed  $default
+     * @return mixed|array
+     */
+    protected function getOriginalWithoutRewindingModel($key = null, $default = null)
+    {
+        if ($key) {
+            return $this->transformModelValue(
+                $key, Arr::get($this->original, $key, $default)
+            );
+        }
+
+        return collect($this->original)->mapWithKeys(function ($value, $key) {
+            return [$key => $this->transformModelValue($key, $value)];
+        })->all();
+    }
+
+    /**
+     * Get the model's raw original attribute values.
+     *
+     * @param  string|null  $key
+     * @param  mixed  $default
+     * @return mixed|array
+     */
+    public function getRawOriginal($key = null, $default = null)
+    {
+        return Arr::get($this->original, $key, $default);
+    }
+
+    /**
+     * Get a subset of the model's attributes.
+     *
+     * @param  array|mixed  $attributes
+     * @return array
+     */
+    public function only($attributes)
+    {
+        $results = [];
+
+        foreach (is_array($attributes) ? $attributes : func_get_args() as $attribute) {
+            $results[$attribute] = $this->getAttribute($attribute);
+        }
+
+        return $results;
+    }
+
+    /**
+     * Sync the original attributes with the current.
+     *
+     * @return $this
+     */
+    public function syncOriginal()
+    {
+        $this->original = $this->getAttributes();
+
+        return $this;
+    }
+
+    /**
+     * Sync a single original attribute with its current value.
+     *
+     * @param  string  $attribute
+     * @return $this
+     */
+    public function syncOriginalAttribute($attribute)
+    {
+        return $this->syncOriginalAttributes($attribute);
+    }
+
+    /**
+     * Sync multiple original attribute with their current values.
+     *
+     * @param  array|string  $attributes
+     * @return $this
+     */
+    public function syncOriginalAttributes($attributes)
+    {
+        $attributes = is_array($attributes) ? $attributes : func_get_args();
+
+        $modelAttributes = $this->getAttributes();
+
+        foreach ($attributes as $attribute) {
+            $this->original[$attribute] = $modelAttributes[$attribute];
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sync the changed attributes.
+     *
+     * @return $this
+     */
+    public function syncChanges()
+    {
+        $this->changes = $this->getDirty();
+
+        return $this;
+    }
+
+    /**
+     * Determine if the model or any of the given attribute(s) have been modified.
+     *
+     * @param  array|string|null  $attributes
+     * @return bool
+     */
+    public function isDirty($attributes = null)
+    {
+        return $this->hasChanges(
+            $this->getDirty(), is_array($attributes) ? $attributes : func_get_args()
+        );
+    }
+
+    /**
+     * Determine if the model or all the given attribute(s) have remained the same.
+     *
+     * @param  array|string|null  $attributes
+     * @return bool
+     */
+    public function isClean($attributes = null)
+    {
+        return ! $this->isDirty(...func_get_args());
+    }
+
+    /**
+     * Discard attribute changes and reset the attributes to their original state.
+     *
+     * @return $this
+     */
+    public function discardChanges()
+    {
+        [$this->attributes, $this->changes] = [$this->original, []];
+
+        return $this;
+    }
+
+    /**
+     * Determine if the model or any of the given attribute(s) were changed when the model was last saved.
+     *
+     * @param  array|string|null  $attributes
+     * @return bool
+     */
+    public function wasChanged($attributes = null)
+    {
+        return $this->hasChanges(
+            $this->getChanges(), is_array($attributes) ? $attributes : func_get_args()
+        );
+    }
+
+    /**
+     * Determine if any of the given attributes were changed when the model was last saved.
+     *
+     * @param  array  $changes
+     * @param  array|string|null  $attributes
+     * @return bool
+     */
+    protected function hasChanges($changes, $attributes = null)
+    {
+        // If no specific attributes were provided, we will just see if the dirty array
+        // already contains any attributes. If it does we will just return that this
+        // count is greater than zero. Else, we need to check specific attributes.
+        if (empty($attributes)) {
+            return count($changes) > 0;
+        }
+
+        // Here we will spin through every attribute and see if this is in the array of
+        // dirty attributes. If it is, we will return true and if we make it through
+        // all of the attributes for the entire array we will return false at end.
+        foreach (Arr::wrap($attributes) as $attribute) {
+            if (array_key_exists($attribute, $changes)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the attributes that have been changed since the last sync.
+     *
+     * @return array
+     */
+    public function getDirty()
+    {
+        $dirty = [];
+
+        foreach ($this->getAttributes() as $key => $value) {
+            if (! $this->originalIsEquivalent($key)) {
+                $dirty[$key] = $value;
+            }
+        }
+
+        return $dirty;
+    }
+
+    /**
+     * Get the attributes that were changed when the model was last saved.
+     *
+     * @return array
+     */
+    public function getChanges()
+    {
+        return $this->changes;
+    }
+
+    /**
+     * Determine if the new and old values for a given key are equivalent.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    public function originalIsEquivalent($key)
+    {
+        if (! array_key_exists($key, $this->original)) {
+            return false;
+        }
+
+        $attribute = Arr::get($this->attributes, $key);
+        $original = Arr::get($this->original, $key);
+
+        if ($attribute === $original) {
+            return true;
+        } elseif (is_null($attribute)) {
+            return false;
+        } elseif ($this->isDateAttribute($key) || $this->isDateCastableWithCustomFormat($key)) {
+            return $this->fromDateTime($attribute) ===
+                $this->fromDateTime($original);
+        } elseif ($this->hasCast($key, ['object', 'collection'])) {
+            return $this->fromJson($attribute) ===
+                $this->fromJson($original);
+        } elseif ($this->hasCast($key, ['real', 'float', 'double'])) {
+            if ($original === null) {
+                return false;
+            }
+
+            return abs($this->castAttribute($key, $attribute) - $this->castAttribute($key, $original)) < PHP_FLOAT_EPSILON * 4;
+        } elseif ($this->hasCast($key, static::$primitiveCastTypes)) {
+            return $this->castAttribute($key, $attribute) ===
+                $this->castAttribute($key, $original);
+        } elseif ($this->isClassCastable($key) && in_array($this->getCasts()[$key], [AsArrayObject::class, AsCollection::class])) {
+            return $this->fromJson($attribute) === $this->fromJson($original);
+        } elseif ($this->isClassCastable($key) && $original !== null && in_array($this->getCasts()[$key], [AsEncryptedArrayObject::class, AsEncryptedCollection::class])) {
+            return $this->fromEncryptedString($attribute) === $this->fromEncryptedString($original);
+        }
+
+        return is_numeric($attribute) && is_numeric($original)
+            && strcmp((string) $attribute, (string) $original) === 0;
+    }
+
+    /**
+     * Transform a raw model value using mutators, casts, etc.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function transformModelValue($key, $value)
+    {
+        // If the attribute has a get mutator, we will call that then return what
+        // it returns as the value, which is useful for transforming values on
+        // retrieval from the model to a form that is more useful for usage.
+        if ($this->hasGetMutator($key)) {
+            return $this->mutateAttribute($key, $value);
+        } elseif ($this->hasAttributeGetMutator($key)) {
+            return $this->mutateAttributeMarkedAttribute($key, $value);
+        }
+
+        // If the attribute exists within the cast array, we will convert it to
+        // an appropriate native PHP type dependent upon the associated value
+        // given with the key in the pair. Dayle made this comment line up.
+        if ($this->hasCast($key)) {
+            return $this->castAttribute($key, $value);
+        }
+
+        // If the attribute is listed as a date, we will convert it to a DateTime
+        // instance on retrieval, which makes it quite convenient to work with
+        // date fields without having to create a mutator for each property.
+        if ($value !== null
+            && \in_array($key, $this->getDates(), false)) {
+            return $this->asDateTime($value);
+        }
+
+        return $value;
+    }
+
+    /**
+     * Append attributes to query when building a query.
+     *
+     * @param  array|string  $attributes
+     * @return $this
+     */
+    public function append($attributes)
+    {
+        $this->appends = array_unique(
+            array_merge($this->appends, is_string($attributes) ? func_get_args() : $attributes)
+        );
+
+        return $this;
+    }
+
+    /**
+     * Get the accessors that are being appended to model arrays.
+     *
+     * @return array
+     */
+    public function getAppends()
+    {
+        return $this->appends;
+    }
+
+    /**
+     * Set the accessors to append to model arrays.
+     *
+     * @param  array  $appends
+     * @return $this
+     */
+    public function setAppends(array $appends)
+    {
+        $this->appends = $appends;
+
+        return $this;
+    }
+
+    /**
+     * Return whether the accessor attribute has been appended.
+     *
+     * @param  string  $attribute
+     * @return bool
+     */
+    public function hasAppended($attribute)
+    {
+        return in_array($attribute, $this->appends);
+    }
+
+    /**
+     * Get the mutated attributes for a given instance.
+     *
+     * @return array
+     */
+    public function getMutatedAttributes()
+    {
+        if (! isset(static::$mutatorCache[static::class])) {
+            static::cacheMutatedAttributes($this);
+        }
+
+        return static::$mutatorCache[static::class];
+    }
+
+    /**
+     * Extract and cache all the mutated attributes of a class.
+     *
+     * @param  object|string  $classOrInstance
+     * @return void
+     */
+    public static function cacheMutatedAttributes($classOrInstance)
+    {
+        $reflection = new ReflectionClass($classOrInstance);
+
+        $class = $reflection->getName();
+
+        static::$getAttributeMutatorCache[$class] =
+            collect($attributeMutatorMethods = static::getAttributeMarkedMutatorMethods($classOrInstance))
+                    ->mapWithKeys(function ($match) {
+                        return [lcfirst(static::$snakeAttributes ? Str::snake($match) : $match) => true];
+                    })->all();
+
+        static::$mutatorCache[$class] = collect(static::getMutatorMethods($class))
+                ->merge($attributeMutatorMethods)
+                ->map(function ($match) {
+                    return lcfirst(static::$snakeAttributes ? Str::snake($match) : $match);
+                })->all();
+    }
+
+    /**
+     * Get all of the attribute mutator methods.
+     *
+     * @param  mixed  $class
+     * @return array
+     */
+    protected static function getMutatorMethods($class)
+    {
+        preg_match_all('/(?<=^|;)get([^;]+?)Attribute(;|$)/', implode(';', get_class_methods($class)), $matches);
+
+        return $matches[1];
+    }
+
+    /**
+     * Get all of the "Attribute" return typed attribute mutator methods.
+     *
+     * @param  mixed  $class
+     * @return array
+     */
+    protected static function getAttributeMarkedMutatorMethods($class)
+    {
+        $instance = is_object($class) ? $class : new $class;
+
+        return collect((new ReflectionClass($instance))->getMethods())->filter(function ($method) use ($instance) {
+            $returnType = $method->getReturnType();
+
+            if ($returnType instanceof ReflectionNamedType &&
+                $returnType->getName() === Attribute::class) {
+                $method->setAccessible(true);
+
+                if (is_callable($method->invoke($instance)->get)) {
+                    return true;
+                }
+            }
+
+            return false;
+        })->map->name->values()->all();
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Concerns/HasEvents.php b/vendor/illuminate/database/Eloquent/Concerns/HasEvents.php
new file mode 100644
index 0000000..37bc063
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Concerns/HasEvents.php
@@ -0,0 +1,415 @@
+registerObserver($class);
+        }
+    }
+
+    /**
+     * Register a single observer with the model.
+     *
+     * @param  object|string  $class
+     * @return void
+     *
+     * @throws \RuntimeException
+     */
+    protected function registerObserver($class)
+    {
+        $className = $this->resolveObserverClassName($class);
+
+        // When registering a model observer, we will spin through the possible events
+        // and determine if this observer has that method. If it does, we will hook
+        // it into the model's event system, making it convenient to watch these.
+        foreach ($this->getObservableEvents() as $event) {
+            if (method_exists($class, $event)) {
+                static::registerModelEvent($event, $className.'@'.$event);
+            }
+        }
+    }
+
+    /**
+     * Resolve the observer's class name from an object or string.
+     *
+     * @param  object|string  $class
+     * @return string
+     *
+     * @throws \InvalidArgumentException
+     */
+    private function resolveObserverClassName($class)
+    {
+        if (is_object($class)) {
+            return get_class($class);
+        }
+
+        if (class_exists($class)) {
+            return $class;
+        }
+
+        throw new InvalidArgumentException('Unable to find observer: '.$class);
+    }
+
+    /**
+     * Get the observable event names.
+     *
+     * @return array
+     */
+    public function getObservableEvents()
+    {
+        return array_merge(
+            [
+                'retrieved', 'creating', 'created', 'updating', 'updated',
+                'saving', 'saved', 'restoring', 'restored', 'replicating',
+                'deleting', 'deleted', 'forceDeleting', 'forceDeleted',
+            ],
+            $this->observables
+        );
+    }
+
+    /**
+     * Set the observable event names.
+     *
+     * @param  array  $observables
+     * @return $this
+     */
+    public function setObservableEvents(array $observables)
+    {
+        $this->observables = $observables;
+
+        return $this;
+    }
+
+    /**
+     * Add an observable event name.
+     *
+     * @param  array|mixed  $observables
+     * @return void
+     */
+    public function addObservableEvents($observables)
+    {
+        $this->observables = array_unique(array_merge(
+            $this->observables, is_array($observables) ? $observables : func_get_args()
+        ));
+    }
+
+    /**
+     * Remove an observable event name.
+     *
+     * @param  array|mixed  $observables
+     * @return void
+     */
+    public function removeObservableEvents($observables)
+    {
+        $this->observables = array_diff(
+            $this->observables, is_array($observables) ? $observables : func_get_args()
+        );
+    }
+
+    /**
+     * Register a model event with the dispatcher.
+     *
+     * @param  string  $event
+     * @param  \Illuminate\Events\QueuedClosure|\Closure|string|array  $callback
+     * @return void
+     */
+    protected static function registerModelEvent($event, $callback)
+    {
+        if (isset(static::$dispatcher)) {
+            $name = static::class;
+
+            static::$dispatcher->listen("eloquent.{$event}: {$name}", $callback);
+        }
+    }
+
+    /**
+     * Fire the given event for the model.
+     *
+     * @param  string  $event
+     * @param  bool  $halt
+     * @return mixed
+     */
+    protected function fireModelEvent($event, $halt = true)
+    {
+        if (! isset(static::$dispatcher)) {
+            return true;
+        }
+
+        // First, we will get the proper method to call on the event dispatcher, and then we
+        // will attempt to fire a custom, object based event for the given event. If that
+        // returns a result we can return that result, or we'll call the string events.
+        $method = $halt ? 'until' : 'dispatch';
+
+        $result = $this->filterModelEventResults(
+            $this->fireCustomModelEvent($event, $method)
+        );
+
+        if ($result === false) {
+            return false;
+        }
+
+        return ! empty($result) ? $result : static::$dispatcher->{$method}(
+            "eloquent.{$event}: ".static::class, $this
+        );
+    }
+
+    /**
+     * Fire a custom model event for the given event.
+     *
+     * @param  string  $event
+     * @param  string  $method
+     * @return mixed|null
+     */
+    protected function fireCustomModelEvent($event, $method)
+    {
+        if (! isset($this->dispatchesEvents[$event])) {
+            return;
+        }
+
+        $result = static::$dispatcher->$method(new $this->dispatchesEvents[$event]($this));
+
+        if (! is_null($result)) {
+            return $result;
+        }
+    }
+
+    /**
+     * Filter the model event results.
+     *
+     * @param  mixed  $result
+     * @return mixed
+     */
+    protected function filterModelEventResults($result)
+    {
+        if (is_array($result)) {
+            $result = array_filter($result, function ($response) {
+                return ! is_null($response);
+            });
+        }
+
+        return $result;
+    }
+
+    /**
+     * Register a retrieved model event with the dispatcher.
+     *
+     * @param  \Illuminate\Events\QueuedClosure|\Closure|string|array  $callback
+     * @return void
+     */
+    public static function retrieved($callback)
+    {
+        static::registerModelEvent('retrieved', $callback);
+    }
+
+    /**
+     * Register a saving model event with the dispatcher.
+     *
+     * @param  \Illuminate\Events\QueuedClosure|\Closure|string|array  $callback
+     * @return void
+     */
+    public static function saving($callback)
+    {
+        static::registerModelEvent('saving', $callback);
+    }
+
+    /**
+     * Register a saved model event with the dispatcher.
+     *
+     * @param  \Illuminate\Events\QueuedClosure|\Closure|string|array  $callback
+     * @return void
+     */
+    public static function saved($callback)
+    {
+        static::registerModelEvent('saved', $callback);
+    }
+
+    /**
+     * Register an updating model event with the dispatcher.
+     *
+     * @param  \Illuminate\Events\QueuedClosure|\Closure|string|array  $callback
+     * @return void
+     */
+    public static function updating($callback)
+    {
+        static::registerModelEvent('updating', $callback);
+    }
+
+    /**
+     * Register an updated model event with the dispatcher.
+     *
+     * @param  \Illuminate\Events\QueuedClosure|\Closure|string|array  $callback
+     * @return void
+     */
+    public static function updated($callback)
+    {
+        static::registerModelEvent('updated', $callback);
+    }
+
+    /**
+     * Register a creating model event with the dispatcher.
+     *
+     * @param  \Illuminate\Events\QueuedClosure|\Closure|string|array  $callback
+     * @return void
+     */
+    public static function creating($callback)
+    {
+        static::registerModelEvent('creating', $callback);
+    }
+
+    /**
+     * Register a created model event with the dispatcher.
+     *
+     * @param  \Illuminate\Events\QueuedClosure|\Closure|string|array  $callback
+     * @return void
+     */
+    public static function created($callback)
+    {
+        static::registerModelEvent('created', $callback);
+    }
+
+    /**
+     * Register a replicating model event with the dispatcher.
+     *
+     * @param  \Illuminate\Events\QueuedClosure|\Closure|string|array  $callback
+     * @return void
+     */
+    public static function replicating($callback)
+    {
+        static::registerModelEvent('replicating', $callback);
+    }
+
+    /**
+     * Register a deleting model event with the dispatcher.
+     *
+     * @param  \Illuminate\Events\QueuedClosure|\Closure|string|array  $callback
+     * @return void
+     */
+    public static function deleting($callback)
+    {
+        static::registerModelEvent('deleting', $callback);
+    }
+
+    /**
+     * Register a deleted model event with the dispatcher.
+     *
+     * @param  \Illuminate\Events\QueuedClosure|\Closure|string|array  $callback
+     * @return void
+     */
+    public static function deleted($callback)
+    {
+        static::registerModelEvent('deleted', $callback);
+    }
+
+    /**
+     * Remove all the event listeners for the model.
+     *
+     * @return void
+     */
+    public static function flushEventListeners()
+    {
+        if (! isset(static::$dispatcher)) {
+            return;
+        }
+
+        $instance = new static;
+
+        foreach ($instance->getObservableEvents() as $event) {
+            static::$dispatcher->forget("eloquent.{$event}: ".static::class);
+        }
+
+        foreach (array_values($instance->dispatchesEvents) as $event) {
+            static::$dispatcher->forget($event);
+        }
+    }
+
+    /**
+     * Get the event dispatcher instance.
+     *
+     * @return \Illuminate\Contracts\Events\Dispatcher
+     */
+    public static function getEventDispatcher()
+    {
+        return static::$dispatcher;
+    }
+
+    /**
+     * Set the event dispatcher instance.
+     *
+     * @param  \Illuminate\Contracts\Events\Dispatcher  $dispatcher
+     * @return void
+     */
+    public static function setEventDispatcher(Dispatcher $dispatcher)
+    {
+        static::$dispatcher = $dispatcher;
+    }
+
+    /**
+     * Unset the event dispatcher for models.
+     *
+     * @return void
+     */
+    public static function unsetEventDispatcher()
+    {
+        static::$dispatcher = null;
+    }
+
+    /**
+     * Execute a callback without firing any model events for any model type.
+     *
+     * @param  callable  $callback
+     * @return mixed
+     */
+    public static function withoutEvents(callable $callback)
+    {
+        $dispatcher = static::getEventDispatcher();
+
+        if ($dispatcher) {
+            static::setEventDispatcher(new NullDispatcher($dispatcher));
+        }
+
+        try {
+            return $callback();
+        } finally {
+            if ($dispatcher) {
+                static::setEventDispatcher($dispatcher);
+            }
+        }
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Concerns/HasGlobalScopes.php b/vendor/illuminate/database/Eloquent/Concerns/HasGlobalScopes.php
new file mode 100644
index 0000000..72afb17
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Concerns/HasGlobalScopes.php
@@ -0,0 +1,71 @@
+relationResolver($parent, $key);
+        }
+
+        return null;
+    }
+
+    /**
+     * Define a dynamic relation resolver.
+     *
+     * @param  string  $name
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public static function resolveRelationUsing($name, Closure $callback)
+    {
+        static::$relationResolvers = array_replace_recursive(
+            static::$relationResolvers,
+            [static::class => [$name => $callback]]
+        );
+    }
+
+    /**
+     * Define a one-to-one relationship.
+     *
+     * @param  string  $related
+     * @param  string|null  $foreignKey
+     * @param  string|null  $localKey
+     * @return \Illuminate\Database\Eloquent\Relations\HasOne
+     */
+    public function hasOne($related, $foreignKey = null, $localKey = null)
+    {
+        $instance = $this->newRelatedInstance($related);
+
+        $foreignKey = $foreignKey ?: $this->getForeignKey();
+
+        $localKey = $localKey ?: $this->getKeyName();
+
+        return $this->newHasOne($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey);
+    }
+
+    /**
+     * Instantiate a new HasOne relationship.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @param  string  $foreignKey
+     * @param  string  $localKey
+     * @return \Illuminate\Database\Eloquent\Relations\HasOne
+     */
+    protected function newHasOne(Builder $query, Model $parent, $foreignKey, $localKey)
+    {
+        return new HasOne($query, $parent, $foreignKey, $localKey);
+    }
+
+    /**
+     * Define a has-one-through relationship.
+     *
+     * @param  string  $related
+     * @param  string  $through
+     * @param  string|null  $firstKey
+     * @param  string|null  $secondKey
+     * @param  string|null  $localKey
+     * @param  string|null  $secondLocalKey
+     * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
+     */
+    public function hasOneThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null)
+    {
+        $through = $this->newRelatedThroughInstance($through);
+
+        $firstKey = $firstKey ?: $this->getForeignKey();
+
+        $secondKey = $secondKey ?: $through->getForeignKey();
+
+        return $this->newHasOneThrough(
+            $this->newRelatedInstance($related)->newQuery(), $this, $through,
+            $firstKey, $secondKey, $localKey ?: $this->getKeyName(),
+            $secondLocalKey ?: $through->getKeyName()
+        );
+    }
+
+    /**
+     * Instantiate a new HasOneThrough relationship.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Model  $farParent
+     * @param  \Illuminate\Database\Eloquent\Model  $throughParent
+     * @param  string  $firstKey
+     * @param  string  $secondKey
+     * @param  string  $localKey
+     * @param  string  $secondLocalKey
+     * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
+     */
+    protected function newHasOneThrough(Builder $query, Model $farParent, Model $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey)
+    {
+        return new HasOneThrough($query, $farParent, $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey);
+    }
+
+    /**
+     * Define a polymorphic one-to-one relationship.
+     *
+     * @param  string  $related
+     * @param  string  $name
+     * @param  string|null  $type
+     * @param  string|null  $id
+     * @param  string|null  $localKey
+     * @return \Illuminate\Database\Eloquent\Relations\MorphOne
+     */
+    public function morphOne($related, $name, $type = null, $id = null, $localKey = null)
+    {
+        $instance = $this->newRelatedInstance($related);
+
+        [$type, $id] = $this->getMorphs($name, $type, $id);
+
+        $table = $instance->getTable();
+
+        $localKey = $localKey ?: $this->getKeyName();
+
+        return $this->newMorphOne($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
+    }
+
+    /**
+     * Instantiate a new MorphOne relationship.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @param  string  $type
+     * @param  string  $id
+     * @param  string  $localKey
+     * @return \Illuminate\Database\Eloquent\Relations\MorphOne
+     */
+    protected function newMorphOne(Builder $query, Model $parent, $type, $id, $localKey)
+    {
+        return new MorphOne($query, $parent, $type, $id, $localKey);
+    }
+
+    /**
+     * Define an inverse one-to-one or many relationship.
+     *
+     * @param  string  $related
+     * @param  string|null  $foreignKey
+     * @param  string|null  $ownerKey
+     * @param  string|null  $relation
+     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+     */
+    public function belongsTo($related, $foreignKey = null, $ownerKey = null, $relation = null)
+    {
+        // If no relation name was given, we will use this debug backtrace to extract
+        // the calling method's name and use that as the relationship name as most
+        // of the time this will be what we desire to use for the relationships.
+        if (is_null($relation)) {
+            $relation = $this->guessBelongsToRelation();
+        }
+
+        $instance = $this->newRelatedInstance($related);
+
+        // If no foreign key was supplied, we can use a backtrace to guess the proper
+        // foreign key name by using the name of the relationship function, which
+        // when combined with an "_id" should conventionally match the columns.
+        if (is_null($foreignKey)) {
+            $foreignKey = Str::snake($relation).'_'.$instance->getKeyName();
+        }
+
+        // Once we have the foreign key names we'll just create a new Eloquent query
+        // for the related models and return the relationship instance which will
+        // actually be responsible for retrieving and hydrating every relation.
+        $ownerKey = $ownerKey ?: $instance->getKeyName();
+
+        return $this->newBelongsTo(
+            $instance->newQuery(), $this, $foreignKey, $ownerKey, $relation
+        );
+    }
+
+    /**
+     * Instantiate a new BelongsTo relationship.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Model  $child
+     * @param  string  $foreignKey
+     * @param  string  $ownerKey
+     * @param  string  $relation
+     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+     */
+    protected function newBelongsTo(Builder $query, Model $child, $foreignKey, $ownerKey, $relation)
+    {
+        return new BelongsTo($query, $child, $foreignKey, $ownerKey, $relation);
+    }
+
+    /**
+     * Define a polymorphic, inverse one-to-one or many relationship.
+     *
+     * @param  string|null  $name
+     * @param  string|null  $type
+     * @param  string|null  $id
+     * @param  string|null  $ownerKey
+     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
+     */
+    public function morphTo($name = null, $type = null, $id = null, $ownerKey = null)
+    {
+        // If no name is provided, we will use the backtrace to get the function name
+        // since that is most likely the name of the polymorphic interface. We can
+        // use that to get both the class and foreign key that will be utilized.
+        $name = $name ?: $this->guessBelongsToRelation();
+
+        [$type, $id] = $this->getMorphs(
+            Str::snake($name), $type, $id
+        );
+
+        // If the type value is null it is probably safe to assume we're eager loading
+        // the relationship. In this case we'll just pass in a dummy query where we
+        // need to remove any eager loads that may already be defined on a model.
+        return is_null($class = $this->getAttributeFromArray($type)) || $class === ''
+                    ? $this->morphEagerTo($name, $type, $id, $ownerKey)
+                    : $this->morphInstanceTo($class, $name, $type, $id, $ownerKey);
+    }
+
+    /**
+     * Define a polymorphic, inverse one-to-one or many relationship.
+     *
+     * @param  string  $name
+     * @param  string  $type
+     * @param  string  $id
+     * @param  string  $ownerKey
+     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
+     */
+    protected function morphEagerTo($name, $type, $id, $ownerKey)
+    {
+        return $this->newMorphTo(
+            $this->newQuery()->setEagerLoads([]), $this, $id, $ownerKey, $type, $name
+        );
+    }
+
+    /**
+     * Define a polymorphic, inverse one-to-one or many relationship.
+     *
+     * @param  string  $target
+     * @param  string  $name
+     * @param  string  $type
+     * @param  string  $id
+     * @param  string  $ownerKey
+     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
+     */
+    protected function morphInstanceTo($target, $name, $type, $id, $ownerKey)
+    {
+        $instance = $this->newRelatedInstance(
+            static::getActualClassNameForMorph($target)
+        );
+
+        return $this->newMorphTo(
+            $instance->newQuery(), $this, $id, $ownerKey ?? $instance->getKeyName(), $type, $name
+        );
+    }
+
+    /**
+     * Instantiate a new MorphTo relationship.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @param  string  $foreignKey
+     * @param  string  $ownerKey
+     * @param  string  $type
+     * @param  string  $relation
+     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
+     */
+    protected function newMorphTo(Builder $query, Model $parent, $foreignKey, $ownerKey, $type, $relation)
+    {
+        return new MorphTo($query, $parent, $foreignKey, $ownerKey, $type, $relation);
+    }
+
+    /**
+     * Retrieve the actual class name for a given morph class.
+     *
+     * @param  string  $class
+     * @return string
+     */
+    public static function getActualClassNameForMorph($class)
+    {
+        return Arr::get(Relation::morphMap() ?: [], $class, $class);
+    }
+
+    /**
+     * Guess the "belongs to" relationship name.
+     *
+     * @return string
+     */
+    protected function guessBelongsToRelation()
+    {
+        [$one, $two, $caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
+
+        return $caller['function'];
+    }
+
+    /**
+     * Create a pending has-many-through or has-one-through relationship.
+     *
+     * @param  string|\Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Eloquent\Relations\HasOne  $relationship
+     * @return \Illuminate\Database\Eloquent\PendingHasThroughRelationship
+     */
+    public function through($relationship)
+    {
+        if (is_string($relationship)) {
+            $relationship = $this->{$relationship}();
+        }
+
+        return new PendingHasThroughRelationship($this, $relationship);
+    }
+
+    /**
+     * Define a one-to-many relationship.
+     *
+     * @param  string  $related
+     * @param  string|null  $foreignKey
+     * @param  string|null  $localKey
+     * @return \Illuminate\Database\Eloquent\Relations\HasMany
+     */
+    public function hasMany($related, $foreignKey = null, $localKey = null)
+    {
+        $instance = $this->newRelatedInstance($related);
+
+        $foreignKey = $foreignKey ?: $this->getForeignKey();
+
+        $localKey = $localKey ?: $this->getKeyName();
+
+        return $this->newHasMany(
+            $instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey
+        );
+    }
+
+    /**
+     * Instantiate a new HasMany relationship.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @param  string  $foreignKey
+     * @param  string  $localKey
+     * @return \Illuminate\Database\Eloquent\Relations\HasMany
+     */
+    protected function newHasMany(Builder $query, Model $parent, $foreignKey, $localKey)
+    {
+        return new HasMany($query, $parent, $foreignKey, $localKey);
+    }
+
+    /**
+     * Define a has-many-through relationship.
+     *
+     * @param  string  $related
+     * @param  string  $through
+     * @param  string|null  $firstKey
+     * @param  string|null  $secondKey
+     * @param  string|null  $localKey
+     * @param  string|null  $secondLocalKey
+     * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
+     */
+    public function hasManyThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null)
+    {
+        $through = $this->newRelatedThroughInstance($through);
+
+        $firstKey = $firstKey ?: $this->getForeignKey();
+
+        $secondKey = $secondKey ?: $through->getForeignKey();
+
+        return $this->newHasManyThrough(
+            $this->newRelatedInstance($related)->newQuery(),
+            $this,
+            $through,
+            $firstKey,
+            $secondKey,
+            $localKey ?: $this->getKeyName(),
+            $secondLocalKey ?: $through->getKeyName()
+        );
+    }
+
+    /**
+     * Instantiate a new HasManyThrough relationship.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Model  $farParent
+     * @param  \Illuminate\Database\Eloquent\Model  $throughParent
+     * @param  string  $firstKey
+     * @param  string  $secondKey
+     * @param  string  $localKey
+     * @param  string  $secondLocalKey
+     * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
+     */
+    protected function newHasManyThrough(Builder $query, Model $farParent, Model $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey)
+    {
+        return new HasManyThrough($query, $farParent, $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey);
+    }
+
+    /**
+     * Define a polymorphic one-to-many relationship.
+     *
+     * @param  string  $related
+     * @param  string  $name
+     * @param  string|null  $type
+     * @param  string|null  $id
+     * @param  string|null  $localKey
+     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
+     */
+    public function morphMany($related, $name, $type = null, $id = null, $localKey = null)
+    {
+        $instance = $this->newRelatedInstance($related);
+
+        // Here we will gather up the morph type and ID for the relationship so that we
+        // can properly query the intermediate table of a relation. Finally, we will
+        // get the table and create the relationship instances for the developers.
+        [$type, $id] = $this->getMorphs($name, $type, $id);
+
+        $table = $instance->getTable();
+
+        $localKey = $localKey ?: $this->getKeyName();
+
+        return $this->newMorphMany($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
+    }
+
+    /**
+     * Instantiate a new MorphMany relationship.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @param  string  $type
+     * @param  string  $id
+     * @param  string  $localKey
+     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
+     */
+    protected function newMorphMany(Builder $query, Model $parent, $type, $id, $localKey)
+    {
+        return new MorphMany($query, $parent, $type, $id, $localKey);
+    }
+
+    /**
+     * Define a many-to-many relationship.
+     *
+     * @param  string  $related
+     * @param  string|null  $table
+     * @param  string|null  $foreignPivotKey
+     * @param  string|null  $relatedPivotKey
+     * @param  string|null  $parentKey
+     * @param  string|null  $relatedKey
+     * @param  string|null  $relation
+     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
+     */
+    public function belongsToMany($related, $table = null, $foreignPivotKey = null, $relatedPivotKey = null,
+                                  $parentKey = null, $relatedKey = null, $relation = null)
+    {
+        // If no relationship name was passed, we will pull backtraces to get the
+        // name of the calling function. We will use that function name as the
+        // title of this relation since that is a great convention to apply.
+        if (is_null($relation)) {
+            $relation = $this->guessBelongsToManyRelation();
+        }
+
+        // First, we'll need to determine the foreign key and "other key" for the
+        // relationship. Once we have determined the keys we'll make the query
+        // instances as well as the relationship instances we need for this.
+        $instance = $this->newRelatedInstance($related);
+
+        $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey();
+
+        $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey();
+
+        // If no table name was provided, we can guess it by concatenating the two
+        // models using underscores in alphabetical order. The two model names
+        // are transformed to snake case from their default CamelCase also.
+        if (is_null($table)) {
+            $table = $this->joiningTable($related, $instance);
+        }
+
+        return $this->newBelongsToMany(
+            $instance->newQuery(), $this, $table, $foreignPivotKey,
+            $relatedPivotKey, $parentKey ?: $this->getKeyName(),
+            $relatedKey ?: $instance->getKeyName(), $relation
+        );
+    }
+
+    /**
+     * Instantiate a new BelongsToMany relationship.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @param  string  $table
+     * @param  string  $foreignPivotKey
+     * @param  string  $relatedPivotKey
+     * @param  string  $parentKey
+     * @param  string  $relatedKey
+     * @param  string|null  $relationName
+     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
+     */
+    protected function newBelongsToMany(Builder $query, Model $parent, $table, $foreignPivotKey, $relatedPivotKey,
+                                        $parentKey, $relatedKey, $relationName = null)
+    {
+        return new BelongsToMany($query, $parent, $table, $foreignPivotKey, $relatedPivotKey, $parentKey, $relatedKey, $relationName);
+    }
+
+    /**
+     * Define a polymorphic many-to-many relationship.
+     *
+     * @param  string  $related
+     * @param  string  $name
+     * @param  string|null  $table
+     * @param  string|null  $foreignPivotKey
+     * @param  string|null  $relatedPivotKey
+     * @param  string|null  $parentKey
+     * @param  string|null  $relatedKey
+     * @param  bool  $inverse
+     * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
+     */
+    public function morphToMany($related, $name, $table = null, $foreignPivotKey = null,
+                                $relatedPivotKey = null, $parentKey = null,
+                                $relatedKey = null, $inverse = false)
+    {
+        $caller = $this->guessBelongsToManyRelation();
+
+        // First, we will need to determine the foreign key and "other key" for the
+        // relationship. Once we have determined the keys we will make the query
+        // instances, as well as the relationship instances we need for these.
+        $instance = $this->newRelatedInstance($related);
+
+        $foreignPivotKey = $foreignPivotKey ?: $name.'_id';
+
+        $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey();
+
+        // Now we're ready to create a new query builder for the related model and
+        // the relationship instances for this relation. This relation will set
+        // appropriate query constraints then entirely manage the hydrations.
+        if (! $table) {
+            $words = preg_split('/(_)/u', $name, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+            $lastWord = array_pop($words);
+
+            $table = implode('', $words).Str::plural($lastWord);
+        }
+
+        return $this->newMorphToMany(
+            $instance->newQuery(), $this, $name, $table,
+            $foreignPivotKey, $relatedPivotKey, $parentKey ?: $this->getKeyName(),
+            $relatedKey ?: $instance->getKeyName(), $caller, $inverse
+        );
+    }
+
+    /**
+     * Instantiate a new MorphToMany relationship.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @param  string  $name
+     * @param  string  $table
+     * @param  string  $foreignPivotKey
+     * @param  string  $relatedPivotKey
+     * @param  string  $parentKey
+     * @param  string  $relatedKey
+     * @param  string|null  $relationName
+     * @param  bool  $inverse
+     * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
+     */
+    protected function newMorphToMany(Builder $query, Model $parent, $name, $table, $foreignPivotKey,
+                                      $relatedPivotKey, $parentKey, $relatedKey,
+                                      $relationName = null, $inverse = false)
+    {
+        return new MorphToMany($query, $parent, $name, $table, $foreignPivotKey, $relatedPivotKey, $parentKey, $relatedKey,
+            $relationName, $inverse);
+    }
+
+    /**
+     * Define a polymorphic, inverse many-to-many relationship.
+     *
+     * @param  string  $related
+     * @param  string  $name
+     * @param  string|null  $table
+     * @param  string|null  $foreignPivotKey
+     * @param  string|null  $relatedPivotKey
+     * @param  string|null  $parentKey
+     * @param  string|null  $relatedKey
+     * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
+     */
+    public function morphedByMany($related, $name, $table = null, $foreignPivotKey = null,
+                                  $relatedPivotKey = null, $parentKey = null, $relatedKey = null)
+    {
+        $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey();
+
+        // For the inverse of the polymorphic many-to-many relations, we will change
+        // the way we determine the foreign and other keys, as it is the opposite
+        // of the morph-to-many method since we're figuring out these inverses.
+        $relatedPivotKey = $relatedPivotKey ?: $name.'_id';
+
+        return $this->morphToMany(
+            $related, $name, $table, $foreignPivotKey,
+            $relatedPivotKey, $parentKey, $relatedKey, true
+        );
+    }
+
+    /**
+     * Get the relationship name of the belongsToMany relationship.
+     *
+     * @return string|null
+     */
+    protected function guessBelongsToManyRelation()
+    {
+        $caller = Arr::first(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), function ($trace) {
+            return ! in_array(
+                $trace['function'],
+                array_merge(static::$manyMethods, ['guessBelongsToManyRelation'])
+            );
+        });
+
+        return ! is_null($caller) ? $caller['function'] : null;
+    }
+
+    /**
+     * Get the joining table name for a many-to-many relation.
+     *
+     * @param  string  $related
+     * @param  \Illuminate\Database\Eloquent\Model|null  $instance
+     * @return string
+     */
+    public function joiningTable($related, $instance = null)
+    {
+        // The joining table name, by convention, is simply the snake cased models
+        // sorted alphabetically and concatenated with an underscore, so we can
+        // just sort the models and join them together to get the table name.
+        $segments = [
+            $instance ? $instance->joiningTableSegment()
+                      : Str::snake(class_basename($related)),
+            $this->joiningTableSegment(),
+        ];
+
+        // Now that we have the model names in an array we can just sort them and
+        // use the implode function to join them together with an underscores,
+        // which is typically used by convention within the database system.
+        sort($segments);
+
+        return strtolower(implode('_', $segments));
+    }
+
+    /**
+     * Get this model's half of the intermediate table name for belongsToMany relationships.
+     *
+     * @return string
+     */
+    public function joiningTableSegment()
+    {
+        return Str::snake(class_basename($this));
+    }
+
+    /**
+     * Determine if the model touches a given relation.
+     *
+     * @param  string  $relation
+     * @return bool
+     */
+    public function touches($relation)
+    {
+        return in_array($relation, $this->getTouchedRelations());
+    }
+
+    /**
+     * Touch the owning relations of the model.
+     *
+     * @return void
+     */
+    public function touchOwners()
+    {
+        foreach ($this->getTouchedRelations() as $relation) {
+            $this->$relation()->touch();
+
+            if ($this->$relation instanceof self) {
+                $this->$relation->fireModelEvent('saved', false);
+
+                $this->$relation->touchOwners();
+            } elseif ($this->$relation instanceof Collection) {
+                $this->$relation->each->touchOwners();
+            }
+        }
+    }
+
+    /**
+     * Get the polymorphic relationship columns.
+     *
+     * @param  string  $name
+     * @param  string  $type
+     * @param  string  $id
+     * @return array
+     */
+    protected function getMorphs($name, $type, $id)
+    {
+        return [$type ?: $name.'_type', $id ?: $name.'_id'];
+    }
+
+    /**
+     * Get the class name for polymorphic relations.
+     *
+     * @return string
+     */
+    public function getMorphClass()
+    {
+        $morphMap = Relation::morphMap();
+
+        if (! empty($morphMap) && in_array(static::class, $morphMap)) {
+            return array_search(static::class, $morphMap, true);
+        }
+
+        if (static::class === Pivot::class) {
+            return static::class;
+        }
+
+        if (Relation::requiresMorphMap()) {
+            throw new ClassMorphViolationException($this);
+        }
+
+        return static::class;
+    }
+
+    /**
+     * Create a new model instance for a related model.
+     *
+     * @param  string  $class
+     * @return mixed
+     */
+    protected function newRelatedInstance($class)
+    {
+        return tap(new $class, function ($instance) {
+            if (! $instance->getConnectionName()) {
+                $instance->setConnection($this->connection);
+            }
+        });
+    }
+
+    /**
+     * Create a new model instance for a related "through" model.
+     *
+     * @param  string  $class
+     * @return mixed
+     */
+    protected function newRelatedThroughInstance($class)
+    {
+        return new $class;
+    }
+
+    /**
+     * Get all the loaded relations for the instance.
+     *
+     * @return array
+     */
+    public function getRelations()
+    {
+        return $this->relations;
+    }
+
+    /**
+     * Get a specified relationship.
+     *
+     * @param  string  $relation
+     * @return mixed
+     */
+    public function getRelation($relation)
+    {
+        return $this->relations[$relation];
+    }
+
+    /**
+     * Determine if the given relation is loaded.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    public function relationLoaded($key)
+    {
+        return array_key_exists($key, $this->relations);
+    }
+
+    /**
+     * Set the given relationship on the model.
+     *
+     * @param  string  $relation
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function setRelation($relation, $value)
+    {
+        $this->relations[$relation] = $value;
+
+        return $this;
+    }
+
+    /**
+     * Unset a loaded relationship.
+     *
+     * @param  string  $relation
+     * @return $this
+     */
+    public function unsetRelation($relation)
+    {
+        unset($this->relations[$relation]);
+
+        return $this;
+    }
+
+    /**
+     * Set the entire relations array on the model.
+     *
+     * @param  array  $relations
+     * @return $this
+     */
+    public function setRelations(array $relations)
+    {
+        $this->relations = $relations;
+
+        return $this;
+    }
+
+    /**
+     * Duplicate the instance and unset all the loaded relations.
+     *
+     * @return $this
+     */
+    public function withoutRelations()
+    {
+        $model = clone $this;
+
+        return $model->unsetRelations();
+    }
+
+    /**
+     * Unset all the loaded relations for the instance.
+     *
+     * @return $this
+     */
+    public function unsetRelations()
+    {
+        $this->relations = [];
+
+        return $this;
+    }
+
+    /**
+     * Get the relationships that are touched on save.
+     *
+     * @return array
+     */
+    public function getTouchedRelations()
+    {
+        return $this->touches;
+    }
+
+    /**
+     * Set the relationships that are touched on save.
+     *
+     * @param  array  $touches
+     * @return $this
+     */
+    public function setTouchedRelations(array $touches)
+    {
+        $this->touches = $touches;
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Concerns/HasTimestamps.php b/vendor/illuminate/database/Eloquent/Concerns/HasTimestamps.php
new file mode 100644
index 0000000..2b6dfab
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Concerns/HasTimestamps.php
@@ -0,0 +1,224 @@
+$attribute = $this->freshTimestamp();
+
+            return $this->save();
+        }
+
+        if (! $this->usesTimestamps()) {
+            return false;
+        }
+
+        $this->updateTimestamps();
+
+        return $this->save();
+    }
+
+    /**
+     * Update the model's update timestamp without raising any events.
+     *
+     * @param  string|null  $attribute
+     * @return bool
+     */
+    public function touchQuietly($attribute = null)
+    {
+        return static::withoutEvents(fn () => $this->touch($attribute));
+    }
+
+    /**
+     * Update the creation and update timestamps.
+     *
+     * @return $this
+     */
+    public function updateTimestamps()
+    {
+        $time = $this->freshTimestamp();
+
+        $updatedAtColumn = $this->getUpdatedAtColumn();
+
+        if (! is_null($updatedAtColumn) && ! $this->isDirty($updatedAtColumn)) {
+            $this->setUpdatedAt($time);
+        }
+
+        $createdAtColumn = $this->getCreatedAtColumn();
+
+        if (! $this->exists && ! is_null($createdAtColumn) && ! $this->isDirty($createdAtColumn)) {
+            $this->setCreatedAt($time);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set the value of the "created at" attribute.
+     *
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function setCreatedAt($value)
+    {
+        $this->{$this->getCreatedAtColumn()} = $value;
+
+        return $this;
+    }
+
+    /**
+     * Set the value of the "updated at" attribute.
+     *
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function setUpdatedAt($value)
+    {
+        $this->{$this->getUpdatedAtColumn()} = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get a fresh timestamp for the model.
+     *
+     * @return \Illuminate\Support\Carbon
+     */
+    public function freshTimestamp()
+    {
+        return Date::now();
+    }
+
+    /**
+     * Get a fresh timestamp for the model.
+     *
+     * @return string
+     */
+    public function freshTimestampString()
+    {
+        return $this->fromDateTime($this->freshTimestamp());
+    }
+
+    /**
+     * Determine if the model uses timestamps.
+     *
+     * @return bool
+     */
+    public function usesTimestamps()
+    {
+        return $this->timestamps && ! static::isIgnoringTimestamps($this::class);
+    }
+
+    /**
+     * Get the name of the "created at" column.
+     *
+     * @return string|null
+     */
+    public function getCreatedAtColumn()
+    {
+        return static::CREATED_AT;
+    }
+
+    /**
+     * Get the name of the "updated at" column.
+     *
+     * @return string|null
+     */
+    public function getUpdatedAtColumn()
+    {
+        return static::UPDATED_AT;
+    }
+
+    /**
+     * Get the fully qualified "created at" column.
+     *
+     * @return string|null
+     */
+    public function getQualifiedCreatedAtColumn()
+    {
+        return $this->qualifyColumn($this->getCreatedAtColumn());
+    }
+
+    /**
+     * Get the fully qualified "updated at" column.
+     *
+     * @return string|null
+     */
+    public function getQualifiedUpdatedAtColumn()
+    {
+        return $this->qualifyColumn($this->getUpdatedAtColumn());
+    }
+
+    /**
+     * Disable timestamps for the current class during the given callback scope.
+     *
+     * @param  callable  $callback
+     * @return mixed
+     */
+    public static function withoutTimestamps(callable $callback)
+    {
+        return static::withoutTimestampsOn([static::class], $callback);
+    }
+
+    /**
+     * Disable timestamps for the given model classes during the given callback scope.
+     *
+     * @param  array  $models
+     * @param  callable  $callback
+     * @return mixed
+     */
+    public static function withoutTimestampsOn($models, $callback)
+    {
+        static::$ignoreTimestampsOn = array_values(array_merge(static::$ignoreTimestampsOn, $models));
+
+        try {
+            return $callback();
+        } finally {
+            static::$ignoreTimestampsOn = array_values(array_diff(static::$ignoreTimestampsOn, $models));
+        }
+    }
+
+    /**
+     * Determine if the given model is ignoring timestamps / touches.
+     *
+     * @param  string|null  $class
+     * @return bool
+     */
+    public static function isIgnoringTimestamps($class = null)
+    {
+        $class ??= static::class;
+
+        foreach (static::$ignoreTimestampsOn as $ignoredClass) {
+            if ($class === $ignoredClass || is_subclass_of($class, $ignoredClass)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Concerns/HasUlids.php b/vendor/illuminate/database/Eloquent/Concerns/HasUlids.php
new file mode 100644
index 0000000..b944c5d
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Concerns/HasUlids.php
@@ -0,0 +1,96 @@
+uniqueIds() as $column) {
+                if (empty($model->{$column})) {
+                    $model->{$column} = $model->newUniqueId();
+                }
+            }
+        });
+    }
+
+    /**
+     * Generate a new ULID for the model.
+     *
+     * @return string
+     */
+    public function newUniqueId()
+    {
+        return strtolower((string) Str::ulid());
+    }
+
+    /**
+     * Retrieve the model for a bound value.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\Relation  $query
+     * @param  mixed  $value
+     * @param  string|null  $field
+     * @return \Illuminate\Database\Eloquent\Relations\Relation
+     *
+     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
+     */
+    public function resolveRouteBindingQuery($query, $value, $field = null)
+    {
+        if ($field && in_array($field, $this->uniqueIds()) && ! Str::isUlid($value)) {
+            throw (new ModelNotFoundException)->setModel(get_class($this), $value);
+        }
+
+        if (! $field && in_array($this->getRouteKeyName(), $this->uniqueIds()) && ! Str::isUlid($value)) {
+            throw (new ModelNotFoundException)->setModel(get_class($this), $value);
+        }
+
+        return parent::resolveRouteBindingQuery($query, $value, $field);
+    }
+
+    /**
+     * Get the columns that should receive a unique identifier.
+     *
+     * @return array
+     */
+    public function uniqueIds()
+    {
+        return [$this->getKeyName()];
+    }
+
+    /**
+     * Get the auto-incrementing key type.
+     *
+     * @return string
+     */
+    public function getKeyType()
+    {
+        if (in_array($this->getKeyName(), $this->uniqueIds())) {
+            return 'string';
+        }
+
+        return $this->keyType;
+    }
+
+    /**
+     * Get the value indicating whether the IDs are incrementing.
+     *
+     * @return bool
+     */
+    public function getIncrementing()
+    {
+        if (in_array($this->getKeyName(), $this->uniqueIds())) {
+            return false;
+        }
+
+        return $this->incrementing;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Concerns/HasUuids.php b/vendor/illuminate/database/Eloquent/Concerns/HasUuids.php
new file mode 100644
index 0000000..96a08b6
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Concerns/HasUuids.php
@@ -0,0 +1,96 @@
+uniqueIds() as $column) {
+                if (empty($model->{$column})) {
+                    $model->{$column} = $model->newUniqueId();
+                }
+            }
+        });
+    }
+
+    /**
+     * Generate a new UUID for the model.
+     *
+     * @return string
+     */
+    public function newUniqueId()
+    {
+        return (string) Str::orderedUuid();
+    }
+
+    /**
+     * Get the columns that should receive a unique identifier.
+     *
+     * @return array
+     */
+    public function uniqueIds()
+    {
+        return [$this->getKeyName()];
+    }
+
+    /**
+     * Retrieve the model for a bound value.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\Relation  $query
+     * @param  mixed  $value
+     * @param  string|null  $field
+     * @return \Illuminate\Database\Eloquent\Relations\Relation
+     *
+     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
+     */
+    public function resolveRouteBindingQuery($query, $value, $field = null)
+    {
+        if ($field && in_array($field, $this->uniqueIds()) && ! Str::isUuid($value)) {
+            throw (new ModelNotFoundException)->setModel(get_class($this), $value);
+        }
+
+        if (! $field && in_array($this->getRouteKeyName(), $this->uniqueIds()) && ! Str::isUuid($value)) {
+            throw (new ModelNotFoundException)->setModel(get_class($this), $value);
+        }
+
+        return parent::resolveRouteBindingQuery($query, $value, $field);
+    }
+
+    /**
+     * Get the auto-incrementing key type.
+     *
+     * @return string
+     */
+    public function getKeyType()
+    {
+        if (in_array($this->getKeyName(), $this->uniqueIds())) {
+            return 'string';
+        }
+
+        return $this->keyType;
+    }
+
+    /**
+     * Get the value indicating whether the IDs are incrementing.
+     *
+     * @return bool
+     */
+    public function getIncrementing()
+    {
+        if (in_array($this->getKeyName(), $this->uniqueIds())) {
+            return false;
+        }
+
+        return $this->incrementing;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Concerns/HidesAttributes.php b/vendor/illuminate/database/Eloquent/Concerns/HidesAttributes.php
new file mode 100644
index 0000000..5a7e3ba
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Concerns/HidesAttributes.php
@@ -0,0 +1,124 @@
+
+     */
+    protected $hidden = [];
+
+    /**
+     * The attributes that should be visible in serialization.
+     *
+     * @var array
+     */
+    protected $visible = [];
+
+    /**
+     * Get the hidden attributes for the model.
+     *
+     * @return array
+     */
+    public function getHidden()
+    {
+        return $this->hidden;
+    }
+
+    /**
+     * Set the hidden attributes for the model.
+     *
+     * @param  array  $hidden
+     * @return $this
+     */
+    public function setHidden(array $hidden)
+    {
+        $this->hidden = $hidden;
+
+        return $this;
+    }
+
+    /**
+     * Get the visible attributes for the model.
+     *
+     * @return array
+     */
+    public function getVisible()
+    {
+        return $this->visible;
+    }
+
+    /**
+     * Set the visible attributes for the model.
+     *
+     * @param  array  $visible
+     * @return $this
+     */
+    public function setVisible(array $visible)
+    {
+        $this->visible = $visible;
+
+        return $this;
+    }
+
+    /**
+     * Make the given, typically hidden, attributes visible.
+     *
+     * @param  array|string|null  $attributes
+     * @return $this
+     */
+    public function makeVisible($attributes)
+    {
+        $attributes = is_array($attributes) ? $attributes : func_get_args();
+
+        $this->hidden = array_diff($this->hidden, $attributes);
+
+        if (! empty($this->visible)) {
+            $this->visible = array_merge($this->visible, $attributes);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Make the given, typically hidden, attributes visible if the given truth test passes.
+     *
+     * @param  bool|\Closure  $condition
+     * @param  array|string|null  $attributes
+     * @return $this
+     */
+    public function makeVisibleIf($condition, $attributes)
+    {
+        return value($condition, $this) ? $this->makeVisible($attributes) : $this;
+    }
+
+    /**
+     * Make the given, typically visible, attributes hidden.
+     *
+     * @param  array|string|null  $attributes
+     * @return $this
+     */
+    public function makeHidden($attributes)
+    {
+        $this->hidden = array_merge(
+            $this->hidden, is_array($attributes) ? $attributes : func_get_args()
+        );
+
+        return $this;
+    }
+
+    /**
+     * Make the given, typically visible, attributes hidden if the given truth test passes.
+     *
+     * @param  bool|\Closure  $condition
+     * @param  array|string|null  $attributes
+     * @return $this
+     */
+    public function makeHiddenIf($condition, $attributes)
+    {
+        return value($condition, $this) ? $this->makeHidden($attributes) : $this;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Concerns/QueriesRelationships.php b/vendor/illuminate/database/Eloquent/Concerns/QueriesRelationships.php
new file mode 100644
index 0000000..6f64884
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Concerns/QueriesRelationships.php
@@ -0,0 +1,879 @@
+=', $count = 1, $boolean = 'and', Closure $callback = null)
+    {
+        if (is_string($relation)) {
+            if (str_contains($relation, '.')) {
+                return $this->hasNested($relation, $operator, $count, $boolean, $callback);
+            }
+
+            $relation = $this->getRelationWithoutConstraints($relation);
+        }
+
+        if ($relation instanceof MorphTo) {
+            return $this->hasMorph($relation, ['*'], $operator, $count, $boolean, $callback);
+        }
+
+        // If we only need to check for the existence of the relation, then we can optimize
+        // the subquery to only run a "where exists" clause instead of this full "count"
+        // clause. This will make these queries run much faster compared with a count.
+        $method = $this->canUseExistsForExistenceCheck($operator, $count)
+                        ? 'getRelationExistenceQuery'
+                        : 'getRelationExistenceCountQuery';
+
+        $hasQuery = $relation->{$method}(
+            $relation->getRelated()->newQueryWithoutRelationships(), $this
+        );
+
+        // Next we will call any given callback as an "anonymous" scope so they can get the
+        // proper logical grouping of the where clauses if needed by this Eloquent query
+        // builder. Then, we will be ready to finalize and return this query instance.
+        if ($callback) {
+            $hasQuery->callScope($callback);
+        }
+
+        return $this->addHasWhere(
+            $hasQuery, $relation, $operator, $count, $boolean
+        );
+    }
+
+    /**
+     * Add nested relationship count / exists conditions to the query.
+     *
+     * Sets up recursive call to whereHas until we finish the nested relation.
+     *
+     * @param  string  $relations
+     * @param  string  $operator
+     * @param  int  $count
+     * @param  string  $boolean
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    protected function hasNested($relations, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
+    {
+        $relations = explode('.', $relations);
+
+        $doesntHave = $operator === '<' && $count === 1;
+
+        if ($doesntHave) {
+            $operator = '>=';
+            $count = 1;
+        }
+
+        $closure = function ($q) use (&$closure, &$relations, $operator, $count, $callback) {
+            // In order to nest "has", we need to add count relation constraints on the
+            // callback Closure. We'll do this by simply passing the Closure its own
+            // reference to itself so it calls itself recursively on each segment.
+            count($relations) > 1
+                ? $q->whereHas(array_shift($relations), $closure)
+                : $q->has(array_shift($relations), $operator, $count, 'and', $callback);
+        };
+
+        return $this->has(array_shift($relations), $doesntHave ? '<' : '>=', 1, $boolean, $closure);
+    }
+
+    /**
+     * Add a relationship count / exists condition to the query with an "or".
+     *
+     * @param  string  $relation
+     * @param  string  $operator
+     * @param  int  $count
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function orHas($relation, $operator = '>=', $count = 1)
+    {
+        return $this->has($relation, $operator, $count, 'or');
+    }
+
+    /**
+     * Add a relationship count / exists condition to the query.
+     *
+     * @param  string  $relation
+     * @param  string  $boolean
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function doesntHave($relation, $boolean = 'and', Closure $callback = null)
+    {
+        return $this->has($relation, '<', 1, $boolean, $callback);
+    }
+
+    /**
+     * Add a relationship count / exists condition to the query with an "or".
+     *
+     * @param  string  $relation
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function orDoesntHave($relation)
+    {
+        return $this->doesntHave($relation, 'or');
+    }
+
+    /**
+     * Add a relationship count / exists condition to the query with where clauses.
+     *
+     * @param  string  $relation
+     * @param  \Closure|null  $callback
+     * @param  string  $operator
+     * @param  int  $count
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function whereHas($relation, Closure $callback = null, $operator = '>=', $count = 1)
+    {
+        return $this->has($relation, $operator, $count, 'and', $callback);
+    }
+
+    /**
+     * Add a relationship count / exists condition to the query with where clauses.
+     *
+     * Also load the relationship with same condition.
+     *
+     * @param  string  $relation
+     * @param  \Closure|null  $callback
+     * @param  string  $operator
+     * @param  int  $count
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function withWhereHas($relation, Closure $callback = null, $operator = '>=', $count = 1)
+    {
+        return $this->whereHas(Str::before($relation, ':'), $callback, $operator, $count)
+            ->with($callback ? [$relation => fn ($query) => $callback($query)] : $relation);
+    }
+
+    /**
+     * Add a relationship count / exists condition to the query with where clauses and an "or".
+     *
+     * @param  string  $relation
+     * @param  \Closure|null  $callback
+     * @param  string  $operator
+     * @param  int  $count
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function orWhereHas($relation, Closure $callback = null, $operator = '>=', $count = 1)
+    {
+        return $this->has($relation, $operator, $count, 'or', $callback);
+    }
+
+    /**
+     * Add a relationship count / exists condition to the query with where clauses.
+     *
+     * @param  string  $relation
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function whereDoesntHave($relation, Closure $callback = null)
+    {
+        return $this->doesntHave($relation, 'and', $callback);
+    }
+
+    /**
+     * Add a relationship count / exists condition to the query with where clauses and an "or".
+     *
+     * @param  string  $relation
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function orWhereDoesntHave($relation, Closure $callback = null)
+    {
+        return $this->doesntHave($relation, 'or', $callback);
+    }
+
+    /**
+     * Add a polymorphic relationship count / exists condition to the query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  string|array  $types
+     * @param  string  $operator
+     * @param  int  $count
+     * @param  string  $boolean
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function hasMorph($relation, $types, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
+    {
+        if (is_string($relation)) {
+            $relation = $this->getRelationWithoutConstraints($relation);
+        }
+
+        $types = (array) $types;
+
+        if ($types === ['*']) {
+            $types = $this->model->newModelQuery()->distinct()->pluck($relation->getMorphType())->filter()->all();
+        }
+
+        foreach ($types as &$type) {
+            $type = Relation::getMorphedModel($type) ?? $type;
+        }
+
+        return $this->where(function ($query) use ($relation, $callback, $operator, $count, $types) {
+            foreach ($types as $type) {
+                $query->orWhere(function ($query) use ($relation, $callback, $operator, $count, $type) {
+                    $belongsTo = $this->getBelongsToRelation($relation, $type);
+
+                    if ($callback) {
+                        $callback = function ($query) use ($callback, $type) {
+                            return $callback($query, $type);
+                        };
+                    }
+
+                    $query->where($this->qualifyColumn($relation->getMorphType()), '=', (new $type)->getMorphClass())
+                                ->whereHas($belongsTo, $callback, $operator, $count);
+                });
+            }
+        }, null, null, $boolean);
+    }
+
+    /**
+     * Get the BelongsTo relationship for a single polymorphic type.
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo  $relation
+     * @param  string  $type
+     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+     */
+    protected function getBelongsToRelation(MorphTo $relation, $type)
+    {
+        $belongsTo = Relation::noConstraints(function () use ($relation, $type) {
+            return $this->model->belongsTo(
+                $type,
+                $relation->getForeignKeyName(),
+                $relation->getOwnerKeyName()
+            );
+        });
+
+        $belongsTo->getQuery()->mergeConstraintsFrom($relation->getQuery());
+
+        return $belongsTo;
+    }
+
+    /**
+     * Add a polymorphic relationship count / exists condition to the query with an "or".
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  string|array  $types
+     * @param  string  $operator
+     * @param  int  $count
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function orHasMorph($relation, $types, $operator = '>=', $count = 1)
+    {
+        return $this->hasMorph($relation, $types, $operator, $count, 'or');
+    }
+
+    /**
+     * Add a polymorphic relationship count / exists condition to the query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  string|array  $types
+     * @param  string  $boolean
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function doesntHaveMorph($relation, $types, $boolean = 'and', Closure $callback = null)
+    {
+        return $this->hasMorph($relation, $types, '<', 1, $boolean, $callback);
+    }
+
+    /**
+     * Add a polymorphic relationship count / exists condition to the query with an "or".
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  string|array  $types
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function orDoesntHaveMorph($relation, $types)
+    {
+        return $this->doesntHaveMorph($relation, $types, 'or');
+    }
+
+    /**
+     * Add a polymorphic relationship count / exists condition to the query with where clauses.
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  string|array  $types
+     * @param  \Closure|null  $callback
+     * @param  string  $operator
+     * @param  int  $count
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function whereHasMorph($relation, $types, Closure $callback = null, $operator = '>=', $count = 1)
+    {
+        return $this->hasMorph($relation, $types, $operator, $count, 'and', $callback);
+    }
+
+    /**
+     * Add a polymorphic relationship count / exists condition to the query with where clauses and an "or".
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  string|array  $types
+     * @param  \Closure|null  $callback
+     * @param  string  $operator
+     * @param  int  $count
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function orWhereHasMorph($relation, $types, Closure $callback = null, $operator = '>=', $count = 1)
+    {
+        return $this->hasMorph($relation, $types, $operator, $count, 'or', $callback);
+    }
+
+    /**
+     * Add a polymorphic relationship count / exists condition to the query with where clauses.
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  string|array  $types
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function whereDoesntHaveMorph($relation, $types, Closure $callback = null)
+    {
+        return $this->doesntHaveMorph($relation, $types, 'and', $callback);
+    }
+
+    /**
+     * Add a polymorphic relationship count / exists condition to the query with where clauses and an "or".
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  string|array  $types
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function orWhereDoesntHaveMorph($relation, $types, Closure $callback = null)
+    {
+        return $this->doesntHaveMorph($relation, $types, 'or', $callback);
+    }
+
+    /**
+     * Add a basic where clause to a relationship query.
+     *
+     * @param  string  $relation
+     * @param  \Closure|string|array|\Illuminate\Database\Query\Expression  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function whereRelation($relation, $column, $operator = null, $value = null)
+    {
+        return $this->whereHas($relation, function ($query) use ($column, $operator, $value) {
+            if ($column instanceof Closure) {
+                $column($query);
+            } else {
+                $query->where($column, $operator, $value);
+            }
+        });
+    }
+
+    /**
+     * Add an "or where" clause to a relationship query.
+     *
+     * @param  string  $relation
+     * @param  \Closure|string|array|\Illuminate\Database\Query\Expression  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function orWhereRelation($relation, $column, $operator = null, $value = null)
+    {
+        return $this->orWhereHas($relation, function ($query) use ($column, $operator, $value) {
+            if ($column instanceof Closure) {
+                $column($query);
+            } else {
+                $query->where($column, $operator, $value);
+            }
+        });
+    }
+
+    /**
+     * Add a polymorphic relationship condition to the query with a where clause.
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  string|array  $types
+     * @param  \Closure|string|array|\Illuminate\Database\Query\Expression  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function whereMorphRelation($relation, $types, $column, $operator = null, $value = null)
+    {
+        return $this->whereHasMorph($relation, $types, function ($query) use ($column, $operator, $value) {
+            $query->where($column, $operator, $value);
+        });
+    }
+
+    /**
+     * Add a polymorphic relationship condition to the query with an "or where" clause.
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  string|array  $types
+     * @param  \Closure|string|array|\Illuminate\Database\Query\Expression  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function orWhereMorphRelation($relation, $types, $column, $operator = null, $value = null)
+    {
+        return $this->orWhereHasMorph($relation, $types, function ($query) use ($column, $operator, $value) {
+            $query->where($column, $operator, $value);
+        });
+    }
+
+    /**
+     * Add a morph-to relationship condition to the query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  \Illuminate\Database\Eloquent\Model|string  $model
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function whereMorphedTo($relation, $model, $boolean = 'and')
+    {
+        if (is_string($relation)) {
+            $relation = $this->getRelationWithoutConstraints($relation);
+        }
+
+        if (is_string($model)) {
+            $morphMap = Relation::morphMap();
+
+            if (! empty($morphMap) && in_array($model, $morphMap)) {
+                $model = array_search($model, $morphMap, true);
+            }
+
+            return $this->where($relation->getMorphType(), $model, null, $boolean);
+        }
+
+        return $this->where(function ($query) use ($relation, $model) {
+            $query->where($relation->getMorphType(), $model->getMorphClass())
+                ->where($relation->getForeignKeyName(), $model->getKey());
+        }, null, null, $boolean);
+    }
+
+    /**
+     * Add a not morph-to relationship condition to the query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  \Illuminate\Database\Eloquent\Model|string  $model
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function whereNotMorphedTo($relation, $model, $boolean = 'and')
+    {
+        if (is_string($relation)) {
+            $relation = $this->getRelationWithoutConstraints($relation);
+        }
+
+        if (is_string($model)) {
+            $morphMap = Relation::morphMap();
+
+            if (! empty($morphMap) && in_array($model, $morphMap)) {
+                $model = array_search($model, $morphMap, true);
+            }
+
+            return $this->whereNot($relation->getMorphType(), '<=>', $model, $boolean);
+        }
+
+        return $this->whereNot(function ($query) use ($relation, $model) {
+            $query->where($relation->getMorphType(), '<=>', $model->getMorphClass())
+                ->where($relation->getForeignKeyName(), '<=>', $model->getKey());
+        }, null, null, $boolean);
+    }
+
+    /**
+     * Add a morph-to relationship condition to the query with an "or where" clause.
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  \Illuminate\Database\Eloquent\Model|string  $model
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function orWhereMorphedTo($relation, $model)
+    {
+        return $this->whereMorphedTo($relation, $model, 'or');
+    }
+
+    /**
+     * Add a not morph-to relationship condition to the query with an "or where" clause.
+     *
+     * @param  \Illuminate\Database\Eloquent\Relations\MorphTo|string  $relation
+     * @param  \Illuminate\Database\Eloquent\Model|string  $model
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function orWhereNotMorphedTo($relation, $model)
+    {
+        return $this->whereNotMorphedTo($relation, $model, 'or');
+    }
+
+    /**
+     * Add a "belongs to" relationship where clause to the query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection<\Illuminate\Database\Eloquent\Model>  $related
+     * @param  string|null  $relationshipName
+     * @param  string  $boolean
+     * @return $this
+     *
+     * @throws \Illuminate\Database\Eloquent\RelationNotFoundException
+     */
+    public function whereBelongsTo($related, $relationshipName = null, $boolean = 'and')
+    {
+        if (! $related instanceof Collection) {
+            $relatedCollection = $related->newCollection([$related]);
+        } else {
+            $relatedCollection = $related;
+
+            $related = $relatedCollection->first();
+        }
+
+        if ($relatedCollection->isEmpty()) {
+            throw new InvalidArgumentException('Collection given to whereBelongsTo method may not be empty.');
+        }
+
+        if ($relationshipName === null) {
+            $relationshipName = Str::camel(class_basename($related));
+        }
+
+        try {
+            $relationship = $this->model->{$relationshipName}();
+        } catch (BadMethodCallException $exception) {
+            throw RelationNotFoundException::make($this->model, $relationshipName);
+        }
+
+        if (! $relationship instanceof BelongsTo) {
+            throw RelationNotFoundException::make($this->model, $relationshipName, BelongsTo::class);
+        }
+
+        $this->whereIn(
+            $relationship->getQualifiedForeignKeyName(),
+            $relatedCollection->pluck($relationship->getOwnerKeyName())->toArray(),
+            $boolean,
+        );
+
+        return $this;
+    }
+
+    /**
+     * Add an "BelongsTo" relationship with an "or where" clause to the query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $related
+     * @param  string|null  $relationshipName
+     * @return $this
+     *
+     * @throws \RuntimeException
+     */
+    public function orWhereBelongsTo($related, $relationshipName = null)
+    {
+        return $this->whereBelongsTo($related, $relationshipName, 'or');
+    }
+
+    /**
+     * Add subselect queries to include an aggregate value for a relationship.
+     *
+     * @param  mixed  $relations
+     * @param  string  $column
+     * @param  string  $function
+     * @return $this
+     */
+    public function withAggregate($relations, $column, $function = null)
+    {
+        if (empty($relations)) {
+            return $this;
+        }
+
+        if (is_null($this->query->columns)) {
+            $this->query->select([$this->query->from.'.*']);
+        }
+
+        $relations = is_array($relations) ? $relations : [$relations];
+
+        foreach ($this->parseWithRelations($relations) as $name => $constraints) {
+            // First we will determine if the name has been aliased using an "as" clause on the name
+            // and if it has we will extract the actual relationship name and the desired name of
+            // the resulting column. This allows multiple aggregates on the same relationships.
+            $segments = explode(' ', $name);
+
+            unset($alias);
+
+            if (count($segments) === 3 && Str::lower($segments[1]) === 'as') {
+                [$name, $alias] = [$segments[0], $segments[2]];
+            }
+
+            $relation = $this->getRelationWithoutConstraints($name);
+
+            if ($function) {
+                $hashedColumn = $this->getRelationHashedColumn($column, $relation);
+
+                $wrappedColumn = $this->getQuery()->getGrammar()->wrap(
+                    $column === '*' ? $column : $relation->getRelated()->qualifyColumn($hashedColumn)
+                );
+
+                $expression = $function === 'exists' ? $wrappedColumn : sprintf('%s(%s)', $function, $wrappedColumn);
+            } else {
+                $expression = $column;
+            }
+
+            // Here, we will grab the relationship sub-query and prepare to add it to the main query
+            // as a sub-select. First, we'll get the "has" query and use that to get the relation
+            // sub-query. We'll format this relationship name and append this column if needed.
+            $query = $relation->getRelationExistenceQuery(
+                $relation->getRelated()->newQuery(), $this, new Expression($expression)
+            )->setBindings([], 'select');
+
+            $query->callScope($constraints);
+
+            $query = $query->mergeConstraintsFrom($relation->getQuery())->toBase();
+
+            // If the query contains certain elements like orderings / more than one column selected
+            // then we will remove those elements from the query so that it will execute properly
+            // when given to the database. Otherwise, we may receive SQL errors or poor syntax.
+            $query->orders = null;
+            $query->setBindings([], 'order');
+
+            if (count($query->columns) > 1) {
+                $query->columns = [$query->columns[0]];
+                $query->bindings['select'] = [];
+            }
+
+            // Finally, we will make the proper column alias to the query and run this sub-select on
+            // the query builder. Then, we will return the builder instance back to the developer
+            // for further constraint chaining that needs to take place on the query as needed.
+            $alias ??= Str::snake(
+                preg_replace('/[^[:alnum:][:space:]_]/u', '', "$name $function $column")
+            );
+
+            if ($function === 'exists') {
+                $this->selectRaw(
+                    sprintf('exists(%s) as %s', $query->toSql(), $this->getQuery()->grammar->wrap($alias)),
+                    $query->getBindings()
+                )->withCasts([$alias => 'bool']);
+            } else {
+                $this->selectSub(
+                    $function ? $query : $query->limit(1),
+                    $alias
+                );
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get the relation hashed column name for the given column and relation.
+     *
+     * @param  string  $column
+     * @param  \Illuminate\Database\Eloquent\Relations\Relationship  $relation
+     * @return string
+     */
+    protected function getRelationHashedColumn($column, $relation)
+    {
+        if (str_contains($column, '.')) {
+            return $column;
+        }
+
+        return $this->getQuery()->from === $relation->getQuery()->getQuery()->from
+            ? "{$relation->getRelationCountHash(false)}.$column"
+            : $column;
+    }
+
+    /**
+     * Add subselect queries to count the relations.
+     *
+     * @param  mixed  $relations
+     * @return $this
+     */
+    public function withCount($relations)
+    {
+        return $this->withAggregate(is_array($relations) ? $relations : func_get_args(), '*', 'count');
+    }
+
+    /**
+     * Add subselect queries to include the max of the relation's column.
+     *
+     * @param  string|array  $relation
+     * @param  string  $column
+     * @return $this
+     */
+    public function withMax($relation, $column)
+    {
+        return $this->withAggregate($relation, $column, 'max');
+    }
+
+    /**
+     * Add subselect queries to include the min of the relation's column.
+     *
+     * @param  string|array  $relation
+     * @param  string  $column
+     * @return $this
+     */
+    public function withMin($relation, $column)
+    {
+        return $this->withAggregate($relation, $column, 'min');
+    }
+
+    /**
+     * Add subselect queries to include the sum of the relation's column.
+     *
+     * @param  string|array  $relation
+     * @param  string  $column
+     * @return $this
+     */
+    public function withSum($relation, $column)
+    {
+        return $this->withAggregate($relation, $column, 'sum');
+    }
+
+    /**
+     * Add subselect queries to include the average of the relation's column.
+     *
+     * @param  string|array  $relation
+     * @param  string  $column
+     * @return $this
+     */
+    public function withAvg($relation, $column)
+    {
+        return $this->withAggregate($relation, $column, 'avg');
+    }
+
+    /**
+     * Add subselect queries to include the existence of related models.
+     *
+     * @param  string|array  $relation
+     * @return $this
+     */
+    public function withExists($relation)
+    {
+        return $this->withAggregate($relation, '*', 'exists');
+    }
+
+    /**
+     * Add the "has" condition where clause to the query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $hasQuery
+     * @param  \Illuminate\Database\Eloquent\Relations\Relation  $relation
+     * @param  string  $operator
+     * @param  int  $count
+     * @param  string  $boolean
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    protected function addHasWhere(Builder $hasQuery, Relation $relation, $operator, $count, $boolean)
+    {
+        $hasQuery->mergeConstraintsFrom($relation->getQuery());
+
+        return $this->canUseExistsForExistenceCheck($operator, $count)
+                ? $this->addWhereExistsQuery($hasQuery->toBase(), $boolean, $operator === '<' && $count === 1)
+                : $this->addWhereCountQuery($hasQuery->toBase(), $operator, $count, $boolean);
+    }
+
+    /**
+     * Merge the where constraints from another query to the current query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $from
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function mergeConstraintsFrom(Builder $from)
+    {
+        $whereBindings = $from->getQuery()->getRawBindings()['where'] ?? [];
+
+        $wheres = $from->getQuery()->from !== $this->getQuery()->from
+            ? $this->requalifyWhereTables(
+                $from->getQuery()->wheres,
+                $from->getQuery()->from,
+                $this->getModel()->getTable()
+            ) : $from->getQuery()->wheres;
+
+        // Here we have some other query that we want to merge the where constraints from. We will
+        // copy over any where constraints on the query as well as remove any global scopes the
+        // query might have removed. Then we will return ourselves with the finished merging.
+        return $this->withoutGlobalScopes(
+            $from->removedScopes()
+        )->mergeWheres(
+            $wheres, $whereBindings
+        );
+    }
+
+    /**
+     * Updates the table name for any columns with a new qualified name.
+     *
+     * @param  array  $wheres
+     * @param  string  $from
+     * @param  string  $to
+     * @return array
+     */
+    protected function requalifyWhereTables(array $wheres, string $from, string $to): array
+    {
+        return collect($wheres)->map(function ($where) use ($from, $to) {
+            return collect($where)->map(function ($value) use ($from, $to) {
+                return is_string($value) && str_starts_with($value, $from.'.')
+                    ? $to.'.'.Str::afterLast($value, '.')
+                    : $value;
+            });
+        })->toArray();
+    }
+
+    /**
+     * Add a sub-query count clause to this query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $operator
+     * @param  int  $count
+     * @param  string  $boolean
+     * @return $this
+     */
+    protected function addWhereCountQuery(QueryBuilder $query, $operator = '>=', $count = 1, $boolean = 'and')
+    {
+        $this->query->addBinding($query->getBindings(), 'where');
+
+        return $this->where(
+            new Expression('('.$query->toSql().')'),
+            $operator,
+            is_numeric($count) ? new Expression($count) : $count,
+            $boolean
+        );
+    }
+
+    /**
+     * Get the "has relation" base query instance.
+     *
+     * @param  string  $relation
+     * @return \Illuminate\Database\Eloquent\Relations\Relation
+     */
+    protected function getRelationWithoutConstraints($relation)
+    {
+        return Relation::noConstraints(function () use ($relation) {
+            return $this->getModel()->{$relation}();
+        });
+    }
+
+    /**
+     * Check if we can run an "exists" query to optimize performance.
+     *
+     * @param  string  $operator
+     * @param  int  $count
+     * @return bool
+     */
+    protected function canUseExistsForExistenceCheck($operator, $count)
+    {
+        return ($operator === '>=' || $operator === '<') && $count === 1;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Factories/BelongsToManyRelationship.php b/vendor/illuminate/database/Eloquent/Factories/BelongsToManyRelationship.php
new file mode 100644
index 0000000..8e40261
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Factories/BelongsToManyRelationship.php
@@ -0,0 +1,76 @@
+factory = $factory;
+        $this->pivot = $pivot;
+        $this->relationship = $relationship;
+    }
+
+    /**
+     * Create the attached relationship for the given model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return void
+     */
+    public function createFor(Model $model)
+    {
+        Collection::wrap($this->factory instanceof Factory ? $this->factory->create([], $model) : $this->factory)->each(function ($attachable) use ($model) {
+            $model->{$this->relationship}()->attach(
+                $attachable,
+                is_callable($this->pivot) ? call_user_func($this->pivot, $model) : $this->pivot
+            );
+        });
+    }
+
+    /**
+     * Specify the model instances to always use when creating relationships.
+     *
+     * @param  \Illuminate\Support\Collection  $recycle
+     * @return $this
+     */
+    public function recycle($recycle)
+    {
+        if ($this->factory instanceof Factory) {
+            $this->factory = $this->factory->recycle($recycle);
+        }
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Factories/BelongsToRelationship.php b/vendor/illuminate/database/Eloquent/Factories/BelongsToRelationship.php
new file mode 100644
index 0000000..b2fb1b2
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Factories/BelongsToRelationship.php
@@ -0,0 +1,97 @@
+factory = $factory;
+        $this->relationship = $relationship;
+    }
+
+    /**
+     * Get the parent model attributes and resolvers for the given child model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return array
+     */
+    public function attributesFor(Model $model)
+    {
+        $relationship = $model->{$this->relationship}();
+
+        return $relationship instanceof MorphTo ? [
+            $relationship->getMorphType() => $this->factory instanceof Factory ? $this->factory->newModel()->getMorphClass() : $this->factory->getMorphClass(),
+            $relationship->getForeignKeyName() => $this->resolver($relationship->getOwnerKeyName()),
+        ] : [
+            $relationship->getForeignKeyName() => $this->resolver($relationship->getOwnerKeyName()),
+        ];
+    }
+
+    /**
+     * Get the deferred resolver for this relationship's parent ID.
+     *
+     * @param  string|null  $key
+     * @return \Closure
+     */
+    protected function resolver($key)
+    {
+        return function () use ($key) {
+            if (! $this->resolved) {
+                $instance = $this->factory instanceof Factory
+                    ? ($this->factory->getRandomRecycledModel($this->factory->modelName()) ?? $this->factory->create())
+                    : $this->factory;
+
+                return $this->resolved = $key ? $instance->{$key} : $instance->getKey();
+            }
+
+            return $this->resolved;
+        };
+    }
+
+    /**
+     * Specify the model instances to always use when creating relationships.
+     *
+     * @param  \Illuminate\Support\Collection  $recycle
+     * @return $this
+     */
+    public function recycle($recycle)
+    {
+        if ($this->factory instanceof Factory) {
+            $this->factory = $this->factory->recycle($recycle);
+        }
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Factories/CrossJoinSequence.php b/vendor/illuminate/database/Eloquent/Factories/CrossJoinSequence.php
new file mode 100644
index 0000000..3270b30
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Factories/CrossJoinSequence.php
@@ -0,0 +1,26 @@
+
+     */
+    protected $model;
+
+    /**
+     * The number of models that should be generated.
+     *
+     * @var int|null
+     */
+    protected $count;
+
+    /**
+     * The state transformations that will be applied to the model.
+     *
+     * @var \Illuminate\Support\Collection
+     */
+    protected $states;
+
+    /**
+     * The parent relationships that will be applied to the model.
+     *
+     * @var \Illuminate\Support\Collection
+     */
+    protected $has;
+
+    /**
+     * The child relationships that will be applied to the model.
+     *
+     * @var \Illuminate\Support\Collection
+     */
+    protected $for;
+
+    /**
+     * The model instances to always use when creating relationships.
+     *
+     * @var \Illuminate\Support\Collection
+     */
+    protected $recycle;
+
+    /**
+     * The "after making" callbacks that will be applied to the model.
+     *
+     * @var \Illuminate\Support\Collection
+     */
+    protected $afterMaking;
+
+    /**
+     * The "after creating" callbacks that will be applied to the model.
+     *
+     * @var \Illuminate\Support\Collection
+     */
+    protected $afterCreating;
+
+    /**
+     * The name of the database connection that will be used to create the models.
+     *
+     * @var string|null
+     */
+    protected $connection;
+
+    /**
+     * The current Faker instance.
+     *
+     * @var \Faker\Generator
+     */
+    protected $faker;
+
+    /**
+     * The default namespace where factories reside.
+     *
+     * @var string
+     */
+    protected static $namespace = 'Database\\Factories\\';
+
+    /**
+     * The default model name resolver.
+     *
+     * @var callable
+     */
+    protected static $modelNameResolver;
+
+    /**
+     * The factory name resolver.
+     *
+     * @var callable
+     */
+    protected static $factoryNameResolver;
+
+    /**
+     * Create a new factory instance.
+     *
+     * @param  int|null  $count
+     * @param  \Illuminate\Support\Collection|null  $states
+     * @param  \Illuminate\Support\Collection|null  $has
+     * @param  \Illuminate\Support\Collection|null  $for
+     * @param  \Illuminate\Support\Collection|null  $afterMaking
+     * @param  \Illuminate\Support\Collection|null  $afterCreating
+     * @param  string|null  $connection
+     * @param  \Illuminate\Support\Collection|null  $recycle
+     * @return void
+     */
+    public function __construct($count = null,
+                                ?Collection $states = null,
+                                ?Collection $has = null,
+                                ?Collection $for = null,
+                                ?Collection $afterMaking = null,
+                                ?Collection $afterCreating = null,
+                                $connection = null,
+                                ?Collection $recycle = null)
+    {
+        $this->count = $count;
+        $this->states = $states ?? new Collection;
+        $this->has = $has ?? new Collection;
+        $this->for = $for ?? new Collection;
+        $this->afterMaking = $afterMaking ?? new Collection;
+        $this->afterCreating = $afterCreating ?? new Collection;
+        $this->connection = $connection;
+        $this->recycle = $recycle ?? new Collection;
+        $this->faker = $this->withFaker();
+    }
+
+    /**
+     * Define the model's default state.
+     *
+     * @return array
+     */
+    abstract public function definition();
+
+    /**
+     * Get a new factory instance for the given attributes.
+     *
+     * @param  (callable(array): array)|array  $attributes
+     * @return static
+     */
+    public static function new($attributes = [])
+    {
+        return (new static)->state($attributes)->configure();
+    }
+
+    /**
+     * Get a new factory instance for the given number of models.
+     *
+     * @param  int  $count
+     * @return static
+     */
+    public static function times(int $count)
+    {
+        return static::new()->count($count);
+    }
+
+    /**
+     * Configure the factory.
+     *
+     * @return $this
+     */
+    public function configure()
+    {
+        return $this;
+    }
+
+    /**
+     * Get the raw attributes generated by the factory.
+     *
+     * @param  (callable(array): array)|array  $attributes
+     * @param  \Illuminate\Database\Eloquent\Model|null  $parent
+     * @return array
+     */
+    public function raw($attributes = [], ?Model $parent = null)
+    {
+        if ($this->count === null) {
+            return $this->state($attributes)->getExpandedAttributes($parent);
+        }
+
+        return array_map(function () use ($attributes, $parent) {
+            return $this->state($attributes)->getExpandedAttributes($parent);
+        }, range(1, $this->count));
+    }
+
+    /**
+     * Create a single model and persist it to the database.
+     *
+     * @param  (callable(array): array)|array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model|TModel
+     */
+    public function createOne($attributes = [])
+    {
+        return $this->count(null)->create($attributes);
+    }
+
+    /**
+     * Create a single model and persist it to the database without dispatching any model events.
+     *
+     * @param  (callable(array): array)|array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model|TModel
+     */
+    public function createOneQuietly($attributes = [])
+    {
+        return $this->count(null)->createQuietly($attributes);
+    }
+
+    /**
+     * Create a collection of models and persist them to the database.
+     *
+     * @param  iterable>  $records
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function createMany(iterable $records)
+    {
+        return new EloquentCollection(
+            collect($records)->map(function ($record) {
+                return $this->state($record)->create();
+            })
+        );
+    }
+
+    /**
+     * Create a collection of models and persist them to the database without dispatching any model events.
+     *
+     * @param  iterable>  $records
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function createManyQuietly(iterable $records)
+    {
+        return Model::withoutEvents(function () use ($records) {
+            return $this->createMany($records);
+        });
+    }
+
+    /**
+     * Create a collection of models and persist them to the database.
+     *
+     * @param  (callable(array): array)|array  $attributes
+     * @param  \Illuminate\Database\Eloquent\Model|null  $parent
+     * @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model|TModel
+     */
+    public function create($attributes = [], ?Model $parent = null)
+    {
+        if (! empty($attributes)) {
+            return $this->state($attributes)->create([], $parent);
+        }
+
+        $results = $this->make($attributes, $parent);
+
+        if ($results instanceof Model) {
+            $this->store(collect([$results]));
+
+            $this->callAfterCreating(collect([$results]), $parent);
+        } else {
+            $this->store($results);
+
+            $this->callAfterCreating($results, $parent);
+        }
+
+        return $results;
+    }
+
+    /**
+     * Create a collection of models and persist them to the database without dispatching any model events.
+     *
+     * @param  (callable(array): array)|array  $attributes
+     * @param  \Illuminate\Database\Eloquent\Model|null  $parent
+     * @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model|TModel
+     */
+    public function createQuietly($attributes = [], ?Model $parent = null)
+    {
+        return Model::withoutEvents(function () use ($attributes, $parent) {
+            return $this->create($attributes, $parent);
+        });
+    }
+
+    /**
+     * Create a callback that persists a model in the database when invoked.
+     *
+     * @param  array  $attributes
+     * @param  \Illuminate\Database\Eloquent\Model|null  $parent
+     * @return \Closure(): (\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model|TModel)
+     */
+    public function lazy(array $attributes = [], ?Model $parent = null)
+    {
+        return fn () => $this->create($attributes, $parent);
+    }
+
+    /**
+     * Set the connection name on the results and store them.
+     *
+     * @param  \Illuminate\Support\Collection  $results
+     * @return void
+     */
+    protected function store(Collection $results)
+    {
+        $results->each(function ($model) {
+            if (! isset($this->connection)) {
+                $model->setConnection($model->newQueryWithoutScopes()->getConnection()->getName());
+            }
+
+            $model->save();
+
+            foreach ($model->getRelations() as $name => $items) {
+                if ($items instanceof Enumerable && $items->isEmpty()) {
+                    $model->unsetRelation($name);
+                }
+            }
+
+            $this->createChildren($model);
+        });
+    }
+
+    /**
+     * Create the children for the given model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return void
+     */
+    protected function createChildren(Model $model)
+    {
+        Model::unguarded(function () use ($model) {
+            $this->has->each(function ($has) use ($model) {
+                $has->recycle($this->recycle)->createFor($model);
+            });
+        });
+    }
+
+    /**
+     * Make a single instance of the model.
+     *
+     * @param  (callable(array): array)|array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model|TModel
+     */
+    public function makeOne($attributes = [])
+    {
+        return $this->count(null)->make($attributes);
+    }
+
+    /**
+     * Create a collection of models.
+     *
+     * @param  (callable(array): array)|array  $attributes
+     * @param  \Illuminate\Database\Eloquent\Model|null  $parent
+     * @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model|TModel
+     */
+    public function make($attributes = [], ?Model $parent = null)
+    {
+        if (! empty($attributes)) {
+            return $this->state($attributes)->make([], $parent);
+        }
+
+        if ($this->count === null) {
+            return tap($this->makeInstance($parent), function ($instance) {
+                $this->callAfterMaking(collect([$instance]));
+            });
+        }
+
+        if ($this->count < 1) {
+            return $this->newModel()->newCollection();
+        }
+
+        $instances = $this->newModel()->newCollection(array_map(function () use ($parent) {
+            return $this->makeInstance($parent);
+        }, range(1, $this->count)));
+
+        $this->callAfterMaking($instances);
+
+        return $instances;
+    }
+
+    /**
+     * Make an instance of the model with the given attributes.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|null  $parent
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    protected function makeInstance(?Model $parent)
+    {
+        return Model::unguarded(function () use ($parent) {
+            return tap($this->newModel($this->getExpandedAttributes($parent)), function ($instance) {
+                if (isset($this->connection)) {
+                    $instance->setConnection($this->connection);
+                }
+            });
+        });
+    }
+
+    /**
+     * Get a raw attributes array for the model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|null  $parent
+     * @return mixed
+     */
+    protected function getExpandedAttributes(?Model $parent)
+    {
+        return $this->expandAttributes($this->getRawAttributes($parent));
+    }
+
+    /**
+     * Get the raw attributes for the model as an array.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|null  $parent
+     * @return array
+     */
+    protected function getRawAttributes(?Model $parent)
+    {
+        return $this->states->pipe(function ($states) {
+            return $this->for->isEmpty() ? $states : new Collection(array_merge([function () {
+                return $this->parentResolvers();
+            }], $states->all()));
+        })->reduce(function ($carry, $state) use ($parent) {
+            if ($state instanceof Closure) {
+                $state = $state->bindTo($this);
+            }
+
+            return array_merge($carry, $state($carry, $parent));
+        }, $this->definition());
+    }
+
+    /**
+     * Create the parent relationship resolvers (as deferred Closures).
+     *
+     * @return array
+     */
+    protected function parentResolvers()
+    {
+        $model = $this->newModel();
+
+        return $this->for->map(function (BelongsToRelationship $for) use ($model) {
+            return $for->recycle($this->recycle)->attributesFor($model);
+        })->collapse()->all();
+    }
+
+    /**
+     * Expand all attributes to their underlying values.
+     *
+     * @param  array  $definition
+     * @return array
+     */
+    protected function expandAttributes(array $definition)
+    {
+        return collect($definition)
+            ->map($evaluateRelations = function ($attribute) {
+                if ($attribute instanceof self) {
+                    $attribute = $this->getRandomRecycledModel($attribute->modelName())
+                        ?? $attribute->recycle($this->recycle)->create()->getKey();
+                } elseif ($attribute instanceof Model) {
+                    $attribute = $attribute->getKey();
+                }
+
+                return $attribute;
+            })
+            ->map(function ($attribute, $key) use (&$definition, $evaluateRelations) {
+                if (is_callable($attribute) && ! is_string($attribute) && ! is_array($attribute)) {
+                    $attribute = $attribute($definition);
+                }
+
+                $attribute = $evaluateRelations($attribute);
+
+                $definition[$key] = $attribute;
+
+                return $attribute;
+            })
+            ->all();
+    }
+
+    /**
+     * Add a new state transformation to the model definition.
+     *
+     * @param  (callable(array, \Illuminate\Database\Eloquent\Model|null): array)|array  $state
+     * @return static
+     */
+    public function state($state)
+    {
+        return $this->newInstance([
+            'states' => $this->states->concat([
+                is_callable($state) ? $state : function () use ($state) {
+                    return $state;
+                },
+            ]),
+        ]);
+    }
+
+    /**
+     * Set a single model attribute.
+     *
+     * @param  string|int  $key
+     * @param  mixed  $value
+     * @return static
+     */
+    public function set($key, $value)
+    {
+        return $this->state([$key => $value]);
+    }
+
+    /**
+     * Add a new sequenced state transformation to the model definition.
+     *
+     * @param  mixed  ...$sequence
+     * @return static
+     */
+    public function sequence(...$sequence)
+    {
+        return $this->state(new Sequence(...$sequence));
+    }
+
+    /**
+     * Add a new sequenced state transformation to the model definition and update the pending creation count to the size of the sequence.
+     *
+     * @param  array  ...$sequence
+     * @return static
+     */
+    public function forEachSequence(...$sequence)
+    {
+        return $this->state(new Sequence(...$sequence))->count(count($sequence));
+    }
+
+    /**
+     * Add a new cross joined sequenced state transformation to the model definition.
+     *
+     * @param  array  ...$sequence
+     * @return static
+     */
+    public function crossJoinSequence(...$sequence)
+    {
+        return $this->state(new CrossJoinSequence(...$sequence));
+    }
+
+    /**
+     * Define a child relationship for the model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Factories\Factory  $factory
+     * @param  string|null  $relationship
+     * @return static
+     */
+    public function has(self $factory, $relationship = null)
+    {
+        return $this->newInstance([
+            'has' => $this->has->concat([new Relationship(
+                $factory, $relationship ?? $this->guessRelationship($factory->modelName())
+            )]),
+        ]);
+    }
+
+    /**
+     * Attempt to guess the relationship name for a "has" relationship.
+     *
+     * @param  string  $related
+     * @return string
+     */
+    protected function guessRelationship(string $related)
+    {
+        $guess = Str::camel(Str::plural(class_basename($related)));
+
+        return method_exists($this->modelName(), $guess) ? $guess : Str::singular($guess);
+    }
+
+    /**
+     * Define an attached relationship for the model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Factories\Factory|\Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array  $factory
+     * @param  (callable(): array)|array  $pivot
+     * @param  string|null  $relationship
+     * @return static
+     */
+    public function hasAttached($factory, $pivot = [], $relationship = null)
+    {
+        return $this->newInstance([
+            'has' => $this->has->concat([new BelongsToManyRelationship(
+                $factory,
+                $pivot,
+                $relationship ?? Str::camel(Str::plural(class_basename(
+                    $factory instanceof Factory
+                        ? $factory->modelName()
+                        : Collection::wrap($factory)->first()
+                )))
+            )]),
+        ]);
+    }
+
+    /**
+     * Define a parent relationship for the model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Factories\Factory|\Illuminate\Database\Eloquent\Model  $factory
+     * @param  string|null  $relationship
+     * @return static
+     */
+    public function for($factory, $relationship = null)
+    {
+        return $this->newInstance(['for' => $this->for->concat([new BelongsToRelationship(
+            $factory,
+            $relationship ?? Str::camel(class_basename(
+                $factory instanceof Factory ? $factory->modelName() : $factory
+            ))
+        )])]);
+    }
+
+    /**
+     * Provide model instances to use instead of any nested factory calls when creating relationships.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|\Illuminate\Support\Collection|array  $model
+     * @return static
+     */
+    public function recycle($model)
+    {
+        // Group provided models by the type and merge them into existing recycle collection
+        return $this->newInstance([
+            'recycle' => $this->recycle
+                ->flatten()
+                ->merge(
+                    Collection::wrap($model instanceof Model ? func_get_args() : $model)
+                        ->flatten()
+                )->groupBy(fn ($model) => get_class($model)),
+        ]);
+    }
+
+    /**
+     * Retrieve a random model of a given type from previously provided models to recycle.
+     *
+     * @param  string  $modelClassName
+     * @return \Illuminate\Database\Eloquent\Model|null
+     */
+    public function getRandomRecycledModel($modelClassName)
+    {
+        return $this->recycle->get($modelClassName)?->random();
+    }
+
+    /**
+     * Add a new "after making" callback to the model definition.
+     *
+     * @param  \Closure(\Illuminate\Database\Eloquent\Model|TModel): mixed  $callback
+     * @return static
+     */
+    public function afterMaking(Closure $callback)
+    {
+        return $this->newInstance(['afterMaking' => $this->afterMaking->concat([$callback])]);
+    }
+
+    /**
+     * Add a new "after creating" callback to the model definition.
+     *
+     * @param  \Closure(\Illuminate\Database\Eloquent\Model|TModel): mixed  $callback
+     * @return static
+     */
+    public function afterCreating(Closure $callback)
+    {
+        return $this->newInstance(['afterCreating' => $this->afterCreating->concat([$callback])]);
+    }
+
+    /**
+     * Call the "after making" callbacks for the given model instances.
+     *
+     * @param  \Illuminate\Support\Collection  $instances
+     * @return void
+     */
+    protected function callAfterMaking(Collection $instances)
+    {
+        $instances->each(function ($model) {
+            $this->afterMaking->each(function ($callback) use ($model) {
+                $callback($model);
+            });
+        });
+    }
+
+    /**
+     * Call the "after creating" callbacks for the given model instances.
+     *
+     * @param  \Illuminate\Support\Collection  $instances
+     * @param  \Illuminate\Database\Eloquent\Model|null  $parent
+     * @return void
+     */
+    protected function callAfterCreating(Collection $instances, ?Model $parent = null)
+    {
+        $instances->each(function ($model) use ($parent) {
+            $this->afterCreating->each(function ($callback) use ($model, $parent) {
+                $callback($model, $parent);
+            });
+        });
+    }
+
+    /**
+     * Specify how many models should be generated.
+     *
+     * @param  int|null  $count
+     * @return static
+     */
+    public function count(?int $count)
+    {
+        return $this->newInstance(['count' => $count]);
+    }
+
+    /**
+     * Specify the database connection that should be used to generate models.
+     *
+     * @param  string  $connection
+     * @return static
+     */
+    public function connection(string $connection)
+    {
+        return $this->newInstance(['connection' => $connection]);
+    }
+
+    /**
+     * Create a new instance of the factory builder with the given mutated properties.
+     *
+     * @param  array  $arguments
+     * @return static
+     */
+    protected function newInstance(array $arguments = [])
+    {
+        return new static(...array_values(array_merge([
+            'count' => $this->count,
+            'states' => $this->states,
+            'has' => $this->has,
+            'for' => $this->for,
+            'afterMaking' => $this->afterMaking,
+            'afterCreating' => $this->afterCreating,
+            'connection' => $this->connection,
+            'recycle' => $this->recycle,
+        ], $arguments)));
+    }
+
+    /**
+     * Get a new model instance.
+     *
+     * @param  array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model|TModel
+     */
+    public function newModel(array $attributes = [])
+    {
+        $model = $this->modelName();
+
+        return new $model($attributes);
+    }
+
+    /**
+     * Get the name of the model that is generated by the factory.
+     *
+     * @return class-string<\Illuminate\Database\Eloquent\Model|TModel>
+     */
+    public function modelName()
+    {
+        $resolver = static::$modelNameResolver ?? function (self $factory) {
+            $namespacedFactoryBasename = Str::replaceLast(
+                'Factory', '', Str::replaceFirst(static::$namespace, '', get_class($factory))
+            );
+
+            $factoryBasename = Str::replaceLast('Factory', '', class_basename($factory));
+
+            $appNamespace = static::appNamespace();
+
+            return class_exists($appNamespace.'Models\\'.$namespacedFactoryBasename)
+                        ? $appNamespace.'Models\\'.$namespacedFactoryBasename
+                        : $appNamespace.$factoryBasename;
+        };
+
+        return $this->model ?? $resolver($this);
+    }
+
+    /**
+     * Specify the callback that should be invoked to guess model names based on factory names.
+     *
+     * @param  callable(self): class-string<\Illuminate\Database\Eloquent\Model|TModel>  $callback
+     * @return void
+     */
+    public static function guessModelNamesUsing(callable $callback)
+    {
+        static::$modelNameResolver = $callback;
+    }
+
+    /**
+     * Specify the default namespace that contains the application's model factories.
+     *
+     * @param  string  $namespace
+     * @return void
+     */
+    public static function useNamespace(string $namespace)
+    {
+        static::$namespace = $namespace;
+    }
+
+    /**
+     * Get a new factory instance for the given model name.
+     *
+     * @param  class-string<\Illuminate\Database\Eloquent\Model>  $modelName
+     * @return \Illuminate\Database\Eloquent\Factories\Factory
+     */
+    public static function factoryForModel(string $modelName)
+    {
+        $factory = static::resolveFactoryName($modelName);
+
+        return $factory::new();
+    }
+
+    /**
+     * Specify the callback that should be invoked to guess factory names based on dynamic relationship names.
+     *
+     * @param  callable(class-string<\Illuminate\Database\Eloquent\Model>): class-string<\Illuminate\Database\Eloquent\Factories\Factory>  $callback
+     * @return void
+     */
+    public static function guessFactoryNamesUsing(callable $callback)
+    {
+        static::$factoryNameResolver = $callback;
+    }
+
+    /**
+     * Get a new Faker instance.
+     *
+     * @return \Faker\Generator
+     */
+    protected function withFaker()
+    {
+        return Container::getInstance()->make(Generator::class);
+    }
+
+    /**
+     * Get the factory name for the given model name.
+     *
+     * @param  class-string<\Illuminate\Database\Eloquent\Model>  $modelName
+     * @return class-string<\Illuminate\Database\Eloquent\Factories\Factory>
+     */
+    public static function resolveFactoryName(string $modelName)
+    {
+        $resolver = static::$factoryNameResolver ?? function (string $modelName) {
+            $appNamespace = static::appNamespace();
+
+            $modelName = Str::startsWith($modelName, $appNamespace.'Models\\')
+                ? Str::after($modelName, $appNamespace.'Models\\')
+                : Str::after($modelName, $appNamespace);
+
+            return static::$namespace.$modelName.'Factory';
+        };
+
+        return $resolver($modelName);
+    }
+
+    /**
+     * Get the application namespace for the application.
+     *
+     * @return string
+     */
+    protected static function appNamespace()
+    {
+        try {
+            return Container::getInstance()
+                            ->make(Application::class)
+                            ->getNamespace();
+        } catch (Throwable $e) {
+            return 'App\\';
+        }
+    }
+
+    /**
+     * Proxy dynamic factory methods onto their proper methods.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        if (static::hasMacro($method)) {
+            return $this->macroCall($method, $parameters);
+        }
+
+        if ($method === 'trashed' && in_array(SoftDeletes::class, class_uses_recursive($this->modelName()))) {
+            return $this->state([
+                $this->newModel()->getDeletedAtColumn() => $parameters[0] ?? Carbon::now()->subDay(),
+            ]);
+        }
+
+        if (! Str::startsWith($method, ['for', 'has'])) {
+            static::throwBadMethodCallException($method);
+        }
+
+        $relationship = Str::camel(Str::substr($method, 3));
+
+        $relatedModel = get_class($this->newModel()->{$relationship}()->getRelated());
+
+        if (method_exists($relatedModel, 'newFactory')) {
+            $factory = $relatedModel::newFactory() ?? static::factoryForModel($relatedModel);
+        } else {
+            $factory = static::factoryForModel($relatedModel);
+        }
+
+        if (str_starts_with($method, 'for')) {
+            return $this->for($factory->state($parameters[0] ?? []), $relationship);
+        } elseif (str_starts_with($method, 'has')) {
+            return $this->has(
+                $factory
+                    ->count(is_numeric($parameters[0] ?? null) ? $parameters[0] : 1)
+                    ->state((is_callable($parameters[0] ?? null) || is_array($parameters[0] ?? null)) ? $parameters[0] : ($parameters[1] ?? [])),
+                $relationship
+            );
+        }
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Factories/HasFactory.php b/vendor/illuminate/database/Eloquent/Factories/HasFactory.php
new file mode 100644
index 0000000..f10281d
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Factories/HasFactory.php
@@ -0,0 +1,32 @@
+
+     */
+    public static function factory($count = null, $state = [])
+    {
+        $factory = static::newFactory() ?: Factory::factoryForModel(get_called_class());
+
+        return $factory
+                    ->count(is_numeric($count) ? $count : null)
+                    ->state(is_callable($count) || is_array($count) ? $count : $state);
+    }
+
+    /**
+     * Create a new factory instance for the model.
+     *
+     * @return \Illuminate\Database\Eloquent\Factories\Factory
+     */
+    protected static function newFactory()
+    {
+        //
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Factories/Relationship.php b/vendor/illuminate/database/Eloquent/Factories/Relationship.php
new file mode 100644
index 0000000..3eb62da
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Factories/Relationship.php
@@ -0,0 +1,75 @@
+factory = $factory;
+        $this->relationship = $relationship;
+    }
+
+    /**
+     * Create the child relationship for the given parent model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @return void
+     */
+    public function createFor(Model $parent)
+    {
+        $relationship = $parent->{$this->relationship}();
+
+        if ($relationship instanceof MorphOneOrMany) {
+            $this->factory->state([
+                $relationship->getMorphType() => $relationship->getMorphClass(),
+                $relationship->getForeignKeyName() => $relationship->getParentKey(),
+            ])->create([], $parent);
+        } elseif ($relationship instanceof HasOneOrMany) {
+            $this->factory->state([
+                $relationship->getForeignKeyName() => $relationship->getParentKey(),
+            ])->create([], $parent);
+        } elseif ($relationship instanceof BelongsToMany) {
+            $relationship->attach($this->factory->create([], $parent));
+        }
+    }
+
+    /**
+     * Specify the model instances to always use when creating relationships.
+     *
+     * @param  \Illuminate\Support\Collection  $recycle
+     * @return $this
+     */
+    public function recycle($recycle)
+    {
+        $this->factory = $this->factory->recycle($recycle);
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Factories/Sequence.php b/vendor/illuminate/database/Eloquent/Factories/Sequence.php
new file mode 100644
index 0000000..e523fb3
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Factories/Sequence.php
@@ -0,0 +1,63 @@
+sequence = $sequence;
+        $this->count = count($sequence);
+    }
+
+    /**
+     * Get the current count of the sequence items.
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        return $this->count;
+    }
+
+    /**
+     * Get the next value in the sequence.
+     *
+     * @return mixed
+     */
+    public function __invoke()
+    {
+        return tap(value($this->sequence[$this->index % $this->count], $this), function () {
+            $this->index = $this->index + 1;
+        });
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/HigherOrderBuilderProxy.php b/vendor/illuminate/database/Eloquent/HigherOrderBuilderProxy.php
new file mode 100644
index 0000000..16b49a1
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/HigherOrderBuilderProxy.php
@@ -0,0 +1,50 @@
+method = $method;
+        $this->builder = $builder;
+    }
+
+    /**
+     * Proxy a scope call onto the query builder.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->builder->{$this->method}(function ($value) use ($method, $parameters) {
+            return $value->{$method}(...$parameters);
+        });
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/InvalidCastException.php b/vendor/illuminate/database/Eloquent/InvalidCastException.php
new file mode 100644
index 0000000..9d00eb3
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/InvalidCastException.php
@@ -0,0 +1,48 @@
+model = $class;
+        $this->column = $column;
+        $this->castType = $castType;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/JsonEncodingException.php b/vendor/illuminate/database/Eloquent/JsonEncodingException.php
new file mode 100644
index 0000000..f62abd4
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/JsonEncodingException.php
@@ -0,0 +1,49 @@
+getKey().'] to JSON: '.$message);
+    }
+
+    /**
+     * Create a new JSON encoding exception for the resource.
+     *
+     * @param  \Illuminate\Http\Resources\Json\JsonResource  $resource
+     * @param  string  $message
+     * @return static
+     */
+    public static function forResource($resource, $message)
+    {
+        $model = $resource->resource;
+
+        return new static('Error encoding resource ['.get_class($resource).'] with model ['.get_class($model).'] with ID ['.$model->getKey().'] to JSON: '.$message);
+    }
+
+    /**
+     * Create a new JSON encoding exception for an attribute.
+     *
+     * @param  mixed  $model
+     * @param  mixed  $key
+     * @param  string  $message
+     * @return static
+     */
+    public static function forAttribute($model, $key, $message)
+    {
+        $class = get_class($model);
+
+        return new static("Unable to encode attribute [{$key}] for model [{$class}] to JSON: {$message}.");
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/MassAssignmentException.php b/vendor/illuminate/database/Eloquent/MassAssignmentException.php
new file mode 100755
index 0000000..7c81aae
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/MassAssignmentException.php
@@ -0,0 +1,10 @@
+prunable(), function ($query) use ($chunkSize) {
+            $query->when(! $query->getQuery()->limit, function ($query) use ($chunkSize) {
+                $query->limit($chunkSize);
+            });
+        });
+
+        $total = 0;
+
+        do {
+            $total += $count = in_array(SoftDeletes::class, class_uses_recursive(get_class($this)))
+                        ? $query->forceDelete()
+                        : $query->delete();
+
+            if ($count > 0) {
+                event(new ModelsPruned(static::class, $total));
+            }
+        } while ($count > 0);
+
+        return $total;
+    }
+
+    /**
+     * Get the prunable model query.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function prunable()
+    {
+        throw new LogicException('Please implement the prunable method on your model.');
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/MissingAttributeException.php b/vendor/illuminate/database/Eloquent/MissingAttributeException.php
new file mode 100755
index 0000000..87935c1
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/MissingAttributeException.php
@@ -0,0 +1,23 @@
+bootIfNotBooted();
+
+        $this->initializeTraits();
+
+        $this->syncOriginal();
+
+        $this->fill($attributes);
+    }
+
+    /**
+     * Check if the model needs to be booted and if so, do it.
+     *
+     * @return void
+     */
+    protected function bootIfNotBooted()
+    {
+        if (! isset(static::$booted[static::class])) {
+            static::$booted[static::class] = true;
+
+            $this->fireModelEvent('booting', false);
+
+            static::booting();
+            static::boot();
+            static::booted();
+
+            $this->fireModelEvent('booted', false);
+        }
+    }
+
+    /**
+     * Perform any actions required before the model boots.
+     *
+     * @return void
+     */
+    protected static function booting()
+    {
+        //
+    }
+
+    /**
+     * Bootstrap the model and its traits.
+     *
+     * @return void
+     */
+    protected static function boot()
+    {
+        static::bootTraits();
+    }
+
+    /**
+     * Boot all of the bootable traits on the model.
+     *
+     * @return void
+     */
+    protected static function bootTraits()
+    {
+        $class = static::class;
+
+        $booted = [];
+
+        static::$traitInitializers[$class] = [];
+
+        foreach (class_uses_recursive($class) as $trait) {
+            $method = 'boot'.class_basename($trait);
+
+            if (method_exists($class, $method) && ! in_array($method, $booted)) {
+                forward_static_call([$class, $method]);
+
+                $booted[] = $method;
+            }
+
+            if (method_exists($class, $method = 'initialize'.class_basename($trait))) {
+                static::$traitInitializers[$class][] = $method;
+
+                static::$traitInitializers[$class] = array_unique(
+                    static::$traitInitializers[$class]
+                );
+            }
+        }
+    }
+
+    /**
+     * Initialize any initializable traits on the model.
+     *
+     * @return void
+     */
+    protected function initializeTraits()
+    {
+        foreach (static::$traitInitializers[static::class] as $method) {
+            $this->{$method}();
+        }
+    }
+
+    /**
+     * Perform any actions required after the model boots.
+     *
+     * @return void
+     */
+    protected static function booted()
+    {
+        //
+    }
+
+    /**
+     * Clear the list of booted models so they will be re-booted.
+     *
+     * @return void
+     */
+    public static function clearBootedModels()
+    {
+        static::$booted = [];
+
+        static::$globalScopes = [];
+    }
+
+    /**
+     * Disables relationship model touching for the current class during given callback scope.
+     *
+     * @param  callable  $callback
+     * @return void
+     */
+    public static function withoutTouching(callable $callback)
+    {
+        static::withoutTouchingOn([static::class], $callback);
+    }
+
+    /**
+     * Disables relationship model touching for the given model classes during given callback scope.
+     *
+     * @param  array  $models
+     * @param  callable  $callback
+     * @return void
+     */
+    public static function withoutTouchingOn(array $models, callable $callback)
+    {
+        static::$ignoreOnTouch = array_values(array_merge(static::$ignoreOnTouch, $models));
+
+        try {
+            $callback();
+        } finally {
+            static::$ignoreOnTouch = array_values(array_diff(static::$ignoreOnTouch, $models));
+        }
+    }
+
+    /**
+     * Determine if the given model is ignoring touches.
+     *
+     * @param  string|null  $class
+     * @return bool
+     */
+    public static function isIgnoringTouch($class = null)
+    {
+        $class = $class ?: static::class;
+
+        if (! get_class_vars($class)['timestamps'] || ! $class::UPDATED_AT) {
+            return true;
+        }
+
+        foreach (static::$ignoreOnTouch as $ignoredClass) {
+            if ($class === $ignoredClass || is_subclass_of($class, $ignoredClass)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Indicate that models should prevent lazy loading, silently discarding attributes, and accessing missing attributes.
+     *
+     * @param  bool  $shouldBeStrict
+     * @return void
+     */
+    public static function shouldBeStrict(bool $shouldBeStrict = true)
+    {
+        static::preventLazyLoading($shouldBeStrict);
+        static::preventSilentlyDiscardingAttributes($shouldBeStrict);
+        static::preventAccessingMissingAttributes($shouldBeStrict);
+    }
+
+    /**
+     * Prevent model relationships from being lazy loaded.
+     *
+     * @param  bool  $value
+     * @return void
+     */
+    public static function preventLazyLoading($value = true)
+    {
+        static::$modelsShouldPreventLazyLoading = $value;
+    }
+
+    /**
+     * Register a callback that is responsible for handling lazy loading violations.
+     *
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public static function handleLazyLoadingViolationUsing(?callable $callback)
+    {
+        static::$lazyLoadingViolationCallback = $callback;
+    }
+
+    /**
+     * Prevent non-fillable attributes from being silently discarded.
+     *
+     * @param  bool  $value
+     * @return void
+     */
+    public static function preventSilentlyDiscardingAttributes($value = true)
+    {
+        static::$modelsShouldPreventSilentlyDiscardingAttributes = $value;
+    }
+
+    /**
+     * Register a callback that is responsible for handling discarded attribute violations.
+     *
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public static function handleDiscardedAttributeViolationUsing(?callable $callback)
+    {
+        static::$discardedAttributeViolationCallback = $callback;
+    }
+
+    /**
+     * Prevent accessing missing attributes on retrieved models.
+     *
+     * @param  bool  $value
+     * @return void
+     */
+    public static function preventAccessingMissingAttributes($value = true)
+    {
+        static::$modelsShouldPreventAccessingMissingAttributes = $value;
+    }
+
+    /**
+     * Register a callback that is responsible for handling lazy loading violations.
+     *
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public static function handleMissingAttributeViolationUsing(?callable $callback)
+    {
+        static::$missingAttributeViolationCallback = $callback;
+    }
+
+    /**
+     * Execute a callback without broadcasting any model events for all model types.
+     *
+     * @param  callable  $callback
+     * @return mixed
+     */
+    public static function withoutBroadcasting(callable $callback)
+    {
+        $isBroadcasting = static::$isBroadcasting;
+
+        static::$isBroadcasting = false;
+
+        try {
+            return $callback();
+        } finally {
+            static::$isBroadcasting = $isBroadcasting;
+        }
+    }
+
+    /**
+     * Fill the model with an array of attributes.
+     *
+     * @param  array  $attributes
+     * @return $this
+     *
+     * @throws \Illuminate\Database\Eloquent\MassAssignmentException
+     */
+    public function fill(array $attributes)
+    {
+        $totallyGuarded = $this->totallyGuarded();
+
+        $fillable = $this->fillableFromArray($attributes);
+
+        foreach ($fillable as $key => $value) {
+            // The developers may choose to place some attributes in the "fillable" array
+            // which means only those attributes may be set through mass assignment to
+            // the model, and all others will just get ignored for security reasons.
+            if ($this->isFillable($key)) {
+                $this->setAttribute($key, $value);
+            } elseif ($totallyGuarded || static::preventsSilentlyDiscardingAttributes()) {
+                if (isset(static::$discardedAttributeViolationCallback)) {
+                    call_user_func(static::$discardedAttributeViolationCallback, $this, [$key]);
+                } else {
+                    throw new MassAssignmentException(sprintf(
+                        'Add [%s] to fillable property to allow mass assignment on [%s].',
+                        $key, get_class($this)
+                    ));
+                }
+            }
+        }
+
+        if (count($attributes) !== count($fillable) &&
+            static::preventsSilentlyDiscardingAttributes()) {
+            $keys = array_diff(array_keys($attributes), array_keys($fillable));
+
+            if (isset(static::$discardedAttributeViolationCallback)) {
+                call_user_func(static::$discardedAttributeViolationCallback, $this, $keys);
+            } else {
+                throw new MassAssignmentException(sprintf(
+                    'Add fillable property [%s] to allow mass assignment on [%s].',
+                    implode(', ', $keys),
+                    get_class($this)
+                ));
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Fill the model with an array of attributes. Force mass assignment.
+     *
+     * @param  array  $attributes
+     * @return $this
+     */
+    public function forceFill(array $attributes)
+    {
+        return static::unguarded(fn () => $this->fill($attributes));
+    }
+
+    /**
+     * Qualify the given column name by the model's table.
+     *
+     * @param  string  $column
+     * @return string
+     */
+    public function qualifyColumn($column)
+    {
+        if (str_contains($column, '.')) {
+            return $column;
+        }
+
+        return $this->getTable().'.'.$column;
+    }
+
+    /**
+     * Qualify the given columns with the model's table.
+     *
+     * @param  array  $columns
+     * @return array
+     */
+    public function qualifyColumns($columns)
+    {
+        return collect($columns)->map(function ($column) {
+            return $this->qualifyColumn($column);
+        })->all();
+    }
+
+    /**
+     * Create a new instance of the given model.
+     *
+     * @param  array  $attributes
+     * @param  bool  $exists
+     * @return static
+     */
+    public function newInstance($attributes = [], $exists = false)
+    {
+        // This method just provides a convenient way for us to generate fresh model
+        // instances of this current model. It is particularly useful during the
+        // hydration of new objects via the Eloquent query builder instances.
+        $model = new static;
+
+        $model->exists = $exists;
+
+        $model->setConnection(
+            $this->getConnectionName()
+        );
+
+        $model->setTable($this->getTable());
+
+        $model->mergeCasts($this->casts);
+
+        $model->fill((array) $attributes);
+
+        return $model;
+    }
+
+    /**
+     * Create a new model instance that is existing.
+     *
+     * @param  array  $attributes
+     * @param  string|null  $connection
+     * @return static
+     */
+    public function newFromBuilder($attributes = [], $connection = null)
+    {
+        $model = $this->newInstance([], true);
+
+        $model->setRawAttributes((array) $attributes, true);
+
+        $model->setConnection($connection ?: $this->getConnectionName());
+
+        $model->fireModelEvent('retrieved', false);
+
+        return $model;
+    }
+
+    /**
+     * Begin querying the model on a given connection.
+     *
+     * @param  string|null  $connection
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public static function on($connection = null)
+    {
+        // First we will just create a fresh instance of this model, and then we can set the
+        // connection on the model so that it is used for the queries we execute, as well
+        // as being set on every relation we retrieve without a custom connection name.
+        $instance = new static;
+
+        $instance->setConnection($connection);
+
+        return $instance->newQuery();
+    }
+
+    /**
+     * Begin querying the model on the write connection.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public static function onWriteConnection()
+    {
+        return static::query()->useWritePdo();
+    }
+
+    /**
+     * Get all of the models from the database.
+     *
+     * @param  array|string  $columns
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public static function all($columns = ['*'])
+    {
+        return static::query()->get(
+            is_array($columns) ? $columns : func_get_args()
+        );
+    }
+
+    /**
+     * Begin querying a model with eager loading.
+     *
+     * @param  array|string  $relations
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public static function with($relations)
+    {
+        return static::query()->with(
+            is_string($relations) ? func_get_args() : $relations
+        );
+    }
+
+    /**
+     * Eager load relations on the model.
+     *
+     * @param  array|string  $relations
+     * @return $this
+     */
+    public function load($relations)
+    {
+        $query = $this->newQueryWithoutRelationships()->with(
+            is_string($relations) ? func_get_args() : $relations
+        );
+
+        $query->eagerLoadRelations([$this]);
+
+        return $this;
+    }
+
+    /**
+     * Eager load relationships on the polymorphic relation of a model.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @return $this
+     */
+    public function loadMorph($relation, $relations)
+    {
+        if (! $this->{$relation}) {
+            return $this;
+        }
+
+        $className = get_class($this->{$relation});
+
+        $this->{$relation}->load($relations[$className] ?? []);
+
+        return $this;
+    }
+
+    /**
+     * Eager load relations on the model if they are not already eager loaded.
+     *
+     * @param  array|string  $relations
+     * @return $this
+     */
+    public function loadMissing($relations)
+    {
+        $relations = is_string($relations) ? func_get_args() : $relations;
+
+        $this->newCollection([$this])->loadMissing($relations);
+
+        return $this;
+    }
+
+    /**
+     * Eager load relation's column aggregations on the model.
+     *
+     * @param  array|string  $relations
+     * @param  string  $column
+     * @param  string  $function
+     * @return $this
+     */
+    public function loadAggregate($relations, $column, $function = null)
+    {
+        $this->newCollection([$this])->loadAggregate($relations, $column, $function);
+
+        return $this;
+    }
+
+    /**
+     * Eager load relation counts on the model.
+     *
+     * @param  array|string  $relations
+     * @return $this
+     */
+    public function loadCount($relations)
+    {
+        $relations = is_string($relations) ? func_get_args() : $relations;
+
+        return $this->loadAggregate($relations, '*', 'count');
+    }
+
+    /**
+     * Eager load relation max column values on the model.
+     *
+     * @param  array|string  $relations
+     * @param  string  $column
+     * @return $this
+     */
+    public function loadMax($relations, $column)
+    {
+        return $this->loadAggregate($relations, $column, 'max');
+    }
+
+    /**
+     * Eager load relation min column values on the model.
+     *
+     * @param  array|string  $relations
+     * @param  string  $column
+     * @return $this
+     */
+    public function loadMin($relations, $column)
+    {
+        return $this->loadAggregate($relations, $column, 'min');
+    }
+
+    /**
+     * Eager load relation's column summations on the model.
+     *
+     * @param  array|string  $relations
+     * @param  string  $column
+     * @return $this
+     */
+    public function loadSum($relations, $column)
+    {
+        return $this->loadAggregate($relations, $column, 'sum');
+    }
+
+    /**
+     * Eager load relation average column values on the model.
+     *
+     * @param  array|string  $relations
+     * @param  string  $column
+     * @return $this
+     */
+    public function loadAvg($relations, $column)
+    {
+        return $this->loadAggregate($relations, $column, 'avg');
+    }
+
+    /**
+     * Eager load related model existence values on the model.
+     *
+     * @param  array|string  $relations
+     * @return $this
+     */
+    public function loadExists($relations)
+    {
+        return $this->loadAggregate($relations, '*', 'exists');
+    }
+
+    /**
+     * Eager load relationship column aggregation on the polymorphic relation of a model.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @param  string  $column
+     * @param  string  $function
+     * @return $this
+     */
+    public function loadMorphAggregate($relation, $relations, $column, $function = null)
+    {
+        if (! $this->{$relation}) {
+            return $this;
+        }
+
+        $className = get_class($this->{$relation});
+
+        $this->{$relation}->loadAggregate($relations[$className] ?? [], $column, $function);
+
+        return $this;
+    }
+
+    /**
+     * Eager load relationship counts on the polymorphic relation of a model.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @return $this
+     */
+    public function loadMorphCount($relation, $relations)
+    {
+        return $this->loadMorphAggregate($relation, $relations, '*', 'count');
+    }
+
+    /**
+     * Eager load relationship max column values on the polymorphic relation of a model.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @param  string  $column
+     * @return $this
+     */
+    public function loadMorphMax($relation, $relations, $column)
+    {
+        return $this->loadMorphAggregate($relation, $relations, $column, 'max');
+    }
+
+    /**
+     * Eager load relationship min column values on the polymorphic relation of a model.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @param  string  $column
+     * @return $this
+     */
+    public function loadMorphMin($relation, $relations, $column)
+    {
+        return $this->loadMorphAggregate($relation, $relations, $column, 'min');
+    }
+
+    /**
+     * Eager load relationship column summations on the polymorphic relation of a model.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @param  string  $column
+     * @return $this
+     */
+    public function loadMorphSum($relation, $relations, $column)
+    {
+        return $this->loadMorphAggregate($relation, $relations, $column, 'sum');
+    }
+
+    /**
+     * Eager load relationship average column values on the polymorphic relation of a model.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @param  string  $column
+     * @return $this
+     */
+    public function loadMorphAvg($relation, $relations, $column)
+    {
+        return $this->loadMorphAggregate($relation, $relations, $column, 'avg');
+    }
+
+    /**
+     * Increment a column's value by a given amount.
+     *
+     * @param  string  $column
+     * @param  float|int  $amount
+     * @param  array  $extra
+     * @return int
+     */
+    protected function increment($column, $amount = 1, array $extra = [])
+    {
+        return $this->incrementOrDecrement($column, $amount, $extra, 'increment');
+    }
+
+    /**
+     * Decrement a column's value by a given amount.
+     *
+     * @param  string  $column
+     * @param  float|int  $amount
+     * @param  array  $extra
+     * @return int
+     */
+    protected function decrement($column, $amount = 1, array $extra = [])
+    {
+        return $this->incrementOrDecrement($column, $amount, $extra, 'decrement');
+    }
+
+    /**
+     * Run the increment or decrement method on the model.
+     *
+     * @param  string  $column
+     * @param  float|int  $amount
+     * @param  array  $extra
+     * @param  string  $method
+     * @return int
+     */
+    protected function incrementOrDecrement($column, $amount, $extra, $method)
+    {
+        $query = $this->newQueryWithoutRelationships();
+
+        if (! $this->exists) {
+            return $query->{$method}($column, $amount, $extra);
+        }
+
+        $this->{$column} = $this->isClassDeviable($column)
+            ? $this->deviateClassCastableAttribute($method, $column, $amount)
+            : $this->{$column} + ($method === 'increment' ? $amount : $amount * -1);
+
+        $this->forceFill($extra);
+
+        if ($this->fireModelEvent('updating') === false) {
+            return false;
+        }
+
+        return tap($this->setKeysForSaveQuery($query)->{$method}($column, $amount, $extra), function () use ($column) {
+            $this->syncChanges();
+
+            $this->fireModelEvent('updated', false);
+
+            $this->syncOriginalAttribute($column);
+        });
+    }
+
+    /**
+     * Update the model in the database.
+     *
+     * @param  array  $attributes
+     * @param  array  $options
+     * @return bool
+     */
+    public function update(array $attributes = [], array $options = [])
+    {
+        if (! $this->exists) {
+            return false;
+        }
+
+        return $this->fill($attributes)->save($options);
+    }
+
+    /**
+     * Update the model in the database within a transaction.
+     *
+     * @param  array  $attributes
+     * @param  array  $options
+     * @return bool
+     *
+     * @throws \Throwable
+     */
+    public function updateOrFail(array $attributes = [], array $options = [])
+    {
+        if (! $this->exists) {
+            return false;
+        }
+
+        return $this->fill($attributes)->saveOrFail($options);
+    }
+
+    /**
+     * Update the model in the database without raising any events.
+     *
+     * @param  array  $attributes
+     * @param  array  $options
+     * @return bool
+     */
+    public function updateQuietly(array $attributes = [], array $options = [])
+    {
+        if (! $this->exists) {
+            return false;
+        }
+
+        return $this->fill($attributes)->saveQuietly($options);
+    }
+
+    /**
+     * Increment a column's value by a given amount without raising any events.
+     *
+     * @param  string  $column
+     * @param  float|int  $amount
+     * @param  array  $extra
+     * @return int
+     */
+    protected function incrementQuietly($column, $amount = 1, array $extra = [])
+    {
+        return static::withoutEvents(function () use ($column, $amount, $extra) {
+            return $this->incrementOrDecrement($column, $amount, $extra, 'increment');
+        });
+    }
+
+    /**
+     * Decrement a column's value by a given amount without raising any events.
+     *
+     * @param  string  $column
+     * @param  float|int  $amount
+     * @param  array  $extra
+     * @return int
+     */
+    protected function decrementQuietly($column, $amount = 1, array $extra = [])
+    {
+        return static::withoutEvents(function () use ($column, $amount, $extra) {
+            return $this->incrementOrDecrement($column, $amount, $extra, 'decrement');
+        });
+    }
+
+    /**
+     * Save the model and all of its relationships.
+     *
+     * @return bool
+     */
+    public function push()
+    {
+        if (! $this->save()) {
+            return false;
+        }
+
+        // To sync all of the relationships to the database, we will simply spin through
+        // the relationships and save each model via this "push" method, which allows
+        // us to recurse into all of these nested relations for the model instance.
+        foreach ($this->relations as $models) {
+            $models = $models instanceof Collection
+                ? $models->all() : [$models];
+
+            foreach (array_filter($models) as $model) {
+                if (! $model->push()) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Save the model and all of its relationships without raising any events to the parent model.
+     *
+     * @return bool
+     */
+    public function pushQuietly()
+    {
+        return static::withoutEvents(fn () => $this->push());
+    }
+
+    /**
+     * Save the model to the database without raising any events.
+     *
+     * @param  array  $options
+     * @return bool
+     */
+    public function saveQuietly(array $options = [])
+    {
+        return static::withoutEvents(fn () => $this->save($options));
+    }
+
+    /**
+     * Save the model to the database.
+     *
+     * @param  array  $options
+     * @return bool
+     */
+    public function save(array $options = [])
+    {
+        $this->mergeAttributesFromCachedCasts();
+
+        $query = $this->newModelQuery();
+
+        // If the "saving" event returns false we'll bail out of the save and return
+        // false, indicating that the save failed. This provides a chance for any
+        // listeners to cancel save operations if validations fail or whatever.
+        if ($this->fireModelEvent('saving') === false) {
+            return false;
+        }
+
+        // If the model already exists in the database we can just update our record
+        // that is already in this database using the current IDs in this "where"
+        // clause to only update this model. Otherwise, we'll just insert them.
+        if ($this->exists) {
+            $saved = $this->isDirty() ?
+                $this->performUpdate($query) : true;
+        }
+
+        // If the model is brand new, we'll insert it into our database and set the
+        // ID attribute on the model to the value of the newly inserted row's ID
+        // which is typically an auto-increment value managed by the database.
+        else {
+            $saved = $this->performInsert($query);
+
+            if (! $this->getConnectionName() &&
+                $connection = $query->getConnection()) {
+                $this->setConnection($connection->getName());
+            }
+        }
+
+        // If the model is successfully saved, we need to do a few more things once
+        // that is done. We will call the "saved" method here to run any actions
+        // we need to happen after a model gets successfully saved right here.
+        if ($saved) {
+            $this->finishSave($options);
+        }
+
+        return $saved;
+    }
+
+    /**
+     * Save the model to the database within a transaction.
+     *
+     * @param  array  $options
+     * @return bool
+     *
+     * @throws \Throwable
+     */
+    public function saveOrFail(array $options = [])
+    {
+        return $this->getConnection()->transaction(fn () => $this->save($options));
+    }
+
+    /**
+     * Perform any actions that are necessary after the model is saved.
+     *
+     * @param  array  $options
+     * @return void
+     */
+    protected function finishSave(array $options)
+    {
+        $this->fireModelEvent('saved', false);
+
+        if ($this->isDirty() && ($options['touch'] ?? true)) {
+            $this->touchOwners();
+        }
+
+        $this->syncOriginal();
+    }
+
+    /**
+     * Perform a model update operation.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @return bool
+     */
+    protected function performUpdate(Builder $query)
+    {
+        // If the updating event returns false, we will cancel the update operation so
+        // developers can hook Validation systems into their models and cancel this
+        // operation if the model does not pass validation. Otherwise, we update.
+        if ($this->fireModelEvent('updating') === false) {
+            return false;
+        }
+
+        // First we need to create a fresh query instance and touch the creation and
+        // update timestamp on the model which are maintained by us for developer
+        // convenience. Then we will just continue saving the model instances.
+        if ($this->usesTimestamps()) {
+            $this->updateTimestamps();
+        }
+
+        // Once we have run the update operation, we will fire the "updated" event for
+        // this model instance. This will allow developers to hook into these after
+        // models are updated, giving them a chance to do any special processing.
+        $dirty = $this->getDirty();
+
+        if (count($dirty) > 0) {
+            $this->setKeysForSaveQuery($query)->update($dirty);
+
+            $this->syncChanges();
+
+            $this->fireModelEvent('updated', false);
+        }
+
+        return true;
+    }
+
+    /**
+     * Set the keys for a select query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function setKeysForSelectQuery($query)
+    {
+        $query->where($this->getKeyName(), '=', $this->getKeyForSelectQuery());
+
+        return $query;
+    }
+
+    /**
+     * Get the primary key value for a select query.
+     *
+     * @return mixed
+     */
+    protected function getKeyForSelectQuery()
+    {
+        return $this->original[$this->getKeyName()] ?? $this->getKey();
+    }
+
+    /**
+     * Set the keys for a save update query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function setKeysForSaveQuery($query)
+    {
+        $query->where($this->getKeyName(), '=', $this->getKeyForSaveQuery());
+
+        return $query;
+    }
+
+    /**
+     * Get the primary key value for a save query.
+     *
+     * @return mixed
+     */
+    protected function getKeyForSaveQuery()
+    {
+        return $this->original[$this->getKeyName()] ?? $this->getKey();
+    }
+
+    /**
+     * Perform a model insert operation.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @return bool
+     */
+    protected function performInsert(Builder $query)
+    {
+        if ($this->fireModelEvent('creating') === false) {
+            return false;
+        }
+
+        // First we'll need to create a fresh query instance and touch the creation and
+        // update timestamps on this model, which are maintained by us for developer
+        // convenience. After, we will just continue saving these model instances.
+        if ($this->usesTimestamps()) {
+            $this->updateTimestamps();
+        }
+
+        // If the model has an incrementing key, we can use the "insertGetId" method on
+        // the query builder, which will give us back the final inserted ID for this
+        // table from the database. Not all tables have to be incrementing though.
+        $attributes = $this->getAttributesForInsert();
+
+        if ($this->getIncrementing()) {
+            $this->insertAndSetId($query, $attributes);
+        }
+
+        // If the table isn't incrementing we'll simply insert these attributes as they
+        // are. These attribute arrays must contain an "id" column previously placed
+        // there by the developer as the manually determined key for these models.
+        else {
+            if (empty($attributes)) {
+                return true;
+            }
+
+            $query->insert($attributes);
+        }
+
+        // We will go ahead and set the exists property to true, so that it is set when
+        // the created event is fired, just in case the developer tries to update it
+        // during the event. This will allow them to do so and run an update here.
+        $this->exists = true;
+
+        $this->wasRecentlyCreated = true;
+
+        $this->fireModelEvent('created', false);
+
+        return true;
+    }
+
+    /**
+     * Insert the given attributes and set the ID on the model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  array  $attributes
+     * @return void
+     */
+    protected function insertAndSetId(Builder $query, $attributes)
+    {
+        $id = $query->insertGetId($attributes, $keyName = $this->getKeyName());
+
+        $this->setAttribute($keyName, $id);
+    }
+
+    /**
+     * Destroy the models for the given IDs.
+     *
+     * @param  \Illuminate\Support\Collection|array|int|string  $ids
+     * @return int
+     */
+    public static function destroy($ids)
+    {
+        if ($ids instanceof EloquentCollection) {
+            $ids = $ids->modelKeys();
+        }
+
+        if ($ids instanceof BaseCollection) {
+            $ids = $ids->all();
+        }
+
+        $ids = is_array($ids) ? $ids : func_get_args();
+
+        if (count($ids) === 0) {
+            return 0;
+        }
+
+        // We will actually pull the models from the database table and call delete on
+        // each of them individually so that their events get fired properly with a
+        // correct set of attributes in case the developers wants to check these.
+        $key = ($instance = new static)->getKeyName();
+
+        $count = 0;
+
+        foreach ($instance->whereIn($key, $ids)->get() as $model) {
+            if ($model->delete()) {
+                $count++;
+            }
+        }
+
+        return $count;
+    }
+
+    /**
+     * Delete the model from the database.
+     *
+     * @return bool|null
+     *
+     * @throws \LogicException
+     */
+    public function delete()
+    {
+        $this->mergeAttributesFromCachedCasts();
+
+        if (is_null($this->getKeyName())) {
+            throw new LogicException('No primary key defined on model.');
+        }
+
+        // If the model doesn't exist, there is nothing to delete so we'll just return
+        // immediately and not do anything else. Otherwise, we will continue with a
+        // deletion process on the model, firing the proper events, and so forth.
+        if (! $this->exists) {
+            return;
+        }
+
+        if ($this->fireModelEvent('deleting') === false) {
+            return false;
+        }
+
+        // Here, we'll touch the owning models, verifying these timestamps get updated
+        // for the models. This will allow any caching to get broken on the parents
+        // by the timestamp. Then we will go ahead and delete the model instance.
+        $this->touchOwners();
+
+        $this->performDeleteOnModel();
+
+        // Once the model has been deleted, we will fire off the deleted event so that
+        // the developers may hook into post-delete operations. We will then return
+        // a boolean true as the delete is presumably successful on the database.
+        $this->fireModelEvent('deleted', false);
+
+        return true;
+    }
+
+    /**
+     * Delete the model from the database without raising any events.
+     *
+     * @return bool
+     */
+    public function deleteQuietly()
+    {
+        return static::withoutEvents(fn () => $this->delete());
+    }
+
+    /**
+     * Delete the model from the database within a transaction.
+     *
+     * @return bool|null
+     *
+     * @throws \Throwable
+     */
+    public function deleteOrFail()
+    {
+        if (! $this->exists) {
+            return false;
+        }
+
+        return $this->getConnection()->transaction(fn () => $this->delete());
+    }
+
+    /**
+     * Force a hard delete on a soft deleted model.
+     *
+     * This method protects developers from running forceDelete when the trait is missing.
+     *
+     * @return bool|null
+     */
+    public function forceDelete()
+    {
+        return $this->delete();
+    }
+
+    /**
+     * Perform the actual delete query on this model instance.
+     *
+     * @return void
+     */
+    protected function performDeleteOnModel()
+    {
+        $this->setKeysForSaveQuery($this->newModelQuery())->delete();
+
+        $this->exists = false;
+    }
+
+    /**
+     * Begin querying the model.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public static function query()
+    {
+        return (new static)->newQuery();
+    }
+
+    /**
+     * Get a new query builder for the model's table.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function newQuery()
+    {
+        return $this->registerGlobalScopes($this->newQueryWithoutScopes());
+    }
+
+    /**
+     * Get a new query builder that doesn't have any global scopes or eager loading.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function newModelQuery()
+    {
+        return $this->newEloquentBuilder(
+            $this->newBaseQueryBuilder()
+        )->setModel($this);
+    }
+
+    /**
+     * Get a new query builder with no relationships loaded.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function newQueryWithoutRelationships()
+    {
+        return $this->registerGlobalScopes($this->newModelQuery());
+    }
+
+    /**
+     * Register the global scopes for this builder instance.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function registerGlobalScopes($builder)
+    {
+        foreach ($this->getGlobalScopes() as $identifier => $scope) {
+            $builder->withGlobalScope($identifier, $scope);
+        }
+
+        return $builder;
+    }
+
+    /**
+     * Get a new query builder that doesn't have any global scopes.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function newQueryWithoutScopes()
+    {
+        return $this->newModelQuery()
+            ->with($this->with)
+            ->withCount($this->withCount);
+    }
+
+    /**
+     * Get a new query instance without a given scope.
+     *
+     * @param  \Illuminate\Database\Eloquent\Scope|string  $scope
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function newQueryWithoutScope($scope)
+    {
+        return $this->newQuery()->withoutGlobalScope($scope);
+    }
+
+    /**
+     * Get a new query to restore one or more models by their queueable IDs.
+     *
+     * @param  array|int  $ids
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function newQueryForRestoration($ids)
+    {
+        return $this->newQueryWithoutScopes()->whereKey($ids);
+    }
+
+    /**
+     * Create a new Eloquent query builder for the model.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return \Illuminate\Database\Eloquent\Builder|static
+     */
+    public function newEloquentBuilder($query)
+    {
+        return new Builder($query);
+    }
+
+    /**
+     * Get a new query builder instance for the connection.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    protected function newBaseQueryBuilder()
+    {
+        return $this->getConnection()->query();
+    }
+
+    /**
+     * Create a new Eloquent Collection instance.
+     *
+     * @param  array  $models
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function newCollection(array $models = [])
+    {
+        return new Collection($models);
+    }
+
+    /**
+     * Create a new pivot model instance.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @param  array  $attributes
+     * @param  string  $table
+     * @param  bool  $exists
+     * @param  string|null  $using
+     * @return \Illuminate\Database\Eloquent\Relations\Pivot
+     */
+    public function newPivot(self $parent, array $attributes, $table, $exists, $using = null)
+    {
+        return $using ? $using::fromRawAttributes($parent, $attributes, $table, $exists)
+            : Pivot::fromAttributes($parent, $attributes, $table, $exists);
+    }
+
+    /**
+     * Determine if the model has a given scope.
+     *
+     * @param  string  $scope
+     * @return bool
+     */
+    public function hasNamedScope($scope)
+    {
+        return method_exists($this, 'scope'.ucfirst($scope));
+    }
+
+    /**
+     * Apply the given named scope if possible.
+     *
+     * @param  string  $scope
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function callNamedScope($scope, array $parameters = [])
+    {
+        return $this->{'scope'.ucfirst($scope)}(...$parameters);
+    }
+
+    /**
+     * Convert the model instance to an array.
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return array_merge($this->attributesToArray(), $this->relationsToArray());
+    }
+
+    /**
+     * Convert the model instance to JSON.
+     *
+     * @param  int  $options
+     * @return string
+     *
+     * @throws \Illuminate\Database\Eloquent\JsonEncodingException
+     */
+    public function toJson($options = 0)
+    {
+        $json = json_encode($this->jsonSerialize(), $options);
+
+        if (json_last_error() !== JSON_ERROR_NONE) {
+            throw JsonEncodingException::forModel($this, json_last_error_msg());
+        }
+
+        return $json;
+    }
+
+    /**
+     * Convert the object into something JSON serializable.
+     *
+     * @return mixed
+     */
+    public function jsonSerialize(): mixed
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * Reload a fresh model instance from the database.
+     *
+     * @param  array|string  $with
+     * @return static|null
+     */
+    public function fresh($with = [])
+    {
+        if (! $this->exists) {
+            return;
+        }
+
+        return $this->setKeysForSelectQuery($this->newQueryWithoutScopes())
+            ->useWritePdo()
+            ->with(is_string($with) ? func_get_args() : $with)
+            ->first();
+    }
+
+    /**
+     * Reload the current model instance with fresh attributes from the database.
+     *
+     * @return $this
+     */
+    public function refresh()
+    {
+        if (! $this->exists) {
+            return $this;
+        }
+
+        $this->setRawAttributes(
+            $this->setKeysForSelectQuery($this->newQueryWithoutScopes())
+                ->useWritePdo()
+                ->firstOrFail()
+                ->attributes
+        );
+
+        $this->load(collect($this->relations)->reject(function ($relation) {
+            return $relation instanceof Pivot
+                || (is_object($relation) && in_array(AsPivot::class, class_uses_recursive($relation), true));
+        })->keys()->all());
+
+        $this->syncOriginal();
+
+        return $this;
+    }
+
+    /**
+     * Clone the model into a new, non-existing instance.
+     *
+     * @param  array|null  $except
+     * @return static
+     */
+    public function replicate(array $except = null)
+    {
+        $defaults = array_values(array_filter([
+            $this->getKeyName(),
+            $this->getCreatedAtColumn(),
+            $this->getUpdatedAtColumn(),
+        ]));
+
+        $attributes = Arr::except(
+            $this->getAttributes(), $except ? array_unique(array_merge($except, $defaults)) : $defaults
+        );
+
+        return tap(new static, function ($instance) use ($attributes) {
+            $instance->setRawAttributes($attributes);
+
+            $instance->setRelations($this->relations);
+
+            $instance->fireModelEvent('replicating', false);
+        });
+    }
+
+    /**
+     * Clone the model into a new, non-existing instance without raising any events.
+     *
+     * @param  array|null  $except
+     * @return static
+     */
+    public function replicateQuietly(array $except = null)
+    {
+        return static::withoutEvents(fn () => $this->replicate($except));
+    }
+
+    /**
+     * Determine if two models have the same ID and belong to the same table.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|null  $model
+     * @return bool
+     */
+    public function is($model)
+    {
+        return ! is_null($model) &&
+            $this->getKey() === $model->getKey() &&
+            $this->getTable() === $model->getTable() &&
+            $this->getConnectionName() === $model->getConnectionName();
+    }
+
+    /**
+     * Determine if two models are not the same.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|null  $model
+     * @return bool
+     */
+    public function isNot($model)
+    {
+        return ! $this->is($model);
+    }
+
+    /**
+     * Get the database connection for the model.
+     *
+     * @return \Illuminate\Database\Connection
+     */
+    public function getConnection()
+    {
+        return static::resolveConnection($this->getConnectionName());
+    }
+
+    /**
+     * Get the current connection name for the model.
+     *
+     * @return string|null
+     */
+    public function getConnectionName()
+    {
+        return $this->connection;
+    }
+
+    /**
+     * Set the connection associated with the model.
+     *
+     * @param  string|null  $name
+     * @return $this
+     */
+    public function setConnection($name)
+    {
+        $this->connection = $name;
+
+        return $this;
+    }
+
+    /**
+     * Resolve a connection instance.
+     *
+     * @param  string|null  $connection
+     * @return \Illuminate\Database\Connection
+     */
+    public static function resolveConnection($connection = null)
+    {
+        return static::$resolver->connection($connection);
+    }
+
+    /**
+     * Get the connection resolver instance.
+     *
+     * @return \Illuminate\Database\ConnectionResolverInterface
+     */
+    public static function getConnectionResolver()
+    {
+        return static::$resolver;
+    }
+
+    /**
+     * Set the connection resolver instance.
+     *
+     * @param  \Illuminate\Database\ConnectionResolverInterface  $resolver
+     * @return void
+     */
+    public static function setConnectionResolver(Resolver $resolver)
+    {
+        static::$resolver = $resolver;
+    }
+
+    /**
+     * Unset the connection resolver for models.
+     *
+     * @return void
+     */
+    public static function unsetConnectionResolver()
+    {
+        static::$resolver = null;
+    }
+
+    /**
+     * Get the table associated with the model.
+     *
+     * @return string
+     */
+    public function getTable()
+    {
+        return $this->table ?? Str::snake(Str::pluralStudly(class_basename($this)));
+    }
+
+    /**
+     * Set the table associated with the model.
+     *
+     * @param  string  $table
+     * @return $this
+     */
+    public function setTable($table)
+    {
+        $this->table = $table;
+
+        return $this;
+    }
+
+    /**
+     * Get the primary key for the model.
+     *
+     * @return string
+     */
+    public function getKeyName()
+    {
+        return $this->primaryKey;
+    }
+
+    /**
+     * Set the primary key for the model.
+     *
+     * @param  string  $key
+     * @return $this
+     */
+    public function setKeyName($key)
+    {
+        $this->primaryKey = $key;
+
+        return $this;
+    }
+
+    /**
+     * Get the table qualified key name.
+     *
+     * @return string
+     */
+    public function getQualifiedKeyName()
+    {
+        return $this->qualifyColumn($this->getKeyName());
+    }
+
+    /**
+     * Get the auto-incrementing key type.
+     *
+     * @return string
+     */
+    public function getKeyType()
+    {
+        return $this->keyType;
+    }
+
+    /**
+     * Set the data type for the primary key.
+     *
+     * @param  string  $type
+     * @return $this
+     */
+    public function setKeyType($type)
+    {
+        $this->keyType = $type;
+
+        return $this;
+    }
+
+    /**
+     * Get the value indicating whether the IDs are incrementing.
+     *
+     * @return bool
+     */
+    public function getIncrementing()
+    {
+        return $this->incrementing;
+    }
+
+    /**
+     * Set whether IDs are incrementing.
+     *
+     * @param  bool  $value
+     * @return $this
+     */
+    public function setIncrementing($value)
+    {
+        $this->incrementing = $value;
+
+        return $this;
+    }
+
+    /**
+     * Get the value of the model's primary key.
+     *
+     * @return mixed
+     */
+    public function getKey()
+    {
+        return $this->getAttribute($this->getKeyName());
+    }
+
+    /**
+     * Get the queueable identity for the entity.
+     *
+     * @return mixed
+     */
+    public function getQueueableId()
+    {
+        return $this->getKey();
+    }
+
+    /**
+     * Get the queueable relationships for the entity.
+     *
+     * @return array
+     */
+    public function getQueueableRelations()
+    {
+        $relations = [];
+
+        foreach ($this->getRelations() as $key => $relation) {
+            if (! method_exists($this, $key)) {
+                continue;
+            }
+
+            $relations[] = $key;
+
+            if ($relation instanceof QueueableCollection) {
+                foreach ($relation->getQueueableRelations() as $collectionValue) {
+                    $relations[] = $key.'.'.$collectionValue;
+                }
+            }
+
+            if ($relation instanceof QueueableEntity) {
+                foreach ($relation->getQueueableRelations() as $entityValue) {
+                    $relations[] = $key.'.'.$entityValue;
+                }
+            }
+        }
+
+        return array_unique($relations);
+    }
+
+    /**
+     * Get the queueable connection for the entity.
+     *
+     * @return string|null
+     */
+    public function getQueueableConnection()
+    {
+        return $this->getConnectionName();
+    }
+
+    /**
+     * Get the value of the model's route key.
+     *
+     * @return mixed
+     */
+    public function getRouteKey()
+    {
+        return $this->getAttribute($this->getRouteKeyName());
+    }
+
+    /**
+     * Get the route key for the model.
+     *
+     * @return string
+     */
+    public function getRouteKeyName()
+    {
+        return $this->getKeyName();
+    }
+
+    /**
+     * Retrieve the model for a bound value.
+     *
+     * @param  mixed  $value
+     * @param  string|null  $field
+     * @return \Illuminate\Database\Eloquent\Model|null
+     */
+    public function resolveRouteBinding($value, $field = null)
+    {
+        return $this->resolveRouteBindingQuery($this, $value, $field)->first();
+    }
+
+    /**
+     * Retrieve the model for a bound value.
+     *
+     * @param  mixed  $value
+     * @param  string|null  $field
+     * @return \Illuminate\Database\Eloquent\Model|null
+     */
+    public function resolveSoftDeletableRouteBinding($value, $field = null)
+    {
+        return $this->resolveRouteBindingQuery($this, $value, $field)->withTrashed()->first();
+    }
+
+    /**
+     * Retrieve the child model for a bound value.
+     *
+     * @param  string  $childType
+     * @param  mixed  $value
+     * @param  string|null  $field
+     * @return \Illuminate\Database\Eloquent\Model|null
+     */
+    public function resolveChildRouteBinding($childType, $value, $field)
+    {
+        return $this->resolveChildRouteBindingQuery($childType, $value, $field)->first();
+    }
+
+    /**
+     * Retrieve the child model for a bound value.
+     *
+     * @param  string  $childType
+     * @param  mixed  $value
+     * @param  string|null  $field
+     * @return \Illuminate\Database\Eloquent\Model|null
+     */
+    public function resolveSoftDeletableChildRouteBinding($childType, $value, $field)
+    {
+        return $this->resolveChildRouteBindingQuery($childType, $value, $field)->withTrashed()->first();
+    }
+
+    /**
+     * Retrieve the child model query for a bound value.
+     *
+     * @param  string  $childType
+     * @param  mixed  $value
+     * @param  string|null  $field
+     * @return \Illuminate\Database\Eloquent\Relations\Relation
+     */
+    protected function resolveChildRouteBindingQuery($childType, $value, $field)
+    {
+        $relationship = $this->{$this->childRouteBindingRelationshipName($childType)}();
+
+        $field = $field ?: $relationship->getRelated()->getRouteKeyName();
+
+        if ($relationship instanceof HasManyThrough ||
+            $relationship instanceof BelongsToMany) {
+            $field = $relationship->getRelated()->getTable().'.'.$field;
+        }
+
+        return $relationship instanceof Model
+            ? $relationship->resolveRouteBindingQuery($relationship, $value, $field)
+            : $relationship->getRelated()->resolveRouteBindingQuery($relationship, $value, $field);
+    }
+
+    /**
+     * Retrieve the child route model binding relationship name for the given child type.
+     *
+     * @param  string  $childType
+     * @return string
+     */
+    protected function childRouteBindingRelationshipName($childType)
+    {
+        return Str::plural(Str::camel($childType));
+    }
+
+    /**
+     * Retrieve the model for a bound value.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\Relation  $query
+     * @param  mixed  $value
+     * @param  string|null  $field
+     * @return \Illuminate\Database\Eloquent\Relations\Relation
+     */
+    public function resolveRouteBindingQuery($query, $value, $field = null)
+    {
+        return $query->where($field ?? $this->getRouteKeyName(), $value);
+    }
+
+    /**
+     * Get the default foreign key name for the model.
+     *
+     * @return string
+     */
+    public function getForeignKey()
+    {
+        return Str::snake(class_basename($this)).'_'.$this->getKeyName();
+    }
+
+    /**
+     * Get the number of models to return per page.
+     *
+     * @return int
+     */
+    public function getPerPage()
+    {
+        return $this->perPage;
+    }
+
+    /**
+     * Set the number of models to return per page.
+     *
+     * @param  int  $perPage
+     * @return $this
+     */
+    public function setPerPage($perPage)
+    {
+        $this->perPage = $perPage;
+
+        return $this;
+    }
+
+    /**
+     * Determine if lazy loading is disabled.
+     *
+     * @return bool
+     */
+    public static function preventsLazyLoading()
+    {
+        return static::$modelsShouldPreventLazyLoading;
+    }
+
+    /**
+     * Determine if discarding guarded attribute fills is disabled.
+     *
+     * @return bool
+     */
+    public static function preventsSilentlyDiscardingAttributes()
+    {
+        return static::$modelsShouldPreventSilentlyDiscardingAttributes;
+    }
+
+    /**
+     * Determine if accessing missing attributes is disabled.
+     *
+     * @return bool
+     */
+    public static function preventsAccessingMissingAttributes()
+    {
+        return static::$modelsShouldPreventAccessingMissingAttributes;
+    }
+
+    /**
+     * Get the broadcast channel route definition that is associated with the given entity.
+     *
+     * @return string
+     */
+    public function broadcastChannelRoute()
+    {
+        return str_replace('\\', '.', get_class($this)).'.{'.Str::camel(class_basename($this)).'}';
+    }
+
+    /**
+     * Get the broadcast channel name that is associated with the given entity.
+     *
+     * @return string
+     */
+    public function broadcastChannel()
+    {
+        return str_replace('\\', '.', get_class($this)).'.'.$this->getKey();
+    }
+
+    /**
+     * Dynamically retrieve attributes on the model.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function __get($key)
+    {
+        return $this->getAttribute($key);
+    }
+
+    /**
+     * Dynamically set attributes on the model.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return void
+     */
+    public function __set($key, $value)
+    {
+        $this->setAttribute($key, $value);
+    }
+
+    /**
+     * Determine if the given attribute exists.
+     *
+     * @param  mixed  $offset
+     * @return bool
+     */
+    public function offsetExists($offset): bool
+    {
+        try {
+            return ! is_null($this->getAttribute($offset));
+        } catch (MissingAttributeException) {
+            return false;
+        }
+    }
+
+    /**
+     * Get the value for a given offset.
+     *
+     * @param  mixed  $offset
+     * @return mixed
+     */
+    public function offsetGet($offset): mixed
+    {
+        return $this->getAttribute($offset);
+    }
+
+    /**
+     * Set the value for a given offset.
+     *
+     * @param  mixed  $offset
+     * @param  mixed  $value
+     * @return void
+     */
+    public function offsetSet($offset, $value): void
+    {
+        $this->setAttribute($offset, $value);
+    }
+
+    /**
+     * Unset the value for a given offset.
+     *
+     * @param  mixed  $offset
+     * @return void
+     */
+    public function offsetUnset($offset): void
+    {
+        unset($this->attributes[$offset], $this->relations[$offset]);
+    }
+
+    /**
+     * Determine if an attribute or relation exists on the model.
+     *
+     * @param  string  $key
+     * @return bool
+     */
+    public function __isset($key)
+    {
+        return $this->offsetExists($key);
+    }
+
+    /**
+     * Unset an attribute on the model.
+     *
+     * @param  string  $key
+     * @return void
+     */
+    public function __unset($key)
+    {
+        $this->offsetUnset($key);
+    }
+
+    /**
+     * Handle dynamic method calls into the model.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        if (in_array($method, ['increment', 'decrement'])) {
+            return $this->$method(...$parameters);
+        }
+
+        if ($resolver = $this->relationResolver(static::class, $method)) {
+            return $resolver($this);
+        }
+
+        if (Str::startsWith($method, 'through') &&
+            method_exists($this, $relationMethod = Str::of($method)->after('through')->lcfirst()->toString())) {
+            return $this->through($relationMethod);
+        }
+
+        return $this->forwardCallTo($this->newQuery(), $method, $parameters);
+    }
+
+    /**
+     * Handle dynamic static method calls into the model.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public static function __callStatic($method, $parameters)
+    {
+        return (new static)->$method(...$parameters);
+    }
+
+    /**
+     * Convert the model to its string representation.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->escapeWhenCastingToString
+            ? e($this->toJson())
+            : $this->toJson();
+    }
+
+    /**
+     * Indicate that the object's string representation should be escaped when __toString is invoked.
+     *
+     * @param  bool  $escape
+     * @return $this
+     */
+    public function escapeWhenCastingToString($escape = true)
+    {
+        $this->escapeWhenCastingToString = $escape;
+
+        return $this;
+    }
+
+    /**
+     * Prepare the object for serialization.
+     *
+     * @return array
+     */
+    public function __sleep()
+    {
+        $this->mergeAttributesFromCachedCasts();
+
+        $this->classCastCache = [];
+        $this->attributeCastCache = [];
+
+        return array_keys(get_object_vars($this));
+    }
+
+    /**
+     * When a model is being unserialized, check if it needs to be booted.
+     *
+     * @return void
+     */
+    public function __wakeup()
+    {
+        $this->bootIfNotBooted();
+
+        $this->initializeTraits();
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/ModelNotFoundException.php b/vendor/illuminate/database/Eloquent/ModelNotFoundException.php
new file mode 100755
index 0000000..79ae8a3
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/ModelNotFoundException.php
@@ -0,0 +1,69 @@
+
+     */
+    protected $model;
+
+    /**
+     * The affected model IDs.
+     *
+     * @var array
+     */
+    protected $ids;
+
+    /**
+     * Set the affected Eloquent model and instance ids.
+     *
+     * @param  class-string  $model
+     * @param  array|int|string  $ids
+     * @return $this
+     */
+    public function setModel($model, $ids = [])
+    {
+        $this->model = $model;
+        $this->ids = Arr::wrap($ids);
+
+        $this->message = "No query results for model [{$model}]";
+
+        if (count($this->ids) > 0) {
+            $this->message .= ' '.implode(', ', $this->ids);
+        } else {
+            $this->message .= '.';
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get the affected Eloquent model.
+     *
+     * @return class-string
+     */
+    public function getModel()
+    {
+        return $this->model;
+    }
+
+    /**
+     * Get the affected Eloquent model IDs.
+     *
+     * @return array
+     */
+    public function getIds()
+    {
+        return $this->ids;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/PendingHasThroughRelationship.php b/vendor/illuminate/database/Eloquent/PendingHasThroughRelationship.php
new file mode 100644
index 0000000..612c51e
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/PendingHasThroughRelationship.php
@@ -0,0 +1,90 @@
+rootModel = $rootModel;
+
+        $this->localRelationship = $localRelationship;
+    }
+
+    /**
+     * Define the distant relationship that this model has.
+     *
+     * @param  string|(callable(\Illuminate\Database\Eloquent\Model): (\Illuminate\Database\Eloquent\Relations\HasOne|\Illuminate\Database\Eloquent\Relations\HasMany))  $callback
+     * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough|\Illuminate\Database\Eloquent\Relations\HasOneThrough
+     */
+    public function has($callback)
+    {
+        if (is_string($callback)) {
+            $callback = fn () => $this->localRelationship->getRelated()->{$callback}();
+        }
+
+        $distantRelation = $callback($this->localRelationship->getRelated());
+
+        if ($distantRelation instanceof HasMany) {
+            return $this->rootModel->hasManyThrough(
+                $distantRelation->getRelated()::class,
+                $this->localRelationship->getRelated()::class,
+                $this->localRelationship->getForeignKeyName(),
+                $distantRelation->getForeignKeyName(),
+                $this->localRelationship->getLocalKeyName(),
+                $distantRelation->getLocalKeyName(),
+            );
+        }
+
+        return $this->rootModel->hasOneThrough(
+            $distantRelation->getRelated()::class,
+            $this->localRelationship->getRelated()::class,
+            $this->localRelationship->getForeignKeyName(),
+            $distantRelation->getForeignKeyName(),
+            $this->localRelationship->getLocalKeyName(),
+            $distantRelation->getLocalKeyName(),
+        );
+    }
+
+    /**
+     * Handle dynamic method calls into the model.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        if (Str::startsWith($method, 'has')) {
+            return $this->has(Str::of($method)->after('has')->lcfirst()->toString());
+        }
+
+        throw new BadMethodCallException(sprintf(
+            'Call to undefined method %s::%s()', static::class, $method
+        ));
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Prunable.php b/vendor/illuminate/database/Eloquent/Prunable.php
new file mode 100644
index 0000000..b4ce1b0
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Prunable.php
@@ -0,0 +1,67 @@
+prunable()
+            ->when(in_array(SoftDeletes::class, class_uses_recursive(get_class($this))), function ($query) {
+                $query->withTrashed();
+            })->chunkById($chunkSize, function ($models) use (&$total) {
+                $models->each->prune();
+
+                $total += $models->count();
+
+                event(new ModelsPruned(static::class, $total));
+            });
+
+        return $total;
+    }
+
+    /**
+     * Get the prunable model query.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function prunable()
+    {
+        throw new LogicException('Please implement the prunable method on your model.');
+    }
+
+    /**
+     * Prune the model in the database.
+     *
+     * @return bool|null
+     */
+    public function prune()
+    {
+        $this->pruning();
+
+        return in_array(SoftDeletes::class, class_uses_recursive(get_class($this)))
+                ? $this->forceDelete()
+                : $this->delete();
+    }
+
+    /**
+     * Prepare the model for pruning.
+     *
+     * @return void
+     */
+    protected function pruning()
+    {
+        //
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/QueueEntityResolver.php b/vendor/illuminate/database/Eloquent/QueueEntityResolver.php
new file mode 100644
index 0000000..22fccf2
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/QueueEntityResolver.php
@@ -0,0 +1,29 @@
+find($id);
+
+        if ($instance) {
+            return $instance;
+        }
+
+        throw new EntityNotFoundException($type, $id);
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/RelationNotFoundException.php b/vendor/illuminate/database/Eloquent/RelationNotFoundException.php
new file mode 100755
index 0000000..73257bb
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/RelationNotFoundException.php
@@ -0,0 +1,46 @@
+model = $class;
+        $instance->relation = $relation;
+
+        return $instance;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/BelongsTo.php b/vendor/illuminate/database/Eloquent/Relations/BelongsTo.php
new file mode 100755
index 0000000..c17b733
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/BelongsTo.php
@@ -0,0 +1,383 @@
+ownerKey = $ownerKey;
+        $this->relationName = $relationName;
+        $this->foreignKey = $foreignKey;
+
+        // In the underlying base relationship class, this variable is referred to as
+        // the "parent" since most relationships are not inversed. But, since this
+        // one is we will create a "child" variable for much better readability.
+        $this->child = $child;
+
+        parent::__construct($query, $child);
+    }
+
+    /**
+     * Get the results of the relationship.
+     *
+     * @return mixed
+     */
+    public function getResults()
+    {
+        if (is_null($this->child->{$this->foreignKey})) {
+            return $this->getDefaultFor($this->parent);
+        }
+
+        return $this->query->first() ?: $this->getDefaultFor($this->parent);
+    }
+
+    /**
+     * Set the base constraints on the relation query.
+     *
+     * @return void
+     */
+    public function addConstraints()
+    {
+        if (static::$constraints) {
+            // For belongs to relationships, which are essentially the inverse of has one
+            // or has many relationships, we need to actually query on the primary key
+            // of the related models matching on the foreign key that's on a parent.
+            $table = $this->related->getTable();
+
+            $this->query->where($table.'.'.$this->ownerKey, '=', $this->child->{$this->foreignKey});
+        }
+    }
+
+    /**
+     * Set the constraints for an eager load of the relation.
+     *
+     * @param  array  $models
+     * @return void
+     */
+    public function addEagerConstraints(array $models)
+    {
+        // We'll grab the primary key name of the related models since it could be set to
+        // a non-standard name and not "id". We will then construct the constraint for
+        // our eagerly loading query so it returns the proper models from execution.
+        $key = $this->related->getTable().'.'.$this->ownerKey;
+
+        $whereIn = $this->whereInMethod($this->related, $this->ownerKey);
+
+        $this->query->{$whereIn}($key, $this->getEagerModelKeys($models));
+    }
+
+    /**
+     * Gather the keys from an array of related models.
+     *
+     * @param  array  $models
+     * @return array
+     */
+    protected function getEagerModelKeys(array $models)
+    {
+        $keys = [];
+
+        // First we need to gather all of the keys from the parent models so we know what
+        // to query for via the eager loading query. We will add them to an array then
+        // execute a "where in" statement to gather up all of those related records.
+        foreach ($models as $model) {
+            if (! is_null($value = $model->{$this->foreignKey})) {
+                $keys[] = $value;
+            }
+        }
+
+        sort($keys);
+
+        return array_values(array_unique($keys));
+    }
+
+    /**
+     * Initialize the relation on a set of models.
+     *
+     * @param  array  $models
+     * @param  string  $relation
+     * @return array
+     */
+    public function initRelation(array $models, $relation)
+    {
+        foreach ($models as $model) {
+            $model->setRelation($relation, $this->getDefaultFor($model));
+        }
+
+        return $models;
+    }
+
+    /**
+     * Match the eagerly loaded results to their parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @return array
+     */
+    public function match(array $models, Collection $results, $relation)
+    {
+        $foreign = $this->foreignKey;
+
+        $owner = $this->ownerKey;
+
+        // First we will get to build a dictionary of the child models by their primary
+        // key of the relationship, then we can easily match the children back onto
+        // the parents using that dictionary and the primary key of the children.
+        $dictionary = [];
+
+        foreach ($results as $result) {
+            $attribute = $this->getDictionaryKey($result->getAttribute($owner));
+
+            $dictionary[$attribute] = $result;
+        }
+
+        // Once we have the dictionary constructed, we can loop through all the parents
+        // and match back onto their children using these keys of the dictionary and
+        // the primary key of the children to map them onto the correct instances.
+        foreach ($models as $model) {
+            $attribute = $this->getDictionaryKey($model->{$foreign});
+
+            if (isset($dictionary[$attribute])) {
+                $model->setRelation($relation, $dictionary[$attribute]);
+            }
+        }
+
+        return $models;
+    }
+
+    /**
+     * Associate the model instance to the given parent.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|int|string|null  $model
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function associate($model)
+    {
+        $ownerKey = $model instanceof Model ? $model->getAttribute($this->ownerKey) : $model;
+
+        $this->child->setAttribute($this->foreignKey, $ownerKey);
+
+        if ($model instanceof Model) {
+            $this->child->setRelation($this->relationName, $model);
+        } else {
+            $this->child->unsetRelation($this->relationName);
+        }
+
+        return $this->child;
+    }
+
+    /**
+     * Dissociate previously associated model from the given parent.
+     *
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function dissociate()
+    {
+        $this->child->setAttribute($this->foreignKey, null);
+
+        return $this->child->setRelation($this->relationName, null);
+    }
+
+    /**
+     * Alias of "dissociate" method.
+     *
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function disassociate()
+    {
+        return $this->dissociate();
+    }
+
+    /**
+     * Add the constraints for a relationship query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        if ($parentQuery->getQuery()->from == $query->getQuery()->from) {
+            return $this->getRelationExistenceQueryForSelfRelation($query, $parentQuery, $columns);
+        }
+
+        return $query->select($columns)->whereColumn(
+            $this->getQualifiedForeignKeyName(), '=', $query->qualifyColumn($this->ownerKey)
+        );
+    }
+
+    /**
+     * Add the constraints for a relationship query on the same table.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQueryForSelfRelation(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        $query->select($columns)->from(
+            $query->getModel()->getTable().' as '.$hash = $this->getRelationCountHash()
+        );
+
+        $query->getModel()->setTable($hash);
+
+        return $query->whereColumn(
+            $hash.'.'.$this->ownerKey, '=', $this->getQualifiedForeignKeyName()
+        );
+    }
+
+    /**
+     * Determine if the related model has an auto-incrementing ID.
+     *
+     * @return bool
+     */
+    protected function relationHasIncrementingId()
+    {
+        return $this->related->getIncrementing() &&
+            in_array($this->related->getKeyType(), ['int', 'integer']);
+    }
+
+    /**
+     * Make a new related instance for the given model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    protected function newRelatedInstanceFor(Model $parent)
+    {
+        return $this->related->newInstance();
+    }
+
+    /**
+     * Get the child of the relationship.
+     *
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function getChild()
+    {
+        return $this->child;
+    }
+
+    /**
+     * Get the foreign key of the relationship.
+     *
+     * @return string
+     */
+    public function getForeignKeyName()
+    {
+        return $this->foreignKey;
+    }
+
+    /**
+     * Get the fully qualified foreign key of the relationship.
+     *
+     * @return string
+     */
+    public function getQualifiedForeignKeyName()
+    {
+        return $this->child->qualifyColumn($this->foreignKey);
+    }
+
+    /**
+     * Get the key value of the child's foreign key.
+     *
+     * @return mixed
+     */
+    public function getParentKey()
+    {
+        return $this->child->{$this->foreignKey};
+    }
+
+    /**
+     * Get the associated key of the relationship.
+     *
+     * @return string
+     */
+    public function getOwnerKeyName()
+    {
+        return $this->ownerKey;
+    }
+
+    /**
+     * Get the fully qualified associated key of the relationship.
+     *
+     * @return string
+     */
+    public function getQualifiedOwnerKeyName()
+    {
+        return $this->related->qualifyColumn($this->ownerKey);
+    }
+
+    /**
+     * Get the value of the model's associated key.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return mixed
+     */
+    protected function getRelatedKeyFrom(Model $model)
+    {
+        return $model->{$this->ownerKey};
+    }
+
+    /**
+     * Get the name of the relationship.
+     *
+     * @return string
+     */
+    public function getRelationName()
+    {
+        return $this->relationName;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/BelongsToMany.php b/vendor/illuminate/database/Eloquent/Relations/BelongsToMany.php
new file mode 100755
index 0000000..6a9ec9e
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/BelongsToMany.php
@@ -0,0 +1,1503 @@
+parentKey = $parentKey;
+        $this->relatedKey = $relatedKey;
+        $this->relationName = $relationName;
+        $this->relatedPivotKey = $relatedPivotKey;
+        $this->foreignPivotKey = $foreignPivotKey;
+        $this->table = $this->resolveTableName($table);
+
+        parent::__construct($query, $parent);
+    }
+
+    /**
+     * Attempt to resolve the intermediate table name from the given string.
+     *
+     * @param  string  $table
+     * @return string
+     */
+    protected function resolveTableName($table)
+    {
+        if (! str_contains($table, '\\') || ! class_exists($table)) {
+            return $table;
+        }
+
+        $model = new $table;
+
+        if (! $model instanceof Model) {
+            return $table;
+        }
+
+        if (in_array(AsPivot::class, class_uses_recursive($model))) {
+            $this->using($table);
+        }
+
+        return $model->getTable();
+    }
+
+    /**
+     * Set the base constraints on the relation query.
+     *
+     * @return void
+     */
+    public function addConstraints()
+    {
+        $this->performJoin();
+
+        if (static::$constraints) {
+            $this->addWhereConstraints();
+        }
+    }
+
+    /**
+     * Set the join clause for the relation query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder|null  $query
+     * @return $this
+     */
+    protected function performJoin($query = null)
+    {
+        $query = $query ?: $this->query;
+
+        // We need to join to the intermediate table on the related model's primary
+        // key column with the intermediate table's foreign key for the related
+        // model instance. Then we can set the "where" for the parent models.
+        $query->join(
+            $this->table,
+            $this->getQualifiedRelatedKeyName(),
+            '=',
+            $this->getQualifiedRelatedPivotKeyName()
+        );
+
+        return $this;
+    }
+
+    /**
+     * Set the where clause for the relation query.
+     *
+     * @return $this
+     */
+    protected function addWhereConstraints()
+    {
+        $this->query->where(
+            $this->getQualifiedForeignPivotKeyName(), '=', $this->parent->{$this->parentKey}
+        );
+
+        return $this;
+    }
+
+    /**
+     * Set the constraints for an eager load of the relation.
+     *
+     * @param  array  $models
+     * @return void
+     */
+    public function addEagerConstraints(array $models)
+    {
+        $whereIn = $this->whereInMethod($this->parent, $this->parentKey);
+
+        $this->query->{$whereIn}(
+            $this->getQualifiedForeignPivotKeyName(),
+            $this->getKeys($models, $this->parentKey)
+        );
+    }
+
+    /**
+     * Initialize the relation on a set of models.
+     *
+     * @param  array  $models
+     * @param  string  $relation
+     * @return array
+     */
+    public function initRelation(array $models, $relation)
+    {
+        foreach ($models as $model) {
+            $model->setRelation($relation, $this->related->newCollection());
+        }
+
+        return $models;
+    }
+
+    /**
+     * Match the eagerly loaded results to their parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @return array
+     */
+    public function match(array $models, Collection $results, $relation)
+    {
+        $dictionary = $this->buildDictionary($results);
+
+        // Once we have an array dictionary of child objects we can easily match the
+        // children back to their parent using the dictionary and the keys on the
+        // parent models. Then we should return these hydrated models back out.
+        foreach ($models as $model) {
+            $key = $this->getDictionaryKey($model->{$this->parentKey});
+
+            if (isset($dictionary[$key])) {
+                $model->setRelation(
+                    $relation, $this->related->newCollection($dictionary[$key])
+                );
+            }
+        }
+
+        return $models;
+    }
+
+    /**
+     * Build model dictionary keyed by the relation's foreign key.
+     *
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @return array
+     */
+    protected function buildDictionary(Collection $results)
+    {
+        // First we'll build a dictionary of child models keyed by the foreign key
+        // of the relation so that we will easily and quickly match them to the
+        // parents without having a possibly slow inner loop for every model.
+        $dictionary = [];
+
+        foreach ($results as $result) {
+            $value = $this->getDictionaryKey($result->{$this->accessor}->{$this->foreignPivotKey});
+
+            $dictionary[$value][] = $result;
+        }
+
+        return $dictionary;
+    }
+
+    /**
+     * Get the class being used for pivot models.
+     *
+     * @return string
+     */
+    public function getPivotClass()
+    {
+        return $this->using ?? Pivot::class;
+    }
+
+    /**
+     * Specify the custom pivot model to use for the relationship.
+     *
+     * @param  string  $class
+     * @return $this
+     */
+    public function using($class)
+    {
+        $this->using = $class;
+
+        return $this;
+    }
+
+    /**
+     * Specify the custom pivot accessor to use for the relationship.
+     *
+     * @param  string  $accessor
+     * @return $this
+     */
+    public function as($accessor)
+    {
+        $this->accessor = $accessor;
+
+        return $this;
+    }
+
+    /**
+     * Set a where clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function wherePivot($column, $operator = null, $value = null, $boolean = 'and')
+    {
+        $this->pivotWheres[] = func_get_args();
+
+        return $this->where($this->qualifyPivotColumn($column), $operator, $value, $boolean);
+    }
+
+    /**
+     * Set a "where between" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  array  $values
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function wherePivotBetween($column, array $values, $boolean = 'and', $not = false)
+    {
+        return $this->whereBetween($this->qualifyPivotColumn($column), $values, $boolean, $not);
+    }
+
+    /**
+     * Set a "or where between" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  array  $values
+     * @return $this
+     */
+    public function orWherePivotBetween($column, array $values)
+    {
+        return $this->wherePivotBetween($column, $values, 'or');
+    }
+
+    /**
+     * Set a "where pivot not between" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  array  $values
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function wherePivotNotBetween($column, array $values, $boolean = 'and')
+    {
+        return $this->wherePivotBetween($column, $values, $boolean, true);
+    }
+
+    /**
+     * Set a "or where not between" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  array  $values
+     * @return $this
+     */
+    public function orWherePivotNotBetween($column, array $values)
+    {
+        return $this->wherePivotBetween($column, $values, 'or', true);
+    }
+
+    /**
+     * Set a "where in" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  mixed  $values
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function wherePivotIn($column, $values, $boolean = 'and', $not = false)
+    {
+        $this->pivotWhereIns[] = func_get_args();
+
+        return $this->whereIn($this->qualifyPivotColumn($column), $values, $boolean, $not);
+    }
+
+    /**
+     * Set an "or where" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function orWherePivot($column, $operator = null, $value = null)
+    {
+        return $this->wherePivot($column, $operator, $value, 'or');
+    }
+
+    /**
+     * Set a where clause for a pivot table column.
+     *
+     * In addition, new pivot records will receive this value.
+     *
+     * @param  string|array  $column
+     * @param  mixed  $value
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function withPivotValue($column, $value = null)
+    {
+        if (is_array($column)) {
+            foreach ($column as $name => $value) {
+                $this->withPivotValue($name, $value);
+            }
+
+            return $this;
+        }
+
+        if (is_null($value)) {
+            throw new InvalidArgumentException('The provided value may not be null.');
+        }
+
+        $this->pivotValues[] = compact('column', 'value');
+
+        return $this->wherePivot($column, '=', $value);
+    }
+
+    /**
+     * Set an "or where in" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  mixed  $values
+     * @return $this
+     */
+    public function orWherePivotIn($column, $values)
+    {
+        return $this->wherePivotIn($column, $values, 'or');
+    }
+
+    /**
+     * Set a "where not in" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  mixed  $values
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function wherePivotNotIn($column, $values, $boolean = 'and')
+    {
+        return $this->wherePivotIn($column, $values, $boolean, true);
+    }
+
+    /**
+     * Set an "or where not in" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  mixed  $values
+     * @return $this
+     */
+    public function orWherePivotNotIn($column, $values)
+    {
+        return $this->wherePivotNotIn($column, $values, 'or');
+    }
+
+    /**
+     * Set a "where null" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function wherePivotNull($column, $boolean = 'and', $not = false)
+    {
+        $this->pivotWhereNulls[] = func_get_args();
+
+        return $this->whereNull($this->qualifyPivotColumn($column), $boolean, $not);
+    }
+
+    /**
+     * Set a "where not null" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function wherePivotNotNull($column, $boolean = 'and')
+    {
+        return $this->wherePivotNull($column, $boolean, true);
+    }
+
+    /**
+     * Set a "or where null" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  bool  $not
+     * @return $this
+     */
+    public function orWherePivotNull($column, $not = false)
+    {
+        return $this->wherePivotNull($column, 'or', $not);
+    }
+
+    /**
+     * Set a "or where not null" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @return $this
+     */
+    public function orWherePivotNotNull($column)
+    {
+        return $this->orWherePivotNull($column, true);
+    }
+
+    /**
+     * Add an "order by" clause for a pivot table column.
+     *
+     * @param  string  $column
+     * @param  string  $direction
+     * @return $this
+     */
+    public function orderByPivot($column, $direction = 'asc')
+    {
+        return $this->orderBy($this->qualifyPivotColumn($column), $direction);
+    }
+
+    /**
+     * Find a related model by its primary key or return a new instance of the related model.
+     *
+     * @param  mixed  $id
+     * @param  array  $columns
+     * @return \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model
+     */
+    public function findOrNew($id, $columns = ['*'])
+    {
+        if (is_null($instance = $this->find($id, $columns))) {
+            $instance = $this->related->newInstance();
+        }
+
+        return $instance;
+    }
+
+    /**
+     * Get the first related model record matching the attributes or instantiate it.
+     *
+     * @param  array  $attributes
+     * @param  array  $values
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function firstOrNew(array $attributes = [], array $values = [])
+    {
+        if (is_null($instance = $this->related->where($attributes)->first())) {
+            $instance = $this->related->newInstance(array_merge($attributes, $values));
+        }
+
+        return $instance;
+    }
+
+    /**
+     * Get the first related record matching the attributes or create it.
+     *
+     * @param  array  $attributes
+     * @param  array  $values
+     * @param  array  $joining
+     * @param  bool  $touch
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function firstOrCreate(array $attributes = [], array $values = [], array $joining = [], $touch = true)
+    {
+        if (is_null($instance = (clone $this)->where($attributes)->first())) {
+            if (is_null($instance = $this->related->where($attributes)->first())) {
+                $instance = $this->create(array_merge($attributes, $values), $joining, $touch);
+            } else {
+                $this->attach($instance, $joining, $touch);
+            }
+        }
+
+        return $instance;
+    }
+
+    /**
+     * Create or update a related record matching the attributes, and fill it with values.
+     *
+     * @param  array  $attributes
+     * @param  array  $values
+     * @param  array  $joining
+     * @param  bool  $touch
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function updateOrCreate(array $attributes, array $values = [], array $joining = [], $touch = true)
+    {
+        if (is_null($instance = (clone $this)->where($attributes)->first())) {
+            if (is_null($instance = $this->related->where($attributes)->first())) {
+                return $this->create(array_merge($attributes, $values), $joining, $touch);
+            } else {
+                $this->attach($instance, $joining, $touch);
+            }
+        }
+
+        $instance->fill($values);
+
+        $instance->save(['touch' => false]);
+
+        return $instance;
+    }
+
+    /**
+     * Find a related model by its primary key.
+     *
+     * @param  mixed  $id
+     * @param  array  $columns
+     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|null
+     */
+    public function find($id, $columns = ['*'])
+    {
+        if (! $id instanceof Model && (is_array($id) || $id instanceof Arrayable)) {
+            return $this->findMany($id, $columns);
+        }
+
+        return $this->where(
+            $this->getRelated()->getQualifiedKeyName(), '=', $this->parseId($id)
+        )->first($columns);
+    }
+
+    /**
+     * Find multiple related models by their primary keys.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|array  $ids
+     * @param  array  $columns
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function findMany($ids, $columns = ['*'])
+    {
+        $ids = $ids instanceof Arrayable ? $ids->toArray() : $ids;
+
+        if (empty($ids)) {
+            return $this->getRelated()->newCollection();
+        }
+
+        return $this->whereKey(
+            $this->parseIds($ids)
+        )->get($columns);
+    }
+
+    /**
+     * Find a related model by its primary key or throw an exception.
+     *
+     * @param  mixed  $id
+     * @param  array  $columns
+     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection
+     *
+     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
+     */
+    public function findOrFail($id, $columns = ['*'])
+    {
+        $result = $this->find($id, $columns);
+
+        $id = $id instanceof Arrayable ? $id->toArray() : $id;
+
+        if (is_array($id)) {
+            if (count($result) === count(array_unique($id))) {
+                return $result;
+            }
+        } elseif (! is_null($result)) {
+            return $result;
+        }
+
+        throw (new ModelNotFoundException)->setModel(get_class($this->related), $id);
+    }
+
+    /**
+     * Find a related model by its primary key or call a callback.
+     *
+     * @param  mixed  $id
+     * @param  \Closure|array  $columns
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|mixed
+     */
+    public function findOr($id, $columns = ['*'], Closure $callback = null)
+    {
+        if ($columns instanceof Closure) {
+            $callback = $columns;
+
+            $columns = ['*'];
+        }
+
+        $result = $this->find($id, $columns);
+
+        $id = $id instanceof Arrayable ? $id->toArray() : $id;
+
+        if (is_array($id)) {
+            if (count($result) === count(array_unique($id))) {
+                return $result;
+            }
+        } elseif (! is_null($result)) {
+            return $result;
+        }
+
+        return $callback();
+    }
+
+    /**
+     * Add a basic where clause to the query, and return the first result.
+     *
+     * @param  \Closure|string|array  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @param  string  $boolean
+     * @return \Illuminate\Database\Eloquent\Model|static
+     */
+    public function firstWhere($column, $operator = null, $value = null, $boolean = 'and')
+    {
+        return $this->where($column, $operator, $value, $boolean)->first();
+    }
+
+    /**
+     * Execute the query and get the first result.
+     *
+     * @param  array  $columns
+     * @return mixed
+     */
+    public function first($columns = ['*'])
+    {
+        $results = $this->take(1)->get($columns);
+
+        return count($results) > 0 ? $results->first() : null;
+    }
+
+    /**
+     * Execute the query and get the first result or throw an exception.
+     *
+     * @param  array  $columns
+     * @return \Illuminate\Database\Eloquent\Model|static
+     *
+     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
+     */
+    public function firstOrFail($columns = ['*'])
+    {
+        if (! is_null($model = $this->first($columns))) {
+            return $model;
+        }
+
+        throw (new ModelNotFoundException)->setModel(get_class($this->related));
+    }
+
+    /**
+     * Execute the query and get the first result or call a callback.
+     *
+     * @param  \Closure|array  $columns
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Model|static|mixed
+     */
+    public function firstOr($columns = ['*'], Closure $callback = null)
+    {
+        if ($columns instanceof Closure) {
+            $callback = $columns;
+
+            $columns = ['*'];
+        }
+
+        if (! is_null($model = $this->first($columns))) {
+            return $model;
+        }
+
+        return $callback();
+    }
+
+    /**
+     * Get the results of the relationship.
+     *
+     * @return mixed
+     */
+    public function getResults()
+    {
+        return ! is_null($this->parent->{$this->parentKey})
+                ? $this->get()
+                : $this->related->newCollection();
+    }
+
+    /**
+     * Execute the query as a "select" statement.
+     *
+     * @param  array  $columns
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function get($columns = ['*'])
+    {
+        // First we'll add the proper select columns onto the query so it is run with
+        // the proper columns. Then, we will get the results and hydrate our pivot
+        // models with the result of those columns as a separate model relation.
+        $builder = $this->query->applyScopes();
+
+        $columns = $builder->getQuery()->columns ? [] : $columns;
+
+        $models = $builder->addSelect(
+            $this->shouldSelect($columns)
+        )->getModels();
+
+        $this->hydratePivotRelation($models);
+
+        // If we actually found models we will also eager load any relationships that
+        // have been specified as needing to be eager loaded. This will solve the
+        // n + 1 query problem for the developer and also increase performance.
+        if (count($models) > 0) {
+            $models = $builder->eagerLoadRelations($models);
+        }
+
+        return $this->related->newCollection($models);
+    }
+
+    /**
+     * Get the select columns for the relation query.
+     *
+     * @param  array  $columns
+     * @return array
+     */
+    protected function shouldSelect(array $columns = ['*'])
+    {
+        if ($columns == ['*']) {
+            $columns = [$this->related->getTable().'.*'];
+        }
+
+        return array_merge($columns, $this->aliasedPivotColumns());
+    }
+
+    /**
+     * Get the pivot columns for the relation.
+     *
+     * "pivot_" is prefixed at each column for easy removal later.
+     *
+     * @return array
+     */
+    protected function aliasedPivotColumns()
+    {
+        $defaults = [$this->foreignPivotKey, $this->relatedPivotKey];
+
+        return collect(array_merge($defaults, $this->pivotColumns))->map(function ($column) {
+            return $this->qualifyPivotColumn($column).' as pivot_'.$column;
+        })->unique()->all();
+    }
+
+    /**
+     * Get a paginator for the "select" statement.
+     *
+     * @param  int|null  $perPage
+     * @param  array  $columns
+     * @param  string  $pageName
+     * @param  int|null  $page
+     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
+     */
+    public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
+    {
+        $this->query->addSelect($this->shouldSelect($columns));
+
+        return tap($this->query->paginate($perPage, $columns, $pageName, $page), function ($paginator) {
+            $this->hydratePivotRelation($paginator->items());
+        });
+    }
+
+    /**
+     * Paginate the given query into a simple paginator.
+     *
+     * @param  int|null  $perPage
+     * @param  array  $columns
+     * @param  string  $pageName
+     * @param  int|null  $page
+     * @return \Illuminate\Contracts\Pagination\Paginator
+     */
+    public function simplePaginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
+    {
+        $this->query->addSelect($this->shouldSelect($columns));
+
+        return tap($this->query->simplePaginate($perPage, $columns, $pageName, $page), function ($paginator) {
+            $this->hydratePivotRelation($paginator->items());
+        });
+    }
+
+    /**
+     * Paginate the given query into a cursor paginator.
+     *
+     * @param  int|null  $perPage
+     * @param  array  $columns
+     * @param  string  $cursorName
+     * @param  string|null  $cursor
+     * @return \Illuminate\Contracts\Pagination\CursorPaginator
+     */
+    public function cursorPaginate($perPage = null, $columns = ['*'], $cursorName = 'cursor', $cursor = null)
+    {
+        $this->query->addSelect($this->shouldSelect($columns));
+
+        return tap($this->query->cursorPaginate($perPage, $columns, $cursorName, $cursor), function ($paginator) {
+            $this->hydratePivotRelation($paginator->items());
+        });
+    }
+
+    /**
+     * Chunk the results of the query.
+     *
+     * @param  int  $count
+     * @param  callable  $callback
+     * @return bool
+     */
+    public function chunk($count, callable $callback)
+    {
+        return $this->prepareQueryBuilder()->chunk($count, function ($results, $page) use ($callback) {
+            $this->hydratePivotRelation($results->all());
+
+            return $callback($results, $page);
+        });
+    }
+
+    /**
+     * Chunk the results of a query by comparing numeric IDs.
+     *
+     * @param  int  $count
+     * @param  callable  $callback
+     * @param  string|null  $column
+     * @param  string|null  $alias
+     * @return bool
+     */
+    public function chunkById($count, callable $callback, $column = null, $alias = null)
+    {
+        $this->prepareQueryBuilder();
+
+        $column ??= $this->getRelated()->qualifyColumn(
+            $this->getRelatedKeyName()
+        );
+
+        $alias ??= $this->getRelatedKeyName();
+
+        return $this->query->chunkById($count, function ($results) use ($callback) {
+            $this->hydratePivotRelation($results->all());
+
+            return $callback($results);
+        }, $column, $alias);
+    }
+
+    /**
+     * Execute a callback over each item while chunking.
+     *
+     * @param  callable  $callback
+     * @param  int  $count
+     * @return bool
+     */
+    public function each(callable $callback, $count = 1000)
+    {
+        return $this->chunk($count, function ($results) use ($callback) {
+            foreach ($results as $key => $value) {
+                if ($callback($value, $key) === false) {
+                    return false;
+                }
+            }
+        });
+    }
+
+    /**
+     * Query lazily, by chunks of the given size.
+     *
+     * @param  int  $chunkSize
+     * @return \Illuminate\Support\LazyCollection
+     */
+    public function lazy($chunkSize = 1000)
+    {
+        return $this->prepareQueryBuilder()->lazy($chunkSize)->map(function ($model) {
+            $this->hydratePivotRelation([$model]);
+
+            return $model;
+        });
+    }
+
+    /**
+     * Query lazily, by chunking the results of a query by comparing IDs.
+     *
+     * @param  int  $chunkSize
+     * @param  string|null  $column
+     * @param  string|null  $alias
+     * @return \Illuminate\Support\LazyCollection
+     */
+    public function lazyById($chunkSize = 1000, $column = null, $alias = null)
+    {
+        $column ??= $this->getRelated()->qualifyColumn(
+            $this->getRelatedKeyName()
+        );
+
+        $alias ??= $this->getRelatedKeyName();
+
+        return $this->prepareQueryBuilder()->lazyById($chunkSize, $column, $alias)->map(function ($model) {
+            $this->hydratePivotRelation([$model]);
+
+            return $model;
+        });
+    }
+
+    /**
+     * Get a lazy collection for the given query.
+     *
+     * @return \Illuminate\Support\LazyCollection
+     */
+    public function cursor()
+    {
+        return $this->prepareQueryBuilder()->cursor()->map(function ($model) {
+            $this->hydratePivotRelation([$model]);
+
+            return $model;
+        });
+    }
+
+    /**
+     * Prepare the query builder for query execution.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function prepareQueryBuilder()
+    {
+        return $this->query->addSelect($this->shouldSelect());
+    }
+
+    /**
+     * Hydrate the pivot table relationship on the models.
+     *
+     * @param  array  $models
+     * @return void
+     */
+    protected function hydratePivotRelation(array $models)
+    {
+        // To hydrate the pivot relationship, we will just gather the pivot attributes
+        // and create a new Pivot model, which is basically a dynamic model that we
+        // will set the attributes, table, and connections on it so it will work.
+        foreach ($models as $model) {
+            $model->setRelation($this->accessor, $this->newExistingPivot(
+                $this->migratePivotAttributes($model)
+            ));
+        }
+    }
+
+    /**
+     * Get the pivot attributes from a model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return array
+     */
+    protected function migratePivotAttributes(Model $model)
+    {
+        $values = [];
+
+        foreach ($model->getAttributes() as $key => $value) {
+            // To get the pivots attributes we will just take any of the attributes which
+            // begin with "pivot_" and add those to this arrays, as well as unsetting
+            // them from the parent's models since they exist in a different table.
+            if (str_starts_with($key, 'pivot_')) {
+                $values[substr($key, 6)] = $value;
+
+                unset($model->$key);
+            }
+        }
+
+        return $values;
+    }
+
+    /**
+     * If we're touching the parent model, touch.
+     *
+     * @return void
+     */
+    public function touchIfTouching()
+    {
+        if ($this->touchingParent()) {
+            $this->getParent()->touch();
+        }
+
+        if ($this->getParent()->touches($this->relationName)) {
+            $this->touch();
+        }
+    }
+
+    /**
+     * Determine if we should touch the parent on sync.
+     *
+     * @return bool
+     */
+    protected function touchingParent()
+    {
+        return $this->getRelated()->touches($this->guessInverseRelation());
+    }
+
+    /**
+     * Attempt to guess the name of the inverse of the relation.
+     *
+     * @return string
+     */
+    protected function guessInverseRelation()
+    {
+        return Str::camel(Str::pluralStudly(class_basename($this->getParent())));
+    }
+
+    /**
+     * Touch all of the related models for the relationship.
+     *
+     * E.g.: Touch all roles associated with this user.
+     *
+     * @return void
+     */
+    public function touch()
+    {
+        $columns = [
+            $this->related->getUpdatedAtColumn() => $this->related->freshTimestampString(),
+        ];
+
+        // If we actually have IDs for the relation, we will run the query to update all
+        // the related model's timestamps, to make sure these all reflect the changes
+        // to the parent models. This will help us keep any caching synced up here.
+        if (count($ids = $this->allRelatedIds()) > 0) {
+            $this->getRelated()->newQueryWithoutRelationships()->whereKey($ids)->update($columns);
+        }
+    }
+
+    /**
+     * Get all of the IDs for the related models.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function allRelatedIds()
+    {
+        return $this->newPivotQuery()->pluck($this->relatedPivotKey);
+    }
+
+    /**
+     * Save a new model and attach it to the parent model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @param  array  $pivotAttributes
+     * @param  bool  $touch
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function save(Model $model, array $pivotAttributes = [], $touch = true)
+    {
+        $model->save(['touch' => false]);
+
+        $this->attach($model, $pivotAttributes, $touch);
+
+        return $model;
+    }
+
+    /**
+     * Save a new model without raising any events and attach it to the parent model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @param  array  $pivotAttributes
+     * @param  bool  $touch
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function saveQuietly(Model $model, array $pivotAttributes = [], $touch = true)
+    {
+        return Model::withoutEvents(function () use ($model, $pivotAttributes, $touch) {
+            return $this->save($model, $pivotAttributes, $touch);
+        });
+    }
+
+    /**
+     * Save an array of new models and attach them to the parent model.
+     *
+     * @param  \Illuminate\Support\Collection|array  $models
+     * @param  array  $pivotAttributes
+     * @return array
+     */
+    public function saveMany($models, array $pivotAttributes = [])
+    {
+        foreach ($models as $key => $model) {
+            $this->save($model, (array) ($pivotAttributes[$key] ?? []), false);
+        }
+
+        $this->touchIfTouching();
+
+        return $models;
+    }
+
+    /**
+     * Save an array of new models without raising any events and attach them to the parent model.
+     *
+     * @param  \Illuminate\Support\Collection|array  $models
+     * @param  array  $pivotAttributes
+     * @return array
+     */
+    public function saveManyQuietly($models, array $pivotAttributes = [])
+    {
+        return Model::withoutEvents(function () use ($models, $pivotAttributes) {
+            return $this->saveMany($models, $pivotAttributes);
+        });
+    }
+
+    /**
+     * Create a new instance of the related model.
+     *
+     * @param  array  $attributes
+     * @param  array  $joining
+     * @param  bool  $touch
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function create(array $attributes = [], array $joining = [], $touch = true)
+    {
+        $instance = $this->related->newInstance($attributes);
+
+        // Once we save the related model, we need to attach it to the base model via
+        // through intermediate table so we'll use the existing "attach" method to
+        // accomplish this which will insert the record and any more attributes.
+        $instance->save(['touch' => false]);
+
+        $this->attach($instance, $joining, $touch);
+
+        return $instance;
+    }
+
+    /**
+     * Create an array of new instances of the related models.
+     *
+     * @param  iterable  $records
+     * @param  array  $joinings
+     * @return array
+     */
+    public function createMany(iterable $records, array $joinings = [])
+    {
+        $instances = [];
+
+        foreach ($records as $key => $record) {
+            $instances[] = $this->create($record, (array) ($joinings[$key] ?? []), false);
+        }
+
+        $this->touchIfTouching();
+
+        return $instances;
+    }
+
+    /**
+     * Add the constraints for a relationship query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        if ($parentQuery->getQuery()->from == $query->getQuery()->from) {
+            return $this->getRelationExistenceQueryForSelfJoin($query, $parentQuery, $columns);
+        }
+
+        $this->performJoin($query);
+
+        return parent::getRelationExistenceQuery($query, $parentQuery, $columns);
+    }
+
+    /**
+     * Add the constraints for a relationship query on the same table.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQueryForSelfJoin(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        $query->select($columns);
+
+        $query->from($this->related->getTable().' as '.$hash = $this->getRelationCountHash());
+
+        $this->related->setTable($hash);
+
+        $this->performJoin($query);
+
+        return parent::getRelationExistenceQuery($query, $parentQuery, $columns);
+    }
+
+    /**
+     * Get the key for comparing against the parent key in "has" query.
+     *
+     * @return string
+     */
+    public function getExistenceCompareKey()
+    {
+        return $this->getQualifiedForeignPivotKeyName();
+    }
+
+    /**
+     * Specify that the pivot table has creation and update timestamps.
+     *
+     * @param  mixed  $createdAt
+     * @param  mixed  $updatedAt
+     * @return $this
+     */
+    public function withTimestamps($createdAt = null, $updatedAt = null)
+    {
+        $this->withTimestamps = true;
+
+        $this->pivotCreatedAt = $createdAt;
+        $this->pivotUpdatedAt = $updatedAt;
+
+        return $this->withPivot($this->createdAt(), $this->updatedAt());
+    }
+
+    /**
+     * Get the name of the "created at" column.
+     *
+     * @return string
+     */
+    public function createdAt()
+    {
+        return $this->pivotCreatedAt ?: $this->parent->getCreatedAtColumn();
+    }
+
+    /**
+     * Get the name of the "updated at" column.
+     *
+     * @return string
+     */
+    public function updatedAt()
+    {
+        return $this->pivotUpdatedAt ?: $this->parent->getUpdatedAtColumn();
+    }
+
+    /**
+     * Get the foreign key for the relation.
+     *
+     * @return string
+     */
+    public function getForeignPivotKeyName()
+    {
+        return $this->foreignPivotKey;
+    }
+
+    /**
+     * Get the fully qualified foreign key for the relation.
+     *
+     * @return string
+     */
+    public function getQualifiedForeignPivotKeyName()
+    {
+        return $this->qualifyPivotColumn($this->foreignPivotKey);
+    }
+
+    /**
+     * Get the "related key" for the relation.
+     *
+     * @return string
+     */
+    public function getRelatedPivotKeyName()
+    {
+        return $this->relatedPivotKey;
+    }
+
+    /**
+     * Get the fully qualified "related key" for the relation.
+     *
+     * @return string
+     */
+    public function getQualifiedRelatedPivotKeyName()
+    {
+        return $this->qualifyPivotColumn($this->relatedPivotKey);
+    }
+
+    /**
+     * Get the parent key for the relationship.
+     *
+     * @return string
+     */
+    public function getParentKeyName()
+    {
+        return $this->parentKey;
+    }
+
+    /**
+     * Get the fully qualified parent key name for the relation.
+     *
+     * @return string
+     */
+    public function getQualifiedParentKeyName()
+    {
+        return $this->parent->qualifyColumn($this->parentKey);
+    }
+
+    /**
+     * Get the related key for the relationship.
+     *
+     * @return string
+     */
+    public function getRelatedKeyName()
+    {
+        return $this->relatedKey;
+    }
+
+    /**
+     * Get the fully qualified related key name for the relation.
+     *
+     * @return string
+     */
+    public function getQualifiedRelatedKeyName()
+    {
+        return $this->related->qualifyColumn($this->relatedKey);
+    }
+
+    /**
+     * Get the intermediate table for the relationship.
+     *
+     * @return string
+     */
+    public function getTable()
+    {
+        return $this->table;
+    }
+
+    /**
+     * Get the relationship name for the relationship.
+     *
+     * @return string
+     */
+    public function getRelationName()
+    {
+        return $this->relationName;
+    }
+
+    /**
+     * Get the name of the pivot accessor for this relationship.
+     *
+     * @return string
+     */
+    public function getPivotAccessor()
+    {
+        return $this->accessor;
+    }
+
+    /**
+     * Get the pivot columns for this relationship.
+     *
+     * @return array
+     */
+    public function getPivotColumns()
+    {
+        return $this->pivotColumns;
+    }
+
+    /**
+     * Qualify the given column name by the pivot table.
+     *
+     * @param  string  $column
+     * @return string
+     */
+    public function qualifyPivotColumn($column)
+    {
+        return str_contains($column, '.')
+                    ? $column
+                    : $this->table.'.'.$column;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/Concerns/AsPivot.php b/vendor/illuminate/database/Eloquent/Relations/Concerns/AsPivot.php
new file mode 100644
index 0000000..e6f9f23
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/Concerns/AsPivot.php
@@ -0,0 +1,333 @@
+timestamps = $instance->hasTimestampAttributes($attributes);
+
+        // The pivot model is a "dynamic" model since we will set the tables dynamically
+        // for the instance. This allows it work for any intermediate tables for the
+        // many to many relationship that are defined by this developer's classes.
+        $instance->setConnection($parent->getConnectionName())
+            ->setTable($table)
+            ->forceFill($attributes)
+            ->syncOriginal();
+
+        // We store off the parent instance so we will access the timestamp column names
+        // for the model, since the pivot model timestamps aren't easily configurable
+        // from the developer's point of view. We can use the parents to get these.
+        $instance->pivotParent = $parent;
+
+        $instance->exists = $exists;
+
+        return $instance;
+    }
+
+    /**
+     * Create a new pivot model from raw values returned from a query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @param  array  $attributes
+     * @param  string  $table
+     * @param  bool  $exists
+     * @return static
+     */
+    public static function fromRawAttributes(Model $parent, $attributes, $table, $exists = false)
+    {
+        $instance = static::fromAttributes($parent, [], $table, $exists);
+
+        $instance->timestamps = $instance->hasTimestampAttributes($attributes);
+
+        $instance->setRawAttributes(
+            array_merge($instance->getRawOriginal(), $attributes), $exists
+        );
+
+        return $instance;
+    }
+
+    /**
+     * Set the keys for a select query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function setKeysForSelectQuery($query)
+    {
+        if (isset($this->attributes[$this->getKeyName()])) {
+            return parent::setKeysForSelectQuery($query);
+        }
+
+        $query->where($this->foreignKey, $this->getOriginal(
+            $this->foreignKey, $this->getAttribute($this->foreignKey)
+        ));
+
+        return $query->where($this->relatedKey, $this->getOriginal(
+            $this->relatedKey, $this->getAttribute($this->relatedKey)
+        ));
+    }
+
+    /**
+     * Set the keys for a save update query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function setKeysForSaveQuery($query)
+    {
+        return $this->setKeysForSelectQuery($query);
+    }
+
+    /**
+     * Delete the pivot model record from the database.
+     *
+     * @return int
+     */
+    public function delete()
+    {
+        if (isset($this->attributes[$this->getKeyName()])) {
+            return (int) parent::delete();
+        }
+
+        if ($this->fireModelEvent('deleting') === false) {
+            return 0;
+        }
+
+        $this->touchOwners();
+
+        return tap($this->getDeleteQuery()->delete(), function () {
+            $this->exists = false;
+
+            $this->fireModelEvent('deleted', false);
+        });
+    }
+
+    /**
+     * Get the query builder for a delete operation on the pivot.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function getDeleteQuery()
+    {
+        return $this->newQueryWithoutRelationships()->where([
+            $this->foreignKey => $this->getOriginal($this->foreignKey, $this->getAttribute($this->foreignKey)),
+            $this->relatedKey => $this->getOriginal($this->relatedKey, $this->getAttribute($this->relatedKey)),
+        ]);
+    }
+
+    /**
+     * Get the table associated with the model.
+     *
+     * @return string
+     */
+    public function getTable()
+    {
+        if (! isset($this->table)) {
+            $this->setTable(str_replace(
+                '\\', '', Str::snake(Str::singular(class_basename($this)))
+            ));
+        }
+
+        return $this->table;
+    }
+
+    /**
+     * Get the foreign key column name.
+     *
+     * @return string
+     */
+    public function getForeignKey()
+    {
+        return $this->foreignKey;
+    }
+
+    /**
+     * Get the "related key" column name.
+     *
+     * @return string
+     */
+    public function getRelatedKey()
+    {
+        return $this->relatedKey;
+    }
+
+    /**
+     * Get the "related key" column name.
+     *
+     * @return string
+     */
+    public function getOtherKey()
+    {
+        return $this->getRelatedKey();
+    }
+
+    /**
+     * Set the key names for the pivot model instance.
+     *
+     * @param  string  $foreignKey
+     * @param  string  $relatedKey
+     * @return $this
+     */
+    public function setPivotKeys($foreignKey, $relatedKey)
+    {
+        $this->foreignKey = $foreignKey;
+
+        $this->relatedKey = $relatedKey;
+
+        return $this;
+    }
+
+    /**
+     * Determine if the pivot model or given attributes has timestamp attributes.
+     *
+     * @param  array|null  $attributes
+     * @return bool
+     */
+    public function hasTimestampAttributes($attributes = null)
+    {
+        return array_key_exists($this->getCreatedAtColumn(), $attributes ?? $this->attributes);
+    }
+
+    /**
+     * Get the name of the "created at" column.
+     *
+     * @return string
+     */
+    public function getCreatedAtColumn()
+    {
+        return $this->pivotParent
+            ? $this->pivotParent->getCreatedAtColumn()
+            : parent::getCreatedAtColumn();
+    }
+
+    /**
+     * Get the name of the "updated at" column.
+     *
+     * @return string
+     */
+    public function getUpdatedAtColumn()
+    {
+        return $this->pivotParent
+            ? $this->pivotParent->getUpdatedAtColumn()
+            : parent::getUpdatedAtColumn();
+    }
+
+    /**
+     * Get the queueable identity for the entity.
+     *
+     * @return mixed
+     */
+    public function getQueueableId()
+    {
+        if (isset($this->attributes[$this->getKeyName()])) {
+            return $this->getKey();
+        }
+
+        return sprintf(
+            '%s:%s:%s:%s',
+            $this->foreignKey, $this->getAttribute($this->foreignKey),
+            $this->relatedKey, $this->getAttribute($this->relatedKey)
+        );
+    }
+
+    /**
+     * Get a new query to restore one or more models by their queueable IDs.
+     *
+     * @param  int[]|string[]|string  $ids
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function newQueryForRestoration($ids)
+    {
+        if (is_array($ids)) {
+            return $this->newQueryForCollectionRestoration($ids);
+        }
+
+        if (! str_contains($ids, ':')) {
+            return parent::newQueryForRestoration($ids);
+        }
+
+        $segments = explode(':', $ids);
+
+        return $this->newQueryWithoutScopes()
+            ->where($segments[0], $segments[1])
+            ->where($segments[2], $segments[3]);
+    }
+
+    /**
+     * Get a new query to restore multiple models by their queueable IDs.
+     *
+     * @param  int[]|string[]  $ids
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function newQueryForCollectionRestoration(array $ids)
+    {
+        $ids = array_values($ids);
+
+        if (! str_contains($ids[0], ':')) {
+            return parent::newQueryForRestoration($ids);
+        }
+
+        $query = $this->newQueryWithoutScopes();
+
+        foreach ($ids as $id) {
+            $segments = explode(':', $id);
+
+            $query->orWhere(function ($query) use ($segments) {
+                return $query->where($segments[0], $segments[1])
+                    ->where($segments[2], $segments[3]);
+            });
+        }
+
+        return $query;
+    }
+
+    /**
+     * Unset all the loaded relations for the instance.
+     *
+     * @return $this
+     */
+    public function unsetRelations()
+    {
+        $this->pivotParent = null;
+        $this->relations = [];
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/Concerns/CanBeOneOfMany.php b/vendor/illuminate/database/Eloquent/Relations/Concerns/CanBeOneOfMany.php
new file mode 100644
index 0000000..25b9a58
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/Concerns/CanBeOneOfMany.php
@@ -0,0 +1,310 @@
+isOneOfMany = true;
+
+        $this->relationName = $relation ?: $this->getDefaultOneOfManyJoinAlias(
+            $this->guessRelationship()
+        );
+
+        $keyName = $this->query->getModel()->getKeyName();
+
+        $columns = is_string($columns = $column) ? [
+            $column => $aggregate,
+            $keyName => $aggregate,
+        ] : $column;
+
+        if (! array_key_exists($keyName, $columns)) {
+            $columns[$keyName] = 'MAX';
+        }
+
+        if ($aggregate instanceof Closure) {
+            $closure = $aggregate;
+        }
+
+        foreach ($columns as $column => $aggregate) {
+            if (! in_array(strtolower($aggregate), ['min', 'max'])) {
+                throw new InvalidArgumentException("Invalid aggregate [{$aggregate}] used within ofMany relation. Available aggregates: MIN, MAX");
+            }
+
+            $subQuery = $this->newOneOfManySubQuery(
+                $this->getOneOfManySubQuerySelectColumns(),
+                $column, $aggregate
+            );
+
+            if (isset($previous)) {
+                $this->addOneOfManyJoinSubQuery($subQuery, $previous['subQuery'], $previous['column']);
+            }
+
+            if (isset($closure)) {
+                $closure($subQuery);
+            }
+
+            if (! isset($previous)) {
+                $this->oneOfManySubQuery = $subQuery;
+            }
+
+            if (array_key_last($columns) == $column) {
+                $this->addOneOfManyJoinSubQuery($this->query, $subQuery, $column);
+            }
+
+            $previous = [
+                'subQuery' => $subQuery,
+                'column' => $column,
+            ];
+        }
+
+        $this->addConstraints();
+
+        $columns = $this->query->getQuery()->columns;
+
+        if (is_null($columns) || $columns === ['*']) {
+            $this->select([$this->qualifyColumn('*')]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Indicate that the relation is the latest single result of a larger one-to-many relationship.
+     *
+     * @param  string|array|null  $column
+     * @param  string|null  $relation
+     * @return $this
+     */
+    public function latestOfMany($column = 'id', $relation = null)
+    {
+        return $this->ofMany(collect(Arr::wrap($column))->mapWithKeys(function ($column) {
+            return [$column => 'MAX'];
+        })->all(), 'MAX', $relation);
+    }
+
+    /**
+     * Indicate that the relation is the oldest single result of a larger one-to-many relationship.
+     *
+     * @param  string|array|null  $column
+     * @param  string|null  $relation
+     * @return $this
+     */
+    public function oldestOfMany($column = 'id', $relation = null)
+    {
+        return $this->ofMany(collect(Arr::wrap($column))->mapWithKeys(function ($column) {
+            return [$column => 'MIN'];
+        })->all(), 'MIN', $relation);
+    }
+
+    /**
+     * Get the default alias for the one of many inner join clause.
+     *
+     * @param  string  $relation
+     * @return string
+     */
+    protected function getDefaultOneOfManyJoinAlias($relation)
+    {
+        return $relation == $this->query->getModel()->getTable()
+            ? $relation.'_of_many'
+            : $relation;
+    }
+
+    /**
+     * Get a new query for the related model, grouping the query by the given column, often the foreign key of the relationship.
+     *
+     * @param  string|array  $groupBy
+     * @param  string|null  $column
+     * @param  string|null  $aggregate
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function newOneOfManySubQuery($groupBy, $column = null, $aggregate = null)
+    {
+        $subQuery = $this->query->getModel()
+            ->newQuery()
+            ->withoutGlobalScopes($this->removedScopes());
+
+        foreach (Arr::wrap($groupBy) as $group) {
+            $subQuery->groupBy($this->qualifyRelatedColumn($group));
+        }
+
+        if (! is_null($column)) {
+            $subQuery->selectRaw($aggregate.'('.$subQuery->getQuery()->grammar->wrap($subQuery->qualifyColumn($column)).') as '.$subQuery->getQuery()->grammar->wrap($column.'_aggregate'));
+        }
+
+        $this->addOneOfManySubQueryConstraints($subQuery, $groupBy, $column, $aggregate);
+
+        return $subQuery;
+    }
+
+    /**
+     * Add the join subquery to the given query on the given column and the relationship's foreign key.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $parent
+     * @param  \Illuminate\Database\Eloquent\Builder  $subQuery
+     * @param  string  $on
+     * @return void
+     */
+    protected function addOneOfManyJoinSubQuery(Builder $parent, Builder $subQuery, $on)
+    {
+        $parent->beforeQuery(function ($parent) use ($subQuery, $on) {
+            $subQuery->applyBeforeQueryCallbacks();
+
+            $parent->joinSub($subQuery, $this->relationName, function ($join) use ($on) {
+                $join->on($this->qualifySubSelectColumn($on.'_aggregate'), '=', $this->qualifyRelatedColumn($on));
+
+                $this->addOneOfManyJoinSubQueryConstraints($join, $on);
+            });
+        });
+    }
+
+    /**
+     * Merge the relationship query joins to the given query builder.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @return void
+     */
+    protected function mergeOneOfManyJoinsTo(Builder $query)
+    {
+        $query->getQuery()->beforeQueryCallbacks = $this->query->getQuery()->beforeQueryCallbacks;
+
+        $query->applyBeforeQueryCallbacks();
+    }
+
+    /**
+     * Get the query builder that will contain the relationship constraints.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function getRelationQuery()
+    {
+        return $this->isOneOfMany()
+            ? $this->oneOfManySubQuery
+            : $this->query;
+    }
+
+    /**
+     * Get the one of many inner join subselect builder instance.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder|void
+     */
+    public function getOneOfManySubQuery()
+    {
+        return $this->oneOfManySubQuery;
+    }
+
+    /**
+     * Get the qualified column name for the one-of-many relationship using the subselect join query's alias.
+     *
+     * @param  string  $column
+     * @return string
+     */
+    public function qualifySubSelectColumn($column)
+    {
+        return $this->getRelationName().'.'.last(explode('.', $column));
+    }
+
+    /**
+     * Qualify related column using the related table name if it is not already qualified.
+     *
+     * @param  string  $column
+     * @return string
+     */
+    protected function qualifyRelatedColumn($column)
+    {
+        return str_contains($column, '.') ? $column : $this->query->getModel()->getTable().'.'.$column;
+    }
+
+    /**
+     * Guess the "hasOne" relationship's name via backtrace.
+     *
+     * @return string
+     */
+    protected function guessRelationship()
+    {
+        return debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)[2]['function'];
+    }
+
+    /**
+     * Determine whether the relationship is a one-of-many relationship.
+     *
+     * @return bool
+     */
+    public function isOneOfMany()
+    {
+        return $this->isOneOfMany;
+    }
+
+    /**
+     * Get the name of the relationship.
+     *
+     * @return string
+     */
+    public function getRelationName()
+    {
+        return $this->relationName;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/Concerns/ComparesRelatedModels.php b/vendor/illuminate/database/Eloquent/Relations/Concerns/ComparesRelatedModels.php
new file mode 100644
index 0000000..ca06698
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/Concerns/ComparesRelatedModels.php
@@ -0,0 +1,77 @@
+compareKeys($this->getParentKey(), $this->getRelatedKeyFrom($model)) &&
+               $this->related->getTable() === $model->getTable() &&
+               $this->related->getConnectionName() === $model->getConnectionName();
+
+        if ($match && $this instanceof SupportsPartialRelations && $this->isOneOfMany()) {
+            return $this->query
+                        ->whereKey($model->getKey())
+                        ->exists();
+        }
+
+        return $match;
+    }
+
+    /**
+     * Determine if the model is not the related instance of the relationship.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|null  $model
+     * @return bool
+     */
+    public function isNot($model)
+    {
+        return ! $this->is($model);
+    }
+
+    /**
+     * Get the value of the parent model's key.
+     *
+     * @return mixed
+     */
+    abstract public function getParentKey();
+
+    /**
+     * Get the value of the model's related key.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return mixed
+     */
+    abstract protected function getRelatedKeyFrom(Model $model);
+
+    /**
+     * Compare the parent key with the related key.
+     *
+     * @param  mixed  $parentKey
+     * @param  mixed  $relatedKey
+     * @return bool
+     */
+    protected function compareKeys($parentKey, $relatedKey)
+    {
+        if (empty($parentKey) || empty($relatedKey)) {
+            return false;
+        }
+
+        if (is_int($parentKey) || is_int($relatedKey)) {
+            return (int) $parentKey === (int) $relatedKey;
+        }
+
+        return $parentKey === $relatedKey;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/Concerns/InteractsWithDictionary.php b/vendor/illuminate/database/Eloquent/Relations/Concerns/InteractsWithDictionary.php
new file mode 100644
index 0000000..91b3bf5
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/Concerns/InteractsWithDictionary.php
@@ -0,0 +1,36 @@
+__toString();
+            }
+
+            if (function_exists('enum_exists') &&
+                $attribute instanceof UnitEnum) {
+                return $attribute instanceof BackedEnum ? $attribute->value : $attribute->name;
+            }
+
+            throw new InvalidArgumentException('Model attribute value is an object but does not have a __toString method.');
+        }
+
+        return $attribute;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php b/vendor/illuminate/database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php
new file mode 100644
index 0000000..2241719
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php
@@ -0,0 +1,681 @@
+ [], 'detached' => [],
+        ];
+
+        $records = $this->formatRecordsList($this->parseIds($ids));
+
+        // Next, we will determine which IDs should get removed from the join table by
+        // checking which of the given ID/records is in the list of current records
+        // and removing all of those rows from this "intermediate" joining table.
+        $detach = array_values(array_intersect(
+            $this->newPivotQuery()->pluck($this->relatedPivotKey)->all(),
+            array_keys($records)
+        ));
+
+        if (count($detach) > 0) {
+            $this->detach($detach, false);
+
+            $changes['detached'] = $this->castKeys($detach);
+        }
+
+        // Finally, for all of the records which were not "detached", we'll attach the
+        // records into the intermediate table. Then, we will add those attaches to
+        // this change list and get ready to return these results to the callers.
+        $attach = array_diff_key($records, array_flip($detach));
+
+        if (count($attach) > 0) {
+            $this->attach($attach, [], false);
+
+            $changes['attached'] = array_keys($attach);
+        }
+
+        // Once we have finished attaching or detaching the records, we will see if we
+        // have done any attaching or detaching, and if we have we will touch these
+        // relationships if they are configured to touch on any database updates.
+        if ($touch && (count($changes['attached']) ||
+                       count($changes['detached']))) {
+            $this->touchIfTouching();
+        }
+
+        return $changes;
+    }
+
+    /**
+     * Sync the intermediate tables with a list of IDs without detaching.
+     *
+     * @param  \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array  $ids
+     * @return array
+     */
+    public function syncWithoutDetaching($ids)
+    {
+        return $this->sync($ids, false);
+    }
+
+    /**
+     * Sync the intermediate tables with a list of IDs or collection of models.
+     *
+     * @param  \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array  $ids
+     * @param  bool  $detaching
+     * @return array
+     */
+    public function sync($ids, $detaching = true)
+    {
+        $changes = [
+            'attached' => [], 'detached' => [], 'updated' => [],
+        ];
+
+        // First we need to attach any of the associated models that are not currently
+        // in this joining table. We'll spin through the given IDs, checking to see
+        // if they exist in the array of current ones, and if not we will insert.
+        $current = $this->getCurrentlyAttachedPivots()
+                        ->pluck($this->relatedPivotKey)->all();
+
+        $records = $this->formatRecordsList($this->parseIds($ids));
+
+        // Next, we will take the differences of the currents and given IDs and detach
+        // all of the entities that exist in the "current" array but are not in the
+        // array of the new IDs given to the method which will complete the sync.
+        if ($detaching) {
+            $detach = array_diff($current, array_keys($records));
+
+            if (count($detach) > 0) {
+                $this->detach($detach);
+
+                $changes['detached'] = $this->castKeys($detach);
+            }
+        }
+
+        // Now we are finally ready to attach the new records. Note that we'll disable
+        // touching until after the entire operation is complete so we don't fire a
+        // ton of touch operations until we are totally done syncing the records.
+        $changes = array_merge(
+            $changes, $this->attachNew($records, $current, false)
+        );
+
+        // Once we have finished attaching or detaching the records, we will see if we
+        // have done any attaching or detaching, and if we have we will touch these
+        // relationships if they are configured to touch on any database updates.
+        if (count($changes['attached']) ||
+            count($changes['updated']) ||
+            count($changes['detached'])) {
+            $this->touchIfTouching();
+        }
+
+        return $changes;
+    }
+
+    /**
+     * Sync the intermediate tables with a list of IDs or collection of models with the given pivot values.
+     *
+     * @param  \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array  $ids
+     * @param  array  $values
+     * @param  bool  $detaching
+     * @return array
+     */
+    public function syncWithPivotValues($ids, array $values, bool $detaching = true)
+    {
+        return $this->sync(collect($this->parseIds($ids))->mapWithKeys(function ($id) use ($values) {
+            return [$id => $values];
+        }), $detaching);
+    }
+
+    /**
+     * Format the sync / toggle record list so that it is keyed by ID.
+     *
+     * @param  array  $records
+     * @return array
+     */
+    protected function formatRecordsList(array $records)
+    {
+        return collect($records)->mapWithKeys(function ($attributes, $id) {
+            if (! is_array($attributes)) {
+                [$id, $attributes] = [$attributes, []];
+            }
+
+            return [$id => $attributes];
+        })->all();
+    }
+
+    /**
+     * Attach all of the records that aren't in the given current records.
+     *
+     * @param  array  $records
+     * @param  array  $current
+     * @param  bool  $touch
+     * @return array
+     */
+    protected function attachNew(array $records, array $current, $touch = true)
+    {
+        $changes = ['attached' => [], 'updated' => []];
+
+        foreach ($records as $id => $attributes) {
+            // If the ID is not in the list of existing pivot IDs, we will insert a new pivot
+            // record, otherwise, we will just update this existing record on this joining
+            // table, so that the developers will easily update these records pain free.
+            if (! in_array($id, $current)) {
+                $this->attach($id, $attributes, $touch);
+
+                $changes['attached'][] = $this->castKey($id);
+            }
+
+            // Now we'll try to update an existing pivot record with the attributes that were
+            // given to the method. If the model is actually updated we will add it to the
+            // list of updated pivot records so we return them back out to the consumer.
+            elseif (count($attributes) > 0 &&
+                $this->updateExistingPivot($id, $attributes, $touch)) {
+                $changes['updated'][] = $this->castKey($id);
+            }
+        }
+
+        return $changes;
+    }
+
+    /**
+     * Update an existing pivot record on the table.
+     *
+     * @param  mixed  $id
+     * @param  array  $attributes
+     * @param  bool  $touch
+     * @return int
+     */
+    public function updateExistingPivot($id, array $attributes, $touch = true)
+    {
+        if ($this->using &&
+            empty($this->pivotWheres) &&
+            empty($this->pivotWhereIns) &&
+            empty($this->pivotWhereNulls)) {
+            return $this->updateExistingPivotUsingCustomClass($id, $attributes, $touch);
+        }
+
+        if ($this->hasPivotColumn($this->updatedAt())) {
+            $attributes = $this->addTimestampsToAttachment($attributes, true);
+        }
+
+        $updated = $this->newPivotStatementForId($this->parseId($id))->update(
+            $this->castAttributes($attributes)
+        );
+
+        if ($touch) {
+            $this->touchIfTouching();
+        }
+
+        return $updated;
+    }
+
+    /**
+     * Update an existing pivot record on the table via a custom class.
+     *
+     * @param  mixed  $id
+     * @param  array  $attributes
+     * @param  bool  $touch
+     * @return int
+     */
+    protected function updateExistingPivotUsingCustomClass($id, array $attributes, $touch)
+    {
+        $pivot = $this->getCurrentlyAttachedPivots()
+                    ->where($this->foreignPivotKey, $this->parent->{$this->parentKey})
+                    ->where($this->relatedPivotKey, $this->parseId($id))
+                    ->first();
+
+        $updated = $pivot ? $pivot->fill($attributes)->isDirty() : false;
+
+        if ($updated) {
+            $pivot->save();
+        }
+
+        if ($touch) {
+            $this->touchIfTouching();
+        }
+
+        return (int) $updated;
+    }
+
+    /**
+     * Attach a model to the parent.
+     *
+     * @param  mixed  $id
+     * @param  array  $attributes
+     * @param  bool  $touch
+     * @return void
+     */
+    public function attach($id, array $attributes = [], $touch = true)
+    {
+        if ($this->using) {
+            $this->attachUsingCustomClass($id, $attributes);
+        } else {
+            // Here we will insert the attachment records into the pivot table. Once we have
+            // inserted the records, we will touch the relationships if necessary and the
+            // function will return. We can parse the IDs before inserting the records.
+            $this->newPivotStatement()->insert($this->formatAttachRecords(
+                $this->parseIds($id), $attributes
+            ));
+        }
+
+        if ($touch) {
+            $this->touchIfTouching();
+        }
+    }
+
+    /**
+     * Attach a model to the parent using a custom class.
+     *
+     * @param  mixed  $id
+     * @param  array  $attributes
+     * @return void
+     */
+    protected function attachUsingCustomClass($id, array $attributes)
+    {
+        $records = $this->formatAttachRecords(
+            $this->parseIds($id), $attributes
+        );
+
+        foreach ($records as $record) {
+            $this->newPivot($record, false)->save();
+        }
+    }
+
+    /**
+     * Create an array of records to insert into the pivot table.
+     *
+     * @param  array  $ids
+     * @param  array  $attributes
+     * @return array
+     */
+    protected function formatAttachRecords($ids, array $attributes)
+    {
+        $records = [];
+
+        $hasTimestamps = ($this->hasPivotColumn($this->createdAt()) ||
+                  $this->hasPivotColumn($this->updatedAt()));
+
+        // To create the attachment records, we will simply spin through the IDs given
+        // and create a new record to insert for each ID. Each ID may actually be a
+        // key in the array, with extra attributes to be placed in other columns.
+        foreach ($ids as $key => $value) {
+            $records[] = $this->formatAttachRecord(
+                $key, $value, $attributes, $hasTimestamps
+            );
+        }
+
+        return $records;
+    }
+
+    /**
+     * Create a full attachment record payload.
+     *
+     * @param  int  $key
+     * @param  mixed  $value
+     * @param  array  $attributes
+     * @param  bool  $hasTimestamps
+     * @return array
+     */
+    protected function formatAttachRecord($key, $value, $attributes, $hasTimestamps)
+    {
+        [$id, $attributes] = $this->extractAttachIdAndAttributes($key, $value, $attributes);
+
+        return array_merge(
+            $this->baseAttachRecord($id, $hasTimestamps), $this->castAttributes($attributes)
+        );
+    }
+
+    /**
+     * Get the attach record ID and extra attributes.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $value
+     * @param  array  $attributes
+     * @return array
+     */
+    protected function extractAttachIdAndAttributes($key, $value, array $attributes)
+    {
+        return is_array($value)
+                    ? [$key, array_merge($value, $attributes)]
+                    : [$value, $attributes];
+    }
+
+    /**
+     * Create a new pivot attachment record.
+     *
+     * @param  int  $id
+     * @param  bool  $timed
+     * @return array
+     */
+    protected function baseAttachRecord($id, $timed)
+    {
+        $record[$this->relatedPivotKey] = $id;
+
+        $record[$this->foreignPivotKey] = $this->parent->{$this->parentKey};
+
+        // If the record needs to have creation and update timestamps, we will make
+        // them by calling the parent model's "freshTimestamp" method which will
+        // provide us with a fresh timestamp in this model's preferred format.
+        if ($timed) {
+            $record = $this->addTimestampsToAttachment($record);
+        }
+
+        foreach ($this->pivotValues as $value) {
+            $record[$value['column']] = $value['value'];
+        }
+
+        return $record;
+    }
+
+    /**
+     * Set the creation and update timestamps on an attach record.
+     *
+     * @param  array  $record
+     * @param  bool  $exists
+     * @return array
+     */
+    protected function addTimestampsToAttachment(array $record, $exists = false)
+    {
+        $fresh = $this->parent->freshTimestamp();
+
+        if ($this->using) {
+            $pivotModel = new $this->using;
+
+            $fresh = $fresh->format($pivotModel->getDateFormat());
+        }
+
+        if (! $exists && $this->hasPivotColumn($this->createdAt())) {
+            $record[$this->createdAt()] = $fresh;
+        }
+
+        if ($this->hasPivotColumn($this->updatedAt())) {
+            $record[$this->updatedAt()] = $fresh;
+        }
+
+        return $record;
+    }
+
+    /**
+     * Determine whether the given column is defined as a pivot column.
+     *
+     * @param  string  $column
+     * @return bool
+     */
+    public function hasPivotColumn($column)
+    {
+        return in_array($column, $this->pivotColumns);
+    }
+
+    /**
+     * Detach models from the relationship.
+     *
+     * @param  mixed  $ids
+     * @param  bool  $touch
+     * @return int
+     */
+    public function detach($ids = null, $touch = true)
+    {
+        if ($this->using &&
+            ! empty($ids) &&
+            empty($this->pivotWheres) &&
+            empty($this->pivotWhereIns) &&
+            empty($this->pivotWhereNulls)) {
+            $results = $this->detachUsingCustomClass($ids);
+        } else {
+            $query = $this->newPivotQuery();
+
+            // If associated IDs were passed to the method we will only delete those
+            // associations, otherwise all of the association ties will be broken.
+            // We'll return the numbers of affected rows when we do the deletes.
+            if (! is_null($ids)) {
+                $ids = $this->parseIds($ids);
+
+                if (empty($ids)) {
+                    return 0;
+                }
+
+                $query->whereIn($this->getQualifiedRelatedPivotKeyName(), (array) $ids);
+            }
+
+            // Once we have all of the conditions set on the statement, we are ready
+            // to run the delete on the pivot table. Then, if the touch parameter
+            // is true, we will go ahead and touch all related models to sync.
+            $results = $query->delete();
+        }
+
+        if ($touch) {
+            $this->touchIfTouching();
+        }
+
+        return $results;
+    }
+
+    /**
+     * Detach models from the relationship using a custom class.
+     *
+     * @param  mixed  $ids
+     * @return int
+     */
+    protected function detachUsingCustomClass($ids)
+    {
+        $results = 0;
+
+        foreach ($this->parseIds($ids) as $id) {
+            $results += $this->newPivot([
+                $this->foreignPivotKey => $this->parent->{$this->parentKey},
+                $this->relatedPivotKey => $id,
+            ], true)->delete();
+        }
+
+        return $results;
+    }
+
+    /**
+     * Get the pivot models that are currently attached.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    protected function getCurrentlyAttachedPivots()
+    {
+        return $this->newPivotQuery()->get()->map(function ($record) {
+            $class = $this->using ?: Pivot::class;
+
+            $pivot = $class::fromRawAttributes($this->parent, (array) $record, $this->getTable(), true);
+
+            return $pivot->setPivotKeys($this->foreignPivotKey, $this->relatedPivotKey);
+        });
+    }
+
+    /**
+     * Create a new pivot model instance.
+     *
+     * @param  array  $attributes
+     * @param  bool  $exists
+     * @return \Illuminate\Database\Eloquent\Relations\Pivot
+     */
+    public function newPivot(array $attributes = [], $exists = false)
+    {
+        $pivot = $this->related->newPivot(
+            $this->parent, $attributes, $this->table, $exists, $this->using
+        );
+
+        return $pivot->setPivotKeys($this->foreignPivotKey, $this->relatedPivotKey);
+    }
+
+    /**
+     * Create a new existing pivot model instance.
+     *
+     * @param  array  $attributes
+     * @return \Illuminate\Database\Eloquent\Relations\Pivot
+     */
+    public function newExistingPivot(array $attributes = [])
+    {
+        return $this->newPivot($attributes, true);
+    }
+
+    /**
+     * Get a new plain query builder for the pivot table.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function newPivotStatement()
+    {
+        return $this->query->getQuery()->newQuery()->from($this->table);
+    }
+
+    /**
+     * Get a new pivot statement for a given "other" ID.
+     *
+     * @param  mixed  $id
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function newPivotStatementForId($id)
+    {
+        return $this->newPivotQuery()->whereIn($this->relatedPivotKey, $this->parseIds($id));
+    }
+
+    /**
+     * Create a new query builder for the pivot table.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function newPivotQuery()
+    {
+        $query = $this->newPivotStatement();
+
+        foreach ($this->pivotWheres as $arguments) {
+            $query->where(...$arguments);
+        }
+
+        foreach ($this->pivotWhereIns as $arguments) {
+            $query->whereIn(...$arguments);
+        }
+
+        foreach ($this->pivotWhereNulls as $arguments) {
+            $query->whereNull(...$arguments);
+        }
+
+        return $query->where($this->getQualifiedForeignPivotKeyName(), $this->parent->{$this->parentKey});
+    }
+
+    /**
+     * Set the columns on the pivot table to retrieve.
+     *
+     * @param  array|mixed  $columns
+     * @return $this
+     */
+    public function withPivot($columns)
+    {
+        $this->pivotColumns = array_merge(
+            $this->pivotColumns, is_array($columns) ? $columns : func_get_args()
+        );
+
+        return $this;
+    }
+
+    /**
+     * Get all of the IDs from the given mixed value.
+     *
+     * @param  mixed  $value
+     * @return array
+     */
+    protected function parseIds($value)
+    {
+        if ($value instanceof Model) {
+            return [$value->{$this->relatedKey}];
+        }
+
+        if ($value instanceof Collection) {
+            return $value->pluck($this->relatedKey)->all();
+        }
+
+        if ($value instanceof BaseCollection) {
+            return $value->toArray();
+        }
+
+        return (array) $value;
+    }
+
+    /**
+     * Get the ID from the given mixed value.
+     *
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function parseId($value)
+    {
+        return $value instanceof Model ? $value->{$this->relatedKey} : $value;
+    }
+
+    /**
+     * Cast the given keys to integers if they are numeric and string otherwise.
+     *
+     * @param  array  $keys
+     * @return array
+     */
+    protected function castKeys(array $keys)
+    {
+        return array_map(function ($v) {
+            return $this->castKey($v);
+        }, $keys);
+    }
+
+    /**
+     * Cast the given key to convert to primary key type.
+     *
+     * @param  mixed  $key
+     * @return mixed
+     */
+    protected function castKey($key)
+    {
+        return $this->getTypeSwapValue(
+            $this->related->getKeyType(),
+            $key
+        );
+    }
+
+    /**
+     * Cast the given pivot attributes.
+     *
+     * @param  array  $attributes
+     * @return array
+     */
+    protected function castAttributes($attributes)
+    {
+        return $this->using
+                    ? $this->newPivot()->fill($attributes)->getAttributes()
+                    : $attributes;
+    }
+
+    /**
+     * Converts a given value to a given type value.
+     *
+     * @param  string  $type
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function getTypeSwapValue($type, $value)
+    {
+        return match (strtolower($type)) {
+            'int', 'integer' => (int) $value,
+            'real', 'float', 'double' => (float) $value,
+            'string' => (string) $value,
+            default => $value,
+        };
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/Concerns/SupportsDefaultModels.php b/vendor/illuminate/database/Eloquent/Relations/Concerns/SupportsDefaultModels.php
new file mode 100644
index 0000000..74e758f
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/Concerns/SupportsDefaultModels.php
@@ -0,0 +1,63 @@
+withDefault = $callback;
+
+        return $this;
+    }
+
+    /**
+     * Get the default value for this relation.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @return \Illuminate\Database\Eloquent\Model|null
+     */
+    protected function getDefaultFor(Model $parent)
+    {
+        if (! $this->withDefault) {
+            return;
+        }
+
+        $instance = $this->newRelatedInstanceFor($parent);
+
+        if (is_callable($this->withDefault)) {
+            return call_user_func($this->withDefault, $instance, $parent) ?: $instance;
+        }
+
+        if (is_array($this->withDefault)) {
+            $instance->forceFill($this->withDefault);
+        }
+
+        return $instance;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/HasMany.php b/vendor/illuminate/database/Eloquent/Relations/HasMany.php
new file mode 100755
index 0000000..b005d4f
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/HasMany.php
@@ -0,0 +1,49 @@
+getParentKey())
+                ? $this->query->get()
+                : $this->related->newCollection();
+    }
+
+    /**
+     * Initialize the relation on a set of models.
+     *
+     * @param  array  $models
+     * @param  string  $relation
+     * @return array
+     */
+    public function initRelation(array $models, $relation)
+    {
+        foreach ($models as $model) {
+            $model->setRelation($relation, $this->related->newCollection());
+        }
+
+        return $models;
+    }
+
+    /**
+     * Match the eagerly loaded results to their parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @return array
+     */
+    public function match(array $models, Collection $results, $relation)
+    {
+        return $this->matchMany($models, $results, $relation);
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/HasManyThrough.php b/vendor/illuminate/database/Eloquent/Relations/HasManyThrough.php
new file mode 100644
index 0000000..b6ca908
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/HasManyThrough.php
@@ -0,0 +1,772 @@
+localKey = $localKey;
+        $this->firstKey = $firstKey;
+        $this->secondKey = $secondKey;
+        $this->farParent = $farParent;
+        $this->throughParent = $throughParent;
+        $this->secondLocalKey = $secondLocalKey;
+
+        parent::__construct($query, $throughParent);
+    }
+
+    /**
+     * Set the base constraints on the relation query.
+     *
+     * @return void
+     */
+    public function addConstraints()
+    {
+        $localValue = $this->farParent[$this->localKey];
+
+        $this->performJoin();
+
+        if (static::$constraints) {
+            $this->query->where($this->getQualifiedFirstKeyName(), '=', $localValue);
+        }
+    }
+
+    /**
+     * Set the join clause on the query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder|null  $query
+     * @return void
+     */
+    protected function performJoin(Builder $query = null)
+    {
+        $query = $query ?: $this->query;
+
+        $farKey = $this->getQualifiedFarKeyName();
+
+        $query->join($this->throughParent->getTable(), $this->getQualifiedParentKeyName(), '=', $farKey);
+
+        if ($this->throughParentSoftDeletes()) {
+            $query->withGlobalScope('SoftDeletableHasManyThrough', function ($query) {
+                $query->whereNull($this->throughParent->getQualifiedDeletedAtColumn());
+            });
+        }
+    }
+
+    /**
+     * Get the fully qualified parent key name.
+     *
+     * @return string
+     */
+    public function getQualifiedParentKeyName()
+    {
+        return $this->parent->qualifyColumn($this->secondLocalKey);
+    }
+
+    /**
+     * Determine whether "through" parent of the relation uses Soft Deletes.
+     *
+     * @return bool
+     */
+    public function throughParentSoftDeletes()
+    {
+        return in_array(SoftDeletes::class, class_uses_recursive($this->throughParent));
+    }
+
+    /**
+     * Indicate that trashed "through" parents should be included in the query.
+     *
+     * @return $this
+     */
+    public function withTrashedParents()
+    {
+        $this->query->withoutGlobalScope('SoftDeletableHasManyThrough');
+
+        return $this;
+    }
+
+    /**
+     * Set the constraints for an eager load of the relation.
+     *
+     * @param  array  $models
+     * @return void
+     */
+    public function addEagerConstraints(array $models)
+    {
+        $whereIn = $this->whereInMethod($this->farParent, $this->localKey);
+
+        $this->query->{$whereIn}(
+            $this->getQualifiedFirstKeyName(), $this->getKeys($models, $this->localKey)
+        );
+    }
+
+    /**
+     * Initialize the relation on a set of models.
+     *
+     * @param  array  $models
+     * @param  string  $relation
+     * @return array
+     */
+    public function initRelation(array $models, $relation)
+    {
+        foreach ($models as $model) {
+            $model->setRelation($relation, $this->related->newCollection());
+        }
+
+        return $models;
+    }
+
+    /**
+     * Match the eagerly loaded results to their parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @return array
+     */
+    public function match(array $models, Collection $results, $relation)
+    {
+        $dictionary = $this->buildDictionary($results);
+
+        // Once we have the dictionary we can simply spin through the parent models to
+        // link them up with their children using the keyed dictionary to make the
+        // matching very convenient and easy work. Then we'll just return them.
+        foreach ($models as $model) {
+            if (isset($dictionary[$key = $this->getDictionaryKey($model->getAttribute($this->localKey))])) {
+                $model->setRelation(
+                    $relation, $this->related->newCollection($dictionary[$key])
+                );
+            }
+        }
+
+        return $models;
+    }
+
+    /**
+     * Build model dictionary keyed by the relation's foreign key.
+     *
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @return array
+     */
+    protected function buildDictionary(Collection $results)
+    {
+        $dictionary = [];
+
+        // First we will create a dictionary of models keyed by the foreign key of the
+        // relationship as this will allow us to quickly access all of the related
+        // models without having to do nested looping which will be quite slow.
+        foreach ($results as $result) {
+            $dictionary[$result->laravel_through_key][] = $result;
+        }
+
+        return $dictionary;
+    }
+
+    /**
+     * Get the first related model record matching the attributes or instantiate it.
+     *
+     * @param  array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function firstOrNew(array $attributes)
+    {
+        if (is_null($instance = $this->where($attributes)->first())) {
+            $instance = $this->related->newInstance($attributes);
+        }
+
+        return $instance;
+    }
+
+    /**
+     * Create or update a related record matching the attributes, and fill it with values.
+     *
+     * @param  array  $attributes
+     * @param  array  $values
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function updateOrCreate(array $attributes, array $values = [])
+    {
+        $instance = $this->firstOrNew($attributes);
+
+        $instance->fill($values)->save();
+
+        return $instance;
+    }
+
+    /**
+     * Add a basic where clause to the query, and return the first result.
+     *
+     * @param  \Closure|string|array  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @param  string  $boolean
+     * @return \Illuminate\Database\Eloquent\Model|static
+     */
+    public function firstWhere($column, $operator = null, $value = null, $boolean = 'and')
+    {
+        return $this->where($column, $operator, $value, $boolean)->first();
+    }
+
+    /**
+     * Execute the query and get the first related model.
+     *
+     * @param  array  $columns
+     * @return mixed
+     */
+    public function first($columns = ['*'])
+    {
+        $results = $this->take(1)->get($columns);
+
+        return count($results) > 0 ? $results->first() : null;
+    }
+
+    /**
+     * Execute the query and get the first result or throw an exception.
+     *
+     * @param  array  $columns
+     * @return \Illuminate\Database\Eloquent\Model|static
+     *
+     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
+     */
+    public function firstOrFail($columns = ['*'])
+    {
+        if (! is_null($model = $this->first($columns))) {
+            return $model;
+        }
+
+        throw (new ModelNotFoundException)->setModel(get_class($this->related));
+    }
+
+    /**
+     * Execute the query and get the first result or call a callback.
+     *
+     * @param  \Closure|array  $columns
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Model|static|mixed
+     */
+    public function firstOr($columns = ['*'], Closure $callback = null)
+    {
+        if ($columns instanceof Closure) {
+            $callback = $columns;
+
+            $columns = ['*'];
+        }
+
+        if (! is_null($model = $this->first($columns))) {
+            return $model;
+        }
+
+        return $callback();
+    }
+
+    /**
+     * Find a related model by its primary key.
+     *
+     * @param  mixed  $id
+     * @param  array  $columns
+     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|null
+     */
+    public function find($id, $columns = ['*'])
+    {
+        if (is_array($id) || $id instanceof Arrayable) {
+            return $this->findMany($id, $columns);
+        }
+
+        return $this->where(
+            $this->getRelated()->getQualifiedKeyName(), '=', $id
+        )->first($columns);
+    }
+
+    /**
+     * Find multiple related models by their primary keys.
+     *
+     * @param  \Illuminate\Contracts\Support\Arrayable|array  $ids
+     * @param  array  $columns
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function findMany($ids, $columns = ['*'])
+    {
+        $ids = $ids instanceof Arrayable ? $ids->toArray() : $ids;
+
+        if (empty($ids)) {
+            return $this->getRelated()->newCollection();
+        }
+
+        return $this->whereIn(
+            $this->getRelated()->getQualifiedKeyName(), $ids
+        )->get($columns);
+    }
+
+    /**
+     * Find a related model by its primary key or throw an exception.
+     *
+     * @param  mixed  $id
+     * @param  array  $columns
+     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection
+     *
+     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
+     */
+    public function findOrFail($id, $columns = ['*'])
+    {
+        $result = $this->find($id, $columns);
+
+        $id = $id instanceof Arrayable ? $id->toArray() : $id;
+
+        if (is_array($id)) {
+            if (count($result) === count(array_unique($id))) {
+                return $result;
+            }
+        } elseif (! is_null($result)) {
+            return $result;
+        }
+
+        throw (new ModelNotFoundException)->setModel(get_class($this->related), $id);
+    }
+
+    /**
+     * Find a related model by its primary key or call a callback.
+     *
+     * @param  mixed  $id
+     * @param  \Closure|array  $columns
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|mixed
+     */
+    public function findOr($id, $columns = ['*'], Closure $callback = null)
+    {
+        if ($columns instanceof Closure) {
+            $callback = $columns;
+
+            $columns = ['*'];
+        }
+
+        $result = $this->find($id, $columns);
+
+        $id = $id instanceof Arrayable ? $id->toArray() : $id;
+
+        if (is_array($id)) {
+            if (count($result) === count(array_unique($id))) {
+                return $result;
+            }
+        } elseif (! is_null($result)) {
+            return $result;
+        }
+
+        return $callback();
+    }
+
+    /**
+     * Get the results of the relationship.
+     *
+     * @return mixed
+     */
+    public function getResults()
+    {
+        return ! is_null($this->farParent->{$this->localKey})
+                ? $this->get()
+                : $this->related->newCollection();
+    }
+
+    /**
+     * Execute the query as a "select" statement.
+     *
+     * @param  array  $columns
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function get($columns = ['*'])
+    {
+        $builder = $this->prepareQueryBuilder($columns);
+
+        $models = $builder->getModels();
+
+        // If we actually found models we will also eager load any relationships that
+        // have been specified as needing to be eager loaded. This will solve the
+        // n + 1 query problem for the developer and also increase performance.
+        if (count($models) > 0) {
+            $models = $builder->eagerLoadRelations($models);
+        }
+
+        return $this->related->newCollection($models);
+    }
+
+    /**
+     * Get a paginator for the "select" statement.
+     *
+     * @param  int|null  $perPage
+     * @param  array  $columns
+     * @param  string  $pageName
+     * @param  int  $page
+     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
+     */
+    public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
+    {
+        $this->query->addSelect($this->shouldSelect($columns));
+
+        return $this->query->paginate($perPage, $columns, $pageName, $page);
+    }
+
+    /**
+     * Paginate the given query into a simple paginator.
+     *
+     * @param  int|null  $perPage
+     * @param  array  $columns
+     * @param  string  $pageName
+     * @param  int|null  $page
+     * @return \Illuminate\Contracts\Pagination\Paginator
+     */
+    public function simplePaginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
+    {
+        $this->query->addSelect($this->shouldSelect($columns));
+
+        return $this->query->simplePaginate($perPage, $columns, $pageName, $page);
+    }
+
+    /**
+     * Paginate the given query into a cursor paginator.
+     *
+     * @param  int|null  $perPage
+     * @param  array  $columns
+     * @param  string  $cursorName
+     * @param  string|null  $cursor
+     * @return \Illuminate\Contracts\Pagination\CursorPaginator
+     */
+    public function cursorPaginate($perPage = null, $columns = ['*'], $cursorName = 'cursor', $cursor = null)
+    {
+        $this->query->addSelect($this->shouldSelect($columns));
+
+        return $this->query->cursorPaginate($perPage, $columns, $cursorName, $cursor);
+    }
+
+    /**
+     * Set the select clause for the relation query.
+     *
+     * @param  array  $columns
+     * @return array
+     */
+    protected function shouldSelect(array $columns = ['*'])
+    {
+        if ($columns == ['*']) {
+            $columns = [$this->related->getTable().'.*'];
+        }
+
+        return array_merge($columns, [$this->getQualifiedFirstKeyName().' as laravel_through_key']);
+    }
+
+    /**
+     * Chunk the results of the query.
+     *
+     * @param  int  $count
+     * @param  callable  $callback
+     * @return bool
+     */
+    public function chunk($count, callable $callback)
+    {
+        return $this->prepareQueryBuilder()->chunk($count, $callback);
+    }
+
+    /**
+     * Chunk the results of a query by comparing numeric IDs.
+     *
+     * @param  int  $count
+     * @param  callable  $callback
+     * @param  string|null  $column
+     * @param  string|null  $alias
+     * @return bool
+     */
+    public function chunkById($count, callable $callback, $column = null, $alias = null)
+    {
+        $column ??= $this->getRelated()->getQualifiedKeyName();
+
+        $alias ??= $this->getRelated()->getKeyName();
+
+        return $this->prepareQueryBuilder()->chunkById($count, $callback, $column, $alias);
+    }
+
+    /**
+     * Get a generator for the given query.
+     *
+     * @return \Illuminate\Support\LazyCollection
+     */
+    public function cursor()
+    {
+        return $this->prepareQueryBuilder()->cursor();
+    }
+
+    /**
+     * Execute a callback over each item while chunking.
+     *
+     * @param  callable  $callback
+     * @param  int  $count
+     * @return bool
+     */
+    public function each(callable $callback, $count = 1000)
+    {
+        return $this->chunk($count, function ($results) use ($callback) {
+            foreach ($results as $key => $value) {
+                if ($callback($value, $key) === false) {
+                    return false;
+                }
+            }
+        });
+    }
+
+    /**
+     * Query lazily, by chunks of the given size.
+     *
+     * @param  int  $chunkSize
+     * @return \Illuminate\Support\LazyCollection
+     */
+    public function lazy($chunkSize = 1000)
+    {
+        return $this->prepareQueryBuilder()->lazy($chunkSize);
+    }
+
+    /**
+     * Query lazily, by chunking the results of a query by comparing IDs.
+     *
+     * @param  int  $chunkSize
+     * @param  string|null  $column
+     * @param  string|null  $alias
+     * @return \Illuminate\Support\LazyCollection
+     */
+    public function lazyById($chunkSize = 1000, $column = null, $alias = null)
+    {
+        $column ??= $this->getRelated()->getQualifiedKeyName();
+
+        $alias ??= $this->getRelated()->getKeyName();
+
+        return $this->prepareQueryBuilder()->lazyById($chunkSize, $column, $alias);
+    }
+
+    /**
+     * Prepare the query builder for query execution.
+     *
+     * @param  array  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function prepareQueryBuilder($columns = ['*'])
+    {
+        $builder = $this->query->applyScopes();
+
+        return $builder->addSelect(
+            $this->shouldSelect($builder->getQuery()->columns ? [] : $columns)
+        );
+    }
+
+    /**
+     * Add the constraints for a relationship query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        if ($parentQuery->getQuery()->from === $query->getQuery()->from) {
+            return $this->getRelationExistenceQueryForSelfRelation($query, $parentQuery, $columns);
+        }
+
+        if ($parentQuery->getQuery()->from === $this->throughParent->getTable()) {
+            return $this->getRelationExistenceQueryForThroughSelfRelation($query, $parentQuery, $columns);
+        }
+
+        $this->performJoin($query);
+
+        return $query->select($columns)->whereColumn(
+            $this->getQualifiedLocalKeyName(), '=', $this->getQualifiedFirstKeyName()
+        );
+    }
+
+    /**
+     * Add the constraints for a relationship query on the same table.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQueryForSelfRelation(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        $query->from($query->getModel()->getTable().' as '.$hash = $this->getRelationCountHash());
+
+        $query->join($this->throughParent->getTable(), $this->getQualifiedParentKeyName(), '=', $hash.'.'.$this->secondKey);
+
+        if ($this->throughParentSoftDeletes()) {
+            $query->whereNull($this->throughParent->getQualifiedDeletedAtColumn());
+        }
+
+        $query->getModel()->setTable($hash);
+
+        return $query->select($columns)->whereColumn(
+            $parentQuery->getQuery()->from.'.'.$this->localKey, '=', $this->getQualifiedFirstKeyName()
+        );
+    }
+
+    /**
+     * Add the constraints for a relationship query on the same table as the through parent.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQueryForThroughSelfRelation(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        $table = $this->throughParent->getTable().' as '.$hash = $this->getRelationCountHash();
+
+        $query->join($table, $hash.'.'.$this->secondLocalKey, '=', $this->getQualifiedFarKeyName());
+
+        if ($this->throughParentSoftDeletes()) {
+            $query->whereNull($hash.'.'.$this->throughParent->getDeletedAtColumn());
+        }
+
+        return $query->select($columns)->whereColumn(
+            $parentQuery->getQuery()->from.'.'.$this->localKey, '=', $hash.'.'.$this->firstKey
+        );
+    }
+
+    /**
+     * Get the qualified foreign key on the related model.
+     *
+     * @return string
+     */
+    public function getQualifiedFarKeyName()
+    {
+        return $this->getQualifiedForeignKeyName();
+    }
+
+    /**
+     * Get the foreign key on the "through" model.
+     *
+     * @return string
+     */
+    public function getFirstKeyName()
+    {
+        return $this->firstKey;
+    }
+
+    /**
+     * Get the qualified foreign key on the "through" model.
+     *
+     * @return string
+     */
+    public function getQualifiedFirstKeyName()
+    {
+        return $this->throughParent->qualifyColumn($this->firstKey);
+    }
+
+    /**
+     * Get the foreign key on the related model.
+     *
+     * @return string
+     */
+    public function getForeignKeyName()
+    {
+        return $this->secondKey;
+    }
+
+    /**
+     * Get the qualified foreign key on the related model.
+     *
+     * @return string
+     */
+    public function getQualifiedForeignKeyName()
+    {
+        return $this->related->qualifyColumn($this->secondKey);
+    }
+
+    /**
+     * Get the local key on the far parent model.
+     *
+     * @return string
+     */
+    public function getLocalKeyName()
+    {
+        return $this->localKey;
+    }
+
+    /**
+     * Get the qualified local key on the far parent model.
+     *
+     * @return string
+     */
+    public function getQualifiedLocalKeyName()
+    {
+        return $this->farParent->qualifyColumn($this->localKey);
+    }
+
+    /**
+     * Get the local key on the intermediary model.
+     *
+     * @return string
+     */
+    public function getSecondLocalKeyName()
+    {
+        return $this->secondLocalKey;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/HasOne.php b/vendor/illuminate/database/Eloquent/Relations/HasOne.php
new file mode 100755
index 0000000..ed85f1e
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/HasOne.php
@@ -0,0 +1,137 @@
+getParentKey())) {
+            return $this->getDefaultFor($this->parent);
+        }
+
+        return $this->query->first() ?: $this->getDefaultFor($this->parent);
+    }
+
+    /**
+     * Initialize the relation on a set of models.
+     *
+     * @param  array  $models
+     * @param  string  $relation
+     * @return array
+     */
+    public function initRelation(array $models, $relation)
+    {
+        foreach ($models as $model) {
+            $model->setRelation($relation, $this->getDefaultFor($model));
+        }
+
+        return $models;
+    }
+
+    /**
+     * Match the eagerly loaded results to their parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @return array
+     */
+    public function match(array $models, Collection $results, $relation)
+    {
+        return $this->matchOne($models, $results, $relation);
+    }
+
+    /**
+     * Add the constraints for an internal relationship existence query.
+     *
+     * Essentially, these queries compare on column names like "whereColumn".
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        if ($this->isOneOfMany()) {
+            $this->mergeOneOfManyJoinsTo($query);
+        }
+
+        return parent::getRelationExistenceQuery($query, $parentQuery, $columns);
+    }
+
+    /**
+     * Add constraints for inner join subselect for one of many relationships.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  string|null  $column
+     * @param  string|null  $aggregate
+     * @return void
+     */
+    public function addOneOfManySubQueryConstraints(Builder $query, $column = null, $aggregate = null)
+    {
+        $query->addSelect($this->foreignKey);
+    }
+
+    /**
+     * Get the columns that should be selected by the one of many subquery.
+     *
+     * @return array|string
+     */
+    public function getOneOfManySubQuerySelectColumns()
+    {
+        return $this->foreignKey;
+    }
+
+    /**
+     * Add join query constraints for one of many relationships.
+     *
+     * @param  \Illuminate\Database\Query\JoinClause  $join
+     * @return void
+     */
+    public function addOneOfManyJoinSubQueryConstraints(JoinClause $join)
+    {
+        $join->on($this->qualifySubSelectColumn($this->foreignKey), '=', $this->qualifyRelatedColumn($this->foreignKey));
+    }
+
+    /**
+     * Make a new related instance for the given model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function newRelatedInstanceFor(Model $parent)
+    {
+        return $this->related->newInstance()->setAttribute(
+            $this->getForeignKeyName(), $parent->{$this->localKey}
+        );
+    }
+
+    /**
+     * Get the value of the model's foreign key.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return mixed
+     */
+    protected function getRelatedKeyFrom(Model $model)
+    {
+        return $model->getAttribute($this->getForeignKeyName());
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/HasOneOrMany.php b/vendor/illuminate/database/Eloquent/Relations/HasOneOrMany.php
new file mode 100755
index 0000000..01f0c1e
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/HasOneOrMany.php
@@ -0,0 +1,486 @@
+localKey = $localKey;
+        $this->foreignKey = $foreignKey;
+
+        parent::__construct($query, $parent);
+    }
+
+    /**
+     * Create and return an un-saved instance of the related model.
+     *
+     * @param  array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function make(array $attributes = [])
+    {
+        return tap($this->related->newInstance($attributes), function ($instance) {
+            $this->setForeignAttributesForCreate($instance);
+        });
+    }
+
+    /**
+     * Create and return an un-saved instance of the related models.
+     *
+     * @param  iterable  $records
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function makeMany($records)
+    {
+        $instances = $this->related->newCollection();
+
+        foreach ($records as $record) {
+            $instances->push($this->make($record));
+        }
+
+        return $instances;
+    }
+
+    /**
+     * Set the base constraints on the relation query.
+     *
+     * @return void
+     */
+    public function addConstraints()
+    {
+        if (static::$constraints) {
+            $query = $this->getRelationQuery();
+
+            $query->where($this->foreignKey, '=', $this->getParentKey());
+
+            $query->whereNotNull($this->foreignKey);
+        }
+    }
+
+    /**
+     * Set the constraints for an eager load of the relation.
+     *
+     * @param  array  $models
+     * @return void
+     */
+    public function addEagerConstraints(array $models)
+    {
+        $whereIn = $this->whereInMethod($this->parent, $this->localKey);
+
+        $this->getRelationQuery()->{$whereIn}(
+            $this->foreignKey, $this->getKeys($models, $this->localKey)
+        );
+    }
+
+    /**
+     * Match the eagerly loaded results to their single parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @return array
+     */
+    public function matchOne(array $models, Collection $results, $relation)
+    {
+        return $this->matchOneOrMany($models, $results, $relation, 'one');
+    }
+
+    /**
+     * Match the eagerly loaded results to their many parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @return array
+     */
+    public function matchMany(array $models, Collection $results, $relation)
+    {
+        return $this->matchOneOrMany($models, $results, $relation, 'many');
+    }
+
+    /**
+     * Match the eagerly loaded results to their many parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @param  string  $type
+     * @return array
+     */
+    protected function matchOneOrMany(array $models, Collection $results, $relation, $type)
+    {
+        $dictionary = $this->buildDictionary($results);
+
+        // Once we have the dictionary we can simply spin through the parent models to
+        // link them up with their children using the keyed dictionary to make the
+        // matching very convenient and easy work. Then we'll just return them.
+        foreach ($models as $model) {
+            if (isset($dictionary[$key = $this->getDictionaryKey($model->getAttribute($this->localKey))])) {
+                $model->setRelation(
+                    $relation, $this->getRelationValue($dictionary, $key, $type)
+                );
+            }
+        }
+
+        return $models;
+    }
+
+    /**
+     * Get the value of a relationship by one or many type.
+     *
+     * @param  array  $dictionary
+     * @param  string  $key
+     * @param  string  $type
+     * @return mixed
+     */
+    protected function getRelationValue(array $dictionary, $key, $type)
+    {
+        $value = $dictionary[$key];
+
+        return $type === 'one' ? reset($value) : $this->related->newCollection($value);
+    }
+
+    /**
+     * Build model dictionary keyed by the relation's foreign key.
+     *
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @return array
+     */
+    protected function buildDictionary(Collection $results)
+    {
+        $foreign = $this->getForeignKeyName();
+
+        return $results->mapToDictionary(function ($result) use ($foreign) {
+            return [$this->getDictionaryKey($result->{$foreign}) => $result];
+        })->all();
+    }
+
+    /**
+     * Find a model by its primary key or return a new instance of the related model.
+     *
+     * @param  mixed  $id
+     * @param  array  $columns
+     * @return \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model
+     */
+    public function findOrNew($id, $columns = ['*'])
+    {
+        if (is_null($instance = $this->find($id, $columns))) {
+            $instance = $this->related->newInstance();
+
+            $this->setForeignAttributesForCreate($instance);
+        }
+
+        return $instance;
+    }
+
+    /**
+     * Get the first related model record matching the attributes or instantiate it.
+     *
+     * @param  array  $attributes
+     * @param  array  $values
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function firstOrNew(array $attributes = [], array $values = [])
+    {
+        if (is_null($instance = $this->where($attributes)->first())) {
+            $instance = $this->related->newInstance(array_merge($attributes, $values));
+
+            $this->setForeignAttributesForCreate($instance);
+        }
+
+        return $instance;
+    }
+
+    /**
+     * Get the first related record matching the attributes or create it.
+     *
+     * @param  array  $attributes
+     * @param  array  $values
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function firstOrCreate(array $attributes = [], array $values = [])
+    {
+        if (is_null($instance = $this->where($attributes)->first())) {
+            $instance = $this->create(array_merge($attributes, $values));
+        }
+
+        return $instance;
+    }
+
+    /**
+     * Create or update a related record matching the attributes, and fill it with values.
+     *
+     * @param  array  $attributes
+     * @param  array  $values
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function updateOrCreate(array $attributes, array $values = [])
+    {
+        return tap($this->firstOrNew($attributes), function ($instance) use ($values) {
+            $instance->fill($values);
+
+            $instance->save();
+        });
+    }
+
+    /**
+     * Attach a model instance to the parent model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return \Illuminate\Database\Eloquent\Model|false
+     */
+    public function save(Model $model)
+    {
+        $this->setForeignAttributesForCreate($model);
+
+        return $model->save() ? $model : false;
+    }
+
+    /**
+     * Attach a model instance without raising any events to the parent model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return \Illuminate\Database\Eloquent\Model|false
+     */
+    public function saveQuietly(Model $model)
+    {
+        return Model::withoutEvents(function () use ($model) {
+            return $this->save($model);
+        });
+    }
+
+    /**
+     * Attach a collection of models to the parent instance.
+     *
+     * @param  iterable  $models
+     * @return iterable
+     */
+    public function saveMany($models)
+    {
+        foreach ($models as $model) {
+            $this->save($model);
+        }
+
+        return $models;
+    }
+
+    /**
+     * Attach a collection of models to the parent instance without raising any events to the parent model.
+     *
+     * @param  iterable  $models
+     * @return iterable
+     */
+    public function saveManyQuietly($models)
+    {
+        return Model::withoutEvents(function () use ($models) {
+            return $this->saveMany($models);
+        });
+    }
+
+    /**
+     * Create a new instance of the related model.
+     *
+     * @param  array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function create(array $attributes = [])
+    {
+        return tap($this->related->newInstance($attributes), function ($instance) {
+            $this->setForeignAttributesForCreate($instance);
+
+            $instance->save();
+        });
+    }
+
+    /**
+     * Create a new instance of the related model without raising any events to the parent model.
+     *
+     * @param  array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function createQuietly(array $attributes = [])
+    {
+        return Model::withoutEvents(fn () => $this->create($attributes));
+    }
+
+    /**
+     * Create a new instance of the related model. Allow mass-assignment.
+     *
+     * @param  array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function forceCreate(array $attributes = [])
+    {
+        $attributes[$this->getForeignKeyName()] = $this->getParentKey();
+
+        return $this->related->forceCreate($attributes);
+    }
+
+    /**
+     * Create a Collection of new instances of the related model.
+     *
+     * @param  iterable  $records
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function createMany(iterable $records)
+    {
+        $instances = $this->related->newCollection();
+
+        foreach ($records as $record) {
+            $instances->push($this->create($record));
+        }
+
+        return $instances;
+    }
+
+    /**
+     * Create a Collection of new instances of the related model without raising any events to the parent model.
+     *
+     * @param  iterable  $records
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function createManyQuietly(iterable $records)
+    {
+        return Model::withoutEvents(fn () => $this->createMany($records));
+    }
+
+    /**
+     * Set the foreign ID for creating a related model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return void
+     */
+    protected function setForeignAttributesForCreate(Model $model)
+    {
+        $model->setAttribute($this->getForeignKeyName(), $this->getParentKey());
+    }
+
+    /**
+     * Add the constraints for a relationship query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        if ($query->getQuery()->from == $parentQuery->getQuery()->from) {
+            return $this->getRelationExistenceQueryForSelfRelation($query, $parentQuery, $columns);
+        }
+
+        return parent::getRelationExistenceQuery($query, $parentQuery, $columns);
+    }
+
+    /**
+     * Add the constraints for a relationship query on the same table.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQueryForSelfRelation(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        $query->from($query->getModel()->getTable().' as '.$hash = $this->getRelationCountHash());
+
+        $query->getModel()->setTable($hash);
+
+        return $query->select($columns)->whereColumn(
+            $this->getQualifiedParentKeyName(), '=', $hash.'.'.$this->getForeignKeyName()
+        );
+    }
+
+    /**
+     * Get the key for comparing against the parent key in "has" query.
+     *
+     * @return string
+     */
+    public function getExistenceCompareKey()
+    {
+        return $this->getQualifiedForeignKeyName();
+    }
+
+    /**
+     * Get the key value of the parent's local key.
+     *
+     * @return mixed
+     */
+    public function getParentKey()
+    {
+        return $this->parent->getAttribute($this->localKey);
+    }
+
+    /**
+     * Get the fully qualified parent key name.
+     *
+     * @return string
+     */
+    public function getQualifiedParentKeyName()
+    {
+        return $this->parent->qualifyColumn($this->localKey);
+    }
+
+    /**
+     * Get the plain foreign key.
+     *
+     * @return string
+     */
+    public function getForeignKeyName()
+    {
+        $segments = explode('.', $this->getQualifiedForeignKeyName());
+
+        return end($segments);
+    }
+
+    /**
+     * Get the foreign key for the relationship.
+     *
+     * @return string
+     */
+    public function getQualifiedForeignKeyName()
+    {
+        return $this->foreignKey;
+    }
+
+    /**
+     * Get the local key for the relationship.
+     *
+     * @return string
+     */
+    public function getLocalKeyName()
+    {
+        return $this->localKey;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/HasOneThrough.php b/vendor/illuminate/database/Eloquent/Relations/HasOneThrough.php
new file mode 100644
index 0000000..ed9c7ba
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/HasOneThrough.php
@@ -0,0 +1,77 @@
+first() ?: $this->getDefaultFor($this->farParent);
+    }
+
+    /**
+     * Initialize the relation on a set of models.
+     *
+     * @param  array  $models
+     * @param  string  $relation
+     * @return array
+     */
+    public function initRelation(array $models, $relation)
+    {
+        foreach ($models as $model) {
+            $model->setRelation($relation, $this->getDefaultFor($model));
+        }
+
+        return $models;
+    }
+
+    /**
+     * Match the eagerly loaded results to their parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @return array
+     */
+    public function match(array $models, Collection $results, $relation)
+    {
+        $dictionary = $this->buildDictionary($results);
+
+        // Once we have the dictionary we can simply spin through the parent models to
+        // link them up with their children using the keyed dictionary to make the
+        // matching very convenient and easy work. Then we'll just return them.
+        foreach ($models as $model) {
+            if (isset($dictionary[$key = $this->getDictionaryKey($model->getAttribute($this->localKey))])) {
+                $value = $dictionary[$key];
+                $model->setRelation(
+                    $relation, reset($value)
+                );
+            }
+        }
+
+        return $models;
+    }
+
+    /**
+     * Make a new related instance for the given model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function newRelatedInstanceFor(Model $parent)
+    {
+        return $this->related->newInstance();
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/MorphMany.php b/vendor/illuminate/database/Eloquent/Relations/MorphMany.php
new file mode 100755
index 0000000..282ba2e
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/MorphMany.php
@@ -0,0 +1,62 @@
+getParentKey())
+                ? $this->query->get()
+                : $this->related->newCollection();
+    }
+
+    /**
+     * Initialize the relation on a set of models.
+     *
+     * @param  array  $models
+     * @param  string  $relation
+     * @return array
+     */
+    public function initRelation(array $models, $relation)
+    {
+        foreach ($models as $model) {
+            $model->setRelation($relation, $this->related->newCollection());
+        }
+
+        return $models;
+    }
+
+    /**
+     * Match the eagerly loaded results to their parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @return array
+     */
+    public function match(array $models, Collection $results, $relation)
+    {
+        return $this->matchMany($models, $results, $relation);
+    }
+
+    /**
+     * Create a new instance of the related model. Allow mass-assignment.
+     *
+     * @param  array  $attributes
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function forceCreate(array $attributes = [])
+    {
+        $attributes[$this->getMorphType()] = $this->morphClass;
+
+        return parent::forceCreate($attributes);
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/MorphOne.php b/vendor/illuminate/database/Eloquent/Relations/MorphOne.php
new file mode 100755
index 0000000..fc8f4dc
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/MorphOne.php
@@ -0,0 +1,137 @@
+getParentKey())) {
+            return $this->getDefaultFor($this->parent);
+        }
+
+        return $this->query->first() ?: $this->getDefaultFor($this->parent);
+    }
+
+    /**
+     * Initialize the relation on a set of models.
+     *
+     * @param  array  $models
+     * @param  string  $relation
+     * @return array
+     */
+    public function initRelation(array $models, $relation)
+    {
+        foreach ($models as $model) {
+            $model->setRelation($relation, $this->getDefaultFor($model));
+        }
+
+        return $models;
+    }
+
+    /**
+     * Match the eagerly loaded results to their parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @return array
+     */
+    public function match(array $models, Collection $results, $relation)
+    {
+        return $this->matchOne($models, $results, $relation);
+    }
+
+    /**
+     * Get the relationship query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        if ($this->isOneOfMany()) {
+            $this->mergeOneOfManyJoinsTo($query);
+        }
+
+        return parent::getRelationExistenceQuery($query, $parentQuery, $columns);
+    }
+
+    /**
+     * Add constraints for inner join subselect for one of many relationships.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  string|null  $column
+     * @param  string|null  $aggregate
+     * @return void
+     */
+    public function addOneOfManySubQueryConstraints(Builder $query, $column = null, $aggregate = null)
+    {
+        $query->addSelect($this->foreignKey, $this->morphType);
+    }
+
+    /**
+     * Get the columns that should be selected by the one of many subquery.
+     *
+     * @return array|string
+     */
+    public function getOneOfManySubQuerySelectColumns()
+    {
+        return [$this->foreignKey, $this->morphType];
+    }
+
+    /**
+     * Add join query constraints for one of many relationships.
+     *
+     * @param  \Illuminate\Database\Query\JoinClause  $join
+     * @return void
+     */
+    public function addOneOfManyJoinSubQueryConstraints(JoinClause $join)
+    {
+        $join
+            ->on($this->qualifySubSelectColumn($this->morphType), '=', $this->qualifyRelatedColumn($this->morphType))
+            ->on($this->qualifySubSelectColumn($this->foreignKey), '=', $this->qualifyRelatedColumn($this->foreignKey));
+    }
+
+    /**
+     * Make a new related instance for the given model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function newRelatedInstanceFor(Model $parent)
+    {
+        return $this->related->newInstance()
+                    ->setAttribute($this->getForeignKeyName(), $parent->{$this->localKey})
+                    ->setAttribute($this->getMorphType(), $this->morphClass);
+    }
+
+    /**
+     * Get the value of the model's foreign key.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return mixed
+     */
+    protected function getRelatedKeyFrom(Model $model)
+    {
+        return $model->getAttribute($this->getForeignKeyName());
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/MorphOneOrMany.php b/vendor/illuminate/database/Eloquent/Relations/MorphOneOrMany.php
new file mode 100755
index 0000000..6e2297f
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/MorphOneOrMany.php
@@ -0,0 +1,127 @@
+morphType = $type;
+
+        $this->morphClass = $parent->getMorphClass();
+
+        parent::__construct($query, $parent, $id, $localKey);
+    }
+
+    /**
+     * Set the base constraints on the relation query.
+     *
+     * @return void
+     */
+    public function addConstraints()
+    {
+        if (static::$constraints) {
+            $this->getRelationQuery()->where($this->morphType, $this->morphClass);
+
+            parent::addConstraints();
+        }
+    }
+
+    /**
+     * Set the constraints for an eager load of the relation.
+     *
+     * @param  array  $models
+     * @return void
+     */
+    public function addEagerConstraints(array $models)
+    {
+        parent::addEagerConstraints($models);
+
+        $this->getRelationQuery()->where($this->morphType, $this->morphClass);
+    }
+
+    /**
+     * Set the foreign ID and type for creating a related model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return void
+     */
+    protected function setForeignAttributesForCreate(Model $model)
+    {
+        $model->{$this->getForeignKeyName()} = $this->getParentKey();
+
+        $model->{$this->getMorphType()} = $this->morphClass;
+    }
+
+    /**
+     * Get the relationship query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        return parent::getRelationExistenceQuery($query, $parentQuery, $columns)->where(
+            $query->qualifyColumn($this->getMorphType()), $this->morphClass
+        );
+    }
+
+    /**
+     * Get the foreign key "type" name.
+     *
+     * @return string
+     */
+    public function getQualifiedMorphType()
+    {
+        return $this->morphType;
+    }
+
+    /**
+     * Get the plain morph type name without the table.
+     *
+     * @return string
+     */
+    public function getMorphType()
+    {
+        return last(explode('.', $this->morphType));
+    }
+
+    /**
+     * Get the class name of the parent model.
+     *
+     * @return string
+     */
+    public function getMorphClass()
+    {
+        return $this->morphClass;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/MorphPivot.php b/vendor/illuminate/database/Eloquent/Relations/MorphPivot.php
new file mode 100644
index 0000000..5ca8b48
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/MorphPivot.php
@@ -0,0 +1,182 @@
+where($this->morphType, $this->morphClass);
+
+        return parent::setKeysForSaveQuery($query);
+    }
+
+    /**
+     * Set the keys for a select query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function setKeysForSelectQuery($query)
+    {
+        $query->where($this->morphType, $this->morphClass);
+
+        return parent::setKeysForSelectQuery($query);
+    }
+
+    /**
+     * Delete the pivot model record from the database.
+     *
+     * @return int
+     */
+    public function delete()
+    {
+        if (isset($this->attributes[$this->getKeyName()])) {
+            return (int) parent::delete();
+        }
+
+        if ($this->fireModelEvent('deleting') === false) {
+            return 0;
+        }
+
+        $query = $this->getDeleteQuery();
+
+        $query->where($this->morphType, $this->morphClass);
+
+        return tap($query->delete(), function () {
+            $this->fireModelEvent('deleted', false);
+        });
+    }
+
+    /**
+     * Get the morph type for the pivot.
+     *
+     * @return string
+     */
+    public function getMorphType()
+    {
+        return $this->morphType;
+    }
+
+    /**
+     * Set the morph type for the pivot.
+     *
+     * @param  string  $morphType
+     * @return $this
+     */
+    public function setMorphType($morphType)
+    {
+        $this->morphType = $morphType;
+
+        return $this;
+    }
+
+    /**
+     * Set the morph class for the pivot.
+     *
+     * @param  string  $morphClass
+     * @return \Illuminate\Database\Eloquent\Relations\MorphPivot
+     */
+    public function setMorphClass($morphClass)
+    {
+        $this->morphClass = $morphClass;
+
+        return $this;
+    }
+
+    /**
+     * Get the queueable identity for the entity.
+     *
+     * @return mixed
+     */
+    public function getQueueableId()
+    {
+        if (isset($this->attributes[$this->getKeyName()])) {
+            return $this->getKey();
+        }
+
+        return sprintf(
+            '%s:%s:%s:%s:%s:%s',
+            $this->foreignKey, $this->getAttribute($this->foreignKey),
+            $this->relatedKey, $this->getAttribute($this->relatedKey),
+            $this->morphType, $this->morphClass
+        );
+    }
+
+    /**
+     * Get a new query to restore one or more models by their queueable IDs.
+     *
+     * @param  array|int  $ids
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function newQueryForRestoration($ids)
+    {
+        if (is_array($ids)) {
+            return $this->newQueryForCollectionRestoration($ids);
+        }
+
+        if (! str_contains($ids, ':')) {
+            return parent::newQueryForRestoration($ids);
+        }
+
+        $segments = explode(':', $ids);
+
+        return $this->newQueryWithoutScopes()
+                        ->where($segments[0], $segments[1])
+                        ->where($segments[2], $segments[3])
+                        ->where($segments[4], $segments[5]);
+    }
+
+    /**
+     * Get a new query to restore multiple models by their queueable IDs.
+     *
+     * @param  array  $ids
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function newQueryForCollectionRestoration(array $ids)
+    {
+        $ids = array_values($ids);
+
+        if (! str_contains($ids[0], ':')) {
+            return parent::newQueryForRestoration($ids);
+        }
+
+        $query = $this->newQueryWithoutScopes();
+
+        foreach ($ids as $id) {
+            $segments = explode(':', $id);
+
+            $query->orWhere(function ($query) use ($segments) {
+                return $query->where($segments[0], $segments[1])
+                             ->where($segments[2], $segments[3])
+                             ->where($segments[4], $segments[5]);
+            });
+        }
+
+        return $query;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/MorphTo.php b/vendor/illuminate/database/Eloquent/Relations/MorphTo.php
new file mode 100644
index 0000000..262741f
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/MorphTo.php
@@ -0,0 +1,396 @@
+morphType = $type;
+
+        parent::__construct($query, $parent, $foreignKey, $ownerKey, $relation);
+    }
+
+    /**
+     * Set the constraints for an eager load of the relation.
+     *
+     * @param  array  $models
+     * @return void
+     */
+    public function addEagerConstraints(array $models)
+    {
+        $this->buildDictionary($this->models = Collection::make($models));
+    }
+
+    /**
+     * Build a dictionary with the models.
+     *
+     * @param  \Illuminate\Database\Eloquent\Collection  $models
+     * @return void
+     */
+    protected function buildDictionary(Collection $models)
+    {
+        foreach ($models as $model) {
+            if ($model->{$this->morphType}) {
+                $morphTypeKey = $this->getDictionaryKey($model->{$this->morphType});
+                $foreignKeyKey = $this->getDictionaryKey($model->{$this->foreignKey});
+
+                $this->dictionary[$morphTypeKey][$foreignKeyKey][] = $model;
+            }
+        }
+    }
+
+    /**
+     * Get the results of the relationship.
+     *
+     * Called via eager load method of Eloquent query builder.
+     *
+     * @return mixed
+     */
+    public function getEager()
+    {
+        foreach (array_keys($this->dictionary) as $type) {
+            $this->matchToMorphParents($type, $this->getResultsByType($type));
+        }
+
+        return $this->models;
+    }
+
+    /**
+     * Get all of the relation results for a type.
+     *
+     * @param  string  $type
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    protected function getResultsByType($type)
+    {
+        $instance = $this->createModelByType($type);
+
+        $ownerKey = $this->ownerKey ?? $instance->getKeyName();
+
+        $query = $this->replayMacros($instance->newQuery())
+                            ->mergeConstraintsFrom($this->getQuery())
+                            ->with(array_merge(
+                                $this->getQuery()->getEagerLoads(),
+                                (array) ($this->morphableEagerLoads[get_class($instance)] ?? [])
+                            ))
+                            ->withCount(
+                                (array) ($this->morphableEagerLoadCounts[get_class($instance)] ?? [])
+                            );
+
+        if ($callback = ($this->morphableConstraints[get_class($instance)] ?? null)) {
+            $callback($query);
+        }
+
+        $whereIn = $this->whereInMethod($instance, $ownerKey);
+
+        return $query->{$whereIn}(
+            $instance->getTable().'.'.$ownerKey, $this->gatherKeysByType($type, $instance->getKeyType())
+        )->get();
+    }
+
+    /**
+     * Gather all of the foreign keys for a given type.
+     *
+     * @param  string  $type
+     * @param  string  $keyType
+     * @return array
+     */
+    protected function gatherKeysByType($type, $keyType)
+    {
+        return $keyType !== 'string'
+                    ? array_keys($this->dictionary[$type])
+                    : array_map(function ($modelId) {
+                        return (string) $modelId;
+                    }, array_filter(array_keys($this->dictionary[$type])));
+    }
+
+    /**
+     * Create a new model instance by type.
+     *
+     * @param  string  $type
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function createModelByType($type)
+    {
+        $class = Model::getActualClassNameForMorph($type);
+
+        return tap(new $class, function ($instance) {
+            if (! $instance->getConnectionName()) {
+                $instance->setConnection($this->getConnection()->getName());
+            }
+        });
+    }
+
+    /**
+     * Match the eagerly loaded results to their parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @return array
+     */
+    public function match(array $models, Collection $results, $relation)
+    {
+        return $models;
+    }
+
+    /**
+     * Match the results for a given type to their parents.
+     *
+     * @param  string  $type
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @return void
+     */
+    protected function matchToMorphParents($type, Collection $results)
+    {
+        foreach ($results as $result) {
+            $ownerKey = ! is_null($this->ownerKey) ? $this->getDictionaryKey($result->{$this->ownerKey}) : $result->getKey();
+
+            if (isset($this->dictionary[$type][$ownerKey])) {
+                foreach ($this->dictionary[$type][$ownerKey] as $model) {
+                    $model->setRelation($this->relationName, $result);
+                }
+            }
+        }
+    }
+
+    /**
+     * Associate the model instance to the given parent.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function associate($model)
+    {
+        if ($model instanceof Model) {
+            $foreignKey = $this->ownerKey && $model->{$this->ownerKey}
+                            ? $this->ownerKey
+                            : $model->getKeyName();
+        }
+
+        $this->parent->setAttribute(
+            $this->foreignKey, $model instanceof Model ? $model->{$foreignKey} : null
+        );
+
+        $this->parent->setAttribute(
+            $this->morphType, $model instanceof Model ? $model->getMorphClass() : null
+        );
+
+        return $this->parent->setRelation($this->relationName, $model);
+    }
+
+    /**
+     * Dissociate previously associated model from the given parent.
+     *
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function dissociate()
+    {
+        $this->parent->setAttribute($this->foreignKey, null);
+
+        $this->parent->setAttribute($this->morphType, null);
+
+        return $this->parent->setRelation($this->relationName, null);
+    }
+
+    /**
+     * Touch all of the related models for the relationship.
+     *
+     * @return void
+     */
+    public function touch()
+    {
+        if (! is_null($this->child->{$this->foreignKey})) {
+            parent::touch();
+        }
+    }
+
+    /**
+     * Make a new related instance for the given model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $parent
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    protected function newRelatedInstanceFor(Model $parent)
+    {
+        return $parent->{$this->getRelationName()}()->getRelated()->newInstance();
+    }
+
+    /**
+     * Get the foreign key "type" name.
+     *
+     * @return string
+     */
+    public function getMorphType()
+    {
+        return $this->morphType;
+    }
+
+    /**
+     * Get the dictionary used by the relationship.
+     *
+     * @return array
+     */
+    public function getDictionary()
+    {
+        return $this->dictionary;
+    }
+
+    /**
+     * Specify which relations to load for a given morph type.
+     *
+     * @param  array  $with
+     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
+     */
+    public function morphWith(array $with)
+    {
+        $this->morphableEagerLoads = array_merge(
+            $this->morphableEagerLoads, $with
+        );
+
+        return $this;
+    }
+
+    /**
+     * Specify which relationship counts to load for a given morph type.
+     *
+     * @param  array  $withCount
+     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
+     */
+    public function morphWithCount(array $withCount)
+    {
+        $this->morphableEagerLoadCounts = array_merge(
+            $this->morphableEagerLoadCounts, $withCount
+        );
+
+        return $this;
+    }
+
+    /**
+     * Specify constraints on the query for a given morph type.
+     *
+     * @param  array  $callbacks
+     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
+     */
+    public function constrain(array $callbacks)
+    {
+        $this->morphableConstraints = array_merge(
+            $this->morphableConstraints, $callbacks
+        );
+
+        return $this;
+    }
+
+    /**
+     * Replay stored macro calls on the actual related instance.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function replayMacros(Builder $query)
+    {
+        foreach ($this->macroBuffer as $macro) {
+            $query->{$macro['method']}(...$macro['parameters']);
+        }
+
+        return $query;
+    }
+
+    /**
+     * Handle dynamic method calls to the relationship.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        try {
+            $result = parent::__call($method, $parameters);
+
+            if (in_array($method, ['select', 'selectRaw', 'selectSub', 'addSelect', 'withoutGlobalScopes'])) {
+                $this->macroBuffer[] = compact('method', 'parameters');
+            }
+
+            return $result;
+        }
+
+        // If we tried to call a method that does not exist on the parent Builder instance,
+        // we'll assume that we want to call a query macro (e.g. withTrashed) that only
+        // exists on related models. We will just store the call and replay it later.
+        catch (BadMethodCallException $e) {
+            $this->macroBuffer[] = compact('method', 'parameters');
+
+            return $this;
+        }
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/MorphToMany.php b/vendor/illuminate/database/Eloquent/Relations/MorphToMany.php
new file mode 100644
index 0000000..c2d5745
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/MorphToMany.php
@@ -0,0 +1,209 @@
+inverse = $inverse;
+        $this->morphType = $name.'_type';
+        $this->morphClass = $inverse ? $query->getModel()->getMorphClass() : $parent->getMorphClass();
+
+        parent::__construct(
+            $query, $parent, $table, $foreignPivotKey,
+            $relatedPivotKey, $parentKey, $relatedKey, $relationName
+        );
+    }
+
+    /**
+     * Set the where clause for the relation query.
+     *
+     * @return $this
+     */
+    protected function addWhereConstraints()
+    {
+        parent::addWhereConstraints();
+
+        $this->query->where($this->qualifyPivotColumn($this->morphType), $this->morphClass);
+
+        return $this;
+    }
+
+    /**
+     * Set the constraints for an eager load of the relation.
+     *
+     * @param  array  $models
+     * @return void
+     */
+    public function addEagerConstraints(array $models)
+    {
+        parent::addEagerConstraints($models);
+
+        $this->query->where($this->qualifyPivotColumn($this->morphType), $this->morphClass);
+    }
+
+    /**
+     * Create a new pivot attachment record.
+     *
+     * @param  int  $id
+     * @param  bool  $timed
+     * @return array
+     */
+    protected function baseAttachRecord($id, $timed)
+    {
+        return Arr::add(
+            parent::baseAttachRecord($id, $timed), $this->morphType, $this->morphClass
+        );
+    }
+
+    /**
+     * Add the constraints for a relationship count query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        return parent::getRelationExistenceQuery($query, $parentQuery, $columns)->where(
+            $this->qualifyPivotColumn($this->morphType), $this->morphClass
+        );
+    }
+
+    /**
+     * Get the pivot models that are currently attached.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    protected function getCurrentlyAttachedPivots()
+    {
+        return parent::getCurrentlyAttachedPivots()->map(function ($record) {
+            return $record instanceof MorphPivot
+                            ? $record->setMorphType($this->morphType)
+                                     ->setMorphClass($this->morphClass)
+                            : $record;
+        });
+    }
+
+    /**
+     * Create a new query builder for the pivot table.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function newPivotQuery()
+    {
+        return parent::newPivotQuery()->where($this->morphType, $this->morphClass);
+    }
+
+    /**
+     * Create a new pivot model instance.
+     *
+     * @param  array  $attributes
+     * @param  bool  $exists
+     * @return \Illuminate\Database\Eloquent\Relations\Pivot
+     */
+    public function newPivot(array $attributes = [], $exists = false)
+    {
+        $using = $this->using;
+
+        $pivot = $using ? $using::fromRawAttributes($this->parent, $attributes, $this->table, $exists)
+                        : MorphPivot::fromAttributes($this->parent, $attributes, $this->table, $exists);
+
+        $pivot->setPivotKeys($this->foreignPivotKey, $this->relatedPivotKey)
+              ->setMorphType($this->morphType)
+              ->setMorphClass($this->morphClass);
+
+        return $pivot;
+    }
+
+    /**
+     * Get the pivot columns for the relation.
+     *
+     * "pivot_" is prefixed at each column for easy removal later.
+     *
+     * @return array
+     */
+    protected function aliasedPivotColumns()
+    {
+        $defaults = [$this->foreignPivotKey, $this->relatedPivotKey, $this->morphType];
+
+        return collect(array_merge($defaults, $this->pivotColumns))->map(function ($column) {
+            return $this->qualifyPivotColumn($column).' as pivot_'.$column;
+        })->unique()->all();
+    }
+
+    /**
+     * Get the foreign key "type" name.
+     *
+     * @return string
+     */
+    public function getMorphType()
+    {
+        return $this->morphType;
+    }
+
+    /**
+     * Get the class name of the parent model.
+     *
+     * @return string
+     */
+    public function getMorphClass()
+    {
+        return $this->morphClass;
+    }
+
+    /**
+     * Get the indicator for a reverse relationship.
+     *
+     * @return bool
+     */
+    public function getInverse()
+    {
+        return $this->inverse;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Relations/Pivot.php b/vendor/illuminate/database/Eloquent/Relations/Pivot.php
new file mode 100755
index 0000000..a65ecde
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Relations/Pivot.php
@@ -0,0 +1,25 @@
+query = $query;
+        $this->parent = $parent;
+        $this->related = $query->getModel();
+
+        $this->addConstraints();
+    }
+
+    /**
+     * Run a callback with constraints disabled on the relation.
+     *
+     * @param  \Closure  $callback
+     * @return mixed
+     */
+    public static function noConstraints(Closure $callback)
+    {
+        $previous = static::$constraints;
+
+        static::$constraints = false;
+
+        // When resetting the relation where clause, we want to shift the first element
+        // off of the bindings, leaving only the constraints that the developers put
+        // as "extra" on the relationships, and not original relation constraints.
+        try {
+            return $callback();
+        } finally {
+            static::$constraints = $previous;
+        }
+    }
+
+    /**
+     * Set the base constraints on the relation query.
+     *
+     * @return void
+     */
+    abstract public function addConstraints();
+
+    /**
+     * Set the constraints for an eager load of the relation.
+     *
+     * @param  array  $models
+     * @return void
+     */
+    abstract public function addEagerConstraints(array $models);
+
+    /**
+     * Initialize the relation on a set of models.
+     *
+     * @param  array  $models
+     * @param  string  $relation
+     * @return array
+     */
+    abstract public function initRelation(array $models, $relation);
+
+    /**
+     * Match the eagerly loaded results to their parents.
+     *
+     * @param  array  $models
+     * @param  \Illuminate\Database\Eloquent\Collection  $results
+     * @param  string  $relation
+     * @return array
+     */
+    abstract public function match(array $models, Collection $results, $relation);
+
+    /**
+     * Get the results of the relationship.
+     *
+     * @return mixed
+     */
+    abstract public function getResults();
+
+    /**
+     * Get the relationship for eager loading.
+     *
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function getEager()
+    {
+        return $this->get();
+    }
+
+    /**
+     * Execute the query and get the first result if it's the sole matching record.
+     *
+     * @param  array|string  $columns
+     * @return \Illuminate\Database\Eloquent\Model
+     *
+     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
+     * @throws \Illuminate\Database\MultipleRecordsFoundException
+     */
+    public function sole($columns = ['*'])
+    {
+        $result = $this->take(2)->get($columns);
+
+        $count = $result->count();
+
+        if ($count === 0) {
+            throw (new ModelNotFoundException)->setModel(get_class($this->related));
+        }
+
+        if ($count > 1) {
+            throw new MultipleRecordsFoundException($count);
+        }
+
+        return $result->first();
+    }
+
+    /**
+     * Execute the query as a "select" statement.
+     *
+     * @param  array  $columns
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function get($columns = ['*'])
+    {
+        return $this->query->get($columns);
+    }
+
+    /**
+     * Touch all of the related models for the relationship.
+     *
+     * @return void
+     */
+    public function touch()
+    {
+        $model = $this->getRelated();
+
+        if (! $model::isIgnoringTouch()) {
+            $this->rawUpdate([
+                $model->getUpdatedAtColumn() => $model->freshTimestampString(),
+            ]);
+        }
+    }
+
+    /**
+     * Run a raw update against the base query.
+     *
+     * @param  array  $attributes
+     * @return int
+     */
+    public function rawUpdate(array $attributes = [])
+    {
+        return $this->query->withoutGlobalScopes()->update($attributes);
+    }
+
+    /**
+     * Add the constraints for a relationship count query.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceCountQuery(Builder $query, Builder $parentQuery)
+    {
+        return $this->getRelationExistenceQuery(
+            $query, $parentQuery, new Expression('count(*)')
+        )->setBindings([], 'select');
+    }
+
+    /**
+     * Add the constraints for an internal relationship existence query.
+     *
+     * Essentially, these queries compare on column names like whereColumn.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
+    {
+        return $query->select($columns)->whereColumn(
+            $this->getQualifiedParentKeyName(), '=', $this->getExistenceCompareKey()
+        );
+    }
+
+    /**
+     * Get a relationship join table hash.
+     *
+     * @param  bool  $incrementJoinCount
+     * @return string
+     */
+    public function getRelationCountHash($incrementJoinCount = true)
+    {
+        return 'laravel_reserved_'.($incrementJoinCount ? static::$selfJoinCount++ : static::$selfJoinCount);
+    }
+
+    /**
+     * Get all of the primary keys for an array of models.
+     *
+     * @param  array  $models
+     * @param  string|null  $key
+     * @return array
+     */
+    protected function getKeys(array $models, $key = null)
+    {
+        return collect($models)->map(function ($value) use ($key) {
+            return $key ? $value->getAttribute($key) : $value->getKey();
+        })->values()->unique(null, true)->sort()->all();
+    }
+
+    /**
+     * Get the query builder that will contain the relationship constraints.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    protected function getRelationQuery()
+    {
+        return $this->query;
+    }
+
+    /**
+     * Get the underlying query for the relation.
+     *
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getQuery()
+    {
+        return $this->query;
+    }
+
+    /**
+     * Get the base query builder driving the Eloquent builder.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function getBaseQuery()
+    {
+        return $this->query->getQuery();
+    }
+
+    /**
+     * Get a base query builder instance.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function toBase()
+    {
+        return $this->query->toBase();
+    }
+
+    /**
+     * Get the parent model of the relation.
+     *
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Get the fully qualified parent key name.
+     *
+     * @return string
+     */
+    public function getQualifiedParentKeyName()
+    {
+        return $this->parent->getQualifiedKeyName();
+    }
+
+    /**
+     * Get the related model of the relation.
+     *
+     * @return \Illuminate\Database\Eloquent\Model
+     */
+    public function getRelated()
+    {
+        return $this->related;
+    }
+
+    /**
+     * Get the name of the "created at" column.
+     *
+     * @return string
+     */
+    public function createdAt()
+    {
+        return $this->parent->getCreatedAtColumn();
+    }
+
+    /**
+     * Get the name of the "updated at" column.
+     *
+     * @return string
+     */
+    public function updatedAt()
+    {
+        return $this->parent->getUpdatedAtColumn();
+    }
+
+    /**
+     * Get the name of the related model's "updated at" column.
+     *
+     * @return string
+     */
+    public function relatedUpdatedAt()
+    {
+        return $this->related->getUpdatedAtColumn();
+    }
+
+    /**
+     * Get the name of the "where in" method for eager loading.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @param  string  $key
+     * @return string
+     */
+    protected function whereInMethod(Model $model, $key)
+    {
+        return $model->getKeyName() === last(explode('.', $key))
+                    && in_array($model->getKeyType(), ['int', 'integer'])
+                        ? 'whereIntegerInRaw'
+                        : 'whereIn';
+    }
+
+    /**
+     * Prevent polymorphic relationships from being used without model mappings.
+     *
+     * @param  bool  $requireMorphMap
+     * @return void
+     */
+    public static function requireMorphMap($requireMorphMap = true)
+    {
+        static::$requireMorphMap = $requireMorphMap;
+    }
+
+    /**
+     * Determine if polymorphic relationships require explicit model mapping.
+     *
+     * @return bool
+     */
+    public static function requiresMorphMap()
+    {
+        return static::$requireMorphMap;
+    }
+
+    /**
+     * Define the morph map for polymorphic relations and require all morphed models to be explicitly mapped.
+     *
+     * @param  array  $map
+     * @param  bool  $merge
+     * @return array
+     */
+    public static function enforceMorphMap(array $map, $merge = true)
+    {
+        static::requireMorphMap();
+
+        return static::morphMap($map, $merge);
+    }
+
+    /**
+     * Set or get the morph map for polymorphic relations.
+     *
+     * @param  array|null  $map
+     * @param  bool  $merge
+     * @return array
+     */
+    public static function morphMap(array $map = null, $merge = true)
+    {
+        $map = static::buildMorphMapFromModels($map);
+
+        if (is_array($map)) {
+            static::$morphMap = $merge && static::$morphMap
+                            ? $map + static::$morphMap : $map;
+        }
+
+        return static::$morphMap;
+    }
+
+    /**
+     * Builds a table-keyed array from model class names.
+     *
+     * @param  string[]|null  $models
+     * @return array|null
+     */
+    protected static function buildMorphMapFromModels(array $models = null)
+    {
+        if (is_null($models) || Arr::isAssoc($models)) {
+            return $models;
+        }
+
+        return array_combine(array_map(function ($model) {
+            return (new $model)->getTable();
+        }, $models), $models);
+    }
+
+    /**
+     * Get the model associated with a custom polymorphic type.
+     *
+     * @param  string  $alias
+     * @return string|null
+     */
+    public static function getMorphedModel($alias)
+    {
+        return static::$morphMap[$alias] ?? null;
+    }
+
+    /**
+     * Handle dynamic method calls to the relationship.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        if (static::hasMacro($method)) {
+            return $this->macroCall($method, $parameters);
+        }
+
+        return $this->forwardDecoratedCallTo($this->query, $method, $parameters);
+    }
+
+    /**
+     * Force a clone of the underlying query builder when cloning.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        $this->query = clone $this->query;
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/Scope.php b/vendor/illuminate/database/Eloquent/Scope.php
new file mode 100644
index 0000000..63cba6a
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/Scope.php
@@ -0,0 +1,15 @@
+casts[$this->getDeletedAtColumn()])) {
+            $this->casts[$this->getDeletedAtColumn()] = 'datetime';
+        }
+    }
+
+    /**
+     * Force a hard delete on a soft deleted model.
+     *
+     * @return bool|null
+     */
+    public function forceDelete()
+    {
+        if ($this->fireModelEvent('forceDeleting') === false) {
+            return false;
+        }
+
+        $this->forceDeleting = true;
+
+        return tap($this->delete(), function ($deleted) {
+            $this->forceDeleting = false;
+
+            if ($deleted) {
+                $this->fireModelEvent('forceDeleted', false);
+            }
+        });
+    }
+
+    /**
+     * Force a hard delete on a soft deleted model without raising any events.
+     *
+     * @return bool|null
+     */
+    public function forceDeleteQuietly()
+    {
+        return static::withoutEvents(fn () => $this->forceDelete());
+    }
+
+    /**
+     * Perform the actual delete query on this model instance.
+     *
+     * @return mixed
+     */
+    protected function performDeleteOnModel()
+    {
+        if ($this->forceDeleting) {
+            return tap($this->setKeysForSaveQuery($this->newModelQuery())->forceDelete(), function () {
+                $this->exists = false;
+            });
+        }
+
+        return $this->runSoftDelete();
+    }
+
+    /**
+     * Perform the actual delete query on this model instance.
+     *
+     * @return void
+     */
+    protected function runSoftDelete()
+    {
+        $query = $this->setKeysForSaveQuery($this->newModelQuery());
+
+        $time = $this->freshTimestamp();
+
+        $columns = [$this->getDeletedAtColumn() => $this->fromDateTime($time)];
+
+        $this->{$this->getDeletedAtColumn()} = $time;
+
+        if ($this->usesTimestamps() && ! is_null($this->getUpdatedAtColumn())) {
+            $this->{$this->getUpdatedAtColumn()} = $time;
+
+            $columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time);
+        }
+
+        $query->update($columns);
+
+        $this->syncOriginalAttributes(array_keys($columns));
+
+        $this->fireModelEvent('trashed', false);
+    }
+
+    /**
+     * Restore a soft-deleted model instance.
+     *
+     * @return bool
+     */
+    public function restore()
+    {
+        // If the restoring event does not return false, we will proceed with this
+        // restore operation. Otherwise, we bail out so the developer will stop
+        // the restore totally. We will clear the deleted timestamp and save.
+        if ($this->fireModelEvent('restoring') === false) {
+            return false;
+        }
+
+        $this->{$this->getDeletedAtColumn()} = null;
+
+        // Once we have saved the model, we will fire the "restored" event so this
+        // developer will do anything they need to after a restore operation is
+        // totally finished. Then we will return the result of the save call.
+        $this->exists = true;
+
+        $result = $this->save();
+
+        $this->fireModelEvent('restored', false);
+
+        return $result;
+    }
+
+    /**
+     * Restore a soft-deleted model instance without raising any events.
+     *
+     * @return bool
+     */
+    public function restoreQuietly()
+    {
+        return static::withoutEvents(fn () => $this->restore());
+    }
+
+    /**
+     * Determine if the model instance has been soft-deleted.
+     *
+     * @return bool
+     */
+    public function trashed()
+    {
+        return ! is_null($this->{$this->getDeletedAtColumn()});
+    }
+
+    /**
+     * Register a "softDeleted" model event callback with the dispatcher.
+     *
+     * @param  \Closure|string  $callback
+     * @return void
+     */
+    public static function softDeleted($callback)
+    {
+        static::registerModelEvent('trashed', $callback);
+    }
+
+    /**
+     * Register a "restoring" model event callback with the dispatcher.
+     *
+     * @param  \Closure|string  $callback
+     * @return void
+     */
+    public static function restoring($callback)
+    {
+        static::registerModelEvent('restoring', $callback);
+    }
+
+    /**
+     * Register a "restored" model event callback with the dispatcher.
+     *
+     * @param  \Closure|string  $callback
+     * @return void
+     */
+    public static function restored($callback)
+    {
+        static::registerModelEvent('restored', $callback);
+    }
+
+    /**
+     * Register a "forceDeleting" model event callback with the dispatcher.
+     *
+     * @param  \Closure|string  $callback
+     * @return void
+     */
+    public static function forceDeleting($callback)
+    {
+        static::registerModelEvent('forceDeleting', $callback);
+    }
+
+    /**
+     * Register a "forceDeleted" model event callback with the dispatcher.
+     *
+     * @param  \Closure|string  $callback
+     * @return void
+     */
+    public static function forceDeleted($callback)
+    {
+        static::registerModelEvent('forceDeleted', $callback);
+    }
+
+    /**
+     * Determine if the model is currently force deleting.
+     *
+     * @return bool
+     */
+    public function isForceDeleting()
+    {
+        return $this->forceDeleting;
+    }
+
+    /**
+     * Get the name of the "deleted at" column.
+     *
+     * @return string
+     */
+    public function getDeletedAtColumn()
+    {
+        return defined(static::class.'::DELETED_AT') ? static::DELETED_AT : 'deleted_at';
+    }
+
+    /**
+     * Get the fully qualified "deleted at" column.
+     *
+     * @return string
+     */
+    public function getQualifiedDeletedAtColumn()
+    {
+        return $this->qualifyColumn($this->getDeletedAtColumn());
+    }
+}
diff --git a/vendor/illuminate/database/Eloquent/SoftDeletingScope.php b/vendor/illuminate/database/Eloquent/SoftDeletingScope.php
new file mode 100644
index 0000000..e6d91d9
--- /dev/null
+++ b/vendor/illuminate/database/Eloquent/SoftDeletingScope.php
@@ -0,0 +1,148 @@
+whereNull($model->getQualifiedDeletedAtColumn());
+    }
+
+    /**
+     * Extend the query builder with the needed functions.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @return void
+     */
+    public function extend(Builder $builder)
+    {
+        foreach ($this->extensions as $extension) {
+            $this->{"add{$extension}"}($builder);
+        }
+
+        $builder->onDelete(function (Builder $builder) {
+            $column = $this->getDeletedAtColumn($builder);
+
+            return $builder->update([
+                $column => $builder->getModel()->freshTimestampString(),
+            ]);
+        });
+    }
+
+    /**
+     * Get the "deleted at" column for the builder.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @return string
+     */
+    protected function getDeletedAtColumn(Builder $builder)
+    {
+        if (count((array) $builder->getQuery()->joins) > 0) {
+            return $builder->getModel()->getQualifiedDeletedAtColumn();
+        }
+
+        return $builder->getModel()->getDeletedAtColumn();
+    }
+
+    /**
+     * Add the restore extension to the builder.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @return void
+     */
+    protected function addRestore(Builder $builder)
+    {
+        $builder->macro('restore', function (Builder $builder) {
+            $builder->withTrashed();
+
+            return $builder->update([$builder->getModel()->getDeletedAtColumn() => null]);
+        });
+    }
+
+    /**
+     * Add the restore-or-create extension to the builder.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @return void
+     */
+    protected function addRestoreOrCreate(Builder $builder)
+    {
+        $builder->macro('restoreOrCreate', function (Builder $builder, array $attributes = [], array $values = []) {
+            $builder->withTrashed();
+
+            return tap($builder->firstOrCreate($attributes, $values), function ($instance) {
+                $instance->restore();
+            });
+        });
+    }
+
+    /**
+     * Add the with-trashed extension to the builder.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @return void
+     */
+    protected function addWithTrashed(Builder $builder)
+    {
+        $builder->macro('withTrashed', function (Builder $builder, $withTrashed = true) {
+            if (! $withTrashed) {
+                return $builder->withoutTrashed();
+            }
+
+            return $builder->withoutGlobalScope($this);
+        });
+    }
+
+    /**
+     * Add the without-trashed extension to the builder.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @return void
+     */
+    protected function addWithoutTrashed(Builder $builder)
+    {
+        $builder->macro('withoutTrashed', function (Builder $builder) {
+            $model = $builder->getModel();
+
+            $builder->withoutGlobalScope($this)->whereNull(
+                $model->getQualifiedDeletedAtColumn()
+            );
+
+            return $builder;
+        });
+    }
+
+    /**
+     * Add the only-trashed extension to the builder.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @return void
+     */
+    protected function addOnlyTrashed(Builder $builder)
+    {
+        $builder->macro('onlyTrashed', function (Builder $builder) {
+            $model = $builder->getModel();
+
+            $builder->withoutGlobalScope($this)->whereNotNull(
+                $model->getQualifiedDeletedAtColumn()
+            );
+
+            return $builder;
+        });
+    }
+}
diff --git a/vendor/illuminate/database/Events/ConnectionEstablished.php b/vendor/illuminate/database/Events/ConnectionEstablished.php
new file mode 100644
index 0000000..22a45b8
--- /dev/null
+++ b/vendor/illuminate/database/Events/ConnectionEstablished.php
@@ -0,0 +1,8 @@
+connection = $connection;
+        $this->connectionName = $connection->getName();
+    }
+}
diff --git a/vendor/illuminate/database/Events/DatabaseBusy.php b/vendor/illuminate/database/Events/DatabaseBusy.php
new file mode 100644
index 0000000..8e903a9
--- /dev/null
+++ b/vendor/illuminate/database/Events/DatabaseBusy.php
@@ -0,0 +1,32 @@
+connectionName = $connectionName;
+        $this->connections = $connections;
+    }
+}
diff --git a/vendor/illuminate/database/Events/DatabaseRefreshed.php b/vendor/illuminate/database/Events/DatabaseRefreshed.php
new file mode 100644
index 0000000..5b1fb45
--- /dev/null
+++ b/vendor/illuminate/database/Events/DatabaseRefreshed.php
@@ -0,0 +1,10 @@
+method = $method;
+        $this->migration = $migration;
+    }
+}
diff --git a/vendor/illuminate/database/Events/MigrationStarted.php b/vendor/illuminate/database/Events/MigrationStarted.php
new file mode 100644
index 0000000..3f206b4
--- /dev/null
+++ b/vendor/illuminate/database/Events/MigrationStarted.php
@@ -0,0 +1,8 @@
+method = $method;
+    }
+}
diff --git a/vendor/illuminate/database/Events/MigrationsStarted.php b/vendor/illuminate/database/Events/MigrationsStarted.php
new file mode 100644
index 0000000..5283b49
--- /dev/null
+++ b/vendor/illuminate/database/Events/MigrationsStarted.php
@@ -0,0 +1,8 @@
+model = $model;
+        $this->count = $count;
+    }
+}
diff --git a/vendor/illuminate/database/Events/NoPendingMigrations.php b/vendor/illuminate/database/Events/NoPendingMigrations.php
new file mode 100644
index 0000000..100f786
--- /dev/null
+++ b/vendor/illuminate/database/Events/NoPendingMigrations.php
@@ -0,0 +1,24 @@
+method = $method;
+    }
+}
diff --git a/vendor/illuminate/database/Events/QueryExecuted.php b/vendor/illuminate/database/Events/QueryExecuted.php
new file mode 100644
index 0000000..833a21e
--- /dev/null
+++ b/vendor/illuminate/database/Events/QueryExecuted.php
@@ -0,0 +1,59 @@
+sql = $sql;
+        $this->time = $time;
+        $this->bindings = $bindings;
+        $this->connection = $connection;
+        $this->connectionName = $connection->getName();
+    }
+}
diff --git a/vendor/illuminate/database/Events/SchemaDumped.php b/vendor/illuminate/database/Events/SchemaDumped.php
new file mode 100644
index 0000000..1cbbfff
--- /dev/null
+++ b/vendor/illuminate/database/Events/SchemaDumped.php
@@ -0,0 +1,41 @@
+connection = $connection;
+        $this->connectionName = $connection->getName();
+        $this->path = $path;
+    }
+}
diff --git a/vendor/illuminate/database/Events/SchemaLoaded.php b/vendor/illuminate/database/Events/SchemaLoaded.php
new file mode 100644
index 0000000..061a079
--- /dev/null
+++ b/vendor/illuminate/database/Events/SchemaLoaded.php
@@ -0,0 +1,41 @@
+connection = $connection;
+        $this->connectionName = $connection->getName();
+        $this->path = $path;
+    }
+}
diff --git a/vendor/illuminate/database/Events/StatementPrepared.php b/vendor/illuminate/database/Events/StatementPrepared.php
new file mode 100644
index 0000000..2f60323
--- /dev/null
+++ b/vendor/illuminate/database/Events/StatementPrepared.php
@@ -0,0 +1,33 @@
+statement = $statement;
+        $this->connection = $connection;
+    }
+}
diff --git a/vendor/illuminate/database/Events/TransactionBeginning.php b/vendor/illuminate/database/Events/TransactionBeginning.php
new file mode 100644
index 0000000..3287b5c
--- /dev/null
+++ b/vendor/illuminate/database/Events/TransactionBeginning.php
@@ -0,0 +1,8 @@
+isExpression($table)) {
+            return $this->wrap($this->tablePrefix.$table, true);
+        }
+
+        return $this->getValue($table);
+    }
+
+    /**
+     * Wrap a value in keyword identifiers.
+     *
+     * @param  \Illuminate\Database\Query\Expression|string  $value
+     * @param  bool  $prefixAlias
+     * @return string
+     */
+    public function wrap($value, $prefixAlias = false)
+    {
+        if ($this->isExpression($value)) {
+            return $this->getValue($value);
+        }
+
+        // If the value being wrapped has a column alias we will need to separate out
+        // the pieces so we can wrap each of the segments of the expression on its
+        // own, and then join these both back together using the "as" connector.
+        if (stripos($value, ' as ') !== false) {
+            return $this->wrapAliasedValue($value, $prefixAlias);
+        }
+
+        // If the given value is a JSON selector we will wrap it differently than a
+        // traditional value. We will need to split this path and wrap each part
+        // wrapped, etc. Otherwise, we will simply wrap the value as a string.
+        if ($this->isJsonSelector($value)) {
+            return $this->wrapJsonSelector($value);
+        }
+
+        return $this->wrapSegments(explode('.', $value));
+    }
+
+    /**
+     * Wrap a value that has an alias.
+     *
+     * @param  string  $value
+     * @param  bool  $prefixAlias
+     * @return string
+     */
+    protected function wrapAliasedValue($value, $prefixAlias = false)
+    {
+        $segments = preg_split('/\s+as\s+/i', $value);
+
+        // If we are wrapping a table we need to prefix the alias with the table prefix
+        // as well in order to generate proper syntax. If this is a column of course
+        // no prefix is necessary. The condition will be true when from wrapTable.
+        if ($prefixAlias) {
+            $segments[1] = $this->tablePrefix.$segments[1];
+        }
+
+        return $this->wrap($segments[0]).' as '.$this->wrapValue($segments[1]);
+    }
+
+    /**
+     * Wrap the given value segments.
+     *
+     * @param  array  $segments
+     * @return string
+     */
+    protected function wrapSegments($segments)
+    {
+        return collect($segments)->map(function ($segment, $key) use ($segments) {
+            return $key == 0 && count($segments) > 1
+                            ? $this->wrapTable($segment)
+                            : $this->wrapValue($segment);
+        })->implode('.');
+    }
+
+    /**
+     * Wrap a single string in keyword identifiers.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapValue($value)
+    {
+        if ($value !== '*') {
+            return '"'.str_replace('"', '""', $value).'"';
+        }
+
+        return $value;
+    }
+
+    /**
+     * Wrap the given JSON selector.
+     *
+     * @param  string  $value
+     * @return string
+     *
+     * @throws \RuntimeException
+     */
+    protected function wrapJsonSelector($value)
+    {
+        throw new RuntimeException('This database engine does not support JSON operations.');
+    }
+
+    /**
+     * Determine if the given string is a JSON selector.
+     *
+     * @param  string  $value
+     * @return bool
+     */
+    protected function isJsonSelector($value)
+    {
+        return str_contains($value, '->');
+    }
+
+    /**
+     * Convert an array of column names into a delimited string.
+     *
+     * @param  array  $columns
+     * @return string
+     */
+    public function columnize(array $columns)
+    {
+        return implode(', ', array_map([$this, 'wrap'], $columns));
+    }
+
+    /**
+     * Create query parameter place-holders for an array.
+     *
+     * @param  array  $values
+     * @return string
+     */
+    public function parameterize(array $values)
+    {
+        return implode(', ', array_map([$this, 'parameter'], $values));
+    }
+
+    /**
+     * Get the appropriate query parameter place-holder for a value.
+     *
+     * @param  mixed  $value
+     * @return string
+     */
+    public function parameter($value)
+    {
+        return $this->isExpression($value) ? $this->getValue($value) : '?';
+    }
+
+    /**
+     * Quote the given string literal.
+     *
+     * @param  string|array  $value
+     * @return string
+     */
+    public function quoteString($value)
+    {
+        if (is_array($value)) {
+            return implode(', ', array_map([$this, __FUNCTION__], $value));
+        }
+
+        return "'$value'";
+    }
+
+    /**
+     * Determine if the given value is a raw expression.
+     *
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function isExpression($value)
+    {
+        return $value instanceof Expression;
+    }
+
+    /**
+     * Get the value of a raw expression.
+     *
+     * @param  \Illuminate\Database\Query\Expression  $expression
+     * @return mixed
+     */
+    public function getValue($expression)
+    {
+        return $expression->getValue();
+    }
+
+    /**
+     * Get the format for database stored dates.
+     *
+     * @return string
+     */
+    public function getDateFormat()
+    {
+        return 'Y-m-d H:i:s';
+    }
+
+    /**
+     * Get the grammar's table prefix.
+     *
+     * @return string
+     */
+    public function getTablePrefix()
+    {
+        return $this->tablePrefix;
+    }
+
+    /**
+     * Set the grammar's table prefix.
+     *
+     * @param  string  $prefix
+     * @return $this
+     */
+    public function setTablePrefix($prefix)
+    {
+        $this->tablePrefix = $prefix;
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/database/LICENSE.md b/vendor/illuminate/database/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/illuminate/database/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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/illuminate/database/LazyLoadingViolationException.php b/vendor/illuminate/database/LazyLoadingViolationException.php
new file mode 100644
index 0000000..1bcd40c
--- /dev/null
+++ b/vendor/illuminate/database/LazyLoadingViolationException.php
@@ -0,0 +1,39 @@
+model = $class;
+        $this->relation = $relation;
+    }
+}
diff --git a/vendor/illuminate/database/LostConnectionException.php b/vendor/illuminate/database/LostConnectionException.php
new file mode 100644
index 0000000..c8e57e3
--- /dev/null
+++ b/vendor/illuminate/database/LostConnectionException.php
@@ -0,0 +1,10 @@
+ MigrateCommand::class,
+        'MigrateFresh' => FreshCommand::class,
+        'MigrateInstall' => InstallCommand::class,
+        'MigrateRefresh' => RefreshCommand::class,
+        'MigrateReset' => ResetCommand::class,
+        'MigrateRollback' => RollbackCommand::class,
+        'MigrateStatus' => StatusCommand::class,
+        'MigrateMake' => MigrateMakeCommand::class,
+    ];
+
+    /**
+     * Register the service provider.
+     *
+     * @return void
+     */
+    public function register()
+    {
+        $this->registerRepository();
+
+        $this->registerMigrator();
+
+        $this->registerCreator();
+
+        $this->registerCommands($this->commands);
+    }
+
+    /**
+     * Register the migration repository service.
+     *
+     * @return void
+     */
+    protected function registerRepository()
+    {
+        $this->app->singleton('migration.repository', function ($app) {
+            $table = $app['config']['database.migrations'];
+
+            return new DatabaseMigrationRepository($app['db'], $table);
+        });
+    }
+
+    /**
+     * Register the migrator service.
+     *
+     * @return void
+     */
+    protected function registerMigrator()
+    {
+        // The migrator is responsible for actually running and rollback the migration
+        // files in the application. We'll pass in our database connection resolver
+        // so the migrator can resolve any of these connections when it needs to.
+        $this->app->singleton('migrator', function ($app) {
+            $repository = $app['migration.repository'];
+
+            return new Migrator($repository, $app['db'], $app['files'], $app['events']);
+        });
+    }
+
+    /**
+     * Register the migration creator.
+     *
+     * @return void
+     */
+    protected function registerCreator()
+    {
+        $this->app->singleton('migration.creator', function ($app) {
+            return new MigrationCreator($app['files'], $app->basePath('stubs'));
+        });
+    }
+
+    /**
+     * Register the given commands.
+     *
+     * @param  array  $commands
+     * @return void
+     */
+    protected function registerCommands(array $commands)
+    {
+        foreach (array_keys($commands) as $command) {
+            $this->{"register{$command}Command"}();
+        }
+
+        $this->commands(array_values($commands));
+    }
+
+    /**
+     * Register the command.
+     *
+     * @return void
+     */
+    protected function registerMigrateCommand()
+    {
+        $this->app->singleton(MigrateCommand::class, function ($app) {
+            return new MigrateCommand($app['migrator'], $app[Dispatcher::class]);
+        });
+    }
+
+    /**
+     * Register the command.
+     *
+     * @return void
+     */
+    protected function registerMigrateFreshCommand()
+    {
+        $this->app->singleton(FreshCommand::class);
+    }
+
+    /**
+     * Register the command.
+     *
+     * @return void
+     */
+    protected function registerMigrateInstallCommand()
+    {
+        $this->app->singleton(InstallCommand::class, function ($app) {
+            return new InstallCommand($app['migration.repository']);
+        });
+    }
+
+    /**
+     * Register the command.
+     *
+     * @return void
+     */
+    protected function registerMigrateMakeCommand()
+    {
+        $this->app->singleton(MigrateMakeCommand::class, function ($app) {
+            // Once we have the migration creator registered, we will create the command
+            // and inject the creator. The creator is responsible for the actual file
+            // creation of the migrations, and may be extended by these developers.
+            $creator = $app['migration.creator'];
+
+            $composer = $app['composer'];
+
+            return new MigrateMakeCommand($creator, $composer);
+        });
+    }
+
+    /**
+     * Register the command.
+     *
+     * @return void
+     */
+    protected function registerMigrateRefreshCommand()
+    {
+        $this->app->singleton(RefreshCommand::class);
+    }
+
+    /**
+     * Register the command.
+     *
+     * @return void
+     */
+    protected function registerMigrateResetCommand()
+    {
+        $this->app->singleton(ResetCommand::class, function ($app) {
+            return new ResetCommand($app['migrator']);
+        });
+    }
+
+    /**
+     * Register the command.
+     *
+     * @return void
+     */
+    protected function registerMigrateRollbackCommand()
+    {
+        $this->app->singleton(RollbackCommand::class, function ($app) {
+            return new RollbackCommand($app['migrator']);
+        });
+    }
+
+    /**
+     * Register the command.
+     *
+     * @return void
+     */
+    protected function registerMigrateStatusCommand()
+    {
+        $this->app->singleton(StatusCommand::class, function ($app) {
+            return new StatusCommand($app['migrator']);
+        });
+    }
+
+    /**
+     * Get the services provided by the provider.
+     *
+     * @return array
+     */
+    public function provides()
+    {
+        return array_merge([
+            'migrator', 'migration.repository', 'migration.creator',
+        ], array_values($this->commands));
+    }
+}
diff --git a/vendor/illuminate/database/Migrations/DatabaseMigrationRepository.php b/vendor/illuminate/database/Migrations/DatabaseMigrationRepository.php
new file mode 100755
index 0000000..ed4ebfb
--- /dev/null
+++ b/vendor/illuminate/database/Migrations/DatabaseMigrationRepository.php
@@ -0,0 +1,224 @@
+table = $table;
+        $this->resolver = $resolver;
+    }
+
+    /**
+     * Get the completed migrations.
+     *
+     * @return array
+     */
+    public function getRan()
+    {
+        return $this->table()
+                ->orderBy('batch', 'asc')
+                ->orderBy('migration', 'asc')
+                ->pluck('migration')->all();
+    }
+
+    /**
+     * Get the list of migrations.
+     *
+     * @param  int  $steps
+     * @return array
+     */
+    public function getMigrations($steps)
+    {
+        $query = $this->table()->where('batch', '>=', '1');
+
+        return $query->orderBy('batch', 'desc')
+                     ->orderBy('migration', 'desc')
+                     ->take($steps)->get()->all();
+    }
+
+    /**
+     * Get the last migration batch.
+     *
+     * @return array
+     */
+    public function getLast()
+    {
+        $query = $this->table()->where('batch', $this->getLastBatchNumber());
+
+        return $query->orderBy('migration', 'desc')->get()->all();
+    }
+
+    /**
+     * Get the completed migrations with their batch numbers.
+     *
+     * @return array
+     */
+    public function getMigrationBatches()
+    {
+        return $this->table()
+                ->orderBy('batch', 'asc')
+                ->orderBy('migration', 'asc')
+                ->pluck('batch', 'migration')->all();
+    }
+
+    /**
+     * Log that a migration was run.
+     *
+     * @param  string  $file
+     * @param  int  $batch
+     * @return void
+     */
+    public function log($file, $batch)
+    {
+        $record = ['migration' => $file, 'batch' => $batch];
+
+        $this->table()->insert($record);
+    }
+
+    /**
+     * Remove a migration from the log.
+     *
+     * @param  object  $migration
+     * @return void
+     */
+    public function delete($migration)
+    {
+        $this->table()->where('migration', $migration->migration)->delete();
+    }
+
+    /**
+     * Get the next migration batch number.
+     *
+     * @return int
+     */
+    public function getNextBatchNumber()
+    {
+        return $this->getLastBatchNumber() + 1;
+    }
+
+    /**
+     * Get the last migration batch number.
+     *
+     * @return int
+     */
+    public function getLastBatchNumber()
+    {
+        return $this->table()->max('batch');
+    }
+
+    /**
+     * Create the migration repository data store.
+     *
+     * @return void
+     */
+    public function createRepository()
+    {
+        $schema = $this->getConnection()->getSchemaBuilder();
+
+        $schema->create($this->table, function ($table) {
+            // The migrations table is responsible for keeping track of which of the
+            // migrations have actually run for the application. We'll create the
+            // table to hold the migration file's path as well as the batch ID.
+            $table->increments('id');
+            $table->string('migration');
+            $table->integer('batch');
+        });
+    }
+
+    /**
+     * Determine if the migration repository exists.
+     *
+     * @return bool
+     */
+    public function repositoryExists()
+    {
+        $schema = $this->getConnection()->getSchemaBuilder();
+
+        return $schema->hasTable($this->table);
+    }
+
+    /**
+     * Delete the migration repository data store.
+     *
+     * @return void
+     */
+    public function deleteRepository()
+    {
+        $schema = $this->getConnection()->getSchemaBuilder();
+
+        $schema->drop($this->table);
+    }
+
+    /**
+     * Get a query builder for the migration table.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    protected function table()
+    {
+        return $this->getConnection()->table($this->table)->useWritePdo();
+    }
+
+    /**
+     * Get the connection resolver instance.
+     *
+     * @return \Illuminate\Database\ConnectionResolverInterface
+     */
+    public function getConnectionResolver()
+    {
+        return $this->resolver;
+    }
+
+    /**
+     * Resolve the database connection instance.
+     *
+     * @return \Illuminate\Database\Connection
+     */
+    public function getConnection()
+    {
+        return $this->resolver->connection($this->connection);
+    }
+
+    /**
+     * Set the information source to gather data.
+     *
+     * @param  string  $name
+     * @return void
+     */
+    public function setSource($name)
+    {
+        $this->connection = $name;
+    }
+}
diff --git a/vendor/illuminate/database/Migrations/Migration.php b/vendor/illuminate/database/Migrations/Migration.php
new file mode 100755
index 0000000..a58f784
--- /dev/null
+++ b/vendor/illuminate/database/Migrations/Migration.php
@@ -0,0 +1,30 @@
+connection;
+    }
+}
diff --git a/vendor/illuminate/database/Migrations/MigrationCreator.php b/vendor/illuminate/database/Migrations/MigrationCreator.php
new file mode 100755
index 0000000..d8f1ce9
--- /dev/null
+++ b/vendor/illuminate/database/Migrations/MigrationCreator.php
@@ -0,0 +1,231 @@
+files = $files;
+        $this->customStubPath = $customStubPath;
+    }
+
+    /**
+     * Create a new migration at the given path.
+     *
+     * @param  string  $name
+     * @param  string  $path
+     * @param  string|null  $table
+     * @param  bool  $create
+     * @return string
+     *
+     * @throws \Exception
+     */
+    public function create($name, $path, $table = null, $create = false)
+    {
+        $this->ensureMigrationDoesntAlreadyExist($name, $path);
+
+        // First we will get the stub file for the migration, which serves as a type
+        // of template for the migration. Once we have those we will populate the
+        // various place-holders, save the file, and run the post create event.
+        $stub = $this->getStub($table, $create);
+
+        $path = $this->getPath($name, $path);
+
+        $this->files->ensureDirectoryExists(dirname($path));
+
+        $this->files->put(
+            $path, $this->populateStub($stub, $table)
+        );
+
+        // Next, we will fire any hooks that are supposed to fire after a migration is
+        // created. Once that is done we'll be ready to return the full path to the
+        // migration file so it can be used however it's needed by the developer.
+        $this->firePostCreateHooks($table, $path);
+
+        return $path;
+    }
+
+    /**
+     * Ensure that a migration with the given name doesn't already exist.
+     *
+     * @param  string  $name
+     * @param  string  $migrationPath
+     * @return void
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function ensureMigrationDoesntAlreadyExist($name, $migrationPath = null)
+    {
+        if (! empty($migrationPath)) {
+            $migrationFiles = $this->files->glob($migrationPath.'/*.php');
+
+            foreach ($migrationFiles as $migrationFile) {
+                $this->files->requireOnce($migrationFile);
+            }
+        }
+
+        if (class_exists($className = $this->getClassName($name))) {
+            throw new InvalidArgumentException("A {$className} class already exists.");
+        }
+    }
+
+    /**
+     * Get the migration stub file.
+     *
+     * @param  string|null  $table
+     * @param  bool  $create
+     * @return string
+     */
+    protected function getStub($table, $create)
+    {
+        if (is_null($table)) {
+            $stub = $this->files->exists($customPath = $this->customStubPath.'/migration.stub')
+                            ? $customPath
+                            : $this->stubPath().'/migration.stub';
+        } elseif ($create) {
+            $stub = $this->files->exists($customPath = $this->customStubPath.'/migration.create.stub')
+                            ? $customPath
+                            : $this->stubPath().'/migration.create.stub';
+        } else {
+            $stub = $this->files->exists($customPath = $this->customStubPath.'/migration.update.stub')
+                            ? $customPath
+                            : $this->stubPath().'/migration.update.stub';
+        }
+
+        return $this->files->get($stub);
+    }
+
+    /**
+     * Populate the place-holders in the migration stub.
+     *
+     * @param  string  $stub
+     * @param  string|null  $table
+     * @return string
+     */
+    protected function populateStub($stub, $table)
+    {
+        // Here we will replace the table place-holders with the table specified by
+        // the developer, which is useful for quickly creating a tables creation
+        // or update migration from the console instead of typing it manually.
+        if (! is_null($table)) {
+            $stub = str_replace(
+                ['DummyTable', '{{ table }}', '{{table}}'],
+                $table, $stub
+            );
+        }
+
+        return $stub;
+    }
+
+    /**
+     * Get the class name of a migration name.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    protected function getClassName($name)
+    {
+        return Str::studly($name);
+    }
+
+    /**
+     * Get the full path to the migration.
+     *
+     * @param  string  $name
+     * @param  string  $path
+     * @return string
+     */
+    protected function getPath($name, $path)
+    {
+        return $path.'/'.$this->getDatePrefix().'_'.$name.'.php';
+    }
+
+    /**
+     * Fire the registered post create hooks.
+     *
+     * @param  string|null  $table
+     * @param  string  $path
+     * @return void
+     */
+    protected function firePostCreateHooks($table, $path)
+    {
+        foreach ($this->postCreate as $callback) {
+            $callback($table, $path);
+        }
+    }
+
+    /**
+     * Register a post migration create hook.
+     *
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function afterCreate(Closure $callback)
+    {
+        $this->postCreate[] = $callback;
+    }
+
+    /**
+     * Get the date prefix for the migration.
+     *
+     * @return string
+     */
+    protected function getDatePrefix()
+    {
+        return date('Y_m_d_His');
+    }
+
+    /**
+     * Get the path to the stubs.
+     *
+     * @return string
+     */
+    public function stubPath()
+    {
+        return __DIR__.'/stubs';
+    }
+
+    /**
+     * Get the filesystem instance.
+     *
+     * @return \Illuminate\Filesystem\Filesystem
+     */
+    public function getFilesystem()
+    {
+        return $this->files;
+    }
+}
diff --git a/vendor/illuminate/database/Migrations/MigrationRepositoryInterface.php b/vendor/illuminate/database/Migrations/MigrationRepositoryInterface.php
new file mode 100755
index 0000000..840a5e1
--- /dev/null
+++ b/vendor/illuminate/database/Migrations/MigrationRepositoryInterface.php
@@ -0,0 +1,88 @@
+
+     */
+    protected static $requiredPathCache = [];
+
+    /**
+     * The output interface implementation.
+     *
+     * @var \Symfony\Component\Console\Output\OutputInterface
+     */
+    protected $output;
+
+    /**
+     * Create a new migrator instance.
+     *
+     * @param  \Illuminate\Database\Migrations\MigrationRepositoryInterface  $repository
+     * @param  \Illuminate\Database\ConnectionResolverInterface  $resolver
+     * @param  \Illuminate\Filesystem\Filesystem  $files
+     * @param  \Illuminate\Contracts\Events\Dispatcher|null  $dispatcher
+     * @return void
+     */
+    public function __construct(MigrationRepositoryInterface $repository,
+                                Resolver $resolver,
+                                Filesystem $files,
+                                Dispatcher $dispatcher = null)
+    {
+        $this->files = $files;
+        $this->events = $dispatcher;
+        $this->resolver = $resolver;
+        $this->repository = $repository;
+    }
+
+    /**
+     * Run the pending migrations at a given path.
+     *
+     * @param  array|string  $paths
+     * @param  array  $options
+     * @return array
+     */
+    public function run($paths = [], array $options = [])
+    {
+        // Once we grab all of the migration files for the path, we will compare them
+        // against the migrations that have already been run for this package then
+        // run each of the outstanding migrations against a database connection.
+        $files = $this->getMigrationFiles($paths);
+
+        $this->requireFiles($migrations = $this->pendingMigrations(
+            $files, $this->repository->getRan()
+        ));
+
+        // Once we have all these migrations that are outstanding we are ready to run
+        // we will go ahead and run them "up". This will execute each migration as
+        // an operation against a database. Then we'll return this list of them.
+        $this->runPending($migrations, $options);
+
+        return $migrations;
+    }
+
+    /**
+     * Get the migration files that have not yet run.
+     *
+     * @param  array  $files
+     * @param  array  $ran
+     * @return array
+     */
+    protected function pendingMigrations($files, $ran)
+    {
+        return Collection::make($files)
+                ->reject(function ($file) use ($ran) {
+                    return in_array($this->getMigrationName($file), $ran);
+                })->values()->all();
+    }
+
+    /**
+     * Run an array of migrations.
+     *
+     * @param  array  $migrations
+     * @param  array  $options
+     * @return void
+     */
+    public function runPending(array $migrations, array $options = [])
+    {
+        // First we will just make sure that there are any migrations to run. If there
+        // aren't, we will just make a note of it to the developer so they're aware
+        // that all of the migrations have been run against this database system.
+        if (count($migrations) === 0) {
+            $this->fireMigrationEvent(new NoPendingMigrations('up'));
+
+            $this->write(Info::class, 'Nothing to migrate');
+
+            return;
+        }
+
+        // Next, we will get the next batch number for the migrations so we can insert
+        // correct batch number in the database migrations repository when we store
+        // each migration's execution. We will also extract a few of the options.
+        $batch = $this->repository->getNextBatchNumber();
+
+        $pretend = $options['pretend'] ?? false;
+
+        $step = $options['step'] ?? false;
+
+        $this->fireMigrationEvent(new MigrationsStarted('up'));
+
+        $this->write(Info::class, 'Running migrations.');
+
+        // Once we have the array of migrations, we will spin through them and run the
+        // migrations "up" so the changes are made to the databases. We'll then log
+        // that the migration was run so we don't repeat it next time we execute.
+        foreach ($migrations as $file) {
+            $this->runUp($file, $batch, $pretend);
+
+            if ($step) {
+                $batch++;
+            }
+        }
+
+        $this->fireMigrationEvent(new MigrationsEnded('up'));
+
+        if ($this->output) {
+            $this->output->writeln('');
+        }
+    }
+
+    /**
+     * Run "up" a migration instance.
+     *
+     * @param  string  $file
+     * @param  int  $batch
+     * @param  bool  $pretend
+     * @return void
+     */
+    protected function runUp($file, $batch, $pretend)
+    {
+        // First we will resolve a "real" instance of the migration class from this
+        // migration file name. Once we have the instances we can run the actual
+        // command such as "up" or "down", or we can just simulate the action.
+        $migration = $this->resolvePath($file);
+
+        $name = $this->getMigrationName($file);
+
+        if ($pretend) {
+            return $this->pretendToRun($migration, 'up');
+        }
+
+        $this->write(Task::class, $name, fn () => $this->runMigration($migration, 'up'));
+
+        // Once we have run a migrations class, we will log that it was run in this
+        // repository so that we don't try to run it next time we do a migration
+        // in the application. A migration repository keeps the migrate order.
+        $this->repository->log($name, $batch);
+    }
+
+    /**
+     * Rollback the last migration operation.
+     *
+     * @param  array|string  $paths
+     * @param  array  $options
+     * @return array
+     */
+    public function rollback($paths = [], array $options = [])
+    {
+        // We want to pull in the last batch of migrations that ran on the previous
+        // migration operation. We'll then reverse those migrations and run each
+        // of them "down" to reverse the last migration "operation" which ran.
+        $migrations = $this->getMigrationsForRollback($options);
+
+        if (count($migrations) === 0) {
+            $this->fireMigrationEvent(new NoPendingMigrations('down'));
+
+            $this->write(Info::class, 'Nothing to rollback.');
+
+            return [];
+        }
+
+        return tap($this->rollbackMigrations($migrations, $paths, $options), function () {
+            if ($this->output) {
+                $this->output->writeln('');
+            }
+        });
+    }
+
+    /**
+     * Get the migrations for a rollback operation.
+     *
+     * @param  array  $options
+     * @return array
+     */
+    protected function getMigrationsForRollback(array $options)
+    {
+        if (($steps = $options['step'] ?? 0) > 0) {
+            return $this->repository->getMigrations($steps);
+        }
+
+        return $this->repository->getLast();
+    }
+
+    /**
+     * Rollback the given migrations.
+     *
+     * @param  array  $migrations
+     * @param  array|string  $paths
+     * @param  array  $options
+     * @return array
+     */
+    protected function rollbackMigrations(array $migrations, $paths, array $options)
+    {
+        $rolledBack = [];
+
+        $this->requireFiles($files = $this->getMigrationFiles($paths));
+
+        $this->fireMigrationEvent(new MigrationsStarted('down'));
+
+        $this->write(Info::class, 'Rolling back migrations.');
+
+        // Next we will run through all of the migrations and call the "down" method
+        // which will reverse each migration in order. This getLast method on the
+        // repository already returns these migration's names in reverse order.
+        foreach ($migrations as $migration) {
+            $migration = (object) $migration;
+
+            if (! $file = Arr::get($files, $migration->migration)) {
+                $this->write(TwoColumnDetail::class, $migration->migration, 'Migration not found');
+
+                continue;
+            }
+
+            $rolledBack[] = $file;
+
+            $this->runDown(
+                $file, $migration,
+                $options['pretend'] ?? false
+            );
+        }
+
+        $this->fireMigrationEvent(new MigrationsEnded('down'));
+
+        return $rolledBack;
+    }
+
+    /**
+     * Rolls all of the currently applied migrations back.
+     *
+     * @param  array|string  $paths
+     * @param  bool  $pretend
+     * @return array
+     */
+    public function reset($paths = [], $pretend = false)
+    {
+        // Next, we will reverse the migration list so we can run them back in the
+        // correct order for resetting this database. This will allow us to get
+        // the database back into its "empty" state ready for the migrations.
+        $migrations = array_reverse($this->repository->getRan());
+
+        if (count($migrations) === 0) {
+            $this->write(Info::class, 'Nothing to rollback.');
+
+            return [];
+        }
+
+        return tap($this->resetMigrations($migrations, $paths, $pretend), function () {
+            if ($this->output) {
+                $this->output->writeln('');
+            }
+        });
+    }
+
+    /**
+     * Reset the given migrations.
+     *
+     * @param  array  $migrations
+     * @param  array  $paths
+     * @param  bool  $pretend
+     * @return array
+     */
+    protected function resetMigrations(array $migrations, array $paths, $pretend = false)
+    {
+        // Since the getRan method that retrieves the migration name just gives us the
+        // migration name, we will format the names into objects with the name as a
+        // property on the objects so that we can pass it to the rollback method.
+        $migrations = collect($migrations)->map(function ($m) {
+            return (object) ['migration' => $m];
+        })->all();
+
+        return $this->rollbackMigrations(
+            $migrations, $paths, compact('pretend')
+        );
+    }
+
+    /**
+     * Run "down" a migration instance.
+     *
+     * @param  string  $file
+     * @param  object  $migration
+     * @param  bool  $pretend
+     * @return void
+     */
+    protected function runDown($file, $migration, $pretend)
+    {
+        // First we will get the file name of the migration so we can resolve out an
+        // instance of the migration. Once we get an instance we can either run a
+        // pretend execution of the migration or we can run the real migration.
+        $instance = $this->resolvePath($file);
+
+        $name = $this->getMigrationName($file);
+
+        if ($pretend) {
+            return $this->pretendToRun($instance, 'down');
+        }
+
+        $this->write(Task::class, $name, fn () => $this->runMigration($instance, 'down'));
+
+        // Once we have successfully run the migration "down" we will remove it from
+        // the migration repository so it will be considered to have not been run
+        // by the application then will be able to fire by any later operation.
+        $this->repository->delete($migration);
+    }
+
+    /**
+     * Run a migration inside a transaction if the database supports it.
+     *
+     * @param  object  $migration
+     * @param  string  $method
+     * @return void
+     */
+    protected function runMigration($migration, $method)
+    {
+        $connection = $this->resolveConnection(
+            $migration->getConnection()
+        );
+
+        $callback = function () use ($connection, $migration, $method) {
+            if (method_exists($migration, $method)) {
+                $this->fireMigrationEvent(new MigrationStarted($migration, $method));
+
+                $this->runMethod($connection, $migration, $method);
+
+                $this->fireMigrationEvent(new MigrationEnded($migration, $method));
+            }
+        };
+
+        $this->getSchemaGrammar($connection)->supportsSchemaTransactions()
+            && $migration->withinTransaction
+                    ? $connection->transaction($callback)
+                    : $callback();
+    }
+
+    /**
+     * Pretend to run the migrations.
+     *
+     * @param  object  $migration
+     * @param  string  $method
+     * @return void
+     */
+    protected function pretendToRun($migration, $method)
+    {
+        try {
+            $name = get_class($migration);
+
+            $reflectionClass = new ReflectionClass($migration);
+
+            if ($reflectionClass->isAnonymous()) {
+                $name = $this->getMigrationName($reflectionClass->getFileName());
+            }
+
+            $this->write(TwoColumnDetail::class, $name);
+            $this->write(BulletList::class, collect($this->getQueries($migration, $method))->map(function ($query) {
+                return $query['query'];
+            }));
+        } catch (SchemaException $e) {
+            $name = get_class($migration);
+
+            $this->write(Error::class, sprintf(
+                '[%s] failed to dump queries. This may be due to changing database columns using Doctrine, which is not supported while pretending to run migrations.',
+                $name,
+            ));
+        }
+    }
+
+    /**
+     * Get all of the queries that would be run for a migration.
+     *
+     * @param  object  $migration
+     * @param  string  $method
+     * @return array
+     */
+    protected function getQueries($migration, $method)
+    {
+        // Now that we have the connections we can resolve it and pretend to run the
+        // queries against the database returning the array of raw SQL statements
+        // that would get fired against the database system for this migration.
+        $db = $this->resolveConnection(
+            $migration->getConnection()
+        );
+
+        return $db->pretend(function () use ($db, $migration, $method) {
+            if (method_exists($migration, $method)) {
+                $this->runMethod($db, $migration, $method);
+            }
+        });
+    }
+
+    /**
+     * Run a migration method on the given connection.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @param  object  $migration
+     * @param  string  $method
+     * @return void
+     */
+    protected function runMethod($connection, $migration, $method)
+    {
+        $previousConnection = $this->resolver->getDefaultConnection();
+
+        try {
+            $this->resolver->setDefaultConnection($connection->getName());
+
+            $migration->{$method}();
+        } finally {
+            $this->resolver->setDefaultConnection($previousConnection);
+        }
+    }
+
+    /**
+     * Resolve a migration instance from a file.
+     *
+     * @param  string  $file
+     * @return object
+     */
+    public function resolve($file)
+    {
+        $class = $this->getMigrationClass($file);
+
+        return new $class;
+    }
+
+    /**
+     * Resolve a migration instance from a migration path.
+     *
+     * @param  string  $path
+     * @return object
+     */
+    protected function resolvePath(string $path)
+    {
+        $class = $this->getMigrationClass($this->getMigrationName($path));
+
+        if (class_exists($class) && realpath($path) == (new ReflectionClass($class))->getFileName()) {
+            return new $class;
+        }
+
+        $migration = static::$requiredPathCache[$path] ??= $this->files->getRequire($path);
+
+        if (is_object($migration)) {
+            return method_exists($migration, '__construct')
+                    ? $this->files->getRequire($path)
+                    : clone $migration;
+        }
+
+        return new $class;
+    }
+
+    /**
+     * Generate a migration class name based on the migration file name.
+     *
+     * @param  string  $migrationName
+     * @return string
+     */
+    protected function getMigrationClass(string $migrationName): string
+    {
+        return Str::studly(implode('_', array_slice(explode('_', $migrationName), 4)));
+    }
+
+    /**
+     * Get all of the migration files in a given path.
+     *
+     * @param  string|array  $paths
+     * @return array
+     */
+    public function getMigrationFiles($paths)
+    {
+        return Collection::make($paths)->flatMap(function ($path) {
+            return str_ends_with($path, '.php') ? [$path] : $this->files->glob($path.'/*_*.php');
+        })->filter()->values()->keyBy(function ($file) {
+            return $this->getMigrationName($file);
+        })->sortBy(function ($file, $key) {
+            return $key;
+        })->all();
+    }
+
+    /**
+     * Require in all the migration files in a given path.
+     *
+     * @param  array  $files
+     * @return void
+     */
+    public function requireFiles(array $files)
+    {
+        foreach ($files as $file) {
+            $this->files->requireOnce($file);
+        }
+    }
+
+    /**
+     * Get the name of the migration.
+     *
+     * @param  string  $path
+     * @return string
+     */
+    public function getMigrationName($path)
+    {
+        return str_replace('.php', '', basename($path));
+    }
+
+    /**
+     * Register a custom migration path.
+     *
+     * @param  string  $path
+     * @return void
+     */
+    public function path($path)
+    {
+        $this->paths = array_unique(array_merge($this->paths, [$path]));
+    }
+
+    /**
+     * Get all of the custom migration paths.
+     *
+     * @return array
+     */
+    public function paths()
+    {
+        return $this->paths;
+    }
+
+    /**
+     * Get the default connection name.
+     *
+     * @return string
+     */
+    public function getConnection()
+    {
+        return $this->connection;
+    }
+
+    /**
+     * Execute the given callback using the given connection as the default connection.
+     *
+     * @param  string  $name
+     * @param  callable  $callback
+     * @return mixed
+     */
+    public function usingConnection($name, callable $callback)
+    {
+        $previousConnection = $this->resolver->getDefaultConnection();
+
+        $this->setConnection($name);
+
+        return tap($callback(), function () use ($previousConnection) {
+            $this->setConnection($previousConnection);
+        });
+    }
+
+    /**
+     * Set the default connection name.
+     *
+     * @param  string  $name
+     * @return void
+     */
+    public function setConnection($name)
+    {
+        if (! is_null($name)) {
+            $this->resolver->setDefaultConnection($name);
+        }
+
+        $this->repository->setSource($name);
+
+        $this->connection = $name;
+    }
+
+    /**
+     * Resolve the database connection instance.
+     *
+     * @param  string  $connection
+     * @return \Illuminate\Database\Connection
+     */
+    public function resolveConnection($connection)
+    {
+        return $this->resolver->connection($connection ?: $this->connection);
+    }
+
+    /**
+     * Get the schema grammar out of a migration connection.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return \Illuminate\Database\Schema\Grammars\Grammar
+     */
+    protected function getSchemaGrammar($connection)
+    {
+        if (is_null($grammar = $connection->getSchemaGrammar())) {
+            $connection->useDefaultSchemaGrammar();
+
+            $grammar = $connection->getSchemaGrammar();
+        }
+
+        return $grammar;
+    }
+
+    /**
+     * Get the migration repository instance.
+     *
+     * @return \Illuminate\Database\Migrations\MigrationRepositoryInterface
+     */
+    public function getRepository()
+    {
+        return $this->repository;
+    }
+
+    /**
+     * Determine if the migration repository exists.
+     *
+     * @return bool
+     */
+    public function repositoryExists()
+    {
+        return $this->repository->repositoryExists();
+    }
+
+    /**
+     * Determine if any migrations have been run.
+     *
+     * @return bool
+     */
+    public function hasRunAnyMigrations()
+    {
+        return $this->repositoryExists() && count($this->repository->getRan()) > 0;
+    }
+
+    /**
+     * Delete the migration repository data store.
+     *
+     * @return void
+     */
+    public function deleteRepository()
+    {
+        return $this->repository->deleteRepository();
+    }
+
+    /**
+     * Get the file system instance.
+     *
+     * @return \Illuminate\Filesystem\Filesystem
+     */
+    public function getFilesystem()
+    {
+        return $this->files;
+    }
+
+    /**
+     * Set the output implementation that should be used by the console.
+     *
+     * @param  \Symfony\Component\Console\Output\OutputInterface  $output
+     * @return $this
+     */
+    public function setOutput(OutputInterface $output)
+    {
+        $this->output = $output;
+
+        return $this;
+    }
+
+    /**
+     * Write to the console's output.
+     *
+     * @param  string  $component
+     * @param  array|string  ...$arguments
+     * @return void
+     */
+    protected function write($component, ...$arguments)
+    {
+        if ($this->output && class_exists($component)) {
+            (new $component($this->output))->render(...$arguments);
+        } else {
+            foreach ($arguments as $argument) {
+                if (is_callable($argument)) {
+                    $argument();
+                }
+            }
+        }
+    }
+
+    /**
+     * Fire the given event for the migration.
+     *
+     * @param  \Illuminate\Contracts\Database\Events\MigrationEvent  $event
+     * @return void
+     */
+    public function fireMigrationEvent($event)
+    {
+        if ($this->events) {
+            $this->events->dispatch($event);
+        }
+    }
+}
diff --git a/vendor/illuminate/database/Migrations/stubs/migration.create.stub b/vendor/illuminate/database/Migrations/stubs/migration.create.stub
new file mode 100755
index 0000000..0e0ec22
--- /dev/null
+++ b/vendor/illuminate/database/Migrations/stubs/migration.create.stub
@@ -0,0 +1,31 @@
+id();
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('{{ table }}');
+    }
+};
diff --git a/vendor/illuminate/database/Migrations/stubs/migration.stub b/vendor/illuminate/database/Migrations/stubs/migration.stub
new file mode 100755
index 0000000..41dd1c8
--- /dev/null
+++ b/vendor/illuminate/database/Migrations/stubs/migration.stub
@@ -0,0 +1,28 @@
+count = $count;
+
+        parent::__construct("$count records were found.", $code, $previous);
+    }
+
+    /**
+     * Get the number of records found.
+     *
+     * @return int
+     */
+    public function getCount()
+    {
+        return $this->count;
+    }
+}
diff --git a/vendor/illuminate/database/MySqlConnection.php b/vendor/illuminate/database/MySqlConnection.php
new file mode 100755
index 0000000..54e3d47
--- /dev/null
+++ b/vendor/illuminate/database/MySqlConnection.php
@@ -0,0 +1,91 @@
+getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION), 'MariaDB');
+    }
+
+    /**
+     * Get the default query grammar instance.
+     *
+     * @return \Illuminate\Database\Query\Grammars\MySqlGrammar
+     */
+    protected function getDefaultQueryGrammar()
+    {
+        return $this->withTablePrefix(new QueryGrammar);
+    }
+
+    /**
+     * Get a schema builder instance for the connection.
+     *
+     * @return \Illuminate\Database\Schema\MySqlBuilder
+     */
+    public function getSchemaBuilder()
+    {
+        if (is_null($this->schemaGrammar)) {
+            $this->useDefaultSchemaGrammar();
+        }
+
+        return new MySqlBuilder($this);
+    }
+
+    /**
+     * Get the default schema grammar instance.
+     *
+     * @return \Illuminate\Database\Schema\Grammars\MySqlGrammar
+     */
+    protected function getDefaultSchemaGrammar()
+    {
+        return $this->withTablePrefix(new SchemaGrammar);
+    }
+
+    /**
+     * Get the schema state for the connection.
+     *
+     * @param  \Illuminate\Filesystem\Filesystem|null  $files
+     * @param  callable|null  $processFactory
+     * @return \Illuminate\Database\Schema\MySqlSchemaState
+     */
+    public function getSchemaState(Filesystem $files = null, callable $processFactory = null)
+    {
+        return new MySqlSchemaState($this, $files, $processFactory);
+    }
+
+    /**
+     * Get the default post processor instance.
+     *
+     * @return \Illuminate\Database\Query\Processors\MySqlProcessor
+     */
+    protected function getDefaultPostProcessor()
+    {
+        return new MySqlProcessor;
+    }
+
+    /**
+     * Get the Doctrine DBAL driver.
+     *
+     * @return \Illuminate\Database\PDO\MySqlDriver
+     */
+    protected function getDoctrineDriver()
+    {
+        return new MySqlDriver;
+    }
+}
diff --git a/vendor/illuminate/database/PDO/Concerns/ConnectsToDatabase.php b/vendor/illuminate/database/PDO/Concerns/ConnectsToDatabase.php
new file mode 100644
index 0000000..d2a8d60
--- /dev/null
+++ b/vendor/illuminate/database/PDO/Concerns/ConnectsToDatabase.php
@@ -0,0 +1,30 @@
+connection = $connection;
+    }
+
+    /**
+     * Execute an SQL statement.
+     *
+     * @param  string  $statement
+     * @return int
+     */
+    public function exec(string $statement): int
+    {
+        try {
+            $result = $this->connection->exec($statement);
+
+            \assert($result !== false);
+
+            return $result;
+        } catch (PDOException $exception) {
+            throw Exception::new($exception);
+        }
+    }
+
+    /**
+     * Prepare a new SQL statement.
+     *
+     * @param  string  $sql
+     * @return \Doctrine\DBAL\Driver\Statement
+     */
+    public function prepare(string $sql): StatementInterface
+    {
+        try {
+            return $this->createStatement(
+                $this->connection->prepare($sql)
+            );
+        } catch (PDOException $exception) {
+            throw Exception::new($exception);
+        }
+    }
+
+    /**
+     * Execute a new query against the connection.
+     *
+     * @param  string  $sql
+     * @return \Doctrine\DBAL\Driver\Result
+     */
+    public function query(string $sql): ResultInterface
+    {
+        try {
+            $stmt = $this->connection->query($sql);
+
+            \assert($stmt instanceof PDOStatement);
+
+            return new Result($stmt);
+        } catch (PDOException $exception) {
+            throw Exception::new($exception);
+        }
+    }
+
+    /**
+     * Get the last insert ID.
+     *
+     * @param  string|null  $name
+     * @return mixed
+     */
+    public function lastInsertId($name = null)
+    {
+        try {
+            if ($name === null) {
+                return $this->connection->lastInsertId();
+            }
+
+            return $this->connection->lastInsertId($name);
+        } catch (PDOException $exception) {
+            throw Exception::new($exception);
+        }
+    }
+
+    /**
+     * Create a new statement instance.
+     *
+     * @param  \PDOStatement  $stmt
+     * @return \Doctrine\DBAL\Driver\PDO\Statement
+     */
+    protected function createStatement(PDOStatement $stmt): Statement
+    {
+        return new Statement($stmt);
+    }
+
+    /**
+     * Begin a new database transaction.
+     *
+     * @return void
+     */
+    public function beginTransaction()
+    {
+        return $this->connection->beginTransaction();
+    }
+
+    /**
+     * Commit a database transaction.
+     *
+     * @return void
+     */
+    public function commit()
+    {
+        return $this->connection->commit();
+    }
+
+    /**
+     * Rollback a database transaction.
+     *
+     * @return void
+     */
+    public function rollBack()
+    {
+        return $this->connection->rollBack();
+    }
+
+    /**
+     * Wrap quotes around the given input.
+     *
+     * @param  string  $input
+     * @param  string  $type
+     * @return string
+     */
+    public function quote($input, $type = ParameterType::STRING)
+    {
+        return $this->connection->quote($input, $type);
+    }
+
+    /**
+     * Get the server version for the connection.
+     *
+     * @return string
+     */
+    public function getServerVersion()
+    {
+        return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION);
+    }
+
+    /**
+     * Get the wrapped PDO connection.
+     *
+     * @return \PDO
+     */
+    public function getWrappedConnection(): PDO
+    {
+        return $this->connection;
+    }
+}
diff --git a/vendor/illuminate/database/PDO/MySqlDriver.php b/vendor/illuminate/database/PDO/MySqlDriver.php
new file mode 100644
index 0000000..54ac375
--- /dev/null
+++ b/vendor/illuminate/database/PDO/MySqlDriver.php
@@ -0,0 +1,19 @@
+connection = $connection;
+    }
+
+    /**
+     * Prepare a new SQL statement.
+     *
+     * @param  string  $sql
+     * @return \Doctrine\DBAL\Driver\Statement
+     */
+    public function prepare(string $sql): StatementInterface
+    {
+        return new Statement(
+            $this->connection->prepare($sql)
+        );
+    }
+
+    /**
+     * Execute a new query against the connection.
+     *
+     * @param  string  $sql
+     * @return \Doctrine\DBAL\Driver\Result
+     */
+    public function query(string $sql): Result
+    {
+        return $this->connection->query($sql);
+    }
+
+    /**
+     * Execute an SQL statement.
+     *
+     * @param  string  $statement
+     * @return int
+     */
+    public function exec(string $statement): int
+    {
+        return $this->connection->exec($statement);
+    }
+
+    /**
+     * Get the last insert ID.
+     *
+     * @param  string|null  $name
+     * @return mixed
+     */
+    public function lastInsertId($name = null)
+    {
+        if ($name === null) {
+            return $this->connection->lastInsertId($name);
+        }
+
+        return $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?')
+            ->execute([$name])
+            ->fetchOne();
+    }
+
+    /**
+     * Begin a new database transaction.
+     *
+     * @return void
+     */
+    public function beginTransaction()
+    {
+        return $this->connection->beginTransaction();
+    }
+
+    /**
+     * Commit a database transaction.
+     *
+     * @return void
+     */
+    public function commit()
+    {
+        return $this->connection->commit();
+    }
+
+    /**
+     * Rollback a database transaction.
+     *
+     * @return void
+     */
+    public function rollBack()
+    {
+        return $this->connection->rollBack();
+    }
+
+    /**
+     * Wrap quotes around the given input.
+     *
+     * @param  string  $value
+     * @param  int  $type
+     * @return string
+     */
+    public function quote($value, $type = ParameterType::STRING)
+    {
+        $val = $this->connection->quote($value, $type);
+
+        // Fix for a driver version terminating all values with null byte...
+        if (\is_string($val) && str_contains($val, "\0")) {
+            $val = \substr($val, 0, -1);
+        }
+
+        return $val;
+    }
+
+    /**
+     * Get the server version for the connection.
+     *
+     * @return string
+     */
+    public function getServerVersion()
+    {
+        return $this->connection->getServerVersion();
+    }
+
+    /**
+     * Get the wrapped PDO connection.
+     *
+     * @return \PDO
+     */
+    public function getWrappedConnection(): PDO
+    {
+        return $this->connection->getWrappedConnection();
+    }
+}
diff --git a/vendor/illuminate/database/PDO/SqlServerDriver.php b/vendor/illuminate/database/PDO/SqlServerDriver.php
new file mode 100644
index 0000000..1b0d957
--- /dev/null
+++ b/vendor/illuminate/database/PDO/SqlServerDriver.php
@@ -0,0 +1,32 @@
+withTablePrefix(new QueryGrammar);
+    }
+
+    /**
+     * Get a schema builder instance for the connection.
+     *
+     * @return \Illuminate\Database\Schema\PostgresBuilder
+     */
+    public function getSchemaBuilder()
+    {
+        if (is_null($this->schemaGrammar)) {
+            $this->useDefaultSchemaGrammar();
+        }
+
+        return new PostgresBuilder($this);
+    }
+
+    /**
+     * Get the default schema grammar instance.
+     *
+     * @return \Illuminate\Database\Schema\Grammars\PostgresGrammar
+     */
+    protected function getDefaultSchemaGrammar()
+    {
+        return $this->withTablePrefix(new SchemaGrammar);
+    }
+
+    /**
+     * Get the schema state for the connection.
+     *
+     * @param  \Illuminate\Filesystem\Filesystem|null  $files
+     * @param  callable|null  $processFactory
+     * @return \Illuminate\Database\Schema\PostgresSchemaState
+     */
+    public function getSchemaState(Filesystem $files = null, callable $processFactory = null)
+    {
+        return new PostgresSchemaState($this, $files, $processFactory);
+    }
+
+    /**
+     * Get the default post processor instance.
+     *
+     * @return \Illuminate\Database\Query\Processors\PostgresProcessor
+     */
+    protected function getDefaultPostProcessor()
+    {
+        return new PostgresProcessor;
+    }
+
+    /**
+     * Get the Doctrine DBAL driver.
+     *
+     * @return \Illuminate\Database\PDO\PostgresDriver
+     */
+    protected function getDoctrineDriver()
+    {
+        return new PostgresDriver;
+    }
+}
diff --git a/vendor/illuminate/database/Query/Builder.php b/vendor/illuminate/database/Query/Builder.php
new file mode 100755
index 0000000..6f98a8c
--- /dev/null
+++ b/vendor/illuminate/database/Query/Builder.php
@@ -0,0 +1,3874 @@
+ [],
+        'from' => [],
+        'join' => [],
+        'where' => [],
+        'groupBy' => [],
+        'having' => [],
+        'order' => [],
+        'union' => [],
+        'unionOrder' => [],
+    ];
+
+    /**
+     * An aggregate function and column to be run.
+     *
+     * @var array
+     */
+    public $aggregate;
+
+    /**
+     * The columns that should be returned.
+     *
+     * @var array
+     */
+    public $columns;
+
+    /**
+     * Indicates if the query returns distinct results.
+     *
+     * Occasionally contains the columns that should be distinct.
+     *
+     * @var bool|array
+     */
+    public $distinct = false;
+
+    /**
+     * The table which the query is targeting.
+     *
+     * @var string
+     */
+    public $from;
+
+    /**
+     * The index hint for the query.
+     *
+     * @var \Illuminate\Database\Query\IndexHint
+     */
+    public $indexHint;
+
+    /**
+     * The table joins for the query.
+     *
+     * @var array
+     */
+    public $joins;
+
+    /**
+     * The where constraints for the query.
+     *
+     * @var array
+     */
+    public $wheres = [];
+
+    /**
+     * The groupings for the query.
+     *
+     * @var array
+     */
+    public $groups;
+
+    /**
+     * The having constraints for the query.
+     *
+     * @var array
+     */
+    public $havings;
+
+    /**
+     * The orderings for the query.
+     *
+     * @var array
+     */
+    public $orders;
+
+    /**
+     * The maximum number of records to return.
+     *
+     * @var int
+     */
+    public $limit;
+
+    /**
+     * The number of records to skip.
+     *
+     * @var int
+     */
+    public $offset;
+
+    /**
+     * The query union statements.
+     *
+     * @var array
+     */
+    public $unions;
+
+    /**
+     * The maximum number of union records to return.
+     *
+     * @var int
+     */
+    public $unionLimit;
+
+    /**
+     * The number of union records to skip.
+     *
+     * @var int
+     */
+    public $unionOffset;
+
+    /**
+     * The orderings for the union query.
+     *
+     * @var array
+     */
+    public $unionOrders;
+
+    /**
+     * Indicates whether row locking is being used.
+     *
+     * @var string|bool
+     */
+    public $lock;
+
+    /**
+     * The callbacks that should be invoked before the query is executed.
+     *
+     * @var array
+     */
+    public $beforeQueryCallbacks = [];
+
+    /**
+     * All of the available clause operators.
+     *
+     * @var string[]
+     */
+    public $operators = [
+        '=', '<', '>', '<=', '>=', '<>', '!=', '<=>',
+        'like', 'like binary', 'not like', 'ilike',
+        '&', '|', '^', '<<', '>>', '&~', 'is', 'is not',
+        'rlike', 'not rlike', 'regexp', 'not regexp',
+        '~', '~*', '!~', '!~*', 'similar to',
+        'not similar to', 'not ilike', '~~*', '!~~*',
+    ];
+
+    /**
+     * All of the available bitwise operators.
+     *
+     * @var string[]
+     */
+    public $bitwiseOperators = [
+        '&', '|', '^', '<<', '>>', '&~',
+    ];
+
+    /**
+     * Whether to use write pdo for the select.
+     *
+     * @var bool
+     */
+    public $useWritePdo = false;
+
+    /**
+     * Create a new query builder instance.
+     *
+     * @param  \Illuminate\Database\ConnectionInterface  $connection
+     * @param  \Illuminate\Database\Query\Grammars\Grammar|null  $grammar
+     * @param  \Illuminate\Database\Query\Processors\Processor|null  $processor
+     * @return void
+     */
+    public function __construct(ConnectionInterface $connection,
+                                Grammar $grammar = null,
+                                Processor $processor = null)
+    {
+        $this->connection = $connection;
+        $this->grammar = $grammar ?: $connection->getQueryGrammar();
+        $this->processor = $processor ?: $connection->getPostProcessor();
+    }
+
+    /**
+     * Set the columns to be selected.
+     *
+     * @param  array|mixed  $columns
+     * @return $this
+     */
+    public function select($columns = ['*'])
+    {
+        $this->columns = [];
+        $this->bindings['select'] = [];
+
+        $columns = is_array($columns) ? $columns : func_get_args();
+
+        foreach ($columns as $as => $column) {
+            if (is_string($as) && $this->isQueryable($column)) {
+                $this->selectSub($column, $as);
+            } else {
+                $this->columns[] = $column;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a subselect expression to the query.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
+     * @param  string  $as
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function selectSub($query, $as)
+    {
+        [$query, $bindings] = $this->createSub($query);
+
+        return $this->selectRaw(
+            '('.$query.') as '.$this->grammar->wrap($as), $bindings
+        );
+    }
+
+    /**
+     * Add a new "raw" select expression to the query.
+     *
+     * @param  string  $expression
+     * @param  array  $bindings
+     * @return $this
+     */
+    public function selectRaw($expression, array $bindings = [])
+    {
+        $this->addSelect(new Expression($expression));
+
+        if ($bindings) {
+            $this->addBinding($bindings, 'select');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Makes "from" fetch from a subquery.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
+     * @param  string  $as
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function fromSub($query, $as)
+    {
+        [$query, $bindings] = $this->createSub($query);
+
+        return $this->fromRaw('('.$query.') as '.$this->grammar->wrapTable($as), $bindings);
+    }
+
+    /**
+     * Add a raw from clause to the query.
+     *
+     * @param  string  $expression
+     * @param  mixed  $bindings
+     * @return $this
+     */
+    public function fromRaw($expression, $bindings = [])
+    {
+        $this->from = new Expression($expression);
+
+        $this->addBinding($bindings, 'from');
+
+        return $this;
+    }
+
+    /**
+     * Creates a subquery and parse it.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
+     * @return array
+     */
+    protected function createSub($query)
+    {
+        // If the given query is a Closure, we will execute it while passing in a new
+        // query instance to the Closure. This will give the developer a chance to
+        // format and work with the query before we cast it to a raw SQL string.
+        if ($query instanceof Closure) {
+            $callback = $query;
+
+            $callback($query = $this->forSubQuery());
+        }
+
+        return $this->parseSub($query);
+    }
+
+    /**
+     * Parse the subquery into SQL and bindings.
+     *
+     * @param  mixed  $query
+     * @return array
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function parseSub($query)
+    {
+        if ($query instanceof self || $query instanceof EloquentBuilder || $query instanceof Relation) {
+            $query = $this->prependDatabaseNameIfCrossDatabaseQuery($query);
+
+            return [$query->toSql(), $query->getBindings()];
+        } elseif (is_string($query)) {
+            return [$query, []];
+        } else {
+            throw new InvalidArgumentException(
+                'A subquery must be a query builder instance, a Closure, or a string.'
+            );
+        }
+    }
+
+    /**
+     * Prepend the database name if the given query is on another database.
+     *
+     * @param  mixed  $query
+     * @return mixed
+     */
+    protected function prependDatabaseNameIfCrossDatabaseQuery($query)
+    {
+        if ($query->getConnection()->getDatabaseName() !==
+            $this->getConnection()->getDatabaseName()) {
+            $databaseName = $query->getConnection()->getDatabaseName();
+
+            if (! str_starts_with($query->from, $databaseName) && ! str_contains($query->from, '.')) {
+                $query->from($databaseName.'.'.$query->from);
+            }
+        }
+
+        return $query;
+    }
+
+    /**
+     * Add a new select column to the query.
+     *
+     * @param  array|mixed  $column
+     * @return $this
+     */
+    public function addSelect($column)
+    {
+        $columns = is_array($column) ? $column : func_get_args();
+
+        foreach ($columns as $as => $column) {
+            if (is_string($as) && $this->isQueryable($column)) {
+                if (is_null($this->columns)) {
+                    $this->select($this->from.'.*');
+                }
+
+                $this->selectSub($column, $as);
+            } else {
+                if (is_array($this->columns) && in_array($column, $this->columns, true)) {
+                    continue;
+                }
+
+                $this->columns[] = $column;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Force the query to only return distinct results.
+     *
+     * @return $this
+     */
+    public function distinct()
+    {
+        $columns = func_get_args();
+
+        if (count($columns) > 0) {
+            $this->distinct = is_array($columns[0]) || is_bool($columns[0]) ? $columns[0] : $columns;
+        } else {
+            $this->distinct = true;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set the table which the query is targeting.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $table
+     * @param  string|null  $as
+     * @return $this
+     */
+    public function from($table, $as = null)
+    {
+        if ($this->isQueryable($table)) {
+            return $this->fromSub($table, $as);
+        }
+
+        $this->from = $as ? "{$table} as {$as}" : $table;
+
+        return $this;
+    }
+
+    /**
+     * Add an index hint to suggest a query index.
+     *
+     * @param  string  $index
+     * @return $this
+     */
+    public function useIndex($index)
+    {
+        $this->indexHint = new IndexHint('hint', $index);
+
+        return $this;
+    }
+
+    /**
+     * Add an index hint to force a query index.
+     *
+     * @param  string  $index
+     * @return $this
+     */
+    public function forceIndex($index)
+    {
+        $this->indexHint = new IndexHint('force', $index);
+
+        return $this;
+    }
+
+    /**
+     * Add an index hint to ignore a query index.
+     *
+     * @param  string  $index
+     * @return $this
+     */
+    public function ignoreIndex($index)
+    {
+        $this->indexHint = new IndexHint('ignore', $index);
+
+        return $this;
+    }
+
+    /**
+     * Add a join clause to the query.
+     *
+     * @param  string  $table
+     * @param  \Closure|string  $first
+     * @param  string|null  $operator
+     * @param  string|null  $second
+     * @param  string  $type
+     * @param  bool  $where
+     * @return $this
+     */
+    public function join($table, $first, $operator = null, $second = null, $type = 'inner', $where = false)
+    {
+        $join = $this->newJoinClause($this, $type, $table);
+
+        // If the first "column" of the join is really a Closure instance the developer
+        // is trying to build a join with a complex "on" clause containing more than
+        // one condition, so we'll add the join and call a Closure with the query.
+        if ($first instanceof Closure) {
+            $first($join);
+
+            $this->joins[] = $join;
+
+            $this->addBinding($join->getBindings(), 'join');
+        }
+
+        // If the column is simply a string, we can assume the join simply has a basic
+        // "on" clause with a single condition. So we will just build the join with
+        // this simple join clauses attached to it. There is not a join callback.
+        else {
+            $method = $where ? 'where' : 'on';
+
+            $this->joins[] = $join->$method($first, $operator, $second);
+
+            $this->addBinding($join->getBindings(), 'join');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a "join where" clause to the query.
+     *
+     * @param  string  $table
+     * @param  \Closure|string  $first
+     * @param  string  $operator
+     * @param  string  $second
+     * @param  string  $type
+     * @return $this
+     */
+    public function joinWhere($table, $first, $operator, $second, $type = 'inner')
+    {
+        return $this->join($table, $first, $operator, $second, $type, true);
+    }
+
+    /**
+     * Add a subquery join clause to the query.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
+     * @param  string  $as
+     * @param  \Closure|string  $first
+     * @param  string|null  $operator
+     * @param  string|null  $second
+     * @param  string  $type
+     * @param  bool  $where
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function joinSub($query, $as, $first, $operator = null, $second = null, $type = 'inner', $where = false)
+    {
+        [$query, $bindings] = $this->createSub($query);
+
+        $expression = '('.$query.') as '.$this->grammar->wrapTable($as);
+
+        $this->addBinding($bindings, 'join');
+
+        return $this->join(new Expression($expression), $first, $operator, $second, $type, $where);
+    }
+
+    /**
+     * Add a left join to the query.
+     *
+     * @param  string  $table
+     * @param  \Closure|string  $first
+     * @param  string|null  $operator
+     * @param  string|null  $second
+     * @return $this
+     */
+    public function leftJoin($table, $first, $operator = null, $second = null)
+    {
+        return $this->join($table, $first, $operator, $second, 'left');
+    }
+
+    /**
+     * Add a "join where" clause to the query.
+     *
+     * @param  string  $table
+     * @param  \Closure|string  $first
+     * @param  string  $operator
+     * @param  string  $second
+     * @return $this
+     */
+    public function leftJoinWhere($table, $first, $operator, $second)
+    {
+        return $this->joinWhere($table, $first, $operator, $second, 'left');
+    }
+
+    /**
+     * Add a subquery left join to the query.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
+     * @param  string  $as
+     * @param  \Closure|string  $first
+     * @param  string|null  $operator
+     * @param  string|null  $second
+     * @return $this
+     */
+    public function leftJoinSub($query, $as, $first, $operator = null, $second = null)
+    {
+        return $this->joinSub($query, $as, $first, $operator, $second, 'left');
+    }
+
+    /**
+     * Add a right join to the query.
+     *
+     * @param  string  $table
+     * @param  \Closure|string  $first
+     * @param  string|null  $operator
+     * @param  string|null  $second
+     * @return $this
+     */
+    public function rightJoin($table, $first, $operator = null, $second = null)
+    {
+        return $this->join($table, $first, $operator, $second, 'right');
+    }
+
+    /**
+     * Add a "right join where" clause to the query.
+     *
+     * @param  string  $table
+     * @param  \Closure|string  $first
+     * @param  string  $operator
+     * @param  string  $second
+     * @return $this
+     */
+    public function rightJoinWhere($table, $first, $operator, $second)
+    {
+        return $this->joinWhere($table, $first, $operator, $second, 'right');
+    }
+
+    /**
+     * Add a subquery right join to the query.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
+     * @param  string  $as
+     * @param  \Closure|string  $first
+     * @param  string|null  $operator
+     * @param  string|null  $second
+     * @return $this
+     */
+    public function rightJoinSub($query, $as, $first, $operator = null, $second = null)
+    {
+        return $this->joinSub($query, $as, $first, $operator, $second, 'right');
+    }
+
+    /**
+     * Add a "cross join" clause to the query.
+     *
+     * @param  string  $table
+     * @param  \Closure|string|null  $first
+     * @param  string|null  $operator
+     * @param  string|null  $second
+     * @return $this
+     */
+    public function crossJoin($table, $first = null, $operator = null, $second = null)
+    {
+        if ($first) {
+            return $this->join($table, $first, $operator, $second, 'cross');
+        }
+
+        $this->joins[] = $this->newJoinClause($this, 'cross', $table);
+
+        return $this;
+    }
+
+    /**
+     * Add a subquery cross join to the query.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
+     * @param  string  $as
+     * @return $this
+     */
+    public function crossJoinSub($query, $as)
+    {
+        [$query, $bindings] = $this->createSub($query);
+
+        $expression = '('.$query.') as '.$this->grammar->wrapTable($as);
+
+        $this->addBinding($bindings, 'join');
+
+        $this->joins[] = $this->newJoinClause($this, 'cross', new Expression($expression));
+
+        return $this;
+    }
+
+    /**
+     * Get a new join clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $parentQuery
+     * @param  string  $type
+     * @param  string  $table
+     * @return \Illuminate\Database\Query\JoinClause
+     */
+    protected function newJoinClause(self $parentQuery, $type, $table)
+    {
+        return new JoinClause($parentQuery, $type, $table);
+    }
+
+    /**
+     * Merge an array of where clauses and bindings.
+     *
+     * @param  array  $wheres
+     * @param  array  $bindings
+     * @return $this
+     */
+    public function mergeWheres($wheres, $bindings)
+    {
+        $this->wheres = array_merge($this->wheres, (array) $wheres);
+
+        $this->bindings['where'] = array_values(
+            array_merge($this->bindings['where'], (array) $bindings)
+        );
+
+        return $this;
+    }
+
+    /**
+     * Add a basic where clause to the query.
+     *
+     * @param  \Closure|string|array  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function where($column, $operator = null, $value = null, $boolean = 'and')
+    {
+        // If the column is an array, we will assume it is an array of key-value pairs
+        // and can add them each as a where clause. We will maintain the boolean we
+        // received when the method was called and pass it into the nested where.
+        if (is_array($column)) {
+            return $this->addArrayOfWheres($column, $boolean);
+        }
+
+        // Here we will make some assumptions about the operator. If only 2 values are
+        // passed to the method, we will assume that the operator is an equals sign
+        // and keep going. Otherwise, we'll require the operator to be passed in.
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        // If the column is actually a Closure instance, we will assume the developer
+        // wants to begin a nested where statement which is wrapped in parentheses.
+        // We will add that Closure to the query and return back out immediately.
+        if ($column instanceof Closure && is_null($operator)) {
+            return $this->whereNested($column, $boolean);
+        }
+
+        // If the column is a Closure instance and there is an operator value, we will
+        // assume the developer wants to run a subquery and then compare the result
+        // of that subquery with the given value that was provided to the method.
+        if ($this->isQueryable($column) && ! is_null($operator)) {
+            [$sub, $bindings] = $this->createSub($column);
+
+            return $this->addBinding($bindings, 'where')
+                ->where(new Expression('('.$sub.')'), $operator, $value, $boolean);
+        }
+
+        // If the given operator is not found in the list of valid operators we will
+        // assume that the developer is just short-cutting the '=' operators and
+        // we will set the operators to '=' and set the values appropriately.
+        if ($this->invalidOperator($operator)) {
+            [$value, $operator] = [$operator, '='];
+        }
+
+        // If the value is a Closure, it means the developer is performing an entire
+        // sub-select within the query and we will need to compile the sub-select
+        // within the where clause to get the appropriate query record results.
+        if ($value instanceof Closure) {
+            return $this->whereSub($column, $operator, $value, $boolean);
+        }
+
+        // If the value is "null", we will just assume the developer wants to add a
+        // where null clause to the query. So, we will allow a short-cut here to
+        // that method for convenience so the developer doesn't have to check.
+        if (is_null($value)) {
+            return $this->whereNull($column, $boolean, $operator !== '=');
+        }
+
+        $type = 'Basic';
+
+        // If the column is making a JSON reference we'll check to see if the value
+        // is a boolean. If it is, we'll add the raw boolean string as an actual
+        // value to the query to ensure this is properly handled by the query.
+        if (str_contains($column, '->') && is_bool($value)) {
+            $value = new Expression($value ? 'true' : 'false');
+
+            if (is_string($column)) {
+                $type = 'JsonBoolean';
+            }
+        }
+
+        if ($this->isBitwiseOperator($operator)) {
+            $type = 'Bitwise';
+        }
+
+        // Now that we are working with just a simple query we can put the elements
+        // in our array and add the query binding to our array of bindings that
+        // will be bound to each SQL statements when it is finally executed.
+        $this->wheres[] = compact(
+            'type', 'column', 'operator', 'value', 'boolean'
+        );
+
+        if (! $value instanceof Expression) {
+            $this->addBinding($this->flattenValue($value), 'where');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add an array of where clauses to the query.
+     *
+     * @param  array  $column
+     * @param  string  $boolean
+     * @param  string  $method
+     * @return $this
+     */
+    protected function addArrayOfWheres($column, $boolean, $method = 'where')
+    {
+        return $this->whereNested(function ($query) use ($column, $method, $boolean) {
+            foreach ($column as $key => $value) {
+                if (is_numeric($key) && is_array($value)) {
+                    $query->{$method}(...array_values($value));
+                } else {
+                    $query->{$method}($key, '=', $value, $boolean);
+                }
+            }
+        }, $boolean);
+    }
+
+    /**
+     * Prepare the value and operator for a where clause.
+     *
+     * @param  string  $value
+     * @param  string  $operator
+     * @param  bool  $useDefault
+     * @return array
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function prepareValueAndOperator($value, $operator, $useDefault = false)
+    {
+        if ($useDefault) {
+            return [$operator, '='];
+        } elseif ($this->invalidOperatorAndValue($operator, $value)) {
+            throw new InvalidArgumentException('Illegal operator and value combination.');
+        }
+
+        return [$value, $operator];
+    }
+
+    /**
+     * Determine if the given operator and value combination is legal.
+     *
+     * Prevents using Null values with invalid operators.
+     *
+     * @param  string  $operator
+     * @param  mixed  $value
+     * @return bool
+     */
+    protected function invalidOperatorAndValue($operator, $value)
+    {
+        return is_null($value) && in_array($operator, $this->operators) &&
+             ! in_array($operator, ['=', '<>', '!=']);
+    }
+
+    /**
+     * Determine if the given operator is supported.
+     *
+     * @param  string  $operator
+     * @return bool
+     */
+    protected function invalidOperator($operator)
+    {
+        return ! is_string($operator) || (! in_array(strtolower($operator), $this->operators, true) &&
+               ! in_array(strtolower($operator), $this->grammar->getOperators(), true));
+    }
+
+    /**
+     * Determine if the operator is a bitwise operator.
+     *
+     * @param  string  $operator
+     * @return bool
+     */
+    protected function isBitwiseOperator($operator)
+    {
+        return in_array(strtolower($operator), $this->bitwiseOperators, true) ||
+               in_array(strtolower($operator), $this->grammar->getBitwiseOperators(), true);
+    }
+
+    /**
+     * Add an "or where" clause to the query.
+     *
+     * @param  \Closure|string|array  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function orWhere($column, $operator = null, $value = null)
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        return $this->where($column, $operator, $value, 'or');
+    }
+
+    /**
+     * Add a basic "where not" clause to the query.
+     *
+     * @param  \Closure|string|array  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereNot($column, $operator = null, $value = null, $boolean = 'and')
+    {
+        if (is_array($column)) {
+            return $this->whereNested(function ($query) use ($column, $operator, $value, $boolean) {
+                $query->where($column, $operator, $value, $boolean);
+            }, $boolean.' not');
+        }
+
+        return $this->where($column, $operator, $value, $boolean.' not');
+    }
+
+    /**
+     * Add an "or where not" clause to the query.
+     *
+     * @param  \Closure|string|array  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function orWhereNot($column, $operator = null, $value = null)
+    {
+        return $this->whereNot($column, $operator, $value, 'or');
+    }
+
+    /**
+     * Add a "where" clause comparing two columns to the query.
+     *
+     * @param  string|array  $first
+     * @param  string|null  $operator
+     * @param  string|null  $second
+     * @param  string|null  $boolean
+     * @return $this
+     */
+    public function whereColumn($first, $operator = null, $second = null, $boolean = 'and')
+    {
+        // If the column is an array, we will assume it is an array of key-value pairs
+        // and can add them each as a where clause. We will maintain the boolean we
+        // received when the method was called and pass it into the nested where.
+        if (is_array($first)) {
+            return $this->addArrayOfWheres($first, $boolean, 'whereColumn');
+        }
+
+        // If the given operator is not found in the list of valid operators we will
+        // assume that the developer is just short-cutting the '=' operators and
+        // we will set the operators to '=' and set the values appropriately.
+        if ($this->invalidOperator($operator)) {
+            [$second, $operator] = [$operator, '='];
+        }
+
+        // Finally, we will add this where clause into this array of clauses that we
+        // are building for the query. All of them will be compiled via a grammar
+        // once the query is about to be executed and run against the database.
+        $type = 'Column';
+
+        $this->wheres[] = compact(
+            'type', 'first', 'operator', 'second', 'boolean'
+        );
+
+        return $this;
+    }
+
+    /**
+     * Add an "or where" clause comparing two columns to the query.
+     *
+     * @param  string|array  $first
+     * @param  string|null  $operator
+     * @param  string|null  $second
+     * @return $this
+     */
+    public function orWhereColumn($first, $operator = null, $second = null)
+    {
+        return $this->whereColumn($first, $operator, $second, 'or');
+    }
+
+    /**
+     * Add a raw where clause to the query.
+     *
+     * @param  string  $sql
+     * @param  mixed  $bindings
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereRaw($sql, $bindings = [], $boolean = 'and')
+    {
+        $this->wheres[] = ['type' => 'raw', 'sql' => $sql, 'boolean' => $boolean];
+
+        $this->addBinding((array) $bindings, 'where');
+
+        return $this;
+    }
+
+    /**
+     * Add a raw or where clause to the query.
+     *
+     * @param  string  $sql
+     * @param  mixed  $bindings
+     * @return $this
+     */
+    public function orWhereRaw($sql, $bindings = [])
+    {
+        return $this->whereRaw($sql, $bindings, 'or');
+    }
+
+    /**
+     * Add a "where in" clause to the query.
+     *
+     * @param  string  $column
+     * @param  mixed  $values
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function whereIn($column, $values, $boolean = 'and', $not = false)
+    {
+        $type = $not ? 'NotIn' : 'In';
+
+        // If the value is a query builder instance we will assume the developer wants to
+        // look for any values that exist within this given query. So, we will add the
+        // query accordingly so that this query is properly executed when it is run.
+        if ($this->isQueryable($values)) {
+            [$query, $bindings] = $this->createSub($values);
+
+            $values = [new Expression($query)];
+
+            $this->addBinding($bindings, 'where');
+        }
+
+        // Next, if the value is Arrayable we need to cast it to its raw array form so we
+        // have the underlying array value instead of an Arrayable object which is not
+        // able to be added as a binding, etc. We will then add to the wheres array.
+        if ($values instanceof Arrayable) {
+            $values = $values->toArray();
+        }
+
+        $this->wheres[] = compact('type', 'column', 'values', 'boolean');
+
+        if (count($values) !== count(Arr::flatten($values, 1))) {
+            throw new InvalidArgumentException('Nested arrays may not be passed to whereIn method.');
+        }
+
+        // Finally, we'll add a binding for each value unless that value is an expression
+        // in which case we will just skip over it since it will be the query as a raw
+        // string and not as a parameterized place-holder to be replaced by the PDO.
+        $this->addBinding($this->cleanBindings($values), 'where');
+
+        return $this;
+    }
+
+    /**
+     * Add an "or where in" clause to the query.
+     *
+     * @param  string  $column
+     * @param  mixed  $values
+     * @return $this
+     */
+    public function orWhereIn($column, $values)
+    {
+        return $this->whereIn($column, $values, 'or');
+    }
+
+    /**
+     * Add a "where not in" clause to the query.
+     *
+     * @param  string  $column
+     * @param  mixed  $values
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereNotIn($column, $values, $boolean = 'and')
+    {
+        return $this->whereIn($column, $values, $boolean, true);
+    }
+
+    /**
+     * Add an "or where not in" clause to the query.
+     *
+     * @param  string  $column
+     * @param  mixed  $values
+     * @return $this
+     */
+    public function orWhereNotIn($column, $values)
+    {
+        return $this->whereNotIn($column, $values, 'or');
+    }
+
+    /**
+     * Add a "where in raw" clause for integer values to the query.
+     *
+     * @param  string  $column
+     * @param  \Illuminate\Contracts\Support\Arrayable|array  $values
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function whereIntegerInRaw($column, $values, $boolean = 'and', $not = false)
+    {
+        $type = $not ? 'NotInRaw' : 'InRaw';
+
+        if ($values instanceof Arrayable) {
+            $values = $values->toArray();
+        }
+
+        $values = Arr::flatten($values);
+
+        foreach ($values as &$value) {
+            $value = (int) $value;
+        }
+
+        $this->wheres[] = compact('type', 'column', 'values', 'boolean');
+
+        return $this;
+    }
+
+    /**
+     * Add an "or where in raw" clause for integer values to the query.
+     *
+     * @param  string  $column
+     * @param  \Illuminate\Contracts\Support\Arrayable|array  $values
+     * @return $this
+     */
+    public function orWhereIntegerInRaw($column, $values)
+    {
+        return $this->whereIntegerInRaw($column, $values, 'or');
+    }
+
+    /**
+     * Add a "where not in raw" clause for integer values to the query.
+     *
+     * @param  string  $column
+     * @param  \Illuminate\Contracts\Support\Arrayable|array  $values
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereIntegerNotInRaw($column, $values, $boolean = 'and')
+    {
+        return $this->whereIntegerInRaw($column, $values, $boolean, true);
+    }
+
+    /**
+     * Add an "or where not in raw" clause for integer values to the query.
+     *
+     * @param  string  $column
+     * @param  \Illuminate\Contracts\Support\Arrayable|array  $values
+     * @return $this
+     */
+    public function orWhereIntegerNotInRaw($column, $values)
+    {
+        return $this->whereIntegerNotInRaw($column, $values, 'or');
+    }
+
+    /**
+     * Add a "where null" clause to the query.
+     *
+     * @param  string|array  $columns
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function whereNull($columns, $boolean = 'and', $not = false)
+    {
+        $type = $not ? 'NotNull' : 'Null';
+
+        foreach (Arr::wrap($columns) as $column) {
+            $this->wheres[] = compact('type', 'column', 'boolean');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add an "or where null" clause to the query.
+     *
+     * @param  string|array  $column
+     * @return $this
+     */
+    public function orWhereNull($column)
+    {
+        return $this->whereNull($column, 'or');
+    }
+
+    /**
+     * Add a "where not null" clause to the query.
+     *
+     * @param  string|array  $columns
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereNotNull($columns, $boolean = 'and')
+    {
+        return $this->whereNull($columns, $boolean, true);
+    }
+
+    /**
+     * Add a where between statement to the query.
+     *
+     * @param  string|\Illuminate\Database\Query\Expression  $column
+     * @param  iterable  $values
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function whereBetween($column, iterable $values, $boolean = 'and', $not = false)
+    {
+        $type = 'between';
+
+        if ($values instanceof CarbonPeriod) {
+            $values = $values->toArray();
+        }
+
+        $this->wheres[] = compact('type', 'column', 'values', 'boolean', 'not');
+
+        $this->addBinding(array_slice($this->cleanBindings(Arr::flatten($values)), 0, 2), 'where');
+
+        return $this;
+    }
+
+    /**
+     * Add a where between statement using columns to the query.
+     *
+     * @param  string  $column
+     * @param  array  $values
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function whereBetweenColumns($column, array $values, $boolean = 'and', $not = false)
+    {
+        $type = 'betweenColumns';
+
+        $this->wheres[] = compact('type', 'column', 'values', 'boolean', 'not');
+
+        return $this;
+    }
+
+    /**
+     * Add an or where between statement to the query.
+     *
+     * @param  string  $column
+     * @param  iterable  $values
+     * @return $this
+     */
+    public function orWhereBetween($column, iterable $values)
+    {
+        return $this->whereBetween($column, $values, 'or');
+    }
+
+    /**
+     * Add an or where between statement using columns to the query.
+     *
+     * @param  string  $column
+     * @param  array  $values
+     * @return $this
+     */
+    public function orWhereBetweenColumns($column, array $values)
+    {
+        return $this->whereBetweenColumns($column, $values, 'or');
+    }
+
+    /**
+     * Add a where not between statement to the query.
+     *
+     * @param  string  $column
+     * @param  iterable  $values
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereNotBetween($column, iterable $values, $boolean = 'and')
+    {
+        return $this->whereBetween($column, $values, $boolean, true);
+    }
+
+    /**
+     * Add a where not between statement using columns to the query.
+     *
+     * @param  string  $column
+     * @param  array  $values
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereNotBetweenColumns($column, array $values, $boolean = 'and')
+    {
+        return $this->whereBetweenColumns($column, $values, $boolean, true);
+    }
+
+    /**
+     * Add an or where not between statement to the query.
+     *
+     * @param  string  $column
+     * @param  iterable  $values
+     * @return $this
+     */
+    public function orWhereNotBetween($column, iterable $values)
+    {
+        return $this->whereNotBetween($column, $values, 'or');
+    }
+
+    /**
+     * Add an or where not between statement using columns to the query.
+     *
+     * @param  string  $column
+     * @param  array  $values
+     * @return $this
+     */
+    public function orWhereNotBetweenColumns($column, array $values)
+    {
+        return $this->whereNotBetweenColumns($column, $values, 'or');
+    }
+
+    /**
+     * Add an "or where not null" clause to the query.
+     *
+     * @param  string  $column
+     * @return $this
+     */
+    public function orWhereNotNull($column)
+    {
+        return $this->whereNotNull($column, 'or');
+    }
+
+    /**
+     * Add a "where date" statement to the query.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  \DateTimeInterface|string|null  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereDate($column, $operator, $value = null, $boolean = 'and')
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        $value = $this->flattenValue($value);
+
+        if ($value instanceof DateTimeInterface) {
+            $value = $value->format('Y-m-d');
+        }
+
+        return $this->addDateBasedWhere('Date', $column, $operator, $value, $boolean);
+    }
+
+    /**
+     * Add an "or where date" statement to the query.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  \DateTimeInterface|string|null  $value
+     * @return $this
+     */
+    public function orWhereDate($column, $operator, $value = null)
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        return $this->whereDate($column, $operator, $value, 'or');
+    }
+
+    /**
+     * Add a "where time" statement to the query.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  \DateTimeInterface|string|null  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereTime($column, $operator, $value = null, $boolean = 'and')
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        $value = $this->flattenValue($value);
+
+        if ($value instanceof DateTimeInterface) {
+            $value = $value->format('H:i:s');
+        }
+
+        return $this->addDateBasedWhere('Time', $column, $operator, $value, $boolean);
+    }
+
+    /**
+     * Add an "or where time" statement to the query.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  \DateTimeInterface|string|null  $value
+     * @return $this
+     */
+    public function orWhereTime($column, $operator, $value = null)
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        return $this->whereTime($column, $operator, $value, 'or');
+    }
+
+    /**
+     * Add a "where day" statement to the query.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  \DateTimeInterface|string|int|null  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereDay($column, $operator, $value = null, $boolean = 'and')
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        $value = $this->flattenValue($value);
+
+        if ($value instanceof DateTimeInterface) {
+            $value = $value->format('d');
+        }
+
+        if (! $value instanceof Expression) {
+            $value = sprintf('%02d', $value);
+        }
+
+        return $this->addDateBasedWhere('Day', $column, $operator, $value, $boolean);
+    }
+
+    /**
+     * Add an "or where day" statement to the query.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  \DateTimeInterface|string|int|null  $value
+     * @return $this
+     */
+    public function orWhereDay($column, $operator, $value = null)
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        return $this->whereDay($column, $operator, $value, 'or');
+    }
+
+    /**
+     * Add a "where month" statement to the query.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  \DateTimeInterface|string|int|null  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereMonth($column, $operator, $value = null, $boolean = 'and')
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        $value = $this->flattenValue($value);
+
+        if ($value instanceof DateTimeInterface) {
+            $value = $value->format('m');
+        }
+
+        if (! $value instanceof Expression) {
+            $value = sprintf('%02d', $value);
+        }
+
+        return $this->addDateBasedWhere('Month', $column, $operator, $value, $boolean);
+    }
+
+    /**
+     * Add an "or where month" statement to the query.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  \DateTimeInterface|string|int|null  $value
+     * @return $this
+     */
+    public function orWhereMonth($column, $operator, $value = null)
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        return $this->whereMonth($column, $operator, $value, 'or');
+    }
+
+    /**
+     * Add a "where year" statement to the query.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  \DateTimeInterface|string|int|null  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereYear($column, $operator, $value = null, $boolean = 'and')
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        $value = $this->flattenValue($value);
+
+        if ($value instanceof DateTimeInterface) {
+            $value = $value->format('Y');
+        }
+
+        return $this->addDateBasedWhere('Year', $column, $operator, $value, $boolean);
+    }
+
+    /**
+     * Add an "or where year" statement to the query.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  \DateTimeInterface|string|int|null  $value
+     * @return $this
+     */
+    public function orWhereYear($column, $operator, $value = null)
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        return $this->whereYear($column, $operator, $value, 'or');
+    }
+
+    /**
+     * Add a date based (year, month, day, time) statement to the query.
+     *
+     * @param  string  $type
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  mixed  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    protected function addDateBasedWhere($type, $column, $operator, $value, $boolean = 'and')
+    {
+        $this->wheres[] = compact('column', 'type', 'boolean', 'operator', 'value');
+
+        if (! $value instanceof Expression) {
+            $this->addBinding($value, 'where');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a nested where statement to the query.
+     *
+     * @param  \Closure  $callback
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereNested(Closure $callback, $boolean = 'and')
+    {
+        $callback($query = $this->forNestedWhere());
+
+        return $this->addNestedWhereQuery($query, $boolean);
+    }
+
+    /**
+     * Create a new query instance for nested where condition.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function forNestedWhere()
+    {
+        return $this->newQuery()->from($this->from);
+    }
+
+    /**
+     * Add another query builder as a nested where to the query builder.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function addNestedWhereQuery($query, $boolean = 'and')
+    {
+        if (count($query->wheres)) {
+            $type = 'Nested';
+
+            $this->wheres[] = compact('type', 'query', 'boolean');
+
+            $this->addBinding($query->getRawBindings()['where'], 'where');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a full sub-select to the query.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  \Closure  $callback
+     * @param  string  $boolean
+     * @return $this
+     */
+    protected function whereSub($column, $operator, Closure $callback, $boolean)
+    {
+        $type = 'Sub';
+
+        // Once we have the query instance we can simply execute it so it can add all
+        // of the sub-select's conditions to itself, and then we can cache it off
+        // in the array of where clauses for the "main" parent query instance.
+        $callback($query = $this->forSubQuery());
+
+        $this->wheres[] = compact(
+            'type', 'column', 'operator', 'query', 'boolean'
+        );
+
+        $this->addBinding($query->getBindings(), 'where');
+
+        return $this;
+    }
+
+    /**
+     * Add an exists clause to the query.
+     *
+     * @param  \Closure  $callback
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function whereExists(Closure $callback, $boolean = 'and', $not = false)
+    {
+        $query = $this->forSubQuery();
+
+        // Similar to the sub-select clause, we will create a new query instance so
+        // the developer may cleanly specify the entire exists query and we will
+        // compile the whole thing in the grammar and insert it into the SQL.
+        $callback($query);
+
+        return $this->addWhereExistsQuery($query, $boolean, $not);
+    }
+
+    /**
+     * Add an or exists clause to the query.
+     *
+     * @param  \Closure  $callback
+     * @param  bool  $not
+     * @return $this
+     */
+    public function orWhereExists(Closure $callback, $not = false)
+    {
+        return $this->whereExists($callback, 'or', $not);
+    }
+
+    /**
+     * Add a where not exists clause to the query.
+     *
+     * @param  \Closure  $callback
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereNotExists(Closure $callback, $boolean = 'and')
+    {
+        return $this->whereExists($callback, $boolean, true);
+    }
+
+    /**
+     * Add a where not exists clause to the query.
+     *
+     * @param  \Closure  $callback
+     * @return $this
+     */
+    public function orWhereNotExists(Closure $callback)
+    {
+        return $this->orWhereExists($callback, true);
+    }
+
+    /**
+     * Add an exists clause to the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function addWhereExistsQuery(self $query, $boolean = 'and', $not = false)
+    {
+        $type = $not ? 'NotExists' : 'Exists';
+
+        $this->wheres[] = compact('type', 'query', 'boolean');
+
+        $this->addBinding($query->getBindings(), 'where');
+
+        return $this;
+    }
+
+    /**
+     * Adds a where condition using row values.
+     *
+     * @param  array  $columns
+     * @param  string  $operator
+     * @param  array  $values
+     * @param  string  $boolean
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function whereRowValues($columns, $operator, $values, $boolean = 'and')
+    {
+        if (count($columns) !== count($values)) {
+            throw new InvalidArgumentException('The number of columns must match the number of values');
+        }
+
+        $type = 'RowValues';
+
+        $this->wheres[] = compact('type', 'columns', 'operator', 'values', 'boolean');
+
+        $this->addBinding($this->cleanBindings($values));
+
+        return $this;
+    }
+
+    /**
+     * Adds an or where condition using row values.
+     *
+     * @param  array  $columns
+     * @param  string  $operator
+     * @param  array  $values
+     * @return $this
+     */
+    public function orWhereRowValues($columns, $operator, $values)
+    {
+        return $this->whereRowValues($columns, $operator, $values, 'or');
+    }
+
+    /**
+     * Add a "where JSON contains" clause to the query.
+     *
+     * @param  string  $column
+     * @param  mixed  $value
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function whereJsonContains($column, $value, $boolean = 'and', $not = false)
+    {
+        $type = 'JsonContains';
+
+        $this->wheres[] = compact('type', 'column', 'value', 'boolean', 'not');
+
+        if (! $value instanceof Expression) {
+            $this->addBinding($this->grammar->prepareBindingForJsonContains($value));
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add an "or where JSON contains" clause to the query.
+     *
+     * @param  string  $column
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function orWhereJsonContains($column, $value)
+    {
+        return $this->whereJsonContains($column, $value, 'or');
+    }
+
+    /**
+     * Add a "where JSON not contains" clause to the query.
+     *
+     * @param  string  $column
+     * @param  mixed  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereJsonDoesntContain($column, $value, $boolean = 'and')
+    {
+        return $this->whereJsonContains($column, $value, $boolean, true);
+    }
+
+    /**
+     * Add an "or where JSON not contains" clause to the query.
+     *
+     * @param  string  $column
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function orWhereJsonDoesntContain($column, $value)
+    {
+        return $this->whereJsonDoesntContain($column, $value, 'or');
+    }
+
+    /**
+     * Add a clause that determines if a JSON path exists to the query.
+     *
+     * @param  string  $column
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function whereJsonContainsKey($column, $boolean = 'and', $not = false)
+    {
+        $type = 'JsonContainsKey';
+
+        $this->wheres[] = compact('type', 'column', 'boolean', 'not');
+
+        return $this;
+    }
+
+    /**
+     * Add an "or" clause that determines if a JSON path exists to the query.
+     *
+     * @param  string  $column
+     * @return $this
+     */
+    public function orWhereJsonContainsKey($column)
+    {
+        return $this->whereJsonContainsKey($column, 'or');
+    }
+
+    /**
+     * Add a clause that determines if a JSON path does not exist to the query.
+     *
+     * @param  string  $column
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereJsonDoesntContainKey($column, $boolean = 'and')
+    {
+        return $this->whereJsonContainsKey($column, $boolean, true);
+    }
+
+    /**
+     * Add an "or" clause that determines if a JSON path does not exist to the query.
+     *
+     * @param  string  $column
+     * @return $this
+     */
+    public function orWhereJsonDoesntContainKey($column)
+    {
+        return $this->whereJsonDoesntContainKey($column, 'or');
+    }
+
+    /**
+     * Add a "where JSON length" clause to the query.
+     *
+     * @param  string  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereJsonLength($column, $operator, $value = null, $boolean = 'and')
+    {
+        $type = 'JsonLength';
+
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        $this->wheres[] = compact('type', 'column', 'operator', 'value', 'boolean');
+
+        if (! $value instanceof Expression) {
+            $this->addBinding((int) $this->flattenValue($value));
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add an "or where JSON length" clause to the query.
+     *
+     * @param  string  $column
+     * @param  mixed  $operator
+     * @param  mixed  $value
+     * @return $this
+     */
+    public function orWhereJsonLength($column, $operator, $value = null)
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        return $this->whereJsonLength($column, $operator, $value, 'or');
+    }
+
+    /**
+     * Handles dynamic "where" clauses to the query.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return $this
+     */
+    public function dynamicWhere($method, $parameters)
+    {
+        $finder = substr($method, 5);
+
+        $segments = preg_split(
+            '/(And|Or)(?=[A-Z])/', $finder, -1, PREG_SPLIT_DELIM_CAPTURE
+        );
+
+        // The connector variable will determine which connector will be used for the
+        // query condition. We will change it as we come across new boolean values
+        // in the dynamic method strings, which could contain a number of these.
+        $connector = 'and';
+
+        $index = 0;
+
+        foreach ($segments as $segment) {
+            // If the segment is not a boolean connector, we can assume it is a column's name
+            // and we will add it to the query as a new constraint as a where clause, then
+            // we can keep iterating through the dynamic method string's segments again.
+            if ($segment !== 'And' && $segment !== 'Or') {
+                $this->addDynamic($segment, $connector, $parameters, $index);
+
+                $index++;
+            }
+
+            // Otherwise, we will store the connector so we know how the next where clause we
+            // find in the query should be connected to the previous ones, meaning we will
+            // have the proper boolean connector to connect the next where clause found.
+            else {
+                $connector = $segment;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a single dynamic where clause statement to the query.
+     *
+     * @param  string  $segment
+     * @param  string  $connector
+     * @param  array  $parameters
+     * @param  int  $index
+     * @return void
+     */
+    protected function addDynamic($segment, $connector, $parameters, $index)
+    {
+        // Once we have parsed out the columns and formatted the boolean operators we
+        // are ready to add it to this query as a where clause just like any other
+        // clause on the query. Then we'll increment the parameter index values.
+        $bool = strtolower($connector);
+
+        $this->where(Str::snake($segment), '=', $parameters[$index], $bool);
+    }
+
+    /**
+     * Add a "where fulltext" clause to the query.
+     *
+     * @param  string|string[]  $columns
+     * @param  string  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function whereFullText($columns, $value, array $options = [], $boolean = 'and')
+    {
+        $type = 'Fulltext';
+
+        $columns = (array) $columns;
+
+        $this->wheres[] = compact('type', 'columns', 'value', 'options', 'boolean');
+
+        $this->addBinding($value);
+
+        return $this;
+    }
+
+    /**
+     * Add a "or where fulltext" clause to the query.
+     *
+     * @param  string|string[]  $columns
+     * @param  string  $value
+     * @return $this
+     */
+    public function orWhereFullText($columns, $value, array $options = [])
+    {
+        return $this->whereFulltext($columns, $value, $options, 'or');
+    }
+
+    /**
+     * Add a "group by" clause to the query.
+     *
+     * @param  array|string  ...$groups
+     * @return $this
+     */
+    public function groupBy(...$groups)
+    {
+        foreach ($groups as $group) {
+            $this->groups = array_merge(
+                (array) $this->groups,
+                Arr::wrap($group)
+            );
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a raw groupBy clause to the query.
+     *
+     * @param  string  $sql
+     * @param  array  $bindings
+     * @return $this
+     */
+    public function groupByRaw($sql, array $bindings = [])
+    {
+        $this->groups[] = new Expression($sql);
+
+        $this->addBinding($bindings, 'groupBy');
+
+        return $this;
+    }
+
+    /**
+     * Add a "having" clause to the query.
+     *
+     * @param  \Closure|string  $column
+     * @param  string|int|float|null  $operator
+     * @param  string|int|float|null  $value
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function having($column, $operator = null, $value = null, $boolean = 'and')
+    {
+        $type = 'Basic';
+
+        // Here we will make some assumptions about the operator. If only 2 values are
+        // passed to the method, we will assume that the operator is an equals sign
+        // and keep going. Otherwise, we'll require the operator to be passed in.
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        if ($column instanceof Closure && is_null($operator)) {
+            return $this->havingNested($column, $boolean);
+        }
+
+        // If the given operator is not found in the list of valid operators we will
+        // assume that the developer is just short-cutting the '=' operators and
+        // we will set the operators to '=' and set the values appropriately.
+        if ($this->invalidOperator($operator)) {
+            [$value, $operator] = [$operator, '='];
+        }
+
+        if ($this->isBitwiseOperator($operator)) {
+            $type = 'Bitwise';
+        }
+
+        $this->havings[] = compact('type', 'column', 'operator', 'value', 'boolean');
+
+        if (! $value instanceof Expression) {
+            $this->addBinding($this->flattenValue($value), 'having');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add an "or having" clause to the query.
+     *
+     * @param  \Closure|string  $column
+     * @param  string|int|float|null  $operator
+     * @param  string|int|float|null  $value
+     * @return $this
+     */
+    public function orHaving($column, $operator = null, $value = null)
+    {
+        [$value, $operator] = $this->prepareValueAndOperator(
+            $value, $operator, func_num_args() === 2
+        );
+
+        return $this->having($column, $operator, $value, 'or');
+    }
+
+    /**
+     * Add a nested having statement to the query.
+     *
+     * @param  \Closure  $callback
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function havingNested(Closure $callback, $boolean = 'and')
+    {
+        $callback($query = $this->forNestedWhere());
+
+        return $this->addNestedHavingQuery($query, $boolean);
+    }
+
+    /**
+     * Add another query builder as a nested having to the query builder.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function addNestedHavingQuery($query, $boolean = 'and')
+    {
+        if (count($query->havings)) {
+            $type = 'Nested';
+
+            $this->havings[] = compact('type', 'query', 'boolean');
+
+            $this->addBinding($query->getRawBindings()['having'], 'having');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a "having null" clause to the query.
+     *
+     * @param  string|array  $columns
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function havingNull($columns, $boolean = 'and', $not = false)
+    {
+        $type = $not ? 'NotNull' : 'Null';
+
+        foreach (Arr::wrap($columns) as $column) {
+            $this->havings[] = compact('type', 'column', 'boolean');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add an "or having null" clause to the query.
+     *
+     * @param  string  $column
+     * @return $this
+     */
+    public function orHavingNull($column)
+    {
+        return $this->havingNull($column, 'or');
+    }
+
+    /**
+     * Add a "having not null" clause to the query.
+     *
+     * @param  string|array  $columns
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function havingNotNull($columns, $boolean = 'and')
+    {
+        return $this->havingNull($columns, $boolean, true);
+    }
+
+    /**
+     * Add an "or having not null" clause to the query.
+     *
+     * @param  string  $column
+     * @return $this
+     */
+    public function orHavingNotNull($column)
+    {
+        return $this->havingNotNull($column, 'or');
+    }
+
+    /**
+     * Add a "having between " clause to the query.
+     *
+     * @param  string  $column
+     * @param  array  $values
+     * @param  string  $boolean
+     * @param  bool  $not
+     * @return $this
+     */
+    public function havingBetween($column, array $values, $boolean = 'and', $not = false)
+    {
+        $type = 'between';
+
+        $this->havings[] = compact('type', 'column', 'values', 'boolean', 'not');
+
+        $this->addBinding(array_slice($this->cleanBindings(Arr::flatten($values)), 0, 2), 'having');
+
+        return $this;
+    }
+
+    /**
+     * Add a raw having clause to the query.
+     *
+     * @param  string  $sql
+     * @param  array  $bindings
+     * @param  string  $boolean
+     * @return $this
+     */
+    public function havingRaw($sql, array $bindings = [], $boolean = 'and')
+    {
+        $type = 'Raw';
+
+        $this->havings[] = compact('type', 'sql', 'boolean');
+
+        $this->addBinding($bindings, 'having');
+
+        return $this;
+    }
+
+    /**
+     * Add a raw or having clause to the query.
+     *
+     * @param  string  $sql
+     * @param  array  $bindings
+     * @return $this
+     */
+    public function orHavingRaw($sql, array $bindings = [])
+    {
+        return $this->havingRaw($sql, $bindings, 'or');
+    }
+
+    /**
+     * Add an "order by" clause to the query.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Expression|string  $column
+     * @param  string  $direction
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function orderBy($column, $direction = 'asc')
+    {
+        if ($this->isQueryable($column)) {
+            [$query, $bindings] = $this->createSub($column);
+
+            $column = new Expression('('.$query.')');
+
+            $this->addBinding($bindings, $this->unions ? 'unionOrder' : 'order');
+        }
+
+        $direction = strtolower($direction);
+
+        if (! in_array($direction, ['asc', 'desc'], true)) {
+            throw new InvalidArgumentException('Order direction must be "asc" or "desc".');
+        }
+
+        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = [
+            'column' => $column,
+            'direction' => $direction,
+        ];
+
+        return $this;
+    }
+
+    /**
+     * Add a descending "order by" clause to the query.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Expression|string  $column
+     * @return $this
+     */
+    public function orderByDesc($column)
+    {
+        return $this->orderBy($column, 'desc');
+    }
+
+    /**
+     * Add an "order by" clause for a timestamp to the query.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Query\Expression|string  $column
+     * @return $this
+     */
+    public function latest($column = 'created_at')
+    {
+        return $this->orderBy($column, 'desc');
+    }
+
+    /**
+     * Add an "order by" clause for a timestamp to the query.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Query\Expression|string  $column
+     * @return $this
+     */
+    public function oldest($column = 'created_at')
+    {
+        return $this->orderBy($column, 'asc');
+    }
+
+    /**
+     * Put the query's results in random order.
+     *
+     * @param  string|int  $seed
+     * @return $this
+     */
+    public function inRandomOrder($seed = '')
+    {
+        return $this->orderByRaw($this->grammar->compileRandom($seed));
+    }
+
+    /**
+     * Add a raw "order by" clause to the query.
+     *
+     * @param  string  $sql
+     * @param  array  $bindings
+     * @return $this
+     */
+    public function orderByRaw($sql, $bindings = [])
+    {
+        $type = 'Raw';
+
+        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = compact('type', 'sql');
+
+        $this->addBinding($bindings, $this->unions ? 'unionOrder' : 'order');
+
+        return $this;
+    }
+
+    /**
+     * Alias to set the "offset" value of the query.
+     *
+     * @param  int  $value
+     * @return $this
+     */
+    public function skip($value)
+    {
+        return $this->offset($value);
+    }
+
+    /**
+     * Set the "offset" value of the query.
+     *
+     * @param  int  $value
+     * @return $this
+     */
+    public function offset($value)
+    {
+        $property = $this->unions ? 'unionOffset' : 'offset';
+
+        $this->$property = max(0, (int) $value);
+
+        return $this;
+    }
+
+    /**
+     * Alias to set the "limit" value of the query.
+     *
+     * @param  int  $value
+     * @return $this
+     */
+    public function take($value)
+    {
+        return $this->limit($value);
+    }
+
+    /**
+     * Set the "limit" value of the query.
+     *
+     * @param  int  $value
+     * @return $this
+     */
+    public function limit($value)
+    {
+        $property = $this->unions ? 'unionLimit' : 'limit';
+
+        if ($value >= 0) {
+            $this->$property = ! is_null($value) ? (int) $value : null;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set the limit and offset for a given page.
+     *
+     * @param  int  $page
+     * @param  int  $perPage
+     * @return $this
+     */
+    public function forPage($page, $perPage = 15)
+    {
+        return $this->offset(($page - 1) * $perPage)->limit($perPage);
+    }
+
+    /**
+     * Constrain the query to the previous "page" of results before a given ID.
+     *
+     * @param  int  $perPage
+     * @param  int|null  $lastId
+     * @param  string  $column
+     * @return $this
+     */
+    public function forPageBeforeId($perPage = 15, $lastId = 0, $column = 'id')
+    {
+        $this->orders = $this->removeExistingOrdersFor($column);
+
+        if (! is_null($lastId)) {
+            $this->where($column, '<', $lastId);
+        }
+
+        return $this->orderBy($column, 'desc')
+                    ->limit($perPage);
+    }
+
+    /**
+     * Constrain the query to the next "page" of results after a given ID.
+     *
+     * @param  int  $perPage
+     * @param  int|null  $lastId
+     * @param  string  $column
+     * @return $this
+     */
+    public function forPageAfterId($perPage = 15, $lastId = 0, $column = 'id')
+    {
+        $this->orders = $this->removeExistingOrdersFor($column);
+
+        if (! is_null($lastId)) {
+            $this->where($column, '>', $lastId);
+        }
+
+        return $this->orderBy($column, 'asc')
+                    ->limit($perPage);
+    }
+
+    /**
+     * Remove all existing orders and optionally add a new order.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Query\Expression|string|null  $column
+     * @param  string  $direction
+     * @return $this
+     */
+    public function reorder($column = null, $direction = 'asc')
+    {
+        $this->orders = null;
+        $this->unionOrders = null;
+        $this->bindings['order'] = [];
+        $this->bindings['unionOrder'] = [];
+
+        if ($column) {
+            return $this->orderBy($column, $direction);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get an array with all orders with a given column removed.
+     *
+     * @param  string  $column
+     * @return array
+     */
+    protected function removeExistingOrdersFor($column)
+    {
+        return Collection::make($this->orders)
+                    ->reject(function ($order) use ($column) {
+                        return isset($order['column'])
+                               ? $order['column'] === $column : false;
+                    })->values()->all();
+    }
+
+    /**
+     * Add a union statement to the query.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder  $query
+     * @param  bool  $all
+     * @return $this
+     */
+    public function union($query, $all = false)
+    {
+        if ($query instanceof Closure) {
+            $query($query = $this->newQuery());
+        }
+
+        $this->unions[] = compact('query', 'all');
+
+        $this->addBinding($query->getBindings(), 'union');
+
+        return $this;
+    }
+
+    /**
+     * Add a union all statement to the query.
+     *
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder  $query
+     * @return $this
+     */
+    public function unionAll($query)
+    {
+        return $this->union($query, true);
+    }
+
+    /**
+     * Lock the selected rows in the table.
+     *
+     * @param  string|bool  $value
+     * @return $this
+     */
+    public function lock($value = true)
+    {
+        $this->lock = $value;
+
+        if (! is_null($this->lock)) {
+            $this->useWritePdo();
+        }
+
+        return $this;
+    }
+
+    /**
+     * Lock the selected rows in the table for updating.
+     *
+     * @return $this
+     */
+    public function lockForUpdate()
+    {
+        return $this->lock(true);
+    }
+
+    /**
+     * Share lock the selected rows in the table.
+     *
+     * @return $this
+     */
+    public function sharedLock()
+    {
+        return $this->lock(false);
+    }
+
+    /**
+     * Register a closure to be invoked before the query is executed.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function beforeQuery(callable $callback)
+    {
+        $this->beforeQueryCallbacks[] = $callback;
+
+        return $this;
+    }
+
+    /**
+     * Invoke the "before query" modification callbacks.
+     *
+     * @return void
+     */
+    public function applyBeforeQueryCallbacks()
+    {
+        foreach ($this->beforeQueryCallbacks as $callback) {
+            $callback($this);
+        }
+
+        $this->beforeQueryCallbacks = [];
+    }
+
+    /**
+     * Get the SQL representation of the query.
+     *
+     * @return string
+     */
+    public function toSql()
+    {
+        $this->applyBeforeQueryCallbacks();
+
+        return $this->grammar->compileSelect($this);
+    }
+
+    /**
+     * Execute a query for a single record by ID.
+     *
+     * @param  int|string  $id
+     * @param  array|string  $columns
+     * @return mixed|static
+     */
+    public function find($id, $columns = ['*'])
+    {
+        return $this->where('id', '=', $id)->first($columns);
+    }
+
+    /**
+     * Execute a query for a single record by ID or call a callback.
+     *
+     * @param  mixed  $id
+     * @param  \Closure|array|string  $columns
+     * @param  \Closure|null  $callback
+     * @return mixed|static
+     */
+    public function findOr($id, $columns = ['*'], Closure $callback = null)
+    {
+        if ($columns instanceof Closure) {
+            $callback = $columns;
+
+            $columns = ['*'];
+        }
+
+        if (! is_null($data = $this->find($id, $columns))) {
+            return $data;
+        }
+
+        return $callback();
+    }
+
+    /**
+     * Get a single column's value from the first result of a query.
+     *
+     * @param  string  $column
+     * @return mixed
+     */
+    public function value($column)
+    {
+        $result = (array) $this->first([$column]);
+
+        return count($result) > 0 ? reset($result) : null;
+    }
+
+    /**
+     * Get a single expression value from the first result of a query.
+     *
+     * @param  string  $expression
+     * @param  array  $bindings
+     * @return mixed
+     */
+    public function rawValue(string $expression, array $bindings = [])
+    {
+        $result = (array) $this->selectRaw($expression, $bindings)->first();
+
+        return count($result) > 0 ? reset($result) : null;
+    }
+
+    /**
+     * Get a single column's value from the first result of a query if it's the sole matching record.
+     *
+     * @param  string  $column
+     * @return mixed
+     *
+     * @throws \Illuminate\Database\RecordsNotFoundException
+     * @throws \Illuminate\Database\MultipleRecordsFoundException
+     */
+    public function soleValue($column)
+    {
+        $result = (array) $this->sole([$column]);
+
+        return reset($result);
+    }
+
+    /**
+     * Execute the query as a "select" statement.
+     *
+     * @param  array|string  $columns
+     * @return \Illuminate\Support\Collection
+     */
+    public function get($columns = ['*'])
+    {
+        return collect($this->onceWithColumns(Arr::wrap($columns), function () {
+            return $this->processor->processSelect($this, $this->runSelect());
+        }));
+    }
+
+    /**
+     * Run the query as a "select" statement against the connection.
+     *
+     * @return array
+     */
+    protected function runSelect()
+    {
+        return $this->connection->select(
+            $this->toSql(), $this->getBindings(), ! $this->useWritePdo
+        );
+    }
+
+    /**
+     * Paginate the given query into a simple paginator.
+     *
+     * @param  int|\Closure  $perPage
+     * @param  array|string  $columns
+     * @param  string  $pageName
+     * @param  int|null  $page
+     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
+     */
+    public function paginate($perPage = 15, $columns = ['*'], $pageName = 'page', $page = null)
+    {
+        $page = $page ?: Paginator::resolveCurrentPage($pageName);
+
+        $total = $this->getCountForPagination();
+
+        $perPage = $perPage instanceof Closure ? $perPage($total) : $perPage;
+
+        $results = $total ? $this->forPage($page, $perPage)->get($columns) : collect();
+
+        return $this->paginator($results, $total, $perPage, $page, [
+            'path' => Paginator::resolveCurrentPath(),
+            'pageName' => $pageName,
+        ]);
+    }
+
+    /**
+     * Get a paginator only supporting simple next and previous links.
+     *
+     * This is more efficient on larger data-sets, etc.
+     *
+     * @param  int  $perPage
+     * @param  array|string  $columns
+     * @param  string  $pageName
+     * @param  int|null  $page
+     * @return \Illuminate\Contracts\Pagination\Paginator
+     */
+    public function simplePaginate($perPage = 15, $columns = ['*'], $pageName = 'page', $page = null)
+    {
+        $page = $page ?: Paginator::resolveCurrentPage($pageName);
+
+        $this->offset(($page - 1) * $perPage)->limit($perPage + 1);
+
+        return $this->simplePaginator($this->get($columns), $perPage, $page, [
+            'path' => Paginator::resolveCurrentPath(),
+            'pageName' => $pageName,
+        ]);
+    }
+
+    /**
+     * Get a paginator only supporting simple next and previous links.
+     *
+     * This is more efficient on larger data-sets, etc.
+     *
+     * @param  int|null  $perPage
+     * @param  array|string  $columns
+     * @param  string  $cursorName
+     * @param  \Illuminate\Pagination\Cursor|string|null  $cursor
+     * @return \Illuminate\Contracts\Pagination\CursorPaginator
+     */
+    public function cursorPaginate($perPage = 15, $columns = ['*'], $cursorName = 'cursor', $cursor = null)
+    {
+        return $this->paginateUsingCursor($perPage, $columns, $cursorName, $cursor);
+    }
+
+    /**
+     * Ensure the proper order by required for cursor pagination.
+     *
+     * @param  bool  $shouldReverse
+     * @return \Illuminate\Support\Collection
+     */
+    protected function ensureOrderForCursorPagination($shouldReverse = false)
+    {
+        $this->enforceOrderBy();
+
+        return collect($this->orders ?? $this->unionOrders ?? [])->filter(function ($order) {
+            return Arr::has($order, 'direction');
+        })->when($shouldReverse, function (Collection $orders) {
+            return $orders->map(function ($order) {
+                $order['direction'] = $order['direction'] === 'asc' ? 'desc' : 'asc';
+
+                return $order;
+            });
+        })->values();
+    }
+
+    /**
+     * Get the count of the total records for the paginator.
+     *
+     * @param  array  $columns
+     * @return int
+     */
+    public function getCountForPagination($columns = ['*'])
+    {
+        $results = $this->runPaginationCountQuery($columns);
+
+        // Once we have run the pagination count query, we will get the resulting count and
+        // take into account what type of query it was. When there is a group by we will
+        // just return the count of the entire results set since that will be correct.
+        if (! isset($results[0])) {
+            return 0;
+        } elseif (is_object($results[0])) {
+            return (int) $results[0]->aggregate;
+        }
+
+        return (int) array_change_key_case((array) $results[0])['aggregate'];
+    }
+
+    /**
+     * Run a pagination count query.
+     *
+     * @param  array  $columns
+     * @return array
+     */
+    protected function runPaginationCountQuery($columns = ['*'])
+    {
+        if ($this->groups || $this->havings) {
+            $clone = $this->cloneForPaginationCount();
+
+            if (is_null($clone->columns) && ! empty($this->joins)) {
+                $clone->select($this->from.'.*');
+            }
+
+            return $this->newQuery()
+                ->from(new Expression('('.$clone->toSql().') as '.$this->grammar->wrap('aggregate_table')))
+                ->mergeBindings($clone)
+                ->setAggregate('count', $this->withoutSelectAliases($columns))
+                ->get()->all();
+        }
+
+        $without = $this->unions ? ['orders', 'limit', 'offset'] : ['columns', 'orders', 'limit', 'offset'];
+
+        return $this->cloneWithout($without)
+                    ->cloneWithoutBindings($this->unions ? ['order'] : ['select', 'order'])
+                    ->setAggregate('count', $this->withoutSelectAliases($columns))
+                    ->get()->all();
+    }
+
+    /**
+     * Clone the existing query instance for usage in a pagination subquery.
+     *
+     * @return self
+     */
+    protected function cloneForPaginationCount()
+    {
+        return $this->cloneWithout(['orders', 'limit', 'offset'])
+                    ->cloneWithoutBindings(['order']);
+    }
+
+    /**
+     * Remove the column aliases since they will break count queries.
+     *
+     * @param  array  $columns
+     * @return array
+     */
+    protected function withoutSelectAliases(array $columns)
+    {
+        return array_map(function ($column) {
+            return is_string($column) && ($aliasPosition = stripos($column, ' as ')) !== false
+                    ? substr($column, 0, $aliasPosition) : $column;
+        }, $columns);
+    }
+
+    /**
+     * Get a lazy collection for the given query.
+     *
+     * @return \Illuminate\Support\LazyCollection
+     */
+    public function cursor()
+    {
+        if (is_null($this->columns)) {
+            $this->columns = ['*'];
+        }
+
+        return new LazyCollection(function () {
+            yield from $this->connection->cursor(
+                $this->toSql(), $this->getBindings(), ! $this->useWritePdo
+            );
+        });
+    }
+
+    /**
+     * Throw an exception if the query doesn't have an orderBy clause.
+     *
+     * @return void
+     *
+     * @throws \RuntimeException
+     */
+    protected function enforceOrderBy()
+    {
+        if (empty($this->orders) && empty($this->unionOrders)) {
+            throw new RuntimeException('You must specify an orderBy clause when using this function.');
+        }
+    }
+
+    /**
+     * Get a collection instance containing the values of a given column.
+     *
+     * @param  string  $column
+     * @param  string|null  $key
+     * @return \Illuminate\Support\Collection
+     */
+    public function pluck($column, $key = null)
+    {
+        // First, we will need to select the results of the query accounting for the
+        // given columns / key. Once we have the results, we will be able to take
+        // the results and get the exact data that was requested for the query.
+        $queryResult = $this->onceWithColumns(
+            is_null($key) ? [$column] : [$column, $key],
+            function () {
+                return $this->processor->processSelect(
+                    $this, $this->runSelect()
+                );
+            }
+        );
+
+        if (empty($queryResult)) {
+            return collect();
+        }
+
+        // If the columns are qualified with a table or have an alias, we cannot use
+        // those directly in the "pluck" operations since the results from the DB
+        // are only keyed by the column itself. We'll strip the table out here.
+        $column = $this->stripTableForPluck($column);
+
+        $key = $this->stripTableForPluck($key);
+
+        return is_array($queryResult[0])
+                    ? $this->pluckFromArrayColumn($queryResult, $column, $key)
+                    : $this->pluckFromObjectColumn($queryResult, $column, $key);
+    }
+
+    /**
+     * Strip off the table name or alias from a column identifier.
+     *
+     * @param  string  $column
+     * @return string|null
+     */
+    protected function stripTableForPluck($column)
+    {
+        if (is_null($column)) {
+            return $column;
+        }
+
+        $separator = str_contains(strtolower($column), ' as ') ? ' as ' : '\.';
+
+        return last(preg_split('~'.$separator.'~i', $column));
+    }
+
+    /**
+     * Retrieve column values from rows represented as objects.
+     *
+     * @param  array  $queryResult
+     * @param  string  $column
+     * @param  string  $key
+     * @return \Illuminate\Support\Collection
+     */
+    protected function pluckFromObjectColumn($queryResult, $column, $key)
+    {
+        $results = [];
+
+        if (is_null($key)) {
+            foreach ($queryResult as $row) {
+                $results[] = $row->$column;
+            }
+        } else {
+            foreach ($queryResult as $row) {
+                $results[$row->$key] = $row->$column;
+            }
+        }
+
+        return collect($results);
+    }
+
+    /**
+     * Retrieve column values from rows represented as arrays.
+     *
+     * @param  array  $queryResult
+     * @param  string  $column
+     * @param  string  $key
+     * @return \Illuminate\Support\Collection
+     */
+    protected function pluckFromArrayColumn($queryResult, $column, $key)
+    {
+        $results = [];
+
+        if (is_null($key)) {
+            foreach ($queryResult as $row) {
+                $results[] = $row[$column];
+            }
+        } else {
+            foreach ($queryResult as $row) {
+                $results[$row[$key]] = $row[$column];
+            }
+        }
+
+        return collect($results);
+    }
+
+    /**
+     * Concatenate values of a given column as a string.
+     *
+     * @param  string  $column
+     * @param  string  $glue
+     * @return string
+     */
+    public function implode($column, $glue = '')
+    {
+        return $this->pluck($column)->implode($glue);
+    }
+
+    /**
+     * Determine if any rows exist for the current query.
+     *
+     * @return bool
+     */
+    public function exists()
+    {
+        $this->applyBeforeQueryCallbacks();
+
+        $results = $this->connection->select(
+            $this->grammar->compileExists($this), $this->getBindings(), ! $this->useWritePdo
+        );
+
+        // If the results have rows, we will get the row and see if the exists column is a
+        // boolean true. If there are no results for this query we will return false as
+        // there are no rows for this query at all, and we can return that info here.
+        if (isset($results[0])) {
+            $results = (array) $results[0];
+
+            return (bool) $results['exists'];
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine if no rows exist for the current query.
+     *
+     * @return bool
+     */
+    public function doesntExist()
+    {
+        return ! $this->exists();
+    }
+
+    /**
+     * Execute the given callback if no rows exist for the current query.
+     *
+     * @param  \Closure  $callback
+     * @return mixed
+     */
+    public function existsOr(Closure $callback)
+    {
+        return $this->exists() ? true : $callback();
+    }
+
+    /**
+     * Execute the given callback if rows exist for the current query.
+     *
+     * @param  \Closure  $callback
+     * @return mixed
+     */
+    public function doesntExistOr(Closure $callback)
+    {
+        return $this->doesntExist() ? true : $callback();
+    }
+
+    /**
+     * Retrieve the "count" result of the query.
+     *
+     * @param  string  $columns
+     * @return int
+     */
+    public function count($columns = '*')
+    {
+        return (int) $this->aggregate(__FUNCTION__, Arr::wrap($columns));
+    }
+
+    /**
+     * Retrieve the minimum value of a given column.
+     *
+     * @param  string  $column
+     * @return mixed
+     */
+    public function min($column)
+    {
+        return $this->aggregate(__FUNCTION__, [$column]);
+    }
+
+    /**
+     * Retrieve the maximum value of a given column.
+     *
+     * @param  string  $column
+     * @return mixed
+     */
+    public function max($column)
+    {
+        return $this->aggregate(__FUNCTION__, [$column]);
+    }
+
+    /**
+     * Retrieve the sum of the values of a given column.
+     *
+     * @param  string  $column
+     * @return mixed
+     */
+    public function sum($column)
+    {
+        $result = $this->aggregate(__FUNCTION__, [$column]);
+
+        return $result ?: 0;
+    }
+
+    /**
+     * Retrieve the average of the values of a given column.
+     *
+     * @param  string  $column
+     * @return mixed
+     */
+    public function avg($column)
+    {
+        return $this->aggregate(__FUNCTION__, [$column]);
+    }
+
+    /**
+     * Alias for the "avg" method.
+     *
+     * @param  string  $column
+     * @return mixed
+     */
+    public function average($column)
+    {
+        return $this->avg($column);
+    }
+
+    /**
+     * Execute an aggregate function on the database.
+     *
+     * @param  string  $function
+     * @param  array  $columns
+     * @return mixed
+     */
+    public function aggregate($function, $columns = ['*'])
+    {
+        $results = $this->cloneWithout($this->unions || $this->havings ? [] : ['columns'])
+                        ->cloneWithoutBindings($this->unions || $this->havings ? [] : ['select'])
+                        ->setAggregate($function, $columns)
+                        ->get($columns);
+
+        if (! $results->isEmpty()) {
+            return array_change_key_case((array) $results[0])['aggregate'];
+        }
+    }
+
+    /**
+     * Execute a numeric aggregate function on the database.
+     *
+     * @param  string  $function
+     * @param  array  $columns
+     * @return float|int
+     */
+    public function numericAggregate($function, $columns = ['*'])
+    {
+        $result = $this->aggregate($function, $columns);
+
+        // If there is no result, we can obviously just return 0 here. Next, we will check
+        // if the result is an integer or float. If it is already one of these two data
+        // types we can just return the result as-is, otherwise we will convert this.
+        if (! $result) {
+            return 0;
+        }
+
+        if (is_int($result) || is_float($result)) {
+            return $result;
+        }
+
+        // If the result doesn't contain a decimal place, we will assume it is an int then
+        // cast it to one. When it does we will cast it to a float since it needs to be
+        // cast to the expected data type for the developers out of pure convenience.
+        return ! str_contains((string) $result, '.')
+                ? (int) $result : (float) $result;
+    }
+
+    /**
+     * Set the aggregate property without running the query.
+     *
+     * @param  string  $function
+     * @param  array  $columns
+     * @return $this
+     */
+    protected function setAggregate($function, $columns)
+    {
+        $this->aggregate = compact('function', 'columns');
+
+        if (empty($this->groups)) {
+            $this->orders = null;
+
+            $this->bindings['order'] = [];
+        }
+
+        return $this;
+    }
+
+    /**
+     * Execute the given callback while selecting the given columns.
+     *
+     * After running the callback, the columns are reset to the original value.
+     *
+     * @param  array  $columns
+     * @param  callable  $callback
+     * @return mixed
+     */
+    protected function onceWithColumns($columns, $callback)
+    {
+        $original = $this->columns;
+
+        if (is_null($original)) {
+            $this->columns = $columns;
+        }
+
+        $result = $callback();
+
+        $this->columns = $original;
+
+        return $result;
+    }
+
+    /**
+     * Insert new records into the database.
+     *
+     * @param  array  $values
+     * @return bool
+     */
+    public function insert(array $values)
+    {
+        // Since every insert gets treated like a batch insert, we will make sure the
+        // bindings are structured in a way that is convenient when building these
+        // inserts statements by verifying these elements are actually an array.
+        if (empty($values)) {
+            return true;
+        }
+
+        if (! is_array(reset($values))) {
+            $values = [$values];
+        }
+
+        // Here, we will sort the insert keys for every record so that each insert is
+        // in the same order for the record. We need to make sure this is the case
+        // so there are not any errors or problems when inserting these records.
+        else {
+            foreach ($values as $key => $value) {
+                ksort($value);
+
+                $values[$key] = $value;
+            }
+        }
+
+        $this->applyBeforeQueryCallbacks();
+
+        // Finally, we will run this query against the database connection and return
+        // the results. We will need to also flatten these bindings before running
+        // the query so they are all in one huge, flattened array for execution.
+        return $this->connection->insert(
+            $this->grammar->compileInsert($this, $values),
+            $this->cleanBindings(Arr::flatten($values, 1))
+        );
+    }
+
+    /**
+     * Insert new records into the database while ignoring errors.
+     *
+     * @param  array  $values
+     * @return int
+     */
+    public function insertOrIgnore(array $values)
+    {
+        if (empty($values)) {
+            return 0;
+        }
+
+        if (! is_array(reset($values))) {
+            $values = [$values];
+        } else {
+            foreach ($values as $key => $value) {
+                ksort($value);
+                $values[$key] = $value;
+            }
+        }
+
+        $this->applyBeforeQueryCallbacks();
+
+        return $this->connection->affectingStatement(
+            $this->grammar->compileInsertOrIgnore($this, $values),
+            $this->cleanBindings(Arr::flatten($values, 1))
+        );
+    }
+
+    /**
+     * Insert a new record and get the value of the primary key.
+     *
+     * @param  array  $values
+     * @param  string|null  $sequence
+     * @return int
+     */
+    public function insertGetId(array $values, $sequence = null)
+    {
+        $this->applyBeforeQueryCallbacks();
+
+        $sql = $this->grammar->compileInsertGetId($this, $values, $sequence);
+
+        $values = $this->cleanBindings($values);
+
+        return $this->processor->processInsertGetId($this, $sql, $values, $sequence);
+    }
+
+    /**
+     * Insert new records into the table using a subquery.
+     *
+     * @param  array  $columns
+     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
+     * @return int
+     */
+    public function insertUsing(array $columns, $query)
+    {
+        $this->applyBeforeQueryCallbacks();
+
+        [$sql, $bindings] = $this->createSub($query);
+
+        return $this->connection->affectingStatement(
+            $this->grammar->compileInsertUsing($this, $columns, $sql),
+            $this->cleanBindings($bindings)
+        );
+    }
+
+    /**
+     * Update records in the database.
+     *
+     * @param  array  $values
+     * @return int
+     */
+    public function update(array $values)
+    {
+        $this->applyBeforeQueryCallbacks();
+
+        $sql = $this->grammar->compileUpdate($this, $values);
+
+        return $this->connection->update($sql, $this->cleanBindings(
+            $this->grammar->prepareBindingsForUpdate($this->bindings, $values)
+        ));
+    }
+
+    /**
+     * Update records in a PostgreSQL database using the update from syntax.
+     *
+     * @param  array  $values
+     * @return int
+     */
+    public function updateFrom(array $values)
+    {
+        if (! method_exists($this->grammar, 'compileUpdateFrom')) {
+            throw new LogicException('This database engine does not support the updateFrom method.');
+        }
+
+        $this->applyBeforeQueryCallbacks();
+
+        $sql = $this->grammar->compileUpdateFrom($this, $values);
+
+        return $this->connection->update($sql, $this->cleanBindings(
+            $this->grammar->prepareBindingsForUpdateFrom($this->bindings, $values)
+        ));
+    }
+
+    /**
+     * Insert or update a record matching the attributes, and fill it with values.
+     *
+     * @param  array  $attributes
+     * @param  array  $values
+     * @return bool
+     */
+    public function updateOrInsert(array $attributes, array $values = [])
+    {
+        if (! $this->where($attributes)->exists()) {
+            return $this->insert(array_merge($attributes, $values));
+        }
+
+        if (empty($values)) {
+            return true;
+        }
+
+        return (bool) $this->limit(1)->update($values);
+    }
+
+    /**
+     * Insert new records or update the existing ones.
+     *
+     * @param  array  $values
+     * @param  array|string  $uniqueBy
+     * @param  array|null  $update
+     * @return int
+     */
+    public function upsert(array $values, $uniqueBy, $update = null)
+    {
+        if (empty($values)) {
+            return 0;
+        } elseif ($update === []) {
+            return (int) $this->insert($values);
+        }
+
+        if (! is_array(reset($values))) {
+            $values = [$values];
+        } else {
+            foreach ($values as $key => $value) {
+                ksort($value);
+
+                $values[$key] = $value;
+            }
+        }
+
+        if (is_null($update)) {
+            $update = array_keys(reset($values));
+        }
+
+        $this->applyBeforeQueryCallbacks();
+
+        $bindings = $this->cleanBindings(array_merge(
+            Arr::flatten($values, 1),
+            collect($update)->reject(function ($value, $key) {
+                return is_int($key);
+            })->all()
+        ));
+
+        return $this->connection->affectingStatement(
+            $this->grammar->compileUpsert($this, $values, (array) $uniqueBy, $update),
+            $bindings
+        );
+    }
+
+    /**
+     * Increment a column's value by a given amount.
+     *
+     * @param  string  $column
+     * @param  float|int  $amount
+     * @param  array  $extra
+     * @return int
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function increment($column, $amount = 1, array $extra = [])
+    {
+        if (! is_numeric($amount)) {
+            throw new InvalidArgumentException('Non-numeric value passed to increment method.');
+        }
+
+        return $this->incrementEach([$column => $amount], $extra);
+    }
+
+    /**
+     * Increment the given column's values by the given amounts.
+     *
+     * @param  array  $columns
+     * @param  array  $extra
+     * @return int
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function incrementEach(array $columns, array $extra = [])
+    {
+        foreach ($columns as $column => $amount) {
+            if (! is_numeric($amount)) {
+                throw new InvalidArgumentException("Non-numeric value passed as increment amount for column: '$column'.");
+            } elseif (! is_string($column)) {
+                throw new InvalidArgumentException('Non-associative array passed to incrementEach method.');
+            }
+
+            $columns[$column] = $this->raw("{$this->grammar->wrap($column)} + $amount");
+        }
+
+        return $this->update(array_merge($columns, $extra));
+    }
+
+    /**
+     * Decrement a column's value by a given amount.
+     *
+     * @param  string  $column
+     * @param  float|int  $amount
+     * @param  array  $extra
+     * @return int
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function decrement($column, $amount = 1, array $extra = [])
+    {
+        if (! is_numeric($amount)) {
+            throw new InvalidArgumentException('Non-numeric value passed to decrement method.');
+        }
+
+        return $this->decrementEach([$column => $amount], $extra);
+    }
+
+    /**
+     * Decrement the given column's values by the given amounts.
+     *
+     * @param  array  $columns
+     * @param  array  $extra
+     * @return int
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function decrementEach(array $columns, array $extra = [])
+    {
+        foreach ($columns as $column => $amount) {
+            if (! is_numeric($amount)) {
+                throw new InvalidArgumentException("Non-numeric value passed as decrement amount for column: '$column'.");
+            } elseif (! is_string($column)) {
+                throw new InvalidArgumentException('Non-associative array passed to decrementEach method.');
+            }
+
+            $columns[$column] = $this->raw("{$this->grammar->wrap($column)} - $amount");
+        }
+
+        return $this->update(array_merge($columns, $extra));
+    }
+
+    /**
+     * Delete records from the database.
+     *
+     * @param  mixed  $id
+     * @return int
+     */
+    public function delete($id = null)
+    {
+        // If an ID is passed to the method, we will set the where clause to check the
+        // ID to let developers to simply and quickly remove a single row from this
+        // database without manually specifying the "where" clauses on the query.
+        if (! is_null($id)) {
+            $this->where($this->from.'.id', '=', $id);
+        }
+
+        $this->applyBeforeQueryCallbacks();
+
+        return $this->connection->delete(
+            $this->grammar->compileDelete($this), $this->cleanBindings(
+                $this->grammar->prepareBindingsForDelete($this->bindings)
+            )
+        );
+    }
+
+    /**
+     * Run a truncate statement on the table.
+     *
+     * @return void
+     */
+    public function truncate()
+    {
+        $this->applyBeforeQueryCallbacks();
+
+        foreach ($this->grammar->compileTruncate($this) as $sql => $bindings) {
+            $this->connection->statement($sql, $bindings);
+        }
+    }
+
+    /**
+     * Get a new instance of the query builder.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function newQuery()
+    {
+        return new static($this->connection, $this->grammar, $this->processor);
+    }
+
+    /**
+     * Create a new query instance for a sub-query.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    protected function forSubQuery()
+    {
+        return $this->newQuery();
+    }
+
+    /**
+     * Create a raw database expression.
+     *
+     * @param  mixed  $value
+     * @return \Illuminate\Database\Query\Expression
+     */
+    public function raw($value)
+    {
+        return $this->connection->raw($value);
+    }
+
+    /**
+     * Get the current query value bindings in a flattened array.
+     *
+     * @return array
+     */
+    public function getBindings()
+    {
+        return Arr::flatten($this->bindings);
+    }
+
+    /**
+     * Get the raw array of bindings.
+     *
+     * @return array
+     */
+    public function getRawBindings()
+    {
+        return $this->bindings;
+    }
+
+    /**
+     * Set the bindings on the query builder.
+     *
+     * @param  array  $bindings
+     * @param  string  $type
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setBindings(array $bindings, $type = 'where')
+    {
+        if (! array_key_exists($type, $this->bindings)) {
+            throw new InvalidArgumentException("Invalid binding type: {$type}.");
+        }
+
+        $this->bindings[$type] = $bindings;
+
+        return $this;
+    }
+
+    /**
+     * Add a binding to the query.
+     *
+     * @param  mixed  $value
+     * @param  string  $type
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function addBinding($value, $type = 'where')
+    {
+        if (! array_key_exists($type, $this->bindings)) {
+            throw new InvalidArgumentException("Invalid binding type: {$type}.");
+        }
+
+        if (is_array($value)) {
+            $this->bindings[$type] = array_values(array_map(
+                [$this, 'castBinding'],
+                array_merge($this->bindings[$type], $value),
+            ));
+        } else {
+            $this->bindings[$type][] = $this->castBinding($value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Cast the given binding value.
+     *
+     * @param  mixed  $value
+     * @return mixed
+     */
+    public function castBinding($value)
+    {
+        if (function_exists('enum_exists') && $value instanceof BackedEnum) {
+            return $value->value;
+        }
+
+        return $value;
+    }
+
+    /**
+     * Merge an array of bindings into our bindings.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return $this
+     */
+    public function mergeBindings(self $query)
+    {
+        $this->bindings = array_merge_recursive($this->bindings, $query->bindings);
+
+        return $this;
+    }
+
+    /**
+     * Remove all of the expressions from a list of bindings.
+     *
+     * @param  array  $bindings
+     * @return array
+     */
+    public function cleanBindings(array $bindings)
+    {
+        return collect($bindings)
+                    ->reject(function ($binding) {
+                        return $binding instanceof Expression;
+                    })
+                    ->map([$this, 'castBinding'])
+                    ->values()
+                    ->all();
+    }
+
+    /**
+     * Get a scalar type value from an unknown type of input.
+     *
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function flattenValue($value)
+    {
+        return is_array($value) ? head(Arr::flatten($value)) : $value;
+    }
+
+    /**
+     * Get the default key name of the table.
+     *
+     * @return string
+     */
+    protected function defaultKeyName()
+    {
+        return 'id';
+    }
+
+    /**
+     * Get the database connection instance.
+     *
+     * @return \Illuminate\Database\ConnectionInterface
+     */
+    public function getConnection()
+    {
+        return $this->connection;
+    }
+
+    /**
+     * Get the database query processor instance.
+     *
+     * @return \Illuminate\Database\Query\Processors\Processor
+     */
+    public function getProcessor()
+    {
+        return $this->processor;
+    }
+
+    /**
+     * Get the query grammar instance.
+     *
+     * @return \Illuminate\Database\Query\Grammars\Grammar
+     */
+    public function getGrammar()
+    {
+        return $this->grammar;
+    }
+
+    /**
+     * Use the "write" PDO connection when executing the query.
+     *
+     * @return $this
+     */
+    public function useWritePdo()
+    {
+        $this->useWritePdo = true;
+
+        return $this;
+    }
+
+    /**
+     * Determine if the value is a query builder instance or a Closure.
+     *
+     * @param  mixed  $value
+     * @return bool
+     */
+    protected function isQueryable($value)
+    {
+        return $value instanceof self ||
+               $value instanceof EloquentBuilder ||
+               $value instanceof Relation ||
+               $value instanceof Closure;
+    }
+
+    /**
+     * Clone the query.
+     *
+     * @return static
+     */
+    public function clone()
+    {
+        return clone $this;
+    }
+
+    /**
+     * Clone the query without the given properties.
+     *
+     * @param  array  $properties
+     * @return static
+     */
+    public function cloneWithout(array $properties)
+    {
+        return tap($this->clone(), function ($clone) use ($properties) {
+            foreach ($properties as $property) {
+                $clone->{$property} = null;
+            }
+        });
+    }
+
+    /**
+     * Clone the query without the given bindings.
+     *
+     * @param  array  $except
+     * @return static
+     */
+    public function cloneWithoutBindings(array $except)
+    {
+        return tap($this->clone(), function ($clone) use ($except) {
+            foreach ($except as $type) {
+                $clone->bindings[$type] = [];
+            }
+        });
+    }
+
+    /**
+     * Dump the current SQL and bindings.
+     *
+     * @return $this
+     */
+    public function dump()
+    {
+        dump($this->toSql(), $this->getBindings());
+
+        return $this;
+    }
+
+    /**
+     * Die and dump the current SQL and bindings.
+     *
+     * @return never
+     */
+    public function dd()
+    {
+        dd($this->toSql(), $this->getBindings());
+    }
+
+    /**
+     * Handle dynamic method calls into the method.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     *
+     * @throws \BadMethodCallException
+     */
+    public function __call($method, $parameters)
+    {
+        if (static::hasMacro($method)) {
+            return $this->macroCall($method, $parameters);
+        }
+
+        if (str_starts_with($method, 'where')) {
+            return $this->dynamicWhere($method, $parameters);
+        }
+
+        static::throwBadMethodCallException($method);
+    }
+}
diff --git a/vendor/illuminate/database/Query/Expression.php b/vendor/illuminate/database/Query/Expression.php
new file mode 100755
index 0000000..de69029
--- /dev/null
+++ b/vendor/illuminate/database/Query/Expression.php
@@ -0,0 +1,44 @@
+value = $value;
+    }
+
+    /**
+     * Get the value of the expression.
+     *
+     * @return mixed
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Get the value of the expression.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return (string) $this->getValue();
+    }
+}
diff --git a/vendor/illuminate/database/Query/Grammars/Grammar.php b/vendor/illuminate/database/Query/Grammars/Grammar.php
new file mode 100755
index 0000000..cd3a0c0
--- /dev/null
+++ b/vendor/illuminate/database/Query/Grammars/Grammar.php
@@ -0,0 +1,1348 @@
+unions || $query->havings) && $query->aggregate) {
+            return $this->compileUnionAggregate($query);
+        }
+
+        // If the query does not have any columns set, we'll set the columns to the
+        // * character to just get all of the columns from the database. Then we
+        // can build the query and concatenate all the pieces together as one.
+        $original = $query->columns;
+
+        if (is_null($query->columns)) {
+            $query->columns = ['*'];
+        }
+
+        // To compile the query, we'll spin through each component of the query and
+        // see if that component exists. If it does we'll just call the compiler
+        // function for the component which is responsible for making the SQL.
+        $sql = trim($this->concatenate(
+            $this->compileComponents($query))
+        );
+
+        if ($query->unions) {
+            $sql = $this->wrapUnion($sql).' '.$this->compileUnions($query);
+        }
+
+        $query->columns = $original;
+
+        return $sql;
+    }
+
+    /**
+     * Compile the components necessary for a select clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return array
+     */
+    protected function compileComponents(Builder $query)
+    {
+        $sql = [];
+
+        foreach ($this->selectComponents as $component) {
+            if (isset($query->$component)) {
+                $method = 'compile'.ucfirst($component);
+
+                $sql[$component] = $this->$method($query, $query->$component);
+            }
+        }
+
+        return $sql;
+    }
+
+    /**
+     * Compile an aggregated select clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $aggregate
+     * @return string
+     */
+    protected function compileAggregate(Builder $query, $aggregate)
+    {
+        $column = $this->columnize($aggregate['columns']);
+
+        // If the query has a "distinct" constraint and we're not asking for all columns
+        // we need to prepend "distinct" onto the column name so that the query takes
+        // it into account when it performs the aggregating operations on the data.
+        if (is_array($query->distinct)) {
+            $column = 'distinct '.$this->columnize($query->distinct);
+        } elseif ($query->distinct && $column !== '*') {
+            $column = 'distinct '.$column;
+        }
+
+        return 'select '.$aggregate['function'].'('.$column.') as aggregate';
+    }
+
+    /**
+     * Compile the "select *" portion of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $columns
+     * @return string|null
+     */
+    protected function compileColumns(Builder $query, $columns)
+    {
+        // If the query is actually performing an aggregating select, we will let that
+        // compiler handle the building of the select clauses, as it will need some
+        // more syntax that is best handled by that function to keep things neat.
+        if (! is_null($query->aggregate)) {
+            return;
+        }
+
+        if ($query->distinct) {
+            $select = 'select distinct ';
+        } else {
+            $select = 'select ';
+        }
+
+        return $select.$this->columnize($columns);
+    }
+
+    /**
+     * Compile the "from" portion of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $table
+     * @return string
+     */
+    protected function compileFrom(Builder $query, $table)
+    {
+        return 'from '.$this->wrapTable($table);
+    }
+
+    /**
+     * Compile the "join" portions of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $joins
+     * @return string
+     */
+    protected function compileJoins(Builder $query, $joins)
+    {
+        return collect($joins)->map(function ($join) use ($query) {
+            $table = $this->wrapTable($join->table);
+
+            $nestedJoins = is_null($join->joins) ? '' : ' '.$this->compileJoins($query, $join->joins);
+
+            $tableAndNestedJoins = is_null($join->joins) ? $table : '('.$table.$nestedJoins.')';
+
+            return trim("{$join->type} join {$tableAndNestedJoins} {$this->compileWheres($join)}");
+        })->implode(' ');
+    }
+
+    /**
+     * Compile the "where" portions of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    public function compileWheres(Builder $query)
+    {
+        // Each type of where clause has its own compiler function, which is responsible
+        // for actually creating the where clauses SQL. This helps keep the code nice
+        // and maintainable since each clause has a very small method that it uses.
+        if (is_null($query->wheres)) {
+            return '';
+        }
+
+        // If we actually have some where clauses, we will strip off the first boolean
+        // operator, which is added by the query builders for convenience so we can
+        // avoid checking for the first clauses in each of the compilers methods.
+        if (count($sql = $this->compileWheresToArray($query)) > 0) {
+            return $this->concatenateWhereClauses($query, $sql);
+        }
+
+        return '';
+    }
+
+    /**
+     * Get an array of all the where clauses for the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return array
+     */
+    protected function compileWheresToArray($query)
+    {
+        return collect($query->wheres)->map(function ($where) use ($query) {
+            return $where['boolean'].' '.$this->{"where{$where['type']}"}($query, $where);
+        })->all();
+    }
+
+    /**
+     * Format the where clause statements into one string.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $sql
+     * @return string
+     */
+    protected function concatenateWhereClauses($query, $sql)
+    {
+        $conjunction = $query instanceof JoinClause ? 'on' : 'where';
+
+        return $conjunction.' '.$this->removeLeadingBoolean(implode(' ', $sql));
+    }
+
+    /**
+     * Compile a raw where clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereRaw(Builder $query, $where)
+    {
+        return $where['sql'];
+    }
+
+    /**
+     * Compile a basic where clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereBasic(Builder $query, $where)
+    {
+        $value = $this->parameter($where['value']);
+
+        $operator = str_replace('?', '??', $where['operator']);
+
+        return $this->wrap($where['column']).' '.$operator.' '.$value;
+    }
+
+    /**
+     * Compile a bitwise operator where clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereBitwise(Builder $query, $where)
+    {
+        return $this->whereBasic($query, $where);
+    }
+
+    /**
+     * Compile a "where in" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereIn(Builder $query, $where)
+    {
+        if (! empty($where['values'])) {
+            return $this->wrap($where['column']).' in ('.$this->parameterize($where['values']).')';
+        }
+
+        return '0 = 1';
+    }
+
+    /**
+     * Compile a "where not in" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereNotIn(Builder $query, $where)
+    {
+        if (! empty($where['values'])) {
+            return $this->wrap($where['column']).' not in ('.$this->parameterize($where['values']).')';
+        }
+
+        return '1 = 1';
+    }
+
+    /**
+     * Compile a "where not in raw" clause.
+     *
+     * For safety, whereIntegerInRaw ensures this method is only used with integer values.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereNotInRaw(Builder $query, $where)
+    {
+        if (! empty($where['values'])) {
+            return $this->wrap($where['column']).' not in ('.implode(', ', $where['values']).')';
+        }
+
+        return '1 = 1';
+    }
+
+    /**
+     * Compile a "where in raw" clause.
+     *
+     * For safety, whereIntegerInRaw ensures this method is only used with integer values.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereInRaw(Builder $query, $where)
+    {
+        if (! empty($where['values'])) {
+            return $this->wrap($where['column']).' in ('.implode(', ', $where['values']).')';
+        }
+
+        return '0 = 1';
+    }
+
+    /**
+     * Compile a "where null" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereNull(Builder $query, $where)
+    {
+        return $this->wrap($where['column']).' is null';
+    }
+
+    /**
+     * Compile a "where not null" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereNotNull(Builder $query, $where)
+    {
+        return $this->wrap($where['column']).' is not null';
+    }
+
+    /**
+     * Compile a "between" where clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereBetween(Builder $query, $where)
+    {
+        $between = $where['not'] ? 'not between' : 'between';
+
+        $min = $this->parameter(is_array($where['values']) ? reset($where['values']) : $where['values'][0]);
+
+        $max = $this->parameter(is_array($where['values']) ? end($where['values']) : $where['values'][1]);
+
+        return $this->wrap($where['column']).' '.$between.' '.$min.' and '.$max;
+    }
+
+    /**
+     * Compile a "between" where clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereBetweenColumns(Builder $query, $where)
+    {
+        $between = $where['not'] ? 'not between' : 'between';
+
+        $min = $this->wrap(is_array($where['values']) ? reset($where['values']) : $where['values'][0]);
+
+        $max = $this->wrap(is_array($where['values']) ? end($where['values']) : $where['values'][1]);
+
+        return $this->wrap($where['column']).' '.$between.' '.$min.' and '.$max;
+    }
+
+    /**
+     * Compile a "where date" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereDate(Builder $query, $where)
+    {
+        return $this->dateBasedWhere('date', $query, $where);
+    }
+
+    /**
+     * Compile a "where time" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereTime(Builder $query, $where)
+    {
+        return $this->dateBasedWhere('time', $query, $where);
+    }
+
+    /**
+     * Compile a "where day" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereDay(Builder $query, $where)
+    {
+        return $this->dateBasedWhere('day', $query, $where);
+    }
+
+    /**
+     * Compile a "where month" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereMonth(Builder $query, $where)
+    {
+        return $this->dateBasedWhere('month', $query, $where);
+    }
+
+    /**
+     * Compile a "where year" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereYear(Builder $query, $where)
+    {
+        return $this->dateBasedWhere('year', $query, $where);
+    }
+
+    /**
+     * Compile a date based where clause.
+     *
+     * @param  string  $type
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function dateBasedWhere($type, Builder $query, $where)
+    {
+        $value = $this->parameter($where['value']);
+
+        return $type.'('.$this->wrap($where['column']).') '.$where['operator'].' '.$value;
+    }
+
+    /**
+     * Compile a where clause comparing two columns.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereColumn(Builder $query, $where)
+    {
+        return $this->wrap($where['first']).' '.$where['operator'].' '.$this->wrap($where['second']);
+    }
+
+    /**
+     * Compile a nested where clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereNested(Builder $query, $where)
+    {
+        // Here we will calculate what portion of the string we need to remove. If this
+        // is a join clause query, we need to remove the "on" portion of the SQL and
+        // if it is a normal query we need to take the leading "where" of queries.
+        $offset = $query instanceof JoinClause ? 3 : 6;
+
+        return '('.substr($this->compileWheres($where['query']), $offset).')';
+    }
+
+    /**
+     * Compile a where condition with a sub-select.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereSub(Builder $query, $where)
+    {
+        $select = $this->compileSelect($where['query']);
+
+        return $this->wrap($where['column']).' '.$where['operator']." ($select)";
+    }
+
+    /**
+     * Compile a where exists clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereExists(Builder $query, $where)
+    {
+        return 'exists ('.$this->compileSelect($where['query']).')';
+    }
+
+    /**
+     * Compile a where exists clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereNotExists(Builder $query, $where)
+    {
+        return 'not exists ('.$this->compileSelect($where['query']).')';
+    }
+
+    /**
+     * Compile a where row values condition.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereRowValues(Builder $query, $where)
+    {
+        $columns = $this->columnize($where['columns']);
+
+        $values = $this->parameterize($where['values']);
+
+        return '('.$columns.') '.$where['operator'].' ('.$values.')';
+    }
+
+    /**
+     * Compile a "where JSON boolean" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereJsonBoolean(Builder $query, $where)
+    {
+        $column = $this->wrapJsonBooleanSelector($where['column']);
+
+        $value = $this->wrapJsonBooleanValue(
+            $this->parameter($where['value'])
+        );
+
+        return $column.' '.$where['operator'].' '.$value;
+    }
+
+    /**
+     * Compile a "where JSON contains" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereJsonContains(Builder $query, $where)
+    {
+        $not = $where['not'] ? 'not ' : '';
+
+        return $not.$this->compileJsonContains(
+            $where['column'],
+            $this->parameter($where['value'])
+        );
+    }
+
+    /**
+     * Compile a "JSON contains" statement into SQL.
+     *
+     * @param  string  $column
+     * @param  string  $value
+     * @return string
+     *
+     * @throws \RuntimeException
+     */
+    protected function compileJsonContains($column, $value)
+    {
+        throw new RuntimeException('This database engine does not support JSON contains operations.');
+    }
+
+    /**
+     * Prepare the binding for a "JSON contains" statement.
+     *
+     * @param  mixed  $binding
+     * @return string
+     */
+    public function prepareBindingForJsonContains($binding)
+    {
+        return json_encode($binding, JSON_UNESCAPED_UNICODE);
+    }
+
+    /**
+     * Compile a "where JSON contains key" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereJsonContainsKey(Builder $query, $where)
+    {
+        $not = $where['not'] ? 'not ' : '';
+
+        return $not.$this->compileJsonContainsKey(
+            $where['column']
+        );
+    }
+
+    /**
+     * Compile a "JSON contains key" statement into SQL.
+     *
+     * @param  string  $column
+     * @return string
+     *
+     * @throws \RuntimeException
+     */
+    protected function compileJsonContainsKey($column)
+    {
+        throw new RuntimeException('This database engine does not support JSON contains key operations.');
+    }
+
+    /**
+     * Compile a "where JSON length" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereJsonLength(Builder $query, $where)
+    {
+        return $this->compileJsonLength(
+            $where['column'],
+            $where['operator'],
+            $this->parameter($where['value'])
+        );
+    }
+
+    /**
+     * Compile a "JSON length" statement into SQL.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  string  $value
+     * @return string
+     *
+     * @throws \RuntimeException
+     */
+    protected function compileJsonLength($column, $operator, $value)
+    {
+        throw new RuntimeException('This database engine does not support JSON length operations.');
+    }
+
+    /**
+     * Compile a "JSON value cast" statement into SQL.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public function compileJsonValueCast($value)
+    {
+        return $value;
+    }
+
+    /**
+     * Compile a "where fulltext" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    public function whereFullText(Builder $query, $where)
+    {
+        throw new RuntimeException('This database engine does not support fulltext search operations.');
+    }
+
+    /**
+     * Compile the "group by" portions of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $groups
+     * @return string
+     */
+    protected function compileGroups(Builder $query, $groups)
+    {
+        return 'group by '.$this->columnize($groups);
+    }
+
+    /**
+     * Compile the "having" portions of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    protected function compileHavings(Builder $query)
+    {
+        return 'having '.$this->removeLeadingBoolean(collect($query->havings)->map(function ($having) {
+            return $having['boolean'].' '.$this->compileHaving($having);
+        })->implode(' '));
+    }
+
+    /**
+     * Compile a single having clause.
+     *
+     * @param  array  $having
+     * @return string
+     */
+    protected function compileHaving(array $having)
+    {
+        // If the having clause is "raw", we can just return the clause straight away
+        // without doing any more processing on it. Otherwise, we will compile the
+        // clause into SQL based on the components that make it up from builder.
+        if ($having['type'] === 'Raw') {
+            return $having['sql'];
+        } elseif ($having['type'] === 'between') {
+            return $this->compileHavingBetween($having);
+        } elseif ($having['type'] === 'Null') {
+            return $this->compileHavingNull($having);
+        } elseif ($having['type'] === 'NotNull') {
+            return $this->compileHavingNotNull($having);
+        } elseif ($having['type'] === 'bit') {
+            return $this->compileHavingBit($having);
+        } elseif ($having['type'] === 'Nested') {
+            return $this->compileNestedHavings($having);
+        }
+
+        return $this->compileBasicHaving($having);
+    }
+
+    /**
+     * Compile a basic having clause.
+     *
+     * @param  array  $having
+     * @return string
+     */
+    protected function compileBasicHaving($having)
+    {
+        $column = $this->wrap($having['column']);
+
+        $parameter = $this->parameter($having['value']);
+
+        return $column.' '.$having['operator'].' '.$parameter;
+    }
+
+    /**
+     * Compile a "between" having clause.
+     *
+     * @param  array  $having
+     * @return string
+     */
+    protected function compileHavingBetween($having)
+    {
+        $between = $having['not'] ? 'not between' : 'between';
+
+        $column = $this->wrap($having['column']);
+
+        $min = $this->parameter(head($having['values']));
+
+        $max = $this->parameter(last($having['values']));
+
+        return $column.' '.$between.' '.$min.' and '.$max;
+    }
+
+    /**
+     * Compile a having null clause.
+     *
+     * @param  array  $having
+     * @return string
+     */
+    protected function compileHavingNull($having)
+    {
+        $column = $this->wrap($having['column']);
+
+        return $column.' is null';
+    }
+
+    /**
+     * Compile a having not null clause.
+     *
+     * @param  array  $having
+     * @return string
+     */
+    protected function compileHavingNotNull($having)
+    {
+        $column = $this->wrap($having['column']);
+
+        return $column.' is not null';
+    }
+
+    /**
+     * Compile a having clause involving a bit operator.
+     *
+     * @param  array  $having
+     * @return string
+     */
+    protected function compileHavingBit($having)
+    {
+        $column = $this->wrap($having['column']);
+
+        $parameter = $this->parameter($having['value']);
+
+        return '('.$column.' '.$having['operator'].' '.$parameter.') != 0';
+    }
+
+    /**
+     * Compile a nested having clause.
+     *
+     * @param  array  $having
+     * @return string
+     */
+    protected function compileNestedHavings($having)
+    {
+        return '('.substr($this->compileHavings($having['query']), 7).')';
+    }
+
+    /**
+     * Compile the "order by" portions of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $orders
+     * @return string
+     */
+    protected function compileOrders(Builder $query, $orders)
+    {
+        if (! empty($orders)) {
+            return 'order by '.implode(', ', $this->compileOrdersToArray($query, $orders));
+        }
+
+        return '';
+    }
+
+    /**
+     * Compile the query orders to an array.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $orders
+     * @return array
+     */
+    protected function compileOrdersToArray(Builder $query, $orders)
+    {
+        return array_map(function ($order) {
+            return $order['sql'] ?? $this->wrap($order['column']).' '.$order['direction'];
+        }, $orders);
+    }
+
+    /**
+     * Compile the random statement into SQL.
+     *
+     * @param  string|int  $seed
+     * @return string
+     */
+    public function compileRandom($seed)
+    {
+        return 'RANDOM()';
+    }
+
+    /**
+     * Compile the "limit" portions of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  int  $limit
+     * @return string
+     */
+    protected function compileLimit(Builder $query, $limit)
+    {
+        return 'limit '.(int) $limit;
+    }
+
+    /**
+     * Compile the "offset" portions of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  int  $offset
+     * @return string
+     */
+    protected function compileOffset(Builder $query, $offset)
+    {
+        return 'offset '.(int) $offset;
+    }
+
+    /**
+     * Compile the "union" queries attached to the main query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    protected function compileUnions(Builder $query)
+    {
+        $sql = '';
+
+        foreach ($query->unions as $union) {
+            $sql .= $this->compileUnion($union);
+        }
+
+        if (! empty($query->unionOrders)) {
+            $sql .= ' '.$this->compileOrders($query, $query->unionOrders);
+        }
+
+        if (isset($query->unionLimit)) {
+            $sql .= ' '.$this->compileLimit($query, $query->unionLimit);
+        }
+
+        if (isset($query->unionOffset)) {
+            $sql .= ' '.$this->compileOffset($query, $query->unionOffset);
+        }
+
+        return ltrim($sql);
+    }
+
+    /**
+     * Compile a single union statement.
+     *
+     * @param  array  $union
+     * @return string
+     */
+    protected function compileUnion(array $union)
+    {
+        $conjunction = $union['all'] ? ' union all ' : ' union ';
+
+        return $conjunction.$this->wrapUnion($union['query']->toSql());
+    }
+
+    /**
+     * Wrap a union subquery in parentheses.
+     *
+     * @param  string  $sql
+     * @return string
+     */
+    protected function wrapUnion($sql)
+    {
+        return '('.$sql.')';
+    }
+
+    /**
+     * Compile a union aggregate query into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    protected function compileUnionAggregate(Builder $query)
+    {
+        $sql = $this->compileAggregate($query, $query->aggregate);
+
+        $query->aggregate = null;
+
+        return $sql.' from ('.$this->compileSelect($query).') as '.$this->wrapTable('temp_table');
+    }
+
+    /**
+     * Compile an exists statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    public function compileExists(Builder $query)
+    {
+        $select = $this->compileSelect($query);
+
+        return "select exists({$select}) as {$this->wrap('exists')}";
+    }
+
+    /**
+     * Compile an insert statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    public function compileInsert(Builder $query, array $values)
+    {
+        // Essentially we will force every insert to be treated as a batch insert which
+        // simply makes creating the SQL easier for us since we can utilize the same
+        // basic routine regardless of an amount of records given to us to insert.
+        $table = $this->wrapTable($query->from);
+
+        if (empty($values)) {
+            return "insert into {$table} default values";
+        }
+
+        if (! is_array(reset($values))) {
+            $values = [$values];
+        }
+
+        $columns = $this->columnize(array_keys(reset($values)));
+
+        // We need to build a list of parameter place-holders of values that are bound
+        // to the query. Each insert should have the exact same number of parameter
+        // bindings so we will loop through the record and parameterize them all.
+        $parameters = collect($values)->map(function ($record) {
+            return '('.$this->parameterize($record).')';
+        })->implode(', ');
+
+        return "insert into $table ($columns) values $parameters";
+    }
+
+    /**
+     * Compile an insert ignore statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     *
+     * @throws \RuntimeException
+     */
+    public function compileInsertOrIgnore(Builder $query, array $values)
+    {
+        throw new RuntimeException('This database engine does not support inserting while ignoring errors.');
+    }
+
+    /**
+     * Compile an insert and get ID statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @param  string  $sequence
+     * @return string
+     */
+    public function compileInsertGetId(Builder $query, $values, $sequence)
+    {
+        return $this->compileInsert($query, $values);
+    }
+
+    /**
+     * Compile an insert statement using a subquery into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $columns
+     * @param  string  $sql
+     * @return string
+     */
+    public function compileInsertUsing(Builder $query, array $columns, string $sql)
+    {
+        return "insert into {$this->wrapTable($query->from)} ({$this->columnize($columns)}) $sql";
+    }
+
+    /**
+     * Compile an update statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    public function compileUpdate(Builder $query, array $values)
+    {
+        $table = $this->wrapTable($query->from);
+
+        $columns = $this->compileUpdateColumns($query, $values);
+
+        $where = $this->compileWheres($query);
+
+        return trim(
+            isset($query->joins)
+                ? $this->compileUpdateWithJoins($query, $table, $columns, $where)
+                : $this->compileUpdateWithoutJoins($query, $table, $columns, $where)
+        );
+    }
+
+    /**
+     * Compile the columns for an update statement.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    protected function compileUpdateColumns(Builder $query, array $values)
+    {
+        return collect($values)->map(function ($value, $key) {
+            return $this->wrap($key).' = '.$this->parameter($value);
+        })->implode(', ');
+    }
+
+    /**
+     * Compile an update statement without joins into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $table
+     * @param  string  $columns
+     * @param  string  $where
+     * @return string
+     */
+    protected function compileUpdateWithoutJoins(Builder $query, $table, $columns, $where)
+    {
+        return "update {$table} set {$columns} {$where}";
+    }
+
+    /**
+     * Compile an update statement with joins into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $table
+     * @param  string  $columns
+     * @param  string  $where
+     * @return string
+     */
+    protected function compileUpdateWithJoins(Builder $query, $table, $columns, $where)
+    {
+        $joins = $this->compileJoins($query, $query->joins);
+
+        return "update {$table} {$joins} set {$columns} {$where}";
+    }
+
+    /**
+     * Compile an "upsert" statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @param  array  $uniqueBy
+     * @param  array  $update
+     * @return string
+     *
+     * @throws \RuntimeException
+     */
+    public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
+    {
+        throw new RuntimeException('This database engine does not support upserts.');
+    }
+
+    /**
+     * Prepare the bindings for an update statement.
+     *
+     * @param  array  $bindings
+     * @param  array  $values
+     * @return array
+     */
+    public function prepareBindingsForUpdate(array $bindings, array $values)
+    {
+        $cleanBindings = Arr::except($bindings, ['select', 'join']);
+
+        return array_values(
+            array_merge($bindings['join'], $values, Arr::flatten($cleanBindings))
+        );
+    }
+
+    /**
+     * Compile a delete statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    public function compileDelete(Builder $query)
+    {
+        $table = $this->wrapTable($query->from);
+
+        $where = $this->compileWheres($query);
+
+        return trim(
+            isset($query->joins)
+                ? $this->compileDeleteWithJoins($query, $table, $where)
+                : $this->compileDeleteWithoutJoins($query, $table, $where)
+        );
+    }
+
+    /**
+     * Compile a delete statement without joins into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $table
+     * @param  string  $where
+     * @return string
+     */
+    protected function compileDeleteWithoutJoins(Builder $query, $table, $where)
+    {
+        return "delete from {$table} {$where}";
+    }
+
+    /**
+     * Compile a delete statement with joins into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $table
+     * @param  string  $where
+     * @return string
+     */
+    protected function compileDeleteWithJoins(Builder $query, $table, $where)
+    {
+        $alias = last(explode(' as ', $table));
+
+        $joins = $this->compileJoins($query, $query->joins);
+
+        return "delete {$alias} from {$table} {$joins} {$where}";
+    }
+
+    /**
+     * Prepare the bindings for a delete statement.
+     *
+     * @param  array  $bindings
+     * @return array
+     */
+    public function prepareBindingsForDelete(array $bindings)
+    {
+        return Arr::flatten(
+            Arr::except($bindings, 'select')
+        );
+    }
+
+    /**
+     * Compile a truncate table statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return array
+     */
+    public function compileTruncate(Builder $query)
+    {
+        return ['truncate table '.$this->wrapTable($query->from) => []];
+    }
+
+    /**
+     * Compile the lock into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  bool|string  $value
+     * @return string
+     */
+    protected function compileLock(Builder $query, $value)
+    {
+        return is_string($value) ? $value : '';
+    }
+
+    /**
+     * Determine if the grammar supports savepoints.
+     *
+     * @return bool
+     */
+    public function supportsSavepoints()
+    {
+        return true;
+    }
+
+    /**
+     * Compile the SQL statement to define a savepoint.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    public function compileSavepoint($name)
+    {
+        return 'SAVEPOINT '.$name;
+    }
+
+    /**
+     * Compile the SQL statement to execute a savepoint rollback.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    public function compileSavepointRollBack($name)
+    {
+        return 'ROLLBACK TO SAVEPOINT '.$name;
+    }
+
+    /**
+     * Wrap the given JSON selector for boolean values.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapJsonBooleanSelector($value)
+    {
+        return $this->wrapJsonSelector($value);
+    }
+
+    /**
+     * Wrap the given JSON boolean value.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapJsonBooleanValue($value)
+    {
+        return $value;
+    }
+
+    /**
+     * Concatenate an array of segments, removing empties.
+     *
+     * @param  array  $segments
+     * @return string
+     */
+    protected function concatenate($segments)
+    {
+        return implode(' ', array_filter($segments, function ($value) {
+            return (string) $value !== '';
+        }));
+    }
+
+    /**
+     * Remove the leading boolean from a statement.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function removeLeadingBoolean($value)
+    {
+        return preg_replace('/and |or /i', '', $value, 1);
+    }
+
+    /**
+     * Get the grammar specific operators.
+     *
+     * @return array
+     */
+    public function getOperators()
+    {
+        return $this->operators;
+    }
+
+    /**
+     * Get the grammar specific bitwise operators.
+     *
+     * @return array
+     */
+    public function getBitwiseOperators()
+    {
+        return $this->bitwiseOperators;
+    }
+}
diff --git a/vendor/illuminate/database/Query/Grammars/MySqlGrammar.php b/vendor/illuminate/database/Query/Grammars/MySqlGrammar.php
new file mode 100755
index 0000000..131f8af
--- /dev/null
+++ b/vendor/illuminate/database/Query/Grammars/MySqlGrammar.php
@@ -0,0 +1,381 @@
+isJsonSelector($where['column'])) {
+            [$field, $path] = $this->wrapJsonFieldAndPath($where['column']);
+
+            return '(json_extract('.$field.$path.') is null OR json_type(json_extract('.$field.$path.')) = \'NULL\')';
+        }
+
+        return parent::whereNull($query, $where);
+    }
+
+    /**
+     * Add a "where not null" clause to the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereNotNull(Builder $query, $where)
+    {
+        if ($this->isJsonSelector($where['column'])) {
+            [$field, $path] = $this->wrapJsonFieldAndPath($where['column']);
+
+            return '(json_extract('.$field.$path.') is not null AND json_type(json_extract('.$field.$path.')) != \'NULL\')';
+        }
+
+        return parent::whereNotNull($query, $where);
+    }
+
+    /**
+     * Compile a "where fulltext" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    public function whereFullText(Builder $query, $where)
+    {
+        $columns = $this->columnize($where['columns']);
+
+        $value = $this->parameter($where['value']);
+
+        $mode = ($where['options']['mode'] ?? []) === 'boolean'
+            ? ' in boolean mode'
+            : ' in natural language mode';
+
+        $expanded = ($where['options']['expanded'] ?? []) && ($where['options']['mode'] ?? []) !== 'boolean'
+            ? ' with query expansion'
+            : '';
+
+        return "match ({$columns}) against (".$value."{$mode}{$expanded})";
+    }
+
+    /**
+     * Compile the index hints for the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  \Illuminate\Database\Query\IndexHint  $indexHint
+     * @return string
+     */
+    protected function compileIndexHint(Builder $query, $indexHint)
+    {
+        return match ($indexHint->type) {
+            'hint' => "use index ({$indexHint->index})",
+            'force' => "force index ({$indexHint->index})",
+            default => "ignore index ({$indexHint->index})",
+        };
+    }
+
+    /**
+     * Compile an insert ignore statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    public function compileInsertOrIgnore(Builder $query, array $values)
+    {
+        return Str::replaceFirst('insert', 'insert ignore', $this->compileInsert($query, $values));
+    }
+
+    /**
+     * Compile a "JSON contains" statement into SQL.
+     *
+     * @param  string  $column
+     * @param  string  $value
+     * @return string
+     */
+    protected function compileJsonContains($column, $value)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($column);
+
+        return 'json_contains('.$field.', '.$value.$path.')';
+    }
+
+    /**
+     * Compile a "JSON contains key" statement into SQL.
+     *
+     * @param  string  $column
+     * @return string
+     */
+    protected function compileJsonContainsKey($column)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($column);
+
+        return 'ifnull(json_contains_path('.$field.', \'one\''.$path.'), 0)';
+    }
+
+    /**
+     * Compile a "JSON length" statement into SQL.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  string  $value
+     * @return string
+     */
+    protected function compileJsonLength($column, $operator, $value)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($column);
+
+        return 'json_length('.$field.$path.') '.$operator.' '.$value;
+    }
+
+    /**
+     * Compile a "JSON value cast" statement into SQL.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public function compileJsonValueCast($value)
+    {
+        return 'cast('.$value.' as json)';
+    }
+
+    /**
+     * Compile the random statement into SQL.
+     *
+     * @param  string|int  $seed
+     * @return string
+     */
+    public function compileRandom($seed)
+    {
+        return 'RAND('.$seed.')';
+    }
+
+    /**
+     * Compile the lock into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  bool|string  $value
+     * @return string
+     */
+    protected function compileLock(Builder $query, $value)
+    {
+        if (! is_string($value)) {
+            return $value ? 'for update' : 'lock in share mode';
+        }
+
+        return $value;
+    }
+
+    /**
+     * Compile an insert statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    public function compileInsert(Builder $query, array $values)
+    {
+        if (empty($values)) {
+            $values = [[]];
+        }
+
+        return parent::compileInsert($query, $values);
+    }
+
+    /**
+     * Compile the columns for an update statement.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    protected function compileUpdateColumns(Builder $query, array $values)
+    {
+        return collect($values)->map(function ($value, $key) {
+            if ($this->isJsonSelector($key)) {
+                return $this->compileJsonUpdateColumn($key, $value);
+            }
+
+            return $this->wrap($key).' = '.$this->parameter($value);
+        })->implode(', ');
+    }
+
+    /**
+     * Compile an "upsert" statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @param  array  $uniqueBy
+     * @param  array  $update
+     * @return string
+     */
+    public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
+    {
+        $useUpsertAlias = $query->connection->getConfig('use_upsert_alias');
+
+        $sql = $this->compileInsert($query, $values);
+
+        if ($useUpsertAlias) {
+            $sql .= ' as laravel_upsert_alias';
+        }
+
+        $sql .= ' on duplicate key update ';
+
+        $columns = collect($update)->map(function ($value, $key) use ($useUpsertAlias) {
+            if (! is_numeric($key)) {
+                return $this->wrap($key).' = '.$this->parameter($value);
+            }
+
+            return $useUpsertAlias
+                ? $this->wrap($value).' = '.$this->wrap('laravel_upsert_alias').'.'.$this->wrap($value)
+                : $this->wrap($value).' = values('.$this->wrap($value).')';
+        })->implode(', ');
+
+        return $sql.$columns;
+    }
+
+    /**
+     * Prepare a JSON column being updated using the JSON_SET function.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return string
+     */
+    protected function compileJsonUpdateColumn($key, $value)
+    {
+        if (is_bool($value)) {
+            $value = $value ? 'true' : 'false';
+        } elseif (is_array($value)) {
+            $value = 'cast(? as json)';
+        } else {
+            $value = $this->parameter($value);
+        }
+
+        [$field, $path] = $this->wrapJsonFieldAndPath($key);
+
+        return "{$field} = json_set({$field}{$path}, {$value})";
+    }
+
+    /**
+     * Compile an update statement without joins into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $table
+     * @param  string  $columns
+     * @param  string  $where
+     * @return string
+     */
+    protected function compileUpdateWithoutJoins(Builder $query, $table, $columns, $where)
+    {
+        $sql = parent::compileUpdateWithoutJoins($query, $table, $columns, $where);
+
+        if (! empty($query->orders)) {
+            $sql .= ' '.$this->compileOrders($query, $query->orders);
+        }
+
+        if (isset($query->limit)) {
+            $sql .= ' '.$this->compileLimit($query, $query->limit);
+        }
+
+        return $sql;
+    }
+
+    /**
+     * Prepare the bindings for an update statement.
+     *
+     * Booleans, integers, and doubles are inserted into JSON updates as raw values.
+     *
+     * @param  array  $bindings
+     * @param  array  $values
+     * @return array
+     */
+    public function prepareBindingsForUpdate(array $bindings, array $values)
+    {
+        $values = collect($values)->reject(function ($value, $column) {
+            return $this->isJsonSelector($column) && is_bool($value);
+        })->map(function ($value) {
+            return is_array($value) ? json_encode($value) : $value;
+        })->all();
+
+        return parent::prepareBindingsForUpdate($bindings, $values);
+    }
+
+    /**
+     * Compile a delete query that does not use joins.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $table
+     * @param  string  $where
+     * @return string
+     */
+    protected function compileDeleteWithoutJoins(Builder $query, $table, $where)
+    {
+        $sql = parent::compileDeleteWithoutJoins($query, $table, $where);
+
+        // When using MySQL, delete statements may contain order by statements and limits
+        // so we will compile both of those here. Once we have finished compiling this
+        // we will return the completed SQL statement so it will be executed for us.
+        if (! empty($query->orders)) {
+            $sql .= ' '.$this->compileOrders($query, $query->orders);
+        }
+
+        if (isset($query->limit)) {
+            $sql .= ' '.$this->compileLimit($query, $query->limit);
+        }
+
+        return $sql;
+    }
+
+    /**
+     * Wrap a single string in keyword identifiers.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapValue($value)
+    {
+        return $value === '*' ? $value : '`'.str_replace('`', '``', $value).'`';
+    }
+
+    /**
+     * Wrap the given JSON selector.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapJsonSelector($value)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($value);
+
+        return 'json_unquote(json_extract('.$field.$path.'))';
+    }
+
+    /**
+     * Wrap the given JSON selector for boolean values.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapJsonBooleanSelector($value)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($value);
+
+        return 'json_extract('.$field.$path.')';
+    }
+}
diff --git a/vendor/illuminate/database/Query/Grammars/PostgresGrammar.php b/vendor/illuminate/database/Query/Grammars/PostgresGrammar.php
new file mode 100755
index 0000000..ad4678b
--- /dev/null
+++ b/vendor/illuminate/database/Query/Grammars/PostgresGrammar.php
@@ -0,0 +1,701 @@
+', '<=', '>=', '<>', '!=',
+        'like', 'not like', 'between', 'ilike', 'not ilike',
+        '~', '&', '|', '#', '<<', '>>', '<<=', '>>=',
+        '&&', '@>', '<@', '?', '?|', '?&', '||', '-', '@?', '@@', '#-',
+        'is distinct from', 'is not distinct from',
+    ];
+
+    /**
+     * The grammar specific bitwise operators.
+     *
+     * @var array
+     */
+    protected $bitwiseOperators = [
+        '~', '&', '|', '#', '<<', '>>', '<<=', '>>=',
+    ];
+
+    /**
+     * Compile a basic where clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereBasic(Builder $query, $where)
+    {
+        if (str_contains(strtolower($where['operator']), 'like')) {
+            return sprintf(
+                '%s::text %s %s',
+                $this->wrap($where['column']),
+                $where['operator'],
+                $this->parameter($where['value'])
+            );
+        }
+
+        return parent::whereBasic($query, $where);
+    }
+
+    /**
+     * Compile a bitwise operator where clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereBitwise(Builder $query, $where)
+    {
+        $value = $this->parameter($where['value']);
+
+        $operator = str_replace('?', '??', $where['operator']);
+
+        return '('.$this->wrap($where['column']).' '.$operator.' '.$value.')::bool';
+    }
+
+    /**
+     * Compile a "where date" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereDate(Builder $query, $where)
+    {
+        $value = $this->parameter($where['value']);
+
+        return $this->wrap($where['column']).'::date '.$where['operator'].' '.$value;
+    }
+
+    /**
+     * Compile a "where time" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereTime(Builder $query, $where)
+    {
+        $value = $this->parameter($where['value']);
+
+        return $this->wrap($where['column']).'::time '.$where['operator'].' '.$value;
+    }
+
+    /**
+     * Compile a date based where clause.
+     *
+     * @param  string  $type
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function dateBasedWhere($type, Builder $query, $where)
+    {
+        $value = $this->parameter($where['value']);
+
+        return 'extract('.$type.' from '.$this->wrap($where['column']).') '.$where['operator'].' '.$value;
+    }
+
+    /**
+     * Compile a "where fulltext" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    public function whereFullText(Builder $query, $where)
+    {
+        $language = $where['options']['language'] ?? 'english';
+
+        if (! in_array($language, $this->validFullTextLanguages())) {
+            $language = 'english';
+        }
+
+        $columns = collect($where['columns'])->map(function ($column) use ($language) {
+            return "to_tsvector('{$language}', {$this->wrap($column)})";
+        })->implode(' || ');
+
+        $mode = 'plainto_tsquery';
+
+        if (($where['options']['mode'] ?? []) === 'phrase') {
+            $mode = 'phraseto_tsquery';
+        }
+
+        if (($where['options']['mode'] ?? []) === 'websearch') {
+            $mode = 'websearch_to_tsquery';
+        }
+
+        return "({$columns}) @@ {$mode}('{$language}', {$this->parameter($where['value'])})";
+    }
+
+    /**
+     * Get an array of valid full text languages.
+     *
+     * @return array
+     */
+    protected function validFullTextLanguages()
+    {
+        return [
+            'simple',
+            'arabic',
+            'danish',
+            'dutch',
+            'english',
+            'finnish',
+            'french',
+            'german',
+            'hungarian',
+            'indonesian',
+            'irish',
+            'italian',
+            'lithuanian',
+            'nepali',
+            'norwegian',
+            'portuguese',
+            'romanian',
+            'russian',
+            'spanish',
+            'swedish',
+            'tamil',
+            'turkish',
+        ];
+    }
+
+    /**
+     * Compile the "select *" portion of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $columns
+     * @return string|null
+     */
+    protected function compileColumns(Builder $query, $columns)
+    {
+        // If the query is actually performing an aggregating select, we will let that
+        // compiler handle the building of the select clauses, as it will need some
+        // more syntax that is best handled by that function to keep things neat.
+        if (! is_null($query->aggregate)) {
+            return;
+        }
+
+        if (is_array($query->distinct)) {
+            $select = 'select distinct on ('.$this->columnize($query->distinct).') ';
+        } elseif ($query->distinct) {
+            $select = 'select distinct ';
+        } else {
+            $select = 'select ';
+        }
+
+        return $select.$this->columnize($columns);
+    }
+
+    /**
+     * Compile a "JSON contains" statement into SQL.
+     *
+     * @param  string  $column
+     * @param  string  $value
+     * @return string
+     */
+    protected function compileJsonContains($column, $value)
+    {
+        $column = str_replace('->>', '->', $this->wrap($column));
+
+        return '('.$column.')::jsonb @> '.$value;
+    }
+
+    /**
+     * Compile a "JSON contains key" statement into SQL.
+     *
+     * @param  string  $column
+     * @return string
+     */
+    protected function compileJsonContainsKey($column)
+    {
+        $segments = explode('->', $column);
+
+        $lastSegment = array_pop($segments);
+
+        if (filter_var($lastSegment, FILTER_VALIDATE_INT) !== false) {
+            $i = $lastSegment;
+        } elseif (preg_match('/\[(-?[0-9]+)\]$/', $lastSegment, $matches)) {
+            $segments[] = Str::beforeLast($lastSegment, $matches[0]);
+
+            $i = $matches[1];
+        }
+
+        $column = str_replace('->>', '->', $this->wrap(implode('->', $segments)));
+
+        if (isset($i)) {
+            return vsprintf('case when %s then %s else false end', [
+                'jsonb_typeof(('.$column.")::jsonb) = 'array'",
+                'jsonb_array_length(('.$column.')::jsonb) >= '.($i < 0 ? abs($i) : $i + 1),
+            ]);
+        }
+
+        $key = "'".str_replace("'", "''", $lastSegment)."'";
+
+        return 'coalesce(('.$column.')::jsonb ?? '.$key.', false)';
+    }
+
+    /**
+     * Compile a "JSON length" statement into SQL.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  string  $value
+     * @return string
+     */
+    protected function compileJsonLength($column, $operator, $value)
+    {
+        $column = str_replace('->>', '->', $this->wrap($column));
+
+        return 'jsonb_array_length(('.$column.')::jsonb) '.$operator.' '.$value;
+    }
+
+    /**
+     * Compile a single having clause.
+     *
+     * @param  array  $having
+     * @return string
+     */
+    protected function compileHaving(array $having)
+    {
+        if ($having['type'] === 'Bitwise') {
+            return $this->compileHavingBitwise($having);
+        }
+
+        return parent::compileHaving($having);
+    }
+
+    /**
+     * Compile a having clause involving a bitwise operator.
+     *
+     * @param  array  $having
+     * @return string
+     */
+    protected function compileHavingBitwise($having)
+    {
+        $column = $this->wrap($having['column']);
+
+        $parameter = $this->parameter($having['value']);
+
+        return '('.$column.' '.$having['operator'].' '.$parameter.')::bool';
+    }
+
+    /**
+     * Compile the lock into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  bool|string  $value
+     * @return string
+     */
+    protected function compileLock(Builder $query, $value)
+    {
+        if (! is_string($value)) {
+            return $value ? 'for update' : 'for share';
+        }
+
+        return $value;
+    }
+
+    /**
+     * Compile an insert ignore statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    public function compileInsertOrIgnore(Builder $query, array $values)
+    {
+        return $this->compileInsert($query, $values).' on conflict do nothing';
+    }
+
+    /**
+     * Compile an insert and get ID statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @param  string  $sequence
+     * @return string
+     */
+    public function compileInsertGetId(Builder $query, $values, $sequence)
+    {
+        return $this->compileInsert($query, $values).' returning '.$this->wrap($sequence ?: 'id');
+    }
+
+    /**
+     * Compile an update statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    public function compileUpdate(Builder $query, array $values)
+    {
+        if (isset($query->joins) || isset($query->limit)) {
+            return $this->compileUpdateWithJoinsOrLimit($query, $values);
+        }
+
+        return parent::compileUpdate($query, $values);
+    }
+
+    /**
+     * Compile the columns for an update statement.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    protected function compileUpdateColumns(Builder $query, array $values)
+    {
+        return collect($values)->map(function ($value, $key) {
+            $column = last(explode('.', $key));
+
+            if ($this->isJsonSelector($key)) {
+                return $this->compileJsonUpdateColumn($column, $value);
+            }
+
+            return $this->wrap($column).' = '.$this->parameter($value);
+        })->implode(', ');
+    }
+
+    /**
+     * Compile an "upsert" statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @param  array  $uniqueBy
+     * @param  array  $update
+     * @return string
+     */
+    public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
+    {
+        $sql = $this->compileInsert($query, $values);
+
+        $sql .= ' on conflict ('.$this->columnize($uniqueBy).') do update set ';
+
+        $columns = collect($update)->map(function ($value, $key) {
+            return is_numeric($key)
+                ? $this->wrap($value).' = '.$this->wrapValue('excluded').'.'.$this->wrap($value)
+                : $this->wrap($key).' = '.$this->parameter($value);
+        })->implode(', ');
+
+        return $sql.$columns;
+    }
+
+    /**
+     * Prepares a JSON column being updated using the JSONB_SET function.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @return string
+     */
+    protected function compileJsonUpdateColumn($key, $value)
+    {
+        $segments = explode('->', $key);
+
+        $field = $this->wrap(array_shift($segments));
+
+        $path = "'{".implode(',', $this->wrapJsonPathAttributes($segments, '"'))."}'";
+
+        return "{$field} = jsonb_set({$field}::jsonb, {$path}, {$this->parameter($value)})";
+    }
+
+    /**
+     * Compile an update from statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    public function compileUpdateFrom(Builder $query, $values)
+    {
+        $table = $this->wrapTable($query->from);
+
+        // Each one of the columns in the update statements needs to be wrapped in the
+        // keyword identifiers, also a place-holder needs to be created for each of
+        // the values in the list of bindings so we can make the sets statements.
+        $columns = $this->compileUpdateColumns($query, $values);
+
+        $from = '';
+
+        if (isset($query->joins)) {
+            // When using Postgres, updates with joins list the joined tables in the from
+            // clause, which is different than other systems like MySQL. Here, we will
+            // compile out the tables that are joined and add them to a from clause.
+            $froms = collect($query->joins)->map(function ($join) {
+                return $this->wrapTable($join->table);
+            })->all();
+
+            if (count($froms) > 0) {
+                $from = ' from '.implode(', ', $froms);
+            }
+        }
+
+        $where = $this->compileUpdateWheres($query);
+
+        return trim("update {$table} set {$columns}{$from} {$where}");
+    }
+
+    /**
+     * Compile the additional where clauses for updates with joins.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    protected function compileUpdateWheres(Builder $query)
+    {
+        $baseWheres = $this->compileWheres($query);
+
+        if (! isset($query->joins)) {
+            return $baseWheres;
+        }
+
+        // Once we compile the join constraints, we will either use them as the where
+        // clause or append them to the existing base where clauses. If we need to
+        // strip the leading boolean we will do so when using as the only where.
+        $joinWheres = $this->compileUpdateJoinWheres($query);
+
+        if (trim($baseWheres) == '') {
+            return 'where '.$this->removeLeadingBoolean($joinWheres);
+        }
+
+        return $baseWheres.' '.$joinWheres;
+    }
+
+    /**
+     * Compile the "join" clause where clauses for an update.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    protected function compileUpdateJoinWheres(Builder $query)
+    {
+        $joinWheres = [];
+
+        // Here we will just loop through all of the join constraints and compile them
+        // all out then implode them. This should give us "where" like syntax after
+        // everything has been built and then we will join it to the real wheres.
+        foreach ($query->joins as $join) {
+            foreach ($join->wheres as $where) {
+                $method = "where{$where['type']}";
+
+                $joinWheres[] = $where['boolean'].' '.$this->$method($query, $where);
+            }
+        }
+
+        return implode(' ', $joinWheres);
+    }
+
+    /**
+     * Prepare the bindings for an update statement.
+     *
+     * @param  array  $bindings
+     * @param  array  $values
+     * @return array
+     */
+    public function prepareBindingsForUpdateFrom(array $bindings, array $values)
+    {
+        $values = collect($values)->map(function ($value, $column) {
+            return is_array($value) || ($this->isJsonSelector($column) && ! $this->isExpression($value))
+                ? json_encode($value)
+                : $value;
+        })->all();
+
+        $bindingsWithoutWhere = Arr::except($bindings, ['select', 'where']);
+
+        return array_values(
+            array_merge($values, $bindings['where'], Arr::flatten($bindingsWithoutWhere))
+        );
+    }
+
+    /**
+     * Compile an update statement with joins or limit into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    protected function compileUpdateWithJoinsOrLimit(Builder $query, array $values)
+    {
+        $table = $this->wrapTable($query->from);
+
+        $columns = $this->compileUpdateColumns($query, $values);
+
+        $alias = last(preg_split('/\s+as\s+/i', $query->from));
+
+        $selectSql = $this->compileSelect($query->select($alias.'.ctid'));
+
+        return "update {$table} set {$columns} where {$this->wrap('ctid')} in ({$selectSql})";
+    }
+
+    /**
+     * Prepare the bindings for an update statement.
+     *
+     * @param  array  $bindings
+     * @param  array  $values
+     * @return array
+     */
+    public function prepareBindingsForUpdate(array $bindings, array $values)
+    {
+        $values = collect($values)->map(function ($value, $column) {
+            return is_array($value) || ($this->isJsonSelector($column) && ! $this->isExpression($value))
+                ? json_encode($value)
+                : $value;
+        })->all();
+
+        $cleanBindings = Arr::except($bindings, 'select');
+
+        return array_values(
+            array_merge($values, Arr::flatten($cleanBindings))
+        );
+    }
+
+    /**
+     * Compile a delete statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    public function compileDelete(Builder $query)
+    {
+        if (isset($query->joins) || isset($query->limit)) {
+            return $this->compileDeleteWithJoinsOrLimit($query);
+        }
+
+        return parent::compileDelete($query);
+    }
+
+    /**
+     * Compile a delete statement with joins or limit into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    protected function compileDeleteWithJoinsOrLimit(Builder $query)
+    {
+        $table = $this->wrapTable($query->from);
+
+        $alias = last(preg_split('/\s+as\s+/i', $query->from));
+
+        $selectSql = $this->compileSelect($query->select($alias.'.ctid'));
+
+        return "delete from {$table} where {$this->wrap('ctid')} in ({$selectSql})";
+    }
+
+    /**
+     * Compile a truncate table statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return array
+     */
+    public function compileTruncate(Builder $query)
+    {
+        return ['truncate '.$this->wrapTable($query->from).' restart identity cascade' => []];
+    }
+
+    /**
+     * Wrap the given JSON selector.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapJsonSelector($value)
+    {
+        $path = explode('->', $value);
+
+        $field = $this->wrapSegments(explode('.', array_shift($path)));
+
+        $wrappedPath = $this->wrapJsonPathAttributes($path);
+
+        $attribute = array_pop($wrappedPath);
+
+        if (! empty($wrappedPath)) {
+            return $field.'->'.implode('->', $wrappedPath).'->>'.$attribute;
+        }
+
+        return $field.'->>'.$attribute;
+    }
+
+    /**
+     * Wrap the given JSON selector for boolean values.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapJsonBooleanSelector($value)
+    {
+        $selector = str_replace(
+            '->>', '->',
+            $this->wrapJsonSelector($value)
+        );
+
+        return '('.$selector.')::jsonb';
+    }
+
+    /**
+     * Wrap the given JSON boolean value.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapJsonBooleanValue($value)
+    {
+        return "'".$value."'::jsonb";
+    }
+
+    /**
+     * Wrap the attributes of the given JSON path.
+     *
+     * @param  array  $path
+     * @return array
+     */
+    protected function wrapJsonPathAttributes($path)
+    {
+        $quote = func_num_args() === 2 ? func_get_arg(1) : "'";
+
+        return collect($path)->map(function ($attribute) {
+            return $this->parseJsonPathArrayKeys($attribute);
+        })->collapse()->map(function ($attribute) use ($quote) {
+            return filter_var($attribute, FILTER_VALIDATE_INT) !== false
+                        ? $attribute
+                        : $quote.$attribute.$quote;
+        })->all();
+    }
+
+    /**
+     * Parse the given JSON path attribute for array keys.
+     *
+     * @param  string  $attribute
+     * @return array
+     */
+    protected function parseJsonPathArrayKeys($attribute)
+    {
+        if (preg_match('/(\[[^\]]+\])+$/', $attribute, $parts)) {
+            $key = Str::beforeLast($attribute, $parts[0]);
+
+            preg_match_all('/\[([^\]]+)\]/', $parts[0], $keys);
+
+            return collect([$key])
+                ->merge($keys[1])
+                ->diff('')
+                ->values()
+                ->all();
+        }
+
+        return [$attribute];
+    }
+}
diff --git a/vendor/illuminate/database/Query/Grammars/SQLiteGrammar.php b/vendor/illuminate/database/Query/Grammars/SQLiteGrammar.php
new file mode 100755
index 0000000..8bf7d39
--- /dev/null
+++ b/vendor/illuminate/database/Query/Grammars/SQLiteGrammar.php
@@ -0,0 +1,369 @@
+', '<=', '>=', '<>', '!=',
+        'like', 'not like', 'ilike',
+        '&', '|', '<<', '>>',
+    ];
+
+    /**
+     * Compile the lock into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  bool|string  $value
+     * @return string
+     */
+    protected function compileLock(Builder $query, $value)
+    {
+        return '';
+    }
+
+    /**
+     * Wrap a union subquery in parentheses.
+     *
+     * @param  string  $sql
+     * @return string
+     */
+    protected function wrapUnion($sql)
+    {
+        return 'select * from ('.$sql.')';
+    }
+
+    /**
+     * Compile a "where date" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereDate(Builder $query, $where)
+    {
+        return $this->dateBasedWhere('%Y-%m-%d', $query, $where);
+    }
+
+    /**
+     * Compile a "where day" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereDay(Builder $query, $where)
+    {
+        return $this->dateBasedWhere('%d', $query, $where);
+    }
+
+    /**
+     * Compile a "where month" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereMonth(Builder $query, $where)
+    {
+        return $this->dateBasedWhere('%m', $query, $where);
+    }
+
+    /**
+     * Compile a "where year" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereYear(Builder $query, $where)
+    {
+        return $this->dateBasedWhere('%Y', $query, $where);
+    }
+
+    /**
+     * Compile a "where time" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereTime(Builder $query, $where)
+    {
+        return $this->dateBasedWhere('%H:%M:%S', $query, $where);
+    }
+
+    /**
+     * Compile a date based where clause.
+     *
+     * @param  string  $type
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function dateBasedWhere($type, Builder $query, $where)
+    {
+        $value = $this->parameter($where['value']);
+
+        return "strftime('{$type}', {$this->wrap($where['column'])}) {$where['operator']} cast({$value} as text)";
+    }
+
+    /**
+     * Compile the index hints for the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  \Illuminate\Database\Query\IndexHint  $indexHint
+     * @return string
+     */
+    protected function compileIndexHint(Builder $query, $indexHint)
+    {
+        return $indexHint->type === 'force'
+                ? "indexed by {$indexHint->index}"
+                : '';
+    }
+
+    /**
+     * Compile a "JSON length" statement into SQL.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  string  $value
+     * @return string
+     */
+    protected function compileJsonLength($column, $operator, $value)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($column);
+
+        return 'json_array_length('.$field.$path.') '.$operator.' '.$value;
+    }
+
+    /**
+     * Compile a "JSON contains key" statement into SQL.
+     *
+     * @param  string  $column
+     * @return string
+     */
+    protected function compileJsonContainsKey($column)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($column);
+
+        return 'json_type('.$field.$path.') is not null';
+    }
+
+    /**
+     * Compile an update statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    public function compileUpdate(Builder $query, array $values)
+    {
+        if (isset($query->joins) || isset($query->limit)) {
+            return $this->compileUpdateWithJoinsOrLimit($query, $values);
+        }
+
+        return parent::compileUpdate($query, $values);
+    }
+
+    /**
+     * Compile an insert ignore statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    public function compileInsertOrIgnore(Builder $query, array $values)
+    {
+        return Str::replaceFirst('insert', 'insert or ignore', $this->compileInsert($query, $values));
+    }
+
+    /**
+     * Compile the columns for an update statement.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    protected function compileUpdateColumns(Builder $query, array $values)
+    {
+        $jsonGroups = $this->groupJsonColumnsForUpdate($values);
+
+        return collect($values)->reject(function ($value, $key) {
+            return $this->isJsonSelector($key);
+        })->merge($jsonGroups)->map(function ($value, $key) use ($jsonGroups) {
+            $column = last(explode('.', $key));
+
+            $value = isset($jsonGroups[$key]) ? $this->compileJsonPatch($column, $value) : $this->parameter($value);
+
+            return $this->wrap($column).' = '.$value;
+        })->implode(', ');
+    }
+
+    /**
+     * Compile an "upsert" statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @param  array  $uniqueBy
+     * @param  array  $update
+     * @return string
+     */
+    public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
+    {
+        $sql = $this->compileInsert($query, $values);
+
+        $sql .= ' on conflict ('.$this->columnize($uniqueBy).') do update set ';
+
+        $columns = collect($update)->map(function ($value, $key) {
+            return is_numeric($key)
+                ? $this->wrap($value).' = '.$this->wrapValue('excluded').'.'.$this->wrap($value)
+                : $this->wrap($key).' = '.$this->parameter($value);
+        })->implode(', ');
+
+        return $sql.$columns;
+    }
+
+    /**
+     * Group the nested JSON columns.
+     *
+     * @param  array  $values
+     * @return array
+     */
+    protected function groupJsonColumnsForUpdate(array $values)
+    {
+        $groups = [];
+
+        foreach ($values as $key => $value) {
+            if ($this->isJsonSelector($key)) {
+                Arr::set($groups, str_replace('->', '.', Str::after($key, '.')), $value);
+            }
+        }
+
+        return $groups;
+    }
+
+    /**
+     * Compile a "JSON" patch statement into SQL.
+     *
+     * @param  string  $column
+     * @param  mixed  $value
+     * @return string
+     */
+    protected function compileJsonPatch($column, $value)
+    {
+        return "json_patch(ifnull({$this->wrap($column)}, json('{}')), json({$this->parameter($value)}))";
+    }
+
+    /**
+     * Compile an update statement with joins or limit into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @return string
+     */
+    protected function compileUpdateWithJoinsOrLimit(Builder $query, array $values)
+    {
+        $table = $this->wrapTable($query->from);
+
+        $columns = $this->compileUpdateColumns($query, $values);
+
+        $alias = last(preg_split('/\s+as\s+/i', $query->from));
+
+        $selectSql = $this->compileSelect($query->select($alias.'.rowid'));
+
+        return "update {$table} set {$columns} where {$this->wrap('rowid')} in ({$selectSql})";
+    }
+
+    /**
+     * Prepare the bindings for an update statement.
+     *
+     * @param  array  $bindings
+     * @param  array  $values
+     * @return array
+     */
+    public function prepareBindingsForUpdate(array $bindings, array $values)
+    {
+        $groups = $this->groupJsonColumnsForUpdate($values);
+
+        $values = collect($values)->reject(function ($value, $key) {
+            return $this->isJsonSelector($key);
+        })->merge($groups)->map(function ($value) {
+            return is_array($value) ? json_encode($value) : $value;
+        })->all();
+
+        $cleanBindings = Arr::except($bindings, 'select');
+
+        return array_values(
+            array_merge($values, Arr::flatten($cleanBindings))
+        );
+    }
+
+    /**
+     * Compile a delete statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    public function compileDelete(Builder $query)
+    {
+        if (isset($query->joins) || isset($query->limit)) {
+            return $this->compileDeleteWithJoinsOrLimit($query);
+        }
+
+        return parent::compileDelete($query);
+    }
+
+    /**
+     * Compile a delete statement with joins or limit into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    protected function compileDeleteWithJoinsOrLimit(Builder $query)
+    {
+        $table = $this->wrapTable($query->from);
+
+        $alias = last(preg_split('/\s+as\s+/i', $query->from));
+
+        $selectSql = $this->compileSelect($query->select($alias.'.rowid'));
+
+        return "delete from {$table} where {$this->wrap('rowid')} in ({$selectSql})";
+    }
+
+    /**
+     * Compile a truncate table statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return array
+     */
+    public function compileTruncate(Builder $query)
+    {
+        return [
+            'delete from sqlite_sequence where name = ?' => [$query->from],
+            'delete from '.$this->wrapTable($query->from) => [],
+        ];
+    }
+
+    /**
+     * Wrap the given JSON selector.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapJsonSelector($value)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($value);
+
+        return 'json_extract('.$field.$path.')';
+    }
+}
diff --git a/vendor/illuminate/database/Query/Grammars/SqlServerGrammar.php b/vendor/illuminate/database/Query/Grammars/SqlServerGrammar.php
new file mode 100755
index 0000000..baebb93
--- /dev/null
+++ b/vendor/illuminate/database/Query/Grammars/SqlServerGrammar.php
@@ -0,0 +1,634 @@
+', '<=', '>=', '!<', '!>', '<>', '!=',
+        'like', 'not like', 'ilike',
+        '&', '&=', '|', '|=', '^', '^=',
+    ];
+
+    /**
+     * Compile a select query into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    public function compileSelect(Builder $query)
+    {
+        if (! $query->offset) {
+            return parent::compileSelect($query);
+        }
+
+        if (is_null($query->columns)) {
+            $query->columns = ['*'];
+        }
+
+        $components = $this->compileComponents($query);
+
+        if (! empty($components['orders'])) {
+            return parent::compileSelect($query)." offset {$query->offset} rows fetch next {$query->limit} rows only";
+        }
+
+        // If an offset is present on the query, we will need to wrap the query in
+        // a big "ANSI" offset syntax block. This is very nasty compared to the
+        // other database systems but is necessary for implementing features.
+        return $this->compileAnsiOffset(
+            $query, $components
+        );
+    }
+
+    /**
+     * Compile the "select *" portion of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $columns
+     * @return string|null
+     */
+    protected function compileColumns(Builder $query, $columns)
+    {
+        if (! is_null($query->aggregate)) {
+            return;
+        }
+
+        $select = $query->distinct ? 'select distinct ' : 'select ';
+
+        // If there is a limit on the query, but not an offset, we will add the top
+        // clause to the query, which serves as a "limit" type clause within the
+        // SQL Server system similar to the limit keywords available in MySQL.
+        if (is_numeric($query->limit) && $query->limit > 0 && $query->offset <= 0) {
+            $select .= 'top '.((int) $query->limit).' ';
+        }
+
+        return $select.$this->columnize($columns);
+    }
+
+    /**
+     * Compile the "from" portion of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $table
+     * @return string
+     */
+    protected function compileFrom(Builder $query, $table)
+    {
+        $from = parent::compileFrom($query, $table);
+
+        if (is_string($query->lock)) {
+            return $from.' '.$query->lock;
+        }
+
+        if (! is_null($query->lock)) {
+            return $from.' with(rowlock,'.($query->lock ? 'updlock,' : '').'holdlock)';
+        }
+
+        return $from;
+    }
+
+    /**
+     * Compile the index hints for the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  \Illuminate\Database\Query\IndexHint  $indexHint
+     * @return string
+     */
+    protected function compileIndexHint(Builder $query, $indexHint)
+    {
+        return $indexHint->type === 'force'
+                    ? "with (index({$indexHint->index}))"
+                    : '';
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereBitwise(Builder $query, $where)
+    {
+        $value = $this->parameter($where['value']);
+
+        $operator = str_replace('?', '??', $where['operator']);
+
+        return '('.$this->wrap($where['column']).' '.$operator.' '.$value.') != 0';
+    }
+
+    /**
+     * Compile a "where date" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereDate(Builder $query, $where)
+    {
+        $value = $this->parameter($where['value']);
+
+        return 'cast('.$this->wrap($where['column']).' as date) '.$where['operator'].' '.$value;
+    }
+
+    /**
+     * Compile a "where time" clause.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $where
+     * @return string
+     */
+    protected function whereTime(Builder $query, $where)
+    {
+        $value = $this->parameter($where['value']);
+
+        return 'cast('.$this->wrap($where['column']).' as time) '.$where['operator'].' '.$value;
+    }
+
+    /**
+     * Compile a "JSON contains" statement into SQL.
+     *
+     * @param  string  $column
+     * @param  string  $value
+     * @return string
+     */
+    protected function compileJsonContains($column, $value)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($column);
+
+        return $value.' in (select [value] from openjson('.$field.$path.'))';
+    }
+
+    /**
+     * Prepare the binding for a "JSON contains" statement.
+     *
+     * @param  mixed  $binding
+     * @return string
+     */
+    public function prepareBindingForJsonContains($binding)
+    {
+        return is_bool($binding) ? json_encode($binding) : $binding;
+    }
+
+    /**
+     * Compile a "JSON contains key" statement into SQL.
+     *
+     * @param  string  $column
+     * @return string
+     */
+    protected function compileJsonContainsKey($column)
+    {
+        $segments = explode('->', $column);
+
+        $lastSegment = array_pop($segments);
+
+        if (preg_match('/\[([0-9]+)\]$/', $lastSegment, $matches)) {
+            $segments[] = Str::beforeLast($lastSegment, $matches[0]);
+
+            $key = $matches[1];
+        } else {
+            $key = "'".str_replace("'", "''", $lastSegment)."'";
+        }
+
+        [$field, $path] = $this->wrapJsonFieldAndPath(implode('->', $segments));
+
+        return $key.' in (select [key] from openjson('.$field.$path.'))';
+    }
+
+    /**
+     * Compile a "JSON length" statement into SQL.
+     *
+     * @param  string  $column
+     * @param  string  $operator
+     * @param  string  $value
+     * @return string
+     */
+    protected function compileJsonLength($column, $operator, $value)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($column);
+
+        return '(select count(*) from openjson('.$field.$path.')) '.$operator.' '.$value;
+    }
+
+    /**
+     * Compile a "JSON value cast" statement into SQL.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public function compileJsonValueCast($value)
+    {
+        return 'json_query('.$value.')';
+    }
+
+    /**
+     * Compile a single having clause.
+     *
+     * @param  array  $having
+     * @return string
+     */
+    protected function compileHaving(array $having)
+    {
+        if ($having['type'] === 'Bitwise') {
+            return $this->compileHavingBitwise($having);
+        }
+
+        return parent::compileHaving($having);
+    }
+
+    /**
+     * Compile a having clause involving a bitwise operator.
+     *
+     * @param  array  $having
+     * @return string
+     */
+    protected function compileHavingBitwise($having)
+    {
+        $column = $this->wrap($having['column']);
+
+        $parameter = $this->parameter($having['value']);
+
+        return '('.$column.' '.$having['operator'].' '.$parameter.') != 0';
+    }
+
+    /**
+     * Create a full ANSI offset clause for the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $components
+     * @return string
+     */
+    protected function compileAnsiOffset(Builder $query, $components)
+    {
+        // An ORDER BY clause is required to make this offset query work, so if one does
+        // not exist we'll just create a dummy clause to trick the database and so it
+        // does not complain about the queries for not having an "order by" clause.
+        if (empty($components['orders'])) {
+            $components['orders'] = 'order by (select 0)';
+        }
+
+        // We need to add the row number to the query so we can compare it to the offset
+        // and limit values given for the statements. So we will add an expression to
+        // the "select" that will give back the row numbers on each of the records.
+        $components['columns'] .= $this->compileOver($components['orders']);
+
+        unset($components['orders']);
+
+        if ($this->queryOrderContainsSubquery($query)) {
+            $query->bindings = $this->sortBindingsForSubqueryOrderBy($query);
+        }
+
+        // Next we need to calculate the constraints that should be placed on the query
+        // to get the right offset and limit from our query but if there is no limit
+        // set we will just handle the offset only since that is all that matters.
+        $sql = $this->concatenate($components);
+
+        return $this->compileTableExpression($sql, $query);
+    }
+
+    /**
+     * Compile the over statement for a table expression.
+     *
+     * @param  string  $orderings
+     * @return string
+     */
+    protected function compileOver($orderings)
+    {
+        return ", row_number() over ({$orderings}) as row_num";
+    }
+
+    /**
+     * Determine if the query's order by clauses contain a subquery.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return bool
+     */
+    protected function queryOrderContainsSubquery($query)
+    {
+        if (! is_array($query->orders)) {
+            return false;
+        }
+
+        return Arr::first($query->orders, function ($value) {
+            return $this->isExpression($value['column'] ?? null);
+        }, false) !== false;
+    }
+
+    /**
+     * Move the order bindings to be after the "select" statement to account for an order by subquery.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return array
+     */
+    protected function sortBindingsForSubqueryOrderBy($query)
+    {
+        return Arr::sort($query->bindings, function ($bindings, $key) {
+            return array_search($key, ['select', 'order', 'from', 'join', 'where', 'groupBy', 'having', 'union', 'unionOrder']);
+        });
+    }
+
+    /**
+     * Compile a common table expression for a query.
+     *
+     * @param  string  $sql
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    protected function compileTableExpression($sql, $query)
+    {
+        $constraint = $this->compileRowConstraint($query);
+
+        return "select * from ({$sql}) as temp_table where row_num {$constraint} order by row_num";
+    }
+
+    /**
+     * Compile the limit / offset row constraint for a query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    protected function compileRowConstraint($query)
+    {
+        $start = (int) $query->offset + 1;
+
+        if ($query->limit > 0) {
+            $finish = (int) $query->offset + (int) $query->limit;
+
+            return "between {$start} and {$finish}";
+        }
+
+        return ">= {$start}";
+    }
+
+    /**
+     * Compile a delete statement without joins into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $table
+     * @param  string  $where
+     * @return string
+     */
+    protected function compileDeleteWithoutJoins(Builder $query, $table, $where)
+    {
+        $sql = parent::compileDeleteWithoutJoins($query, $table, $where);
+
+        return ! is_null($query->limit) && $query->limit > 0 && $query->offset <= 0
+                        ? Str::replaceFirst('delete', 'delete top ('.$query->limit.')', $sql)
+                        : $sql;
+    }
+
+    /**
+     * Compile the random statement into SQL.
+     *
+     * @param  string|int  $seed
+     * @return string
+     */
+    public function compileRandom($seed)
+    {
+        return 'NEWID()';
+    }
+
+    /**
+     * Compile the "limit" portions of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  int  $limit
+     * @return string
+     */
+    protected function compileLimit(Builder $query, $limit)
+    {
+        return '';
+    }
+
+    /**
+     * Compile the "offset" portions of the query.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  int  $offset
+     * @return string
+     */
+    protected function compileOffset(Builder $query, $offset)
+    {
+        return '';
+    }
+
+    /**
+     * Compile the lock into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  bool|string  $value
+     * @return string
+     */
+    protected function compileLock(Builder $query, $value)
+    {
+        return '';
+    }
+
+    /**
+     * Wrap a union subquery in parentheses.
+     *
+     * @param  string  $sql
+     * @return string
+     */
+    protected function wrapUnion($sql)
+    {
+        return 'select * from ('.$sql.') as '.$this->wrapTable('temp_table');
+    }
+
+    /**
+     * Compile an exists statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @return string
+     */
+    public function compileExists(Builder $query)
+    {
+        $existsQuery = clone $query;
+
+        $existsQuery->columns = [];
+
+        return $this->compileSelect($existsQuery->selectRaw('1 [exists]')->limit(1));
+    }
+
+    /**
+     * Compile an update statement with joins into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  string  $table
+     * @param  string  $columns
+     * @param  string  $where
+     * @return string
+     */
+    protected function compileUpdateWithJoins(Builder $query, $table, $columns, $where)
+    {
+        $alias = last(explode(' as ', $table));
+
+        $joins = $this->compileJoins($query, $query->joins);
+
+        return "update {$alias} set {$columns} from {$table} {$joins} {$where}";
+    }
+
+    /**
+     * Compile an "upsert" statement into SQL.
+     *
+     * @param  \Illuminate\Database\Query\Builder  $query
+     * @param  array  $values
+     * @param  array  $uniqueBy
+     * @param  array  $update
+     * @return string
+     */
+    public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
+    {
+        $columns = $this->columnize(array_keys(reset($values)));
+
+        $sql = 'merge '.$this->wrapTable($query->from).' ';
+
+        $parameters = collect($values)->map(function ($record) {
+            return '('.$this->parameterize($record).')';
+        })->implode(', ');
+
+        $sql .= 'using (values '.$parameters.') '.$this->wrapTable('laravel_source').' ('.$columns.') ';
+
+        $on = collect($uniqueBy)->map(function ($column) use ($query) {
+            return $this->wrap('laravel_source.'.$column).' = '.$this->wrap($query->from.'.'.$column);
+        })->implode(' and ');
+
+        $sql .= 'on '.$on.' ';
+
+        if ($update) {
+            $update = collect($update)->map(function ($value, $key) {
+                return is_numeric($key)
+                    ? $this->wrap($value).' = '.$this->wrap('laravel_source.'.$value)
+                    : $this->wrap($key).' = '.$this->parameter($value);
+            })->implode(', ');
+
+            $sql .= 'when matched then update set '.$update.' ';
+        }
+
+        $sql .= 'when not matched then insert ('.$columns.') values ('.$columns.');';
+
+        return $sql;
+    }
+
+    /**
+     * Prepare the bindings for an update statement.
+     *
+     * @param  array  $bindings
+     * @param  array  $values
+     * @return array
+     */
+    public function prepareBindingsForUpdate(array $bindings, array $values)
+    {
+        $cleanBindings = Arr::except($bindings, 'select');
+
+        return array_values(
+            array_merge($values, Arr::flatten($cleanBindings))
+        );
+    }
+
+    /**
+     * Compile the SQL statement to define a savepoint.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    public function compileSavepoint($name)
+    {
+        return 'SAVE TRANSACTION '.$name;
+    }
+
+    /**
+     * Compile the SQL statement to execute a savepoint rollback.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    public function compileSavepointRollBack($name)
+    {
+        return 'ROLLBACK TRANSACTION '.$name;
+    }
+
+    /**
+     * Get the format for database stored dates.
+     *
+     * @return string
+     */
+    public function getDateFormat()
+    {
+        return 'Y-m-d H:i:s.v';
+    }
+
+    /**
+     * Wrap a single string in keyword identifiers.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapValue($value)
+    {
+        return $value === '*' ? $value : '['.str_replace(']', ']]', $value).']';
+    }
+
+    /**
+     * Wrap the given JSON selector.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapJsonSelector($value)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($value);
+
+        return 'json_value('.$field.$path.')';
+    }
+
+    /**
+     * Wrap the given JSON boolean value.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapJsonBooleanValue($value)
+    {
+        return "'".$value."'";
+    }
+
+    /**
+     * Wrap a table in keyword identifiers.
+     *
+     * @param  \Illuminate\Database\Query\Expression|string  $table
+     * @return string
+     */
+    public function wrapTable($table)
+    {
+        if (! $this->isExpression($table)) {
+            return $this->wrapTableValuedFunction(parent::wrapTable($table));
+        }
+
+        return $this->getValue($table);
+    }
+
+    /**
+     * Wrap a table in keyword identifiers.
+     *
+     * @param  string  $table
+     * @return string
+     */
+    protected function wrapTableValuedFunction($table)
+    {
+        if (preg_match('/^(.+?)(\(.*?\))]$/', $table, $matches) === 1) {
+            $table = $matches[1].']'.$matches[2];
+        }
+
+        return $table;
+    }
+}
diff --git a/vendor/illuminate/database/Query/IndexHint.php b/vendor/illuminate/database/Query/IndexHint.php
new file mode 100755
index 0000000..2a720a2
--- /dev/null
+++ b/vendor/illuminate/database/Query/IndexHint.php
@@ -0,0 +1,33 @@
+type = $type;
+        $this->index = $index;
+    }
+}
diff --git a/vendor/illuminate/database/Query/JoinClause.php b/vendor/illuminate/database/Query/JoinClause.php
new file mode 100755
index 0000000..57d650a
--- /dev/null
+++ b/vendor/illuminate/database/Query/JoinClause.php
@@ -0,0 +1,146 @@
+type = $type;
+        $this->table = $table;
+        $this->parentClass = get_class($parentQuery);
+        $this->parentGrammar = $parentQuery->getGrammar();
+        $this->parentProcessor = $parentQuery->getProcessor();
+        $this->parentConnection = $parentQuery->getConnection();
+
+        parent::__construct(
+            $this->parentConnection, $this->parentGrammar, $this->parentProcessor
+        );
+    }
+
+    /**
+     * Add an "on" clause to the join.
+     *
+     * On clauses can be chained, e.g.
+     *
+     *  $join->on('contacts.user_id', '=', 'users.id')
+     *       ->on('contacts.info_id', '=', 'info.id')
+     *
+     * will produce the following SQL:
+     *
+     * on `contacts`.`user_id` = `users`.`id` and `contacts`.`info_id` = `info`.`id`
+     *
+     * @param  \Closure|string  $first
+     * @param  string|null  $operator
+     * @param  \Illuminate\Database\Query\Expression|string|null  $second
+     * @param  string  $boolean
+     * @return $this
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function on($first, $operator = null, $second = null, $boolean = 'and')
+    {
+        if ($first instanceof Closure) {
+            return $this->whereNested($first, $boolean);
+        }
+
+        return $this->whereColumn($first, $operator, $second, $boolean);
+    }
+
+    /**
+     * Add an "or on" clause to the join.
+     *
+     * @param  \Closure|string  $first
+     * @param  string|null  $operator
+     * @param  \Illuminate\Database\Query\Expression|string|null  $second
+     * @return \Illuminate\Database\Query\JoinClause
+     */
+    public function orOn($first, $operator = null, $second = null)
+    {
+        return $this->on($first, $operator, $second, 'or');
+    }
+
+    /**
+     * Get a new instance of the join clause builder.
+     *
+     * @return \Illuminate\Database\Query\JoinClause
+     */
+    public function newQuery()
+    {
+        return new static($this->newParentQuery(), $this->type, $this->table);
+    }
+
+    /**
+     * Create a new query instance for sub-query.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    protected function forSubQuery()
+    {
+        return $this->newParentQuery()->newQuery();
+    }
+
+    /**
+     * Create a new parent query instance.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    protected function newParentQuery()
+    {
+        $class = $this->parentClass;
+
+        return new $class($this->parentConnection, $this->parentGrammar, $this->parentProcessor);
+    }
+}
diff --git a/vendor/illuminate/database/Query/Processors/MySqlProcessor.php b/vendor/illuminate/database/Query/Processors/MySqlProcessor.php
new file mode 100644
index 0000000..ce91838
--- /dev/null
+++ b/vendor/illuminate/database/Query/Processors/MySqlProcessor.php
@@ -0,0 +1,19 @@
+column_name;
+        }, $results);
+    }
+}
diff --git a/vendor/illuminate/database/Query/Processors/PostgresProcessor.php b/vendor/illuminate/database/Query/Processors/PostgresProcessor.php
new file mode 100755
index 0000000..5956a8f
--- /dev/null
+++ b/vendor/illuminate/database/Query/Processors/PostgresProcessor.php
@@ -0,0 +1,45 @@
+getConnection();
+
+        $connection->recordsHaveBeenModified();
+
+        $result = $connection->selectFromWriteConnection($sql, $values)[0];
+
+        $sequence = $sequence ?: 'id';
+
+        $id = is_object($result) ? $result->{$sequence} : $result[$sequence];
+
+        return is_numeric($id) ? (int) $id : $id;
+    }
+
+    /**
+     * Process the results of a column listing query.
+     *
+     * @param  array  $results
+     * @return array
+     */
+    public function processColumnListing($results)
+    {
+        return array_map(function ($result) {
+            return ((object) $result)->column_name;
+        }, $results);
+    }
+}
diff --git a/vendor/illuminate/database/Query/Processors/Processor.php b/vendor/illuminate/database/Query/Processors/Processor.php
new file mode 100755
index 0000000..0069b43
--- /dev/null
+++ b/vendor/illuminate/database/Query/Processors/Processor.php
@@ -0,0 +1,49 @@
+getConnection()->insert($sql, $values);
+
+        $id = $query->getConnection()->getPdo()->lastInsertId($sequence);
+
+        return is_numeric($id) ? (int) $id : $id;
+    }
+
+    /**
+     * Process the results of a column listing query.
+     *
+     * @param  array  $results
+     * @return array
+     */
+    public function processColumnListing($results)
+    {
+        return $results;
+    }
+}
diff --git a/vendor/illuminate/database/Query/Processors/SQLiteProcessor.php b/vendor/illuminate/database/Query/Processors/SQLiteProcessor.php
new file mode 100644
index 0000000..65da1df
--- /dev/null
+++ b/vendor/illuminate/database/Query/Processors/SQLiteProcessor.php
@@ -0,0 +1,19 @@
+name;
+        }, $results);
+    }
+}
diff --git a/vendor/illuminate/database/Query/Processors/SqlServerProcessor.php b/vendor/illuminate/database/Query/Processors/SqlServerProcessor.php
new file mode 100755
index 0000000..49476f0
--- /dev/null
+++ b/vendor/illuminate/database/Query/Processors/SqlServerProcessor.php
@@ -0,0 +1,70 @@
+getConnection();
+
+        $connection->insert($sql, $values);
+
+        if ($connection->getConfig('odbc') === true) {
+            $id = $this->processInsertGetIdForOdbc($connection);
+        } else {
+            $id = $connection->getPdo()->lastInsertId();
+        }
+
+        return is_numeric($id) ? (int) $id : $id;
+    }
+
+    /**
+     * Process an "insert get ID" query for ODBC.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return int
+     *
+     * @throws \Exception
+     */
+    protected function processInsertGetIdForOdbc(Connection $connection)
+    {
+        $result = $connection->selectFromWriteConnection(
+            'SELECT CAST(COALESCE(SCOPE_IDENTITY(), @@IDENTITY) AS int) AS insertid'
+        );
+
+        if (! $result) {
+            throw new Exception('Unable to retrieve lastInsertID for ODBC.');
+        }
+
+        $row = $result[0];
+
+        return is_object($row) ? $row->insertid : $row['insertid'];
+    }
+
+    /**
+     * Process the results of a column listing query.
+     *
+     * @param  array  $results
+     * @return array
+     */
+    public function processColumnListing($results)
+    {
+        return array_map(function ($result) {
+            return ((object) $result)->name;
+        }, $results);
+    }
+}
diff --git a/vendor/illuminate/database/QueryException.php b/vendor/illuminate/database/QueryException.php
new file mode 100644
index 0000000..74e5a31
--- /dev/null
+++ b/vendor/illuminate/database/QueryException.php
@@ -0,0 +1,79 @@
+sql = $sql;
+        $this->bindings = $bindings;
+        $this->code = $previous->getCode();
+        $this->message = $this->formatMessage($sql, $bindings, $previous);
+
+        if ($previous instanceof PDOException) {
+            $this->errorInfo = $previous->errorInfo;
+        }
+    }
+
+    /**
+     * Format the SQL error message.
+     *
+     * @param  string  $sql
+     * @param  array  $bindings
+     * @param  \Throwable  $previous
+     * @return string
+     */
+    protected function formatMessage($sql, $bindings, Throwable $previous)
+    {
+        return $previous->getMessage().' (SQL: '.Str::replaceArray('?', $bindings, $sql).')';
+    }
+
+    /**
+     * Get the SQL for the query.
+     *
+     * @return string
+     */
+    public function getSql()
+    {
+        return $this->sql;
+    }
+
+    /**
+     * Get the bindings for the query.
+     *
+     * @return array
+     */
+    public function getBindings()
+    {
+        return $this->bindings;
+    }
+}
diff --git a/vendor/illuminate/database/README.md b/vendor/illuminate/database/README.md
new file mode 100755
index 0000000..9019936
--- /dev/null
+++ b/vendor/illuminate/database/README.md
@@ -0,0 +1,69 @@
+## Illuminate Database
+
+The Illuminate Database component is a full database toolkit for PHP, providing an expressive query builder, ActiveRecord style ORM, and schema builder. It currently supports MySQL, Postgres, SQL Server, and SQLite. It also serves as the database layer of the Laravel PHP framework.
+
+### Usage Instructions
+
+First, create a new "Capsule" manager instance. Capsule aims to make configuring the library for usage outside of the Laravel framework as easy as possible.
+
+```PHP
+use Illuminate\Database\Capsule\Manager as Capsule;
+
+$capsule = new Capsule;
+
+$capsule->addConnection([
+    'driver' => 'mysql',
+    'host' => 'localhost',
+    'database' => 'database',
+    'username' => 'root',
+    'password' => 'password',
+    'charset' => 'utf8',
+    'collation' => 'utf8_unicode_ci',
+    'prefix' => '',
+]);
+
+// Set the event dispatcher used by Eloquent models... (optional)
+use Illuminate\Events\Dispatcher;
+use Illuminate\Container\Container;
+$capsule->setEventDispatcher(new Dispatcher(new Container));
+
+// Make this Capsule instance available globally via static methods... (optional)
+$capsule->setAsGlobal();
+
+// Setup the Eloquent ORM... (optional; unless you've used setEventDispatcher())
+$capsule->bootEloquent();
+```
+
+> `composer require "illuminate/events"` required when you need to use observers with Eloquent.
+
+Once the Capsule instance has been registered. You may use it like so:
+
+**Using The Query Builder**
+
+```PHP
+$users = Capsule::table('users')->where('votes', '>', 100)->get();
+```
+Other core methods may be accessed directly from the Capsule in the same manner as from the DB facade:
+```PHP
+$results = Capsule::select('select * from users where id = ?', [1]);
+```
+
+**Using The Schema Builder**
+
+```PHP
+Capsule::schema()->create('users', function ($table) {
+    $table->increments('id');
+    $table->string('email')->unique();
+    $table->timestamps();
+});
+```
+
+**Using The Eloquent ORM**
+
+```PHP
+class User extends Illuminate\Database\Eloquent\Model {}
+
+$users = User::where('votes', '>', 1)->get();
+```
+
+For further documentation on using the various database facilities this library provides, consult the [Laravel framework documentation](https://laravel.com/docs).
diff --git a/vendor/illuminate/database/RecordsNotFoundException.php b/vendor/illuminate/database/RecordsNotFoundException.php
new file mode 100755
index 0000000..3e0d955
--- /dev/null
+++ b/vendor/illuminate/database/RecordsNotFoundException.php
@@ -0,0 +1,10 @@
+getForeignKeyConstraintsConfigurationValue();
+
+        if ($enableForeignKeyConstraints === null) {
+            return;
+        }
+
+        $enableForeignKeyConstraints
+            ? $this->getSchemaBuilder()->enableForeignKeyConstraints()
+            : $this->getSchemaBuilder()->disableForeignKeyConstraints();
+    }
+
+    /**
+     * Get the default query grammar instance.
+     *
+     * @return \Illuminate\Database\Query\Grammars\SQLiteGrammar
+     */
+    protected function getDefaultQueryGrammar()
+    {
+        return $this->withTablePrefix(new QueryGrammar);
+    }
+
+    /**
+     * Get a schema builder instance for the connection.
+     *
+     * @return \Illuminate\Database\Schema\SQLiteBuilder
+     */
+    public function getSchemaBuilder()
+    {
+        if (is_null($this->schemaGrammar)) {
+            $this->useDefaultSchemaGrammar();
+        }
+
+        return new SQLiteBuilder($this);
+    }
+
+    /**
+     * Get the default schema grammar instance.
+     *
+     * @return \Illuminate\Database\Schema\Grammars\SQLiteGrammar
+     */
+    protected function getDefaultSchemaGrammar()
+    {
+        return $this->withTablePrefix(new SchemaGrammar);
+    }
+
+    /**
+     * Get the schema state for the connection.
+     *
+     * @param  \Illuminate\Filesystem\Filesystem|null  $files
+     * @param  callable|null  $processFactory
+     *
+     * @throws \RuntimeException
+     */
+    public function getSchemaState(Filesystem $files = null, callable $processFactory = null)
+    {
+        return new SqliteSchemaState($this, $files, $processFactory);
+    }
+
+    /**
+     * Get the default post processor instance.
+     *
+     * @return \Illuminate\Database\Query\Processors\SQLiteProcessor
+     */
+    protected function getDefaultPostProcessor()
+    {
+        return new SQLiteProcessor;
+    }
+
+    /**
+     * Get the Doctrine DBAL driver.
+     *
+     * @return \Illuminate\Database\PDO\SQLiteDriver
+     */
+    protected function getDoctrineDriver()
+    {
+        return new SQLiteDriver;
+    }
+
+    /**
+     * Get the database connection foreign key constraints configuration option.
+     *
+     * @return bool|null
+     */
+    protected function getForeignKeyConstraintsConfigurationValue()
+    {
+        return $this->getConfig('foreign_key_constraints');
+    }
+}
diff --git a/vendor/illuminate/database/SQLiteDatabaseDoesNotExistException.php b/vendor/illuminate/database/SQLiteDatabaseDoesNotExistException.php
new file mode 100644
index 0000000..f93cfe4
--- /dev/null
+++ b/vendor/illuminate/database/SQLiteDatabaseDoesNotExistException.php
@@ -0,0 +1,28 @@
+path = $path;
+    }
+}
diff --git a/vendor/illuminate/database/Schema/Blueprint.php b/vendor/illuminate/database/Schema/Blueprint.php
new file mode 100755
index 0000000..3ea7d15
--- /dev/null
+++ b/vendor/illuminate/database/Schema/Blueprint.php
@@ -0,0 +1,1829 @@
+table = $table;
+        $this->prefix = $prefix;
+
+        if (! is_null($callback)) {
+            $callback($this);
+        }
+    }
+
+    /**
+     * Execute the blueprint against the database.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
+     * @return void
+     */
+    public function build(Connection $connection, Grammar $grammar)
+    {
+        foreach ($this->toSql($connection, $grammar) as $statement) {
+            $connection->statement($statement);
+        }
+    }
+
+    /**
+     * Get the raw SQL statements for the blueprint.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
+     * @return array
+     */
+    public function toSql(Connection $connection, Grammar $grammar)
+    {
+        $this->addImpliedCommands($grammar);
+
+        $statements = [];
+
+        // Each type of command has a corresponding compiler function on the schema
+        // grammar which is used to build the necessary SQL statements to build
+        // the blueprint element, so we'll just call that compilers function.
+        $this->ensureCommandsAreValid($connection);
+
+        foreach ($this->commands as $command) {
+            $method = 'compile'.ucfirst($command->name);
+
+            if (method_exists($grammar, $method) || $grammar::hasMacro($method)) {
+                if (! is_null($sql = $grammar->$method($this, $command, $connection))) {
+                    $statements = array_merge($statements, (array) $sql);
+                }
+            }
+        }
+
+        return $statements;
+    }
+
+    /**
+     * Ensure the commands on the blueprint are valid for the connection type.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return void
+     *
+     * @throws \BadMethodCallException
+     */
+    protected function ensureCommandsAreValid(Connection $connection)
+    {
+        if ($connection instanceof SQLiteConnection) {
+            if ($this->commandsNamed(['dropColumn', 'renameColumn'])->count() > 1
+                && ! $connection->usingNativeSchemaOperations()) {
+                throw new BadMethodCallException(
+                    "SQLite doesn't support multiple calls to dropColumn / renameColumn in a single modification."
+                );
+            }
+
+            if ($this->commandsNamed(['dropForeign'])->count() > 0) {
+                throw new BadMethodCallException(
+                    "SQLite doesn't support dropping foreign keys (you would need to re-create the table)."
+                );
+            }
+        }
+    }
+
+    /**
+     * Get all of the commands matching the given names.
+     *
+     * @param  array  $names
+     * @return \Illuminate\Support\Collection
+     */
+    protected function commandsNamed(array $names)
+    {
+        return collect($this->commands)->filter(function ($command) use ($names) {
+            return in_array($command->name, $names);
+        });
+    }
+
+    /**
+     * Add the commands that are implied by the blueprint's state.
+     *
+     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
+     * @return void
+     */
+    protected function addImpliedCommands(Grammar $grammar)
+    {
+        if (count($this->getAddedColumns()) > 0 && ! $this->creating()) {
+            array_unshift($this->commands, $this->createCommand('add'));
+        }
+
+        if (count($this->getChangedColumns()) > 0 && ! $this->creating()) {
+            array_unshift($this->commands, $this->createCommand('change'));
+        }
+
+        $this->addFluentIndexes();
+
+        $this->addFluentCommands($grammar);
+    }
+
+    /**
+     * Add the index commands fluently specified on columns.
+     *
+     * @return void
+     */
+    protected function addFluentIndexes()
+    {
+        foreach ($this->columns as $column) {
+            foreach (['primary', 'unique', 'index', 'fulltext', 'fullText', 'spatialIndex'] as $index) {
+                // If the index has been specified on the given column, but is simply equal
+                // to "true" (boolean), no name has been specified for this index so the
+                // index method can be called without a name and it will generate one.
+                if ($column->{$index} === true) {
+                    $this->{$index}($column->name);
+                    $column->{$index} = null;
+
+                    continue 2;
+                }
+
+                // If the index has been specified on the given column, but it equals false
+                // and the column is supposed to be changed, we will call the drop index
+                // method with an array of column to drop it by its conventional name.
+                elseif ($column->{$index} === false && $column->change) {
+                    $this->{'drop'.ucfirst($index)}([$column->name]);
+                    $column->{$index} = null;
+
+                    continue 2;
+                }
+
+                // If the index has been specified on the given column, and it has a string
+                // value, we'll go ahead and call the index method and pass the name for
+                // the index since the developer specified the explicit name for this.
+                elseif (isset($column->{$index})) {
+                    $this->{$index}($column->name, $column->{$index});
+                    $column->{$index} = null;
+
+                    continue 2;
+                }
+            }
+        }
+    }
+
+    /**
+     * Add the fluent commands specified on any columns.
+     *
+     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
+     * @return void
+     */
+    public function addFluentCommands(Grammar $grammar)
+    {
+        foreach ($this->columns as $column) {
+            foreach ($grammar->getFluentCommands() as $commandName) {
+                $attributeName = lcfirst($commandName);
+
+                if (! isset($column->{$attributeName})) {
+                    continue;
+                }
+
+                $value = $column->{$attributeName};
+
+                $this->addCommand(
+                    $commandName, compact('value', 'column')
+                );
+            }
+        }
+    }
+
+    /**
+     * Determine if the blueprint has a create command.
+     *
+     * @return bool
+     */
+    public function creating()
+    {
+        return collect($this->commands)->contains(function ($command) {
+            return $command->name === 'create';
+        });
+    }
+
+    /**
+     * Indicate that the table needs to be created.
+     *
+     * @return \Illuminate\Support\Fluent
+     */
+    public function create()
+    {
+        return $this->addCommand('create');
+    }
+
+    /**
+     * Indicate that the table needs to be temporary.
+     *
+     * @return void
+     */
+    public function temporary()
+    {
+        $this->temporary = true;
+    }
+
+    /**
+     * Indicate that the table should be dropped.
+     *
+     * @return \Illuminate\Support\Fluent
+     */
+    public function drop()
+    {
+        return $this->addCommand('drop');
+    }
+
+    /**
+     * Indicate that the table should be dropped if it exists.
+     *
+     * @return \Illuminate\Support\Fluent
+     */
+    public function dropIfExists()
+    {
+        return $this->addCommand('dropIfExists');
+    }
+
+    /**
+     * Indicate that the given columns should be dropped.
+     *
+     * @param  array|mixed  $columns
+     * @return \Illuminate\Support\Fluent
+     */
+    public function dropColumn($columns)
+    {
+        $columns = is_array($columns) ? $columns : func_get_args();
+
+        return $this->addCommand('dropColumn', compact('columns'));
+    }
+
+    /**
+     * Indicate that the given columns should be renamed.
+     *
+     * @param  string  $from
+     * @param  string  $to
+     * @return \Illuminate\Support\Fluent
+     */
+    public function renameColumn($from, $to)
+    {
+        return $this->addCommand('renameColumn', compact('from', 'to'));
+    }
+
+    /**
+     * Indicate that the given primary key should be dropped.
+     *
+     * @param  string|array|null  $index
+     * @return \Illuminate\Support\Fluent
+     */
+    public function dropPrimary($index = null)
+    {
+        return $this->dropIndexCommand('dropPrimary', 'primary', $index);
+    }
+
+    /**
+     * Indicate that the given unique key should be dropped.
+     *
+     * @param  string|array  $index
+     * @return \Illuminate\Support\Fluent
+     */
+    public function dropUnique($index)
+    {
+        return $this->dropIndexCommand('dropUnique', 'unique', $index);
+    }
+
+    /**
+     * Indicate that the given index should be dropped.
+     *
+     * @param  string|array  $index
+     * @return \Illuminate\Support\Fluent
+     */
+    public function dropIndex($index)
+    {
+        return $this->dropIndexCommand('dropIndex', 'index', $index);
+    }
+
+    /**
+     * Indicate that the given fulltext index should be dropped.
+     *
+     * @param  string|array  $index
+     * @return \Illuminate\Support\Fluent
+     */
+    public function dropFullText($index)
+    {
+        return $this->dropIndexCommand('dropFullText', 'fulltext', $index);
+    }
+
+    /**
+     * Indicate that the given spatial index should be dropped.
+     *
+     * @param  string|array  $index
+     * @return \Illuminate\Support\Fluent
+     */
+    public function dropSpatialIndex($index)
+    {
+        return $this->dropIndexCommand('dropSpatialIndex', 'spatialIndex', $index);
+    }
+
+    /**
+     * Indicate that the given foreign key should be dropped.
+     *
+     * @param  string|array  $index
+     * @return \Illuminate\Support\Fluent
+     */
+    public function dropForeign($index)
+    {
+        return $this->dropIndexCommand('dropForeign', 'foreign', $index);
+    }
+
+    /**
+     * Indicate that the given column and foreign key should be dropped.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Support\Fluent
+     */
+    public function dropConstrainedForeignId($column)
+    {
+        $this->dropForeign([$column]);
+
+        return $this->dropColumn($column);
+    }
+
+    /**
+     * Indicate that the given foreign key should be dropped.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|string  $model
+     * @param  string|null  $column
+     * @return \Illuminate\Support\Fluent
+     */
+    public function dropForeignIdFor($model, $column = null)
+    {
+        if (is_string($model)) {
+            $model = new $model;
+        }
+
+        return $this->dropForeign([$column ?: $model->getForeignKey()]);
+    }
+
+    /**
+     * Indicate that the given foreign key should be dropped.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|string  $model
+     * @param  string|null  $column
+     * @return \Illuminate\Support\Fluent
+     */
+    public function dropConstrainedForeignIdFor($model, $column = null)
+    {
+        if (is_string($model)) {
+            $model = new $model;
+        }
+
+        return $this->dropConstrainedForeignId($column ?: $model->getForeignKey());
+    }
+
+    /**
+     * Indicate that the given indexes should be renamed.
+     *
+     * @param  string  $from
+     * @param  string  $to
+     * @return \Illuminate\Support\Fluent
+     */
+    public function renameIndex($from, $to)
+    {
+        return $this->addCommand('renameIndex', compact('from', 'to'));
+    }
+
+    /**
+     * Indicate that the timestamp columns should be dropped.
+     *
+     * @return void
+     */
+    public function dropTimestamps()
+    {
+        $this->dropColumn('created_at', 'updated_at');
+    }
+
+    /**
+     * Indicate that the timestamp columns should be dropped.
+     *
+     * @return void
+     */
+    public function dropTimestampsTz()
+    {
+        $this->dropTimestamps();
+    }
+
+    /**
+     * Indicate that the soft delete column should be dropped.
+     *
+     * @param  string  $column
+     * @return void
+     */
+    public function dropSoftDeletes($column = 'deleted_at')
+    {
+        $this->dropColumn($column);
+    }
+
+    /**
+     * Indicate that the soft delete column should be dropped.
+     *
+     * @param  string  $column
+     * @return void
+     */
+    public function dropSoftDeletesTz($column = 'deleted_at')
+    {
+        $this->dropSoftDeletes($column);
+    }
+
+    /**
+     * Indicate that the remember token column should be dropped.
+     *
+     * @return void
+     */
+    public function dropRememberToken()
+    {
+        $this->dropColumn('remember_token');
+    }
+
+    /**
+     * Indicate that the polymorphic columns should be dropped.
+     *
+     * @param  string  $name
+     * @param  string|null  $indexName
+     * @return void
+     */
+    public function dropMorphs($name, $indexName = null)
+    {
+        $this->dropIndex($indexName ?: $this->createIndexName('index', ["{$name}_type", "{$name}_id"]));
+
+        $this->dropColumn("{$name}_type", "{$name}_id");
+    }
+
+    /**
+     * Rename the table to a given name.
+     *
+     * @param  string  $to
+     * @return \Illuminate\Support\Fluent
+     */
+    public function rename($to)
+    {
+        return $this->addCommand('rename', compact('to'));
+    }
+
+    /**
+     * Specify the primary key(s) for the table.
+     *
+     * @param  string|array  $columns
+     * @param  string|null  $name
+     * @param  string|null  $algorithm
+     * @return \Illuminate\Database\Schema\IndexDefinition
+     */
+    public function primary($columns, $name = null, $algorithm = null)
+    {
+        return $this->indexCommand('primary', $columns, $name, $algorithm);
+    }
+
+    /**
+     * Specify a unique index for the table.
+     *
+     * @param  string|array  $columns
+     * @param  string|null  $name
+     * @param  string|null  $algorithm
+     * @return \Illuminate\Database\Schema\IndexDefinition
+     */
+    public function unique($columns, $name = null, $algorithm = null)
+    {
+        return $this->indexCommand('unique', $columns, $name, $algorithm);
+    }
+
+    /**
+     * Specify an index for the table.
+     *
+     * @param  string|array  $columns
+     * @param  string|null  $name
+     * @param  string|null  $algorithm
+     * @return \Illuminate\Database\Schema\IndexDefinition
+     */
+    public function index($columns, $name = null, $algorithm = null)
+    {
+        return $this->indexCommand('index', $columns, $name, $algorithm);
+    }
+
+    /**
+     * Specify an fulltext for the table.
+     *
+     * @param  string|array  $columns
+     * @param  string|null  $name
+     * @param  string|null  $algorithm
+     * @return \Illuminate\Database\Schema\IndexDefinition
+     */
+    public function fullText($columns, $name = null, $algorithm = null)
+    {
+        return $this->indexCommand('fulltext', $columns, $name, $algorithm);
+    }
+
+    /**
+     * Specify a spatial index for the table.
+     *
+     * @param  string|array  $columns
+     * @param  string|null  $name
+     * @return \Illuminate\Database\Schema\IndexDefinition
+     */
+    public function spatialIndex($columns, $name = null)
+    {
+        return $this->indexCommand('spatialIndex', $columns, $name);
+    }
+
+    /**
+     * Specify a raw index for the table.
+     *
+     * @param  string  $expression
+     * @param  string  $name
+     * @return \Illuminate\Database\Schema\IndexDefinition
+     */
+    public function rawIndex($expression, $name)
+    {
+        return $this->index([new Expression($expression)], $name);
+    }
+
+    /**
+     * Specify a foreign key for the table.
+     *
+     * @param  string|array  $columns
+     * @param  string|null  $name
+     * @return \Illuminate\Database\Schema\ForeignKeyDefinition
+     */
+    public function foreign($columns, $name = null)
+    {
+        $command = new ForeignKeyDefinition(
+            $this->indexCommand('foreign', $columns, $name)->getAttributes()
+        );
+
+        $this->commands[count($this->commands) - 1] = $command;
+
+        return $command;
+    }
+
+    /**
+     * Create a new auto-incrementing big integer (8-byte) column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function id($column = 'id')
+    {
+        return $this->bigIncrements($column);
+    }
+
+    /**
+     * Create a new auto-incrementing integer (4-byte) column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function increments($column)
+    {
+        return $this->unsignedInteger($column, true);
+    }
+
+    /**
+     * Create a new auto-incrementing integer (4-byte) column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function integerIncrements($column)
+    {
+        return $this->unsignedInteger($column, true);
+    }
+
+    /**
+     * Create a new auto-incrementing tiny integer (1-byte) column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function tinyIncrements($column)
+    {
+        return $this->unsignedTinyInteger($column, true);
+    }
+
+    /**
+     * Create a new auto-incrementing small integer (2-byte) column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function smallIncrements($column)
+    {
+        return $this->unsignedSmallInteger($column, true);
+    }
+
+    /**
+     * Create a new auto-incrementing medium integer (3-byte) column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function mediumIncrements($column)
+    {
+        return $this->unsignedMediumInteger($column, true);
+    }
+
+    /**
+     * Create a new auto-incrementing big integer (8-byte) column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function bigIncrements($column)
+    {
+        return $this->unsignedBigInteger($column, true);
+    }
+
+    /**
+     * Create a new char column on the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $length
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function char($column, $length = null)
+    {
+        $length = ! is_null($length) ? $length : Builder::$defaultStringLength;
+
+        return $this->addColumn('char', $column, compact('length'));
+    }
+
+    /**
+     * Create a new string column on the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $length
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function string($column, $length = null)
+    {
+        $length = $length ?: Builder::$defaultStringLength;
+
+        return $this->addColumn('string', $column, compact('length'));
+    }
+
+    /**
+     * Create a new tiny text column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function tinyText($column)
+    {
+        return $this->addColumn('tinyText', $column);
+    }
+
+    /**
+     * Create a new text column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function text($column)
+    {
+        return $this->addColumn('text', $column);
+    }
+
+    /**
+     * Create a new medium text column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function mediumText($column)
+    {
+        return $this->addColumn('mediumText', $column);
+    }
+
+    /**
+     * Create a new long text column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function longText($column)
+    {
+        return $this->addColumn('longText', $column);
+    }
+
+    /**
+     * Create a new integer (4-byte) column on the table.
+     *
+     * @param  string  $column
+     * @param  bool  $autoIncrement
+     * @param  bool  $unsigned
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function integer($column, $autoIncrement = false, $unsigned = false)
+    {
+        return $this->addColumn('integer', $column, compact('autoIncrement', 'unsigned'));
+    }
+
+    /**
+     * Create a new tiny integer (1-byte) column on the table.
+     *
+     * @param  string  $column
+     * @param  bool  $autoIncrement
+     * @param  bool  $unsigned
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function tinyInteger($column, $autoIncrement = false, $unsigned = false)
+    {
+        return $this->addColumn('tinyInteger', $column, compact('autoIncrement', 'unsigned'));
+    }
+
+    /**
+     * Create a new small integer (2-byte) column on the table.
+     *
+     * @param  string  $column
+     * @param  bool  $autoIncrement
+     * @param  bool  $unsigned
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function smallInteger($column, $autoIncrement = false, $unsigned = false)
+    {
+        return $this->addColumn('smallInteger', $column, compact('autoIncrement', 'unsigned'));
+    }
+
+    /**
+     * Create a new medium integer (3-byte) column on the table.
+     *
+     * @param  string  $column
+     * @param  bool  $autoIncrement
+     * @param  bool  $unsigned
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function mediumInteger($column, $autoIncrement = false, $unsigned = false)
+    {
+        return $this->addColumn('mediumInteger', $column, compact('autoIncrement', 'unsigned'));
+    }
+
+    /**
+     * Create a new big integer (8-byte) column on the table.
+     *
+     * @param  string  $column
+     * @param  bool  $autoIncrement
+     * @param  bool  $unsigned
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function bigInteger($column, $autoIncrement = false, $unsigned = false)
+    {
+        return $this->addColumn('bigInteger', $column, compact('autoIncrement', 'unsigned'));
+    }
+
+    /**
+     * Create a new unsigned integer (4-byte) column on the table.
+     *
+     * @param  string  $column
+     * @param  bool  $autoIncrement
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function unsignedInteger($column, $autoIncrement = false)
+    {
+        return $this->integer($column, $autoIncrement, true);
+    }
+
+    /**
+     * Create a new unsigned tiny integer (1-byte) column on the table.
+     *
+     * @param  string  $column
+     * @param  bool  $autoIncrement
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function unsignedTinyInteger($column, $autoIncrement = false)
+    {
+        return $this->tinyInteger($column, $autoIncrement, true);
+    }
+
+    /**
+     * Create a new unsigned small integer (2-byte) column on the table.
+     *
+     * @param  string  $column
+     * @param  bool  $autoIncrement
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function unsignedSmallInteger($column, $autoIncrement = false)
+    {
+        return $this->smallInteger($column, $autoIncrement, true);
+    }
+
+    /**
+     * Create a new unsigned medium integer (3-byte) column on the table.
+     *
+     * @param  string  $column
+     * @param  bool  $autoIncrement
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function unsignedMediumInteger($column, $autoIncrement = false)
+    {
+        return $this->mediumInteger($column, $autoIncrement, true);
+    }
+
+    /**
+     * Create a new unsigned big integer (8-byte) column on the table.
+     *
+     * @param  string  $column
+     * @param  bool  $autoIncrement
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function unsignedBigInteger($column, $autoIncrement = false)
+    {
+        return $this->bigInteger($column, $autoIncrement, true);
+    }
+
+    /**
+     * Create a new unsigned big integer (8-byte) column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ForeignIdColumnDefinition
+     */
+    public function foreignId($column)
+    {
+        return $this->addColumnDefinition(new ForeignIdColumnDefinition($this, [
+            'type' => 'bigInteger',
+            'name' => $column,
+            'autoIncrement' => false,
+            'unsigned' => true,
+        ]));
+    }
+
+    /**
+     * Create a foreign ID column for the given model.
+     *
+     * @param  \Illuminate\Database\Eloquent\Model|string  $model
+     * @param  string|null  $column
+     * @return \Illuminate\Database\Schema\ForeignIdColumnDefinition
+     */
+    public function foreignIdFor($model, $column = null)
+    {
+        if (is_string($model)) {
+            $model = new $model;
+        }
+
+        return $model->getKeyType() === 'int' && $model->getIncrementing()
+                    ? $this->foreignId($column ?: $model->getForeignKey())
+                    : $this->foreignUuid($column ?: $model->getForeignKey());
+    }
+
+    /**
+     * Create a new float column on the table.
+     *
+     * @param  string  $column
+     * @param  int  $total
+     * @param  int  $places
+     * @param  bool  $unsigned
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function float($column, $total = 8, $places = 2, $unsigned = false)
+    {
+        return $this->addColumn('float', $column, compact('total', 'places', 'unsigned'));
+    }
+
+    /**
+     * Create a new double column on the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $total
+     * @param  int|null  $places
+     * @param  bool  $unsigned
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function double($column, $total = null, $places = null, $unsigned = false)
+    {
+        return $this->addColumn('double', $column, compact('total', 'places', 'unsigned'));
+    }
+
+    /**
+     * Create a new decimal column on the table.
+     *
+     * @param  string  $column
+     * @param  int  $total
+     * @param  int  $places
+     * @param  bool  $unsigned
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function decimal($column, $total = 8, $places = 2, $unsigned = false)
+    {
+        return $this->addColumn('decimal', $column, compact('total', 'places', 'unsigned'));
+    }
+
+    /**
+     * Create a new unsigned float column on the table.
+     *
+     * @param  string  $column
+     * @param  int  $total
+     * @param  int  $places
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function unsignedFloat($column, $total = 8, $places = 2)
+    {
+        return $this->float($column, $total, $places, true);
+    }
+
+    /**
+     * Create a new unsigned double column on the table.
+     *
+     * @param  string  $column
+     * @param  int  $total
+     * @param  int  $places
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function unsignedDouble($column, $total = null, $places = null)
+    {
+        return $this->double($column, $total, $places, true);
+    }
+
+    /**
+     * Create a new unsigned decimal column on the table.
+     *
+     * @param  string  $column
+     * @param  int  $total
+     * @param  int  $places
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function unsignedDecimal($column, $total = 8, $places = 2)
+    {
+        return $this->decimal($column, $total, $places, true);
+    }
+
+    /**
+     * Create a new boolean column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function boolean($column)
+    {
+        return $this->addColumn('boolean', $column);
+    }
+
+    /**
+     * Create a new enum column on the table.
+     *
+     * @param  string  $column
+     * @param  array  $allowed
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function enum($column, array $allowed)
+    {
+        return $this->addColumn('enum', $column, compact('allowed'));
+    }
+
+    /**
+     * Create a new set column on the table.
+     *
+     * @param  string  $column
+     * @param  array  $allowed
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function set($column, array $allowed)
+    {
+        return $this->addColumn('set', $column, compact('allowed'));
+    }
+
+    /**
+     * Create a new json column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function json($column)
+    {
+        return $this->addColumn('json', $column);
+    }
+
+    /**
+     * Create a new jsonb column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function jsonb($column)
+    {
+        return $this->addColumn('jsonb', $column);
+    }
+
+    /**
+     * Create a new date column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function date($column)
+    {
+        return $this->addColumn('date', $column);
+    }
+
+    /**
+     * Create a new date-time column on the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $precision
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function dateTime($column, $precision = 0)
+    {
+        return $this->addColumn('dateTime', $column, compact('precision'));
+    }
+
+    /**
+     * Create a new date-time column (with time zone) on the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $precision
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function dateTimeTz($column, $precision = 0)
+    {
+        return $this->addColumn('dateTimeTz', $column, compact('precision'));
+    }
+
+    /**
+     * Create a new time column on the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $precision
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function time($column, $precision = 0)
+    {
+        return $this->addColumn('time', $column, compact('precision'));
+    }
+
+    /**
+     * Create a new time column (with time zone) on the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $precision
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function timeTz($column, $precision = 0)
+    {
+        return $this->addColumn('timeTz', $column, compact('precision'));
+    }
+
+    /**
+     * Create a new timestamp column on the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $precision
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function timestamp($column, $precision = 0)
+    {
+        return $this->addColumn('timestamp', $column, compact('precision'));
+    }
+
+    /**
+     * Create a new timestamp (with time zone) column on the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $precision
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function timestampTz($column, $precision = 0)
+    {
+        return $this->addColumn('timestampTz', $column, compact('precision'));
+    }
+
+    /**
+     * Add nullable creation and update timestamps to the table.
+     *
+     * @param  int|null  $precision
+     * @return void
+     */
+    public function timestamps($precision = 0)
+    {
+        $this->timestamp('created_at', $precision)->nullable();
+
+        $this->timestamp('updated_at', $precision)->nullable();
+    }
+
+    /**
+     * Add nullable creation and update timestamps to the table.
+     *
+     * Alias for self::timestamps().
+     *
+     * @param  int|null  $precision
+     * @return void
+     */
+    public function nullableTimestamps($precision = 0)
+    {
+        $this->timestamps($precision);
+    }
+
+    /**
+     * Add creation and update timestampTz columns to the table.
+     *
+     * @param  int|null  $precision
+     * @return void
+     */
+    public function timestampsTz($precision = 0)
+    {
+        $this->timestampTz('created_at', $precision)->nullable();
+
+        $this->timestampTz('updated_at', $precision)->nullable();
+    }
+
+    /**
+     * Add a "deleted at" timestamp for the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $precision
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function softDeletes($column = 'deleted_at', $precision = 0)
+    {
+        return $this->timestamp($column, $precision)->nullable();
+    }
+
+    /**
+     * Add a "deleted at" timestampTz for the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $precision
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function softDeletesTz($column = 'deleted_at', $precision = 0)
+    {
+        return $this->timestampTz($column, $precision)->nullable();
+    }
+
+    /**
+     * Create a new year column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function year($column)
+    {
+        return $this->addColumn('year', $column);
+    }
+
+    /**
+     * Create a new binary column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function binary($column)
+    {
+        return $this->addColumn('binary', $column);
+    }
+
+    /**
+     * Create a new UUID column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function uuid($column = 'uuid')
+    {
+        return $this->addColumn('uuid', $column);
+    }
+
+    /**
+     * Create a new UUID column on the table with a foreign key constraint.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ForeignIdColumnDefinition
+     */
+    public function foreignUuid($column)
+    {
+        return $this->addColumnDefinition(new ForeignIdColumnDefinition($this, [
+            'type' => 'uuid',
+            'name' => $column,
+        ]));
+    }
+
+    /**
+     * Create a new ULID column on the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $length
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function ulid($column = 'uuid', $length = 26)
+    {
+        return $this->char($column, $length);
+    }
+
+    /**
+     * Create a new ULID column on the table with a foreign key constraint.
+     *
+     * @param  string  $column
+     * @param  int|null  $length
+     * @return \Illuminate\Database\Schema\ForeignIdColumnDefinition
+     */
+    public function foreignUlid($column, $length = 26)
+    {
+        return $this->addColumnDefinition(new ForeignIdColumnDefinition($this, [
+            'type' => 'char',
+            'name' => $column,
+            'length' => $length,
+        ]));
+    }
+
+    /**
+     * Create a new IP address column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function ipAddress($column = 'ip_address')
+    {
+        return $this->addColumn('ipAddress', $column);
+    }
+
+    /**
+     * Create a new MAC address column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function macAddress($column = 'mac_address')
+    {
+        return $this->addColumn('macAddress', $column);
+    }
+
+    /**
+     * Create a new geometry column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function geometry($column)
+    {
+        return $this->addColumn('geometry', $column);
+    }
+
+    /**
+     * Create a new point column on the table.
+     *
+     * @param  string  $column
+     * @param  int|null  $srid
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function point($column, $srid = null)
+    {
+        return $this->addColumn('point', $column, compact('srid'));
+    }
+
+    /**
+     * Create a new linestring column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function lineString($column)
+    {
+        return $this->addColumn('linestring', $column);
+    }
+
+    /**
+     * Create a new polygon column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function polygon($column)
+    {
+        return $this->addColumn('polygon', $column);
+    }
+
+    /**
+     * Create a new geometrycollection column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function geometryCollection($column)
+    {
+        return $this->addColumn('geometrycollection', $column);
+    }
+
+    /**
+     * Create a new multipoint column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function multiPoint($column)
+    {
+        return $this->addColumn('multipoint', $column);
+    }
+
+    /**
+     * Create a new multilinestring column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function multiLineString($column)
+    {
+        return $this->addColumn('multilinestring', $column);
+    }
+
+    /**
+     * Create a new multipolygon column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function multiPolygon($column)
+    {
+        return $this->addColumn('multipolygon', $column);
+    }
+
+    /**
+     * Create a new multipolygon column on the table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function multiPolygonZ($column)
+    {
+        return $this->addColumn('multipolygonz', $column);
+    }
+
+    /**
+     * Create a new generated, computed column on the table.
+     *
+     * @param  string  $column
+     * @param  string  $expression
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function computed($column, $expression)
+    {
+        return $this->addColumn('computed', $column, compact('expression'));
+    }
+
+    /**
+     * Add the proper columns for a polymorphic table.
+     *
+     * @param  string  $name
+     * @param  string|null  $indexName
+     * @return void
+     */
+    public function morphs($name, $indexName = null)
+    {
+        if (Builder::$defaultMorphKeyType === 'uuid') {
+            $this->uuidMorphs($name, $indexName);
+        } elseif (Builder::$defaultMorphKeyType === 'ulid') {
+            $this->ulidMorphs($name, $indexName);
+        } else {
+            $this->numericMorphs($name, $indexName);
+        }
+    }
+
+    /**
+     * Add nullable columns for a polymorphic table.
+     *
+     * @param  string  $name
+     * @param  string|null  $indexName
+     * @return void
+     */
+    public function nullableMorphs($name, $indexName = null)
+    {
+        if (Builder::$defaultMorphKeyType === 'uuid') {
+            $this->nullableUuidMorphs($name, $indexName);
+        } elseif (Builder::$defaultMorphKeyType === 'ulid') {
+            $this->nullableUlidMorphs($name, $indexName);
+        } else {
+            $this->nullableNumericMorphs($name, $indexName);
+        }
+    }
+
+    /**
+     * Add the proper columns for a polymorphic table using numeric IDs (incremental).
+     *
+     * @param  string  $name
+     * @param  string|null  $indexName
+     * @return void
+     */
+    public function numericMorphs($name, $indexName = null)
+    {
+        $this->string("{$name}_type");
+
+        $this->unsignedBigInteger("{$name}_id");
+
+        $this->index(["{$name}_type", "{$name}_id"], $indexName);
+    }
+
+    /**
+     * Add nullable columns for a polymorphic table using numeric IDs (incremental).
+     *
+     * @param  string  $name
+     * @param  string|null  $indexName
+     * @return void
+     */
+    public function nullableNumericMorphs($name, $indexName = null)
+    {
+        $this->string("{$name}_type")->nullable();
+
+        $this->unsignedBigInteger("{$name}_id")->nullable();
+
+        $this->index(["{$name}_type", "{$name}_id"], $indexName);
+    }
+
+    /**
+     * Add the proper columns for a polymorphic table using UUIDs.
+     *
+     * @param  string  $name
+     * @param  string|null  $indexName
+     * @return void
+     */
+    public function uuidMorphs($name, $indexName = null)
+    {
+        $this->string("{$name}_type");
+
+        $this->uuid("{$name}_id");
+
+        $this->index(["{$name}_type", "{$name}_id"], $indexName);
+    }
+
+    /**
+     * Add nullable columns for a polymorphic table using UUIDs.
+     *
+     * @param  string  $name
+     * @param  string|null  $indexName
+     * @return void
+     */
+    public function nullableUuidMorphs($name, $indexName = null)
+    {
+        $this->string("{$name}_type")->nullable();
+
+        $this->uuid("{$name}_id")->nullable();
+
+        $this->index(["{$name}_type", "{$name}_id"], $indexName);
+    }
+
+    /**
+     * Add the proper columns for a polymorphic table using ULIDs.
+     *
+     * @param  string  $name
+     * @param  string|null  $indexName
+     * @return void
+     */
+    public function ulidMorphs($name, $indexName = null)
+    {
+        $this->string("{$name}_type");
+
+        $this->ulid("{$name}_id");
+
+        $this->index(["{$name}_type", "{$name}_id"], $indexName);
+    }
+
+    /**
+     * Add nullable columns for a polymorphic table using ULIDs.
+     *
+     * @param  string  $name
+     * @param  string|null  $indexName
+     * @return void
+     */
+    public function nullableUlidMorphs($name, $indexName = null)
+    {
+        $this->string("{$name}_type")->nullable();
+
+        $this->ulid("{$name}_id")->nullable();
+
+        $this->index(["{$name}_type", "{$name}_id"], $indexName);
+    }
+
+    /**
+     * Adds the `remember_token` column to the table.
+     *
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function rememberToken()
+    {
+        return $this->string('remember_token', 100)->nullable();
+    }
+
+    /**
+     * Add a comment to the table.
+     *
+     * @param  string  $comment
+     * @return \Illuminate\Support\Fluent
+     */
+    public function comment($comment)
+    {
+        return $this->addCommand('tableComment', compact('comment'));
+    }
+
+    /**
+     * Add a new index command to the blueprint.
+     *
+     * @param  string  $type
+     * @param  string|array  $columns
+     * @param  string  $index
+     * @param  string|null  $algorithm
+     * @return \Illuminate\Support\Fluent
+     */
+    protected function indexCommand($type, $columns, $index, $algorithm = null)
+    {
+        $columns = (array) $columns;
+
+        // If no name was specified for this index, we will create one using a basic
+        // convention of the table name, followed by the columns, followed by an
+        // index type, such as primary or index, which makes the index unique.
+        $index = $index ?: $this->createIndexName($type, $columns);
+
+        return $this->addCommand(
+            $type, compact('index', 'columns', 'algorithm')
+        );
+    }
+
+    /**
+     * Create a new drop index command on the blueprint.
+     *
+     * @param  string  $command
+     * @param  string  $type
+     * @param  string|array  $index
+     * @return \Illuminate\Support\Fluent
+     */
+    protected function dropIndexCommand($command, $type, $index)
+    {
+        $columns = [];
+
+        // If the given "index" is actually an array of columns, the developer means
+        // to drop an index merely by specifying the columns involved without the
+        // conventional name, so we will build the index name from the columns.
+        if (is_array($index)) {
+            $index = $this->createIndexName($type, $columns = $index);
+        }
+
+        return $this->indexCommand($command, $columns, $index);
+    }
+
+    /**
+     * Create a default index name for the table.
+     *
+     * @param  string  $type
+     * @param  array  $columns
+     * @return string
+     */
+    protected function createIndexName($type, array $columns)
+    {
+        $index = strtolower($this->prefix.$this->table.'_'.implode('_', $columns).'_'.$type);
+
+        return str_replace(['-', '.'], '_', $index);
+    }
+
+    /**
+     * Add a new column to the blueprint.
+     *
+     * @param  string  $type
+     * @param  string  $name
+     * @param  array  $parameters
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    public function addColumn($type, $name, array $parameters = [])
+    {
+        return $this->addColumnDefinition(new ColumnDefinition(
+            array_merge(compact('type', 'name'), $parameters)
+        ));
+    }
+
+    /**
+     * Add a new column definition to the blueprint.
+     *
+     * @param  \Illuminate\Database\Schema\ColumnDefinition  $definition
+     * @return \Illuminate\Database\Schema\ColumnDefinition
+     */
+    protected function addColumnDefinition($definition)
+    {
+        $this->columns[] = $definition;
+
+        if ($this->after) {
+            $definition->after($this->after);
+
+            $this->after = $definition->name;
+        }
+
+        return $definition;
+    }
+
+    /**
+     * Add the columns from the callback after the given column.
+     *
+     * @param  string  $column
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function after($column, Closure $callback)
+    {
+        $this->after = $column;
+
+        $callback($this);
+
+        $this->after = null;
+    }
+
+    /**
+     * Remove a column from the schema blueprint.
+     *
+     * @param  string  $name
+     * @return $this
+     */
+    public function removeColumn($name)
+    {
+        $this->columns = array_values(array_filter($this->columns, function ($c) use ($name) {
+            return $c['name'] != $name;
+        }));
+
+        return $this;
+    }
+
+    /**
+     * Add a new command to the blueprint.
+     *
+     * @param  string  $name
+     * @param  array  $parameters
+     * @return \Illuminate\Support\Fluent
+     */
+    protected function addCommand($name, array $parameters = [])
+    {
+        $this->commands[] = $command = $this->createCommand($name, $parameters);
+
+        return $command;
+    }
+
+    /**
+     * Create a new Fluent command.
+     *
+     * @param  string  $name
+     * @param  array  $parameters
+     * @return \Illuminate\Support\Fluent
+     */
+    protected function createCommand($name, array $parameters = [])
+    {
+        return new Fluent(array_merge(compact('name'), $parameters));
+    }
+
+    /**
+     * Get the table the blueprint describes.
+     *
+     * @return string
+     */
+    public function getTable()
+    {
+        return $this->table;
+    }
+
+    /**
+     * Get the columns on the blueprint.
+     *
+     * @return \Illuminate\Database\Schema\ColumnDefinition[]
+     */
+    public function getColumns()
+    {
+        return $this->columns;
+    }
+
+    /**
+     * Get the commands on the blueprint.
+     *
+     * @return \Illuminate\Support\Fluent[]
+     */
+    public function getCommands()
+    {
+        return $this->commands;
+    }
+
+    /**
+     * Get the columns on the blueprint that should be added.
+     *
+     * @return \Illuminate\Database\Schema\ColumnDefinition[]
+     */
+    public function getAddedColumns()
+    {
+        return array_filter($this->columns, function ($column) {
+            return ! $column->change;
+        });
+    }
+
+    /**
+     * Get the columns on the blueprint that should be changed.
+     *
+     * @return \Illuminate\Database\Schema\ColumnDefinition[]
+     */
+    public function getChangedColumns()
+    {
+        return array_filter($this->columns, function ($column) {
+            return (bool) $column->change;
+        });
+    }
+
+    /**
+     * Determine if the blueprint has auto-increment columns.
+     *
+     * @return bool
+     */
+    public function hasAutoIncrementColumn()
+    {
+        return ! is_null(collect($this->getAddedColumns())->first(function ($column) {
+            return $column->autoIncrement === true;
+        }));
+    }
+
+    /**
+     * Get the auto-increment column starting values.
+     *
+     * @return array
+     */
+    public function autoIncrementingStartingValues()
+    {
+        if (! $this->hasAutoIncrementColumn()) {
+            return [];
+        }
+
+        return collect($this->getAddedColumns())->mapWithKeys(function ($column) {
+            return $column->autoIncrement === true
+                        ? [$column->name => $column->get('startingValue', $column->get('from'))]
+                        : [$column->name => null];
+        })->filter()->all();
+    }
+}
diff --git a/vendor/illuminate/database/Schema/Builder.php b/vendor/illuminate/database/Schema/Builder.php
new file mode 100755
index 0000000..88cd965
--- /dev/null
+++ b/vendor/illuminate/database/Schema/Builder.php
@@ -0,0 +1,495 @@
+connection = $connection;
+        $this->grammar = $connection->getSchemaGrammar();
+    }
+
+    /**
+     * Set the default string length for migrations.
+     *
+     * @param  int  $length
+     * @return void
+     */
+    public static function defaultStringLength($length)
+    {
+        static::$defaultStringLength = $length;
+    }
+
+    /**
+     * Set the default morph key type for migrations.
+     *
+     * @param  string  $type
+     * @return void
+     *
+     * @throws \InvalidArgumentException
+     */
+    public static function defaultMorphKeyType(string $type)
+    {
+        if (! in_array($type, ['int', 'uuid', 'ulid'])) {
+            throw new InvalidArgumentException("Morph key type must be 'int', 'uuid', or 'ulid'.");
+        }
+
+        static::$defaultMorphKeyType = $type;
+    }
+
+    /**
+     * Set the default morph key type for migrations to UUIDs.
+     *
+     * @return void
+     */
+    public static function morphUsingUuids()
+    {
+        return static::defaultMorphKeyType('uuid');
+    }
+
+    /**
+     * Set the default morph key type for migrations to ULIDs.
+     *
+     * @return void
+     */
+    public static function morphUsingUlids()
+    {
+        return static::defaultMorphKeyType('ulid');
+    }
+
+    /**
+     * Attempt to use native schema operations for dropping and renaming columns, even if Doctrine DBAL is installed.
+     *
+     * @param  bool  $value
+     * @return void
+     */
+    public static function useNativeSchemaOperationsIfPossible(bool $value = true)
+    {
+        static::$alwaysUsesNativeSchemaOperationsIfPossible = $value;
+    }
+
+    /**
+     * Create a database in the schema.
+     *
+     * @param  string  $name
+     * @return bool
+     *
+     * @throws \LogicException
+     */
+    public function createDatabase($name)
+    {
+        throw new LogicException('This database driver does not support creating databases.');
+    }
+
+    /**
+     * Drop a database from the schema if the database exists.
+     *
+     * @param  string  $name
+     * @return bool
+     *
+     * @throws \LogicException
+     */
+    public function dropDatabaseIfExists($name)
+    {
+        throw new LogicException('This database driver does not support dropping databases.');
+    }
+
+    /**
+     * Determine if the given table exists.
+     *
+     * @param  string  $table
+     * @return bool
+     */
+    public function hasTable($table)
+    {
+        $table = $this->connection->getTablePrefix().$table;
+
+        return count($this->connection->selectFromWriteConnection(
+            $this->grammar->compileTableExists(), [$table]
+        )) > 0;
+    }
+
+    /**
+     * Determine if the given table has a given column.
+     *
+     * @param  string  $table
+     * @param  string  $column
+     * @return bool
+     */
+    public function hasColumn($table, $column)
+    {
+        return in_array(
+            strtolower($column), array_map('strtolower', $this->getColumnListing($table))
+        );
+    }
+
+    /**
+     * Determine if the given table has given columns.
+     *
+     * @param  string  $table
+     * @param  array  $columns
+     * @return bool
+     */
+    public function hasColumns($table, array $columns)
+    {
+        $tableColumns = array_map('strtolower', $this->getColumnListing($table));
+
+        foreach ($columns as $column) {
+            if (! in_array(strtolower($column), $tableColumns)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Execute a table builder callback if the given table has a given column.
+     *
+     * @param  string  $table
+     * @param  string  $column
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function whenTableHasColumn(string $table, string $column, Closure $callback)
+    {
+        if ($this->hasColumn($table, $column)) {
+            $this->table($table, fn (Blueprint $table) => $callback($table));
+        }
+    }
+
+    /**
+     * Execute a table builder callback if the given table doesn't have a given column.
+     *
+     * @param  string  $table
+     * @param  string  $column
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function whenTableDoesntHaveColumn(string $table, string $column, Closure $callback)
+    {
+        if (! $this->hasColumn($table, $column)) {
+            $this->table($table, fn (Blueprint $table) => $callback($table));
+        }
+    }
+
+    /**
+     * Get the data type for the given column name.
+     *
+     * @param  string  $table
+     * @param  string  $column
+     * @return string
+     */
+    public function getColumnType($table, $column)
+    {
+        $table = $this->connection->getTablePrefix().$table;
+
+        return $this->connection->getDoctrineColumn($table, $column)->getType()->getName();
+    }
+
+    /**
+     * Get the column listing for a given table.
+     *
+     * @param  string  $table
+     * @return array
+     */
+    public function getColumnListing($table)
+    {
+        $results = $this->connection->selectFromWriteConnection($this->grammar->compileColumnListing(
+            $this->connection->getTablePrefix().$table
+        ));
+
+        return $this->connection->getPostProcessor()->processColumnListing($results);
+    }
+
+    /**
+     * Modify a table on the schema.
+     *
+     * @param  string  $table
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function table($table, Closure $callback)
+    {
+        $this->build($this->createBlueprint($table, $callback));
+    }
+
+    /**
+     * Create a new table on the schema.
+     *
+     * @param  string  $table
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function create($table, Closure $callback)
+    {
+        $this->build(tap($this->createBlueprint($table), function ($blueprint) use ($callback) {
+            $blueprint->create();
+
+            $callback($blueprint);
+        }));
+    }
+
+    /**
+     * Drop a table from the schema.
+     *
+     * @param  string  $table
+     * @return void
+     */
+    public function drop($table)
+    {
+        $this->build(tap($this->createBlueprint($table), function ($blueprint) {
+            $blueprint->drop();
+        }));
+    }
+
+    /**
+     * Drop a table from the schema if it exists.
+     *
+     * @param  string  $table
+     * @return void
+     */
+    public function dropIfExists($table)
+    {
+        $this->build(tap($this->createBlueprint($table), function ($blueprint) {
+            $blueprint->dropIfExists();
+        }));
+    }
+
+    /**
+     * Drop columns from a table schema.
+     *
+     * @param  string  $table
+     * @param  string|array  $columns
+     * @return void
+     */
+    public function dropColumns($table, $columns)
+    {
+        $this->table($table, function (Blueprint $blueprint) use ($columns) {
+            $blueprint->dropColumn($columns);
+        });
+    }
+
+    /**
+     * Drop all tables from the database.
+     *
+     * @return void
+     *
+     * @throws \LogicException
+     */
+    public function dropAllTables()
+    {
+        throw new LogicException('This database driver does not support dropping all tables.');
+    }
+
+    /**
+     * Drop all views from the database.
+     *
+     * @return void
+     *
+     * @throws \LogicException
+     */
+    public function dropAllViews()
+    {
+        throw new LogicException('This database driver does not support dropping all views.');
+    }
+
+    /**
+     * Drop all types from the database.
+     *
+     * @return void
+     *
+     * @throws \LogicException
+     */
+    public function dropAllTypes()
+    {
+        throw new LogicException('This database driver does not support dropping all types.');
+    }
+
+    /**
+     * Get all of the table names for the database.
+     *
+     * @return void
+     *
+     * @throws \LogicException
+     */
+    public function getAllTables()
+    {
+        throw new LogicException('This database driver does not support getting all tables.');
+    }
+
+    /**
+     * Rename a table on the schema.
+     *
+     * @param  string  $from
+     * @param  string  $to
+     * @return void
+     */
+    public function rename($from, $to)
+    {
+        $this->build(tap($this->createBlueprint($from), function ($blueprint) use ($to) {
+            $blueprint->rename($to);
+        }));
+    }
+
+    /**
+     * Enable foreign key constraints.
+     *
+     * @return bool
+     */
+    public function enableForeignKeyConstraints()
+    {
+        return $this->connection->statement(
+            $this->grammar->compileEnableForeignKeyConstraints()
+        );
+    }
+
+    /**
+     * Disable foreign key constraints.
+     *
+     * @return bool
+     */
+    public function disableForeignKeyConstraints()
+    {
+        return $this->connection->statement(
+            $this->grammar->compileDisableForeignKeyConstraints()
+        );
+    }
+
+    /**
+     * Disable foreign key constraints during the execution of a callback.
+     *
+     * @param  \Closure  $callback
+     * @return mixed
+     */
+    public function withoutForeignKeyConstraints(Closure $callback)
+    {
+        $this->disableForeignKeyConstraints();
+
+        $result = $callback();
+
+        $this->enableForeignKeyConstraints();
+
+        return $result;
+    }
+
+    /**
+     * Execute the blueprint to build / modify the table.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @return void
+     */
+    protected function build(Blueprint $blueprint)
+    {
+        $blueprint->build($this->connection, $this->grammar);
+    }
+
+    /**
+     * Create a new command set with a Closure.
+     *
+     * @param  string  $table
+     * @param  \Closure|null  $callback
+     * @return \Illuminate\Database\Schema\Blueprint
+     */
+    protected function createBlueprint($table, Closure $callback = null)
+    {
+        $prefix = $this->connection->getConfig('prefix_indexes')
+                    ? $this->connection->getConfig('prefix')
+                    : '';
+
+        if (isset($this->resolver)) {
+            return call_user_func($this->resolver, $table, $callback, $prefix);
+        }
+
+        return Container::getInstance()->make(Blueprint::class, compact('table', 'callback', 'prefix'));
+    }
+
+    /**
+     * Get the database connection instance.
+     *
+     * @return \Illuminate\Database\Connection
+     */
+    public function getConnection()
+    {
+        return $this->connection;
+    }
+
+    /**
+     * Set the database connection instance.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return $this
+     */
+    public function setConnection(Connection $connection)
+    {
+        $this->connection = $connection;
+
+        return $this;
+    }
+
+    /**
+     * Set the Schema Blueprint resolver callback.
+     *
+     * @param  \Closure  $resolver
+     * @return void
+     */
+    public function blueprintResolver(Closure $resolver)
+    {
+        $this->resolver = $resolver;
+    }
+}
diff --git a/vendor/illuminate/database/Schema/ColumnDefinition.php b/vendor/illuminate/database/Schema/ColumnDefinition.php
new file mode 100644
index 0000000..51265ac
--- /dev/null
+++ b/vendor/illuminate/database/Schema/ColumnDefinition.php
@@ -0,0 +1,38 @@
+blueprint = $blueprint;
+    }
+
+    /**
+     * Create a foreign key constraint on this column referencing the "id" column of the conventionally related table.
+     *
+     * @param  string|null  $table
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ForeignKeyDefinition
+     */
+    public function constrained($table = null, $column = 'id')
+    {
+        return $this->references($column)->on($table ?? Str::of($this->name)->beforeLast('_'.$column)->plural());
+    }
+
+    /**
+     * Specify which column this foreign ID references on another table.
+     *
+     * @param  string  $column
+     * @return \Illuminate\Database\Schema\ForeignKeyDefinition
+     */
+    public function references($column)
+    {
+        return $this->blueprint->foreign($this->name)->references($column);
+    }
+}
diff --git a/vendor/illuminate/database/Schema/ForeignKeyDefinition.php b/vendor/illuminate/database/Schema/ForeignKeyDefinition.php
new file mode 100644
index 0000000..3bb8b71
--- /dev/null
+++ b/vendor/illuminate/database/Schema/ForeignKeyDefinition.php
@@ -0,0 +1,76 @@
+onUpdate('cascade');
+    }
+
+    /**
+     * Indicate that updates should be restricted.
+     *
+     * @return $this
+     */
+    public function restrictOnUpdate()
+    {
+        return $this->onUpdate('restrict');
+    }
+
+    /**
+     * Indicate that deletes should cascade.
+     *
+     * @return $this
+     */
+    public function cascadeOnDelete()
+    {
+        return $this->onDelete('cascade');
+    }
+
+    /**
+     * Indicate that deletes should be restricted.
+     *
+     * @return $this
+     */
+    public function restrictOnDelete()
+    {
+        return $this->onDelete('restrict');
+    }
+
+    /**
+     * Indicate that deletes should set the foreign key value to null.
+     *
+     * @return $this
+     */
+    public function nullOnDelete()
+    {
+        return $this->onDelete('set null');
+    }
+
+    /**
+     * Indicate that deletes should have "no action".
+     *
+     * @return $this
+     */
+    public function noActionOnDelete()
+    {
+        return $this->onDelete('no action');
+    }
+}
diff --git a/vendor/illuminate/database/Schema/Grammars/ChangeColumn.php b/vendor/illuminate/database/Schema/Grammars/ChangeColumn.php
new file mode 100644
index 0000000..9579222
--- /dev/null
+++ b/vendor/illuminate/database/Schema/Grammars/ChangeColumn.php
@@ -0,0 +1,235 @@
+isDoctrineAvailable()) {
+            throw new RuntimeException(sprintf(
+                'Changing columns for table "%s" requires Doctrine DBAL. Please install the doctrine/dbal package.',
+                $blueprint->getTable()
+            ));
+        }
+
+        $schema = $connection->getDoctrineSchemaManager();
+        $databasePlatform = $schema->getDatabasePlatform();
+        $databasePlatform->registerDoctrineTypeMapping('enum', 'string');
+
+        $tableDiff = static::getChangedDiff(
+            $grammar, $blueprint, $schema
+        );
+
+        if ($tableDiff !== false) {
+            return (array) $databasePlatform->getAlterTableSQL($tableDiff);
+        }
+
+        return [];
+    }
+
+    /**
+     * Get the Doctrine table difference for the given changes.
+     *
+     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Doctrine\DBAL\Schema\AbstractSchemaManager  $schema
+     * @return \Doctrine\DBAL\Schema\TableDiff|bool
+     */
+    protected static function getChangedDiff($grammar, Blueprint $blueprint, SchemaManager $schema)
+    {
+        $current = $schema->listTableDetails($grammar->getTablePrefix().$blueprint->getTable());
+
+        return (new Comparator)->diffTable(
+            $current, static::getTableWithColumnChanges($blueprint, $current)
+        );
+    }
+
+    /**
+     * Get a copy of the given Doctrine table after making the column changes.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Doctrine\DBAL\Schema\Table  $table
+     * @return \Doctrine\DBAL\Schema\Table
+     */
+    protected static function getTableWithColumnChanges(Blueprint $blueprint, Table $table)
+    {
+        $table = clone $table;
+
+        foreach ($blueprint->getChangedColumns() as $fluent) {
+            $column = static::getDoctrineColumn($table, $fluent);
+
+            // Here we will spin through each fluent column definition and map it to the proper
+            // Doctrine column definitions - which is necessary because Laravel and Doctrine
+            // use some different terminology for various column attributes on the tables.
+            foreach ($fluent->getAttributes() as $key => $value) {
+                if (! is_null($option = static::mapFluentOptionToDoctrine($key))) {
+                    if (method_exists($column, $method = 'set'.ucfirst($option))) {
+                        $column->{$method}(static::mapFluentValueToDoctrine($option, $value));
+                        continue;
+                    }
+
+                    $column->setCustomSchemaOption($option, static::mapFluentValueToDoctrine($option, $value));
+                }
+            }
+        }
+
+        return $table;
+    }
+
+    /**
+     * Get the Doctrine column instance for a column change.
+     *
+     * @param  \Doctrine\DBAL\Schema\Table  $table
+     * @param  \Illuminate\Support\Fluent  $fluent
+     * @return \Doctrine\DBAL\Schema\Column
+     */
+    protected static function getDoctrineColumn(Table $table, Fluent $fluent)
+    {
+        return $table->changeColumn(
+            $fluent['name'], static::getDoctrineColumnChangeOptions($fluent)
+        )->getColumn($fluent['name']);
+    }
+
+    /**
+     * Get the Doctrine column change options.
+     *
+     * @param  \Illuminate\Support\Fluent  $fluent
+     * @return array
+     */
+    protected static function getDoctrineColumnChangeOptions(Fluent $fluent)
+    {
+        $options = ['type' => static::getDoctrineColumnType($fluent['type'])];
+
+        if (in_array($fluent['type'], ['tinyText', 'text', 'mediumText', 'longText'])) {
+            $options['length'] = static::calculateDoctrineTextLength($fluent['type']);
+        }
+
+        if ($fluent['type'] === 'char') {
+            $options['fixed'] = true;
+        }
+
+        if (static::doesntNeedCharacterOptions($fluent['type'])) {
+            $options['customSchemaOptions'] = [
+                'collation' => '',
+                'charset' => '',
+            ];
+        }
+
+        return $options;
+    }
+
+    /**
+     * Get the doctrine column type.
+     *
+     * @param  string  $type
+     * @return \Doctrine\DBAL\Types\Type
+     */
+    protected static function getDoctrineColumnType($type)
+    {
+        $type = strtolower($type);
+
+        return Type::getType(match ($type) {
+            'biginteger' => 'bigint',
+            'smallinteger' => 'smallint',
+            'tinytext', 'mediumtext', 'longtext' => 'text',
+            'binary' => 'blob',
+            'uuid' => 'guid',
+            'char' => 'string',
+            'double' => 'float',
+            default => $type,
+        });
+    }
+
+    /**
+     * Calculate the proper column length to force the Doctrine text type.
+     *
+     * @param  string  $type
+     * @return int
+     */
+    protected static function calculateDoctrineTextLength($type)
+    {
+        return match ($type) {
+            'tinyText' => 1,
+            'mediumText' => 65535 + 1,
+            'longText' => 16777215 + 1,
+            default => 255 + 1,
+        };
+    }
+
+    /**
+     * Determine if the given type does not need character / collation options.
+     *
+     * @param  string  $type
+     * @return bool
+     */
+    protected static function doesntNeedCharacterOptions($type)
+    {
+        return in_array($type, [
+            'bigInteger',
+            'binary',
+            'boolean',
+            'date',
+            'dateTime',
+            'decimal',
+            'double',
+            'float',
+            'integer',
+            'json',
+            'mediumInteger',
+            'smallInteger',
+            'time',
+            'timestamp',
+            'tinyInteger',
+        ]);
+    }
+
+    /**
+     * Get the matching Doctrine option for a given Fluent attribute name.
+     *
+     * @param  string  $attribute
+     * @return string|null
+     */
+    protected static function mapFluentOptionToDoctrine($attribute)
+    {
+        return match ($attribute) {
+            'type', 'name' => null,
+            'nullable' => 'notnull',
+            'total' => 'precision',
+            'places' => 'scale',
+            default => $attribute,
+        };
+    }
+
+    /**
+     * Get the matching Doctrine value for a given Fluent attribute.
+     *
+     * @param  string  $option
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected static function mapFluentValueToDoctrine($option, $value)
+    {
+        return $option === 'notnull' ? ! $value : $value;
+    }
+}
diff --git a/vendor/illuminate/database/Schema/Grammars/Grammar.php b/vendor/illuminate/database/Schema/Grammars/Grammar.php
new file mode 100755
index 0000000..ea8333e
--- /dev/null
+++ b/vendor/illuminate/database/Schema/Grammars/Grammar.php
@@ -0,0 +1,345 @@
+wrapTable($blueprint),
+            $this->wrap($command->index)
+        );
+
+        // Once we have the initial portion of the SQL statement we will add on the
+        // key name, table name, and referenced columns. These will complete the
+        // main portion of the SQL statement and this SQL will almost be done.
+        $sql .= sprintf('foreign key (%s) references %s (%s)',
+            $this->columnize($command->columns),
+            $this->wrapTable($command->on),
+            $this->columnize((array) $command->references)
+        );
+
+        // Once we have the basic foreign key creation statement constructed we can
+        // build out the syntax for what should happen on an update or delete of
+        // the affected columns, which will get something like "cascade", etc.
+        if (! is_null($command->onDelete)) {
+            $sql .= " on delete {$command->onDelete}";
+        }
+
+        if (! is_null($command->onUpdate)) {
+            $sql .= " on update {$command->onUpdate}";
+        }
+
+        return $sql;
+    }
+
+    /**
+     * Compile the blueprint's column definitions.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @return array
+     */
+    protected function getColumns(Blueprint $blueprint)
+    {
+        $columns = [];
+
+        foreach ($blueprint->getAddedColumns() as $column) {
+            // Each of the column types has their own compiler functions, which are tasked
+            // with turning the column definition into its SQL format for this platform
+            // used by the connection. The column's modifiers are compiled and added.
+            $sql = $this->wrap($column).' '.$this->getType($column);
+
+            $columns[] = $this->addModifiers($sql, $blueprint, $column);
+        }
+
+        return $columns;
+    }
+
+    /**
+     * Get the SQL for the column data type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function getType(Fluent $column)
+    {
+        return $this->{'type'.ucfirst($column->type)}($column);
+    }
+
+    /**
+     * Create the column definition for a generated, computed column type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return void
+     *
+     * @throws \RuntimeException
+     */
+    protected function typeComputed(Fluent $column)
+    {
+        throw new RuntimeException('This database driver does not support the computed type.');
+    }
+
+    /**
+     * Add the column modifiers to the definition.
+     *
+     * @param  string  $sql
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function addModifiers($sql, Blueprint $blueprint, Fluent $column)
+    {
+        foreach ($this->modifiers as $modifier) {
+            if (method_exists($this, $method = "modify{$modifier}")) {
+                $sql .= $this->{$method}($blueprint, $column);
+            }
+        }
+
+        return $sql;
+    }
+
+    /**
+     * Get the primary key command if it exists on the blueprint.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  string  $name
+     * @return \Illuminate\Support\Fluent|null
+     */
+    protected function getCommandByName(Blueprint $blueprint, $name)
+    {
+        $commands = $this->getCommandsByName($blueprint, $name);
+
+        if (count($commands) > 0) {
+            return reset($commands);
+        }
+    }
+
+    /**
+     * Get all of the commands with a given name.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  string  $name
+     * @return array
+     */
+    protected function getCommandsByName(Blueprint $blueprint, $name)
+    {
+        return array_filter($blueprint->getCommands(), function ($value) use ($name) {
+            return $value->name == $name;
+        });
+    }
+
+    /**
+     * Add a prefix to an array of values.
+     *
+     * @param  string  $prefix
+     * @param  array  $values
+     * @return array
+     */
+    public function prefixArray($prefix, array $values)
+    {
+        return array_map(function ($value) use ($prefix) {
+            return $prefix.' '.$value;
+        }, $values);
+    }
+
+    /**
+     * Wrap a table in keyword identifiers.
+     *
+     * @param  mixed  $table
+     * @return string
+     */
+    public function wrapTable($table)
+    {
+        return parent::wrapTable(
+            $table instanceof Blueprint ? $table->getTable() : $table
+        );
+    }
+
+    /**
+     * Wrap a value in keyword identifiers.
+     *
+     * @param  \Illuminate\Database\Query\Expression|string  $value
+     * @param  bool  $prefixAlias
+     * @return string
+     */
+    public function wrap($value, $prefixAlias = false)
+    {
+        return parent::wrap(
+            $value instanceof Fluent ? $value->name : $value, $prefixAlias
+        );
+    }
+
+    /**
+     * Format a value so that it can be used in "default" clauses.
+     *
+     * @param  mixed  $value
+     * @return string
+     */
+    protected function getDefaultValue($value)
+    {
+        if ($value instanceof Expression) {
+            return $value;
+        }
+
+        return is_bool($value)
+                    ? "'".(int) $value."'"
+                    : "'".(string) $value."'";
+    }
+
+    /**
+     * Create an empty Doctrine DBAL TableDiff from the Blueprint.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Doctrine\DBAL\Schema\AbstractSchemaManager  $schema
+     * @return \Doctrine\DBAL\Schema\TableDiff
+     */
+    public function getDoctrineTableDiff(Blueprint $blueprint, SchemaManager $schema)
+    {
+        $table = $this->getTablePrefix().$blueprint->getTable();
+
+        return tap(new TableDiff($table), function ($tableDiff) use ($schema, $table) {
+            $tableDiff->fromTable = $schema->listTableDetails($table);
+        });
+    }
+
+    /**
+     * Get the fluent commands for the grammar.
+     *
+     * @return array
+     */
+    public function getFluentCommands()
+    {
+        return $this->fluentCommands;
+    }
+
+    /**
+     * Check if this Grammar supports schema changes wrapped in a transaction.
+     *
+     * @return bool
+     */
+    public function supportsSchemaTransactions()
+    {
+        return $this->transactions;
+    }
+}
diff --git a/vendor/illuminate/database/Schema/Grammars/MySqlGrammar.php b/vendor/illuminate/database/Schema/Grammars/MySqlGrammar.php
new file mode 100755
index 0000000..f87acfd
--- /dev/null
+++ b/vendor/illuminate/database/Schema/Grammars/MySqlGrammar.php
@@ -0,0 +1,1221 @@
+wrapValue($name),
+            $this->wrapValue($connection->getConfig('charset')),
+            $this->wrapValue($connection->getConfig('collation')),
+        );
+    }
+
+    /**
+     * Compile a drop database if exists command.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    public function compileDropDatabaseIfExists($name)
+    {
+        return sprintf(
+            'drop database if exists %s',
+            $this->wrapValue($name)
+        );
+    }
+
+    /**
+     * Compile the query to determine the list of tables.
+     *
+     * @return string
+     */
+    public function compileTableExists()
+    {
+        return "select * from information_schema.tables where table_schema = ? and table_name = ? and table_type = 'BASE TABLE'";
+    }
+
+    /**
+     * Compile the query to determine the list of columns.
+     *
+     * @return string
+     */
+    public function compileColumnListing()
+    {
+        return 'select column_name as `column_name` from information_schema.columns where table_schema = ? and table_name = ?';
+    }
+
+    /**
+     * Compile a create table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return array
+     */
+    public function compileCreate(Blueprint $blueprint, Fluent $command, Connection $connection)
+    {
+        $sql = $this->compileCreateTable(
+            $blueprint, $command, $connection
+        );
+
+        // Once we have the primary SQL, we can add the encoding option to the SQL for
+        // the table.  Then, we can check if a storage engine has been supplied for
+        // the table. If so, we will add the engine declaration to the SQL query.
+        $sql = $this->compileCreateEncoding(
+            $sql, $connection, $blueprint
+        );
+
+        // Finally, we will append the engine configuration onto this SQL statement as
+        // the final thing we do before returning this finished SQL. Once this gets
+        // added the query will be ready to execute against the real connections.
+        return array_values(array_filter(array_merge([$this->compileCreateEngine(
+            $sql, $connection, $blueprint
+        )], $this->compileAutoIncrementStartingValues($blueprint))));
+    }
+
+    /**
+     * Create the main create table clause.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return array
+     */
+    protected function compileCreateTable($blueprint, $command, $connection)
+    {
+        return trim(sprintf('%s table %s (%s)',
+            $blueprint->temporary ? 'create temporary' : 'create',
+            $this->wrapTable($blueprint),
+            implode(', ', $this->getColumns($blueprint))
+        ));
+    }
+
+    /**
+     * Append the character set specifications to a command.
+     *
+     * @param  string  $sql
+     * @param  \Illuminate\Database\Connection  $connection
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @return string
+     */
+    protected function compileCreateEncoding($sql, Connection $connection, Blueprint $blueprint)
+    {
+        // First we will set the character set if one has been set on either the create
+        // blueprint itself or on the root configuration for the connection that the
+        // table is being created on. We will add these to the create table query.
+        if (isset($blueprint->charset)) {
+            $sql .= ' default character set '.$blueprint->charset;
+        } elseif (! is_null($charset = $connection->getConfig('charset'))) {
+            $sql .= ' default character set '.$charset;
+        }
+
+        // Next we will add the collation to the create table statement if one has been
+        // added to either this create table blueprint or the configuration for this
+        // connection that the query is targeting. We'll add it to this SQL query.
+        if (isset($blueprint->collation)) {
+            $sql .= " collate '{$blueprint->collation}'";
+        } elseif (! is_null($collation = $connection->getConfig('collation'))) {
+            $sql .= " collate '{$collation}'";
+        }
+
+        return $sql;
+    }
+
+    /**
+     * Append the engine specifications to a command.
+     *
+     * @param  string  $sql
+     * @param  \Illuminate\Database\Connection  $connection
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @return string
+     */
+    protected function compileCreateEngine($sql, Connection $connection, Blueprint $blueprint)
+    {
+        if (isset($blueprint->engine)) {
+            return $sql.' engine = '.$blueprint->engine;
+        } elseif (! is_null($engine = $connection->getConfig('engine'))) {
+            return $sql.' engine = '.$engine;
+        }
+
+        return $sql;
+    }
+
+    /**
+     * Compile an add column command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return array
+     */
+    public function compileAdd(Blueprint $blueprint, Fluent $command)
+    {
+        $columns = $this->prefixArray('add', $this->getColumns($blueprint));
+
+        return array_values(array_merge(
+            ['alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns)],
+            $this->compileAutoIncrementStartingValues($blueprint)
+        ));
+    }
+
+    /**
+     * Compile the auto-incrementing column starting values.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @return array
+     */
+    public function compileAutoIncrementStartingValues(Blueprint $blueprint)
+    {
+        return collect($blueprint->autoIncrementingStartingValues())->map(function ($value, $column) use ($blueprint) {
+            return 'alter table '.$this->wrapTable($blueprint->getTable()).' auto_increment = '.$value;
+        })->all();
+    }
+
+    /**
+     * Compile a rename column command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return array|string
+     */
+    public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
+    {
+        return $connection->usingNativeSchemaOperations()
+            ? sprintf('alter table %s rename column %s to %s',
+                $this->wrapTable($blueprint),
+                $this->wrap($command->from),
+                $this->wrap($command->to)
+            )
+            : parent::compileRenameColumn($blueprint, $command, $connection);
+    }
+
+    /**
+     * Compile a primary key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compilePrimary(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('alter table %s add primary key %s(%s)',
+            $this->wrapTable($blueprint),
+            $command->algorithm ? 'using '.$command->algorithm : '',
+            $this->columnize($command->columns)
+        );
+    }
+
+    /**
+     * Compile a unique key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileUnique(Blueprint $blueprint, Fluent $command)
+    {
+        return $this->compileKey($blueprint, $command, 'unique');
+    }
+
+    /**
+     * Compile a plain index key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return $this->compileKey($blueprint, $command, 'index');
+    }
+
+    /**
+     * Compile a fulltext index key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileFullText(Blueprint $blueprint, Fluent $command)
+    {
+        return $this->compileKey($blueprint, $command, 'fulltext');
+    }
+
+    /**
+     * Compile a spatial index key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileSpatialIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return $this->compileKey($blueprint, $command, 'spatial index');
+    }
+
+    /**
+     * Compile an index creation command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @param  string  $type
+     * @return string
+     */
+    protected function compileKey(Blueprint $blueprint, Fluent $command, $type)
+    {
+        return sprintf('alter table %s add %s %s%s(%s)',
+            $this->wrapTable($blueprint),
+            $type,
+            $this->wrap($command->index),
+            $command->algorithm ? ' using '.$command->algorithm : '',
+            $this->columnize($command->columns)
+        );
+    }
+
+    /**
+     * Compile a drop table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDrop(Blueprint $blueprint, Fluent $command)
+    {
+        return 'drop table '.$this->wrapTable($blueprint);
+    }
+
+    /**
+     * Compile a drop table (if exists) command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropIfExists(Blueprint $blueprint, Fluent $command)
+    {
+        return 'drop table if exists '.$this->wrapTable($blueprint);
+    }
+
+    /**
+     * Compile a drop column command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropColumn(Blueprint $blueprint, Fluent $command)
+    {
+        $columns = $this->prefixArray('drop', $this->wrapArray($command->columns));
+
+        return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns);
+    }
+
+    /**
+     * Compile a drop primary key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropPrimary(Blueprint $blueprint, Fluent $command)
+    {
+        return 'alter table '.$this->wrapTable($blueprint).' drop primary key';
+    }
+
+    /**
+     * Compile a drop unique key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropUnique(Blueprint $blueprint, Fluent $command)
+    {
+        $index = $this->wrap($command->index);
+
+        return "alter table {$this->wrapTable($blueprint)} drop index {$index}";
+    }
+
+    /**
+     * Compile a drop index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropIndex(Blueprint $blueprint, Fluent $command)
+    {
+        $index = $this->wrap($command->index);
+
+        return "alter table {$this->wrapTable($blueprint)} drop index {$index}";
+    }
+
+    /**
+     * Compile a drop fulltext index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropFullText(Blueprint $blueprint, Fluent $command)
+    {
+        return $this->compileDropIndex($blueprint, $command);
+    }
+
+    /**
+     * Compile a drop spatial index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropSpatialIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return $this->compileDropIndex($blueprint, $command);
+    }
+
+    /**
+     * Compile a drop foreign key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropForeign(Blueprint $blueprint, Fluent $command)
+    {
+        $index = $this->wrap($command->index);
+
+        return "alter table {$this->wrapTable($blueprint)} drop foreign key {$index}";
+    }
+
+    /**
+     * Compile a rename table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileRename(Blueprint $blueprint, Fluent $command)
+    {
+        $from = $this->wrapTable($blueprint);
+
+        return "rename table {$from} to ".$this->wrapTable($command->to);
+    }
+
+    /**
+     * Compile a rename index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileRenameIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('alter table %s rename index %s to %s',
+            $this->wrapTable($blueprint),
+            $this->wrap($command->from),
+            $this->wrap($command->to)
+        );
+    }
+
+    /**
+     * Compile the SQL needed to drop all tables.
+     *
+     * @param  array  $tables
+     * @return string
+     */
+    public function compileDropAllTables($tables)
+    {
+        return 'drop table '.implode(',', $this->wrapArray($tables));
+    }
+
+    /**
+     * Compile the SQL needed to drop all views.
+     *
+     * @param  array  $views
+     * @return string
+     */
+    public function compileDropAllViews($views)
+    {
+        return 'drop view '.implode(',', $this->wrapArray($views));
+    }
+
+    /**
+     * Compile the SQL needed to retrieve all table names.
+     *
+     * @return string
+     */
+    public function compileGetAllTables()
+    {
+        return 'SHOW FULL TABLES WHERE table_type = \'BASE TABLE\'';
+    }
+
+    /**
+     * Compile the SQL needed to retrieve all view names.
+     *
+     * @return string
+     */
+    public function compileGetAllViews()
+    {
+        return 'SHOW FULL TABLES WHERE table_type = \'VIEW\'';
+    }
+
+    /**
+     * Compile the command to enable foreign key constraints.
+     *
+     * @return string
+     */
+    public function compileEnableForeignKeyConstraints()
+    {
+        return 'SET FOREIGN_KEY_CHECKS=1;';
+    }
+
+    /**
+     * Compile the command to disable foreign key constraints.
+     *
+     * @return string
+     */
+    public function compileDisableForeignKeyConstraints()
+    {
+        return 'SET FOREIGN_KEY_CHECKS=0;';
+    }
+
+    /**
+     * Compile a table comment command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileTableComment(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('alter table %s comment = %s',
+            $this->wrapTable($blueprint),
+            "'".str_replace("'", "''", $command->comment)."'"
+        );
+    }
+
+    /**
+     * Create the column definition for a char type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeChar(Fluent $column)
+    {
+        return "char({$column->length})";
+    }
+
+    /**
+     * Create the column definition for a string type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeString(Fluent $column)
+    {
+        return "varchar({$column->length})";
+    }
+
+    /**
+     * Create the column definition for a tiny text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTinyText(Fluent $column)
+    {
+        return 'tinytext';
+    }
+
+    /**
+     * Create the column definition for a text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeText(Fluent $column)
+    {
+        return 'text';
+    }
+
+    /**
+     * Create the column definition for a medium text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMediumText(Fluent $column)
+    {
+        return 'mediumtext';
+    }
+
+    /**
+     * Create the column definition for a long text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeLongText(Fluent $column)
+    {
+        return 'longtext';
+    }
+
+    /**
+     * Create the column definition for a big integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeBigInteger(Fluent $column)
+    {
+        return 'bigint';
+    }
+
+    /**
+     * Create the column definition for an integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeInteger(Fluent $column)
+    {
+        return 'int';
+    }
+
+    /**
+     * Create the column definition for a medium integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMediumInteger(Fluent $column)
+    {
+        return 'mediumint';
+    }
+
+    /**
+     * Create the column definition for a tiny integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTinyInteger(Fluent $column)
+    {
+        return 'tinyint';
+    }
+
+    /**
+     * Create the column definition for a small integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeSmallInteger(Fluent $column)
+    {
+        return 'smallint';
+    }
+
+    /**
+     * Create the column definition for a float type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeFloat(Fluent $column)
+    {
+        return $this->typeDouble($column);
+    }
+
+    /**
+     * Create the column definition for a double type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDouble(Fluent $column)
+    {
+        if ($column->total && $column->places) {
+            return "double({$column->total}, {$column->places})";
+        }
+
+        return 'double';
+    }
+
+    /**
+     * Create the column definition for a decimal type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDecimal(Fluent $column)
+    {
+        return "decimal({$column->total}, {$column->places})";
+    }
+
+    /**
+     * Create the column definition for a boolean type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeBoolean(Fluent $column)
+    {
+        return 'tinyint(1)';
+    }
+
+    /**
+     * Create the column definition for an enumeration type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeEnum(Fluent $column)
+    {
+        return sprintf('enum(%s)', $this->quoteString($column->allowed));
+    }
+
+    /**
+     * Create the column definition for a set enumeration type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeSet(Fluent $column)
+    {
+        return sprintf('set(%s)', $this->quoteString($column->allowed));
+    }
+
+    /**
+     * Create the column definition for a json type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeJson(Fluent $column)
+    {
+        return 'json';
+    }
+
+    /**
+     * Create the column definition for a jsonb type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeJsonb(Fluent $column)
+    {
+        return 'json';
+    }
+
+    /**
+     * Create the column definition for a date type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDate(Fluent $column)
+    {
+        return 'date';
+    }
+
+    /**
+     * Create the column definition for a date-time type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDateTime(Fluent $column)
+    {
+        $columnType = $column->precision ? "datetime($column->precision)" : 'datetime';
+
+        $current = $column->precision ? "CURRENT_TIMESTAMP($column->precision)" : 'CURRENT_TIMESTAMP';
+
+        $columnType = $column->useCurrent ? "$columnType default $current" : $columnType;
+
+        return $column->useCurrentOnUpdate ? "$columnType on update $current" : $columnType;
+    }
+
+    /**
+     * Create the column definition for a date-time (with time zone) type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDateTimeTz(Fluent $column)
+    {
+        return $this->typeDateTime($column);
+    }
+
+    /**
+     * Create the column definition for a time type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTime(Fluent $column)
+    {
+        return $column->precision ? "time($column->precision)" : 'time';
+    }
+
+    /**
+     * Create the column definition for a time (with time zone) type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTimeTz(Fluent $column)
+    {
+        return $this->typeTime($column);
+    }
+
+    /**
+     * Create the column definition for a timestamp type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTimestamp(Fluent $column)
+    {
+        $columnType = $column->precision ? "timestamp($column->precision)" : 'timestamp';
+
+        $current = $column->precision ? "CURRENT_TIMESTAMP($column->precision)" : 'CURRENT_TIMESTAMP';
+
+        $columnType = $column->useCurrent ? "$columnType default $current" : $columnType;
+
+        return $column->useCurrentOnUpdate ? "$columnType on update $current" : $columnType;
+    }
+
+    /**
+     * Create the column definition for a timestamp (with time zone) type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTimestampTz(Fluent $column)
+    {
+        return $this->typeTimestamp($column);
+    }
+
+    /**
+     * Create the column definition for a year type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeYear(Fluent $column)
+    {
+        return 'year';
+    }
+
+    /**
+     * Create the column definition for a binary type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeBinary(Fluent $column)
+    {
+        return 'blob';
+    }
+
+    /**
+     * Create the column definition for a uuid type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeUuid(Fluent $column)
+    {
+        return 'char(36)';
+    }
+
+    /**
+     * Create the column definition for an IP address type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeIpAddress(Fluent $column)
+    {
+        return 'varchar(45)';
+    }
+
+    /**
+     * Create the column definition for a MAC address type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMacAddress(Fluent $column)
+    {
+        return 'varchar(17)';
+    }
+
+    /**
+     * Create the column definition for a spatial Geometry type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeGeometry(Fluent $column)
+    {
+        return 'geometry';
+    }
+
+    /**
+     * Create the column definition for a spatial Point type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typePoint(Fluent $column)
+    {
+        return 'point';
+    }
+
+    /**
+     * Create the column definition for a spatial LineString type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeLineString(Fluent $column)
+    {
+        return 'linestring';
+    }
+
+    /**
+     * Create the column definition for a spatial Polygon type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typePolygon(Fluent $column)
+    {
+        return 'polygon';
+    }
+
+    /**
+     * Create the column definition for a spatial GeometryCollection type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeGeometryCollection(Fluent $column)
+    {
+        return 'geometrycollection';
+    }
+
+    /**
+     * Create the column definition for a spatial MultiPoint type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeMultiPoint(Fluent $column)
+    {
+        return 'multipoint';
+    }
+
+    /**
+     * Create the column definition for a spatial MultiLineString type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeMultiLineString(Fluent $column)
+    {
+        return 'multilinestring';
+    }
+
+    /**
+     * Create the column definition for a spatial MultiPolygon type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeMultiPolygon(Fluent $column)
+    {
+        return 'multipolygon';
+    }
+
+    /**
+     * Create the column definition for a generated, computed column type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return void
+     *
+     * @throws \RuntimeException
+     */
+    protected function typeComputed(Fluent $column)
+    {
+        throw new RuntimeException('This database driver requires a type, see the virtualAs / storedAs modifiers.');
+    }
+
+    /**
+     * Get the SQL for a generated virtual column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyVirtualAs(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($virtualAs = $column->virtualAsJson)) {
+            if ($this->isJsonSelector($virtualAs)) {
+                $virtualAs = $this->wrapJsonSelector($virtualAs);
+            }
+
+            return " as ({$virtualAs})";
+        }
+
+        if (! is_null($virtualAs = $column->virtualAs)) {
+            return " as ({$virtualAs})";
+        }
+    }
+
+    /**
+     * Get the SQL for a generated stored column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyStoredAs(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($storedAs = $column->storedAsJson)) {
+            if ($this->isJsonSelector($storedAs)) {
+                $storedAs = $this->wrapJsonSelector($storedAs);
+            }
+
+            return " as ({$storedAs}) stored";
+        }
+
+        if (! is_null($storedAs = $column->storedAs)) {
+            return " as ({$storedAs}) stored";
+        }
+    }
+
+    /**
+     * Get the SQL for an unsigned column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyUnsigned(Blueprint $blueprint, Fluent $column)
+    {
+        if ($column->unsigned) {
+            return ' unsigned';
+        }
+    }
+
+    /**
+     * Get the SQL for a character set column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyCharset(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($column->charset)) {
+            return ' character set '.$column->charset;
+        }
+    }
+
+    /**
+     * Get the SQL for a collation column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyCollate(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($column->collation)) {
+            return " collate '{$column->collation}'";
+        }
+    }
+
+    /**
+     * Get the SQL for a nullable column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyNullable(Blueprint $blueprint, Fluent $column)
+    {
+        if (is_null($column->virtualAs) &&
+            is_null($column->virtualAsJson) &&
+            is_null($column->storedAs) &&
+            is_null($column->storedAsJson)) {
+            return $column->nullable ? ' null' : ' not null';
+        }
+
+        if ($column->nullable === false) {
+            return ' not null';
+        }
+    }
+
+    /**
+     * Get the SQL for an invisible column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyInvisible(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($column->invisible)) {
+            return ' invisible';
+        }
+    }
+
+    /**
+     * Get the SQL for a default column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyDefault(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($column->default)) {
+            return ' default '.$this->getDefaultValue($column->default);
+        }
+    }
+
+    /**
+     * Get the SQL for an auto-increment column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyIncrement(Blueprint $blueprint, Fluent $column)
+    {
+        if (in_array($column->type, $this->serials) && $column->autoIncrement) {
+            return ' auto_increment primary key';
+        }
+    }
+
+    /**
+     * Get the SQL for a "first" column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyFirst(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($column->first)) {
+            return ' first';
+        }
+    }
+
+    /**
+     * Get the SQL for an "after" column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyAfter(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($column->after)) {
+            return ' after '.$this->wrap($column->after);
+        }
+    }
+
+    /**
+     * Get the SQL for a "comment" column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyComment(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($column->comment)) {
+            return " comment '".addslashes($column->comment)."'";
+        }
+    }
+
+    /**
+     * Get the SQL for a SRID column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifySrid(Blueprint $blueprint, Fluent $column)
+    {
+        if (is_int($column->srid) && $column->srid > 0) {
+            return ' srid '.$column->srid;
+        }
+    }
+
+    /**
+     * Wrap a single string in keyword identifiers.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapValue($value)
+    {
+        if ($value !== '*') {
+            return '`'.str_replace('`', '``', $value).'`';
+        }
+
+        return $value;
+    }
+
+    /**
+     * Wrap the given JSON selector.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapJsonSelector($value)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($value);
+
+        return 'json_unquote(json_extract('.$field.$path.'))';
+    }
+}
diff --git a/vendor/illuminate/database/Schema/Grammars/PostgresGrammar.php b/vendor/illuminate/database/Schema/Grammars/PostgresGrammar.php
new file mode 100755
index 0000000..ef60d0f
--- /dev/null
+++ b/vendor/illuminate/database/Schema/Grammars/PostgresGrammar.php
@@ -0,0 +1,1141 @@
+wrapValue($name),
+            $this->wrapValue($connection->getConfig('charset')),
+        );
+    }
+
+    /**
+     * Compile a drop database if exists command.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    public function compileDropDatabaseIfExists($name)
+    {
+        return sprintf(
+            'drop database if exists %s',
+            $this->wrapValue($name)
+        );
+    }
+
+    /**
+     * Compile the query to determine if a table exists.
+     *
+     * @return string
+     */
+    public function compileTableExists()
+    {
+        return "select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'";
+    }
+
+    /**
+     * Compile the query to determine the list of columns.
+     *
+     * @return string
+     */
+    public function compileColumnListing()
+    {
+        return 'select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?';
+    }
+
+    /**
+     * Compile a create table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return array
+     */
+    public function compileCreate(Blueprint $blueprint, Fluent $command)
+    {
+        return array_values(array_filter(array_merge([sprintf('%s table %s (%s)',
+            $blueprint->temporary ? 'create temporary' : 'create',
+            $this->wrapTable($blueprint),
+            implode(', ', $this->getColumns($blueprint))
+        )], $this->compileAutoIncrementStartingValues($blueprint))));
+    }
+
+    /**
+     * Compile a column addition command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileAdd(Blueprint $blueprint, Fluent $command)
+    {
+        return array_values(array_filter(array_merge([sprintf('alter table %s %s',
+            $this->wrapTable($blueprint),
+            implode(', ', $this->prefixArray('add column', $this->getColumns($blueprint)))
+        )], $this->compileAutoIncrementStartingValues($blueprint))));
+    }
+
+    /**
+     * Compile the auto-incrementing column starting values.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @return array
+     */
+    public function compileAutoIncrementStartingValues(Blueprint $blueprint)
+    {
+        return collect($blueprint->autoIncrementingStartingValues())->map(function ($value, $column) use ($blueprint) {
+            return 'alter sequence '.$blueprint->getTable().'_'.$column.'_seq restart with '.$value;
+        })->all();
+    }
+
+    /**
+     * Compile a rename column command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return array|string
+     */
+    public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
+    {
+        return $connection->usingNativeSchemaOperations()
+            ? sprintf('alter table %s rename column %s to %s',
+                $this->wrapTable($blueprint),
+                $this->wrap($command->from),
+                $this->wrap($command->to)
+            )
+            : parent::compileRenameColumn($blueprint, $command, $connection);
+    }
+
+    /**
+     * Compile a primary key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compilePrimary(Blueprint $blueprint, Fluent $command)
+    {
+        $columns = $this->columnize($command->columns);
+
+        return 'alter table '.$this->wrapTable($blueprint)." add primary key ({$columns})";
+    }
+
+    /**
+     * Compile a unique key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileUnique(Blueprint $blueprint, Fluent $command)
+    {
+        $sql = sprintf('alter table %s add constraint %s unique (%s)',
+            $this->wrapTable($blueprint),
+            $this->wrap($command->index),
+            $this->columnize($command->columns)
+        );
+
+        if (! is_null($command->deferrable)) {
+            $sql .= $command->deferrable ? ' deferrable' : ' not deferrable';
+        }
+
+        if ($command->deferrable && ! is_null($command->initiallyImmediate)) {
+            $sql .= $command->initiallyImmediate ? ' initially immediate' : ' initially deferred';
+        }
+
+        return $sql;
+    }
+
+    /**
+     * Compile a plain index key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('create index %s on %s%s (%s)',
+            $this->wrap($command->index),
+            $this->wrapTable($blueprint),
+            $command->algorithm ? ' using '.$command->algorithm : '',
+            $this->columnize($command->columns)
+        );
+    }
+
+    /**
+     * Compile a fulltext index key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     *
+     * @throws \RuntimeException
+     */
+    public function compileFulltext(Blueprint $blueprint, Fluent $command)
+    {
+        $language = $command->language ?: 'english';
+
+        $columns = array_map(function ($column) use ($language) {
+            return "to_tsvector({$this->quoteString($language)}, {$this->wrap($column)})";
+        }, $command->columns);
+
+        return sprintf('create index %s on %s using gin ((%s))',
+            $this->wrap($command->index),
+            $this->wrapTable($blueprint),
+            implode(' || ', $columns)
+        );
+    }
+
+    /**
+     * Compile a spatial index key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileSpatialIndex(Blueprint $blueprint, Fluent $command)
+    {
+        $command->algorithm = 'gist';
+
+        return $this->compileIndex($blueprint, $command);
+    }
+
+    /**
+     * Compile a foreign key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileForeign(Blueprint $blueprint, Fluent $command)
+    {
+        $sql = parent::compileForeign($blueprint, $command);
+
+        if (! is_null($command->deferrable)) {
+            $sql .= $command->deferrable ? ' deferrable' : ' not deferrable';
+        }
+
+        if ($command->deferrable && ! is_null($command->initiallyImmediate)) {
+            $sql .= $command->initiallyImmediate ? ' initially immediate' : ' initially deferred';
+        }
+
+        if (! is_null($command->notValid)) {
+            $sql .= ' not valid';
+        }
+
+        return $sql;
+    }
+
+    /**
+     * Compile a drop table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDrop(Blueprint $blueprint, Fluent $command)
+    {
+        return 'drop table '.$this->wrapTable($blueprint);
+    }
+
+    /**
+     * Compile a drop table (if exists) command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropIfExists(Blueprint $blueprint, Fluent $command)
+    {
+        return 'drop table if exists '.$this->wrapTable($blueprint);
+    }
+
+    /**
+     * Compile the SQL needed to drop all tables.
+     *
+     * @param  array  $tables
+     * @return string
+     */
+    public function compileDropAllTables($tables)
+    {
+        return 'drop table '.implode(',', $this->escapeNames($tables)).' cascade';
+    }
+
+    /**
+     * Compile the SQL needed to drop all views.
+     *
+     * @param  array  $views
+     * @return string
+     */
+    public function compileDropAllViews($views)
+    {
+        return 'drop view '.implode(',', $this->escapeNames($views)).' cascade';
+    }
+
+    /**
+     * Compile the SQL needed to drop all types.
+     *
+     * @param  array  $types
+     * @return string
+     */
+    public function compileDropAllTypes($types)
+    {
+        return 'drop type '.implode(',', $this->escapeNames($types)).' cascade';
+    }
+
+    /**
+     * Compile the SQL needed to retrieve all table names.
+     *
+     * @param  string|array  $searchPath
+     * @return string
+     */
+    public function compileGetAllTables($searchPath)
+    {
+        return "select tablename, concat('\"', schemaname, '\".\"', tablename, '\"') as qualifiedname from pg_catalog.pg_tables where schemaname in ('".implode("','", (array) $searchPath)."')";
+    }
+
+    /**
+     * Compile the SQL needed to retrieve all view names.
+     *
+     * @param  string|array  $searchPath
+     * @return string
+     */
+    public function compileGetAllViews($searchPath)
+    {
+        return "select viewname, concat('\"', schemaname, '\".\"', viewname, '\"') as qualifiedname from pg_catalog.pg_views where schemaname in ('".implode("','", (array) $searchPath)."')";
+    }
+
+    /**
+     * Compile the SQL needed to retrieve all type names.
+     *
+     * @return string
+     */
+    public function compileGetAllTypes()
+    {
+        return 'select distinct pg_type.typname from pg_type inner join pg_enum on pg_enum.enumtypid = pg_type.oid';
+    }
+
+    /**
+     * Compile a drop column command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropColumn(Blueprint $blueprint, Fluent $command)
+    {
+        $columns = $this->prefixArray('drop column', $this->wrapArray($command->columns));
+
+        return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns);
+    }
+
+    /**
+     * Compile a drop primary key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropPrimary(Blueprint $blueprint, Fluent $command)
+    {
+        $index = $this->wrap("{$blueprint->getTable()}_pkey");
+
+        return 'alter table '.$this->wrapTable($blueprint)." drop constraint {$index}";
+    }
+
+    /**
+     * Compile a drop unique key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropUnique(Blueprint $blueprint, Fluent $command)
+    {
+        $index = $this->wrap($command->index);
+
+        return "alter table {$this->wrapTable($blueprint)} drop constraint {$index}";
+    }
+
+    /**
+     * Compile a drop index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return "drop index {$this->wrap($command->index)}";
+    }
+
+    /**
+     * Compile a drop fulltext index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropFullText(Blueprint $blueprint, Fluent $command)
+    {
+        return $this->compileDropIndex($blueprint, $command);
+    }
+
+    /**
+     * Compile a drop spatial index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropSpatialIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return $this->compileDropIndex($blueprint, $command);
+    }
+
+    /**
+     * Compile a drop foreign key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropForeign(Blueprint $blueprint, Fluent $command)
+    {
+        $index = $this->wrap($command->index);
+
+        return "alter table {$this->wrapTable($blueprint)} drop constraint {$index}";
+    }
+
+    /**
+     * Compile a rename table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileRename(Blueprint $blueprint, Fluent $command)
+    {
+        $from = $this->wrapTable($blueprint);
+
+        return "alter table {$from} rename to ".$this->wrapTable($command->to);
+    }
+
+    /**
+     * Compile a rename index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileRenameIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('alter index %s rename to %s',
+            $this->wrap($command->from),
+            $this->wrap($command->to)
+        );
+    }
+
+    /**
+     * Compile the command to enable foreign key constraints.
+     *
+     * @return string
+     */
+    public function compileEnableForeignKeyConstraints()
+    {
+        return 'SET CONSTRAINTS ALL IMMEDIATE;';
+    }
+
+    /**
+     * Compile the command to disable foreign key constraints.
+     *
+     * @return string
+     */
+    public function compileDisableForeignKeyConstraints()
+    {
+        return 'SET CONSTRAINTS ALL DEFERRED;';
+    }
+
+    /**
+     * Compile a comment command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileComment(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('comment on column %s.%s is %s',
+            $this->wrapTable($blueprint),
+            $this->wrap($command->column->name),
+            "'".str_replace("'", "''", $command->value)."'"
+        );
+    }
+
+    /**
+     * Compile a table comment command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileTableComment(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('comment on table %s is %s',
+            $this->wrapTable($blueprint),
+            "'".str_replace("'", "''", $command->comment)."'"
+        );
+    }
+
+    /**
+     * Quote-escape the given tables, views, or types.
+     *
+     * @param  array  $names
+     * @return array
+     */
+    public function escapeNames($names)
+    {
+        return array_map(static function ($name) {
+            return '"'.collect(explode('.', $name))
+                ->map(fn ($segment) => trim($segment, '\'"'))
+                ->implode('"."').'"';
+        }, $names);
+    }
+
+    /**
+     * Create the column definition for a char type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeChar(Fluent $column)
+    {
+        if ($column->length) {
+            return "char({$column->length})";
+        }
+
+        return 'char';
+    }
+
+    /**
+     * Create the column definition for a string type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeString(Fluent $column)
+    {
+        if ($column->length) {
+            return "varchar({$column->length})";
+        }
+
+        return 'varchar';
+    }
+
+    /**
+     * Create the column definition for a tiny text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTinyText(Fluent $column)
+    {
+        return 'varchar(255)';
+    }
+
+    /**
+     * Create the column definition for a text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeText(Fluent $column)
+    {
+        return 'text';
+    }
+
+    /**
+     * Create the column definition for a medium text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMediumText(Fluent $column)
+    {
+        return 'text';
+    }
+
+    /**
+     * Create the column definition for a long text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeLongText(Fluent $column)
+    {
+        return 'text';
+    }
+
+    /**
+     * Create the column definition for an integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeInteger(Fluent $column)
+    {
+        return $this->generatableColumn('integer', $column);
+    }
+
+    /**
+     * Create the column definition for a big integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeBigInteger(Fluent $column)
+    {
+        return $this->generatableColumn('bigint', $column);
+    }
+
+    /**
+     * Create the column definition for a medium integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMediumInteger(Fluent $column)
+    {
+        return $this->generatableColumn('integer', $column);
+    }
+
+    /**
+     * Create the column definition for a tiny integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTinyInteger(Fluent $column)
+    {
+        return $this->generatableColumn('smallint', $column);
+    }
+
+    /**
+     * Create the column definition for a small integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeSmallInteger(Fluent $column)
+    {
+        return $this->generatableColumn('smallint', $column);
+    }
+
+    /**
+     * Create the column definition for a generatable column.
+     *
+     * @param  string  $type
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function generatableColumn($type, Fluent $column)
+    {
+        if (! $column->autoIncrement && is_null($column->generatedAs)) {
+            return $type;
+        }
+
+        if ($column->autoIncrement && is_null($column->generatedAs)) {
+            return with([
+                'integer' => 'serial',
+                'bigint' => 'bigserial',
+                'smallint' => 'smallserial',
+            ])[$type];
+        }
+
+        $options = '';
+
+        if (! is_bool($column->generatedAs) && ! empty($column->generatedAs)) {
+            $options = sprintf(' (%s)', $column->generatedAs);
+        }
+
+        return sprintf(
+            '%s generated %s as identity%s',
+            $type,
+            $column->always ? 'always' : 'by default',
+            $options
+        );
+    }
+
+    /**
+     * Create the column definition for a float type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeFloat(Fluent $column)
+    {
+        return $this->typeDouble($column);
+    }
+
+    /**
+     * Create the column definition for a double type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDouble(Fluent $column)
+    {
+        return 'double precision';
+    }
+
+    /**
+     * Create the column definition for a real type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeReal(Fluent $column)
+    {
+        return 'real';
+    }
+
+    /**
+     * Create the column definition for a decimal type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDecimal(Fluent $column)
+    {
+        return "decimal({$column->total}, {$column->places})";
+    }
+
+    /**
+     * Create the column definition for a boolean type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeBoolean(Fluent $column)
+    {
+        return 'boolean';
+    }
+
+    /**
+     * Create the column definition for an enumeration type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeEnum(Fluent $column)
+    {
+        return sprintf(
+            'varchar(255) check ("%s" in (%s))',
+            $column->name,
+            $this->quoteString($column->allowed)
+        );
+    }
+
+    /**
+     * Create the column definition for a json type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeJson(Fluent $column)
+    {
+        return 'json';
+    }
+
+    /**
+     * Create the column definition for a jsonb type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeJsonb(Fluent $column)
+    {
+        return 'jsonb';
+    }
+
+    /**
+     * Create the column definition for a date type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDate(Fluent $column)
+    {
+        return 'date';
+    }
+
+    /**
+     * Create the column definition for a date-time type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDateTime(Fluent $column)
+    {
+        return $this->typeTimestamp($column);
+    }
+
+    /**
+     * Create the column definition for a date-time (with time zone) type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDateTimeTz(Fluent $column)
+    {
+        return $this->typeTimestampTz($column);
+    }
+
+    /**
+     * Create the column definition for a time type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTime(Fluent $column)
+    {
+        return 'time'.(is_null($column->precision) ? '' : "($column->precision)").' without time zone';
+    }
+
+    /**
+     * Create the column definition for a time (with time zone) type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTimeTz(Fluent $column)
+    {
+        return 'time'.(is_null($column->precision) ? '' : "($column->precision)").' with time zone';
+    }
+
+    /**
+     * Create the column definition for a timestamp type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTimestamp(Fluent $column)
+    {
+        $columnType = 'timestamp'.(is_null($column->precision) ? '' : "($column->precision)").' without time zone';
+
+        return $column->useCurrent ? "$columnType default CURRENT_TIMESTAMP" : $columnType;
+    }
+
+    /**
+     * Create the column definition for a timestamp (with time zone) type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTimestampTz(Fluent $column)
+    {
+        $columnType = 'timestamp'.(is_null($column->precision) ? '' : "($column->precision)").' with time zone';
+
+        return $column->useCurrent ? "$columnType default CURRENT_TIMESTAMP" : $columnType;
+    }
+
+    /**
+     * Create the column definition for a year type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeYear(Fluent $column)
+    {
+        return $this->typeInteger($column);
+    }
+
+    /**
+     * Create the column definition for a binary type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeBinary(Fluent $column)
+    {
+        return 'bytea';
+    }
+
+    /**
+     * Create the column definition for a uuid type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeUuid(Fluent $column)
+    {
+        return 'uuid';
+    }
+
+    /**
+     * Create the column definition for an IP address type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeIpAddress(Fluent $column)
+    {
+        return 'inet';
+    }
+
+    /**
+     * Create the column definition for a MAC address type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMacAddress(Fluent $column)
+    {
+        return 'macaddr';
+    }
+
+    /**
+     * Create the column definition for a spatial Geometry type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeGeometry(Fluent $column)
+    {
+        return $this->formatPostGisType('geometry', $column);
+    }
+
+    /**
+     * Create the column definition for a spatial Point type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typePoint(Fluent $column)
+    {
+        return $this->formatPostGisType('point', $column);
+    }
+
+    /**
+     * Create the column definition for a spatial LineString type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeLineString(Fluent $column)
+    {
+        return $this->formatPostGisType('linestring', $column);
+    }
+
+    /**
+     * Create the column definition for a spatial Polygon type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typePolygon(Fluent $column)
+    {
+        return $this->formatPostGisType('polygon', $column);
+    }
+
+    /**
+     * Create the column definition for a spatial GeometryCollection type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeGeometryCollection(Fluent $column)
+    {
+        return $this->formatPostGisType('geometrycollection', $column);
+    }
+
+    /**
+     * Create the column definition for a spatial MultiPoint type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMultiPoint(Fluent $column)
+    {
+        return $this->formatPostGisType('multipoint', $column);
+    }
+
+    /**
+     * Create the column definition for a spatial MultiLineString type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeMultiLineString(Fluent $column)
+    {
+        return $this->formatPostGisType('multilinestring', $column);
+    }
+
+    /**
+     * Create the column definition for a spatial MultiPolygon type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMultiPolygon(Fluent $column)
+    {
+        return $this->formatPostGisType('multipolygon', $column);
+    }
+
+    /**
+     * Create the column definition for a spatial MultiPolygonZ type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMultiPolygonZ(Fluent $column)
+    {
+        return $this->formatPostGisType('multipolygonz', $column);
+    }
+
+    /**
+     * Format the column definition for a PostGIS spatial type.
+     *
+     * @param  string  $type
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    private function formatPostGisType($type, Fluent $column)
+    {
+        if ($column->isGeometry === null) {
+            return sprintf('geography(%s, %s)', $type, $column->projection ?? '4326');
+        }
+
+        if ($column->projection !== null) {
+            return sprintf('geometry(%s, %s)', $type, $column->projection);
+        }
+
+        return "geometry({$type})";
+    }
+
+    /**
+     * Get the SQL for a collation column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyCollate(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($column->collation)) {
+            return ' collate '.$this->wrapValue($column->collation);
+        }
+    }
+
+    /**
+     * Get the SQL for a nullable column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyNullable(Blueprint $blueprint, Fluent $column)
+    {
+        return $column->nullable ? ' null' : ' not null';
+    }
+
+    /**
+     * Get the SQL for a default column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyDefault(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($column->default)) {
+            return ' default '.$this->getDefaultValue($column->default);
+        }
+    }
+
+    /**
+     * Get the SQL for an auto-increment column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyIncrement(Blueprint $blueprint, Fluent $column)
+    {
+        if ((in_array($column->type, $this->serials) || ($column->generatedAs !== null)) && $column->autoIncrement) {
+            return ' primary key';
+        }
+    }
+
+    /**
+     * Get the SQL for a generated virtual column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyVirtualAs(Blueprint $blueprint, Fluent $column)
+    {
+        if ($column->virtualAs !== null) {
+            return " generated always as ({$column->virtualAs})";
+        }
+    }
+
+    /**
+     * Get the SQL for a generated stored column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyStoredAs(Blueprint $blueprint, Fluent $column)
+    {
+        if ($column->storedAs !== null) {
+            return " generated always as ({$column->storedAs}) stored";
+        }
+    }
+}
diff --git a/vendor/illuminate/database/Schema/Grammars/RenameColumn.php b/vendor/illuminate/database/Schema/Grammars/RenameColumn.php
new file mode 100644
index 0000000..0db0c50
--- /dev/null
+++ b/vendor/illuminate/database/Schema/Grammars/RenameColumn.php
@@ -0,0 +1,84 @@
+getDoctrineSchemaManager();
+        $databasePlatform = $schema->getDatabasePlatform();
+        $databasePlatform->registerDoctrineTypeMapping('enum', 'string');
+
+        $column = $connection->getDoctrineColumn(
+            $grammar->getTablePrefix().$blueprint->getTable(), $command->from
+        );
+
+        return (array) $databasePlatform->getAlterTableSQL(static::getRenamedDiff(
+            $grammar, $blueprint, $command, $column, $schema
+        ));
+    }
+
+    /**
+     * Get a new column instance with the new column name.
+     *
+     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @param  \Doctrine\DBAL\Schema\Column  $column
+     * @param  \Doctrine\DBAL\Schema\AbstractSchemaManager  $schema
+     * @return \Doctrine\DBAL\Schema\TableDiff
+     */
+    protected static function getRenamedDiff(Grammar $grammar, Blueprint $blueprint, Fluent $command, Column $column, SchemaManager $schema)
+    {
+        return static::setRenamedColumns(
+            $grammar->getDoctrineTableDiff($blueprint, $schema), $command, $column
+        );
+    }
+
+    /**
+     * Set the renamed columns on the table diff.
+     *
+     * @param  \Doctrine\DBAL\Schema\TableDiff  $tableDiff
+     * @param  \Illuminate\Support\Fluent  $command
+     * @param  \Doctrine\DBAL\Schema\Column  $column
+     * @return \Doctrine\DBAL\Schema\TableDiff
+     */
+    protected static function setRenamedColumns(TableDiff $tableDiff, Fluent $command, Column $column)
+    {
+        $tableDiff->renamedColumns = [
+            $command->from => new Column($command->to, $column->getType(), self::getWritableColumnOptions($column)),
+        ];
+
+        return $tableDiff;
+    }
+
+    /**
+     * Get the writable column options.
+     *
+     * @param  \Doctrine\DBAL\Schema\Column  $column
+     * @return array
+     */
+    private static function getWritableColumnOptions(Column $column)
+    {
+        return array_filter($column->toArray(), function (string $name) use ($column) {
+            return method_exists($column, 'set'.$name);
+        }, ARRAY_FILTER_USE_KEY);
+    }
+}
diff --git a/vendor/illuminate/database/Schema/Grammars/SQLiteGrammar.php b/vendor/illuminate/database/Schema/Grammars/SQLiteGrammar.php
new file mode 100755
index 0000000..c9d1c55
--- /dev/null
+++ b/vendor/illuminate/database/Schema/Grammars/SQLiteGrammar.php
@@ -0,0 +1,1005 @@
+wrap(str_replace('.', '__', $table)).')';
+    }
+
+    /**
+     * Compile a create table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileCreate(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('%s table %s (%s%s%s)',
+            $blueprint->temporary ? 'create temporary' : 'create',
+            $this->wrapTable($blueprint),
+            implode(', ', $this->getColumns($blueprint)),
+            (string) $this->addForeignKeys($blueprint),
+            (string) $this->addPrimaryKeys($blueprint)
+        );
+    }
+
+    /**
+     * Get the foreign key syntax for a table creation statement.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @return string|null
+     */
+    protected function addForeignKeys(Blueprint $blueprint)
+    {
+        $foreigns = $this->getCommandsByName($blueprint, 'foreign');
+
+        return collect($foreigns)->reduce(function ($sql, $foreign) {
+            // Once we have all the foreign key commands for the table creation statement
+            // we'll loop through each of them and add them to the create table SQL we
+            // are building, since SQLite needs foreign keys on the tables creation.
+            $sql .= $this->getForeignKey($foreign);
+
+            if (! is_null($foreign->onDelete)) {
+                $sql .= " on delete {$foreign->onDelete}";
+            }
+
+            // If this foreign key specifies the action to be taken on update we will add
+            // that to the statement here. We'll append it to this SQL and then return
+            // the SQL so we can keep adding any other foreign constraints onto this.
+            if (! is_null($foreign->onUpdate)) {
+                $sql .= " on update {$foreign->onUpdate}";
+            }
+
+            return $sql;
+        }, '');
+    }
+
+    /**
+     * Get the SQL for the foreign key.
+     *
+     * @param  \Illuminate\Support\Fluent  $foreign
+     * @return string
+     */
+    protected function getForeignKey($foreign)
+    {
+        // We need to columnize the columns that the foreign key is being defined for
+        // so that it is a properly formatted list. Once we have done this, we can
+        // return the foreign key SQL declaration to the calling method for use.
+        return sprintf(', foreign key(%s) references %s(%s)',
+            $this->columnize($foreign->columns),
+            $this->wrapTable($foreign->on),
+            $this->columnize((array) $foreign->references)
+        );
+    }
+
+    /**
+     * Get the primary key syntax for a table creation statement.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @return string|null
+     */
+    protected function addPrimaryKeys(Blueprint $blueprint)
+    {
+        if (! is_null($primary = $this->getCommandByName($blueprint, 'primary'))) {
+            return ", primary key ({$this->columnize($primary->columns)})";
+        }
+    }
+
+    /**
+     * Compile alter table commands for adding columns.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return array
+     */
+    public function compileAdd(Blueprint $blueprint, Fluent $command)
+    {
+        $columns = $this->prefixArray('add column', $this->getColumns($blueprint));
+
+        return collect($columns)->reject(function ($column) {
+            return preg_match('/as \(.*\) stored/', $column) > 0;
+        })->map(function ($column) use ($blueprint) {
+            return 'alter table '.$this->wrapTable($blueprint).' '.$column;
+        })->all();
+    }
+
+    /**
+     * Compile a rename column command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return array|string
+     */
+    public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
+    {
+        return $connection->usingNativeSchemaOperations()
+            ? sprintf('alter table %s rename column %s to %s',
+                $this->wrapTable($blueprint),
+                $this->wrap($command->from),
+                $this->wrap($command->to)
+            )
+            : parent::compileRenameColumn($blueprint, $command, $connection);
+    }
+
+    /**
+     * Compile a unique key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileUnique(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('create unique index %s on %s (%s)',
+            $this->wrap($command->index),
+            $this->wrapTable($blueprint),
+            $this->columnize($command->columns)
+        );
+    }
+
+    /**
+     * Compile a plain index key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('create index %s on %s (%s)',
+            $this->wrap($command->index),
+            $this->wrapTable($blueprint),
+            $this->columnize($command->columns)
+        );
+    }
+
+    /**
+     * Compile a spatial index key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return void
+     *
+     * @throws \RuntimeException
+     */
+    public function compileSpatialIndex(Blueprint $blueprint, Fluent $command)
+    {
+        throw new RuntimeException('The database driver in use does not support spatial indexes.');
+    }
+
+    /**
+     * Compile a foreign key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileForeign(Blueprint $blueprint, Fluent $command)
+    {
+        // Handled on table creation...
+    }
+
+    /**
+     * Compile a drop table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDrop(Blueprint $blueprint, Fluent $command)
+    {
+        return 'drop table '.$this->wrapTable($blueprint);
+    }
+
+    /**
+     * Compile a drop table (if exists) command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropIfExists(Blueprint $blueprint, Fluent $command)
+    {
+        return 'drop table if exists '.$this->wrapTable($blueprint);
+    }
+
+    /**
+     * Compile the SQL needed to drop all tables.
+     *
+     * @return string
+     */
+    public function compileDropAllTables()
+    {
+        return "delete from sqlite_master where type in ('table', 'index', 'trigger')";
+    }
+
+    /**
+     * Compile the SQL needed to drop all views.
+     *
+     * @return string
+     */
+    public function compileDropAllViews()
+    {
+        return "delete from sqlite_master where type in ('view')";
+    }
+
+    /**
+     * Compile the SQL needed to retrieve all table names.
+     *
+     * @return string
+     */
+    public function compileGetAllTables()
+    {
+        return 'select type, name from sqlite_master where type = \'table\' and name not like \'sqlite_%\'';
+    }
+
+    /**
+     * Compile the SQL needed to retrieve all view names.
+     *
+     * @return string
+     */
+    public function compileGetAllViews()
+    {
+        return 'select type, name from sqlite_master where type = \'view\'';
+    }
+
+    /**
+     * Compile the SQL needed to rebuild the database.
+     *
+     * @return string
+     */
+    public function compileRebuild()
+    {
+        return 'vacuum';
+    }
+
+    /**
+     * Compile a drop column command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return array
+     */
+    public function compileDropColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
+    {
+        if ($connection->usingNativeSchemaOperations()) {
+            $table = $this->wrapTable($blueprint);
+
+            $columns = $this->prefixArray('drop column', $this->wrapArray($command->columns));
+
+            return collect($columns)->map(fn ($column) => 'alter table '.$table.' '.$column
+            )->all();
+        } else {
+            $tableDiff = $this->getDoctrineTableDiff(
+                $blueprint, $schema = $connection->getDoctrineSchemaManager()
+            );
+
+            foreach ($command->columns as $name) {
+                $tableDiff->removedColumns[$name] = $connection->getDoctrineColumn(
+                    $this->getTablePrefix().$blueprint->getTable(), $name
+                );
+            }
+
+            return (array) $schema->getDatabasePlatform()->getAlterTableSQL($tableDiff);
+        }
+    }
+
+    /**
+     * Compile a drop unique key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropUnique(Blueprint $blueprint, Fluent $command)
+    {
+        $index = $this->wrap($command->index);
+
+        return "drop index {$index}";
+    }
+
+    /**
+     * Compile a drop index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropIndex(Blueprint $blueprint, Fluent $command)
+    {
+        $index = $this->wrap($command->index);
+
+        return "drop index {$index}";
+    }
+
+    /**
+     * Compile a drop spatial index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return void
+     *
+     * @throws \RuntimeException
+     */
+    public function compileDropSpatialIndex(Blueprint $blueprint, Fluent $command)
+    {
+        throw new RuntimeException('The database driver in use does not support spatial indexes.');
+    }
+
+    /**
+     * Compile a rename table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileRename(Blueprint $blueprint, Fluent $command)
+    {
+        $from = $this->wrapTable($blueprint);
+
+        return "alter table {$from} rename to ".$this->wrapTable($command->to);
+    }
+
+    /**
+     * Compile a rename index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return array
+     *
+     * @throws \RuntimeException
+     */
+    public function compileRenameIndex(Blueprint $blueprint, Fluent $command, Connection $connection)
+    {
+        $schemaManager = $connection->getDoctrineSchemaManager();
+
+        $indexes = $schemaManager->listTableIndexes($this->getTablePrefix().$blueprint->getTable());
+
+        $index = Arr::get($indexes, $command->from);
+
+        if (! $index) {
+            throw new RuntimeException("Index [{$command->from}] does not exist.");
+        }
+
+        $newIndex = new Index(
+            $command->to, $index->getColumns(), $index->isUnique(),
+            $index->isPrimary(), $index->getFlags(), $index->getOptions()
+        );
+
+        $platform = $schemaManager->getDatabasePlatform();
+
+        return [
+            $platform->getDropIndexSQL($command->from, $this->getTablePrefix().$blueprint->getTable()),
+            $platform->getCreateIndexSQL($newIndex, $this->getTablePrefix().$blueprint->getTable()),
+        ];
+    }
+
+    /**
+     * Compile the command to enable foreign key constraints.
+     *
+     * @return string
+     */
+    public function compileEnableForeignKeyConstraints()
+    {
+        return 'PRAGMA foreign_keys = ON;';
+    }
+
+    /**
+     * Compile the command to disable foreign key constraints.
+     *
+     * @return string
+     */
+    public function compileDisableForeignKeyConstraints()
+    {
+        return 'PRAGMA foreign_keys = OFF;';
+    }
+
+    /**
+     * Compile the SQL needed to enable a writable schema.
+     *
+     * @return string
+     */
+    public function compileEnableWriteableSchema()
+    {
+        return 'PRAGMA writable_schema = 1;';
+    }
+
+    /**
+     * Compile the SQL needed to disable a writable schema.
+     *
+     * @return string
+     */
+    public function compileDisableWriteableSchema()
+    {
+        return 'PRAGMA writable_schema = 0;';
+    }
+
+    /**
+     * Create the column definition for a char type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeChar(Fluent $column)
+    {
+        return 'varchar';
+    }
+
+    /**
+     * Create the column definition for a string type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeString(Fluent $column)
+    {
+        return 'varchar';
+    }
+
+    /**
+     * Create the column definition for a tiny text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTinyText(Fluent $column)
+    {
+        return 'text';
+    }
+
+    /**
+     * Create the column definition for a text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeText(Fluent $column)
+    {
+        return 'text';
+    }
+
+    /**
+     * Create the column definition for a medium text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMediumText(Fluent $column)
+    {
+        return 'text';
+    }
+
+    /**
+     * Create the column definition for a long text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeLongText(Fluent $column)
+    {
+        return 'text';
+    }
+
+    /**
+     * Create the column definition for an integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeInteger(Fluent $column)
+    {
+        return 'integer';
+    }
+
+    /**
+     * Create the column definition for a big integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeBigInteger(Fluent $column)
+    {
+        return 'integer';
+    }
+
+    /**
+     * Create the column definition for a medium integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMediumInteger(Fluent $column)
+    {
+        return 'integer';
+    }
+
+    /**
+     * Create the column definition for a tiny integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTinyInteger(Fluent $column)
+    {
+        return 'integer';
+    }
+
+    /**
+     * Create the column definition for a small integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeSmallInteger(Fluent $column)
+    {
+        return 'integer';
+    }
+
+    /**
+     * Create the column definition for a float type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeFloat(Fluent $column)
+    {
+        return 'float';
+    }
+
+    /**
+     * Create the column definition for a double type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDouble(Fluent $column)
+    {
+        return 'float';
+    }
+
+    /**
+     * Create the column definition for a decimal type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDecimal(Fluent $column)
+    {
+        return 'numeric';
+    }
+
+    /**
+     * Create the column definition for a boolean type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeBoolean(Fluent $column)
+    {
+        return 'tinyint(1)';
+    }
+
+    /**
+     * Create the column definition for an enumeration type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeEnum(Fluent $column)
+    {
+        return sprintf(
+            'varchar check ("%s" in (%s))',
+            $column->name,
+            $this->quoteString($column->allowed)
+        );
+    }
+
+    /**
+     * Create the column definition for a json type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeJson(Fluent $column)
+    {
+        return 'text';
+    }
+
+    /**
+     * Create the column definition for a jsonb type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeJsonb(Fluent $column)
+    {
+        return 'text';
+    }
+
+    /**
+     * Create the column definition for a date type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDate(Fluent $column)
+    {
+        return 'date';
+    }
+
+    /**
+     * Create the column definition for a date-time type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDateTime(Fluent $column)
+    {
+        return $this->typeTimestamp($column);
+    }
+
+    /**
+     * Create the column definition for a date-time (with time zone) type.
+     *
+     * Note: "SQLite does not have a storage class set aside for storing dates and/or times."
+     *
+     * @link https://www.sqlite.org/datatype3.html
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDateTimeTz(Fluent $column)
+    {
+        return $this->typeDateTime($column);
+    }
+
+    /**
+     * Create the column definition for a time type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTime(Fluent $column)
+    {
+        return 'time';
+    }
+
+    /**
+     * Create the column definition for a time (with time zone) type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTimeTz(Fluent $column)
+    {
+        return $this->typeTime($column);
+    }
+
+    /**
+     * Create the column definition for a timestamp type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTimestamp(Fluent $column)
+    {
+        return $column->useCurrent ? 'datetime default CURRENT_TIMESTAMP' : 'datetime';
+    }
+
+    /**
+     * Create the column definition for a timestamp (with time zone) type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTimestampTz(Fluent $column)
+    {
+        return $this->typeTimestamp($column);
+    }
+
+    /**
+     * Create the column definition for a year type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeYear(Fluent $column)
+    {
+        return $this->typeInteger($column);
+    }
+
+    /**
+     * Create the column definition for a binary type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeBinary(Fluent $column)
+    {
+        return 'blob';
+    }
+
+    /**
+     * Create the column definition for a uuid type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeUuid(Fluent $column)
+    {
+        return 'varchar';
+    }
+
+    /**
+     * Create the column definition for an IP address type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeIpAddress(Fluent $column)
+    {
+        return 'varchar';
+    }
+
+    /**
+     * Create the column definition for a MAC address type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMacAddress(Fluent $column)
+    {
+        return 'varchar';
+    }
+
+    /**
+     * Create the column definition for a spatial Geometry type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeGeometry(Fluent $column)
+    {
+        return 'geometry';
+    }
+
+    /**
+     * Create the column definition for a spatial Point type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typePoint(Fluent $column)
+    {
+        return 'point';
+    }
+
+    /**
+     * Create the column definition for a spatial LineString type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeLineString(Fluent $column)
+    {
+        return 'linestring';
+    }
+
+    /**
+     * Create the column definition for a spatial Polygon type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typePolygon(Fluent $column)
+    {
+        return 'polygon';
+    }
+
+    /**
+     * Create the column definition for a spatial GeometryCollection type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeGeometryCollection(Fluent $column)
+    {
+        return 'geometrycollection';
+    }
+
+    /**
+     * Create the column definition for a spatial MultiPoint type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeMultiPoint(Fluent $column)
+    {
+        return 'multipoint';
+    }
+
+    /**
+     * Create the column definition for a spatial MultiLineString type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeMultiLineString(Fluent $column)
+    {
+        return 'multilinestring';
+    }
+
+    /**
+     * Create the column definition for a spatial MultiPolygon type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeMultiPolygon(Fluent $column)
+    {
+        return 'multipolygon';
+    }
+
+    /**
+     * Create the column definition for a generated, computed column type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return void
+     *
+     * @throws \RuntimeException
+     */
+    protected function typeComputed(Fluent $column)
+    {
+        throw new RuntimeException('This database driver requires a type, see the virtualAs / storedAs modifiers.');
+    }
+
+    /**
+     * Get the SQL for a generated virtual column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyVirtualAs(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($virtualAs = $column->virtualAsJson)) {
+            if ($this->isJsonSelector($virtualAs)) {
+                $virtualAs = $this->wrapJsonSelector($virtualAs);
+            }
+
+            return " as ({$virtualAs})";
+        }
+
+        if (! is_null($virtualAs = $column->virtualAs)) {
+            return " as ({$virtualAs})";
+        }
+    }
+
+    /**
+     * Get the SQL for a generated stored column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyStoredAs(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($storedAs = $column->storedAsJson)) {
+            if ($this->isJsonSelector($storedAs)) {
+                $storedAs = $this->wrapJsonSelector($storedAs);
+            }
+
+            return " as ({$storedAs}) stored";
+        }
+
+        if (! is_null($storedAs = $column->storedAs)) {
+            return " as ({$column->storedAs}) stored";
+        }
+    }
+
+    /**
+     * Get the SQL for a nullable column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyNullable(Blueprint $blueprint, Fluent $column)
+    {
+        if (is_null($column->virtualAs) &&
+            is_null($column->virtualAsJson) &&
+            is_null($column->storedAs) &&
+            is_null($column->storedAsJson)) {
+            return $column->nullable ? '' : ' not null';
+        }
+
+        if ($column->nullable === false) {
+            return ' not null';
+        }
+    }
+
+    /**
+     * Get the SQL for a default column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyDefault(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($column->default) && is_null($column->virtualAs) && is_null($column->virtualAsJson) && is_null($column->storedAs)) {
+            return ' default '.$this->getDefaultValue($column->default);
+        }
+    }
+
+    /**
+     * Get the SQL for an auto-increment column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyIncrement(Blueprint $blueprint, Fluent $column)
+    {
+        if (in_array($column->type, $this->serials) && $column->autoIncrement) {
+            return ' primary key autoincrement';
+        }
+    }
+
+    /**
+     * Wrap the given JSON selector.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    protected function wrapJsonSelector($value)
+    {
+        [$field, $path] = $this->wrapJsonFieldAndPath($value);
+
+        return 'json_extract('.$field.$path.')';
+    }
+}
diff --git a/vendor/illuminate/database/Schema/Grammars/SqlServerGrammar.php b/vendor/illuminate/database/Schema/Grammars/SqlServerGrammar.php
new file mode 100755
index 0000000..4d7271c
--- /dev/null
+++ b/vendor/illuminate/database/Schema/Grammars/SqlServerGrammar.php
@@ -0,0 +1,973 @@
+wrapValue($name),
+        );
+    }
+
+    /**
+     * Compile a drop database if exists command.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    public function compileDropDatabaseIfExists($name)
+    {
+        return sprintf(
+            'drop database if exists %s',
+            $this->wrapValue($name)
+        );
+    }
+
+    /**
+     * Compile the query to determine if a table exists.
+     *
+     * @return string
+     */
+    public function compileTableExists()
+    {
+        return "select * from sys.sysobjects where id = object_id(?) and xtype in ('U', 'V')";
+    }
+
+    /**
+     * Compile the query to determine the list of columns.
+     *
+     * @param  string  $table
+     * @return string
+     */
+    public function compileColumnListing($table)
+    {
+        return "select name from sys.columns where object_id = object_id('$table')";
+    }
+
+    /**
+     * Compile a create table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileCreate(Blueprint $blueprint, Fluent $command)
+    {
+        $columns = implode(', ', $this->getColumns($blueprint));
+
+        return 'create table '.$this->wrapTable($blueprint)." ($columns)";
+    }
+
+    /**
+     * Compile a column addition table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileAdd(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('alter table %s add %s',
+            $this->wrapTable($blueprint),
+            implode(', ', $this->getColumns($blueprint))
+        );
+    }
+
+    /**
+     * Compile a rename column command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @param  \Illuminate\Database\Connection  $connection
+     * @return array|string
+     */
+    public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
+    {
+        return $connection->usingNativeSchemaOperations()
+            ? sprintf("sp_rename '%s', %s, 'COLUMN'",
+                $this->wrap($blueprint->getTable().'.'.$command->from),
+                $this->wrap($command->to)
+            )
+            : parent::compileRenameColumn($blueprint, $command, $connection);
+    }
+
+    /**
+     * Compile a primary key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compilePrimary(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('alter table %s add constraint %s primary key (%s)',
+            $this->wrapTable($blueprint),
+            $this->wrap($command->index),
+            $this->columnize($command->columns)
+        );
+    }
+
+    /**
+     * Compile a unique key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileUnique(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('create unique index %s on %s (%s)',
+            $this->wrap($command->index),
+            $this->wrapTable($blueprint),
+            $this->columnize($command->columns)
+        );
+    }
+
+    /**
+     * Compile a plain index key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('create index %s on %s (%s)',
+            $this->wrap($command->index),
+            $this->wrapTable($blueprint),
+            $this->columnize($command->columns)
+        );
+    }
+
+    /**
+     * Compile a spatial index key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileSpatialIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('create spatial index %s on %s (%s)',
+            $this->wrap($command->index),
+            $this->wrapTable($blueprint),
+            $this->columnize($command->columns)
+        );
+    }
+
+    /**
+     * Compile a drop table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDrop(Blueprint $blueprint, Fluent $command)
+    {
+        return 'drop table '.$this->wrapTable($blueprint);
+    }
+
+    /**
+     * Compile a drop table (if exists) command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropIfExists(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf('if exists (select * from sys.sysobjects where id = object_id(%s, \'U\')) drop table %s',
+            "'".str_replace("'", "''", $this->getTablePrefix().$blueprint->getTable())."'",
+            $this->wrapTable($blueprint)
+        );
+    }
+
+    /**
+     * Compile the SQL needed to drop all tables.
+     *
+     * @return string
+     */
+    public function compileDropAllTables()
+    {
+        return "EXEC sp_msforeachtable 'DROP TABLE ?'";
+    }
+
+    /**
+     * Compile a drop column command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropColumn(Blueprint $blueprint, Fluent $command)
+    {
+        $columns = $this->wrapArray($command->columns);
+
+        $dropExistingConstraintsSql = $this->compileDropDefaultConstraint($blueprint, $command).';';
+
+        return $dropExistingConstraintsSql.'alter table '.$this->wrapTable($blueprint).' drop column '.implode(', ', $columns);
+    }
+
+    /**
+     * Compile a drop default constraint command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropDefaultConstraint(Blueprint $blueprint, Fluent $command)
+    {
+        $columns = "'".implode("','", $command->columns)."'";
+
+        $tableName = $this->getTablePrefix().$blueprint->getTable();
+
+        $sql = "DECLARE @sql NVARCHAR(MAX) = '';";
+        $sql .= "SELECT @sql += 'ALTER TABLE [dbo].[{$tableName}] DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' ";
+        $sql .= 'FROM sys.columns ';
+        $sql .= "WHERE [object_id] = OBJECT_ID('[dbo].[{$tableName}]') AND [name] in ({$columns}) AND [default_object_id] <> 0;";
+        $sql .= 'EXEC(@sql)';
+
+        return $sql;
+    }
+
+    /**
+     * Compile a drop primary key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropPrimary(Blueprint $blueprint, Fluent $command)
+    {
+        $index = $this->wrap($command->index);
+
+        return "alter table {$this->wrapTable($blueprint)} drop constraint {$index}";
+    }
+
+    /**
+     * Compile a drop unique key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropUnique(Blueprint $blueprint, Fluent $command)
+    {
+        $index = $this->wrap($command->index);
+
+        return "drop index {$index} on {$this->wrapTable($blueprint)}";
+    }
+
+    /**
+     * Compile a drop index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropIndex(Blueprint $blueprint, Fluent $command)
+    {
+        $index = $this->wrap($command->index);
+
+        return "drop index {$index} on {$this->wrapTable($blueprint)}";
+    }
+
+    /**
+     * Compile a drop spatial index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropSpatialIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return $this->compileDropIndex($blueprint, $command);
+    }
+
+    /**
+     * Compile a drop foreign key command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileDropForeign(Blueprint $blueprint, Fluent $command)
+    {
+        $index = $this->wrap($command->index);
+
+        return "alter table {$this->wrapTable($blueprint)} drop constraint {$index}";
+    }
+
+    /**
+     * Compile a rename table command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileRename(Blueprint $blueprint, Fluent $command)
+    {
+        $from = $this->wrapTable($blueprint);
+
+        return "sp_rename {$from}, ".$this->wrapTable($command->to);
+    }
+
+    /**
+     * Compile a rename index command.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $command
+     * @return string
+     */
+    public function compileRenameIndex(Blueprint $blueprint, Fluent $command)
+    {
+        return sprintf("sp_rename N'%s', %s, N'INDEX'",
+            $this->wrap($blueprint->getTable().'.'.$command->from),
+            $this->wrap($command->to)
+        );
+    }
+
+    /**
+     * Compile the command to enable foreign key constraints.
+     *
+     * @return string
+     */
+    public function compileEnableForeignKeyConstraints()
+    {
+        return 'EXEC sp_msforeachtable @command1="print \'?\'", @command2="ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all";';
+    }
+
+    /**
+     * Compile the command to disable foreign key constraints.
+     *
+     * @return string
+     */
+    public function compileDisableForeignKeyConstraints()
+    {
+        return 'EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all";';
+    }
+
+    /**
+     * Compile the command to drop all foreign keys.
+     *
+     * @return string
+     */
+    public function compileDropAllForeignKeys()
+    {
+        return "DECLARE @sql NVARCHAR(MAX) = N'';
+            SELECT @sql += 'ALTER TABLE '
+                + QUOTENAME(OBJECT_SCHEMA_NAME(parent_object_id)) + '.' + + QUOTENAME(OBJECT_NAME(parent_object_id))
+                + ' DROP CONSTRAINT ' + QUOTENAME(name) + ';'
+            FROM sys.foreign_keys;
+
+            EXEC sp_executesql @sql;";
+    }
+
+    /**
+     * Compile the command to drop all views.
+     *
+     * @return string
+     */
+    public function compileDropAllViews()
+    {
+        return "DECLARE @sql NVARCHAR(MAX) = N'';
+            SELECT @sql += 'DROP VIEW ' + QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + '.' + QUOTENAME(name) + ';'
+            FROM sys.views;
+
+            EXEC sp_executesql @sql;";
+    }
+
+    /**
+     * Compile the SQL needed to retrieve all table names.
+     *
+     * @return string
+     */
+    public function compileGetAllTables()
+    {
+        return "select name, type from sys.tables where type = 'U'";
+    }
+
+    /**
+     * Compile the SQL needed to retrieve all view names.
+     *
+     * @return string
+     */
+    public function compileGetAllViews()
+    {
+        return "select name, type from sys.objects where type = 'V'";
+    }
+
+    /**
+     * Create the column definition for a char type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeChar(Fluent $column)
+    {
+        return "nchar({$column->length})";
+    }
+
+    /**
+     * Create the column definition for a string type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeString(Fluent $column)
+    {
+        return "nvarchar({$column->length})";
+    }
+
+    /**
+     * Create the column definition for a tiny text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTinyText(Fluent $column)
+    {
+        return 'nvarchar(255)';
+    }
+
+    /**
+     * Create the column definition for a text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeText(Fluent $column)
+    {
+        return 'nvarchar(max)';
+    }
+
+    /**
+     * Create the column definition for a medium text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMediumText(Fluent $column)
+    {
+        return 'nvarchar(max)';
+    }
+
+    /**
+     * Create the column definition for a long text type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeLongText(Fluent $column)
+    {
+        return 'nvarchar(max)';
+    }
+
+    /**
+     * Create the column definition for an integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeInteger(Fluent $column)
+    {
+        return 'int';
+    }
+
+    /**
+     * Create the column definition for a big integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeBigInteger(Fluent $column)
+    {
+        return 'bigint';
+    }
+
+    /**
+     * Create the column definition for a medium integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMediumInteger(Fluent $column)
+    {
+        return 'int';
+    }
+
+    /**
+     * Create the column definition for a tiny integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTinyInteger(Fluent $column)
+    {
+        return 'tinyint';
+    }
+
+    /**
+     * Create the column definition for a small integer type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeSmallInteger(Fluent $column)
+    {
+        return 'smallint';
+    }
+
+    /**
+     * Create the column definition for a float type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeFloat(Fluent $column)
+    {
+        return 'float';
+    }
+
+    /**
+     * Create the column definition for a double type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDouble(Fluent $column)
+    {
+        return 'float';
+    }
+
+    /**
+     * Create the column definition for a decimal type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDecimal(Fluent $column)
+    {
+        return "decimal({$column->total}, {$column->places})";
+    }
+
+    /**
+     * Create the column definition for a boolean type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeBoolean(Fluent $column)
+    {
+        return 'bit';
+    }
+
+    /**
+     * Create the column definition for an enumeration type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeEnum(Fluent $column)
+    {
+        return sprintf(
+            'nvarchar(255) check ("%s" in (%s))',
+            $column->name,
+            $this->quoteString($column->allowed)
+        );
+    }
+
+    /**
+     * Create the column definition for a json type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeJson(Fluent $column)
+    {
+        return 'nvarchar(max)';
+    }
+
+    /**
+     * Create the column definition for a jsonb type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeJsonb(Fluent $column)
+    {
+        return 'nvarchar(max)';
+    }
+
+    /**
+     * Create the column definition for a date type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDate(Fluent $column)
+    {
+        return 'date';
+    }
+
+    /**
+     * Create the column definition for a date-time type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDateTime(Fluent $column)
+    {
+        return $this->typeTimestamp($column);
+    }
+
+    /**
+     * Create the column definition for a date-time (with time zone) type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeDateTimeTz(Fluent $column)
+    {
+        return $this->typeTimestampTz($column);
+    }
+
+    /**
+     * Create the column definition for a time type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTime(Fluent $column)
+    {
+        return $column->precision ? "time($column->precision)" : 'time';
+    }
+
+    /**
+     * Create the column definition for a time (with time zone) type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTimeTz(Fluent $column)
+    {
+        return $this->typeTime($column);
+    }
+
+    /**
+     * Create the column definition for a timestamp type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTimestamp(Fluent $column)
+    {
+        $columnType = $column->precision ? "datetime2($column->precision)" : 'datetime';
+
+        return $column->useCurrent ? "$columnType default CURRENT_TIMESTAMP" : $columnType;
+    }
+
+    /**
+     * Create the column definition for a timestamp (with time zone) type.
+     *
+     * @link https://docs.microsoft.com/en-us/sql/t-sql/data-types/datetimeoffset-transact-sql?view=sql-server-ver15
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeTimestampTz(Fluent $column)
+    {
+        $columnType = $column->precision ? "datetimeoffset($column->precision)" : 'datetimeoffset';
+
+        return $column->useCurrent ? "$columnType default CURRENT_TIMESTAMP" : $columnType;
+    }
+
+    /**
+     * Create the column definition for a year type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeYear(Fluent $column)
+    {
+        return $this->typeInteger($column);
+    }
+
+    /**
+     * Create the column definition for a binary type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeBinary(Fluent $column)
+    {
+        return 'varbinary(max)';
+    }
+
+    /**
+     * Create the column definition for a uuid type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeUuid(Fluent $column)
+    {
+        return 'uniqueidentifier';
+    }
+
+    /**
+     * Create the column definition for an IP address type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeIpAddress(Fluent $column)
+    {
+        return 'nvarchar(45)';
+    }
+
+    /**
+     * Create the column definition for a MAC address type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    protected function typeMacAddress(Fluent $column)
+    {
+        return 'nvarchar(17)';
+    }
+
+    /**
+     * Create the column definition for a spatial Geometry type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeGeometry(Fluent $column)
+    {
+        return 'geography';
+    }
+
+    /**
+     * Create the column definition for a spatial Point type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typePoint(Fluent $column)
+    {
+        return 'geography';
+    }
+
+    /**
+     * Create the column definition for a spatial LineString type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeLineString(Fluent $column)
+    {
+        return 'geography';
+    }
+
+    /**
+     * Create the column definition for a spatial Polygon type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typePolygon(Fluent $column)
+    {
+        return 'geography';
+    }
+
+    /**
+     * Create the column definition for a spatial GeometryCollection type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeGeometryCollection(Fluent $column)
+    {
+        return 'geography';
+    }
+
+    /**
+     * Create the column definition for a spatial MultiPoint type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeMultiPoint(Fluent $column)
+    {
+        return 'geography';
+    }
+
+    /**
+     * Create the column definition for a spatial MultiLineString type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeMultiLineString(Fluent $column)
+    {
+        return 'geography';
+    }
+
+    /**
+     * Create the column definition for a spatial MultiPolygon type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string
+     */
+    public function typeMultiPolygon(Fluent $column)
+    {
+        return 'geography';
+    }
+
+    /**
+     * Create the column definition for a generated, computed column type.
+     *
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function typeComputed(Fluent $column)
+    {
+        return "as ({$column->expression})";
+    }
+
+    /**
+     * Get the SQL for a collation column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyCollate(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($column->collation)) {
+            return ' collate '.$column->collation;
+        }
+    }
+
+    /**
+     * Get the SQL for a nullable column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyNullable(Blueprint $blueprint, Fluent $column)
+    {
+        if ($column->type !== 'computed') {
+            return $column->nullable ? ' null' : ' not null';
+        }
+    }
+
+    /**
+     * Get the SQL for a default column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyDefault(Blueprint $blueprint, Fluent $column)
+    {
+        if (! is_null($column->default)) {
+            return ' default '.$this->getDefaultValue($column->default);
+        }
+    }
+
+    /**
+     * Get the SQL for an auto-increment column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyIncrement(Blueprint $blueprint, Fluent $column)
+    {
+        if (in_array($column->type, $this->serials) && $column->autoIncrement) {
+            return ' identity primary key';
+        }
+    }
+
+    /**
+     * Get the SQL for a generated stored column modifier.
+     *
+     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
+     * @param  \Illuminate\Support\Fluent  $column
+     * @return string|null
+     */
+    protected function modifyPersisted(Blueprint $blueprint, Fluent $column)
+    {
+        if ($column->persisted) {
+            return ' persisted';
+        }
+    }
+
+    /**
+     * Wrap a table in keyword identifiers.
+     *
+     * @param  \Illuminate\Database\Query\Expression|string  $table
+     * @return string
+     */
+    public function wrapTable($table)
+    {
+        if ($table instanceof Blueprint && $table->temporary) {
+            $this->setTablePrefix('#');
+        }
+
+        return parent::wrapTable($table);
+    }
+
+    /**
+     * Quote the given string literal.
+     *
+     * @param  string|array  $value
+     * @return string
+     */
+    public function quoteString($value)
+    {
+        if (is_array($value)) {
+            return implode(', ', array_map([$this, __FUNCTION__], $value));
+        }
+
+        return "N'$value'";
+    }
+}
diff --git a/vendor/illuminate/database/Schema/IndexDefinition.php b/vendor/illuminate/database/Schema/IndexDefinition.php
new file mode 100644
index 0000000..fc5d78e
--- /dev/null
+++ b/vendor/illuminate/database/Schema/IndexDefinition.php
@@ -0,0 +1,16 @@
+connection->statement(
+            $this->grammar->compileCreateDatabase($name, $this->connection)
+        );
+    }
+
+    /**
+     * Drop a database from the schema if the database exists.
+     *
+     * @param  string  $name
+     * @return bool
+     */
+    public function dropDatabaseIfExists($name)
+    {
+        return $this->connection->statement(
+            $this->grammar->compileDropDatabaseIfExists($name)
+        );
+    }
+
+    /**
+     * Determine if the given table exists.
+     *
+     * @param  string  $table
+     * @return bool
+     */
+    public function hasTable($table)
+    {
+        $table = $this->connection->getTablePrefix().$table;
+
+        return count($this->connection->selectFromWriteConnection(
+            $this->grammar->compileTableExists(), [$this->connection->getDatabaseName(), $table]
+        )) > 0;
+    }
+
+    /**
+     * Get the column listing for a given table.
+     *
+     * @param  string  $table
+     * @return array
+     */
+    public function getColumnListing($table)
+    {
+        $table = $this->connection->getTablePrefix().$table;
+
+        $results = $this->connection->selectFromWriteConnection(
+            $this->grammar->compileColumnListing(), [$this->connection->getDatabaseName(), $table]
+        );
+
+        return $this->connection->getPostProcessor()->processColumnListing($results);
+    }
+
+    /**
+     * Drop all tables from the database.
+     *
+     * @return void
+     */
+    public function dropAllTables()
+    {
+        $tables = [];
+
+        foreach ($this->getAllTables() as $row) {
+            $row = (array) $row;
+
+            $tables[] = reset($row);
+        }
+
+        if (empty($tables)) {
+            return;
+        }
+
+        $this->disableForeignKeyConstraints();
+
+        $this->connection->statement(
+            $this->grammar->compileDropAllTables($tables)
+        );
+
+        $this->enableForeignKeyConstraints();
+    }
+
+    /**
+     * Drop all views from the database.
+     *
+     * @return void
+     */
+    public function dropAllViews()
+    {
+        $views = [];
+
+        foreach ($this->getAllViews() as $row) {
+            $row = (array) $row;
+
+            $views[] = reset($row);
+        }
+
+        if (empty($views)) {
+            return;
+        }
+
+        $this->connection->statement(
+            $this->grammar->compileDropAllViews($views)
+        );
+    }
+
+    /**
+     * Get all of the table names for the database.
+     *
+     * @return array
+     */
+    public function getAllTables()
+    {
+        return $this->connection->select(
+            $this->grammar->compileGetAllTables()
+        );
+    }
+
+    /**
+     * Get all of the view names for the database.
+     *
+     * @return array
+     */
+    public function getAllViews()
+    {
+        return $this->connection->select(
+            $this->grammar->compileGetAllViews()
+        );
+    }
+}
diff --git a/vendor/illuminate/database/Schema/MySqlSchemaState.php b/vendor/illuminate/database/Schema/MySqlSchemaState.php
new file mode 100644
index 0000000..0cd3486
--- /dev/null
+++ b/vendor/illuminate/database/Schema/MySqlSchemaState.php
@@ -0,0 +1,170 @@
+executeDumpProcess($this->makeProcess(
+            $this->baseDumpCommand().' --routines --result-file="${:LARAVEL_LOAD_PATH}" --no-data'
+        ), $this->output, array_merge($this->baseVariables($this->connection->getConfig()), [
+            'LARAVEL_LOAD_PATH' => $path,
+        ]));
+
+        $this->removeAutoIncrementingState($path);
+
+        $this->appendMigrationData($path);
+    }
+
+    /**
+     * Remove the auto-incrementing state from the given schema dump.
+     *
+     * @param  string  $path
+     * @return void
+     */
+    protected function removeAutoIncrementingState(string $path)
+    {
+        $this->files->put($path, preg_replace(
+            '/\s+AUTO_INCREMENT=[0-9]+/iu',
+            '',
+            $this->files->get($path)
+        ));
+    }
+
+    /**
+     * Append the migration data to the schema dump.
+     *
+     * @param  string  $path
+     * @return void
+     */
+    protected function appendMigrationData(string $path)
+    {
+        $process = $this->executeDumpProcess($this->makeProcess(
+            $this->baseDumpCommand().' '.$this->migrationTable.' --no-create-info --skip-extended-insert --skip-routines --compact'
+        ), null, array_merge($this->baseVariables($this->connection->getConfig()), [
+            //
+        ]));
+
+        $this->files->append($path, $process->getOutput());
+    }
+
+    /**
+     * Load the given schema file into the database.
+     *
+     * @param  string  $path
+     * @return void
+     */
+    public function load($path)
+    {
+        $command = 'mysql '.$this->connectionString().' --database="${:LARAVEL_LOAD_DATABASE}" < "${:LARAVEL_LOAD_PATH}"';
+
+        $process = $this->makeProcess($command)->setTimeout(null);
+
+        $process->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [
+            'LARAVEL_LOAD_PATH' => $path,
+        ]));
+    }
+
+    /**
+     * Get the base dump command arguments for MySQL as a string.
+     *
+     * @return string
+     */
+    protected function baseDumpCommand()
+    {
+        $command = 'mysqldump '.$this->connectionString().' --no-tablespaces --skip-add-locks --skip-comments --skip-set-charset --tz-utc --column-statistics=0';
+
+        if (! $this->connection->isMaria()) {
+            $command .= ' --set-gtid-purged=OFF';
+        }
+
+        return $command.' "${:LARAVEL_LOAD_DATABASE}"';
+    }
+
+    /**
+     * Generate a basic connection string (--socket, --host, --port, --user, --password) for the database.
+     *
+     * @return string
+     */
+    protected function connectionString()
+    {
+        $value = ' --user="${:LARAVEL_LOAD_USER}" --password="${:LARAVEL_LOAD_PASSWORD}"';
+
+        $config = $this->connection->getConfig();
+
+        $value .= $config['unix_socket'] ?? false
+                        ? ' --socket="${:LARAVEL_LOAD_SOCKET}"'
+                        : ' --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}"';
+
+        if (isset($config['options'][\PDO::MYSQL_ATTR_SSL_CA])) {
+            $value .= ' --ssl-ca="${:LARAVEL_LOAD_SSL_CA}"';
+        }
+
+        return $value;
+    }
+
+    /**
+     * Get the base variables for a dump / load command.
+     *
+     * @param  array  $config
+     * @return array
+     */
+    protected function baseVariables(array $config)
+    {
+        $config['host'] ??= '';
+
+        return [
+            'LARAVEL_LOAD_SOCKET' => $config['unix_socket'] ?? '',
+            'LARAVEL_LOAD_HOST' => is_array($config['host']) ? $config['host'][0] : $config['host'],
+            'LARAVEL_LOAD_PORT' => $config['port'] ?? '',
+            'LARAVEL_LOAD_USER' => $config['username'],
+            'LARAVEL_LOAD_PASSWORD' => $config['password'] ?? '',
+            'LARAVEL_LOAD_DATABASE' => $config['database'],
+            'LARAVEL_LOAD_SSL_CA' => $config['options'][\PDO::MYSQL_ATTR_SSL_CA] ?? '',
+        ];
+    }
+
+    /**
+     * Execute the given dump process.
+     *
+     * @param  \Symfony\Component\Process\Process  $process
+     * @param  callable  $output
+     * @param  array  $variables
+     * @return \Symfony\Component\Process\Process
+     */
+    protected function executeDumpProcess(Process $process, $output, array $variables)
+    {
+        try {
+            $process->setTimeout(null)->mustRun($output, $variables);
+        } catch (Exception $e) {
+            if (Str::contains($e->getMessage(), ['column-statistics', 'column_statistics'])) {
+                return $this->executeDumpProcess(Process::fromShellCommandLine(
+                    str_replace(' --column-statistics=0', '', $process->getCommandLine())
+                ), $output, $variables);
+            }
+
+            if (str_contains($e->getMessage(), 'set-gtid-purged')) {
+                return $this->executeDumpProcess(Process::fromShellCommandLine(
+                    str_replace(' --set-gtid-purged=OFF', '', $process->getCommandLine())
+                ), $output, $variables);
+            }
+
+            throw $e;
+        }
+
+        return $process;
+    }
+}
diff --git a/vendor/illuminate/database/Schema/PostgresBuilder.php b/vendor/illuminate/database/Schema/PostgresBuilder.php
new file mode 100755
index 0000000..adfbd68
--- /dev/null
+++ b/vendor/illuminate/database/Schema/PostgresBuilder.php
@@ -0,0 +1,248 @@
+connection->statement(
+            $this->grammar->compileCreateDatabase($name, $this->connection)
+        );
+    }
+
+    /**
+     * Drop a database from the schema if the database exists.
+     *
+     * @param  string  $name
+     * @return bool
+     */
+    public function dropDatabaseIfExists($name)
+    {
+        return $this->connection->statement(
+            $this->grammar->compileDropDatabaseIfExists($name)
+        );
+    }
+
+    /**
+     * Determine if the given table exists.
+     *
+     * @param  string  $table
+     * @return bool
+     */
+    public function hasTable($table)
+    {
+        [$database, $schema, $table] = $this->parseSchemaAndTable($table);
+
+        $table = $this->connection->getTablePrefix().$table;
+
+        return count($this->connection->selectFromWriteConnection(
+            $this->grammar->compileTableExists(), [$database, $schema, $table]
+        )) > 0;
+    }
+
+    /**
+     * Drop all tables from the database.
+     *
+     * @return void
+     */
+    public function dropAllTables()
+    {
+        $tables = [];
+
+        $excludedTables = $this->grammar->escapeNames(
+            $this->connection->getConfig('dont_drop') ?? ['spatial_ref_sys']
+        );
+
+        foreach ($this->getAllTables() as $row) {
+            $row = (array) $row;
+
+            if (empty(array_intersect($this->grammar->escapeNames($row), $excludedTables))) {
+                $tables[] = $row['qualifiedname'] ?? reset($row);
+            }
+        }
+
+        if (empty($tables)) {
+            return;
+        }
+
+        $this->connection->statement(
+            $this->grammar->compileDropAllTables($tables)
+        );
+    }
+
+    /**
+     * Drop all views from the database.
+     *
+     * @return void
+     */
+    public function dropAllViews()
+    {
+        $views = [];
+
+        foreach ($this->getAllViews() as $row) {
+            $row = (array) $row;
+
+            $views[] = $row['qualifiedname'] ?? reset($row);
+        }
+
+        if (empty($views)) {
+            return;
+        }
+
+        $this->connection->statement(
+            $this->grammar->compileDropAllViews($views)
+        );
+    }
+
+    /**
+     * Drop all types from the database.
+     *
+     * @return void
+     */
+    public function dropAllTypes()
+    {
+        $types = [];
+
+        foreach ($this->getAllTypes() as $row) {
+            $row = (array) $row;
+
+            $types[] = reset($row);
+        }
+
+        if (empty($types)) {
+            return;
+        }
+
+        $this->connection->statement(
+            $this->grammar->compileDropAllTypes($types)
+        );
+    }
+
+    /**
+     * Get all of the table names for the database.
+     *
+     * @return array
+     */
+    public function getAllTables()
+    {
+        return $this->connection->select(
+            $this->grammar->compileGetAllTables(
+                $this->parseSearchPath(
+                    $this->connection->getConfig('search_path') ?: $this->connection->getConfig('schema')
+                )
+            )
+        );
+    }
+
+    /**
+     * Get all of the view names for the database.
+     *
+     * @return array
+     */
+    public function getAllViews()
+    {
+        return $this->connection->select(
+            $this->grammar->compileGetAllViews(
+                $this->parseSearchPath(
+                    $this->connection->getConfig('search_path') ?: $this->connection->getConfig('schema')
+                )
+            )
+        );
+    }
+
+    /**
+     * Get all of the type names for the database.
+     *
+     * @return array
+     */
+    public function getAllTypes()
+    {
+        return $this->connection->select(
+            $this->grammar->compileGetAllTypes()
+        );
+    }
+
+    /**
+     * Get the column listing for a given table.
+     *
+     * @param  string  $table
+     * @return array
+     */
+    public function getColumnListing($table)
+    {
+        [$database, $schema, $table] = $this->parseSchemaAndTable($table);
+
+        $table = $this->connection->getTablePrefix().$table;
+
+        $results = $this->connection->selectFromWriteConnection(
+            $this->grammar->compileColumnListing(), [$database, $schema, $table]
+        );
+
+        return $this->connection->getPostProcessor()->processColumnListing($results);
+    }
+
+    /**
+     * Parse the database object reference and extract the database, schema, and table.
+     *
+     * @param  string  $reference
+     * @return array
+     */
+    protected function parseSchemaAndTable($reference)
+    {
+        $searchPath = $this->parseSearchPath(
+            $this->connection->getConfig('search_path') ?: $this->connection->getConfig('schema') ?: 'public'
+        );
+
+        $parts = explode('.', $reference);
+
+        $database = $this->connection->getConfig('database');
+
+        // If the reference contains a database name, we will use that instead of the
+        // default database name for the connection. This allows the database name
+        // to be specified in the query instead of at the full connection level.
+        if (count($parts) === 3) {
+            $database = $parts[0];
+            array_shift($parts);
+        }
+
+        // We will use the default schema unless the schema has been specified in the
+        // query. If the schema has been specified in the query then we can use it
+        // instead of a default schema configured in the connection search path.
+        $schema = $searchPath[0];
+
+        if (count($parts) === 2) {
+            $schema = $parts[0];
+            array_shift($parts);
+        }
+
+        return [$database, $schema, $parts[0]];
+    }
+
+    /**
+     * Parse the "search_path" configuration value into an array.
+     *
+     * @param  string|array|null  $searchPath
+     * @return array
+     */
+    protected function parseSearchPath($searchPath)
+    {
+        return array_map(function ($schema) {
+            return $schema === '$user'
+                ? $this->connection->getConfig('username')
+                : $schema;
+        }, $this->baseParseSearchPath($searchPath));
+    }
+}
diff --git a/vendor/illuminate/database/Schema/PostgresSchemaState.php b/vendor/illuminate/database/Schema/PostgresSchemaState.php
new file mode 100644
index 0000000..cfb100d
--- /dev/null
+++ b/vendor/illuminate/database/Schema/PostgresSchemaState.php
@@ -0,0 +1,79 @@
+baseDumpCommand().' --schema-only > '.$path,
+            $this->baseDumpCommand().' -t '.$this->migrationTable.' --data-only >> '.$path,
+        ]);
+
+        $commands->map(function ($command, $path) {
+            $this->makeProcess($command)->mustRun($this->output, array_merge($this->baseVariables($this->connection->getConfig()), [
+                'LARAVEL_LOAD_PATH' => $path,
+            ]));
+        });
+    }
+
+    /**
+     * Load the given schema file into the database.
+     *
+     * @param  string  $path
+     * @return void
+     */
+    public function load($path)
+    {
+        $command = 'pg_restore --no-owner --no-acl --clean --if-exists --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}" --username="${:LARAVEL_LOAD_USER}" --dbname="${:LARAVEL_LOAD_DATABASE}" "${:LARAVEL_LOAD_PATH}"';
+
+        if (str_ends_with($path, '.sql')) {
+            $command = 'psql --file="${:LARAVEL_LOAD_PATH}" --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}" --username="${:LARAVEL_LOAD_USER}" --dbname="${:LARAVEL_LOAD_DATABASE}"';
+        }
+
+        $process = $this->makeProcess($command);
+
+        $process->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [
+            'LARAVEL_LOAD_PATH' => $path,
+        ]));
+    }
+
+    /**
+     * Get the base dump command arguments for PostgreSQL as a string.
+     *
+     * @return string
+     */
+    protected function baseDumpCommand()
+    {
+        return 'pg_dump --no-owner --no-acl --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}" --username="${:LARAVEL_LOAD_USER}" --dbname="${:LARAVEL_LOAD_DATABASE}"';
+    }
+
+    /**
+     * Get the base variables for a dump / load command.
+     *
+     * @param  array  $config
+     * @return array
+     */
+    protected function baseVariables(array $config)
+    {
+        $config['host'] ??= '';
+
+        return [
+            'LARAVEL_LOAD_HOST' => is_array($config['host']) ? $config['host'][0] : $config['host'],
+            'LARAVEL_LOAD_PORT' => $config['port'],
+            'LARAVEL_LOAD_USER' => $config['username'],
+            'PGPASSWORD' => $config['password'],
+            'LARAVEL_LOAD_DATABASE' => $config['database'],
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/Schema/SQLiteBuilder.php b/vendor/illuminate/database/Schema/SQLiteBuilder.php
new file mode 100644
index 0000000..4e74f92
--- /dev/null
+++ b/vendor/illuminate/database/Schema/SQLiteBuilder.php
@@ -0,0 +1,102 @@
+connection->getDatabaseName() !== ':memory:') {
+            return $this->refreshDatabaseFile();
+        }
+
+        $this->connection->select($this->grammar->compileEnableWriteableSchema());
+
+        $this->connection->select($this->grammar->compileDropAllTables());
+
+        $this->connection->select($this->grammar->compileDisableWriteableSchema());
+
+        $this->connection->select($this->grammar->compileRebuild());
+    }
+
+    /**
+     * Drop all views from the database.
+     *
+     * @return void
+     */
+    public function dropAllViews()
+    {
+        $this->connection->select($this->grammar->compileEnableWriteableSchema());
+
+        $this->connection->select($this->grammar->compileDropAllViews());
+
+        $this->connection->select($this->grammar->compileDisableWriteableSchema());
+
+        $this->connection->select($this->grammar->compileRebuild());
+    }
+
+    /**
+     * Get all of the table names for the database.
+     *
+     * @return array
+     */
+    public function getAllTables()
+    {
+        return $this->connection->select(
+            $this->grammar->compileGetAllTables()
+        );
+    }
+
+    /**
+     * Get all of the view names for the database.
+     *
+     * @return array
+     */
+    public function getAllViews()
+    {
+        return $this->connection->select(
+            $this->grammar->compileGetAllViews()
+        );
+    }
+
+    /**
+     * Empty the database file.
+     *
+     * @return void
+     */
+    public function refreshDatabaseFile()
+    {
+        file_put_contents($this->connection->getDatabaseName(), '');
+    }
+}
diff --git a/vendor/illuminate/database/Schema/SchemaState.php b/vendor/illuminate/database/Schema/SchemaState.php
new file mode 100644
index 0000000..58d9c3a
--- /dev/null
+++ b/vendor/illuminate/database/Schema/SchemaState.php
@@ -0,0 +1,122 @@
+connection = $connection;
+
+        $this->files = $files ?: new Filesystem;
+
+        $this->processFactory = $processFactory ?: function (...$arguments) {
+            return Process::fromShellCommandline(...$arguments)->setTimeout(null);
+        };
+
+        $this->handleOutputUsing(function () {
+            //
+        });
+    }
+
+    /**
+     * Dump the database's schema into a file.
+     *
+     * @param  \Illuminate\Database\Connection  $connection
+     * @param  string  $path
+     * @return void
+     */
+    abstract public function dump(Connection $connection, $path);
+
+    /**
+     * Load the given schema file into the database.
+     *
+     * @param  string  $path
+     * @return void
+     */
+    abstract public function load($path);
+
+    /**
+     * Create a new process instance.
+     *
+     * @param  mixed  ...$arguments
+     * @return \Symfony\Component\Process\Process
+     */
+    public function makeProcess(...$arguments)
+    {
+        return call_user_func($this->processFactory, ...$arguments);
+    }
+
+    /**
+     * Specify the name of the application's migration table.
+     *
+     * @param  string  $table
+     * @return $this
+     */
+    public function withMigrationTable(string $table)
+    {
+        $this->migrationTable = $table;
+
+        return $this;
+    }
+
+    /**
+     * Specify the callback that should be used to handle process output.
+     *
+     * @param  callable  $output
+     * @return $this
+     */
+    public function handleOutputUsing(callable $output)
+    {
+        $this->output = $output;
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/database/Schema/SqlServerBuilder.php b/vendor/illuminate/database/Schema/SqlServerBuilder.php
new file mode 100644
index 0000000..c323e12
--- /dev/null
+++ b/vendor/illuminate/database/Schema/SqlServerBuilder.php
@@ -0,0 +1,78 @@
+connection->statement(
+            $this->grammar->compileCreateDatabase($name, $this->connection)
+        );
+    }
+
+    /**
+     * Drop a database from the schema if the database exists.
+     *
+     * @param  string  $name
+     * @return bool
+     */
+    public function dropDatabaseIfExists($name)
+    {
+        return $this->connection->statement(
+            $this->grammar->compileDropDatabaseIfExists($name)
+        );
+    }
+
+    /**
+     * Drop all tables from the database.
+     *
+     * @return void
+     */
+    public function dropAllTables()
+    {
+        $this->connection->statement($this->grammar->compileDropAllForeignKeys());
+
+        $this->connection->statement($this->grammar->compileDropAllTables());
+    }
+
+    /**
+     * Drop all views from the database.
+     *
+     * @return void
+     */
+    public function dropAllViews()
+    {
+        $this->connection->statement($this->grammar->compileDropAllViews());
+    }
+
+    /**
+     * Drop all tables from the database.
+     *
+     * @return array
+     */
+    public function getAllTables()
+    {
+        return $this->connection->select(
+            $this->grammar->compileGetAllTables()
+        );
+    }
+
+    /**
+     * Get all of the view names for the database.
+     *
+     * @return array
+     */
+    public function getAllViews()
+    {
+        return $this->connection->select(
+            $this->grammar->compileGetAllViews()
+        );
+    }
+}
diff --git a/vendor/illuminate/database/Schema/SqliteSchemaState.php b/vendor/illuminate/database/Schema/SqliteSchemaState.php
new file mode 100644
index 0000000..10efc7c
--- /dev/null
+++ b/vendor/illuminate/database/Schema/SqliteSchemaState.php
@@ -0,0 +1,99 @@
+makeProcess(
+            $this->baseCommand().' .schema'
+        ))->setTimeout(null)->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [
+            //
+        ]));
+
+        $migrations = collect(preg_split("/\r\n|\n|\r/", $process->getOutput()))->filter(function ($line) {
+            return stripos($line, 'sqlite_sequence') === false &&
+                   strlen($line) > 0;
+        })->all();
+
+        $this->files->put($path, implode(PHP_EOL, $migrations).PHP_EOL);
+
+        $this->appendMigrationData($path);
+    }
+
+    /**
+     * Append the migration data to the schema dump.
+     *
+     * @param  string  $path
+     * @return void
+     */
+    protected function appendMigrationData(string $path)
+    {
+        with($process = $this->makeProcess(
+            $this->baseCommand().' ".dump \''.$this->migrationTable.'\'"'
+        ))->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [
+            //
+        ]));
+
+        $migrations = collect(preg_split("/\r\n|\n|\r/", $process->getOutput()))->filter(function ($line) {
+            return preg_match('/^\s*(--|INSERT\s)/iu', $line) === 1 &&
+                   strlen($line) > 0;
+        })->all();
+
+        $this->files->append($path, implode(PHP_EOL, $migrations).PHP_EOL);
+    }
+
+    /**
+     * Load the given schema file into the database.
+     *
+     * @param  string  $path
+     * @return void
+     */
+    public function load($path)
+    {
+        if ($this->connection->getDatabaseName() === ':memory:') {
+            $this->connection->getPdo()->exec($this->files->get($path));
+
+            return;
+        }
+
+        $process = $this->makeProcess($this->baseCommand().' < "${:LARAVEL_LOAD_PATH}"');
+
+        $process->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [
+            'LARAVEL_LOAD_PATH' => $path,
+        ]));
+    }
+
+    /**
+     * Get the base sqlite command arguments as a string.
+     *
+     * @return string
+     */
+    protected function baseCommand()
+    {
+        return 'sqlite3 "${:LARAVEL_LOAD_DATABASE}"';
+    }
+
+    /**
+     * Get the base variables for a dump / load command.
+     *
+     * @param  array  $config
+     * @return array
+     */
+    protected function baseVariables(array $config)
+    {
+        return [
+            'LARAVEL_LOAD_DATABASE' => $config['database'],
+        ];
+    }
+}
diff --git a/vendor/illuminate/database/Seeder.php b/vendor/illuminate/database/Seeder.php
new file mode 100755
index 0000000..ba4cd4a
--- /dev/null
+++ b/vendor/illuminate/database/Seeder.php
@@ -0,0 +1,195 @@
+resolve($class);
+
+            $name = get_class($seeder);
+
+            if ($silent === false && isset($this->command)) {
+                with(new TwoColumnDetail($this->command->getOutput()))->render(
+                    $name,
+                    'RUNNING'
+                );
+            }
+
+            $startTime = microtime(true);
+
+            $seeder->__invoke($parameters);
+
+            if ($silent === false && isset($this->command)) {
+                $runTime = number_format((microtime(true) - $startTime) * 1000, 2);
+
+                with(new TwoColumnDetail($this->command->getOutput()))->render(
+                    $name,
+                    "$runTime ms DONE"
+                );
+
+                $this->command->getOutput()->writeln('');
+            }
+
+            static::$called[] = $class;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Run the given seeder class.
+     *
+     * @param  array|string  $class
+     * @param  array  $parameters
+     * @return void
+     */
+    public function callWith($class, array $parameters = [])
+    {
+        $this->call($class, false, $parameters);
+    }
+
+    /**
+     * Silently run the given seeder class.
+     *
+     * @param  array|string  $class
+     * @param  array  $parameters
+     * @return void
+     */
+    public function callSilent($class, array $parameters = [])
+    {
+        $this->call($class, true, $parameters);
+    }
+
+    /**
+     * Run the given seeder class once.
+     *
+     * @param  array|string  $class
+     * @param  bool  $silent
+     * @return void
+     */
+    public function callOnce($class, $silent = false, array $parameters = [])
+    {
+        if (in_array($class, static::$called)) {
+            return;
+        }
+
+        $this->call($class, $silent, $parameters);
+    }
+
+    /**
+     * Resolve an instance of the given seeder class.
+     *
+     * @param  string  $class
+     * @return \Illuminate\Database\Seeder
+     */
+    protected function resolve($class)
+    {
+        if (isset($this->container)) {
+            $instance = $this->container->make($class);
+
+            $instance->setContainer($this->container);
+        } else {
+            $instance = new $class;
+        }
+
+        if (isset($this->command)) {
+            $instance->setCommand($this->command);
+        }
+
+        return $instance;
+    }
+
+    /**
+     * Set the IoC container instance.
+     *
+     * @param  \Illuminate\Contracts\Container\Container  $container
+     * @return $this
+     */
+    public function setContainer(Container $container)
+    {
+        $this->container = $container;
+
+        return $this;
+    }
+
+    /**
+     * Set the console command instance.
+     *
+     * @param  \Illuminate\Console\Command  $command
+     * @return $this
+     */
+    public function setCommand(Command $command)
+    {
+        $this->command = $command;
+
+        return $this;
+    }
+
+    /**
+     * Run the database seeds.
+     *
+     * @param  array  $parameters
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function __invoke(array $parameters = [])
+    {
+        if (! method_exists($this, 'run')) {
+            throw new InvalidArgumentException('Method [run] missing from '.get_class($this));
+        }
+
+        $callback = fn () => isset($this->container)
+            ? $this->container->call([$this, 'run'], $parameters)
+            : $this->run(...$parameters);
+
+        $uses = array_flip(class_uses_recursive(static::class));
+
+        if (isset($uses[WithoutModelEvents::class])) {
+            $callback = $this->withoutModelEvents($callback);
+        }
+
+        return $callback();
+    }
+}
diff --git a/vendor/illuminate/database/SqlServerConnection.php b/vendor/illuminate/database/SqlServerConnection.php
new file mode 100755
index 0000000..feb4577
--- /dev/null
+++ b/vendor/illuminate/database/SqlServerConnection.php
@@ -0,0 +1,123 @@
+getDriverName() === 'sqlsrv') {
+                return parent::transaction($callback, $attempts);
+            }
+
+            $this->getPdo()->exec('BEGIN TRAN');
+
+            // We'll simply execute the given callback within a try / catch block
+            // and if we catch any exception we can rollback the transaction
+            // so that none of the changes are persisted to the database.
+            try {
+                $result = $callback($this);
+
+                $this->getPdo()->exec('COMMIT TRAN');
+            }
+
+            // If we catch an exception, we will rollback so nothing gets messed
+            // up in the database. Then we'll re-throw the exception so it can
+            // be handled how the developer sees fit for their applications.
+            catch (Throwable $e) {
+                $this->getPdo()->exec('ROLLBACK TRAN');
+
+                throw $e;
+            }
+
+            return $result;
+        }
+    }
+
+    /**
+     * Get the default query grammar instance.
+     *
+     * @return \Illuminate\Database\Query\Grammars\SqlServerGrammar
+     */
+    protected function getDefaultQueryGrammar()
+    {
+        return $this->withTablePrefix(new QueryGrammar);
+    }
+
+    /**
+     * Get a schema builder instance for the connection.
+     *
+     * @return \Illuminate\Database\Schema\SqlServerBuilder
+     */
+    public function getSchemaBuilder()
+    {
+        if (is_null($this->schemaGrammar)) {
+            $this->useDefaultSchemaGrammar();
+        }
+
+        return new SqlServerBuilder($this);
+    }
+
+    /**
+     * Get the default schema grammar instance.
+     *
+     * @return \Illuminate\Database\Schema\Grammars\SqlServerGrammar
+     */
+    protected function getDefaultSchemaGrammar()
+    {
+        return $this->withTablePrefix(new SchemaGrammar);
+    }
+
+    /**
+     * Get the schema state for the connection.
+     *
+     * @param  \Illuminate\Filesystem\Filesystem|null  $files
+     * @param  callable|null  $processFactory
+     *
+     * @throws \RuntimeException
+     */
+    public function getSchemaState(Filesystem $files = null, callable $processFactory = null)
+    {
+        throw new RuntimeException('Schema dumping is not supported when using SQL Server.');
+    }
+
+    /**
+     * Get the default post processor instance.
+     *
+     * @return \Illuminate\Database\Query\Processors\SqlServerProcessor
+     */
+    protected function getDefaultPostProcessor()
+    {
+        return new SqlServerProcessor;
+    }
+
+    /**
+     * Get the Doctrine DBAL driver.
+     *
+     * @return \Illuminate\Database\PDO\SqlServerDriver
+     */
+    protected function getDoctrineDriver()
+    {
+        return new SqlServerDriver;
+    }
+}
diff --git a/vendor/illuminate/database/composer.json b/vendor/illuminate/database/composer.json
new file mode 100644
index 0000000..ba2dc30
--- /dev/null
+++ b/vendor/illuminate/database/composer.json
@@ -0,0 +1,52 @@
+{
+    "name": "illuminate/database",
+    "description": "The Illuminate Database package.",
+    "license": "MIT",
+    "homepage": "https://laravel.com",
+    "support": {
+        "issues": "https://github.com/laravel/framework/issues",
+        "source": "https://github.com/laravel/framework"
+    },
+    "keywords": ["laravel", "database", "sql", "orm"],
+    "authors": [
+        {
+            "name": "Taylor Otwell",
+            "email": "taylor@laravel.com"
+        }
+    ],
+    "require": {
+        "php": "^8.0.2",
+        "ext-pdo": "*",
+        "brick/math": "^0.9.3|^0.10.2|^0.11",
+        "illuminate/collections": "^9.0",
+        "illuminate/container": "^9.0",
+        "illuminate/contracts": "^9.0",
+        "illuminate/macroable": "^9.0",
+        "illuminate/support": "^9.0",
+        "symfony/console": "^6.0.9"
+    },
+    "autoload": {
+        "psr-4": {
+            "Illuminate\\Database\\": ""
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "9.x-dev"
+        }
+    },
+    "suggest": {
+        "ext-filter": "Required to use the Postgres database driver.",
+        "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).",
+        "fakerphp/faker": "Required to use the eloquent factory builder (^1.21).",
+        "illuminate/console": "Required to use the database commands (^9.0).",
+        "illuminate/events": "Required to use the observers with Eloquent (^9.0).",
+        "illuminate/filesystem": "Required to use the migrations (^9.0).",
+        "illuminate/pagination": "Required to paginate the result set (^9.0).",
+        "symfony/finder": "Required to use Eloquent model factories (^6.0)."
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/events/CallQueuedListener.php b/vendor/illuminate/events/CallQueuedListener.php
new file mode 100644
index 0000000..6a39008
--- /dev/null
+++ b/vendor/illuminate/events/CallQueuedListener.php
@@ -0,0 +1,180 @@
+data = $data;
+        $this->class = $class;
+        $this->method = $method;
+    }
+
+    /**
+     * Handle the queued job.
+     *
+     * @param  \Illuminate\Container\Container  $container
+     * @return void
+     */
+    public function handle(Container $container)
+    {
+        $this->prepareData();
+
+        $handler = $this->setJobInstanceIfNecessary(
+            $this->job, $container->make($this->class)
+        );
+
+        $handler->{$this->method}(...array_values($this->data));
+    }
+
+    /**
+     * Set the job instance of the given class if necessary.
+     *
+     * @param  \Illuminate\Contracts\Queue\Job  $job
+     * @param  object  $instance
+     * @return object
+     */
+    protected function setJobInstanceIfNecessary(Job $job, $instance)
+    {
+        if (in_array(InteractsWithQueue::class, class_uses_recursive($instance))) {
+            $instance->setJob($job);
+        }
+
+        return $instance;
+    }
+
+    /**
+     * Call the failed method on the job instance.
+     *
+     * The event instance and the exception will be passed.
+     *
+     * @param  \Throwable  $e
+     * @return void
+     */
+    public function failed($e)
+    {
+        $this->prepareData();
+
+        $handler = Container::getInstance()->make($this->class);
+
+        $parameters = array_merge(array_values($this->data), [$e]);
+
+        if (method_exists($handler, 'failed')) {
+            $handler->failed(...$parameters);
+        }
+    }
+
+    /**
+     * Unserialize the data if needed.
+     *
+     * @return void
+     */
+    protected function prepareData()
+    {
+        if (is_string($this->data)) {
+            $this->data = unserialize($this->data);
+        }
+    }
+
+    /**
+     * Get the display name for the queued job.
+     *
+     * @return string
+     */
+    public function displayName()
+    {
+        return $this->class;
+    }
+
+    /**
+     * Prepare the instance for cloning.
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        $this->data = array_map(function ($data) {
+            return is_object($data) ? clone $data : $data;
+        }, $this->data);
+    }
+}
diff --git a/vendor/illuminate/events/Dispatcher.php b/vendor/illuminate/events/Dispatcher.php
new file mode 100755
index 0000000..1876897
--- /dev/null
+++ b/vendor/illuminate/events/Dispatcher.php
@@ -0,0 +1,705 @@
+container = $container ?: new Container;
+    }
+
+    /**
+     * Register an event listener with the dispatcher.
+     *
+     * @param  \Closure|string|array  $events
+     * @param  \Closure|string|array|null  $listener
+     * @return void
+     */
+    public function listen($events, $listener = null)
+    {
+        if ($events instanceof Closure) {
+            return collect($this->firstClosureParameterTypes($events))
+                ->each(function ($event) use ($events) {
+                    $this->listen($event, $events);
+                });
+        } elseif ($events instanceof QueuedClosure) {
+            return collect($this->firstClosureParameterTypes($events->closure))
+                ->each(function ($event) use ($events) {
+                    $this->listen($event, $events->resolve());
+                });
+        } elseif ($listener instanceof QueuedClosure) {
+            $listener = $listener->resolve();
+        }
+
+        foreach ((array) $events as $event) {
+            if (str_contains($event, '*')) {
+                $this->setupWildcardListen($event, $listener);
+            } else {
+                $this->listeners[$event][] = $listener;
+            }
+        }
+    }
+
+    /**
+     * Setup a wildcard listener callback.
+     *
+     * @param  string  $event
+     * @param  \Closure|string  $listener
+     * @return void
+     */
+    protected function setupWildcardListen($event, $listener)
+    {
+        $this->wildcards[$event][] = $listener;
+
+        $this->wildcardsCache = [];
+    }
+
+    /**
+     * Determine if a given event has listeners.
+     *
+     * @param  string  $eventName
+     * @return bool
+     */
+    public function hasListeners($eventName)
+    {
+        return isset($this->listeners[$eventName]) ||
+               isset($this->wildcards[$eventName]) ||
+               $this->hasWildcardListeners($eventName);
+    }
+
+    /**
+     * Determine if the given event has any wildcard listeners.
+     *
+     * @param  string  $eventName
+     * @return bool
+     */
+    public function hasWildcardListeners($eventName)
+    {
+        foreach ($this->wildcards as $key => $listeners) {
+            if (Str::is($key, $eventName)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Register an event and payload to be fired later.
+     *
+     * @param  string  $event
+     * @param  object|array  $payload
+     * @return void
+     */
+    public function push($event, $payload = [])
+    {
+        $this->listen($event.'_pushed', function () use ($event, $payload) {
+            $this->dispatch($event, $payload);
+        });
+    }
+
+    /**
+     * Flush a set of pushed events.
+     *
+     * @param  string  $event
+     * @return void
+     */
+    public function flush($event)
+    {
+        $this->dispatch($event.'_pushed');
+    }
+
+    /**
+     * Register an event subscriber with the dispatcher.
+     *
+     * @param  object|string  $subscriber
+     * @return void
+     */
+    public function subscribe($subscriber)
+    {
+        $subscriber = $this->resolveSubscriber($subscriber);
+
+        $events = $subscriber->subscribe($this);
+
+        if (is_array($events)) {
+            foreach ($events as $event => $listeners) {
+                foreach (Arr::wrap($listeners) as $listener) {
+                    if (is_string($listener) && method_exists($subscriber, $listener)) {
+                        $this->listen($event, [get_class($subscriber), $listener]);
+
+                        continue;
+                    }
+
+                    $this->listen($event, $listener);
+                }
+            }
+        }
+    }
+
+    /**
+     * Resolve the subscriber instance.
+     *
+     * @param  object|string  $subscriber
+     * @return mixed
+     */
+    protected function resolveSubscriber($subscriber)
+    {
+        if (is_string($subscriber)) {
+            return $this->container->make($subscriber);
+        }
+
+        return $subscriber;
+    }
+
+    /**
+     * Fire an event until the first non-null response is returned.
+     *
+     * @param  string|object  $event
+     * @param  mixed  $payload
+     * @return array|null
+     */
+    public function until($event, $payload = [])
+    {
+        return $this->dispatch($event, $payload, true);
+    }
+
+    /**
+     * Fire an event and call the listeners.
+     *
+     * @param  string|object  $event
+     * @param  mixed  $payload
+     * @param  bool  $halt
+     * @return array|null
+     */
+    public function dispatch($event, $payload = [], $halt = false)
+    {
+        // When the given "event" is actually an object we will assume it is an event
+        // object and use the class as the event name and this event itself as the
+        // payload to the handler, which makes object based events quite simple.
+        [$event, $payload] = $this->parseEventAndPayload(
+            $event, $payload
+        );
+
+        if ($this->shouldBroadcast($payload)) {
+            $this->broadcastEvent($payload[0]);
+        }
+
+        $responses = [];
+
+        foreach ($this->getListeners($event) as $listener) {
+            $response = $listener($event, $payload);
+
+            // If a response is returned from the listener and event halting is enabled
+            // we will just return this response, and not call the rest of the event
+            // listeners. Otherwise we will add the response on the response list.
+            if ($halt && ! is_null($response)) {
+                return $response;
+            }
+
+            // If a boolean false is returned from a listener, we will stop propagating
+            // the event to any further listeners down in the chain, else we keep on
+            // looping through the listeners and firing every one in our sequence.
+            if ($response === false) {
+                break;
+            }
+
+            $responses[] = $response;
+        }
+
+        return $halt ? null : $responses;
+    }
+
+    /**
+     * Parse the given event and payload and prepare them for dispatching.
+     *
+     * @param  mixed  $event
+     * @param  mixed  $payload
+     * @return array
+     */
+    protected function parseEventAndPayload($event, $payload)
+    {
+        if (is_object($event)) {
+            [$payload, $event] = [[$event], get_class($event)];
+        }
+
+        return [$event, Arr::wrap($payload)];
+    }
+
+    /**
+     * Determine if the payload has a broadcastable event.
+     *
+     * @param  array  $payload
+     * @return bool
+     */
+    protected function shouldBroadcast(array $payload)
+    {
+        return isset($payload[0]) &&
+               $payload[0] instanceof ShouldBroadcast &&
+               $this->broadcastWhen($payload[0]);
+    }
+
+    /**
+     * Check if the event should be broadcasted by the condition.
+     *
+     * @param  mixed  $event
+     * @return bool
+     */
+    protected function broadcastWhen($event)
+    {
+        return method_exists($event, 'broadcastWhen')
+                ? $event->broadcastWhen() : true;
+    }
+
+    /**
+     * Broadcast the given event class.
+     *
+     * @param  \Illuminate\Contracts\Broadcasting\ShouldBroadcast  $event
+     * @return void
+     */
+    protected function broadcastEvent($event)
+    {
+        $this->container->make(BroadcastFactory::class)->queue($event);
+    }
+
+    /**
+     * Get all of the listeners for a given event name.
+     *
+     * @param  string  $eventName
+     * @return array
+     */
+    public function getListeners($eventName)
+    {
+        $listeners = array_merge(
+            $this->prepareListeners($eventName),
+            $this->wildcardsCache[$eventName] ?? $this->getWildcardListeners($eventName)
+        );
+
+        return class_exists($eventName, false)
+                    ? $this->addInterfaceListeners($eventName, $listeners)
+                    : $listeners;
+    }
+
+    /**
+     * Get the wildcard listeners for the event.
+     *
+     * @param  string  $eventName
+     * @return array
+     */
+    protected function getWildcardListeners($eventName)
+    {
+        $wildcards = [];
+
+        foreach ($this->wildcards as $key => $listeners) {
+            if (Str::is($key, $eventName)) {
+                foreach ($listeners as $listener) {
+                    $wildcards[] = $this->makeListener($listener, true);
+                }
+            }
+        }
+
+        return $this->wildcardsCache[$eventName] = $wildcards;
+    }
+
+    /**
+     * Add the listeners for the event's interfaces to the given array.
+     *
+     * @param  string  $eventName
+     * @param  array  $listeners
+     * @return array
+     */
+    protected function addInterfaceListeners($eventName, array $listeners = [])
+    {
+        foreach (class_implements($eventName) as $interface) {
+            if (isset($this->listeners[$interface])) {
+                foreach ($this->prepareListeners($interface) as $names) {
+                    $listeners = array_merge($listeners, (array) $names);
+                }
+            }
+        }
+
+        return $listeners;
+    }
+
+    /**
+     * Prepare the listeners for a given event.
+     *
+     * @param  string  $eventName
+     * @return \Closure[]
+     */
+    protected function prepareListeners(string $eventName)
+    {
+        $listeners = [];
+
+        foreach ($this->listeners[$eventName] ?? [] as $listener) {
+            $listeners[] = $this->makeListener($listener);
+        }
+
+        return $listeners;
+    }
+
+    /**
+     * Register an event listener with the dispatcher.
+     *
+     * @param  \Closure|string|array  $listener
+     * @param  bool  $wildcard
+     * @return \Closure
+     */
+    public function makeListener($listener, $wildcard = false)
+    {
+        if (is_string($listener)) {
+            return $this->createClassListener($listener, $wildcard);
+        }
+
+        if (is_array($listener) && isset($listener[0]) && is_string($listener[0])) {
+            return $this->createClassListener($listener, $wildcard);
+        }
+
+        return function ($event, $payload) use ($listener, $wildcard) {
+            if ($wildcard) {
+                return $listener($event, $payload);
+            }
+
+            return $listener(...array_values($payload));
+        };
+    }
+
+    /**
+     * Create a class based listener using the IoC container.
+     *
+     * @param  string  $listener
+     * @param  bool  $wildcard
+     * @return \Closure
+     */
+    public function createClassListener($listener, $wildcard = false)
+    {
+        return function ($event, $payload) use ($listener, $wildcard) {
+            if ($wildcard) {
+                return call_user_func($this->createClassCallable($listener), $event, $payload);
+            }
+
+            $callable = $this->createClassCallable($listener);
+
+            return $callable(...array_values($payload));
+        };
+    }
+
+    /**
+     * Create the class based event callable.
+     *
+     * @param  array|string  $listener
+     * @return callable
+     */
+    protected function createClassCallable($listener)
+    {
+        [$class, $method] = is_array($listener)
+                            ? $listener
+                            : $this->parseClassCallable($listener);
+
+        if (! method_exists($class, $method)) {
+            $method = '__invoke';
+        }
+
+        if ($this->handlerShouldBeQueued($class)) {
+            return $this->createQueuedHandlerCallable($class, $method);
+        }
+
+        $listener = $this->container->make($class);
+
+        return $this->handlerShouldBeDispatchedAfterDatabaseTransactions($listener)
+                    ? $this->createCallbackForListenerRunningAfterCommits($listener, $method)
+                    : [$listener, $method];
+    }
+
+    /**
+     * Parse the class listener into class and method.
+     *
+     * @param  string  $listener
+     * @return array
+     */
+    protected function parseClassCallable($listener)
+    {
+        return Str::parseCallback($listener, 'handle');
+    }
+
+    /**
+     * Determine if the event handler class should be queued.
+     *
+     * @param  string  $class
+     * @return bool
+     */
+    protected function handlerShouldBeQueued($class)
+    {
+        try {
+            return (new ReflectionClass($class))->implementsInterface(
+                ShouldQueue::class
+            );
+        } catch (Exception $e) {
+            return false;
+        }
+    }
+
+    /**
+     * Create a callable for putting an event handler on the queue.
+     *
+     * @param  string  $class
+     * @param  string  $method
+     * @return \Closure
+     */
+    protected function createQueuedHandlerCallable($class, $method)
+    {
+        return function () use ($class, $method) {
+            $arguments = array_map(function ($a) {
+                return is_object($a) ? clone $a : $a;
+            }, func_get_args());
+
+            if ($this->handlerWantsToBeQueued($class, $arguments)) {
+                $this->queueHandler($class, $method, $arguments);
+            }
+        };
+    }
+
+    /**
+     * Determine if the given event handler should be dispatched after all database transactions have committed.
+     *
+     * @param  object|mixed  $listener
+     * @return bool
+     */
+    protected function handlerShouldBeDispatchedAfterDatabaseTransactions($listener)
+    {
+        return ($listener->afterCommit ?? null) && $this->container->bound('db.transactions');
+    }
+
+    /**
+     * Create a callable for dispatching a listener after database transactions.
+     *
+     * @param  mixed  $listener
+     * @param  string  $method
+     * @return \Closure
+     */
+    protected function createCallbackForListenerRunningAfterCommits($listener, $method)
+    {
+        return function () use ($method, $listener) {
+            $payload = func_get_args();
+
+            $this->container->make('db.transactions')->addCallback(
+                function () use ($listener, $method, $payload) {
+                    $listener->$method(...$payload);
+                }
+            );
+        };
+    }
+
+    /**
+     * Determine if the event handler wants to be queued.
+     *
+     * @param  string  $class
+     * @param  array  $arguments
+     * @return bool
+     */
+    protected function handlerWantsToBeQueued($class, $arguments)
+    {
+        $instance = $this->container->make($class);
+
+        if (method_exists($instance, 'shouldQueue')) {
+            return $instance->shouldQueue($arguments[0]);
+        }
+
+        return true;
+    }
+
+    /**
+     * Queue the handler class.
+     *
+     * @param  string  $class
+     * @param  string  $method
+     * @param  array  $arguments
+     * @return void
+     */
+    protected function queueHandler($class, $method, $arguments)
+    {
+        [$listener, $job] = $this->createListenerAndJob($class, $method, $arguments);
+
+        $connection = $this->resolveQueue()->connection(method_exists($listener, 'viaConnection')
+                    ? (isset($arguments[0]) ? $listener->viaConnection($arguments[0]) : $listener->viaConnection())
+                    : $listener->connection ?? null);
+
+        $queue = method_exists($listener, 'viaQueue')
+                    ? (isset($arguments[0]) ? $listener->viaQueue($arguments[0]) : $listener->viaQueue())
+                    : $listener->queue ?? null;
+
+        isset($listener->delay)
+                    ? $connection->laterOn($queue, $listener->delay, $job)
+                    : $connection->pushOn($queue, $job);
+    }
+
+    /**
+     * Create the listener and job for a queued listener.
+     *
+     * @param  string  $class
+     * @param  string  $method
+     * @param  array  $arguments
+     * @return array
+     */
+    protected function createListenerAndJob($class, $method, $arguments)
+    {
+        $listener = (new ReflectionClass($class))->newInstanceWithoutConstructor();
+
+        return [$listener, $this->propagateListenerOptions(
+            $listener, new CallQueuedListener($class, $method, $arguments)
+        )];
+    }
+
+    /**
+     * Propagate listener options to the job.
+     *
+     * @param  mixed  $listener
+     * @param  \Illuminate\Events\CallQueuedListener  $job
+     * @return mixed
+     */
+    protected function propagateListenerOptions($listener, $job)
+    {
+        return tap($job, function ($job) use ($listener) {
+            $data = array_values($job->data);
+
+            $job->afterCommit = property_exists($listener, 'afterCommit') ? $listener->afterCommit : null;
+            $job->backoff = method_exists($listener, 'backoff') ? $listener->backoff(...$data) : ($listener->backoff ?? null);
+            $job->maxExceptions = $listener->maxExceptions ?? null;
+            $job->retryUntil = method_exists($listener, 'retryUntil') ? $listener->retryUntil(...$data) : null;
+            $job->shouldBeEncrypted = $listener instanceof ShouldBeEncrypted;
+            $job->timeout = $listener->timeout ?? null;
+            $job->tries = $listener->tries ?? null;
+
+            $job->through(array_merge(
+                method_exists($listener, 'middleware') ? $listener->middleware(...$data) : [],
+                $listener->middleware ?? []
+            ));
+        });
+    }
+
+    /**
+     * Remove a set of listeners from the dispatcher.
+     *
+     * @param  string  $event
+     * @return void
+     */
+    public function forget($event)
+    {
+        if (str_contains($event, '*')) {
+            unset($this->wildcards[$event]);
+        } else {
+            unset($this->listeners[$event]);
+        }
+
+        foreach ($this->wildcardsCache as $key => $listeners) {
+            if (Str::is($event, $key)) {
+                unset($this->wildcardsCache[$key]);
+            }
+        }
+    }
+
+    /**
+     * Forget all of the pushed listeners.
+     *
+     * @return void
+     */
+    public function forgetPushed()
+    {
+        foreach ($this->listeners as $key => $value) {
+            if (str_ends_with($key, '_pushed')) {
+                $this->forget($key);
+            }
+        }
+    }
+
+    /**
+     * Get the queue implementation from the resolver.
+     *
+     * @return \Illuminate\Contracts\Queue\Queue
+     */
+    protected function resolveQueue()
+    {
+        return call_user_func($this->queueResolver);
+    }
+
+    /**
+     * Set the queue resolver implementation.
+     *
+     * @param  callable  $resolver
+     * @return $this
+     */
+    public function setQueueResolver(callable $resolver)
+    {
+        $this->queueResolver = $resolver;
+
+        return $this;
+    }
+
+    /**
+     * Gets the raw, unprepared listeners.
+     *
+     * @return array
+     */
+    public function getRawListeners()
+    {
+        return $this->listeners;
+    }
+}
diff --git a/vendor/illuminate/events/EventServiceProvider.php b/vendor/illuminate/events/EventServiceProvider.php
new file mode 100755
index 0000000..15fb60b
--- /dev/null
+++ b/vendor/illuminate/events/EventServiceProvider.php
@@ -0,0 +1,23 @@
+app->singleton('events', function ($app) {
+            return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
+                return $app->make(QueueFactoryContract::class);
+            });
+        });
+    }
+}
diff --git a/vendor/illuminate/events/InvokeQueuedClosure.php b/vendor/illuminate/events/InvokeQueuedClosure.php
new file mode 100644
index 0000000..bc68b19
--- /dev/null
+++ b/vendor/illuminate/events/InvokeQueuedClosure.php
@@ -0,0 +1,34 @@
+getClosure(), ...$arguments);
+    }
+
+    /**
+     * Handle a job failure.
+     *
+     * @param  \Laravel\SerializableClosure\SerializableClosure  $closure
+     * @param  array  $arguments
+     * @param  array  $catchCallbacks
+     * @param  \Throwable  $exception
+     * @return void
+     */
+    public function failed($closure, array $arguments, array $catchCallbacks, $exception)
+    {
+        $arguments[] = $exception;
+
+        collect($catchCallbacks)->each->__invoke(...$arguments);
+    }
+}
diff --git a/vendor/illuminate/events/LICENSE.md b/vendor/illuminate/events/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/illuminate/events/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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/illuminate/events/NullDispatcher.php b/vendor/illuminate/events/NullDispatcher.php
new file mode 100644
index 0000000..3164fbb
--- /dev/null
+++ b/vendor/illuminate/events/NullDispatcher.php
@@ -0,0 +1,144 @@
+dispatcher = $dispatcher;
+    }
+
+    /**
+     * Don't fire an event.
+     *
+     * @param  string|object  $event
+     * @param  mixed  $payload
+     * @param  bool  $halt
+     * @return void
+     */
+    public function dispatch($event, $payload = [], $halt = false)
+    {
+        //
+    }
+
+    /**
+     * Don't register an event and payload to be fired later.
+     *
+     * @param  string  $event
+     * @param  array  $payload
+     * @return void
+     */
+    public function push($event, $payload = [])
+    {
+        //
+    }
+
+    /**
+     * Don't dispatch an event.
+     *
+     * @param  string|object  $event
+     * @param  mixed  $payload
+     * @return array|null
+     */
+    public function until($event, $payload = [])
+    {
+        //
+    }
+
+    /**
+     * Register an event listener with the dispatcher.
+     *
+     * @param  \Closure|string|array  $events
+     * @param  \Closure|string|array|null  $listener
+     * @return void
+     */
+    public function listen($events, $listener = null)
+    {
+        $this->dispatcher->listen($events, $listener);
+    }
+
+    /**
+     * Determine if a given event has listeners.
+     *
+     * @param  string  $eventName
+     * @return bool
+     */
+    public function hasListeners($eventName)
+    {
+        return $this->dispatcher->hasListeners($eventName);
+    }
+
+    /**
+     * Register an event subscriber with the dispatcher.
+     *
+     * @param  object|string  $subscriber
+     * @return void
+     */
+    public function subscribe($subscriber)
+    {
+        $this->dispatcher->subscribe($subscriber);
+    }
+
+    /**
+     * Flush a set of pushed events.
+     *
+     * @param  string  $event
+     * @return void
+     */
+    public function flush($event)
+    {
+        $this->dispatcher->flush($event);
+    }
+
+    /**
+     * Remove a set of listeners from the dispatcher.
+     *
+     * @param  string  $event
+     * @return void
+     */
+    public function forget($event)
+    {
+        $this->dispatcher->forget($event);
+    }
+
+    /**
+     * Forget all of the queued listeners.
+     *
+     * @return void
+     */
+    public function forgetPushed()
+    {
+        $this->dispatcher->forgetPushed();
+    }
+
+    /**
+     * Dynamically pass method calls to the underlying dispatcher.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->forwardDecoratedCallTo($this->dispatcher, $method, $parameters);
+    }
+}
diff --git a/vendor/illuminate/events/QueuedClosure.php b/vendor/illuminate/events/QueuedClosure.php
new file mode 100644
index 0000000..31a462a
--- /dev/null
+++ b/vendor/illuminate/events/QueuedClosure.php
@@ -0,0 +1,125 @@
+closure = $closure;
+    }
+
+    /**
+     * Set the desired connection for the job.
+     *
+     * @param  string|null  $connection
+     * @return $this
+     */
+    public function onConnection($connection)
+    {
+        $this->connection = $connection;
+
+        return $this;
+    }
+
+    /**
+     * Set the desired queue for the job.
+     *
+     * @param  string|null  $queue
+     * @return $this
+     */
+    public function onQueue($queue)
+    {
+        $this->queue = $queue;
+
+        return $this;
+    }
+
+    /**
+     * Set the desired delay in seconds for the job.
+     *
+     * @param  \DateTimeInterface|\DateInterval|int|null  $delay
+     * @return $this
+     */
+    public function delay($delay)
+    {
+        $this->delay = $delay;
+
+        return $this;
+    }
+
+    /**
+     * Specify a callback that should be invoked if the queued listener job fails.
+     *
+     * @param  \Closure  $closure
+     * @return $this
+     */
+    public function catch(Closure $closure)
+    {
+        $this->catchCallbacks[] = $closure;
+
+        return $this;
+    }
+
+    /**
+     * Resolve the actual event listener callback.
+     *
+     * @return \Closure
+     */
+    public function resolve()
+    {
+        return function (...$arguments) {
+            dispatch(new CallQueuedListener(InvokeQueuedClosure::class, 'handle', [
+                'closure' => new SerializableClosure($this->closure),
+                'arguments' => $arguments,
+                'catch' => collect($this->catchCallbacks)->map(function ($callback) {
+                    return new SerializableClosure($callback);
+                })->all(),
+            ]))->onConnection($this->connection)->onQueue($this->queue)->delay($this->delay);
+        };
+    }
+}
diff --git a/vendor/illuminate/events/composer.json b/vendor/illuminate/events/composer.json
new file mode 100755
index 0000000..d074212
--- /dev/null
+++ b/vendor/illuminate/events/composer.json
@@ -0,0 +1,42 @@
+{
+    "name": "illuminate/events",
+    "description": "The Illuminate Events package.",
+    "license": "MIT",
+    "homepage": "https://laravel.com",
+    "support": {
+        "issues": "https://github.com/laravel/framework/issues",
+        "source": "https://github.com/laravel/framework"
+    },
+    "authors": [
+        {
+            "name": "Taylor Otwell",
+            "email": "taylor@laravel.com"
+        }
+    ],
+    "require": {
+        "php": "^8.0.2",
+        "illuminate/bus": "^9.0",
+        "illuminate/collections": "^9.0",
+        "illuminate/container": "^9.0",
+        "illuminate/contracts": "^9.0",
+        "illuminate/macroable": "^9.0",
+        "illuminate/support": "^9.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Illuminate\\Events\\": ""
+        },
+        "files": [
+            "functions.php"
+        ]
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "9.x-dev"
+        }
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/events/functions.php b/vendor/illuminate/events/functions.php
new file mode 100644
index 0000000..df1b0fe
--- /dev/null
+++ b/vendor/illuminate/events/functions.php
@@ -0,0 +1,18 @@
+getMethods(
+            ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
+        );
+
+        foreach ($methods as $method) {
+            if ($replace || ! static::hasMacro($method->name)) {
+                $method->setAccessible(true);
+                static::macro($method->name, $method->invoke($mixin));
+            }
+        }
+    }
+
+    /**
+     * Checks if macro is registered.
+     *
+     * @param  string  $name
+     * @return bool
+     */
+    public static function hasMacro($name)
+    {
+        return isset(static::$macros[$name]);
+    }
+
+    /**
+     * Flush the existing macros.
+     *
+     * @return void
+     */
+    public static function flushMacros()
+    {
+        static::$macros = [];
+    }
+
+    /**
+     * Dynamically handle calls to the class.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     *
+     * @throws \BadMethodCallException
+     */
+    public static function __callStatic($method, $parameters)
+    {
+        if (! static::hasMacro($method)) {
+            throw new BadMethodCallException(sprintf(
+                'Method %s::%s does not exist.', static::class, $method
+            ));
+        }
+
+        $macro = static::$macros[$method];
+
+        if ($macro instanceof Closure) {
+            $macro = $macro->bindTo(null, static::class);
+        }
+
+        return $macro(...$parameters);
+    }
+
+    /**
+     * Dynamically handle calls to the class.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     *
+     * @throws \BadMethodCallException
+     */
+    public function __call($method, $parameters)
+    {
+        if (! static::hasMacro($method)) {
+            throw new BadMethodCallException(sprintf(
+                'Method %s::%s does not exist.', static::class, $method
+            ));
+        }
+
+        $macro = static::$macros[$method];
+
+        if ($macro instanceof Closure) {
+            $macro = $macro->bindTo($this, static::class);
+        }
+
+        return $macro(...$parameters);
+    }
+}
diff --git a/vendor/illuminate/macroable/composer.json b/vendor/illuminate/macroable/composer.json
new file mode 100644
index 0000000..0417dbe
--- /dev/null
+++ b/vendor/illuminate/macroable/composer.json
@@ -0,0 +1,33 @@
+{
+    "name": "illuminate/macroable",
+    "description": "The Illuminate Macroable package.",
+    "license": "MIT",
+    "homepage": "https://laravel.com",
+    "support": {
+        "issues": "https://github.com/laravel/framework/issues",
+        "source": "https://github.com/laravel/framework"
+    },
+    "authors": [
+        {
+            "name": "Taylor Otwell",
+            "email": "taylor@laravel.com"
+        }
+    ],
+    "require": {
+        "php": "^8.0.2"
+    },
+    "autoload": {
+        "psr-4": {
+            "Illuminate\\Support\\": ""
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "9.x-dev"
+        }
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/pagination/AbstractCursorPaginator.php b/vendor/illuminate/pagination/AbstractCursorPaginator.php
new file mode 100644
index 0000000..e670078
--- /dev/null
+++ b/vendor/illuminate/pagination/AbstractCursorPaginator.php
@@ -0,0 +1,671 @@
+cursorName => $cursor->encode()];
+
+        if (count($this->query) > 0) {
+            $parameters = array_merge($this->query, $parameters);
+        }
+
+        return $this->path()
+            .(str_contains($this->path(), '?') ? '&' : '?')
+            .Arr::query($parameters)
+            .$this->buildFragment();
+    }
+
+    /**
+     * Get the URL for the previous page.
+     *
+     * @return string|null
+     */
+    public function previousPageUrl()
+    {
+        if (is_null($previousCursor = $this->previousCursor())) {
+            return null;
+        }
+
+        return $this->url($previousCursor);
+    }
+
+    /**
+     * The URL for the next page, or null.
+     *
+     * @return string|null
+     */
+    public function nextPageUrl()
+    {
+        if (is_null($nextCursor = $this->nextCursor())) {
+            return null;
+        }
+
+        return $this->url($nextCursor);
+    }
+
+    /**
+     * Get the "cursor" that points to the previous set of items.
+     *
+     * @return \Illuminate\Pagination\Cursor|null
+     */
+    public function previousCursor()
+    {
+        if (is_null($this->cursor) ||
+            ($this->cursor->pointsToPreviousItems() && ! $this->hasMore)) {
+            return null;
+        }
+
+        if ($this->items->isEmpty()) {
+            return null;
+        }
+
+        return $this->getCursorForItem($this->items->first(), false);
+    }
+
+    /**
+     * Get the "cursor" that points to the next set of items.
+     *
+     * @return \Illuminate\Pagination\Cursor|null
+     */
+    public function nextCursor()
+    {
+        if ((is_null($this->cursor) && ! $this->hasMore) ||
+            (! is_null($this->cursor) && $this->cursor->pointsToNextItems() && ! $this->hasMore)) {
+            return null;
+        }
+
+        if ($this->items->isEmpty()) {
+            return null;
+        }
+
+        return $this->getCursorForItem($this->items->last(), true);
+    }
+
+    /**
+     * Get a cursor instance for the given item.
+     *
+     * @param  \ArrayAccess|\stdClass  $item
+     * @param  bool  $isNext
+     * @return \Illuminate\Pagination\Cursor
+     */
+    public function getCursorForItem($item, $isNext = true)
+    {
+        return new Cursor($this->getParametersForItem($item), $isNext);
+    }
+
+    /**
+     * Get the cursor parameters for a given object.
+     *
+     * @param  \ArrayAccess|\stdClass  $item
+     * @return array
+     *
+     * @throws \Exception
+     */
+    public function getParametersForItem($item)
+    {
+        return collect($this->parameters)
+            ->flip()
+            ->map(function ($_, $parameterName) use ($item) {
+                if ($item instanceof JsonResource) {
+                    $item = $item->resource;
+                }
+
+                if ($item instanceof Model &&
+                    ! is_null($parameter = $this->getPivotParameterForItem($item, $parameterName))) {
+                    return $parameter;
+                } elseif ($item instanceof ArrayAccess || is_array($item)) {
+                    return $this->ensureParameterIsPrimitive(
+                        $item[$parameterName] ?? $item[Str::afterLast($parameterName, '.')]
+                    );
+                } elseif (is_object($item)) {
+                    return $this->ensureParameterIsPrimitive(
+                        $item->{$parameterName} ?? $item->{Str::afterLast($parameterName, '.')}
+                    );
+                }
+
+                throw new Exception('Only arrays and objects are supported when cursor paginating items.');
+            })->toArray();
+    }
+
+    /**
+     * Get the cursor parameter value from a pivot model if applicable.
+     *
+     * @param  \ArrayAccess|\stdClass  $item
+     * @param  string  $parameterName
+     * @return string|null
+     */
+    protected function getPivotParameterForItem($item, $parameterName)
+    {
+        $table = Str::beforeLast($parameterName, '.');
+
+        foreach ($item->getRelations() as $relation) {
+            if ($relation instanceof Pivot && $relation->getTable() === $table) {
+                return $this->ensureParameterIsPrimitive(
+                    $relation->getAttribute(Str::afterLast($parameterName, '.'))
+                );
+            }
+        }
+    }
+
+    /**
+     * Ensure the parameter is a primitive type.
+     *
+     * This can resolve issues that arise the developer uses a value object for an attribute.
+     *
+     * @param  mixed  $parameter
+     * @return mixed
+     */
+    protected function ensureParameterIsPrimitive($parameter)
+    {
+        return is_object($parameter) && method_exists($parameter, '__toString')
+                        ? (string) $parameter
+                        : $parameter;
+    }
+
+    /**
+     * Get / set the URL fragment to be appended to URLs.
+     *
+     * @param  string|null  $fragment
+     * @return $this|string|null
+     */
+    public function fragment($fragment = null)
+    {
+        if (is_null($fragment)) {
+            return $this->fragment;
+        }
+
+        $this->fragment = $fragment;
+
+        return $this;
+    }
+
+    /**
+     * Add a set of query string values to the paginator.
+     *
+     * @param  array|string|null  $key
+     * @param  string|null  $value
+     * @return $this
+     */
+    public function appends($key, $value = null)
+    {
+        if (is_null($key)) {
+            return $this;
+        }
+
+        if (is_array($key)) {
+            return $this->appendArray($key);
+        }
+
+        return $this->addQuery($key, $value);
+    }
+
+    /**
+     * Add an array of query string values.
+     *
+     * @param  array  $keys
+     * @return $this
+     */
+    protected function appendArray(array $keys)
+    {
+        foreach ($keys as $key => $value) {
+            $this->addQuery($key, $value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add all current query string values to the paginator.
+     *
+     * @return $this
+     */
+    public function withQueryString()
+    {
+        if (! is_null($query = Paginator::resolveQueryString())) {
+            return $this->appends($query);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a query string value to the paginator.
+     *
+     * @param  string  $key
+     * @param  string  $value
+     * @return $this
+     */
+    protected function addQuery($key, $value)
+    {
+        if ($key !== $this->cursorName) {
+            $this->query[$key] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Build the full fragment portion of a URL.
+     *
+     * @return string
+     */
+    protected function buildFragment()
+    {
+        return $this->fragment ? '#'.$this->fragment : '';
+    }
+
+    /**
+     * Load a set of relationships onto the mixed relationship collection.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @return $this
+     */
+    public function loadMorph($relation, $relations)
+    {
+        $this->getCollection()->loadMorph($relation, $relations);
+
+        return $this;
+    }
+
+    /**
+     * Load a set of relationship counts onto the mixed relationship collection.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @return $this
+     */
+    public function loadMorphCount($relation, $relations)
+    {
+        $this->getCollection()->loadMorphCount($relation, $relations);
+
+        return $this;
+    }
+
+    /**
+     * Get the slice of items being paginated.
+     *
+     * @return array
+     */
+    public function items()
+    {
+        return $this->items->all();
+    }
+
+    /**
+     * Transform each item in the slice of items using a callback.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function through(callable $callback)
+    {
+        $this->items->transform($callback);
+
+        return $this;
+    }
+
+    /**
+     * Get the number of items shown per page.
+     *
+     * @return int
+     */
+    public function perPage()
+    {
+        return $this->perPage;
+    }
+
+    /**
+     * Get the current cursor being paginated.
+     *
+     * @return \Illuminate\Pagination\Cursor|null
+     */
+    public function cursor()
+    {
+        return $this->cursor;
+    }
+
+    /**
+     * Get the query string variable used to store the cursor.
+     *
+     * @return string
+     */
+    public function getCursorName()
+    {
+        return $this->cursorName;
+    }
+
+    /**
+     * Set the query string variable used to store the cursor.
+     *
+     * @param  string  $name
+     * @return $this
+     */
+    public function setCursorName($name)
+    {
+        $this->cursorName = $name;
+
+        return $this;
+    }
+
+    /**
+     * Set the base path to assign to all URLs.
+     *
+     * @param  string  $path
+     * @return $this
+     */
+    public function withPath($path)
+    {
+        return $this->setPath($path);
+    }
+
+    /**
+     * Set the base path to assign to all URLs.
+     *
+     * @param  string  $path
+     * @return $this
+     */
+    public function setPath($path)
+    {
+        $this->path = $path;
+
+        return $this;
+    }
+
+    /**
+     * Get the base path for paginator generated URLs.
+     *
+     * @return string|null
+     */
+    public function path()
+    {
+        return $this->path;
+    }
+
+    /**
+     * Resolve the current cursor or return the default value.
+     *
+     * @param  string  $cursorName
+     * @return \Illuminate\Pagination\Cursor|null
+     */
+    public static function resolveCurrentCursor($cursorName = 'cursor', $default = null)
+    {
+        if (isset(static::$currentCursorResolver)) {
+            return call_user_func(static::$currentCursorResolver, $cursorName);
+        }
+
+        return $default;
+    }
+
+    /**
+     * Set the current cursor resolver callback.
+     *
+     * @param  \Closure  $resolver
+     * @return void
+     */
+    public static function currentCursorResolver(Closure $resolver)
+    {
+        static::$currentCursorResolver = $resolver;
+    }
+
+    /**
+     * Get an instance of the view factory from the resolver.
+     *
+     * @return \Illuminate\Contracts\View\Factory
+     */
+    public static function viewFactory()
+    {
+        return Paginator::viewFactory();
+    }
+
+    /**
+     * Get an iterator for the items.
+     *
+     * @return \ArrayIterator
+     */
+    public function getIterator(): Traversable
+    {
+        return $this->items->getIterator();
+    }
+
+    /**
+     * Determine if the list of items is empty.
+     *
+     * @return bool
+     */
+    public function isEmpty()
+    {
+        return $this->items->isEmpty();
+    }
+
+    /**
+     * Determine if the list of items is not empty.
+     *
+     * @return bool
+     */
+    public function isNotEmpty()
+    {
+        return $this->items->isNotEmpty();
+    }
+
+    /**
+     * Get the number of items for the current page.
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        return $this->items->count();
+    }
+
+    /**
+     * Get the paginator's underlying collection.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function getCollection()
+    {
+        return $this->items;
+    }
+
+    /**
+     * Set the paginator's underlying collection.
+     *
+     * @param  \Illuminate\Support\Collection  $collection
+     * @return $this
+     */
+    public function setCollection(Collection $collection)
+    {
+        $this->items = $collection;
+
+        return $this;
+    }
+
+    /**
+     * Get the paginator options.
+     *
+     * @return array
+     */
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * Determine if the given item exists.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function offsetExists($key): bool
+    {
+        return $this->items->has($key);
+    }
+
+    /**
+     * Get the item at the given offset.
+     *
+     * @param  mixed  $key
+     * @return mixed
+     */
+    public function offsetGet($key): mixed
+    {
+        return $this->items->get($key);
+    }
+
+    /**
+     * Set the item at the given offset.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $value
+     * @return void
+     */
+    public function offsetSet($key, $value): void
+    {
+        $this->items->put($key, $value);
+    }
+
+    /**
+     * Unset the item at the given key.
+     *
+     * @param  mixed  $key
+     * @return void
+     */
+    public function offsetUnset($key): void
+    {
+        $this->items->forget($key);
+    }
+
+    /**
+     * Render the contents of the paginator to HTML.
+     *
+     * @return string
+     */
+    public function toHtml()
+    {
+        return (string) $this->render();
+    }
+
+    /**
+     * Make dynamic calls into the collection.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->forwardCallTo($this->getCollection(), $method, $parameters);
+    }
+
+    /**
+     * Render the contents of the paginator when casting to a string.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return (string) $this->render();
+    }
+}
diff --git a/vendor/illuminate/pagination/AbstractPaginator.php b/vendor/illuminate/pagination/AbstractPaginator.php
new file mode 100644
index 0000000..a497984
--- /dev/null
+++ b/vendor/illuminate/pagination/AbstractPaginator.php
@@ -0,0 +1,797 @@
+= 1 && filter_var($page, FILTER_VALIDATE_INT) !== false;
+    }
+
+    /**
+     * Get the URL for the previous page.
+     *
+     * @return string|null
+     */
+    public function previousPageUrl()
+    {
+        if ($this->currentPage() > 1) {
+            return $this->url($this->currentPage() - 1);
+        }
+    }
+
+    /**
+     * Create a range of pagination URLs.
+     *
+     * @param  int  $start
+     * @param  int  $end
+     * @return array
+     */
+    public function getUrlRange($start, $end)
+    {
+        return collect(range($start, $end))->mapWithKeys(function ($page) {
+            return [$page => $this->url($page)];
+        })->all();
+    }
+
+    /**
+     * Get the URL for a given page number.
+     *
+     * @param  int  $page
+     * @return string
+     */
+    public function url($page)
+    {
+        if ($page <= 0) {
+            $page = 1;
+        }
+
+        // If we have any extra query string key / value pairs that need to be added
+        // onto the URL, we will put them in query string form and then attach it
+        // to the URL. This allows for extra information like sortings storage.
+        $parameters = [$this->pageName => $page];
+
+        if (count($this->query) > 0) {
+            $parameters = array_merge($this->query, $parameters);
+        }
+
+        return $this->path()
+                        .(str_contains($this->path(), '?') ? '&' : '?')
+                        .Arr::query($parameters)
+                        .$this->buildFragment();
+    }
+
+    /**
+     * Get / set the URL fragment to be appended to URLs.
+     *
+     * @param  string|null  $fragment
+     * @return $this|string|null
+     */
+    public function fragment($fragment = null)
+    {
+        if (is_null($fragment)) {
+            return $this->fragment;
+        }
+
+        $this->fragment = $fragment;
+
+        return $this;
+    }
+
+    /**
+     * Add a set of query string values to the paginator.
+     *
+     * @param  array|string|null  $key
+     * @param  string|null  $value
+     * @return $this
+     */
+    public function appends($key, $value = null)
+    {
+        if (is_null($key)) {
+            return $this;
+        }
+
+        if (is_array($key)) {
+            return $this->appendArray($key);
+        }
+
+        return $this->addQuery($key, $value);
+    }
+
+    /**
+     * Add an array of query string values.
+     *
+     * @param  array  $keys
+     * @return $this
+     */
+    protected function appendArray(array $keys)
+    {
+        foreach ($keys as $key => $value) {
+            $this->addQuery($key, $value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add all current query string values to the paginator.
+     *
+     * @return $this
+     */
+    public function withQueryString()
+    {
+        if (isset(static::$queryStringResolver)) {
+            return $this->appends(call_user_func(static::$queryStringResolver));
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a query string value to the paginator.
+     *
+     * @param  string  $key
+     * @param  string  $value
+     * @return $this
+     */
+    protected function addQuery($key, $value)
+    {
+        if ($key !== $this->pageName) {
+            $this->query[$key] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Build the full fragment portion of a URL.
+     *
+     * @return string
+     */
+    protected function buildFragment()
+    {
+        return $this->fragment ? '#'.$this->fragment : '';
+    }
+
+    /**
+     * Load a set of relationships onto the mixed relationship collection.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @return $this
+     */
+    public function loadMorph($relation, $relations)
+    {
+        $this->getCollection()->loadMorph($relation, $relations);
+
+        return $this;
+    }
+
+    /**
+     * Load a set of relationship counts onto the mixed relationship collection.
+     *
+     * @param  string  $relation
+     * @param  array  $relations
+     * @return $this
+     */
+    public function loadMorphCount($relation, $relations)
+    {
+        $this->getCollection()->loadMorphCount($relation, $relations);
+
+        return $this;
+    }
+
+    /**
+     * Get the slice of items being paginated.
+     *
+     * @return array
+     */
+    public function items()
+    {
+        return $this->items->all();
+    }
+
+    /**
+     * Get the number of the first item in the slice.
+     *
+     * @return int
+     */
+    public function firstItem()
+    {
+        return count($this->items) > 0 ? ($this->currentPage - 1) * $this->perPage + 1 : null;
+    }
+
+    /**
+     * Get the number of the last item in the slice.
+     *
+     * @return int
+     */
+    public function lastItem()
+    {
+        return count($this->items) > 0 ? $this->firstItem() + $this->count() - 1 : null;
+    }
+
+    /**
+     * Transform each item in the slice of items using a callback.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function through(callable $callback)
+    {
+        $this->items->transform($callback);
+
+        return $this;
+    }
+
+    /**
+     * Get the number of items shown per page.
+     *
+     * @return int
+     */
+    public function perPage()
+    {
+        return $this->perPage;
+    }
+
+    /**
+     * Determine if there are enough items to split into multiple pages.
+     *
+     * @return bool
+     */
+    public function hasPages()
+    {
+        return $this->currentPage() != 1 || $this->hasMorePages();
+    }
+
+    /**
+     * Determine if the paginator is on the first page.
+     *
+     * @return bool
+     */
+    public function onFirstPage()
+    {
+        return $this->currentPage() <= 1;
+    }
+
+    /**
+     * Determine if the paginator is on the last page.
+     *
+     * @return bool
+     */
+    public function onLastPage()
+    {
+        return ! $this->hasMorePages();
+    }
+
+    /**
+     * Get the current page.
+     *
+     * @return int
+     */
+    public function currentPage()
+    {
+        return $this->currentPage;
+    }
+
+    /**
+     * Get the query string variable used to store the page.
+     *
+     * @return string
+     */
+    public function getPageName()
+    {
+        return $this->pageName;
+    }
+
+    /**
+     * Set the query string variable used to store the page.
+     *
+     * @param  string  $name
+     * @return $this
+     */
+    public function setPageName($name)
+    {
+        $this->pageName = $name;
+
+        return $this;
+    }
+
+    /**
+     * Set the base path to assign to all URLs.
+     *
+     * @param  string  $path
+     * @return $this
+     */
+    public function withPath($path)
+    {
+        return $this->setPath($path);
+    }
+
+    /**
+     * Set the base path to assign to all URLs.
+     *
+     * @param  string  $path
+     * @return $this
+     */
+    public function setPath($path)
+    {
+        $this->path = $path;
+
+        return $this;
+    }
+
+    /**
+     * Set the number of links to display on each side of current page link.
+     *
+     * @param  int  $count
+     * @return $this
+     */
+    public function onEachSide($count)
+    {
+        $this->onEachSide = $count;
+
+        return $this;
+    }
+
+    /**
+     * Get the base path for paginator generated URLs.
+     *
+     * @return string|null
+     */
+    public function path()
+    {
+        return $this->path;
+    }
+
+    /**
+     * Resolve the current request path or return the default value.
+     *
+     * @param  string  $default
+     * @return string
+     */
+    public static function resolveCurrentPath($default = '/')
+    {
+        if (isset(static::$currentPathResolver)) {
+            return call_user_func(static::$currentPathResolver);
+        }
+
+        return $default;
+    }
+
+    /**
+     * Set the current request path resolver callback.
+     *
+     * @param  \Closure  $resolver
+     * @return void
+     */
+    public static function currentPathResolver(Closure $resolver)
+    {
+        static::$currentPathResolver = $resolver;
+    }
+
+    /**
+     * Resolve the current page or return the default value.
+     *
+     * @param  string  $pageName
+     * @param  int  $default
+     * @return int
+     */
+    public static function resolveCurrentPage($pageName = 'page', $default = 1)
+    {
+        if (isset(static::$currentPageResolver)) {
+            return (int) call_user_func(static::$currentPageResolver, $pageName);
+        }
+
+        return $default;
+    }
+
+    /**
+     * Set the current page resolver callback.
+     *
+     * @param  \Closure  $resolver
+     * @return void
+     */
+    public static function currentPageResolver(Closure $resolver)
+    {
+        static::$currentPageResolver = $resolver;
+    }
+
+    /**
+     * Resolve the query string or return the default value.
+     *
+     * @param  string|array|null  $default
+     * @return string
+     */
+    public static function resolveQueryString($default = null)
+    {
+        if (isset(static::$queryStringResolver)) {
+            return (static::$queryStringResolver)();
+        }
+
+        return $default;
+    }
+
+    /**
+     * Set with query string resolver callback.
+     *
+     * @param  \Closure  $resolver
+     * @return void
+     */
+    public static function queryStringResolver(Closure $resolver)
+    {
+        static::$queryStringResolver = $resolver;
+    }
+
+    /**
+     * Get an instance of the view factory from the resolver.
+     *
+     * @return \Illuminate\Contracts\View\Factory
+     */
+    public static function viewFactory()
+    {
+        return call_user_func(static::$viewFactoryResolver);
+    }
+
+    /**
+     * Set the view factory resolver callback.
+     *
+     * @param  \Closure  $resolver
+     * @return void
+     */
+    public static function viewFactoryResolver(Closure $resolver)
+    {
+        static::$viewFactoryResolver = $resolver;
+    }
+
+    /**
+     * Set the default pagination view.
+     *
+     * @param  string  $view
+     * @return void
+     */
+    public static function defaultView($view)
+    {
+        static::$defaultView = $view;
+    }
+
+    /**
+     * Set the default "simple" pagination view.
+     *
+     * @param  string  $view
+     * @return void
+     */
+    public static function defaultSimpleView($view)
+    {
+        static::$defaultSimpleView = $view;
+    }
+
+    /**
+     * Indicate that Tailwind styling should be used for generated links.
+     *
+     * @return void
+     */
+    public static function useTailwind()
+    {
+        static::defaultView('pagination::tailwind');
+        static::defaultSimpleView('pagination::simple-tailwind');
+    }
+
+    /**
+     * Indicate that Bootstrap 4 styling should be used for generated links.
+     *
+     * @return void
+     */
+    public static function useBootstrap()
+    {
+        static::useBootstrapFour();
+    }
+
+    /**
+     * Indicate that Bootstrap 3 styling should be used for generated links.
+     *
+     * @return void
+     */
+    public static function useBootstrapThree()
+    {
+        static::defaultView('pagination::default');
+        static::defaultSimpleView('pagination::simple-default');
+    }
+
+    /**
+     * Indicate that Bootstrap 4 styling should be used for generated links.
+     *
+     * @return void
+     */
+    public static function useBootstrapFour()
+    {
+        static::defaultView('pagination::bootstrap-4');
+        static::defaultSimpleView('pagination::simple-bootstrap-4');
+    }
+
+    /**
+     * Indicate that Bootstrap 5 styling should be used for generated links.
+     *
+     * @return void
+     */
+    public static function useBootstrapFive()
+    {
+        static::defaultView('pagination::bootstrap-5');
+        static::defaultSimpleView('pagination::simple-bootstrap-5');
+    }
+
+    /**
+     * Get an iterator for the items.
+     *
+     * @return \ArrayIterator
+     */
+    public function getIterator(): Traversable
+    {
+        return $this->items->getIterator();
+    }
+
+    /**
+     * Determine if the list of items is empty.
+     *
+     * @return bool
+     */
+    public function isEmpty()
+    {
+        return $this->items->isEmpty();
+    }
+
+    /**
+     * Determine if the list of items is not empty.
+     *
+     * @return bool
+     */
+    public function isNotEmpty()
+    {
+        return $this->items->isNotEmpty();
+    }
+
+    /**
+     * Get the number of items for the current page.
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        return $this->items->count();
+    }
+
+    /**
+     * Get the paginator's underlying collection.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function getCollection()
+    {
+        return $this->items;
+    }
+
+    /**
+     * Set the paginator's underlying collection.
+     *
+     * @param  \Illuminate\Support\Collection  $collection
+     * @return $this
+     */
+    public function setCollection(Collection $collection)
+    {
+        $this->items = $collection;
+
+        return $this;
+    }
+
+    /**
+     * Get the paginator options.
+     *
+     * @return array
+     */
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * Determine if the given item exists.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function offsetExists($key): bool
+    {
+        return $this->items->has($key);
+    }
+
+    /**
+     * Get the item at the given offset.
+     *
+     * @param  mixed  $key
+     * @return mixed
+     */
+    public function offsetGet($key): mixed
+    {
+        return $this->items->get($key);
+    }
+
+    /**
+     * Set the item at the given offset.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $value
+     * @return void
+     */
+    public function offsetSet($key, $value): void
+    {
+        $this->items->put($key, $value);
+    }
+
+    /**
+     * Unset the item at the given key.
+     *
+     * @param  mixed  $key
+     * @return void
+     */
+    public function offsetUnset($key): void
+    {
+        $this->items->forget($key);
+    }
+
+    /**
+     * Render the contents of the paginator to HTML.
+     *
+     * @return string
+     */
+    public function toHtml()
+    {
+        return (string) $this->render();
+    }
+
+    /**
+     * Make dynamic calls into the collection.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->forwardCallTo($this->getCollection(), $method, $parameters);
+    }
+
+    /**
+     * Render the contents of the paginator when casting to a string.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return (string) $this->render();
+    }
+}
diff --git a/vendor/illuminate/pagination/Cursor.php b/vendor/illuminate/pagination/Cursor.php
new file mode 100644
index 0000000..1d62280
--- /dev/null
+++ b/vendor/illuminate/pagination/Cursor.php
@@ -0,0 +1,132 @@
+parameters = $parameters;
+        $this->pointsToNextItems = $pointsToNextItems;
+    }
+
+    /**
+     * Get the given parameter from the cursor.
+     *
+     * @param  string  $parameterName
+     * @return string|null
+     *
+     * @throws \UnexpectedValueException
+     */
+    public function parameter(string $parameterName)
+    {
+        if (! array_key_exists($parameterName, $this->parameters)) {
+            throw new UnexpectedValueException("Unable to find parameter [{$parameterName}] in pagination item.");
+        }
+
+        return $this->parameters[$parameterName];
+    }
+
+    /**
+     * Get the given parameters from the cursor.
+     *
+     * @param  array  $parameterNames
+     * @return array
+     */
+    public function parameters(array $parameterNames)
+    {
+        return collect($parameterNames)->map(function ($parameterName) {
+            return $this->parameter($parameterName);
+        })->toArray();
+    }
+
+    /**
+     * Determine whether the cursor points to the next set of items.
+     *
+     * @return bool
+     */
+    public function pointsToNextItems()
+    {
+        return $this->pointsToNextItems;
+    }
+
+    /**
+     * Determine whether the cursor points to the previous set of items.
+     *
+     * @return bool
+     */
+    public function pointsToPreviousItems()
+    {
+        return ! $this->pointsToNextItems;
+    }
+
+    /**
+     * Get the array representation of the cursor.
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return array_merge($this->parameters, [
+            '_pointsToNextItems' => $this->pointsToNextItems,
+        ]);
+    }
+
+    /**
+     * Get the encoded string representation of the cursor to construct a URL.
+     *
+     * @return string
+     */
+    public function encode()
+    {
+        return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode(json_encode($this->toArray())));
+    }
+
+    /**
+     * Get a cursor instance from the encoded string representation.
+     *
+     * @param  string|null  $encodedString
+     * @return static|null
+     */
+    public static function fromEncoded($encodedString)
+    {
+        if (! is_string($encodedString)) {
+            return null;
+        }
+
+        $parameters = json_decode(base64_decode(str_replace(['-', '_'], ['+', '/'], $encodedString)), true);
+
+        if (json_last_error() !== JSON_ERROR_NONE) {
+            return null;
+        }
+
+        $pointsToNextItems = $parameters['_pointsToNextItems'];
+
+        unset($parameters['_pointsToNextItems']);
+
+        return new static($parameters, $pointsToNextItems);
+    }
+}
diff --git a/vendor/illuminate/pagination/CursorPaginator.php b/vendor/illuminate/pagination/CursorPaginator.php
new file mode 100644
index 0000000..4c0a896
--- /dev/null
+++ b/vendor/illuminate/pagination/CursorPaginator.php
@@ -0,0 +1,172 @@
+options = $options;
+
+        foreach ($options as $key => $value) {
+            $this->{$key} = $value;
+        }
+
+        $this->perPage = (int) $perPage;
+        $this->cursor = $cursor;
+        $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path;
+
+        $this->setItems($items);
+    }
+
+    /**
+     * Set the items for the paginator.
+     *
+     * @param  mixed  $items
+     * @return void
+     */
+    protected function setItems($items)
+    {
+        $this->items = $items instanceof Collection ? $items : Collection::make($items);
+
+        $this->hasMore = $this->items->count() > $this->perPage;
+
+        $this->items = $this->items->slice(0, $this->perPage);
+
+        if (! is_null($this->cursor) && $this->cursor->pointsToPreviousItems()) {
+            $this->items = $this->items->reverse()->values();
+        }
+    }
+
+    /**
+     * Render the paginator using the given view.
+     *
+     * @param  string|null  $view
+     * @param  array  $data
+     * @return \Illuminate\Contracts\Support\Htmlable
+     */
+    public function links($view = null, $data = [])
+    {
+        return $this->render($view, $data);
+    }
+
+    /**
+     * Render the paginator using the given view.
+     *
+     * @param  string|null  $view
+     * @param  array  $data
+     * @return \Illuminate\Contracts\Support\Htmlable
+     */
+    public function render($view = null, $data = [])
+    {
+        return static::viewFactory()->make($view ?: Paginator::$defaultSimpleView, array_merge($data, [
+            'paginator' => $this,
+        ]));
+    }
+
+    /**
+     * Determine if there are more items in the data source.
+     *
+     * @return bool
+     */
+    public function hasMorePages()
+    {
+        return (is_null($this->cursor) && $this->hasMore) ||
+            (! is_null($this->cursor) && $this->cursor->pointsToNextItems() && $this->hasMore) ||
+            (! is_null($this->cursor) && $this->cursor->pointsToPreviousItems());
+    }
+
+    /**
+     * Determine if there are enough items to split into multiple pages.
+     *
+     * @return bool
+     */
+    public function hasPages()
+    {
+        return ! $this->onFirstPage() || $this->hasMorePages();
+    }
+
+    /**
+     * Determine if the paginator is on the first page.
+     *
+     * @return bool
+     */
+    public function onFirstPage()
+    {
+        return is_null($this->cursor) || ($this->cursor->pointsToPreviousItems() && ! $this->hasMore);
+    }
+
+    /**
+     * Determine if the paginator is on the last page.
+     *
+     * @return bool
+     */
+    public function onLastPage()
+    {
+        return ! $this->hasMorePages();
+    }
+
+    /**
+     * Get the instance as an array.
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return [
+            'data' => $this->items->toArray(),
+            'path' => $this->path(),
+            'per_page' => $this->perPage(),
+            'next_cursor' => $this->nextCursor()?->encode(),
+            'next_page_url' => $this->nextPageUrl(),
+            'prev_cursor' => $this->previousCursor()?->encode(),
+            'prev_page_url' => $this->previousPageUrl(),
+        ];
+    }
+
+    /**
+     * Convert the object into something JSON serializable.
+     *
+     * @return array
+     */
+    public function jsonSerialize(): array
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * Convert the object to its JSON representation.
+     *
+     * @param  int  $options
+     * @return string
+     */
+    public function toJson($options = 0)
+    {
+        return json_encode($this->jsonSerialize(), $options);
+    }
+}
diff --git a/vendor/illuminate/pagination/LICENSE.md b/vendor/illuminate/pagination/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/illuminate/pagination/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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/illuminate/pagination/LengthAwarePaginator.php b/vendor/illuminate/pagination/LengthAwarePaginator.php
new file mode 100644
index 0000000..8d3e0ab
--- /dev/null
+++ b/vendor/illuminate/pagination/LengthAwarePaginator.php
@@ -0,0 +1,231 @@
+options = $options;
+
+        foreach ($options as $key => $value) {
+            $this->{$key} = $value;
+        }
+
+        $this->total = $total;
+        $this->perPage = (int) $perPage;
+        $this->lastPage = max((int) ceil($total / $perPage), 1);
+        $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path;
+        $this->currentPage = $this->setCurrentPage($currentPage, $this->pageName);
+        $this->items = $items instanceof Collection ? $items : Collection::make($items);
+    }
+
+    /**
+     * Get the current page for the request.
+     *
+     * @param  int  $currentPage
+     * @param  string  $pageName
+     * @return int
+     */
+    protected function setCurrentPage($currentPage, $pageName)
+    {
+        $currentPage = $currentPage ?: static::resolveCurrentPage($pageName);
+
+        return $this->isValidPageNumber($currentPage) ? (int) $currentPage : 1;
+    }
+
+    /**
+     * Render the paginator using the given view.
+     *
+     * @param  string|null  $view
+     * @param  array  $data
+     * @return \Illuminate\Contracts\Support\Htmlable
+     */
+    public function links($view = null, $data = [])
+    {
+        return $this->render($view, $data);
+    }
+
+    /**
+     * Render the paginator using the given view.
+     *
+     * @param  string|null  $view
+     * @param  array  $data
+     * @return \Illuminate\Contracts\Support\Htmlable
+     */
+    public function render($view = null, $data = [])
+    {
+        return static::viewFactory()->make($view ?: static::$defaultView, array_merge($data, [
+            'paginator' => $this,
+            'elements' => $this->elements(),
+        ]));
+    }
+
+    /**
+     * Get the paginator links as a collection (for JSON responses).
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function linkCollection()
+    {
+        return collect($this->elements())->flatMap(function ($item) {
+            if (! is_array($item)) {
+                return [['url' => null, 'label' => '...', 'active' => false]];
+            }
+
+            return collect($item)->map(function ($url, $page) {
+                return [
+                    'url' => $url,
+                    'label' => (string) $page,
+                    'active' => $this->currentPage() === $page,
+                ];
+            });
+        })->prepend([
+            'url' => $this->previousPageUrl(),
+            'label' => function_exists('__') ? __('pagination.previous') : 'Previous',
+            'active' => false,
+        ])->push([
+            'url' => $this->nextPageUrl(),
+            'label' => function_exists('__') ? __('pagination.next') : 'Next',
+            'active' => false,
+        ]);
+    }
+
+    /**
+     * Get the array of elements to pass to the view.
+     *
+     * @return array
+     */
+    protected function elements()
+    {
+        $window = UrlWindow::make($this);
+
+        return array_filter([
+            $window['first'],
+            is_array($window['slider']) ? '...' : null,
+            $window['slider'],
+            is_array($window['last']) ? '...' : null,
+            $window['last'],
+        ]);
+    }
+
+    /**
+     * Get the total number of items being paginated.
+     *
+     * @return int
+     */
+    public function total()
+    {
+        return $this->total;
+    }
+
+    /**
+     * Determine if there are more items in the data source.
+     *
+     * @return bool
+     */
+    public function hasMorePages()
+    {
+        return $this->currentPage() < $this->lastPage();
+    }
+
+    /**
+     * Get the URL for the next page.
+     *
+     * @return string|null
+     */
+    public function nextPageUrl()
+    {
+        if ($this->hasMorePages()) {
+            return $this->url($this->currentPage() + 1);
+        }
+    }
+
+    /**
+     * Get the last page.
+     *
+     * @return int
+     */
+    public function lastPage()
+    {
+        return $this->lastPage;
+    }
+
+    /**
+     * Get the instance as an array.
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return [
+            'current_page' => $this->currentPage(),
+            'data' => $this->items->toArray(),
+            'first_page_url' => $this->url(1),
+            'from' => $this->firstItem(),
+            'last_page' => $this->lastPage(),
+            'last_page_url' => $this->url($this->lastPage()),
+            'links' => $this->linkCollection()->toArray(),
+            'next_page_url' => $this->nextPageUrl(),
+            'path' => $this->path(),
+            'per_page' => $this->perPage(),
+            'prev_page_url' => $this->previousPageUrl(),
+            'to' => $this->lastItem(),
+            'total' => $this->total(),
+        ];
+    }
+
+    /**
+     * Convert the object into something JSON serializable.
+     *
+     * @return array
+     */
+    public function jsonSerialize(): array
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * Convert the object to its JSON representation.
+     *
+     * @param  int  $options
+     * @return string
+     */
+    public function toJson($options = 0)
+    {
+        return json_encode($this->jsonSerialize(), $options);
+    }
+}
diff --git a/vendor/illuminate/pagination/PaginationServiceProvider.php b/vendor/illuminate/pagination/PaginationServiceProvider.php
new file mode 100755
index 0000000..e94cebd
--- /dev/null
+++ b/vendor/illuminate/pagination/PaginationServiceProvider.php
@@ -0,0 +1,34 @@
+loadViewsFrom(__DIR__.'/resources/views', 'pagination');
+
+        if ($this->app->runningInConsole()) {
+            $this->publishes([
+                __DIR__.'/resources/views' => $this->app->resourcePath('views/vendor/pagination'),
+            ], 'laravel-pagination');
+        }
+    }
+
+    /**
+     * Register the service provider.
+     *
+     * @return void
+     */
+    public function register()
+    {
+        PaginationState::resolveUsing($this->app);
+    }
+}
diff --git a/vendor/illuminate/pagination/PaginationState.php b/vendor/illuminate/pagination/PaginationState.php
new file mode 100644
index 0000000..347c6e2
--- /dev/null
+++ b/vendor/illuminate/pagination/PaginationState.php
@@ -0,0 +1,35 @@
+ $app['view']);
+
+        Paginator::currentPathResolver(fn () => $app['request']->url());
+
+        Paginator::currentPageResolver(function ($pageName = 'page') use ($app) {
+            $page = $app['request']->input($pageName);
+
+            if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
+                return (int) $page;
+            }
+
+            return 1;
+        });
+
+        Paginator::queryStringResolver(fn () => $app['request']->query());
+
+        CursorPaginator::currentCursorResolver(function ($cursorName = 'cursor') use ($app) {
+            return Cursor::fromEncoded($app['request']->input($cursorName));
+        });
+    }
+}
diff --git a/vendor/illuminate/pagination/Paginator.php b/vendor/illuminate/pagination/Paginator.php
new file mode 100644
index 0000000..d307ee9
--- /dev/null
+++ b/vendor/illuminate/pagination/Paginator.php
@@ -0,0 +1,176 @@
+options = $options;
+
+        foreach ($options as $key => $value) {
+            $this->{$key} = $value;
+        }
+
+        $this->perPage = $perPage;
+        $this->currentPage = $this->setCurrentPage($currentPage);
+        $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path;
+
+        $this->setItems($items);
+    }
+
+    /**
+     * Get the current page for the request.
+     *
+     * @param  int  $currentPage
+     * @return int
+     */
+    protected function setCurrentPage($currentPage)
+    {
+        $currentPage = $currentPage ?: static::resolveCurrentPage();
+
+        return $this->isValidPageNumber($currentPage) ? (int) $currentPage : 1;
+    }
+
+    /**
+     * Set the items for the paginator.
+     *
+     * @param  mixed  $items
+     * @return void
+     */
+    protected function setItems($items)
+    {
+        $this->items = $items instanceof Collection ? $items : Collection::make($items);
+
+        $this->hasMore = $this->items->count() > $this->perPage;
+
+        $this->items = $this->items->slice(0, $this->perPage);
+    }
+
+    /**
+     * Get the URL for the next page.
+     *
+     * @return string|null
+     */
+    public function nextPageUrl()
+    {
+        if ($this->hasMorePages()) {
+            return $this->url($this->currentPage() + 1);
+        }
+    }
+
+    /**
+     * Render the paginator using the given view.
+     *
+     * @param  string|null  $view
+     * @param  array  $data
+     * @return string
+     */
+    public function links($view = null, $data = [])
+    {
+        return $this->render($view, $data);
+    }
+
+    /**
+     * Render the paginator using the given view.
+     *
+     * @param  string|null  $view
+     * @param  array  $data
+     * @return \Illuminate\Contracts\Support\Htmlable
+     */
+    public function render($view = null, $data = [])
+    {
+        return static::viewFactory()->make($view ?: static::$defaultSimpleView, array_merge($data, [
+            'paginator' => $this,
+        ]));
+    }
+
+    /**
+     * Manually indicate that the paginator does have more pages.
+     *
+     * @param  bool  $hasMore
+     * @return $this
+     */
+    public function hasMorePagesWhen($hasMore = true)
+    {
+        $this->hasMore = $hasMore;
+
+        return $this;
+    }
+
+    /**
+     * Determine if there are more items in the data source.
+     *
+     * @return bool
+     */
+    public function hasMorePages()
+    {
+        return $this->hasMore;
+    }
+
+    /**
+     * Get the instance as an array.
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return [
+            'current_page' => $this->currentPage(),
+            'data' => $this->items->toArray(),
+            'first_page_url' => $this->url(1),
+            'from' => $this->firstItem(),
+            'next_page_url' => $this->nextPageUrl(),
+            'path' => $this->path(),
+            'per_page' => $this->perPage(),
+            'prev_page_url' => $this->previousPageUrl(),
+            'to' => $this->lastItem(),
+        ];
+    }
+
+    /**
+     * Convert the object into something JSON serializable.
+     *
+     * @return array
+     */
+    public function jsonSerialize(): array
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * Convert the object to its JSON representation.
+     *
+     * @param  int  $options
+     * @return string
+     */
+    public function toJson($options = 0)
+    {
+        return json_encode($this->jsonSerialize(), $options);
+    }
+}
diff --git a/vendor/illuminate/pagination/UrlWindow.php b/vendor/illuminate/pagination/UrlWindow.php
new file mode 100644
index 0000000..99286f7
--- /dev/null
+++ b/vendor/illuminate/pagination/UrlWindow.php
@@ -0,0 +1,220 @@
+paginator = $paginator;
+    }
+
+    /**
+     * Create a new URL window instance.
+     *
+     * @param  \Illuminate\Contracts\Pagination\LengthAwarePaginator  $paginator
+     * @return array
+     */
+    public static function make(PaginatorContract $paginator)
+    {
+        return (new static($paginator))->get();
+    }
+
+    /**
+     * Get the window of URLs to be shown.
+     *
+     * @return array
+     */
+    public function get()
+    {
+        $onEachSide = $this->paginator->onEachSide;
+
+        if ($this->paginator->lastPage() < ($onEachSide * 2) + 8) {
+            return $this->getSmallSlider();
+        }
+
+        return $this->getUrlSlider($onEachSide);
+    }
+
+    /**
+     * Get the slider of URLs there are not enough pages to slide.
+     *
+     * @return array
+     */
+    protected function getSmallSlider()
+    {
+        return [
+            'first' => $this->paginator->getUrlRange(1, $this->lastPage()),
+            'slider' => null,
+            'last' => null,
+        ];
+    }
+
+    /**
+     * Create a URL slider links.
+     *
+     * @param  int  $onEachSide
+     * @return array
+     */
+    protected function getUrlSlider($onEachSide)
+    {
+        $window = $onEachSide + 4;
+
+        if (! $this->hasPages()) {
+            return ['first' => null, 'slider' => null, 'last' => null];
+        }
+
+        // If the current page is very close to the beginning of the page range, we will
+        // just render the beginning of the page range, followed by the last 2 of the
+        // links in this list, since we will not have room to create a full slider.
+        if ($this->currentPage() <= $window) {
+            return $this->getSliderTooCloseToBeginning($window, $onEachSide);
+        }
+
+        // If the current page is close to the ending of the page range we will just get
+        // this first couple pages, followed by a larger window of these ending pages
+        // since we're too close to the end of the list to create a full on slider.
+        elseif ($this->currentPage() > ($this->lastPage() - $window)) {
+            return $this->getSliderTooCloseToEnding($window, $onEachSide);
+        }
+
+        // If we have enough room on both sides of the current page to build a slider we
+        // will surround it with both the beginning and ending caps, with this window
+        // of pages in the middle providing a Google style sliding paginator setup.
+        return $this->getFullSlider($onEachSide);
+    }
+
+    /**
+     * Get the slider of URLs when too close to the beginning of the window.
+     *
+     * @param  int  $window
+     * @param  int  $onEachSide
+     * @return array
+     */
+    protected function getSliderTooCloseToBeginning($window, $onEachSide)
+    {
+        return [
+            'first' => $this->paginator->getUrlRange(1, $window + $onEachSide),
+            'slider' => null,
+            'last' => $this->getFinish(),
+        ];
+    }
+
+    /**
+     * Get the slider of URLs when too close to the ending of the window.
+     *
+     * @param  int  $window
+     * @param  int  $onEachSide
+     * @return array
+     */
+    protected function getSliderTooCloseToEnding($window, $onEachSide)
+    {
+        $last = $this->paginator->getUrlRange(
+            $this->lastPage() - ($window + ($onEachSide - 1)),
+            $this->lastPage()
+        );
+
+        return [
+            'first' => $this->getStart(),
+            'slider' => null,
+            'last' => $last,
+        ];
+    }
+
+    /**
+     * Get the slider of URLs when a full slider can be made.
+     *
+     * @param  int  $onEachSide
+     * @return array
+     */
+    protected function getFullSlider($onEachSide)
+    {
+        return [
+            'first' => $this->getStart(),
+            'slider' => $this->getAdjacentUrlRange($onEachSide),
+            'last' => $this->getFinish(),
+        ];
+    }
+
+    /**
+     * Get the page range for the current page window.
+     *
+     * @param  int  $onEachSide
+     * @return array
+     */
+    public function getAdjacentUrlRange($onEachSide)
+    {
+        return $this->paginator->getUrlRange(
+            $this->currentPage() - $onEachSide,
+            $this->currentPage() + $onEachSide
+        );
+    }
+
+    /**
+     * Get the starting URLs of a pagination slider.
+     *
+     * @return array
+     */
+    public function getStart()
+    {
+        return $this->paginator->getUrlRange(1, 2);
+    }
+
+    /**
+     * Get the ending URLs of a pagination slider.
+     *
+     * @return array
+     */
+    public function getFinish()
+    {
+        return $this->paginator->getUrlRange(
+            $this->lastPage() - 1,
+            $this->lastPage()
+        );
+    }
+
+    /**
+     * Determine if the underlying paginator being presented has pages to show.
+     *
+     * @return bool
+     */
+    public function hasPages()
+    {
+        return $this->paginator->lastPage() > 1;
+    }
+
+    /**
+     * Get the current page from the paginator.
+     *
+     * @return int
+     */
+    protected function currentPage()
+    {
+        return $this->paginator->currentPage();
+    }
+
+    /**
+     * Get the last page from the paginator.
+     *
+     * @return int
+     */
+    protected function lastPage()
+    {
+        return $this->paginator->lastPage();
+    }
+}
diff --git a/vendor/illuminate/pagination/composer.json b/vendor/illuminate/pagination/composer.json
new file mode 100755
index 0000000..1b054f5
--- /dev/null
+++ b/vendor/illuminate/pagination/composer.json
@@ -0,0 +1,37 @@
+{
+    "name": "illuminate/pagination",
+    "description": "The Illuminate Pagination package.",
+    "license": "MIT",
+    "homepage": "https://laravel.com",
+    "support": {
+        "issues": "https://github.com/laravel/framework/issues",
+        "source": "https://github.com/laravel/framework"
+    },
+    "authors": [
+        {
+            "name": "Taylor Otwell",
+            "email": "taylor@laravel.com"
+        }
+    ],
+    "require": {
+        "php": "^8.0.2",
+        "ext-filter": "*",
+        "illuminate/collections": "^9.0",
+        "illuminate/contracts": "^9.0",
+        "illuminate/support": "^9.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Illuminate\\Pagination\\": ""
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "9.x-dev"
+        }
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/pagination/resources/views/bootstrap-4.blade.php b/vendor/illuminate/pagination/resources/views/bootstrap-4.blade.php
new file mode 100644
index 0000000..63c6f56
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/bootstrap-4.blade.php
@@ -0,0 +1,46 @@
+@if ($paginator->hasPages())
+    
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/bootstrap-5.blade.php b/vendor/illuminate/pagination/resources/views/bootstrap-5.blade.php
new file mode 100644
index 0000000..a1795a4
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/bootstrap-5.blade.php
@@ -0,0 +1,88 @@
+@if ($paginator->hasPages())
+    
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/default.blade.php b/vendor/illuminate/pagination/resources/views/default.blade.php
new file mode 100644
index 0000000..0db70b5
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/default.blade.php
@@ -0,0 +1,46 @@
+@if ($paginator->hasPages())
+    
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/semantic-ui.blade.php b/vendor/illuminate/pagination/resources/views/semantic-ui.blade.php
new file mode 100644
index 0000000..ef0dbb1
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/semantic-ui.blade.php
@@ -0,0 +1,36 @@
+@if ($paginator->hasPages())
+    
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/simple-bootstrap-4.blade.php b/vendor/illuminate/pagination/resources/views/simple-bootstrap-4.blade.php
new file mode 100644
index 0000000..4bb4917
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/simple-bootstrap-4.blade.php
@@ -0,0 +1,27 @@
+@if ($paginator->hasPages())
+    
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/simple-bootstrap-5.blade.php b/vendor/illuminate/pagination/resources/views/simple-bootstrap-5.blade.php
new file mode 100644
index 0000000..a89005e
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/simple-bootstrap-5.blade.php
@@ -0,0 +1,29 @@
+@if ($paginator->hasPages())
+    
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/simple-default.blade.php b/vendor/illuminate/pagination/resources/views/simple-default.blade.php
new file mode 100644
index 0000000..36bdbc1
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/simple-default.blade.php
@@ -0,0 +1,19 @@
+@if ($paginator->hasPages())
+    
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/simple-tailwind.blade.php b/vendor/illuminate/pagination/resources/views/simple-tailwind.blade.php
new file mode 100644
index 0000000..6872cca
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/simple-tailwind.blade.php
@@ -0,0 +1,25 @@
+@if ($paginator->hasPages())
+    
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/tailwind.blade.php b/vendor/illuminate/pagination/resources/views/tailwind.blade.php
new file mode 100644
index 0000000..5bf323b
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/tailwind.blade.php
@@ -0,0 +1,106 @@
+@if ($paginator->hasPages())
+    
+@endif
diff --git a/vendor/illuminate/pipeline/Hub.php b/vendor/illuminate/pipeline/Hub.php
new file mode 100644
index 0000000..91e9b3f
--- /dev/null
+++ b/vendor/illuminate/pipeline/Hub.php
@@ -0,0 +1,97 @@
+container = $container;
+    }
+
+    /**
+     * Define the default named pipeline.
+     *
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function defaults(Closure $callback)
+    {
+        return $this->pipeline('default', $callback);
+    }
+
+    /**
+     * Define a new named pipeline.
+     *
+     * @param  string  $name
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function pipeline($name, Closure $callback)
+    {
+        $this->pipelines[$name] = $callback;
+    }
+
+    /**
+     * Send an object through one of the available pipelines.
+     *
+     * @param  mixed  $object
+     * @param  string|null  $pipeline
+     * @return mixed
+     */
+    public function pipe($object, $pipeline = null)
+    {
+        $pipeline = $pipeline ?: 'default';
+
+        return call_user_func(
+            $this->pipelines[$pipeline], new Pipeline($this->container), $object
+        );
+    }
+
+    /**
+     * Get the container instance used by the hub.
+     *
+     * @return \Illuminate\Contracts\Container\Container
+     */
+    public function getContainer()
+    {
+        return $this->container;
+    }
+
+    /**
+     * Set the container instance used by the hub.
+     *
+     * @param  \Illuminate\Contracts\Container\Container  $container
+     * @return $this
+     */
+    public function setContainer(Container $container)
+    {
+        $this->container = $container;
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/pipeline/LICENSE.md b/vendor/illuminate/pipeline/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/illuminate/pipeline/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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/illuminate/pipeline/Pipeline.php b/vendor/illuminate/pipeline/Pipeline.php
new file mode 100644
index 0000000..53b9fef
--- /dev/null
+++ b/vendor/illuminate/pipeline/Pipeline.php
@@ -0,0 +1,271 @@
+container = $container;
+    }
+
+    /**
+     * Set the object being sent through the pipeline.
+     *
+     * @param  mixed  $passable
+     * @return $this
+     */
+    public function send($passable)
+    {
+        $this->passable = $passable;
+
+        return $this;
+    }
+
+    /**
+     * Set the array of pipes.
+     *
+     * @param  array|mixed  $pipes
+     * @return $this
+     */
+    public function through($pipes)
+    {
+        $this->pipes = is_array($pipes) ? $pipes : func_get_args();
+
+        return $this;
+    }
+
+    /**
+     * Push additional pipes onto the pipeline.
+     *
+     * @param  array|mixed  $pipes
+     * @return $this
+     */
+    public function pipe($pipes)
+    {
+        array_push($this->pipes, ...(is_array($pipes) ? $pipes : func_get_args()));
+
+        return $this;
+    }
+
+    /**
+     * Set the method to call on the pipes.
+     *
+     * @param  string  $method
+     * @return $this
+     */
+    public function via($method)
+    {
+        $this->method = $method;
+
+        return $this;
+    }
+
+    /**
+     * Run the pipeline with a final destination callback.
+     *
+     * @param  \Closure  $destination
+     * @return mixed
+     */
+    public function then(Closure $destination)
+    {
+        $pipeline = array_reduce(
+            array_reverse($this->pipes()), $this->carry(), $this->prepareDestination($destination)
+        );
+
+        return $pipeline($this->passable);
+    }
+
+    /**
+     * Run the pipeline and return the result.
+     *
+     * @return mixed
+     */
+    public function thenReturn()
+    {
+        return $this->then(function ($passable) {
+            return $passable;
+        });
+    }
+
+    /**
+     * Get the final piece of the Closure onion.
+     *
+     * @param  \Closure  $destination
+     * @return \Closure
+     */
+    protected function prepareDestination(Closure $destination)
+    {
+        return function ($passable) use ($destination) {
+            try {
+                return $destination($passable);
+            } catch (Throwable $e) {
+                return $this->handleException($passable, $e);
+            }
+        };
+    }
+
+    /**
+     * Get a Closure that represents a slice of the application onion.
+     *
+     * @return \Closure
+     */
+    protected function carry()
+    {
+        return function ($stack, $pipe) {
+            return function ($passable) use ($stack, $pipe) {
+                try {
+                    if (is_callable($pipe)) {
+                        // If the pipe is a callable, then we will call it directly, but otherwise we
+                        // will resolve the pipes out of the dependency container and call it with
+                        // the appropriate method and arguments, returning the results back out.
+                        return $pipe($passable, $stack);
+                    } elseif (! is_object($pipe)) {
+                        [$name, $parameters] = $this->parsePipeString($pipe);
+
+                        // If the pipe is a string we will parse the string and resolve the class out
+                        // of the dependency injection container. We can then build a callable and
+                        // execute the pipe function giving in the parameters that are required.
+                        $pipe = $this->getContainer()->make($name);
+
+                        $parameters = array_merge([$passable, $stack], $parameters);
+                    } else {
+                        // If the pipe is already an object we'll just make a callable and pass it to
+                        // the pipe as-is. There is no need to do any extra parsing and formatting
+                        // since the object we're given was already a fully instantiated object.
+                        $parameters = [$passable, $stack];
+                    }
+
+                    $carry = method_exists($pipe, $this->method)
+                                    ? $pipe->{$this->method}(...$parameters)
+                                    : $pipe(...$parameters);
+
+                    return $this->handleCarry($carry);
+                } catch (Throwable $e) {
+                    return $this->handleException($passable, $e);
+                }
+            };
+        };
+    }
+
+    /**
+     * Parse full pipe string to get name and parameters.
+     *
+     * @param  string  $pipe
+     * @return array
+     */
+    protected function parsePipeString($pipe)
+    {
+        [$name, $parameters] = array_pad(explode(':', $pipe, 2), 2, []);
+
+        if (is_string($parameters)) {
+            $parameters = explode(',', $parameters);
+        }
+
+        return [$name, $parameters];
+    }
+
+    /**
+     * Get the array of configured pipes.
+     *
+     * @return array
+     */
+    protected function pipes()
+    {
+        return $this->pipes;
+    }
+
+    /**
+     * Get the container instance.
+     *
+     * @return \Illuminate\Contracts\Container\Container
+     *
+     * @throws \RuntimeException
+     */
+    protected function getContainer()
+    {
+        if (! $this->container) {
+            throw new RuntimeException('A container instance has not been passed to the Pipeline.');
+        }
+
+        return $this->container;
+    }
+
+    /**
+     * Set the container instance.
+     *
+     * @param  \Illuminate\Contracts\Container\Container  $container
+     * @return $this
+     */
+    public function setContainer(Container $container)
+    {
+        $this->container = $container;
+
+        return $this;
+    }
+
+    /**
+     * Handle the value returned from each pipe before passing it to the next.
+     *
+     * @param  mixed  $carry
+     * @return mixed
+     */
+    protected function handleCarry($carry)
+    {
+        return $carry;
+    }
+
+    /**
+     * Handle the given exception.
+     *
+     * @param  mixed  $passable
+     * @param  \Throwable  $e
+     * @return mixed
+     *
+     * @throws \Throwable
+     */
+    protected function handleException($passable, Throwable $e)
+    {
+        throw $e;
+    }
+}
diff --git a/vendor/illuminate/pipeline/PipelineServiceProvider.php b/vendor/illuminate/pipeline/PipelineServiceProvider.php
new file mode 100644
index 0000000..982608b
--- /dev/null
+++ b/vendor/illuminate/pipeline/PipelineServiceProvider.php
@@ -0,0 +1,34 @@
+app->singleton(
+            PipelineHubContract::class, Hub::class
+        );
+    }
+
+    /**
+     * Get the services provided by the provider.
+     *
+     * @return array
+     */
+    public function provides()
+    {
+        return [
+            PipelineHubContract::class,
+        ];
+    }
+}
diff --git a/vendor/illuminate/pipeline/composer.json b/vendor/illuminate/pipeline/composer.json
new file mode 100644
index 0000000..7e640af
--- /dev/null
+++ b/vendor/illuminate/pipeline/composer.json
@@ -0,0 +1,35 @@
+{
+    "name": "illuminate/pipeline",
+    "description": "The Illuminate Pipeline package.",
+    "license": "MIT",
+    "homepage": "https://laravel.com",
+    "support": {
+        "issues": "https://github.com/laravel/framework/issues",
+        "source": "https://github.com/laravel/framework"
+    },
+    "authors": [
+        {
+            "name": "Taylor Otwell",
+            "email": "taylor@laravel.com"
+        }
+    ],
+    "require": {
+        "php": "^8.0.2",
+        "illuminate/contracts": "^9.0",
+        "illuminate/support": "^9.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Illuminate\\Pipeline\\": ""
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "9.x-dev"
+        }
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/redis/Connections/Connection.php b/vendor/illuminate/redis/Connections/Connection.php
new file mode 100644
index 0000000..69ed5c1
--- /dev/null
+++ b/vendor/illuminate/redis/Connections/Connection.php
@@ -0,0 +1,231 @@
+client;
+    }
+
+    /**
+     * Subscribe to a set of given channels for messages.
+     *
+     * @param  array|string  $channels
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function subscribe($channels, Closure $callback)
+    {
+        return $this->createSubscription($channels, $callback, __FUNCTION__);
+    }
+
+    /**
+     * Subscribe to a set of given channels with wildcards.
+     *
+     * @param  array|string  $channels
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function psubscribe($channels, Closure $callback)
+    {
+        return $this->createSubscription($channels, $callback, __FUNCTION__);
+    }
+
+    /**
+     * Run a command against the Redis database.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function command($method, array $parameters = [])
+    {
+        $start = microtime(true);
+
+        $result = $this->client->{$method}(...$parameters);
+
+        $time = round((microtime(true) - $start) * 1000, 2);
+
+        if (isset($this->events)) {
+            $this->event(new CommandExecuted(
+                $method, $this->parseParametersForEvent($parameters), $time, $this
+            ));
+        }
+
+        return $result;
+    }
+
+    /**
+     * Parse the command's parameters for event dispatching.
+     *
+     * @param  array  $parameters
+     * @return array
+     */
+    protected function parseParametersForEvent(array $parameters)
+    {
+        return $parameters;
+    }
+
+    /**
+     * Fire the given event if possible.
+     *
+     * @param  mixed  $event
+     * @return void
+     */
+    protected function event($event)
+    {
+        $this->events?->dispatch($event);
+    }
+
+    /**
+     * Register a Redis command listener with the connection.
+     *
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function listen(Closure $callback)
+    {
+        $this->events?->listen(CommandExecuted::class, $callback);
+    }
+
+    /**
+     * Get the connection name.
+     *
+     * @return string|null
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Set the connections name.
+     *
+     * @param  string  $name
+     * @return $this
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+
+        return $this;
+    }
+
+    /**
+     * Get the event dispatcher used by the connection.
+     *
+     * @return \Illuminate\Contracts\Events\Dispatcher
+     */
+    public function getEventDispatcher()
+    {
+        return $this->events;
+    }
+
+    /**
+     * Set the event dispatcher instance on the connection.
+     *
+     * @param  \Illuminate\Contracts\Events\Dispatcher  $events
+     * @return void
+     */
+    public function setEventDispatcher(Dispatcher $events)
+    {
+        $this->events = $events;
+    }
+
+    /**
+     * Unset the event dispatcher instance on the connection.
+     *
+     * @return void
+     */
+    public function unsetEventDispatcher()
+    {
+        $this->events = null;
+    }
+
+    /**
+     * Pass other method calls down to the underlying client.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        if (static::hasMacro($method)) {
+            return $this->macroCall($method, $parameters);
+        }
+
+        return $this->command($method, $parameters);
+    }
+}
diff --git a/vendor/illuminate/redis/Connections/PacksPhpRedisValues.php b/vendor/illuminate/redis/Connections/PacksPhpRedisValues.php
new file mode 100644
index 0000000..4d27ff5
--- /dev/null
+++ b/vendor/illuminate/redis/Connections/PacksPhpRedisValues.php
@@ -0,0 +1,183 @@
+  $values
+     * @return array
+     */
+    public function pack(array $values): array
+    {
+        if (empty($values)) {
+            return $values;
+        }
+
+        if ($this->supportsPacking()) {
+            return array_map([$this->client, '_pack'], $values);
+        }
+
+        if ($this->compressed()) {
+            if ($this->supportsLzf() && $this->lzfCompressed()) {
+                if (! function_exists('lzf_compress')) {
+                    throw new RuntimeException("'lzf' extension required to call 'lzf_compress'.");
+                }
+
+                $processor = function ($value) {
+                    return \lzf_compress($this->client->_serialize($value));
+                };
+            } elseif ($this->supportsZstd() && $this->zstdCompressed()) {
+                if (! function_exists('zstd_compress')) {
+                    throw new RuntimeException("'zstd' extension required to call 'zstd_compress'.");
+                }
+
+                $compressionLevel = $this->client->getOption(Redis::OPT_COMPRESSION_LEVEL);
+
+                $processor = function ($value) use ($compressionLevel) {
+                    return \zstd_compress(
+                        $this->client->_serialize($value),
+                        $compressionLevel === 0 ? Redis::COMPRESSION_ZSTD_DEFAULT : $compressionLevel
+                    );
+                };
+            } else {
+                throw new UnexpectedValueException(sprintf(
+                    'Unsupported phpredis compression in use [%d].',
+                    $this->client->getOption(Redis::OPT_COMPRESSION)
+                ));
+            }
+        } else {
+            $processor = function ($value) {
+                return $this->client->_serialize($value);
+            };
+        }
+
+        return array_map($processor, $values);
+    }
+
+    /**
+     * Determine if compression is enabled.
+     *
+     * @return bool
+     */
+    public function compressed(): bool
+    {
+        return defined('Redis::OPT_COMPRESSION') &&
+               $this->client->getOption(Redis::OPT_COMPRESSION) !== Redis::COMPRESSION_NONE;
+    }
+
+    /**
+     * Determine if LZF compression is enabled.
+     *
+     * @return bool
+     */
+    public function lzfCompressed(): bool
+    {
+        return defined('Redis::COMPRESSION_LZF') &&
+               $this->client->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_LZF;
+    }
+
+    /**
+     * Determine if ZSTD compression is enabled.
+     *
+     * @return bool
+     */
+    public function zstdCompressed(): bool
+    {
+        return defined('Redis::COMPRESSION_ZSTD') &&
+               $this->client->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_ZSTD;
+    }
+
+    /**
+     * Determine if LZ4 compression is enabled.
+     *
+     * @return bool
+     */
+    public function lz4Compressed(): bool
+    {
+        return defined('Redis::COMPRESSION_LZ4') &&
+               $this->client->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_LZ4;
+    }
+
+    /**
+     * Determine if the current PhpRedis extension version supports packing.
+     *
+     * @return bool
+     */
+    protected function supportsPacking(): bool
+    {
+        if ($this->supportsPacking === null) {
+            $this->supportsPacking = $this->phpRedisVersionAtLeast('5.3.5');
+        }
+
+        return $this->supportsPacking;
+    }
+
+    /**
+     * Determine if the current PhpRedis extension version supports LZF compression.
+     *
+     * @return bool
+     */
+    protected function supportsLzf(): bool
+    {
+        if ($this->supportsLzf === null) {
+            $this->supportsLzf = $this->phpRedisVersionAtLeast('4.3.0');
+        }
+
+        return $this->supportsLzf;
+    }
+
+    /**
+     * Determine if the current PhpRedis extension version supports Zstd compression.
+     *
+     * @return bool
+     */
+    protected function supportsZstd(): bool
+    {
+        if ($this->supportsZstd === null) {
+            $this->supportsZstd = $this->phpRedisVersionAtLeast('5.1.0');
+        }
+
+        return $this->supportsZstd;
+    }
+
+    /**
+     * Determine if the PhpRedis extension version is at least the given version.
+     *
+     * @param  string  $version
+     * @return bool
+     */
+    protected function phpRedisVersionAtLeast(string $version): bool
+    {
+        $phpredisVersion = phpversion('redis');
+
+        return $phpredisVersion !== false && version_compare($phpredisVersion, $version, '>=');
+    }
+}
diff --git a/vendor/illuminate/redis/Connections/PhpRedisClusterConnection.php b/vendor/illuminate/redis/Connections/PhpRedisClusterConnection.php
new file mode 100644
index 0000000..bf4816a
--- /dev/null
+++ b/vendor/illuminate/redis/Connections/PhpRedisClusterConnection.php
@@ -0,0 +1,24 @@
+client->_masters() as $master) {
+            $async
+                ? $this->command('rawCommand', [$master, 'flushdb', 'async'])
+                : $this->command('flushdb', [$master]);
+        }
+    }
+}
diff --git a/vendor/illuminate/redis/Connections/PhpRedisConnection.php b/vendor/illuminate/redis/Connections/PhpRedisConnection.php
new file mode 100644
index 0000000..0f39ae9
--- /dev/null
+++ b/vendor/illuminate/redis/Connections/PhpRedisConnection.php
@@ -0,0 +1,567 @@
+client = $client;
+        $this->config = $config;
+        $this->connector = $connector;
+    }
+
+    /**
+     * Returns the value of the given key.
+     *
+     * @param  string  $key
+     * @return string|null
+     */
+    public function get($key)
+    {
+        $result = $this->command('get', [$key]);
+
+        return $result !== false ? $result : null;
+    }
+
+    /**
+     * Get the values of all the given keys.
+     *
+     * @param  array  $keys
+     * @return array
+     */
+    public function mget(array $keys)
+    {
+        return array_map(function ($value) {
+            return $value !== false ? $value : null;
+        }, $this->command('mget', [$keys]));
+    }
+
+    /**
+     * Set the string value in the argument as the value of the key.
+     *
+     * @param  string  $key
+     * @param  mixed  $value
+     * @param  string|null  $expireResolution
+     * @param  int|null  $expireTTL
+     * @param  string|null  $flag
+     * @return bool
+     */
+    public function set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
+    {
+        return $this->command('set', [
+            $key,
+            $value,
+            $expireResolution ? [$flag, $expireResolution => $expireTTL] : null,
+        ]);
+    }
+
+    /**
+     * Set the given key if it doesn't exist.
+     *
+     * @param  string  $key
+     * @param  string  $value
+     * @return int
+     */
+    public function setnx($key, $value)
+    {
+        return (int) $this->command('setnx', [$key, $value]);
+    }
+
+    /**
+     * Get the value of the given hash fields.
+     *
+     * @param  string  $key
+     * @param  mixed  ...$dictionary
+     * @return array
+     */
+    public function hmget($key, ...$dictionary)
+    {
+        if (count($dictionary) === 1) {
+            $dictionary = $dictionary[0];
+        }
+
+        return array_values($this->command('hmget', [$key, $dictionary]));
+    }
+
+    /**
+     * Set the given hash fields to their respective values.
+     *
+     * @param  string  $key
+     * @param  mixed  ...$dictionary
+     * @return int
+     */
+    public function hmset($key, ...$dictionary)
+    {
+        if (count($dictionary) === 1) {
+            $dictionary = $dictionary[0];
+        } else {
+            $input = collect($dictionary);
+
+            $dictionary = $input->nth(2)->combine($input->nth(2, 1))->toArray();
+        }
+
+        return $this->command('hmset', [$key, $dictionary]);
+    }
+
+    /**
+     * Set the given hash field if it doesn't exist.
+     *
+     * @param  string  $hash
+     * @param  string  $key
+     * @param  string  $value
+     * @return int
+     */
+    public function hsetnx($hash, $key, $value)
+    {
+        return (int) $this->command('hsetnx', [$hash, $key, $value]);
+    }
+
+    /**
+     * Removes the first count occurrences of the value element from the list.
+     *
+     * @param  string  $key
+     * @param  int  $count
+     * @param  mixed  $value
+     * @return int|false
+     */
+    public function lrem($key, $count, $value)
+    {
+        return $this->command('lrem', [$key, $value, $count]);
+    }
+
+    /**
+     * Removes and returns the first element of the list stored at key.
+     *
+     * @param  mixed  ...$arguments
+     * @return array|null
+     */
+    public function blpop(...$arguments)
+    {
+        $result = $this->command('blpop', $arguments);
+
+        return empty($result) ? null : $result;
+    }
+
+    /**
+     * Removes and returns the last element of the list stored at key.
+     *
+     * @param  mixed  ...$arguments
+     * @return array|null
+     */
+    public function brpop(...$arguments)
+    {
+        $result = $this->command('brpop', $arguments);
+
+        return empty($result) ? null : $result;
+    }
+
+    /**
+     * Removes and returns a random element from the set value at key.
+     *
+     * @param  string  $key
+     * @param  int|null  $count
+     * @return mixed|false
+     */
+    public function spop($key, $count = 1)
+    {
+        return $this->command('spop', func_get_args());
+    }
+
+    /**
+     * Add one or more members to a sorted set or update its score if it already exists.
+     *
+     * @param  string  $key
+     * @param  mixed  ...$dictionary
+     * @return int
+     */
+    public function zadd($key, ...$dictionary)
+    {
+        if (is_array(end($dictionary))) {
+            foreach (array_pop($dictionary) as $member => $score) {
+                $dictionary[] = $score;
+                $dictionary[] = $member;
+            }
+        }
+
+        $options = [];
+
+        foreach (array_slice($dictionary, 0, 3) as $i => $value) {
+            if (in_array($value, ['nx', 'xx', 'ch', 'incr', 'gt', 'lt', 'NX', 'XX', 'CH', 'INCR', 'GT', 'LT'], true)) {
+                $options[] = $value;
+
+                unset($dictionary[$i]);
+            }
+        }
+
+        return $this->command('zadd', array_merge([$key], [$options], array_values($dictionary)));
+    }
+
+    /**
+     * Return elements with score between $min and $max.
+     *
+     * @param  string  $key
+     * @param  mixed  $min
+     * @param  mixed  $max
+     * @param  array  $options
+     * @return array
+     */
+    public function zrangebyscore($key, $min, $max, $options = [])
+    {
+        if (isset($options['limit']) && Arr::isAssoc($options['limit'])) {
+            $options['limit'] = [
+                $options['limit']['offset'],
+                $options['limit']['count'],
+            ];
+        }
+
+        return $this->command('zRangeByScore', [$key, $min, $max, $options]);
+    }
+
+    /**
+     * Return elements with score between $min and $max.
+     *
+     * @param  string  $key
+     * @param  mixed  $min
+     * @param  mixed  $max
+     * @param  array  $options
+     * @return array
+     */
+    public function zrevrangebyscore($key, $min, $max, $options = [])
+    {
+        if (isset($options['limit']) && Arr::isAssoc($options['limit'])) {
+            $options['limit'] = [
+                $options['limit']['offset'],
+                $options['limit']['count'],
+            ];
+        }
+
+        return $this->command('zRevRangeByScore', [$key, $min, $max, $options]);
+    }
+
+    /**
+     * Find the intersection between sets and store in a new set.
+     *
+     * @param  string  $output
+     * @param  array  $keys
+     * @param  array  $options
+     * @return int
+     */
+    public function zinterstore($output, $keys, $options = [])
+    {
+        return $this->command('zinterstore', [$output, $keys,
+            $options['weights'] ?? null,
+            $options['aggregate'] ?? 'sum',
+        ]);
+    }
+
+    /**
+     * Find the union between sets and store in a new set.
+     *
+     * @param  string  $output
+     * @param  array  $keys
+     * @param  array  $options
+     * @return int
+     */
+    public function zunionstore($output, $keys, $options = [])
+    {
+        return $this->command('zunionstore', [$output, $keys,
+            $options['weights'] ?? null,
+            $options['aggregate'] ?? 'sum',
+        ]);
+    }
+
+    /**
+     * Scans all keys based on options.
+     *
+     * @param  mixed  $cursor
+     * @param  array  $options
+     * @return mixed
+     */
+    public function scan($cursor, $options = [])
+    {
+        $result = $this->client->scan($cursor,
+            $options['match'] ?? '*',
+            $options['count'] ?? 10
+        );
+
+        if ($result === false) {
+            $result = [];
+        }
+
+        return $cursor === 0 && empty($result) ? false : [$cursor, $result];
+    }
+
+    /**
+     * Scans the given set for all values based on options.
+     *
+     * @param  string  $key
+     * @param  mixed  $cursor
+     * @param  array  $options
+     * @return mixed
+     */
+    public function zscan($key, $cursor, $options = [])
+    {
+        $result = $this->client->zscan($key, $cursor,
+            $options['match'] ?? '*',
+            $options['count'] ?? 10
+        );
+
+        if ($result === false) {
+            $result = [];
+        }
+
+        return $cursor === 0 && empty($result) ? false : [$cursor, $result];
+    }
+
+    /**
+     * Scans the given hash for all values based on options.
+     *
+     * @param  string  $key
+     * @param  mixed  $cursor
+     * @param  array  $options
+     * @return mixed
+     */
+    public function hscan($key, $cursor, $options = [])
+    {
+        $result = $this->client->hscan($key, $cursor,
+            $options['match'] ?? '*',
+            $options['count'] ?? 10
+        );
+
+        if ($result === false) {
+            $result = [];
+        }
+
+        return $cursor === 0 && empty($result) ? false : [$cursor, $result];
+    }
+
+    /**
+     * Scans the given set for all values based on options.
+     *
+     * @param  string  $key
+     * @param  mixed  $cursor
+     * @param  array  $options
+     * @return mixed
+     */
+    public function sscan($key, $cursor, $options = [])
+    {
+        $result = $this->client->sscan($key, $cursor,
+            $options['match'] ?? '*',
+            $options['count'] ?? 10
+        );
+
+        if ($result === false) {
+            $result = [];
+        }
+
+        return $cursor === 0 && empty($result) ? false : [$cursor, $result];
+    }
+
+    /**
+     * Execute commands in a pipeline.
+     *
+     * @param  callable|null  $callback
+     * @return \Redis|array
+     */
+    public function pipeline(callable $callback = null)
+    {
+        $pipeline = $this->client()->pipeline();
+
+        return is_null($callback)
+            ? $pipeline
+            : tap($pipeline, $callback)->exec();
+    }
+
+    /**
+     * Execute commands in a transaction.
+     *
+     * @param  callable|null  $callback
+     * @return \Redis|array
+     */
+    public function transaction(callable $callback = null)
+    {
+        $transaction = $this->client()->multi();
+
+        return is_null($callback)
+            ? $transaction
+            : tap($transaction, $callback)->exec();
+    }
+
+    /**
+     * Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself.
+     *
+     * @param  string  $script
+     * @param  int  $numkeys
+     * @param  mixed  ...$arguments
+     * @return mixed
+     */
+    public function evalsha($script, $numkeys, ...$arguments)
+    {
+        return $this->command('evalsha', [
+            $this->script('load', $script), $arguments, $numkeys,
+        ]);
+    }
+
+    /**
+     * Evaluate a script and return its result.
+     *
+     * @param  string  $script
+     * @param  int  $numberOfKeys
+     * @param  dynamic  ...$arguments
+     * @return mixed
+     */
+    public function eval($script, $numberOfKeys, ...$arguments)
+    {
+        return $this->command('eval', [$script, $arguments, $numberOfKeys]);
+    }
+
+    /**
+     * Subscribe to a set of given channels for messages.
+     *
+     * @param  array|string  $channels
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function subscribe($channels, Closure $callback)
+    {
+        $this->client->subscribe((array) $channels, function ($redis, $channel, $message) use ($callback) {
+            $callback($message, $channel);
+        });
+    }
+
+    /**
+     * Subscribe to a set of given channels with wildcards.
+     *
+     * @param  array|string  $channels
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function psubscribe($channels, Closure $callback)
+    {
+        $this->client->psubscribe((array) $channels, function ($redis, $pattern, $channel, $message) use ($callback) {
+            $callback($message, $channel);
+        });
+    }
+
+    /**
+     * Subscribe to a set of given channels for messages.
+     *
+     * @param  array|string  $channels
+     * @param  \Closure  $callback
+     * @param  string  $method
+     * @return void
+     */
+    public function createSubscription($channels, Closure $callback, $method = 'subscribe')
+    {
+        //
+    }
+
+    /**
+     * Flush the selected Redis database.
+     *
+     * @return mixed
+     */
+    public function flushdb()
+    {
+        $arguments = func_get_args();
+
+        if (strtoupper((string) ($arguments[0] ?? null)) === 'ASYNC') {
+            return $this->command('flushdb', [true]);
+        }
+
+        return $this->command('flushdb');
+    }
+
+    /**
+     * Execute a raw command.
+     *
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function executeRaw(array $parameters)
+    {
+        return $this->command('rawCommand', $parameters);
+    }
+
+    /**
+     * Run a command against the Redis database.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     *
+     * @throws \RedisException
+     */
+    public function command($method, array $parameters = [])
+    {
+        try {
+            return parent::command($method, $parameters);
+        } catch (RedisException $e) {
+            foreach (['went away', 'socket', 'read error on connection'] as $errorMessage) {
+                if (str_contains($e->getMessage(), $errorMessage)) {
+                    $this->client = $this->connector ? call_user_func($this->connector) : $this->client;
+
+                    break;
+                }
+            }
+
+            throw $e;
+        }
+    }
+
+    /**
+     * Disconnects from the Redis instance.
+     *
+     * @return void
+     */
+    public function disconnect()
+    {
+        $this->client->close();
+    }
+
+    /**
+     * Pass other method calls down to the underlying client.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return parent::__call(strtolower($method), $parameters);
+    }
+}
diff --git a/vendor/illuminate/redis/Connections/PredisClusterConnection.php b/vendor/illuminate/redis/Connections/PredisClusterConnection.php
new file mode 100644
index 0000000..dbf91dc
--- /dev/null
+++ b/vendor/illuminate/redis/Connections/PredisClusterConnection.php
@@ -0,0 +1,25 @@
+client as $node) {
+            $node->executeCommand(tap(new $command)->setArguments(func_get_args()));
+        }
+    }
+}
diff --git a/vendor/illuminate/redis/Connections/PredisConnection.php b/vendor/illuminate/redis/Connections/PredisConnection.php
new file mode 100644
index 0000000..94f7cb4
--- /dev/null
+++ b/vendor/illuminate/redis/Connections/PredisConnection.php
@@ -0,0 +1,70 @@
+client = $client;
+    }
+
+    /**
+     * Subscribe to a set of given channels for messages.
+     *
+     * @param  array|string  $channels
+     * @param  \Closure  $callback
+     * @param  string  $method
+     * @return void
+     */
+    public function createSubscription($channels, Closure $callback, $method = 'subscribe')
+    {
+        $loop = $this->pubSubLoop();
+
+        $loop->{$method}(...array_values((array) $channels));
+
+        foreach ($loop as $message) {
+            if ($message->kind === 'message' || $message->kind === 'pmessage') {
+                $callback($message->payload, $message->channel);
+            }
+        }
+
+        unset($loop);
+    }
+
+    /**
+     * Parse the command's parameters for event dispatching.
+     *
+     * @param  array  $parameters
+     * @return array
+     */
+    protected function parseParametersForEvent(array $parameters)
+    {
+        return collect($parameters)
+            ->transform(function ($parameter) {
+                return $parameter instanceof ArrayableArgument
+                    ? $parameter->toArray()
+                    : $parameter;
+            })->all();
+    }
+}
diff --git a/vendor/illuminate/redis/Connectors/PhpRedisConnector.php b/vendor/illuminate/redis/Connectors/PhpRedisConnector.php
new file mode 100644
index 0000000..8953c5f
--- /dev/null
+++ b/vendor/illuminate/redis/Connectors/PhpRedisConnector.php
@@ -0,0 +1,234 @@
+createClient(array_merge(
+                $config, $options, $formattedOptions
+            ));
+        };
+
+        return new PhpRedisConnection($connector(), $connector, $config);
+    }
+
+    /**
+     * Create a new clustered PhpRedis connection.
+     *
+     * @param  array  $config
+     * @param  array  $clusterOptions
+     * @param  array  $options
+     * @return \Illuminate\Redis\Connections\PhpRedisClusterConnection
+     */
+    public function connectToCluster(array $config, array $clusterOptions, array $options)
+    {
+        $options = array_merge($options, $clusterOptions, Arr::pull($config, 'options', []));
+
+        return new PhpRedisClusterConnection($this->createRedisClusterInstance(
+            array_map([$this, 'buildClusterConnectionString'], $config), $options
+        ));
+    }
+
+    /**
+     * Build a single cluster seed string from an array.
+     *
+     * @param  array  $server
+     * @return string
+     */
+    protected function buildClusterConnectionString(array $server)
+    {
+        return $this->formatHost($server).':'.$server['port'].'?'.Arr::query(Arr::only($server, [
+            'database', 'password', 'prefix', 'read_timeout',
+        ]));
+    }
+
+    /**
+     * Create the Redis client instance.
+     *
+     * @param  array  $config
+     * @return \Redis
+     *
+     * @throws \LogicException
+     */
+    protected function createClient(array $config)
+    {
+        return tap(new Redis, function ($client) use ($config) {
+            if ($client instanceof RedisFacade) {
+                throw new LogicException(
+                    extension_loaded('redis')
+                        ? 'Please remove or rename the Redis facade alias in your "app" configuration file in order to avoid collision with the PHP Redis extension.'
+                        : 'Please make sure the PHP Redis extension is installed and enabled.'
+                );
+            }
+
+            $this->establishConnection($client, $config);
+
+            if (! empty($config['password'])) {
+                if (isset($config['username']) && $config['username'] !== '' && is_string($config['password'])) {
+                    $client->auth([$config['username'], $config['password']]);
+                } else {
+                    $client->auth($config['password']);
+                }
+            }
+
+            if (isset($config['database'])) {
+                $client->select((int) $config['database']);
+            }
+
+            if (! empty($config['prefix'])) {
+                $client->setOption(Redis::OPT_PREFIX, $config['prefix']);
+            }
+
+            if (! empty($config['read_timeout'])) {
+                $client->setOption(Redis::OPT_READ_TIMEOUT, $config['read_timeout']);
+            }
+
+            if (! empty($config['scan'])) {
+                $client->setOption(Redis::OPT_SCAN, $config['scan']);
+            }
+
+            if (! empty($config['name'])) {
+                $client->client('SETNAME', $config['name']);
+            }
+
+            if (array_key_exists('serializer', $config)) {
+                $client->setOption(Redis::OPT_SERIALIZER, $config['serializer']);
+            }
+
+            if (array_key_exists('compression', $config)) {
+                $client->setOption(Redis::OPT_COMPRESSION, $config['compression']);
+            }
+
+            if (array_key_exists('compression_level', $config)) {
+                $client->setOption(Redis::OPT_COMPRESSION_LEVEL, $config['compression_level']);
+            }
+        });
+    }
+
+    /**
+     * Establish a connection with the Redis host.
+     *
+     * @param  \Redis  $client
+     * @param  array  $config
+     * @return void
+     */
+    protected function establishConnection($client, array $config)
+    {
+        $persistent = $config['persistent'] ?? false;
+
+        $parameters = [
+            $this->formatHost($config),
+            $config['port'],
+            Arr::get($config, 'timeout', 0.0),
+            $persistent ? Arr::get($config, 'persistent_id', null) : null,
+            Arr::get($config, 'retry_interval', 0),
+        ];
+
+        if (version_compare(phpversion('redis'), '3.1.3', '>=')) {
+            $parameters[] = Arr::get($config, 'read_timeout', 0.0);
+        }
+
+        if (version_compare(phpversion('redis'), '5.3.0', '>=') && ! is_null($context = Arr::get($config, 'context'))) {
+            $parameters[] = $context;
+        }
+
+        $client->{$persistent ? 'pconnect' : 'connect'}(...$parameters);
+    }
+
+    /**
+     * Create a new redis cluster instance.
+     *
+     * @param  array  $servers
+     * @param  array  $options
+     * @return \RedisCluster
+     */
+    protected function createRedisClusterInstance(array $servers, array $options)
+    {
+        $parameters = [
+            null,
+            array_values($servers),
+            $options['timeout'] ?? 0,
+            $options['read_timeout'] ?? 0,
+            isset($options['persistent']) && $options['persistent'],
+        ];
+
+        if (version_compare(phpversion('redis'), '4.3.0', '>=')) {
+            $parameters[] = $options['password'] ?? null;
+        }
+
+        if (version_compare(phpversion('redis'), '5.3.2', '>=') && ! is_null($context = Arr::get($options, 'context'))) {
+            $parameters[] = $context;
+        }
+
+        return tap(new RedisCluster(...$parameters), function ($client) use ($options) {
+            if (! empty($options['prefix'])) {
+                $client->setOption(RedisCluster::OPT_PREFIX, $options['prefix']);
+            }
+
+            if (! empty($options['scan'])) {
+                $client->setOption(RedisCluster::OPT_SCAN, $options['scan']);
+            }
+
+            if (! empty($options['failover'])) {
+                $client->setOption(RedisCluster::OPT_SLAVE_FAILOVER, $options['failover']);
+            }
+
+            if (! empty($options['name'])) {
+                $client->client('SETNAME', $options['name']);
+            }
+
+            if (array_key_exists('serializer', $options)) {
+                $client->setOption(RedisCluster::OPT_SERIALIZER, $options['serializer']);
+            }
+
+            if (array_key_exists('compression', $options)) {
+                $client->setOption(RedisCluster::OPT_COMPRESSION, $options['compression']);
+            }
+
+            if (array_key_exists('compression_level', $options)) {
+                $client->setOption(RedisCluster::OPT_COMPRESSION_LEVEL, $options['compression_level']);
+            }
+        });
+    }
+
+    /**
+     * Format the host using the scheme if available.
+     *
+     * @param  array  $options
+     * @return string
+     */
+    protected function formatHost(array $options)
+    {
+        if (isset($options['scheme'])) {
+            return Str::start($options['host'], "{$options['scheme']}://");
+        }
+
+        return $options['host'];
+    }
+}
diff --git a/vendor/illuminate/redis/Connectors/PredisConnector.php b/vendor/illuminate/redis/Connectors/PredisConnector.php
new file mode 100644
index 0000000..6222a4b
--- /dev/null
+++ b/vendor/illuminate/redis/Connectors/PredisConnector.php
@@ -0,0 +1,53 @@
+ 10.0], $options, Arr::pull($config, 'options', [])
+        );
+
+        if (isset($config['prefix'])) {
+            $formattedOptions['prefix'] = $config['prefix'];
+        }
+
+        return new PredisConnection(new Client($config, $formattedOptions));
+    }
+
+    /**
+     * Create a new clustered Predis connection.
+     *
+     * @param  array  $config
+     * @param  array  $clusterOptions
+     * @param  array  $options
+     * @return \Illuminate\Redis\Connections\PredisClusterConnection
+     */
+    public function connectToCluster(array $config, array $clusterOptions, array $options)
+    {
+        $clusterSpecificOptions = Arr::pull($config, 'options', []);
+
+        if (isset($config['prefix'])) {
+            $clusterSpecificOptions['prefix'] = $config['prefix'];
+        }
+
+        return new PredisClusterConnection(new Client(array_values($config), array_merge(
+            $options, $clusterOptions, $clusterSpecificOptions
+        )));
+    }
+}
diff --git a/vendor/illuminate/redis/Events/CommandExecuted.php b/vendor/illuminate/redis/Events/CommandExecuted.php
new file mode 100644
index 0000000..fa65719
--- /dev/null
+++ b/vendor/illuminate/redis/Events/CommandExecuted.php
@@ -0,0 +1,59 @@
+time = $time;
+        $this->command = $command;
+        $this->parameters = $parameters;
+        $this->connection = $connection;
+        $this->connectionName = $connection->getName();
+    }
+}
diff --git a/vendor/illuminate/redis/LICENSE.md b/vendor/illuminate/redis/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/illuminate/redis/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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/illuminate/redis/Limiters/ConcurrencyLimiter.php b/vendor/illuminate/redis/Limiters/ConcurrencyLimiter.php
new file mode 100644
index 0000000..e249053
--- /dev/null
+++ b/vendor/illuminate/redis/Limiters/ConcurrencyLimiter.php
@@ -0,0 +1,167 @@
+name = $name;
+        $this->redis = $redis;
+        $this->maxLocks = $maxLocks;
+        $this->releaseAfter = $releaseAfter;
+    }
+
+    /**
+     * Attempt to acquire the lock for the given number of seconds.
+     *
+     * @param  int  $timeout
+     * @param  callable|null  $callback
+     * @param  int  $sleep
+     * @return mixed
+     *
+     * @throws \Illuminate\Contracts\Redis\LimiterTimeoutException
+     * @throws \Throwable
+     */
+    public function block($timeout, $callback = null, $sleep = 250)
+    {
+        $starting = time();
+
+        $id = Str::random(20);
+
+        while (! $slot = $this->acquire($id)) {
+            if (time() - $timeout >= $starting) {
+                throw new LimiterTimeoutException;
+            }
+
+            usleep($sleep * 1000);
+        }
+
+        if (is_callable($callback)) {
+            try {
+                return tap($callback(), function () use ($slot, $id) {
+                    $this->release($slot, $id);
+                });
+            } catch (Throwable $exception) {
+                $this->release($slot, $id);
+
+                throw $exception;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Attempt to acquire the lock.
+     *
+     * @param  string  $id  A unique identifier for this lock
+     * @return mixed
+     */
+    protected function acquire($id)
+    {
+        $slots = array_map(function ($i) {
+            return $this->name.$i;
+        }, range(1, $this->maxLocks));
+
+        return $this->redis->eval(...array_merge(
+            [$this->lockScript(), count($slots)],
+            array_merge($slots, [$this->name, $this->releaseAfter, $id])
+        ));
+    }
+
+    /**
+     * Get the Lua script for acquiring a lock.
+     *
+     * KEYS    - The keys that represent available slots
+     * ARGV[1] - The limiter name
+     * ARGV[2] - The number of seconds the slot should be reserved
+     * ARGV[3] - The unique identifier for this lock
+     *
+     * @return string
+     */
+    protected function lockScript()
+    {
+        return <<<'LUA'
+for index, value in pairs(redis.call('mget', unpack(KEYS))) do
+    if not value then
+        redis.call('set', KEYS[index], ARGV[3], "EX", ARGV[2])
+        return ARGV[1]..index
+    end
+end
+LUA;
+    }
+
+    /**
+     * Release the lock.
+     *
+     * @param  string  $key
+     * @param  string  $id
+     * @return void
+     */
+    protected function release($key, $id)
+    {
+        $this->redis->eval($this->releaseScript(), 1, $key, $id);
+    }
+
+    /**
+     * Get the Lua script to atomically release a lock.
+     *
+     * KEYS[1] - The name of the lock
+     * ARGV[1] - The unique identifier for this lock
+     *
+     * @return string
+     */
+    protected function releaseScript()
+    {
+        return <<<'LUA'
+if redis.call('get', KEYS[1]) == ARGV[1]
+then
+    return redis.call('del', KEYS[1])
+else
+    return 0
+end
+LUA;
+    }
+}
diff --git a/vendor/illuminate/redis/Limiters/ConcurrencyLimiterBuilder.php b/vendor/illuminate/redis/Limiters/ConcurrencyLimiterBuilder.php
new file mode 100644
index 0000000..fd2bf12
--- /dev/null
+++ b/vendor/illuminate/redis/Limiters/ConcurrencyLimiterBuilder.php
@@ -0,0 +1,142 @@
+name = $name;
+        $this->connection = $connection;
+    }
+
+    /**
+     * Set the maximum number of locks that can be obtained per time window.
+     *
+     * @param  int  $maxLocks
+     * @return $this
+     */
+    public function limit($maxLocks)
+    {
+        $this->maxLocks = $maxLocks;
+
+        return $this;
+    }
+
+    /**
+     * Set the number of seconds until the lock will be released.
+     *
+     * @param  int  $releaseAfter
+     * @return $this
+     */
+    public function releaseAfter($releaseAfter)
+    {
+        $this->releaseAfter = $this->secondsUntil($releaseAfter);
+
+        return $this;
+    }
+
+    /**
+     * Set the amount of time to block until a lock is available.
+     *
+     * @param  int  $timeout
+     * @return $this
+     */
+    public function block($timeout)
+    {
+        $this->timeout = $timeout;
+
+        return $this;
+    }
+
+    /**
+     * The number of milliseconds to wait between lock acquisition attempts.
+     *
+     * @param  int  $sleep
+     * @return $this
+     */
+    public function sleep($sleep)
+    {
+        $this->sleep = $sleep;
+
+        return $this;
+    }
+
+    /**
+     * Execute the given callback if a lock is obtained, otherwise call the failure callback.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $failure
+     * @return mixed
+     *
+     * @throws \Illuminate\Contracts\Redis\LimiterTimeoutException
+     */
+    public function then(callable $callback, callable $failure = null)
+    {
+        try {
+            return (new ConcurrencyLimiter(
+                $this->connection, $this->name, $this->maxLocks, $this->releaseAfter
+            ))->block($this->timeout, $callback, $this->sleep);
+        } catch (LimiterTimeoutException $e) {
+            if ($failure) {
+                return $failure($e);
+            }
+
+            throw $e;
+        }
+    }
+}
diff --git a/vendor/illuminate/redis/Limiters/DurationLimiter.php b/vendor/illuminate/redis/Limiters/DurationLimiter.php
new file mode 100644
index 0000000..65297ff
--- /dev/null
+++ b/vendor/illuminate/redis/Limiters/DurationLimiter.php
@@ -0,0 +1,203 @@
+name = $name;
+        $this->decay = $decay;
+        $this->redis = $redis;
+        $this->maxLocks = $maxLocks;
+    }
+
+    /**
+     * Attempt to acquire the lock for the given number of seconds.
+     *
+     * @param  int  $timeout
+     * @param  callable|null  $callback
+     * @param  int  $sleep
+     * @return mixed
+     *
+     * @throws \Illuminate\Contracts\Redis\LimiterTimeoutException
+     */
+    public function block($timeout, $callback = null, $sleep = 750)
+    {
+        $starting = time();
+
+        while (! $this->acquire()) {
+            if (time() - $timeout >= $starting) {
+                throw new LimiterTimeoutException;
+            }
+
+            usleep($sleep * 1000);
+        }
+
+        if (is_callable($callback)) {
+            return $callback();
+        }
+
+        return true;
+    }
+
+    /**
+     * Attempt to acquire the lock.
+     *
+     * @return bool
+     */
+    public function acquire()
+    {
+        $results = $this->redis->eval(
+            $this->luaScript(), 1, $this->name, microtime(true), time(), $this->decay, $this->maxLocks
+        );
+
+        $this->decaysAt = $results[1];
+
+        $this->remaining = max(0, $results[2]);
+
+        return (bool) $results[0];
+    }
+
+    /**
+     * Determine if the key has been "accessed" too many times.
+     *
+     * @return bool
+     */
+    public function tooManyAttempts()
+    {
+        [$this->decaysAt, $this->remaining] = $this->redis->eval(
+            $this->tooManyAttemptsLuaScript(), 1, $this->name, microtime(true), time(), $this->decay, $this->maxLocks
+        );
+
+        return $this->remaining <= 0;
+    }
+
+    /**
+     * Clear the limiter.
+     *
+     * @return void
+     */
+    public function clear()
+    {
+        $this->redis->del($this->name);
+    }
+
+    /**
+     * Get the Lua script for acquiring a lock.
+     *
+     * KEYS[1] - The limiter name
+     * ARGV[1] - Current time in microseconds
+     * ARGV[2] - Current time in seconds
+     * ARGV[3] - Duration of the bucket
+     * ARGV[4] - Allowed number of tasks
+     *
+     * @return string
+     */
+    protected function luaScript()
+    {
+        return <<<'LUA'
+local function reset()
+    redis.call('HMSET', KEYS[1], 'start', ARGV[2], 'end', ARGV[2] + ARGV[3], 'count', 1)
+    return redis.call('EXPIRE', KEYS[1], ARGV[3] * 2)
+end
+
+if redis.call('EXISTS', KEYS[1]) == 0 then
+    return {reset(), ARGV[2] + ARGV[3], ARGV[4] - 1}
+end
+
+if ARGV[1] >= redis.call('HGET', KEYS[1], 'start') and ARGV[1] <= redis.call('HGET', KEYS[1], 'end') then
+    return {
+        tonumber(redis.call('HINCRBY', KEYS[1], 'count', 1)) <= tonumber(ARGV[4]),
+        redis.call('HGET', KEYS[1], 'end'),
+        ARGV[4] - redis.call('HGET', KEYS[1], 'count')
+    }
+end
+
+return {reset(), ARGV[2] + ARGV[3], ARGV[4] - 1}
+LUA;
+    }
+
+    /**
+     * Get the Lua script to determine if the key has been "accessed" too many times.
+     *
+     * KEYS[1] - The limiter name
+     * ARGV[1] - Current time in microseconds
+     * ARGV[2] - Current time in seconds
+     * ARGV[3] - Duration of the bucket
+     * ARGV[4] - Allowed number of tasks
+     *
+     * @return string
+     */
+    protected function tooManyAttemptsLuaScript()
+    {
+        return <<<'LUA'
+
+if redis.call('EXISTS', KEYS[1]) == 0 then
+    return {0, ARGV[2] + ARGV[3]}
+end
+
+if ARGV[1] >= redis.call('HGET', KEYS[1], 'start') and ARGV[1] <= redis.call('HGET', KEYS[1], 'end') then
+    return {
+        redis.call('HGET', KEYS[1], 'end'),
+        ARGV[4] - redis.call('HGET', KEYS[1], 'count')
+    }
+end
+
+return {0, ARGV[2] + ARGV[3]}
+LUA;
+    }
+}
diff --git a/vendor/illuminate/redis/Limiters/DurationLimiterBuilder.php b/vendor/illuminate/redis/Limiters/DurationLimiterBuilder.php
new file mode 100644
index 0000000..ddb3218
--- /dev/null
+++ b/vendor/illuminate/redis/Limiters/DurationLimiterBuilder.php
@@ -0,0 +1,142 @@
+name = $name;
+        $this->connection = $connection;
+    }
+
+    /**
+     * Set the maximum number of locks that can be obtained per time window.
+     *
+     * @param  int  $maxLocks
+     * @return $this
+     */
+    public function allow($maxLocks)
+    {
+        $this->maxLocks = $maxLocks;
+
+        return $this;
+    }
+
+    /**
+     * Set the amount of time the lock window is maintained.
+     *
+     * @param  \DateTimeInterface|\DateInterval|int  $decay
+     * @return $this
+     */
+    public function every($decay)
+    {
+        $this->decay = $this->secondsUntil($decay);
+
+        return $this;
+    }
+
+    /**
+     * Set the amount of time to block until a lock is available.
+     *
+     * @param  int  $timeout
+     * @return $this
+     */
+    public function block($timeout)
+    {
+        $this->timeout = $timeout;
+
+        return $this;
+    }
+
+    /**
+     * The number of milliseconds to wait between lock acquisition attempts.
+     *
+     * @param  int  $sleep
+     * @return $this
+     */
+    public function sleep($sleep)
+    {
+        $this->sleep = $sleep;
+
+        return $this;
+    }
+
+    /**
+     * Execute the given callback if a lock is obtained, otherwise call the failure callback.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $failure
+     * @return mixed
+     *
+     * @throws \Illuminate\Contracts\Redis\LimiterTimeoutException
+     */
+    public function then(callable $callback, callable $failure = null)
+    {
+        try {
+            return (new DurationLimiter(
+                $this->connection, $this->name, $this->maxLocks, $this->decay
+            ))->block($this->timeout, $callback, $this->sleep);
+        } catch (LimiterTimeoutException $e) {
+            if ($failure) {
+                return $failure($e);
+            }
+
+            throw $e;
+        }
+    }
+}
diff --git a/vendor/illuminate/redis/RedisManager.php b/vendor/illuminate/redis/RedisManager.php
new file mode 100644
index 0000000..e869741
--- /dev/null
+++ b/vendor/illuminate/redis/RedisManager.php
@@ -0,0 +1,278 @@
+app = $app;
+        $this->driver = $driver;
+        $this->config = $config;
+    }
+
+    /**
+     * Get a Redis connection by name.
+     *
+     * @param  string|null  $name
+     * @return \Illuminate\Redis\Connections\Connection
+     */
+    public function connection($name = null)
+    {
+        $name = $name ?: 'default';
+
+        if (isset($this->connections[$name])) {
+            return $this->connections[$name];
+        }
+
+        return $this->connections[$name] = $this->configure(
+            $this->resolve($name), $name
+        );
+    }
+
+    /**
+     * Resolve the given connection by name.
+     *
+     * @param  string|null  $name
+     * @return \Illuminate\Redis\Connections\Connection
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function resolve($name = null)
+    {
+        $name = $name ?: 'default';
+
+        $options = $this->config['options'] ?? [];
+
+        if (isset($this->config[$name])) {
+            return $this->connector()->connect(
+                $this->parseConnectionConfiguration($this->config[$name]),
+                array_merge(Arr::except($options, 'parameters'), ['parameters' => Arr::get($options, 'parameters.'.$name, Arr::get($options, 'parameters', []))])
+            );
+        }
+
+        if (isset($this->config['clusters'][$name])) {
+            return $this->resolveCluster($name);
+        }
+
+        throw new InvalidArgumentException("Redis connection [{$name}] not configured.");
+    }
+
+    /**
+     * Resolve the given cluster connection by name.
+     *
+     * @param  string  $name
+     * @return \Illuminate\Redis\Connections\Connection
+     */
+    protected function resolveCluster($name)
+    {
+        return $this->connector()->connectToCluster(
+            array_map(function ($config) {
+                return $this->parseConnectionConfiguration($config);
+            }, $this->config['clusters'][$name]),
+            $this->config['clusters']['options'] ?? [],
+            $this->config['options'] ?? []
+        );
+    }
+
+    /**
+     * Configure the given connection to prepare it for commands.
+     *
+     * @param  \Illuminate\Redis\Connections\Connection  $connection
+     * @param  string  $name
+     * @return \Illuminate\Redis\Connections\Connection
+     */
+    protected function configure(Connection $connection, $name)
+    {
+        $connection->setName($name);
+
+        if ($this->events && $this->app->bound('events')) {
+            $connection->setEventDispatcher($this->app->make('events'));
+        }
+
+        return $connection;
+    }
+
+    /**
+     * Get the connector instance for the current driver.
+     *
+     * @return \Illuminate\Contracts\Redis\Connector|null
+     */
+    protected function connector()
+    {
+        $customCreator = $this->customCreators[$this->driver] ?? null;
+
+        if ($customCreator) {
+            return $customCreator();
+        }
+
+        return match ($this->driver) {
+            'predis' => new PredisConnector,
+            'phpredis' => new PhpRedisConnector,
+            default => null,
+        };
+    }
+
+    /**
+     * Parse the Redis connection configuration.
+     *
+     * @param  mixed  $config
+     * @return array
+     */
+    protected function parseConnectionConfiguration($config)
+    {
+        $parsed = (new ConfigurationUrlParser)->parseConfiguration($config);
+
+        $driver = strtolower($parsed['driver'] ?? '');
+
+        if (in_array($driver, ['tcp', 'tls'])) {
+            $parsed['scheme'] = $driver;
+        }
+
+        return array_filter($parsed, function ($key) {
+            return ! in_array($key, ['driver'], true);
+        }, ARRAY_FILTER_USE_KEY);
+    }
+
+    /**
+     * Return all of the created connections.
+     *
+     * @return array
+     */
+    public function connections()
+    {
+        return $this->connections;
+    }
+
+    /**
+     * Enable the firing of Redis command events.
+     *
+     * @return void
+     */
+    public function enableEvents()
+    {
+        $this->events = true;
+    }
+
+    /**
+     * Disable the firing of Redis command events.
+     *
+     * @return void
+     */
+    public function disableEvents()
+    {
+        $this->events = false;
+    }
+
+    /**
+     * Set the default driver.
+     *
+     * @param  string  $driver
+     * @return void
+     */
+    public function setDriver($driver)
+    {
+        $this->driver = $driver;
+    }
+
+    /**
+     * Disconnect the given connection and remove from local cache.
+     *
+     * @param  string|null  $name
+     * @return void
+     */
+    public function purge($name = null)
+    {
+        $name = $name ?: 'default';
+
+        unset($this->connections[$name]);
+    }
+
+    /**
+     * Register a custom driver creator Closure.
+     *
+     * @param  string  $driver
+     * @param  \Closure  $callback
+     * @return $this
+     */
+    public function extend($driver, Closure $callback)
+    {
+        $this->customCreators[$driver] = $callback->bindTo($this, $this);
+
+        return $this;
+    }
+
+    /**
+     * Pass methods onto the default Redis connection.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->connection()->{$method}(...$parameters);
+    }
+}
diff --git a/vendor/illuminate/redis/RedisServiceProvider.php b/vendor/illuminate/redis/RedisServiceProvider.php
new file mode 100755
index 0000000..66282e5
--- /dev/null
+++ b/vendor/illuminate/redis/RedisServiceProvider.php
@@ -0,0 +1,38 @@
+app->singleton('redis', function ($app) {
+            $config = $app->make('config')->get('database.redis', []);
+
+            return new RedisManager($app, Arr::pull($config, 'client', 'phpredis'), $config);
+        });
+
+        $this->app->bind('redis.connection', function ($app) {
+            return $app['redis']->connection();
+        });
+    }
+
+    /**
+     * Get the services provided by the provider.
+     *
+     * @return array
+     */
+    public function provides()
+    {
+        return ['redis', 'redis.connection'];
+    }
+}
diff --git a/vendor/illuminate/redis/composer.json b/vendor/illuminate/redis/composer.json
new file mode 100755
index 0000000..93fe330
--- /dev/null
+++ b/vendor/illuminate/redis/composer.json
@@ -0,0 +1,41 @@
+{
+    "name": "illuminate/redis",
+    "description": "The Illuminate Redis package.",
+    "license": "MIT",
+    "homepage": "https://laravel.com",
+    "support": {
+        "issues": "https://github.com/laravel/framework/issues",
+        "source": "https://github.com/laravel/framework"
+    },
+    "authors": [
+        {
+            "name": "Taylor Otwell",
+            "email": "taylor@laravel.com"
+        }
+    ],
+    "require": {
+        "php": "^8.0.2",
+        "illuminate/collections": "^9.0",
+        "illuminate/contracts": "^9.0",
+        "illuminate/macroable": "^9.0",
+        "illuminate/support": "^9.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Illuminate\\Redis\\": ""
+        }
+    },
+    "suggest": {
+        "ext-redis": "Required to use the phpredis connector (^4.0|^5.0).",
+        "predis/predis": "Required to use the predis connector (^1.1.9|^2.0.2)."
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "9.x-dev"
+        }
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/support/AggregateServiceProvider.php b/vendor/illuminate/support/AggregateServiceProvider.php
new file mode 100644
index 0000000..d7425c5
--- /dev/null
+++ b/vendor/illuminate/support/AggregateServiceProvider.php
@@ -0,0 +1,52 @@
+instances = [];
+
+        foreach ($this->providers as $provider) {
+            $this->instances[] = $this->app->register($provider);
+        }
+    }
+
+    /**
+     * Get the services provided by the provider.
+     *
+     * @return array
+     */
+    public function provides()
+    {
+        $provides = [];
+
+        foreach ($this->providers as $provider) {
+            $instance = $this->app->resolveProvider($provider);
+
+            $provides = array_merge($provides, $instance->provides());
+        }
+
+        return $provides;
+    }
+}
diff --git a/vendor/illuminate/support/Benchmark.php b/vendor/illuminate/support/Benchmark.php
new file mode 100644
index 0000000..3a09d59
--- /dev/null
+++ b/vendor/illuminate/support/Benchmark.php
@@ -0,0 +1,50 @@
+map(function ($callback) use ($iterations) {
+            return collect(range(1, $iterations))->map(function () use ($callback) {
+                gc_collect_cycles();
+
+                $start = hrtime(true);
+
+                $callback();
+
+                return (hrtime(true) - $start) / 1000000;
+            })->average();
+        })->when(
+            $benchmarkables instanceof Closure,
+            fn ($c) => $c->first(),
+            fn ($c) => $c->all(),
+        );
+    }
+
+    /**
+     * Measure a callable or array of callables over the given number of iterations, then dump and die.
+     *
+     * @param  \Closure|array  $benchmarkables
+     * @param  int  $iterations
+     * @return never
+     */
+    public static function dd(Closure|array $benchmarkables, int $iterations = 1): void
+    {
+        $result = collect(static::measure(Arr::wrap($benchmarkables), $iterations))
+            ->map(fn ($average) => number_format($average, 3).'ms')
+            ->when($benchmarkables instanceof Closure, fn ($c) => $c->first(), fn ($c) => $c->all());
+
+        dd($result);
+    }
+}
diff --git a/vendor/illuminate/support/Carbon.php b/vendor/illuminate/support/Carbon.php
new file mode 100644
index 0000000..972d7eb
--- /dev/null
+++ b/vendor/illuminate/support/Carbon.php
@@ -0,0 +1,21 @@
+files = $files;
+        $this->workingPath = $workingPath;
+    }
+
+    /**
+     * Regenerate the Composer autoloader files.
+     *
+     * @param  string|array  $extra
+     * @return int
+     */
+    public function dumpAutoloads($extra = '')
+    {
+        $extra = $extra ? (array) $extra : [];
+
+        $command = array_merge($this->findComposer(), ['dump-autoload'], $extra);
+
+        return $this->getProcess($command)->run();
+    }
+
+    /**
+     * Regenerate the optimized Composer autoloader files.
+     *
+     * @return int
+     */
+    public function dumpOptimized()
+    {
+        return $this->dumpAutoloads('--optimize');
+    }
+
+    /**
+     * Get the composer command for the environment.
+     *
+     * @return array
+     */
+    public function findComposer()
+    {
+        if ($this->files->exists($this->workingPath.'/composer.phar')) {
+            return [$this->phpBinary(), 'composer.phar'];
+        }
+
+        return ['composer'];
+    }
+
+    /**
+     * Get the PHP binary.
+     *
+     * @return string
+     */
+    protected function phpBinary()
+    {
+        return ProcessUtils::escapeArgument((new PhpExecutableFinder)->find(false));
+    }
+
+    /**
+     * Get a new Symfony process instance.
+     *
+     * @param  array  $command
+     * @return \Symfony\Component\Process\Process
+     */
+    protected function getProcess(array $command)
+    {
+        return (new Process($command, $this->workingPath))->setTimeout(null);
+    }
+
+    /**
+     * Set the working path used by the class.
+     *
+     * @param  string  $path
+     * @return $this
+     */
+    public function setWorkingPath($path)
+    {
+        $this->workingPath = realpath($path);
+
+        return $this;
+    }
+
+    /**
+     * Get the version of Composer.
+     *
+     * @return string|null
+     */
+    public function getVersion()
+    {
+        $command = array_merge($this->findComposer(), ['-V', '--no-ansi']);
+
+        $process = $this->getProcess($command);
+
+        $process->run();
+
+        $output = $process->getOutput();
+
+        if (preg_match('/(\d+(\.\d+){2})/', $output, $version)) {
+            return $version[1];
+        }
+
+        return explode(' ', $output)[2] ?? null;
+    }
+}
diff --git a/vendor/illuminate/support/ConfigurationUrlParser.php b/vendor/illuminate/support/ConfigurationUrlParser.php
new file mode 100644
index 0000000..a1b9337
--- /dev/null
+++ b/vendor/illuminate/support/ConfigurationUrlParser.php
@@ -0,0 +1,191 @@
+ 'sqlsrv',
+        'mysql2' => 'mysql', // RDS
+        'postgres' => 'pgsql',
+        'postgresql' => 'pgsql',
+        'sqlite3' => 'sqlite',
+        'redis' => 'tcp',
+        'rediss' => 'tls',
+    ];
+
+    /**
+     * Parse the database configuration, hydrating options using a database configuration URL if possible.
+     *
+     * @param  array|string  $config
+     * @return array
+     */
+    public function parseConfiguration($config)
+    {
+        if (is_string($config)) {
+            $config = ['url' => $config];
+        }
+
+        $url = Arr::pull($config, 'url');
+
+        if (! $url) {
+            return $config;
+        }
+
+        $rawComponents = $this->parseUrl($url);
+
+        $decodedComponents = $this->parseStringsToNativeTypes(
+            array_map('rawurldecode', $rawComponents)
+        );
+
+        return array_merge(
+            $config,
+            $this->getPrimaryOptions($decodedComponents),
+            $this->getQueryOptions($rawComponents)
+        );
+    }
+
+    /**
+     * Get the primary database connection options.
+     *
+     * @param  array  $url
+     * @return array
+     */
+    protected function getPrimaryOptions($url)
+    {
+        return array_filter([
+            'driver' => $this->getDriver($url),
+            'database' => $this->getDatabase($url),
+            'host' => $url['host'] ?? null,
+            'port' => $url['port'] ?? null,
+            'username' => $url['user'] ?? null,
+            'password' => $url['pass'] ?? null,
+        ], fn ($value) => ! is_null($value));
+    }
+
+    /**
+     * Get the database driver from the URL.
+     *
+     * @param  array  $url
+     * @return string|null
+     */
+    protected function getDriver($url)
+    {
+        $alias = $url['scheme'] ?? null;
+
+        if (! $alias) {
+            return;
+        }
+
+        return static::$driverAliases[$alias] ?? $alias;
+    }
+
+    /**
+     * Get the database name from the URL.
+     *
+     * @param  array  $url
+     * @return string|null
+     */
+    protected function getDatabase($url)
+    {
+        $path = $url['path'] ?? null;
+
+        return $path && $path !== '/' ? substr($path, 1) : null;
+    }
+
+    /**
+     * Get all of the additional database options from the query string.
+     *
+     * @param  array  $url
+     * @return array
+     */
+    protected function getQueryOptions($url)
+    {
+        $queryString = $url['query'] ?? null;
+
+        if (! $queryString) {
+            return [];
+        }
+
+        $query = [];
+
+        parse_str($queryString, $query);
+
+        return $this->parseStringsToNativeTypes($query);
+    }
+
+    /**
+     * Parse the string URL to an array of components.
+     *
+     * @param  string  $url
+     * @return array
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function parseUrl($url)
+    {
+        $url = preg_replace('#^(sqlite3?):///#', '$1://null/', $url);
+
+        $parsedUrl = parse_url($url);
+
+        if ($parsedUrl === false) {
+            throw new InvalidArgumentException('The database configuration URL is malformed.');
+        }
+
+        return $parsedUrl;
+    }
+
+    /**
+     * Convert string casted values to their native types.
+     *
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function parseStringsToNativeTypes($value)
+    {
+        if (is_array($value)) {
+            return array_map([$this, 'parseStringsToNativeTypes'], $value);
+        }
+
+        if (! is_string($value)) {
+            return $value;
+        }
+
+        $parsedValue = json_decode($value, true);
+
+        if (json_last_error() === JSON_ERROR_NONE) {
+            return $parsedValue;
+        }
+
+        return $value;
+    }
+
+    /**
+     * Get all of the current drivers' aliases.
+     *
+     * @return array
+     */
+    public static function getDriverAliases()
+    {
+        return static::$driverAliases;
+    }
+
+    /**
+     * Add the given driver alias to the driver aliases array.
+     *
+     * @param  string  $alias
+     * @param  string  $driver
+     * @return void
+     */
+    public static function addDriverAlias($alias, $driver)
+    {
+        static::$driverAliases[$alias] = $driver;
+    }
+}
diff --git a/vendor/illuminate/support/DateFactory.php b/vendor/illuminate/support/DateFactory.php
new file mode 100644
index 0000000..3d0cd04
--- /dev/null
+++ b/vendor/illuminate/support/DateFactory.php
@@ -0,0 +1,231 @@
+$method(...$parameters);
+        }
+
+        $dateClass = static::$dateClass ?: $defaultClassName;
+
+        // Check if the date can be created using the public class method...
+        if (method_exists($dateClass, $method) ||
+            method_exists($dateClass, 'hasMacro') && $dateClass::hasMacro($method)) {
+            return $dateClass::$method(...$parameters);
+        }
+
+        // If that fails, create the date with the default class...
+        $date = $defaultClassName::$method(...$parameters);
+
+        // If the configured class has an "instance" method, we'll try to pass our date into there...
+        if (method_exists($dateClass, 'instance')) {
+            return $dateClass::instance($date);
+        }
+
+        // Otherwise, assume the configured class has a DateTime compatible constructor...
+        return new $dateClass($date->format('Y-m-d H:i:s.u'), $date->getTimezone());
+    }
+}
diff --git a/vendor/illuminate/support/Env.php b/vendor/illuminate/support/Env.php
new file mode 100644
index 0000000..4d5747a
--- /dev/null
+++ b/vendor/illuminate/support/Env.php
@@ -0,0 +1,101 @@
+addAdapter(PutenvAdapter::class);
+            }
+
+            static::$repository = $builder->immutable()->make();
+        }
+
+        return static::$repository;
+    }
+
+    /**
+     * Gets the value of an environment variable.
+     *
+     * @param  string  $key
+     * @param  mixed  $default
+     * @return mixed
+     */
+    public static function get($key, $default = null)
+    {
+        return Option::fromValue(static::getRepository()->get($key))
+            ->map(function ($value) {
+                switch (strtolower($value)) {
+                    case 'true':
+                    case '(true)':
+                        return true;
+                    case 'false':
+                    case '(false)':
+                        return false;
+                    case 'empty':
+                    case '(empty)':
+                        return '';
+                    case 'null':
+                    case '(null)':
+                        return;
+                }
+
+                if (preg_match('/\A([\'"])(.*)\1\z/', $value, $matches)) {
+                    return $matches[2];
+                }
+
+                return $value;
+            })
+            ->getOrCall(fn () => value($default));
+    }
+}
diff --git a/vendor/illuminate/support/Exceptions/MathException.php b/vendor/illuminate/support/Exceptions/MathException.php
new file mode 100644
index 0000000..6f9158d
--- /dev/null
+++ b/vendor/illuminate/support/Exceptions/MathException.php
@@ -0,0 +1,10 @@
+providerIsLoaded(UiServiceProvider::class)) {
+            throw new RuntimeException('In order to use the Auth::routes() method, please install the laravel/ui package.');
+        }
+
+        static::$app->make('router')->auth($options);
+    }
+}
diff --git a/vendor/illuminate/support/Facades/Blade.php b/vendor/illuminate/support/Facades/Blade.php
new file mode 100755
index 0000000..ff537e4
--- /dev/null
+++ b/vendor/illuminate/support/Facades/Blade.php
@@ -0,0 +1,60 @@
+dispatch();
+    }
+
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return BusDispatcherContract::class;
+    }
+}
diff --git a/vendor/illuminate/support/Facades/Cache.php b/vendor/illuminate/support/Facades/Cache.php
new file mode 100755
index 0000000..f4867c8
--- /dev/null
+++ b/vendor/illuminate/support/Facades/Cache.php
@@ -0,0 +1,68 @@
+cookie($key, null));
+    }
+
+    /**
+     * Retrieve a cookie from the request.
+     *
+     * @param  string|null  $key
+     * @param  mixed  $default
+     * @return string|array|null
+     */
+    public static function get($key = null, $default = null)
+    {
+        return static::$app['request']->cookie($key, $default);
+    }
+
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return 'cookie';
+    }
+}
diff --git a/vendor/illuminate/support/Facades/Crypt.php b/vendor/illuminate/support/Facades/Crypt.php
new file mode 100755
index 0000000..20f269d
--- /dev/null
+++ b/vendor/illuminate/support/Facades/Crypt.php
@@ -0,0 +1,27 @@
+resolved($accessor) === true) {
+            $callback(static::getFacadeRoot());
+        }
+
+        static::$app->afterResolving($accessor, function ($service) use ($callback) {
+            $callback($service);
+        });
+    }
+
+    /**
+     * Convert the facade into a Mockery spy.
+     *
+     * @return \Mockery\MockInterface
+     */
+    public static function spy()
+    {
+        if (! static::isMock()) {
+            $class = static::getMockableClass();
+
+            return tap($class ? Mockery::spy($class) : Mockery::spy(), function ($spy) {
+                static::swap($spy);
+            });
+        }
+    }
+
+    /**
+     * Initiate a partial mock on the facade.
+     *
+     * @return \Mockery\MockInterface
+     */
+    public static function partialMock()
+    {
+        $name = static::getFacadeAccessor();
+
+        $mock = static::isMock()
+            ? static::$resolvedInstance[$name]
+            : static::createFreshMockInstance();
+
+        return $mock->makePartial();
+    }
+
+    /**
+     * Initiate a mock expectation on the facade.
+     *
+     * @return \Mockery\Expectation
+     */
+    public static function shouldReceive()
+    {
+        $name = static::getFacadeAccessor();
+
+        $mock = static::isMock()
+            ? static::$resolvedInstance[$name]
+            : static::createFreshMockInstance();
+
+        return $mock->shouldReceive(...func_get_args());
+    }
+
+    /**
+     * Initiate a mock expectation on the facade.
+     *
+     * @return \Mockery\Expectation
+     */
+    public static function expects()
+    {
+        $name = static::getFacadeAccessor();
+
+        $mock = static::isMock()
+            ? static::$resolvedInstance[$name]
+            : static::createFreshMockInstance();
+
+        return $mock->expects(...func_get_args());
+    }
+
+    /**
+     * Create a fresh mock instance for the given class.
+     *
+     * @return \Mockery\MockInterface
+     */
+    protected static function createFreshMockInstance()
+    {
+        return tap(static::createMock(), function ($mock) {
+            static::swap($mock);
+
+            $mock->shouldAllowMockingProtectedMethods();
+        });
+    }
+
+    /**
+     * Create a fresh mock instance for the given class.
+     *
+     * @return \Mockery\MockInterface
+     */
+    protected static function createMock()
+    {
+        $class = static::getMockableClass();
+
+        return $class ? Mockery::mock($class) : Mockery::mock();
+    }
+
+    /**
+     * Determines whether a mock is set as the instance of the facade.
+     *
+     * @return bool
+     */
+    protected static function isMock()
+    {
+        $name = static::getFacadeAccessor();
+
+        return isset(static::$resolvedInstance[$name]) &&
+               static::$resolvedInstance[$name] instanceof LegacyMockInterface;
+    }
+
+    /**
+     * Get the mockable class for the bound instance.
+     *
+     * @return string|null
+     */
+    protected static function getMockableClass()
+    {
+        if ($root = static::getFacadeRoot()) {
+            return get_class($root);
+        }
+    }
+
+    /**
+     * Hotswap the underlying instance behind the facade.
+     *
+     * @param  mixed  $instance
+     * @return void
+     */
+    public static function swap($instance)
+    {
+        static::$resolvedInstance[static::getFacadeAccessor()] = $instance;
+
+        if (isset(static::$app)) {
+            static::$app->instance(static::getFacadeAccessor(), $instance);
+        }
+    }
+
+    /**
+     * Get the root object behind the facade.
+     *
+     * @return mixed
+     */
+    public static function getFacadeRoot()
+    {
+        return static::resolveFacadeInstance(static::getFacadeAccessor());
+    }
+
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     *
+     * @throws \RuntimeException
+     */
+    protected static function getFacadeAccessor()
+    {
+        throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
+    }
+
+    /**
+     * Resolve the facade root instance from the container.
+     *
+     * @param  string  $name
+     * @return mixed
+     */
+    protected static function resolveFacadeInstance($name)
+    {
+        if (isset(static::$resolvedInstance[$name])) {
+            return static::$resolvedInstance[$name];
+        }
+
+        if (static::$app) {
+            if (static::$cached) {
+                return static::$resolvedInstance[$name] = static::$app[$name];
+            }
+
+            return static::$app[$name];
+        }
+    }
+
+    /**
+     * Clear a resolved facade instance.
+     *
+     * @param  string  $name
+     * @return void
+     */
+    public static function clearResolvedInstance($name)
+    {
+        unset(static::$resolvedInstance[$name]);
+    }
+
+    /**
+     * Clear all of the resolved instances.
+     *
+     * @return void
+     */
+    public static function clearResolvedInstances()
+    {
+        static::$resolvedInstance = [];
+    }
+
+    /**
+     * Get the application default aliases.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public static function defaultAliases()
+    {
+        return collect([
+            'App' => App::class,
+            'Arr' => Arr::class,
+            'Artisan' => Artisan::class,
+            'Auth' => Auth::class,
+            'Blade' => Blade::class,
+            'Broadcast' => Broadcast::class,
+            'Bus' => Bus::class,
+            'Cache' => Cache::class,
+            'Config' => Config::class,
+            'Cookie' => Cookie::class,
+            'Crypt' => Crypt::class,
+            'Date' => Date::class,
+            'DB' => DB::class,
+            'Eloquent' => Model::class,
+            'Event' => Event::class,
+            'File' => File::class,
+            'Gate' => Gate::class,
+            'Hash' => Hash::class,
+            'Http' => Http::class,
+            'Js' => Js::class,
+            'Lang' => Lang::class,
+            'Log' => Log::class,
+            'Mail' => Mail::class,
+            'Notification' => Notification::class,
+            'Password' => Password::class,
+            'Queue' => Queue::class,
+            'RateLimiter' => RateLimiter::class,
+            'Redirect' => Redirect::class,
+            'Request' => Request::class,
+            'Response' => Response::class,
+            'Route' => Route::class,
+            'Schema' => Schema::class,
+            'Session' => Session::class,
+            'Storage' => Storage::class,
+            'Str' => Str::class,
+            'URL' => URL::class,
+            'Validator' => Validator::class,
+            'View' => View::class,
+            'Vite' => Vite::class,
+        ]);
+    }
+
+    /**
+     * Get the application instance behind the facade.
+     *
+     * @return \Illuminate\Contracts\Foundation\Application
+     */
+    public static function getFacadeApplication()
+    {
+        return static::$app;
+    }
+
+    /**
+     * Set the application instance.
+     *
+     * @param  \Illuminate\Contracts\Foundation\Application  $app
+     * @return void
+     */
+    public static function setFacadeApplication($app)
+    {
+        static::$app = $app;
+    }
+
+    /**
+     * Handle dynamic, static calls to the object.
+     *
+     * @param  string  $method
+     * @param  array  $args
+     * @return mixed
+     *
+     * @throws \RuntimeException
+     */
+    public static function __callStatic($method, $args)
+    {
+        $instance = static::getFacadeRoot();
+
+        if (! $instance) {
+            throw new RuntimeException('A facade root has not been set.');
+        }
+
+        return $instance->$method(...$args);
+    }
+}
diff --git a/vendor/illuminate/support/Facades/File.php b/vendor/illuminate/support/Facades/File.php
new file mode 100755
index 0000000..7063fef
--- /dev/null
+++ b/vendor/illuminate/support/Facades/File.php
@@ -0,0 +1,71 @@
+fake($callback));
+        });
+    }
+
+    /**
+     * Register a response sequence for the given URL pattern.
+     *
+     * @param  string  $urlPattern
+     * @return \Illuminate\Http\Client\ResponseSequence
+     */
+    public static function fakeSequence(string $urlPattern = '*')
+    {
+        $fake = tap(static::getFacadeRoot(), function ($fake) {
+            static::swap($fake);
+        });
+
+        return $fake->fakeSequence($urlPattern);
+    }
+
+    /**
+     * Indicate that an exception should be thrown if any request is not faked.
+     *
+     * @return \Illuminate\Http\Client\Factory
+     */
+    public static function preventStrayRequests()
+    {
+        return tap(static::getFacadeRoot(), function ($fake) {
+            static::swap($fake->preventStrayRequests());
+        });
+    }
+
+    /**
+     * Stub the given URL using the given callback.
+     *
+     * @param  string  $url
+     * @param  \Illuminate\Http\Client\Response|\GuzzleHttp\Promise\PromiseInterface|callable  $callback
+     * @return \Illuminate\Http\Client\Factory
+     */
+    public static function stubUrl($url, $callback)
+    {
+        return tap(static::getFacadeRoot(), function ($fake) use ($url, $callback) {
+            static::swap($fake->stubUrl($url, $callback));
+        });
+    }
+}
diff --git a/vendor/illuminate/support/Facades/Lang.php b/vendor/illuminate/support/Facades/Lang.php
new file mode 100755
index 0000000..992282c
--- /dev/null
+++ b/vendor/illuminate/support/Facades/Lang.php
@@ -0,0 +1,46 @@
+route($channel, $route);
+    }
+
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return ChannelManager::class;
+    }
+}
diff --git a/vendor/illuminate/support/Facades/ParallelTesting.php b/vendor/illuminate/support/Facades/ParallelTesting.php
new file mode 100644
index 0000000..d91558c
--- /dev/null
+++ b/vendor/illuminate/support/Facades/ParallelTesting.php
@@ -0,0 +1,34 @@
+connection($name)->getSchemaBuilder();
+    }
+
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return 'db.schema';
+    }
+}
diff --git a/vendor/illuminate/support/Facades/Session.php b/vendor/illuminate/support/Facades/Session.php
new file mode 100755
index 0000000..e1ea753
--- /dev/null
+++ b/vendor/illuminate/support/Facades/Session.php
@@ -0,0 +1,80 @@
+get('filesystems.default');
+
+        $root = storage_path('framework/testing/disks/'.$disk);
+
+        if ($token = ParallelTesting::token()) {
+            $root = "{$root}_test_{$token}";
+        }
+
+        (new Filesystem)->cleanDirectory($root);
+
+        static::set($disk, $fake = static::createLocalDriver(array_merge($config, [
+            'root' => $root,
+        ])));
+
+        return tap($fake)->buildTemporaryUrlsUsing(function ($path, $expiration) {
+            return URL::to($path.'?expiration='.$expiration->getTimestamp());
+        });
+    }
+
+    /**
+     * Replace the given disk with a persistent local testing disk.
+     *
+     * @param  string|null  $disk
+     * @param  array  $config
+     * @return \Illuminate\Contracts\Filesystem\Filesystem
+     */
+    public static function persistentFake($disk = null, array $config = [])
+    {
+        $disk = $disk ?: static::$app['config']->get('filesystems.default');
+
+        static::set($disk, $fake = static::createLocalDriver(array_merge($config, [
+            'root' => storage_path('framework/testing/disks/'.$disk),
+        ])));
+
+        return $fake;
+    }
+
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return 'filesystem';
+    }
+}
diff --git a/vendor/illuminate/support/Facades/URL.php b/vendor/illuminate/support/Facades/URL.php
new file mode 100755
index 0000000..8da96f4
--- /dev/null
+++ b/vendor/illuminate/support/Facades/URL.php
@@ -0,0 +1,62 @@
+
+ * @implements \ArrayAccess
+ */
+class Fluent implements Arrayable, ArrayAccess, Jsonable, JsonSerializable
+{
+    /**
+     * All of the attributes set on the fluent instance.
+     *
+     * @var array
+     */
+    protected $attributes = [];
+
+    /**
+     * Create a new fluent instance.
+     *
+     * @param  iterable  $attributes
+     * @return void
+     */
+    public function __construct($attributes = [])
+    {
+        foreach ($attributes as $key => $value) {
+            $this->attributes[$key] = $value;
+        }
+    }
+
+    /**
+     * Get an attribute from the fluent instance.
+     *
+     * @template TGetDefault
+     *
+     * @param  TKey  $key
+     * @param  TGetDefault|(\Closure(): TGetDefault)  $default
+     * @return TValue|TGetDefault
+     */
+    public function get($key, $default = null)
+    {
+        if (array_key_exists($key, $this->attributes)) {
+            return $this->attributes[$key];
+        }
+
+        return value($default);
+    }
+
+    /**
+     * Get the attributes from the fluent instance.
+     *
+     * @return array
+     */
+    public function getAttributes()
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * Convert the fluent instance to an array.
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * Convert the object into something JSON serializable.
+     *
+     * @return array
+     */
+    public function jsonSerialize(): array
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * Convert the fluent instance to JSON.
+     *
+     * @param  int  $options
+     * @return string
+     */
+    public function toJson($options = 0)
+    {
+        return json_encode($this->jsonSerialize(), $options);
+    }
+
+    /**
+     * Determine if the given offset exists.
+     *
+     * @param  TKey  $offset
+     * @return bool
+     */
+    public function offsetExists($offset): bool
+    {
+        return isset($this->attributes[$offset]);
+    }
+
+    /**
+     * Get the value for a given offset.
+     *
+     * @param  TKey  $offset
+     * @return TValue|null
+     */
+    public function offsetGet($offset): mixed
+    {
+        return $this->get($offset);
+    }
+
+    /**
+     * Set the value at the given offset.
+     *
+     * @param  TKey  $offset
+     * @param  TValue  $value
+     * @return void
+     */
+    public function offsetSet($offset, $value): void
+    {
+        $this->attributes[$offset] = $value;
+    }
+
+    /**
+     * Unset the value at the given offset.
+     *
+     * @param  TKey  $offset
+     * @return void
+     */
+    public function offsetUnset($offset): void
+    {
+        unset($this->attributes[$offset]);
+    }
+
+    /**
+     * Handle dynamic calls to the fluent instance to set attributes.
+     *
+     * @param  TKey  $method
+     * @param  array{0: ?TValue}  $parameters
+     * @return $this
+     */
+    public function __call($method, $parameters)
+    {
+        $this->attributes[$method] = count($parameters) > 0 ? reset($parameters) : true;
+
+        return $this;
+    }
+
+    /**
+     * Dynamically retrieve the value of an attribute.
+     *
+     * @param  TKey  $key
+     * @return TValue|null
+     */
+    public function __get($key)
+    {
+        return $this->get($key);
+    }
+
+    /**
+     * Dynamically set the value of an attribute.
+     *
+     * @param  TKey  $key
+     * @param  TValue  $value
+     * @return void
+     */
+    public function __set($key, $value)
+    {
+        $this->offsetSet($key, $value);
+    }
+
+    /**
+     * Dynamically check if an attribute is set.
+     *
+     * @param  TKey  $key
+     * @return bool
+     */
+    public function __isset($key)
+    {
+        return $this->offsetExists($key);
+    }
+
+    /**
+     * Dynamically unset an attribute.
+     *
+     * @param  TKey  $key
+     * @return void
+     */
+    public function __unset($key)
+    {
+        $this->offsetUnset($key);
+    }
+}
diff --git a/vendor/illuminate/support/HigherOrderTapProxy.php b/vendor/illuminate/support/HigherOrderTapProxy.php
new file mode 100644
index 0000000..bbf9b2e
--- /dev/null
+++ b/vendor/illuminate/support/HigherOrderTapProxy.php
@@ -0,0 +1,38 @@
+target = $target;
+    }
+
+    /**
+     * Dynamically pass method calls to the target.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        $this->target->{$method}(...$parameters);
+
+        return $this->target;
+    }
+}
diff --git a/vendor/illuminate/support/HtmlString.php b/vendor/illuminate/support/HtmlString.php
new file mode 100644
index 0000000..d6b71d4
--- /dev/null
+++ b/vendor/illuminate/support/HtmlString.php
@@ -0,0 +1,66 @@
+html = $html;
+    }
+
+    /**
+     * Get the HTML string.
+     *
+     * @return string
+     */
+    public function toHtml()
+    {
+        return $this->html;
+    }
+
+    /**
+     * Determine if the given HTML string is empty.
+     *
+     * @return bool
+     */
+    public function isEmpty()
+    {
+        return $this->html === '';
+    }
+
+    /**
+     * Determine if the given HTML string is not empty.
+     *
+     * @return bool
+     */
+    public function isNotEmpty()
+    {
+        return ! $this->isEmpty();
+    }
+
+    /**
+     * Get the HTML string.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->toHtml();
+    }
+}
diff --git a/vendor/illuminate/support/InteractsWithTime.php b/vendor/illuminate/support/InteractsWithTime.php
new file mode 100644
index 0000000..2b617c3
--- /dev/null
+++ b/vendor/illuminate/support/InteractsWithTime.php
@@ -0,0 +1,64 @@
+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/illuminate/support/Js.php b/vendor/illuminate/support/Js.php
new file mode 100644
index 0000000..69e9a76
--- /dev/null
+++ b/vendor/illuminate/support/Js.php
@@ -0,0 +1,150 @@
+js = $this->convertDataToJavaScriptExpression($data, $flags, $depth);
+    }
+
+    /**
+     * Create a new JavaScript string from the given data.
+     *
+     * @param  mixed  $data
+     * @param  int  $flags
+     * @param  int  $depth
+     * @return static
+     *
+     * @throws \JsonException
+     */
+    public static function from($data, $flags = 0, $depth = 512)
+    {
+        return new static($data, $flags, $depth);
+    }
+
+    /**
+     * Convert the given data to a JavaScript expression.
+     *
+     * @param  mixed  $data
+     * @param  int  $flags
+     * @param  int  $depth
+     * @return string
+     *
+     * @throws \JsonException
+     */
+    protected function convertDataToJavaScriptExpression($data, $flags = 0, $depth = 512)
+    {
+        if ($data instanceof self) {
+            return $data->toHtml();
+        }
+
+        if ($data instanceof BackedEnum) {
+            $data = $data->value;
+        }
+
+        $json = $this->jsonEncode($data, $flags, $depth);
+
+        if (is_string($data)) {
+            return "'".substr($json, 1, -1)."'";
+        }
+
+        return $this->convertJsonToJavaScriptExpression($json, $flags);
+    }
+
+    /**
+     * Encode the given data as JSON.
+     *
+     * @param  mixed  $data
+     * @param  int  $flags
+     * @param  int  $depth
+     * @return string
+     *
+     * @throws \JsonException
+     */
+    protected function jsonEncode($data, $flags = 0, $depth = 512)
+    {
+        if ($data instanceof Jsonable) {
+            return $data->toJson($flags | static::REQUIRED_FLAGS);
+        }
+
+        if ($data instanceof Arrayable && ! ($data instanceof JsonSerializable)) {
+            $data = $data->toArray();
+        }
+
+        return json_encode($data, $flags | static::REQUIRED_FLAGS, $depth);
+    }
+
+    /**
+     * Convert the given JSON to a JavaScript expression.
+     *
+     * @param  string  $json
+     * @param  int  $flags
+     * @return string
+     *
+     * @throws \JsonException
+     */
+    protected function convertJsonToJavaScriptExpression($json, $flags = 0)
+    {
+        if ($json === '[]' || $json === '{}') {
+            return $json;
+        }
+
+        if (Str::startsWith($json, ['"', '{', '['])) {
+            return "JSON.parse('".substr(json_encode($json, $flags | static::REQUIRED_FLAGS), 1, -1)."')";
+        }
+
+        return $json;
+    }
+
+    /**
+     * Get the string representation of the data for use in HTML.
+     *
+     * @return string
+     */
+    public function toHtml()
+    {
+        return $this->js;
+    }
+
+    /**
+     * Get the string representation of the data for use in HTML.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->toHtml();
+    }
+}
diff --git a/vendor/illuminate/support/LICENSE.md b/vendor/illuminate/support/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/illuminate/support/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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/illuminate/support/Lottery.php b/vendor/illuminate/support/Lottery.php
new file mode 100644
index 0000000..9bf3b47
--- /dev/null
+++ b/vendor/illuminate/support/Lottery.php
@@ -0,0 +1,271 @@
+ 1) {
+            throw new RuntimeException('Float must not be greater than 1.');
+        }
+
+        $this->chances = $chances;
+
+        $this->outOf = $outOf;
+    }
+
+    /**
+     * Create a new Lottery instance.
+     *
+     * @param  int|float  $chances
+     * @param  int|null  $outOf
+     * @return static
+     */
+    public static function odds($chances, $outOf = null)
+    {
+        return new static($chances, $outOf);
+    }
+
+    /**
+     * Set the winner callback.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function winner($callback)
+    {
+        $this->winner = $callback;
+
+        return $this;
+    }
+
+    /**
+     * Set the loser callback.
+     *
+     * @param  callable  $callback
+     * @return $this
+     */
+    public function loser($callback)
+    {
+        $this->loser = $callback;
+
+        return $this;
+    }
+
+    /**
+     * Run the lottery.
+     *
+     * @param  mixed  ...$args
+     * @return mixed
+     */
+    public function __invoke(...$args)
+    {
+        return $this->runCallback(...$args);
+    }
+
+    /**
+     * Run the lottery.
+     *
+     * @param  null|int  $times
+     * @return mixed
+     */
+    public function choose($times = null)
+    {
+        if ($times === null) {
+            return $this->runCallback();
+        }
+
+        $results = [];
+
+        for ($i = 0; $i < $times; $i++) {
+            $results[] = $this->runCallback();
+        }
+
+        return $results;
+    }
+
+    /**
+     * Run the winner or loser callback, randomly.
+     *
+     * @param  mixed  ...$args
+     * @return callable
+     */
+    protected function runCallback(...$args)
+    {
+        return $this->wins()
+            ? ($this->winner ?? fn () => true)(...$args)
+            : ($this->loser ?? fn () => false)(...$args);
+    }
+
+    /**
+     * Determine if the lottery "wins" or "loses".
+     *
+     * @return bool
+     */
+    protected function wins()
+    {
+        return static::resultFactory()($this->chances, $this->outOf);
+    }
+
+    /**
+     * The factory that determines the lottery result.
+     *
+     * @return callable
+     */
+    protected static function resultFactory()
+    {
+        return static::$resultFactory ?? fn ($chances, $outOf) => $outOf === null
+            ? random_int(0, PHP_INT_MAX) / PHP_INT_MAX <= $chances
+            : random_int(1, $outOf) <= $chances;
+    }
+
+    /**
+     * Force the lottery to always result in a win.
+     *
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public static function alwaysWin($callback = null)
+    {
+        self::setResultFactory(fn () => true);
+
+        if ($callback === null) {
+            return;
+        }
+
+        $callback();
+
+        static::determineResultNormally();
+    }
+
+    /**
+     * Force the lottery to always result in a lose.
+     *
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public static function alwaysLose($callback = null)
+    {
+        self::setResultFactory(fn () => false);
+
+        if ($callback === null) {
+            return;
+        }
+
+        $callback();
+
+        static::determineResultNormally();
+    }
+
+    /**
+     * Set the sequence that will be used to determine lottery results.
+     *
+     * @param  array  $sequence
+     * @param  callable|null  $whenMissing
+     * @return void
+     */
+    public static function fix($sequence, $whenMissing = null)
+    {
+        return static::forceResultWithSequence($sequence, $whenMissing);
+    }
+
+    /**
+     * Set the sequence that will be used to determine lottery results.
+     *
+     * @param  array  $sequence
+     * @param  callable|null  $whenMissing
+     * @return void
+     */
+    public static function forceResultWithSequence($sequence, $whenMissing = null)
+    {
+        $next = 0;
+
+        $whenMissing ??= function ($chances, $outOf) use (&$next) {
+            $factoryCache = static::$resultFactory;
+
+            static::$resultFactory = null;
+
+            $result = static::resultFactory()($chances, $outOf);
+
+            static::$resultFactory = $factoryCache;
+
+            $next++;
+
+            return $result;
+        };
+
+        static::setResultFactory(function ($chances, $outOf) use (&$next, $sequence, $whenMissing) {
+            if (array_key_exists($next, $sequence)) {
+                return $sequence[$next++];
+            }
+
+            return $whenMissing($chances, $outOf);
+        });
+    }
+
+    /**
+     * Indicate that the lottery results should be determined normally.
+     *
+     * @return void
+     */
+    public static function determineResultNormally()
+    {
+        static::$resultFactory = null;
+    }
+
+    /**
+     * Set the factory that should be used to determine the lottery results.
+     *
+     * @param  callable  $factory
+     * @return void
+     */
+    public static function setResultFactory($factory)
+    {
+        self::$resultFactory = $factory;
+    }
+}
diff --git a/vendor/illuminate/support/Manager.php b/vendor/illuminate/support/Manager.php
new file mode 100755
index 0000000..f8ae072
--- /dev/null
+++ b/vendor/illuminate/support/Manager.php
@@ -0,0 +1,193 @@
+container = $container;
+        $this->config = $container->make('config');
+    }
+
+    /**
+     * Get the default driver name.
+     *
+     * @return string
+     */
+    abstract public function getDefaultDriver();
+
+    /**
+     * Get a driver instance.
+     *
+     * @param  string|null  $driver
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    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 the given driver has not been created before, we will create the instances
+        // here and cache it so we can return it next time very quickly. If there is
+        // already a driver created by this name, we'll just return that instance.
+        if (! isset($this->drivers[$driver])) {
+            $this->drivers[$driver] = $this->createDriver($driver);
+        }
+
+        return $this->drivers[$driver];
+    }
+
+    /**
+     * Create a new driver instance.
+     *
+     * @param  string  $driver
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function createDriver($driver)
+    {
+        // First, we will determine if a custom driver creator exists for the given driver and
+        // if it does not we will check for a creator method for the driver. Custom creator
+        // callbacks allow developers to build their own "drivers" easily using Closures.
+        if (isset($this->customCreators[$driver])) {
+            return $this->callCustomCreator($driver);
+        } else {
+            $method = 'create'.Str::studly($driver).'Driver';
+
+            if (method_exists($this, $method)) {
+                return $this->$method();
+            }
+        }
+
+        throw new InvalidArgumentException("Driver [$driver] not supported.");
+    }
+
+    /**
+     * Call a custom driver creator.
+     *
+     * @param  string  $driver
+     * @return mixed
+     */
+    protected function callCustomCreator($driver)
+    {
+        return $this->customCreators[$driver]($this->container);
+    }
+
+    /**
+     * Register a custom driver creator Closure.
+     *
+     * @param  string  $driver
+     * @param  \Closure  $callback
+     * @return $this
+     */
+    public function extend($driver, Closure $callback)
+    {
+        $this->customCreators[$driver] = $callback;
+
+        return $this;
+    }
+
+    /**
+     * Get all of the created "drivers".
+     *
+     * @return array
+     */
+    public function getDrivers()
+    {
+        return $this->drivers;
+    }
+
+    /**
+     * Get the container instance used by the manager.
+     *
+     * @return \Illuminate\Contracts\Container\Container
+     */
+    public function getContainer()
+    {
+        return $this->container;
+    }
+
+    /**
+     * Set the container instance used by the manager.
+     *
+     * @param  \Illuminate\Contracts\Container\Container  $container
+     * @return $this
+     */
+    public function setContainer(Container $container)
+    {
+        $this->container = $container;
+
+        return $this;
+    }
+
+    /**
+     * Forget all of the resolved driver instances.
+     *
+     * @return $this
+     */
+    public function forgetDrivers()
+    {
+        $this->drivers = [];
+
+        return $this;
+    }
+
+    /**
+     * Dynamically call the default driver instance.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->driver()->$method(...$parameters);
+    }
+}
diff --git a/vendor/illuminate/support/MessageBag.php b/vendor/illuminate/support/MessageBag.php
new file mode 100755
index 0000000..88ebfb0
--- /dev/null
+++ b/vendor/illuminate/support/MessageBag.php
@@ -0,0 +1,420 @@
+ $value) {
+            $value = $value instanceof Arrayable ? $value->toArray() : (array) $value;
+
+            $this->messages[$key] = array_unique($value);
+        }
+    }
+
+    /**
+     * Get the keys present in the message bag.
+     *
+     * @return array
+     */
+    public function keys()
+    {
+        return array_keys($this->messages);
+    }
+
+    /**
+     * Add a message to the message bag.
+     *
+     * @param  string  $key
+     * @param  string  $message
+     * @return $this
+     */
+    public function add($key, $message)
+    {
+        if ($this->isUnique($key, $message)) {
+            $this->messages[$key][] = $message;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a message to the message bag if the given conditional is "true".
+     *
+     * @param  bool  $boolean
+     * @param  string  $key
+     * @param  string  $message
+     * @return $this
+     */
+    public function addIf($boolean, $key, $message)
+    {
+        return $boolean ? $this->add($key, $message) : $this;
+    }
+
+    /**
+     * Determine if a key and message combination already exists.
+     *
+     * @param  string  $key
+     * @param  string  $message
+     * @return bool
+     */
+    protected function isUnique($key, $message)
+    {
+        $messages = (array) $this->messages;
+
+        return ! isset($messages[$key]) || ! in_array($message, $messages[$key]);
+    }
+
+    /**
+     * Merge a new array of messages into the message bag.
+     *
+     * @param  \Illuminate\Contracts\Support\MessageProvider|array  $messages
+     * @return $this
+     */
+    public function merge($messages)
+    {
+        if ($messages instanceof MessageProvider) {
+            $messages = $messages->getMessageBag()->getMessages();
+        }
+
+        $this->messages = array_merge_recursive($this->messages, $messages);
+
+        return $this;
+    }
+
+    /**
+     * Determine if messages exist for all of the given keys.
+     *
+     * @param  array|string|null  $key
+     * @return bool
+     */
+    public function has($key)
+    {
+        if ($this->isEmpty()) {
+            return false;
+        }
+
+        if (is_null($key)) {
+            return $this->any();
+        }
+
+        $keys = is_array($key) ? $key : func_get_args();
+
+        foreach ($keys as $key) {
+            if ($this->first($key) === '') {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Determine if messages exist for any of the given keys.
+     *
+     * @param  array|string|null  $keys
+     * @return bool
+     */
+    public function hasAny($keys = [])
+    {
+        if ($this->isEmpty()) {
+            return false;
+        }
+
+        $keys = is_array($keys) ? $keys : func_get_args();
+
+        foreach ($keys as $key) {
+            if ($this->has($key)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the first message from the message bag for a given key.
+     *
+     * @param  string|null  $key
+     * @param  string|null  $format
+     * @return string
+     */
+    public function first($key = null, $format = null)
+    {
+        $messages = is_null($key) ? $this->all($format) : $this->get($key, $format);
+
+        $firstMessage = Arr::first($messages, null, '');
+
+        return is_array($firstMessage) ? Arr::first($firstMessage) : $firstMessage;
+    }
+
+    /**
+     * Get all of the messages from the message bag for a given key.
+     *
+     * @param  string  $key
+     * @param  string|null  $format
+     * @return array
+     */
+    public function get($key, $format = null)
+    {
+        // If the message exists in the message bag, we will transform it and return
+        // the message. Otherwise, we will check if the key is implicit & collect
+        // all the messages that match the given key and output it as an array.
+        if (array_key_exists($key, $this->messages)) {
+            return $this->transform(
+                $this->messages[$key], $this->checkFormat($format), $key
+            );
+        }
+
+        if (str_contains($key, '*')) {
+            return $this->getMessagesForWildcardKey($key, $format);
+        }
+
+        return [];
+    }
+
+    /**
+     * Get the messages for a wildcard key.
+     *
+     * @param  string  $key
+     * @param  string|null  $format
+     * @return array
+     */
+    protected function getMessagesForWildcardKey($key, $format)
+    {
+        return collect($this->messages)
+                ->filter(function ($messages, $messageKey) use ($key) {
+                    return Str::is($key, $messageKey);
+                })
+                ->map(function ($messages, $messageKey) use ($format) {
+                    return $this->transform(
+                        $messages, $this->checkFormat($format), $messageKey
+                    );
+                })->all();
+    }
+
+    /**
+     * Get all of the messages for every key in the message bag.
+     *
+     * @param  string|null  $format
+     * @return array
+     */
+    public function all($format = null)
+    {
+        $format = $this->checkFormat($format);
+
+        $all = [];
+
+        foreach ($this->messages as $key => $messages) {
+            $all = array_merge($all, $this->transform($messages, $format, $key));
+        }
+
+        return $all;
+    }
+
+    /**
+     * Get all of the unique messages for every key in the message bag.
+     *
+     * @param  string|null  $format
+     * @return array
+     */
+    public function unique($format = null)
+    {
+        return array_unique($this->all($format));
+    }
+
+    /**
+     * Format an array of messages.
+     *
+     * @param  array  $messages
+     * @param  string  $format
+     * @param  string  $messageKey
+     * @return array
+     */
+    protected function transform($messages, $format, $messageKey)
+    {
+        if ($format == ':message') {
+            return (array) $messages;
+        }
+
+        return collect((array) $messages)
+            ->map(function ($message) use ($format, $messageKey) {
+                // We will simply spin through the given messages and transform each one
+                // replacing the :message place holder with the real message allowing
+                // the messages to be easily formatted to each developer's desires.
+                return str_replace([':message', ':key'], [$message, $messageKey], $format);
+            })->all();
+    }
+
+    /**
+     * Get the appropriate format based on the given format.
+     *
+     * @param  string  $format
+     * @return string
+     */
+    protected function checkFormat($format)
+    {
+        return $format ?: $this->format;
+    }
+
+    /**
+     * Get the raw messages in the message bag.
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return $this->messages;
+    }
+
+    /**
+     * Get the raw messages in the message bag.
+     *
+     * @return array
+     */
+    public function getMessages()
+    {
+        return $this->messages();
+    }
+
+    /**
+     * Get the messages for the instance.
+     *
+     * @return \Illuminate\Support\MessageBag
+     */
+    public function getMessageBag()
+    {
+        return $this;
+    }
+
+    /**
+     * Get the default message format.
+     *
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->format;
+    }
+
+    /**
+     * Set the default message format.
+     *
+     * @param  string  $format
+     * @return \Illuminate\Support\MessageBag
+     */
+    public function setFormat($format = ':message')
+    {
+        $this->format = $format;
+
+        return $this;
+    }
+
+    /**
+     * Determine if the message bag has any messages.
+     *
+     * @return bool
+     */
+    public function isEmpty()
+    {
+        return ! $this->any();
+    }
+
+    /**
+     * Determine if the message bag has any messages.
+     *
+     * @return bool
+     */
+    public function isNotEmpty()
+    {
+        return $this->any();
+    }
+
+    /**
+     * Determine if the message bag has any messages.
+     *
+     * @return bool
+     */
+    public function any()
+    {
+        return $this->count() > 0;
+    }
+
+    /**
+     * Get the number of messages in the message bag.
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        return count($this->messages, COUNT_RECURSIVE) - count($this->messages);
+    }
+
+    /**
+     * Get the instance as an array.
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return $this->getMessages();
+    }
+
+    /**
+     * Convert the object into something JSON serializable.
+     *
+     * @return array
+     */
+    public function jsonSerialize(): array
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * Convert the object to its JSON representation.
+     *
+     * @param  int  $options
+     * @return string
+     */
+    public function toJson($options = 0)
+    {
+        return json_encode($this->jsonSerialize(), $options);
+    }
+
+    /**
+     * Convert the message bag to its string representation.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->toJson();
+    }
+}
diff --git a/vendor/illuminate/support/MultipleInstanceManager.php b/vendor/illuminate/support/MultipleInstanceManager.php
new file mode 100644
index 0000000..8544bdf
--- /dev/null
+++ b/vendor/illuminate/support/MultipleInstanceManager.php
@@ -0,0 +1,191 @@
+app = $app;
+    }
+
+    /**
+     * Get the default instance name.
+     *
+     * @return string
+     */
+    abstract public function getDefaultInstance();
+
+    /**
+     * Set the default instance name.
+     *
+     * @param  string  $name
+     * @return void
+     */
+    abstract public function setDefaultInstance($name);
+
+    /**
+     * Get the instance specific configuration.
+     *
+     * @param  string  $name
+     * @return array
+     */
+    abstract public function getInstanceConfig($name);
+
+    /**
+     * Get an instance instance by name.
+     *
+     * @param  string|null  $name
+     * @return mixed
+     */
+    public function instance($name = null)
+    {
+        $name = $name ?: $this->getDefaultInstance();
+
+        return $this->instances[$name] = $this->get($name);
+    }
+
+    /**
+     * Attempt to get an instance from the local cache.
+     *
+     * @param  string  $name
+     * @return mixed
+     */
+    protected function get($name)
+    {
+        return $this->instances[$name] ?? $this->resolve($name);
+    }
+
+    /**
+     * Resolve the given instance.
+     *
+     * @param  string  $name
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException
+     */
+    protected function resolve($name)
+    {
+        $config = $this->getInstanceConfig($name);
+
+        if (is_null($config)) {
+            throw new InvalidArgumentException("Instance [{$name}] is not defined.");
+        }
+
+        if (! array_key_exists('driver', $config)) {
+            throw new RuntimeException("Instance [{$name}] does not specify a driver.");
+        }
+
+        if (isset($this->customCreators[$config['driver']])) {
+            return $this->callCustomCreator($config);
+        } else {
+            $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
+
+            if (method_exists($this, $driverMethod)) {
+                return $this->{$driverMethod}($config);
+            } else {
+                throw new InvalidArgumentException("Instance driver [{$config['driver']}] is not supported.");
+            }
+        }
+    }
+
+    /**
+     * Call a custom instance creator.
+     *
+     * @param  array  $config
+     * @return mixed
+     */
+    protected function callCustomCreator(array $config)
+    {
+        return $this->customCreators[$config['driver']]($this->app, $config);
+    }
+
+    /**
+     * Unset the given instances.
+     *
+     * @param  array|string|null  $name
+     * @return $this
+     */
+    public function forgetInstance($name = null)
+    {
+        $name ??= $this->getDefaultInstance();
+
+        foreach ((array) $name as $instanceName) {
+            if (isset($this->instances[$instanceName])) {
+                unset($this->instances[$instanceName]);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Disconnect the given instance and remove from local cache.
+     *
+     * @param  string|null  $name
+     * @return void
+     */
+    public function purge($name = null)
+    {
+        $name ??= $this->getDefaultInstance();
+
+        unset($this->instances[$name]);
+    }
+
+    /**
+     * Register a custom instance creator Closure.
+     *
+     * @param  string  $name
+     * @param  \Closure  $callback
+     * @return $this
+     */
+    public function extend($name, Closure $callback)
+    {
+        $this->customCreators[$name] = $callback->bindTo($this, $this);
+
+        return $this;
+    }
+
+    /**
+     * Dynamically call the default instance.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->instance()->$method(...$parameters);
+    }
+}
diff --git a/vendor/illuminate/support/NamespacedItemResolver.php b/vendor/illuminate/support/NamespacedItemResolver.php
new file mode 100755
index 0000000..a059c6d
--- /dev/null
+++ b/vendor/illuminate/support/NamespacedItemResolver.php
@@ -0,0 +1,112 @@
+parsed[$key])) {
+            return $this->parsed[$key];
+        }
+
+        // If the key does not contain a double colon, it means the key is not in a
+        // namespace, and is just a regular configuration item. Namespaces are a
+        // tool for organizing configuration items for things such as modules.
+        if (! str_contains($key, '::')) {
+            $segments = explode('.', $key);
+
+            $parsed = $this->parseBasicSegments($segments);
+        } else {
+            $parsed = $this->parseNamespacedSegments($key);
+        }
+
+        // Once we have the parsed array of this key's elements, such as its groups
+        // and namespace, we will cache each array inside a simple list that has
+        // the key and the parsed array for quick look-ups for later requests.
+        return $this->parsed[$key] = $parsed;
+    }
+
+    /**
+     * Parse an array of basic segments.
+     *
+     * @param  array  $segments
+     * @return array
+     */
+    protected function parseBasicSegments(array $segments)
+    {
+        // The first segment in a basic array will always be the group, so we can go
+        // ahead and grab that segment. If there is only one total segment we are
+        // just pulling an entire group out of the array and not a single item.
+        $group = $segments[0];
+
+        // If there is more than one segment in this group, it means we are pulling
+        // a specific item out of a group and will need to return this item name
+        // as well as the group so we know which item to pull from the arrays.
+        $item = count($segments) === 1
+                    ? null
+                    : implode('.', array_slice($segments, 1));
+
+        return [null, $group, $item];
+    }
+
+    /**
+     * Parse an array of namespaced segments.
+     *
+     * @param  string  $key
+     * @return array
+     */
+    protected function parseNamespacedSegments($key)
+    {
+        [$namespace, $item] = explode('::', $key);
+
+        // First we'll just explode the first segment to get the namespace and group
+        // since the item should be in the remaining segments. Once we have these
+        // two pieces of data we can proceed with parsing out the item's value.
+        $itemSegments = explode('.', $item);
+
+        $groupAndItem = array_slice(
+            $this->parseBasicSegments($itemSegments), 1
+        );
+
+        return array_merge([$namespace], $groupAndItem);
+    }
+
+    /**
+     * Set the parsed value of a key.
+     *
+     * @param  string  $key
+     * @param  array  $parsed
+     * @return void
+     */
+    public function setParsedKey($key, $parsed)
+    {
+        $this->parsed[$key] = $parsed;
+    }
+
+    /**
+     * Flush the cache of parsed keys.
+     *
+     * @return void
+     */
+    public function flushParsedKeys()
+    {
+        $this->parsed = [];
+    }
+}
diff --git a/vendor/illuminate/support/Optional.php b/vendor/illuminate/support/Optional.php
new file mode 100644
index 0000000..ba84a2c
--- /dev/null
+++ b/vendor/illuminate/support/Optional.php
@@ -0,0 +1,131 @@
+value = $value;
+    }
+
+    /**
+     * Dynamically access a property on the underlying object.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function __get($key)
+    {
+        if (is_object($this->value)) {
+            return $this->value->{$key} ?? null;
+        }
+    }
+
+    /**
+     * Dynamically check a property exists on the underlying object.
+     *
+     * @param  mixed  $name
+     * @return bool
+     */
+    public function __isset($name)
+    {
+        if (is_object($this->value)) {
+            return isset($this->value->{$name});
+        }
+
+        if (is_array($this->value) || $this->value instanceof ArrayObject) {
+            return isset($this->value[$name]);
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine if an item exists at an offset.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function offsetExists($key): bool
+    {
+        return Arr::accessible($this->value) && Arr::exists($this->value, $key);
+    }
+
+    /**
+     * Get an item at a given offset.
+     *
+     * @param  mixed  $key
+     * @return mixed
+     */
+    public function offsetGet($key): mixed
+    {
+        return Arr::get($this->value, $key);
+    }
+
+    /**
+     * Set the item at a given offset.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $value
+     * @return void
+     */
+    public function offsetSet($key, $value): void
+    {
+        if (Arr::accessible($this->value)) {
+            $this->value[$key] = $value;
+        }
+    }
+
+    /**
+     * Unset the item at a given offset.
+     *
+     * @param  string  $key
+     * @return void
+     */
+    public function offsetUnset($key): void
+    {
+        if (Arr::accessible($this->value)) {
+            unset($this->value[$key]);
+        }
+    }
+
+    /**
+     * Dynamically pass a method to the underlying object.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        if (static::hasMacro($method)) {
+            return $this->macroCall($method, $parameters);
+        }
+
+        if (is_object($this->value)) {
+            return $this->value->{$method}(...$parameters);
+        }
+    }
+}
diff --git a/vendor/illuminate/support/Pluralizer.php b/vendor/illuminate/support/Pluralizer.php
new file mode 100755
index 0000000..0d909de
--- /dev/null
+++ b/vendor/illuminate/support/Pluralizer.php
@@ -0,0 +1,127 @@
+pluralize($value);
+
+        return static::matchCase($plural, $value);
+    }
+
+    /**
+     * Get the singular form of an English word.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public static function singular($value)
+    {
+        $singular = static::inflector()->singularize($value);
+
+        return static::matchCase($singular, $value);
+    }
+
+    /**
+     * Determine if the given value is uncountable.
+     *
+     * @param  string  $value
+     * @return bool
+     */
+    protected static function uncountable($value)
+    {
+        return in_array(strtolower($value), static::$uncountable);
+    }
+
+    /**
+     * Attempt to match the case on two strings.
+     *
+     * @param  string  $value
+     * @param  string  $comparison
+     * @return string
+     */
+    protected static function matchCase($value, $comparison)
+    {
+        $functions = ['mb_strtolower', 'mb_strtoupper', 'ucfirst', 'ucwords'];
+
+        foreach ($functions as $function) {
+            if ($function($comparison) === $comparison) {
+                return $function($value);
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * Get the inflector instance.
+     *
+     * @return \Doctrine\Inflector\Inflector
+     */
+    public static function inflector()
+    {
+        if (is_null(static::$inflector)) {
+            static::$inflector = InflectorFactory::createForLanguage(static::$language)->build();
+        }
+
+        return static::$inflector;
+    }
+
+    /**
+     * Specify the language that should be used by the inflector.
+     *
+     * @param  string  $language
+     * @return void
+     */
+    public static function useLanguage(string $language)
+    {
+        static::$language = $language;
+
+        static::$inflector = null;
+    }
+}
diff --git a/vendor/illuminate/support/ProcessUtils.php b/vendor/illuminate/support/ProcessUtils.php
new file mode 100644
index 0000000..165e751
--- /dev/null
+++ b/vendor/illuminate/support/ProcessUtils.php
@@ -0,0 +1,69 @@
+ 2 && $char === $arg[0] && $char === $arg[strlen($arg) - 1];
+    }
+}
diff --git a/vendor/illuminate/support/Reflector.php b/vendor/illuminate/support/Reflector.php
new file mode 100644
index 0000000..1390c9d
--- /dev/null
+++ b/vendor/illuminate/support/Reflector.php
@@ -0,0 +1,162 @@
+isPublic();
+        }
+
+        if (is_object($var[0]) && method_exists($class, '__call')) {
+            return (new ReflectionMethod($class, '__call'))->isPublic();
+        }
+
+        if (! is_object($var[0]) && method_exists($class, '__callStatic')) {
+            return (new ReflectionMethod($class, '__callStatic'))->isPublic();
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the class name of the given parameter's type, if possible.
+     *
+     * @param  \ReflectionParameter  $parameter
+     * @return string|null
+     */
+    public static function getParameterClassName($parameter)
+    {
+        $type = $parameter->getType();
+
+        if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) {
+            return;
+        }
+
+        return static::getTypeName($parameter, $type);
+    }
+
+    /**
+     * Get the class names of the given parameter's type, including union types.
+     *
+     * @param  \ReflectionParameter  $parameter
+     * @return array
+     */
+    public static function getParameterClassNames($parameter)
+    {
+        $type = $parameter->getType();
+
+        if (! $type instanceof ReflectionUnionType) {
+            return array_filter([static::getParameterClassName($parameter)]);
+        }
+
+        $unionTypes = [];
+
+        foreach ($type->getTypes() as $listedType) {
+            if (! $listedType instanceof ReflectionNamedType || $listedType->isBuiltin()) {
+                continue;
+            }
+
+            $unionTypes[] = static::getTypeName($parameter, $listedType);
+        }
+
+        return array_filter($unionTypes);
+    }
+
+    /**
+     * Get the given type's class name.
+     *
+     * @param  \ReflectionParameter  $parameter
+     * @param  \ReflectionNamedType  $type
+     * @return string
+     */
+    protected static function getTypeName($parameter, $type)
+    {
+        $name = $type->getName();
+
+        if (! is_null($class = $parameter->getDeclaringClass())) {
+            if ($name === 'self') {
+                return $class->getName();
+            }
+
+            if ($name === 'parent' && $parent = $class->getParentClass()) {
+                return $parent->getName();
+            }
+        }
+
+        return $name;
+    }
+
+    /**
+     * Determine if the parameter's type is a subclass of the given type.
+     *
+     * @param  \ReflectionParameter  $parameter
+     * @param  string  $className
+     * @return bool
+     */
+    public static function isParameterSubclassOf($parameter, $className)
+    {
+        $paramClassName = static::getParameterClassName($parameter);
+
+        return $paramClassName
+            && (class_exists($paramClassName) || interface_exists($paramClassName))
+            && (new ReflectionClass($paramClassName))->isSubclassOf($className);
+    }
+
+    /**
+     * Determine if the parameter's type is a Backed Enum with a string backing type.
+     *
+     * @param  \ReflectionParameter  $parameter
+     * @return bool
+     */
+    public static function isParameterBackedEnumWithStringBackingType($parameter)
+    {
+        $backedEnumClass = (string) $parameter->getType();
+
+        if (function_exists('enum_exists') && enum_exists($backedEnumClass)) {
+            $reflectionBackedEnum = new ReflectionEnum($backedEnumClass);
+
+            return $reflectionBackedEnum->isBacked()
+                && $reflectionBackedEnum->getBackingType()->getName() == 'string';
+        }
+
+        return false;
+    }
+}
diff --git a/vendor/illuminate/support/ServiceProvider.php b/vendor/illuminate/support/ServiceProvider.php
new file mode 100755
index 0000000..6c530c1
--- /dev/null
+++ b/vendor/illuminate/support/ServiceProvider.php
@@ -0,0 +1,437 @@
+app = $app;
+    }
+
+    /**
+     * Register any application services.
+     *
+     * @return void
+     */
+    public function register()
+    {
+        //
+    }
+
+    /**
+     * Register a booting callback to be run before the "boot" method is called.
+     *
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function booting(Closure $callback)
+    {
+        $this->bootingCallbacks[] = $callback;
+    }
+
+    /**
+     * Register a booted callback to be run after the "boot" method is called.
+     *
+     * @param  \Closure  $callback
+     * @return void
+     */
+    public function booted(Closure $callback)
+    {
+        $this->bootedCallbacks[] = $callback;
+    }
+
+    /**
+     * Call the registered booting callbacks.
+     *
+     * @return void
+     */
+    public function callBootingCallbacks()
+    {
+        $index = 0;
+
+        while ($index < count($this->bootingCallbacks)) {
+            $this->app->call($this->bootingCallbacks[$index]);
+
+            $index++;
+        }
+    }
+
+    /**
+     * Call the registered booted callbacks.
+     *
+     * @return void
+     */
+    public function callBootedCallbacks()
+    {
+        $index = 0;
+
+        while ($index < count($this->bootedCallbacks)) {
+            $this->app->call($this->bootedCallbacks[$index]);
+
+            $index++;
+        }
+    }
+
+    /**
+     * Merge the given configuration with the existing configuration.
+     *
+     * @param  string  $path
+     * @param  string  $key
+     * @return void
+     */
+    protected function mergeConfigFrom($path, $key)
+    {
+        if (! ($this->app instanceof CachesConfiguration && $this->app->configurationIsCached())) {
+            $config = $this->app->make('config');
+
+            $config->set($key, array_merge(
+                require $path, $config->get($key, [])
+            ));
+        }
+    }
+
+    /**
+     * Load the given routes file if routes are not already cached.
+     *
+     * @param  string  $path
+     * @return void
+     */
+    protected function loadRoutesFrom($path)
+    {
+        if (! ($this->app instanceof CachesRoutes && $this->app->routesAreCached())) {
+            require $path;
+        }
+    }
+
+    /**
+     * Register a view file namespace.
+     *
+     * @param  string|array  $path
+     * @param  string  $namespace
+     * @return void
+     */
+    protected function loadViewsFrom($path, $namespace)
+    {
+        $this->callAfterResolving('view', function ($view) use ($path, $namespace) {
+            if (isset($this->app->config['view']['paths']) &&
+                is_array($this->app->config['view']['paths'])) {
+                foreach ($this->app->config['view']['paths'] as $viewPath) {
+                    if (is_dir($appPath = $viewPath.'/vendor/'.$namespace)) {
+                        $view->addNamespace($namespace, $appPath);
+                    }
+                }
+            }
+
+            $view->addNamespace($namespace, $path);
+        });
+    }
+
+    /**
+     * Register the given view components with a custom prefix.
+     *
+     * @param  string  $prefix
+     * @param  array  $components
+     * @return void
+     */
+    protected function loadViewComponentsAs($prefix, array $components)
+    {
+        $this->callAfterResolving(BladeCompiler::class, function ($blade) use ($prefix, $components) {
+            foreach ($components as $alias => $component) {
+                $blade->component($component, is_string($alias) ? $alias : null, $prefix);
+            }
+        });
+    }
+
+    /**
+     * Register a translation file namespace.
+     *
+     * @param  string  $path
+     * @param  string  $namespace
+     * @return void
+     */
+    protected function loadTranslationsFrom($path, $namespace)
+    {
+        $this->callAfterResolving('translator', function ($translator) use ($path, $namespace) {
+            $translator->addNamespace($namespace, $path);
+        });
+    }
+
+    /**
+     * Register a JSON translation file path.
+     *
+     * @param  string  $path
+     * @return void
+     */
+    protected function loadJsonTranslationsFrom($path)
+    {
+        $this->callAfterResolving('translator', function ($translator) use ($path) {
+            $translator->addJsonPath($path);
+        });
+    }
+
+    /**
+     * Register database migration paths.
+     *
+     * @param  array|string  $paths
+     * @return void
+     */
+    protected function loadMigrationsFrom($paths)
+    {
+        $this->callAfterResolving('migrator', function ($migrator) use ($paths) {
+            foreach ((array) $paths as $path) {
+                $migrator->path($path);
+            }
+        });
+    }
+
+    /**
+     * Register Eloquent model factory paths.
+     *
+     * @deprecated Will be removed in a future Laravel version.
+     *
+     * @param  array|string  $paths
+     * @return void
+     */
+    protected function loadFactoriesFrom($paths)
+    {
+        $this->callAfterResolving(ModelFactory::class, function ($factory) use ($paths) {
+            foreach ((array) $paths as $path) {
+                $factory->load($path);
+            }
+        });
+    }
+
+    /**
+     * Setup an after resolving listener, or fire immediately if already resolved.
+     *
+     * @param  string  $name
+     * @param  callable  $callback
+     * @return void
+     */
+    protected function callAfterResolving($name, $callback)
+    {
+        $this->app->afterResolving($name, $callback);
+
+        if ($this->app->resolved($name)) {
+            $callback($this->app->make($name), $this->app);
+        }
+    }
+
+    /**
+     * Register paths to be published by the publish command.
+     *
+     * @param  array  $paths
+     * @param  mixed  $groups
+     * @return void
+     */
+    protected function publishes(array $paths, $groups = null)
+    {
+        $this->ensurePublishArrayInitialized($class = static::class);
+
+        static::$publishes[$class] = array_merge(static::$publishes[$class], $paths);
+
+        foreach ((array) $groups as $group) {
+            $this->addPublishGroup($group, $paths);
+        }
+    }
+
+    /**
+     * Ensure the publish array for the service provider is initialized.
+     *
+     * @param  string  $class
+     * @return void
+     */
+    protected function ensurePublishArrayInitialized($class)
+    {
+        if (! array_key_exists($class, static::$publishes)) {
+            static::$publishes[$class] = [];
+        }
+    }
+
+    /**
+     * Add a publish group / tag to the service provider.
+     *
+     * @param  string  $group
+     * @param  array  $paths
+     * @return void
+     */
+    protected function addPublishGroup($group, $paths)
+    {
+        if (! array_key_exists($group, static::$publishGroups)) {
+            static::$publishGroups[$group] = [];
+        }
+
+        static::$publishGroups[$group] = array_merge(
+            static::$publishGroups[$group], $paths
+        );
+    }
+
+    /**
+     * Get the paths to publish.
+     *
+     * @param  string|null  $provider
+     * @param  string|null  $group
+     * @return array
+     */
+    public static function pathsToPublish($provider = null, $group = null)
+    {
+        if (! is_null($paths = static::pathsForProviderOrGroup($provider, $group))) {
+            return $paths;
+        }
+
+        return collect(static::$publishes)->reduce(function ($paths, $p) {
+            return array_merge($paths, $p);
+        }, []);
+    }
+
+    /**
+     * Get the paths for the provider or group (or both).
+     *
+     * @param  string|null  $provider
+     * @param  string|null  $group
+     * @return array
+     */
+    protected static function pathsForProviderOrGroup($provider, $group)
+    {
+        if ($provider && $group) {
+            return static::pathsForProviderAndGroup($provider, $group);
+        } elseif ($group && array_key_exists($group, static::$publishGroups)) {
+            return static::$publishGroups[$group];
+        } elseif ($provider && array_key_exists($provider, static::$publishes)) {
+            return static::$publishes[$provider];
+        } elseif ($group || $provider) {
+            return [];
+        }
+    }
+
+    /**
+     * Get the paths for the provider and group.
+     *
+     * @param  string  $provider
+     * @param  string  $group
+     * @return array
+     */
+    protected static function pathsForProviderAndGroup($provider, $group)
+    {
+        if (! empty(static::$publishes[$provider]) && ! empty(static::$publishGroups[$group])) {
+            return array_intersect_key(static::$publishes[$provider], static::$publishGroups[$group]);
+        }
+
+        return [];
+    }
+
+    /**
+     * Get the service providers available for publishing.
+     *
+     * @return array
+     */
+    public static function publishableProviders()
+    {
+        return array_keys(static::$publishes);
+    }
+
+    /**
+     * Get the groups available for publishing.
+     *
+     * @return array
+     */
+    public static function publishableGroups()
+    {
+        return array_keys(static::$publishGroups);
+    }
+
+    /**
+     * Register the package's custom Artisan commands.
+     *
+     * @param  array|mixed  $commands
+     * @return void
+     */
+    public function commands($commands)
+    {
+        $commands = is_array($commands) ? $commands : func_get_args();
+
+        Artisan::starting(function ($artisan) use ($commands) {
+            $artisan->resolveCommands($commands);
+        });
+    }
+
+    /**
+     * Get the services provided by the provider.
+     *
+     * @return array
+     */
+    public function provides()
+    {
+        return [];
+    }
+
+    /**
+     * Get the events that trigger this service provider to register.
+     *
+     * @return array
+     */
+    public function when()
+    {
+        return [];
+    }
+
+    /**
+     * Determine if the provider is deferred.
+     *
+     * @return bool
+     */
+    public function isDeferred()
+    {
+        return $this instanceof DeferrableProvider;
+    }
+}
diff --git a/vendor/illuminate/support/Str.php b/vendor/illuminate/support/Str.php
new file mode 100644
index 0000000..e103ccd
--- /dev/null
+++ b/vendor/illuminate/support/Str.php
@@ -0,0 +1,1367 @@
+  $needles
+     * @param  bool  $ignoreCase
+     * @return bool
+     */
+    public static function contains($haystack, $needles, $ignoreCase = false)
+    {
+        if ($ignoreCase) {
+            $haystack = mb_strtolower($haystack);
+        }
+
+        if (! is_iterable($needles)) {
+            $needles = (array) $needles;
+        }
+
+        foreach ($needles as $needle) {
+            if ($ignoreCase) {
+                $needle = mb_strtolower($needle);
+            }
+
+            if ($needle !== '' && str_contains($haystack, $needle)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine if a given string contains all array values.
+     *
+     * @param  string  $haystack
+     * @param  iterable  $needles
+     * @param  bool  $ignoreCase
+     * @return bool
+     */
+    public static function containsAll($haystack, $needles, $ignoreCase = false)
+    {
+        foreach ($needles as $needle) {
+            if (! static::contains($haystack, $needle, $ignoreCase)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Determine if a given string ends with a given substring.
+     *
+     * @param  string  $haystack
+     * @param  string|iterable  $needles
+     * @return bool
+     */
+    public static function endsWith($haystack, $needles)
+    {
+        if (! is_iterable($needles)) {
+            $needles = (array) $needles;
+        }
+
+        foreach ($needles as $needle) {
+            if ((string) $needle !== '' && str_ends_with($haystack, $needle)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Extracts an excerpt from text that matches the first instance of a phrase.
+     *
+     * @param  string  $text
+     * @param  string  $phrase
+     * @param  array  $options
+     * @return string|null
+     */
+    public static function excerpt($text, $phrase = '', $options = [])
+    {
+        $radius = $options['radius'] ?? 100;
+        $omission = $options['omission'] ?? '...';
+
+        preg_match('/^(.*?)('.preg_quote((string) $phrase).')(.*)$/iu', (string) $text, $matches);
+
+        if (empty($matches)) {
+            return null;
+        }
+
+        $start = ltrim($matches[1]);
+
+        $start = str(mb_substr($start, max(mb_strlen($start, 'UTF-8') - $radius, 0), $radius, 'UTF-8'))->ltrim()->unless(
+            fn ($startWithRadius) => $startWithRadius->exactly($start),
+            fn ($startWithRadius) => $startWithRadius->prepend($omission),
+        );
+
+        $end = rtrim($matches[3]);
+
+        $end = str(mb_substr($end, 0, $radius, 'UTF-8'))->rtrim()->unless(
+            fn ($endWithRadius) => $endWithRadius->exactly($end),
+            fn ($endWithRadius) => $endWithRadius->append($omission),
+        );
+
+        return $start->append($matches[2], $end)->toString();
+    }
+
+    /**
+     * Cap a string with a single instance of a given value.
+     *
+     * @param  string  $value
+     * @param  string  $cap
+     * @return string
+     */
+    public static function finish($value, $cap)
+    {
+        $quoted = preg_quote($cap, '/');
+
+        return preg_replace('/(?:'.$quoted.')+$/u', '', $value).$cap;
+    }
+
+    /**
+     * Wrap the string with the given strings.
+     *
+     * @param  string  $value
+     * @param  string  $before
+     * @param  string|null  $after
+     * @return string
+     */
+    public static function wrap($value, $before, $after = null)
+    {
+        return $before.$value.($after ??= $before);
+    }
+
+    /**
+     * Determine if a given string matches a given pattern.
+     *
+     * @param  string|iterable  $pattern
+     * @param  string  $value
+     * @return bool
+     */
+    public static function is($pattern, $value)
+    {
+        $value = (string) $value;
+
+        if (! is_iterable($pattern)) {
+            $pattern = [$pattern];
+        }
+
+        foreach ($pattern as $pattern) {
+            $pattern = (string) $pattern;
+
+            // If the given value is an exact match we can of course return true right
+            // from the beginning. Otherwise, we will translate asterisks and do an
+            // actual pattern match against the two strings to see if they match.
+            if ($pattern === $value) {
+                return true;
+            }
+
+            $pattern = preg_quote($pattern, '#');
+
+            // Asterisks are translated into zero-or-more regular expression wildcards
+            // to make it convenient to check if the strings starts with the given
+            // pattern such as "library/*", making any string check convenient.
+            $pattern = str_replace('\*', '.*', $pattern);
+
+            if (preg_match('#^'.$pattern.'\z#u', $value) === 1) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine if a given string is 7 bit ASCII.
+     *
+     * @param  string  $value
+     * @return bool
+     */
+    public static function isAscii($value)
+    {
+        return ASCII::is_ascii((string) $value);
+    }
+
+    /**
+     * Determine if a given string is valid JSON.
+     *
+     * @param  string  $value
+     * @return bool
+     */
+    public static function isJson($value)
+    {
+        if (! is_string($value)) {
+            return false;
+        }
+
+        try {
+            json_decode($value, true, 512, JSON_THROW_ON_ERROR);
+        } catch (JsonException) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Determine if a given string is a valid UUID.
+     *
+     * @param  string  $value
+     * @return bool
+     */
+    public static function isUuid($value)
+    {
+        if (! is_string($value)) {
+            return false;
+        }
+
+        return preg_match('/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iD', $value) > 0;
+    }
+
+    /**
+     * Determine if a given string is a valid ULID.
+     *
+     * @param  string  $value
+     * @return bool
+     */
+    public static function isUlid($value)
+    {
+        if (! is_string($value)) {
+            return false;
+        }
+
+        return Ulid::isValid($value);
+    }
+
+    /**
+     * Convert a string to kebab case.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public static function kebab($value)
+    {
+        return static::snake($value, '-');
+    }
+
+    /**
+     * Return the length of the given string.
+     *
+     * @param  string  $value
+     * @param  string|null  $encoding
+     * @return int
+     */
+    public static function length($value, $encoding = null)
+    {
+        if ($encoding) {
+            return mb_strlen($value, $encoding);
+        }
+
+        return mb_strlen($value);
+    }
+
+    /**
+     * Limit the number of characters in a string.
+     *
+     * @param  string  $value
+     * @param  int  $limit
+     * @param  string  $end
+     * @return string
+     */
+    public static function limit($value, $limit = 100, $end = '...')
+    {
+        if (mb_strwidth($value, 'UTF-8') <= $limit) {
+            return $value;
+        }
+
+        return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end;
+    }
+
+    /**
+     * Convert the given string to lower-case.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public static function lower($value)
+    {
+        return mb_strtolower($value, 'UTF-8');
+    }
+
+    /**
+     * Limit the number of words in a string.
+     *
+     * @param  string  $value
+     * @param  int  $words
+     * @param  string  $end
+     * @return string
+     */
+    public static function words($value, $words = 100, $end = '...')
+    {
+        preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches);
+
+        if (! isset($matches[0]) || static::length($value) === static::length($matches[0])) {
+            return $value;
+        }
+
+        return rtrim($matches[0]).$end;
+    }
+
+    /**
+     * Converts GitHub flavored Markdown into HTML.
+     *
+     * @param  string  $string
+     * @param  array  $options
+     * @return string
+     */
+    public static function markdown($string, array $options = [])
+    {
+        $converter = new GithubFlavoredMarkdownConverter($options);
+
+        return (string) $converter->convert($string);
+    }
+
+    /**
+     * Converts inline Markdown into HTML.
+     *
+     * @param  string  $string
+     * @param  array  $options
+     * @return string
+     */
+    public static function inlineMarkdown($string, array $options = [])
+    {
+        $environment = new Environment($options);
+
+        $environment->addExtension(new GithubFlavoredMarkdownExtension());
+        $environment->addExtension(new InlinesOnlyExtension());
+
+        $converter = new MarkdownConverter($environment);
+
+        return (string) $converter->convert($string);
+    }
+
+    /**
+     * Masks a portion of a string with a repeated character.
+     *
+     * @param  string  $string
+     * @param  string  $character
+     * @param  int  $index
+     * @param  int|null  $length
+     * @param  string  $encoding
+     * @return string
+     */
+    public static function mask($string, $character, $index, $length = null, $encoding = 'UTF-8')
+    {
+        if ($character === '') {
+            return $string;
+        }
+
+        $segment = mb_substr($string, $index, $length, $encoding);
+
+        if ($segment === '') {
+            return $string;
+        }
+
+        $strlen = mb_strlen($string, $encoding);
+        $startIndex = $index;
+
+        if ($index < 0) {
+            $startIndex = $index < -$strlen ? 0 : $strlen + $index;
+        }
+
+        $start = mb_substr($string, 0, $startIndex, $encoding);
+        $segmentLen = mb_strlen($segment, $encoding);
+        $end = mb_substr($string, $startIndex + $segmentLen);
+
+        return $start.str_repeat(mb_substr($character, 0, 1, $encoding), $segmentLen).$end;
+    }
+
+    /**
+     * Get the string matching the given pattern.
+     *
+     * @param  string  $pattern
+     * @param  string  $subject
+     * @return string
+     */
+    public static function match($pattern, $subject)
+    {
+        preg_match($pattern, $subject, $matches);
+
+        if (! $matches) {
+            return '';
+        }
+
+        return $matches[1] ?? $matches[0];
+    }
+
+    /**
+     * Get the string matching the given pattern.
+     *
+     * @param  string  $pattern
+     * @param  string  $subject
+     * @return \Illuminate\Support\Collection
+     */
+    public static function matchAll($pattern, $subject)
+    {
+        preg_match_all($pattern, $subject, $matches);
+
+        if (empty($matches[0])) {
+            return collect();
+        }
+
+        return collect($matches[1] ?? $matches[0]);
+    }
+
+    /**
+     * Pad both sides of a string with another.
+     *
+     * @param  string  $value
+     * @param  int  $length
+     * @param  string  $pad
+     * @return string
+     */
+    public static function padBoth($value, $length, $pad = ' ')
+    {
+        $short = max(0, $length - mb_strlen($value));
+        $shortLeft = floor($short / 2);
+        $shortRight = ceil($short / 2);
+
+        return mb_substr(str_repeat($pad, $shortLeft), 0, $shortLeft).
+               $value.
+               mb_substr(str_repeat($pad, $shortRight), 0, $shortRight);
+    }
+
+    /**
+     * Pad the left side of a string with another.
+     *
+     * @param  string  $value
+     * @param  int  $length
+     * @param  string  $pad
+     * @return string
+     */
+    public static function padLeft($value, $length, $pad = ' ')
+    {
+        $short = max(0, $length - mb_strlen($value));
+
+        return mb_substr(str_repeat($pad, $short), 0, $short).$value;
+    }
+
+    /**
+     * Pad the right side of a string with another.
+     *
+     * @param  string  $value
+     * @param  int  $length
+     * @param  string  $pad
+     * @return string
+     */
+    public static function padRight($value, $length, $pad = ' ')
+    {
+        $short = max(0, $length - mb_strlen($value));
+
+        return $value.mb_substr(str_repeat($pad, $short), 0, $short);
+    }
+
+    /**
+     * Parse a Class[@]method style callback into class and method.
+     *
+     * @param  string  $callback
+     * @param  string|null  $default
+     * @return array
+     */
+    public static function parseCallback($callback, $default = null)
+    {
+        return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
+    }
+
+    /**
+     * Get the plural form of an English word.
+     *
+     * @param  string  $value
+     * @param  int|array|\Countable  $count
+     * @return string
+     */
+    public static function plural($value, $count = 2)
+    {
+        return Pluralizer::plural($value, $count);
+    }
+
+    /**
+     * Pluralize the last word of an English, studly caps case string.
+     *
+     * @param  string  $value
+     * @param  int|array|\Countable  $count
+     * @return string
+     */
+    public static function pluralStudly($value, $count = 2)
+    {
+        $parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+        $lastWord = array_pop($parts);
+
+        return implode('', $parts).self::plural($lastWord, $count);
+    }
+
+    /**
+     * Generate a more truly "random" alpha-numeric string.
+     *
+     * @param  int  $length
+     * @return string
+     */
+    public static function random($length = 16)
+    {
+        return (static::$randomStringFactory ?? function ($length) {
+            $string = '';
+
+            while (($len = strlen($string)) < $length) {
+                $size = $length - $len;
+
+                $bytesSize = (int) ceil($size / 3) * 3;
+
+                $bytes = random_bytes($bytesSize);
+
+                $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
+            }
+
+            return $string;
+        })($length);
+    }
+
+    /**
+     * Set the callable that will be used to generate random strings.
+     *
+     * @param  callable|null  $factory
+     * @return void
+     */
+    public static function createRandomStringsUsing(callable $factory = null)
+    {
+        static::$randomStringFactory = $factory;
+    }
+
+    /**
+     * Set the sequence that will be used to generate random strings.
+     *
+     * @param  array  $sequence
+     * @param  callable|null  $whenMissing
+     * @return void
+     */
+    public static function createRandomStringsUsingSequence(array $sequence, $whenMissing = null)
+    {
+        $next = 0;
+
+        $whenMissing ??= function ($length) use (&$next) {
+            $factoryCache = static::$randomStringFactory;
+
+            static::$randomStringFactory = null;
+
+            $randomString = static::random($length);
+
+            static::$randomStringFactory = $factoryCache;
+
+            $next++;
+
+            return $randomString;
+        };
+
+        static::createRandomStringsUsing(function ($length) use (&$next, $sequence, $whenMissing) {
+            if (array_key_exists($next, $sequence)) {
+                return $sequence[$next++];
+            }
+
+            return $whenMissing($length);
+        });
+    }
+
+    /**
+     * Indicate that random strings should be created normally and not using a custom factory.
+     *
+     * @return void
+     */
+    public static function createRandomStringsNormally()
+    {
+        static::$randomStringFactory = null;
+    }
+
+    /**
+     * Repeat the given string.
+     *
+     * @param  string  $string
+     * @param  int  $times
+     * @return string
+     */
+    public static function repeat(string $string, int $times)
+    {
+        return str_repeat($string, $times);
+    }
+
+    /**
+     * Replace a given value in the string sequentially with an array.
+     *
+     * @param  string  $search
+     * @param  iterable  $replace
+     * @param  string  $subject
+     * @return string
+     */
+    public static function replaceArray($search, $replace, $subject)
+    {
+        if ($replace instanceof Traversable) {
+            $replace = collect($replace)->all();
+        }
+
+        $segments = explode($search, $subject);
+
+        $result = array_shift($segments);
+
+        foreach ($segments as $segment) {
+            $result .= (array_shift($replace) ?? $search).$segment;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Replace the given value in the given string.
+     *
+     * @param  string|iterable  $search
+     * @param  string|iterable  $replace
+     * @param  string|iterable  $subject
+     * @return string
+     */
+    public static function replace($search, $replace, $subject)
+    {
+        if ($search instanceof Traversable) {
+            $search = collect($search)->all();
+        }
+
+        if ($replace instanceof Traversable) {
+            $replace = collect($replace)->all();
+        }
+
+        if ($subject instanceof Traversable) {
+            $subject = collect($subject)->all();
+        }
+
+        return str_replace($search, $replace, $subject);
+    }
+
+    /**
+     * Replace the first occurrence of a given value in the string.
+     *
+     * @param  string  $search
+     * @param  string  $replace
+     * @param  string  $subject
+     * @return string
+     */
+    public static function replaceFirst($search, $replace, $subject)
+    {
+        $search = (string) $search;
+
+        if ($search === '') {
+            return $subject;
+        }
+
+        $position = strpos($subject, $search);
+
+        if ($position !== false) {
+            return substr_replace($subject, $replace, $position, strlen($search));
+        }
+
+        return $subject;
+    }
+
+    /**
+     * Replace the last occurrence of a given value in the string.
+     *
+     * @param  string  $search
+     * @param  string  $replace
+     * @param  string  $subject
+     * @return string
+     */
+    public static function replaceLast($search, $replace, $subject)
+    {
+        if ($search === '') {
+            return $subject;
+        }
+
+        $position = strrpos($subject, $search);
+
+        if ($position !== false) {
+            return substr_replace($subject, $replace, $position, strlen($search));
+        }
+
+        return $subject;
+    }
+
+    /**
+     * Remove any occurrence of the given string in the subject.
+     *
+     * @param  string|iterable  $search
+     * @param  string  $subject
+     * @param  bool  $caseSensitive
+     * @return string
+     */
+    public static function remove($search, $subject, $caseSensitive = true)
+    {
+        if ($search instanceof Traversable) {
+            $search = collect($search)->all();
+        }
+
+        $subject = $caseSensitive
+                    ? str_replace($search, '', $subject)
+                    : str_ireplace($search, '', $subject);
+
+        return $subject;
+    }
+
+    /**
+     * Reverse the given string.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public static function reverse(string $value)
+    {
+        return implode(array_reverse(mb_str_split($value)));
+    }
+
+    /**
+     * Begin a string with a single instance of a given value.
+     *
+     * @param  string  $value
+     * @param  string  $prefix
+     * @return string
+     */
+    public static function start($value, $prefix)
+    {
+        $quoted = preg_quote($prefix, '/');
+
+        return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value);
+    }
+
+    /**
+     * Convert the given string to upper-case.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public static function upper($value)
+    {
+        return mb_strtoupper($value, 'UTF-8');
+    }
+
+    /**
+     * Convert the given string to title case.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public static function title($value)
+    {
+        return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
+    }
+
+    /**
+     * Convert the given string to title case for each word.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public static function headline($value)
+    {
+        $parts = explode(' ', $value);
+
+        $parts = count($parts) > 1
+            ? array_map([static::class, 'title'], $parts)
+            : array_map([static::class, 'title'], static::ucsplit(implode('_', $parts)));
+
+        $collapsed = static::replace(['-', '_', ' '], '_', implode('_', $parts));
+
+        return implode(' ', array_filter(explode('_', $collapsed)));
+    }
+
+    /**
+     * Get the singular form of an English word.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public static function singular($value)
+    {
+        return Pluralizer::singular($value);
+    }
+
+    /**
+     * Generate a URL friendly "slug" from a given string.
+     *
+     * @param  string  $title
+     * @param  string  $separator
+     * @param  string|null  $language
+     * @param  array  $dictionary
+     * @return string
+     */
+    public static function slug($title, $separator = '-', $language = 'en', $dictionary = ['@' => 'at'])
+    {
+        $title = $language ? static::ascii($title, $language) : $title;
+
+        // Convert all dashes/underscores into separator
+        $flip = $separator === '-' ? '_' : '-';
+
+        $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title);
+
+        // Replace dictionary words
+        foreach ($dictionary as $key => $value) {
+            $dictionary[$key] = $separator.$value.$separator;
+        }
+
+        $title = str_replace(array_keys($dictionary), array_values($dictionary), $title);
+
+        // Remove all characters that are not the separator, letters, numbers, or whitespace
+        $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', static::lower($title));
+
+        // Replace all separator characters and whitespace by a single separator
+        $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);
+
+        return trim($title, $separator);
+    }
+
+    /**
+     * Convert a string to snake case.
+     *
+     * @param  string  $value
+     * @param  string  $delimiter
+     * @return string
+     */
+    public static function snake($value, $delimiter = '_')
+    {
+        $key = $value;
+
+        if (isset(static::$snakeCache[$key][$delimiter])) {
+            return static::$snakeCache[$key][$delimiter];
+        }
+
+        if (! ctype_lower($value)) {
+            $value = preg_replace('/\s+/u', '', ucwords($value));
+
+            $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
+        }
+
+        return static::$snakeCache[$key][$delimiter] = $value;
+    }
+
+    /**
+     * Remove all "extra" blank space from the given string.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public static function squish($value)
+    {
+        return preg_replace('~(\s|\x{3164})+~u', ' ', preg_replace('~^[\s\x{FEFF}]+|[\s\x{FEFF}]+$~u', '', $value));
+    }
+
+    /**
+     * Determine if a given string starts with a given substring.
+     *
+     * @param  string  $haystack
+     * @param  string|iterable  $needles
+     * @return bool
+     */
+    public static function startsWith($haystack, $needles)
+    {
+        if (! is_iterable($needles)) {
+            $needles = [$needles];
+        }
+
+        foreach ($needles as $needle) {
+            if ((string) $needle !== '' && str_starts_with($haystack, $needle)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Convert a value to studly caps case.
+     *
+     * @param  string  $value
+     * @return string
+     */
+    public static function studly($value)
+    {
+        $key = $value;
+
+        if (isset(static::$studlyCache[$key])) {
+            return static::$studlyCache[$key];
+        }
+
+        $words = explode(' ', static::replace(['-', '_'], ' ', $value));
+
+        $studlyWords = array_map(fn ($word) => static::ucfirst($word), $words);
+
+        return static::$studlyCache[$key] = implode($studlyWords);
+    }
+
+    /**
+     * Returns the portion of the string specified by the start and length parameters.
+     *
+     * @param  string  $string
+     * @param  int  $start
+     * @param  int|null  $length
+     * @param  string  $encoding
+     * @return string
+     */
+    public static function substr($string, $start, $length = null, $encoding = 'UTF-8')
+    {
+        return mb_substr($string, $start, $length, $encoding);
+    }
+
+    /**
+     * Returns the number of substring occurrences.
+     *
+     * @param  string  $haystack
+     * @param  string  $needle
+     * @param  int  $offset
+     * @param  int|null  $length
+     * @return int
+     */
+    public static function substrCount($haystack, $needle, $offset = 0, $length = null)
+    {
+        if (! is_null($length)) {
+            return substr_count($haystack, $needle, $offset, $length);
+        }
+
+        return substr_count($haystack, $needle, $offset);
+    }
+
+    /**
+     * Replace text within a portion of a string.
+     *
+     * @param  string|string[]  $string
+     * @param  string|string[]  $replace
+     * @param  int|int[]  $offset
+     * @param  int|int[]|null  $length
+     * @return string|string[]
+     */
+    public static function substrReplace($string, $replace, $offset = 0, $length = null)
+    {
+        if ($length === null) {
+            $length = strlen($string);
+        }
+
+        return substr_replace($string, $replace, $offset, $length);
+    }
+
+    /**
+     * Swap multiple keywords in a string with other keywords.
+     *
+     * @param  array  $map
+     * @param  string  $subject
+     * @return string
+     */
+    public static function swap(array $map, $subject)
+    {
+        return strtr($subject, $map);
+    }
+
+    /**
+     * Make a string's first character lowercase.
+     *
+     * @param  string  $string
+     * @return string
+     */
+    public static function lcfirst($string)
+    {
+        return static::lower(static::substr($string, 0, 1)).static::substr($string, 1);
+    }
+
+    /**
+     * Make a string's first character uppercase.
+     *
+     * @param  string  $string
+     * @return string
+     */
+    public static function ucfirst($string)
+    {
+        return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);
+    }
+
+    /**
+     * Split a string into pieces by uppercase characters.
+     *
+     * @param  string  $string
+     * @return string[]
+     */
+    public static function ucsplit($string)
+    {
+        return preg_split('/(?=\p{Lu})/u', $string, -1, PREG_SPLIT_NO_EMPTY);
+    }
+
+    /**
+     * Get the number of words a string contains.
+     *
+     * @param  string  $string
+     * @param  string|null  $characters
+     * @return int
+     */
+    public static function wordCount($string, $characters = null)
+    {
+        return str_word_count($string, 0, $characters);
+    }
+
+    /**
+     * Generate a UUID (version 4).
+     *
+     * @return \Ramsey\Uuid\UuidInterface
+     */
+    public static function uuid()
+    {
+        return static::$uuidFactory
+                    ? call_user_func(static::$uuidFactory)
+                    : Uuid::uuid4();
+    }
+
+    /**
+     * Generate a time-ordered UUID (version 4).
+     *
+     * @return \Ramsey\Uuid\UuidInterface
+     */
+    public static function orderedUuid()
+    {
+        if (static::$uuidFactory) {
+            return call_user_func(static::$uuidFactory);
+        }
+
+        $factory = new UuidFactory;
+
+        $factory->setRandomGenerator(new CombGenerator(
+            $factory->getRandomGenerator(),
+            $factory->getNumberConverter()
+        ));
+
+        $factory->setCodec(new TimestampFirstCombCodec(
+            $factory->getUuidBuilder()
+        ));
+
+        return $factory->uuid4();
+    }
+
+    /**
+     * Set the callable that will be used to generate UUIDs.
+     *
+     * @param  callable|null  $factory
+     * @return void
+     */
+    public static function createUuidsUsing(callable $factory = null)
+    {
+        static::$uuidFactory = $factory;
+    }
+
+    /**
+     * Set the sequence that will be used to generate UUIDs.
+     *
+     * @param  array  $sequence
+     * @param  callable|null  $whenMissing
+     * @return void
+     */
+    public static function createUuidsUsingSequence(array $sequence, $whenMissing = null)
+    {
+        $next = 0;
+
+        $whenMissing ??= function () use (&$next) {
+            $factoryCache = static::$uuidFactory;
+
+            static::$uuidFactory = null;
+
+            $uuid = static::uuid();
+
+            static::$uuidFactory = $factoryCache;
+
+            $next++;
+
+            return $uuid;
+        };
+
+        static::createUuidsUsing(function () use (&$next, $sequence, $whenMissing) {
+            if (array_key_exists($next, $sequence)) {
+                return $sequence[$next++];
+            }
+
+            return $whenMissing();
+        });
+    }
+
+    /**
+     * Always return the same UUID when generating new UUIDs.
+     *
+     * @param  \Closure|null  $callback
+     * @return \Ramsey\Uuid\UuidInterface
+     */
+    public static function freezeUuids(Closure $callback = null)
+    {
+        $uuid = Str::uuid();
+
+        Str::createUuidsUsing(fn () => $uuid);
+
+        if ($callback !== null) {
+            try {
+                $callback($uuid);
+            } finally {
+                Str::createUuidsNormally();
+            }
+        }
+
+        return $uuid;
+    }
+
+    /**
+     * Indicate that UUIDs should be created normally and not using a custom factory.
+     *
+     * @return void
+     */
+    public static function createUuidsNormally()
+    {
+        static::$uuidFactory = null;
+    }
+
+    /**
+     * Generate a ULID.
+     *
+     * @return \Symfony\Component\Uid\Ulid
+     */
+    public static function ulid()
+    {
+        return new Ulid();
+    }
+
+    /**
+     * Remove all strings from the casing caches.
+     *
+     * @return void
+     */
+    public static function flushCache()
+    {
+        static::$snakeCache = [];
+        static::$camelCache = [];
+        static::$studlyCache = [];
+    }
+}
diff --git a/vendor/illuminate/support/Stringable.php b/vendor/illuminate/support/Stringable.php
new file mode 100644
index 0000000..7453570
--- /dev/null
+++ b/vendor/illuminate/support/Stringable.php
@@ -0,0 +1,1228 @@
+value = (string) $value;
+    }
+
+    /**
+     * Return the remainder of a string after the first occurrence of a given value.
+     *
+     * @param  string  $search
+     * @return static
+     */
+    public function after($search)
+    {
+        return new static(Str::after($this->value, $search));
+    }
+
+    /**
+     * Return the remainder of a string after the last occurrence of a given value.
+     *
+     * @param  string  $search
+     * @return static
+     */
+    public function afterLast($search)
+    {
+        return new static(Str::afterLast($this->value, $search));
+    }
+
+    /**
+     * Append the given values to the string.
+     *
+     * @param  string  ...$values
+     * @return static
+     */
+    public function append(...$values)
+    {
+        return new static($this->value.implode('', $values));
+    }
+
+    /**
+     * Append a new line to the string.
+     *
+     * @param  int  $count
+     * @return $this
+     */
+    public function newLine($count = 1)
+    {
+        return $this->append(str_repeat(PHP_EOL, $count));
+    }
+
+    /**
+     * Transliterate a UTF-8 value to ASCII.
+     *
+     * @param  string  $language
+     * @return static
+     */
+    public function ascii($language = 'en')
+    {
+        return new static(Str::ascii($this->value, $language));
+    }
+
+    /**
+     * Get the trailing name component of the path.
+     *
+     * @param  string  $suffix
+     * @return static
+     */
+    public function basename($suffix = '')
+    {
+        return new static(basename($this->value, $suffix));
+    }
+
+    /**
+     * Get the basename of the class path.
+     *
+     * @return static
+     */
+    public function classBasename()
+    {
+        return new static(class_basename($this->value));
+    }
+
+    /**
+     * Get the portion of a string before the first occurrence of a given value.
+     *
+     * @param  string  $search
+     * @return static
+     */
+    public function before($search)
+    {
+        return new static(Str::before($this->value, $search));
+    }
+
+    /**
+     * Get the portion of a string before the last occurrence of a given value.
+     *
+     * @param  string  $search
+     * @return static
+     */
+    public function beforeLast($search)
+    {
+        return new static(Str::beforeLast($this->value, $search));
+    }
+
+    /**
+     * Get the portion of a string between two given values.
+     *
+     * @param  string  $from
+     * @param  string  $to
+     * @return static
+     */
+    public function between($from, $to)
+    {
+        return new static(Str::between($this->value, $from, $to));
+    }
+
+    /**
+     * Get the smallest possible portion of a string between two given values.
+     *
+     * @param  string  $from
+     * @param  string  $to
+     * @return static
+     */
+    public function betweenFirst($from, $to)
+    {
+        return new static(Str::betweenFirst($this->value, $from, $to));
+    }
+
+    /**
+     * Convert a value to camel case.
+     *
+     * @return static
+     */
+    public function camel()
+    {
+        return new static(Str::camel($this->value));
+    }
+
+    /**
+     * Determine if a given string contains a given substring.
+     *
+     * @param  string|iterable  $needles
+     * @param  bool  $ignoreCase
+     * @return bool
+     */
+    public function contains($needles, $ignoreCase = false)
+    {
+        return Str::contains($this->value, $needles, $ignoreCase);
+    }
+
+    /**
+     * Determine if a given string contains all array values.
+     *
+     * @param  iterable  $needles
+     * @param  bool  $ignoreCase
+     * @return bool
+     */
+    public function containsAll($needles, $ignoreCase = false)
+    {
+        return Str::containsAll($this->value, $needles, $ignoreCase);
+    }
+
+    /**
+     * Get the parent directory's path.
+     *
+     * @param  int  $levels
+     * @return static
+     */
+    public function dirname($levels = 1)
+    {
+        return new static(dirname($this->value, $levels));
+    }
+
+    /**
+     * Determine if a given string ends with a given substring.
+     *
+     * @param  string|iterable  $needles
+     * @return bool
+     */
+    public function endsWith($needles)
+    {
+        return Str::endsWith($this->value, $needles);
+    }
+
+    /**
+     * Determine if the string is an exact match with the given value.
+     *
+     * @param  \Illuminate\Support\Stringable|string  $value
+     * @return bool
+     */
+    public function exactly($value)
+    {
+        if ($value instanceof Stringable) {
+            $value = $value->toString();
+        }
+
+        return $this->value === $value;
+    }
+
+    /**
+     * Extracts an excerpt from text that matches the first instance of a phrase.
+     *
+     * @param  string  $phrase
+     * @param  array  $options
+     * @return string|null
+     */
+    public function excerpt($phrase = '', $options = [])
+    {
+        return Str::excerpt($this->value, $phrase, $options);
+    }
+
+    /**
+     * Explode the string into an array.
+     *
+     * @param  string  $delimiter
+     * @param  int  $limit
+     * @return \Illuminate\Support\Collection
+     */
+    public function explode($delimiter, $limit = PHP_INT_MAX)
+    {
+        return collect(explode($delimiter, $this->value, $limit));
+    }
+
+    /**
+     * Split a string using a regular expression or by length.
+     *
+     * @param  string|int  $pattern
+     * @param  int  $limit
+     * @param  int  $flags
+     * @return \Illuminate\Support\Collection
+     */
+    public function split($pattern, $limit = -1, $flags = 0)
+    {
+        if (filter_var($pattern, FILTER_VALIDATE_INT) !== false) {
+            return collect(mb_str_split($this->value, $pattern));
+        }
+
+        $segments = preg_split($pattern, $this->value, $limit, $flags);
+
+        return ! empty($segments) ? collect($segments) : collect();
+    }
+
+    /**
+     * Cap a string with a single instance of a given value.
+     *
+     * @param  string  $cap
+     * @return static
+     */
+    public function finish($cap)
+    {
+        return new static(Str::finish($this->value, $cap));
+    }
+
+    /**
+     * Determine if a given string matches a given pattern.
+     *
+     * @param  string|iterable  $pattern
+     * @return bool
+     */
+    public function is($pattern)
+    {
+        return Str::is($pattern, $this->value);
+    }
+
+    /**
+     * Determine if a given string is 7 bit ASCII.
+     *
+     * @return bool
+     */
+    public function isAscii()
+    {
+        return Str::isAscii($this->value);
+    }
+
+    /**
+     * Determine if a given string is valid JSON.
+     *
+     * @return bool
+     */
+    public function isJson()
+    {
+        return Str::isJson($this->value);
+    }
+
+    /**
+     * Determine if a given string is a valid UUID.
+     *
+     * @return bool
+     */
+    public function isUuid()
+    {
+        return Str::isUuid($this->value);
+    }
+
+    /**
+     * Determine if a given string is a valid ULID.
+     *
+     * @return bool
+     */
+    public function isUlid()
+    {
+        return Str::isUlid($this->value);
+    }
+
+    /**
+     * Determine if the given string is empty.
+     *
+     * @return bool
+     */
+    public function isEmpty()
+    {
+        return $this->value === '';
+    }
+
+    /**
+     * Determine if the given string is not empty.
+     *
+     * @return bool
+     */
+    public function isNotEmpty()
+    {
+        return ! $this->isEmpty();
+    }
+
+    /**
+     * Convert a string to kebab case.
+     *
+     * @return static
+     */
+    public function kebab()
+    {
+        return new static(Str::kebab($this->value));
+    }
+
+    /**
+     * Return the length of the given string.
+     *
+     * @param  string|null  $encoding
+     * @return int
+     */
+    public function length($encoding = null)
+    {
+        return Str::length($this->value, $encoding);
+    }
+
+    /**
+     * Limit the number of characters in a string.
+     *
+     * @param  int  $limit
+     * @param  string  $end
+     * @return static
+     */
+    public function limit($limit = 100, $end = '...')
+    {
+        return new static(Str::limit($this->value, $limit, $end));
+    }
+
+    /**
+     * Convert the given string to lower-case.
+     *
+     * @return static
+     */
+    public function lower()
+    {
+        return new static(Str::lower($this->value));
+    }
+
+    /**
+     * Convert GitHub flavored Markdown into HTML.
+     *
+     * @param  array  $options
+     * @return static
+     */
+    public function markdown(array $options = [])
+    {
+        return new static(Str::markdown($this->value, $options));
+    }
+
+    /**
+     * Convert inline Markdown into HTML.
+     *
+     * @param  array  $options
+     * @return static
+     */
+    public function inlineMarkdown(array $options = [])
+    {
+        return new static(Str::inlineMarkdown($this->value, $options));
+    }
+
+    /**
+     * Masks a portion of a string with a repeated character.
+     *
+     * @param  string  $character
+     * @param  int  $index
+     * @param  int|null  $length
+     * @param  string  $encoding
+     * @return static
+     */
+    public function mask($character, $index, $length = null, $encoding = 'UTF-8')
+    {
+        return new static(Str::mask($this->value, $character, $index, $length, $encoding));
+    }
+
+    /**
+     * Get the string matching the given pattern.
+     *
+     * @param  string  $pattern
+     * @return static
+     */
+    public function match($pattern)
+    {
+        return new static(Str::match($pattern, $this->value));
+    }
+
+    /**
+     * Get the string matching the given pattern.
+     *
+     * @param  string  $pattern
+     * @return \Illuminate\Support\Collection
+     */
+    public function matchAll($pattern)
+    {
+        return Str::matchAll($pattern, $this->value);
+    }
+
+    /**
+     * Determine if the string matches the given pattern.
+     *
+     * @param  string  $pattern
+     * @return bool
+     */
+    public function test($pattern)
+    {
+        return $this->match($pattern)->isNotEmpty();
+    }
+
+    /**
+     * Pad both sides of the string with another.
+     *
+     * @param  int  $length
+     * @param  string  $pad
+     * @return static
+     */
+    public function padBoth($length, $pad = ' ')
+    {
+        return new static(Str::padBoth($this->value, $length, $pad));
+    }
+
+    /**
+     * Pad the left side of the string with another.
+     *
+     * @param  int  $length
+     * @param  string  $pad
+     * @return static
+     */
+    public function padLeft($length, $pad = ' ')
+    {
+        return new static(Str::padLeft($this->value, $length, $pad));
+    }
+
+    /**
+     * Pad the right side of the string with another.
+     *
+     * @param  int  $length
+     * @param  string  $pad
+     * @return static
+     */
+    public function padRight($length, $pad = ' ')
+    {
+        return new static(Str::padRight($this->value, $length, $pad));
+    }
+
+    /**
+     * Parse a Class@method style callback into class and method.
+     *
+     * @param  string|null  $default
+     * @return array
+     */
+    public function parseCallback($default = null)
+    {
+        return Str::parseCallback($this->value, $default);
+    }
+
+    /**
+     * Call the given callback and return a new string.
+     *
+     * @param  callable  $callback
+     * @return static
+     */
+    public function pipe(callable $callback)
+    {
+        return new static($callback($this));
+    }
+
+    /**
+     * Get the plural form of an English word.
+     *
+     * @param  int|array|\Countable  $count
+     * @return static
+     */
+    public function plural($count = 2)
+    {
+        return new static(Str::plural($this->value, $count));
+    }
+
+    /**
+     * Pluralize the last word of an English, studly caps case string.
+     *
+     * @param  int|array|\Countable  $count
+     * @return static
+     */
+    public function pluralStudly($count = 2)
+    {
+        return new static(Str::pluralStudly($this->value, $count));
+    }
+
+    /**
+     * Prepend the given values to the string.
+     *
+     * @param  string  ...$values
+     * @return static
+     */
+    public function prepend(...$values)
+    {
+        return new static(implode('', $values).$this->value);
+    }
+
+    /**
+     * Remove any occurrence of the given string in the subject.
+     *
+     * @param  string|iterable  $search
+     * @param  bool  $caseSensitive
+     * @return static
+     */
+    public function remove($search, $caseSensitive = true)
+    {
+        return new static(Str::remove($search, $this->value, $caseSensitive));
+    }
+
+    /**
+     * Reverse the string.
+     *
+     * @return static
+     */
+    public function reverse()
+    {
+        return new static(Str::reverse($this->value));
+    }
+
+    /**
+     * Repeat the string.
+     *
+     * @param  int  $times
+     * @return static
+     */
+    public function repeat(int $times)
+    {
+        return new static(str_repeat($this->value, $times));
+    }
+
+    /**
+     * Replace the given value in the given string.
+     *
+     * @param  string|iterable  $search
+     * @param  string|iterable  $replace
+     * @return static
+     */
+    public function replace($search, $replace)
+    {
+        return new static(Str::replace($search, $replace, $this->value));
+    }
+
+    /**
+     * Replace a given value in the string sequentially with an array.
+     *
+     * @param  string  $search
+     * @param  iterable  $replace
+     * @return static
+     */
+    public function replaceArray($search, $replace)
+    {
+        return new static(Str::replaceArray($search, $replace, $this->value));
+    }
+
+    /**
+     * Replace the first occurrence of a given value in the string.
+     *
+     * @param  string  $search
+     * @param  string  $replace
+     * @return static
+     */
+    public function replaceFirst($search, $replace)
+    {
+        return new static(Str::replaceFirst($search, $replace, $this->value));
+    }
+
+    /**
+     * Replace the last occurrence of a given value in the string.
+     *
+     * @param  string  $search
+     * @param  string  $replace
+     * @return static
+     */
+    public function replaceLast($search, $replace)
+    {
+        return new static(Str::replaceLast($search, $replace, $this->value));
+    }
+
+    /**
+     * Replace the patterns matching the given regular expression.
+     *
+     * @param  string  $pattern
+     * @param  \Closure|string  $replace
+     * @param  int  $limit
+     * @return static
+     */
+    public function replaceMatches($pattern, $replace, $limit = -1)
+    {
+        if ($replace instanceof Closure) {
+            return new static(preg_replace_callback($pattern, $replace, $this->value, $limit));
+        }
+
+        return new static(preg_replace($pattern, $replace, $this->value, $limit));
+    }
+
+    /**
+     * Parse input from a string to a collection, according to a format.
+     *
+     * @param  string  $format
+     * @return \Illuminate\Support\Collection
+     */
+    public function scan($format)
+    {
+        return collect(sscanf($this->value, $format));
+    }
+
+    /**
+     * Remove all "extra" blank space from the given string.
+     *
+     * @return static
+     */
+    public function squish()
+    {
+        return new static(Str::squish($this->value));
+    }
+
+    /**
+     * Begin a string with a single instance of a given value.
+     *
+     * @param  string  $prefix
+     * @return static
+     */
+    public function start($prefix)
+    {
+        return new static(Str::start($this->value, $prefix));
+    }
+
+    /**
+     * Strip HTML and PHP tags from the given string.
+     *
+     * @param  string  $allowedTags
+     * @return static
+     */
+    public function stripTags($allowedTags = null)
+    {
+        return new static(strip_tags($this->value, $allowedTags));
+    }
+
+    /**
+     * Convert the given string to upper-case.
+     *
+     * @return static
+     */
+    public function upper()
+    {
+        return new static(Str::upper($this->value));
+    }
+
+    /**
+     * Convert the given string to title case.
+     *
+     * @return static
+     */
+    public function title()
+    {
+        return new static(Str::title($this->value));
+    }
+
+    /**
+     * Convert the given string to title case for each word.
+     *
+     * @return static
+     */
+    public function headline()
+    {
+        return new static(Str::headline($this->value));
+    }
+
+    /**
+     * Get the singular form of an English word.
+     *
+     * @return static
+     */
+    public function singular()
+    {
+        return new static(Str::singular($this->value));
+    }
+
+    /**
+     * Generate a URL friendly "slug" from a given string.
+     *
+     * @param  string  $separator
+     * @param  string|null  $language
+     * @param  array  $dictionary
+     * @return static
+     */
+    public function slug($separator = '-', $language = 'en', $dictionary = ['@' => 'at'])
+    {
+        return new static(Str::slug($this->value, $separator, $language, $dictionary));
+    }
+
+    /**
+     * Convert a string to snake case.
+     *
+     * @param  string  $delimiter
+     * @return static
+     */
+    public function snake($delimiter = '_')
+    {
+        return new static(Str::snake($this->value, $delimiter));
+    }
+
+    /**
+     * Determine if a given string starts with a given substring.
+     *
+     * @param  string|iterable  $needles
+     * @return bool
+     */
+    public function startsWith($needles)
+    {
+        return Str::startsWith($this->value, $needles);
+    }
+
+    /**
+     * Convert a value to studly caps case.
+     *
+     * @return static
+     */
+    public function studly()
+    {
+        return new static(Str::studly($this->value));
+    }
+
+    /**
+     * Returns the portion of the string specified by the start and length parameters.
+     *
+     * @param  int  $start
+     * @param  int|null  $length
+     * @param  string  $encoding
+     * @return static
+     */
+    public function substr($start, $length = null, $encoding = 'UTF-8')
+    {
+        return new static(Str::substr($this->value, $start, $length, $encoding));
+    }
+
+    /**
+     * Returns the number of substring occurrences.
+     *
+     * @param  string  $needle
+     * @param  int  $offset
+     * @param  int|null  $length
+     * @return int
+     */
+    public function substrCount($needle, $offset = 0, $length = null)
+    {
+        return Str::substrCount($this->value, $needle, $offset, $length);
+    }
+
+    /**
+     * Replace text within a portion of a string.
+     *
+     * @param  string|string[]  $replace
+     * @param  int|int[]  $offset
+     * @param  int|int[]|null  $length
+     * @return static
+     */
+    public function substrReplace($replace, $offset = 0, $length = null)
+    {
+        return new static(Str::substrReplace($this->value, $replace, $offset, $length));
+    }
+
+    /**
+     * Swap multiple keywords in a string with other keywords.
+     *
+     * @param  array  $map
+     * @return static
+     */
+    public function swap(array $map)
+    {
+        return new static(strtr($this->value, $map));
+    }
+
+    /**
+     * Trim the string of the given characters.
+     *
+     * @param  string  $characters
+     * @return static
+     */
+    public function trim($characters = null)
+    {
+        return new static(trim(...array_merge([$this->value], func_get_args())));
+    }
+
+    /**
+     * Left trim the string of the given characters.
+     *
+     * @param  string  $characters
+     * @return static
+     */
+    public function ltrim($characters = null)
+    {
+        return new static(ltrim(...array_merge([$this->value], func_get_args())));
+    }
+
+    /**
+     * Right trim the string of the given characters.
+     *
+     * @param  string  $characters
+     * @return static
+     */
+    public function rtrim($characters = null)
+    {
+        return new static(rtrim(...array_merge([$this->value], func_get_args())));
+    }
+
+    /**
+     * Make a string's first character lowercase.
+     *
+     * @return static
+     */
+    public function lcfirst()
+    {
+        return new static(Str::lcfirst($this->value));
+    }
+
+    /**
+     * Make a string's first character uppercase.
+     *
+     * @return static
+     */
+    public function ucfirst()
+    {
+        return new static(Str::ucfirst($this->value));
+    }
+
+    /**
+     * Split a string by uppercase characters.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function ucsplit()
+    {
+        return collect(Str::ucsplit($this->value));
+    }
+
+    /**
+     * Execute the given callback if the string contains a given substring.
+     *
+     * @param  string|iterable  $needles
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenContains($needles, $callback, $default = null)
+    {
+        return $this->when($this->contains($needles), $callback, $default);
+    }
+
+    /**
+     * Execute the given callback if the string contains all array values.
+     *
+     * @param  iterable  $needles
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenContainsAll(array $needles, $callback, $default = null)
+    {
+        return $this->when($this->containsAll($needles), $callback, $default);
+    }
+
+    /**
+     * Execute the given callback if the string is empty.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenEmpty($callback, $default = null)
+    {
+        return $this->when($this->isEmpty(), $callback, $default);
+    }
+
+    /**
+     * Execute the given callback if the string is not empty.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenNotEmpty($callback, $default = null)
+    {
+        return $this->when($this->isNotEmpty(), $callback, $default);
+    }
+
+    /**
+     * Execute the given callback if the string ends with a given substring.
+     *
+     * @param  string|iterable  $needles
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenEndsWith($needles, $callback, $default = null)
+    {
+        return $this->when($this->endsWith($needles), $callback, $default);
+    }
+
+    /**
+     * Execute the given callback if the string is an exact match with the given value.
+     *
+     * @param  string  $value
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenExactly($value, $callback, $default = null)
+    {
+        return $this->when($this->exactly($value), $callback, $default);
+    }
+
+    /**
+     * Execute the given callback if the string is not an exact match with the given value.
+     *
+     * @param  string  $value
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenNotExactly($value, $callback, $default = null)
+    {
+        return $this->when(! $this->exactly($value), $callback, $default);
+    }
+
+    /**
+     * Execute the given callback if the string matches a given pattern.
+     *
+     * @param  string|iterable  $pattern
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenIs($pattern, $callback, $default = null)
+    {
+        return $this->when($this->is($pattern), $callback, $default);
+    }
+
+    /**
+     * Execute the given callback if the string is 7 bit ASCII.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenIsAscii($callback, $default = null)
+    {
+        return $this->when($this->isAscii(), $callback, $default);
+    }
+
+    /**
+     * Execute the given callback if the string is a valid UUID.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenIsUuid($callback, $default = null)
+    {
+        return $this->when($this->isUuid(), $callback, $default);
+    }
+
+    /**
+     * Execute the given callback if the string is a valid ULID.
+     *
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenIsUlid($callback, $default = null)
+    {
+        return $this->when($this->isUlid(), $callback, $default);
+    }
+
+    /**
+     * Execute the given callback if the string starts with a given substring.
+     *
+     * @param  string|iterable  $needles
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenStartsWith($needles, $callback, $default = null)
+    {
+        return $this->when($this->startsWith($needles), $callback, $default);
+    }
+
+    /**
+     * Execute the given callback if the string matches the given pattern.
+     *
+     * @param  string  $pattern
+     * @param  callable  $callback
+     * @param  callable|null  $default
+     * @return static
+     */
+    public function whenTest($pattern, $callback, $default = null)
+    {
+        return $this->when($this->test($pattern), $callback, $default);
+    }
+
+    /**
+     * Limit the number of words in a string.
+     *
+     * @param  int  $words
+     * @param  string  $end
+     * @return static
+     */
+    public function words($words = 100, $end = '...')
+    {
+        return new static(Str::words($this->value, $words, $end));
+    }
+
+    /**
+     * Get the number of words a string contains.
+     *
+     * @param  string|null  $characters
+     * @return int
+     */
+    public function wordCount($characters = null)
+    {
+        return Str::wordCount($this->value, $characters);
+    }
+
+    /**
+     * Wrap the string with the given strings.
+     *
+     * @param  string  $before
+     * @param  string|null  $after
+     * @return static
+     */
+    public function wrap($before, $after = null)
+    {
+        return new static(Str::wrap($this->value, $before, $after));
+    }
+
+    /**
+     * Convert the string into a `HtmlString` instance.
+     *
+     * @return \Illuminate\Support\HtmlString
+     */
+    public function toHtmlString()
+    {
+        return new HtmlString($this->value);
+    }
+
+    /**
+     * Dump the string.
+     *
+     * @return $this
+     */
+    public function dump()
+    {
+        VarDumper::dump($this->value);
+
+        return $this;
+    }
+
+    /**
+     * Dump the string and end the script.
+     *
+     * @return never
+     */
+    public function dd()
+    {
+        $this->dump();
+
+        exit(1);
+    }
+
+    /**
+     * Get the underlying string value.
+     *
+     * @return string
+     */
+    public function value()
+    {
+        return $this->toString();
+    }
+
+    /**
+     * Get the underlying string value.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Get the underlying string value as an integer.
+     *
+     * @return int
+     */
+    public function toInteger()
+    {
+        return intval($this->value);
+    }
+
+    /**
+     * Get the underlying string value as a float.
+     *
+     * @return float
+     */
+    public function toFloat()
+    {
+        return floatval($this->value);
+    }
+
+    /**
+     * Get the underlying string value as a boolean.
+     *
+     * Returns true when value is "1", "true", "on", and "yes". Otherwise, returns false.
+     *
+     * @return bool
+     */
+    public function toBoolean()
+    {
+        return filter_var($this->value, FILTER_VALIDATE_BOOLEAN);
+    }
+
+    /**
+     * Get the underlying string value as a Carbon instance.
+     *
+     * @param  string|null  $format
+     * @param  string|null  $tz
+     * @return \Illuminate\Support\Carbon
+     *
+     * @throws \Carbon\Exceptions\InvalidFormatException
+     */
+    public function toDate($format = null, $tz = null)
+    {
+        if (is_null($format)) {
+            return Date::parse($this->value, $tz);
+        }
+
+        return Date::createFromFormat($format, $this->value, $tz);
+    }
+
+    /**
+     * Convert the object to a string when JSON encoded.
+     *
+     * @return string
+     */
+    public function jsonSerialize(): string
+    {
+        return $this->__toString();
+    }
+
+    /**
+     * Proxy dynamic properties onto methods.
+     *
+     * @param  string  $key
+     * @return mixed
+     */
+    public function __get($key)
+    {
+        return $this->{$key}();
+    }
+
+    /**
+     * Get the raw string value.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return (string) $this->value;
+    }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/BatchFake.php b/vendor/illuminate/support/Testing/Fakes/BatchFake.php
new file mode 100644
index 0000000..607168b
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/BatchFake.php
@@ -0,0 +1,163 @@
+id = $id;
+        $this->name = $name;
+        $this->totalJobs = $totalJobs;
+        $this->pendingJobs = $pendingJobs;
+        $this->failedJobs = $failedJobs;
+        $this->failedJobIds = $failedJobIds;
+        $this->options = $options;
+        $this->createdAt = $createdAt;
+        $this->cancelledAt = $cancelledAt;
+        $this->finishedAt = $finishedAt;
+    }
+
+    /**
+     * Get a fresh instance of the batch represented by this ID.
+     *
+     * @return self
+     */
+    public function fresh()
+    {
+        return $this;
+    }
+
+    /**
+     * Add additional jobs to the batch.
+     *
+     * @param  \Illuminate\Support\Enumerable|object|array  $jobs
+     * @return self
+     */
+    public function add($jobs)
+    {
+        foreach ($jobs as $job) {
+            $this->added[] = $job;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Record that a job within the batch finished successfully, executing any callbacks if necessary.
+     *
+     * @param  string  $jobId
+     * @return void
+     */
+    public function recordSuccessfulJob(string $jobId)
+    {
+        //
+    }
+
+    /**
+     * Decrement the pending jobs for the batch.
+     *
+     * @param  string  $jobId
+     * @return \Illuminate\Bus\UpdatedBatchJobCounts
+     */
+    public function decrementPendingJobs(string $jobId)
+    {
+        //
+    }
+
+    /**
+     * Record that a job within the batch failed to finish successfully, executing any callbacks if necessary.
+     *
+     * @param  string  $jobId
+     * @param  \Throwable  $e
+     * @return void
+     */
+    public function recordFailedJob(string $jobId, $e)
+    {
+        //
+    }
+
+    /**
+     * Increment the failed jobs for the batch.
+     *
+     * @param  string  $jobId
+     * @return \Illuminate\Bus\UpdatedBatchJobCounts
+     */
+    public function incrementFailedJobs(string $jobId)
+    {
+        return new UpdatedBatchJobCounts;
+    }
+
+    /**
+     * Cancel the batch.
+     *
+     * @return void
+     */
+    public function cancel()
+    {
+        $this->cancelledAt = Carbon::now();
+    }
+
+    /**
+     * Delete the batch from storage.
+     *
+     * @return void
+     */
+    public function delete()
+    {
+        $this->deleted = true;
+    }
+
+    /**
+     * Determine if the batch has been deleted.
+     *
+     * @return bool
+     */
+    public function deleted()
+    {
+        return $this->deleted;
+    }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/BatchRepositoryFake.php b/vendor/illuminate/support/Testing/Fakes/BatchRepositoryFake.php
new file mode 100644
index 0000000..021c73f
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/BatchRepositoryFake.php
@@ -0,0 +1,153 @@
+batches;
+    }
+
+    /**
+     * Retrieve information about an existing batch.
+     *
+     * @param  string  $batchId
+     * @return \Illuminate\Bus\Batch|null
+     */
+    public function find(string $batchId)
+    {
+        return $this->batches[$batchId] ?? null;
+    }
+
+    /**
+     * Store a new pending batch.
+     *
+     * @param  \Illuminate\Bus\PendingBatch  $batch
+     * @return \Illuminate\Bus\Batch
+     */
+    public function store(PendingBatch $batch)
+    {
+        $id = (string) Str::orderedUuid();
+
+        $this->batches[$id] = new BatchFake(
+            $id,
+            $batch->name,
+            count($batch->jobs),
+            count($batch->jobs),
+            0,
+            [],
+            $batch->options,
+            CarbonImmutable::now(),
+            null,
+            null
+        );
+
+        return $this->batches[$id];
+    }
+
+    /**
+     * Increment the total number of jobs within the batch.
+     *
+     * @param  string  $batchId
+     * @param  int  $amount
+     * @return void
+     */
+    public function incrementTotalJobs(string $batchId, int $amount)
+    {
+        //
+    }
+
+    /**
+     * Decrement the total number of pending jobs for the batch.
+     *
+     * @param  string  $batchId
+     * @param  string  $jobId
+     * @return \Illuminate\Bus\UpdatedBatchJobCounts
+     */
+    public function decrementPendingJobs(string $batchId, string $jobId)
+    {
+        return new UpdatedBatchJobCounts;
+    }
+
+    /**
+     * Increment the total number of failed jobs for the batch.
+     *
+     * @param  string  $batchId
+     * @param  string  $jobId
+     * @return \Illuminate\Bus\UpdatedBatchJobCounts
+     */
+    public function incrementFailedJobs(string $batchId, string $jobId)
+    {
+        return new UpdatedBatchJobCounts;
+    }
+
+    /**
+     * Mark the batch that has the given ID as finished.
+     *
+     * @param  string  $batchId
+     * @return void
+     */
+    public function markAsFinished(string $batchId)
+    {
+        if (isset($this->batches[$batchId])) {
+            $this->batches[$batchId]->finishedAt = now();
+        }
+    }
+
+    /**
+     * Cancel the batch that has the given ID.
+     *
+     * @param  string  $batchId
+     * @return void
+     */
+    public function cancel(string $batchId)
+    {
+        if (isset($this->batches[$batchId])) {
+            $this->batches[$batchId]->cancel();
+        }
+    }
+
+    /**
+     * Delete the batch that has the given ID.
+     *
+     * @param  string  $batchId
+     * @return void
+     */
+    public function delete(string $batchId)
+    {
+        unset($this->batches[$batchId]);
+    }
+
+    /**
+     * Execute the given Closure within a storage specific transaction.
+     *
+     * @param  \Closure  $callback
+     * @return mixed
+     */
+    public function transaction(Closure $callback)
+    {
+        return $callback();
+    }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/BusFake.php b/vendor/illuminate/support/Testing/Fakes/BusFake.php
new file mode 100644
index 0000000..9da1fac
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/BusFake.php
@@ -0,0 +1,809 @@
+dispatcher = $dispatcher;
+        $this->jobsToFake = Arr::wrap($jobsToFake);
+        $this->batchRepository = $batchRepository ?: new BatchRepositoryFake;
+    }
+
+    /**
+     * Specify the jobs that should be dispatched instead of faked.
+     *
+     * @param  array|string  $jobsToDispatch
+     * @return void
+     */
+    public function except($jobsToDispatch)
+    {
+        $this->jobsToDispatch = array_merge($this->jobsToDispatch, Arr::wrap($jobsToDispatch));
+
+        return $this;
+    }
+
+    /**
+     * Assert if a job was dispatched based on a truth-test callback.
+     *
+     * @param  string|\Closure  $command
+     * @param  callable|int|null  $callback
+     * @return void
+     */
+    public function assertDispatched($command, $callback = null)
+    {
+        if ($command instanceof Closure) {
+            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+        }
+
+        if (is_numeric($callback)) {
+            return $this->assertDispatchedTimes($command, $callback);
+        }
+
+        PHPUnit::assertTrue(
+            $this->dispatched($command, $callback)->count() > 0 ||
+            $this->dispatchedAfterResponse($command, $callback)->count() > 0 ||
+            $this->dispatchedSync($command, $callback)->count() > 0,
+            "The expected [{$command}] job was not dispatched."
+        );
+    }
+
+    /**
+     * Assert if a job was pushed a number of times.
+     *
+     * @param  string|\Closure  $command
+     * @param  int  $times
+     * @return void
+     */
+    public function assertDispatchedTimes($command, $times = 1)
+    {
+        $callback = null;
+
+        if ($command instanceof Closure) {
+            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+        }
+
+        $count = $this->dispatched($command, $callback)->count() +
+                 $this->dispatchedAfterResponse($command, $callback)->count() +
+                 $this->dispatchedSync($command, $callback)->count();
+
+        PHPUnit::assertSame(
+            $times, $count,
+            "The expected [{$command}] job was pushed {$count} times instead of {$times} times."
+        );
+    }
+
+    /**
+     * Determine if a job was dispatched based on a truth-test callback.
+     *
+     * @param  string|\Closure  $command
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public function assertNotDispatched($command, $callback = null)
+    {
+        if ($command instanceof Closure) {
+            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+        }
+
+        PHPUnit::assertTrue(
+            $this->dispatched($command, $callback)->count() === 0 &&
+            $this->dispatchedAfterResponse($command, $callback)->count() === 0 &&
+            $this->dispatchedSync($command, $callback)->count() === 0,
+            "The unexpected [{$command}] job was dispatched."
+        );
+    }
+
+    /**
+     * Assert that no jobs were dispatched.
+     *
+     * @return void
+     */
+    public function assertNothingDispatched()
+    {
+        PHPUnit::assertEmpty($this->commands, 'Jobs were dispatched unexpectedly.');
+    }
+
+    /**
+     * Assert if a job was explicitly dispatched synchronously based on a truth-test callback.
+     *
+     * @param  string|\Closure  $command
+     * @param  callable|int|null  $callback
+     * @return void
+     */
+    public function assertDispatchedSync($command, $callback = null)
+    {
+        if ($command instanceof Closure) {
+            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+        }
+
+        if (is_numeric($callback)) {
+            return $this->assertDispatchedSyncTimes($command, $callback);
+        }
+
+        PHPUnit::assertTrue(
+            $this->dispatchedSync($command, $callback)->count() > 0,
+            "The expected [{$command}] job was not dispatched synchronously."
+        );
+    }
+
+    /**
+     * Assert if a job was pushed synchronously a number of times.
+     *
+     * @param  string|\Closure  $command
+     * @param  int  $times
+     * @return void
+     */
+    public function assertDispatchedSyncTimes($command, $times = 1)
+    {
+        $callback = null;
+
+        if ($command instanceof Closure) {
+            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+        }
+
+        $count = $this->dispatchedSync($command, $callback)->count();
+
+        PHPUnit::assertSame(
+            $times, $count,
+            "The expected [{$command}] job was synchronously pushed {$count} times instead of {$times} times."
+        );
+    }
+
+    /**
+     * Determine if a job was dispatched based on a truth-test callback.
+     *
+     * @param  string|\Closure  $command
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public function assertNotDispatchedSync($command, $callback = null)
+    {
+        if ($command instanceof Closure) {
+            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+        }
+
+        PHPUnit::assertCount(
+            0, $this->dispatchedSync($command, $callback),
+            "The unexpected [{$command}] job was dispatched synchronously."
+        );
+    }
+
+    /**
+     * Assert if a job was dispatched after the response was sent based on a truth-test callback.
+     *
+     * @param  string|\Closure  $command
+     * @param  callable|int|null  $callback
+     * @return void
+     */
+    public function assertDispatchedAfterResponse($command, $callback = null)
+    {
+        if ($command instanceof Closure) {
+            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+        }
+
+        if (is_numeric($callback)) {
+            return $this->assertDispatchedAfterResponseTimes($command, $callback);
+        }
+
+        PHPUnit::assertTrue(
+            $this->dispatchedAfterResponse($command, $callback)->count() > 0,
+            "The expected [{$command}] job was not dispatched after sending the response."
+        );
+    }
+
+    /**
+     * Assert if a job was pushed after the response was sent a number of times.
+     *
+     * @param  string|\Closure  $command
+     * @param  int  $times
+     * @return void
+     */
+    public function assertDispatchedAfterResponseTimes($command, $times = 1)
+    {
+        $callback = null;
+
+        if ($command instanceof Closure) {
+            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+        }
+
+        $count = $this->dispatchedAfterResponse($command, $callback)->count();
+
+        PHPUnit::assertSame(
+            $times, $count,
+            "The expected [{$command}] job was pushed {$count} times instead of {$times} times."
+        );
+    }
+
+    /**
+     * Determine if a job was dispatched based on a truth-test callback.
+     *
+     * @param  string|\Closure  $command
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public function assertNotDispatchedAfterResponse($command, $callback = null)
+    {
+        if ($command instanceof Closure) {
+            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+        }
+
+        PHPUnit::assertCount(
+            0, $this->dispatchedAfterResponse($command, $callback),
+            "The unexpected [{$command}] job was dispatched after sending the response."
+        );
+    }
+
+    /**
+     * Assert if a chain of jobs was dispatched.
+     *
+     * @param  array  $expectedChain
+     * @return void
+     */
+    public function assertChained(array $expectedChain)
+    {
+        $command = $expectedChain[0];
+
+        $expectedChain = array_slice($expectedChain, 1);
+
+        $callback = null;
+
+        if ($command instanceof Closure) {
+            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+        } elseif (! is_string($command)) {
+            $instance = $command;
+
+            $command = get_class($instance);
+
+            $callback = function ($job) use ($instance) {
+                return serialize($this->resetChainPropertiesToDefaults($job)) === serialize($instance);
+            };
+        }
+
+        PHPUnit::assertTrue(
+            $this->dispatched($command, $callback)->isNotEmpty(),
+            "The expected [{$command}] job was not dispatched."
+        );
+
+        PHPUnit::assertTrue(
+            collect($expectedChain)->isNotEmpty(),
+            'The expected chain can not be empty.'
+        );
+
+        $this->isChainOfObjects($expectedChain)
+            ? $this->assertDispatchedWithChainOfObjects($command, $expectedChain, $callback)
+            : $this->assertDispatchedWithChainOfClasses($command, $expectedChain, $callback);
+    }
+
+    /**
+     * Reset the chain properties to their default values on the job.
+     *
+     * @param  mixed  $job
+     * @return mixed
+     */
+    protected function resetChainPropertiesToDefaults($job)
+    {
+        return tap(clone $job, function ($job) {
+            $job->chainConnection = null;
+            $job->chainQueue = null;
+            $job->chainCatchCallbacks = null;
+            $job->chained = [];
+        });
+    }
+
+    /**
+     * Assert if a job was dispatched with an empty chain based on a truth-test callback.
+     *
+     * @param  string|\Closure  $command
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public function assertDispatchedWithoutChain($command, $callback = null)
+    {
+        if ($command instanceof Closure) {
+            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+        }
+
+        PHPUnit::assertTrue(
+            $this->dispatched($command, $callback)->isNotEmpty(),
+            "The expected [{$command}] job was not dispatched."
+        );
+
+        $this->assertDispatchedWithChainOfClasses($command, [], $callback);
+    }
+
+    /**
+     * Assert if a job was dispatched with chained jobs based on a truth-test callback.
+     *
+     * @param  string  $command
+     * @param  array  $expectedChain
+     * @param  callable|null  $callback
+     * @return void
+     */
+    protected function assertDispatchedWithChainOfObjects($command, $expectedChain, $callback)
+    {
+        $chain = collect($expectedChain)->map(fn ($job) => serialize($job))->all();
+
+        PHPUnit::assertTrue(
+            $this->dispatched($command, $callback)->filter(
+                fn ($job) => $job->chained == $chain
+            )->isNotEmpty(),
+            'The expected chain was not dispatched.'
+        );
+    }
+
+    /**
+     * Assert if a job was dispatched with chained jobs based on a truth-test callback.
+     *
+     * @param  string  $command
+     * @param  array  $expectedChain
+     * @param  callable|null  $callback
+     * @return void
+     */
+    protected function assertDispatchedWithChainOfClasses($command, $expectedChain, $callback)
+    {
+        $matching = $this->dispatched($command, $callback)->map->chained->map(function ($chain) {
+            return collect($chain)->map(
+                fn ($job) => get_class(unserialize($job))
+            );
+        })->filter(
+            fn ($chain) => $chain->all() === $expectedChain
+        );
+
+        PHPUnit::assertTrue(
+            $matching->isNotEmpty(), 'The expected chain was not dispatched.'
+        );
+    }
+
+    /**
+     * Determine if the given chain is entirely composed of objects.
+     *
+     * @param  array  $chain
+     * @return bool
+     */
+    protected function isChainOfObjects($chain)
+    {
+        return ! collect($chain)->contains(fn ($job) => ! is_object($job));
+    }
+
+    /**
+     * Assert if a batch was dispatched based on a truth-test callback.
+     *
+     * @param  callable  $callback
+     * @return void
+     */
+    public function assertBatched(callable $callback)
+    {
+        PHPUnit::assertTrue(
+            $this->batched($callback)->count() > 0,
+            'The expected batch was not dispatched.'
+        );
+    }
+
+    /**
+     * Assert the number of batches that have been dispatched.
+     *
+     * @param  int  $count
+     * @return void
+     */
+    public function assertBatchCount($count)
+    {
+        PHPUnit::assertCount(
+            $count, $this->batches,
+        );
+    }
+
+    /**
+     * Assert that no batched jobs were dispatched.
+     *
+     * @return void
+     */
+    public function assertNothingBatched()
+    {
+        PHPUnit::assertEmpty($this->batches, 'Batched jobs were dispatched unexpectedly.');
+    }
+
+    /**
+     * Get all of the jobs matching a truth-test callback.
+     *
+     * @param  string  $command
+     * @param  callable|null  $callback
+     * @return \Illuminate\Support\Collection
+     */
+    public function dispatched($command, $callback = null)
+    {
+        if (! $this->hasDispatched($command)) {
+            return collect();
+        }
+
+        $callback = $callback ?: fn () => true;
+
+        return collect($this->commands[$command])->filter(fn ($command) => $callback($command));
+    }
+
+    /**
+     * Get all of the jobs dispatched synchronously matching a truth-test callback.
+     *
+     * @param  string  $command
+     * @param  callable|null  $callback
+     * @return \Illuminate\Support\Collection
+     */
+    public function dispatchedSync(string $command, $callback = null)
+    {
+        if (! $this->hasDispatchedSync($command)) {
+            return collect();
+        }
+
+        $callback = $callback ?: fn () => true;
+
+        return collect($this->commandsSync[$command])->filter(fn ($command) => $callback($command));
+    }
+
+    /**
+     * Get all of the jobs dispatched after the response was sent matching a truth-test callback.
+     *
+     * @param  string  $command
+     * @param  callable|null  $callback
+     * @return \Illuminate\Support\Collection
+     */
+    public function dispatchedAfterResponse(string $command, $callback = null)
+    {
+        if (! $this->hasDispatchedAfterResponse($command)) {
+            return collect();
+        }
+
+        $callback = $callback ?: fn () => true;
+
+        return collect($this->commandsAfterResponse[$command])->filter(fn ($command) => $callback($command));
+    }
+
+    /**
+     * Get all of the pending batches matching a truth-test callback.
+     *
+     * @param  callable  $callback
+     * @return \Illuminate\Support\Collection
+     */
+    public function batched(callable $callback)
+    {
+        if (empty($this->batches)) {
+            return collect();
+        }
+
+        return collect($this->batches)->filter(fn ($batch) => $callback($batch));
+    }
+
+    /**
+     * Determine if there are any stored commands for a given class.
+     *
+     * @param  string  $command
+     * @return bool
+     */
+    public function hasDispatched($command)
+    {
+        return isset($this->commands[$command]) && ! empty($this->commands[$command]);
+    }
+
+    /**
+     * Determine if there are any stored commands for a given class.
+     *
+     * @param  string  $command
+     * @return bool
+     */
+    public function hasDispatchedSync($command)
+    {
+        return isset($this->commandsSync[$command]) && ! empty($this->commandsSync[$command]);
+    }
+
+    /**
+     * Determine if there are any stored commands for a given class.
+     *
+     * @param  string  $command
+     * @return bool
+     */
+    public function hasDispatchedAfterResponse($command)
+    {
+        return isset($this->commandsAfterResponse[$command]) && ! empty($this->commandsAfterResponse[$command]);
+    }
+
+    /**
+     * Dispatch a command to its appropriate handler.
+     *
+     * @param  mixed  $command
+     * @return mixed
+     */
+    public function dispatch($command)
+    {
+        if ($this->shouldFakeJob($command)) {
+            $this->commands[get_class($command)][] = $command;
+        } else {
+            return $this->dispatcher->dispatch($command);
+        }
+    }
+
+    /**
+     * Dispatch a command to its appropriate handler in the current process.
+     *
+     * Queueable jobs will be dispatched to the "sync" queue.
+     *
+     * @param  mixed  $command
+     * @param  mixed  $handler
+     * @return mixed
+     */
+    public function dispatchSync($command, $handler = null)
+    {
+        if ($this->shouldFakeJob($command)) {
+            $this->commandsSync[get_class($command)][] = $command;
+        } else {
+            return $this->dispatcher->dispatchSync($command, $handler);
+        }
+    }
+
+    /**
+     * Dispatch a command to its appropriate handler in the current process.
+     *
+     * @param  mixed  $command
+     * @param  mixed  $handler
+     * @return mixed
+     */
+    public function dispatchNow($command, $handler = null)
+    {
+        if ($this->shouldFakeJob($command)) {
+            $this->commands[get_class($command)][] = $command;
+        } else {
+            return $this->dispatcher->dispatchNow($command, $handler);
+        }
+    }
+
+    /**
+     * Dispatch a command to its appropriate handler behind a queue.
+     *
+     * @param  mixed  $command
+     * @return mixed
+     */
+    public function dispatchToQueue($command)
+    {
+        if ($this->shouldFakeJob($command)) {
+            $this->commands[get_class($command)][] = $command;
+        } else {
+            return $this->dispatcher->dispatchToQueue($command);
+        }
+    }
+
+    /**
+     * Dispatch a command to its appropriate handler.
+     *
+     * @param  mixed  $command
+     * @return mixed
+     */
+    public function dispatchAfterResponse($command)
+    {
+        if ($this->shouldFakeJob($command)) {
+            $this->commandsAfterResponse[get_class($command)][] = $command;
+        } else {
+            return $this->dispatcher->dispatch($command);
+        }
+    }
+
+    /**
+     * Create a new chain of queueable jobs.
+     *
+     * @param  \Illuminate\Support\Collection|array  $jobs
+     * @return \Illuminate\Foundation\Bus\PendingChain
+     */
+    public function chain($jobs)
+    {
+        $jobs = Collection::wrap($jobs);
+
+        return new PendingChainFake($this, $jobs->shift(), $jobs->toArray());
+    }
+
+    /**
+     * Attempt to find the batch with the given ID.
+     *
+     * @param  string  $batchId
+     * @return \Illuminate\Bus\Batch|null
+     */
+    public function findBatch(string $batchId)
+    {
+        return $this->batchRepository->find($batchId);
+    }
+
+    /**
+     * Create a new batch of queueable jobs.
+     *
+     * @param  \Illuminate\Support\Collection|array  $jobs
+     * @return \Illuminate\Bus\PendingBatch
+     */
+    public function batch($jobs)
+    {
+        return new PendingBatchFake($this, Collection::wrap($jobs));
+    }
+
+    /**
+     * Dispatch an empty job batch for testing.
+     *
+     * @param  string  $name
+     * @return \Illuminate\Bus\Batch
+     */
+    public function dispatchFakeBatch($name = '')
+    {
+        return $this->batch([])->name($name)->dispatch();
+    }
+
+    /**
+     * Record the fake pending batch dispatch.
+     *
+     * @param  \Illuminate\Bus\PendingBatch  $pendingBatch
+     * @return \Illuminate\Bus\Batch
+     */
+    public function recordPendingBatch(PendingBatch $pendingBatch)
+    {
+        $this->batches[] = $pendingBatch;
+
+        return $this->batchRepository->store($pendingBatch);
+    }
+
+    /**
+     * Determine if a command should be faked or actually dispatched.
+     *
+     * @param  mixed  $command
+     * @return bool
+     */
+    protected function shouldFakeJob($command)
+    {
+        if ($this->shouldDispatchCommand($command)) {
+            return false;
+        }
+
+        if (empty($this->jobsToFake)) {
+            return true;
+        }
+
+        return collect($this->jobsToFake)
+            ->filter(function ($job) use ($command) {
+                return $job instanceof Closure
+                            ? $job($command)
+                            : $job === get_class($command);
+            })->isNotEmpty();
+    }
+
+    /**
+     * Determine if a command should be dispatched or not.
+     *
+     * @param  mixed  $command
+     * @return bool
+     */
+    protected function shouldDispatchCommand($command)
+    {
+        return collect($this->jobsToDispatch)
+            ->filter(function ($job) use ($command) {
+                return $job instanceof Closure
+                    ? $job($command)
+                    : $job === get_class($command);
+            })->isNotEmpty();
+    }
+
+    /**
+     * Set the pipes commands should be piped through before dispatching.
+     *
+     * @param  array  $pipes
+     * @return $this
+     */
+    public function pipeThrough(array $pipes)
+    {
+        $this->dispatcher->pipeThrough($pipes);
+
+        return $this;
+    }
+
+    /**
+     * Determine if the given command has a handler.
+     *
+     * @param  mixed  $command
+     * @return bool
+     */
+    public function hasCommandHandler($command)
+    {
+        return $this->dispatcher->hasCommandHandler($command);
+    }
+
+    /**
+     * Retrieve the handler for a command.
+     *
+     * @param  mixed  $command
+     * @return mixed
+     */
+    public function getCommandHandler($command)
+    {
+        return $this->dispatcher->getCommandHandler($command);
+    }
+
+    /**
+     * Map a command to a handler.
+     *
+     * @param  array  $map
+     * @return $this
+     */
+    public function map(array $map)
+    {
+        $this->dispatcher->map($map);
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/EventFake.php b/vendor/illuminate/support/Testing/Fakes/EventFake.php
new file mode 100644
index 0000000..3a16cef
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/EventFake.php
@@ -0,0 +1,385 @@
+dispatcher = $dispatcher;
+
+        $this->eventsToFake = Arr::wrap($eventsToFake);
+    }
+
+    /**
+     * Specify the events that should be dispatched instead of faked.
+     *
+     * @param  array|string  $eventsToDispatch
+     * @return $this
+     */
+    public function except($eventsToDispatch)
+    {
+        $this->eventsToDispatch = array_merge(
+            $this->eventsToDispatch,
+            Arr::wrap($eventsToDispatch)
+        );
+
+        return $this;
+    }
+
+    /**
+     * Assert if an event has a listener attached to it.
+     *
+     * @param  string  $expectedEvent
+     * @param  string|array  $expectedListener
+     * @return void
+     */
+    public function assertListening($expectedEvent, $expectedListener)
+    {
+        foreach ($this->dispatcher->getListeners($expectedEvent) as $listenerClosure) {
+            $actualListener = (new ReflectionFunction($listenerClosure))
+                        ->getStaticVariables()['listener'];
+
+            $normalizedListener = $expectedListener;
+
+            if (is_string($actualListener) && Str::contains($actualListener, '@')) {
+                $actualListener = Str::parseCallback($actualListener);
+
+                if (is_string($expectedListener)) {
+                    if (Str::contains($expectedListener, '@')) {
+                        $normalizedListener = Str::parseCallback($expectedListener);
+                    } else {
+                        $normalizedListener = [
+                            $expectedListener,
+                            method_exists($expectedListener, 'handle') ? 'handle' : '__invoke',
+                        ];
+                    }
+                }
+            }
+
+            if ($actualListener === $normalizedListener ||
+                ($actualListener instanceof Closure &&
+                $normalizedListener === Closure::class)) {
+                PHPUnit::assertTrue(true);
+
+                return;
+            }
+        }
+
+        PHPUnit::assertTrue(
+            false,
+            sprintf(
+                'Event [%s] does not have the [%s] listener attached to it',
+                $expectedEvent,
+                print_r($expectedListener, true)
+            )
+        );
+    }
+
+    /**
+     * Assert if an event was dispatched based on a truth-test callback.
+     *
+     * @param  string|\Closure  $event
+     * @param  callable|int|null  $callback
+     * @return void
+     */
+    public function assertDispatched($event, $callback = null)
+    {
+        if ($event instanceof Closure) {
+            [$event, $callback] = [$this->firstClosureParameterType($event), $event];
+        }
+
+        if (is_int($callback)) {
+            return $this->assertDispatchedTimes($event, $callback);
+        }
+
+        PHPUnit::assertTrue(
+            $this->dispatched($event, $callback)->count() > 0,
+            "The expected [{$event}] event was not dispatched."
+        );
+    }
+
+    /**
+     * Assert if an event was dispatched a number of times.
+     *
+     * @param  string  $event
+     * @param  int  $times
+     * @return void
+     */
+    public function assertDispatchedTimes($event, $times = 1)
+    {
+        $count = $this->dispatched($event)->count();
+
+        PHPUnit::assertSame(
+            $times, $count,
+            "The expected [{$event}] event was dispatched {$count} times instead of {$times} times."
+        );
+    }
+
+    /**
+     * Determine if an event was dispatched based on a truth-test callback.
+     *
+     * @param  string|\Closure  $event
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public function assertNotDispatched($event, $callback = null)
+    {
+        if ($event instanceof Closure) {
+            [$event, $callback] = [$this->firstClosureParameterType($event), $event];
+        }
+
+        PHPUnit::assertCount(
+            0, $this->dispatched($event, $callback),
+            "The unexpected [{$event}] event was dispatched."
+        );
+    }
+
+    /**
+     * Assert that no events were dispatched.
+     *
+     * @return void
+     */
+    public function assertNothingDispatched()
+    {
+        $count = count(Arr::flatten($this->events));
+
+        PHPUnit::assertSame(
+            0, $count,
+            "{$count} unexpected events were dispatched."
+        );
+    }
+
+    /**
+     * Get all of the events matching a truth-test callback.
+     *
+     * @param  string  $event
+     * @param  callable|null  $callback
+     * @return \Illuminate\Support\Collection
+     */
+    public function dispatched($event, $callback = null)
+    {
+        if (! $this->hasDispatched($event)) {
+            return collect();
+        }
+
+        $callback = $callback ?: fn () => true;
+
+        return collect($this->events[$event])->filter(
+            fn ($arguments) => $callback(...$arguments)
+        );
+    }
+
+    /**
+     * Determine if the given event has been dispatched.
+     *
+     * @param  string  $event
+     * @return bool
+     */
+    public function hasDispatched($event)
+    {
+        return isset($this->events[$event]) && ! empty($this->events[$event]);
+    }
+
+    /**
+     * Register an event listener with the dispatcher.
+     *
+     * @param  \Closure|string|array  $events
+     * @param  mixed  $listener
+     * @return void
+     */
+    public function listen($events, $listener = null)
+    {
+        $this->dispatcher->listen($events, $listener);
+    }
+
+    /**
+     * Determine if a given event has listeners.
+     *
+     * @param  string  $eventName
+     * @return bool
+     */
+    public function hasListeners($eventName)
+    {
+        return $this->dispatcher->hasListeners($eventName);
+    }
+
+    /**
+     * Register an event and payload to be dispatched later.
+     *
+     * @param  string  $event
+     * @param  array  $payload
+     * @return void
+     */
+    public function push($event, $payload = [])
+    {
+        //
+    }
+
+    /**
+     * Register an event subscriber with the dispatcher.
+     *
+     * @param  object|string  $subscriber
+     * @return void
+     */
+    public function subscribe($subscriber)
+    {
+        $this->dispatcher->subscribe($subscriber);
+    }
+
+    /**
+     * Flush a set of pushed events.
+     *
+     * @param  string  $event
+     * @return void
+     */
+    public function flush($event)
+    {
+        //
+    }
+
+    /**
+     * Fire an event and call the listeners.
+     *
+     * @param  string|object  $event
+     * @param  mixed  $payload
+     * @param  bool  $halt
+     * @return array|null
+     */
+    public function dispatch($event, $payload = [], $halt = false)
+    {
+        $name = is_object($event) ? get_class($event) : (string) $event;
+
+        if ($this->shouldFakeEvent($name, $payload)) {
+            $this->events[$name][] = func_get_args();
+        } else {
+            return $this->dispatcher->dispatch($event, $payload, $halt);
+        }
+    }
+
+    /**
+     * Determine if an event should be faked or actually dispatched.
+     *
+     * @param  string  $eventName
+     * @param  mixed  $payload
+     * @return bool
+     */
+    protected function shouldFakeEvent($eventName, $payload)
+    {
+        if ($this->shouldDispatchEvent($eventName, $payload)) {
+            return false;
+        }
+
+        if (empty($this->eventsToFake)) {
+            return true;
+        }
+
+        return collect($this->eventsToFake)
+            ->filter(function ($event) use ($eventName, $payload) {
+                return $event instanceof Closure
+                            ? $event($eventName, $payload)
+                            : $event === $eventName;
+            })
+            ->isNotEmpty();
+    }
+
+    /**
+     * Determine whether an event should be dispatched or not.
+     *
+     * @param  string  $eventName
+     * @param  mixed  $payload
+     * @return bool
+     */
+    protected function shouldDispatchEvent($eventName, $payload)
+    {
+        if (empty($this->eventsToDispatch)) {
+            return false;
+        }
+
+        return collect($this->eventsToDispatch)
+            ->filter(function ($event) use ($eventName, $payload) {
+                return $event instanceof Closure
+                    ? $event($eventName, $payload)
+                    : $event === $eventName;
+            })
+            ->isNotEmpty();
+    }
+
+    /**
+     * Remove a set of listeners from the dispatcher.
+     *
+     * @param  string  $event
+     * @return void
+     */
+    public function forget($event)
+    {
+        //
+    }
+
+    /**
+     * Forget all of the queued listeners.
+     *
+     * @return void
+     */
+    public function forgetPushed()
+    {
+        //
+    }
+
+    /**
+     * Dispatch an event and call the listeners.
+     *
+     * @param  string|object  $event
+     * @param  mixed  $payload
+     * @return array|null
+     */
+    public function until($event, $payload = [])
+    {
+        return $this->dispatch($event, $payload, true);
+    }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/MailFake.php b/vendor/illuminate/support/Testing/Fakes/MailFake.php
new file mode 100644
index 0000000..c95c15a
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/MailFake.php
@@ -0,0 +1,444 @@
+prepareMailableAndCallback($mailable, $callback);
+
+        if (is_numeric($callback)) {
+            return $this->assertSentTimes($mailable, $callback);
+        }
+
+        $message = "The expected [{$mailable}] mailable was not sent.";
+
+        if (count($this->queuedMailables) > 0) {
+            $message .= ' Did you mean to use assertQueued() instead?';
+        }
+
+        PHPUnit::assertTrue(
+            $this->sent($mailable, $callback)->count() > 0,
+            $message
+        );
+    }
+
+    /**
+     * Assert if a mailable was sent a number of times.
+     *
+     * @param  string  $mailable
+     * @param  int  $times
+     * @return void
+     */
+    protected function assertSentTimes($mailable, $times = 1)
+    {
+        $count = $this->sent($mailable)->count();
+
+        PHPUnit::assertSame(
+            $times, $count,
+            "The expected [{$mailable}] mailable was sent {$count} times instead of {$times} times."
+        );
+    }
+
+    /**
+     * Determine if a mailable was not sent or queued to be sent based on a truth-test callback.
+     *
+     * @param  string|\Closure  $mailable
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public function assertNotOutgoing($mailable, $callback = null)
+    {
+        $this->assertNotSent($mailable, $callback);
+        $this->assertNotQueued($mailable, $callback);
+    }
+
+    /**
+     * Determine if a mailable was not sent based on a truth-test callback.
+     *
+     * @param  string|\Closure  $mailable
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public function assertNotSent($mailable, $callback = null)
+    {
+        [$mailable, $callback] = $this->prepareMailableAndCallback($mailable, $callback);
+
+        PHPUnit::assertCount(
+            0, $this->sent($mailable, $callback),
+            "The unexpected [{$mailable}] mailable was sent."
+        );
+    }
+
+    /**
+     * Assert that no mailables were sent or queued to be sent.
+     *
+     * @return void
+     */
+    public function assertNothingOutgoing()
+    {
+        $this->assertNothingSent();
+        $this->assertNothingQueued();
+    }
+
+    /**
+     * Assert that no mailables were sent.
+     *
+     * @return void
+     */
+    public function assertNothingSent()
+    {
+        $mailableNames = collect($this->mailables)->map(
+            fn ($mailable) => get_class($mailable)
+        )->join(', ');
+
+        PHPUnit::assertEmpty($this->mailables, 'The following mailables were sent unexpectedly: '.$mailableNames);
+    }
+
+    /**
+     * Assert if a mailable was queued based on a truth-test callback.
+     *
+     * @param  string|\Closure  $mailable
+     * @param  callable|int|null  $callback
+     * @return void
+     */
+    public function assertQueued($mailable, $callback = null)
+    {
+        [$mailable, $callback] = $this->prepareMailableAndCallback($mailable, $callback);
+
+        if (is_numeric($callback)) {
+            return $this->assertQueuedTimes($mailable, $callback);
+        }
+
+        PHPUnit::assertTrue(
+            $this->queued($mailable, $callback)->count() > 0,
+            "The expected [{$mailable}] mailable was not queued."
+        );
+    }
+
+    /**
+     * Assert if a mailable was queued a number of times.
+     *
+     * @param  string  $mailable
+     * @param  int  $times
+     * @return void
+     */
+    protected function assertQueuedTimes($mailable, $times = 1)
+    {
+        $count = $this->queued($mailable)->count();
+
+        PHPUnit::assertSame(
+            $times, $count,
+            "The expected [{$mailable}] mailable was queued {$count} times instead of {$times} times."
+        );
+    }
+
+    /**
+     * Determine if a mailable was not queued based on a truth-test callback.
+     *
+     * @param  string|\Closure  $mailable
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public function assertNotQueued($mailable, $callback = null)
+    {
+        [$mailable, $callback] = $this->prepareMailableAndCallback($mailable, $callback);
+
+        PHPUnit::assertCount(
+            0, $this->queued($mailable, $callback),
+            "The unexpected [{$mailable}] mailable was queued."
+        );
+    }
+
+    /**
+     * Assert that no mailables were queued.
+     *
+     * @return void
+     */
+    public function assertNothingQueued()
+    {
+        $mailableNames = collect($this->queuedMailables)->map(
+            fn ($mailable) => get_class($mailable)
+        )->join(', ');
+
+        PHPUnit::assertEmpty($this->queuedMailables, 'The following mailables were queued unexpectedly: '.$mailableNames);
+    }
+
+    /**
+     * Get all of the mailables matching a truth-test callback.
+     *
+     * @param  string|\Closure  $mailable
+     * @param  callable|null  $callback
+     * @return \Illuminate\Support\Collection
+     */
+    public function sent($mailable, $callback = null)
+    {
+        [$mailable, $callback] = $this->prepareMailableAndCallback($mailable, $callback);
+
+        if (! $this->hasSent($mailable)) {
+            return collect();
+        }
+
+        $callback = $callback ?: fn () => true;
+
+        return $this->mailablesOf($mailable)->filter(fn ($mailable) => $callback($mailable));
+    }
+
+    /**
+     * Determine if the given mailable has been sent.
+     *
+     * @param  string  $mailable
+     * @return bool
+     */
+    public function hasSent($mailable)
+    {
+        return $this->mailablesOf($mailable)->count() > 0;
+    }
+
+    /**
+     * Get all of the queued mailables matching a truth-test callback.
+     *
+     * @param  string|\Closure  $mailable
+     * @param  callable|null  $callback
+     * @return \Illuminate\Support\Collection
+     */
+    public function queued($mailable, $callback = null)
+    {
+        [$mailable, $callback] = $this->prepareMailableAndCallback($mailable, $callback);
+
+        if (! $this->hasQueued($mailable)) {
+            return collect();
+        }
+
+        $callback = $callback ?: fn () => true;
+
+        return $this->queuedMailablesOf($mailable)->filter(fn ($mailable) => $callback($mailable));
+    }
+
+    /**
+     * Determine if the given mailable has been queued.
+     *
+     * @param  string  $mailable
+     * @return bool
+     */
+    public function hasQueued($mailable)
+    {
+        return $this->queuedMailablesOf($mailable)->count() > 0;
+    }
+
+    /**
+     * Get all of the mailed mailables for a given type.
+     *
+     * @param  string  $type
+     * @return \Illuminate\Support\Collection
+     */
+    protected function mailablesOf($type)
+    {
+        return collect($this->mailables)->filter(fn ($mailable) => $mailable instanceof $type);
+    }
+
+    /**
+     * Get all of the mailed mailables for a given type.
+     *
+     * @param  string  $type
+     * @return \Illuminate\Support\Collection
+     */
+    protected function queuedMailablesOf($type)
+    {
+        return collect($this->queuedMailables)->filter(fn ($mailable) => $mailable instanceof $type);
+    }
+
+    /**
+     * Get a mailer instance by name.
+     *
+     * @param  string|null  $name
+     * @return \Illuminate\Contracts\Mail\Mailer
+     */
+    public function mailer($name = null)
+    {
+        $this->currentMailer = $name;
+
+        return $this;
+    }
+
+    /**
+     * Begin the process of mailing a mailable class instance.
+     *
+     * @param  mixed  $users
+     * @return \Illuminate\Mail\PendingMail
+     */
+    public function to($users)
+    {
+        return (new PendingMailFake($this))->to($users);
+    }
+
+    /**
+     * Begin the process of mailing a mailable class instance.
+     *
+     * @param  mixed  $users
+     * @return \Illuminate\Mail\PendingMail
+     */
+    public function cc($users)
+    {
+        return (new PendingMailFake($this))->cc($users);
+    }
+
+    /**
+     * Begin the process of mailing a mailable class instance.
+     *
+     * @param  mixed  $users
+     * @return \Illuminate\Mail\PendingMail
+     */
+    public function bcc($users)
+    {
+        return (new PendingMailFake($this))->bcc($users);
+    }
+
+    /**
+     * Send a new message with only a raw text part.
+     *
+     * @param  string  $text
+     * @param  \Closure|string  $callback
+     * @return void
+     */
+    public function raw($text, $callback)
+    {
+        //
+    }
+
+    /**
+     * Send a new message using a view.
+     *
+     * @param  \Illuminate\Contracts\Mail\Mailable|string|array  $view
+     * @param  array  $data
+     * @param  \Closure|string|null  $callback
+     * @return void
+     */
+    public function send($view, array $data = [], $callback = null)
+    {
+        if (! $view instanceof Mailable) {
+            return;
+        }
+
+        $view->mailer($this->currentMailer);
+
+        if ($view instanceof ShouldQueue) {
+            return $this->queue($view, $data);
+        }
+
+        $this->currentMailer = null;
+
+        $this->mailables[] = $view;
+    }
+
+    /**
+     * Queue a new e-mail message for sending.
+     *
+     * @param  \Illuminate\Contracts\Mail\Mailable|string|array  $view
+     * @param  string|null  $queue
+     * @return mixed
+     */
+    public function queue($view, $queue = null)
+    {
+        if (! $view instanceof Mailable) {
+            return;
+        }
+
+        $view->mailer($this->currentMailer);
+
+        $this->currentMailer = null;
+
+        $this->queuedMailables[] = $view;
+    }
+
+    /**
+     * Queue a new e-mail message for sending after (n) seconds.
+     *
+     * @param  \DateTimeInterface|\DateInterval|int  $delay
+     * @param  \Illuminate\Contracts\Mail\Mailable|string|array  $view
+     * @param  string|null  $queue
+     * @return mixed
+     */
+    public function later($delay, $view, $queue = null)
+    {
+        $this->queue($view, $queue);
+    }
+
+    /**
+     * Get the array of failed recipients.
+     *
+     * @return array
+     */
+    public function failures()
+    {
+        return [];
+    }
+
+    /**
+     * Infer mailable class using reflection if a typehinted closure is passed to assertion.
+     *
+     * @param  string|\Closure  $mailable
+     * @param  callable|null  $callback
+     * @return array
+     */
+    protected function prepareMailableAndCallback($mailable, $callback)
+    {
+        if ($mailable instanceof Closure) {
+            return [$this->firstClosureParameterType($mailable), $mailable];
+        }
+
+        return [$mailable, $callback];
+    }
+
+    /**
+     * Forget all of the resolved mailer instances.
+     *
+     * @return $this
+     */
+    public function forgetMailers()
+    {
+        $this->currentMailer = null;
+
+        return $this;
+    }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/NotificationFake.php b/vendor/illuminate/support/Testing/Fakes/NotificationFake.php
new file mode 100644
index 0000000..5991420
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/NotificationFake.php
@@ -0,0 +1,375 @@
+assertSentTo(new AnonymousNotifiable, $notification, $callback);
+    }
+
+    /**
+     * Assert if a notification was sent based on a truth-test callback.
+     *
+     * @param  mixed  $notifiable
+     * @param  string|\Closure  $notification
+     * @param  callable|null  $callback
+     * @return void
+     *
+     * @throws \Exception
+     */
+    public function assertSentTo($notifiable, $notification, $callback = null)
+    {
+        if (is_array($notifiable) || $notifiable instanceof Collection) {
+            if (count($notifiable) === 0) {
+                throw new Exception('No notifiable given.');
+            }
+
+            foreach ($notifiable as $singleNotifiable) {
+                $this->assertSentTo($singleNotifiable, $notification, $callback);
+            }
+
+            return;
+        }
+
+        if ($notification instanceof Closure) {
+            [$notification, $callback] = [$this->firstClosureParameterType($notification), $notification];
+        }
+
+        if (is_numeric($callback)) {
+            return $this->assertSentToTimes($notifiable, $notification, $callback);
+        }
+
+        PHPUnit::assertTrue(
+            $this->sent($notifiable, $notification, $callback)->count() > 0,
+            "The expected [{$notification}] notification was not sent."
+        );
+    }
+
+    /**
+     * Assert if a notification was sent on-demand a number of times.
+     *
+     * @param  string  $notification
+     * @param  int  $times
+     * @return void
+     */
+    public function assertSentOnDemandTimes($notification, $times = 1)
+    {
+        return $this->assertSentToTimes(new AnonymousNotifiable, $notification, $times);
+    }
+
+    /**
+     * Assert if a notification was sent a number of times.
+     *
+     * @param  mixed  $notifiable
+     * @param  string  $notification
+     * @param  int  $times
+     * @return void
+     */
+    public function assertSentToTimes($notifiable, $notification, $times = 1)
+    {
+        $count = $this->sent($notifiable, $notification)->count();
+
+        PHPUnit::assertSame(
+            $times, $count,
+            "Expected [{$notification}] to be sent {$times} times, but was sent {$count} times."
+        );
+    }
+
+    /**
+     * Determine if a notification was sent based on a truth-test callback.
+     *
+     * @param  mixed  $notifiable
+     * @param  string|\Closure  $notification
+     * @param  callable|null  $callback
+     * @return void
+     *
+     * @throws \Exception
+     */
+    public function assertNotSentTo($notifiable, $notification, $callback = null)
+    {
+        if (is_array($notifiable) || $notifiable instanceof Collection) {
+            if (count($notifiable) === 0) {
+                throw new Exception('No notifiable given.');
+            }
+
+            foreach ($notifiable as $singleNotifiable) {
+                $this->assertNotSentTo($singleNotifiable, $notification, $callback);
+            }
+
+            return;
+        }
+
+        if ($notification instanceof Closure) {
+            [$notification, $callback] = [$this->firstClosureParameterType($notification), $notification];
+        }
+
+        PHPUnit::assertCount(
+            0, $this->sent($notifiable, $notification, $callback),
+            "The unexpected [{$notification}] notification was sent."
+        );
+    }
+
+    /**
+     * Assert that no notifications were sent.
+     *
+     * @return void
+     */
+    public function assertNothingSent()
+    {
+        PHPUnit::assertEmpty($this->notifications, 'Notifications were sent unexpectedly.');
+    }
+
+    /**
+     * Assert that no notifications were sent to the given notifiable.
+     *
+     * @param  mixed  $notifiable
+     * @return void
+     *
+     * @throws \Exception
+     */
+    public function assertNothingSentTo($notifiable)
+    {
+        if (is_array($notifiable) || $notifiable instanceof Collection) {
+            if (count($notifiable) === 0) {
+                throw new Exception('No notifiable given.');
+            }
+
+            foreach ($notifiable as $singleNotifiable) {
+                $this->assertNothingSentTo($singleNotifiable);
+            }
+
+            return;
+        }
+
+        PHPUnit::assertEmpty(
+            $this->notifications[get_class($notifiable)][$notifiable->getKey()] ?? [],
+            'Notifications were sent unexpectedly.',
+        );
+    }
+
+    /**
+     * Assert the total amount of times a notification was sent.
+     *
+     * @param  string  $notification
+     * @param  int  $expectedCount
+     * @return void
+     */
+    public function assertSentTimes($notification, $expectedCount)
+    {
+        $actualCount = collect($this->notifications)
+            ->flatten(1)
+            ->reduce(fn ($count, $sent) => $count + count($sent[$notification] ?? []), 0);
+
+        PHPUnit::assertSame(
+            $expectedCount, $actualCount,
+            "Expected [{$notification}] to be sent {$expectedCount} times, but was sent {$actualCount} times."
+        );
+    }
+
+    /**
+     * Assert the total count of notification that were sent.
+     *
+     * @param  int  $expectedCount
+     * @return void
+     */
+    public function assertCount($expectedCount)
+    {
+        $actualCount = collect($this->notifications)->flatten(3)->count();
+
+        PHPUnit::assertSame(
+            $expectedCount, $actualCount,
+            "Expected {$expectedCount} notifications to be sent, but {$actualCount} were sent."
+        );
+    }
+
+    /**
+     * Assert the total amount of times a notification was sent.
+     *
+     * @param  int  $expectedCount
+     * @param  string  $notification
+     * @return void
+     *
+     * @deprecated Use the assertSentTimes method instead
+     */
+    public function assertTimesSent($expectedCount, $notification)
+    {
+        $this->assertSentTimes($notification, $expectedCount);
+    }
+
+    /**
+     * Get all of the notifications matching a truth-test callback.
+     *
+     * @param  mixed  $notifiable
+     * @param  string  $notification
+     * @param  callable|null  $callback
+     * @return \Illuminate\Support\Collection
+     */
+    public function sent($notifiable, $notification, $callback = null)
+    {
+        if (! $this->hasSent($notifiable, $notification)) {
+            return collect();
+        }
+
+        $callback = $callback ?: fn () => true;
+
+        $notifications = collect($this->notificationsFor($notifiable, $notification));
+
+        return $notifications->filter(
+            fn ($arguments) => $callback(...array_values($arguments))
+        )->pluck('notification');
+    }
+
+    /**
+     * Determine if there are more notifications left to inspect.
+     *
+     * @param  mixed  $notifiable
+     * @param  string  $notification
+     * @return bool
+     */
+    public function hasSent($notifiable, $notification)
+    {
+        return ! empty($this->notificationsFor($notifiable, $notification));
+    }
+
+    /**
+     * Get all of the notifications for a notifiable entity by type.
+     *
+     * @param  mixed  $notifiable
+     * @param  string  $notification
+     * @return array
+     */
+    protected function notificationsFor($notifiable, $notification)
+    {
+        return $this->notifications[get_class($notifiable)][$notifiable->getKey()][$notification] ?? [];
+    }
+
+    /**
+     * Send the given notification to the given notifiable entities.
+     *
+     * @param  \Illuminate\Support\Collection|array|mixed  $notifiables
+     * @param  mixed  $notification
+     * @return void
+     */
+    public function send($notifiables, $notification)
+    {
+        $this->sendNow($notifiables, $notification);
+    }
+
+    /**
+     * Send the given notification immediately.
+     *
+     * @param  \Illuminate\Support\Collection|array|mixed  $notifiables
+     * @param  mixed  $notification
+     * @param  array|null  $channels
+     * @return void
+     */
+    public function sendNow($notifiables, $notification, array $channels = null)
+    {
+        if (! $notifiables instanceof Collection && ! is_array($notifiables)) {
+            $notifiables = [$notifiables];
+        }
+
+        foreach ($notifiables as $notifiable) {
+            if (! $notification->id) {
+                $notification->id = Str::uuid()->toString();
+            }
+
+            $notifiableChannels = $channels ?: $notification->via($notifiable);
+
+            if (method_exists($notification, 'shouldSend')) {
+                $notifiableChannels = array_filter(
+                    $notifiableChannels,
+                    fn ($channel) => $notification->shouldSend($notifiable, $channel) !== false
+                );
+
+                if (empty($notifiableChannels)) {
+                    continue;
+                }
+            }
+
+            $this->notifications[get_class($notifiable)][$notifiable->getKey()][get_class($notification)][] = [
+                'notification' => $notification,
+                'channels' => $notifiableChannels,
+                'notifiable' => $notifiable,
+                'locale' => $notification->locale ?? $this->locale ?? value(function () use ($notifiable) {
+                    if ($notifiable instanceof HasLocalePreference) {
+                        return $notifiable->preferredLocale();
+                    }
+                }),
+            ];
+        }
+    }
+
+    /**
+     * Get a channel instance by name.
+     *
+     * @param  string|null  $name
+     * @return mixed
+     */
+    public function channel($name = null)
+    {
+        //
+    }
+
+    /**
+     * Set the locale of notifications.
+     *
+     * @param  string  $locale
+     * @return $this
+     */
+    public function locale($locale)
+    {
+        $this->locale = $locale;
+
+        return $this;
+    }
+
+    /**
+     * Get the notifications that have been sent.
+     *
+     * @return array
+     */
+    public function sentNotifications()
+    {
+        return $this->notifications;
+    }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/PendingBatchFake.php b/vendor/illuminate/support/Testing/Fakes/PendingBatchFake.php
new file mode 100644
index 0000000..3d0f499
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/PendingBatchFake.php
@@ -0,0 +1,49 @@
+bus = $bus;
+        $this->jobs = $jobs;
+    }
+
+    /**
+     * Dispatch the batch.
+     *
+     * @return \Illuminate\Bus\Batch
+     */
+    public function dispatch()
+    {
+        return $this->bus->recordPendingBatch($this);
+    }
+
+    /**
+     * Dispatch the batch after the response is sent to the browser.
+     *
+     * @return \Illuminate\Bus\Batch
+     */
+    public function dispatchAfterResponse()
+    {
+        return $this->bus->recordPendingBatch($this);
+    }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/PendingChainFake.php b/vendor/illuminate/support/Testing/Fakes/PendingChainFake.php
new file mode 100644
index 0000000..533c649
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/PendingChainFake.php
@@ -0,0 +1,56 @@
+bus = $bus;
+        $this->job = $job;
+        $this->chain = $chain;
+    }
+
+    /**
+     * Dispatch the job with the given arguments.
+     *
+     * @return \Illuminate\Foundation\Bus\PendingDispatch
+     */
+    public function dispatch()
+    {
+        if (is_string($this->job)) {
+            $firstJob = new $this->job(...func_get_args());
+        } elseif ($this->job instanceof Closure) {
+            $firstJob = CallQueuedClosure::create($this->job);
+        } else {
+            $firstJob = $this->job;
+        }
+
+        $firstJob->allOnConnection($this->connection);
+        $firstJob->allOnQueue($this->queue);
+        $firstJob->chain($this->chain);
+        $firstJob->delay($this->delay);
+        $firstJob->chainCatchCallbacks = $this->catchCallbacks();
+
+        return $this->bus->dispatch($firstJob);
+    }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/PendingMailFake.php b/vendor/illuminate/support/Testing/Fakes/PendingMailFake.php
new file mode 100644
index 0000000..5225130
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/PendingMailFake.php
@@ -0,0 +1,42 @@
+mailer = $mailer;
+    }
+
+    /**
+     * Send a new mailable message instance.
+     *
+     * @param  \Illuminate\Contracts\Mail\Mailable  $mailable
+     * @return void
+     */
+    public function send(Mailable $mailable)
+    {
+        $this->mailer->send($this->fill($mailable));
+    }
+
+    /**
+     * Push the given mailable onto the queue.
+     *
+     * @param  \Illuminate\Contracts\Mail\Mailable  $mailable
+     * @return mixed
+     */
+    public function queue(Mailable $mailable)
+    {
+        return $this->mailer->queue($this->fill($mailable));
+    }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/QueueFake.php b/vendor/illuminate/support/Testing/Fakes/QueueFake.php
new file mode 100644
index 0000000..f2caa76
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/QueueFake.php
@@ -0,0 +1,503 @@
+jobsToFake = Collection::wrap($jobsToFake);
+        $this->jobsToBeQueued = Collection::make();
+        $this->queue = $queue;
+    }
+
+    /**
+     * Specify the jobs that should be queued instead of faked.
+     *
+     * @param  array|string  $jobsToBeQueued
+     * @return $this
+     */
+    public function except($jobsToBeQueued)
+    {
+        $this->jobsToBeQueued = Collection::wrap($jobsToBeQueued)->merge($this->jobsToBeQueued);
+
+        return $this;
+    }
+
+    /**
+     * Assert if a job was pushed based on a truth-test callback.
+     *
+     * @param  string|\Closure  $job
+     * @param  callable|int|null  $callback
+     * @return void
+     */
+    public function assertPushed($job, $callback = null)
+    {
+        if ($job instanceof Closure) {
+            [$job, $callback] = [$this->firstClosureParameterType($job), $job];
+        }
+
+        if (is_numeric($callback)) {
+            return $this->assertPushedTimes($job, $callback);
+        }
+
+        PHPUnit::assertTrue(
+            $this->pushed($job, $callback)->count() > 0,
+            "The expected [{$job}] job was not pushed."
+        );
+    }
+
+    /**
+     * Assert if a job was pushed a number of times.
+     *
+     * @param  string  $job
+     * @param  int  $times
+     * @return void
+     */
+    protected function assertPushedTimes($job, $times = 1)
+    {
+        $count = $this->pushed($job)->count();
+
+        PHPUnit::assertSame(
+            $times, $count,
+            "The expected [{$job}] job was pushed {$count} times instead of {$times} times."
+        );
+    }
+
+    /**
+     * Assert if a job was pushed based on a truth-test callback.
+     *
+     * @param  string  $queue
+     * @param  string|\Closure  $job
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public function assertPushedOn($queue, $job, $callback = null)
+    {
+        if ($job instanceof Closure) {
+            [$job, $callback] = [$this->firstClosureParameterType($job), $job];
+        }
+
+        $this->assertPushed($job, function ($job, $pushedQueue) use ($callback, $queue) {
+            if ($pushedQueue !== $queue) {
+                return false;
+            }
+
+            return $callback ? $callback(...func_get_args()) : true;
+        });
+    }
+
+    /**
+     * Assert if a job was pushed with chained jobs based on a truth-test callback.
+     *
+     * @param  string  $job
+     * @param  array  $expectedChain
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public function assertPushedWithChain($job, $expectedChain = [], $callback = null)
+    {
+        PHPUnit::assertTrue(
+            $this->pushed($job, $callback)->isNotEmpty(),
+            "The expected [{$job}] job was not pushed."
+        );
+
+        PHPUnit::assertTrue(
+            collect($expectedChain)->isNotEmpty(),
+            'The expected chain can not be empty.'
+        );
+
+        $this->isChainOfObjects($expectedChain)
+                ? $this->assertPushedWithChainOfObjects($job, $expectedChain, $callback)
+                : $this->assertPushedWithChainOfClasses($job, $expectedChain, $callback);
+    }
+
+    /**
+     * Assert if a job was pushed with an empty chain based on a truth-test callback.
+     *
+     * @param  string  $job
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public function assertPushedWithoutChain($job, $callback = null)
+    {
+        PHPUnit::assertTrue(
+            $this->pushed($job, $callback)->isNotEmpty(),
+            "The expected [{$job}] job was not pushed."
+        );
+
+        $this->assertPushedWithChainOfClasses($job, [], $callback);
+    }
+
+    /**
+     * Assert if a job was pushed with chained jobs based on a truth-test callback.
+     *
+     * @param  string  $job
+     * @param  array  $expectedChain
+     * @param  callable|null  $callback
+     * @return void
+     */
+    protected function assertPushedWithChainOfObjects($job, $expectedChain, $callback)
+    {
+        $chain = collect($expectedChain)->map(fn ($job) => serialize($job))->all();
+
+        PHPUnit::assertTrue(
+            $this->pushed($job, $callback)->filter(fn ($job) => $job->chained == $chain)->isNotEmpty(),
+            'The expected chain was not pushed.'
+        );
+    }
+
+    /**
+     * Assert if a job was pushed with chained jobs based on a truth-test callback.
+     *
+     * @param  string  $job
+     * @param  array  $expectedChain
+     * @param  callable|null  $callback
+     * @return void
+     */
+    protected function assertPushedWithChainOfClasses($job, $expectedChain, $callback)
+    {
+        $matching = $this->pushed($job, $callback)->map->chained->map(function ($chain) {
+            return collect($chain)->map(function ($job) {
+                return get_class(unserialize($job));
+            });
+        })->filter(function ($chain) use ($expectedChain) {
+            return $chain->all() === $expectedChain;
+        });
+
+        PHPUnit::assertTrue(
+            $matching->isNotEmpty(), 'The expected chain was not pushed.'
+        );
+    }
+
+    /**
+     * Determine if the given chain is entirely composed of objects.
+     *
+     * @param  array  $chain
+     * @return bool
+     */
+    protected function isChainOfObjects($chain)
+    {
+        return ! collect($chain)->contains(fn ($job) => ! is_object($job));
+    }
+
+    /**
+     * Determine if a job was pushed based on a truth-test callback.
+     *
+     * @param  string|\Closure  $job
+     * @param  callable|null  $callback
+     * @return void
+     */
+    public function assertNotPushed($job, $callback = null)
+    {
+        if ($job instanceof Closure) {
+            [$job, $callback] = [$this->firstClosureParameterType($job), $job];
+        }
+
+        PHPUnit::assertCount(
+            0, $this->pushed($job, $callback),
+            "The unexpected [{$job}] job was pushed."
+        );
+    }
+
+    /**
+     * Assert that no jobs were pushed.
+     *
+     * @return void
+     */
+    public function assertNothingPushed()
+    {
+        PHPUnit::assertEmpty($this->jobs, 'Jobs were pushed unexpectedly.');
+    }
+
+    /**
+     * Get all of the jobs matching a truth-test callback.
+     *
+     * @param  string  $job
+     * @param  callable|null  $callback
+     * @return \Illuminate\Support\Collection
+     */
+    public function pushed($job, $callback = null)
+    {
+        if (! $this->hasPushed($job)) {
+            return collect();
+        }
+
+        $callback = $callback ?: fn () => true;
+
+        return collect($this->jobs[$job])->filter(
+            fn ($data) => $callback($data['job'], $data['queue'], $data['data'])
+        )->pluck('job');
+    }
+
+    /**
+     * Determine if there are any stored jobs for a given class.
+     *
+     * @param  string  $job
+     * @return bool
+     */
+    public function hasPushed($job)
+    {
+        return isset($this->jobs[$job]) && ! empty($this->jobs[$job]);
+    }
+
+    /**
+     * Resolve a queue connection instance.
+     *
+     * @param  mixed  $value
+     * @return \Illuminate\Contracts\Queue\Queue
+     */
+    public function connection($value = null)
+    {
+        return $this;
+    }
+
+    /**
+     * Get the size of the queue.
+     *
+     * @param  string|null  $queue
+     * @return int
+     */
+    public function size($queue = null)
+    {
+        return collect($this->jobs)->flatten(1)->filter(
+            fn ($job) => $job['queue'] === $queue
+        )->count();
+    }
+
+    /**
+     * Push a new job onto the queue.
+     *
+     * @param  string|object  $job
+     * @param  mixed  $data
+     * @param  string|null  $queue
+     * @return mixed
+     */
+    public function push($job, $data = '', $queue = null)
+    {
+        if ($this->shouldFakeJob($job)) {
+            $this->jobs[is_object($job) ? get_class($job) : $job][] = [
+                'job' => $job,
+                'queue' => $queue,
+                'data' => $data,
+            ];
+        } else {
+            is_object($job) && isset($job->connection)
+                ? $this->queue->connection($job->connection)->push($job, $data, $queue)
+                : $this->queue->push($job, $data, $queue);
+        }
+    }
+
+    /**
+     * Determine if a job should be faked or actually dispatched.
+     *
+     * @param  object  $job
+     * @return bool
+     */
+    public function shouldFakeJob($job)
+    {
+        if ($this->shouldDispatchJob($job)) {
+            return false;
+        }
+
+        if ($this->jobsToFake->isEmpty()) {
+            return true;
+        }
+
+        return $this->jobsToFake->contains(
+            fn ($jobToFake) => $job instanceof ((string) $jobToFake)
+        );
+    }
+
+    /**
+     * Determine if a job should be pushed to the queue instead of faked.
+     *
+     * @param  object  $job
+     * @return bool
+     */
+    protected function shouldDispatchJob($job)
+    {
+        if ($this->jobsToBeQueued->isEmpty()) {
+            return false;
+        }
+
+        return $this->jobsToBeQueued->contains(
+            fn ($jobToQueue) => $job instanceof ((string) $jobToQueue)
+        );
+    }
+
+    /**
+     * Push a raw payload onto the queue.
+     *
+     * @param  string  $payload
+     * @param  string|null  $queue
+     * @param  array  $options
+     * @return mixed
+     */
+    public function pushRaw($payload, $queue = null, array $options = [])
+    {
+        //
+    }
+
+    /**
+     * Push a new job onto the queue after (n) seconds.
+     *
+     * @param  \DateTimeInterface|\DateInterval|int  $delay
+     * @param  string|object  $job
+     * @param  mixed  $data
+     * @param  string|null  $queue
+     * @return mixed
+     */
+    public function later($delay, $job, $data = '', $queue = null)
+    {
+        return $this->push($job, $data, $queue);
+    }
+
+    /**
+     * Push a new job onto the queue.
+     *
+     * @param  string  $queue
+     * @param  string|object  $job
+     * @param  mixed  $data
+     * @return mixed
+     */
+    public function pushOn($queue, $job, $data = '')
+    {
+        return $this->push($job, $data, $queue);
+    }
+
+    /**
+     * Push a new job onto a specific queue after (n) seconds.
+     *
+     * @param  string  $queue
+     * @param  \DateTimeInterface|\DateInterval|int  $delay
+     * @param  string|object  $job
+     * @param  mixed  $data
+     * @return mixed
+     */
+    public function laterOn($queue, $delay, $job, $data = '')
+    {
+        return $this->push($job, $data, $queue);
+    }
+
+    /**
+     * Pop the next job off of the queue.
+     *
+     * @param  string|null  $queue
+     * @return \Illuminate\Contracts\Queue\Job|null
+     */
+    public function pop($queue = null)
+    {
+        //
+    }
+
+    /**
+     * Push an array of jobs onto the queue.
+     *
+     * @param  array  $jobs
+     * @param  mixed  $data
+     * @param  string|null  $queue
+     * @return mixed
+     */
+    public function bulk($jobs, $data = '', $queue = null)
+    {
+        foreach ($jobs as $job) {
+            $this->push($job, $data, $queue);
+        }
+    }
+
+    /**
+     * Get the jobs that have been pushed.
+     *
+     * @return array
+     */
+    public function pushedJobs()
+    {
+        return $this->jobs;
+    }
+
+    /**
+     * Get the connection name for the queue.
+     *
+     * @return string
+     */
+    public function getConnectionName()
+    {
+        //
+    }
+
+    /**
+     * Set the connection name for the queue.
+     *
+     * @param  string  $name
+     * @return $this
+     */
+    public function setConnectionName($name)
+    {
+        return $this;
+    }
+
+    /**
+     * Override the QueueManager to prevent circular dependency.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     *
+     * @throws \BadMethodCallException
+     */
+    public function __call($method, $parameters)
+    {
+        throw new BadMethodCallException(sprintf(
+            'Call to undefined method %s::%s()', static::class, $method
+        ));
+    }
+}
diff --git a/vendor/illuminate/support/Timebox.php b/vendor/illuminate/support/Timebox.php
new file mode 100644
index 0000000..6e2aa4d
--- /dev/null
+++ b/vendor/illuminate/support/Timebox.php
@@ -0,0 +1,72 @@
+earlyReturn && $remainder > 0) {
+            $this->usleep($remainder);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Indicate that the timebox can return early.
+     *
+     * @return $this
+     */
+    public function returnEarly()
+    {
+        $this->earlyReturn = true;
+
+        return $this;
+    }
+
+    /**
+     * Indicate that the timebox cannot return early.
+     *
+     * @return $this
+     */
+    public function dontReturnEarly()
+    {
+        $this->earlyReturn = false;
+
+        return $this;
+    }
+
+    /**
+     * Sleep for the specified number of microseconds.
+     *
+     * @param  int  $microseconds
+     * @return void
+     */
+    protected function usleep(int $microseconds)
+    {
+        usleep($microseconds);
+    }
+}
diff --git a/vendor/illuminate/support/Traits/CapsuleManagerTrait.php b/vendor/illuminate/support/Traits/CapsuleManagerTrait.php
new file mode 100644
index 0000000..0532755
--- /dev/null
+++ b/vendor/illuminate/support/Traits/CapsuleManagerTrait.php
@@ -0,0 +1,69 @@
+container = $container;
+
+        if (! $this->container->bound('config')) {
+            $this->container->instance('config', new Fluent);
+        }
+    }
+
+    /**
+     * Make this capsule instance available globally.
+     *
+     * @return void
+     */
+    public function setAsGlobal()
+    {
+        static::$instance = $this;
+    }
+
+    /**
+     * Get the IoC container instance.
+     *
+     * @return \Illuminate\Contracts\Container\Container
+     */
+    public function getContainer()
+    {
+        return $this->container;
+    }
+
+    /**
+     * Set the IoC container instance.
+     *
+     * @param  \Illuminate\Contracts\Container\Container  $container
+     * @return void
+     */
+    public function setContainer(Container $container)
+    {
+        $this->container = $container;
+    }
+}
diff --git a/vendor/illuminate/support/Traits/ForwardsCalls.php b/vendor/illuminate/support/Traits/ForwardsCalls.php
new file mode 100644
index 0000000..e718180
--- /dev/null
+++ b/vendor/illuminate/support/Traits/ForwardsCalls.php
@@ -0,0 +1,75 @@
+{$method}(...$parameters);
+        } catch (Error|BadMethodCallException $e) {
+            $pattern = '~^Call to undefined method (?P[^:]+)::(?P[^\(]+)\(\)$~';
+
+            if (! preg_match($pattern, $e->getMessage(), $matches)) {
+                throw $e;
+            }
+
+            if ($matches['class'] != get_class($object) ||
+                $matches['method'] != $method) {
+                throw $e;
+            }
+
+            static::throwBadMethodCallException($method);
+        }
+    }
+
+    /**
+     * Forward a method call to the given object, returning $this if the forwarded call returned itself.
+     *
+     * @param  mixed  $object
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     *
+     * @throws \BadMethodCallException
+     */
+    protected function forwardDecoratedCallTo($object, $method, $parameters)
+    {
+        $result = $this->forwardCallTo($object, $method, $parameters);
+
+        if ($result === $object) {
+            return $this;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Throw a bad method call exception for the given method.
+     *
+     * @param  string  $method
+     * @return void
+     *
+     * @throws \BadMethodCallException
+     */
+    protected static function throwBadMethodCallException($method)
+    {
+        throw new BadMethodCallException(sprintf(
+            'Call to undefined method %s::%s()', static::class, $method
+        ));
+    }
+}
diff --git a/vendor/illuminate/support/Traits/Localizable.php b/vendor/illuminate/support/Traits/Localizable.php
new file mode 100644
index 0000000..1e9fa58
--- /dev/null
+++ b/vendor/illuminate/support/Traits/Localizable.php
@@ -0,0 +1,34 @@
+getLocale();
+
+        try {
+            $app->setLocale($locale);
+
+            return $callback();
+        } finally {
+            $app->setLocale($original);
+        }
+    }
+}
diff --git a/vendor/illuminate/support/Traits/ReflectsClosures.php b/vendor/illuminate/support/Traits/ReflectsClosures.php
new file mode 100644
index 0000000..bf47d7e
--- /dev/null
+++ b/vendor/illuminate/support/Traits/ReflectsClosures.php
@@ -0,0 +1,88 @@
+closureParameterTypes($closure));
+
+        if (! $types) {
+            throw new RuntimeException('The given Closure has no parameters.');
+        }
+
+        if ($types[0] === null) {
+            throw new RuntimeException('The first parameter of the given Closure is missing a type hint.');
+        }
+
+        return $types[0];
+    }
+
+    /**
+     * Get the class names of the first parameter of the given Closure, including union types.
+     *
+     * @param  \Closure  $closure
+     * @return array
+     *
+     * @throws \ReflectionException
+     * @throws \RuntimeException
+     */
+    protected function firstClosureParameterTypes(Closure $closure)
+    {
+        $reflection = new ReflectionFunction($closure);
+
+        $types = collect($reflection->getParameters())->mapWithKeys(function ($parameter) {
+            if ($parameter->isVariadic()) {
+                return [$parameter->getName() => null];
+            }
+
+            return [$parameter->getName() => Reflector::getParameterClassNames($parameter)];
+        })->filter()->values()->all();
+
+        if (empty($types)) {
+            throw new RuntimeException('The given Closure has no parameters.');
+        }
+
+        if (isset($types[0]) && empty($types[0])) {
+            throw new RuntimeException('The first parameter of the given Closure is missing a type hint.');
+        }
+
+        return $types[0];
+    }
+
+    /**
+     * Get the class names / types of the parameters of the given Closure.
+     *
+     * @param  \Closure  $closure
+     * @return array
+     *
+     * @throws \ReflectionException
+     */
+    protected function closureParameterTypes(Closure $closure)
+    {
+        $reflection = new ReflectionFunction($closure);
+
+        return collect($reflection->getParameters())->mapWithKeys(function ($parameter) {
+            if ($parameter->isVariadic()) {
+                return [$parameter->getName() => null];
+            }
+
+            return [$parameter->getName() => Reflector::getParameterClassName($parameter)];
+        })->all();
+    }
+}
diff --git a/vendor/illuminate/support/Traits/Tappable.php b/vendor/illuminate/support/Traits/Tappable.php
new file mode 100644
index 0000000..9353451
--- /dev/null
+++ b/vendor/illuminate/support/Traits/Tappable.php
@@ -0,0 +1,17 @@
+input = $input;
+    }
+
+    /**
+     * Determine if the validated input has one or more keys.
+     *
+     * @param  mixed  $keys
+     * @return bool
+     */
+    public function has($keys)
+    {
+        $keys = is_array($keys) ? $keys : func_get_args();
+
+        foreach ($keys as $key) {
+            if (! Arr::has($this->input, $key)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Determine if the validated input is missing one or more keys.
+     *
+     * @param  mixed  $keys
+     * @return bool
+     */
+    public function missing($keys)
+    {
+        return ! $this->has($keys);
+    }
+
+    /**
+     * Get a subset containing the provided keys with values from the input data.
+     *
+     * @param  mixed  $keys
+     * @return array
+     */
+    public function only($keys)
+    {
+        $results = [];
+
+        $input = $this->input;
+
+        $placeholder = new stdClass;
+
+        foreach (is_array($keys) ? $keys : func_get_args() as $key) {
+            $value = data_get($input, $key, $placeholder);
+
+            if ($value !== $placeholder) {
+                Arr::set($results, $key, $value);
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Get all of the input except for a specified array of items.
+     *
+     * @param  mixed  $keys
+     * @return array
+     */
+    public function except($keys)
+    {
+        $keys = is_array($keys) ? $keys : func_get_args();
+
+        $results = $this->input;
+
+        Arr::forget($results, $keys);
+
+        return $results;
+    }
+
+    /**
+     * Merge the validated input with the given array of additional data.
+     *
+     * @param  array  $items
+     * @return static
+     */
+    public function merge(array $items)
+    {
+        return new static(array_merge($this->input, $items));
+    }
+
+    /**
+     * Get the input as a collection.
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function collect()
+    {
+        return new Collection($this->input);
+    }
+
+    /**
+     * Get the raw, underlying input array.
+     *
+     * @return array
+     */
+    public function all()
+    {
+        return $this->input;
+    }
+
+    /**
+     * Get the instance as an array.
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return $this->all();
+    }
+
+    /**
+     * Dynamically access input data.
+     *
+     * @param  string  $name
+     * @return mixed
+     */
+    public function __get($name)
+    {
+        return $this->input[$name];
+    }
+
+    /**
+     * Dynamically set input data.
+     *
+     * @param  string  $name
+     * @param  mixed  $value
+     * @return mixed
+     */
+    public function __set($name, $value)
+    {
+        $this->input[$name] = $value;
+    }
+
+    /**
+     * Determine if an input key is set.
+     *
+     * @return bool
+     */
+    public function __isset($name)
+    {
+        return isset($this->input[$name]);
+    }
+
+    /**
+     * Remove an input key.
+     *
+     * @param  string  $name
+     * @return void
+     */
+    public function __unset($name)
+    {
+        unset($this->input[$name]);
+    }
+
+    /**
+     * Determine if an item exists at an offset.
+     *
+     * @param  mixed  $key
+     * @return bool
+     */
+    public function offsetExists($key): bool
+    {
+        return isset($this->input[$key]);
+    }
+
+    /**
+     * Get an item at a given offset.
+     *
+     * @param  mixed  $key
+     * @return mixed
+     */
+    public function offsetGet($key): mixed
+    {
+        return $this->input[$key];
+    }
+
+    /**
+     * Set the item at a given offset.
+     *
+     * @param  mixed  $key
+     * @param  mixed  $value
+     * @return void
+     */
+    public function offsetSet($key, $value): void
+    {
+        if (is_null($key)) {
+            $this->input[] = $value;
+        } else {
+            $this->input[$key] = $value;
+        }
+    }
+
+    /**
+     * Unset the item at a given offset.
+     *
+     * @param  string  $key
+     * @return void
+     */
+    public function offsetUnset($key): void
+    {
+        unset($this->input[$key]);
+    }
+
+    /**
+     * Get an iterator for the input.
+     *
+     * @return \ArrayIterator
+     */
+    public function getIterator(): Traversable
+    {
+        return new ArrayIterator($this->input);
+    }
+}
diff --git a/vendor/illuminate/support/ViewErrorBag.php b/vendor/illuminate/support/ViewErrorBag.php
new file mode 100644
index 0000000..ff9da4f
--- /dev/null
+++ b/vendor/illuminate/support/ViewErrorBag.php
@@ -0,0 +1,130 @@
+bags[$key]);
+    }
+
+    /**
+     * Get a MessageBag instance from the bags.
+     *
+     * @param  string  $key
+     * @return \Illuminate\Contracts\Support\MessageBag
+     */
+    public function getBag($key)
+    {
+        return Arr::get($this->bags, $key) ?: new MessageBag;
+    }
+
+    /**
+     * Get all the bags.
+     *
+     * @return array
+     */
+    public function getBags()
+    {
+        return $this->bags;
+    }
+
+    /**
+     * Add a new MessageBag instance to the bags.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\MessageBag  $bag
+     * @return $this
+     */
+    public function put($key, MessageBagContract $bag)
+    {
+        $this->bags[$key] = $bag;
+
+        return $this;
+    }
+
+    /**
+     * Determine if the default message bag has any messages.
+     *
+     * @return bool
+     */
+    public function any()
+    {
+        return $this->count() > 0;
+    }
+
+    /**
+     * Get the number of messages in the default bag.
+     *
+     * @return int
+     */
+    public function count(): int
+    {
+        return $this->getBag('default')->count();
+    }
+
+    /**
+     * Dynamically call methods on the default bag.
+     *
+     * @param  string  $method
+     * @param  array  $parameters
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->getBag('default')->$method(...$parameters);
+    }
+
+    /**
+     * Dynamically access a view error bag.
+     *
+     * @param  string  $key
+     * @return \Illuminate\Contracts\Support\MessageBag
+     */
+    public function __get($key)
+    {
+        return $this->getBag($key);
+    }
+
+    /**
+     * Dynamically set a view error bag.
+     *
+     * @param  string  $key
+     * @param  \Illuminate\Contracts\Support\MessageBag  $value
+     * @return void
+     */
+    public function __set($key, $value)
+    {
+        $this->put($key, $value);
+    }
+
+    /**
+     * Convert the default bag to its string representation.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return (string) $this->getBag('default');
+    }
+}
diff --git a/vendor/illuminate/support/composer.json b/vendor/illuminate/support/composer.json
new file mode 100644
index 0000000..61b01cf
--- /dev/null
+++ b/vendor/illuminate/support/composer.json
@@ -0,0 +1,58 @@
+{
+    "name": "illuminate/support",
+    "description": "The Illuminate Support package.",
+    "license": "MIT",
+    "homepage": "https://laravel.com",
+    "support": {
+        "issues": "https://github.com/laravel/framework/issues",
+        "source": "https://github.com/laravel/framework"
+    },
+    "authors": [
+        {
+            "name": "Taylor Otwell",
+            "email": "taylor@laravel.com"
+        }
+    ],
+    "require": {
+        "php": "^8.0.2",
+        "ext-ctype": "*",
+        "ext-filter": "*",
+        "ext-mbstring": "*",
+        "doctrine/inflector": "^2.0",
+        "illuminate/collections": "^9.0",
+        "illuminate/conditionable": "^9.0",
+        "illuminate/contracts": "^9.0",
+        "illuminate/macroable": "^9.0",
+        "nesbot/carbon": "^2.62.1",
+        "voku/portable-ascii": "^2.0"
+    },
+    "conflict": {
+        "tightenco/collect": "<5.5.33"
+    },
+    "autoload": {
+        "psr-4": {
+            "Illuminate\\Support\\": ""
+        },
+        "files": [
+            "helpers.php"
+        ]
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "9.x-dev"
+        }
+    },
+    "suggest": {
+        "illuminate/filesystem": "Required to use the composer class (^9.0).",
+        "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.0.2).",
+        "ramsey/uuid": "Required to use Str::uuid() (^4.7).",
+        "symfony/process": "Required to use the composer class (^6.0).",
+        "symfony/uid": "Required to use Str::ulid() (^6.0).",
+        "symfony/var-dumper": "Required to use the dd function (^6.0).",
+        "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)."
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/support/helpers.php b/vendor/illuminate/support/helpers.php
new file mode 100755
index 0000000..bb8d178
--- /dev/null
+++ b/vendor/illuminate/support/helpers.php
@@ -0,0 +1,425 @@
+ $value) {
+            if (is_numeric($key)) {
+                $start++;
+
+                $array[$start] = Arr::pull($array, $key);
+            }
+        }
+
+        return $array;
+    }
+}
+
+if (! function_exists('blank')) {
+    /**
+     * Determine if the given value is "blank".
+     *
+     * @param  mixed  $value
+     * @return bool
+     */
+    function blank($value)
+    {
+        if (is_null($value)) {
+            return true;
+        }
+
+        if (is_string($value)) {
+            return trim($value) === '';
+        }
+
+        if (is_numeric($value) || is_bool($value)) {
+            return false;
+        }
+
+        if ($value instanceof Countable) {
+            return count($value) === 0;
+        }
+
+        return empty($value);
+    }
+}
+
+if (! function_exists('class_basename')) {
+    /**
+     * Get the class "basename" of the given object / class.
+     *
+     * @param  string|object  $class
+     * @return string
+     */
+    function class_basename($class)
+    {
+        $class = is_object($class) ? get_class($class) : $class;
+
+        return basename(str_replace('\\', '/', $class));
+    }
+}
+
+if (! function_exists('class_uses_recursive')) {
+    /**
+     * Returns all traits used by a class, its parent classes and trait of their traits.
+     *
+     * @param  object|string  $class
+     * @return array
+     */
+    function class_uses_recursive($class)
+    {
+        if (is_object($class)) {
+            $class = get_class($class);
+        }
+
+        $results = [];
+
+        foreach (array_reverse(class_parents($class)) + [$class => $class] as $class) {
+            $results += trait_uses_recursive($class);
+        }
+
+        return array_unique($results);
+    }
+}
+
+if (! function_exists('e')) {
+    /**
+     * Encode HTML special characters in a string.
+     *
+     * @param  \Illuminate\Contracts\Support\DeferringDisplayableValue|\Illuminate\Contracts\Support\Htmlable|\BackedEnum|string|null  $value
+     * @param  bool  $doubleEncode
+     * @return string
+     */
+    function e($value, $doubleEncode = true)
+    {
+        if ($value instanceof DeferringDisplayableValue) {
+            $value = $value->resolveDisplayableValue();
+        }
+
+        if ($value instanceof Htmlable) {
+            return $value->toHtml();
+        }
+
+        if ($value instanceof BackedEnum) {
+            $value = $value->value;
+        }
+
+        return htmlspecialchars($value ?? '', ENT_QUOTES, 'UTF-8', $doubleEncode);
+    }
+}
+
+if (! function_exists('env')) {
+    /**
+     * Gets the value of an environment variable.
+     *
+     * @param  string  $key
+     * @param  mixed  $default
+     * @return mixed
+     */
+    function env($key, $default = null)
+    {
+        return Env::get($key, $default);
+    }
+}
+
+if (! function_exists('filled')) {
+    /**
+     * Determine if a value is "filled".
+     *
+     * @param  mixed  $value
+     * @return bool
+     */
+    function filled($value)
+    {
+        return ! blank($value);
+    }
+}
+
+if (! function_exists('object_get')) {
+    /**
+     * Get an item from an object using "dot" notation.
+     *
+     * @param  object  $object
+     * @param  string|null  $key
+     * @param  mixed  $default
+     * @return mixed
+     */
+    function object_get($object, $key, $default = null)
+    {
+        if (is_null($key) || trim($key) === '') {
+            return $object;
+        }
+
+        foreach (explode('.', $key) as $segment) {
+            if (! is_object($object) || ! isset($object->{$segment})) {
+                return value($default);
+            }
+
+            $object = $object->{$segment};
+        }
+
+        return $object;
+    }
+}
+
+if (! function_exists('optional')) {
+    /**
+     * Provide access to optional objects.
+     *
+     * @param  mixed  $value
+     * @param  callable|null  $callback
+     * @return mixed
+     */
+    function optional($value = null, callable $callback = null)
+    {
+        if (is_null($callback)) {
+            return new Optional($value);
+        } elseif (! is_null($value)) {
+            return $callback($value);
+        }
+    }
+}
+
+if (! function_exists('preg_replace_array')) {
+    /**
+     * Replace a given pattern with each value in the array in sequentially.
+     *
+     * @param  string  $pattern
+     * @param  array  $replacements
+     * @param  string  $subject
+     * @return string
+     */
+    function preg_replace_array($pattern, array $replacements, $subject)
+    {
+        return preg_replace_callback($pattern, function () use (&$replacements) {
+            foreach ($replacements as $value) {
+                return array_shift($replacements);
+            }
+        }, $subject);
+    }
+}
+
+if (! function_exists('retry')) {
+    /**
+     * Retry an operation a given number of times.
+     *
+     * @param  int|array  $times
+     * @param  callable  $callback
+     * @param  int|\Closure  $sleepMilliseconds
+     * @param  callable|null  $when
+     * @return mixed
+     *
+     * @throws \Exception
+     */
+    function retry($times, callable $callback, $sleepMilliseconds = 0, $when = null)
+    {
+        $attempts = 0;
+
+        $backoff = [];
+
+        if (is_array($times)) {
+            $backoff = $times;
+
+            $times = count($times) + 1;
+        }
+
+        beginning:
+        $attempts++;
+        $times--;
+
+        try {
+            return $callback($attempts);
+        } catch (Exception $e) {
+            if ($times < 1 || ($when && ! $when($e))) {
+                throw $e;
+            }
+
+            $sleepMilliseconds = $backoff[$attempts - 1] ?? $sleepMilliseconds;
+
+            if ($sleepMilliseconds) {
+                usleep(value($sleepMilliseconds, $attempts, $e) * 1000);
+            }
+
+            goto beginning;
+        }
+    }
+}
+
+if (! function_exists('str')) {
+    /**
+     * Get a new stringable object from the given string.
+     *
+     * @param  string|null  $string
+     * @return \Illuminate\Support\Stringable|mixed
+     */
+    function str($string = null)
+    {
+        if (func_num_args() === 0) {
+            return new class
+            {
+                public function __call($method, $parameters)
+                {
+                    return Str::$method(...$parameters);
+                }
+
+                public function __toString()
+                {
+                    return '';
+                }
+            };
+        }
+
+        return Str::of($string);
+    }
+}
+
+if (! function_exists('tap')) {
+    /**
+     * Call the given Closure with the given value then return the value.
+     *
+     * @param  mixed  $value
+     * @param  callable|null  $callback
+     * @return mixed
+     */
+    function tap($value, $callback = null)
+    {
+        if (is_null($callback)) {
+            return new HigherOrderTapProxy($value);
+        }
+
+        $callback($value);
+
+        return $value;
+    }
+}
+
+if (! function_exists('throw_if')) {
+    /**
+     * Throw the given exception if the given condition is true.
+     *
+     * @param  mixed  $condition
+     * @param  \Throwable|string  $exception
+     * @param  mixed  ...$parameters
+     * @return mixed
+     *
+     * @throws \Throwable
+     */
+    function throw_if($condition, $exception = 'RuntimeException', ...$parameters)
+    {
+        if ($condition) {
+            if (is_string($exception) && class_exists($exception)) {
+                $exception = new $exception(...$parameters);
+            }
+
+            throw is_string($exception) ? new RuntimeException($exception) : $exception;
+        }
+
+        return $condition;
+    }
+}
+
+if (! function_exists('throw_unless')) {
+    /**
+     * Throw the given exception unless the given condition is true.
+     *
+     * @param  mixed  $condition
+     * @param  \Throwable|string  $exception
+     * @param  mixed  ...$parameters
+     * @return mixed
+     *
+     * @throws \Throwable
+     */
+    function throw_unless($condition, $exception = 'RuntimeException', ...$parameters)
+    {
+        throw_if(! $condition, $exception, ...$parameters);
+
+        return $condition;
+    }
+}
+
+if (! function_exists('trait_uses_recursive')) {
+    /**
+     * Returns all traits used by a trait and its traits.
+     *
+     * @param  string  $trait
+     * @return array
+     */
+    function trait_uses_recursive($trait)
+    {
+        $traits = class_uses($trait) ?: [];
+
+        foreach ($traits as $trait) {
+            $traits += trait_uses_recursive($trait);
+        }
+
+        return $traits;
+    }
+}
+
+if (! function_exists('transform')) {
+    /**
+     * Transform the given value if it is present.
+     *
+     * @param  mixed  $value
+     * @param  callable  $callback
+     * @param  mixed  $default
+     * @return mixed|null
+     */
+    function transform($value, callable $callback, $default = null)
+    {
+        if (filled($value)) {
+            return $callback($value);
+        }
+
+        if (is_callable($default)) {
+            return $default($value);
+        }
+
+        return $default;
+    }
+}
+
+if (! function_exists('windows_os')) {
+    /**
+     * Determine whether the current environment is Windows based.
+     *
+     * @return bool
+     */
+    function windows_os()
+    {
+        return PHP_OS_FAMILY === 'Windows';
+    }
+}
+
+if (! function_exists('with')) {
+    /**
+     * Return the given value, optionally passed through the given callback.
+     *
+     * @template TValue
+     * @template TReturn
+     *
+     * @param  TValue  $value
+     * @param  (callable(TValue): (TReturn))|null  $callback
+     * @return ($callback is null ? TValue : TReturn)
+     */
+    function with($value, callable $callback = null)
+    {
+        return is_null($callback) ? $value : $callback($value);
+    }
+}
diff --git a/vendor/jasongrimes/paginator/.gitignore b/vendor/jasongrimes/paginator/.gitignore
new file mode 100644
index 0000000..7e48875
--- /dev/null
+++ b/vendor/jasongrimes/paginator/.gitignore
@@ -0,0 +1,5 @@
+/vendor/
+composer.lock
+*.swp
+*.swo
+*~
diff --git a/vendor/jasongrimes/paginator/.travis.yml b/vendor/jasongrimes/paginator/.travis.yml
new file mode 100644
index 0000000..77d4c9c
--- /dev/null
+++ b/vendor/jasongrimes/paginator/.travis.yml
@@ -0,0 +1,14 @@
+language: php
+
+php:
+  - 5.3
+  - 5.4
+  - 5.5
+  - 5.6
+  - hhvm
+
+
+before_script: composer install --dev --prefer-source
+
+notifications:
+  email: jason@grimesit.com
diff --git a/vendor/jasongrimes/paginator/LICENSE b/vendor/jasongrimes/paginator/LICENSE
new file mode 100644
index 0000000..1bc1549
--- /dev/null
+++ b/vendor/jasongrimes/paginator/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Jason Grimes
+
+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/jasongrimes/paginator/README.md b/vendor/jasongrimes/paginator/README.md
new file mode 100644
index 0000000..2cbd848
--- /dev/null
+++ b/vendor/jasongrimes/paginator/README.md
@@ -0,0 +1,170 @@
+PHP Paginator
+=============
+
+[![Build Status](https://travis-ci.org/jasongrimes/php-paginator.svg?branch=master)](https://travis-ci.org/jasongrimes/php-paginator)
+[![Total Downloads](https://poser.pugx.org/jasongrimes/paginator/downloads.svg)](https://packagist.org/packages/jasongrimes/paginator)
+[![Latest Stable Version](https://poser.pugx.org/jasongrimes/paginator/v/stable.svg)](https://packagist.org/packages/jasongrimes/paginator) [![Latest Unstable Version](https://poser.pugx.org/jasongrimes/paginator/v/unstable.svg)](https://packagist.org/packages/jasongrimes/paginator) [![License](https://poser.pugx.org/jasongrimes/paginator/license.svg)](https://packagist.org/packages/jasongrimes/paginator)
+
+A lightweight PHP paginator, for generating pagination controls in the style of Stack Overflow and Flickr. The "first" and "last" page links are shown inline as page numbers, and excess page numbers are replaced by ellipses.
+
+## Screenshots
+
+These examples show how the paginator handles overflow when there are a lot of pages.
+They're rendered using the sample templates provided in the [examples](examples/) directory,
+which depend on Twitter Bootstrap. 
+You can easily use your own custom HTML to render the pagination control instead.
+
+Default template:
+
+
+
+
+ +Small template (useful for mobile interfaces): + +
+
+
+ +The small template renders the page number as a select list to save space: + + + + +## Installation + +Install with composer: + + composer require "jasongrimes/paginator:~1.0" + +## Basic usage + +Here's a quick example using the defaults: + + + + + + + + + + + + + + +This will output the following: + + + + + +To render it with one of the other example templates, just make sure the variable is named `$paginator` and then include the template file: + + $paginator = new Paginator($totalItems, $itemsPerPage, $currentPage, $urlPattern); + + include '../vendor/jasongrimes/paginator/examples/pagerSmall.phtml'; + +
+ +If the example templates don't suit you, you can iterate over the paginated data to render your own pagination control. + +## Rendering a custom pagination control + +Use `$paginator->getPages()`, `$paginator->getNextUrl()`, and `$paginator->getPrevUrl()` to render a pagination control with your own HTML. +For example: + +
    + getPrevUrl()): ?> +
  • « Previous
  • + + + getPages() as $page): ?> + +
  • > + +
  • + +
  • + + + + getNextUrl()): ?> +
  • Next »
  • + +
+ +

+ getTotalItems(); ?> found. + + Showing + getCurrentPageFirstItem(); ?> + - + getCurrentPageLastItem(); ?>. +

+ + +See the [examples](examples) directory for more sample templates. + +## Pages data structure + + $paginator->getPages(); + +`getPages()` returns a data structure like the following: + + array ( + array ('num' => 1, 'url' => '/foo/page/1', 'isCurrent' => false), + array ('num' => '...', 'url' => NULL, 'isCurrent' => false), + array ('num' => 5, 'url' => '/foo/page/5', 'isCurrent' => false), + array ('num' => 6, 'url' => '/foo/page/6', 'isCurrent' => false), + array ('num' => 7, 'url' => '/foo/page/7', 'isCurrent' => false), + array ('num' => 8, 'url' => '/foo/page/8', 'isCurrent' => true), + array ('num' => 9, 'url' => '/foo/page/9', 'isCurrent' => false), + array ('num' => 10, 'url' => '/foo/page/10', 'isCurrent' => false), + array ('num' => 11, 'url' => '/foo/page/11', 'isCurrent' => false), + array ('num' => 12, 'url' => '/foo/page/12', 'isCurrent' => false), + array ('num' => '...', 'url' => NULL, 'isCurrent' => false), + array ('num' => 20, 'url' => '/foo/page/20', 'isCurrent' => false), + ) + +## Customizing the number of pages shown + +By default, no more than 10 pages are shown, including the first and last page, with the overflow replaced by ellipses. +To change the default number of pages: + + $paginator->setMaxPagesToShow(5); + diff --git a/vendor/jasongrimes/paginator/composer.json b/vendor/jasongrimes/paginator/composer.json new file mode 100644 index 0000000..8883b6a --- /dev/null +++ b/vendor/jasongrimes/paginator/composer.json @@ -0,0 +1,29 @@ +{ + "name": "jasongrimes/paginator", + "type": "library", + "description": "A lightweight PHP paginator, for generating pagination controls in the style of Stack Overflow and Flickr. The 'first' and 'last' page links are shown inline as page numbers, and excess page numbers are replaced by ellipses.", + "keywords": ["paginator", "pagination", "pager"], + "homepage": "http://github.com/jasongrimes/php-paginator", + "license": "MIT", + "authors": [ + { + "name": "Jason Grimes", + "email": "jason@grimesit.com" + } + ], + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "autoload": { + "psr-0": {"JasonGrimes": "src/"} + }, + "autoload-dev": { + "psr-0": {"JasonGrimes\\Tests": "tests/"} + } + +} + + diff --git a/vendor/jasongrimes/paginator/examples/pager.phtml b/vendor/jasongrimes/paginator/examples/pager.phtml new file mode 100644 index 0000000..412220a --- /dev/null +++ b/vendor/jasongrimes/paginator/examples/pager.phtml @@ -0,0 +1,21 @@ +getNumPages() > 1): ?> +
    + getPrevUrl()): ?> +
  • « Previous
  • + + + getPages() as $page): ?> + +
  • > + +
  • + +
  • + + + + getNextUrl()): ?> +
  • Next »
  • + +
+ diff --git a/vendor/jasongrimes/paginator/examples/pager.twig b/vendor/jasongrimes/paginator/examples/pager.twig new file mode 100644 index 0000000..002049b --- /dev/null +++ b/vendor/jasongrimes/paginator/examples/pager.twig @@ -0,0 +1,19 @@ +{% if paginator.numPages > 1 %} +
    + {% if paginator.prevUrl %} +
  • « Previous
  • + {% endif %} + + {% for page in paginator.pages %} + {% if page.url %} +
  • {{ page.num }}
  • + {% else %} +
  • {{ page.num }}
  • + {% endif %} + {% endfor %} + + {% if paginator.nextUrl %} +
  • Next »
  • + {% endif %} +
+{% endif %} \ No newline at end of file diff --git a/vendor/jasongrimes/paginator/examples/pagerSmall.phtml b/vendor/jasongrimes/paginator/examples/pagerSmall.phtml new file mode 100644 index 0000000..b49870e --- /dev/null +++ b/vendor/jasongrimes/paginator/examples/pagerSmall.phtml @@ -0,0 +1,49 @@ +getNumPages() > 1): ?> +
+ getPrevUrl()): ?> + + « Prev + + + + + + getNextUrl()): ?> + + Next » + + +
+ + + + diff --git a/vendor/jasongrimes/paginator/examples/pagerSmall.twig b/vendor/jasongrimes/paginator/examples/pagerSmall.twig new file mode 100644 index 0000000..0b3ef1a --- /dev/null +++ b/vendor/jasongrimes/paginator/examples/pagerSmall.twig @@ -0,0 +1,49 @@ +{% if paginator.numPages > 1 %} +
+ {% if paginator.prevUrl %} + + « Prev + + {% endif %} + + + + {% if paginator.nextUrl %} + + Next » + + {% endif %} +
+{% endif %} + +{# Depends on this little bit of javascript being included somewhere: + $(function() { + $('.paginator-select-page').on('change', function() { + document.location = $(this).val(); + }); + // Workaround to prevent iOS from zooming the page when clicking the select list: + $('.paginator-select-page') + .on('focus', function() { + if (/(iPad|iPhone|iPod)/g.test(navigator.userAgent)) { + $(this).css('font-size', '16px'); + } + }) + .on('blur', function() { + if (/(iPad|iPhone|iPod)/g.test(navigator.userAgent)) { + $(this).css('font-size', ''); + } + }) + ; + }); +#} + diff --git a/vendor/jasongrimes/paginator/examples/screenshot-default-first.png b/vendor/jasongrimes/paginator/examples/screenshot-default-first.png new file mode 100644 index 0000000000000000000000000000000000000000..958b72e358722432565a85502818fe2be178ee4c GIT binary patch literal 12648 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 literal 0 HcmV?d00001 diff --git a/vendor/jasongrimes/paginator/examples/screenshot-default-last.png b/vendor/jasongrimes/paginator/examples/screenshot-default-last.png new file mode 100644 index 0000000000000000000000000000000000000000..0c0ad8b281d61d268a282de804fb254fb632e530 GIT binary patch literal 14620 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 literal 0 HcmV?d00001 diff --git a/vendor/jasongrimes/paginator/examples/screenshot-small-first.png b/vendor/jasongrimes/paginator/examples/screenshot-small-first.png new file mode 100644 index 0000000000000000000000000000000000000000..042d39048447a733b9b6fbcfbc9bbc22875a2f64 GIT binary patch literal 8489 zcmcI|Wl)^mvhEBLJTN%H-I+myd(hw-EZE>az~C+u!7UIh0fGhz7MuXVCAdrQ!QJKX z?S1Y!yXyYDb>FJ>Ri0i!qieAYS{|?1TIb4A3jTbHlF9&AB4h|od zE}JheTQ3(*Gf{KxIpYUT-MB*n-Xt*4^F>IBaA%GD#NTOu_QEbKN!pMNKQ=Bd^3k`Y z0Z0yQ-ckB}ljHK}@`v;tP9(rkoX_?>cH}kYBZ{;~gvToo;5+&;DgTFc;saSjgHSYv zyM&Hej@ODiGB05S#w7uXgtEZxC4d80sB{8eJC>Yyo8hOJRX2G|u>pB)iVjDD=L9Nf zh&2;ApVvVTxDkRNwE4ld-+d31q?mk)me^>sj6M)D2ezU}WtfAh3C)?^k~v=o>bUthy9apbhPL!*xz z6X>{}Sug@JiwD)7({xOspv!Uui_04kX9+~;5c3m8t*(juXnr?wC+&XJKsnU$eDXqVv z*QM%jlg}>9SXN@yq96W{?*of~6O*z&kCV{JSNiIs`*Kww)?8fhZGz2eaWh+X30A=t zb(bt#1dukl2-<<$3$!=Z2*me$Q6nD$AdbhgbfdD2Qp_*m;+tTV#k#}dTh|fh-uJh?Bq;C?cituHJQqEWcqk4`7qrJvDkrrocerSs> zpMdn*S^W3))cIb|Aq%-DRfex^^j@(sRp-aq}49X1ek;99Ek z+e+GkjK_MaXrEY#9xJ6+n{N8ss?QBpbF^9w^XX*GcpuN0S{a{%Y~Lky`L42Q;Rmdu zjY`)L_OOZPi^%F_?IqMR7ja~K@Q8nYD?BX<7Yn8edeNsBLaqWl$NO^!CLH0AJ;j%` z?X~61pG{;4f*3{qzOzgJV~@K$Is2PB#AuwIHpD=D(kSMbC*Uu31|XpS&kJep!hn7p zX>*|4IxpaIh-`S3`KCZ0g{p~5m*YLgz0!^XDvJ@s>lfg4JBpK*die@g5CPg>2+b-T z)|mN=l18+Xgq?H<8L13=a;J(R4y6UBQ}p5y5mkhrFgEL7T$6|aH|m4jhEc4MT(Tu; zsVEEFk1_7NsDs4S9aIp5K}wwVjw(YgplT;yrPJ#u_Ns=NX+shHQ0J~X3&EL@8PFb+ zH;rSDixqyQ%O3LukkyT}`~&6M)Ro}^C4RTn5mNw1b`04H^A%x$NGnZ6_vNbBmGT`! z5cOeu&fh>Zf){9U5JoC2SB7K+3z^tHApJv)-Tna&+|6fe?Lc58|209#p(6-T-Yn&#_617M{I&} z5?l-~x#X`+7kV9RL&zW9fN02Wi1W?xO}kc+lj}6;RJ2Nr(zW{1?H7akVoQL=uGtn@ zx%cKv89NuVqlhC}={v8_=kJGXjH|yr=)U z=h?!!_L&V*4@o_~y8;q-G!p>F)rZ03;o+AS$;U-2TB};Biu;sa&=~%%I!mnCp=@2SmT8O5PTASKtTx{%X ztXwwrCac!Z(b+1?;r_c2=0~bGA zqFcIJ`yUcf4_1(LUNd`tB%_47Z9h7_KL7MNmaE6S$Fj$$2SZ)F(6I2g`nmc@k5Q}| zZIi>^Y*kK{MCNHGTzJ`i)Scq+$4tTKMnP#y>7UyE^9;&)?hfZrWq8*Ko8WR6dj-%xz2o9AY$m3`yJ{n1ty2xPF9A zWSKvGfpS0+abgVvQvHYX555MGSNu>F z6}A5PiOPxh6R~|(tRtTTc!wZr8B_7YN*c<`=}VV-R|bx4JDIDz1)lve#X>r^Y7$aI zZbnNrR^x(&INxadbD{Y*dwo}I2qIma8@@dLm(UArxyap!5qX)ff?r*#+J4I3F%3wS zGL&-8OZj_w*E4Bqvm+CtR9>pQU6zyTqq+mg(C?C*ONj-T!n*8(!!L$y( zE|ZFZ%MI^tb|)$*)5yVZey)$cOjw>s^-v)xc-eM->%9wKhmRf3r8aUeR-VDqi}qw2 zV7}1n9%u)YtQCG8bbpCDP@P);{m7&W${bkWv=O5hTIy|JXOOHbZX{vhwfN#8 zwE?@)wE0pCbEFO5%jX&!J;Vb2k1u+}w7oO$M{j#FvZBNW;6ZcD^OIF#7I4)V1GOQV zJPA&RVuTRr&F>gaQ@=-B8@f#wI5X@c=+nN``4%Nq3CA({oCI=X=>38 z(E~e`jk8`Y=ZQD5Q8iH)jB9ZMFK+~C9ww2~riw$c`qblMb*sXsSBYz=tSahff_62Lom>3jZ*<9VNV!ggF~ zj+>3+%=si)jP%$SoueKNyha*mh~@OG`MFeEwRHSt#b+pp(kuO&QZ4Vno$qbWV z9<35xkTc@Vwz_j$QWyXZqqIkSDevHVX+Y9$h+QM-`x0{0PaZOgd8Et>`P1fWzB2y2 z#-I)Fk1oV#DzCYie;xlBu|vzd6{+$^l;#5av5&RGRZmj|mYdQW7yv-Z{LcX@Ycm}^ z4IP>Gx`v*HYOh7DT%CC=tX(akJU-5DPiO!@+(-0DbcT9b(D^t!xxhqyBpCjM5Pg#W zQG*%i{sr-LlwdGa)1;Gg^?=d|@(A!i7$mXj=;*{ftZhWKI#;2_&Zny;Hk-U`oai#cwC; z!ND42dPPJ};q=u9D;LX?(9V)qMpI!l-x7s|mYKpLEH6F;KNSP=%O1s@u1Zu{IqF4& zomkdRjn8!jte`M6wM0|hYoELk>{wAf?0N~GnoHAp+3bHA+a7^QhcX({-fagVbe2aW z01^W8IfL*CLW^Vo$QOcgkx>jNN(Oh&=}@%70b_y=QP_B|(BjLlsHM@qJ_F`EPT-Qf zc#lbEdMp-#DUXMuG+ji&AwUe8p?VRP27W7=rCvXemtOC>97Wo#FAo6ufYF3p7RZT+ zh{(v&;^P77B)2Y@?wH53)waR+=hN>wGyx!KG)Y3>%6Ai<~6;t+pCZu>&NS@9P?m(ef{L6R{|OLH|M(s6=p*tBT~T+RC{r8 zagUpyU1b*#ZV=oclAnzholzBS4}QB--{ZgNV7{6mZoveYx__(Y>8$rXwLwJCGcz-f zJ|`?9>*&G$L-MWI6Sq0=)_Y~=o7(4goz5YE)$~T{(O1V9fQQ$2keQYgg!Hat$Oa}% z6^}0zN0Phyfeb7yMnKXCG_Y@EFffo8_d{Q5_9bfR4(3ZmU2MJC;Y7R-4LOuCFvNI< zX$Hk2KC#4$I%KSO@ zARUUOG##7iXvKRApFhv!+i62{J`5kO#mDY*jBSsJZF_UXk*U$aI0|5r+{ z1Ws4pwB*FY@|XGQ$@AlSql5eWQJ142)icEzsD*;xabKx8dRs{VbJS8xGUrZ_EQTg9wdYhU7YCQ6LC3< z&ran^v{&L@cVntBf)agPcc1U{4|r`%3RQ&gT9&^@z)ex4Pu6BX%wk1IF=oXUhYnw8 zVGwb?m6D2|HzM|m?c*1lowXx36NxbFScMI|0Vm@-C}4+2cY#ou)RhUWKV}oZi|#C0 z(u+WHXy|=oI+9UQR#tYLT!&{1;cB`kwpBWsIw zSAoKdg~g8b3v2&$P}nAuCQ-VoZ~Zj1HDY8doHqCC>0MjvEJaV|xSW1B3)@C(uT|td)x9ns74vTbA2-N>|*Z!3))<%S>^-RLb1BimU+@@AU)y z88QlGtiPEVmf?Qt&-l@_G?x=>Ij7;Mq?dT2s>8V~?5_=*@iFEd&K9k=chWBOO-p$6 z_|4{cuX67F8hJ}g26QtHSe3*DcKgAG#Th6t4R!l>zbd?pF870rEY(MPH;N2B3Gq#{ z_@7HL6_>%9@0HdT+l?CcGH$L1+52j}KVk9r*}8H|@xN#nw=>SXnuP#!D(|W>KP#yA z&BAXGzO$$(bF9yTcxdb2I!g2+&33v5l&c5MW`hJTTE}hC1aJBRVrLcQjjW*iwFfY! zIUK>pCI6P+77<+s^F3{X3uJ)3;YcEQl;dGK8}o^*&PGL}P-4;)4u)%~;ziZqspJl{ zLB&o@5Bo4agy%=U%)<|FfqX<6ZZt3gw#Tx&#w3==4&SJz5GhyjPNn(o*yvM*bh`h0 zmsKX2*09{YDRFU2WJ(&_dn;JrcKQ?}?IROag7P*Ne5o664@Yg{l3c@E(sGdzzGe4U{(G?~r!tGdw7&l2l*5}hU!{%pjP>;N{4<%a z_!x6h>hfaYbN5s6`{CT&sVVzpW9L;~yBY>Lw%%nG=Srs#f6g8133sfxd*Isj>gjyr zi}wN?P=^ve?zN`(QFVC^3ZF8E68miH7tHUigG}eItzJ>s_vYa1$$1^=IvT4`WMX7wRECAzX{oz)W^QQI!cd(Jc3Kp?jf2PjfNpkxt`HDY>ZlW1 zl(8=-{Whm;@KRsFWgVx;nUr+GenDSPJl3SIx*oow{FBRywoE%2*d!X_GJ5)X&lfPL zn`U1PaH_;C7aZFZv(J)v`PRgFolAYf`$<45^$fVt>Nka_zPs~GE@1KdVj2=>W8yRR zb*o0Uo{eR|-kCG!?ndIoH&vs?0nhq!LxVEfud44=O@Tf9%`3ab>*248Ac=8C=|8e( za^@|o!ked6^%q>l5=z&67a_WVWl9mq#;^uoscebVp?I|n{WF{tBm8gfi{dnefn$~e z84WKu*3tt8xfD`Ji?7tI*{w7}!q3HRHem3s5OuQ?pG5vdduF10{FyJN*?kK7CCRj$(^z>W6Z%?#eqms!o|=&d^3jaQehw z9!K;SzmlQ$c<27e0Jxg+VlHFgTREU#;jq{h0E{V)r_g2WX?WiqkW8Wkuyq+wt z{NT^?{kKMnOa8?-R(;q8iO>VbpK0l*&hKcjD|6(#ph6Ep;EDs|{!l4A>IqnCxe7~% zXUeo-)(8Pz@=?p{3kp0N4=s4)dOD@mX63I7uWJp=kp(8kwlK?8=qC?Oh^JILxZ9@^d6nY zbnK5<9W6&^T5Q*X4j&+<7GP!*S3&;u9=&%AkGFS={S(B#9v-sT7KfaVeIxAMEVinM zCZ=lfiN6S_p~x21gV&1l8dkqv&!LD>6@L_+MDyE3B|ASbj|%!-OAeil6fV#gsuB3I z1zj}%C9S?=-N|`xvM+Ynds`B{A+Mv@*fgnuWZo_MT>XPK6>C}n@?I#5(Bd@ZvvEqC z6sPM>$;EJP=#2}D@O)bKwij;d!8AYgmHKLlIDQ8|_o^q}2p3)U2Fsek#Z zbdMb6`Qkibx4&H?U+HqAb)ONbzb8PxdGqGsqcyrcN_BIA19dp_-i@}UM7CnQ0eyiO_lq;ESs7)H3|8rn1}K$Nvb|x zXxR>_4bawNTlO-r)oiw0+^-tiSanI?_Ks^PPH0_QdD_v|n3{WUmSqM?O?CG@TVI;s z=6+9|i}NNsN897O%9s6yX&jS{8;8&SevAWpKWhw;jq8(}d)_oaiKKhm^6E3Iulfv^ zx1G4L7-O2taT zkguohkl&DX9{lyjat6NpFLB}9PU=PsSygpZhu>gbX2OI({;9wr78lmK%(-{axirZl z{!Z23<_$l?RMuLS9AZn{`%$b$S{!2d45MpH$v92lty3SLnSz(Dki99A15Ek6s!hkb z4S%1J&5ClNGA0p!))7>M^CxHWQ3e%3F5W0a-OOp+YX$`-fAsr;6IaEakyLgk4TDBj zE%FK3SF6RI)f!M)YA7n7JU?0%Fl@585*O@&rg|8ILos^=(=PaQn{(&iF8r~tlB}7h zjafS28s@f`3L>iYFNV)9>a&zTW3DxwR53CznSwIzwl#TW?Y+acvu4#9Z%ka{ju*v9 zM`voZYJZ#F{PQE(asTA+2Nxmf0JRUG^d{}y+CyK;s%w(#`l7csfO5Rg70+=l;4u$Q zKAOv;DIm_tZSZhrxpkeR2~lSB`b`3phT4kK=hC*TE8Pu%$QrW=5xhcuVS}mo^9v$nJAJEWMi;77TQ2;wx+l0L}feWk** z!n7>82`-L~QzF^a)N+GdjnLLe#un_ku)v&}x|5)$4leQ#PjFiM#gz(17_2VS1L@r{B4U0!m z@=#h$v1c}`=9vR+ie9Sr)SMPvICz#B^VQPd57he&w9@+C%bz6A2hFyt?~{2Sc)R>p z_@IpbMFi%FeW^AwM>jsj_FdLx)>d=O#v#w$kErBhLdL@EIN+Y&J3ZsH{Zlb2+JaxD8Qlk~E(FlH(G(aUya49ZE6a8rp(JpAdf| zS%1Fr(sijh=>8^7>H%PFPVX%uxofuOJ<>$hO9awJPIYxL+0fc94jG}M+FhViMk3HU zY2gO#qlQyrw~i{xY8&q*{g=P++pzWHPt?;4(&axGLnJ@=h9p>1^G+uWqczEgkdsd1 zg#P@=bX9L=KsMsVNQkR=6Y9bz8B~(U@j@5VikA+0-#CKH)^k?Cw}7y3l^?Jv=<`US&49i--m}^MqnO+q#I9; z+X)%yET1+B3^Y^%c%T)=5YLUZP-c1QuBTmJ)}NN&o~sF-VSAa_tcSS|G5|WkE^511CN}nTs9;(LTo@ofONGyOM&o-3T2Z zCAhy_4n`Lvt}Rq8Q{N0>llJhIjq5JF;8S> zwC$UXw6|em-VG+fkN1}+IZ>dAuoMJ<;V1#K{c-O6?i!X$Ljd{20c`B<>gwHC|D5OD zL`_av#yZ9Ll!OC4z$EZw9m@`N2M7!=CJ734nXhyHLrcX~1Sn@^W)8)^DyTI#s-*d6 z;{gB~8i!qTbhWP-eDMzm5Z44p(1gB~UC+;v@ZWS3UTO^-m^6R(gq%VFB0_&vL~U}I zyd*aLa=kH-R=J6mO_~ktov$TlowycZ1aL)qnLoKB7m~*vl&L{66%lz_0U+8N8R;q~ zY7T_H0H120LjcC~2uRE1(YpSTKIVl9(W8VtH6PvLmx{df$0zBxFozw{|1U0^6daaaJS&@E`z&!aEBlnTnBdz?(PsYxCfU&2oNl|JHcge3-I{vz3(UW z>Yb`{YIW~k-Mf3AfA)$}Q#3)yOB{GE4@-$ejR`|N2+6@n%B}p1algMF?2*hh` zX%2BO-0oAKs*KdG!b=pybkW3Zb@%%!BT+FJjFNV~%5gGNz7e?LpYM8h{<418b$8cu zw|bQeS7^hWIC|yG8sw*+L`BIJCMLj|H`#`9)c20aIwVD0j{ze-A>s3@cXvC?dvMp0 z{NQ7OKgU{sNynW*7HNQcpJ+NcA_Ve~C$1 z?nr{q71OZNFBw}B=3pIQ%MvV}gw}^7CETl*8Mo~$jVLrKtwGpthl!4<2#nk@lJZgx zctH&VVF6dhdIv{di187*y4+}5J6euihfSbn?risgq9lpII(AX6<`&Zlr+K%mV%~6X<5Kd1;X4B`| zSb{Z$SnTxqoGpo_lq%M38*OkpH44!c(~)sZrG!V#r8ZSwlF{T(u^OFPHY?UfT4`h< zL|PQc+>1%6?bmO_pw_KKesLy~T*C09xIwyFo&-Oh!-t(ypGApmT1tLbhEj{}py*lMYOo^9km?thiJ$izOW{G@}mlof)Y`m#3=wp)Qc10IMekD@rKIsohhA!#K19vZK>*xDG;?PX^KiM6N*1nJdP$_eNL;&`{9J6(U_2?#ZLzre zPzzE@B`~W_-048JDYX%?SnqwvmrAA&v zHf+gj{~$%qdvLjUw>^IZ`-OFa&1rK)^Kmo!vV(lEn3j4tS00;j^?Mx-Y;Rb%LhDd% ztm5dALf^%RX4y)SVa#drL%C%IG8@GB5}{u-nW)n3-V`2fKB=~UBEPY>kkmfalb2?{ zwvt46#fuDC$jj_G>l`Sd>uwjQ_vn>SNSbiIUeokYqhnb`rTp;TW>ClQ+XhaFH)9Vm z2$u**YUiCKwbFiN%8r2~qCfF32zm+yk^~TqXa~JlgtL>#W~4s2g)jgQP(QGmPXxy25h?aTjcUTgce}zhA$J#97PzexZn)!Z`18 z0`A5MCbnrGOLbsLI$5-sq7k0uk7VKK3^?4s1C;wy;Wk2pB7LWKc@URf=$|cY(Ie>vbvvl+wS)Yf2jgU&-)6t z{DGK6zyuCz5+o<8)JW>-C>BYHG5TMTibdIray&&&{%HpkK9P4cdArGe(^%t^3|v{b z$?&^=5W@DPRh9W9)?MvOm?fovvIZMj9)xUm)@m1~bjY9I2@?;NK59DfO2P+^KA)qgGaEspYkZ|gt! z)TKG2#;xtD9iyhCn^M85Q>C?1>|t~oy^PZm;LhsK?EdkF-#zKk{s!+zXqI@6y~4Bd zj=Lp`S2@rUn>(UCvOT{&!8_YK<3UkMYQShf<Nn%i_yVpEx+8eI7FFE~~@(6a6pM zj4ZTv0(JydQSLc6(c_keO`(g2Csel(540!TC($R;h*6hqX;S_I=Cgc6o32L_QEt}zx&#QIqLT%;kc^z)X#B0`TBx{hh0=E@6-y65<)fmB(aNeI*vG&S0nK|(+L#9LKLk2?#s>0=Z<%6m>s*^(o@haqC@;bXZxoy1u1zp~CpD|nzTKCPm6Clt25IG53P%L`xFXA8Lhw=FGXa6AM zxOJ$`E_a;&qK$Ni>>=j~Zl*d^ZFFelY8Q~<1}iG6{92i< zpN*c4AF-gH%<Pn?kdpsl3@El~t2a%|{rll)6FDpo~R#k?Zs z>+aD?qo%=lm=vzar}%MK`j7p%cMfqzC$lF7JIE=p#~?uV1uH9_MR%TS;M31)!cz!> zcSTomU#qEuL>-lELlY)*uEC;ONeu5~jiTWnzF!D~FoW8I=ayw-!aPd8>GnFu_D@Sn zR$`1%3=b~o0&@K2-nC1sGOpxfDEX}pqP$|R0avgw@Jje@Y_UddzF4m6XGI(MLhQAA ze;xeVnv+K9*PVpse%%(#W@k;r6Z=O7kJ$F5)&zI9XSl8;WU9-NPNVdCrgCB;# z{a}I~&xe5LJGjxN^wt`vQ3IIPztsLuoOW=Phpx45s+O>Uh?)Bu(MxtCsBS^^qi@f2 zH{@7wPw-Di{7u6#F|VGZUi;RL?8wh!ZA$GYK0*hBj>A*SsXb=HkZ&g|wp}fL#}VWu zVynye?KQQ_9UTxMf8UGSAt4Qq+~=vMq3pbHp;6C(W!jaw1|c&~r8r%cak63&X4?w` zu*2Otqc8g_?ycs4=#c-)?bU4tc@W$$xDSC7_YXh6XNHbPbYYx64IX8vM6^eY9@Td& zxp&_rKgNeQhu>1~B=GP(g2-Ox4i}8&#^ZHJXN10O^IzR3@4(S3s$PRea(q-R3;bq< zyB{VhM#^hjlx=Nlj%#Ew$j<+_11B6!h zmUKKvjx`3r1*37NJIe_lk-8w4wu^Jz3q^ItU7{C0PjS1%t&XbgOD;-oRmUFdrv$wO zuj_$UTgJ<4jlKqVOy|UFbuS5Pfw!Js9wfe)w}m%m+XF%gVF~%q>i0uK6SN6S3CsnV zq7{dbQ^94@DgTE<-48+qBLz2isV^1J`(^vzCIcUs59;REN4oRaaoOLQu=#?awhMN5 z3*-J5{&yR@y@!E@w-cLdXLsx8lL7M2(2~GCxt~Kf1t+4QfcOVLPl`ZOaW*wXEngVe z8!sLFzENYq2~V?U8*1M!YJR&G+2W=EmYMxm^=mQcW_v7Gp$PB<8CKo|5pxAt4-aAv z(?3vk=uHU$c)AkXM1HC5XW`Su>(fJS26^*wK!3dtnnHvsaB}?a^)}s{L2uUWMf9KepQ zZ#6`@IeCQt761RN{CCCw!PNh6Cjb8;{*Q(K5QW+Q+5i9O=ikHnm-lU2qR7JR|2270 znca3gX>} zC%QgtPAuhAMyYSo9N+0eq6)Xe>7tPmW0RF1WXlufKUQ}Eu%CH@+mfpvx07Q9QF2ag z-5vOzMedwljoI!I+T16RY$p9DT=QKg_E#spCq(wI8_(vdbb;@r0a)_yfb;ZHv<>l* z04j0HAh*G%Hy;r&u~uP5TTfjYa2S0Dpi1OHq%ck?tEw-G1Weh%Wr+uGV17-TD!h72crFGP`~cwO#} zu=oCLEJ4x|2Vw@dN&~#AbgEh%*O(lw)H_6du7~%pB=%ShzkT}j$$q)nX6GlZQG=I4 zW`{D61RcRT;C?Ft+U~g40p62r`6Nw2f*v7Bnb|Gsdy9Hhr^O`pctD*Lj>wJqm&qCL zJx11d&6wSx5U)cj=M-n@l*; zZXAaq;(!C>n6^IcLSkU%dpMYnp|y!HYC-#RcYv5(2iP6s;D(VEVD$q86VP$_K?Hh& zd}4vfkg40(+2eHqt7Yb(zyXK+&(uh%00DTg?}6)FC2s+GVSU9|O}){_eIZ_7FtPNI zfI$tL7GN&I_XGl)6yghMU(uGOftWB@Kx=~04t*?8Nfjc0W*A~M6YN;EUWxpFOnDTT zlB(uMIJ;29NQxjgJQ#!&Oc)t@X^Krn!TN@#k1(pys3<}J)7n51 zWl3Pk=Qm{V9NZ!Slzuz*=%7%A$e`voq*x_Z&l?$p2rI9P<;;PEm5Bd_Y;F+d-(g`& zh;QE2QZ3u1?9!jd3ra%TO%I`#mE$?Be~3_*V{s+mak0ZZ{T4;ZO@LA2L26*fg+uq{ zY8jqnaT4fQ($&?Lcw{28){(NiPCE3oA2fJOxxZ>cgq_92ZJ1NDtwHmg2XsU0pf{Mv z{fI52VsqR|FRD&$g_DFlc5`D`(P|Kl05;JylnO|fG*YsyfXV1C-nmrvyGq>)MQa)C z__6GhJ3xB++;kC|(O7+`;+Q~h@+jg-fS8_p(9;wR? zn0Nh)7|uOMN&=QnkTUajc*}Jj$33L;CXDcoB@%)92B|wwi^2?%tAjg zy~}%#873Db2HYM7%YACoU23x4*I(4QEIqc3WR$hER&^y#ZLD0ZEeU^f*y=IV)UdO3 z^G}YhMVSI6#FwY#TU*ox5>tm7X(T47C&TkS^ljZQk;?5;3Eb>;H+yC()5DnDJD{%)(194S(c>A>2<%_{Wd5Ot0>39O zyQJXL<%i9Je0i}bk2oJ?y`xGmfKVkfiV|GmlJ37ZA5N0#EE7`MQK~Rv^q6a82YYuK zi$i;f9#|TaFE-qQR%5BNCVCdNm}JCKK{pUnoKZB+N)f4G5PZmgc}wubLcy_mzmev! zyH+N}n$oL2a(L72Y3DH&{T;!xGsoba4JT`%m?CknfC*T+cxQKQ&!4qetGUgg%m2e) zZa3&YJ95>bB#L8fU{&0QG8LNf2%S1g%f%-F4V${rb=vwwxL0!Xp@#H2A>jJlqmWIkvWj;q?1Q1gj%f0Odv^%OE937nD+M`FW+K@EpwG7Ij_7QDh zy$vqXdd^3GM}U^ zhYyEk{libXZ@r*IpUV49o5)o&q*vg~NQC>K@KZcD$X`GT)TuH1Qx?r7|56}+ssP`E z(A;*jO+=oUg2^tH6U;ST@_4=8Iq|^1bgTU6f!@Bn>|js*NvP6x^k#)SDriB#qkH4$ z{f7eg#k7xkQpdn>!=C(2hA)hjm7K@gmwl;my72|!8=Y=HehFN4C5QegBa!J7ay;$? zNnjA48GgJu@U?Z1Df;@`{pMDqf=h<#Nf?K^+98WAM-@F$^Tau|LU9&>8`+lsOUS)) z!IXa0T8pt#K9hD0vrSp})@Mv1cvYv)ivcp7>bX4=MvfbQpRdG?6CMQ+EmXqkX=V4p z{lf>SX+iNiCWst8S;{k$vZ&3R9t5PZL4+69G@%$mn&0-_~(c(|ugjE%3g_~?jq>gcw0*46rlaZmXBp5&cCDOLg>kJd@j zB@B!+mKHTX)ZIPW8*6Hd_((WTS>nP9m*NQD;+OdVzc4ataDpNBG-3Yb<_N;5V~`yL zU6l&|I&|ajF=P(rBnwT2xkZ)F2R(GlN$Nseqi9Y0VlNr@-ci@Lf{tYR+2V50wwo z@K!nch?1ZgE09gG?Kx)K$Jj5~v8GW)ym$?}I}l5o7U1`poa71AjZfI%pLF?EvC^e= zHeAbs-jhm7=f>ow8X#>4VO>H6od5^dq$d9{G@P@oC$etFT4!0isSgQPdl~7&(JpYu zf043sOIa7HWmx3}%k&as~7qtKOFNFl(e0{*nLESQ}nShw82eUM`x5;iBpvd zOUvaPh>Fgn3Mq*pm5y=cMR-1WuaA3Qa=A|E7J*U3U$X)9D47>j7eeDshRR|6u;N?Z=sBX9nx}xlC)YPs`P${Z) zqow`vqlUqIld}9{n+TpgL^WF{X@tu?3n-&&?sFe7+^kEMrpbehVtl%A5Zj#H(cVcb z#2H%esWO{V^a<}kQTQp&#``etLUS`RL`=-0ZpATox!9jo3l$#TB@bZ*?(&*M)5WEM zF+8odjgDfYJXB-I2cCuQ>%dgMhf;4&M%B30ifwU5VLv5% zPTT!S_9W}luo>dPV%RgSw)P_pO5zqLh@Y`waa&z8AQ0N(Od2IFS&tN51wr90`E7IUrtg#f3C0DyLTd)?ZNioq+9NksS zo&@_ahh!4RZC5H=yo}|Rk56b=Synb8UhV~_%*&_XWj%29%W^VfV(w;mif-SDqjc3I z7aF%T?mC~wYlU1w)q@``MUDD6=+Jm^_)6t<=P(B2_{=aS=P@STh;orIbebdA3I!N*ZOLi192$>KoXp(ltf zt$HJmz3rB`(8Jm+hOR?z!f1B|?x~+d7YtUh8Vt~&F6IlMbh;-a{2C{Qh(*;-tzlcE z*!5ffqKe$G)B<~1WGJv}>gS1(W?o*p@87cohqP4jp5@Jw6Bcuw1gn(@G8(EL4wuSA z^oVeIQ9Mh;o5Sn5-Aw;}QAIhWUWUz8_PYBXY00Zq^TZC1Kvdl?pRY=|zvUs&_V!Qu4*L{Lf&od^yw%I`wb4v=e9X3k*Bm)ky$PH{U-UVI7uChJZ_ zug5A6QAtx)Z(2jBM)y>1kZ9b1L?q$vasa`#)5>v!ZuZTG3{6>|zIp}v+s$512#=6< zfP)bEBYJy&P9?kqmQb+&wHHfK95WDdl;!5&Se}Cws&=_jGDhm$J89B?JR8iPMoXF> zt@h6o%>gk(L43c(q+-${I)$)>eazntI&%B6_{=ZkxpMJuo^0OQq*?A9JCM+(Hzg;} zWunR*6{H(#y=TWU!9gT?=$J>M`ZRqXeC}zSyuxaT=n8^!9uXGf6NL4&;Ojqgx`l z(e^W2T`!}v4W{NlZ679`O|)AW=Y+V;OEI?lQE~s`KacQ=jk>Ya?Nil=P3w#0Th;Os z&oP+?SQbs%UU4cm7Byz3+;a|p$1-&sgQ9&r_h#q{B&X9VTecP1!E7AY&VEU8d+lJK z*ZrVP%6rV+T^p5eM{OS!b}8S^R`x#1xSGv)qvPUBn5K%8<+PV=;l)Moq|n#ZCeMx> zy@+4LGk!C^!T}=$yrw9T=?XgKJ^jOWN+OL1=g!hhwHyoCP7S?uXbJl42}nnebZiD^ zt+(h0xDd_J&+T_RZ|wG^BKDckK-xH67t(h3bRJLmaOgnCM?Fz_c3*qroFzL# zAFQN}p9Z?Q&9^N7axKba#ZpyP)#}|V@C8v)v-wrhDdB$d$H12`t4|Y-W33@}f~?wy zwm$4Uggf8zDHYmxpodj*KVo7VI(Z3>O}c&XbQ=dG1W5IG;!=9+G6G&hUiprs!;E?a zL_|W@hV(nvcxg(;{?{)e-QDgDWbKwRJu%&2sbI36rR5gDq+E zvoJV?1W%?Nhe6PX5kpf|YL1oNXo^ob#e zeSN;$2z67RIqTS66iv zS*0D-EmZ~xb=0vPuTmb&+^;|@;y+#wWA!K<@>uFj=lsa$O1O@5LTvk_IZDwZP$5U| zY(oLyYHNq1V6mQ1AMe2xhJ|d!R2`q-IXP0e z^opz44wimbC;MzeH`>%aYN!q$s#7SyXQ5RBs0n;Fx4rJ!&H(~06OdOc} z4yMFz+6_+!7fZZjTOY*iFM zN-#VqHlZ4?bqg zup3QhB_t%|C5@Z@Q`7THa8}c@Lcryt~ nDhipmW6?OltH$~N1^yMAYN+qT)U(Lu+y)k!+GZQFid_C9Byeb2?aW4yoK8si&d z)mnvBwW?OlIe#;Wke3yMgT{ge0s?}Q5EoVi0s?LT{GK2|09Pu<;=Ta^!3tRj3CT+c z2@%LU+L>Bdn*afc!&R!esVk{r`(28I5JC|n0%ID`2|Ofqf`g|E;s%raq&I{FLu+nq z2z1HY=~0+23s)&e`^kyupoH4&;$0#lP&yC*lYF^Ge>PjZ>9^^V<8*QPvGLgP_}KNh zcAEv7XF>II^wyrn-&-S|h>$UukAo(AvIXI!=Np!JU?RUd1ww3GT*#+KXDje`la3Sd zftOs{PunT<2$&Ecb#6wpNa&Df$WIV{$6!YhB%n(0YjmclJ=6<9b+rI6qPO_IWlCwW z69G0SM6ELK1Y|+rqYWTS>Hz+DxE?4W?r!z8m>qjzNUl*~Ma(`cM0i9=u<%`NA$Mut z57=N9B(T-7?t$SCY;;IQF+*suWny=F3`>fV5DA!lkHF=!uurqp++yK-MIF-;GYM1$ zsH3W=lcpbTqOc(vkoZ4MWTS=DA+`$?DR%|d$P`3LiKU<2AYp_QXad5ntl|l%zZnn% zWtEP}z~l8TfPf29`f&?uqGmG(E1@zWhwkih)OAG6z41F-wIVy*(N`g2gpw2CS@by7 z6(UVT7TASc(#De%5k))izzxhKML;?s+Wr`mDP)#+s7ewS{Gq#FAWtTrL4&lJToj(? zD9j5+)s0A~;@ziBA>S#5e|;g6P)PBopjNm-90xs?-jj|&gIWr2Mo4^5gisl0q+2+r zIAc?sT!C;Rs?gmj*bNn(96pXmsZc!MU72l1G*+LRMK9iTr?i72rwpp-5ciKDMKCaa zQi(|)>|bCDEjb?qPpk%Ee<0MKaEA6chM{7GWz>vwg#@?2NR+CTKzoJA5%U zK}NWQQYJL2F@Hmbj^LgP2{HL!;%iF~1jm!_$b3SFu35rdX8Ml}?kGIL)@{>(2^F)$ zGC&GH`vwRyznc{EcUrTD(OsLzS)4b7HJmoUuh@tO@F^>VGG^22RD4&ZNA>`5&NB~E zK`MwG&huIdYY;8t`HMJ%e=N2lfoB03TgWv+k_Ic>>OtVz;+bT*AKpaQh*R}igI|>K z*^CqB9?LUiBrbAbuX-c}ueOt`(4}5TAgIgmc}LPk43A_Mk@&}Bhe83tdk1WqzX5rO zg1eAIP$l~;zL~UyG9$|IC;Th>BBvXdAC529u!{e8N#J|9sW&?0NlL+61VOXEW>kgC z2}Hj1njr&k=IK)wu$%MC1GxU0vp?|t)wt(1W3Fkvx1lqDyho<6_-Ts0M_~B%f$jGg zfF8#&CU!_)ic~>x+NqT(BO%_!PeehfuwsttQBOdwxE0w!M5q~b_k$m^s&m? zz;5DC(&;hK1%4#;ORB?Q8&Fw?tzV(w1bebWlW)Un2anq0UgWh-LrvykZ1D4i8o)-! zI52>Yq1N}|(5sDM!}Rpks#60?^)uF3ONaibZCzSa=THr>`O{>;vNXAbbVlNeXEkJN zgivF9M*09uZjW9Sg}uA*q`roYFlcl|;!T+ogRw>WgzU}Hg;zcJxa0C9@kZo}d)bq_ z?E{901r|q7j00syloU=p6G1H~Fh)KSE}5UPB*vU?=aYOy;2Hi%lD(JUJ%cnpNx_(o zngG7%?TFbLzb3N~PqU|T1H3Hd`@7aclo=v}jwXXOJw+x{f~rVm#&rgMR<^*eIPoD= zw)pml)tTa(PfP``G+m{CHgveBVuY!&xjrzUEy9)UwL0~!$uu;gq?)5z_Tc2aSx#nTYtCx zPByJMU>-*vmmC+$NM-hFm}{(P6l?_3ylpINh&5lIJDEMOY&RdWIJBah-6-(UZI=rB zWmJh$MLeUCU$1kRG(k+;+qf0#7_wH~%GFZbn%(BCgS~FPj~-&aw|1|1 zXL_u1j5~X}g~V;8%S;#79O(AbE$Go8>AGY~VMjqq>>S(0WKQdS`bFTy@SXAf{bli< z@B0;8ADkncD&`#a8WRcJIUG6W1oN7q`$~;j!-aYGblt3;!QF(UOM)kUTIV56-mjVaw)rCJ_Z5)}&? z*^Qo7Hb&W&@0Bi{E~znHG7T~jGL_>xT1_^2HUTz`=PX=p+08mt>!$6a4=tTxoqxJU zJ`zALwvGv0md>VlLs9_sE!u2%M3X0;Z%HoVdwIq);&!Y4s;3wSSmczmL~5nev_506q$ zn}@2cvc}o3TX1*ro-FobouVZ z!0I22KcActygz&u=L?6RXrY`?CdeF7ipeh|yc1Cq8iul;6TRB9SOe7^`Mpox) z<|5}}hmFW5Grbwc>18q&eolzXNo=NXJgPjYS#_Ue?JyKMkHnO+Dw)af@QvGRZZsIp zvJ}Jo#vjQuDKtGBexe9RQHFV8EMQ_Yd4LuQISrl^7RYDGx2^4N5qu*V$#ai{$`gK{# zjPFMT#j^uEhZuXYN7eG0h!g%8Oir^6FDsvu?=46axD>iGZM1fa7m}00MgAr_7hRS5 z+so-(HFoNEI*pdSX}0*yxfDl9v?8|d`&ZXfw>`I+%axQin)RAHr}UCD!B!^^ljk9m zJ`;>Cw`bq?N6^vwl;+AS?OGF3pCar17?pr>S2c6BBxP<*9s`$ktdER3mg+@WJ+H2r zPRCQu1J3=n*!$X3Y*uw!_15iO(c!^U6+)F)HcT7Mw&Qct=>saQz^1cR%Z^6x(=hx( zzO@zf*2=1twl+sDAFu0&AudJNtoP~Hp^WTMu2DDN71GuDS}p@OsTehxal8T^D$8q4 z6Pw44A6|5ys1Hj0yhA>#54R7g`2L_HpmKf_PtSwBX+h&*9SG;I11G66VXa}KCpB%$ zE}i!YFR`Hwp%28nam;KlEO;OD$BQ~*ETSp6W?-q940>3rf<6r>%THZ5&M~?% zJ~#Z#wslt4>%24{DKD|tt3Tq_{T|%hU2(h+AM)-EcKW&Eg5z@D6`qEMCP?F!pu3=dYIT+zj)lZob(lczbf=Q5E~r2&pqR1@r`}qv%;Nz&g9Z`Tg+yEhs{v=B z3f4H1wdqa_1afo2wg~@K)kn>yhSsAF-N53(Mt?Q(-G3VLN`irYs@p?%{5KL-davoPhJM%XCBp z|H$HO#Y3bnBTpb?=V(H}Ld#4`Ps9sNKtRClXl%-9GG4h{}FdPX`%MjAj48Yd51X9IT{TPNawR`Q?q2%9(=Ia=5|TiDqWeAR1U zXy@Y0Lqzn|(7%p+~Pp0(6k>>kb_QEj``8>IM|${<_L3Z{coYtuAa~ zV`A$BXoHuTiIw{w`TwUo|JCBZmDKpJk}UMh|6cOn?)*ndZo01y{9A|qIa>d?3K%Y4 zXl}ZH4LvV32&Fd^5D-6*gs_0JJMftf48O9-V|UMuB0sG3v?3#l3#%WYI^dPvmm(ZxgbV11%})Q8fjquBjJ_T(lPxqJ7KAkjNh;0eKZQJs_VTu9J> zZy`0tBFlx4DL!|@!?EhL!a;+>H7Q=eUL&BgSB`c{%Ko+t1#ioY5DWh;DWhDGx;%lt zG7zOIgg+TXFF9BT214yR1wstsF^R`u0uOU19Rz^|=}!Uv4wq9^etsZWTgI;8dEiDzlzR|hM)xh~L&;dJ0r;2{M{y|Rm5s|h5Ho%(b=Z`>5!S>U z4>$MIV6o+vqXiW3ck%uG{TlaRt)(6#2xKV~2d7x$(C!6D;4a4s5N~T75=-i^8T#J_ z9XEtp;Kr>bIKYg)OW7vx`3Ol-LpZe4KU!8dxjRBE1WO}e z)|M!%Q>f}eq8H-Fk~s?c#y{&QsT7Fr&=>IafS}R?PX!v1{=C1oB@^KaI?{;sAwZnj zfq{4SB=0C>pujibZ?*q?0}QJs=--*YM$f^aRD?Y^q!sB0COE)akW&G{gXnQ_ObgO+d0%2fJAQI|-7Nj;Tzxo` z=5 zMHv2lyiPhKWHr4r*@v5hL!oi7ar6M>vsHvkt%H|YUZw9iU1O8R$Jg_WFneuU{?4G? zGx0A!ks31aFAHXv;N%){sZ%(t=59i7EgNhnMx0Wd@j9?65-BK3JvXrW;F)|>S~ru~ zXmWCIZ@~$tbZy%~0~2GgO}mBiT)rZs2t;Fl0jhDR8Jna_#?GU)*qg1A8NRY0>E1#O z0&x%_WD8FcnSqycGYd5gJmF(u6AfS4!3B{?1q@{JuE%mKyjd1o*EmEx7O#D?QnQJ( zwE38(5?-V;dqWFzV&ml$%DO`F(tzIa{P@d6SI_y(S_eD%WfDv|z9%Lz|<4QS0z~z9* zgyIvLBW1dbd}~cp0?eCJdPXobWaqy#ihaM-$Xf~f+bWL^d<(>24DF>q_T%1i)-#9< z3O=+4BkqifefhiyyPrH26ijy8FYvjIm-1{YHI%4rsM$Zy7i?Zz{S0!-tvg}&S42O1WLul& zs4SC<^BIn6k7x})liR3XQxmfX>a!Ag4RmsIJuE+;ck(A*iARyJS=~#TS*9#K-tDeZ ztr3lb6%(d!V=tTzy|c8QR^c{LMiz`!V>XnA_~EqziOIzx)QPjML_L!@G$7!hqaD$) zYJ&2Z8ATumZ4><*ji1=V=~HCBM{qZy4-OH;klQOI6g1;0b{@y)HhweIgwA@hhQ!2* z1kGOX5%EZWF0SJ@xmdHZcUDtGMQxOwlbplM_13yyCO;Nf9v`9gane|p2Z7viF%Xw_34iB$Am^C!6Vk`-uZnQF#FPyw9!a<7HO-M=qGZnGsD@yHe8{z|8>NVt96~@U9bQ}*h*$%Tho>L(k zt^9l_Kg)}U?}l2BE+d!Gf4p|5d(K9yZ1mq1QXj4Ub*v2LIJsEzbT|pVqaT?lmcU4&STNB< zqv#4Nc$k!9Q0Tz7n7t{i&YBE0=ID?a6rw%A8CO4MCBo0INB47r>6YIIk_fZs+cwpI zW;)$l>eg;)xu1n=OQmD;)&wPxLYn-(d|*S%NDDJuw1#a<`~dP#ReRWo$j8ABlqrKt zB{G7tZ2}Uo>-%XS%CFFg&=1X~eTFmb*ton6*kz2CuM zi&Mm501nLA!2ivhQUa88pTE=tmIb5WCaVSlxc-M>oMM0CFQM5PcTy1RQj~x~odiGo z498ch$IH8>*2lOsyPXiLAyH>~(_%6bsd#xxsvjLhHe;ZFvu#WNjFfLDOvHvYfJ)zP zMplL@35O+GZV?N?PZ0{c`iJ{~;Hz1{qQ$v=J6?;K)_l&-H>3>Y$Gc&rz zX!tVUVy)F|G`ODs9ELH7qF=|EnRK(>XGF%4xPsJ(<>|)1aSL zZ}Wui9I~pIYUo zzDW|3UPZU&%6T~()=BnX_}xiFv|FC%P)8h8FCY>S$KWalgK|0}*5Oz+xogZL0r12( zQ~D=riJwHA(xqu=Xq3Cs!M_>#oz1LnkY=5hUYyD|ammq1@Fy72z~VfpWN$tbZ9fW{ zi%qpV(ynKlScG^oV>EvIHXWg`TB{_LG_W(Cl;2q;fWKHWf0uTVui^=|AhtUN>cz8~ zAnu1JaZRiB4rx}6L`~qfLIFMvQ(&FP4)#Cs2cXd~vedqT!;LFjK}KlHF3BV@&gNozC*Jpy0hw+Y|s3VfzR(re-wC^?=ieKD_hL)FXlEN(EV z6HyntkNJu?L$L=K3!=5Z!yHa;#d~pwBx}HWl@FBd&W(nwpL35|54n%m4!1YRzF5ry zNzyqjdq&k<;OuY|*A~lcn(rEnj1B^Q#}?--9MFM z6OoGlfv;zfvtYjoA<24ncGc8e4aQ)DgM%+t>rIZ1zP?^|SL=3WBqk$`8O)R?yBwEZC5h4_XHOa5J%zCqu=mp-WXn-Kgr zJXpT(!N$yeH6*(z-zZ_#7;T5YqeC!%w1%`0-Ci97N4zeb?HK!ujm_w%ErdCviLKVPZeWMymq0wp^ zMS-Q%?@1;VM_0~+tYVfj_XH0le?_vtXH$FC(qxff`-58SHsc zs1Gsw5*lQWEmwp`Oz^cu2#Y@L)MI>eV@CV?BSubtmHsrYXcP_0I5w7)GvD98#|+{7 zgVc&qrNPLRO{31ctQfht5*DOjeX!1fwyyZ+@luuCxUSdJoSvs=)N%=57tKYi?oL2K z9xevqYampros$krk9RZ@{F4wAxQ<%YLcQnnNNonL&H%hIvtww(Fp_%ZN*ZbA3?-t zym=KE@^iMtOGqMNn57BEDscqA#CzEe=c+dE`#1sU#I?ZCoN{hSF-0|eK9jnwV5XG( zZ9Ws7!$4>$;LprKm0&3mR>r8ktP0ZBs%3E6eY_cz(iBl-B-OCy%#L@ge>P;^HmQ#z zzA}XjBv*`l?TLlL{WtNettB9?Y_@db9;BKWAOM46FeE)%rSriqF6#Le!N}2zRKog2 zfCOR?GaM-dz}dGc0E}8$VCKNz2pn?UQJLBiT`bQOG7W(#Q)uEh|2TKq=L$Fb%8`+m zXxej6qd^je6IfZG|2ybd&bDT926h8ALhM3YEI{Sihr1v4PTFl)+orF%&(5NM?8#KEPSeNeF5sMLlGc%^elhv9?M?guR};%kaJJ~hWP)hTT|@qL2KP7 zL|2P(dfsiH2y$|hfX{i?uBrLzQ)G%Hot^rV1o}h^TU^@5jgevif%=cKDs1gI;;np0 zz-*6l&9Z5Z8DQgwBqG+1iV;l|^njx5Db25G(9WMOPbN~0p2Z3H<`0-(_brJ$rw*&` zv+N*^U-PXo#y)#_@hKy)pg5F+@A}G?gk%5Wa-VSeXkHSDYS1EDLP;1H96e;Bc5P6$}mu7!L>Au=oV>Sq0;duVe2 zNLd4#EszOs0TS~kor|Pm=;u!bT4$-m_-(gKHvG~k3qOi)pJf!*rUy6x$1UMou#+$&4+tUqW}j zI%Kq7?@;(K16kup00f$vjrYJ@WiYxn!kd$oRT9~|V&W9=xt5c3Y;G}b2>rP$Izm}$z*vAv7pm=m?fjE)gw2nzD{ zyxJ9YS9IGp98_@o5Yx{+TSdzqJ`*ug_m&l18I@Czg7QVu0}ix*PSO6QrKL(K?(6H@ zu|Y|5`-WkS;kFEgxOG`AI|bEv8F*JdpV5PUF4om`of$a#8NC`-KA$RK(s(ICGlvMs zR&b5tGz2igy;Pnp1gKgKA6+R-&TuF zOLfwlX&%g^7KdF<9wN!jwykWZ$Q%#pgem1eOEb)DZ#LbSAwryRg8U5^*as<^cQ_$~ zn-Q)d?6wzN+y81D*wSU+W(=j>tFd-}w^nsEo$QN+PBw7}d?>nJT=dHqnpwxdmG$au zmY%fteGY#8ZTrJ4-70nbJN@bDA8;LMsH@##CxsAN0LAWxptH7+J&p*zb7iy+Y*JK zBrh^49cBcUDbsddWb;`ji^uV$0TiG!P6byY3;6A#q0Z}fv;`T7@t4zIOA8-eA0J(| zI-y;yl*iVzaS038J4Gt|fQ3Zqzh9OQth=*2H5houW7QVM?J905u@KoU>ir+8NuP_=C7VI~+ypB~#7Nfhk9=u`>ZJy^ODt&Crw9#0kJ+WF%~P{eZ&3gZtAEOdYADdhEk}r>Zz}h zfbGP5xyL}D-F7XpBaMmeQxCjk3PDnG#_jimUrQ|5K^N4?!5tlSkFuy}ONbSQK_=k5 z+$XJJB7;{BiJRK-4o5+0T~YQxw400%*$SN)aIL8-Tg+m4WsoJ~v1Kb;HxqIE?(QwJ z#V`o?ct{*?r3P|$eiTa;hj41LHmh&PZJ1KWzqJ}Q{&!RuSA-d6wyT@1e(6@%v7*)e zU_wLa_YaK5*s|zVi|>$WGV!6)XQA_w=Lv5?tV9FHHcprL44Yt5Ew0wW5Js++f_11+ zMRf^%R5OFz{%gl6k`*0Tw8wuy#i#@Y;c`c(8+@Gn*2+Gf0!Y?-`R-;tZ{&v6gECW> z`TcV_R)9))Q6bzl{1C?Y+Hyn(s~AS(Om%~>7cv*B)*GJSZVz79S9vt|$WH5&;UBs8 z5w7$#x^)$H!xIkYa7~iYXR0JvqD^BitZCcGAkSLKOdJD=_4&RwiMZrZ$rbidrr+nN zxuXGxoqpP{nv+dCKHq~BI5CYgZBfmIxvSd$r08pYD$RUj`pus_l5*Z|u6srhD{2Hi zW&&R~JkG5CwJp`?$YEDailaSt-0DReF9JeQ3pLof-R&=uZefeUbkE76k~L;y@7^l~ zD2b&eC=Q%{H}?z*SoFP9Y`l@d2Nwm*wC<6egdI7Smk)a+=ABSro59zp6-t zWE62hmt;4YMqagOP_Ht?{)$|Z&YS7TAGmyPaL zNh4_i3F*^7#Rjp7b3q%MpXI~K++DQOA>)h^0$GKpn=^*o=Fsl4NeM~CjU{`t(cRP! zJzRFr56P+x{V9ySw>y(8wM@c0-q(YH=`&>Y0WdQMFl)k|N4zG)GK_uIRO5^xGCJru zujsX)50;M@itAt2Pd{B+@=)w#hTt!dsm8{uvcmWjP@Z-Us5Zv)hp1P}{mE^cZSA*L z@O~p;WQ_1=ecrwdpZ9YikP}*atx>HWgCvPtNL9R8Fsir`(-C^0y``(evhZyGNu$TZ z#uR4PpaFXtsnx7$4Nh=&5}e(ZJ=Kzyi3$jtJc~%-m5{-DyveUz9+FEkwKZGG@EvYw zfbn%beq5X=^lfsyeiQkrYAwCuN84?E@N=Ror1?7<=5oVqO;AXB?j)PfYE>ya_sKYh zr{-fKw3lbALI&weoCg=n?ZJ-1O-c=N)&b;Fj6XKFib1n|Ct4u^VxVq-d|_lHbRTdZ zcXDbf=5axuuZ9mrR2+U7 zd#TB-U(qbK?qZ?hSU06&4GoM!gXn<8{|mgPWrLC*O1y5=L4o({{G+TpT8fL3ZmxvY?0q*FOn<1ylS~+)gPo5832HE&-AY{o6qB- z%=YZlR%6FC0furHze zPv|M=cLrW@YPWV}Ghb!&$N$6U^{$O;YTt{G>ApPVvb&x4xe%mvVkI@jR`XunN)J5O zj05C>c<4;z#V>M% z%1>fD2838fdH3IukXKAYlpqI^J7cM#77B`Q4I{rX~hZOz=LR`FGk7bR$n-14&JynaMdoRKx(JzdSa5)WRGPKJrErlQim0cJgAQbN3u1 zn+g9Darw^q5LGoZInW|GdRNRlKY{4?WdCObzboqv5_5>Ka@EzVZXI2vqXu5=Tg4Z@W+2I@sdfj~PsF(SI=3 z`9d_6uyr>Mutv=`qX7b|9hLTrDkhP(7i^vl1UU<&|HQBE@g{S*qwMj%XLFZkbbAuolEEBFI}QzZ)W&A@W-_%}cGE|x*tMsotU{sCVP zM%j!GrjrV@Unlm_R5sf7|0_0Bbh6eU*OY|v5&8-1)TmM|8N@!yfh0eMb?BVnrQ-EZ z7<+1O)vmKgeQTLA;W2JB_Cl4Z2g^#uRc~e>7BW)DF7P_@bb87+O?Z3}1z+5`q4nF0 zz1zFbN2zE^G;)D9lR2_i2mfgnj?IJ_?Dyv|l6%{St(9m^up37yB249RySQ`sN@3~GAGB5@%@$l+0 z()2I?=(4deo)Z-jlW+@v7dR~=d^~bnW!q^u9$oLw8BL9R zrV`5&{Sp9(pR`5N|AlXJ!T|WTy7TJ)5caVsyB=xQ$zK$Y9xl~ji|>mVIZZEhk`Yj$ zOx1myz;Bc+9Ks(1OQv(prr?Ta4!fG!-FW(dKK)(z+k5^qt0Mp)kskS;AHC3ajeElR zTH?q~iix?jKgQb9RkIhXm0fdxp)J6Jsr=EF=k1Z=PGdqZywsTG*<-NKqWn|o&H~0( z^1llI7+Qf+F5jjKCD94vPUvAyujGca*kChU>Tjo{RZ)IN14Mslv#*-Xd`fJjl$p2X z#@F$LtWUdSpLIm0qg99;SfjmaWU8;-ArQ)Se*A>#tG@p1TJ-Cy=H`{;zg|26d}WO8 z?VWy9?nM;n5lE5%B*laq1lBpqHq)t7<(S+jh*3>AKSM`&2 zY)`1Z=prmV^8q5d9Y}G=t6#)~ z3f+4B{rxVSI_^(b;eWT~;Jorbg?deS>{=WxR}bVC$JHg-*2{h->5OvzKsO)GwpC%^ zn00^dvrD=?XmLoYLPFwYixm|eo+?23EjGTC5?$AJ%Ml_7b88ksqVO?-R1@jHj9N=M z4x@t%{DpsKlUDUSq-5J+zueC^jl%HdW1piitU~)5#AZ@q)Ym(4ln|Ta2WbZg}Ty;FgS?QfFt9Bq5 z(MysCSv$pm#DovIq%I*#pZ<^GpYlG7nG-yPx4z8~%E*uNeC3v`0i+U2z{aPFR{KI; z{qL|C%L3`It^51M2^G&+3z=ucSp=rh#o;Vob~wTtD*gKBQ6jS0{0ff-vQGA*>w7Np z>0)O+&pI_X`cVQYgeL3u=Ak-na4Y;5!{tS0P6j4|)Jlv?!}FWY)q1~RuO)c4PbnpE zAJcL`w#;?D#qZ={+1(k4&TXp1JXy9LW4x~y$*nOcF9cuT`CC#wQ!RmhR6eawKQGWq zDXc%~7ZoQ8UG3`kSP_86;M?vV`yIQn9HI#=ciEVf9iX~=i8|+CWq47~YuFnx%yd4> zH93G7!~6Qh(?G55$Ky=g3zVSXh_6n7ZnTiwgO|zs_ZRS8K%8gomrN#nTyz>q<&tvk zO_WBb80A#V$;MmhvX#;-(?s^DxExPf&B^+-4z~|}Trsw*gyOvllktM=ar&i*L07?J zp&s-1m=~(zonoHrN%regyTxjc93Aj-tR3lCe>AX)+&L|UJ&~$l|9R5%gWBP!zFoZ8 z?Qic`pQ1@m@~j_A5wSHPOT9XVA+5{Fw}L;_x1utu-Pxk>>aD9mLR!|_dPA(d(s;Ga zDIcxhCr`O%9w;xg=8g}AC?kHU*M`69)HjSu=DC;@%nZCin;qCFEdfLo?Mq)%Y z%^#+uLbjMjk&lmThN@}tNbRB{zkl%sf~{|IDocnKKm({?f>Tt8k;)1R0_^;0#Wy#O z`np}-t#Y}%K$(C;D!}aj4KT;dRO6lSVcOM*{kI@Yjr^SXn-3_+g*m$UYF!zrkm<{( z?`X@#`K6AcqKDfO=yvu+3XWQ`8ys0QgFQ0~OQTA;>czzc9v+@OiyVCq0dOv2B$YZ4 zd?&z8!@Vr*Z&z+3IA;&$Ew~c8ty5nyjMe?KM)tru9d>*Ve_fb}_-t73`&#@BTJRuw zjZND9&#0|@yRoH2=q(4xya1q<@*vHi#c4mzCaN_B4(}5>p3VX5Is8p@ zH?e9p5&MQ+B4a)J=`Ds`pcbqzq+`i*fck+IW&0+2SrQy(B_tHGXY5!Ya%Xp&5dp7( z$tmL0iUNlWz%|xC*L7z_F+Rt+n+j%e6GTMMqvtB}8Qx4CWCRXgT%v*WI2IfrT1@Z% zJ7oTE5fkU}CZ}rbFWVt&QjV)N*EDXjEYJ32t!XPutpDwoEIBcYv4--c-bQ?x#UeO^ z+WP=1FFOLD2&bH$z1){^k^IZ$M}7i(0Sy4qTik%n_K4f3muDLw#b!YQ)@F7IQw!CX zWDBSfpkCgAKL!Ipw>=SnpgZSJY*l?B@`)e-(Ei^x_O=@YxCj40;xyS;dfi4Qa}?L< z@AE&Iw9GHDA1gEJUd(@0<_b);hsi|y-9~s`tvAEtVXkHQK(-v+)l@scX`QG(T<`4( ztH1(q6wKQp`nmS?i#*1gAwz$9VVvvVE&`dx1V{n96^qOt!On|SiQ6^kl&0jn;xrsB4C52yg)Us1u%pWf=a?XDh|HJ%LZ}Csa+niKC);MYk zn|ANA6Ig831^%%eeUn4K zJz3=4eOaDB`O3;-B4=i-pHosdHXe%m0{=(k`|`g>zN2OH(wZU;gNrL1UbU;!*u@BW z-ev?8X<7D!Uvqe7!m!x+UKDIuKH~ z{}OgBVIDTsy3Ub1MGd7tew-cz=(NLUm5hx_XO|~0nO46Z04z_8L->Ecux}2wdoBpPb&DChBBONUFAgD#Q%ZM3_Et{N_!2Wg*u&MPkb;!4E>o3)e z+AMEDdvs(7$FS>@;z&sUl8385mXON84NVjJYJhRa(oIMc6pJJV{&1tV zn;K1J7Zv6IOjVociXiftm1#sD&MHz9+8Gm**t-vZ=4?yMZIw!4wjt zS{AgA8ya3+7K>~HM2dlSwy3G`*4K{yreb0g2N<%J3=pJi-ZO4*Z*ykTa(k-_@6~Mo zyMwChc{XlST@oaw$aAF1BpY?K0f1{sB|_~wvhK0RCp-*rimYVcvR|f|u6GRf7n#C1 zmJJF3Hg{k2q2NEsx&k?bGAmhSpkQOa#c`kHC(9fBoMMVYBuOeaoEa=EQupxEelLAw zNHV;cHI1KhJ9d?l_ca<3aVD7o992>AhwwL(NlO1pQk#8fzJx#N8!e`9axbscN1mj>aW(wuP^!&MKJX4x9gUfX1ok}ua7mmJ z>Tyd*L_}E=D4VLXYq4yYs~L5b$$#k05M;D`H$y7B)ex-SPt#_XlXmh<8#lgj?>d~4c3#%<=d+dGlZ(d=)I{Hdpns-(B%6%mlq|J?Oz5L?UGhK)8hQ`6O zf{9OM|K>FV!NwhRjKDx%D>p+fsdJ#Cp`oFoqN1aNC;*P;t_9t5^OhZ9sK2$K8KzK> z@?c=CiXe3WYqx!;>$#?|aB$H&bE&vP5sHALu!3$qwHKGjKmvnLF@Phzrrn3}dItFF zh4fNn!`5kv25hiO@Ofw)p$?+=HRt1QGzm-yaFiKqcU|rz{IVG*E$d~-(cxCS)4siA zd!bkuoTqnRRkn8~8KRlGM_-6b#x!FB*x?NbJOCkt_V(6(KQ`wgFiay7Df5;YVJ%P` zL4Yrt1rdb6a;_x4yW3A+p8(OdX=P>P`eeSmST1*BczF8s{+^w2B|`Z;V&2xc;k^0d z%)nvtbpj@ynK3XYPE-@@n>_xuQh?k2e55EJR_C4?){g+QF`4jrS!vbMx?N%+(u=Yu zJ6cKS7+t1BzGq>^8fR-d?91DP!rP7+Hon&J1hTh-s0()K!u?Suirh11Qzlo(}J4N4U-W zUhI8K!SrgP!i71OWZ?Y9KYPTpuW)EX!5?d34s_w*!(sI!dgmnQU~xIv7+n6|3s;E{ znQM}74z_XaAyGpQKJ@sYy8FFyMNacPE-)}KR`Yde6`uP0;R~^*5!A>5GLQpk z6$uXFRm|nOTAc@2F@rlhJ8NrervT$tn=@2X-}y>o{j%YGGBKE1cr=jb>%;kF>mi<*2)jewd ztgP{XI#k94hSWp+Ly#oEA5`KliH;;&~ZBID$$idIJ}bZ-322Jx4?yeq9YY&l-M`w{G?dWP*JnvM_m)I9&ks&Lng zuA1YKgHq6CO2|$E4Y#u>H4)yH(Ye$4mt2pwb4>{|u{El=ha-Evc6bEdnakzH1Cl(g ztuNjaAaDPm_Jb9g3Bv8(n9`8S29erVWI^uJLK9Y+ zr;~gI6)JvbG^$BgmP_I0&~?!Dg>)9m=&D|-tJ{ zsE@4ag3A`G-8|>gDs7XF>fzT?;>pr`|x#1-75lu%9rcnx%LZ7@aEFz^a7)H%n6*y9G=w{hh2DF zPW9Rf#)a)YWYjJAXfzgGV+4?yt!A;5ksefm+kR9`2ML-m5yf^n@zol{lW<}4H^Dy6 z^!g&hXRD8C84|`mjNDH8mV%C{q}eC1COD^GVs_FwY7vYfTpa|#z9F{BJB1;%BJXh@ zYNbT9+N5CO_QZez%O7dNQsvL1H&>)@E6GZf~(XFHN1Hq#E*EmQA%x zy0t5f{4~WgpF4&hCMs|hwNyp=Oa>_zns{zsDtm=1_*spEEy~B$lKkJP8c;FYQC{0T zkIu^@K90d7a8Xe9&H+0Jya)j1hxIM%NB{a~bR)=u!OlEL2*5V6wNr1uefen~zIYZ7 zg#+f738(ifp0V4GlY{KmpX&z^5ya60{3RKjK8DCBEP*rPQ>;jz};^1BE2z(76u0+(2F0OQ2t!H?um4-JNZIe~o3@HrAU*F1Q4P zU?XUgVIZ;*uCiQc*EL)cqKO1JgbbrP;Y#!_pyXiDtXm&qfdqX)P;8B^Ob~Ll{`;Zu zBlLz?%VIuI@wFw4x4YdLCzqsj4Hw zlC0#QBZfe86ew><5f&RT zEhtGvSI_ySpQ9_~Utlk3V=o6FzbUc}=>iAHCjRZfhiVhDr|XKp*VQUddz5x})R2;v z{@R0u^m>GXPwjcrVo;!Lhe3Ova}I&0j=&TsY{uA_OU$>^@)$Rr<5r5cOc#*mo*UM- zGeVU{a>V>0=cWWLxK}U_cKia*l)Q%+A-0K$*PoFD5<4w zwQgSUj{vjhAQhRi;DPNA`@Q}4tnD;HI@P85c${3=ow&gA8*O8GkXMloQz)1 zrJ0)2dm*0BE4^nLvAzvN2kmClp@G%T1VMzoWuZHT;sFo0C1P@m*N}bO{tJ1fl&yMS zq`0qEWi8a=mJW6v9F0ZtLK19gy(K?jYTb|A?AIZt$+w7HqxzIkC(;US<6j$@UZmQh zshR3f`S#*l0g;p>NZ=YUq*wL_)jI@w8FA+Y7qzzfcZrO4G~e908q+($x2BURDPuG$ zaZ8TCGvv2`SC-9&q)WVh%D*C-S9e-U;f>=(=QUmWi=|XX(9NTVZ~Jf&eb~!x84aNI zNZ19zG?aw$7%CD9S*Q7#Vxv5rW>T$Dw?~2{d^3vK3b3=1R%I3(lhbatx5Pk`vm!*s z(7T+kzZDu;7kWK+0?h2^&#*Ak6BSOl2JBI}Z-QH|Tzd$+m0oc_w-hJjZ+vSR;Y=1E z6LyLw%0hGz2{{qUqnt;Ih=gwTQocX;BuNmJPm04Y_6T1J+$|xEVv7;bN-akkLY?oc zXlar_5$M@k`*F5!KJWu7+|Q*SZJLSV)MDA(_e~L3hL+>i@+K6D0!30YELen;!X@Aq zkZ4HCH!gcC_-{FaX0^BVy>`&B8|6}b{W=D!6TEWua_|EsLat}|t}^Z|Gmcot*%rKA+C+f4Kb@?z!~xEc--$|W zL&@xlYgjXhYdY&|-BGWq9VviqoyT)$9aewgBLH_kg1CUdAf>r z17vd&YbECYj*I-J#f9#=#(b~|~-eTHkc>J9o=pSfY_a2cx zcu`I-%+ z>LXKEiF6yWSN%*VP($?K-%*OzpG#%^h{XJiR&zN*gd<`86o=9hfi_A#074Bx`*P^< zJ~tQjsugTc|IM2~y@D_eqR0y<$E^VSlUtpJ4gpX=9tlG#N+}vTv@==w0@mvfz0wdo&<)Fnin2) z31do$Mg4hAL~)hQ$?_qlq@1NK^3jlfZYn0@oAe}fEZg1S?$0vPhZXe&*A z#4F{ikfs}uMTtMM{w_Jj$T`@@Oy1RCM8{w@aFhaxQ1a7Tf6Clp7JWLC3-ya(!tpekNz8s5zlt% zu0E81ezaSxN~(=mInv}Li%OUm(j$sxN|$L~S|?x)O$8N>(h;euY(E8@lGSGLA08TZ-=I9=|Kue}jFnuXK52LE>!z@^k( z1TM(0j<_{=5A(^HJstzqVM5~=nSEh-XVG!n-S8q_pWvPHcul2%2!m1Rij8kJ`{&WP z?_Y-3>Uj=F+3E;ihnqdq|5Vg+VG|bBMq*=;I%#>1`}5tufRn5?PVWi_H5NMDdK=O%f0<*A_?Kgm;Bt^z_E)M`nKrV(P{KJ6w z*CH(#0c&54on%i6dO*CB2Fah6ysnF=E$$lN2@>K?wMpO# z=})5UL8ddh4-ju%;gv~6K_1ZCToW`a$^`9`C>fT4jo&Qw;BT~V{$C4eG0~v~QlNUD z(0WEDV6p>(wy3kXURBw)k@%w*r{XgKod;q(ud=j}$J zvxja4YSG>!FS$ot1x?;pYGo}ruZ6zEC4z*B;{ciiyJ^2ASPP^CT8el;5yM5xnh-3b z3DN}2EX)h<^8{`+G~g3Jb>Rz;&_m`!HDq&tq!PA>|X%hNX0_Y>z=EB009C zk_lSDVQDjUeN*Xdi%Q->GHP8qwc^K4vI*f|cG_XE3I#{|aXJiBTm~?0I{D2uvN$w| zX2eALz1}PoR(U#n1e&;xb=S(2k$bArdIbt3MUCzzCf zI&Z^u%<4cC4Ql6NH3ECoWDq>0JB)+qfB<}=cy<+b87FEFbqQCREpK67@h#G7wFz1l z$AsV4$&rWRaCQux=ufWmWg)EvtI0Zvf$DQ#na?uXI3b=fmonmV!?EHt-xcp+*eeIqB)I9-mVd+?W`#n|Y+% zyd(7KWSJNzF*@{_4DC!`>medURNQM8V`YI_9>zCR@WGq zp;R*4&#WZTO7tT3kd%=T!kCA~S!ykgbe(H@9a=J`u2dV2iM60MA&f@mToi;wQQjrs z<)UL_W2+hsUu)*!^GvZYug-AmLXz;idJPDEcsg;Ws0spLx)t{YwZ^|AobQR7oS_Sm zi%`4@OM`k3OA|Ed5&6|kii+MEnyu%4D0iP_)_6ZKG`R{+3ctZXRTIiqVm19GbBRca zq!qt_#e*#H3N0g%e=!gBwDahX+8OJzZ+nDtC9*uMHs7JkX&g<@39D9x2~m%pe371? zr$Up=5wRu#35sRW8?^Pm#cY=0T!lINlqT^D&=$J%hvy!tj_ycKL`Y+684P+Pz;N- zlW;w-8}wU}F;Tz)3htX0j}b*sVGXh1&wM~E`1s0Fw%*EX;=(*|8aBEiQ9!Cd-5#*f4BeQq2VLE6KmdE;;=XAg>YC_P_Ut@JPdzpja#Q%+aQ_%ZirCF{p0tu#KTN_Qm0T$f_qi+%|WB758p_2$-g+JV-Fle4XF;Acx1di@l1>!|# zSNb4ZhiP|y38nrcOn{>nDpj{w7EaR7^Q!Abqh)@H(}ycmOExu@)InAvejdDD5#cL} z@RdMaR@531!ZZ9VHJiPhh&Vlpq;K=+=e1zY_nPzC(^=)MA)|>YFIprQ1((Z+fyZe9YQ|o7vnmm# z^JOVOv9+D@!?-YADbFpuVPnH}sjt!-7lz8wx>@)J?#t5}w3fTvK3aDp$;hD>#b>0( z5u!Z3kj6{@m#F|&4Jn2S_%mXPLg+jJUKo}~jrqtq$ts77 z+Dj`=XI=J4jzc-~bcdV+*9h@DZR&hS1j+xjK zK8jvw8{NPYx98Lswh+OxK$Ltf!CRPOzSJll-x|90E`ynGq0?YEjW%lK6v}ot6(Qy&1|Ugm-(7$!Al&=|8n?*9(?8_%hMof95=jQYXj`t9 zuvJ0&Np@t_Q@vX89?zGq80q;D!1a5cXYz7aDtyF-uiNA4y>KW=o*U0@LNjw7!ga8< z7w4dCJ;TJieTcf;_&;R`ek+o_hK=I$bUllD)O4~l(z770)= zRoq0~XIE94;$@1iX@l5_8U&W03D5Vzw9TjjX{Dx8QpA}8UpI6HCFcoQkW&_K z%gw#@ujJI-kYs3KZPj3Qt^*c02&*L>bOpalW`7n>zBY!tk+dDq5~)bvPU_ws2A;on zbHc>@n`QeMr7bBN2uh8BHT=@y;oZ@fQJL(f?dxlU1-oS_@wxi+DKj7akgIz%Zp|Ct zIMqOSk&FhgXqD6nHbB&C?Ez=cu0nmEe`3H-q6)J~tG^<% zx|a^CN~QK)C!P+h8Ie5Kw?Dc|D4j+%J3Wzl{g0k2!gv&OArm8|Foiu2Yb!CAXP8(M zazjI2SV{Ch%&-2MH3CDMTgu;ydu+tNxbLDgK0wLnjw^N`9m#JLY;b{ZAU0$^3^2So zB_$=HHLHQH1>BZg^5w>&+MWUnuwFIu6SrH}h39 zGzXG!DhKdHuzzEZ$qbtItz~O$m3SN?+l>8o0E`MRY*f@)FB00|5xmavT~e82kpxhO0p6GE9p;12_-3>|(vl6bp9-Ry>MLW1qSGh+ zt6gmGxZjqm1>CvQg``VFYOOEt%cNg-&{D70L)0nByW}6=`MeDsgUbS^e35t``vakg zE)UD-y@d4#rGbNrHdvEKXwCEnldXTjbcqo#ur%Xx*u2gy>2{0S-)NB;uJlZ{!uDvM zLloq$dMu6{FjUa6h;mZc+0J_p7q)~Drt+{zc>d`9%fhRW~J@V9SwrLU4NQ1ai zgCsfoyH|OYlEq!3zP$vYCHphQX{3|L{iUN36*rO{VU|Z3{tG{O*#0*4T$~+u&WH0=kFdv-ah^k-o&yNz;n% zf?&|0LyL>In}im4*zOAzarNHrNp91+pK~^eMB68H6eRi zJS<3`xBn$<+%EOAzbREE!d{{@*n`_(klAA4UF6;kb1)Nj8wme1&?w(J8HzpBs^WIG z{}(U>ZetH$7mxZmXrzol7cK@jNjOzW?$^chXB_{ks*Nn$1HV1&ls9mZ)@YRo`Gy1F zp0V}K^S&t04`?E+gwL5%zLS%w&d(w{nCBz8rSAL`gXyUSaRV>03ABqfR35(mLO&7{ z^!U{w9aU4eE5b=KhmJhP)dC^UKEMm(T?}S2PM8rh#I}KU$X_8RvtQ|TSyBLcx;Vj7 zcS;0jtDj%fADg)ZwfYAgbNB&*_!amDGZ4$~_x7Ql-D6%8DrDgj0Profnsi+#o+myL z!M|i9h163_OQj~2pmY0-g;fv;EvoESeZcrE21!J;EREMUZT=~aMEn~+u-6K8%~SH~ zgf9;`=&lsL9D3e(15!BLcgIZ{HFivP{V%NDOFuQmd0!}Z z21;ehWs)f)hhm*W{E4Nuw1nxR%Dc(=aR0KJ@R_ktGG~{l58QFn(V% zzxu|l7Wnk&&Xu)(K3J7;{{!fYOZz3+QiVO<`v4T(4k%S*bjf)*FM4T!Ay{Wp3qyFN zYyo&z=z;TnvH6)=E>FHFjJWx0dE%2&m#?IV*uO1Pf2hs!;BF$Ih`Vm;TM+zNJJ8eWIw=8gG0mzX;-W%=N82Ge!hy~bKQ!y z27AzNt_U15(a6%^5fQ(M=uW_TSAlhWb0}U6a8*9TZ~bVYDQg&XOgNv6zM@rPx|@<9 zsO(yJ)whtIFPV@;A?k_itLp z?auM`Cs0zcenm<3mxvGmzAih6vkn9sQ6>uqQBswT?n3{hf3H=;>`a1qAgfe!b8-LP zFI11-c-%;{=%YkVx3N3k3h5)>?) zfb~d6txf$F`t|$VKTHkY-dd!D!8_P=M(!|_GXLi}@dZtAENlv+>o`T09gtad;KW7} zyCtg0QjZMIF?0m$gw;TW#cXMF!sDaPQ`zi1nA#R74#0%CkJ*+d{u=QEZ zWA^@gG(C;{Zj|ua>bwg%qE9D~2;2{ed>kFBt&~_-5lpz1>z2+x-Su5y+;GiBkL6lS z=24XI7BVSW2f5$+D;~qXj-eq=CLuE3M~A&8JLeuG{ZUEpZI#eIXZ+P}0Eu1v1TcMXXou$WQJRVWilipDKH&yy z{uyUsc*exH7Pu7~%nyOO)4_BfJFpuGD7qp0bO0P&i_yeGL80Mf1*f&0!zDp9Qj$dH z5(Fln*cZmSl|{W(Oo)VW>~=m^vQ~0C&XkHJQX#5V6)8B^z2*&xykX2ZnlhcT*`O@! z$p&kk=giq3IdF+$DhnIkj@g#7Rm~ZvlSl1kRfDoV;VTO2bkjUupSsWe#@KGusom@d ziRnC$?f8z)2Z+BW3s})T@z+Is^u=BS-=-^Q9`Cl&G}fx%eP|B_Oehc$sg>D=B>(~l zW?f&-mhdLP+ai1~wT9;pM{eeK2$`$MCcm?i@hFYg{jk5?k7o^+@qIAVDsHJ49Ly7_Y< z-dVW@;|DO{cPBxhWL8p{4{YbE+Pggff|qY{Ce9KPiZ>vG1-HOC(WL{_E+9o)TV}Er zSttx6RN9$ZEw%}^>~O&z1m5l%n>>x3b+jMr((i)Na!aUvQC?lWwIN1C6ZsyAlkz)%6A8;gm4DOq4!h3n6()~mrM`{ zJgEm^6~xH7@mGbg?vp?vEu?#n6fN*KfoUjX@E*<~;u#{8i60Ij*M0pJi`dzQx3d@i?2JmZ4D%%r~pyM036{d2FboS1?lIFo2H5o zjH~UfD2gv}rXMf7-vz_gQn>%F~nhi&YH=GG-F^EDshQGlA2=j}XRL(cX*Vtoz$sATtJ&_b$@S>!yvanP z_pupFB|{EUnkAzL(W!~i6Nsd>+Kecuh1AIe10lggV7~#``aN$zpZSnJCayX|e(L>% zFJbQ?aL`XIGcI8NSv){QS#i%8=%EFvCL}0b8lGSZK?jiXZ=vz21U1lA?_yBbl7pxk zh*@9S~I^K{!KKt+6j!;}3MMTAxK* zKBgWUd!u^EJb(A(5cPxx%ol&rV(ejGrTyt~^`Fiu>A$?7Bv&}Ms2^KOnf8C{?)NwQ z4*dKe<>bIvr&0u#c+Qugm-8EQ1v)i_F$`&NX>c37)P699(As%m)|K zW-s7C+%uuPMq5EsvbXp5oer1EpBU?1{ZAH~OA7aLaaQYVNRPIuamQ`=c!uu^r&q$ zglp51O655!9ts@9}{JkWA#B@aYUpTjtq42+(^TIyai0?xwNSooMux^v~s zb$jg1MkmyVQqOnJZ(kR5PAxId9S;63Rdb2!B)P10vbofN&e${-S(D(&#N+5mn1 z`m_`G4XizSkAGE&$Yi}4xAP7nd7m%kD9nf5?^+8WrgpuY7je4>wOTFkG1$)srTJ@+ zvs|fM=X0t$9u%Od?;>5x&WhFqVK^;M4TO>tSUqYwpalLC*lzsqk8u{F;0fk?JeN?9QyUWxsvYY)qv*UaU2o%ZZ$&w zFqbfJ+}87r=IXbZVfSl`iIyMRy)I)#m&+t?+hlzG?B}8*`iW{T|1+tftC#uG*DZRd zV}#zUEPQS!CitvyJ><;SZe5F<{bQNE;&q)>tc8h*$&vo7TUaik^NT<2*R~*+cQ6>P zFgS1tRG3b==@?2TN>f=rG7!$sWR!?#|LEa8C{qKF8M=pu2ja7VFMKFSc=(CUMLn!` z6P7-#mNuQTcd(>ic8a{8Js95#U+s1nEpbZiz4g}Df3A&J%Tx zH_}G*^#Ra&>n>h^TgFAN$t|Y8`q)mpP4o@5Fmd{Pg@CErUf&l>9y|Dylt>DfVVX;; zSu-2%!8khOgddml0IwVpo+(l~i_(~qhiw!BbP`*sl|RssNE;{oJlqVn&2@<-?@Ayk zQ~>dZjbb5#c%Nh3tIYq@!+ZE3C`{CXtI>VLz1lu+_s11IqKo4K6ma*U-TbmUuk;XY zG>2aw-*#l#a}=-reYoqR{j*f(bmFtg-Jq8JM*}JkO1(4)?*~>32&i&<) zniLfWLXL!yk`Sr^B8jk134oEV{eMd%KdxS-pZw^)kYE$Mk!QxwRe}91K=(&*%s$n5 z;%XVj$)u9tttC1#IT;yg!r&;QT#Ust;K~P9LuGjbKm;lXT#zs3++`q6hY$MxM}N(s z>*QBInnvkRQyJPn$t&eJ=Y}R_oa`Wv@k!{eeSTx#j(iUn6fc0H@MdUo#E?#{RSwC(FBP4%|I6DTUS zIU$#u&CQIimpq&98@*fof)1^Q0xHonefE_2$oYpaXc8lf+*zDoV>lKbU@wMO6+*37 zGqaZ1*mwrLMD_1?%)M~Qm;{a8O2k@SHWwIZ)I8fNn2J+*GeAG-9rGxmgB-NsMPcY;QJC38x+Lownl5^(Xr|dkJyJfINmda;3w#9UsjW^&>QZ z(8>NNds6O7Cs!C|;<)fbsHj+y&q0lb+B@|-DYYSs(t9vRV~WvI-+S-)AIo7az68yL z4)Xm}n0NUf{Nk{b(}hZ~!(JH~UC;nEn|tRN3bzb3fxV#V(M>yqU%1h+YY%~LQ}C14*${}f`($EocaK$5k*ANf*q}jc#FXrWAb{uLl^3){ndl~muf-CE&olY2CviT& zW(l4ibOGfjssT1)CV0o`Z`pWmp1-AT*;`3!vPO=H z>GE*>lENKE7_AL|U_yinf7Arb$cpnx1ppQ^5r2y#7fe{z7M}(_2hmv7-FN9?%3t8e zc>-&7){-nLe|ZF-FouT}H~0q3$Cjcz2v7$olT>e8idI|iVs|z^Lq1}uZ!%i&eZJPn zX=>*a#K#4XaObL!NQ|^U7mqLd3$vlZ<&b&4@-#Pz1mv;KnIwj*D`eZdxV>_2?^hQF zYtN&?es>Q|DN~h-!f~h^m6X3pjH&wwdnNQTyric3^TbE#Niv_52=L zTGcvgvJg0Ridcbd*H2hkLCY;?HwLz~zD_INWBTu>wZ*~Ja zBYEVu3c&uW<@%X{|KQ~@&Bc6lb(EvwoZE3OFmXv5=!HCu3*l=XZ{sg0W|wvJhaueC z3h{bPrm{rIsz|^6<@aTE^*JiP@APtw4leegv|ii%*G%t-MYR4MjKt_uy?1m^5uN0W)IV!8y8P0%!$hG{&1s9 z*Jte$2gYW6quAlEfKS;H9aQ?e9Lnx^jA4&5i(}NpL?l8Qc&rSCk&{{2!IS z^9@wX>ThZ8h>sR?=Tj~UwzN>f<2;I_uVcTxPKJD|>qJ#W1cHGe#F5Ednb!bUbT(;? z2psn~Wl*H5C$-T8e5`MsguT+b9J|wQdpZwsd5f+$i%V86BGoVH>YTDJr`j1D=k@(x z@9^J?i;b^+L(+7ye9(VbJwnYK-E$@FijPh(QIGgp`mWGd1fcKx?#fLNzmvR#MwJQ|Md-b z5e*!iu|Xlv<*Kn+%`kq1YDP($ND=QfH>$j=;DBg47*~bP0&h5ZB9F(R-_llazAa!} z_@%$&c^9i!udHqc-4XwoZD!;%F<8GvK|%X;_95~#9-W@gOcrQL)}1McnI|IeF=)jz zJ%e*Go2}ya5a=0`VOoNwpFQQl5xh;nH2F7Tz)+B}TMF#_}sI5M#503@<|DFTDU)_W*N_fm@Rz3Sg3FTVz6ardsvK*@DYB`sYF z1+T9*Pj`C0z4KTsXrrVn12!qk>|lGYN_9VqZmbu{mmGh*%qipXC8)G9%HE-giH7EBaWMH^_Tv-!4*sIUNazO$d;lS}S^@82=HW zi?-*#f}W;a8Q2gWnlPBzKj-vjK^%;{<)@@utGXr=ZBS8|GS2fk&R=HjQ$-h!!U43S z44h=Kb40?z+t8lkqWKEX=6bQx8@(>NpElKS_`u-Sg-)dYwBn}(yRH|yho*})BzHCQ zM{}#8xW6WbwiZIm3v$t0l$Gb`N#^9B_9p7H{atKZ1EO&L=+W^#M4hZAKA5UD--hIi zxmOy^`5PLK%0*k;K@>X~2X{L;gE4V&zeBmoeZ;t_gu;dO&Xg1U5e3^I1n#P`^^2~D z5CKBA7x=GeFV8Ru8CWFm>j!MCePbEJhXax9`9m4lJNF3=Qjmg#3w)6J_t$?hkKiBy zi}V5il%4qph02P7Aq6~(|LnmZ#?kCVAmRR?B>w&Lz>x4+(NIJnS8+fPqtByQe+0mg zfFVVI{D=?#J1A91KwVpt^b%QOSD3n3*kgnCRJyWe{Fo(3aHmvX!vCR{>#r+*MC#91 z)fY?r`m5(~0_mj8zTLSP*DZk9r$fs`)qg zs#qCo)A{$lE2x0@0*Pe<2zblM8WgFs0@e(=n2lmGZfLB9jE~(|SWw-jE5=oqnwu;- z+Q4D@!i^g4bQfEcwD4jwsFXp!hAX}$e91&2oBO$%Q0p~>5jRG-UYY3UG*XAodr~I< zxonIn&y&#MG7ZRIdeQ3Aw0V&OOQDe!3F6UTj#=*1SFM zdTC23q4~J4ITZ^90|VpZc8ETU{d%j73E+s2j3Cir=Z$ng)2Al$=}v;%m9 zax`z(UtmY?wpT7ZuxA~j5BJ6=Ghtlmv+*&JQCRW-!Y#NqT-_8onnO^jqOl3 z_+Eo`07DYKprquFMlj_d1IyHK#s>p4kGXWl^O8bRfP;Aihq1NUC2UrJn}`&x?Udhk zcDS6Jl8Rd7PI>DG1xZSgWI3i?tmL#|)9ntWB;;<{Q-sa!Xsk5*bhvKOreifVJ!lKj z_j~x(-+VHY@TC9%+b)Ou7Hlr)eGM3yHn15tE`|A)L*o<2o?9m+xI%;7Hf)fGYR-)h z`0PIX{oa-(jIOC1JN$L4s9G#lTr{k`srM%SZr3CMHJ#SDHfF*0Gt0;Ehx}N9cG2D7 zU&_tiA(SL2mPe}I{Y1J^@NUj<3R|&jY8D;K%R{AUV0u19DLCkEy*NRO@Ao(VcZ;GN z&R!Q{Gnh^1HU?d`ko@CQE@RRal-X_IxpDL{Ru+7&(OrMf{1oAHuh#DFOzj`HzY8<0 zzjPn@x3%y`EGV$#;$Um|ByVE!&3Gko{12C>rz>;1cYURReqB(f=HJ}Mk(?-NR*%M( zUlY7vT;i)6BgCE#zW;3~0IbQ+-|d$Y!ZyAq9k+eZzr0J6_&n+7L=dwQO6)e6uI}o9 z2<6nT3Ey90wrq4V^iewjiNbFz%lVnh$UZ(1y#)QpH)gAL(oo0StfL!)(nDl>$HJ)oJ+K0HgnMb-q2v9t+yKDMg8zYYS0}Bgb;d;1zC#Kq= z7mc=ldj^Y77tG3r(SN#Lr|)`*Qj6uZ1Z6 zcdOZ#?cr5Ad>xGc#Swp>u9p|pd5p?mJ>kVXPsLf)jQ9$=Ep}iJobKN(rjsOw`^$KX z5mCv73y59NOzKgm`n?SQ9m`i5v@7dY(o}V6Ry@)5d#o!?sY7G+t&1m5I=74TfMYfP z?wGy4_DNE|f2|z*V`)~J#;*$oi#B+1TB*85 zOIc4ZmX6c4?WpcDPM+={%DY(jxZp}chuL{>GymMuCqMwZILG`LR%YFW#2tB?VK+Is z>0|@v(Anz`CO>Yok}V&i$PYUP{Y{~pE3V=&G-iC=`d(tTP@{P;sasRA$?FcU5{yiU z&5theX`rOuk-M9Whr9Dn_@;Ferpb-!zqPkyzrL!0>*Wz@%+r4AxaV;LN?nVHcv8dd z@*nK3UL`6-W|nsfNJ+Q2>c`I#m3jRlG5-PHtul1BZn2+!IeXWwTN|!LLXTtdH3f5z zU|dbY<#;3x^V`?(Logi#KeV=UC*9dOs!6nci~S}st#GY5EA6j7SOaDk-%I~AeB#P5 z1HT+8c^#ZOUoIz2j(%YMm%IIvvbTe*lo_?W+gAPPpz$mCO(wwN0v!GH<~8!LEJs~q z*B?p4Xx`($`?kw?b0KFu>op5~b2)4}ist`-Z^Wm3rA8uy3m(a?oBv&iq$>p; ztadI#dIZ(3HiVJhuwntjHcCQ+zi0mROGtkpaxcDC-&=4ZM{AnNXZvhGjrMT z4;J6KttC$)pyRLiGdqA0qqceJ;S3`AKC|-_>G~fS*L{R}K|ODhjJr!KiVg^Wl+?)j zrHyZS@P{Jo<;`k;^dT1;bxl`IUDJc+CeL%Q;76lj)x4^Om1PC zu#)oDY#I+8Wd|pU6-<`}mlj1DC#$yBnB3&MPbBahED5FrLxw5cjA`9qqJJpp95M;1 zghWOnJ(cN~VdQ{F$T>_BMhQd4pb6hYpP46B zulZ-baz=fsp|RQhb7WUw)-ZOo;y3sMf^pvi?XwPd%E$SIsdOT-&*OTm4>|igCux}H zs=m1l-57yzLE~iS2pRjcK$`(bm>hK|!I_c6)1kn;4Ta-5}my9!d~_z+c!Y6$mFu7HkNhW0RQ|*hsR6 zj)jLpb@6wLlF1}IoqmCp*(%nWI00GD`*~pqvM7a!%<~O0%=tjqur?mKP*@C0+k7JP z55>JM&JTKaJ>PF_eb|sM36b-pJyzKFp#RB*n>#6{eDV(7v`?l!B%#w|GPF~*2!nD) z0cmsmN4AIGsr5qwBBG1bS!LoLsn90Azf7&}^&ornwO5DT4kqI5zzL9nKal%U3cuFc z>r4+#PbBSPC`L(GXi%M`aZ3;^_gQ~2M3cC7|M?*dqcyyIx=k+sA0mJyN!kTPXVF(z zZ%irH-e!hU>zY0 zk5=Rdtcwf$JU~gKQCg6Z1;DuRevl$Q!Y{S|%3}S0Si3kDxiu+?Ka?}q&t}Mj=U9dG zQ-%lpQx?l4wI=pME6(hHc4AkqBMsu8GA@vxGJM12x|knYF}MFiEB@J!)ek%i{>K9} zY44U*FPqoRO}8nfvO$4A-}0QI3q=?sS^3kmD$aMdvlA>YFS>%R{fLaV*5*kG--3Dv z<9ceIC+te)38u%TX1B^`eo-sa)iS1=Kg3`@YceyDoTET-eV*^56q=}0)~X+sFYiiZ zmQ?A$ePSlM`8MrILi*HeoX64XeQti@#h3!#A96$MOcoP;)Ryp|(}aK+E3@6(GRTt` z(&_k7NH-H-m!jdp5jFs{qG7=a$exbVBW~3H8XXnF>(*N%eoosa*c|dy%I&zaF^$E- zstSgUJcr-zMb@hyf!z~_Zra(sZa2gjT1BgxU#8c^QY-AB)*8h}mDU;F@?W_BR#O9T z$~6K2+Ux&;;mybLWLNCvR!?0r8x56?kn(yfY#D!a1OZ`tS)25hhyT?Ol&bDz8Se)f6xKs*D-3!c(@x5Bs1ns|8; z{{n3%9Ge-Ez8k*#Ea6g<= z;rXxv6z+Qsj25o03v_c+QKVnw8Rk%(*(3yFF8$qTJm@29(NbS{XBH2K(@9-8u$;;(@c)BL7rI<{E_@O`bCG$JHayfxN5ZzK$@@=6MU! z6J|aoT|`jCc~+rwX%fDZMxjTL*5fi**sOY zsMqiOQuo$CbDuAhoxkoZbu>G>q>;y|lDGK8_AdF0@d;UEW~SaF$eVq}k;nU*;?7ra ztNn%^?zopOoUB$uO;MP|0GAZk>rkV3O0#TP=1C^KCU??oNKpZvI1G?1Rg#`0}2Ps(|M`Fh^|N2k?R z~$1w^0QojP4xS+lF0{jU? z+((yi*%@gnl#B@P%kb@e5`*6d-U#d~TM@dkAMP5bu~T9c$aBPK?`#_ z=I(GxX5(Jt>omh>Uk6g!4y&8omevyl1evNpp(#YalPYOY#Azy;G?QD;AyzPa?XYq^h zT7BRvI|cWb3|;6MW@-^?v7I`m7sI=$}RE{tpvx)>#LB12K}X z4L6FLvdufhWRk3$3#CCRl0JqV`W4yj9}+C$^M-nf4`Ub`9&$J&4K z@#80f1SiE7110TO&R=HV?~hG}eogHZoV&<4Ke0VMi>C;m8!#_jOR_rNXYpI`e){@X zWF)cRN8_x7gtyr}Dfs^*u(3<+?_r~g-rr#3p|h-|5yKAuU}eoT=Kap3)Zi|p^xF7= zm&6P1gx=-y#`f&1)zL}9d|fty0RNXapHi(P+~7U8J|Sr1SP)`4mSlalLH(+Zc)t*g zaXbjMoJ4Xo8;gN$Cs9BM_Bb(wMNW?+sMmed^w-hBr0(opoc{HUoOf`Lr9j zJ|{)|66f7W$$)0RG!LhXo5bsm!PQ6K=I!ucGUX7P_|h-Wp5x zJ1s`6Wv_x0LSh_6JvwvVoyE%DW;xBQUcBgOUVrYGnqX4ZjNE@?M!ql3!MtCWiX_M4YHVoGs;CQtJ0#E* zOh!CtsI$s)ySYB~>B}x-eo^+|LsoVxa-qhhre&?;L0oIK{4e87Eic2HaD&*szX-YhjSsrF`(o~kJrwG#p>K`pf`>jQ*6>Qd2gIJH#y-v^RU6%d$tvUq zD8z4l=;(f(;Ne!@X*L!hCxU!%r2wP=z#r3M#hc!m5uoir&L~h(P?wA|+kBRv8D;%6 zlEXyTzxyF1J!kF*1^_HHB42RLTTW{IdnD+9yXUdj=&Vax94Jf^%D}kSwp!GjF zPV?&|YiQkwg|Wl*e3C&6@6;j|VI0w)#uo zt+2;RUVc;mJ3+WiTQL&T9)iGYgdX3^=A5NWdaN()=3nUd<0J23E}MLDa90ck0N#sG z08~7taNg$zkeeo;?dJWDd~hfFO#)-_s1Jqi`JmoS*FR!X|jll{=@|r zr!3+5wWuPIQ?rJ0G}D0bNRPkjz)hTj6H}8e7z_8XuKLP6DWMaL?@?HdvrKInLN3tf zOoDbrd|UZ)mCddCukEawi&^uwxGurT!@Sb5;FzeH}K(r|LI zCzrwaMtl+l5l+YqpDl!a0}U06;5s<`-M#%%bA2+vCbaM z>*??}=w>fmE}j!i>U?bbabI0nUvs&2#9i!cDYnaGCv2w*y;NAPWI?>T6U`iz-pE|8 zm#`Bay+SUj^V!w!J>_St)kAudQ#MrXi$T ze%G#%<<<@waM97&W7f@kbBo9u(;TDR(b&U0h4=XF{kv1(&FJf6IwqCan82b@Ne!9YGkBj-; ztX<5YhJnGX5c-nOwIo$0VKC;LUNXLI@Rm&dSElb6#tM&2Id zi8HRiY_4iae2w$_Ii`E*`v?-OoGV+Br=$@NK41IBf0dA=0`j;10jYu^95ajgrr%r7 z#Z2{jycz|?ujxiHTC|47y~!e7~V z-cq>*o|9Yrlo54Zb+oqHfRWirBJ>i?&xjxO5O&DU!#AQoGp8#2>M;K0#HMNaL%ejD zaBk|^=zAYX#eW21%Ym>g$9uY<6O&Rgu8%a=*92ewChsPgnH{VYz~=?y!pFRr{6a!> z+0AVvQGS+Px&C4$2t4THh;ODK?tP>3YUz6O(y*^`;^rx zZ=3$TGbAsFrOdw|`KMn1bBdgo%79Ta@9DIMqtj7Zb8L~Rgt~w!kg|bAY1vmQC{!f% z#4DxZa@SbFp6#_#9Ybg2k=*XS_3di|Jbz4jB5c5qs@Nqi-Ypk;*Z%__IX7lnk6CW% zy7#(^_$*gvyGwQI4WnQ$Ry;5oI6swcT+#CoZKIjuvDA(N^{#BIm#2Py>PLP@ubskC zj~4y{I!$v8Ntk`8{l+)LxqUHMLo?)0%f=*N*`RcH4M{0dR0w-wQQRZ1avBqzv{2mE z^CtNwhJT^Ct53;E#ORdTGG(BmH#Dpkm^s!;!OIi4=WA;->a|GWFZ-<)WxC;ZIWrF! zGi+@2FCg;87AKoP;#w(J0YRGoGlb#7-s724H$F{N13;Hk;Z1MZ+a7G~nyrZ)jGCGo zt9oe0M8s4^AUi^w0J3HdP1^~JXc72YnKA!Ij)qVCbP$H!F9UmouIpeCJsO-`)X**? zS97r(IS4KcC)?3DWFZXW4_0Zq2w1tDJloGbyINQ9z)*{v#Q5eQ#{V{Fg?$7Ng1=`mA27)ma-QAH|2y=T=qa`t9sxo^5kE6r3&QYDk}v5u4U9%bcs-(Ji71lG*dwQN+&? zbh=YpnV{xe95)v=pjQd3w9tor57Ab}+N~!?xm+6G2-KZ0sPGA8OPPH#aiSrKXLUIp z3&%^$`XS62#^pM_K+~QmQ9o zAUd5h>BzTM5|QusB+abqmu+`T8kotFf5FOJ%T@w^4#@T#uK-1lza)K?;8?R zQ<_`A70YjdDVB==Te7O5=UlngC&iZEW7c~IP^Nxkr*pC{lJ1%tu@;v5(^KnUwLx;q zi#qp_oyel;9~K(X2To2-VrZm9(E)jrWYYfJ_rKCp zDG_>V?9H>`Pr{WR-`LayMpzrL##ly$XLfDEv2YyOv_Df4~9 ztf~~csbo778;|&{eb`?_QylTZ7?!ihV_8DVXm#J{?F~wDQC633{H*;4Ou6q6W#v_xFF9{_lphBSE;Wyx5_w+xr}m{Q5ZdWz)+SS*u6 zkM3==e|8nhPy==f2GwX&RG)T<2rgEVtSgDr_q|tDrL&SovAwCj%Kx4qxfn3YPY)7FW&JMni+u#K`ci zk%4vM`$^@-W3%-6eK5=o;aO?HKZM@fTG?5jMRVRfKXjV!eOa1TQN;i1X_UJ7C$h0% z?}t>&Qxg;GC<`^C5`SrC{Jy(6@<;XO0chjHWMDKrjZ~yR8>%%~~^*sGSJ{@4g7O(?=i)2;Mf2yoH> zvDa)siZ&)O!bpR7S!{>qMS1{68X&-0ACZU6i15)cA$&B%QWQ4`9}PEf6%f{~im=gW zhy#p_DU$Dk6p-8ja}y$VyVx7Js5}_pZ5reb`MhK3EYMxJVZt(B;c{c7K=x zTlq5}R|DzZIViwI^)A|H4g>!q5Ul+O$mteEN9=bAv}C93M#Eo~!;ul*wHGoD z=U*gSo(ZAaY%O@)Sx0$2d|q?WcQsb}{uo>TFQ%O+qc!g`D?Bu9o}}5lcV{!Lm_(+Y zY%kTuTljBEtt|C2oJM}*iHASscU`CPGBc+&UvivXX=&Zhzy8QNZRH^>Wn^CVS7J6* z_~p=IxAQWEsamtYt?b^sH(c1Ik~v@R8L~TJy$yEsV^$SkqTZ)%=)BF_Y1Um8$QTMq zu7u7Vfo(!W_Yn!CccMTnFNwg8l|)`xXbn@~)eu103QJD><&#uiCmw1X{3I3Z2k{TbyUAGW@-8Vm+e{I5ZKSWPPs`loYfWwt-w@-~k*HAQ{10XuH zQJSwWtcw%#AL7RJKqVtc-<1R-wd-7@*8+yYLkFYQ?-7QYsl5oqZ1czU_ z8p`HO87Q^~)^Y-iJ4d=)zLNO{)cgC0^lZR)xb+QBq*apoaKE#{wD9%q`R}NrC{pHm za6E#`=_beZivQ+FfBn^pgLyF{_Ys>@^>j~zn=_1H%9!`rv(0j<$wW-exrPt@t|J;n zV5%pZ>?(ylu@|Y@`FBStsVfG8;WhuF>tI=a@HdZ-T%MfcR)$khgc~Eo)_*u*{Dy1e zSWEK{j}K&+|xx7|0!3YkU;nGYQa${7@qB z(f*sRpYEdLb+PXx0`FE-oE#^^g@+a#!%PZ4$&uF`19j(q?_lZCs2U^Ni3_i!s%%cOGOI&mIo}oI{Z=OA6D1$#eJ@ati{h+xz-MH6Xui^VbR~L! zDD=%M+JV?bKnkKluf|BnvN(IuCp_krCn0Lqn%*rbnTPyohH3-oX!Ibz&V-O;?fjbw z2%j*;!fLGA(w1}mEQj*4@7h^Myj@#fodjP{kXqMJZ+ntev^(YSrorFIS1JkNh>?9H z+!Xb;KAFpF2)BGL^z)5ePNzLOyGQ?FbN_jSJo-$NqsiX;6T++IFCWeiwI4kH+D#eQ zUA{(IZ6{knii4e68fN!6&5x0FD@sciQn9kVGt;eq(p`??(SdvT*2Z(}DX>U`>x0_v z6GJaOIXLdUE4&KfB8Yp*obHt>gl6gy4d=3DyC&Myb!P!#a-I@&l(^! zUS41SsR7dUCTcm@sd}%D&i~{v#Rx$jukzECuB}Yi*nxqQEJM;%tz2)8?gH4&=*;O| z_!(o{3Nu^J2Y)7B1U_}Y`tKNpR%wF&N+fUagbN5EM~5vuQ(P~r z5aI~uk}b@+oyY2P;eQc1RtKL*a=*WAm-bY8($r=@zTLX0EY#wDCHURL#l(xUw<4Hy za+WX5Z95zd{#XKFp!M{)3JaDSC#m}`o&9h6pWUanUUn2ECA(m6)C6|~hR#E|D6w2- zBCf;8A}?s18eKvX@(z6i=d5dTX;0r*L5O?pC6*$|$-i<*Hq>tmAK?%r^owOWyo){m`(!U7GTl*Jm z6rJ@nyIZfiE2&9WZS~*is3i+WpYdx{VuLQJZ?DjEDM|W_8^?a%UfWqXSL3yW>G+U8 zM?7hScrTW?{!pj{KaPi!ZN{nqv~LD?y-t_9SBKX>K|Oy9h#;WFC;t? zh;el<(6M0E@Z~@{U^|9dPa=K&$_cZQ@bzp z95P9GRjq8?YGZ3F?6^c-_TPh_ih{2VWv-3Fihsk?Pdg$S*)})A6}Qg9Sl9# z{kkcUP-}cJ#xoRjtyrQ3Vmr^EUHtWpZHa-jLBhmD0%LqyaA)-WuFOu0L`C#A7vqqb zR{ZYATiIp|SLn9=CLg_qZ?T}jk@<%brdQ6aeXAW*SPqdJ#C&$W&Z2E3tu_87GCK7v zVD^k)A!nM@-@ith`Qv$nfijZe6>fPx(oC5Iw399e){i`IUpUOTzg+S;EAl+9UR$_< ze$oC=UH+E?0}F-nt`^p9o__adXUrvZG!=BGfKjRUF`}O&=gLI)vaL8jpI-PSzvbW< zFeA75KE3aSn2X6u<(-94Sx+<>p^#iUkO%WN_>C6#u2<5}ug9ejW%FSG-Ynrz1Po~10&O$TZT~#8p$zCgPRtjwxoJKu#(cnmtT6zxni@F{~ zP)A|^^~iuM;>ZB6#)sP@4{6d&MUH8M!cwxXR(DZhh9Mr_5qm%Wt(KAYUjw@^1_IgE zOrN9)lH__vWR1un{=3`29Cz!ZO-dVf$2WgDLU8p)nNa*K#Hyl)sa`9+11a9Cqq*jx zvuAgvY>}<7UJG!3C_hSQuCgLu^8W3*;?eb9cnJE8k+i*|$ImU<0joK#Fyhj>H)J1L zGMYJ-*jt$$&SKp4zccY%Z;kyPmw1qamDYE1$CYg|8TKh=B^`75rE&P0jL?h-mU8~9 zS$n}O+U@3|p{_!hlHt*8-|E=AG^<$SvDzG&o%f5$KW{)Qk zgpY*u{ukUg<4HF65m|rOZo#yV>6HriV=0cM7)h%{tYjXW^NdV%c zghHRj8LkF;HP3%okD6)G;Lv~B0coyEY=5jZNCOzrB{7hPKEFORNH zYR(Q+p6+DLwGpi6yjnh3y>oLn`!m&mwXh5So78*n+;9$4@wg7!_3@JMABpAA( z=*vhosty!R3np5YTl=F$ibYQ6-+aGLUL>E{FY0U?Amg)9w-14mx!GLwv8lN;!jOc} zK6Fvu{hq~d-WIkL{miQiy1Klan4I)EZ8~fW3$LV51Bnd)xHdSP%~Jm)`u#gcWu;V5 zS$CJv$Em4Rx$AmciybrVES*n8X40*#tqH(L$4K&$@^bDcT@?i%%v*}Lz?ki{J|!rG z4+?SQo^gbkE#*j?vm59!PDv@qBE*;=0y)->K7|45Qe&{PiQ{V&`7l^JQBwbgbqEnT zxjO*fJ4p!Uy=aF`KO2eZRm$aO5FqmjN+0QOuPpWtroCLbkQEaX112T{XWx6(X_VN8 zo(nNoL8Z|pNL~O5V1~HF)T89Uy-X7eT>JVdwca?Ipk zp99dYK{xsZFfbKEEV%^(A}M z!$s4Dj{y0GT&vN8@JW9$VobtQM^WH7Oqh279R3xjz=sM--Hs3HqS){wj#0)1S#x-k ziHl|Xc!QNtQIfk*2SccpsCe;_Nqc`8hKOl0{K>3Lk>9u|!HHT>wThjC$bC>SRH?!uymp(P;8 zcY=wqoNJQ>EuB;aH^F|r{D;!uk3(hG$@@;I%b`%59#3sY?M!) zs2GbH^RvD*W|~Vsmt~`^7W$*e&YU1E^qAlc8Cw|gL@_3&2v}L4z)D;uMUgfphCUh` zQ>BI~PlPGLNK8KhE(w08EJWZ)gS0$XQ@{rUrGi$;b@3rjDH-Ba#fj+S97KtWK{X{$bT(!P5f*a4B)uDEE<{WI~NfB_+Vs%1ek(<^1xL*20+D2;&@V&ne+ zVx!*>3htA2px< zt7KJ-kU50$0vgCRNyUgynxlpL#lVy{#~c@Jz@brr7VSx=m3i+8C|?o1I0lU4+fJ=U zh+62TI}Gg9yc;0Vs-Dp355z!) zVp;a(HRU|Rw9l5-1EtzxBFANl!^vj7z0s5jKs#7smr#s4*m(McC{K+fEguCk-~0%H z=r}nhH$M7uG>^1IjWiY`0ilJLNFNE^PU5yS8JN&7FM=1z?%^ta8X@Y>beeZm0SpT2 z!Ky*F^vJ;PT`!*GU@B1g#M4RRBWmc4t#W`zZH6IwkG)I_OG$if1PGc$>kS7}IX_4x z(dz7~1u2;@qWIQEj94g0{8?6YLZ8VA1v6MEF!$!uf_KBvM7W7iaQHyjJDD^gii&8W z(ZQ6R`8D3z4#>!<7Wg~dQr_WJ`8te-SjadMsqI(=2I??;+dgDqo#oz1%ps-v!|fPF zbLD}}`GYx}?W#*8Xh3?I5G6bWmxaKo-M<$n2V|Q~9rJ}YB)lDs>t||=GtG5$*i$6A z)hsh*vXDqAZwi~lz{F_qIy5B37}py`R?(v`pPWjXk|F3a-yK}QXMt~i1AIMw7tY(K zPyJwWyz$xGu_}c5Yz=T@TJ{)g=?PSPI`3Czsf0m_`C!rL=7wF^yEnd}KgbU3#x~V0 z7YDhma(+;oa}F|yKy!K^`c)Fh@?e^E6d@MucNjut1-vodQQe7%(}`p6@*7fclD50{t-J@ILukO1jRVWgr-bQ;+870KlhP?Bpgtc2N(X>}G|`)7k|rIVAF ca8d3lw|5lXM_7IgBLP2f2?g-g{NwsXAS~yZ7GR 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 literal 0 HcmV?d00001 diff --git a/vendor/jasongrimes/paginator/phpunit.xml.dist b/vendor/jasongrimes/paginator/phpunit.xml.dist new file mode 100644 index 0000000..5a9c1be --- /dev/null +++ b/vendor/jasongrimes/paginator/phpunit.xml.dist @@ -0,0 +1,18 @@ + + + + + ./tests/ + + + diff --git a/vendor/jasongrimes/paginator/src/JasonGrimes/Paginator.php b/vendor/jasongrimes/paginator/src/JasonGrimes/Paginator.php new file mode 100644 index 0000000..16fc51d --- /dev/null +++ b/vendor/jasongrimes/paginator/src/JasonGrimes/Paginator.php @@ -0,0 +1,345 @@ +totalItems = $totalItems; + $this->itemsPerPage = $itemsPerPage; + $this->currentPage = $currentPage; + $this->urlPattern = $urlPattern; + + $this->updateNumPages(); + } + + protected function updateNumPages() + { + $this->numPages = ($this->itemsPerPage == 0 ? 0 : (int) ceil($this->totalItems/$this->itemsPerPage)); + } + + /** + * @param int $maxPagesToShow + * @throws \InvalidArgumentException if $maxPagesToShow is less than 3. + */ + public function setMaxPagesToShow($maxPagesToShow) + { + if ($maxPagesToShow < 3) { + throw new \InvalidArgumentException('maxPagesToShow cannot be less than 3.'); + } + $this->maxPagesToShow = $maxPagesToShow; + } + + /** + * @return int + */ + public function getMaxPagesToShow() + { + return $this->maxPagesToShow; + } + + /** + * @param int $currentPage + */ + public function setCurrentPage($currentPage) + { + $this->currentPage = $currentPage; + } + + /** + * @return int + */ + public function getCurrentPage() + { + return $this->currentPage; + } + + /** + * @param int $itemsPerPage + */ + public function setItemsPerPage($itemsPerPage) + { + $this->itemsPerPage = $itemsPerPage; + $this->updateNumPages(); + } + + /** + * @return int + */ + public function getItemsPerPage() + { + return $this->itemsPerPage; + } + + /** + * @param int $totalItems + */ + public function setTotalItems($totalItems) + { + $this->totalItems = $totalItems; + $this->updateNumPages(); + } + + /** + * @return int + */ + public function getTotalItems() + { + return $this->totalItems; + } + + /** + * @return int + */ + public function getNumPages() + { + return $this->numPages; + } + + /** + * @param string $urlPattern + */ + public function setUrlPattern($urlPattern) + { + $this->urlPattern = $urlPattern; + } + + /** + * @return string + */ + public function getUrlPattern() + { + return $this->urlPattern; + } + + /** + * @param int $pageNum + * @return string + */ + public function getPageUrl($pageNum) + { + return str_replace(self::NUM_PLACEHOLDER, $pageNum, $this->urlPattern); + } + + public function getNextPage() + { + if ($this->currentPage < $this->numPages) { + return $this->currentPage + 1; + } + + return null; + } + + public function getPrevPage() + { + if ($this->currentPage > 1) { + return $this->currentPage - 1; + } + + return null; + } + + public function getNextUrl() + { + if (!$this->getNextPage()) { + return null; + } + + return $this->getPageUrl($this->getNextPage()); + } + + /** + * @return string|null + */ + public function getPrevUrl() + { + if (!$this->getPrevPage()) { + return null; + } + + return $this->getPageUrl($this->getPrevPage()); + } + + /** + * Get an array of paginated page data. + * + * Example: + * array( + * array ('num' => 1, 'url' => '/example/page/1', 'isCurrent' => false), + * array ('num' => '...', 'url' => NULL, 'isCurrent' => false), + * array ('num' => 3, 'url' => '/example/page/3', 'isCurrent' => false), + * array ('num' => 4, 'url' => '/example/page/4', 'isCurrent' => true ), + * array ('num' => 5, 'url' => '/example/page/5', 'isCurrent' => false), + * array ('num' => '...', 'url' => NULL, 'isCurrent' => false), + * array ('num' => 10, 'url' => '/example/page/10', 'isCurrent' => false), + * ) + * + * @return array + */ + public function getPages() + { + $pages = array(); + + if ($this->numPages <= 1) { + return array(); + } + + if ($this->numPages <= $this->maxPagesToShow) { + for ($i = 1; $i <= $this->numPages; $i++) { + $pages[] = $this->createPage($i, $i == $this->currentPage); + } + } else { + + // Determine the sliding range, centered around the current page. + $numAdjacents = (int) floor(($this->maxPagesToShow - 3) / 2); + + if ($this->currentPage + $numAdjacents > $this->numPages) { + $slidingStart = $this->numPages - $this->maxPagesToShow + 2; + } else { + $slidingStart = $this->currentPage - $numAdjacents; + } + if ($slidingStart < 2) $slidingStart = 2; + + $slidingEnd = $slidingStart + $this->maxPagesToShow - 3; + if ($slidingEnd >= $this->numPages) $slidingEnd = $this->numPages - 1; + + // Build the list of pages. + $pages[] = $this->createPage(1, $this->currentPage == 1); + if ($slidingStart > 2) { + $pages[] = $this->createPageEllipsis(); + } + for ($i = $slidingStart; $i <= $slidingEnd; $i++) { + $pages[] = $this->createPage($i, $i == $this->currentPage); + } + if ($slidingEnd < $this->numPages - 1) { + $pages[] = $this->createPageEllipsis(); + } + $pages[] = $this->createPage($this->numPages, $this->currentPage == $this->numPages); + } + + + return $pages; + } + + + /** + * Create a page data structure. + * + * @param int $pageNum + * @param bool $isCurrent + * @return Array + */ + protected function createPage($pageNum, $isCurrent = false) + { + return array( + 'num' => $pageNum, + 'url' => $this->getPageUrl($pageNum), + 'isCurrent' => $isCurrent, + ); + } + + /** + * @return array + */ + protected function createPageEllipsis() + { + return array( + 'num' => '...', + 'url' => null, + 'isCurrent' => false, + ); + } + + /** + * Render an HTML pagination control. + * + * @return string + */ + public function toHtml() + { + if ($this->numPages <= 1) { + return ''; + } + + $html = ''; + + return $html; + } + + public function __toString() + { + return $this->toHtml(); + } + + public function getCurrentPageFirstItem() + { + $first = ($this->currentPage - 1) * $this->itemsPerPage + 1; + + if ($first > $this->totalItems) { + return null; + } + + return $first; + } + + public function getCurrentPageLastItem() + { + $first = $this->getCurrentPageFirstItem(); + if ($first === null) { + return null; + } + + $last = $first + $this->itemsPerPage - 1; + if ($last > $this->totalItems) { + return $this->totalItems; + } + + return $last; + } + + public function setPreviousText($text) + { + $this->previousText = $text; + return $this; + } + + public function setNextText($text) + { + $this->nextText = $text; + return $this; + } +} diff --git a/vendor/jasongrimes/paginator/tests/JasonGrimes/Tests/PaginatorTest.php b/vendor/jasongrimes/paginator/tests/JasonGrimes/Tests/PaginatorTest.php new file mode 100644 index 0000000..00879ab --- /dev/null +++ b/vendor/jasongrimes/paginator/tests/JasonGrimes/Tests/PaginatorTest.php @@ -0,0 +1,123 @@ +paginator = new Paginator($numItems, $itemsPerPage, $currentPage, $urlPattern); + } + + public function testGetNextPage() + { + $this->paginator->setCurrentPage(1); + $this->assertEquals(2, $this->paginator->getNextPage()); + + // If we're on the last page, getNextPage() returns null. + $this->paginator->setCurrentPage($this->paginator->getNumPages()); + $this->assertNull($this->paginator->getNextPage()); + } + + public function testGetPrevPage() + { + $this->paginator->setCurrentPage(2); + $this->assertEquals(1, $this->paginator->getPrevPage()); + + // If we're on the first page, getPrevPage() returns null. + $this->paginator->setCurrentPage(1); + $this->assertNull($this->paginator->getPrevPage()); + } + + public function testGetNextUrl() + { + $this->paginator->setCurrentPage(1); + $this->paginator->setUrlPattern('/example/page(:num)'); + $this->assertEquals('/example/page2', $this->paginator->getNextUrl()); + + // Returns null if on the last page. + $this->paginator->setCurrentPage($this->paginator->getNumPages()); + $this->assertNull($this->paginator->getNextUrl()); + } + + public function testGetPrevUrl() + { + $this->paginator->setCurrentPage(2); + $this->paginator->setUrlPattern('/example/page(:num)'); + $this->assertEquals('/example/page1', $this->paginator->getPrevUrl()); + + // Returns null if on the first page. + $this->paginator->setCurrentPage(1); + $this->assertNull($this->paginator->getPrevUrl()); + } + + /** + * @dataProvider getTestData + */ + public function testGetPages($numPages, $currentPage, $maxPages, $expected) + { + $paginator = new Paginator($numPages, 1, $currentPage); + $paginator->setMaxPagesToShow($maxPages); + + $pages = $paginator->getPages(); + $pageNums = array_map(function($page) { return $page['num']; }, $pages); + + $this->assertEquals($expected, $pageNums); + } + + public function getTestData() + { + return array( + // num pages, current page, max pages to show, expected pagination + array(13, 2, 5, array(1, 2, 3, 4, '...', 13)), + array(13, 4, 5, array(1, '...', 3, 4, 5, '...', 13)), + array(13, 5, 5, array(1, '...', 4, 5, 6, '...', 13)), + array(13, 11, 5, array(1, '...', 10, 11, 12, 13)), + array(13, 10, 5, array(1, '...', 9, 10, 11, '...', 13)), + array(20, 1, 10, array(1, 2, 3, 4, 5, 6, 7, 8, 9, '...', 20)), + array(20, 2, 10, array(1, 2, 3, 4, 5, 6, 7, 8, 9, '...', 20)), + array(20, 20, 10, array(1, '...', 12, 13, 14, 15, 16, 17, 18, 19, 20)), + array(20, 19, 10, array(1, '...', 12, 13, 14, 15, 16, 17, 18, 19, 20)), + array(20, 10, 10, array(1, '...', 7, 8, 9, 10, 11, 12, 13, 14, '...', 20)), + array(20, 9, 10, array(1, '...', 6, 7, 8, 9, 10, 11, 12, 13, '...', 20)), + array(5, 3, 10, array(1, 2, 3, 4, 5)), + array(1, 1, 10, array()), // No pagination if there's only one page. + array(20, 5, 3, array(1, '...', 5, '...', 20)), + ); + } + + /** + * @dataProvider getRangeData + */ + public function testGetItemRanges($numItems, $itemsPerPage, $currentPage, $expectedFirst, $expectedLast) + { + $paginator = new Paginator($numItems, $itemsPerPage, $currentPage); + + $this->assertEquals($numItems, $paginator->getTotalItems()); + $this->assertEquals($expectedFirst, $paginator->getCurrentPageFirstItem()); + $this->assertEquals($expectedLast, $paginator->getCurrentPageLastItem()); + + } + + public function getRangeData() + { + return array( + // $numItems, $itemsPerPage, $currentPage, $expectedFirstItem, $expectedLastItem + array(95, 10, 1, 1, 10), + array(95, 10, 2, 11, 20), + array(95, 10, 10, 91, 95), + array(95, 10, 11, null, null), // If current page exceeds total items, first and last item are null. + ); + } + +} \ No newline at end of file diff --git a/vendor/monolog/monolog/CHANGELOG.md b/vendor/monolog/monolog/CHANGELOG.md new file mode 100644 index 0000000..a9a31e7 --- /dev/null +++ b/vendor/monolog/monolog/CHANGELOG.md @@ -0,0 +1,633 @@ +### 2.9.3 (2024-04-12) + + * Fixed PHP 8.4 deprecation warnings (#1874) + +### 2.9.2 (2023-10-27) + + * Fixed display_errors parsing in ErrorHandler which did not support string values (#1804) + * Fixed bug where the previous error handler would not be restored in some cases where StreamHandler fails (#1815) + * Fixed normalization error when normalizing incomplete classes (#1833) + +### 2.9.1 (2023-02-06) + + * Fixed Logger not being serializable anymore (#1792) + +### 2.9.0 (2023-02-05) + + * Deprecated FlowdockHandler & Formatter as the flowdock service was shutdown (#1748) + * Added support for enum context values in PsrLogMessageProcessor (#1773) + * Added graylog2/gelf-php 2.x support (#1747) + * Improved `BrowserConsoleHandler` logging to use more appropriate methods than just console.log in the browser (#1739) + * Fixed `WhatFailureGroupHandler` not catching errors happening inside `close()` (#1791) + * Fixed datetime field in `GoogleCloudLoggingFormatter` (#1758) + * Fixed infinite loop detection within Fibers (#1753) + * Fixed `AmqpHandler->setExtraAttributes` not working with buffering handler wrappers (#1781) + +### 2.8.0 (2022-07-24) + + * Deprecated `CubeHandler` and `PHPConsoleHandler` as both projects are abandoned and those should not be used anymore (#1734) + * Added RFC 5424 level (`7` to `0`) support to `Logger::log` and `Logger::addRecord` to increase interoperability (#1723) + * Added support for `__toString` for objects which are not json serializable in `JsonFormatter` (#1733) + * Added `GoogleCloudLoggingFormatter` (#1719) + * Added support for Predis 2.x (#1732) + * Added `AmqpHandler->setExtraAttributes` to allow configuring attributes when using an AMQPExchange (#1724) + * Fixed serialization/unserialization of handlers to make sure private properties are included (#1727) + * Fixed allowInlineLineBreaks in LineFormatter causing issues with windows paths containing `\n` or `\r` sequences (#1720) + * Fixed max normalization depth not being taken into account when formatting exceptions with a deep chain of previous exceptions (#1726) + * Fixed PHP 8.2 deprecation warnings (#1722) + * Fixed rare race condition or filesystem issue where StreamHandler is unable to create the directory the log should go into yet it exists already (#1678) + +### 2.7.0 (2022-06-09) + + * Added `$datetime` parameter to `Logger::addRecord` as low level API to allow logging into the past or future (#1682) + * Added `Logger::useLoggingLoopDetection` to allow disabling cyclic logging detection in concurrent frameworks (#1681) + * Fixed handling of fatal errors if callPrevious is disabled in ErrorHandler (#1670) + * Marked the reusable `Monolog\Test\TestCase` class as `@internal` to make sure PHPStorm does not show it above PHPUnit, you may still use it to test your own handlers/etc though (#1677) + * Fixed RotatingFileHandler issue when the date format contained slashes (#1671) + +### 2.6.0 (2022-05-10) + + * Deprecated `SwiftMailerHandler`, use `SymfonyMailerHandler` instead + * Added `SymfonyMailerHandler` (#1663) + * Added ElasticSearch 8.x support to the ElasticsearchHandler (#1662) + * Added a way to filter/modify stack traces in LineFormatter (#1665) + * Fixed UdpSocket not being able to reopen/reconnect after close() + * Fixed infinite loops if a Handler is triggering logging while handling log records + +### 2.5.0 (2022-04-08) + + * Added `callType` to IntrospectionProcessor (#1612) + * Fixed AsMonologProcessor syntax to be compatible with PHP 7.2 (#1651) + +### 2.4.0 (2022-03-14) + + * Added [`Monolog\LogRecord`](src/Monolog/LogRecord.php) interface that can be used to type-hint records like `array|\Monolog\LogRecord $record` to be forward compatible with the upcoming Monolog 3 changes + * Added `includeStacktraces` constructor params to LineFormatter & JsonFormatter (#1603) + * Added `persistent`, `timeout`, `writingTimeout`, `connectionTimeout`, `chunkSize` constructor params to SocketHandler and derivatives (#1600) + * Added `AsMonologProcessor` PHP attribute which can help autowiring / autoconfiguration of processors if frameworks / integrations decide to make use of it. This is useless when used purely with Monolog (#1637) + * Added support for keeping native BSON types as is in MongoDBFormatter (#1620) + * Added support for a `user_agent` key in WebProcessor, disabled by default but you can use it by configuring the $extraFields you want (#1613) + * Added support for username/userIcon in SlackWebhookHandler (#1617) + * Added extension points to BrowserConsoleHandler (#1593) + * Added record message/context/extra info to exceptions thrown when a StreamHandler cannot open its stream to avoid completely losing the data logged (#1630) + * Fixed error handler signature to accept a null $context which happens with internal PHP errors (#1614) + * Fixed a few setter methods not returning `self` (#1609) + * Fixed handling of records going over the max Telegram message length (#1616) + +### 2.3.5 (2021-10-01) + + * Fixed regression in StreamHandler since 2.3.3 on systems with the memory_limit set to >=20GB (#1592) + +### 2.3.4 (2021-09-15) + + * Fixed support for psr/log 3.x (#1589) + +### 2.3.3 (2021-09-14) + + * Fixed memory usage when using StreamHandler and calling stream_get_contents on the resource you passed to it (#1578, #1577) + * Fixed support for psr/log 2.x (#1587) + * Fixed some type annotations + +### 2.3.2 (2021-07-23) + + * Fixed compatibility with PHP 7.2 - 7.4 when experiencing PCRE errors (#1568) + +### 2.3.1 (2021-07-14) + + * Fixed Utils::getClass handling of anonymous classes not being fully compatible with PHP 8 (#1563) + * Fixed some `@inheritDoc` annotations having the wrong case + +### 2.3.0 (2021-07-05) + + * Added a ton of PHPStan type annotations as well as type aliases on Monolog\Logger for Record, Level and LevelName that you can import (#1557) + * Added ability to customize date format when using JsonFormatter (#1561) + * Fixed FilterHandler not calling reset on its internal handler when reset() is called on it (#1531) + * Fixed SyslogUdpHandler not setting the timezone correctly on DateTimeImmutable instances (#1540) + * Fixed StreamHandler thread safety - chunk size set to 2GB now to avoid interlacing when doing concurrent writes (#1553) + +### 2.2.0 (2020-12-14) + + * Added JSON_PARTIAL_OUTPUT_ON_ERROR to default json encoding flags, to avoid dropping entire context data or even records due to an invalid subset of it somewhere + * Added setDateFormat to NormalizerFormatter (and Line/Json formatters by extension) to allow changing this after object creation + * Added RedisPubSubHandler to log records to a Redis channel using PUBLISH + * Added support for Elastica 7, and deprecated the $type argument of ElasticaFormatter which is not in use anymore as of Elastica 7 + * Added support for millisecond write timeouts in SocketHandler, you can now pass floats to setWritingTimeout, e.g. 0.2 is 200ms + * Added support for unix sockets in SyslogUdpHandler (set $port to 0 to make the $host a unix socket) + * Added handleBatch support for TelegramBotHandler + * Added RFC5424e extended date format including milliseconds to SyslogUdpHandler + * Added support for configuring handlers with numeric level values in strings (coming from e.g. env vars) + * Fixed Wildfire/FirePHP/ChromePHP handling of unicode characters + * Fixed PHP 8 issues in SyslogUdpHandler + * Fixed internal type error when mbstring is missing + +### 2.1.1 (2020-07-23) + + * Fixed removing of json encoding options + * Fixed type hint of $level not accepting strings in SendGridHandler and OverflowHandler + * Fixed SwiftMailerHandler not accepting email templates with an empty subject + * Fixed array access on null in RavenHandler + * Fixed unique_id in WebProcessor not being disableable + +### 2.1.0 (2020-05-22) + + * Added `JSON_INVALID_UTF8_SUBSTITUTE` to default json flags, so that invalid UTF8 characters now get converted to [�](https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character) instead of being converted from ISO-8859-15 to UTF8 as it was before, which was hardly a comprehensive solution + * Added `$ignoreEmptyContextAndExtra` option to JsonFormatter to skip empty context/extra entirely from the output + * Added `$parseMode`, `$disableWebPagePreview` and `$disableNotification` options to TelegramBotHandler + * Added tentative support for PHP 8 + * NormalizerFormatter::addJsonEncodeOption and removeJsonEncodeOption are now public to allow modifying default json flags + * Fixed GitProcessor type error when there is no git repo present + * Fixed normalization of SoapFault objects containing deeply nested objects as "detail" + * Fixed support for relative paths in RotatingFileHandler + +### 2.0.2 (2019-12-20) + + * Fixed ElasticsearchHandler swallowing exceptions details when failing to index log records + * Fixed normalization of SoapFault objects containing non-strings as "detail" in LineFormatter + * Fixed formatting of resources in JsonFormatter + * Fixed RedisHandler failing to use MULTI properly when passed a proxied Redis instance (e.g. in Symfony with lazy services) + * Fixed FilterHandler triggering a notice when handleBatch was filtering all records passed to it + * Fixed Turkish locale messing up the conversion of level names to their constant values + +### 2.0.1 (2019-11-13) + + * Fixed normalization of Traversables to avoid traversing them as not all of them are rewindable + * Fixed setFormatter/getFormatter to forward to the nested handler in FilterHandler, FingersCrossedHandler, BufferHandler, OverflowHandler and SamplingHandler + * Fixed BrowserConsoleHandler formatting when using multiple styles + * Fixed normalization of exception codes to be always integers even for PDOException which have them as numeric strings + * Fixed normalization of SoapFault objects containing non-strings as "detail" + * Fixed json encoding across all handlers to always attempt recovery of non-UTF-8 strings instead of failing the whole encoding + * Fixed ChromePHPHandler to avoid sending more data than latest Chrome versions allow in headers (4KB down from 256KB). + * Fixed type error in BrowserConsoleHandler when the context array of log records was not associative. + +### 2.0.0 (2019-08-30) + + * BC Break: This is a major release, see [UPGRADE.md](UPGRADE.md) for details if you are coming from a 1.x release + * BC Break: Logger methods log/debug/info/notice/warning/error/critical/alert/emergency now have explicit void return types + * Added FallbackGroupHandler which works like the WhatFailureGroupHandler but stops dispatching log records as soon as one handler accepted it + * Fixed support for UTF-8 when cutting strings to avoid cutting a multibyte-character in half + * Fixed normalizers handling of exception backtraces to avoid serializing arguments in some cases + * Fixed date timezone handling in SyslogUdpHandler + +### 2.0.0-beta2 (2019-07-06) + + * BC Break: This is a major release, see [UPGRADE.md](UPGRADE.md) for details if you are coming from a 1.x release + * BC Break: PHP 7.2 is now the minimum required PHP version. + * BC Break: Removed SlackbotHandler, RavenHandler and HipChatHandler, see [UPGRADE.md](UPGRADE.md) for details + * Added OverflowHandler which will only flush log records to its nested handler when reaching a certain amount of logs (i.e. only pass through when things go really bad) + * Added TelegramBotHandler to log records to a [Telegram](https://core.telegram.org/bots/api) bot account + * Added support for JsonSerializable when normalizing exceptions + * Added support for RFC3164 (outdated BSD syslog protocol) to SyslogUdpHandler + * Added SoapFault details to formatted exceptions + * Fixed DeduplicationHandler silently failing to start when file could not be opened + * Fixed issue in GroupHandler and WhatFailureGroupHandler where setting multiple processors would duplicate records + * Fixed GelfFormatter losing some data when one attachment was too long + * Fixed issue in SignalHandler restarting syscalls functionality + * Improved performance of LogglyHandler when sending multiple logs in a single request + +### 2.0.0-beta1 (2018-12-08) + + * BC Break: This is a major release, see [UPGRADE.md](UPGRADE.md) for details if you are coming from a 1.x release + * BC Break: PHP 7.1 is now the minimum required PHP version. + * BC Break: Quite a few interface changes, only relevant if you implemented your own handlers/processors/formatters + * BC Break: Removed non-PSR-3 methods to add records, all the `add*` (e.g. `addWarning`) methods as well as `emerg`, `crit`, `err` and `warn` + * BC Break: The record timezone is now set per Logger instance and not statically anymore + * BC Break: There is no more default handler configured on empty Logger instances + * BC Break: ElasticSearchHandler renamed to ElasticaHandler + * BC Break: Various handler-specific breaks, see [UPGRADE.md](UPGRADE.md) for details + * Added scalar type hints and return hints in all the places it was possible. Switched strict_types on for more reliability. + * Added DateTimeImmutable support, all record datetime are now immutable, and will toString/json serialize with the correct date format, including microseconds (unless disabled) + * Added timezone and microseconds to the default date format + * Added SendGridHandler to use the SendGrid API to send emails + * Added LogmaticHandler to use the Logmatic.io API to store log records + * Added SqsHandler to send log records to an AWS SQS queue + * Added ElasticsearchHandler to send records via the official ES library. Elastica users should now use ElasticaHandler instead of ElasticSearchHandler + * Added NoopHandler which is similar to the NullHandle but does not prevent the bubbling of log records to handlers further down the configuration, useful for temporarily disabling a handler in configuration files + * Added ProcessHandler to write log output to the STDIN of a given process + * Added HostnameProcessor that adds the machine's hostname to log records + * Added a `$dateFormat` option to the PsrLogMessageProcessor which lets you format DateTime instances nicely + * Added support for the PHP 7.x `mongodb` extension in the MongoDBHandler + * Fixed many minor issues in various handlers, and probably added a few regressions too + +### 1.26.1 (2021-05-28) + + * Fixed PHP 8.1 deprecation warning + +### 1.26.0 (2020-12-14) + + * Added $dateFormat and $removeUsedContextFields arguments to PsrLogMessageProcessor (backport from 2.x) + +### 1.25.5 (2020-07-23) + + * Fixed array access on null in RavenHandler + * Fixed unique_id in WebProcessor not being disableable + +### 1.25.4 (2020-05-22) + + * Fixed GitProcessor type error when there is no git repo present + * Fixed normalization of SoapFault objects containing deeply nested objects as "detail" + * Fixed support for relative paths in RotatingFileHandler + +### 1.25.3 (2019-12-20) + + * Fixed formatting of resources in JsonFormatter + * Fixed RedisHandler failing to use MULTI properly when passed a proxied Redis instance (e.g. in Symfony with lazy services) + * Fixed FilterHandler triggering a notice when handleBatch was filtering all records passed to it + * Fixed Turkish locale messing up the conversion of level names to their constant values + +### 1.25.2 (2019-11-13) + + * Fixed normalization of Traversables to avoid traversing them as not all of them are rewindable + * Fixed setFormatter/getFormatter to forward to the nested handler in FilterHandler, FingersCrossedHandler, BufferHandler and SamplingHandler + * Fixed BrowserConsoleHandler formatting when using multiple styles + * Fixed normalization of exception codes to be always integers even for PDOException which have them as numeric strings + * Fixed normalization of SoapFault objects containing non-strings as "detail" + * Fixed json encoding across all handlers to always attempt recovery of non-UTF-8 strings instead of failing the whole encoding + +### 1.25.1 (2019-09-06) + + * Fixed forward-compatible interfaces to be compatible with Monolog 1.x too. + +### 1.25.0 (2019-09-06) + + * Deprecated SlackbotHandler, use SlackWebhookHandler or SlackHandler instead + * Deprecated RavenHandler, use sentry/sentry 2.x and their Sentry\Monolog\Handler instead + * Deprecated HipChatHandler, migrate to Slack and use SlackWebhookHandler or SlackHandler instead + * Added forward-compatible interfaces and traits FormattableHandlerInterface, FormattableHandlerTrait, ProcessableHandlerInterface, ProcessableHandlerTrait. If you use modern PHP and want to make code compatible with Monolog 1 and 2 this can help. You will have to require at least Monolog 1.25 though. + * Added support for RFC3164 (outdated BSD syslog protocol) to SyslogUdpHandler + * Fixed issue in GroupHandler and WhatFailureGroupHandler where setting multiple processors would duplicate records + * Fixed issue in SignalHandler restarting syscalls functionality + * Fixed normalizers handling of exception backtraces to avoid serializing arguments in some cases + * Fixed ZendMonitorHandler to work with the latest Zend Server versions + * Fixed ChromePHPHandler to avoid sending more data than latest Chrome versions allow in headers (4KB down from 256KB). + +### 1.24.0 (2018-11-05) + + * BC Notice: If you are extending any of the Monolog's Formatters' `normalize` method, make sure you add the new `$depth = 0` argument to your function signature to avoid strict PHP warnings. + * Added a `ResettableInterface` in order to reset/reset/clear/flush handlers and processors + * Added a `ProcessorInterface` as an optional way to label a class as being a processor (mostly useful for autowiring dependency containers) + * Added a way to log signals being received using Monolog\SignalHandler + * Added ability to customize error handling at the Logger level using Logger::setExceptionHandler + * Added InsightOpsHandler to migrate users of the LogEntriesHandler + * Added protection to NormalizerFormatter against circular and very deep structures, it now stops normalizing at a depth of 9 + * Added capture of stack traces to ErrorHandler when logging PHP errors + * Added RavenHandler support for a `contexts` context or extra key to forward that to Sentry's contexts + * Added forwarding of context info to FluentdFormatter + * Added SocketHandler::setChunkSize to override the default chunk size in case you must send large log lines to rsyslog for example + * Added ability to extend/override BrowserConsoleHandler + * Added SlackWebhookHandler::getWebhookUrl and SlackHandler::getToken to enable class extensibility + * Added SwiftMailerHandler::getSubjectFormatter to enable class extensibility + * Dropped official support for HHVM in test builds + * Fixed normalization of exception traces when call_user_func is used to avoid serializing objects and the data they contain + * Fixed naming of fields in Slack handler, all field names are now capitalized in all cases + * Fixed HipChatHandler bug where slack dropped messages randomly + * Fixed normalization of objects in Slack handlers + * Fixed support for PHP7's Throwable in NewRelicHandler + * Fixed race bug when StreamHandler sometimes incorrectly reported it failed to create a directory + * Fixed table row styling issues in HtmlFormatter + * Fixed RavenHandler dropping the message when logging exception + * Fixed WhatFailureGroupHandler skipping processors when using handleBatch + and implement it where possible + * Fixed display of anonymous class names + +### 1.23.0 (2017-06-19) + + * Improved SyslogUdpHandler's support for RFC5424 and added optional `$ident` argument + * Fixed GelfHandler truncation to be per field and not per message + * Fixed compatibility issue with PHP <5.3.6 + * Fixed support for headless Chrome in ChromePHPHandler + * Fixed support for latest Aws SDK in DynamoDbHandler + * Fixed support for SwiftMailer 6.0+ in SwiftMailerHandler + +### 1.22.1 (2017-03-13) + + * Fixed lots of minor issues in the new Slack integrations + * Fixed support for allowInlineLineBreaks in LineFormatter when formatting exception backtraces + +### 1.22.0 (2016-11-26) + + * Added SlackbotHandler and SlackWebhookHandler to set up Slack integration more easily + * Added MercurialProcessor to add mercurial revision and branch names to log records + * Added support for AWS SDK v3 in DynamoDbHandler + * Fixed fatal errors occurring when normalizing generators that have been fully consumed + * Fixed RollbarHandler to include a level (rollbar level), monolog_level (original name), channel and datetime (unix) + * Fixed RollbarHandler not flushing records automatically, calling close() explicitly is not necessary anymore + * Fixed SyslogUdpHandler to avoid sending empty frames + * Fixed a few PHP 7.0 and 7.1 compatibility issues + +### 1.21.0 (2016-07-29) + + * Break: Reverted the addition of $context when the ErrorHandler handles regular php errors from 1.20.0 as it was causing issues + * Added support for more formats in RotatingFileHandler::setFilenameFormat as long as they have Y, m and d in order + * Added ability to format the main line of text the SlackHandler sends by explicitly setting a formatter on the handler + * Added information about SoapFault instances in NormalizerFormatter + * Added $handleOnlyReportedErrors option on ErrorHandler::registerErrorHandler (default true) to allow logging of all errors no matter the error_reporting level + +### 1.20.0 (2016-07-02) + + * Added FingersCrossedHandler::activate() to manually trigger the handler regardless of the activation policy + * Added StreamHandler::getUrl to retrieve the stream's URL + * Added ability to override addRow/addTitle in HtmlFormatter + * Added the $context to context information when the ErrorHandler handles a regular php error + * Deprecated RotatingFileHandler::setFilenameFormat to only support 3 formats: Y, Y-m and Y-m-d + * Fixed WhatFailureGroupHandler to work with PHP7 throwables + * Fixed a few minor bugs + +### 1.19.0 (2016-04-12) + + * Break: StreamHandler will not close streams automatically that it does not own. If you pass in a stream (not a path/url), then it will not close it for you. You can retrieve those using getStream() if needed + * Added DeduplicationHandler to remove duplicate records from notifications across multiple requests, useful for email or other notifications on errors + * Added ability to use `%message%` and other LineFormatter replacements in the subject line of emails sent with NativeMailHandler and SwiftMailerHandler + * Fixed HipChatHandler handling of long messages + +### 1.18.2 (2016-04-02) + + * Fixed ElasticaFormatter to use more precise dates + * Fixed GelfMessageFormatter sending too long messages + +### 1.18.1 (2016-03-13) + + * Fixed SlackHandler bug where slack dropped messages randomly + * Fixed RedisHandler issue when using with the PHPRedis extension + * Fixed AmqpHandler content-type being incorrectly set when using with the AMQP extension + * Fixed BrowserConsoleHandler regression + +### 1.18.0 (2016-03-01) + + * Added optional reduction of timestamp precision via `Logger->useMicrosecondTimestamps(false)`, disabling it gets you a bit of performance boost but reduces the precision to the second instead of microsecond + * Added possibility to skip some extra stack frames in IntrospectionProcessor if you have some library wrapping Monolog that is always adding frames + * Added `Logger->withName` to clone a logger (keeping all handlers) with a new name + * Added FluentdFormatter for the Fluentd unix socket protocol + * Added HandlerWrapper base class to ease the creation of handler wrappers, just extend it and override as needed + * Added support for replacing context sub-keys using `%context.*%` in LineFormatter + * Added support for `payload` context value in RollbarHandler + * Added setRelease to RavenHandler to describe the application version, sent with every log + * Added support for `fingerprint` context value in RavenHandler + * Fixed JSON encoding errors that would gobble up the whole log record, we now handle those more gracefully by dropping chars as needed + * Fixed write timeouts in SocketHandler and derivatives, set to 10sec by default, lower it with `setWritingTimeout()` + * Fixed PHP7 compatibility with regard to Exception/Throwable handling in a few places + +### 1.17.2 (2015-10-14) + + * Fixed ErrorHandler compatibility with non-Monolog PSR-3 loggers + * Fixed SlackHandler handling to use slack functionalities better + * Fixed SwiftMailerHandler bug when sending multiple emails they all had the same id + * Fixed 5.3 compatibility regression + +### 1.17.1 (2015-08-31) + + * Fixed RollbarHandler triggering PHP notices + +### 1.17.0 (2015-08-30) + + * Added support for `checksum` and `release` context/extra values in RavenHandler + * Added better support for exceptions in RollbarHandler + * Added UidProcessor::getUid + * Added support for showing the resource type in NormalizedFormatter + * Fixed IntrospectionProcessor triggering PHP notices + +### 1.16.0 (2015-08-09) + + * Added IFTTTHandler to notify ifttt.com triggers + * Added Logger::setHandlers() to allow setting/replacing all handlers + * Added $capSize in RedisHandler to cap the log size + * Fixed StreamHandler creation of directory to only trigger when the first log write happens + * Fixed bug in the handling of curl failures + * Fixed duplicate logging of fatal errors when both error and fatal error handlers are registered in monolog's ErrorHandler + * Fixed missing fatal errors records with handlers that need to be closed to flush log records + * Fixed TagProcessor::addTags support for associative arrays + +### 1.15.0 (2015-07-12) + + * Added addTags and setTags methods to change a TagProcessor + * Added automatic creation of directories if they are missing for a StreamHandler to open a log file + * Added retry functionality to Loggly, Cube and Mandrill handlers so they retry up to 5 times in case of network failure + * Fixed process exit code being incorrectly reset to 0 if ErrorHandler::registerExceptionHandler was used + * Fixed HTML/JS escaping in BrowserConsoleHandler + * Fixed JSON encoding errors being silently suppressed (PHP 5.5+ only) + +### 1.14.0 (2015-06-19) + + * Added PHPConsoleHandler to send record to Chrome's PHP Console extension and library + * Added support for objects implementing __toString in the NormalizerFormatter + * Added support for HipChat's v2 API in HipChatHandler + * Added Logger::setTimezone() to initialize the timezone monolog should use in case date.timezone isn't correct for your app + * Added an option to send formatted message instead of the raw record on PushoverHandler via ->useFormattedMessage(true) + * Fixed curl errors being silently suppressed + +### 1.13.1 (2015-03-09) + + * Fixed regression in HipChat requiring a new token to be created + +### 1.13.0 (2015-03-05) + + * Added Registry::hasLogger to check for the presence of a logger instance + * Added context.user support to RavenHandler + * Added HipChat API v2 support in the HipChatHandler + * Added NativeMailerHandler::addParameter to pass params to the mail() process + * Added context data to SlackHandler when $includeContextAndExtra is true + * Added ability to customize the Swift_Message per-email in SwiftMailerHandler + * Fixed SwiftMailerHandler to lazily create message instances if a callback is provided + * Fixed serialization of INF and NaN values in Normalizer and LineFormatter + +### 1.12.0 (2014-12-29) + + * Break: HandlerInterface::isHandling now receives a partial record containing only a level key. This was always the intent and does not break any Monolog handler but is strictly speaking a BC break and you should check if you relied on any other field in your own handlers. + * Added PsrHandler to forward records to another PSR-3 logger + * Added SamplingHandler to wrap around a handler and include only every Nth record + * Added MongoDBFormatter to support better storage with MongoDBHandler (it must be enabled manually for now) + * Added exception codes in the output of most formatters + * Added LineFormatter::includeStacktraces to enable exception stack traces in logs (uses more than one line) + * Added $useShortAttachment to SlackHandler to minify attachment size and $includeExtra to append extra data + * Added $host to HipChatHandler for users of private instances + * Added $transactionName to NewRelicHandler and support for a transaction_name context value + * Fixed MandrillHandler to avoid outputting API call responses + * Fixed some non-standard behaviors in SyslogUdpHandler + +### 1.11.0 (2014-09-30) + + * Break: The NewRelicHandler extra and context data are now prefixed with extra_ and context_ to avoid clashes. Watch out if you have scripts reading those from the API and rely on names + * Added WhatFailureGroupHandler to suppress any exception coming from the wrapped handlers and avoid chain failures if a logging service fails + * Added MandrillHandler to send emails via the Mandrillapp.com API + * Added SlackHandler to log records to a Slack.com account + * Added FleepHookHandler to log records to a Fleep.io account + * Added LogglyHandler::addTag to allow adding tags to an existing handler + * Added $ignoreEmptyContextAndExtra to LineFormatter to avoid empty [] at the end + * Added $useLocking to StreamHandler and RotatingFileHandler to enable flock() while writing + * Added support for PhpAmqpLib in the AmqpHandler + * Added FingersCrossedHandler::clear and BufferHandler::clear to reset them between batches in long running jobs + * Added support for adding extra fields from $_SERVER in the WebProcessor + * Fixed support for non-string values in PrsLogMessageProcessor + * Fixed SwiftMailer messages being sent with the wrong date in long running scripts + * Fixed minor PHP 5.6 compatibility issues + * Fixed BufferHandler::close being called twice + +### 1.10.0 (2014-06-04) + + * Added Logger::getHandlers() and Logger::getProcessors() methods + * Added $passthruLevel argument to FingersCrossedHandler to let it always pass some records through even if the trigger level is not reached + * Added support for extra data in NewRelicHandler + * Added $expandNewlines flag to the ErrorLogHandler to create multiple log entries when a message has multiple lines + +### 1.9.1 (2014-04-24) + + * Fixed regression in RotatingFileHandler file permissions + * Fixed initialization of the BufferHandler to make sure it gets flushed after receiving records + * Fixed ChromePHPHandler and FirePHPHandler's activation strategies to be more conservative + +### 1.9.0 (2014-04-20) + + * Added LogEntriesHandler to send logs to a LogEntries account + * Added $filePermissions to tweak file mode on StreamHandler and RotatingFileHandler + * Added $useFormatting flag to MemoryProcessor to make it send raw data in bytes + * Added support for table formatting in FirePHPHandler via the table context key + * Added a TagProcessor to add tags to records, and support for tags in RavenHandler + * Added $appendNewline flag to the JsonFormatter to enable using it when logging to files + * Added sound support to the PushoverHandler + * Fixed multi-threading support in StreamHandler + * Fixed empty headers issue when ChromePHPHandler received no records + * Fixed default format of the ErrorLogHandler + +### 1.8.0 (2014-03-23) + + * Break: the LineFormatter now strips newlines by default because this was a bug, set $allowInlineLineBreaks to true if you need them + * Added BrowserConsoleHandler to send logs to any browser's console via console.log() injection in the output + * Added FilterHandler to filter records and only allow those of a given list of levels through to the wrapped handler + * Added FlowdockHandler to send logs to a Flowdock account + * Added RollbarHandler to send logs to a Rollbar account + * Added HtmlFormatter to send prettier log emails with colors for each log level + * Added GitProcessor to add the current branch/commit to extra record data + * Added a Monolog\Registry class to allow easier global access to pre-configured loggers + * Added support for the new official graylog2/gelf-php lib for GelfHandler, upgrade if you can by replacing the mlehner/gelf-php requirement + * Added support for HHVM + * Added support for Loggly batch uploads + * Added support for tweaking the content type and encoding in NativeMailerHandler + * Added $skipClassesPartials to tweak the ignored classes in the IntrospectionProcessor + * Fixed batch request support in GelfHandler + +### 1.7.0 (2013-11-14) + + * Added ElasticSearchHandler to send logs to an Elastic Search server + * Added DynamoDbHandler and ScalarFormatter to send logs to Amazon's Dynamo DB + * Added SyslogUdpHandler to send logs to a remote syslogd server + * Added LogglyHandler to send logs to a Loggly account + * Added $level to IntrospectionProcessor so it only adds backtraces when needed + * Added $version to LogstashFormatter to allow using the new v1 Logstash format + * Added $appName to NewRelicHandler + * Added configuration of Pushover notification retries/expiry + * Added $maxColumnWidth to NativeMailerHandler to change the 70 chars default + * Added chainability to most setters for all handlers + * Fixed RavenHandler batch processing so it takes the message from the record with highest priority + * Fixed HipChatHandler batch processing so it sends all messages at once + * Fixed issues with eAccelerator + * Fixed and improved many small things + +### 1.6.0 (2013-07-29) + + * Added HipChatHandler to send logs to a HipChat chat room + * Added ErrorLogHandler to send logs to PHP's error_log function + * Added NewRelicHandler to send logs to NewRelic's service + * Added Monolog\ErrorHandler helper class to register a Logger as exception/error/fatal handler + * Added ChannelLevelActivationStrategy for the FingersCrossedHandler to customize levels by channel + * Added stack traces output when normalizing exceptions (json output & co) + * Added Monolog\Logger::API constant (currently 1) + * Added support for ChromePHP's v4.0 extension + * Added support for message priorities in PushoverHandler, see $highPriorityLevel and $emergencyLevel + * Added support for sending messages to multiple users at once with the PushoverHandler + * Fixed RavenHandler's support for batch sending of messages (when behind a Buffer or FingersCrossedHandler) + * Fixed normalization of Traversables with very large data sets, only the first 1000 items are shown now + * Fixed issue in RotatingFileHandler when an open_basedir restriction is active + * Fixed minor issues in RavenHandler and bumped the API to Raven 0.5.0 + * Fixed SyslogHandler issue when many were used concurrently with different facilities + +### 1.5.0 (2013-04-23) + + * Added ProcessIdProcessor to inject the PID in log records + * Added UidProcessor to inject a unique identifier to all log records of one request/run + * Added support for previous exceptions in the LineFormatter exception serialization + * Added Monolog\Logger::getLevels() to get all available levels + * Fixed ChromePHPHandler so it avoids sending headers larger than Chrome can handle + +### 1.4.1 (2013-04-01) + + * Fixed exception formatting in the LineFormatter to be more minimalistic + * Fixed RavenHandler's handling of context/extra data, requires Raven client >0.1.0 + * Fixed log rotation in RotatingFileHandler to work with long running scripts spanning multiple days + * Fixed WebProcessor array access so it checks for data presence + * Fixed Buffer, Group and FingersCrossed handlers to make use of their processors + +### 1.4.0 (2013-02-13) + + * Added RedisHandler to log to Redis via the Predis library or the phpredis extension + * Added ZendMonitorHandler to log to the Zend Server monitor + * Added the possibility to pass arrays of handlers and processors directly in the Logger constructor + * Added `$useSSL` option to the PushoverHandler which is enabled by default + * Fixed ChromePHPHandler and FirePHPHandler issue when multiple instances are used simultaneously + * Fixed header injection capability in the NativeMailHandler + +### 1.3.1 (2013-01-11) + + * Fixed LogstashFormatter to be usable with stream handlers + * Fixed GelfMessageFormatter levels on Windows + +### 1.3.0 (2013-01-08) + + * Added PSR-3 compliance, the `Monolog\Logger` class is now an instance of `Psr\Log\LoggerInterface` + * Added PsrLogMessageProcessor that you can selectively enable for full PSR-3 compliance + * Added LogstashFormatter (combine with SocketHandler or StreamHandler to send logs to Logstash) + * Added PushoverHandler to send mobile notifications + * Added CouchDBHandler and DoctrineCouchDBHandler + * Added RavenHandler to send data to Sentry servers + * Added support for the new MongoClient class in MongoDBHandler + * Added microsecond precision to log records' timestamps + * Added `$flushOnOverflow` param to BufferHandler to flush by batches instead of losing + the oldest entries + * Fixed normalization of objects with cyclic references + +### 1.2.1 (2012-08-29) + + * Added new $logopts arg to SyslogHandler to provide custom openlog options + * Fixed fatal error in SyslogHandler + +### 1.2.0 (2012-08-18) + + * Added AmqpHandler (for use with AMQP servers) + * Added CubeHandler + * Added NativeMailerHandler::addHeader() to send custom headers in mails + * Added the possibility to specify more than one recipient in NativeMailerHandler + * Added the possibility to specify float timeouts in SocketHandler + * Added NOTICE and EMERGENCY levels to conform with RFC 5424 + * Fixed the log records to use the php default timezone instead of UTC + * Fixed BufferHandler not being flushed properly on PHP fatal errors + * Fixed normalization of exotic resource types + * Fixed the default format of the SyslogHandler to avoid duplicating datetimes in syslog + +### 1.1.0 (2012-04-23) + + * Added Monolog\Logger::isHandling() to check if a handler will + handle the given log level + * Added ChromePHPHandler + * Added MongoDBHandler + * Added GelfHandler (for use with Graylog2 servers) + * Added SocketHandler (for use with syslog-ng for example) + * Added NormalizerFormatter + * Added the possibility to change the activation strategy of the FingersCrossedHandler + * Added possibility to show microseconds in logs + * Added `server` and `referer` to WebProcessor output + +### 1.0.2 (2011-10-24) + + * Fixed bug in IE with large response headers and FirePHPHandler + +### 1.0.1 (2011-08-25) + + * Added MemoryPeakUsageProcessor and MemoryUsageProcessor + * Added Monolog\Logger::getName() to get a logger's channel name + +### 1.0.0 (2011-07-06) + + * Added IntrospectionProcessor to get info from where the logger was called + * Fixed WebProcessor in CLI + +### 1.0.0-RC1 (2011-07-01) + + * Initial release diff --git a/vendor/monolog/monolog/LICENSE b/vendor/monolog/monolog/LICENSE new file mode 100644 index 0000000..aa2a042 --- /dev/null +++ b/vendor/monolog/monolog/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2020 Jordi Boggiano + +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/monolog/monolog/README.md b/vendor/monolog/monolog/README.md new file mode 100644 index 0000000..bfcae0c --- /dev/null +++ b/vendor/monolog/monolog/README.md @@ -0,0 +1,112 @@ +# Monolog - Logging for PHP [![Continuous Integration](https://github.com/Seldaek/monolog/workflows/Continuous%20Integration/badge.svg?branch=main)](https://github.com/Seldaek/monolog/actions) + +[![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) +[![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) + + +Monolog sends your logs to files, sockets, inboxes, databases and various +web services. See the complete list of handlers below. Special handlers +allow you to build advanced logging strategies. + +This library implements the [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +interface that you can type-hint against in your own libraries to keep +a maximum of interoperability. You can also use it in your applications to +make sure you can always use another compatible logger at a later time. +As of 1.11.0 Monolog public APIs will also accept PSR-3 log levels. +Internally Monolog still uses its own level scheme since it predates PSR-3. + +## Installation + +Install the latest version with + +```bash +$ composer require monolog/monolog +``` + +## Basic Usage + +```php +pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING)); + +// add records to the log +$log->warning('Foo'); +$log->error('Bar'); +``` + +## Documentation + +- [Usage Instructions](doc/01-usage.md) +- [Handlers, Formatters and Processors](doc/02-handlers-formatters-processors.md) +- [Utility Classes](doc/03-utilities.md) +- [Extending Monolog](doc/04-extending.md) +- [Log Record Structure](doc/message-structure.md) + +## Support Monolog Financially + +Get supported Monolog and help fund the project with the [Tidelift Subscription](https://tidelift.com/subscription/pkg/packagist-monolog-monolog?utm_source=packagist-monolog-monolog&utm_medium=referral&utm_campaign=enterprise) or via [GitHub sponsorship](https://github.com/sponsors/Seldaek). + +Tidelift delivers commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. + +## Third Party Packages + +Third party handlers, formatters and processors are +[listed in the wiki](https://github.com/Seldaek/monolog/wiki/Third-Party-Packages). You +can also add your own there if you publish one. + +## About + +### Requirements + +- Monolog `^2.0` works with PHP 7.2 or above, use Monolog `^1.25` for PHP 5.3+ support. + +### Support + +Monolog 1.x support is somewhat limited at this point and only important fixes will be done. You should migrate to Monolog 2 where possible to benefit from all the latest features and fixes. + +### Submitting bugs and feature requests + +Bugs and feature request are tracked on [GitHub](https://github.com/Seldaek/monolog/issues) + +### Framework Integrations + +- Frameworks and libraries using [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) + can be used very easily with Monolog since it implements the interface. +- [Symfony](http://symfony.com) comes out of the box with Monolog. +- [Laravel](http://laravel.com/) comes out of the box with Monolog. +- [Lumen](http://lumen.laravel.com/) comes out of the box with Monolog. +- [PPI](https://github.com/ppi/framework) comes out of the box with Monolog. +- [CakePHP](http://cakephp.org/) is usable with Monolog via the [cakephp-monolog](https://github.com/jadb/cakephp-monolog) plugin. +- [Slim](http://www.slimframework.com/) is usable with Monolog via the [Slim-Monolog](https://github.com/Flynsarmy/Slim-Monolog) log writer. +- [XOOPS 2.6](http://xoops.org/) comes out of the box with Monolog. +- [Aura.Web_Project](https://github.com/auraphp/Aura.Web_Project) comes out of the box with Monolog. +- [Nette Framework](http://nette.org/en/) is usable with Monolog via the [contributte/monolog](https://github.com/contributte/monolog) or [orisai/nette-monolog](https://github.com/orisai/nette-monolog) extensions. +- [Proton Micro Framework](https://github.com/alexbilbie/Proton) comes out of the box with Monolog. +- [FuelPHP](http://fuelphp.com/) comes out of the box with Monolog. +- [Equip Framework](https://github.com/equip/framework) comes out of the box with Monolog. +- [Yii 2](http://www.yiiframework.com/) is usable with Monolog via the [yii2-monolog](https://github.com/merorafael/yii2-monolog) or [yii2-psr-log-target](https://github.com/samdark/yii2-psr-log-target) plugins. +- [Hawkbit Micro Framework](https://github.com/HawkBitPhp/hawkbit) comes out of the box with Monolog. +- [SilverStripe 4](https://www.silverstripe.org/) comes out of the box with Monolog. +- [Drupal](https://www.drupal.org/) is usable with Monolog via the [monolog](https://www.drupal.org/project/monolog) module. +- [Aimeos ecommerce framework](https://aimeos.org/) is usable with Monolog via the [ai-monolog](https://github.com/aimeos/ai-monolog) extension. +- [Magento](https://magento.com/) comes out of the box with Monolog. + +### Author + +Jordi Boggiano - -
+See also the list of [contributors](https://github.com/Seldaek/monolog/contributors) who participated in this project. + +### License + +Monolog is licensed under the MIT License - see the [LICENSE](LICENSE) file for details + +### Acknowledgements + +This library is heavily inspired by Python's [Logbook](https://logbook.readthedocs.io/en/stable/) +library, although most concepts have been adjusted to fit to the PHP world. diff --git a/vendor/monolog/monolog/UPGRADE.md b/vendor/monolog/monolog/UPGRADE.md new file mode 100644 index 0000000..84e15e6 --- /dev/null +++ b/vendor/monolog/monolog/UPGRADE.md @@ -0,0 +1,72 @@ +### 2.0.0 + +- `Monolog\Logger::API` can be used to distinguish between a Monolog `1` and `2` + install of Monolog when writing integration code. + +- Removed non-PSR-3 methods to add records, all the `add*` (e.g. `addWarning`) + methods as well as `emerg`, `crit`, `err` and `warn`. + +- DateTime are now formatted with a timezone and microseconds (unless disabled). + Various formatters and log output might be affected, which may mess with log parsing + in some cases. + +- The `datetime` in every record array is now a DateTimeImmutable, not that you + should have been modifying these anyway. + +- The timezone is now set per Logger instance and not statically, either + via ->setTimezone or passed in the constructor. Calls to Logger::setTimezone + should be converted. + +- `HandlerInterface` has been split off and two new interfaces now exist for + more granular controls: `ProcessableHandlerInterface` and + `FormattableHandlerInterface`. Handlers not extending `AbstractHandler` + should make sure to implement the relevant interfaces. + +- `HandlerInterface` now requires the `close` method to be implemented. This + only impacts you if you implement the interface yourself, but you can extend + the new `Monolog\Handler\Handler` base class too. + +- There is no more default handler configured on empty Logger instances, if + you were relying on that you will not get any output anymore, make sure to + configure the handler you need. + +#### LogglyFormatter + +- The records' `datetime` is not sent anymore. Only `timestamp` is sent to Loggly. + +#### AmqpHandler + +- Log levels are not shortened to 4 characters anymore. e.g. a warning record + will be sent using the `warning.channel` routing key instead of `warn.channel` + as in 1.x. +- The exchange name does not default to 'log' anymore, and it is completely ignored + now for the AMQP extension users. Only PHPAmqpLib uses it if provided. + +#### RotatingFileHandler + +- The file name format must now contain `{date}` and the date format must be set + to one of the predefined FILE_PER_* constants to avoid issues with file rotation. + See `setFilenameFormat`. + +#### LogstashFormatter + +- Removed Logstash V0 support +- Context/extra prefix has been removed in favor of letting users configure the exact key being sent +- Context/extra data are now sent as an object instead of single keys + +#### HipChatHandler + +- Removed deprecated HipChat handler, migrate to Slack and use SlackWebhookHandler or SlackHandler instead + +#### SlackbotHandler + +- Removed deprecated SlackbotHandler handler, use SlackWebhookHandler or SlackHandler instead + +#### RavenHandler + +- Removed deprecated RavenHandler handler, use sentry/sentry 2.x and their Sentry\Monolog\Handler instead + +#### ElasticSearchHandler + +- As support for the official Elasticsearch library was added, the former ElasticSearchHandler has been + renamed to ElasticaHandler and the new one added as ElasticsearchHandler. diff --git a/vendor/monolog/monolog/composer.json b/vendor/monolog/monolog/composer.json new file mode 100644 index 0000000..a1f08a2 --- /dev/null +++ b/vendor/monolog/monolog/composer.json @@ -0,0 +1,81 @@ +{ + "name": "monolog/monolog", + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "keywords": ["log", "logging", "psr-3"], + "homepage": "https://github.com/Seldaek/monolog", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "require": { + "php": ">=7.2", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "ext-json": "*", + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "graylog2/gelf-php": "^1.4.2 || ^2@dev", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpspec/prophecy": "^1.15", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.38 || ^9.6.19", + "predis/predis": "^1.1 || ^2.0", + "rollbar/rollbar": "^1.3 || ^2 || ^3", + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-openssl": "Required to send log messages using SSL" + }, + "autoload": { + "psr-4": {"Monolog\\": "src/Monolog"} + }, + "autoload-dev": { + "psr-4": {"Monolog\\": "tests/Monolog"} + }, + "provide": { + "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + }, + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "scripts": { + "test": "@php vendor/bin/phpunit", + "phpstan": "@php vendor/bin/phpstan analyse" + }, + "config": { + "lock": false, + "sort-packages": true, + "platform-check": false, + "allow-plugins": { + "composer/package-versions-deprecated": true + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php b/vendor/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php new file mode 100644 index 0000000..188bbb0 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Attribute; + +/** + * A reusable attribute to help configure a class or a method as a processor. + * + * Using it offers no guarantee: it needs to be leveraged by a Monolog third-party consumer. + * + * Using it with the Monolog library only has no effect at all: processors should still be turned into a callable if + * needed and manually pushed to the loggers and to the processable handlers. + */ +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +class AsMonologProcessor +{ + /** @var string|null */ + public $channel = null; + /** @var string|null */ + public $handler = null; + /** @var string|null */ + public $method = null; + + /** + * @param string|null $channel The logging channel the processor should be pushed to. + * @param string|null $handler The handler the processor should be pushed to. + * @param string|null $method The method that processes the records (if the attribute is used at the class level). + */ + public function __construct( + ?string $channel = null, + ?string $handler = null, + ?string $method = null + ) { + $this->channel = $channel; + $this->handler = $handler; + $this->method = $method; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/DateTimeImmutable.php b/vendor/monolog/monolog/src/Monolog/DateTimeImmutable.php new file mode 100644 index 0000000..789f9bf --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/DateTimeImmutable.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use DateTimeZone; + +/** + * Overrides default json encoding of date time objects + * + * @author Menno Holtkamp + * @author Jordi Boggiano + */ +class DateTimeImmutable extends \DateTimeImmutable implements \JsonSerializable +{ + /** + * @var bool + */ + private $useMicroseconds; + + public function __construct(bool $useMicroseconds, ?DateTimeZone $timezone = null) + { + $this->useMicroseconds = $useMicroseconds; + + // if you like to use a custom time to pass to Logger::addRecord directly, + // call modify() or setTimestamp() on this instance to change the date after creating it + parent::__construct('now', $timezone); + } + + public function jsonSerialize(): string + { + if ($this->useMicroseconds) { + return $this->format('Y-m-d\TH:i:s.uP'); + } + + return $this->format('Y-m-d\TH:i:sP'); + } + + public function __toString(): string + { + return $this->jsonSerialize(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/ErrorHandler.php b/vendor/monolog/monolog/src/Monolog/ErrorHandler.php new file mode 100644 index 0000000..1406d34 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/ErrorHandler.php @@ -0,0 +1,307 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; + +/** + * Monolog error handler + * + * A facility to enable logging of runtime errors, exceptions and fatal errors. + * + * Quick setup: ErrorHandler::register($logger); + * + * @author Jordi Boggiano + */ +class ErrorHandler +{ + /** @var LoggerInterface */ + private $logger; + + /** @var ?callable */ + private $previousExceptionHandler = null; + /** @var array an array of class name to LogLevel::* constant mapping */ + private $uncaughtExceptionLevelMap = []; + + /** @var callable|true|null */ + private $previousErrorHandler = null; + /** @var array an array of E_* constant to LogLevel::* constant mapping */ + private $errorLevelMap = []; + /** @var bool */ + private $handleOnlyReportedErrors = true; + + /** @var bool */ + private $hasFatalErrorHandler = false; + /** @var LogLevel::* */ + private $fatalLevel = LogLevel::ALERT; + /** @var ?string */ + private $reservedMemory = null; + /** @var ?array{type: int, message: string, file: string, line: int, trace: mixed} */ + private $lastFatalData = null; + /** @var int[] */ + private static $fatalErrors = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR]; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * Registers a new ErrorHandler for a given Logger + * + * By default it will handle errors, exceptions and fatal errors + * + * @param LoggerInterface $logger + * @param array|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling + * @param array|false $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling + * @param LogLevel::*|null|false $fatalLevel a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling + * @return ErrorHandler + */ + public static function register(LoggerInterface $logger, $errorLevelMap = [], $exceptionLevelMap = [], $fatalLevel = null): self + { + /** @phpstan-ignore-next-line */ + $handler = new static($logger); + if ($errorLevelMap !== false) { + $handler->registerErrorHandler($errorLevelMap); + } + if ($exceptionLevelMap !== false) { + $handler->registerExceptionHandler($exceptionLevelMap); + } + if ($fatalLevel !== false) { + $handler->registerFatalHandler($fatalLevel); + } + + return $handler; + } + + /** + * @param array $levelMap an array of class name to LogLevel::* constant mapping + * @return $this + */ + public function registerExceptionHandler(array $levelMap = [], bool $callPrevious = true): self + { + $prev = set_exception_handler(function (\Throwable $e): void { + $this->handleException($e); + }); + $this->uncaughtExceptionLevelMap = $levelMap; + foreach ($this->defaultExceptionLevelMap() as $class => $level) { + if (!isset($this->uncaughtExceptionLevelMap[$class])) { + $this->uncaughtExceptionLevelMap[$class] = $level; + } + } + if ($callPrevious && $prev) { + $this->previousExceptionHandler = $prev; + } + + return $this; + } + + /** + * @param array $levelMap an array of E_* constant to LogLevel::* constant mapping + * @return $this + */ + public function registerErrorHandler(array $levelMap = [], bool $callPrevious = true, int $errorTypes = -1, bool $handleOnlyReportedErrors = true): self + { + $prev = set_error_handler([$this, 'handleError'], $errorTypes); + $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap); + if ($callPrevious) { + $this->previousErrorHandler = $prev ?: true; + } else { + $this->previousErrorHandler = null; + } + + $this->handleOnlyReportedErrors = $handleOnlyReportedErrors; + + return $this; + } + + /** + * @param LogLevel::*|null $level a LogLevel::* constant, null to use the default LogLevel::ALERT + * @param int $reservedMemorySize Amount of KBs to reserve in memory so that it can be freed when handling fatal errors giving Monolog some room in memory to get its job done + */ + public function registerFatalHandler($level = null, int $reservedMemorySize = 20): self + { + register_shutdown_function([$this, 'handleFatalError']); + + $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize); + $this->fatalLevel = null === $level ? LogLevel::ALERT : $level; + $this->hasFatalErrorHandler = true; + + return $this; + } + + /** + * @return array + */ + protected function defaultExceptionLevelMap(): array + { + return [ + 'ParseError' => LogLevel::CRITICAL, + 'Throwable' => LogLevel::ERROR, + ]; + } + + /** + * @return array + */ + protected function defaultErrorLevelMap(): array + { + return [ + E_ERROR => LogLevel::CRITICAL, + E_WARNING => LogLevel::WARNING, + E_PARSE => LogLevel::ALERT, + E_NOTICE => LogLevel::NOTICE, + E_CORE_ERROR => LogLevel::CRITICAL, + E_CORE_WARNING => LogLevel::WARNING, + E_COMPILE_ERROR => LogLevel::ALERT, + E_COMPILE_WARNING => LogLevel::WARNING, + E_USER_ERROR => LogLevel::ERROR, + E_USER_WARNING => LogLevel::WARNING, + E_USER_NOTICE => LogLevel::NOTICE, + E_STRICT => LogLevel::NOTICE, + E_RECOVERABLE_ERROR => LogLevel::ERROR, + E_DEPRECATED => LogLevel::NOTICE, + E_USER_DEPRECATED => LogLevel::NOTICE, + ]; + } + + /** + * @phpstan-return never + */ + private function handleException(\Throwable $e): void + { + $level = LogLevel::ERROR; + foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) { + if ($e instanceof $class) { + $level = $candidate; + break; + } + } + + $this->logger->log( + $level, + sprintf('Uncaught Exception %s: "%s" at %s line %s', Utils::getClass($e), $e->getMessage(), $e->getFile(), $e->getLine()), + ['exception' => $e] + ); + + if ($this->previousExceptionHandler) { + ($this->previousExceptionHandler)($e); + } + + if (!headers_sent() && in_array(strtolower((string) ini_get('display_errors')), ['0', '', 'false', 'off', 'none', 'no'], true)) { + http_response_code(500); + } + + exit(255); + } + + /** + * @private + * + * @param mixed[] $context + */ + public function handleError(int $code, string $message, string $file = '', int $line = 0, ?array $context = []): bool + { + if ($this->handleOnlyReportedErrors && !(error_reporting() & $code)) { + return false; + } + + // fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries + if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) { + $level = $this->errorLevelMap[$code] ?? LogLevel::CRITICAL; + $this->logger->log($level, self::codeToString($code).': '.$message, ['code' => $code, 'message' => $message, 'file' => $file, 'line' => $line]); + } else { + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + array_shift($trace); // Exclude handleError from trace + $this->lastFatalData = ['type' => $code, 'message' => $message, 'file' => $file, 'line' => $line, 'trace' => $trace]; + } + + if ($this->previousErrorHandler === true) { + return false; + } elseif ($this->previousErrorHandler) { + return (bool) ($this->previousErrorHandler)($code, $message, $file, $line, $context); + } + + return true; + } + + /** + * @private + */ + public function handleFatalError(): void + { + $this->reservedMemory = ''; + + if (is_array($this->lastFatalData)) { + $lastError = $this->lastFatalData; + } else { + $lastError = error_get_last(); + } + + if ($lastError && in_array($lastError['type'], self::$fatalErrors, true)) { + $trace = $lastError['trace'] ?? null; + $this->logger->log( + $this->fatalLevel, + 'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'], + ['code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'trace' => $trace] + ); + + if ($this->logger instanceof Logger) { + foreach ($this->logger->getHandlers() as $handler) { + $handler->close(); + } + } + } + } + + /** + * @param int $code + */ + private static function codeToString($code): string + { + switch ($code) { + case E_ERROR: + return 'E_ERROR'; + case E_WARNING: + return 'E_WARNING'; + case E_PARSE: + return 'E_PARSE'; + case E_NOTICE: + return 'E_NOTICE'; + case E_CORE_ERROR: + return 'E_CORE_ERROR'; + case E_CORE_WARNING: + return 'E_CORE_WARNING'; + case E_COMPILE_ERROR: + return 'E_COMPILE_ERROR'; + case E_COMPILE_WARNING: + return 'E_COMPILE_WARNING'; + case E_USER_ERROR: + return 'E_USER_ERROR'; + case E_USER_WARNING: + return 'E_USER_WARNING'; + case E_USER_NOTICE: + return 'E_USER_NOTICE'; + case E_STRICT: + return 'E_STRICT'; + case E_RECOVERABLE_ERROR: + return 'E_RECOVERABLE_ERROR'; + case E_DEPRECATED: + return 'E_DEPRECATED'; + case E_USER_DEPRECATED: + return 'E_USER_DEPRECATED'; + } + + return 'Unknown PHP error'; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php new file mode 100644 index 0000000..aa1884b --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Formats a log message according to the ChromePHP array format + * + * @author Christophe Coevoet + */ +class ChromePHPFormatter implements FormatterInterface +{ + /** + * Translates Monolog log levels to Wildfire levels. + * + * @var array + */ + private $logLevels = [ + Logger::DEBUG => 'log', + Logger::INFO => 'info', + Logger::NOTICE => 'info', + Logger::WARNING => 'warn', + Logger::ERROR => 'error', + Logger::CRITICAL => 'error', + Logger::ALERT => 'error', + Logger::EMERGENCY => 'error', + ]; + + /** + * {@inheritDoc} + */ + public function format(array $record) + { + // Retrieve the line and file if set and remove them from the formatted extra + $backtrace = 'unknown'; + if (isset($record['extra']['file'], $record['extra']['line'])) { + $backtrace = $record['extra']['file'].' : '.$record['extra']['line']; + unset($record['extra']['file'], $record['extra']['line']); + } + + $message = ['message' => $record['message']]; + if ($record['context']) { + $message['context'] = $record['context']; + } + if ($record['extra']) { + $message['extra'] = $record['extra']; + } + if (count($message) === 1) { + $message = reset($message); + } + + return [ + $record['channel'], + $message, + $backtrace, + $this->logLevels[$record['level']], + ]; + } + + /** + * {@inheritDoc} + */ + public function formatBatch(array $records) + { + $formatted = []; + + foreach ($records as $record) { + $formatted[] = $this->format($record); + } + + return $formatted; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php new file mode 100644 index 0000000..6c8a9ab --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Elastica\Document; + +/** + * Format a log message into an Elastica Document + * + * @author Jelle Vink + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class ElasticaFormatter extends NormalizerFormatter +{ + /** + * @var string Elastic search index name + */ + protected $index; + + /** + * @var ?string Elastic search document type + */ + protected $type; + + /** + * @param string $index Elastic Search index name + * @param ?string $type Elastic Search document type, deprecated as of Elastica 7 + */ + public function __construct(string $index, ?string $type) + { + // elasticsearch requires a ISO 8601 format date with optional millisecond precision. + parent::__construct('Y-m-d\TH:i:s.uP'); + + $this->index = $index; + $this->type = $type; + } + + /** + * {@inheritDoc} + */ + public function format(array $record) + { + $record = parent::format($record); + + return $this->getDocument($record); + } + + public function getIndex(): string + { + return $this->index; + } + + /** + * @deprecated since Elastica 7 type has no effect + */ + public function getType(): string + { + /** @phpstan-ignore-next-line */ + return $this->type; + } + + /** + * Convert a log message into an Elastica Document + * + * @phpstan-param Record $record + */ + protected function getDocument(array $record): Document + { + $document = new Document(); + $document->setData($record); + if (method_exists($document, 'setType')) { + /** @phpstan-ignore-next-line */ + $document->setType($this->type); + } + $document->setIndex($this->index); + + return $document; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php new file mode 100644 index 0000000..b792b81 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use DateTimeInterface; + +/** + * Format a log message into an Elasticsearch record + * + * @author Avtandil Kikabidze + */ +class ElasticsearchFormatter extends NormalizerFormatter +{ + /** + * @var string Elasticsearch index name + */ + protected $index; + + /** + * @var string Elasticsearch record type + */ + protected $type; + + /** + * @param string $index Elasticsearch index name + * @param string $type Elasticsearch record type + */ + public function __construct(string $index, string $type) + { + // Elasticsearch requires an ISO 8601 format date with optional millisecond precision. + parent::__construct(DateTimeInterface::ISO8601); + + $this->index = $index; + $this->type = $type; + } + + /** + * {@inheritDoc} + */ + public function format(array $record) + { + $record = parent::format($record); + + return $this->getDocument($record); + } + + /** + * Getter index + * + * @return string + */ + public function getIndex(): string + { + return $this->index; + } + + /** + * Getter type + * + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * Convert a log message into an Elasticsearch record + * + * @param mixed[] $record Log message + * @return mixed[] + */ + protected function getDocument(array $record): array + { + $record['_index'] = $this->index; + $record['_type'] = $this->type; + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php new file mode 100644 index 0000000..867ae58 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * formats the record to be used in the FlowdockHandler + * + * @author Dominik Liebler + * @deprecated Since 2.9.0 and 3.3.0, Flowdock was shutdown we will thus drop this handler in Monolog 4 + */ +class FlowdockFormatter implements FormatterInterface +{ + /** + * @var string + */ + private $source; + + /** + * @var string + */ + private $sourceEmail; + + public function __construct(string $source, string $sourceEmail) + { + $this->source = $source; + $this->sourceEmail = $sourceEmail; + } + + /** + * {@inheritDoc} + * + * @return mixed[] + */ + public function format(array $record): array + { + $tags = [ + '#logs', + '#' . strtolower($record['level_name']), + '#' . $record['channel'], + ]; + + foreach ($record['extra'] as $value) { + $tags[] = '#' . $value; + } + + $subject = sprintf( + 'in %s: %s - %s', + $this->source, + $record['level_name'], + $this->getShortMessage($record['message']) + ); + + $record['flowdock'] = [ + 'source' => $this->source, + 'from_address' => $this->sourceEmail, + 'subject' => $subject, + 'content' => $record['message'], + 'tags' => $tags, + 'project' => $this->source, + ]; + + return $record; + } + + /** + * {@inheritDoc} + * + * @return mixed[][] + */ + public function formatBatch(array $records): array + { + $formatted = []; + + foreach ($records as $record) { + $formatted[] = $this->format($record); + } + + return $formatted; + } + + public function getShortMessage(string $message): string + { + static $hasMbString; + + if (null === $hasMbString) { + $hasMbString = function_exists('mb_strlen'); + } + + $maxLength = 45; + + if ($hasMbString) { + if (mb_strlen($message, 'UTF-8') > $maxLength) { + $message = mb_substr($message, 0, $maxLength - 4, 'UTF-8') . ' ...'; + } + } else { + if (strlen($message) > $maxLength) { + $message = substr($message, 0, $maxLength - 4) . ' ...'; + } + } + + return $message; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php new file mode 100644 index 0000000..29b14d3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Utils; + +/** + * Class FluentdFormatter + * + * Serializes a log message to Fluentd unix socket protocol + * + * Fluentd config: + * + * + * type unix + * path /var/run/td-agent/td-agent.sock + * + * + * Monolog setup: + * + * $logger = new Monolog\Logger('fluent.tag'); + * $fluentHandler = new Monolog\Handler\SocketHandler('unix:///var/run/td-agent/td-agent.sock'); + * $fluentHandler->setFormatter(new Monolog\Formatter\FluentdFormatter()); + * $logger->pushHandler($fluentHandler); + * + * @author Andrius Putna + */ +class FluentdFormatter implements FormatterInterface +{ + /** + * @var bool $levelTag should message level be a part of the fluentd tag + */ + protected $levelTag = false; + + public function __construct(bool $levelTag = false) + { + if (!function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s FluentdUnixFormatter'); + } + + $this->levelTag = $levelTag; + } + + public function isUsingLevelsInTag(): bool + { + return $this->levelTag; + } + + public function format(array $record): string + { + $tag = $record['channel']; + if ($this->levelTag) { + $tag .= '.' . strtolower($record['level_name']); + } + + $message = [ + 'message' => $record['message'], + 'context' => $record['context'], + 'extra' => $record['extra'], + ]; + + if (!$this->levelTag) { + $message['level'] = $record['level']; + $message['level_name'] = $record['level_name']; + } + + return Utils::jsonEncode([$tag, $record['datetime']->getTimestamp(), $message]); + } + + public function formatBatch(array $records): string + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php b/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php new file mode 100644 index 0000000..19617ec --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Interface for formatters + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface FormatterInterface +{ + /** + * Formats a log record. + * + * @param array $record A record to format + * @return mixed The formatted record + * + * @phpstan-param Record $record + */ + public function format(array $record); + + /** + * Formats a set of log records. + * + * @param array $records A set of records to format + * @return mixed The formatted set of records + * + * @phpstan-param Record[] $records + */ + public function formatBatch(array $records); +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php new file mode 100644 index 0000000..3b3e1e7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; +use Gelf\Message; +use Monolog\Utils; + +/** + * Serializes a log message to GELF + * @see http://docs.graylog.org/en/latest/pages/gelf.html + * + * @author Matt Lehner + * + * @phpstan-import-type Level from \Monolog\Logger + */ +class GelfMessageFormatter extends NormalizerFormatter +{ + protected const DEFAULT_MAX_LENGTH = 32766; + + /** + * @var string the name of the system for the Gelf log message + */ + protected $systemName; + + /** + * @var string a prefix for 'extra' fields from the Monolog record (optional) + */ + protected $extraPrefix; + + /** + * @var string a prefix for 'context' fields from the Monolog record (optional) + */ + protected $contextPrefix; + + /** + * @var int max length per field + */ + protected $maxLength; + + /** + * @var int + */ + private $gelfVersion = 2; + + /** + * Translates Monolog log levels to Graylog2 log priorities. + * + * @var array + * + * @phpstan-var array + */ + private $logLevels = [ + Logger::DEBUG => 7, + Logger::INFO => 6, + Logger::NOTICE => 5, + Logger::WARNING => 4, + Logger::ERROR => 3, + Logger::CRITICAL => 2, + Logger::ALERT => 1, + Logger::EMERGENCY => 0, + ]; + + public function __construct(?string $systemName = null, ?string $extraPrefix = null, string $contextPrefix = 'ctxt_', ?int $maxLength = null) + { + if (!class_exists(Message::class)) { + throw new \RuntimeException('Composer package graylog2/gelf-php is required to use Monolog\'s GelfMessageFormatter'); + } + + parent::__construct('U.u'); + + $this->systemName = (is_null($systemName) || $systemName === '') ? (string) gethostname() : $systemName; + + $this->extraPrefix = is_null($extraPrefix) ? '' : $extraPrefix; + $this->contextPrefix = $contextPrefix; + $this->maxLength = is_null($maxLength) ? self::DEFAULT_MAX_LENGTH : $maxLength; + + if (method_exists(Message::class, 'setFacility')) { + $this->gelfVersion = 1; + } + } + + /** + * {@inheritDoc} + */ + public function format(array $record): Message + { + $context = $extra = []; + if (isset($record['context'])) { + /** @var mixed[] $context */ + $context = parent::normalize($record['context']); + } + if (isset($record['extra'])) { + /** @var mixed[] $extra */ + $extra = parent::normalize($record['extra']); + } + + if (!isset($record['datetime'], $record['message'], $record['level'])) { + throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, '.var_export($record, true).' given'); + } + + $message = new Message(); + $message + ->setTimestamp($record['datetime']) + ->setShortMessage((string) $record['message']) + ->setHost($this->systemName) + ->setLevel($this->logLevels[$record['level']]); + + // message length + system name length + 200 for padding / metadata + $len = 200 + strlen((string) $record['message']) + strlen($this->systemName); + + if ($len > $this->maxLength) { + $message->setShortMessage(Utils::substr($record['message'], 0, $this->maxLength)); + } + + if ($this->gelfVersion === 1) { + if (isset($record['channel'])) { + $message->setFacility($record['channel']); + } + if (isset($extra['line'])) { + $message->setLine($extra['line']); + unset($extra['line']); + } + if (isset($extra['file'])) { + $message->setFile($extra['file']); + unset($extra['file']); + } + } else { + $message->setAdditional('facility', $record['channel']); + } + + foreach ($extra as $key => $val) { + $val = is_scalar($val) || null === $val ? $val : $this->toJson($val); + $len = strlen($this->extraPrefix . $key . $val); + if ($len > $this->maxLength) { + $message->setAdditional($this->extraPrefix . $key, Utils::substr((string) $val, 0, $this->maxLength)); + + continue; + } + $message->setAdditional($this->extraPrefix . $key, $val); + } + + foreach ($context as $key => $val) { + $val = is_scalar($val) || null === $val ? $val : $this->toJson($val); + $len = strlen($this->contextPrefix . $key . $val); + if ($len > $this->maxLength) { + $message->setAdditional($this->contextPrefix . $key, Utils::substr((string) $val, 0, $this->maxLength)); + + continue; + } + $message->setAdditional($this->contextPrefix . $key, $val); + } + + if ($this->gelfVersion === 1) { + /** @phpstan-ignore-next-line */ + if (null === $message->getFile() && isset($context['exception']['file'])) { + if (preg_match("/^(.+):([0-9]+)$/", $context['exception']['file'], $matches)) { + $message->setFile($matches[1]); + $message->setLine($matches[2]); + } + } + } + + return $message; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php new file mode 100644 index 0000000..ca52ebf --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use DateTimeInterface; +use Monolog\LogRecord; + +/** + * Encodes message information into JSON in a format compatible with Cloud logging. + * + * @see https://cloud.google.com/logging/docs/structured-logging + * @see https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry + * + * @author Luís Cobucci + */ +final class GoogleCloudLoggingFormatter extends JsonFormatter +{ + /** {@inheritdoc} **/ + public function format(array $record): string + { + // Re-key level for GCP logging + $record['severity'] = $record['level_name']; + $record['time'] = $record['datetime']->format(DateTimeInterface::RFC3339_EXTENDED); + + // Remove keys that are not used by GCP + unset($record['level'], $record['level_name'], $record['datetime']); + + return parent::format($record); + } +} + diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php new file mode 100644 index 0000000..10a4311 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * Formats incoming records into an HTML table + * + * This is especially useful for html email logging + * + * @author Tiago Brito + */ +class HtmlFormatter extends NormalizerFormatter +{ + /** + * Translates Monolog log levels to html color priorities. + * + * @var array + */ + protected $logLevels = [ + Logger::DEBUG => '#CCCCCC', + Logger::INFO => '#28A745', + Logger::NOTICE => '#17A2B8', + Logger::WARNING => '#FFC107', + Logger::ERROR => '#FD7E14', + Logger::CRITICAL => '#DC3545', + Logger::ALERT => '#821722', + Logger::EMERGENCY => '#000000', + ]; + + /** + * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct(?string $dateFormat = null) + { + parent::__construct($dateFormat); + } + + /** + * Creates an HTML table row + * + * @param string $th Row header content + * @param string $td Row standard cell content + * @param bool $escapeTd false if td content must not be html escaped + */ + protected function addRow(string $th, string $td = ' ', bool $escapeTd = true): string + { + $th = htmlspecialchars($th, ENT_NOQUOTES, 'UTF-8'); + if ($escapeTd) { + $td = '
'.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'
'; + } + + return "\n$th:\n".$td."\n"; + } + + /** + * Create a HTML h1 tag + * + * @param string $title Text to be in the h1 + * @param int $level Error level + * @return string + */ + protected function addTitle(string $title, int $level): string + { + $title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8'); + + return '

'.$title.'

'; + } + + /** + * Formats a log record. + * + * @return string The formatted record + */ + public function format(array $record): string + { + $output = $this->addTitle($record['level_name'], $record['level']); + $output .= ''; + + $output .= $this->addRow('Message', (string) $record['message']); + $output .= $this->addRow('Time', $this->formatDate($record['datetime'])); + $output .= $this->addRow('Channel', $record['channel']); + if ($record['context']) { + $embeddedTable = '
'; + foreach ($record['context'] as $key => $value) { + $embeddedTable .= $this->addRow((string) $key, $this->convertToString($value)); + } + $embeddedTable .= '
'; + $output .= $this->addRow('Context', $embeddedTable, false); + } + if ($record['extra']) { + $embeddedTable = ''; + foreach ($record['extra'] as $key => $value) { + $embeddedTable .= $this->addRow((string) $key, $this->convertToString($value)); + } + $embeddedTable .= '
'; + $output .= $this->addRow('Extra', $embeddedTable, false); + } + + return $output.''; + } + + /** + * Formats a set of log records. + * + * @return string The formatted set of records + */ + public function formatBatch(array $records): string + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } + + /** + * @param mixed $data + */ + protected function convertToString($data): string + { + if (null === $data || is_scalar($data)) { + return (string) $data; + } + + $data = $this->normalize($data); + + return Utils::jsonEncode($data, JSON_PRETTY_PRINT | Utils::DEFAULT_JSON_FLAGS, true); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php new file mode 100644 index 0000000..b737d82 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Throwable; + +/** + * Encodes whatever record data is passed to it as json + * + * This can be useful to log to databases or remote APIs + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class JsonFormatter extends NormalizerFormatter +{ + public const BATCH_MODE_JSON = 1; + public const BATCH_MODE_NEWLINES = 2; + + /** @var self::BATCH_MODE_* */ + protected $batchMode; + /** @var bool */ + protected $appendNewline; + /** @var bool */ + protected $ignoreEmptyContextAndExtra; + /** @var bool */ + protected $includeStacktraces = false; + + /** + * @param self::BATCH_MODE_* $batchMode + */ + public function __construct(int $batchMode = self::BATCH_MODE_JSON, bool $appendNewline = true, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false) + { + $this->batchMode = $batchMode; + $this->appendNewline = $appendNewline; + $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra; + $this->includeStacktraces = $includeStacktraces; + + parent::__construct(); + } + + /** + * The batch mode option configures the formatting style for + * multiple records. By default, multiple records will be + * formatted as a JSON-encoded array. However, for + * compatibility with some API endpoints, alternative styles + * are available. + */ + public function getBatchMode(): int + { + return $this->batchMode; + } + + /** + * True if newlines are appended to every formatted record + */ + public function isAppendingNewlines(): bool + { + return $this->appendNewline; + } + + /** + * {@inheritDoc} + */ + public function format(array $record): string + { + $normalized = $this->normalize($record); + + if (isset($normalized['context']) && $normalized['context'] === []) { + if ($this->ignoreEmptyContextAndExtra) { + unset($normalized['context']); + } else { + $normalized['context'] = new \stdClass; + } + } + if (isset($normalized['extra']) && $normalized['extra'] === []) { + if ($this->ignoreEmptyContextAndExtra) { + unset($normalized['extra']); + } else { + $normalized['extra'] = new \stdClass; + } + } + + return $this->toJson($normalized, true) . ($this->appendNewline ? "\n" : ''); + } + + /** + * {@inheritDoc} + */ + public function formatBatch(array $records): string + { + switch ($this->batchMode) { + case static::BATCH_MODE_NEWLINES: + return $this->formatBatchNewlines($records); + + case static::BATCH_MODE_JSON: + default: + return $this->formatBatchJson($records); + } + } + + /** + * @return self + */ + public function includeStacktraces(bool $include = true): self + { + $this->includeStacktraces = $include; + + return $this; + } + + /** + * Return a JSON-encoded array of records. + * + * @phpstan-param Record[] $records + */ + protected function formatBatchJson(array $records): string + { + return $this->toJson($this->normalize($records), true); + } + + /** + * Use new lines to separate records instead of a + * JSON-encoded array. + * + * @phpstan-param Record[] $records + */ + protected function formatBatchNewlines(array $records): string + { + $instance = $this; + + $oldNewline = $this->appendNewline; + $this->appendNewline = false; + array_walk($records, function (&$value, $key) use ($instance) { + $value = $instance->format($value); + }); + $this->appendNewline = $oldNewline; + + return implode("\n", $records); + } + + /** + * Normalizes given $data. + * + * @param mixed $data + * + * @return mixed + */ + protected function normalize($data, int $depth = 0) + { + if ($depth > $this->maxNormalizeDepth) { + return 'Over '.$this->maxNormalizeDepth.' levels deep, aborting normalization'; + } + + if (is_array($data)) { + $normalized = []; + + $count = 1; + foreach ($data as $key => $value) { + if ($count++ > $this->maxNormalizeItemCount) { + $normalized['...'] = 'Over '.$this->maxNormalizeItemCount.' items ('.count($data).' total), aborting normalization'; + break; + } + + $normalized[$key] = $this->normalize($value, $depth + 1); + } + + return $normalized; + } + + if (is_object($data)) { + if ($data instanceof \DateTimeInterface) { + return $this->formatDate($data); + } + + if ($data instanceof Throwable) { + return $this->normalizeException($data, $depth); + } + + // if the object has specific json serializability we want to make sure we skip the __toString treatment below + if ($data instanceof \JsonSerializable) { + return $data; + } + + if (method_exists($data, '__toString')) { + return $data->__toString(); + } + + return $data; + } + + if (is_resource($data)) { + return parent::normalize($data); + } + + return $data; + } + + /** + * Normalizes given exception with or without its own stack trace based on + * `includeStacktraces` property. + * + * {@inheritDoc} + */ + protected function normalizeException(Throwable $e, int $depth = 0): array + { + $data = parent::normalizeException($e, $depth); + if (!$this->includeStacktraces) { + unset($data['trace']); + } + + return $data; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php new file mode 100644 index 0000000..e6e7898 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php @@ -0,0 +1,246 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Utils; + +/** + * Formats incoming records into a one-line string + * + * This is especially useful for logging to files + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +class LineFormatter extends NormalizerFormatter +{ + public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; + + /** @var string */ + protected $format; + /** @var bool */ + protected $allowInlineLineBreaks; + /** @var bool */ + protected $ignoreEmptyContextAndExtra; + /** @var bool */ + protected $includeStacktraces; + /** @var ?callable */ + protected $stacktracesParser; + + /** + * @param string|null $format The format of the message + * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format + * @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries + * @param bool $ignoreEmptyContextAndExtra + */ + public function __construct(?string $format = null, ?string $dateFormat = null, bool $allowInlineLineBreaks = false, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false) + { + $this->format = $format === null ? static::SIMPLE_FORMAT : $format; + $this->allowInlineLineBreaks = $allowInlineLineBreaks; + $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra; + $this->includeStacktraces($includeStacktraces); + parent::__construct($dateFormat); + } + + public function includeStacktraces(bool $include = true, ?callable $parser = null): self + { + $this->includeStacktraces = $include; + if ($this->includeStacktraces) { + $this->allowInlineLineBreaks = true; + $this->stacktracesParser = $parser; + } + + return $this; + } + + public function allowInlineLineBreaks(bool $allow = true): self + { + $this->allowInlineLineBreaks = $allow; + + return $this; + } + + public function ignoreEmptyContextAndExtra(bool $ignore = true): self + { + $this->ignoreEmptyContextAndExtra = $ignore; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function format(array $record): string + { + $vars = parent::format($record); + + $output = $this->format; + + foreach ($vars['extra'] as $var => $val) { + if (false !== strpos($output, '%extra.'.$var.'%')) { + $output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output); + unset($vars['extra'][$var]); + } + } + + foreach ($vars['context'] as $var => $val) { + if (false !== strpos($output, '%context.'.$var.'%')) { + $output = str_replace('%context.'.$var.'%', $this->stringify($val), $output); + unset($vars['context'][$var]); + } + } + + if ($this->ignoreEmptyContextAndExtra) { + if (empty($vars['context'])) { + unset($vars['context']); + $output = str_replace('%context%', '', $output); + } + + if (empty($vars['extra'])) { + unset($vars['extra']); + $output = str_replace('%extra%', '', $output); + } + } + + foreach ($vars as $var => $val) { + if (false !== strpos($output, '%'.$var.'%')) { + $output = str_replace('%'.$var.'%', $this->stringify($val), $output); + } + } + + // remove leftover %extra.xxx% and %context.xxx% if any + if (false !== strpos($output, '%')) { + $output = preg_replace('/%(?:extra|context)\..+?%/', '', $output); + if (null === $output) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode)); + } + } + + return $output; + } + + public function formatBatch(array $records): string + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } + + /** + * @param mixed $value + */ + public function stringify($value): string + { + return $this->replaceNewlines($this->convertToString($value)); + } + + protected function normalizeException(\Throwable $e, int $depth = 0): string + { + $str = $this->formatException($e); + + if ($previous = $e->getPrevious()) { + do { + $depth++; + if ($depth > $this->maxNormalizeDepth) { + $str .= "\n[previous exception] Over " . $this->maxNormalizeDepth . ' levels deep, aborting normalization'; + break; + } + + $str .= "\n[previous exception] " . $this->formatException($previous); + } while ($previous = $previous->getPrevious()); + } + + return $str; + } + + /** + * @param mixed $data + */ + protected function convertToString($data): string + { + if (null === $data || is_bool($data)) { + return var_export($data, true); + } + + if (is_scalar($data)) { + return (string) $data; + } + + return $this->toJson($data, true); + } + + protected function replaceNewlines(string $str): string + { + if ($this->allowInlineLineBreaks) { + if (0 === strpos($str, '{')) { + $str = preg_replace('/(?getCode(); + if ($e instanceof \SoapFault) { + if (isset($e->faultcode)) { + $str .= ' faultcode: ' . $e->faultcode; + } + + if (isset($e->faultactor)) { + $str .= ' faultactor: ' . $e->faultactor; + } + + if (isset($e->detail)) { + if (is_string($e->detail)) { + $str .= ' detail: ' . $e->detail; + } elseif (is_object($e->detail) || is_array($e->detail)) { + $str .= ' detail: ' . $this->toJson($e->detail, true); + } + } + } + $str .= '): ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine() . ')'; + + if ($this->includeStacktraces) { + $str .= $this->stacktracesParser($e); + } + + return $str; + } + + private function stacktracesParser(\Throwable $e): string + { + $trace = $e->getTraceAsString(); + + if ($this->stacktracesParser) { + $trace = $this->stacktracesParserCustom($trace); + } + + return "\n[stacktrace]\n" . $trace . "\n"; + } + + private function stacktracesParserCustom(string $trace): string + { + return implode("\n", array_filter(array_map($this->stacktracesParser, explode("\n", $trace)))); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php new file mode 100644 index 0000000..29841aa --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Encodes message information into JSON in a format compatible with Loggly. + * + * @author Adam Pancutt + */ +class LogglyFormatter extends JsonFormatter +{ + /** + * Overrides the default batch mode to new lines for compatibility with the + * Loggly bulk API. + */ + public function __construct(int $batchMode = self::BATCH_MODE_NEWLINES, bool $appendNewline = false) + { + parent::__construct($batchMode, $appendNewline); + } + + /** + * Appends the 'timestamp' parameter for indexing by Loggly. + * + * @see https://www.loggly.com/docs/automated-parsing/#json + * @see \Monolog\Formatter\JsonFormatter::format() + */ + public function format(array $record): string + { + if (isset($record["datetime"]) && ($record["datetime"] instanceof \DateTimeInterface)) { + $record["timestamp"] = $record["datetime"]->format("Y-m-d\TH:i:s.uO"); + unset($record["datetime"]); + } + + return parent::format($record); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php new file mode 100644 index 0000000..b0451ab --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Encodes message information into JSON in a format compatible with Logmatic. + * + * @author Julien Breux + */ +class LogmaticFormatter extends JsonFormatter +{ + protected const MARKERS = ["sourcecode", "php"]; + + /** + * @var string + */ + protected $hostname = ''; + + /** + * @var string + */ + protected $appname = ''; + + public function setHostname(string $hostname): self + { + $this->hostname = $hostname; + + return $this; + } + + public function setAppname(string $appname): self + { + $this->appname = $appname; + + return $this; + } + + /** + * Appends the 'hostname' and 'appname' parameter for indexing by Logmatic. + * + * @see http://doc.logmatic.io/docs/basics-to-send-data + * @see \Monolog\Formatter\JsonFormatter::format() + */ + public function format(array $record): string + { + if (!empty($this->hostname)) { + $record["hostname"] = $this->hostname; + } + if (!empty($this->appname)) { + $record["appname"] = $this->appname; + } + + $record["@marker"] = static::MARKERS; + + return parent::format($record); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php new file mode 100644 index 0000000..f8de0d3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Serializes a log message to Logstash Event Format + * + * @see https://www.elastic.co/products/logstash + * @see https://github.com/elastic/logstash/blob/master/logstash-core/src/main/java/org/logstash/Event.java + * + * @author Tim Mower + */ +class LogstashFormatter extends NormalizerFormatter +{ + /** + * @var string the name of the system for the Logstash log message, used to fill the @source field + */ + protected $systemName; + + /** + * @var string an application name for the Logstash log message, used to fill the @type field + */ + protected $applicationName; + + /** + * @var string the key for 'extra' fields from the Monolog record + */ + protected $extraKey; + + /** + * @var string the key for 'context' fields from the Monolog record + */ + protected $contextKey; + + /** + * @param string $applicationName The application that sends the data, used as the "type" field of logstash + * @param string|null $systemName The system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine + * @param string $extraKey The key for extra keys inside logstash "fields", defaults to extra + * @param string $contextKey The key for context keys inside logstash "fields", defaults to context + */ + public function __construct(string $applicationName, ?string $systemName = null, string $extraKey = 'extra', string $contextKey = 'context') + { + // logstash requires a ISO 8601 format date with optional millisecond precision. + parent::__construct('Y-m-d\TH:i:s.uP'); + + $this->systemName = $systemName === null ? (string) gethostname() : $systemName; + $this->applicationName = $applicationName; + $this->extraKey = $extraKey; + $this->contextKey = $contextKey; + } + + /** + * {@inheritDoc} + */ + public function format(array $record): string + { + $record = parent::format($record); + + if (empty($record['datetime'])) { + $record['datetime'] = gmdate('c'); + } + $message = [ + '@timestamp' => $record['datetime'], + '@version' => 1, + 'host' => $this->systemName, + ]; + if (isset($record['message'])) { + $message['message'] = $record['message']; + } + if (isset($record['channel'])) { + $message['type'] = $record['channel']; + $message['channel'] = $record['channel']; + } + if (isset($record['level_name'])) { + $message['level'] = $record['level_name']; + } + if (isset($record['level'])) { + $message['monolog_level'] = $record['level']; + } + if ($this->applicationName) { + $message['type'] = $this->applicationName; + } + if (!empty($record['extra'])) { + $message[$this->extraKey] = $record['extra']; + } + if (!empty($record['context'])) { + $message[$this->contextKey] = $record['context']; + } + + return $this->toJson($message) . "\n"; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php new file mode 100644 index 0000000..fca69a8 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use MongoDB\BSON\Type; +use MongoDB\BSON\UTCDateTime; +use Monolog\Utils; + +/** + * Formats a record for use with the MongoDBHandler. + * + * @author Florian Plattner + */ +class MongoDBFormatter implements FormatterInterface +{ + /** @var bool */ + private $exceptionTraceAsString; + /** @var int */ + private $maxNestingLevel; + /** @var bool */ + private $isLegacyMongoExt; + + /** + * @param int $maxNestingLevel 0 means infinite nesting, the $record itself is level 1, $record['context'] is 2 + * @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings + */ + public function __construct(int $maxNestingLevel = 3, bool $exceptionTraceAsString = true) + { + $this->maxNestingLevel = max($maxNestingLevel, 0); + $this->exceptionTraceAsString = $exceptionTraceAsString; + + $this->isLegacyMongoExt = extension_loaded('mongodb') && version_compare((string) phpversion('mongodb'), '1.1.9', '<='); + } + + /** + * {@inheritDoc} + * + * @return mixed[] + */ + public function format(array $record): array + { + /** @var mixed[] $res */ + $res = $this->formatArray($record); + + return $res; + } + + /** + * {@inheritDoc} + * + * @return array + */ + public function formatBatch(array $records): array + { + $formatted = []; + foreach ($records as $key => $record) { + $formatted[$key] = $this->format($record); + } + + return $formatted; + } + + /** + * @param mixed[] $array + * @return mixed[]|string Array except when max nesting level is reached then a string "[...]" + */ + protected function formatArray(array $array, int $nestingLevel = 0) + { + if ($this->maxNestingLevel > 0 && $nestingLevel > $this->maxNestingLevel) { + return '[...]'; + } + + foreach ($array as $name => $value) { + if ($value instanceof \DateTimeInterface) { + $array[$name] = $this->formatDate($value, $nestingLevel + 1); + } elseif ($value instanceof \Throwable) { + $array[$name] = $this->formatException($value, $nestingLevel + 1); + } elseif (is_array($value)) { + $array[$name] = $this->formatArray($value, $nestingLevel + 1); + } elseif (is_object($value) && !$value instanceof Type) { + $array[$name] = $this->formatObject($value, $nestingLevel + 1); + } + } + + return $array; + } + + /** + * @param mixed $value + * @return mixed[]|string + */ + protected function formatObject($value, int $nestingLevel) + { + $objectVars = get_object_vars($value); + $objectVars['class'] = Utils::getClass($value); + + return $this->formatArray($objectVars, $nestingLevel); + } + + /** + * @return mixed[]|string + */ + protected function formatException(\Throwable $exception, int $nestingLevel) + { + $formattedException = [ + 'class' => Utils::getClass($exception), + 'message' => $exception->getMessage(), + 'code' => (int) $exception->getCode(), + 'file' => $exception->getFile() . ':' . $exception->getLine(), + ]; + + if ($this->exceptionTraceAsString === true) { + $formattedException['trace'] = $exception->getTraceAsString(); + } else { + $formattedException['trace'] = $exception->getTrace(); + } + + return $this->formatArray($formattedException, $nestingLevel); + } + + protected function formatDate(\DateTimeInterface $value, int $nestingLevel): UTCDateTime + { + if ($this->isLegacyMongoExt) { + return $this->legacyGetMongoDbDateTime($value); + } + + return $this->getMongoDbDateTime($value); + } + + private function getMongoDbDateTime(\DateTimeInterface $value): UTCDateTime + { + return new UTCDateTime((int) floor(((float) $value->format('U.u')) * 1000)); + } + + /** + * This is needed to support MongoDB Driver v1.19 and below + * + * See https://github.com/mongodb/mongo-php-driver/issues/426 + * + * It can probably be removed in 2.1 or later once MongoDB's 1.2 is released and widely adopted + */ + private function legacyGetMongoDbDateTime(\DateTimeInterface $value): UTCDateTime + { + $milliseconds = floor(((float) $value->format('U.u')) * 1000); + + $milliseconds = (PHP_INT_SIZE == 8) //64-bit OS? + ? (int) $milliseconds + : (string) $milliseconds; + + // @phpstan-ignore-next-line + return new UTCDateTime($milliseconds); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php new file mode 100644 index 0000000..f926a84 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php @@ -0,0 +1,290 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\DateTimeImmutable; +use Monolog\Utils; +use Throwable; + +/** + * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets + * + * @author Jordi Boggiano + */ +class NormalizerFormatter implements FormatterInterface +{ + public const SIMPLE_DATE = "Y-m-d\TH:i:sP"; + + /** @var string */ + protected $dateFormat; + /** @var int */ + protected $maxNormalizeDepth = 9; + /** @var int */ + protected $maxNormalizeItemCount = 1000; + + /** @var int */ + private $jsonEncodeOptions = Utils::DEFAULT_JSON_FLAGS; + + /** + * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct(?string $dateFormat = null) + { + $this->dateFormat = null === $dateFormat ? static::SIMPLE_DATE : $dateFormat; + if (!function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter'); + } + } + + /** + * {@inheritDoc} + * + * @param mixed[] $record + */ + public function format(array $record) + { + return $this->normalize($record); + } + + /** + * {@inheritDoc} + */ + public function formatBatch(array $records) + { + foreach ($records as $key => $record) { + $records[$key] = $this->format($record); + } + + return $records; + } + + public function getDateFormat(): string + { + return $this->dateFormat; + } + + public function setDateFormat(string $dateFormat): self + { + $this->dateFormat = $dateFormat; + + return $this; + } + + /** + * The maximum number of normalization levels to go through + */ + public function getMaxNormalizeDepth(): int + { + return $this->maxNormalizeDepth; + } + + public function setMaxNormalizeDepth(int $maxNormalizeDepth): self + { + $this->maxNormalizeDepth = $maxNormalizeDepth; + + return $this; + } + + /** + * The maximum number of items to normalize per level + */ + public function getMaxNormalizeItemCount(): int + { + return $this->maxNormalizeItemCount; + } + + public function setMaxNormalizeItemCount(int $maxNormalizeItemCount): self + { + $this->maxNormalizeItemCount = $maxNormalizeItemCount; + + return $this; + } + + /** + * Enables `json_encode` pretty print. + */ + public function setJsonPrettyPrint(bool $enable): self + { + if ($enable) { + $this->jsonEncodeOptions |= JSON_PRETTY_PRINT; + } else { + $this->jsonEncodeOptions &= ~JSON_PRETTY_PRINT; + } + + return $this; + } + + /** + * @param mixed $data + * @return null|scalar|array + */ + protected function normalize($data, int $depth = 0) + { + if ($depth > $this->maxNormalizeDepth) { + return 'Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization'; + } + + if (null === $data || is_scalar($data)) { + if (is_float($data)) { + if (is_infinite($data)) { + return ($data > 0 ? '' : '-') . 'INF'; + } + if (is_nan($data)) { + return 'NaN'; + } + } + + return $data; + } + + if (is_array($data)) { + $normalized = []; + + $count = 1; + foreach ($data as $key => $value) { + if ($count++ > $this->maxNormalizeItemCount) { + $normalized['...'] = 'Over ' . $this->maxNormalizeItemCount . ' items ('.count($data).' total), aborting normalization'; + break; + } + + $normalized[$key] = $this->normalize($value, $depth + 1); + } + + return $normalized; + } + + if ($data instanceof \DateTimeInterface) { + return $this->formatDate($data); + } + + if (is_object($data)) { + if ($data instanceof Throwable) { + return $this->normalizeException($data, $depth); + } + + if ($data instanceof \JsonSerializable) { + /** @var null|scalar|array $value */ + $value = $data->jsonSerialize(); + } elseif (\get_class($data) === '__PHP_Incomplete_Class') { + $accessor = new \ArrayObject($data); + $value = (string) $accessor['__PHP_Incomplete_Class_Name']; + } elseif (method_exists($data, '__toString')) { + /** @var string $value */ + $value = $data->__toString(); + } else { + // the rest is normalized by json encoding and decoding it + /** @var null|scalar|array $value */ + $value = json_decode($this->toJson($data, true), true); + } + + return [Utils::getClass($data) => $value]; + } + + if (is_resource($data)) { + return sprintf('[resource(%s)]', get_resource_type($data)); + } + + return '[unknown('.gettype($data).')]'; + } + + /** + * @return mixed[] + */ + protected function normalizeException(Throwable $e, int $depth = 0) + { + if ($depth > $this->maxNormalizeDepth) { + return ['Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization']; + } + + if ($e instanceof \JsonSerializable) { + return (array) $e->jsonSerialize(); + } + + $data = [ + 'class' => Utils::getClass($e), + 'message' => $e->getMessage(), + 'code' => (int) $e->getCode(), + 'file' => $e->getFile().':'.$e->getLine(), + ]; + + if ($e instanceof \SoapFault) { + if (isset($e->faultcode)) { + $data['faultcode'] = $e->faultcode; + } + + if (isset($e->faultactor)) { + $data['faultactor'] = $e->faultactor; + } + + if (isset($e->detail)) { + if (is_string($e->detail)) { + $data['detail'] = $e->detail; + } elseif (is_object($e->detail) || is_array($e->detail)) { + $data['detail'] = $this->toJson($e->detail, true); + } + } + } + + $trace = $e->getTrace(); + foreach ($trace as $frame) { + if (isset($frame['file'])) { + $data['trace'][] = $frame['file'].':'.$frame['line']; + } + } + + if ($previous = $e->getPrevious()) { + $data['previous'] = $this->normalizeException($previous, $depth + 1); + } + + return $data; + } + + /** + * Return the JSON representation of a value + * + * @param mixed $data + * @throws \RuntimeException if encoding fails and errors are not ignored + * @return string if encoding fails and ignoreErrors is true 'null' is returned + */ + protected function toJson($data, bool $ignoreErrors = false): string + { + return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors); + } + + /** + * @return string + */ + protected function formatDate(\DateTimeInterface $date) + { + // in case the date format isn't custom then we defer to the custom DateTimeImmutable + // formatting logic, which will pick the right format based on whether useMicroseconds is on + if ($this->dateFormat === self::SIMPLE_DATE && $date instanceof DateTimeImmutable) { + return (string) $date; + } + + return $date->format($this->dateFormat); + } + + public function addJsonEncodeOption(int $option): self + { + $this->jsonEncodeOptions |= $option; + + return $this; + } + + public function removeJsonEncodeOption(int $option): self + { + $this->jsonEncodeOptions &= ~$option; + + return $this; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php new file mode 100644 index 0000000..187bc55 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Formats data into an associative array of scalar values. + * Objects and arrays will be JSON encoded. + * + * @author Andrew Lawson + */ +class ScalarFormatter extends NormalizerFormatter +{ + /** + * {@inheritDoc} + * + * @phpstan-return array $record + */ + public function format(array $record): array + { + $result = []; + foreach ($record as $key => $value) { + $result[$key] = $this->normalizeValue($value); + } + + return $result; + } + + /** + * @param mixed $value + * @return scalar|null + */ + protected function normalizeValue($value) + { + $normalized = $this->normalize($value); + + if (is_array($normalized)) { + return $this->toJson($normalized, true); + } + + return $normalized; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php new file mode 100644 index 0000000..6539b34 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Serializes a log message according to Wildfire's header requirements + * + * @author Eric Clemmons (@ericclemmons) + * @author Christophe Coevoet + * @author Kirill chEbba Chebunin + * + * @phpstan-import-type Level from \Monolog\Logger + */ +class WildfireFormatter extends NormalizerFormatter +{ + /** + * Translates Monolog log levels to Wildfire levels. + * + * @var array + */ + private $logLevels = [ + Logger::DEBUG => 'LOG', + Logger::INFO => 'INFO', + Logger::NOTICE => 'INFO', + Logger::WARNING => 'WARN', + Logger::ERROR => 'ERROR', + Logger::CRITICAL => 'ERROR', + Logger::ALERT => 'ERROR', + Logger::EMERGENCY => 'ERROR', + ]; + + /** + * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct(?string $dateFormat = null) + { + parent::__construct($dateFormat); + + // http headers do not like non-ISO-8559-1 characters + $this->removeJsonEncodeOption(JSON_UNESCAPED_UNICODE); + } + + /** + * {@inheritDoc} + * + * @return string + */ + public function format(array $record): string + { + // Retrieve the line and file if set and remove them from the formatted extra + $file = $line = ''; + if (isset($record['extra']['file'])) { + $file = $record['extra']['file']; + unset($record['extra']['file']); + } + if (isset($record['extra']['line'])) { + $line = $record['extra']['line']; + unset($record['extra']['line']); + } + + /** @var mixed[] $record */ + $record = $this->normalize($record); + $message = ['message' => $record['message']]; + $handleError = false; + if ($record['context']) { + $message['context'] = $record['context']; + $handleError = true; + } + if ($record['extra']) { + $message['extra'] = $record['extra']; + $handleError = true; + } + if (count($message) === 1) { + $message = reset($message); + } + + if (isset($record['context']['table'])) { + $type = 'TABLE'; + $label = $record['channel'] .': '. $record['message']; + $message = $record['context']['table']; + } else { + $type = $this->logLevels[$record['level']]; + $label = $record['channel']; + } + + // Create JSON object describing the appearance of the message in the console + $json = $this->toJson([ + [ + 'Type' => $type, + 'File' => $file, + 'Line' => $line, + 'Label' => $label, + ], + $message, + ], $handleError); + + // The message itself is a serialization of the above JSON object + it's length + return sprintf( + '%d|%s|', + strlen($json), + $json + ); + } + + /** + * {@inheritDoc} + * + * @phpstan-return never + */ + public function formatBatch(array $records) + { + throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); + } + + /** + * {@inheritDoc} + * + * @return null|scalar|array|object + */ + protected function normalize($data, int $depth = 0) + { + if (is_object($data) && !$data instanceof \DateTimeInterface) { + return $data; + } + + return parent::normalize($data, $depth); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php new file mode 100644 index 0000000..a5cdaa7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\ResettableInterface; +use Psr\Log\LogLevel; + +/** + * Base Handler class providing basic level/bubble support + * + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +abstract class AbstractHandler extends Handler implements ResettableInterface +{ + /** + * @var int + * @phpstan-var Level + */ + protected $level = Logger::DEBUG; + /** @var bool */ + protected $bubble = true; + + /** + * @param int|string $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG, bool $bubble = true) + { + $this->setLevel($level); + $this->bubble = $bubble; + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return $record['level'] >= $this->level; + } + + /** + * Sets minimum logging level at which this handler will be triggered. + * + * @param Level|LevelName|LogLevel::* $level Level or level name + * @return self + */ + public function setLevel($level): self + { + $this->level = Logger::toMonologLevel($level); + + return $this; + } + + /** + * Gets minimum logging level at which this handler will be triggered. + * + * @return int + * + * @phpstan-return Level + */ + public function getLevel(): int + { + return $this->level; + } + + /** + * Sets the bubbling behavior. + * + * @param bool $bubble true means that this handler allows bubbling. + * false means that bubbling is not permitted. + * @return self + */ + public function setBubble(bool $bubble): self + { + $this->bubble = $bubble; + + return $this; + } + + /** + * Gets the bubbling behavior. + * + * @return bool true means that this handler allows bubbling. + * false means that bubbling is not permitted. + */ + public function getBubble(): bool + { + return $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function reset() + { + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php new file mode 100644 index 0000000..77e533f --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Base Handler class providing the Handler structure, including processors and formatters + * + * Classes extending it should (in most cases) only implement write($record) + * + * @author Jordi Boggiano + * @author Christophe Coevoet + * + * @phpstan-import-type LevelName from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-type FormattedRecord array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[], formatted: mixed} + */ +abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + use FormattableHandlerTrait; + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if (!$this->isHandling($record)) { + return false; + } + + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $record['formatted'] = $this->getFormatter()->format($record); + + $this->write($record); + + return false === $this->bubble; + } + + /** + * Writes the record down to the log of the implementing handler + * + * @phpstan-param FormattedRecord $record + */ + abstract protected function write(array $record): void; + + /** + * @return void + */ + public function reset() + { + parent::reset(); + + $this->resetProcessors(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php new file mode 100644 index 0000000..5e5ad1c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; + +/** + * Common syslog functionality + * + * @phpstan-import-type Level from \Monolog\Logger + */ +abstract class AbstractSyslogHandler extends AbstractProcessingHandler +{ + /** @var int */ + protected $facility; + + /** + * Translates Monolog log levels to syslog log priorities. + * @var array + * @phpstan-var array + */ + protected $logLevels = [ + Logger::DEBUG => LOG_DEBUG, + Logger::INFO => LOG_INFO, + Logger::NOTICE => LOG_NOTICE, + Logger::WARNING => LOG_WARNING, + Logger::ERROR => LOG_ERR, + Logger::CRITICAL => LOG_CRIT, + Logger::ALERT => LOG_ALERT, + Logger::EMERGENCY => LOG_EMERG, + ]; + + /** + * List of valid log facility names. + * @var array + */ + protected $facilities = [ + 'auth' => LOG_AUTH, + 'authpriv' => LOG_AUTHPRIV, + 'cron' => LOG_CRON, + 'daemon' => LOG_DAEMON, + 'kern' => LOG_KERN, + 'lpr' => LOG_LPR, + 'mail' => LOG_MAIL, + 'news' => LOG_NEWS, + 'syslog' => LOG_SYSLOG, + 'user' => LOG_USER, + 'uucp' => LOG_UUCP, + ]; + + /** + * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant + */ + public function __construct($facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->facilities['local0'] = LOG_LOCAL0; + $this->facilities['local1'] = LOG_LOCAL1; + $this->facilities['local2'] = LOG_LOCAL2; + $this->facilities['local3'] = LOG_LOCAL3; + $this->facilities['local4'] = LOG_LOCAL4; + $this->facilities['local5'] = LOG_LOCAL5; + $this->facilities['local6'] = LOG_LOCAL6; + $this->facilities['local7'] = LOG_LOCAL7; + } else { + $this->facilities['local0'] = 128; // LOG_LOCAL0 + $this->facilities['local1'] = 136; // LOG_LOCAL1 + $this->facilities['local2'] = 144; // LOG_LOCAL2 + $this->facilities['local3'] = 152; // LOG_LOCAL3 + $this->facilities['local4'] = 160; // LOG_LOCAL4 + $this->facilities['local5'] = 168; // LOG_LOCAL5 + $this->facilities['local6'] = 176; // LOG_LOCAL6 + $this->facilities['local7'] = 184; // LOG_LOCAL7 + } + + // convert textual description of facility to syslog constant + if (is_string($facility) && array_key_exists(strtolower($facility), $this->facilities)) { + $facility = $this->facilities[strtolower($facility)]; + } elseif (!in_array($facility, array_values($this->facilities), true)) { + throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given'); + } + + $this->facility = $facility; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php new file mode 100644 index 0000000..994872c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\JsonFormatter; +use PhpAmqpLib\Message\AMQPMessage; +use PhpAmqpLib\Channel\AMQPChannel; +use AMQPExchange; + +/** + * @phpstan-import-type Record from \Monolog\Logger + */ +class AmqpHandler extends AbstractProcessingHandler +{ + /** + * @var AMQPExchange|AMQPChannel $exchange + */ + protected $exchange; + /** @var array */ + private $extraAttributes = []; + + /** + * @return array + */ + public function getExtraAttributes(): array + { + return $this->extraAttributes; + } + + /** + * Configure extra attributes to pass to the AMQPExchange (if you are using the amqp extension) + * + * @param array $extraAttributes One of content_type, content_encoding, + * message_id, user_id, app_id, delivery_mode, + * priority, timestamp, expiration, type + * or reply_to, headers. + * @return AmqpHandler + */ + public function setExtraAttributes(array $extraAttributes): self + { + $this->extraAttributes = $extraAttributes; + return $this; + } + + /** + * @var string + */ + protected $exchangeName; + + /** + * @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use + * @param string|null $exchangeName Optional exchange name, for AMQPChannel (PhpAmqpLib) only + */ + public function __construct($exchange, ?string $exchangeName = null, $level = Logger::DEBUG, bool $bubble = true) + { + if ($exchange instanceof AMQPChannel) { + $this->exchangeName = (string) $exchangeName; + } elseif (!$exchange instanceof AMQPExchange) { + throw new \InvalidArgumentException('PhpAmqpLib\Channel\AMQPChannel or AMQPExchange instance required'); + } elseif ($exchangeName) { + @trigger_error('The $exchangeName parameter can only be passed when using PhpAmqpLib, if using an AMQPExchange instance configure it beforehand', E_USER_DEPRECATED); + } + $this->exchange = $exchange; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $data = $record["formatted"]; + $routingKey = $this->getRoutingKey($record); + + if ($this->exchange instanceof AMQPExchange) { + $attributes = [ + 'delivery_mode' => 2, + 'content_type' => 'application/json', + ]; + if ($this->extraAttributes) { + $attributes = array_merge($attributes, $this->extraAttributes); + } + $this->exchange->publish( + $data, + $routingKey, + 0, + $attributes + ); + } else { + $this->exchange->basic_publish( + $this->createAmqpMessage($data), + $this->exchangeName, + $routingKey + ); + } + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if ($this->exchange instanceof AMQPExchange) { + parent::handleBatch($records); + + return; + } + + foreach ($records as $record) { + if (!$this->isHandling($record)) { + continue; + } + + /** @var Record $record */ + $record = $this->processRecord($record); + $data = $this->getFormatter()->format($record); + + $this->exchange->batch_basic_publish( + $this->createAmqpMessage($data), + $this->exchangeName, + $this->getRoutingKey($record) + ); + } + + $this->exchange->publish_batch(); + } + + /** + * Gets the routing key for the AMQP exchange + * + * @phpstan-param Record $record + */ + protected function getRoutingKey(array $record): string + { + $routingKey = sprintf('%s.%s', $record['level_name'], $record['channel']); + + return strtolower($routingKey); + } + + private function createAmqpMessage(string $data): AMQPMessage + { + $attributes = [ + 'delivery_mode' => 2, + 'content_type' => 'application/json', + ]; + if ($this->extraAttributes) { + $attributes = array_merge($attributes, $this->extraAttributes); + } + return new AMQPMessage($data, $attributes); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php new file mode 100644 index 0000000..95bbfed --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php @@ -0,0 +1,308 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; +use Monolog\Utils; +use Monolog\Logger; + +use function count; +use function headers_list; +use function stripos; +use function trigger_error; + +use const E_USER_DEPRECATED; + +/** + * Handler sending logs to browser's javascript console with no browser extension required + * + * @author Olivier Poitrey + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class BrowserConsoleHandler extends AbstractProcessingHandler +{ + /** @var bool */ + protected static $initialized = false; + /** @var FormattedRecord[] */ + protected static $records = []; + + protected const FORMAT_HTML = 'html'; + protected const FORMAT_JS = 'js'; + protected const FORMAT_UNKNOWN = 'unknown'; + + /** + * {@inheritDoc} + * + * Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format. + * + * Example of formatted string: + * + * You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%'); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + // Accumulate records + static::$records[] = $record; + + // Register shutdown handler if not already done + if (!static::$initialized) { + static::$initialized = true; + $this->registerShutdownFunction(); + } + } + + /** + * Convert records to javascript console commands and send it to the browser. + * This method is automatically called on PHP shutdown if output is HTML or Javascript. + */ + public static function send(): void + { + $format = static::getResponseFormat(); + if ($format === self::FORMAT_UNKNOWN) { + return; + } + + if (count(static::$records)) { + if ($format === self::FORMAT_HTML) { + static::writeOutput(''); + } elseif ($format === self::FORMAT_JS) { + static::writeOutput(static::generateScript()); + } + static::resetStatic(); + } + } + + public function close(): void + { + self::resetStatic(); + } + + public function reset() + { + parent::reset(); + + self::resetStatic(); + } + + /** + * Forget all logged records + */ + public static function resetStatic(): void + { + static::$records = []; + } + + /** + * Wrapper for register_shutdown_function to allow overriding + */ + protected function registerShutdownFunction(): void + { + if (PHP_SAPI !== 'cli') { + register_shutdown_function(['Monolog\Handler\BrowserConsoleHandler', 'send']); + } + } + + /** + * Wrapper for echo to allow overriding + */ + protected static function writeOutput(string $str): void + { + echo $str; + } + + /** + * Checks the format of the response + * + * If Content-Type is set to application/javascript or text/javascript -> js + * If Content-Type is set to text/html, or is unset -> html + * If Content-Type is anything else -> unknown + * + * @return string One of 'js', 'html' or 'unknown' + * @phpstan-return self::FORMAT_* + */ + protected static function getResponseFormat(): string + { + // Check content type + foreach (headers_list() as $header) { + if (stripos($header, 'content-type:') === 0) { + return static::getResponseFormatFromContentType($header); + } + } + + return self::FORMAT_HTML; + } + + /** + * @return string One of 'js', 'html' or 'unknown' + * @phpstan-return self::FORMAT_* + */ + protected static function getResponseFormatFromContentType(string $contentType): string + { + // This handler only works with HTML and javascript outputs + // text/javascript is obsolete in favour of application/javascript, but still used + if (stripos($contentType, 'application/javascript') !== false || stripos($contentType, 'text/javascript') !== false) { + return self::FORMAT_JS; + } + + if (stripos($contentType, 'text/html') !== false) { + return self::FORMAT_HTML; + } + + return self::FORMAT_UNKNOWN; + } + + private static function generateScript(): string + { + $script = []; + foreach (static::$records as $record) { + $context = static::dump('Context', $record['context']); + $extra = static::dump('Extra', $record['extra']); + + if (empty($context) && empty($extra)) { + $script[] = static::call_array(static::getConsoleMethodForLevel($record['level']), static::handleStyles($record['formatted'])); + } else { + $script = array_merge( + $script, + [static::call_array('groupCollapsed', static::handleStyles($record['formatted']))], + $context, + $extra, + [static::call('groupEnd')] + ); + } + } + + return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);"; + } + + private static function getConsoleMethodForLevel(int $level): string + { + return [ + Logger::DEBUG => 'debug', + Logger::INFO => 'info', + Logger::NOTICE => 'info', + Logger::WARNING => 'warn', + Logger::ERROR => 'error', + Logger::CRITICAL => 'error', + Logger::ALERT => 'error', + Logger::EMERGENCY => 'error', + ][$level] ?? 'log'; + } + + /** + * @return string[] + */ + private static function handleStyles(string $formatted): array + { + $args = []; + $format = '%c' . $formatted; + preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); + + foreach (array_reverse($matches) as $match) { + $args[] = '"font-weight: normal"'; + $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0])); + + $pos = $match[0][1]; + $format = Utils::substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . Utils::substr($format, $pos + strlen($match[0][0])); + } + + $args[] = static::quote('font-weight: normal'); + $args[] = static::quote($format); + + return array_reverse($args); + } + + private static function handleCustomStyles(string $style, string $string): string + { + static $colors = ['blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey']; + static $labels = []; + + $style = preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function (array $m) use ($string, &$colors, &$labels) { + if (trim($m[1]) === 'autolabel') { + // Format the string as a label with consistent auto assigned background color + if (!isset($labels[$string])) { + $labels[$string] = $colors[count($labels) % count($colors)]; + } + $color = $labels[$string]; + + return "background-color: $color; color: white; border-radius: 3px; padding: 0 2px 0 2px"; + } + + return $m[1]; + }, $style); + + if (null === $style) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Failed to run preg_replace_callback: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode)); + } + + return $style; + } + + /** + * @param mixed[] $dict + * @return mixed[] + */ + private static function dump(string $title, array $dict): array + { + $script = []; + $dict = array_filter($dict); + if (empty($dict)) { + return $script; + } + $script[] = static::call('log', static::quote('%c%s'), static::quote('font-weight: bold'), static::quote($title)); + foreach ($dict as $key => $value) { + $value = json_encode($value); + if (empty($value)) { + $value = static::quote(''); + } + $script[] = static::call('log', static::quote('%s: %o'), static::quote((string) $key), $value); + } + + return $script; + } + + private static function quote(string $arg): string + { + return '"' . addcslashes($arg, "\"\n\\") . '"'; + } + + /** + * @param mixed $args + */ + private static function call(...$args): string + { + $method = array_shift($args); + if (!is_string($method)) { + throw new \UnexpectedValueException('Expected the first arg to be a string, got: '.var_export($method, true)); + } + + return static::call_array($method, $args); + } + + /** + * @param mixed[] $args + */ + private static function call_array(string $method, array $args): string + { + return 'c.' . $method . '(' . implode(', ', $args) . ');'; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php new file mode 100644 index 0000000..fcce5d6 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; + +/** + * Buffers all records until closing the handler and then pass them as batch. + * + * This is useful for a MailHandler to send only one mail per request instead of + * sending one per log message. + * + * @author Christophe Coevoet + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class BufferHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + + /** @var HandlerInterface */ + protected $handler; + /** @var int */ + protected $bufferSize = 0; + /** @var int */ + protected $bufferLimit; + /** @var bool */ + protected $flushOnOverflow; + /** @var Record[] */ + protected $buffer = []; + /** @var bool */ + protected $initialized = false; + + /** + * @param HandlerInterface $handler Handler. + * @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded + */ + public function __construct(HandlerInterface $handler, int $bufferLimit = 0, $level = Logger::DEBUG, bool $bubble = true, bool $flushOnOverflow = false) + { + parent::__construct($level, $bubble); + $this->handler = $handler; + $this->bufferLimit = $bufferLimit; + $this->flushOnOverflow = $flushOnOverflow; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($record['level'] < $this->level) { + return false; + } + + if (!$this->initialized) { + // __destructor() doesn't get called on Fatal errors + register_shutdown_function([$this, 'close']); + $this->initialized = true; + } + + if ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) { + if ($this->flushOnOverflow) { + $this->flush(); + } else { + array_shift($this->buffer); + $this->bufferSize--; + } + } + + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $this->buffer[] = $record; + $this->bufferSize++; + + return false === $this->bubble; + } + + public function flush(): void + { + if ($this->bufferSize === 0) { + return; + } + + $this->handler->handleBatch($this->buffer); + $this->clear(); + } + + public function __destruct() + { + // suppress the parent behavior since we already have register_shutdown_function() + // to call close(), and the reference contained there will prevent this from being + // GC'd until the end of the request + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + $this->flush(); + + $this->handler->close(); + } + + /** + * Clears the buffer without flushing any messages down to the wrapped handler. + */ + public function clear(): void + { + $this->bufferSize = 0; + $this->buffer = []; + } + + public function reset() + { + $this->flush(); + + parent::reset(); + + $this->resetProcessors(); + + if ($this->handler instanceof ResettableInterface) { + $this->handler->reset(); + } + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + $this->handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + return $this->handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php new file mode 100644 index 0000000..234ecf6 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php @@ -0,0 +1,196 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\ChromePHPFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) + * + * This also works out of the box with Firefox 43+ + * + * @author Christophe Coevoet + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class ChromePHPHandler extends AbstractProcessingHandler +{ + use WebRequestRecognizerTrait; + + /** + * Version of the extension + */ + protected const VERSION = '4.0'; + + /** + * Header name + */ + protected const HEADER_NAME = 'X-ChromeLogger-Data'; + + /** + * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+) + */ + protected const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}'; + + /** @var bool */ + protected static $initialized = false; + + /** + * Tracks whether we sent too much data + * + * Chrome limits the headers to 4KB, so when we sent 3KB we stop sending + * + * @var bool + */ + protected static $overflowed = false; + + /** @var mixed[] */ + protected static $json = [ + 'version' => self::VERSION, + 'columns' => ['label', 'log', 'backtrace', 'type'], + 'rows' => [], + ]; + + /** @var bool */ + protected static $sendHeaders = true; + + public function __construct($level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + if (!function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s ChromePHPHandler'); + } + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if (!$this->isWebRequest()) { + return; + } + + $messages = []; + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + /** @var Record $message */ + $message = $this->processRecord($record); + $messages[] = $message; + } + + if (!empty($messages)) { + $messages = $this->getFormatter()->formatBatch($messages); + self::$json['rows'] = array_merge(self::$json['rows'], $messages); + $this->send(); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new ChromePHPFormatter(); + } + + /** + * Creates & sends header for a record + * + * @see sendHeader() + * @see send() + */ + protected function write(array $record): void + { + if (!$this->isWebRequest()) { + return; + } + + self::$json['rows'][] = $record['formatted']; + + $this->send(); + } + + /** + * Sends the log header + * + * @see sendHeader() + */ + protected function send(): void + { + if (self::$overflowed || !self::$sendHeaders) { + return; + } + + if (!self::$initialized) { + self::$initialized = true; + + self::$sendHeaders = $this->headersAccepted(); + if (!self::$sendHeaders) { + return; + } + + self::$json['request_uri'] = $_SERVER['REQUEST_URI'] ?? ''; + } + + $json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true); + $data = base64_encode($json); + if (strlen($data) > 3 * 1024) { + self::$overflowed = true; + + $record = [ + 'message' => 'Incomplete logs, chrome header size limit reached', + 'context' => [], + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'monolog', + 'datetime' => new \DateTimeImmutable(), + 'extra' => [], + ]; + self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record); + $json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true); + $data = base64_encode($json); + } + + if (trim($data) !== '') { + $this->sendHeader(static::HEADER_NAME, $data); + } + } + + /** + * Send header string to the client + */ + protected function sendHeader(string $header, string $content): void + { + if (!headers_sent() && self::$sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Verifies if the headers are accepted by the current user agent + */ + protected function headersAccepted(): bool + { + if (empty($_SERVER['HTTP_USER_AGENT'])) { + return false; + } + + return preg_match(static::USER_AGENT_REGEX, $_SERVER['HTTP_USER_AGENT']) === 1; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php new file mode 100644 index 0000000..5265761 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\JsonFormatter; +use Monolog\Logger; + +/** + * CouchDB handler + * + * @author Markus Bachmann + */ +class CouchDBHandler extends AbstractProcessingHandler +{ + /** @var mixed[] */ + private $options; + + /** + * @param mixed[] $options + */ + public function __construct(array $options = [], $level = Logger::DEBUG, bool $bubble = true) + { + $this->options = array_merge([ + 'host' => 'localhost', + 'port' => 5984, + 'dbname' => 'logger', + 'username' => null, + 'password' => null, + ], $options); + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $basicAuth = null; + if ($this->options['username']) { + $basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']); + } + + $url = 'http://'.$basicAuth.$this->options['host'].':'.$this->options['port'].'/'.$this->options['dbname']; + $context = stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'content' => $record['formatted'], + 'ignore_errors' => true, + 'max_redirects' => 0, + 'header' => 'Content-type: application/json', + ], + ]); + + if (false === @file_get_contents($url, false, $context)) { + throw new \RuntimeException(sprintf('Could not connect to %s', $url)); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php new file mode 100644 index 0000000..3535a4f --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * Logs to Cube. + * + * @link https://github.com/square/cube/wiki + * @author Wan Chen + * @deprecated Since 2.8.0 and 3.2.0, Cube appears abandoned and thus we will drop this handler in Monolog 4 + */ +class CubeHandler extends AbstractProcessingHandler +{ + /** @var resource|\Socket|null */ + private $udpConnection = null; + /** @var resource|\CurlHandle|null */ + private $httpConnection = null; + /** @var string */ + private $scheme; + /** @var string */ + private $host; + /** @var int */ + private $port; + /** @var string[] */ + private $acceptedSchemes = ['http', 'udp']; + + /** + * Create a Cube handler + * + * @throws \UnexpectedValueException when given url is not a valid url. + * A valid url must consist of three parts : protocol://host:port + * Only valid protocols used by Cube are http and udp + */ + public function __construct(string $url, $level = Logger::DEBUG, bool $bubble = true) + { + $urlInfo = parse_url($url); + + if ($urlInfo === false || !isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) { + throw new \UnexpectedValueException('URL "'.$url.'" is not valid'); + } + + if (!in_array($urlInfo['scheme'], $this->acceptedSchemes)) { + throw new \UnexpectedValueException( + 'Invalid protocol (' . $urlInfo['scheme'] . ').' + . ' Valid options are ' . implode(', ', $this->acceptedSchemes) + ); + } + + $this->scheme = $urlInfo['scheme']; + $this->host = $urlInfo['host']; + $this->port = (int) $urlInfo['port']; + + parent::__construct($level, $bubble); + } + + /** + * Establish a connection to an UDP socket + * + * @throws \LogicException when unable to connect to the socket + * @throws MissingExtensionException when there is no socket extension + */ + protected function connectUdp(): void + { + if (!extension_loaded('sockets')) { + throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler'); + } + + $udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0); + if (false === $udpConnection) { + throw new \LogicException('Unable to create a socket'); + } + + $this->udpConnection = $udpConnection; + if (!socket_connect($this->udpConnection, $this->host, $this->port)) { + throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port); + } + } + + /** + * Establish a connection to an http server + * + * @throws \LogicException when unable to connect to the socket + * @throws MissingExtensionException when no curl extension + */ + protected function connectHttp(): void + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is required to use http URLs with the CubeHandler'); + } + + $httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put'); + if (false === $httpConnection) { + throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port); + } + + $this->httpConnection = $httpConnection; + curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST"); + curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $date = $record['datetime']; + + $data = ['time' => $date->format('Y-m-d\TH:i:s.uO')]; + unset($record['datetime']); + + if (isset($record['context']['type'])) { + $data['type'] = $record['context']['type']; + unset($record['context']['type']); + } else { + $data['type'] = $record['channel']; + } + + $data['data'] = $record['context']; + $data['data']['level'] = $record['level']; + + if ($this->scheme === 'http') { + $this->writeHttp(Utils::jsonEncode($data)); + } else { + $this->writeUdp(Utils::jsonEncode($data)); + } + } + + private function writeUdp(string $data): void + { + if (!$this->udpConnection) { + $this->connectUdp(); + } + + socket_send($this->udpConnection, $data, strlen($data), 0); + } + + private function writeHttp(string $data): void + { + if (!$this->httpConnection) { + $this->connectHttp(); + } + + if (null === $this->httpConnection) { + throw new \LogicException('No connection could be established'); + } + + curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']'); + curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json', + 'Content-Length: ' . strlen('['.$data.']'), + ]); + + Curl\Util::execute($this->httpConnection, 5, false); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php b/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php new file mode 100644 index 0000000..7213e8e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\Curl; + +use CurlHandle; + +/** + * This class is marked as internal and it is not under the BC promise of the package. + * + * @internal + */ +final class Util +{ + /** @var array */ + private static $retriableErrorCodes = [ + CURLE_COULDNT_RESOLVE_HOST, + CURLE_COULDNT_CONNECT, + CURLE_HTTP_NOT_FOUND, + CURLE_READ_ERROR, + CURLE_OPERATION_TIMEOUTED, + CURLE_HTTP_POST_ERROR, + CURLE_SSL_CONNECT_ERROR, + ]; + + /** + * Executes a CURL request with optional retries and exception on failure + * + * @param resource|CurlHandle $ch curl handler + * @param int $retries + * @param bool $closeAfterDone + * @return bool|string @see curl_exec + */ + public static function execute($ch, int $retries = 5, bool $closeAfterDone = true) + { + while ($retries--) { + $curlResponse = curl_exec($ch); + if ($curlResponse === false) { + $curlErrno = curl_errno($ch); + + if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || !$retries) { + $curlError = curl_error($ch); + + if ($closeAfterDone) { + curl_close($ch); + } + + throw new \RuntimeException(sprintf('Curl error (code %d): %s', $curlErrno, $curlError)); + } + + continue; + } + + if ($closeAfterDone) { + curl_close($ch); + } + + return $curlResponse; + } + + return false; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php new file mode 100644 index 0000000..9b85ae7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Simple handler wrapper that deduplicates log records across multiple requests + * + * It also includes the BufferHandler functionality and will buffer + * all messages until the end of the request or flush() is called. + * + * This works by storing all log records' messages above $deduplicationLevel + * to the file specified by $deduplicationStore. When further logs come in at the end of the + * request (or when flush() is called), all those above $deduplicationLevel are checked + * against the existing stored logs. If they match and the timestamps in the stored log is + * not older than $time seconds, the new log record is discarded. If no log record is new, the + * whole data set is discarded. + * + * This is mainly useful in combination with Mail handlers or things like Slack or HipChat handlers + * that send messages to people, to avoid spamming with the same message over and over in case of + * a major component failure like a database server being down which makes all requests fail in the + * same way. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +class DeduplicationHandler extends BufferHandler +{ + /** + * @var string + */ + protected $deduplicationStore; + + /** + * @var Level + */ + protected $deduplicationLevel; + + /** + * @var int + */ + protected $time; + + /** + * @var bool + */ + private $gc = false; + + /** + * @param HandlerInterface $handler Handler. + * @param string $deduplicationStore The file/path where the deduplication log should be kept + * @param string|int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes + * @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @phpstan-param Level|LevelName|LogLevel::* $deduplicationLevel + */ + public function __construct(HandlerInterface $handler, ?string $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, int $time = 60, bool $bubble = true) + { + parent::__construct($handler, 0, Logger::DEBUG, $bubble, false); + + $this->deduplicationStore = $deduplicationStore === null ? sys_get_temp_dir() . '/monolog-dedup-' . substr(md5(__FILE__), 0, 20) .'.log' : $deduplicationStore; + $this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel); + $this->time = $time; + } + + public function flush(): void + { + if ($this->bufferSize === 0) { + return; + } + + $passthru = null; + + foreach ($this->buffer as $record) { + if ($record['level'] >= $this->deduplicationLevel) { + $passthru = $passthru || !$this->isDuplicate($record); + if ($passthru) { + $this->appendRecord($record); + } + } + } + + // default of null is valid as well as if no record matches duplicationLevel we just pass through + if ($passthru === true || $passthru === null) { + $this->handler->handleBatch($this->buffer); + } + + $this->clear(); + + if ($this->gc) { + $this->collectLogs(); + } + } + + /** + * @phpstan-param Record $record + */ + private function isDuplicate(array $record): bool + { + if (!file_exists($this->deduplicationStore)) { + return false; + } + + $store = file($this->deduplicationStore, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if (!is_array($store)) { + return false; + } + + $yesterday = time() - 86400; + $timestampValidity = $record['datetime']->getTimestamp() - $this->time; + $expectedMessage = preg_replace('{[\r\n].*}', '', $record['message']); + + for ($i = count($store) - 1; $i >= 0; $i--) { + list($timestamp, $level, $message) = explode(':', $store[$i], 3); + + if ($level === $record['level_name'] && $message === $expectedMessage && $timestamp > $timestampValidity) { + return true; + } + + if ($timestamp < $yesterday) { + $this->gc = true; + } + } + + return false; + } + + private function collectLogs(): void + { + if (!file_exists($this->deduplicationStore)) { + return; + } + + $handle = fopen($this->deduplicationStore, 'rw+'); + + if (!$handle) { + throw new \RuntimeException('Failed to open file for reading and writing: ' . $this->deduplicationStore); + } + + flock($handle, LOCK_EX); + $validLogs = []; + + $timestampValidity = time() - $this->time; + + while (!feof($handle)) { + $log = fgets($handle); + if ($log && substr($log, 0, 10) >= $timestampValidity) { + $validLogs[] = $log; + } + } + + ftruncate($handle, 0); + rewind($handle); + foreach ($validLogs as $log) { + fwrite($handle, $log); + } + + flock($handle, LOCK_UN); + fclose($handle); + + $this->gc = false; + } + + /** + * @phpstan-param Record $record + */ + private function appendRecord(array $record): void + { + file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . preg_replace('{[\r\n].*}', '', $record['message']) . "\n", FILE_APPEND); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php new file mode 100644 index 0000000..ebd52c3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Formatter\FormatterInterface; +use Doctrine\CouchDB\CouchDBClient; + +/** + * CouchDB handler for Doctrine CouchDB ODM + * + * @author Markus Bachmann + */ +class DoctrineCouchDBHandler extends AbstractProcessingHandler +{ + /** @var CouchDBClient */ + private $client; + + public function __construct(CouchDBClient $client, $level = Logger::DEBUG, bool $bubble = true) + { + $this->client = $client; + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->client->postDocument($record['formatted']); + } + + protected function getDefaultFormatter(): FormatterInterface + { + return new NormalizerFormatter; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php new file mode 100644 index 0000000..21840bf --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Aws\Sdk; +use Aws\DynamoDb\DynamoDbClient; +use Monolog\Formatter\FormatterInterface; +use Aws\DynamoDb\Marshaler; +use Monolog\Formatter\ScalarFormatter; +use Monolog\Logger; + +/** + * Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/) + * + * @link https://github.com/aws/aws-sdk-php/ + * @author Andrew Lawson + */ +class DynamoDbHandler extends AbstractProcessingHandler +{ + public const DATE_FORMAT = 'Y-m-d\TH:i:s.uO'; + + /** + * @var DynamoDbClient + */ + protected $client; + + /** + * @var string + */ + protected $table; + + /** + * @var int + */ + protected $version; + + /** + * @var Marshaler + */ + protected $marshaler; + + public function __construct(DynamoDbClient $client, string $table, $level = Logger::DEBUG, bool $bubble = true) + { + /** @phpstan-ignore-next-line */ + if (defined('Aws\Sdk::VERSION') && version_compare(Sdk::VERSION, '3.0', '>=')) { + $this->version = 3; + $this->marshaler = new Marshaler; + } else { + $this->version = 2; + } + + $this->client = $client; + $this->table = $table; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $filtered = $this->filterEmptyFields($record['formatted']); + if ($this->version === 3) { + $formatted = $this->marshaler->marshalItem($filtered); + } else { + /** @phpstan-ignore-next-line */ + $formatted = $this->client->formatAttributes($filtered); + } + + $this->client->putItem([ + 'TableName' => $this->table, + 'Item' => $formatted, + ]); + } + + /** + * @param mixed[] $record + * @return mixed[] + */ + protected function filterEmptyFields(array $record): array + { + return array_filter($record, function ($value) { + return !empty($value) || false === $value || 0 === $value; + }); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new ScalarFormatter(self::DATE_FORMAT); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php new file mode 100644 index 0000000..fc92ca4 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Elastica\Document; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\ElasticaFormatter; +use Monolog\Logger; +use Elastica\Client; +use Elastica\Exception\ExceptionInterface; + +/** + * Elastic Search handler + * + * Usage example: + * + * $client = new \Elastica\Client(); + * $options = array( + * 'index' => 'elastic_index_name', + * 'type' => 'elastic_doc_type', Types have been removed in Elastica 7 + * ); + * $handler = new ElasticaHandler($client, $options); + * $log = new Logger('application'); + * $log->pushHandler($handler); + * + * @author Jelle Vink + */ +class ElasticaHandler extends AbstractProcessingHandler +{ + /** + * @var Client + */ + protected $client; + + /** + * @var mixed[] Handler config options + */ + protected $options = []; + + /** + * @param Client $client Elastica Client object + * @param mixed[] $options Handler configuration + */ + public function __construct(Client $client, array $options = [], $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + $this->client = $client; + $this->options = array_merge( + [ + 'index' => 'monolog', // Elastic index name + 'type' => 'record', // Elastic document type + 'ignore_error' => false, // Suppress Elastica exceptions + ], + $options + ); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->bulkSend([$record['formatted']]); + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($formatter instanceof ElasticaFormatter) { + return parent::setFormatter($formatter); + } + + throw new \InvalidArgumentException('ElasticaHandler is only compatible with ElasticaFormatter'); + } + + /** + * @return mixed[] + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new ElasticaFormatter($this->options['index'], $this->options['type']); + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $documents = $this->getFormatter()->formatBatch($records); + $this->bulkSend($documents); + } + + /** + * Use Elasticsearch bulk API to send list of documents + * + * @param Document[] $documents + * + * @throws \RuntimeException + */ + protected function bulkSend(array $documents): void + { + try { + $this->client->addDocuments($documents); + } catch (ExceptionInterface $e) { + if (!$this->options['ignore_error']) { + throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e); + } + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php new file mode 100644 index 0000000..e88375c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Elastic\Elasticsearch\Response\Elasticsearch; +use Throwable; +use RuntimeException; +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\ElasticsearchFormatter; +use InvalidArgumentException; +use Elasticsearch\Common\Exceptions\RuntimeException as ElasticsearchRuntimeException; +use Elasticsearch\Client; +use Elastic\Elasticsearch\Exception\InvalidArgumentException as ElasticInvalidArgumentException; +use Elastic\Elasticsearch\Client as Client8; + +/** + * Elasticsearch handler + * + * @link https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/index.html + * + * Simple usage example: + * + * $client = \Elasticsearch\ClientBuilder::create() + * ->setHosts($hosts) + * ->build(); + * + * $options = array( + * 'index' => 'elastic_index_name', + * 'type' => 'elastic_doc_type', + * ); + * $handler = new ElasticsearchHandler($client, $options); + * $log = new Logger('application'); + * $log->pushHandler($handler); + * + * @author Avtandil Kikabidze + */ +class ElasticsearchHandler extends AbstractProcessingHandler +{ + /** + * @var Client|Client8 + */ + protected $client; + + /** + * @var mixed[] Handler config options + */ + protected $options = []; + + /** + * @var bool + */ + private $needsType; + + /** + * @param Client|Client8 $client Elasticsearch Client object + * @param mixed[] $options Handler configuration + */ + public function __construct($client, array $options = [], $level = Logger::DEBUG, bool $bubble = true) + { + if (!$client instanceof Client && !$client instanceof Client8) { + throw new \TypeError('Elasticsearch\Client or Elastic\Elasticsearch\Client instance required'); + } + + parent::__construct($level, $bubble); + $this->client = $client; + $this->options = array_merge( + [ + 'index' => 'monolog', // Elastic index name + 'type' => '_doc', // Elastic document type + 'ignore_error' => false, // Suppress Elasticsearch exceptions + ], + $options + ); + + if ($client instanceof Client8 || $client::VERSION[0] === '7') { + $this->needsType = false; + // force the type to _doc for ES8/ES7 + $this->options['type'] = '_doc'; + } else { + $this->needsType = true; + } + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->bulkSend([$record['formatted']]); + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($formatter instanceof ElasticsearchFormatter) { + return parent::setFormatter($formatter); + } + + throw new InvalidArgumentException('ElasticsearchHandler is only compatible with ElasticsearchFormatter'); + } + + /** + * Getter options + * + * @return mixed[] + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new ElasticsearchFormatter($this->options['index'], $this->options['type']); + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $documents = $this->getFormatter()->formatBatch($records); + $this->bulkSend($documents); + } + + /** + * Use Elasticsearch bulk API to send list of documents + * + * @param array[] $records Records + _index/_type keys + * @throws \RuntimeException + */ + protected function bulkSend(array $records): void + { + try { + $params = [ + 'body' => [], + ]; + + foreach ($records as $record) { + $params['body'][] = [ + 'index' => $this->needsType ? [ + '_index' => $record['_index'], + '_type' => $record['_type'], + ] : [ + '_index' => $record['_index'], + ], + ]; + unset($record['_index'], $record['_type']); + + $params['body'][] = $record; + } + + /** @var Elasticsearch */ + $responses = $this->client->bulk($params); + + if ($responses['errors'] === true) { + throw $this->createExceptionFromResponses($responses); + } + } catch (Throwable $e) { + if (! $this->options['ignore_error']) { + throw new RuntimeException('Error sending messages to Elasticsearch', 0, $e); + } + } + } + + /** + * Creates elasticsearch exception from responses array + * + * Only the first error is converted into an exception. + * + * @param mixed[]|Elasticsearch $responses returned by $this->client->bulk() + */ + protected function createExceptionFromResponses($responses): Throwable + { + foreach ($responses['items'] ?? [] as $item) { + if (isset($item['index']['error'])) { + return $this->createExceptionFromError($item['index']['error']); + } + } + + if (class_exists(ElasticInvalidArgumentException::class)) { + return new ElasticInvalidArgumentException('Elasticsearch failed to index one or more records.'); + } + + return new ElasticsearchRuntimeException('Elasticsearch failed to index one or more records.'); + } + + /** + * Creates elasticsearch exception from error array + * + * @param mixed[] $error + */ + protected function createExceptionFromError(array $error): Throwable + { + $previous = isset($error['caused_by']) ? $this->createExceptionFromError($error['caused_by']) : null; + + if (class_exists(ElasticInvalidArgumentException::class)) { + return new ElasticInvalidArgumentException($error['type'] . ': ' . $error['reason'], 0, $previous); + } + + return new ElasticsearchRuntimeException($error['type'] . ': ' . $error['reason'], 0, $previous); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php new file mode 100644 index 0000000..f2e2203 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Stores to PHP error_log() handler. + * + * @author Elan Ruusamäe + */ +class ErrorLogHandler extends AbstractProcessingHandler +{ + public const OPERATING_SYSTEM = 0; + public const SAPI = 4; + + /** @var int */ + protected $messageType; + /** @var bool */ + protected $expandNewlines; + + /** + * @param int $messageType Says where the error should go. + * @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries + */ + public function __construct(int $messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, bool $bubble = true, bool $expandNewlines = false) + { + parent::__construct($level, $bubble); + + if (false === in_array($messageType, self::getAvailableTypes(), true)) { + $message = sprintf('The given message type "%s" is not supported', print_r($messageType, true)); + + throw new \InvalidArgumentException($message); + } + + $this->messageType = $messageType; + $this->expandNewlines = $expandNewlines; + } + + /** + * @return int[] With all available types + */ + public static function getAvailableTypes(): array + { + return [ + self::OPERATING_SYSTEM, + self::SAPI, + ]; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%'); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!$this->expandNewlines) { + error_log((string) $record['formatted'], $this->messageType); + + return; + } + + $lines = preg_split('{[\r\n]+}', (string) $record['formatted']); + if ($lines === false) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Failed to preg_split formatted string: ' . $pcreErrorCode . ' / '. Utils::pcreLastErrorMessage($pcreErrorCode)); + } + foreach ($lines as $line) { + error_log($line, $this->messageType); + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php new file mode 100644 index 0000000..d4e234c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Throwable; + +/** + * Forwards records to at most one handler + * + * If a handler fails, the exception is suppressed and the record is forwarded to the next handler. + * + * As soon as one handler handles a record successfully, the handling stops there. + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class FallbackGroupHandler extends GroupHandler +{ + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + foreach ($this->handlers as $handler) { + try { + $handler->handle($record); + break; + } catch (Throwable $e) { + // What throwable? + } + } + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if ($this->processors) { + $processed = []; + foreach ($records as $record) { + $processed[] = $this->processRecord($record); + } + /** @var Record[] $records */ + $records = $processed; + } + + foreach ($this->handlers as $handler) { + try { + $handler->handleBatch($records); + break; + } catch (Throwable $e) { + // What throwable? + } + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php new file mode 100644 index 0000000..5e43e1d --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; +use Psr\Log\LogLevel; + +/** + * Simple handler wrapper that filters records based on a list of levels + * + * It can be configured with an exact list of levels to allow, or a min/max level. + * + * @author Hennadiy Verkh + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class FilterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + + /** + * Handler or factory callable($record, $this) + * + * @var callable|HandlerInterface + * @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface + */ + protected $handler; + + /** + * Minimum level for logs that are passed to handler + * + * @var int[] + * @phpstan-var array + */ + protected $acceptedLevels; + + /** + * Whether the messages that are handled can bubble up the stack or not + * + * @var bool + */ + protected $bubble; + + /** + * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler + * + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler). + * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided + * @param int|string $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @phpstan-param Level|LevelName|LogLevel::*|array $minLevelOrList + * @phpstan-param Level|LevelName|LogLevel::* $maxLevel + */ + public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, bool $bubble = true) + { + $this->handler = $handler; + $this->bubble = $bubble; + $this->setAcceptedLevels($minLevelOrList, $maxLevel); + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + /** + * @phpstan-return array + */ + public function getAcceptedLevels(): array + { + return array_flip($this->acceptedLevels); + } + + /** + * @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided + * @param int|string $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array + * + * @phpstan-param Level|LevelName|LogLevel::*|array $minLevelOrList + * @phpstan-param Level|LevelName|LogLevel::* $maxLevel + */ + public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY): self + { + if (is_array($minLevelOrList)) { + $acceptedLevels = array_map('Monolog\Logger::toMonologLevel', $minLevelOrList); + } else { + $minLevelOrList = Logger::toMonologLevel($minLevelOrList); + $maxLevel = Logger::toMonologLevel($maxLevel); + $acceptedLevels = array_values(array_filter(Logger::getLevels(), function ($level) use ($minLevelOrList, $maxLevel) { + return $level >= $minLevelOrList && $level <= $maxLevel; + })); + } + $this->acceptedLevels = array_flip($acceptedLevels); + + return $this; + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return isset($this->acceptedLevels[$record['level']]); + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if (!$this->isHandling($record)) { + return false; + } + + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $this->getHandler($record)->handle($record); + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $filtered = []; + foreach ($records as $record) { + if ($this->isHandling($record)) { + $filtered[] = $record; + } + } + + if (count($filtered) > 0) { + $this->getHandler($filtered[count($filtered) - 1])->handleBatch($filtered); + } + } + + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @return HandlerInterface + * + * @phpstan-param Record $record + */ + public function getHandler(?array $record = null) + { + if (!$this->handler instanceof HandlerInterface) { + $this->handler = ($this->handler)($record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + return $this->handler; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + return $handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } + + public function reset() + { + $this->resetProcessors(); + + if ($this->getHandler() instanceof ResettableInterface) { + $this->getHandler()->reset(); + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php new file mode 100644 index 0000000..0aa5607 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +/** + * Interface for activation strategies for the FingersCrossedHandler. + * + * @author Johannes M. Schmitt + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface ActivationStrategyInterface +{ + /** + * Returns whether the given record activates the handler. + * + * @phpstan-param Record $record + */ + public function isHandlerActivated(array $record): bool; +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php new file mode 100644 index 0000000..7b9abb5 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Channel and Error level based monolog activation strategy. Allows to trigger activation + * based on level per channel. e.g. trigger activation on level 'ERROR' by default, except + * for records of the 'sql' channel; those should trigger activation on level 'WARN'. + * + * Example: + * + * + * $activationStrategy = new ChannelLevelActivationStrategy( + * Logger::CRITICAL, + * array( + * 'request' => Logger::ALERT, + * 'sensitive' => Logger::ERROR, + * ) + * ); + * $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy); + * + * + * @author Mike Meessen + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class ChannelLevelActivationStrategy implements ActivationStrategyInterface +{ + /** + * @var Level + */ + private $defaultActionLevel; + + /** + * @var array + */ + private $channelToActionLevel; + + /** + * @param int|string $defaultActionLevel The default action level to be used if the record's category doesn't match any + * @param array $channelToActionLevel An array that maps channel names to action levels. + * + * @phpstan-param array $channelToActionLevel + * @phpstan-param Level|LevelName|LogLevel::* $defaultActionLevel + */ + public function __construct($defaultActionLevel, array $channelToActionLevel = []) + { + $this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel); + $this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel); + } + + /** + * @phpstan-param Record $record + */ + public function isHandlerActivated(array $record): bool + { + if (isset($this->channelToActionLevel[$record['channel']])) { + return $record['level'] >= $this->channelToActionLevel[$record['channel']]; + } + + return $record['level'] >= $this->defaultActionLevel; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php new file mode 100644 index 0000000..5ec88ea --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Error level based activation strategy. + * + * @author Johannes M. Schmitt + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class ErrorLevelActivationStrategy implements ActivationStrategyInterface +{ + /** + * @var Level + */ + private $actionLevel; + + /** + * @param int|string $actionLevel Level or name or value + * + * @phpstan-param Level|LevelName|LogLevel::* $actionLevel + */ + public function __construct($actionLevel) + { + $this->actionLevel = Logger::toMonologLevel($actionLevel); + } + + public function isHandlerActivated(array $record): bool + { + return $record['level'] >= $this->actionLevel; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php new file mode 100644 index 0000000..dfcb3af --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php @@ -0,0 +1,252 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; +use Monolog\Logger; +use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; +use Psr\Log\LogLevel; + +/** + * Buffers all records until a certain level is reached + * + * The advantage of this approach is that you don't get any clutter in your log files. + * Only requests which actually trigger an error (or whatever your actionLevel is) will be + * in the logs, but they will contain all records, not only those above the level threshold. + * + * You can then have a passthruLevel as well which means that at the end of the request, + * even if it did not get activated, it will still send through log records of e.g. at least a + * warning level. + * + * You can find the various activation strategies in the + * Monolog\Handler\FingersCrossed\ namespace. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + + /** + * @var callable|HandlerInterface + * @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface + */ + protected $handler; + /** @var ActivationStrategyInterface */ + protected $activationStrategy; + /** @var bool */ + protected $buffering = true; + /** @var int */ + protected $bufferSize; + /** @var Record[] */ + protected $buffer = []; + /** @var bool */ + protected $stopBuffering; + /** + * @var ?int + * @phpstan-var ?Level + */ + protected $passthruLevel; + /** @var bool */ + protected $bubble; + + /** + * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler + * + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler). + * @param int|string|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated + * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true) + * @param int|string $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $passthruLevel + * @phpstan-param Level|LevelName|LogLevel::*|ActivationStrategyInterface $activationStrategy + */ + public function __construct($handler, $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, $passthruLevel = null) + { + if (null === $activationStrategy) { + $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); + } + + // convert simple int activationStrategy to an object + if (!$activationStrategy instanceof ActivationStrategyInterface) { + $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy); + } + + $this->handler = $handler; + $this->activationStrategy = $activationStrategy; + $this->bufferSize = $bufferSize; + $this->bubble = $bubble; + $this->stopBuffering = $stopBuffering; + + if ($passthruLevel !== null) { + $this->passthruLevel = Logger::toMonologLevel($passthruLevel); + } + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return true; + } + + /** + * Manually activate this logger regardless of the activation strategy + */ + public function activate(): void + { + if ($this->stopBuffering) { + $this->buffering = false; + } + + $this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer); + $this->buffer = []; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + if ($this->buffering) { + $this->buffer[] = $record; + if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { + array_shift($this->buffer); + } + if ($this->activationStrategy->isHandlerActivated($record)) { + $this->activate(); + } + } else { + $this->getHandler($record)->handle($record); + } + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + $this->flushBuffer(); + + $this->getHandler()->close(); + } + + public function reset() + { + $this->flushBuffer(); + + $this->resetProcessors(); + + if ($this->getHandler() instanceof ResettableInterface) { + $this->getHandler()->reset(); + } + } + + /** + * Clears the buffer without flushing any messages down to the wrapped handler. + * + * It also resets the handler to its initial buffering state. + */ + public function clear(): void + { + $this->buffer = []; + $this->reset(); + } + + /** + * Resets the state of the handler. Stops forwarding records to the wrapped handler. + */ + private function flushBuffer(): void + { + if (null !== $this->passthruLevel) { + $level = $this->passthruLevel; + $this->buffer = array_filter($this->buffer, function ($record) use ($level) { + return $record['level'] >= $level; + }); + if (count($this->buffer) > 0) { + $this->getHandler(end($this->buffer))->handleBatch($this->buffer); + } + } + + $this->buffer = []; + $this->buffering = true; + } + + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @return HandlerInterface + * + * @phpstan-param Record $record + */ + public function getHandler(?array $record = null) + { + if (!$this->handler instanceof HandlerInterface) { + $this->handler = ($this->handler)($record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + return $this->handler; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + return $handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php new file mode 100644 index 0000000..72718de --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\WildfireFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. + * + * @author Eric Clemmons (@ericclemmons) + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class FirePHPHandler extends AbstractProcessingHandler +{ + use WebRequestRecognizerTrait; + + /** + * WildFire JSON header message format + */ + protected const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; + + /** + * FirePHP structure for parsing messages & their presentation + */ + protected const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; + + /** + * Must reference a "known" plugin, otherwise headers won't display in FirePHP + */ + protected const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; + + /** + * Header prefix for Wildfire to recognize & parse headers + */ + protected const HEADER_PREFIX = 'X-Wf'; + + /** + * Whether or not Wildfire vendor-specific headers have been generated & sent yet + * @var bool + */ + protected static $initialized = false; + + /** + * Shared static message index between potentially multiple handlers + * @var int + */ + protected static $messageIndex = 1; + + /** @var bool */ + protected static $sendHeaders = true; + + /** + * Base header creation function used by init headers & record headers + * + * @param array $meta Wildfire Plugin, Protocol & Structure Indexes + * @param string $message Log message + * + * @return array Complete header string ready for the client as key and message as value + * + * @phpstan-return non-empty-array + */ + protected function createHeader(array $meta, string $message): array + { + $header = sprintf('%s-%s', static::HEADER_PREFIX, join('-', $meta)); + + return [$header => $message]; + } + + /** + * Creates message header from record + * + * @return array + * + * @phpstan-return non-empty-array + * + * @see createHeader() + * + * @phpstan-param FormattedRecord $record + */ + protected function createRecordHeader(array $record): array + { + // Wildfire is extensible to support multiple protocols & plugins in a single request, + // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. + return $this->createHeader( + [1, 1, 1, self::$messageIndex++], + $record['formatted'] + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new WildfireFormatter(); + } + + /** + * Wildfire initialization headers to enable message parsing + * + * @see createHeader() + * @see sendHeader() + * + * @return array + */ + protected function getInitHeaders(): array + { + // Initial payload consists of required headers for Wildfire + return array_merge( + $this->createHeader(['Protocol', 1], static::PROTOCOL_URI), + $this->createHeader([1, 'Structure', 1], static::STRUCTURE_URI), + $this->createHeader([1, 'Plugin', 1], static::PLUGIN_URI) + ); + } + + /** + * Send header string to the client + */ + protected function sendHeader(string $header, string $content): void + { + if (!headers_sent() && self::$sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Creates & sends header for a record, ensuring init headers have been sent prior + * + * @see sendHeader() + * @see sendInitHeaders() + */ + protected function write(array $record): void + { + if (!self::$sendHeaders || !$this->isWebRequest()) { + return; + } + + // WildFire-specific headers must be sent prior to any messages + if (!self::$initialized) { + self::$initialized = true; + + self::$sendHeaders = $this->headersAccepted(); + if (!self::$sendHeaders) { + return; + } + + foreach ($this->getInitHeaders() as $header => $content) { + $this->sendHeader($header, $content); + } + } + + $header = $this->createRecordHeader($record); + if (trim(current($header)) !== '') { + $this->sendHeader(key($header), current($header)); + } + } + + /** + * Verifies if the headers are accepted by the current user agent + */ + protected function headersAccepted(): bool + { + if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) { + return true; + } + + return isset($_SERVER['HTTP_X_FIREPHP_VERSION']); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php new file mode 100644 index 0000000..85c95b9 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; + +/** + * Sends logs to Fleep.io using Webhook integrations + * + * You'll need a Fleep.io account to use this handler. + * + * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation + * @author Ando Roots + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class FleepHookHandler extends SocketHandler +{ + protected const FLEEP_HOST = 'fleep.io'; + + protected const FLEEP_HOOK_URI = '/hook/'; + + /** + * @var string Webhook token (specifies the conversation where logs are sent) + */ + protected $token; + + /** + * Construct a new Fleep.io Handler. + * + * For instructions on how to create a new web hook in your conversations + * see https://fleep.io/integrations/webhooks/ + * + * @param string $token Webhook token + * @throws MissingExtensionException + */ + public function __construct( + string $token, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler'); + } + + $this->token = $token; + + $connectionString = 'ssl://' . static::FLEEP_HOST . ':443'; + parent::__construct( + $connectionString, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + } + + /** + * Returns the default formatter to use with this handler + * + * Overloaded to remove empty context and extra arrays from the end of the log message. + * + * @return LineFormatter + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(null, null, true, true); + } + + /** + * Handles a log record + */ + public function write(array $record): void + { + parent::write($record); + $this->closeSocket(); + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the header of the API Call + */ + private function buildHeader(string $content): string + { + $header = "POST " . static::FLEEP_HOOK_URI . $this->token . " HTTP/1.1\r\n"; + $header .= "Host: " . static::FLEEP_HOST . "\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + /** + * Builds the body of API call + * + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record): string + { + $dataArray = [ + 'message' => $record['formatted'], + ]; + + return http_build_query($dataArray); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php new file mode 100644 index 0000000..5715d58 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\FlowdockFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Sends notifications through the Flowdock push API + * + * This must be configured with a FlowdockFormatter instance via setFormatter() + * + * Notes: + * API token - Flowdock API token + * + * @author Dominik Liebler + * @see https://www.flowdock.com/api/push + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + * @deprecated Since 2.9.0 and 3.3.0, Flowdock was shutdown we will thus drop this handler in Monolog 4 + */ +class FlowdockHandler extends SocketHandler +{ + /** + * @var string + */ + protected $apiToken; + + /** + * @throws MissingExtensionException if OpenSSL is missing + */ + public function __construct( + string $apiToken, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler'); + } + + parent::__construct( + 'ssl://api.flowdock.com:443', + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + $this->apiToken = $apiToken; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if (!$formatter instanceof FlowdockFormatter) { + throw new \InvalidArgumentException('The FlowdockHandler requires an instance of Monolog\Formatter\FlowdockFormatter to function correctly'); + } + + return parent::setFormatter($formatter); + } + + /** + * Gets the default formatter. + */ + protected function getDefaultFormatter(): FormatterInterface + { + throw new \InvalidArgumentException('The FlowdockHandler must be configured (via setFormatter) with an instance of Monolog\Formatter\FlowdockFormatter to function correctly'); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + parent::write($record); + + $this->closeSocket(); + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the body of API call + * + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record): string + { + return Utils::jsonEncode($record['formatted']['flowdock']); + } + + /** + * Builds the header of the API Call + */ + private function buildHeader(string $content): string + { + $header = "POST /v1/messages/team_inbox/" . $this->apiToken . " HTTP/1.1\r\n"; + $header .= "Host: api.flowdock.com\r\n"; + $header .= "Content-Type: application/json\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php b/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php new file mode 100644 index 0000000..fc1693c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * Interface to describe loggers that have a formatter + * + * @author Jordi Boggiano + */ +interface FormattableHandlerInterface +{ + /** + * Sets the formatter. + * + * @param FormatterInterface $formatter + * @return HandlerInterface self + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface; + + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + public function getFormatter(): FormatterInterface; +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php b/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php new file mode 100644 index 0000000..b60bdce --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; + +/** + * Helper trait for implementing FormattableInterface + * + * @author Jordi Boggiano + */ +trait FormattableHandlerTrait +{ + /** + * @var ?FormatterInterface + */ + protected $formatter; + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $this->formatter = $formatter; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + if (!$this->formatter) { + $this->formatter = $this->getDefaultFormatter(); + } + + return $this->formatter; + } + + /** + * Gets the default formatter. + * + * Overwrite this if the LineFormatter is not a good default for your handler. + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php new file mode 100644 index 0000000..4ff26c4 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Gelf\PublisherInterface; +use Monolog\Logger; +use Monolog\Formatter\GelfMessageFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Handler to send messages to a Graylog2 (http://www.graylog2.org) server + * + * @author Matt Lehner + * @author Benjamin Zikarsky + */ +class GelfHandler extends AbstractProcessingHandler +{ + /** + * @var PublisherInterface the publisher object that sends the message to the server + */ + protected $publisher; + + /** + * @param PublisherInterface $publisher a gelf publisher object + */ + public function __construct(PublisherInterface $publisher, $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + + $this->publisher = $publisher; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->publisher->publish($record['formatted']); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new GelfMessageFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php new file mode 100644 index 0000000..3c9dc4b --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\ResettableInterface; + +/** + * Forwards records to multiple handlers + * + * @author Lenar Lõhmus + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class GroupHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface +{ + use ProcessableHandlerTrait; + + /** @var HandlerInterface[] */ + protected $handlers; + /** @var bool */ + protected $bubble; + + /** + * @param HandlerInterface[] $handlers Array of Handlers. + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(array $handlers, bool $bubble = true) + { + foreach ($handlers as $handler) { + if (!$handler instanceof HandlerInterface) { + throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.'); + } + } + + $this->handlers = $handlers; + $this->bubble = $bubble; + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + foreach ($this->handlers as $handler) { + $handler->handle($record); + } + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if ($this->processors) { + $processed = []; + foreach ($records as $record) { + $processed[] = $this->processRecord($record); + } + /** @var Record[] $records */ + $records = $processed; + } + + foreach ($this->handlers as $handler) { + $handler->handleBatch($records); + } + } + + public function reset() + { + $this->resetProcessors(); + + foreach ($this->handlers as $handler) { + if ($handler instanceof ResettableInterface) { + $handler->reset(); + } + } + } + + public function close(): void + { + parent::close(); + + foreach ($this->handlers as $handler) { + $handler->close(); + } + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + foreach ($this->handlers as $handler) { + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter($formatter); + } + } + + return $this; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/Handler.php b/vendor/monolog/monolog/src/Monolog/Handler/Handler.php new file mode 100644 index 0000000..34b4935 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/Handler.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Base Handler class providing basic close() support as well as handleBatch + * + * @author Jordi Boggiano + */ +abstract class Handler implements HandlerInterface +{ + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + foreach ($records as $record) { + $this->handle($record); + } + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + } + + public function __destruct() + { + try { + $this->close(); + } catch (\Throwable $e) { + // do nothing + } + } + + public function __sleep() + { + $this->close(); + + $reflClass = new \ReflectionClass($this); + + $keys = []; + foreach ($reflClass->getProperties() as $reflProp) { + if (!$reflProp->isStatic()) { + $keys[] = $reflProp->getName(); + } + } + + return $keys; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php b/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php new file mode 100644 index 0000000..affcc51 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Interface that all Monolog Handlers must implement + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +interface HandlerInterface +{ + /** + * Checks whether the given record will be handled by this handler. + * + * This is mostly done for performance reasons, to avoid calling processors for nothing. + * + * Handlers should still check the record levels within handle(), returning false in isHandling() + * is no guarantee that handle() will not be called, and isHandling() might not be called + * for a given record. + * + * @param array $record Partial log record containing only a level key + * + * @return bool + * + * @phpstan-param array{level: Level} $record + */ + public function isHandling(array $record): bool; + + /** + * Handles a record. + * + * All records may be passed to this method, and the handler should discard + * those that it does not want to handle. + * + * The return value of this function controls the bubbling process of the handler stack. + * Unless the bubbling is interrupted (by returning true), the Logger class will keep on + * calling further handlers in the stack with a given log record. + * + * @param array $record The record to handle + * @return bool true means that this handler handled the record, and that bubbling is not permitted. + * false means the record was either not processed or that this handler allows bubbling. + * + * @phpstan-param Record $record + */ + public function handle(array $record): bool; + + /** + * Handles a set of records at once. + * + * @param array $records The records to handle (an array of record arrays) + * + * @phpstan-param Record[] $records + */ + public function handleBatch(array $records): void; + + /** + * Closes the handler. + * + * Ends a log cycle and frees all resources used by the handler. + * + * Closing a Handler means flushing all buffers and freeing any open resources/handles. + * + * Implementations have to be idempotent (i.e. it should be possible to call close several times without breakage) + * and ideally handlers should be able to reopen themselves on handle() after they have been closed. + * + * This is useful at the end of a request and will be called automatically when the object + * is destroyed if you extend Monolog\Handler\Handler. + * + * If you are thinking of calling this method yourself, most likely you should be + * calling ResettableInterface::reset instead. Have a look. + */ + public function close(): void; +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php b/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php new file mode 100644 index 0000000..d4351b9 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; + +/** + * This simple wrapper class can be used to extend handlers functionality. + * + * Example: A custom filtering that can be applied to any handler. + * + * Inherit from this class and override handle() like this: + * + * public function handle(array $record) + * { + * if ($record meets certain conditions) { + * return false; + * } + * return $this->handler->handle($record); + * } + * + * @author Alexey Karapetov + */ +class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, FormattableHandlerInterface, ResettableInterface +{ + /** + * @var HandlerInterface + */ + protected $handler; + + public function __construct(HandlerInterface $handler) + { + $this->handler = $handler; + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return $this->handler->isHandling($record); + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + return $this->handler->handle($record); + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $this->handler->handleBatch($records); + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + $this->handler->close(); + } + + /** + * {@inheritDoc} + */ + public function pushProcessor(callable $callback): HandlerInterface + { + if ($this->handler instanceof ProcessableHandlerInterface) { + $this->handler->pushProcessor($callback); + + return $this; + } + + throw new \LogicException('The wrapped handler does not implement ' . ProcessableHandlerInterface::class); + } + + /** + * {@inheritDoc} + */ + public function popProcessor(): callable + { + if ($this->handler instanceof ProcessableHandlerInterface) { + return $this->handler->popProcessor(); + } + + throw new \LogicException('The wrapped handler does not implement ' . ProcessableHandlerInterface::class); + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + $this->handler->setFormatter($formatter); + + return $this; + } + + throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + return $this->handler->getFormatter(); + } + + throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class); + } + + public function reset() + { + if ($this->handler instanceof ResettableInterface) { + $this->handler->reset(); + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php new file mode 100644 index 0000000..000ccea --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * IFTTTHandler uses cURL to trigger IFTTT Maker actions + * + * Register a secret key and trigger/event name at https://ifttt.com/maker + * + * value1 will be the channel from monolog's Logger constructor, + * value2 will be the level name (ERROR, WARNING, ..) + * value3 will be the log record's message + * + * @author Nehal Patel + */ +class IFTTTHandler extends AbstractProcessingHandler +{ + /** @var string */ + private $eventName; + /** @var string */ + private $secretKey; + + /** + * @param string $eventName The name of the IFTTT Maker event that should be triggered + * @param string $secretKey A valid IFTTT secret key + */ + public function __construct(string $eventName, string $secretKey, $level = Logger::ERROR, bool $bubble = true) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the IFTTTHandler'); + } + + $this->eventName = $eventName; + $this->secretKey = $secretKey; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + public function write(array $record): void + { + $postData = [ + "value1" => $record["channel"], + "value2" => $record["level_name"], + "value3" => $record["message"], + ]; + $postString = Utils::jsonEncode($postData); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $postString); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + "Content-Type: application/json", + ]); + + Curl\Util::execute($ch); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php new file mode 100644 index 0000000..71f64a2 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Inspired on LogEntriesHandler. + * + * @author Robert Kaufmann III + * @author Gabriel Machado + */ +class InsightOpsHandler extends SocketHandler +{ + /** + * @var string + */ + protected $logToken; + + /** + * @param string $token Log token supplied by InsightOps + * @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'. + * @param bool $useSSL Whether or not SSL encryption should be used + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct( + string $token, + string $region = 'us', + bool $useSSL = true, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for InsightOpsHandler'); + } + + $endpoint = $useSSL + ? 'ssl://' . $region . '.data.logs.insight.rapid7.com:443' + : $region . '.data.logs.insight.rapid7.com:80'; + + parent::__construct( + $endpoint, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + $this->logToken = $token; + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + return $this->logToken . ' ' . $record['formatted']; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php new file mode 100644 index 0000000..25fcd15 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * @author Robert Kaufmann III + */ +class LogEntriesHandler extends SocketHandler +{ + /** + * @var string + */ + protected $logToken; + + /** + * @param string $token Log token supplied by LogEntries + * @param bool $useSSL Whether or not SSL encryption should be used. + * @param string $host Custom hostname to send the data to if needed + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct( + string $token, + bool $useSSL = true, + $level = Logger::DEBUG, + bool $bubble = true, + string $host = 'data.logentries.com', + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler'); + } + + $endpoint = $useSSL ? 'ssl://' . $host . ':443' : $host . ':80'; + parent::__construct( + $endpoint, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + $this->logToken = $token; + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + return $this->logToken . ' ' . $record['formatted']; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php new file mode 100644 index 0000000..6d13db3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LogglyFormatter; +use function array_key_exists; +use CurlHandle; + +/** + * Sends errors to Loggly. + * + * @author Przemek Sobstel + * @author Adam Pancutt + * @author Gregory Barchard + */ +class LogglyHandler extends AbstractProcessingHandler +{ + protected const HOST = 'logs-01.loggly.com'; + protected const ENDPOINT_SINGLE = 'inputs'; + protected const ENDPOINT_BATCH = 'bulk'; + + /** + * Caches the curl handlers for every given endpoint. + * + * @var resource[]|CurlHandle[] + */ + protected $curlHandlers = []; + + /** @var string */ + protected $token; + + /** @var string[] */ + protected $tag = []; + + /** + * @param string $token API token supplied by Loggly + * + * @throws MissingExtensionException If the curl extension is missing + */ + public function __construct(string $token, $level = Logger::DEBUG, bool $bubble = true) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the LogglyHandler'); + } + + $this->token = $token; + + parent::__construct($level, $bubble); + } + + /** + * Loads and returns the shared curl handler for the given endpoint. + * + * @param string $endpoint + * + * @return resource|CurlHandle + */ + protected function getCurlHandler(string $endpoint) + { + if (!array_key_exists($endpoint, $this->curlHandlers)) { + $this->curlHandlers[$endpoint] = $this->loadCurlHandle($endpoint); + } + + return $this->curlHandlers[$endpoint]; + } + + /** + * Starts a fresh curl session for the given endpoint and returns its handler. + * + * @param string $endpoint + * + * @return resource|CurlHandle + */ + private function loadCurlHandle(string $endpoint) + { + $url = sprintf("https://%s/%s/%s/", static::HOST, $endpoint, $this->token); + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + return $ch; + } + + /** + * @param string[]|string $tag + */ + public function setTag($tag): self + { + $tag = !empty($tag) ? $tag : []; + $this->tag = is_array($tag) ? $tag : [$tag]; + + return $this; + } + + /** + * @param string[]|string $tag + */ + public function addTag($tag): self + { + if (!empty($tag)) { + $tag = is_array($tag) ? $tag : [$tag]; + $this->tag = array_unique(array_merge($this->tag, $tag)); + } + + return $this; + } + + protected function write(array $record): void + { + $this->send($record["formatted"], static::ENDPOINT_SINGLE); + } + + public function handleBatch(array $records): void + { + $level = $this->level; + + $records = array_filter($records, function ($record) use ($level) { + return ($record['level'] >= $level); + }); + + if ($records) { + $this->send($this->getFormatter()->formatBatch($records), static::ENDPOINT_BATCH); + } + } + + protected function send(string $data, string $endpoint): void + { + $ch = $this->getCurlHandler($endpoint); + + $headers = ['Content-Type: application/json']; + + if (!empty($this->tag)) { + $headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag); + } + + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + Curl\Util::execute($ch, 5, false); + } + + protected function getDefaultFormatter(): FormatterInterface + { + return new LogglyFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php new file mode 100644 index 0000000..859a469 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LogmaticFormatter; + +/** + * @author Julien Breux + */ +class LogmaticHandler extends SocketHandler +{ + /** + * @var string + */ + private $logToken; + + /** + * @var string + */ + private $hostname; + + /** + * @var string + */ + private $appname; + + /** + * @param string $token Log token supplied by Logmatic. + * @param string $hostname Host name supplied by Logmatic. + * @param string $appname Application name supplied by Logmatic. + * @param bool $useSSL Whether or not SSL encryption should be used. + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct( + string $token, + string $hostname = '', + string $appname = '', + bool $useSSL = true, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use SSL encrypted connection for LogmaticHandler'); + } + + $endpoint = $useSSL ? 'ssl://api.logmatic.io:10515' : 'api.logmatic.io:10514'; + $endpoint .= '/v1/'; + + parent::__construct( + $endpoint, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + + $this->logToken = $token; + $this->hostname = $hostname; + $this->appname = $appname; + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + return $this->logToken . ' ' . $record['formatted']; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + $formatter = new LogmaticFormatter(); + + if (!empty($this->hostname)) { + $formatter->setHostname($this->hostname); + } + if (!empty($this->appname)) { + $formatter->setAppname($this->appname); + } + + return $formatter; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php new file mode 100644 index 0000000..97f3432 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\HtmlFormatter; + +/** + * Base class for all mail handlers + * + * @author Gyula Sallai + * + * @phpstan-import-type Record from \Monolog\Logger + */ +abstract class MailHandler extends AbstractProcessingHandler +{ + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $messages = []; + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + /** @var Record $message */ + $message = $this->processRecord($record); + $messages[] = $message; + } + + if (!empty($messages)) { + $this->send((string) $this->getFormatter()->formatBatch($messages), $messages); + } + } + + /** + * Send a mail with the given content + * + * @param string $content formatted email body to be sent + * @param array $records the array of log records that formed this content + * + * @phpstan-param Record[] $records + */ + abstract protected function send(string $content, array $records): void; + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->send((string) $record['formatted'], [$record]); + } + + /** + * @phpstan-param non-empty-array $records + * @phpstan-return Record + */ + protected function getHighestRecord(array $records): array + { + $highestRecord = null; + foreach ($records as $record) { + if ($highestRecord === null || $highestRecord['level'] < $record['level']) { + $highestRecord = $record; + } + } + + return $highestRecord; + } + + protected function isHtmlBody(string $body): bool + { + return ($body[0] ?? null) === '<'; + } + + /** + * Gets the default formatter. + * + * @return FormatterInterface + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new HtmlFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php new file mode 100644 index 0000000..3003500 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Swift; +use Swift_Message; + +/** + * MandrillHandler uses cURL to send the emails to the Mandrill API + * + * @author Adam Nicholson + */ +class MandrillHandler extends MailHandler +{ + /** @var Swift_Message */ + protected $message; + /** @var string */ + protected $apiKey; + + /** + * @psalm-param Swift_Message|callable(): Swift_Message $message + * + * @param string $apiKey A valid Mandrill API key + * @param callable|Swift_Message $message An example message for real messages, only the body will be replaced + */ + public function __construct(string $apiKey, $message, $level = Logger::ERROR, bool $bubble = true) + { + parent::__construct($level, $bubble); + + if (!$message instanceof Swift_Message && is_callable($message)) { + $message = $message(); + } + if (!$message instanceof Swift_Message) { + throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callable returning it'); + } + $this->message = $message; + $this->apiKey = $apiKey; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $mime = 'text/plain'; + if ($this->isHtmlBody($content)) { + $mime = 'text/html'; + } + + $message = clone $this->message; + $message->setBody($content, $mime); + /** @phpstan-ignore-next-line */ + if (version_compare(Swift::VERSION, '6.0.0', '>=')) { + $message->setDate(new \DateTimeImmutable()); + } else { + /** @phpstan-ignore-next-line */ + $message->setDate(time()); + } + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, 'https://mandrillapp.com/api/1.0/messages/send-raw.json'); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ + 'key' => $this->apiKey, + 'raw_message' => (string) $message, + 'async' => false, + ])); + + Curl\Util::execute($ch); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php b/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php new file mode 100644 index 0000000..3965aee --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Exception can be thrown if an extension for a handler is missing + * + * @author Christian Bergau + */ +class MissingExtensionException extends \Exception +{ +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php new file mode 100644 index 0000000..3063091 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use MongoDB\Driver\BulkWrite; +use MongoDB\Driver\Manager; +use MongoDB\Client; +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\MongoDBFormatter; + +/** + * Logs to a MongoDB database. + * + * Usage example: + * + * $log = new \Monolog\Logger('application'); + * $client = new \MongoDB\Client('mongodb://localhost:27017'); + * $mongodb = new \Monolog\Handler\MongoDBHandler($client, 'logs', 'prod'); + * $log->pushHandler($mongodb); + * + * The above examples uses the MongoDB PHP library's client class; however, the + * MongoDB\Driver\Manager class from ext-mongodb is also supported. + */ +class MongoDBHandler extends AbstractProcessingHandler +{ + /** @var \MongoDB\Collection */ + private $collection; + /** @var Client|Manager */ + private $manager; + /** @var string */ + private $namespace; + + /** + * Constructor. + * + * @param Client|Manager $mongodb MongoDB library or driver client + * @param string $database Database name + * @param string $collection Collection name + */ + public function __construct($mongodb, string $database, string $collection, $level = Logger::DEBUG, bool $bubble = true) + { + if (!($mongodb instanceof Client || $mongodb instanceof Manager)) { + throw new \InvalidArgumentException('MongoDB\Client or MongoDB\Driver\Manager instance required'); + } + + if ($mongodb instanceof Client) { + $this->collection = $mongodb->selectCollection($database, $collection); + } else { + $this->manager = $mongodb; + $this->namespace = $database . '.' . $collection; + } + + parent::__construct($level, $bubble); + } + + protected function write(array $record): void + { + if (isset($this->collection)) { + $this->collection->insertOne($record['formatted']); + } + + if (isset($this->manager, $this->namespace)) { + $bulk = new BulkWrite; + $bulk->insert($record["formatted"]); + $this->manager->executeBulkWrite($this->namespace, $bulk); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new MongoDBFormatter; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php new file mode 100644 index 0000000..0c0a3bd --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +/** + * NativeMailerHandler uses the mail() function to send the emails + * + * @author Christophe Coevoet + * @author Mark Garrett + */ +class NativeMailerHandler extends MailHandler +{ + /** + * The email addresses to which the message will be sent + * @var string[] + */ + protected $to; + + /** + * The subject of the email + * @var string + */ + protected $subject; + + /** + * Optional headers for the message + * @var string[] + */ + protected $headers = []; + + /** + * Optional parameters for the message + * @var string[] + */ + protected $parameters = []; + + /** + * The wordwrap length for the message + * @var int + */ + protected $maxColumnWidth; + + /** + * The Content-type for the message + * @var string|null + */ + protected $contentType; + + /** + * The encoding for the message + * @var string + */ + protected $encoding = 'utf-8'; + + /** + * @param string|string[] $to The receiver of the mail + * @param string $subject The subject of the mail + * @param string $from The sender of the mail + * @param int $maxColumnWidth The maximum column width that the message lines will have + */ + public function __construct($to, string $subject, string $from, $level = Logger::ERROR, bool $bubble = true, int $maxColumnWidth = 70) + { + parent::__construct($level, $bubble); + $this->to = (array) $to; + $this->subject = $subject; + $this->addHeader(sprintf('From: %s', $from)); + $this->maxColumnWidth = $maxColumnWidth; + } + + /** + * Add headers to the message + * + * @param string|string[] $headers Custom added headers + */ + public function addHeader($headers): self + { + foreach ((array) $headers as $header) { + if (strpos($header, "\n") !== false || strpos($header, "\r") !== false) { + throw new \InvalidArgumentException('Headers can not contain newline characters for security reasons'); + } + $this->headers[] = $header; + } + + return $this; + } + + /** + * Add parameters to the message + * + * @param string|string[] $parameters Custom added parameters + */ + public function addParameter($parameters): self + { + $this->parameters = array_merge($this->parameters, (array) $parameters); + + return $this; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $contentType = $this->getContentType() ?: ($this->isHtmlBody($content) ? 'text/html' : 'text/plain'); + + if ($contentType !== 'text/html') { + $content = wordwrap($content, $this->maxColumnWidth); + } + + $headers = ltrim(implode("\r\n", $this->headers) . "\r\n", "\r\n"); + $headers .= 'Content-type: ' . $contentType . '; charset=' . $this->getEncoding() . "\r\n"; + if ($contentType === 'text/html' && false === strpos($headers, 'MIME-Version:')) { + $headers .= 'MIME-Version: 1.0' . "\r\n"; + } + + $subject = $this->subject; + if ($records) { + $subjectFormatter = new LineFormatter($this->subject); + $subject = $subjectFormatter->format($this->getHighestRecord($records)); + } + + $parameters = implode(' ', $this->parameters); + foreach ($this->to as $to) { + mail($to, $subject, $content, $headers, $parameters); + } + } + + public function getContentType(): ?string + { + return $this->contentType; + } + + public function getEncoding(): string + { + return $this->encoding; + } + + /** + * @param string $contentType The content type of the email - Defaults to text/plain. Use text/html for HTML messages. + */ + public function setContentType(string $contentType): self + { + if (strpos($contentType, "\n") !== false || strpos($contentType, "\r") !== false) { + throw new \InvalidArgumentException('The content type can not contain newline characters to prevent email header injection'); + } + + $this->contentType = $contentType; + + return $this; + } + + public function setEncoding(string $encoding): self + { + if (strpos($encoding, "\n") !== false || strpos($encoding, "\r") !== false) { + throw new \InvalidArgumentException('The encoding can not contain newline characters to prevent email header injection'); + } + + $this->encoding = $encoding; + + return $this; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php new file mode 100644 index 0000000..114d749 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php @@ -0,0 +1,199 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Class to record a log on a NewRelic application. + * Enabling New Relic High Security mode may prevent capture of useful information. + * + * This handler requires a NormalizerFormatter to function and expects an array in $record['formatted'] + * + * @see https://docs.newrelic.com/docs/agents/php-agent + * @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security + */ +class NewRelicHandler extends AbstractProcessingHandler +{ + /** + * Name of the New Relic application that will receive logs from this handler. + * + * @var ?string + */ + protected $appName; + + /** + * Name of the current transaction + * + * @var ?string + */ + protected $transactionName; + + /** + * Some context and extra data is passed into the handler as arrays of values. Do we send them as is + * (useful if we are using the API), or explode them for display on the NewRelic RPM website? + * + * @var bool + */ + protected $explodeArrays; + + /** + * {@inheritDoc} + * + * @param string|null $appName + * @param bool $explodeArrays + * @param string|null $transactionName + */ + public function __construct( + $level = Logger::ERROR, + bool $bubble = true, + ?string $appName = null, + bool $explodeArrays = false, + ?string $transactionName = null + ) { + parent::__construct($level, $bubble); + + $this->appName = $appName; + $this->explodeArrays = $explodeArrays; + $this->transactionName = $transactionName; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!$this->isNewRelicEnabled()) { + throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler'); + } + + if ($appName = $this->getAppName($record['context'])) { + $this->setNewRelicAppName($appName); + } + + if ($transactionName = $this->getTransactionName($record['context'])) { + $this->setNewRelicTransactionName($transactionName); + unset($record['formatted']['context']['transaction_name']); + } + + if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) { + newrelic_notice_error($record['message'], $record['context']['exception']); + unset($record['formatted']['context']['exception']); + } else { + newrelic_notice_error($record['message']); + } + + if (isset($record['formatted']['context']) && is_array($record['formatted']['context'])) { + foreach ($record['formatted']['context'] as $key => $parameter) { + if (is_array($parameter) && $this->explodeArrays) { + foreach ($parameter as $paramKey => $paramValue) { + $this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue); + } + } else { + $this->setNewRelicParameter('context_' . $key, $parameter); + } + } + } + + if (isset($record['formatted']['extra']) && is_array($record['formatted']['extra'])) { + foreach ($record['formatted']['extra'] as $key => $parameter) { + if (is_array($parameter) && $this->explodeArrays) { + foreach ($parameter as $paramKey => $paramValue) { + $this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue); + } + } else { + $this->setNewRelicParameter('extra_' . $key, $parameter); + } + } + } + } + + /** + * Checks whether the NewRelic extension is enabled in the system. + * + * @return bool + */ + protected function isNewRelicEnabled(): bool + { + return extension_loaded('newrelic'); + } + + /** + * Returns the appname where this log should be sent. Each log can override the default appname, set in this + * handler's constructor, by providing the appname in it's context. + * + * @param mixed[] $context + */ + protected function getAppName(array $context): ?string + { + if (isset($context['appname'])) { + return $context['appname']; + } + + return $this->appName; + } + + /** + * Returns the name of the current transaction. Each log can override the default transaction name, set in this + * handler's constructor, by providing the transaction_name in it's context + * + * @param mixed[] $context + */ + protected function getTransactionName(array $context): ?string + { + if (isset($context['transaction_name'])) { + return $context['transaction_name']; + } + + return $this->transactionName; + } + + /** + * Sets the NewRelic application that should receive this log. + */ + protected function setNewRelicAppName(string $appName): void + { + newrelic_set_appname($appName); + } + + /** + * Overwrites the name of the current transaction + */ + protected function setNewRelicTransactionName(string $transactionName): void + { + newrelic_name_transaction($transactionName); + } + + /** + * @param string $key + * @param mixed $value + */ + protected function setNewRelicParameter(string $key, $value): void + { + if (null === $value || is_scalar($value)) { + newrelic_add_custom_parameter($key, $value); + } else { + newrelic_add_custom_parameter($key, Utils::jsonEncode($value, null, true)); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new NormalizerFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php new file mode 100644 index 0000000..1ddf0be --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * No-op + * + * This handler handles anything, but does nothing, and does not stop bubbling to the rest of the stack. + * This can be used for testing, or to disable a handler when overriding a configuration without + * influencing the rest of the stack. + * + * @author Roel Harbers + */ +class NoopHandler extends Handler +{ + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return true; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + return false; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php new file mode 100644 index 0000000..e75ee0c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Blackhole + * + * Any record it can handle will be thrown away. This can be used + * to put on top of an existing stack to override it temporarily. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class NullHandler extends Handler +{ + /** + * @var int + */ + private $level; + + /** + * @param string|int $level The minimum logging level at which this handler will be triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG) + { + $this->level = Logger::toMonologLevel($level); + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return $record['level'] >= $this->level; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + return $record['level'] >= $this->level; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php new file mode 100644 index 0000000..22068c9 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; + +/** + * Handler to only pass log messages when a certain threshold of number of messages is reached. + * + * This can be useful in cases of processing a batch of data, but you're for example only interested + * in case it fails catastrophically instead of a warning for 1 or 2 events. Worse things can happen, right? + * + * Usage example: + * + * ``` + * $log = new Logger('application'); + * $handler = new SomeHandler(...) + * + * // Pass all warnings to the handler when more than 10 & all error messages when more then 5 + * $overflow = new OverflowHandler($handler, [Logger::WARNING => 10, Logger::ERROR => 5]); + * + * $log->pushHandler($overflow); + *``` + * + * @author Kris Buist + */ +class OverflowHandler extends AbstractHandler implements FormattableHandlerInterface +{ + /** @var HandlerInterface */ + private $handler; + + /** @var int[] */ + private $thresholdMap = [ + Logger::DEBUG => 0, + Logger::INFO => 0, + Logger::NOTICE => 0, + Logger::WARNING => 0, + Logger::ERROR => 0, + Logger::CRITICAL => 0, + Logger::ALERT => 0, + Logger::EMERGENCY => 0, + ]; + + /** + * Buffer of all messages passed to the handler before the threshold was reached + * + * @var mixed[][] + */ + private $buffer = []; + + /** + * @param HandlerInterface $handler + * @param int[] $thresholdMap Dictionary of logger level => threshold + */ + public function __construct( + HandlerInterface $handler, + array $thresholdMap = [], + $level = Logger::DEBUG, + bool $bubble = true + ) { + $this->handler = $handler; + foreach ($thresholdMap as $thresholdLevel => $threshold) { + $this->thresholdMap[$thresholdLevel] = $threshold; + } + parent::__construct($level, $bubble); + } + + /** + * Handles a record. + * + * All records may be passed to this method, and the handler should discard + * those that it does not want to handle. + * + * The return value of this function controls the bubbling process of the handler stack. + * Unless the bubbling is interrupted (by returning true), the Logger class will keep on + * calling further handlers in the stack with a given log record. + * + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($record['level'] < $this->level) { + return false; + } + + $level = $record['level']; + + if (!isset($this->thresholdMap[$level])) { + $this->thresholdMap[$level] = 0; + } + + if ($this->thresholdMap[$level] > 0) { + // The overflow threshold is not yet reached, so we're buffering the record and lowering the threshold by 1 + $this->thresholdMap[$level]--; + $this->buffer[$level][] = $record; + + return false === $this->bubble; + } + + if ($this->thresholdMap[$level] == 0) { + // This current message is breaking the threshold. Flush the buffer and continue handling the current record + foreach ($this->buffer[$level] ?? [] as $buffered) { + $this->handler->handle($buffered); + } + $this->thresholdMap[$level]--; + unset($this->buffer[$level]); + } + + $this->handler->handle($record); + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + $this->handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + return $this->handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php new file mode 100644 index 0000000..23a1d11 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php @@ -0,0 +1,263 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; +use PhpConsole\Connector; +use PhpConsole\Handler as VendorPhpConsoleHandler; +use PhpConsole\Helper; + +/** + * Monolog handler for Google Chrome extension "PHP Console" + * + * Display PHP error/debug log messages in Google Chrome console and notification popups, executes PHP code remotely + * + * Usage: + * 1. Install Google Chrome extension [now dead and removed from the chrome store] + * 2. See overview https://github.com/barbushin/php-console#overview + * 3. Install PHP Console library https://github.com/barbushin/php-console#installation + * 4. Example (result will looks like http://i.hizliresim.com/vg3Pz4.png) + * + * $logger = new \Monolog\Logger('all', array(new \Monolog\Handler\PHPConsoleHandler())); + * \Monolog\ErrorHandler::register($logger); + * echo $undefinedVar; + * $logger->debug('SELECT * FROM users', array('db', 'time' => 0.012)); + * PC::debug($_SERVER); // PHP Console debugger for any type of vars + * + * @author Sergey Barbushin https://www.linkedin.com/in/barbushin + * + * @phpstan-import-type Record from \Monolog\Logger + * @deprecated Since 2.8.0 and 3.2.0, PHPConsole is abandoned and thus we will drop this handler in Monolog 4 + */ +class PHPConsoleHandler extends AbstractProcessingHandler +{ + /** @var array */ + private $options = [ + 'enabled' => true, // bool Is PHP Console server enabled + 'classesPartialsTraceIgnore' => ['Monolog\\'], // array Hide calls of classes started with... + 'debugTagsKeysInContext' => [0, 'tag'], // bool Is PHP Console server enabled + 'useOwnErrorsHandler' => false, // bool Enable errors handling + 'useOwnExceptionsHandler' => false, // bool Enable exceptions handling + 'sourcesBasePath' => null, // string Base path of all project sources to strip in errors source paths + 'registerHelper' => true, // bool Register PhpConsole\Helper that allows short debug calls like PC::debug($var, 'ta.g.s') + 'serverEncoding' => null, // string|null Server internal encoding + 'headersLimit' => null, // int|null Set headers size limit for your web-server + 'password' => null, // string|null Protect PHP Console connection by password + 'enableSslOnlyMode' => false, // bool Force connection by SSL for clients with PHP Console installed + 'ipMasks' => [], // array Set IP masks of clients that will be allowed to connect to PHP Console: array('192.168.*.*', '127.0.0.1') + 'enableEvalListener' => false, // bool Enable eval request to be handled by eval dispatcher(if enabled, 'password' option is also required) + 'dumperDetectCallbacks' => false, // bool Convert callback items in dumper vars to (callback SomeClass::someMethod) strings + 'dumperLevelLimit' => 5, // int Maximum dumped vars array or object nested dump level + 'dumperItemsCountLimit' => 100, // int Maximum dumped var same level array items or object properties number + 'dumperItemSizeLimit' => 5000, // int Maximum length of any string or dumped array item + 'dumperDumpSizeLimit' => 500000, // int Maximum approximate size of dumped vars result formatted in JSON + 'detectDumpTraceAndSource' => false, // bool Autodetect and append trace data to debug + 'dataStorage' => null, // \PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ) + ]; + + /** @var Connector */ + private $connector; + + /** + * @param array $options See \Monolog\Handler\PHPConsoleHandler::$options for more details + * @param Connector|null $connector Instance of \PhpConsole\Connector class (optional) + * @throws \RuntimeException + */ + public function __construct(array $options = [], ?Connector $connector = null, $level = Logger::DEBUG, bool $bubble = true) + { + if (!class_exists('PhpConsole\Connector')) { + throw new \RuntimeException('PHP Console library not found. See https://github.com/barbushin/php-console#installation'); + } + parent::__construct($level, $bubble); + $this->options = $this->initOptions($options); + $this->connector = $this->initConnector($connector); + } + + /** + * @param array $options + * + * @return array + */ + private function initOptions(array $options): array + { + $wrongOptions = array_diff(array_keys($options), array_keys($this->options)); + if ($wrongOptions) { + throw new \RuntimeException('Unknown options: ' . implode(', ', $wrongOptions)); + } + + return array_replace($this->options, $options); + } + + private function initConnector(?Connector $connector = null): Connector + { + if (!$connector) { + if ($this->options['dataStorage']) { + Connector::setPostponeStorage($this->options['dataStorage']); + } + $connector = Connector::getInstance(); + } + + if ($this->options['registerHelper'] && !Helper::isRegistered()) { + Helper::register(); + } + + if ($this->options['enabled'] && $connector->isActiveClient()) { + if ($this->options['useOwnErrorsHandler'] || $this->options['useOwnExceptionsHandler']) { + $handler = VendorPhpConsoleHandler::getInstance(); + $handler->setHandleErrors($this->options['useOwnErrorsHandler']); + $handler->setHandleExceptions($this->options['useOwnExceptionsHandler']); + $handler->start(); + } + if ($this->options['sourcesBasePath']) { + $connector->setSourcesBasePath($this->options['sourcesBasePath']); + } + if ($this->options['serverEncoding']) { + $connector->setServerEncoding($this->options['serverEncoding']); + } + if ($this->options['password']) { + $connector->setPassword($this->options['password']); + } + if ($this->options['enableSslOnlyMode']) { + $connector->enableSslOnlyMode(); + } + if ($this->options['ipMasks']) { + $connector->setAllowedIpMasks($this->options['ipMasks']); + } + if ($this->options['headersLimit']) { + $connector->setHeadersLimit($this->options['headersLimit']); + } + if ($this->options['detectDumpTraceAndSource']) { + $connector->getDebugDispatcher()->detectTraceAndSource = true; + } + $dumper = $connector->getDumper(); + $dumper->levelLimit = $this->options['dumperLevelLimit']; + $dumper->itemsCountLimit = $this->options['dumperItemsCountLimit']; + $dumper->itemSizeLimit = $this->options['dumperItemSizeLimit']; + $dumper->dumpSizeLimit = $this->options['dumperDumpSizeLimit']; + $dumper->detectCallbacks = $this->options['dumperDetectCallbacks']; + if ($this->options['enableEvalListener']) { + $connector->startEvalRequestsListener(); + } + } + + return $connector; + } + + public function getConnector(): Connector + { + return $this->connector; + } + + /** + * @return array + */ + public function getOptions(): array + { + return $this->options; + } + + public function handle(array $record): bool + { + if ($this->options['enabled'] && $this->connector->isActiveClient()) { + return parent::handle($record); + } + + return !$this->bubble; + } + + /** + * Writes the record down to the log of the implementing handler + */ + protected function write(array $record): void + { + if ($record['level'] < Logger::NOTICE) { + $this->handleDebugRecord($record); + } elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) { + $this->handleExceptionRecord($record); + } else { + $this->handleErrorRecord($record); + } + } + + /** + * @phpstan-param Record $record + */ + private function handleDebugRecord(array $record): void + { + $tags = $this->getRecordTags($record); + $message = $record['message']; + if ($record['context']) { + $message .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(array_filter($record['context'])), null, true); + } + $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']); + } + + /** + * @phpstan-param Record $record + */ + private function handleExceptionRecord(array $record): void + { + $this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']); + } + + /** + * @phpstan-param Record $record + */ + private function handleErrorRecord(array $record): void + { + $context = $record['context']; + + $this->connector->getErrorsDispatcher()->dispatchError( + $context['code'] ?? null, + $context['message'] ?? $record['message'], + $context['file'] ?? null, + $context['line'] ?? null, + $this->options['classesPartialsTraceIgnore'] + ); + } + + /** + * @phpstan-param Record $record + * @return string + */ + private function getRecordTags(array &$record) + { + $tags = null; + if (!empty($record['context'])) { + $context = & $record['context']; + foreach ($this->options['debugTagsKeysInContext'] as $key) { + if (!empty($context[$key])) { + $tags = $context[$key]; + if ($key === 0) { + array_shift($context); + } else { + unset($context[$key]); + } + break; + } + } + } + + return $tags ?: strtolower($record['level_name']); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter('%message%'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php new file mode 100644 index 0000000..8a8cf1b --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php @@ -0,0 +1,191 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to STDIN of any process, specified by a command. + * + * Usage example: + *
+ * $log = new Logger('myLogger');
+ * $log->pushHandler(new ProcessHandler('/usr/bin/php /var/www/monolog/someScript.php'));
+ * 
+ * + * @author Kolja Zuelsdorf + */ +class ProcessHandler extends AbstractProcessingHandler +{ + /** + * Holds the process to receive data on its STDIN. + * + * @var resource|bool|null + */ + private $process; + + /** + * @var string + */ + private $command; + + /** + * @var string|null + */ + private $cwd; + + /** + * @var resource[] + */ + private $pipes = []; + + /** + * @var array + */ + protected const DESCRIPTOR_SPEC = [ + 0 => ['pipe', 'r'], // STDIN is a pipe that the child will read from + 1 => ['pipe', 'w'], // STDOUT is a pipe that the child will write to + 2 => ['pipe', 'w'], // STDERR is a pipe to catch the any errors + ]; + + /** + * @param string $command Command for the process to start. Absolute paths are recommended, + * especially if you do not use the $cwd parameter. + * @param string|null $cwd "Current working directory" (CWD) for the process to be executed in. + * @throws \InvalidArgumentException + */ + public function __construct(string $command, $level = Logger::DEBUG, bool $bubble = true, ?string $cwd = null) + { + if ($command === '') { + throw new \InvalidArgumentException('The command argument must be a non-empty string.'); + } + if ($cwd === '') { + throw new \InvalidArgumentException('The optional CWD argument must be a non-empty string or null.'); + } + + parent::__construct($level, $bubble); + + $this->command = $command; + $this->cwd = $cwd; + } + + /** + * Writes the record down to the log of the implementing handler + * + * @throws \UnexpectedValueException + */ + protected function write(array $record): void + { + $this->ensureProcessIsStarted(); + + $this->writeProcessInput($record['formatted']); + + $errors = $this->readProcessErrors(); + if (empty($errors) === false) { + throw new \UnexpectedValueException(sprintf('Errors while writing to process: %s', $errors)); + } + } + + /** + * Makes sure that the process is actually started, and if not, starts it, + * assigns the stream pipes, and handles startup errors, if any. + */ + private function ensureProcessIsStarted(): void + { + if (is_resource($this->process) === false) { + $this->startProcess(); + + $this->handleStartupErrors(); + } + } + + /** + * Starts the actual process and sets all streams to non-blocking. + */ + private function startProcess(): void + { + $this->process = proc_open($this->command, static::DESCRIPTOR_SPEC, $this->pipes, $this->cwd); + + foreach ($this->pipes as $pipe) { + stream_set_blocking($pipe, false); + } + } + + /** + * Selects the STDERR stream, handles upcoming startup errors, and throws an exception, if any. + * + * @throws \UnexpectedValueException + */ + private function handleStartupErrors(): void + { + $selected = $this->selectErrorStream(); + if (false === $selected) { + throw new \UnexpectedValueException('Something went wrong while selecting a stream.'); + } + + $errors = $this->readProcessErrors(); + + if (is_resource($this->process) === false || empty($errors) === false) { + throw new \UnexpectedValueException( + sprintf('The process "%s" could not be opened: ' . $errors, $this->command) + ); + } + } + + /** + * Selects the STDERR stream. + * + * @return int|bool + */ + protected function selectErrorStream() + { + $empty = []; + $errorPipes = [$this->pipes[2]]; + + return stream_select($errorPipes, $empty, $empty, 1); + } + + /** + * Reads the errors of the process, if there are any. + * + * @codeCoverageIgnore + * @return string Empty string if there are no errors. + */ + protected function readProcessErrors(): string + { + return (string) stream_get_contents($this->pipes[2]); + } + + /** + * Writes to the input stream of the opened process. + * + * @codeCoverageIgnore + */ + protected function writeProcessInput(string $string): void + { + fwrite($this->pipes[0], $string); + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + if (is_resource($this->process)) { + foreach ($this->pipes as $pipe) { + fclose($pipe); + } + proc_close($this->process); + $this->process = null; + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php b/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php new file mode 100644 index 0000000..3adec7a --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Processor\ProcessorInterface; + +/** + * Interface to describe loggers that have processors + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface ProcessableHandlerInterface +{ + /** + * Adds a processor in the stack. + * + * @psalm-param ProcessorInterface|callable(Record): Record $callback + * + * @param ProcessorInterface|callable $callback + * @return HandlerInterface self + */ + public function pushProcessor(callable $callback): HandlerInterface; + + /** + * Removes the processor on top of the stack and returns it. + * + * @psalm-return ProcessorInterface|callable(Record): Record $callback + * + * @throws \LogicException In case the processor stack is empty + * @return callable|ProcessorInterface + */ + public function popProcessor(): callable; +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php b/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php new file mode 100644 index 0000000..9ef6e30 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\ResettableInterface; +use Monolog\Processor\ProcessorInterface; + +/** + * Helper trait for implementing ProcessableInterface + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +trait ProcessableHandlerTrait +{ + /** + * @var callable[] + * @phpstan-var array + */ + protected $processors = []; + + /** + * {@inheritDoc} + */ + public function pushProcessor(callable $callback): HandlerInterface + { + array_unshift($this->processors, $callback); + + return $this; + } + + /** + * {@inheritDoc} + */ + public function popProcessor(): callable + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + + return array_shift($this->processors); + } + + /** + * Processes a record. + * + * @phpstan-param Record $record + * @phpstan-return Record + */ + protected function processRecord(array $record): array + { + foreach ($this->processors as $processor) { + $record = $processor($record); + } + + return $record; + } + + protected function resetProcessors(): void + { + foreach ($this->processors as $processor) { + if ($processor instanceof ResettableInterface) { + $processor->reset(); + } + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php new file mode 100644 index 0000000..36e19cc --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LoggerInterface; +use Monolog\Formatter\FormatterInterface; + +/** + * Proxies log messages to an existing PSR-3 compliant logger. + * + * If a formatter is configured, the formatter's output MUST be a string and the + * formatted message will be fed to the wrapped PSR logger instead of the original + * log record's message. + * + * @author Michael Moussa + */ +class PsrHandler extends AbstractHandler implements FormattableHandlerInterface +{ + /** + * PSR-3 compliant logger + * + * @var LoggerInterface + */ + protected $logger; + + /** + * @var FormatterInterface|null + */ + protected $formatter; + + /** + * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied + */ + public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + + $this->logger = $logger; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if (!$this->isHandling($record)) { + return false; + } + + if ($this->formatter) { + $formatted = $this->formatter->format($record); + $this->logger->log(strtolower($record['level_name']), (string) $formatted, $record['context']); + } else { + $this->logger->log(strtolower($record['level_name']), $record['message'], $record['context']); + } + + return false === $this->bubble; + } + + /** + * Sets the formatter. + * + * @param FormatterInterface $formatter + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $this->formatter = $formatter; + + return $this; + } + + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + public function getFormatter(): FormatterInterface + { + if (!$this->formatter) { + throw new \LogicException('No formatter has been set and this handler does not have a default formatter'); + } + + return $this->formatter; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php new file mode 100644 index 0000000..fed2303 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php @@ -0,0 +1,246 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Psr\Log\LogLevel; + +/** + * Sends notifications through the pushover api to mobile phones + * + * @author Sebastian Göttschkes + * @see https://www.pushover.net/api + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class PushoverHandler extends SocketHandler +{ + /** @var string */ + private $token; + /** @var array */ + private $users; + /** @var string */ + private $title; + /** @var string|int|null */ + private $user = null; + /** @var int */ + private $retry; + /** @var int */ + private $expire; + + /** @var int */ + private $highPriorityLevel; + /** @var int */ + private $emergencyLevel; + /** @var bool */ + private $useFormattedMessage = false; + + /** + * All parameters that can be sent to Pushover + * @see https://pushover.net/api + * @var array + */ + private $parameterNames = [ + 'token' => true, + 'user' => true, + 'message' => true, + 'device' => true, + 'title' => true, + 'url' => true, + 'url_title' => true, + 'priority' => true, + 'timestamp' => true, + 'sound' => true, + 'retry' => true, + 'expire' => true, + 'callback' => true, + ]; + + /** + * Sounds the api supports by default + * @see https://pushover.net/api#sounds + * @var string[] + */ + private $sounds = [ + 'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming', + 'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb', + 'persistent', 'echo', 'updown', 'none', + ]; + + /** + * @param string $token Pushover api token + * @param string|array $users Pushover user id or array of ids the message will be sent to + * @param string|null $title Title sent to the Pushover API + * @param bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not + * the pushover.net app owner. OpenSSL is required for this option. + * @param string|int $highPriorityLevel The minimum logging level at which this handler will start + * sending "high priority" requests to the Pushover API + * @param string|int $emergencyLevel The minimum logging level at which this handler will start + * sending "emergency" requests to the Pushover API + * @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will + * send the same notification to the user. + * @param int $expire The expire parameter specifies how many seconds your notification will continue + * to be retried for (every retry seconds). + * + * @phpstan-param string|array $users + * @phpstan-param Level|LevelName|LogLevel::* $highPriorityLevel + * @phpstan-param Level|LevelName|LogLevel::* $emergencyLevel + */ + public function __construct( + string $token, + $users, + ?string $title = null, + $level = Logger::CRITICAL, + bool $bubble = true, + bool $useSSL = true, + $highPriorityLevel = Logger::CRITICAL, + $emergencyLevel = Logger::EMERGENCY, + int $retry = 30, + int $expire = 25200, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + $connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80'; + parent::__construct( + $connectionString, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + + $this->token = $token; + $this->users = (array) $users; + $this->title = $title ?: (string) gethostname(); + $this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel); + $this->emergencyLevel = Logger::toMonologLevel($emergencyLevel); + $this->retry = $retry; + $this->expire = $expire; + } + + protected function generateDataStream(array $record): string + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record): string + { + // Pushover has a limit of 512 characters on title and message combined. + $maxMessageLength = 512 - strlen($this->title); + + $message = ($this->useFormattedMessage) ? $record['formatted'] : $record['message']; + $message = Utils::substr($message, 0, $maxMessageLength); + + $timestamp = $record['datetime']->getTimestamp(); + + $dataArray = [ + 'token' => $this->token, + 'user' => $this->user, + 'message' => $message, + 'title' => $this->title, + 'timestamp' => $timestamp, + ]; + + if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) { + $dataArray['priority'] = 2; + $dataArray['retry'] = $this->retry; + $dataArray['expire'] = $this->expire; + } elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) { + $dataArray['priority'] = 1; + } + + // First determine the available parameters + $context = array_intersect_key($record['context'], $this->parameterNames); + $extra = array_intersect_key($record['extra'], $this->parameterNames); + + // Least important info should be merged with subsequent info + $dataArray = array_merge($extra, $context, $dataArray); + + // Only pass sounds that are supported by the API + if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds)) { + unset($dataArray['sound']); + } + + return http_build_query($dataArray); + } + + private function buildHeader(string $content): string + { + $header = "POST /1/messages.json HTTP/1.1\r\n"; + $header .= "Host: api.pushover.net\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + protected function write(array $record): void + { + foreach ($this->users as $user) { + $this->user = $user; + + parent::write($record); + $this->closeSocket(); + } + + $this->user = null; + } + + /** + * @param int|string $value + * + * @phpstan-param Level|LevelName|LogLevel::* $value + */ + public function setHighPriorityLevel($value): self + { + $this->highPriorityLevel = Logger::toMonologLevel($value); + + return $this; + } + + /** + * @param int|string $value + * + * @phpstan-param Level|LevelName|LogLevel::* $value + */ + public function setEmergencyLevel($value): self + { + $this->emergencyLevel = Logger::toMonologLevel($value); + + return $this; + } + + /** + * Use the formatted message? + */ + public function useFormattedMessage(bool $value): self + { + $this->useFormattedMessage = $value; + + return $this; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php new file mode 100644 index 0000000..91d16ea --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; + +/** + * Logs to a Redis key using rpush + * + * usage example: + * + * $log = new Logger('application'); + * $redis = new RedisHandler(new Predis\Client("tcp://localhost:6379"), "logs", "prod"); + * $log->pushHandler($redis); + * + * @author Thomas Tourlourat + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class RedisHandler extends AbstractProcessingHandler +{ + /** @var \Predis\Client<\Predis\Client>|\Redis */ + private $redisClient; + /** @var string */ + private $redisKey; + /** @var int */ + protected $capSize; + + /** + * @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance + * @param string $key The key name to push records to + * @param int $capSize Number of entries to limit list size to, 0 = unlimited + */ + public function __construct($redis, string $key, $level = Logger::DEBUG, bool $bubble = true, int $capSize = 0) + { + if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { + throw new \InvalidArgumentException('Predis\Client or Redis instance required'); + } + + $this->redisClient = $redis; + $this->redisKey = $key; + $this->capSize = $capSize; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if ($this->capSize) { + $this->writeCapped($record); + } else { + $this->redisClient->rpush($this->redisKey, $record["formatted"]); + } + } + + /** + * Write and cap the collection + * Writes the record to the redis list and caps its + * + * @phpstan-param FormattedRecord $record + */ + protected function writeCapped(array $record): void + { + if ($this->redisClient instanceof \Redis) { + $mode = defined('\Redis::MULTI') ? \Redis::MULTI : 1; + $this->redisClient->multi($mode) + ->rpush($this->redisKey, $record["formatted"]) + ->ltrim($this->redisKey, -$this->capSize, -1) + ->exec(); + } else { + $redisKey = $this->redisKey; + $capSize = $this->capSize; + $this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) { + $tx->rpush($redisKey, $record["formatted"]); + $tx->ltrim($redisKey, -$capSize, -1); + }); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php new file mode 100644 index 0000000..7789309 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; + +/** + * Sends the message to a Redis Pub/Sub channel using PUBLISH + * + * usage example: + * + * $log = new Logger('application'); + * $redis = new RedisPubSubHandler(new Predis\Client("tcp://localhost:6379"), "logs", Logger::WARNING); + * $log->pushHandler($redis); + * + * @author Gaëtan Faugère + */ +class RedisPubSubHandler extends AbstractProcessingHandler +{ + /** @var \Predis\Client<\Predis\Client>|\Redis */ + private $redisClient; + /** @var string */ + private $channelKey; + + /** + * @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance + * @param string $key The channel key to publish records to + */ + public function __construct($redis, string $key, $level = Logger::DEBUG, bool $bubble = true) + { + if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { + throw new \InvalidArgumentException('Predis\Client or Redis instance required'); + } + + $this->redisClient = $redis; + $this->channelKey = $key; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->redisClient->publish($this->channelKey, $record["formatted"]); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php new file mode 100644 index 0000000..adcc939 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Rollbar\RollbarLogger; +use Throwable; +use Monolog\Logger; + +/** + * Sends errors to Rollbar + * + * If the context data contains a `payload` key, that is used as an array + * of payload options to RollbarLogger's log method. + * + * Rollbar's context info will contain the context + extra keys from the log record + * merged, and then on top of that a few keys: + * + * - level (rollbar level name) + * - monolog_level (monolog level name, raw level, as rollbar only has 5 but monolog 8) + * - channel + * - datetime (unix timestamp) + * + * @author Paul Statezny + */ +class RollbarHandler extends AbstractProcessingHandler +{ + /** + * @var RollbarLogger + */ + protected $rollbarLogger; + + /** @var string[] */ + protected $levelMap = [ + Logger::DEBUG => 'debug', + Logger::INFO => 'info', + Logger::NOTICE => 'info', + Logger::WARNING => 'warning', + Logger::ERROR => 'error', + Logger::CRITICAL => 'critical', + Logger::ALERT => 'critical', + Logger::EMERGENCY => 'critical', + ]; + + /** + * Records whether any log records have been added since the last flush of the rollbar notifier + * + * @var bool + */ + private $hasRecords = false; + + /** @var bool */ + protected $initialized = false; + + /** + * @param RollbarLogger $rollbarLogger RollbarLogger object constructed with valid token + */ + public function __construct(RollbarLogger $rollbarLogger, $level = Logger::ERROR, bool $bubble = true) + { + $this->rollbarLogger = $rollbarLogger; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!$this->initialized) { + // __destructor() doesn't get called on Fatal errors + register_shutdown_function(array($this, 'close')); + $this->initialized = true; + } + + $context = $record['context']; + $context = array_merge($context, $record['extra'], [ + 'level' => $this->levelMap[$record['level']], + 'monolog_level' => $record['level_name'], + 'channel' => $record['channel'], + 'datetime' => $record['datetime']->format('U'), + ]); + + if (isset($context['exception']) && $context['exception'] instanceof Throwable) { + $exception = $context['exception']; + unset($context['exception']); + $toLog = $exception; + } else { + $toLog = $record['message']; + } + + // @phpstan-ignore-next-line + $this->rollbarLogger->log($context['level'], $toLog, $context); + + $this->hasRecords = true; + } + + public function flush(): void + { + if ($this->hasRecords) { + $this->rollbarLogger->flush(); + $this->hasRecords = false; + } + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + $this->flush(); + } + + /** + * {@inheritDoc} + */ + public function reset() + { + $this->flush(); + + parent::reset(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php new file mode 100644 index 0000000..17745d2 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use InvalidArgumentException; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Stores logs to files that are rotated every day and a limited number of files are kept. + * + * This rotation is only intended to be used as a workaround. Using logrotate to + * handle the rotation is strongly encouraged when you can use it. + * + * @author Christophe Coevoet + * @author Jordi Boggiano + */ +class RotatingFileHandler extends StreamHandler +{ + public const FILE_PER_DAY = 'Y-m-d'; + public const FILE_PER_MONTH = 'Y-m'; + public const FILE_PER_YEAR = 'Y'; + + /** @var string */ + protected $filename; + /** @var int */ + protected $maxFiles; + /** @var bool */ + protected $mustRotate; + /** @var \DateTimeImmutable */ + protected $nextRotation; + /** @var string */ + protected $filenameFormat; + /** @var string */ + protected $dateFormat; + + /** + * @param string $filename + * @param int $maxFiles The maximal amount of files to keep (0 means unlimited) + * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) + * @param bool $useLocking Try to lock log file before doing any writes + */ + public function __construct(string $filename, int $maxFiles = 0, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false) + { + $this->filename = Utils::canonicalizePath($filename); + $this->maxFiles = $maxFiles; + $this->nextRotation = new \DateTimeImmutable('tomorrow'); + $this->filenameFormat = '{filename}-{date}'; + $this->dateFormat = static::FILE_PER_DAY; + + parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking); + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + parent::close(); + + if (true === $this->mustRotate) { + $this->rotate(); + } + } + + /** + * {@inheritDoc} + */ + public function reset() + { + parent::reset(); + + if (true === $this->mustRotate) { + $this->rotate(); + } + } + + public function setFilenameFormat(string $filenameFormat, string $dateFormat): self + { + if (!preg_match('{^[Yy](([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) { + throw new InvalidArgumentException( + 'Invalid date format - format must be one of '. + 'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '. + 'or RotatingFileHandler::FILE_PER_YEAR ("Y"), or you can set one of the '. + 'date formats using slashes, underscores and/or dots instead of dashes.' + ); + } + if (substr_count($filenameFormat, '{date}') === 0) { + throw new InvalidArgumentException( + 'Invalid filename format - format must contain at least `{date}`, because otherwise rotating is impossible.' + ); + } + $this->filenameFormat = $filenameFormat; + $this->dateFormat = $dateFormat; + $this->url = $this->getTimedFilename(); + $this->close(); + + return $this; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + // on the first record written, if the log is new, we should rotate (once per day) + if (null === $this->mustRotate) { + $this->mustRotate = null === $this->url || !file_exists($this->url); + } + + if ($this->nextRotation <= $record['datetime']) { + $this->mustRotate = true; + $this->close(); + } + + parent::write($record); + } + + /** + * Rotates the files. + */ + protected function rotate(): void + { + // update filename + $this->url = $this->getTimedFilename(); + $this->nextRotation = new \DateTimeImmutable('tomorrow'); + + // skip GC of old logs if files are unlimited + if (0 === $this->maxFiles) { + return; + } + + $logFiles = glob($this->getGlobPattern()); + if (false === $logFiles) { + // failed to glob + return; + } + + if ($this->maxFiles >= count($logFiles)) { + // no files to remove + return; + } + + // Sorting the files by name to remove the older ones + usort($logFiles, function ($a, $b) { + return strcmp($b, $a); + }); + + foreach (array_slice($logFiles, $this->maxFiles) as $file) { + if (is_writable($file)) { + // suppress errors here as unlink() might fail if two processes + // are cleaning up/rotating at the same time + set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline): bool { + return false; + }); + unlink($file); + restore_error_handler(); + } + } + + $this->mustRotate = false; + } + + protected function getTimedFilename(): string + { + $fileInfo = pathinfo($this->filename); + $timedFilename = str_replace( + ['{filename}', '{date}'], + [$fileInfo['filename'], date($this->dateFormat)], + $fileInfo['dirname'] . '/' . $this->filenameFormat + ); + + if (isset($fileInfo['extension'])) { + $timedFilename .= '.'.$fileInfo['extension']; + } + + return $timedFilename; + } + + protected function getGlobPattern(): string + { + $fileInfo = pathinfo($this->filename); + $glob = str_replace( + ['{filename}', '{date}'], + [$fileInfo['filename'], str_replace( + ['Y', 'y', 'm', 'd'], + ['[0-9][0-9][0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]'], + $this->dateFormat) + ], + $fileInfo['dirname'] . '/' . $this->filenameFormat + ); + if (isset($fileInfo['extension'])) { + $glob .= '.'.$fileInfo['extension']; + } + + return $glob; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php new file mode 100644 index 0000000..25cce07 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * Sampling handler + * + * A sampled event stream can be useful for logging high frequency events in + * a production environment where you only need an idea of what is happening + * and are not concerned with capturing every occurrence. Since the decision to + * handle or not handle a particular event is determined randomly, the + * resulting sampled log is not guaranteed to contain 1/N of the events that + * occurred in the application, but based on the Law of large numbers, it will + * tend to be close to this ratio with a large number of attempts. + * + * @author Bryan Davis + * @author Kunal Mehta + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +class SamplingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + + /** + * @var HandlerInterface|callable + * @phpstan-var HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface + */ + protected $handler; + + /** + * @var int $factor + */ + protected $factor; + + /** + * @psalm-param HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface $handler + * + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler). + * @param int $factor Sample factor (e.g. 10 means every ~10th record is sampled) + */ + public function __construct($handler, int $factor) + { + parent::__construct(); + $this->handler = $handler; + $this->factor = $factor; + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + public function isHandling(array $record): bool + { + return $this->getHandler($record)->isHandling($record); + } + + public function handle(array $record): bool + { + if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $this->getHandler($record)->handle($record); + } + + return false === $this->bubble; + } + + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @phpstan-param Record|array{level: Level}|null $record + * + * @return HandlerInterface + */ + public function getHandler(?array $record = null) + { + if (!$this->handler instanceof HandlerInterface) { + $this->handler = ($this->handler)($record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + return $this->handler; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + return $handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php new file mode 100644 index 0000000..1280ee7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * SendGridrHandler uses the SendGrid API v2 function to send Log emails, more information in https://sendgrid.com/docs/API_Reference/Web_API/mail.html + * + * @author Ricardo Fontanelli + */ +class SendGridHandler extends MailHandler +{ + /** + * The SendGrid API User + * @var string + */ + protected $apiUser; + + /** + * The SendGrid API Key + * @var string + */ + protected $apiKey; + + /** + * The email addresses to which the message will be sent + * @var string + */ + protected $from; + + /** + * The email addresses to which the message will be sent + * @var string[] + */ + protected $to; + + /** + * The subject of the email + * @var string + */ + protected $subject; + + /** + * @param string $apiUser The SendGrid API User + * @param string $apiKey The SendGrid API Key + * @param string $from The sender of the email + * @param string|string[] $to The recipients of the email + * @param string $subject The subject of the mail + */ + public function __construct(string $apiUser, string $apiKey, string $from, $to, string $subject, $level = Logger::ERROR, bool $bubble = true) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the SendGridHandler'); + } + + parent::__construct($level, $bubble); + $this->apiUser = $apiUser; + $this->apiKey = $apiKey; + $this->from = $from; + $this->to = (array) $to; + $this->subject = $subject; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $message = []; + $message['api_user'] = $this->apiUser; + $message['api_key'] = $this->apiKey; + $message['from'] = $this->from; + foreach ($this->to as $recipient) { + $message['to[]'] = $recipient; + } + $message['subject'] = $this->subject; + $message['date'] = date('r'); + + if ($this->isHtmlBody($content)) { + $message['html'] = $content; + } else { + $message['text'] = $content; + } + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'https://api.sendgrid.com/api/mail.send.json'); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($message)); + Curl\Util::execute($ch, 2); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php b/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php new file mode 100644 index 0000000..9ae1003 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php @@ -0,0 +1,387 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\Slack; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Slack record utility helping to log to Slack webhooks or API. + * + * @author Greg Kedzierski + * @author Haralan Dobrev + * @see https://api.slack.com/incoming-webhooks + * @see https://api.slack.com/docs/message-attachments + * + * @phpstan-import-type FormattedRecord from \Monolog\Handler\AbstractProcessingHandler + * @phpstan-import-type Record from \Monolog\Logger + */ +class SlackRecord +{ + public const COLOR_DANGER = 'danger'; + + public const COLOR_WARNING = 'warning'; + + public const COLOR_GOOD = 'good'; + + public const COLOR_DEFAULT = '#e3e4e6'; + + /** + * Slack channel (encoded ID or name) + * @var string|null + */ + private $channel; + + /** + * Name of a bot + * @var string|null + */ + private $username; + + /** + * User icon e.g. 'ghost', 'http://example.com/user.png' + * @var string|null + */ + private $userIcon; + + /** + * Whether the message should be added to Slack as attachment (plain text otherwise) + * @var bool + */ + private $useAttachment; + + /** + * Whether the the context/extra messages added to Slack as attachments are in a short style + * @var bool + */ + private $useShortAttachment; + + /** + * Whether the attachment should include context and extra data + * @var bool + */ + private $includeContextAndExtra; + + /** + * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + * @var string[] + */ + private $excludeFields; + + /** + * @var ?FormatterInterface + */ + private $formatter; + + /** + * @var NormalizerFormatter + */ + private $normalizerFormatter; + + /** + * @param string[] $excludeFields + */ + public function __construct( + ?string $channel = null, + ?string $username = null, + bool $useAttachment = true, + ?string $userIcon = null, + bool $useShortAttachment = false, + bool $includeContextAndExtra = false, + array $excludeFields = array(), + ?FormatterInterface $formatter = null + ) { + $this + ->setChannel($channel) + ->setUsername($username) + ->useAttachment($useAttachment) + ->setUserIcon($userIcon) + ->useShortAttachment($useShortAttachment) + ->includeContextAndExtra($includeContextAndExtra) + ->excludeFields($excludeFields) + ->setFormatter($formatter); + + if ($this->includeContextAndExtra) { + $this->normalizerFormatter = new NormalizerFormatter(); + } + } + + /** + * Returns required data in format that Slack + * is expecting. + * + * @phpstan-param FormattedRecord $record + * @phpstan-return mixed[] + */ + public function getSlackData(array $record): array + { + $dataArray = array(); + $record = $this->removeExcludedFields($record); + + if ($this->username) { + $dataArray['username'] = $this->username; + } + + if ($this->channel) { + $dataArray['channel'] = $this->channel; + } + + if ($this->formatter && !$this->useAttachment) { + /** @phpstan-ignore-next-line */ + $message = $this->formatter->format($record); + } else { + $message = $record['message']; + } + + if ($this->useAttachment) { + $attachment = array( + 'fallback' => $message, + 'text' => $message, + 'color' => $this->getAttachmentColor($record['level']), + 'fields' => array(), + 'mrkdwn_in' => array('fields'), + 'ts' => $record['datetime']->getTimestamp(), + 'footer' => $this->username, + 'footer_icon' => $this->userIcon, + ); + + if ($this->useShortAttachment) { + $attachment['title'] = $record['level_name']; + } else { + $attachment['title'] = 'Message'; + $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']); + } + + if ($this->includeContextAndExtra) { + foreach (array('extra', 'context') as $key) { + if (empty($record[$key])) { + continue; + } + + if ($this->useShortAttachment) { + $attachment['fields'][] = $this->generateAttachmentField( + (string) $key, + $record[$key] + ); + } else { + // Add all extra fields as individual fields in attachment + $attachment['fields'] = array_merge( + $attachment['fields'], + $this->generateAttachmentFields($record[$key]) + ); + } + } + } + + $dataArray['attachments'] = array($attachment); + } else { + $dataArray['text'] = $message; + } + + if ($this->userIcon) { + if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) { + $dataArray['icon_url'] = $this->userIcon; + } else { + $dataArray['icon_emoji'] = ":{$this->userIcon}:"; + } + } + + return $dataArray; + } + + /** + * Returns a Slack message attachment color associated with + * provided level. + */ + public function getAttachmentColor(int $level): string + { + switch (true) { + case $level >= Logger::ERROR: + return static::COLOR_DANGER; + case $level >= Logger::WARNING: + return static::COLOR_WARNING; + case $level >= Logger::INFO: + return static::COLOR_GOOD; + default: + return static::COLOR_DEFAULT; + } + } + + /** + * Stringifies an array of key/value pairs to be used in attachment fields + * + * @param mixed[] $fields + */ + public function stringify(array $fields): string + { + /** @var Record $fields */ + $normalized = $this->normalizerFormatter->format($fields); + + $hasSecondDimension = count(array_filter($normalized, 'is_array')); + $hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric')); + + return $hasSecondDimension || $hasNonNumericKeys + ? Utils::jsonEncode($normalized, JSON_PRETTY_PRINT|Utils::DEFAULT_JSON_FLAGS) + : Utils::jsonEncode($normalized, Utils::DEFAULT_JSON_FLAGS); + } + + /** + * Channel used by the bot when posting + * + * @param ?string $channel + * + * @return static + */ + public function setChannel(?string $channel = null): self + { + $this->channel = $channel; + + return $this; + } + + /** + * Username used by the bot when posting + * + * @param ?string $username + * + * @return static + */ + public function setUsername(?string $username = null): self + { + $this->username = $username; + + return $this; + } + + public function useAttachment(bool $useAttachment = true): self + { + $this->useAttachment = $useAttachment; + + return $this; + } + + public function setUserIcon(?string $userIcon = null): self + { + $this->userIcon = $userIcon; + + if (\is_string($userIcon)) { + $this->userIcon = trim($userIcon, ':'); + } + + return $this; + } + + public function useShortAttachment(bool $useShortAttachment = false): self + { + $this->useShortAttachment = $useShortAttachment; + + return $this; + } + + public function includeContextAndExtra(bool $includeContextAndExtra = false): self + { + $this->includeContextAndExtra = $includeContextAndExtra; + + if ($this->includeContextAndExtra) { + $this->normalizerFormatter = new NormalizerFormatter(); + } + + return $this; + } + + /** + * @param string[] $excludeFields + */ + public function excludeFields(array $excludeFields = []): self + { + $this->excludeFields = $excludeFields; + + return $this; + } + + public function setFormatter(?FormatterInterface $formatter = null): self + { + $this->formatter = $formatter; + + return $this; + } + + /** + * Generates attachment field + * + * @param string|mixed[] $value + * + * @return array{title: string, value: string, short: false} + */ + private function generateAttachmentField(string $title, $value): array + { + $value = is_array($value) + ? sprintf('```%s```', substr($this->stringify($value), 0, 1990)) + : $value; + + return array( + 'title' => ucfirst($title), + 'value' => $value, + 'short' => false, + ); + } + + /** + * Generates a collection of attachment fields from array + * + * @param mixed[] $data + * + * @return array + */ + private function generateAttachmentFields(array $data): array + { + /** @var Record $data */ + $normalized = $this->normalizerFormatter->format($data); + + $fields = array(); + foreach ($normalized as $key => $value) { + $fields[] = $this->generateAttachmentField((string) $key, $value); + } + + return $fields; + } + + /** + * Get a copy of record with fields excluded according to $this->excludeFields + * + * @phpstan-param FormattedRecord $record + * + * @return mixed[] + */ + private function removeExcludedFields(array $record): array + { + foreach ($this->excludeFields as $field) { + $keys = explode('.', $field); + $node = &$record; + $lastKey = end($keys); + foreach ($keys as $key) { + if (!isset($node[$key])) { + break; + } + if ($lastKey === $key) { + unset($node[$key]); + break; + } + $node = &$node[$key]; + } + } + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php new file mode 100644 index 0000000..a648513 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php @@ -0,0 +1,256 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Handler\Slack\SlackRecord; + +/** + * Sends notifications through Slack API + * + * @author Greg Kedzierski + * @see https://api.slack.com/ + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class SlackHandler extends SocketHandler +{ + /** + * Slack API token + * @var string + */ + private $token; + + /** + * Instance of the SlackRecord util class preparing data for Slack API. + * @var SlackRecord + */ + private $slackRecord; + + /** + * @param string $token Slack API token + * @param string $channel Slack channel (encoded ID or name) + * @param string|null $username Name of a bot + * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) + * @param string|null $iconEmoji The emoji name to use (or null) + * @param bool $useShortAttachment Whether the context/extra messages added to Slack as attachments are in a short style + * @param bool $includeContextAndExtra Whether the attachment should include context and extra data + * @param string[] $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + * @throws MissingExtensionException If no OpenSSL PHP extension configured + */ + public function __construct( + string $token, + string $channel, + ?string $username = null, + bool $useAttachment = true, + ?string $iconEmoji = null, + $level = Logger::CRITICAL, + bool $bubble = true, + bool $useShortAttachment = false, + bool $includeContextAndExtra = false, + array $excludeFields = array(), + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler'); + } + + parent::__construct( + 'ssl://slack.com:443', + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + + $this->slackRecord = new SlackRecord( + $channel, + $username, + $useAttachment, + $iconEmoji, + $useShortAttachment, + $includeContextAndExtra, + $excludeFields + ); + + $this->token = $token; + } + + public function getSlackRecord(): SlackRecord + { + return $this->slackRecord; + } + + public function getToken(): string + { + return $this->token; + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the body of API call + * + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record): string + { + $dataArray = $this->prepareContentData($record); + + return http_build_query($dataArray); + } + + /** + * @phpstan-param FormattedRecord $record + * @return string[] + */ + protected function prepareContentData(array $record): array + { + $dataArray = $this->slackRecord->getSlackData($record); + $dataArray['token'] = $this->token; + + if (!empty($dataArray['attachments'])) { + $dataArray['attachments'] = Utils::jsonEncode($dataArray['attachments']); + } + + return $dataArray; + } + + /** + * Builds the header of the API Call + */ + private function buildHeader(string $content): string + { + $header = "POST /api/chat.postMessage HTTP/1.1\r\n"; + $header .= "Host: slack.com\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + parent::write($record); + $this->finalizeWrite(); + } + + /** + * Finalizes the request by reading some bytes and then closing the socket + * + * If we do not read some but close the socket too early, slack sometimes + * drops the request entirely. + */ + protected function finalizeWrite(): void + { + $res = $this->getResource(); + if (is_resource($res)) { + @fread($res, 2048); + } + $this->closeSocket(); + } + + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + parent::setFormatter($formatter); + $this->slackRecord->setFormatter($formatter); + + return $this; + } + + public function getFormatter(): FormatterInterface + { + $formatter = parent::getFormatter(); + $this->slackRecord->setFormatter($formatter); + + return $formatter; + } + + /** + * Channel used by the bot when posting + */ + public function setChannel(string $channel): self + { + $this->slackRecord->setChannel($channel); + + return $this; + } + + /** + * Username used by the bot when posting + */ + public function setUsername(string $username): self + { + $this->slackRecord->setUsername($username); + + return $this; + } + + public function useAttachment(bool $useAttachment): self + { + $this->slackRecord->useAttachment($useAttachment); + + return $this; + } + + public function setIconEmoji(string $iconEmoji): self + { + $this->slackRecord->setUserIcon($iconEmoji); + + return $this; + } + + public function useShortAttachment(bool $useShortAttachment): self + { + $this->slackRecord->useShortAttachment($useShortAttachment); + + return $this; + } + + public function includeContextAndExtra(bool $includeContextAndExtra): self + { + $this->slackRecord->includeContextAndExtra($includeContextAndExtra); + + return $this; + } + + /** + * @param string[] $excludeFields + */ + public function excludeFields(array $excludeFields): self + { + $this->slackRecord->excludeFields($excludeFields); + + return $this; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php new file mode 100644 index 0000000..8ae3c78 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Handler\Slack\SlackRecord; + +/** + * Sends notifications through Slack Webhooks + * + * @author Haralan Dobrev + * @see https://api.slack.com/incoming-webhooks + */ +class SlackWebhookHandler extends AbstractProcessingHandler +{ + /** + * Slack Webhook token + * @var string + */ + private $webhookUrl; + + /** + * Instance of the SlackRecord util class preparing data for Slack API. + * @var SlackRecord + */ + private $slackRecord; + + /** + * @param string $webhookUrl Slack Webhook URL + * @param string|null $channel Slack channel (encoded ID or name) + * @param string|null $username Name of a bot + * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) + * @param string|null $iconEmoji The emoji name to use (or null) + * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style + * @param bool $includeContextAndExtra Whether the attachment should include context and extra data + * @param string[] $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + */ + public function __construct( + string $webhookUrl, + ?string $channel = null, + ?string $username = null, + bool $useAttachment = true, + ?string $iconEmoji = null, + bool $useShortAttachment = false, + bool $includeContextAndExtra = false, + $level = Logger::CRITICAL, + bool $bubble = true, + array $excludeFields = array() + ) { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the SlackWebhookHandler'); + } + + parent::__construct($level, $bubble); + + $this->webhookUrl = $webhookUrl; + + $this->slackRecord = new SlackRecord( + $channel, + $username, + $useAttachment, + $iconEmoji, + $useShortAttachment, + $includeContextAndExtra, + $excludeFields + ); + } + + public function getSlackRecord(): SlackRecord + { + return $this->slackRecord; + } + + public function getWebhookUrl(): string + { + return $this->webhookUrl; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $postData = $this->slackRecord->getSlackData($record); + $postString = Utils::jsonEncode($postData); + + $ch = curl_init(); + $options = array( + CURLOPT_URL => $this->webhookUrl, + CURLOPT_POST => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => array('Content-type: application/json'), + CURLOPT_POSTFIELDS => $postString, + ); + if (defined('CURLOPT_SAFE_UPLOAD')) { + $options[CURLOPT_SAFE_UPLOAD] = true; + } + + curl_setopt_array($ch, $options); + + Curl\Util::execute($ch); + } + + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + parent::setFormatter($formatter); + $this->slackRecord->setFormatter($formatter); + + return $this; + } + + public function getFormatter(): FormatterInterface + { + $formatter = parent::getFormatter(); + $this->slackRecord->setFormatter($formatter); + + return $formatter; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php new file mode 100644 index 0000000..21701af --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php @@ -0,0 +1,448 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to any socket - uses fsockopen() or pfsockopen(). + * + * @author Pablo de Leon Belloc + * @see http://php.net/manual/en/function.fsockopen.php + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class SocketHandler extends AbstractProcessingHandler +{ + /** @var string */ + private $connectionString; + /** @var float */ + private $connectionTimeout; + /** @var resource|null */ + private $resource; + /** @var float */ + private $timeout; + /** @var float */ + private $writingTimeout; + /** @var ?int */ + private $lastSentBytes = null; + /** @var ?int */ + private $chunkSize; + /** @var bool */ + private $persistent; + /** @var ?int */ + private $errno = null; + /** @var ?string */ + private $errstr = null; + /** @var ?float */ + private $lastWritingAt = null; + + /** + * @param string $connectionString Socket connection string + * @param bool $persistent Flag to enable/disable persistent connections + * @param float $timeout Socket timeout to wait until the request is being aborted + * @param float $writingTimeout Socket timeout to wait until the request should've been sent/written + * @param float|null $connectionTimeout Socket connect timeout to wait until the connection should've been + * established + * @param int|null $chunkSize Sets the chunk size. Only has effect during connection in the writing cycle + * + * @throws \InvalidArgumentException If an invalid timeout value (less than 0) is passed. + */ + public function __construct( + string $connectionString, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + parent::__construct($level, $bubble); + $this->connectionString = $connectionString; + + if ($connectionTimeout !== null) { + $this->validateTimeout($connectionTimeout); + } + + $this->connectionTimeout = $connectionTimeout ?? (float) ini_get('default_socket_timeout'); + $this->persistent = $persistent; + $this->validateTimeout($timeout); + $this->timeout = $timeout; + $this->validateTimeout($writingTimeout); + $this->writingTimeout = $writingTimeout; + $this->chunkSize = $chunkSize; + } + + /** + * Connect (if necessary) and write to the socket + * + * {@inheritDoc} + * + * @throws \UnexpectedValueException + * @throws \RuntimeException + */ + protected function write(array $record): void + { + $this->connectIfNotConnected(); + $data = $this->generateDataStream($record); + $this->writeToSocket($data); + } + + /** + * We will not close a PersistentSocket instance so it can be reused in other requests. + */ + public function close(): void + { + if (!$this->isPersistent()) { + $this->closeSocket(); + } + } + + /** + * Close socket, if open + */ + public function closeSocket(): void + { + if (is_resource($this->resource)) { + fclose($this->resource); + $this->resource = null; + } + } + + /** + * Set socket connection to be persistent. It only has effect before the connection is initiated. + */ + public function setPersistent(bool $persistent): self + { + $this->persistent = $persistent; + + return $this; + } + + /** + * Set connection timeout. Only has effect before we connect. + * + * @see http://php.net/manual/en/function.fsockopen.php + */ + public function setConnectionTimeout(float $seconds): self + { + $this->validateTimeout($seconds); + $this->connectionTimeout = $seconds; + + return $this; + } + + /** + * Set write timeout. Only has effect before we connect. + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + */ + public function setTimeout(float $seconds): self + { + $this->validateTimeout($seconds); + $this->timeout = $seconds; + + return $this; + } + + /** + * Set writing timeout. Only has effect during connection in the writing cycle. + * + * @param float $seconds 0 for no timeout + */ + public function setWritingTimeout(float $seconds): self + { + $this->validateTimeout($seconds); + $this->writingTimeout = $seconds; + + return $this; + } + + /** + * Set chunk size. Only has effect during connection in the writing cycle. + */ + public function setChunkSize(int $bytes): self + { + $this->chunkSize = $bytes; + + return $this; + } + + /** + * Get current connection string + */ + public function getConnectionString(): string + { + return $this->connectionString; + } + + /** + * Get persistent setting + */ + public function isPersistent(): bool + { + return $this->persistent; + } + + /** + * Get current connection timeout setting + */ + public function getConnectionTimeout(): float + { + return $this->connectionTimeout; + } + + /** + * Get current in-transfer timeout + */ + public function getTimeout(): float + { + return $this->timeout; + } + + /** + * Get current local writing timeout + * + * @return float + */ + public function getWritingTimeout(): float + { + return $this->writingTimeout; + } + + /** + * Get current chunk size + */ + public function getChunkSize(): ?int + { + return $this->chunkSize; + } + + /** + * Check to see if the socket is currently available. + * + * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details. + */ + public function isConnected(): bool + { + return is_resource($this->resource) + && !feof($this->resource); // on TCP - other party can close connection. + } + + /** + * Wrapper to allow mocking + * + * @return resource|false + */ + protected function pfsockopen() + { + return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + * + * @return resource|false + */ + protected function fsockopen() + { + return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + * + * @return bool + */ + protected function streamSetTimeout() + { + $seconds = floor($this->timeout); + $microseconds = round(($this->timeout - $seconds) * 1e6); + + if (!is_resource($this->resource)) { + throw new \LogicException('streamSetTimeout called but $this->resource is not a resource'); + } + + return stream_set_timeout($this->resource, (int) $seconds, (int) $microseconds); + } + + /** + * Wrapper to allow mocking + * + * @see http://php.net/manual/en/function.stream-set-chunk-size.php + * + * @return int|bool + */ + protected function streamSetChunkSize() + { + if (!is_resource($this->resource)) { + throw new \LogicException('streamSetChunkSize called but $this->resource is not a resource'); + } + + if (null === $this->chunkSize) { + throw new \LogicException('streamSetChunkSize called but $this->chunkSize is not set'); + } + + return stream_set_chunk_size($this->resource, $this->chunkSize); + } + + /** + * Wrapper to allow mocking + * + * @return int|bool + */ + protected function fwrite(string $data) + { + if (!is_resource($this->resource)) { + throw new \LogicException('fwrite called but $this->resource is not a resource'); + } + + return @fwrite($this->resource, $data); + } + + /** + * Wrapper to allow mocking + * + * @return mixed[]|bool + */ + protected function streamGetMetadata() + { + if (!is_resource($this->resource)) { + throw new \LogicException('streamGetMetadata called but $this->resource is not a resource'); + } + + return stream_get_meta_data($this->resource); + } + + private function validateTimeout(float $value): void + { + if ($value < 0) { + throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)"); + } + } + + private function connectIfNotConnected(): void + { + if ($this->isConnected()) { + return; + } + $this->connect(); + } + + /** + * @phpstan-param FormattedRecord $record + */ + protected function generateDataStream(array $record): string + { + return (string) $record['formatted']; + } + + /** + * @return resource|null + */ + protected function getResource() + { + return $this->resource; + } + + private function connect(): void + { + $this->createSocketResource(); + $this->setSocketTimeout(); + $this->setStreamChunkSize(); + } + + private function createSocketResource(): void + { + if ($this->isPersistent()) { + $resource = $this->pfsockopen(); + } else { + $resource = $this->fsockopen(); + } + if (is_bool($resource)) { + throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)"); + } + $this->resource = $resource; + } + + private function setSocketTimeout(): void + { + if (!$this->streamSetTimeout()) { + throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()"); + } + } + + private function setStreamChunkSize(): void + { + if ($this->chunkSize && !$this->streamSetChunkSize()) { + throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()"); + } + } + + private function writeToSocket(string $data): void + { + $length = strlen($data); + $sent = 0; + $this->lastSentBytes = $sent; + while ($this->isConnected() && $sent < $length) { + if (0 == $sent) { + $chunk = $this->fwrite($data); + } else { + $chunk = $this->fwrite(substr($data, $sent)); + } + if ($chunk === false) { + throw new \RuntimeException("Could not write to socket"); + } + $sent += $chunk; + $socketInfo = $this->streamGetMetadata(); + if (is_array($socketInfo) && $socketInfo['timed_out']) { + throw new \RuntimeException("Write timed-out"); + } + + if ($this->writingIsTimedOut($sent)) { + throw new \RuntimeException("Write timed-out, no data sent for `{$this->writingTimeout}` seconds, probably we got disconnected (sent $sent of $length)"); + } + } + if (!$this->isConnected() && $sent < $length) { + throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)"); + } + } + + private function writingIsTimedOut(int $sent): bool + { + // convert to ms + if (0.0 == $this->writingTimeout) { + return false; + } + + if ($sent !== $this->lastSentBytes) { + $this->lastWritingAt = microtime(true); + $this->lastSentBytes = $sent; + + return false; + } else { + usleep(100); + } + + if ((microtime(true) - $this->lastWritingAt) >= $this->writingTimeout) { + $this->closeSocket(); + + return true; + } + + return false; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php new file mode 100644 index 0000000..dcf282b --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Aws\Sqs\SqsClient; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Writes to any sqs queue. + * + * @author Martijn van Calker + */ +class SqsHandler extends AbstractProcessingHandler +{ + /** 256 KB in bytes - maximum message size in SQS */ + protected const MAX_MESSAGE_SIZE = 262144; + /** 100 KB in bytes - head message size for new error log */ + protected const HEAD_MESSAGE_SIZE = 102400; + + /** @var SqsClient */ + private $client; + /** @var string */ + private $queueUrl; + + public function __construct(SqsClient $sqsClient, string $queueUrl, $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + + $this->client = $sqsClient; + $this->queueUrl = $queueUrl; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!isset($record['formatted']) || 'string' !== gettype($record['formatted'])) { + throw new \InvalidArgumentException('SqsHandler accepts only formatted records as a string' . Utils::getRecordMessageForException($record)); + } + + $messageBody = $record['formatted']; + if (strlen($messageBody) >= static::MAX_MESSAGE_SIZE) { + $messageBody = Utils::substr($messageBody, 0, static::HEAD_MESSAGE_SIZE); + } + + $this->client->sendMessage([ + 'QueueUrl' => $this->queueUrl, + 'MessageBody' => $messageBody, + ]); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php new file mode 100644 index 0000000..82c048e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * Stores to any stream resource + * + * Can be used to store into php://stderr, remote and local files, etc. + * + * @author Jordi Boggiano + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class StreamHandler extends AbstractProcessingHandler +{ + /** @const int */ + protected const MAX_CHUNK_SIZE = 2147483647; + /** @const int 10MB */ + protected const DEFAULT_CHUNK_SIZE = 10 * 1024 * 1024; + /** @var int */ + protected $streamChunkSize; + /** @var resource|null */ + protected $stream; + /** @var ?string */ + protected $url = null; + /** @var ?string */ + private $errorMessage = null; + /** @var ?int */ + protected $filePermission; + /** @var bool */ + protected $useLocking; + /** @var true|null */ + private $dirCreated = null; + + /** + * @param resource|string $stream If a missing path can't be created, an UnexpectedValueException will be thrown on first write + * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) + * @param bool $useLocking Try to lock log file before doing any writes + * + * @throws \InvalidArgumentException If stream is not a resource or string + */ + public function __construct($stream, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false) + { + parent::__construct($level, $bubble); + + if (($phpMemoryLimit = Utils::expandIniShorthandBytes(ini_get('memory_limit'))) !== false) { + if ($phpMemoryLimit > 0) { + // use max 10% of allowed memory for the chunk size, and at least 100KB + $this->streamChunkSize = min(static::MAX_CHUNK_SIZE, max((int) ($phpMemoryLimit / 10), 100 * 1024)); + } else { + // memory is unlimited, set to the default 10MB + $this->streamChunkSize = static::DEFAULT_CHUNK_SIZE; + } + } else { + // no memory limit information, set to the default 10MB + $this->streamChunkSize = static::DEFAULT_CHUNK_SIZE; + } + + if (is_resource($stream)) { + $this->stream = $stream; + + stream_set_chunk_size($this->stream, $this->streamChunkSize); + } elseif (is_string($stream)) { + $this->url = Utils::canonicalizePath($stream); + } else { + throw new \InvalidArgumentException('A stream must either be a resource or a string.'); + } + + $this->filePermission = $filePermission; + $this->useLocking = $useLocking; + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + if ($this->url && is_resource($this->stream)) { + fclose($this->stream); + } + $this->stream = null; + $this->dirCreated = null; + } + + /** + * Return the currently active stream if it is open + * + * @return resource|null + */ + public function getStream() + { + return $this->stream; + } + + /** + * Return the stream URL if it was configured with a URL and not an active resource + * + * @return string|null + */ + public function getUrl(): ?string + { + return $this->url; + } + + /** + * @return int + */ + public function getStreamChunkSize(): int + { + return $this->streamChunkSize; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!is_resource($this->stream)) { + $url = $this->url; + if (null === $url || '' === $url) { + throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().' . Utils::getRecordMessageForException($record)); + } + $this->createDir($url); + $this->errorMessage = null; + set_error_handler([$this, 'customErrorHandler']); + try { + $stream = fopen($url, 'a'); + if ($this->filePermission !== null) { + @chmod($url, $this->filePermission); + } + } finally { + restore_error_handler(); + } + if (!is_resource($stream)) { + $this->stream = null; + + throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened in append mode: '.$this->errorMessage, $url) . Utils::getRecordMessageForException($record)); + } + stream_set_chunk_size($stream, $this->streamChunkSize); + $this->stream = $stream; + } + + $stream = $this->stream; + if (!is_resource($stream)) { + throw new \LogicException('No stream was opened yet' . Utils::getRecordMessageForException($record)); + } + + if ($this->useLocking) { + // ignoring errors here, there's not much we can do about them + flock($stream, LOCK_EX); + } + + $this->streamWrite($stream, $record); + + if ($this->useLocking) { + flock($stream, LOCK_UN); + } + } + + /** + * Write to stream + * @param resource $stream + * @param array $record + * + * @phpstan-param FormattedRecord $record + */ + protected function streamWrite($stream, array $record): void + { + fwrite($stream, (string) $record['formatted']); + } + + private function customErrorHandler(int $code, string $msg): bool + { + $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg); + + return true; + } + + private function getDirFromStream(string $stream): ?string + { + $pos = strpos($stream, '://'); + if ($pos === false) { + return dirname($stream); + } + + if ('file://' === substr($stream, 0, 7)) { + return dirname(substr($stream, 7)); + } + + return null; + } + + private function createDir(string $url): void + { + // Do not try to create dir if it has already been tried. + if ($this->dirCreated) { + return; + } + + $dir = $this->getDirFromStream($url); + if (null !== $dir && !is_dir($dir)) { + $this->errorMessage = null; + set_error_handler([$this, 'customErrorHandler']); + $status = mkdir($dir, 0777, true); + restore_error_handler(); + if (false === $status && !is_dir($dir) && strpos((string) $this->errorMessage, 'File exists') === false) { + throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and it could not be created: '.$this->errorMessage, $dir)); + } + } + $this->dirCreated = true; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php new file mode 100644 index 0000000..fae9251 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; +use Swift_Message; +use Swift; + +/** + * SwiftMailerHandler uses Swift_Mailer to send the emails + * + * @author Gyula Sallai + * + * @phpstan-import-type Record from \Monolog\Logger + * @deprecated Since Monolog 2.6. Use SymfonyMailerHandler instead. + */ +class SwiftMailerHandler extends MailHandler +{ + /** @var \Swift_Mailer */ + protected $mailer; + /** @var Swift_Message|callable(string, Record[]): Swift_Message */ + private $messageTemplate; + + /** + * @psalm-param Swift_Message|callable(string, Record[]): Swift_Message $message + * + * @param \Swift_Mailer $mailer The mailer to use + * @param callable|Swift_Message $message An example message for real messages, only the body will be replaced + */ + public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, bool $bubble = true) + { + parent::__construct($level, $bubble); + + @trigger_error('The SwiftMailerHandler is deprecated since Monolog 2.6. Use SymfonyMailerHandler instead.', E_USER_DEPRECATED); + + $this->mailer = $mailer; + $this->messageTemplate = $message; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $this->mailer->send($this->buildMessage($content, $records)); + } + + /** + * Gets the formatter for the Swift_Message subject. + * + * @param string|null $format The format of the subject + */ + protected function getSubjectFormatter(?string $format): FormatterInterface + { + return new LineFormatter($format); + } + + /** + * Creates instance of Swift_Message to be sent + * + * @param string $content formatted email body to be sent + * @param array $records Log records that formed the content + * @return Swift_Message + * + * @phpstan-param Record[] $records + */ + protected function buildMessage(string $content, array $records): Swift_Message + { + $message = null; + if ($this->messageTemplate instanceof Swift_Message) { + $message = clone $this->messageTemplate; + $message->generateId(); + } elseif (is_callable($this->messageTemplate)) { + $message = ($this->messageTemplate)($content, $records); + } + + if (!$message instanceof Swift_Message) { + $record = reset($records); + throw new \InvalidArgumentException('Could not resolve message as instance of Swift_Message or a callable returning it' . ($record ? Utils::getRecordMessageForException($record) : '')); + } + + if ($records) { + $subjectFormatter = $this->getSubjectFormatter($message->getSubject()); + $message->setSubject($subjectFormatter->format($this->getHighestRecord($records))); + } + + $mime = 'text/plain'; + if ($this->isHtmlBody($content)) { + $mime = 'text/html'; + } + + $message->setBody($content, $mime); + /** @phpstan-ignore-next-line */ + if (version_compare(Swift::VERSION, '6.0.0', '>=')) { + $message->setDate(new \DateTimeImmutable()); + } else { + /** @phpstan-ignore-next-line */ + $message->setDate(time()); + } + + return $message; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php new file mode 100644 index 0000000..130e6f1 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Component\Mime\Email; + +/** + * SymfonyMailerHandler uses Symfony's Mailer component to send the emails + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class SymfonyMailerHandler extends MailHandler +{ + /** @var MailerInterface|TransportInterface */ + protected $mailer; + /** @var Email|callable(string, Record[]): Email */ + private $emailTemplate; + + /** + * @psalm-param Email|callable(string, Record[]): Email $email + * + * @param MailerInterface|TransportInterface $mailer The mailer to use + * @param callable|Email $email An email template, the subject/body will be replaced + */ + public function __construct($mailer, $email, $level = Logger::ERROR, bool $bubble = true) + { + parent::__construct($level, $bubble); + + $this->mailer = $mailer; + $this->emailTemplate = $email; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $this->mailer->send($this->buildMessage($content, $records)); + } + + /** + * Gets the formatter for the Swift_Message subject. + * + * @param string|null $format The format of the subject + */ + protected function getSubjectFormatter(?string $format): FormatterInterface + { + return new LineFormatter($format); + } + + /** + * Creates instance of Email to be sent + * + * @param string $content formatted email body to be sent + * @param array $records Log records that formed the content + * + * @phpstan-param Record[] $records + */ + protected function buildMessage(string $content, array $records): Email + { + $message = null; + if ($this->emailTemplate instanceof Email) { + $message = clone $this->emailTemplate; + } elseif (is_callable($this->emailTemplate)) { + $message = ($this->emailTemplate)($content, $records); + } + + if (!$message instanceof Email) { + $record = reset($records); + throw new \InvalidArgumentException('Could not resolve message as instance of Email or a callable returning it' . ($record ? Utils::getRecordMessageForException($record) : '')); + } + + if ($records) { + $subjectFormatter = $this->getSubjectFormatter($message->getSubject()); + $message->subject($subjectFormatter->format($this->getHighestRecord($records))); + } + + if ($this->isHtmlBody($content)) { + if (null !== ($charset = $message->getHtmlCharset())) { + $message->html($content, $charset); + } else { + $message->html($content); + } + } else { + if (null !== ($charset = $message->getTextCharset())) { + $message->text($content, $charset); + } else { + $message->text($content); + } + } + + return $message->date(new \DateTimeImmutable()); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php new file mode 100644 index 0000000..1d543b7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * Logs to syslog service. + * + * usage example: + * + * $log = new Logger('application'); + * $syslog = new SyslogHandler('myfacility', 'local6'); + * $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%"); + * $syslog->setFormatter($formatter); + * $log->pushHandler($syslog); + * + * @author Sven Paulus + */ +class SyslogHandler extends AbstractSyslogHandler +{ + /** @var string */ + protected $ident; + /** @var int */ + protected $logopts; + + /** + * @param string $ident + * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant + * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID + */ + public function __construct(string $ident, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, int $logopts = LOG_PID) + { + parent::__construct($facility, $level, $bubble); + + $this->ident = $ident; + $this->logopts = $logopts; + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + closelog(); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!openlog($this->ident, $this->logopts, $this->facility)) { + throw new \LogicException('Can\'t open syslog for ident "'.$this->ident.'" and facility "'.$this->facility.'"' . Utils::getRecordMessageForException($record)); + } + syslog($this->logLevels[$record['level']], (string) $record['formatted']); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php new file mode 100644 index 0000000..dbd8ef6 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\SyslogUdp; + +use Monolog\Utils; +use Socket; + +class UdpSocket +{ + protected const DATAGRAM_MAX_LENGTH = 65023; + + /** @var string */ + protected $ip; + /** @var int */ + protected $port; + /** @var resource|Socket|null */ + protected $socket = null; + + public function __construct(string $ip, int $port = 514) + { + $this->ip = $ip; + $this->port = $port; + } + + /** + * @param string $line + * @param string $header + * @return void + */ + public function write($line, $header = "") + { + $this->send($this->assembleMessage($line, $header)); + } + + public function close(): void + { + if (is_resource($this->socket) || $this->socket instanceof Socket) { + socket_close($this->socket); + $this->socket = null; + } + } + + /** + * @return resource|Socket + */ + protected function getSocket() + { + if (null !== $this->socket) { + return $this->socket; + } + + $domain = AF_INET; + $protocol = SOL_UDP; + // Check if we are using unix sockets. + if ($this->port === 0) { + $domain = AF_UNIX; + $protocol = IPPROTO_IP; + } + + $this->socket = socket_create($domain, SOCK_DGRAM, $protocol) ?: null; + if (null === $this->socket) { + throw new \RuntimeException('The UdpSocket to '.$this->ip.':'.$this->port.' could not be opened via socket_create'); + } + + return $this->socket; + } + + protected function send(string $chunk): void + { + socket_sendto($this->getSocket(), $chunk, strlen($chunk), $flags = 0, $this->ip, $this->port); + } + + protected function assembleMessage(string $line, string $header): string + { + $chunkSize = static::DATAGRAM_MAX_LENGTH - strlen($header); + + return $header . Utils::substr($line, 0, $chunkSize); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php new file mode 100644 index 0000000..deaa19f --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use DateTimeInterface; +use Monolog\Logger; +use Monolog\Handler\SyslogUdp\UdpSocket; +use Monolog\Utils; + +/** + * A Handler for logging to a remote syslogd server. + * + * @author Jesper Skovgaard Nielsen + * @author Dominik Kukacka + */ +class SyslogUdpHandler extends AbstractSyslogHandler +{ + const RFC3164 = 0; + const RFC5424 = 1; + const RFC5424e = 2; + + /** @var array */ + private $dateFormats = array( + self::RFC3164 => 'M d H:i:s', + self::RFC5424 => \DateTime::RFC3339, + self::RFC5424e => \DateTime::RFC3339_EXTENDED, + ); + + /** @var UdpSocket */ + protected $socket; + /** @var string */ + protected $ident; + /** @var self::RFC* */ + protected $rfc; + + /** + * @param string $host Either IP/hostname or a path to a unix socket (port must be 0 then) + * @param int $port Port number, or 0 if $host is a unix socket + * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param string $ident Program name or tag for each log message. + * @param int $rfc RFC to format the message for. + * @throws MissingExtensionException + * + * @phpstan-param self::RFC* $rfc + */ + public function __construct(string $host, int $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, string $ident = 'php', int $rfc = self::RFC5424) + { + if (!extension_loaded('sockets')) { + throw new MissingExtensionException('The sockets extension is required to use the SyslogUdpHandler'); + } + + parent::__construct($facility, $level, $bubble); + + $this->ident = $ident; + $this->rfc = $rfc; + + $this->socket = new UdpSocket($host, $port); + } + + protected function write(array $record): void + { + $lines = $this->splitMessageIntoLines($record['formatted']); + + $header = $this->makeCommonSyslogHeader($this->logLevels[$record['level']], $record['datetime']); + + foreach ($lines as $line) { + $this->socket->write($line, $header); + } + } + + public function close(): void + { + $this->socket->close(); + } + + /** + * @param string|string[] $message + * @return string[] + */ + private function splitMessageIntoLines($message): array + { + if (is_array($message)) { + $message = implode("\n", $message); + } + + $lines = preg_split('/$\R?^/m', (string) $message, -1, PREG_SPLIT_NO_EMPTY); + if (false === $lines) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Could not preg_split: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode)); + } + + return $lines; + } + + /** + * Make common syslog header (see rfc5424 or rfc3164) + */ + protected function makeCommonSyslogHeader(int $severity, DateTimeInterface $datetime): string + { + $priority = $severity + $this->facility; + + if (!$pid = getmypid()) { + $pid = '-'; + } + + if (!$hostname = gethostname()) { + $hostname = '-'; + } + + if ($this->rfc === self::RFC3164) { + // see https://github.com/phpstan/phpstan/issues/5348 + // @phpstan-ignore-next-line + $dateNew = $datetime->setTimezone(new \DateTimeZone('UTC')); + $date = $dateNew->format($this->dateFormats[$this->rfc]); + + return "<$priority>" . + $date . " " . + $hostname . " " . + $this->ident . "[" . $pid . "]: "; + } + + $date = $datetime->format($this->dateFormats[$this->rfc]); + + return "<$priority>1 " . + $date . " " . + $hostname . " " . + $this->ident . " " . + $pid . " - - "; + } + + /** + * Inject your own socket, mainly used for testing + */ + public function setSocket(UdpSocket $socket): self + { + $this->socket = $socket; + + return $this; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php new file mode 100644 index 0000000..a6223b7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php @@ -0,0 +1,274 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use RuntimeException; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Handler send logs to Telegram using Telegram Bot API. + * + * How to use: + * 1) Create telegram bot with https://telegram.me/BotFather + * 2) Create a telegram channel where logs will be recorded. + * 3) Add created bot from step 1 to the created channel from step 2. + * + * Use telegram bot API key from step 1 and channel name with '@' prefix from step 2 to create instance of TelegramBotHandler + * + * @link https://core.telegram.org/bots/api + * + * @author Mazur Alexandr + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class TelegramBotHandler extends AbstractProcessingHandler +{ + private const BOT_API = 'https://api.telegram.org/bot'; + + /** + * The available values of parseMode according to the Telegram api documentation + */ + private const AVAILABLE_PARSE_MODES = [ + 'HTML', + 'MarkdownV2', + 'Markdown', // legacy mode without underline and strikethrough, use MarkdownV2 instead + ]; + + /** + * The maximum number of characters allowed in a message according to the Telegram api documentation + */ + private const MAX_MESSAGE_LENGTH = 4096; + + /** + * Telegram bot access token provided by BotFather. + * Create telegram bot with https://telegram.me/BotFather and use access token from it. + * @var string + */ + private $apiKey; + + /** + * Telegram channel name. + * Since to start with '@' symbol as prefix. + * @var string + */ + private $channel; + + /** + * The kind of formatting that is used for the message. + * See available options at https://core.telegram.org/bots/api#formatting-options + * or in AVAILABLE_PARSE_MODES + * @var ?string + */ + private $parseMode; + + /** + * Disables link previews for links in the message. + * @var ?bool + */ + private $disableWebPagePreview; + + /** + * Sends the message silently. Users will receive a notification with no sound. + * @var ?bool + */ + private $disableNotification; + + /** + * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. + * False - truncates a message that is too long. + * @var bool + */ + private $splitLongMessages; + + /** + * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests). + * @var bool + */ + private $delayBetweenMessages; + + /** + * @param string $apiKey Telegram bot access token provided by BotFather + * @param string $channel Telegram channel name + * @param bool $splitLongMessages Split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages + * @param bool $delayBetweenMessages Adds delay between sending a split message according to Telegram API + * @throws MissingExtensionException + */ + public function __construct( + string $apiKey, + string $channel, + $level = Logger::DEBUG, + bool $bubble = true, + ?string $parseMode = null, + ?bool $disableWebPagePreview = null, + ?bool $disableNotification = null, + bool $splitLongMessages = false, + bool $delayBetweenMessages = false + ) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the TelegramBotHandler'); + } + + parent::__construct($level, $bubble); + + $this->apiKey = $apiKey; + $this->channel = $channel; + $this->setParseMode($parseMode); + $this->disableWebPagePreview($disableWebPagePreview); + $this->disableNotification($disableNotification); + $this->splitLongMessages($splitLongMessages); + $this->delayBetweenMessages($delayBetweenMessages); + } + + public function setParseMode(?string $parseMode = null): self + { + if ($parseMode !== null && !in_array($parseMode, self::AVAILABLE_PARSE_MODES)) { + throw new \InvalidArgumentException('Unknown parseMode, use one of these: ' . implode(', ', self::AVAILABLE_PARSE_MODES) . '.'); + } + + $this->parseMode = $parseMode; + + return $this; + } + + public function disableWebPagePreview(?bool $disableWebPagePreview = null): self + { + $this->disableWebPagePreview = $disableWebPagePreview; + + return $this; + } + + public function disableNotification(?bool $disableNotification = null): self + { + $this->disableNotification = $disableNotification; + + return $this; + } + + /** + * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. + * False - truncates a message that is too long. + * @param bool $splitLongMessages + * @return $this + */ + public function splitLongMessages(bool $splitLongMessages = false): self + { + $this->splitLongMessages = $splitLongMessages; + + return $this; + } + + /** + * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests). + * @param bool $delayBetweenMessages + * @return $this + */ + public function delayBetweenMessages(bool $delayBetweenMessages = false): self + { + $this->delayBetweenMessages = $delayBetweenMessages; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + /** @var Record[] $messages */ + $messages = []; + + foreach ($records as $record) { + if (!$this->isHandling($record)) { + continue; + } + + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $messages[] = $record; + } + + if (!empty($messages)) { + $this->send((string)$this->getFormatter()->formatBatch($messages)); + } + } + + /** + * @inheritDoc + */ + protected function write(array $record): void + { + $this->send($record['formatted']); + } + + /** + * Send request to @link https://api.telegram.org/bot on SendMessage action. + * @param string $message + */ + protected function send(string $message): void + { + $messages = $this->handleMessageLength($message); + + foreach ($messages as $key => $msg) { + if ($this->delayBetweenMessages && $key > 0) { + sleep(1); + } + + $this->sendCurl($msg); + } + } + + protected function sendCurl(string $message): void + { + $ch = curl_init(); + $url = self::BOT_API . $this->apiKey . '/SendMessage'; + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ + 'text' => $message, + 'chat_id' => $this->channel, + 'parse_mode' => $this->parseMode, + 'disable_web_page_preview' => $this->disableWebPagePreview, + 'disable_notification' => $this->disableNotification, + ])); + + $result = Curl\Util::execute($ch); + if (!is_string($result)) { + throw new RuntimeException('Telegram API error. Description: No response'); + } + $result = json_decode($result, true); + + if ($result['ok'] === false) { + throw new RuntimeException('Telegram API error. Description: ' . $result['description']); + } + } + + /** + * Handle a message that is too long: truncates or splits into several + * @param string $message + * @return string[] + */ + private function handleMessageLength(string $message): array + { + $truncatedMarker = ' (...truncated)'; + if (!$this->splitLongMessages && strlen($message) > self::MAX_MESSAGE_LENGTH) { + return [Utils::substr($message, 0, self::MAX_MESSAGE_LENGTH - strlen($truncatedMarker)) . $truncatedMarker]; + } + + return str_split($message, self::MAX_MESSAGE_LENGTH); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php new file mode 100644 index 0000000..0986da2 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php @@ -0,0 +1,231 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Used for testing purposes. + * + * It records all records and gives you access to them for verification. + * + * @author Jordi Boggiano + * + * @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) + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class TestHandler extends AbstractProcessingHandler +{ + /** @var Record[] */ + protected $records = []; + /** @var array */ + protected $recordsByLevel = []; + /** @var bool */ + private $skipReset = false; + + /** + * @return array + * + * @phpstan-return Record[] + */ + public function getRecords() + { + return $this->records; + } + + /** + * @return void + */ + public function clear() + { + $this->records = []; + $this->recordsByLevel = []; + } + + /** + * @return void + */ + public function reset() + { + if (!$this->skipReset) { + $this->clear(); + } + } + + /** + * @return void + */ + public function setSkipReset(bool $skipReset) + { + $this->skipReset = $skipReset; + } + + /** + * @param string|int $level Logging level value or name + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecords($level): bool + { + return isset($this->recordsByLevel[Logger::toMonologLevel($level)]); + } + + /** + * @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records + * @param string|int $level Logging level value or name + * + * @phpstan-param array{message: string, context?: mixed[]}|string $record + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecord($record, $level): bool + { + if (is_string($record)) { + $record = array('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); + } + + /** + * @param string|int $level Logging level value or name + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecordThatContains(string $message, $level): bool + { + return $this->hasRecordThatPasses(function ($rec) use ($message) { + return strpos($rec['message'], $message) !== false; + }, $level); + } + + /** + * @param string|int $level Logging level value or name + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecordThatMatches(string $regex, $level): bool + { + return $this->hasRecordThatPasses(function (array $rec) use ($regex): bool { + return preg_match($regex, $rec['message']) > 0; + }, $level); + } + + /** + * @param string|int $level Logging level value or name + * @return bool + * + * @psalm-param callable(Record, int): mixed $predicate + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecordThatPasses(callable $predicate, $level) + { + $level = Logger::toMonologLevel($level); + + if (!isset($this->recordsByLevel[$level])) { + return false; + } + + foreach ($this->recordsByLevel[$level] as $i => $rec) { + if ($predicate($rec, $i)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } + + /** + * @param string $method + * @param mixed[] $args + * @return bool + */ + 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 = constant('Monolog\Logger::' . strtoupper($matches[2])); + $callback = [$this, $genericMethod]; + if (is_callable($callback)) { + $args[] = $level; + + return call_user_func_array($callback, $args); + } + } + + throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php b/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php new file mode 100644 index 0000000..c818352 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +trait WebRequestRecognizerTrait +{ + /** + * Checks if PHP's serving a web request + * @return bool + */ + protected function isWebRequest(): bool + { + return 'cli' !== \PHP_SAPI && 'phpdbg' !== \PHP_SAPI; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php new file mode 100644 index 0000000..b6d3d3b --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Forwards records to multiple handlers suppressing failures of each handler + * and continuing through to give every handler a chance to succeed. + * + * @author Craig D'Amelio + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class WhatFailureGroupHandler extends GroupHandler +{ + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + foreach ($this->handlers as $handler) { + try { + $handler->handle($record); + } catch (\Throwable $e) { + // What failure? + } + } + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if ($this->processors) { + $processed = array(); + foreach ($records as $record) { + $processed[] = $this->processRecord($record); + } + /** @var Record[] $records */ + $records = $processed; + } + + foreach ($this->handlers as $handler) { + try { + $handler->handleBatch($records); + } catch (\Throwable $e) { + // What failure? + } + } + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + foreach ($this->handlers as $handler) { + try { + $handler->close(); + } catch (\Throwable $e) { + // What failure? + } + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php new file mode 100644 index 0000000..ddd46d8 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Logger; + +/** + * Handler sending logs to Zend Monitor + * + * @author Christian Bergau + * @author Jason Davis + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class ZendMonitorHandler extends AbstractProcessingHandler +{ + /** + * Monolog level / ZendMonitor Custom Event priority map + * + * @var array + */ + protected $levelMap = []; + + /** + * @throws MissingExtensionException + */ + public function __construct($level = Logger::DEBUG, bool $bubble = true) + { + if (!function_exists('zend_monitor_custom_event')) { + throw new MissingExtensionException( + 'You must have Zend Server installed with Zend Monitor enabled in order to use this handler' + ); + } + //zend monitor constants are not defined if zend monitor is not enabled. + $this->levelMap = [ + Logger::DEBUG => \ZEND_MONITOR_EVENT_SEVERITY_INFO, + Logger::INFO => \ZEND_MONITOR_EVENT_SEVERITY_INFO, + Logger::NOTICE => \ZEND_MONITOR_EVENT_SEVERITY_INFO, + Logger::WARNING => \ZEND_MONITOR_EVENT_SEVERITY_WARNING, + Logger::ERROR => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, + Logger::CRITICAL => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, + Logger::ALERT => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, + Logger::EMERGENCY => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, + ]; + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->writeZendMonitorCustomEvent( + Logger::getLevelName($record['level']), + $record['message'], + $record['formatted'], + $this->levelMap[$record['level']] + ); + } + + /** + * Write to Zend Monitor Events + * @param string $type Text displayed in "Class Name (custom)" field + * @param string $message Text displayed in "Error String" + * @param array $formatted Displayed in Custom Variables tab + * @param int $severity Set the event severity level (-1,0,1) + * + * @phpstan-param FormattedRecord $formatted + */ + protected function writeZendMonitorCustomEvent(string $type, string $message, array $formatted, int $severity): void + { + zend_monitor_custom_event($type, $message, $formatted, $severity); + } + + /** + * {@inheritDoc} + */ + public function getDefaultFormatter(): FormatterInterface + { + return new NormalizerFormatter(); + } + + /** + * @return array + */ + public function getLevelMap(): array + { + return $this->levelMap; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/LogRecord.php b/vendor/monolog/monolog/src/Monolog/LogRecord.php new file mode 100644 index 0000000..702807d --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/LogRecord.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use ArrayAccess; + +/** + * Monolog log record interface for forward compatibility with Monolog 3.0 + * + * This is just present in Monolog 2.4+ to allow interoperable code to be written against + * both versions by type-hinting arguments as `array|\Monolog\LogRecord $record` + * + * Do not rely on this interface for other purposes, and do not implement it. + * + * @author Jordi Boggiano + * @template-extends \ArrayAccess<'message'|'level'|'context'|'level_name'|'channel'|'datetime'|'extra'|'formatted', mixed> + * @phpstan-import-type Record from Logger + */ +interface LogRecord extends \ArrayAccess +{ + /** + * @phpstan-return Record + */ + public function toArray(): array; +} diff --git a/vendor/monolog/monolog/src/Monolog/Logger.php b/vendor/monolog/monolog/src/Monolog/Logger.php new file mode 100644 index 0000000..3c588a7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Logger.php @@ -0,0 +1,761 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use DateTimeZone; +use Monolog\Handler\HandlerInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\InvalidArgumentException; +use Psr\Log\LogLevel; +use Throwable; +use Stringable; + +/** + * Monolog log channel + * + * It contains a stack of Handlers and a stack of Processors, + * and uses them to store records that are added to it. + * + * @author Jordi Boggiano + * + * @phpstan-type Level Logger::DEBUG|Logger::INFO|Logger::NOTICE|Logger::WARNING|Logger::ERROR|Logger::CRITICAL|Logger::ALERT|Logger::EMERGENCY + * @phpstan-type LevelName 'DEBUG'|'INFO'|'NOTICE'|'WARNING'|'ERROR'|'CRITICAL'|'ALERT'|'EMERGENCY' + * @phpstan-type Record array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[]} + */ +class Logger implements LoggerInterface, ResettableInterface +{ + /** + * Detailed debug information + */ + public const DEBUG = 100; + + /** + * Interesting events + * + * Examples: User logs in, SQL logs. + */ + public const INFO = 200; + + /** + * Uncommon events + */ + public const NOTICE = 250; + + /** + * Exceptional occurrences that are not errors + * + * Examples: Use of deprecated APIs, poor use of an API, + * undesirable things that are not necessarily wrong. + */ + public const WARNING = 300; + + /** + * Runtime errors + */ + public const ERROR = 400; + + /** + * Critical conditions + * + * Example: Application component unavailable, unexpected exception. + */ + public const CRITICAL = 500; + + /** + * Action must be taken immediately + * + * Example: Entire website down, database unavailable, etc. + * This should trigger the SMS alerts and wake you up. + */ + public const ALERT = 550; + + /** + * Urgent alert. + */ + public const EMERGENCY = 600; + + /** + * Monolog API version + * + * This is only bumped when API breaks are done and should + * follow the major version of the library + * + * @var int + */ + public const API = 2; + + /** + * This is a static variable and not a constant to serve as an extension point for custom levels + * + * @var array $levels Logging levels with the levels as key + * + * @phpstan-var array $levels Logging levels with the levels as key + */ + protected static $levels = [ + self::DEBUG => 'DEBUG', + self::INFO => 'INFO', + self::NOTICE => 'NOTICE', + self::WARNING => 'WARNING', + self::ERROR => 'ERROR', + self::CRITICAL => 'CRITICAL', + self::ALERT => 'ALERT', + self::EMERGENCY => 'EMERGENCY', + ]; + + /** + * Mapping between levels numbers defined in RFC 5424 and Monolog ones + * + * @phpstan-var array $rfc_5424_levels + */ + private const RFC_5424_LEVELS = [ + 7 => self::DEBUG, + 6 => self::INFO, + 5 => self::NOTICE, + 4 => self::WARNING, + 3 => self::ERROR, + 2 => self::CRITICAL, + 1 => self::ALERT, + 0 => self::EMERGENCY, + ]; + + /** + * @var string + */ + protected $name; + + /** + * The handler stack + * + * @var HandlerInterface[] + */ + protected $handlers; + + /** + * Processors that will process all log records + * + * To process records of a single handler instead, add the processor on that specific handler + * + * @var callable[] + */ + protected $processors; + + /** + * @var bool + */ + protected $microsecondTimestamps = true; + + /** + * @var DateTimeZone + */ + protected $timezone; + + /** + * @var callable|null + */ + protected $exceptionHandler; + + /** + * @var int Keeps track of depth to prevent infinite logging loops + */ + private $logDepth = 0; + + /** + * @var \WeakMap<\Fiber, int>|null Keeps track of depth inside fibers to prevent infinite logging loops + */ + private $fiberLogDepth; + + /** + * @var bool Whether to detect infinite logging loops + * + * This can be disabled via {@see useLoggingLoopDetection} if you have async handlers that do not play well with this + */ + private $detectCycles = true; + + /** + * @psalm-param array $processors + * + * @param string $name The logging channel, a simple descriptive name that is attached to all log records + * @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc. + * @param callable[] $processors Optional array of processors + * @param DateTimeZone|null $timezone Optional timezone, if not provided date_default_timezone_get() will be used + */ + public function __construct(string $name, array $handlers = [], array $processors = [], ?DateTimeZone $timezone = null) + { + $this->name = $name; + $this->setHandlers($handlers); + $this->processors = $processors; + $this->timezone = $timezone ?: new DateTimeZone(date_default_timezone_get() ?: 'UTC'); + + if (\PHP_VERSION_ID >= 80100) { + // Local variable for phpstan, see https://github.com/phpstan/phpstan/issues/6732#issuecomment-1111118412 + /** @var \WeakMap<\Fiber, int> $fiberLogDepth */ + $fiberLogDepth = new \WeakMap(); + $this->fiberLogDepth = $fiberLogDepth; + } + } + + public function getName(): string + { + return $this->name; + } + + /** + * Return a new cloned instance with the name changed + */ + public function withName(string $name): self + { + $new = clone $this; + $new->name = $name; + + return $new; + } + + /** + * Pushes a handler on to the stack. + */ + public function pushHandler(HandlerInterface $handler): self + { + array_unshift($this->handlers, $handler); + + return $this; + } + + /** + * Pops a handler from the stack + * + * @throws \LogicException If empty handler stack + */ + public function popHandler(): HandlerInterface + { + if (!$this->handlers) { + throw new \LogicException('You tried to pop from an empty handler stack.'); + } + + return array_shift($this->handlers); + } + + /** + * Set handlers, replacing all existing ones. + * + * If a map is passed, keys will be ignored. + * + * @param HandlerInterface[] $handlers + */ + public function setHandlers(array $handlers): self + { + $this->handlers = []; + foreach (array_reverse($handlers) as $handler) { + $this->pushHandler($handler); + } + + return $this; + } + + /** + * @return HandlerInterface[] + */ + public function getHandlers(): array + { + return $this->handlers; + } + + /** + * Adds a processor on to the stack. + */ + public function pushProcessor(callable $callback): self + { + array_unshift($this->processors, $callback); + + return $this; + } + + /** + * Removes the processor on top of the stack and returns it. + * + * @throws \LogicException If empty processor stack + * @return callable + */ + public function popProcessor(): callable + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + + return array_shift($this->processors); + } + + /** + * @return callable[] + */ + public function getProcessors(): array + { + return $this->processors; + } + + /** + * Control the use of microsecond resolution timestamps in the 'datetime' + * member of new records. + * + * As of PHP7.1 microseconds are always included by the engine, so + * there is no performance penalty and Monolog 2 enabled microseconds + * by default. This function lets you disable them though in case you want + * to suppress microseconds from the output. + * + * @param bool $micro True to use microtime() to create timestamps + */ + public function useMicrosecondTimestamps(bool $micro): self + { + $this->microsecondTimestamps = $micro; + + return $this; + } + + public function useLoggingLoopDetection(bool $detectCycles): self + { + $this->detectCycles = $detectCycles; + + return $this; + } + + /** + * Adds a log record. + * + * @param int $level The logging level (a Monolog or RFC 5424 level) + * @param string $message The log message + * @param mixed[] $context The log context + * @param DateTimeImmutable $datetime Optional log date to log into the past or future + * @return bool Whether the record has been processed + * + * @phpstan-param Level $level + */ + public function addRecord(int $level, string $message, array $context = [], ?DateTimeImmutable $datetime = null): bool + { + if (isset(self::RFC_5424_LEVELS[$level])) { + $level = self::RFC_5424_LEVELS[$level]; + } + + if ($this->detectCycles) { + if (\PHP_VERSION_ID >= 80100 && $fiber = \Fiber::getCurrent()) { + $this->fiberLogDepth[$fiber] = $this->fiberLogDepth[$fiber] ?? 0; + $logDepth = ++$this->fiberLogDepth[$fiber]; + } else { + $logDepth = ++$this->logDepth; + } + } else { + $logDepth = 0; + } + + if ($logDepth === 3) { + $this->warning('A possible infinite logging loop was detected and aborted. It appears some of your handler code is triggering logging, see the previous log record for a hint as to what may be the cause.'); + return false; + } elseif ($logDepth >= 5) { // log depth 4 is let through, so we can log the warning above + return false; + } + + try { + $record = null; + + foreach ($this->handlers as $handler) { + if (null === $record) { + // skip creating the record as long as no handler is going to handle it + if (!$handler->isHandling(['level' => $level])) { + continue; + } + + $levelName = static::getLevelName($level); + + $record = [ + 'message' => $message, + 'context' => $context, + 'level' => $level, + 'level_name' => $levelName, + 'channel' => $this->name, + 'datetime' => $datetime ?? new DateTimeImmutable($this->microsecondTimestamps, $this->timezone), + 'extra' => [], + ]; + + try { + foreach ($this->processors as $processor) { + $record = $processor($record); + } + } catch (Throwable $e) { + $this->handleException($e, $record); + + return true; + } + } + + // once the record exists, send it to all handlers as long as the bubbling chain is not interrupted + try { + if (true === $handler->handle($record)) { + break; + } + } catch (Throwable $e) { + $this->handleException($e, $record); + + return true; + } + } + } finally { + if ($this->detectCycles) { + if (isset($fiber)) { + $this->fiberLogDepth[$fiber]--; + } else { + $this->logDepth--; + } + } + } + + return null !== $record; + } + + /** + * Ends a log cycle and frees all resources used by handlers. + * + * Closing a Handler means flushing all buffers and freeing any open resources/handles. + * Handlers that have been closed should be able to accept log records again and re-open + * themselves on demand, but this may not always be possible depending on implementation. + * + * This is useful at the end of a request and will be called automatically on every handler + * when they get destructed. + */ + public function close(): void + { + foreach ($this->handlers as $handler) { + $handler->close(); + } + } + + /** + * Ends a log cycle and resets all handlers and processors to their initial state. + * + * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal + * state, and getting it back to a state in which it can receive log records again. + * + * This is useful in case you want to avoid logs leaking between two requests or jobs when you + * have a long running process like a worker or an application server serving multiple requests + * in one process. + */ + public function reset(): void + { + foreach ($this->handlers as $handler) { + if ($handler instanceof ResettableInterface) { + $handler->reset(); + } + } + + foreach ($this->processors as $processor) { + if ($processor instanceof ResettableInterface) { + $processor->reset(); + } + } + } + + /** + * Gets all supported logging levels. + * + * @return array Assoc array with human-readable level names => level codes. + * @phpstan-return array + */ + public static function getLevels(): array + { + return array_flip(static::$levels); + } + + /** + * Gets the name of the logging level. + * + * @throws \Psr\Log\InvalidArgumentException If level is not defined + * + * @phpstan-param Level $level + * @phpstan-return LevelName + */ + public static function getLevelName(int $level): string + { + if (!isset(static::$levels[$level])) { + throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels))); + } + + return static::$levels[$level]; + } + + /** + * Converts PSR-3 levels to Monolog ones if necessary + * + * @param string|int $level Level number (monolog) or name (PSR-3) + * @throws \Psr\Log\InvalidArgumentException If level is not defined + * + * @phpstan-param Level|LevelName|LogLevel::* $level + * @phpstan-return Level + */ + public static function toMonologLevel($level): int + { + if (is_string($level)) { + if (is_numeric($level)) { + /** @phpstan-ignore-next-line */ + return intval($level); + } + + // Contains chars of all log levels and avoids using strtoupper() which may have + // strange results depending on locale (for example, "i" will become "İ" in Turkish locale) + $upper = strtr($level, 'abcdefgilmnortuwy', 'ABCDEFGILMNORTUWY'); + if (defined(__CLASS__.'::'.$upper)) { + return constant(__CLASS__ . '::' . $upper); + } + + throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels) + static::$levels)); + } + + if (!is_int($level)) { + throw new InvalidArgumentException('Level "'.var_export($level, true).'" is not defined, use one of: '.implode(', ', array_keys(static::$levels) + static::$levels)); + } + + return $level; + } + + /** + * Checks whether the Logger has a handler that listens on the given level + * + * @phpstan-param Level $level + */ + public function isHandling(int $level): bool + { + $record = [ + 'level' => $level, + ]; + + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * Set a custom exception handler that will be called if adding a new record fails + * + * The callable will receive an exception object and the record that failed to be logged + */ + public function setExceptionHandler(?callable $callback): self + { + $this->exceptionHandler = $callback; + + return $this; + } + + public function getExceptionHandler(): ?callable + { + return $this->exceptionHandler; + } + + /** + * Adds a log record at an arbitrary level. + * + * This method allows for compatibility with common interfaces. + * + * @param mixed $level The log level (a Monolog, PSR-3 or RFC 5424 level) + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function log($level, $message, array $context = []): void + { + if (!is_int($level) && !is_string($level)) { + throw new \InvalidArgumentException('$level is expected to be a string or int'); + } + + if (isset(self::RFC_5424_LEVELS[$level])) { + $level = self::RFC_5424_LEVELS[$level]; + } + + $level = static::toMonologLevel($level); + + $this->addRecord($level, (string) $message, $context); + } + + /** + * Adds a log record at the DEBUG level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function debug($message, array $context = []): void + { + $this->addRecord(static::DEBUG, (string) $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function info($message, array $context = []): void + { + $this->addRecord(static::INFO, (string) $message, $context); + } + + /** + * Adds a log record at the NOTICE level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function notice($message, array $context = []): void + { + $this->addRecord(static::NOTICE, (string) $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function warning($message, array $context = []): void + { + $this->addRecord(static::WARNING, (string) $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function error($message, array $context = []): void + { + $this->addRecord(static::ERROR, (string) $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function critical($message, array $context = []): void + { + $this->addRecord(static::CRITICAL, (string) $message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function alert($message, array $context = []): void + { + $this->addRecord(static::ALERT, (string) $message, $context); + } + + /** + * Adds a log record at the EMERGENCY level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function emergency($message, array $context = []): void + { + $this->addRecord(static::EMERGENCY, (string) $message, $context); + } + + /** + * Sets the timezone to be used for the timestamp of log records. + */ + public function setTimezone(DateTimeZone $tz): self + { + $this->timezone = $tz; + + return $this; + } + + /** + * Returns the timezone to be used for the timestamp of log records. + */ + public function getTimezone(): DateTimeZone + { + return $this->timezone; + } + + /** + * Delegates exception management to the custom exception handler, + * or throws the exception if no custom handler is set. + * + * @param array $record + * @phpstan-param Record $record + */ + protected function handleException(Throwable $e, array $record): void + { + if (!$this->exceptionHandler) { + throw $e; + } + + ($this->exceptionHandler)($e, $record); + } + + /** + * @return array + */ + public function __serialize(): array + { + return [ + 'name' => $this->name, + 'handlers' => $this->handlers, + 'processors' => $this->processors, + 'microsecondTimestamps' => $this->microsecondTimestamps, + 'timezone' => $this->timezone, + 'exceptionHandler' => $this->exceptionHandler, + 'logDepth' => $this->logDepth, + 'detectCycles' => $this->detectCycles, + ]; + } + + /** + * @param array $data + */ + public function __unserialize(array $data): void + { + foreach (['name', 'handlers', 'processors', 'microsecondTimestamps', 'timezone', 'exceptionHandler', 'logDepth', 'detectCycles'] as $property) { + if (isset($data[$property])) { + $this->$property = $data[$property]; + } + } + + if (\PHP_VERSION_ID >= 80100) { + // Local variable for phpstan, see https://github.com/phpstan/phpstan/issues/6732#issuecomment-1111118412 + /** @var \WeakMap<\Fiber, int> $fiberLogDepth */ + $fiberLogDepth = new \WeakMap(); + $this->fiberLogDepth = $fiberLogDepth; + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php new file mode 100644 index 0000000..8166bdc --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Injects Git branch and Git commit SHA in all records + * + * @author Nick Otter + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class GitProcessor implements ProcessorInterface +{ + /** @var int */ + private $level; + /** @var array{branch: string, commit: string}|array|null */ + private static $cache = null; + + /** + * @param string|int $level The minimum logging level at which this Processor will be triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG) + { + $this->level = Logger::toMonologLevel($level); + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $record['extra']['git'] = self::getGitInfo(); + + return $record; + } + + /** + * @return array{branch: string, commit: string}|array + */ + private static function getGitInfo(): array + { + if (self::$cache) { + return self::$cache; + } + + $branches = `git branch -v --no-abbrev`; + if ($branches && preg_match('{^\* (.+?)\s+([a-f0-9]{40})(?:\s|$)}m', $branches, $matches)) { + return self::$cache = [ + 'branch' => $matches[1], + 'commit' => $matches[2], + ]; + } + + return self::$cache = []; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php new file mode 100644 index 0000000..91fda7d --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects value of gethostname in all records + */ +class HostnameProcessor implements ProcessorInterface +{ + /** @var string */ + private static $host; + + public function __construct() + { + self::$host = (string) gethostname(); + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $record['extra']['hostname'] = self::$host; + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php new file mode 100644 index 0000000..a32e76b --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Injects line/file:class/function where the log message came from + * + * Warning: This only works if the handler processes the logs directly. + * If you put the processor on a handler that is behind a FingersCrossedHandler + * for example, the processor will only be called once the trigger level is reached, + * and all the log records will have the same file/line/.. data from the call that + * triggered the FingersCrossedHandler. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class IntrospectionProcessor implements ProcessorInterface +{ + /** @var int */ + private $level; + /** @var string[] */ + private $skipClassesPartials; + /** @var int */ + private $skipStackFramesCount; + /** @var string[] */ + private $skipFunctions = [ + 'call_user_func', + 'call_user_func_array', + ]; + + /** + * @param string|int $level The minimum logging level at which this Processor will be triggered + * @param string[] $skipClassesPartials + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG, array $skipClassesPartials = [], int $skipStackFramesCount = 0) + { + $this->level = Logger::toMonologLevel($level); + $this->skipClassesPartials = array_merge(['Monolog\\'], $skipClassesPartials); + $this->skipStackFramesCount = $skipStackFramesCount; + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + + // skip first since it's always the current method + array_shift($trace); + // the call_user_func call is also skipped + array_shift($trace); + + $i = 0; + + while ($this->isTraceClassOrSkippedFunction($trace, $i)) { + if (isset($trace[$i]['class'])) { + foreach ($this->skipClassesPartials as $part) { + if (strpos($trace[$i]['class'], $part) !== false) { + $i++; + + continue 2; + } + } + } elseif (in_array($trace[$i]['function'], $this->skipFunctions)) { + $i++; + + continue; + } + + break; + } + + $i += $this->skipStackFramesCount; + + // we should have the call source now + $record['extra'] = array_merge( + $record['extra'], + [ + 'file' => isset($trace[$i - 1]['file']) ? $trace[$i - 1]['file'] : null, + 'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null, + 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null, + 'callType' => isset($trace[$i]['type']) ? $trace[$i]['type'] : null, + 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null, + ] + ); + + return $record; + } + + /** + * @param array[] $trace + */ + private function isTraceClassOrSkippedFunction(array $trace, int $index): bool + { + if (!isset($trace[$index])) { + return false; + } + + return isset($trace[$index]['class']) || in_array($trace[$index]['function'], $this->skipFunctions); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php new file mode 100644 index 0000000..37c756f --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects memory_get_peak_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryPeakUsageProcessor extends MemoryProcessor +{ + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $usage = memory_get_peak_usage($this->realUsage); + + if ($this->useFormatting) { + $usage = $this->formatBytes($usage); + } + + $record['extra']['memory_peak_usage'] = $usage; + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php new file mode 100644 index 0000000..227deb7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Some methods that are common for all memory processors + * + * @author Rob Jensen + */ +abstract class MemoryProcessor implements ProcessorInterface +{ + /** + * @var bool If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported. + */ + protected $realUsage; + + /** + * @var bool If true, then format memory size to human readable string (MB, KB, B depending on size) + */ + protected $useFormatting; + + /** + * @param bool $realUsage Set this to true to get the real size of memory allocated from system. + * @param bool $useFormatting If true, then format memory size to human readable string (MB, KB, B depending on size) + */ + public function __construct(bool $realUsage = true, bool $useFormatting = true) + { + $this->realUsage = $realUsage; + $this->useFormatting = $useFormatting; + } + + /** + * Formats bytes into a human readable string if $this->useFormatting is true, otherwise return $bytes as is + * + * @param int $bytes + * @return string|int Formatted string if $this->useFormatting is true, otherwise return $bytes as int + */ + protected function formatBytes(int $bytes) + { + if (!$this->useFormatting) { + return $bytes; + } + + if ($bytes > 1024 * 1024) { + return round($bytes / 1024 / 1024, 2).' MB'; + } elseif ($bytes > 1024) { + return round($bytes / 1024, 2).' KB'; + } + + return $bytes . ' B'; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php new file mode 100644 index 0000000..e141921 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects memory_get_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryUsageProcessor extends MemoryProcessor +{ + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $usage = memory_get_usage($this->realUsage); + + if ($this->useFormatting) { + $usage = $this->formatBytes($usage); + } + + $record['extra']['memory_usage'] = $usage; + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php new file mode 100644 index 0000000..d4a628f --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Injects Hg branch and Hg revision number in all records + * + * @author Jonathan A. Schweder + * + * @phpstan-import-type LevelName from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +class MercurialProcessor implements ProcessorInterface +{ + /** @var Level */ + private $level; + /** @var array{branch: string, revision: string}|array|null */ + private static $cache = null; + + /** + * @param int|string $level The minimum logging level at which this Processor will be triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG) + { + $this->level = Logger::toMonologLevel($level); + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $record['extra']['hg'] = self::getMercurialInfo(); + + return $record; + } + + /** + * @return array{branch: string, revision: string}|array + */ + private static function getMercurialInfo(): array + { + if (self::$cache) { + return self::$cache; + } + + $result = explode(' ', trim(`hg id -nb`)); + + if (count($result) >= 3) { + return self::$cache = [ + 'branch' => $result[1], + 'revision' => $result[2], + ]; + } + + return self::$cache = []; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php new file mode 100644 index 0000000..3b939a9 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Adds value of getmypid into records + * + * @author Andreas Hörnicke + */ +class ProcessIdProcessor implements ProcessorInterface +{ + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $record['extra']['process_id'] = getmypid(); + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php b/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php new file mode 100644 index 0000000..5defb7e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * An optional interface to allow labelling Monolog processors. + * + * @author Nicolas Grekas + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface ProcessorInterface +{ + /** + * @return array The processed record + * + * @phpstan-param Record $record + * @phpstan-return Record + */ + public function __invoke(array $record); +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php new file mode 100644 index 0000000..e7c1217 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Utils; + +/** + * Processes a record's message according to PSR-3 rules + * + * It replaces {foo} with the value from $context['foo'] + * + * @author Jordi Boggiano + */ +class PsrLogMessageProcessor implements ProcessorInterface +{ + public const SIMPLE_DATE = "Y-m-d\TH:i:s.uP"; + + /** @var string|null */ + private $dateFormat; + + /** @var bool */ + private $removeUsedContextFields; + + /** + * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format + * @param bool $removeUsedContextFields If set to true the fields interpolated into message gets unset + */ + public function __construct(?string $dateFormat = null, bool $removeUsedContextFields = false) + { + $this->dateFormat = $dateFormat; + $this->removeUsedContextFields = $removeUsedContextFields; + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + if (false === strpos($record['message'], '{')) { + return $record; + } + + $replacements = []; + foreach ($record['context'] as $key => $val) { + $placeholder = '{' . $key . '}'; + if (strpos($record['message'], $placeholder) === false) { + continue; + } + + if (is_null($val) || is_scalar($val) || (is_object($val) && method_exists($val, "__toString"))) { + $replacements[$placeholder] = $val; + } elseif ($val instanceof \DateTimeInterface) { + if (!$this->dateFormat && $val instanceof \Monolog\DateTimeImmutable) { + // handle monolog dates using __toString if no specific dateFormat was asked for + // so that it follows the useMicroseconds flag + $replacements[$placeholder] = (string) $val; + } else { + $replacements[$placeholder] = $val->format($this->dateFormat ?: static::SIMPLE_DATE); + } + } elseif ($val instanceof \UnitEnum) { + $replacements[$placeholder] = $val instanceof \BackedEnum ? $val->value : $val->name; + } elseif (is_object($val)) { + $replacements[$placeholder] = '[object '.Utils::getClass($val).']'; + } elseif (is_array($val)) { + $replacements[$placeholder] = 'array'.Utils::jsonEncode($val, null, true); + } else { + $replacements[$placeholder] = '['.gettype($val).']'; + } + + if ($this->removeUsedContextFields) { + unset($record['context'][$key]); + } + } + + $record['message'] = strtr($record['message'], $replacements); + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php new file mode 100644 index 0000000..80f1874 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Adds a tags array into record + * + * @author Martijn Riemers + */ +class TagProcessor implements ProcessorInterface +{ + /** @var string[] */ + private $tags; + + /** + * @param string[] $tags + */ + public function __construct(array $tags = []) + { + $this->setTags($tags); + } + + /** + * @param string[] $tags + */ + public function addTags(array $tags = []): self + { + $this->tags = array_merge($this->tags, $tags); + + return $this; + } + + /** + * @param string[] $tags + */ + public function setTags(array $tags = []): self + { + $this->tags = $tags; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $record['extra']['tags'] = $this->tags; + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php new file mode 100644 index 0000000..a27b74d --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\ResettableInterface; + +/** + * Adds a unique identifier into records + * + * @author Simon Mönch + */ +class UidProcessor implements ProcessorInterface, ResettableInterface +{ + /** @var string */ + private $uid; + + public function __construct(int $length = 7) + { + if ($length > 32 || $length < 1) { + throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32'); + } + + $this->uid = $this->generateUid($length); + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $record['extra']['uid'] = $this->uid; + + return $record; + } + + public function getUid(): string + { + return $this->uid; + } + + public function reset() + { + $this->uid = $this->generateUid(strlen($this->uid)); + } + + private function generateUid(int $length): string + { + return substr(bin2hex(random_bytes((int) ceil($length / 2))), 0, $length); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php new file mode 100644 index 0000000..887f4d3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects url/method and remote IP of the current web request in all records + * + * @author Jordi Boggiano + */ +class WebProcessor implements ProcessorInterface +{ + /** + * @var array|\ArrayAccess + */ + protected $serverData; + + /** + * Default fields + * + * Array is structured as [key in record.extra => key in $serverData] + * + * @var array + */ + protected $extraFields = [ + 'url' => 'REQUEST_URI', + 'ip' => 'REMOTE_ADDR', + 'http_method' => 'REQUEST_METHOD', + 'server' => 'SERVER_NAME', + 'referrer' => 'HTTP_REFERER', + 'user_agent' => 'HTTP_USER_AGENT', + ]; + + /** + * @param array|\ArrayAccess|null $serverData Array or object w/ ArrayAccess that provides access to the $_SERVER data + * @param array|array|null $extraFields Field names and the related key inside $serverData to be added (or just a list of field names to use the default configured $serverData mapping). If not provided it defaults to: [url, ip, http_method, server, referrer] + unique_id if present in server data + */ + public function __construct($serverData = null, ?array $extraFields = null) + { + if (null === $serverData) { + $this->serverData = &$_SERVER; + } elseif (is_array($serverData) || $serverData instanceof \ArrayAccess) { + $this->serverData = $serverData; + } else { + throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.'); + } + + $defaultEnabled = ['url', 'ip', 'http_method', 'server', 'referrer']; + if (isset($this->serverData['UNIQUE_ID'])) { + $this->extraFields['unique_id'] = 'UNIQUE_ID'; + $defaultEnabled[] = 'unique_id'; + } + + if (null === $extraFields) { + $extraFields = $defaultEnabled; + } + if (isset($extraFields[0])) { + foreach (array_keys($this->extraFields) as $fieldName) { + if (!in_array($fieldName, $extraFields)) { + unset($this->extraFields[$fieldName]); + } + } + } else { + $this->extraFields = $extraFields; + } + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + // skip processing if for some reason request data + // is not present (CLI or wonky SAPIs) + if (!isset($this->serverData['REQUEST_URI'])) { + return $record; + } + + $record['extra'] = $this->appendExtraFields($record['extra']); + + return $record; + } + + public function addExtraField(string $extraName, string $serverName): self + { + $this->extraFields[$extraName] = $serverName; + + return $this; + } + + /** + * @param mixed[] $extra + * @return mixed[] + */ + private function appendExtraFields(array $extra): array + { + foreach ($this->extraFields as $extraName => $serverName) { + $extra[$extraName] = $this->serverData[$serverName] ?? null; + } + + return $extra; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Registry.php b/vendor/monolog/monolog/src/Monolog/Registry.php new file mode 100644 index 0000000..ae94ae6 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Registry.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use InvalidArgumentException; + +/** + * Monolog log registry + * + * Allows to get `Logger` instances in the global scope + * via static method calls on this class. + * + * + * $application = new Monolog\Logger('application'); + * $api = new Monolog\Logger('api'); + * + * Monolog\Registry::addLogger($application); + * Monolog\Registry::addLogger($api); + * + * function testLogger() + * { + * Monolog\Registry::api()->error('Sent to $api Logger instance'); + * Monolog\Registry::application()->error('Sent to $application Logger instance'); + * } + * + * + * @author Tomas Tatarko + */ +class Registry +{ + /** + * List of all loggers in the registry (by named indexes) + * + * @var Logger[] + */ + private static $loggers = []; + + /** + * Adds new logging channel to the registry + * + * @param Logger $logger Instance of the logging channel + * @param string|null $name Name of the logging channel ($logger->getName() by default) + * @param bool $overwrite Overwrite instance in the registry if the given name already exists? + * @throws \InvalidArgumentException If $overwrite set to false and named Logger instance already exists + * @return void + */ + public static function addLogger(Logger $logger, ?string $name = null, bool $overwrite = false) + { + $name = $name ?: $logger->getName(); + + if (isset(self::$loggers[$name]) && !$overwrite) { + throw new InvalidArgumentException('Logger with the given name already exists'); + } + + self::$loggers[$name] = $logger; + } + + /** + * Checks if such logging channel exists by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function hasLogger($logger): bool + { + if ($logger instanceof Logger) { + $index = array_search($logger, self::$loggers, true); + + return false !== $index; + } + + return isset(self::$loggers[$logger]); + } + + /** + * Removes instance from registry by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function removeLogger($logger): void + { + if ($logger instanceof Logger) { + if (false !== ($idx = array_search($logger, self::$loggers, true))) { + unset(self::$loggers[$idx]); + } + } else { + unset(self::$loggers[$logger]); + } + } + + /** + * Clears the registry + */ + public static function clear(): void + { + self::$loggers = []; + } + + /** + * Gets Logger instance from the registry + * + * @param string $name Name of the requested Logger instance + * @throws \InvalidArgumentException If named Logger instance is not in the registry + */ + public static function getInstance($name): Logger + { + if (!isset(self::$loggers[$name])) { + throw new InvalidArgumentException(sprintf('Requested "%s" logger instance is not in the registry', $name)); + } + + return self::$loggers[$name]; + } + + /** + * Gets Logger instance from the registry via static method call + * + * @param string $name Name of the requested Logger instance + * @param mixed[] $arguments Arguments passed to static method call + * @throws \InvalidArgumentException If named Logger instance is not in the registry + * @return Logger Requested instance of Logger + */ + public static function __callStatic($name, $arguments) + { + return self::getInstance($name); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/ResettableInterface.php b/vendor/monolog/monolog/src/Monolog/ResettableInterface.php new file mode 100644 index 0000000..2c5fd78 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/ResettableInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +/** + * Handler or Processor implementing this interface will be reset when Logger::reset() is called. + * + * Resetting ends a log cycle gets them back to their initial state. + * + * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal + * state, and getting it back to a state in which it can receive log records again. + * + * This is useful in case you want to avoid logs leaking between two requests or jobs when you + * have a long running process like a worker or an application server serving multiple requests + * in one process. + * + * @author Grégoire Pineau + */ +interface ResettableInterface +{ + /** + * @return void + */ + public function reset(); +} diff --git a/vendor/monolog/monolog/src/Monolog/SignalHandler.php b/vendor/monolog/monolog/src/Monolog/SignalHandler.php new file mode 100644 index 0000000..d730eea --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/SignalHandler.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use ReflectionExtension; + +/** + * Monolog POSIX signal handler + * + * @author Robert Gust-Bardon + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class SignalHandler +{ + /** @var LoggerInterface */ + private $logger; + + /** @var array SIG_DFL, SIG_IGN or previous callable */ + private $previousSignalHandler = []; + /** @var array */ + private $signalLevelMap = []; + /** @var array */ + private $signalRestartSyscalls = []; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * @param int|string $level Level or level name + * @param bool $callPrevious + * @param bool $restartSyscalls + * @param bool|null $async + * @return $this + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function registerSignalHandler(int $signo, $level = LogLevel::CRITICAL, bool $callPrevious = true, bool $restartSyscalls = true, ?bool $async = true): self + { + if (!extension_loaded('pcntl') || !function_exists('pcntl_signal')) { + return $this; + } + + $level = Logger::toMonologLevel($level); + + if ($callPrevious) { + $handler = pcntl_signal_get_handler($signo); + $this->previousSignalHandler[$signo] = $handler; + } else { + unset($this->previousSignalHandler[$signo]); + } + $this->signalLevelMap[$signo] = $level; + $this->signalRestartSyscalls[$signo] = $restartSyscalls; + + if ($async !== null) { + pcntl_async_signals($async); + } + + pcntl_signal($signo, [$this, 'handleSignal'], $restartSyscalls); + + return $this; + } + + /** + * @param mixed $siginfo + */ + public function handleSignal(int $signo, $siginfo = null): void + { + static $signals = []; + + if (!$signals && extension_loaded('pcntl')) { + $pcntl = new ReflectionExtension('pcntl'); + // HHVM 3.24.2 returns an empty array. + foreach ($pcntl->getConstants() ?: get_defined_constants(true)['Core'] as $name => $value) { + if (substr($name, 0, 3) === 'SIG' && $name[3] !== '_' && is_int($value)) { + $signals[$value] = $name; + } + } + } + + $level = $this->signalLevelMap[$signo] ?? LogLevel::CRITICAL; + $signal = $signals[$signo] ?? $signo; + $context = $siginfo ?? []; + $this->logger->log($level, sprintf('Program received signal %s', $signal), $context); + + if (!isset($this->previousSignalHandler[$signo])) { + return; + } + + if ($this->previousSignalHandler[$signo] === SIG_DFL) { + if (extension_loaded('pcntl') && function_exists('pcntl_signal') && function_exists('pcntl_sigprocmask') && function_exists('pcntl_signal_dispatch') + && extension_loaded('posix') && function_exists('posix_getpid') && function_exists('posix_kill') + ) { + $restartSyscalls = $this->signalRestartSyscalls[$signo] ?? true; + pcntl_signal($signo, SIG_DFL, $restartSyscalls); + pcntl_sigprocmask(SIG_UNBLOCK, [$signo], $oldset); + posix_kill(posix_getpid(), $signo); + pcntl_signal_dispatch(); + pcntl_sigprocmask(SIG_SETMASK, $oldset); + pcntl_signal($signo, [$this, 'handleSignal'], $restartSyscalls); + } + } elseif (is_callable($this->previousSignalHandler[$signo])) { + $this->previousSignalHandler[$signo]($signo, $siginfo); + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Test/TestCase.php b/vendor/monolog/monolog/src/Monolog/Test/TestCase.php new file mode 100644 index 0000000..bc0b425 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Test/TestCase.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Test; + +use Monolog\Logger; +use Monolog\DateTimeImmutable; +use Monolog\Formatter\FormatterInterface; + +/** + * Lets you easily generate log records and a dummy formatter for testing purposes + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * + * @internal feel free to reuse this to test your own handlers, this is marked internal to avoid issues with PHPStorm https://github.com/Seldaek/monolog/issues/1677 + */ +class TestCase extends \PHPUnit\Framework\TestCase +{ + public function tearDown(): void + { + parent::tearDown(); + + if (isset($this->handler)) { + unset($this->handler); + } + } + + /** + * @param mixed[] $context + * + * @return array Record + * + * @phpstan-param Level $level + * @phpstan-return Record + */ + protected function getRecord(int $level = Logger::WARNING, string $message = 'test', array $context = []): array + { + return [ + 'message' => (string) $message, + 'context' => $context, + 'level' => $level, + 'level_name' => Logger::getLevelName($level), + 'channel' => 'test', + 'datetime' => new DateTimeImmutable(true), + 'extra' => [], + ]; + } + + /** + * @phpstan-return Record[] + */ + protected function getMultipleRecords(): array + { + return [ + $this->getRecord(Logger::DEBUG, 'debug message 1'), + $this->getRecord(Logger::DEBUG, 'debug message 2'), + $this->getRecord(Logger::INFO, 'information'), + $this->getRecord(Logger::WARNING, 'warning'), + $this->getRecord(Logger::ERROR, 'error'), + ]; + } + + protected function getIdentityFormatter(): FormatterInterface + { + $formatter = $this->createMock(FormatterInterface::class); + $formatter->expects($this->any()) + ->method('format') + ->will($this->returnCallback(function ($record) { + return $record['message']; + })); + + return $formatter; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Utils.php b/vendor/monolog/monolog/src/Monolog/Utils.php new file mode 100644 index 0000000..360c421 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Utils.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +final class Utils +{ + const DEFAULT_JSON_FLAGS = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION | JSON_INVALID_UTF8_SUBSTITUTE | JSON_PARTIAL_OUTPUT_ON_ERROR; + + public static function getClass(object $object): string + { + $class = \get_class($object); + + if (false === ($pos = \strpos($class, "@anonymous\0"))) { + return $class; + } + + if (false === ($parent = \get_parent_class($class))) { + return \substr($class, 0, $pos + 10); + } + + return $parent . '@anonymous'; + } + + public static function substr(string $string, int $start, ?int $length = null): string + { + if (extension_loaded('mbstring')) { + return mb_strcut($string, $start, $length); + } + + return substr($string, $start, (null === $length) ? strlen($string) : $length); + } + + /** + * Makes sure if a relative path is passed in it is turned into an absolute path + * + * @param string $streamUrl stream URL or path without protocol + */ + public static function canonicalizePath(string $streamUrl): string + { + $prefix = ''; + if ('file://' === substr($streamUrl, 0, 7)) { + $streamUrl = substr($streamUrl, 7); + $prefix = 'file://'; + } + + // other type of stream, not supported + if (false !== strpos($streamUrl, '://')) { + return $streamUrl; + } + + // already absolute + if (substr($streamUrl, 0, 1) === '/' || substr($streamUrl, 1, 1) === ':' || substr($streamUrl, 0, 2) === '\\\\') { + return $prefix.$streamUrl; + } + + $streamUrl = getcwd() . '/' . $streamUrl; + + return $prefix.$streamUrl; + } + + /** + * Return the JSON representation of a value + * + * @param mixed $data + * @param int $encodeFlags flags to pass to json encode, defaults to DEFAULT_JSON_FLAGS + * @param bool $ignoreErrors whether to ignore encoding errors or to throw on error, when ignored and the encoding fails, "null" is returned which is valid json for null + * @throws \RuntimeException if encoding fails and errors are not ignored + * @return string when errors are ignored and the encoding fails, "null" is returned which is valid json for null + */ + public static function jsonEncode($data, ?int $encodeFlags = null, bool $ignoreErrors = false): string + { + if (null === $encodeFlags) { + $encodeFlags = self::DEFAULT_JSON_FLAGS; + } + + if ($ignoreErrors) { + $json = @json_encode($data, $encodeFlags); + if (false === $json) { + return 'null'; + } + + return $json; + } + + $json = json_encode($data, $encodeFlags); + if (false === $json) { + $json = self::handleJsonError(json_last_error(), $data); + } + + return $json; + } + + /** + * Handle a json_encode failure. + * + * If the failure is due to invalid string encoding, try to clean the + * input and encode again. If the second encoding attempt fails, the + * initial error is not encoding related or the input can't be cleaned then + * raise a descriptive exception. + * + * @param int $code return code of json_last_error function + * @param mixed $data data that was meant to be encoded + * @param int $encodeFlags flags to pass to json encode, defaults to JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION + * @throws \RuntimeException if failure can't be corrected + * @return string JSON encoded data after error correction + */ + public static function handleJsonError(int $code, $data, ?int $encodeFlags = null): string + { + if ($code !== JSON_ERROR_UTF8) { + self::throwEncodeError($code, $data); + } + + if (is_string($data)) { + self::detectAndCleanUtf8($data); + } elseif (is_array($data)) { + array_walk_recursive($data, array('Monolog\Utils', 'detectAndCleanUtf8')); + } else { + self::throwEncodeError($code, $data); + } + + if (null === $encodeFlags) { + $encodeFlags = self::DEFAULT_JSON_FLAGS; + } + + $json = json_encode($data, $encodeFlags); + + if ($json === false) { + self::throwEncodeError(json_last_error(), $data); + } + + return $json; + } + + /** + * @internal + */ + public static function pcreLastErrorMessage(int $code): string + { + if (PHP_VERSION_ID >= 80000) { + return preg_last_error_msg(); + } + + $constants = (get_defined_constants(true))['pcre']; + $constants = array_filter($constants, function ($key) { + return substr($key, -6) == '_ERROR'; + }, ARRAY_FILTER_USE_KEY); + + $constants = array_flip($constants); + + return $constants[$code] ?? 'UNDEFINED_ERROR'; + } + + /** + * Throws an exception according to a given code with a customized message + * + * @param int $code return code of json_last_error function + * @param mixed $data data that was meant to be encoded + * @throws \RuntimeException + * + * @return never + */ + private static function throwEncodeError(int $code, $data): void + { + switch ($code) { + case JSON_ERROR_DEPTH: + $msg = 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Unexpected control character found'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'Unknown error'; + } + + throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true)); + } + + /** + * Detect invalid UTF-8 string characters and convert to valid UTF-8. + * + * Valid UTF-8 input will be left unmodified, but strings containing + * invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed + * original encoding of ISO-8859-15. This conversion may result in + * incorrect output if the actual encoding was not ISO-8859-15, but it + * will be clean UTF-8 output and will not rely on expensive and fragile + * detection algorithms. + * + * Function converts the input in place in the passed variable so that it + * can be used as a callback for array_walk_recursive. + * + * @param mixed $data Input to check and convert if needed, passed by ref + */ + private static function detectAndCleanUtf8(&$data): void + { + if (is_string($data) && !preg_match('//u', $data)) { + $data = preg_replace_callback( + '/[\x80-\xFF]+/', + function ($m) { + return function_exists('mb_convert_encoding') ? mb_convert_encoding($m[0], 'UTF-8', 'ISO-8859-1') : utf8_encode($m[0]); + }, + $data + ); + if (!is_string($data)) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Failed to preg_replace_callback: ' . $pcreErrorCode . ' / ' . self::pcreLastErrorMessage($pcreErrorCode)); + } + $data = str_replace( + ['¤', '¦', '¨', '´', '¸', '¼', '½', '¾'], + ['€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'], + $data + ); + } + } + + /** + * Converts a string with a valid 'memory_limit' format, to bytes. + * + * @param string|false $val + * @return int|false Returns an integer representing bytes. Returns FALSE in case of error. + */ + public static function expandIniShorthandBytes($val) + { + if (!is_string($val)) { + return false; + } + + // support -1 + if ((int) $val < 0) { + return (int) $val; + } + + if (!preg_match('/^\s*(?\d+)(?:\.\d+)?\s*(?[gmk]?)\s*$/i', $val, $match)) { + return false; + } + + $val = (int) $match['val']; + switch (strtolower($match['unit'] ?? '')) { + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + } + + return $val; + } + + /** + * @param array $record + */ + public static function getRecordMessageForException(array $record): string + { + $context = ''; + $extra = ''; + try { + if ($record['context']) { + $context = "\nContext: " . json_encode($record['context']); + } + if ($record['extra']) { + $extra = "\nExtra: " . json_encode($record['extra']); + } + } catch (\Throwable $e) { + // noop + } + + return "\nThe exception occurred while attempting to log: " . $record['message'] . $context . $extra; + } +} diff --git a/vendor/nesbot/carbon/.phpstorm.meta.php b/vendor/nesbot/carbon/.phpstorm.meta.php new file mode 100644 index 0000000..bd7c7e0 --- /dev/null +++ b/vendor/nesbot/carbon/.phpstorm.meta.php @@ -0,0 +1,10 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\MessageFormatter; + +use Symfony\Component\Translation\Formatter\MessageFormatterInterface; + +if (!class_exists(LazyMessageFormatter::class, false)) { + abstract class LazyMessageFormatter implements MessageFormatterInterface + { + public function format(string $message, string $locale, array $parameters = []): string + { + return $this->formatter->format( + $message, + $this->transformLocale($locale), + $parameters + ); + } + } +} diff --git a/vendor/nesbot/carbon/lazy/Carbon/MessageFormatter/MessageFormatterMapperWeakType.php b/vendor/nesbot/carbon/lazy/Carbon/MessageFormatter/MessageFormatterMapperWeakType.php new file mode 100644 index 0000000..cbd890d --- /dev/null +++ b/vendor/nesbot/carbon/lazy/Carbon/MessageFormatter/MessageFormatterMapperWeakType.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\MessageFormatter; + +use Symfony\Component\Translation\Formatter\ChoiceMessageFormatterInterface; +use Symfony\Component\Translation\Formatter\MessageFormatterInterface; + +if (!class_exists(LazyMessageFormatter::class, false)) { + abstract class LazyMessageFormatter implements MessageFormatterInterface, ChoiceMessageFormatterInterface + { + abstract protected function transformLocale(?string $locale): ?string; + + public function format($message, $locale, array $parameters = []) + { + return $this->formatter->format( + $message, + $this->transformLocale($locale), + $parameters + ); + } + + public function choiceFormat($message, $number, $locale, array $parameters = []) + { + return $this->formatter->choiceFormat($message, $number, $locale, $parameters); + } + } +} diff --git a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroBuiltin.php b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroBuiltin.php new file mode 100644 index 0000000..ba7cf63 --- /dev/null +++ b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroBuiltin.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\PHPStan; + +use PHPStan\BetterReflection\Reflection; +use ReflectionMethod; + +if (!class_exists(AbstractReflectionMacro::class, false)) { + abstract class AbstractReflectionMacro extends AbstractMacro + { + /** + * {@inheritdoc} + */ + public function getReflection(): ?ReflectionMethod + { + if ($this->reflectionFunction instanceof Reflection\ReflectionMethod) { + return new Reflection\Adapter\ReflectionMethod($this->reflectionFunction); + } + + return $this->reflectionFunction instanceof ReflectionMethod + ? $this->reflectionFunction + : null; + } + } +} diff --git a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroStatic.php b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroStatic.php new file mode 100644 index 0000000..bd4c8e8 --- /dev/null +++ b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroStatic.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\PHPStan; + +use PHPStan\BetterReflection\Reflection; +use ReflectionMethod; + +if (!class_exists(AbstractReflectionMacro::class, false)) { + abstract class AbstractReflectionMacro extends AbstractMacro + { + /** + * {@inheritdoc} + */ + public function getReflection(): ?Reflection\Adapter\ReflectionMethod + { + if ($this->reflectionFunction instanceof Reflection\Adapter\ReflectionMethod) { + return $this->reflectionFunction; + } + + if ($this->reflectionFunction instanceof Reflection\ReflectionMethod) { + return new Reflection\Adapter\ReflectionMethod($this->reflectionFunction); + } + + return $this->reflectionFunction instanceof ReflectionMethod + ? new Reflection\Adapter\ReflectionMethod( + Reflection\ReflectionMethod::createFromName( + $this->reflectionFunction->getDeclaringClass()->getName(), + $this->reflectionFunction->getName() + ) + ) + : null; + } + } +} diff --git a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroStrongType.php b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroStrongType.php new file mode 100644 index 0000000..f615b3a --- /dev/null +++ b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroStrongType.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\PHPStan; + +if (!class_exists(LazyMacro::class, false)) { + abstract class LazyMacro extends AbstractReflectionMacro + { + /** + * {@inheritdoc} + */ + public function getFileName(): ?string + { + $file = $this->reflectionFunction->getFileName(); + + return (($file ? realpath($file) : null) ?: $file) ?: null; + } + + /** + * {@inheritdoc} + */ + public function getStartLine(): ?int + { + return $this->reflectionFunction->getStartLine(); + } + + /** + * {@inheritdoc} + */ + public function getEndLine(): ?int + { + return $this->reflectionFunction->getEndLine(); + } + } +} diff --git a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroWeakType.php b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroWeakType.php new file mode 100644 index 0000000..bf64c1d --- /dev/null +++ b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroWeakType.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\PHPStan; + +if (!class_exists(LazyMacro::class, false)) { + abstract class LazyMacro extends AbstractReflectionMacro + { + /** + * {@inheritdoc} + * + * @return string|false + */ + public function getFileName() + { + $file = $this->reflectionFunction->getFileName(); + + return (($file ? realpath($file) : null) ?: $file) ?: null; + } + + /** + * {@inheritdoc} + * + * @return int|false + */ + public function getStartLine() + { + return $this->reflectionFunction->getStartLine(); + } + + /** + * {@inheritdoc} + * + * @return int|false + */ + public function getEndLine() + { + return $this->reflectionFunction->getEndLine(); + } + } +} diff --git a/vendor/nesbot/carbon/lazy/Carbon/TranslatorStrongType.php b/vendor/nesbot/carbon/lazy/Carbon/TranslatorStrongType.php new file mode 100644 index 0000000..d35308a --- /dev/null +++ b/vendor/nesbot/carbon/lazy/Carbon/TranslatorStrongType.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Symfony\Component\Translation\MessageCatalogueInterface; + +if (!class_exists(LazyTranslator::class, false)) { + class LazyTranslator extends AbstractTranslator implements TranslatorStrongTypeInterface + { + public function trans(?string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string + { + return $this->translate($id, $parameters, $domain, $locale); + } + + public function getFromCatalogue(MessageCatalogueInterface $catalogue, string $id, string $domain = 'messages') + { + $messages = $this->getPrivateProperty($catalogue, 'messages'); + + if (isset($messages[$domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX][$id])) { + return $messages[$domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX][$id]; + } + + if (isset($messages[$domain][$id])) { + return $messages[$domain][$id]; + } + + $fallbackCatalogue = $this->getPrivateProperty($catalogue, 'fallbackCatalogue'); + + if ($fallbackCatalogue !== null) { + return $this->getFromCatalogue($fallbackCatalogue, $id, $domain); + } + + return $id; + } + + private function getPrivateProperty($instance, string $field) + { + return (function (string $field) { + return $this->$field; + })->call($instance, $field); + } + } +} diff --git a/vendor/nesbot/carbon/lazy/Carbon/TranslatorWeakType.php b/vendor/nesbot/carbon/lazy/Carbon/TranslatorWeakType.php new file mode 100644 index 0000000..94dbdc3 --- /dev/null +++ b/vendor/nesbot/carbon/lazy/Carbon/TranslatorWeakType.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +if (!class_exists(LazyTranslator::class, false)) { + class LazyTranslator extends AbstractTranslator + { + /** + * Returns the translation. + * + * @param string|null $id + * @param array $parameters + * @param string|null $domain + * @param string|null $locale + * + * @return string + */ + public function trans($id, array $parameters = [], $domain = null, $locale = null) + { + return $this->translate($id, $parameters, $domain, $locale); + } + } +} diff --git a/vendor/nesbot/carbon/readme.md b/vendor/nesbot/carbon/readme.md new file mode 100644 index 0000000..97ec8ce --- /dev/null +++ b/vendor/nesbot/carbon/readme.md @@ -0,0 +1,176 @@ +# Carbon + +[![Latest Stable Version](https://img.shields.io/packagist/v/nesbot/carbon.svg?style=flat-square)](https://packagist.org/packages/nesbot/carbon) +[![Total Downloads](https://img.shields.io/packagist/dt/nesbot/carbon.svg?style=flat-square)](https://packagist.org/packages/nesbot/carbon) +[![GitHub Actions](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fbriannesbitt%2FCarbon%2Fbadge&style=flat-square&label=Build&logo=none)](https://github.com/briannesbitt/Carbon/actions) +[![codecov.io](https://img.shields.io/codecov/c/github/briannesbitt/Carbon.svg?style=flat-square)](https://codecov.io/github/briannesbitt/Carbon?branch=master) +[![Tidelift](https://tidelift.com/badges/github/briannesbitt/Carbon)](https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme) + +An international PHP extension for DateTime. [https://carbon.nesbot.com](https://carbon.nesbot.com) + +```php +toDateTimeString()); +printf("Right now in Vancouver is %s", Carbon::now('America/Vancouver')); //implicit __toString() +$tomorrow = Carbon::now()->addDay(); +$lastWeek = Carbon::now()->subWeek(); +$nextSummerOlympics = Carbon::createFromDate(2016)->addYears(4); + +$officialDate = Carbon::now()->toRfc2822String(); + +$howOldAmI = Carbon::createFromDate(1975, 5, 21)->age; + +$noonTodayLondonTime = Carbon::createFromTime(12, 0, 0, 'Europe/London'); + +$internetWillBlowUpOn = Carbon::create(2038, 01, 19, 3, 14, 7, 'GMT'); + +// Don't really want this to happen so mock now +Carbon::setTestNow(Carbon::createFromDate(2000, 1, 1)); + +// comparisons are always done in UTC +if (Carbon::now()->gte($internetWillBlowUpOn)) { + die(); +} + +// Phew! Return to normal behaviour +Carbon::setTestNow(); + +if (Carbon::now()->isWeekend()) { + echo 'Party!'; +} +// Over 200 languages (and over 500 regional variants) supported: +echo Carbon::now()->subMinutes(2)->diffForHumans(); // '2 minutes ago' +echo Carbon::now()->subMinutes(2)->locale('zh_CN')->diffForHumans(); // '2分钟前' +echo Carbon::parse('2019-07-23 14:51')->isoFormat('LLLL'); // 'Tuesday, July 23, 2019 2:51 PM' +echo Carbon::parse('2019-07-23 14:51')->locale('fr_FR')->isoFormat('LLLL'); // 'mardi 23 juillet 2019 14:51' + +// ... but also does 'from now', 'after' and 'before' +// rolling up to seconds, minutes, hours, days, months, years + +$daysSinceEpoch = Carbon::createFromTimestamp(0)->diffInDays(); +``` + +[Get supported nesbot/carbon with the Tidelift Subscription](https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme) + +## Installation + +### With Composer + +``` +$ composer require nesbot/carbon +``` + +```json +{ + "require": { + "nesbot/carbon": "^2.16" + } +} +``` + +```php + + +### Translators + +[Thanks to people helping us to translate Carbon in so many languages](https://carbon.nesbot.com/contribute/translators/) + +### Sponsors + +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. + + +Онлайн казино +CasinoHex Canada +Probukmacher +Casino-portugal.pt +Игровые автоматы +Slots City +inkedin +Онлайн казино України +OnlineCasinosSpelen +Best non Gamstop sites in the UK +Real Money Pokies +Non GamStop Bookies UK +Онлайн Казино Украины +SSSTwitter +Non-GamStop Bets UK +Chudovo +UK Casino Gap +NZ Casino Deps +NonStopCasino.org +Migliori Siti Non AAMS +UK NonGamStopCasinos +SnapTik +Proxidize +IG Downloader +Blastup +Organic Social Boost +AzuraCast +Triplebyte +GitHub Sponsors +Salesforce + + +[[Become a sponsor via OpenCollective](https://opencollective.com/Carbon#sponsor)] + + + + + + +[[Become a sponsor via GitHub](https://github.com/sponsors/kylekatarnls)] + +### Backers + +Thank you to all our backers! 🙏 + + + +[[Become a backer](https://opencollective.com/Carbon#backer)] + +## Carbon for enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of ``Carbon`` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) diff --git a/vendor/nesbot/carbon/sponsors.php b/vendor/nesbot/carbon/sponsors.php new file mode 100644 index 0000000..67b2171 --- /dev/null +++ b/vendor/nesbot/carbon/sponsors.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Carbon\CarbonImmutable; + +require_once __DIR__.'/vendor/autoload.php'; + +function getMaxHistoryMonthsByAmount($amount): int +{ + if ($amount >= 50) { + return 6; + } + + if ($amount >= 20) { + return 4; + } + + return 2; +} + +function getHtmlAttribute($rawValue): string +{ + return str_replace( + ['​', "\r"], + '', + trim(htmlspecialchars((string) $rawValue), "  \n\r\t\v\0"), + ); +} + +function getOpenCollectiveSponsors(): string +{ + $customSponsorImages = [ + // For consistency and equity among sponsors, as of now, we kindly ask our sponsors + // to provide an image having a width/height ratio between 1/1 and 2/1. + // By default, we'll show the member picture from OpenCollective, and will resize it if bigger + // int(OpenCollective.MemberId) => ImageURL + ]; + + $members = json_decode(file_get_contents('https://opencollective.com/carbon/members/all.json'), true); + + $list = array_filter($members, static function ($member): bool { + return ($member['lastTransactionAmount'] > 3 || $member['isActive']) && + $member['role'] === 'BACKER' && + $member['type'] !== 'USER' && + ( + $member['totalAmountDonated'] > 100 || + $member['lastTransactionAt'] > CarbonImmutable::now() + ->subMonthsNoOverflow(getMaxHistoryMonthsByAmount($member['lastTransactionAmount'])) + ->format('Y-m-d h:i') || + $member['isActive'] && $member['lastTransactionAmount'] >= 30 + ); + }); + + $list = array_map(static function (array $member): array { + $createdAt = CarbonImmutable::parse($member['createdAt']); + $lastTransactionAt = CarbonImmutable::parse($member['lastTransactionAt']); + + if ($createdAt->format('d H:i:s.u') > $lastTransactionAt->format('d H:i:s.u')) { + $createdAt = $createdAt + ->setDay($lastTransactionAt->day) + ->modify($lastTransactionAt->format('H:i:s.u')); + } + + $monthlyContribution = (float) ($member['totalAmountDonated'] / ceil($createdAt->floatDiffInMonths())); + + if ( + $lastTransactionAt->isAfter('last month') && + $member['lastTransactionAmount'] > $monthlyContribution + ) { + $monthlyContribution = (float) $member['lastTransactionAmount']; + } + + $yearlyContribution = (float) ($member['totalAmountDonated'] / max(1, $createdAt->floatDiffInYears())); + $status = null; + + if ($monthlyContribution > 29) { + $status = 'sponsor'; + } elseif ($monthlyContribution > 4.5 || $yearlyContribution > 29) { + $status = 'backer'; + } elseif ($member['totalAmountDonated'] > 0) { + $status = 'helper'; + } + + return array_merge($member, [ + 'star' => ($monthlyContribution > 98 || $yearlyContribution > 500), + 'status' => $status, + 'monthlyContribution' => $monthlyContribution, + 'yearlyContribution' => $yearlyContribution, + ]); + }, $list); + + usort($list, static function (array $a, array $b): int { + return ($b['monthlyContribution'] <=> $a['monthlyContribution']) + ?: ($b['totalAmountDonated'] <=> $a['totalAmountDonated']); + }); + + return implode('', array_map(static function (array $member) use ($customSponsorImages): string { + $href = htmlspecialchars($member['website'] ?? $member['profile']); + $src = $customSponsorImages[$member['MemberId'] ?? ''] ?? $member['image'] ?? (strtr($member['profile'], ['https://opencollective.com/' => 'https://images.opencollective.com/']).'/avatar/256.png'); + [$x, $y] = @getimagesize($src) ?: [0, 0]; + $validImage = ($x && $y); + $src = $validImage ? htmlspecialchars($src) : 'https://opencollective.com/static/images/default-guest-logo.svg'; + $height = $member['status'] === 'sponsor' ? 64 : 42; + $width = min($height * 2, $validImage ? round($x * $height / $y) : $height); + $href .= (strpos($href, '?') === false ? '?' : '&').'utm_source=opencollective&utm_medium=github&utm_campaign=Carbon'; + $title = getHtmlAttribute(($member['description'] ?? null) ?: $member['name']); + $alt = getHtmlAttribute($member['name']); + + return "\n".''. + ''.$alt.''. + ''; + }, $list))."\n"; +} + +file_put_contents('readme.md', preg_replace_callback( + '/()[\s\S]+()/', + static function (array $match): string { + return $match[1].getOpenCollectiveSponsors().$match[2]; + }, + file_get_contents('readme.md') +)); diff --git a/vendor/nesbot/carbon/src/Carbon/AbstractTranslator.php b/vendor/nesbot/carbon/src/Carbon/AbstractTranslator.php new file mode 100644 index 0000000..8b8fe08 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/AbstractTranslator.php @@ -0,0 +1,398 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Carbon\MessageFormatter\MessageFormatterMapper; +use Closure; +use ReflectionException; +use ReflectionFunction; +use Symfony\Component\Translation; +use Symfony\Component\Translation\Formatter\MessageFormatterInterface; +use Symfony\Component\Translation\Loader\ArrayLoader; + +abstract class AbstractTranslator extends Translation\Translator +{ + /** + * Translator singletons for each language. + * + * @var array + */ + protected static $singletons = []; + + /** + * List of custom localized messages. + * + * @var array + */ + protected $messages = []; + + /** + * List of custom directories that contain translation files. + * + * @var string[] + */ + protected $directories = []; + + /** + * Set to true while constructing. + * + * @var bool + */ + protected $initializing = false; + + /** + * List of locales aliases. + * + * @var array + */ + protected $aliases = [ + 'me' => 'sr_Latn_ME', + 'scr' => 'sh', + ]; + + /** + * Return a singleton instance of Translator. + * + * @param string|null $locale optional initial locale ("en" - english by default) + * + * @return static + */ + public static function get($locale = null) + { + $locale = $locale ?: 'en'; + $key = static::class === Translator::class ? $locale : static::class.'|'.$locale; + + if (!isset(static::$singletons[$key])) { + static::$singletons[$key] = new static($locale); + } + + return static::$singletons[$key]; + } + + public function __construct($locale, MessageFormatterInterface $formatter = null, $cacheDir = null, $debug = false) + { + parent::setLocale($locale); + $this->initializing = true; + $this->directories = [__DIR__.'/Lang']; + $this->addLoader('array', new ArrayLoader()); + parent::__construct($locale, new MessageFormatterMapper($formatter), $cacheDir, $debug); + $this->initializing = false; + } + + /** + * Returns the list of directories translation files are searched in. + * + * @return array + */ + public function getDirectories(): array + { + return $this->directories; + } + + /** + * Set list of directories translation files are searched in. + * + * @param array $directories new directories list + * + * @return $this + */ + public function setDirectories(array $directories) + { + $this->directories = $directories; + + return $this; + } + + /** + * Add a directory to the list translation files are searched in. + * + * @param string $directory new directory + * + * @return $this + */ + public function addDirectory(string $directory) + { + $this->directories[] = $directory; + + return $this; + } + + /** + * Remove a directory from the list translation files are searched in. + * + * @param string $directory directory path + * + * @return $this + */ + public function removeDirectory(string $directory) + { + $search = rtrim(strtr($directory, '\\', '/'), '/'); + + return $this->setDirectories(array_filter($this->getDirectories(), function ($item) use ($search) { + return rtrim(strtr($item, '\\', '/'), '/') !== $search; + })); + } + + /** + * Reset messages of a locale (all locale if no locale passed). + * Remove custom messages and reload initial messages from matching + * file in Lang directory. + * + * @param string|null $locale + * + * @return bool + */ + public function resetMessages($locale = null) + { + if ($locale === null) { + $this->messages = []; + + return true; + } + + foreach ($this->getDirectories() as $directory) { + $data = @include sprintf('%s/%s.php', rtrim($directory, '\\/'), $locale); + + if ($data !== false) { + $this->messages[$locale] = $data; + $this->addResource('array', $this->messages[$locale], $locale); + + return true; + } + } + + return false; + } + + /** + * Returns the list of files matching a given locale prefix (or all if empty). + * + * @param string $prefix prefix required to filter result + * + * @return array + */ + public function getLocalesFiles($prefix = '') + { + $files = []; + + foreach ($this->getDirectories() as $directory) { + $directory = rtrim($directory, '\\/'); + + foreach (glob("$directory/$prefix*.php") as $file) { + $files[] = $file; + } + } + + return array_unique($files); + } + + /** + * Returns the list of internally available locales and already loaded custom locales. + * (It will ignore custom translator dynamic loading.) + * + * @param string $prefix prefix required to filter result + * + * @return array + */ + public function getAvailableLocales($prefix = '') + { + $locales = []; + foreach ($this->getLocalesFiles($prefix) as $file) { + $locales[] = substr($file, strrpos($file, '/') + 1, -4); + } + + return array_unique(array_merge($locales, array_keys($this->messages))); + } + + protected function translate(?string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string + { + if ($domain === null) { + $domain = 'messages'; + } + + $catalogue = $this->getCatalogue($locale); + $format = $this instanceof TranslatorStrongTypeInterface + ? $this->getFromCatalogue($catalogue, (string) $id, $domain) + : $this->getCatalogue($locale)->get((string) $id, $domain); // @codeCoverageIgnore + + if ($format instanceof Closure) { + // @codeCoverageIgnoreStart + try { + $count = (new ReflectionFunction($format))->getNumberOfRequiredParameters(); + } catch (ReflectionException $exception) { + $count = 0; + } + // @codeCoverageIgnoreEnd + + return $format( + ...array_values($parameters), + ...array_fill(0, max(0, $count - \count($parameters)), null) + ); + } + + return parent::trans($id, $parameters, $domain, $locale); + } + + /** + * Init messages language from matching file in Lang directory. + * + * @param string $locale + * + * @return bool + */ + protected function loadMessagesFromFile($locale) + { + return isset($this->messages[$locale]) || $this->resetMessages($locale); + } + + /** + * Set messages of a locale and take file first if present. + * + * @param string $locale + * @param array $messages + * + * @return $this + */ + public function setMessages($locale, $messages) + { + $this->loadMessagesFromFile($locale); + $this->addResource('array', $messages, $locale); + $this->messages[$locale] = array_merge( + $this->messages[$locale] ?? [], + $messages + ); + + return $this; + } + + /** + * Set messages of the current locale and take file first if present. + * + * @param array $messages + * + * @return $this + */ + public function setTranslations($messages) + { + return $this->setMessages($this->getLocale(), $messages); + } + + /** + * Get messages of a locale, if none given, return all the + * languages. + * + * @param string|null $locale + * + * @return array + */ + public function getMessages($locale = null) + { + return $locale === null ? $this->messages : $this->messages[$locale]; + } + + /** + * Set the current translator locale and indicate if the source locale file exists + * + * @param string $locale locale ex. en + * + * @return bool + */ + public function setLocale($locale) + { + $locale = preg_replace_callback('/[-_]([a-z]{2,}|\d{2,})/', function ($matches) { + // _2-letters or YUE is a region, _3+-letters is a variant + $upper = strtoupper($matches[1]); + + if ($upper === 'YUE' || $upper === 'ISO' || \strlen($upper) < 3) { + return "_$upper"; + } + + return '_'.ucfirst($matches[1]); + }, strtolower($locale)); + + $previousLocale = $this->getLocale(); + + if ($previousLocale === $locale && isset($this->messages[$locale])) { + return true; + } + + unset(static::$singletons[$previousLocale]); + + if ($locale === 'auto') { + $completeLocale = setlocale(LC_TIME, '0'); + $locale = preg_replace('/^([^_.-]+).*$/', '$1', $completeLocale); + $locales = $this->getAvailableLocales($locale); + + $completeLocaleChunks = preg_split('/[_.-]+/', $completeLocale); + + $getScore = function ($language) use ($completeLocaleChunks) { + return self::compareChunkLists($completeLocaleChunks, preg_split('/[_.-]+/', $language)); + }; + + usort($locales, function ($first, $second) use ($getScore) { + return $getScore($second) <=> $getScore($first); + }); + + $locale = $locales[0]; + } + + if (isset($this->aliases[$locale])) { + $locale = $this->aliases[$locale]; + } + + // If subtag (ex: en_CA) first load the macro (ex: en) to have a fallback + if (str_contains($locale, '_') && + $this->loadMessagesFromFile($macroLocale = preg_replace('/^([^_]+).*$/', '$1', $locale)) + ) { + parent::setLocale($macroLocale); + } + + if (!$this->loadMessagesFromFile($locale) && !$this->initializing) { + return false; + } + + parent::setLocale($locale); + + return true; + } + + /** + * Show locale on var_dump(). + * + * @return array + */ + public function __debugInfo() + { + return [ + 'locale' => $this->getLocale(), + ]; + } + + private static function compareChunkLists($referenceChunks, $chunks) + { + $score = 0; + + foreach ($referenceChunks as $index => $chunk) { + if (!isset($chunks[$index])) { + $score++; + + continue; + } + + if (strtolower($chunks[$index]) === strtolower($chunk)) { + $score += 10; + } + } + + return $score; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Carbon.php b/vendor/nesbot/carbon/src/Carbon/Carbon.php new file mode 100644 index 0000000..e32569a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Carbon.php @@ -0,0 +1,523 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Carbon\Traits\Date; +use Carbon\Traits\DeprecatedProperties; +use DateTime; +use DateTimeInterface; +use DateTimeZone; + +/** + * A simple API extension for DateTime. + * + * @mixin DeprecatedProperties + * + * + * + * @property int $year + * @property int $yearIso + * @property int $month + * @property int $day + * @property int $hour + * @property int $minute + * @property int $second + * @property int $micro + * @property int $microsecond + * @property int|float|string $timestamp seconds since the Unix Epoch + * @property string $englishDayOfWeek the day of week in English + * @property string $shortEnglishDayOfWeek the abbreviated day of week in English + * @property string $englishMonth the month in English + * @property string $shortEnglishMonth the abbreviated month in English + * @property int $milliseconds + * @property int $millisecond + * @property int $milli + * @property int $week 1 through 53 + * @property int $isoWeek 1 through 53 + * @property int $weekYear year according to week format + * @property int $isoWeekYear year according to ISO week format + * @property int $dayOfYear 1 through 366 + * @property int $age does a diffInYears() with default parameters + * @property int $offset the timezone offset in seconds from UTC + * @property int $offsetMinutes the timezone offset in minutes from UTC + * @property int $offsetHours the timezone offset in hours from UTC + * @property CarbonTimeZone $timezone the current timezone + * @property CarbonTimeZone $tz alias of $timezone + * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) + * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) + * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday + * @property-read int $daysInMonth number of days in the given month + * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark) + * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark) + * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name + * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName + * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language + * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language + * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + * @property-read int $noZeroHour current hour from 1 to 24 + * @property-read int $weeksInYear 51 through 53 + * @property-read int $isoWeeksInYear 51 through 53 + * @property-read int $weekOfMonth 1 through 5 + * @property-read int $weekNumberInMonth 1 through 5 + * @property-read int $firstWeekDay 0 through 6 + * @property-read int $lastWeekDay 0 through 6 + * @property-read int $daysInYear 365 or 366 + * @property-read int $quarter the quarter of this instance, 1 - 4 + * @property-read int $decade the decade of this instance + * @property-read int $century the century of this instance + * @property-read int $millennium the millennium of this instance + * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise + * @property-read bool $local checks if the timezone is local, true if local, false otherwise + * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise + * @property-read string $timezoneName the current timezone name + * @property-read string $tzName alias of $timezoneName + * @property-read string $locale locale of the current instance + * + * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) + * @method bool isLocal() Check if the current instance has non-UTC timezone. + * @method bool isValid() Check if the current instance is a valid date. + * @method bool isDST() Check if the current instance is in a daylight saving time. + * @method bool isSunday() Checks if the instance day is sunday. + * @method bool isMonday() Checks if the instance day is monday. + * @method bool isTuesday() Checks if the instance day is tuesday. + * @method bool isWednesday() Checks if the instance day is wednesday. + * @method bool isThursday() Checks if the instance day is thursday. + * @method bool isFriday() Checks if the instance day is friday. + * @method bool isSaturday() Checks if the instance day is saturday. + * @method bool isSameYear(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment. + * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year. + * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year. + * @method bool isSameWeek(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment. + * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week. + * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week. + * @method bool isSameDay(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment. + * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day. + * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day. + * @method bool isSameHour(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment. + * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour. + * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour. + * @method bool isSameMinute(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment. + * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute. + * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute. + * @method bool isSameSecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment. + * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second. + * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second. + * @method bool isSameMicro(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment. + * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond. + * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond. + * @method bool isSameMicrosecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment. + * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond. + * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond. + * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. + * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. + * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. + * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. + * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. + * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. + * @method bool isSameDecade(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment. + * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade. + * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade. + * @method bool isSameCentury(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment. + * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century. + * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century. + * @method bool isSameMillennium(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment. + * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium. + * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium. + * @method $this years(int $value) Set current instance year to the given value. + * @method $this year(int $value) Set current instance year to the given value. + * @method $this setYears(int $value) Set current instance year to the given value. + * @method $this setYear(int $value) Set current instance year to the given value. + * @method $this months(int $value) Set current instance month to the given value. + * @method $this month(int $value) Set current instance month to the given value. + * @method $this setMonths(int $value) Set current instance month to the given value. + * @method $this setMonth(int $value) Set current instance month to the given value. + * @method $this days(int $value) Set current instance day to the given value. + * @method $this day(int $value) Set current instance day to the given value. + * @method $this setDays(int $value) Set current instance day to the given value. + * @method $this setDay(int $value) Set current instance day to the given value. + * @method $this hours(int $value) Set current instance hour to the given value. + * @method $this hour(int $value) Set current instance hour to the given value. + * @method $this setHours(int $value) Set current instance hour to the given value. + * @method $this setHour(int $value) Set current instance hour to the given value. + * @method $this minutes(int $value) Set current instance minute to the given value. + * @method $this minute(int $value) Set current instance minute to the given value. + * @method $this setMinutes(int $value) Set current instance minute to the given value. + * @method $this setMinute(int $value) Set current instance minute to the given value. + * @method $this seconds(int $value) Set current instance second to the given value. + * @method $this second(int $value) Set current instance second to the given value. + * @method $this setSeconds(int $value) Set current instance second to the given value. + * @method $this setSecond(int $value) Set current instance second to the given value. + * @method $this millis(int $value) Set current instance millisecond to the given value. + * @method $this milli(int $value) Set current instance millisecond to the given value. + * @method $this setMillis(int $value) Set current instance millisecond to the given value. + * @method $this setMilli(int $value) Set current instance millisecond to the given value. + * @method $this milliseconds(int $value) Set current instance millisecond to the given value. + * @method $this millisecond(int $value) Set current instance millisecond to the given value. + * @method $this setMilliseconds(int $value) Set current instance millisecond to the given value. + * @method $this setMillisecond(int $value) Set current instance millisecond to the given value. + * @method $this micros(int $value) Set current instance microsecond to the given value. + * @method $this micro(int $value) Set current instance microsecond to the given value. + * @method $this setMicros(int $value) Set current instance microsecond to the given value. + * @method $this setMicro(int $value) Set current instance microsecond to the given value. + * @method $this microseconds(int $value) Set current instance microsecond to the given value. + * @method $this microsecond(int $value) Set current instance microsecond to the given value. + * @method $this setMicroseconds(int $value) Set current instance microsecond to the given value. + * @method $this setMicrosecond(int $value) Set current instance microsecond to the given value. + * @method $this addYears(int $value = 1) Add years (the $value count passed in) to the instance (using date interval). + * @method $this addYear() Add one year to the instance (using date interval). + * @method $this subYears(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval). + * @method $this subYear() Sub one year to the instance (using date interval). + * @method $this addYearsWithOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed. + * @method $this subYearsWithOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed. + * @method $this addYearsWithoutOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subYearsWithoutOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addYearsWithNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subYearsWithNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addYearsNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subYearsNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMonths(int $value = 1) Add months (the $value count passed in) to the instance (using date interval). + * @method $this addMonth() Add one month to the instance (using date interval). + * @method $this subMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval). + * @method $this subMonth() Sub one month to the instance (using date interval). + * @method $this addMonthsWithOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed. + * @method $this subMonthsWithOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed. + * @method $this addMonthsWithoutOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMonthsWithoutOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMonthsWithNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMonthsWithNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMonthsNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMonthsNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDays(int $value = 1) Add days (the $value count passed in) to the instance (using date interval). + * @method $this addDay() Add one day to the instance (using date interval). + * @method $this subDays(int $value = 1) Sub days (the $value count passed in) to the instance (using date interval). + * @method $this subDay() Sub one day to the instance (using date interval). + * @method $this addHours(int $value = 1) Add hours (the $value count passed in) to the instance (using date interval). + * @method $this addHour() Add one hour to the instance (using date interval). + * @method $this subHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). + * @method $this subHour() Sub one hour to the instance (using date interval). + * @method $this addMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). + * @method $this addMinute() Add one minute to the instance (using date interval). + * @method $this subMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). + * @method $this subMinute() Sub one minute to the instance (using date interval). + * @method $this addSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). + * @method $this addSecond() Add one second to the instance (using date interval). + * @method $this subSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). + * @method $this subSecond() Sub one second to the instance (using date interval). + * @method $this addMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method $this addMilli() Add one millisecond to the instance (using date interval). + * @method $this subMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method $this subMilli() Sub one millisecond to the instance (using date interval). + * @method $this addMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method $this addMillisecond() Add one millisecond to the instance (using date interval). + * @method $this subMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method $this subMillisecond() Sub one millisecond to the instance (using date interval). + * @method $this addMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method $this addMicro() Add one microsecond to the instance (using date interval). + * @method $this subMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method $this subMicro() Sub one microsecond to the instance (using date interval). + * @method $this addMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method $this addMicrosecond() Add one microsecond to the instance (using date interval). + * @method $this subMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method $this subMicrosecond() Sub one microsecond to the instance (using date interval). + * @method $this addMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). + * @method $this addMillennium() Add one millennium to the instance (using date interval). + * @method $this subMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). + * @method $this subMillennium() Sub one millennium to the instance (using date interval). + * @method $this addMillenniaWithOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed. + * @method $this subMillenniaWithOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed. + * @method $this addMillenniaWithoutOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMillenniaWithoutOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMillenniaWithNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMillenniaWithNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMillenniaNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMillenniaNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). + * @method $this addCentury() Add one century to the instance (using date interval). + * @method $this subCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). + * @method $this subCentury() Sub one century to the instance (using date interval). + * @method $this addCenturiesWithOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed. + * @method $this subCenturiesWithOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed. + * @method $this addCenturiesWithoutOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subCenturiesWithoutOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addCenturiesWithNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subCenturiesWithNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addCenturiesNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subCenturiesNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval). + * @method $this addDecade() Add one decade to the instance (using date interval). + * @method $this subDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). + * @method $this subDecade() Sub one decade to the instance (using date interval). + * @method $this addDecadesWithOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed. + * @method $this subDecadesWithOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed. + * @method $this addDecadesWithoutOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subDecadesWithoutOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDecadesWithNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subDecadesWithNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDecadesNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subDecadesNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). + * @method $this addQuarter() Add one quarter to the instance (using date interval). + * @method $this subQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). + * @method $this subQuarter() Sub one quarter to the instance (using date interval). + * @method $this addQuartersWithOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed. + * @method $this subQuartersWithOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method $this subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed. + * @method $this addQuartersWithoutOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subQuartersWithoutOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addQuartersWithNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subQuartersWithNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addQuartersNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subQuartersNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method $this subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method $this addWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). + * @method $this addWeek() Add one week to the instance (using date interval). + * @method $this subWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). + * @method $this subWeek() Sub one week to the instance (using date interval). + * @method $this addWeekdays(int $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). + * @method $this addWeekday() Add one weekday to the instance (using date interval). + * @method $this subWeekdays(int $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). + * @method $this subWeekday() Sub one weekday to the instance (using date interval). + * @method $this addRealMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method $this addRealMicro() Add one microsecond to the instance (using timestamp). + * @method $this subRealMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method $this subRealMicro() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method $this addRealMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method $this addRealMicrosecond() Add one microsecond to the instance (using timestamp). + * @method $this subRealMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method $this subRealMicrosecond() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method $this addRealMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method $this addRealMilli() Add one millisecond to the instance (using timestamp). + * @method $this subRealMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method $this subRealMilli() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method $this addRealMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method $this addRealMillisecond() Add one millisecond to the instance (using timestamp). + * @method $this subRealMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method $this subRealMillisecond() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method $this addRealSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). + * @method $this addRealSecond() Add one second to the instance (using timestamp). + * @method $this subRealSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). + * @method $this subRealSecond() Sub one second to the instance (using timestamp). + * @method CarbonPeriod secondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. + * @method $this addRealMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). + * @method $this addRealMinute() Add one minute to the instance (using timestamp). + * @method $this subRealMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). + * @method $this subRealMinute() Sub one minute to the instance (using timestamp). + * @method CarbonPeriod minutesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. + * @method $this addRealHours(int $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). + * @method $this addRealHour() Add one hour to the instance (using timestamp). + * @method $this subRealHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). + * @method $this subRealHour() Sub one hour to the instance (using timestamp). + * @method CarbonPeriod hoursUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. + * @method $this addRealDays(int $value = 1) Add days (the $value count passed in) to the instance (using timestamp). + * @method $this addRealDay() Add one day to the instance (using timestamp). + * @method $this subRealDays(int $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). + * @method $this subRealDay() Sub one day to the instance (using timestamp). + * @method CarbonPeriod daysUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. + * @method $this addRealWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). + * @method $this addRealWeek() Add one week to the instance (using timestamp). + * @method $this subRealWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). + * @method $this subRealWeek() Sub one week to the instance (using timestamp). + * @method CarbonPeriod weeksUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. + * @method $this addRealMonths(int $value = 1) Add months (the $value count passed in) to the instance (using timestamp). + * @method $this addRealMonth() Add one month to the instance (using timestamp). + * @method $this subRealMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). + * @method $this subRealMonth() Sub one month to the instance (using timestamp). + * @method CarbonPeriod monthsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. + * @method $this addRealQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). + * @method $this addRealQuarter() Add one quarter to the instance (using timestamp). + * @method $this subRealQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). + * @method $this subRealQuarter() Sub one quarter to the instance (using timestamp). + * @method CarbonPeriod quartersUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. + * @method $this addRealYears(int $value = 1) Add years (the $value count passed in) to the instance (using timestamp). + * @method $this addRealYear() Add one year to the instance (using timestamp). + * @method $this subRealYears(int $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). + * @method $this subRealYear() Sub one year to the instance (using timestamp). + * @method CarbonPeriod yearsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. + * @method $this addRealDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). + * @method $this addRealDecade() Add one decade to the instance (using timestamp). + * @method $this subRealDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). + * @method $this subRealDecade() Sub one decade to the instance (using timestamp). + * @method CarbonPeriod decadesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. + * @method $this addRealCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). + * @method $this addRealCentury() Add one century to the instance (using timestamp). + * @method $this subRealCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). + * @method $this subRealCentury() Sub one century to the instance (using timestamp). + * @method CarbonPeriod centuriesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. + * @method $this addRealMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). + * @method $this addRealMillennium() Add one millennium to the instance (using timestamp). + * @method $this subRealMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). + * @method $this subRealMillennium() Sub one millennium to the instance (using timestamp). + * @method CarbonPeriod millenniaUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. + * @method $this roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method $this roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method $this floorYear(float $precision = 1) Truncate the current instance year with given precision. + * @method $this floorYears(float $precision = 1) Truncate the current instance year with given precision. + * @method $this ceilYear(float $precision = 1) Ceil the current instance year with given precision. + * @method $this ceilYears(float $precision = 1) Ceil the current instance year with given precision. + * @method $this roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method $this roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method $this floorMonth(float $precision = 1) Truncate the current instance month with given precision. + * @method $this floorMonths(float $precision = 1) Truncate the current instance month with given precision. + * @method $this ceilMonth(float $precision = 1) Ceil the current instance month with given precision. + * @method $this ceilMonths(float $precision = 1) Ceil the current instance month with given precision. + * @method $this roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method $this roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method $this floorDay(float $precision = 1) Truncate the current instance day with given precision. + * @method $this floorDays(float $precision = 1) Truncate the current instance day with given precision. + * @method $this ceilDay(float $precision = 1) Ceil the current instance day with given precision. + * @method $this ceilDays(float $precision = 1) Ceil the current instance day with given precision. + * @method $this roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method $this roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method $this floorHour(float $precision = 1) Truncate the current instance hour with given precision. + * @method $this floorHours(float $precision = 1) Truncate the current instance hour with given precision. + * @method $this ceilHour(float $precision = 1) Ceil the current instance hour with given precision. + * @method $this ceilHours(float $precision = 1) Ceil the current instance hour with given precision. + * @method $this roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method $this roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method $this floorMinute(float $precision = 1) Truncate the current instance minute with given precision. + * @method $this floorMinutes(float $precision = 1) Truncate the current instance minute with given precision. + * @method $this ceilMinute(float $precision = 1) Ceil the current instance minute with given precision. + * @method $this ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision. + * @method $this roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method $this roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method $this floorSecond(float $precision = 1) Truncate the current instance second with given precision. + * @method $this floorSeconds(float $precision = 1) Truncate the current instance second with given precision. + * @method $this ceilSecond(float $precision = 1) Ceil the current instance second with given precision. + * @method $this ceilSeconds(float $precision = 1) Ceil the current instance second with given precision. + * @method $this roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method $this roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method $this floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision. + * @method $this floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision. + * @method $this ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision. + * @method $this ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision. + * @method $this roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method $this roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method $this floorCentury(float $precision = 1) Truncate the current instance century with given precision. + * @method $this floorCenturies(float $precision = 1) Truncate the current instance century with given precision. + * @method $this ceilCentury(float $precision = 1) Ceil the current instance century with given precision. + * @method $this ceilCenturies(float $precision = 1) Ceil the current instance century with given precision. + * @method $this roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method $this roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method $this floorDecade(float $precision = 1) Truncate the current instance decade with given precision. + * @method $this floorDecades(float $precision = 1) Truncate the current instance decade with given precision. + * @method $this ceilDecade(float $precision = 1) Ceil the current instance decade with given precision. + * @method $this ceilDecades(float $precision = 1) Ceil the current instance decade with given precision. + * @method $this roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method $this roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method $this floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision. + * @method $this floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision. + * @method $this ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision. + * @method $this ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision. + * @method $this roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method $this roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method $this floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method $this floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method $this ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method $this ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method $this roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method $this roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method $this floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method $this floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method $this ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method $this ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method static static|false createFromFormat(string $format, string $time, DateTimeZone|string|false|null $timezone = null) Parse a string into a new Carbon object according to the specified format. + * @method static static __set_state(array $array) https://php.net/manual/en/datetime.set-state.php + * + * + */ +class Carbon extends DateTime implements CarbonInterface +{ + use Date; + + /** + * Returns true if the current class/instance is mutable. + * + * @return bool + */ + public static function isMutable() + { + return true; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonConverterInterface.php b/vendor/nesbot/carbon/src/Carbon/CarbonConverterInterface.php new file mode 100644 index 0000000..1ce967b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/CarbonConverterInterface.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use DateTimeInterface; + +interface CarbonConverterInterface +{ + public function convertDate(DateTimeInterface $dateTime, bool $negated = false): CarbonInterface; +} diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonImmutable.php b/vendor/nesbot/carbon/src/Carbon/CarbonImmutable.php new file mode 100644 index 0000000..4c9c1cf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/CarbonImmutable.php @@ -0,0 +1,582 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Carbon\Traits\Date; +use Carbon\Traits\DeprecatedProperties; +use DateTimeImmutable; +use DateTimeInterface; +use DateTimeZone; + +/** + * A simple API extension for DateTimeImmutable. + * + * @mixin DeprecatedProperties + * + * + * + * @property int $year + * @property int $yearIso + * @property int $month + * @property int $day + * @property int $hour + * @property int $minute + * @property int $second + * @property int $micro + * @property int $microsecond + * @property int|float|string $timestamp seconds since the Unix Epoch + * @property string $englishDayOfWeek the day of week in English + * @property string $shortEnglishDayOfWeek the abbreviated day of week in English + * @property string $englishMonth the month in English + * @property string $shortEnglishMonth the abbreviated month in English + * @property int $milliseconds + * @property int $millisecond + * @property int $milli + * @property int $week 1 through 53 + * @property int $isoWeek 1 through 53 + * @property int $weekYear year according to week format + * @property int $isoWeekYear year according to ISO week format + * @property int $dayOfYear 1 through 366 + * @property int $age does a diffInYears() with default parameters + * @property int $offset the timezone offset in seconds from UTC + * @property int $offsetMinutes the timezone offset in minutes from UTC + * @property int $offsetHours the timezone offset in hours from UTC + * @property CarbonTimeZone $timezone the current timezone + * @property CarbonTimeZone $tz alias of $timezone + * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) + * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) + * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday + * @property-read int $daysInMonth number of days in the given month + * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark) + * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark) + * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name + * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName + * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language + * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language + * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + * @property-read int $noZeroHour current hour from 1 to 24 + * @property-read int $weeksInYear 51 through 53 + * @property-read int $isoWeeksInYear 51 through 53 + * @property-read int $weekOfMonth 1 through 5 + * @property-read int $weekNumberInMonth 1 through 5 + * @property-read int $firstWeekDay 0 through 6 + * @property-read int $lastWeekDay 0 through 6 + * @property-read int $daysInYear 365 or 366 + * @property-read int $quarter the quarter of this instance, 1 - 4 + * @property-read int $decade the decade of this instance + * @property-read int $century the century of this instance + * @property-read int $millennium the millennium of this instance + * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise + * @property-read bool $local checks if the timezone is local, true if local, false otherwise + * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise + * @property-read string $timezoneName the current timezone name + * @property-read string $tzName alias of $timezoneName + * @property-read string $locale locale of the current instance + * + * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) + * @method bool isLocal() Check if the current instance has non-UTC timezone. + * @method bool isValid() Check if the current instance is a valid date. + * @method bool isDST() Check if the current instance is in a daylight saving time. + * @method bool isSunday() Checks if the instance day is sunday. + * @method bool isMonday() Checks if the instance day is monday. + * @method bool isTuesday() Checks if the instance day is tuesday. + * @method bool isWednesday() Checks if the instance day is wednesday. + * @method bool isThursday() Checks if the instance day is thursday. + * @method bool isFriday() Checks if the instance day is friday. + * @method bool isSaturday() Checks if the instance day is saturday. + * @method bool isSameYear(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment. + * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year. + * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year. + * @method bool isSameWeek(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment. + * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week. + * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week. + * @method bool isSameDay(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment. + * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day. + * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day. + * @method bool isSameHour(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment. + * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour. + * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour. + * @method bool isSameMinute(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment. + * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute. + * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute. + * @method bool isSameSecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment. + * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second. + * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second. + * @method bool isSameMicro(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment. + * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond. + * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond. + * @method bool isSameMicrosecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment. + * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond. + * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond. + * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. + * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. + * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. + * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. + * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. + * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. + * @method bool isSameDecade(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment. + * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade. + * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade. + * @method bool isSameCentury(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment. + * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century. + * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century. + * @method bool isSameMillennium(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment. + * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium. + * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium. + * @method CarbonImmutable years(int $value) Set current instance year to the given value. + * @method CarbonImmutable year(int $value) Set current instance year to the given value. + * @method CarbonImmutable setYears(int $value) Set current instance year to the given value. + * @method CarbonImmutable setYear(int $value) Set current instance year to the given value. + * @method CarbonImmutable months(int $value) Set current instance month to the given value. + * @method CarbonImmutable month(int $value) Set current instance month to the given value. + * @method CarbonImmutable setMonths(int $value) Set current instance month to the given value. + * @method CarbonImmutable setMonth(int $value) Set current instance month to the given value. + * @method CarbonImmutable days(int $value) Set current instance day to the given value. + * @method CarbonImmutable day(int $value) Set current instance day to the given value. + * @method CarbonImmutable setDays(int $value) Set current instance day to the given value. + * @method CarbonImmutable setDay(int $value) Set current instance day to the given value. + * @method CarbonImmutable hours(int $value) Set current instance hour to the given value. + * @method CarbonImmutable hour(int $value) Set current instance hour to the given value. + * @method CarbonImmutable setHours(int $value) Set current instance hour to the given value. + * @method CarbonImmutable setHour(int $value) Set current instance hour to the given value. + * @method CarbonImmutable minutes(int $value) Set current instance minute to the given value. + * @method CarbonImmutable minute(int $value) Set current instance minute to the given value. + * @method CarbonImmutable setMinutes(int $value) Set current instance minute to the given value. + * @method CarbonImmutable setMinute(int $value) Set current instance minute to the given value. + * @method CarbonImmutable seconds(int $value) Set current instance second to the given value. + * @method CarbonImmutable second(int $value) Set current instance second to the given value. + * @method CarbonImmutable setSeconds(int $value) Set current instance second to the given value. + * @method CarbonImmutable setSecond(int $value) Set current instance second to the given value. + * @method CarbonImmutable millis(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable milli(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable setMillis(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable setMilli(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable milliseconds(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable millisecond(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable setMilliseconds(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable setMillisecond(int $value) Set current instance millisecond to the given value. + * @method CarbonImmutable micros(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable micro(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable setMicros(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable setMicro(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable microseconds(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable microsecond(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable setMicroseconds(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable setMicrosecond(int $value) Set current instance microsecond to the given value. + * @method CarbonImmutable addYears(int $value = 1) Add years (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addYear() Add one year to the instance (using date interval). + * @method CarbonImmutable subYears(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subYear() Sub one year to the instance (using date interval). + * @method CarbonImmutable addYearsWithOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subYearsWithOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addYearsWithoutOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subYearsWithoutOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addYearsWithNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subYearsWithNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addYearsNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subYearsNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMonths(int $value = 1) Add months (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMonth() Add one month to the instance (using date interval). + * @method CarbonImmutable subMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMonth() Sub one month to the instance (using date interval). + * @method CarbonImmutable addMonthsWithOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subMonthsWithOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addMonthsWithoutOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMonthsWithoutOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMonthsWithNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMonthsWithNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMonthsNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMonthsNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDays(int $value = 1) Add days (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addDay() Add one day to the instance (using date interval). + * @method CarbonImmutable subDays(int $value = 1) Sub days (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subDay() Sub one day to the instance (using date interval). + * @method CarbonImmutable addHours(int $value = 1) Add hours (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addHour() Add one hour to the instance (using date interval). + * @method CarbonImmutable subHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subHour() Sub one hour to the instance (using date interval). + * @method CarbonImmutable addMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMinute() Add one minute to the instance (using date interval). + * @method CarbonImmutable subMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMinute() Sub one minute to the instance (using date interval). + * @method CarbonImmutable addSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addSecond() Add one second to the instance (using date interval). + * @method CarbonImmutable subSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subSecond() Sub one second to the instance (using date interval). + * @method CarbonImmutable addMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMilli() Add one millisecond to the instance (using date interval). + * @method CarbonImmutable subMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMilli() Sub one millisecond to the instance (using date interval). + * @method CarbonImmutable addMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMillisecond() Add one millisecond to the instance (using date interval). + * @method CarbonImmutable subMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMillisecond() Sub one millisecond to the instance (using date interval). + * @method CarbonImmutable addMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMicro() Add one microsecond to the instance (using date interval). + * @method CarbonImmutable subMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMicro() Sub one microsecond to the instance (using date interval). + * @method CarbonImmutable addMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMicrosecond() Add one microsecond to the instance (using date interval). + * @method CarbonImmutable subMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMicrosecond() Sub one microsecond to the instance (using date interval). + * @method CarbonImmutable addMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addMillennium() Add one millennium to the instance (using date interval). + * @method CarbonImmutable subMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subMillennium() Sub one millennium to the instance (using date interval). + * @method CarbonImmutable addMillenniaWithOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subMillenniaWithOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addMillenniaWithoutOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMillenniaWithoutOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMillenniaWithNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMillenniaWithNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMillenniaNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMillenniaNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addCentury() Add one century to the instance (using date interval). + * @method CarbonImmutable subCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subCentury() Sub one century to the instance (using date interval). + * @method CarbonImmutable addCenturiesWithOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subCenturiesWithOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addCenturiesWithoutOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subCenturiesWithoutOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addCenturiesWithNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subCenturiesWithNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addCenturiesNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subCenturiesNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addDecade() Add one decade to the instance (using date interval). + * @method CarbonImmutable subDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subDecade() Sub one decade to the instance (using date interval). + * @method CarbonImmutable addDecadesWithOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subDecadesWithOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addDecadesWithoutOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subDecadesWithoutOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDecadesWithNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subDecadesWithNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDecadesNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subDecadesNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addQuarter() Add one quarter to the instance (using date interval). + * @method CarbonImmutable subQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subQuarter() Sub one quarter to the instance (using date interval). + * @method CarbonImmutable addQuartersWithOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subQuartersWithOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonImmutable addQuartersWithoutOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subQuartersWithoutOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addQuartersWithNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subQuartersWithNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addQuartersNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subQuartersNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonImmutable addWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addWeek() Add one week to the instance (using date interval). + * @method CarbonImmutable subWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subWeek() Sub one week to the instance (using date interval). + * @method CarbonImmutable addWeekdays(int $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable addWeekday() Add one weekday to the instance (using date interval). + * @method CarbonImmutable subWeekdays(int $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). + * @method CarbonImmutable subWeekday() Sub one weekday to the instance (using date interval). + * @method CarbonImmutable addRealMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealMicro() Add one microsecond to the instance (using timestamp). + * @method CarbonImmutable subRealMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealMicro() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method CarbonImmutable addRealMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealMicrosecond() Add one microsecond to the instance (using timestamp). + * @method CarbonImmutable subRealMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealMicrosecond() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method CarbonImmutable addRealMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealMilli() Add one millisecond to the instance (using timestamp). + * @method CarbonImmutable subRealMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealMilli() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method CarbonImmutable addRealMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealMillisecond() Add one millisecond to the instance (using timestamp). + * @method CarbonImmutable subRealMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealMillisecond() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method CarbonImmutable addRealSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealSecond() Add one second to the instance (using timestamp). + * @method CarbonImmutable subRealSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealSecond() Sub one second to the instance (using timestamp). + * @method CarbonPeriod secondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. + * @method CarbonImmutable addRealMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealMinute() Add one minute to the instance (using timestamp). + * @method CarbonImmutable subRealMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealMinute() Sub one minute to the instance (using timestamp). + * @method CarbonPeriod minutesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. + * @method CarbonImmutable addRealHours(int $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealHour() Add one hour to the instance (using timestamp). + * @method CarbonImmutable subRealHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealHour() Sub one hour to the instance (using timestamp). + * @method CarbonPeriod hoursUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. + * @method CarbonImmutable addRealDays(int $value = 1) Add days (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealDay() Add one day to the instance (using timestamp). + * @method CarbonImmutable subRealDays(int $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealDay() Sub one day to the instance (using timestamp). + * @method CarbonPeriod daysUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. + * @method CarbonImmutable addRealWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealWeek() Add one week to the instance (using timestamp). + * @method CarbonImmutable subRealWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealWeek() Sub one week to the instance (using timestamp). + * @method CarbonPeriod weeksUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. + * @method CarbonImmutable addRealMonths(int $value = 1) Add months (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealMonth() Add one month to the instance (using timestamp). + * @method CarbonImmutable subRealMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealMonth() Sub one month to the instance (using timestamp). + * @method CarbonPeriod monthsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. + * @method CarbonImmutable addRealQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealQuarter() Add one quarter to the instance (using timestamp). + * @method CarbonImmutable subRealQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealQuarter() Sub one quarter to the instance (using timestamp). + * @method CarbonPeriod quartersUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. + * @method CarbonImmutable addRealYears(int $value = 1) Add years (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealYear() Add one year to the instance (using timestamp). + * @method CarbonImmutable subRealYears(int $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealYear() Sub one year to the instance (using timestamp). + * @method CarbonPeriod yearsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. + * @method CarbonImmutable addRealDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealDecade() Add one decade to the instance (using timestamp). + * @method CarbonImmutable subRealDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealDecade() Sub one decade to the instance (using timestamp). + * @method CarbonPeriod decadesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. + * @method CarbonImmutable addRealCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealCentury() Add one century to the instance (using timestamp). + * @method CarbonImmutable subRealCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealCentury() Sub one century to the instance (using timestamp). + * @method CarbonPeriod centuriesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. + * @method CarbonImmutable addRealMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable addRealMillennium() Add one millennium to the instance (using timestamp). + * @method CarbonImmutable subRealMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). + * @method CarbonImmutable subRealMillennium() Sub one millennium to the instance (using timestamp). + * @method CarbonPeriod millenniaUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. + * @method CarbonImmutable roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method CarbonImmutable roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method CarbonImmutable floorYear(float $precision = 1) Truncate the current instance year with given precision. + * @method CarbonImmutable floorYears(float $precision = 1) Truncate the current instance year with given precision. + * @method CarbonImmutable ceilYear(float $precision = 1) Ceil the current instance year with given precision. + * @method CarbonImmutable ceilYears(float $precision = 1) Ceil the current instance year with given precision. + * @method CarbonImmutable roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method CarbonImmutable roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method CarbonImmutable floorMonth(float $precision = 1) Truncate the current instance month with given precision. + * @method CarbonImmutable floorMonths(float $precision = 1) Truncate the current instance month with given precision. + * @method CarbonImmutable ceilMonth(float $precision = 1) Ceil the current instance month with given precision. + * @method CarbonImmutable ceilMonths(float $precision = 1) Ceil the current instance month with given precision. + * @method CarbonImmutable roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method CarbonImmutable roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method CarbonImmutable floorDay(float $precision = 1) Truncate the current instance day with given precision. + * @method CarbonImmutable floorDays(float $precision = 1) Truncate the current instance day with given precision. + * @method CarbonImmutable ceilDay(float $precision = 1) Ceil the current instance day with given precision. + * @method CarbonImmutable ceilDays(float $precision = 1) Ceil the current instance day with given precision. + * @method CarbonImmutable roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method CarbonImmutable roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method CarbonImmutable floorHour(float $precision = 1) Truncate the current instance hour with given precision. + * @method CarbonImmutable floorHours(float $precision = 1) Truncate the current instance hour with given precision. + * @method CarbonImmutable ceilHour(float $precision = 1) Ceil the current instance hour with given precision. + * @method CarbonImmutable ceilHours(float $precision = 1) Ceil the current instance hour with given precision. + * @method CarbonImmutable roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method CarbonImmutable roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method CarbonImmutable floorMinute(float $precision = 1) Truncate the current instance minute with given precision. + * @method CarbonImmutable floorMinutes(float $precision = 1) Truncate the current instance minute with given precision. + * @method CarbonImmutable ceilMinute(float $precision = 1) Ceil the current instance minute with given precision. + * @method CarbonImmutable ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision. + * @method CarbonImmutable roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method CarbonImmutable roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method CarbonImmutable floorSecond(float $precision = 1) Truncate the current instance second with given precision. + * @method CarbonImmutable floorSeconds(float $precision = 1) Truncate the current instance second with given precision. + * @method CarbonImmutable ceilSecond(float $precision = 1) Ceil the current instance second with given precision. + * @method CarbonImmutable ceilSeconds(float $precision = 1) Ceil the current instance second with given precision. + * @method CarbonImmutable roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method CarbonImmutable roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method CarbonImmutable floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision. + * @method CarbonImmutable floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision. + * @method CarbonImmutable ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision. + * @method CarbonImmutable ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision. + * @method CarbonImmutable roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method CarbonImmutable roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method CarbonImmutable floorCentury(float $precision = 1) Truncate the current instance century with given precision. + * @method CarbonImmutable floorCenturies(float $precision = 1) Truncate the current instance century with given precision. + * @method CarbonImmutable ceilCentury(float $precision = 1) Ceil the current instance century with given precision. + * @method CarbonImmutable ceilCenturies(float $precision = 1) Ceil the current instance century with given precision. + * @method CarbonImmutable roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method CarbonImmutable roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method CarbonImmutable floorDecade(float $precision = 1) Truncate the current instance decade with given precision. + * @method CarbonImmutable floorDecades(float $precision = 1) Truncate the current instance decade with given precision. + * @method CarbonImmutable ceilDecade(float $precision = 1) Ceil the current instance decade with given precision. + * @method CarbonImmutable ceilDecades(float $precision = 1) Ceil the current instance decade with given precision. + * @method CarbonImmutable roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method CarbonImmutable roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method CarbonImmutable floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision. + * @method CarbonImmutable floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision. + * @method CarbonImmutable ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision. + * @method CarbonImmutable ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision. + * @method CarbonImmutable roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method CarbonImmutable roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method CarbonImmutable floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method CarbonImmutable floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method CarbonImmutable ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method CarbonImmutable ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method CarbonImmutable roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method CarbonImmutable roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method CarbonImmutable floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method CarbonImmutable floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method CarbonImmutable ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method CarbonImmutable ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method static static|false createFromFormat(string $format, string $time, DateTimeZone|string|false|null $timezone = null) Parse a string into a new CarbonImmutable object according to the specified format. + * @method static static __set_state(array $array) https://php.net/manual/en/datetime.set-state.php + * + * + */ +class CarbonImmutable extends DateTimeImmutable implements CarbonInterface +{ + use Date { + __clone as dateTraitClone; + } + + public function __clone() + { + $this->dateTraitClone(); + $this->endOfTime = false; + $this->startOfTime = false; + } + + /** + * Create a very old date representing start of time. + * + * @return static + */ + public static function startOfTime(): self + { + $date = static::parse('0001-01-01')->years(self::getStartOfTimeYear()); + $date->startOfTime = true; + + return $date; + } + + /** + * Create a very far date representing end of time. + * + * @return static + */ + public static function endOfTime(): self + { + $date = static::parse('9999-12-31 23:59:59.999999')->years(self::getEndOfTimeYear()); + $date->endOfTime = true; + + return $date; + } + + /** + * @codeCoverageIgnore + */ + private static function getEndOfTimeYear(): int + { + if (version_compare(PHP_VERSION, '7.3.0-dev', '<')) { + return 145261681241552; + } + + // Remove if https://bugs.php.net/bug.php?id=81107 is fixed + if (version_compare(PHP_VERSION, '8.1.0-dev', '>=')) { + return 1118290769066902787; + } + + return PHP_INT_MAX; + } + + /** + * @codeCoverageIgnore + */ + private static function getStartOfTimeYear(): int + { + if (version_compare(PHP_VERSION, '7.3.0-dev', '<')) { + return -135908816449551; + } + + // Remove if https://bugs.php.net/bug.php?id=81107 is fixed + if (version_compare(PHP_VERSION, '8.1.0-dev', '>=')) { + return -1118290769066898816; + } + + return max(PHP_INT_MIN, -9223372036854773760); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonInterface.php b/vendor/nesbot/carbon/src/Carbon/CarbonInterface.php new file mode 100644 index 0000000..b90e298 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/CarbonInterface.php @@ -0,0 +1,5142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use BadMethodCallException; +use Carbon\Exceptions\BadComparisonUnitException; +use Carbon\Exceptions\ImmutableException; +use Carbon\Exceptions\InvalidDateException; +use Carbon\Exceptions\InvalidFormatException; +use Carbon\Exceptions\UnknownGetterException; +use Carbon\Exceptions\UnknownMethodException; +use Carbon\Exceptions\UnknownSetterException; +use Closure; +use DateInterval; +use DateTime; +use DateTimeImmutable; +use DateTimeInterface; +use DateTimeZone; +use JsonSerializable; +use ReflectionException; +use ReturnTypeWillChange; +use Symfony\Component\Translation\TranslatorInterface; +use Throwable; + +/** + * Common interface for Carbon and CarbonImmutable. + * + * + * + * @property int $year + * @property int $yearIso + * @property int $month + * @property int $day + * @property int $hour + * @property int $minute + * @property int $second + * @property int $micro + * @property int $microsecond + * @property int|float|string $timestamp seconds since the Unix Epoch + * @property string $englishDayOfWeek the day of week in English + * @property string $shortEnglishDayOfWeek the abbreviated day of week in English + * @property string $englishMonth the month in English + * @property string $shortEnglishMonth the abbreviated month in English + * @property int $milliseconds + * @property int $millisecond + * @property int $milli + * @property int $week 1 through 53 + * @property int $isoWeek 1 through 53 + * @property int $weekYear year according to week format + * @property int $isoWeekYear year according to ISO week format + * @property int $dayOfYear 1 through 366 + * @property int $age does a diffInYears() with default parameters + * @property int $offset the timezone offset in seconds from UTC + * @property int $offsetMinutes the timezone offset in minutes from UTC + * @property int $offsetHours the timezone offset in hours from UTC + * @property CarbonTimeZone $timezone the current timezone + * @property CarbonTimeZone $tz alias of $timezone + * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) + * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) + * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday + * @property-read int $daysInMonth number of days in the given month + * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark) + * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark) + * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name + * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName + * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language + * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language + * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + * @property-read int $noZeroHour current hour from 1 to 24 + * @property-read int $weeksInYear 51 through 53 + * @property-read int $isoWeeksInYear 51 through 53 + * @property-read int $weekOfMonth 1 through 5 + * @property-read int $weekNumberInMonth 1 through 5 + * @property-read int $firstWeekDay 0 through 6 + * @property-read int $lastWeekDay 0 through 6 + * @property-read int $daysInYear 365 or 366 + * @property-read int $quarter the quarter of this instance, 1 - 4 + * @property-read int $decade the decade of this instance + * @property-read int $century the century of this instance + * @property-read int $millennium the millennium of this instance + * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise + * @property-read bool $local checks if the timezone is local, true if local, false otherwise + * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise + * @property-read string $timezoneName the current timezone name + * @property-read string $tzName alias of $timezoneName + * @property-read string $locale locale of the current instance + * + * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) + * @method bool isLocal() Check if the current instance has non-UTC timezone. + * @method bool isValid() Check if the current instance is a valid date. + * @method bool isDST() Check if the current instance is in a daylight saving time. + * @method bool isSunday() Checks if the instance day is sunday. + * @method bool isMonday() Checks if the instance day is monday. + * @method bool isTuesday() Checks if the instance day is tuesday. + * @method bool isWednesday() Checks if the instance day is wednesday. + * @method bool isThursday() Checks if the instance day is thursday. + * @method bool isFriday() Checks if the instance day is friday. + * @method bool isSaturday() Checks if the instance day is saturday. + * @method bool isSameYear(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment. + * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year. + * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year. + * @method bool isSameWeek(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment. + * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week. + * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week. + * @method bool isSameDay(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment. + * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day. + * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day. + * @method bool isSameHour(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment. + * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour. + * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour. + * @method bool isSameMinute(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment. + * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute. + * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute. + * @method bool isSameSecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment. + * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second. + * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second. + * @method bool isSameMicro(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment. + * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond. + * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond. + * @method bool isSameMicrosecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment. + * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond. + * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond. + * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. + * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. + * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. + * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. + * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. + * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. + * @method bool isSameDecade(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment. + * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade. + * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade. + * @method bool isSameCentury(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment. + * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century. + * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century. + * @method bool isSameMillennium(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment. + * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium. + * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium. + * @method CarbonInterface years(int $value) Set current instance year to the given value. + * @method CarbonInterface year(int $value) Set current instance year to the given value. + * @method CarbonInterface setYears(int $value) Set current instance year to the given value. + * @method CarbonInterface setYear(int $value) Set current instance year to the given value. + * @method CarbonInterface months(int $value) Set current instance month to the given value. + * @method CarbonInterface month(int $value) Set current instance month to the given value. + * @method CarbonInterface setMonths(int $value) Set current instance month to the given value. + * @method CarbonInterface setMonth(int $value) Set current instance month to the given value. + * @method CarbonInterface days(int $value) Set current instance day to the given value. + * @method CarbonInterface day(int $value) Set current instance day to the given value. + * @method CarbonInterface setDays(int $value) Set current instance day to the given value. + * @method CarbonInterface setDay(int $value) Set current instance day to the given value. + * @method CarbonInterface hours(int $value) Set current instance hour to the given value. + * @method CarbonInterface hour(int $value) Set current instance hour to the given value. + * @method CarbonInterface setHours(int $value) Set current instance hour to the given value. + * @method CarbonInterface setHour(int $value) Set current instance hour to the given value. + * @method CarbonInterface minutes(int $value) Set current instance minute to the given value. + * @method CarbonInterface minute(int $value) Set current instance minute to the given value. + * @method CarbonInterface setMinutes(int $value) Set current instance minute to the given value. + * @method CarbonInterface setMinute(int $value) Set current instance minute to the given value. + * @method CarbonInterface seconds(int $value) Set current instance second to the given value. + * @method CarbonInterface second(int $value) Set current instance second to the given value. + * @method CarbonInterface setSeconds(int $value) Set current instance second to the given value. + * @method CarbonInterface setSecond(int $value) Set current instance second to the given value. + * @method CarbonInterface millis(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface milli(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface setMillis(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface setMilli(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface milliseconds(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface millisecond(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface setMilliseconds(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface setMillisecond(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface micros(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface micro(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface setMicros(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface setMicro(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface microseconds(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface microsecond(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface setMicroseconds(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface setMicrosecond(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface addYears(int $value = 1) Add years (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addYear() Add one year to the instance (using date interval). + * @method CarbonInterface subYears(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subYear() Sub one year to the instance (using date interval). + * @method CarbonInterface addYearsWithOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subYearsWithOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addYearsWithoutOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearsWithoutOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearsWithNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearsWithNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearsNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearsNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonths(int $value = 1) Add months (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMonth() Add one month to the instance (using date interval). + * @method CarbonInterface subMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMonth() Sub one month to the instance (using date interval). + * @method CarbonInterface addMonthsWithOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subMonthsWithOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addMonthsWithoutOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthsWithoutOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthsWithNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthsWithNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthsNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthsNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDays(int $value = 1) Add days (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addDay() Add one day to the instance (using date interval). + * @method CarbonInterface subDays(int $value = 1) Sub days (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subDay() Sub one day to the instance (using date interval). + * @method CarbonInterface addHours(int $value = 1) Add hours (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addHour() Add one hour to the instance (using date interval). + * @method CarbonInterface subHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subHour() Sub one hour to the instance (using date interval). + * @method CarbonInterface addMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMinute() Add one minute to the instance (using date interval). + * @method CarbonInterface subMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMinute() Sub one minute to the instance (using date interval). + * @method CarbonInterface addSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addSecond() Add one second to the instance (using date interval). + * @method CarbonInterface subSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subSecond() Sub one second to the instance (using date interval). + * @method CarbonInterface addMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMilli() Add one millisecond to the instance (using date interval). + * @method CarbonInterface subMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMilli() Sub one millisecond to the instance (using date interval). + * @method CarbonInterface addMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMillisecond() Add one millisecond to the instance (using date interval). + * @method CarbonInterface subMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMillisecond() Sub one millisecond to the instance (using date interval). + * @method CarbonInterface addMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMicro() Add one microsecond to the instance (using date interval). + * @method CarbonInterface subMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMicro() Sub one microsecond to the instance (using date interval). + * @method CarbonInterface addMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMicrosecond() Add one microsecond to the instance (using date interval). + * @method CarbonInterface subMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMicrosecond() Sub one microsecond to the instance (using date interval). + * @method CarbonInterface addMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMillennium() Add one millennium to the instance (using date interval). + * @method CarbonInterface subMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMillennium() Sub one millennium to the instance (using date interval). + * @method CarbonInterface addMillenniaWithOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subMillenniaWithOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addMillenniaWithoutOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniaWithoutOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniaWithNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniaWithNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniaNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniaNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addCentury() Add one century to the instance (using date interval). + * @method CarbonInterface subCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subCentury() Sub one century to the instance (using date interval). + * @method CarbonInterface addCenturiesWithOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subCenturiesWithOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addCenturiesWithoutOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturiesWithoutOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturiesWithNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturiesWithNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturiesNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturiesNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addDecade() Add one decade to the instance (using date interval). + * @method CarbonInterface subDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subDecade() Sub one decade to the instance (using date interval). + * @method CarbonInterface addDecadesWithOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subDecadesWithOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addDecadesWithoutOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadesWithoutOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadesWithNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadesWithNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadesNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadesNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addQuarter() Add one quarter to the instance (using date interval). + * @method CarbonInterface subQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subQuarter() Sub one quarter to the instance (using date interval). + * @method CarbonInterface addQuartersWithOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subQuartersWithOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addQuartersWithoutOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuartersWithoutOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuartersWithNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuartersWithNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuartersNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuartersNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addWeek() Add one week to the instance (using date interval). + * @method CarbonInterface subWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subWeek() Sub one week to the instance (using date interval). + * @method CarbonInterface addWeekdays(int $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addWeekday() Add one weekday to the instance (using date interval). + * @method CarbonInterface subWeekdays(int $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subWeekday() Sub one weekday to the instance (using date interval). + * @method CarbonInterface addRealMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMicro() Add one microsecond to the instance (using timestamp). + * @method CarbonInterface subRealMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMicro() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method CarbonInterface addRealMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMicrosecond() Add one microsecond to the instance (using timestamp). + * @method CarbonInterface subRealMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMicrosecond() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method CarbonInterface addRealMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMilli() Add one millisecond to the instance (using timestamp). + * @method CarbonInterface subRealMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMilli() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method CarbonInterface addRealMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMillisecond() Add one millisecond to the instance (using timestamp). + * @method CarbonInterface subRealMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMillisecond() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method CarbonInterface addRealSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealSecond() Add one second to the instance (using timestamp). + * @method CarbonInterface subRealSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealSecond() Sub one second to the instance (using timestamp). + * @method CarbonPeriod secondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. + * @method CarbonInterface addRealMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMinute() Add one minute to the instance (using timestamp). + * @method CarbonInterface subRealMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMinute() Sub one minute to the instance (using timestamp). + * @method CarbonPeriod minutesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. + * @method CarbonInterface addRealHours(int $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealHour() Add one hour to the instance (using timestamp). + * @method CarbonInterface subRealHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealHour() Sub one hour to the instance (using timestamp). + * @method CarbonPeriod hoursUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. + * @method CarbonInterface addRealDays(int $value = 1) Add days (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealDay() Add one day to the instance (using timestamp). + * @method CarbonInterface subRealDays(int $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealDay() Sub one day to the instance (using timestamp). + * @method CarbonPeriod daysUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. + * @method CarbonInterface addRealWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealWeek() Add one week to the instance (using timestamp). + * @method CarbonInterface subRealWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealWeek() Sub one week to the instance (using timestamp). + * @method CarbonPeriod weeksUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. + * @method CarbonInterface addRealMonths(int $value = 1) Add months (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMonth() Add one month to the instance (using timestamp). + * @method CarbonInterface subRealMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMonth() Sub one month to the instance (using timestamp). + * @method CarbonPeriod monthsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. + * @method CarbonInterface addRealQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealQuarter() Add one quarter to the instance (using timestamp). + * @method CarbonInterface subRealQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealQuarter() Sub one quarter to the instance (using timestamp). + * @method CarbonPeriod quartersUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. + * @method CarbonInterface addRealYears(int $value = 1) Add years (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealYear() Add one year to the instance (using timestamp). + * @method CarbonInterface subRealYears(int $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealYear() Sub one year to the instance (using timestamp). + * @method CarbonPeriod yearsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. + * @method CarbonInterface addRealDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealDecade() Add one decade to the instance (using timestamp). + * @method CarbonInterface subRealDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealDecade() Sub one decade to the instance (using timestamp). + * @method CarbonPeriod decadesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. + * @method CarbonInterface addRealCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealCentury() Add one century to the instance (using timestamp). + * @method CarbonInterface subRealCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealCentury() Sub one century to the instance (using timestamp). + * @method CarbonPeriod centuriesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. + * @method CarbonInterface addRealMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMillennium() Add one millennium to the instance (using timestamp). + * @method CarbonInterface subRealMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMillennium() Sub one millennium to the instance (using timestamp). + * @method CarbonPeriod millenniaUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. + * @method CarbonInterface roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method CarbonInterface roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method CarbonInterface floorYear(float $precision = 1) Truncate the current instance year with given precision. + * @method CarbonInterface floorYears(float $precision = 1) Truncate the current instance year with given precision. + * @method CarbonInterface ceilYear(float $precision = 1) Ceil the current instance year with given precision. + * @method CarbonInterface ceilYears(float $precision = 1) Ceil the current instance year with given precision. + * @method CarbonInterface roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method CarbonInterface roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method CarbonInterface floorMonth(float $precision = 1) Truncate the current instance month with given precision. + * @method CarbonInterface floorMonths(float $precision = 1) Truncate the current instance month with given precision. + * @method CarbonInterface ceilMonth(float $precision = 1) Ceil the current instance month with given precision. + * @method CarbonInterface ceilMonths(float $precision = 1) Ceil the current instance month with given precision. + * @method CarbonInterface roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method CarbonInterface roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method CarbonInterface floorDay(float $precision = 1) Truncate the current instance day with given precision. + * @method CarbonInterface floorDays(float $precision = 1) Truncate the current instance day with given precision. + * @method CarbonInterface ceilDay(float $precision = 1) Ceil the current instance day with given precision. + * @method CarbonInterface ceilDays(float $precision = 1) Ceil the current instance day with given precision. + * @method CarbonInterface roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method CarbonInterface roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method CarbonInterface floorHour(float $precision = 1) Truncate the current instance hour with given precision. + * @method CarbonInterface floorHours(float $precision = 1) Truncate the current instance hour with given precision. + * @method CarbonInterface ceilHour(float $precision = 1) Ceil the current instance hour with given precision. + * @method CarbonInterface ceilHours(float $precision = 1) Ceil the current instance hour with given precision. + * @method CarbonInterface roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method CarbonInterface roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method CarbonInterface floorMinute(float $precision = 1) Truncate the current instance minute with given precision. + * @method CarbonInterface floorMinutes(float $precision = 1) Truncate the current instance minute with given precision. + * @method CarbonInterface ceilMinute(float $precision = 1) Ceil the current instance minute with given precision. + * @method CarbonInterface ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision. + * @method CarbonInterface roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method CarbonInterface roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method CarbonInterface floorSecond(float $precision = 1) Truncate the current instance second with given precision. + * @method CarbonInterface floorSeconds(float $precision = 1) Truncate the current instance second with given precision. + * @method CarbonInterface ceilSecond(float $precision = 1) Ceil the current instance second with given precision. + * @method CarbonInterface ceilSeconds(float $precision = 1) Ceil the current instance second with given precision. + * @method CarbonInterface roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method CarbonInterface roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method CarbonInterface floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision. + * @method CarbonInterface floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision. + * @method CarbonInterface ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision. + * @method CarbonInterface ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision. + * @method CarbonInterface roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method CarbonInterface roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method CarbonInterface floorCentury(float $precision = 1) Truncate the current instance century with given precision. + * @method CarbonInterface floorCenturies(float $precision = 1) Truncate the current instance century with given precision. + * @method CarbonInterface ceilCentury(float $precision = 1) Ceil the current instance century with given precision. + * @method CarbonInterface ceilCenturies(float $precision = 1) Ceil the current instance century with given precision. + * @method CarbonInterface roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method CarbonInterface roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method CarbonInterface floorDecade(float $precision = 1) Truncate the current instance decade with given precision. + * @method CarbonInterface floorDecades(float $precision = 1) Truncate the current instance decade with given precision. + * @method CarbonInterface ceilDecade(float $precision = 1) Ceil the current instance decade with given precision. + * @method CarbonInterface ceilDecades(float $precision = 1) Ceil the current instance decade with given precision. + * @method CarbonInterface roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method CarbonInterface roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method CarbonInterface floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision. + * @method CarbonInterface floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision. + * @method CarbonInterface ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision. + * @method CarbonInterface ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision. + * @method CarbonInterface roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method CarbonInterface roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method CarbonInterface floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method CarbonInterface floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method CarbonInterface ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method CarbonInterface ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method CarbonInterface roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method CarbonInterface roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method CarbonInterface floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method CarbonInterface floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method CarbonInterface ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method CarbonInterface ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * + * + */ +interface CarbonInterface extends DateTimeInterface, JsonSerializable +{ + /** + * Diff wording options(expressed in octal). + */ + public const NO_ZERO_DIFF = 01; + public const JUST_NOW = 02; + public const ONE_DAY_WORDS = 04; + public const TWO_DAY_WORDS = 010; + public const SEQUENTIAL_PARTS_ONLY = 020; + public const ROUND = 040; + public const FLOOR = 0100; + public const CEIL = 0200; + + /** + * Diff syntax options. + */ + public const DIFF_ABSOLUTE = 1; // backward compatibility with true + public const DIFF_RELATIVE_AUTO = 0; // backward compatibility with false + public const DIFF_RELATIVE_TO_NOW = 2; + public const DIFF_RELATIVE_TO_OTHER = 3; + + /** + * Translate string options. + */ + public const TRANSLATE_MONTHS = 1; + public const TRANSLATE_DAYS = 2; + public const TRANSLATE_UNITS = 4; + public const TRANSLATE_MERIDIEM = 8; + public const TRANSLATE_DIFF = 0x10; + public const TRANSLATE_ALL = self::TRANSLATE_MONTHS | self::TRANSLATE_DAYS | self::TRANSLATE_UNITS | self::TRANSLATE_MERIDIEM | self::TRANSLATE_DIFF; + + /** + * The day constants. + */ + public const SUNDAY = 0; + public const MONDAY = 1; + public const TUESDAY = 2; + public const WEDNESDAY = 3; + public const THURSDAY = 4; + public const FRIDAY = 5; + public const SATURDAY = 6; + + /** + * The month constants. + * These aren't used by Carbon itself but exist for + * convenience sake alone. + */ + public const JANUARY = 1; + public const FEBRUARY = 2; + public const MARCH = 3; + public const APRIL = 4; + public const MAY = 5; + public const JUNE = 6; + public const JULY = 7; + public const AUGUST = 8; + public const SEPTEMBER = 9; + public const OCTOBER = 10; + public const NOVEMBER = 11; + public const DECEMBER = 12; + + /** + * Number of X in Y. + */ + public const YEARS_PER_MILLENNIUM = 1000; + public const YEARS_PER_CENTURY = 100; + public const YEARS_PER_DECADE = 10; + public const MONTHS_PER_YEAR = 12; + public const MONTHS_PER_QUARTER = 3; + public const QUARTERS_PER_YEAR = 4; + public const WEEKS_PER_YEAR = 52; + public const WEEKS_PER_MONTH = 4; + public const DAYS_PER_YEAR = 365; + public const DAYS_PER_WEEK = 7; + public const HOURS_PER_DAY = 24; + public const MINUTES_PER_HOUR = 60; + public const SECONDS_PER_MINUTE = 60; + public const MILLISECONDS_PER_SECOND = 1000; + public const MICROSECONDS_PER_MILLISECOND = 1000; + public const MICROSECONDS_PER_SECOND = 1000000; + + /** + * Special settings to get the start of week from current locale culture. + */ + public const WEEK_DAY_AUTO = 'auto'; + + /** + * RFC7231 DateTime format. + * + * @var string + */ + public const RFC7231_FORMAT = 'D, d M Y H:i:s \G\M\T'; + + /** + * Default format to use for __toString method when type juggling occurs. + * + * @var string + */ + public const DEFAULT_TO_STRING_FORMAT = 'Y-m-d H:i:s'; + + /** + * Format for converting mocked time, includes microseconds. + * + * @var string + */ + public const MOCK_DATETIME_FORMAT = 'Y-m-d H:i:s.u'; + + /** + * Pattern detection for ->isoFormat and ::createFromIsoFormat. + * + * @var string + */ + public const ISO_FORMAT_REGEXP = '(O[YMDHhms]|[Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY?|g{1,5}|G{1,5}|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?)'; + + // + + /** + * Dynamically handle calls to the class. + * + * @param string $method magic method name called + * @param array $parameters parameters list + * + * @throws UnknownMethodException|BadMethodCallException|ReflectionException|Throwable + * + * @return mixed + */ + public function __call($method, $parameters); + + /** + * Dynamically handle calls to the class. + * + * @param string $method magic method name called + * @param array $parameters parameters list + * + * @throws BadMethodCallException + * + * @return mixed + */ + public static function __callStatic($method, $parameters); + + /** + * Update constructedObjectId on cloned. + */ + public function __clone(); + + /** + * Create a new Carbon instance. + * + * Please see the testing aids section (specifically static::setTestNow()) + * for more on the possibility of this constructor returning a test instance. + * + * @param DateTimeInterface|string|null $time + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + */ + public function __construct($time = null, $tz = null); + + /** + * Show truthy properties on var_dump(). + * + * @return array + */ + public function __debugInfo(); + + /** + * Get a part of the Carbon object + * + * @param string $name + * + * @throws UnknownGetterException + * + * @return string|int|bool|DateTimeZone|null + */ + public function __get($name); + + /** + * Check if an attribute exists on the object + * + * @param string $name + * + * @return bool + */ + public function __isset($name); + + /** + * Set a part of the Carbon object + * + * @param string $name + * @param string|int|DateTimeZone $value + * + * @throws UnknownSetterException|ReflectionException + * + * @return void + */ + public function __set($name, $value); + + /** + * The __set_state handler. + * + * @param string|array $dump + * + * @return static + */ + #[ReturnTypeWillChange] + public static function __set_state($dump); + + /** + * Returns the list of properties to dump on serialize() called on. + * + * Only used by PHP < 7.4. + * + * @return array + */ + public function __sleep(); + + /** + * Format the instance as a string using the set format + * + * @example + * ``` + * echo Carbon::now(); // Carbon instances can be cast to string + * ``` + * + * @return string + */ + public function __toString(); + + /** + * Add given units or interval to the current instance. + * + * @example $date->add('hour', 3) + * @example $date->add(15, 'days') + * @example $date->add(CarbonInterval::days(4)) + * + * @param string|DateInterval|Closure|CarbonConverterInterface $unit + * @param int $value + * @param bool|null $overflow + * + * @return static + */ + #[ReturnTypeWillChange] + public function add($unit, $value = 1, $overflow = null); + + /** + * Add seconds to the instance using timestamp. Positive $value travels + * forward while negative $value travels into the past. + * + * @param string $unit + * @param int $value + * + * @return static + */ + public function addRealUnit($unit, $value = 1); + + /** + * Add given units to the current instance. + * + * @param string $unit + * @param int $value + * @param bool|null $overflow + * + * @return static + */ + public function addUnit($unit, $value = 1, $overflow = null); + + /** + * Add any unit to a new value without overflowing current other unit given. + * + * @param string $valueUnit unit name to modify + * @param int $value amount to add to the input unit + * @param string $overflowUnit unit name to not overflow + * + * @return static + */ + public function addUnitNoOverflow($valueUnit, $value, $overflowUnit); + + /** + * Get the difference in a human readable format in the current locale from an other + * instance given to now + * + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single part) + * @param int $options human diff options + * + * @return string + */ + public function ago($syntax = null, $short = false, $parts = 1, $options = null); + + /** + * Modify the current instance to the average of a given instance (default now) and the current instance + * (second-precision). + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date + * + * @return static + */ + public function average($date = null); + + /** + * Clone the current instance if it's mutable. + * + * This method is convenient to ensure you don't mutate the initial object + * but avoid to make a useless copy of it if it's already immutable. + * + * @return static + */ + public function avoidMutation(); + + /** + * Determines if the instance is between two others. + * + * The third argument allow you to specify if bounds are included or not (true by default) + * but for when you including/excluding bounds may produce different results in your application, + * we recommend to use the explicit methods ->betweenIncluded() or ->betweenExcluded() instead. + * + * @example + * ``` + * Carbon::parse('2018-07-25')->between('2018-07-14', '2018-08-01'); // true + * Carbon::parse('2018-07-25')->between('2018-08-01', '2018-08-20'); // false + * Carbon::parse('2018-07-25')->between('2018-07-25', '2018-08-01'); // true + * Carbon::parse('2018-07-25')->between('2018-07-25', '2018-08-01', false); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * @param bool $equal Indicates if an equal to comparison should be done + * + * @return bool + */ + public function between($date1, $date2, $equal = true): bool; + + /** + * Determines if the instance is between two others, bounds excluded. + * + * @example + * ``` + * Carbon::parse('2018-07-25')->betweenExcluded('2018-07-14', '2018-08-01'); // true + * Carbon::parse('2018-07-25')->betweenExcluded('2018-08-01', '2018-08-20'); // false + * Carbon::parse('2018-07-25')->betweenExcluded('2018-07-25', '2018-08-01'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * + * @return bool + */ + public function betweenExcluded($date1, $date2): bool; + + /** + * Determines if the instance is between two others, bounds included. + * + * @example + * ``` + * Carbon::parse('2018-07-25')->betweenIncluded('2018-07-14', '2018-08-01'); // true + * Carbon::parse('2018-07-25')->betweenIncluded('2018-08-01', '2018-08-20'); // false + * Carbon::parse('2018-07-25')->betweenIncluded('2018-07-25', '2018-08-01'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * + * @return bool + */ + public function betweenIncluded($date1, $date2): bool; + + /** + * Returns either day of week + time (e.g. "Last Friday at 3:30 PM") if reference time is within 7 days, + * or a calendar date (e.g. "10/29/2017") otherwise. + * + * Language, date and time formats will change according to the current locale. + * + * @param Carbon|\DateTimeInterface|string|null $referenceTime + * @param array $formats + * + * @return string + */ + public function calendar($referenceTime = null, array $formats = []); + + /** + * Checks if the (date)time string is in a given format and valid to create a + * new instance. + * + * @example + * ``` + * Carbon::canBeCreatedFromFormat('11:12:45', 'h:i:s'); // true + * Carbon::canBeCreatedFromFormat('13:12:45', 'h:i:s'); // false + * ``` + * + * @param string $date + * @param string $format + * + * @return bool + */ + public static function canBeCreatedFromFormat($date, $format); + + /** + * Return the Carbon instance passed through, a now instance in the same timezone + * if null given or parse the input if string given. + * + * @param Carbon|\Carbon\CarbonPeriod|\Carbon\CarbonInterval|\DateInterval|\DatePeriod|DateTimeInterface|string|null $date + * + * @return static + */ + public function carbonize($date = null); + + /** + * Cast the current instance into the given class. + * + * @param string $className The $className::instance() method will be called to cast the current object. + * + * @return DateTimeInterface + */ + public function cast(string $className); + + /** + * Ceil the current instance second with given precision if specified. + * + * @param float|int|string|\DateInterval|null $precision + * + * @return CarbonInterface + */ + public function ceil($precision = 1); + + /** + * Ceil the current instance at the given unit with given precision if specified. + * + * @param string $unit + * @param float|int $precision + * + * @return CarbonInterface + */ + public function ceilUnit($unit, $precision = 1); + + /** + * Ceil the current instance week. + * + * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week + * + * @return CarbonInterface + */ + public function ceilWeek($weekStartsAt = null); + + /** + * Similar to native modify() method of DateTime but can handle more grammars. + * + * @example + * ``` + * echo Carbon::now()->change('next 2pm'); + * ``` + * + * @link https://php.net/manual/en/datetime.modify.php + * + * @param string $modifier + * + * @return static|false + */ + public function change($modifier); + + /** + * Cleanup properties attached to the public scope of DateTime when a dump of the date is requested. + * foreach ($date as $_) {} + * serializer($date) + * var_export($date) + * get_object_vars($date) + */ + public function cleanupDumpProperties(); + + /** + * @alias copy + * + * Get a copy of the instance. + * + * @return static + */ + public function clone(); + + /** + * Get the closest date from the instance (second-precision). + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * + * @return static + */ + public function closest($date1, $date2); + + /** + * Get a copy of the instance. + * + * @return static + */ + public function copy(); + + /** + * Create a new Carbon instance from a specific date and time. + * + * If any of $year, $month or $day are set to null their now() values will + * be used. + * + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * + * If $hour is not null then the default values for $minute and $second + * will be 0. + * + * @param DateTimeInterface|int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static|false + */ + public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null); + + /** + * Create a Carbon instance from just a date. The time portion is set to now. + * + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function createFromDate($year = null, $month = null, $day = null, $tz = null); + + /** + * Create a Carbon instance from a specific format. + * + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|false|null $tz + * + * @throws InvalidFormatException + * + * @return static|false + */ + #[ReturnTypeWillChange] + public static function createFromFormat($format, $time, $tz = null); + + /** + * Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()). + * + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|false|null $tz optional timezone + * @param string|null $locale locale to be used for LTS, LT, LL, LLL, etc. macro-formats (en by fault, unneeded if no such macro-format in use) + * @param \Symfony\Component\Translation\TranslatorInterface $translator optional custom translator to use for macro-formats + * + * @throws InvalidFormatException + * + * @return static|false + */ + public static function createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null); + + /** + * Create a Carbon instance from a specific format and a string in a given language. + * + * @param string $format Datetime format + * @param string $locale + * @param string $time + * @param DateTimeZone|string|false|null $tz + * + * @throws InvalidFormatException + * + * @return static|false + */ + public static function createFromLocaleFormat($format, $locale, $time, $tz = null); + + /** + * Create a Carbon instance from a specific ISO format and a string in a given language. + * + * @param string $format Datetime ISO format + * @param string $locale + * @param string $time + * @param DateTimeZone|string|false|null $tz + * + * @throws InvalidFormatException + * + * @return static|false + */ + public static function createFromLocaleIsoFormat($format, $locale, $time, $tz = null); + + /** + * Create a Carbon instance from just a time. The date portion is set to today. + * + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null); + + /** + * Create a Carbon instance from a time string. The date portion is set to today. + * + * @param string $time + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function createFromTimeString($time, $tz = null); + + /** + * Create a Carbon instance from a timestamp and set the timezone (use default one if not specified). + * + * Timestamp input can be given as int, float or a string containing one or more numbers. + * + * @param float|int|string $timestamp + * @param \DateTimeZone|string|null $tz + * + * @return static + */ + public static function createFromTimestamp($timestamp, $tz = null); + + /** + * Create a Carbon instance from a timestamp in milliseconds. + * + * Timestamp input can be given as int, float or a string containing one or more numbers. + * + * @param float|int|string $timestamp + * @param \DateTimeZone|string|null $tz + * + * @return static + */ + public static function createFromTimestampMs($timestamp, $tz = null); + + /** + * Create a Carbon instance from a timestamp in milliseconds. + * + * Timestamp input can be given as int, float or a string containing one or more numbers. + * + * @param float|int|string $timestamp + * + * @return static + */ + public static function createFromTimestampMsUTC($timestamp); + + /** + * Create a Carbon instance from an timestamp keeping the timezone to UTC. + * + * Timestamp input can be given as int, float or a string containing one or more numbers. + * + * @param float|int|string $timestamp + * + * @return static + */ + public static function createFromTimestampUTC($timestamp); + + /** + * Create a Carbon instance from just a date. The time portion is set to midnight. + * + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function createMidnightDate($year = null, $month = null, $day = null, $tz = null); + + /** + * Create a new safe Carbon instance from a specific date and time. + * + * If any of $year, $month or $day are set to null their now() values will + * be used. + * + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * + * If $hour is not null then the default values for $minute and $second + * will be 0. + * + * If one of the set values is not valid, an InvalidDateException + * will be thrown. + * + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|null $tz + * + * @throws InvalidDateException + * + * @return static|false + */ + public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null); + + /** + * Create a new Carbon instance from a specific date and time using strict validation. + * + * @see create() + * + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null); + + /** + * Get/set the day of year. + * + * @param int|null $value new value for day of year if using as setter. + * + * @return static|int + */ + public function dayOfYear($value = null); + + /** + * Get the difference as a CarbonInterval instance. + * Return relative interval (negative if $absolute flag is not set to true and the given date is before + * current one). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return CarbonInterval + */ + public function diffAsCarbonInterval($date = null, $absolute = true, array $skip = []); + + /** + * Get the difference by the given interval using a filter closure. + * + * @param CarbonInterval $ci An interval to traverse by + * @param Closure $callback + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffFiltered(CarbonInterval $ci, Closure $callback, $date = null, $absolute = true); + + /** + * Get the difference in a human readable format in the current locale from current instance to an other + * instance given (or now if null given). + * + * @example + * ``` + * echo Carbon::tomorrow()->diffForHumans() . "\n"; + * echo Carbon::tomorrow()->diffForHumans(['parts' => 2]) . "\n"; + * echo Carbon::tomorrow()->diffForHumans(['parts' => 3, 'join' => true]) . "\n"; + * echo Carbon::tomorrow()->diffForHumans(Carbon::yesterday()) . "\n"; + * echo Carbon::tomorrow()->diffForHumans(Carbon::yesterday(), ['short' => true]) . "\n"; + * ``` + * + * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; + * if null passed, now will be used as comparison reference; + * if any other type, it will be converted to date and used as reference. + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'skip' entry, list of units to skip (array of strings or a single string, + * ` it can be the unit name (singular or plural) or its shortcut + * ` (y, m, w, d, h, min, s, ms, µs). + * - 'aUnit' entry, prefer "an hour" over "1 hour" if true + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * - 'other' entry (see above) + * - 'minimumUnit' entry determines the smallest unit of time to display can be long or + * ` short form of the units, e.g. 'hour' or 'h' (default value: s) + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single unit) + * @param int $options human diff options + * + * @return string + */ + public function diffForHumans($other = null, $syntax = null, $short = false, $parts = 1, $options = null); + + /** + * Get the difference in days rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInDays($date = null, $absolute = true); + + /** + * Get the difference in days using a filter closure rounded down. + * + * @param Closure $callback + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInDaysFiltered(Closure $callback, $date = null, $absolute = true); + + /** + * Get the difference in hours rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInHours($date = null, $absolute = true); + + /** + * Get the difference in hours using a filter closure rounded down. + * + * @param Closure $callback + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInHoursFiltered(Closure $callback, $date = null, $absolute = true); + + /** + * Get the difference in microseconds. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInMicroseconds($date = null, $absolute = true); + + /** + * Get the difference in milliseconds rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInMilliseconds($date = null, $absolute = true); + + /** + * Get the difference in minutes rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInMinutes($date = null, $absolute = true); + + /** + * Get the difference in months rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInMonths($date = null, $absolute = true); + + /** + * Get the difference in quarters rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInQuarters($date = null, $absolute = true); + + /** + * Get the difference in hours rounded down using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealHours($date = null, $absolute = true); + + /** + * Get the difference in microseconds using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealMicroseconds($date = null, $absolute = true); + + /** + * Get the difference in milliseconds rounded down using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealMilliseconds($date = null, $absolute = true); + + /** + * Get the difference in minutes rounded down using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealMinutes($date = null, $absolute = true); + + /** + * Get the difference in seconds using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealSeconds($date = null, $absolute = true); + + /** + * Get the difference in seconds rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInSeconds($date = null, $absolute = true); + + /** + * Get the difference in weekdays rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInWeekdays($date = null, $absolute = true); + + /** + * Get the difference in weekend days using a filter rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInWeekendDays($date = null, $absolute = true); + + /** + * Get the difference in weeks rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInWeeks($date = null, $absolute = true); + + /** + * Get the difference in years + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInYears($date = null, $absolute = true); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @see settings + * + * @param int $humanDiffOption + */ + public static function disableHumanDiffOption($humanDiffOption); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @see settings + * + * @param int $humanDiffOption + */ + public static function enableHumanDiffOption($humanDiffOption); + + /** + * Modify to end of current given unit. + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16.334455') + * ->startOf('month') + * ->endOf('week', Carbon::FRIDAY); + * ``` + * + * @param string $unit + * @param array $params + * + * @return static + */ + public function endOf($unit, ...$params); + + /** + * Resets the date to end of the century and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfCentury(); + * ``` + * + * @return static + */ + public function endOfCentury(); + + /** + * Resets the time to 23:59:59.999999 end of day + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfDay(); + * ``` + * + * @return static + */ + public function endOfDay(); + + /** + * Resets the date to end of the decade and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfDecade(); + * ``` + * + * @return static + */ + public function endOfDecade(); + + /** + * Modify to end of current hour, minutes and seconds become 59 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfHour(); + * ``` + * + * @return static + */ + public function endOfHour(); + + /** + * Resets the date to end of the millennium and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfMillennium(); + * ``` + * + * @return static + */ + public function endOfMillennium(); + + /** + * Modify to end of current minute, seconds become 59 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfMinute(); + * ``` + * + * @return static + */ + public function endOfMinute(); + + /** + * Resets the date to end of the month and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfMonth(); + * ``` + * + * @return static + */ + public function endOfMonth(); + + /** + * Resets the date to end of the quarter and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfQuarter(); + * ``` + * + * @return static + */ + public function endOfQuarter(); + + /** + * Modify to end of current second, microseconds become 999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16.334455') + * ->endOfSecond() + * ->format('H:i:s.u'); + * ``` + * + * @return static + */ + public function endOfSecond(); + + /** + * Resets the date to end of week (defined in $weekEndsAt) and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfWeek() . "\n"; + * echo Carbon::parse('2018-07-25 12:45:16')->locale('ar')->endOfWeek() . "\n"; + * echo Carbon::parse('2018-07-25 12:45:16')->endOfWeek(Carbon::SATURDAY) . "\n"; + * ``` + * + * @param int $weekEndsAt optional start allow you to specify the day of week to use to end the week + * + * @return static + */ + public function endOfWeek($weekEndsAt = null); + + /** + * Resets the date to end of the year and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfYear(); + * ``` + * + * @return static + */ + public function endOfYear(); + + /** + * Determines if the instance is equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->eq('2018-07-25 12:45:16'); // true + * Carbon::parse('2018-07-25 12:45:16')->eq(Carbon::parse('2018-07-25 12:45:16')); // true + * Carbon::parse('2018-07-25 12:45:16')->eq('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see equalTo() + * + * @return bool + */ + public function eq($date): bool; + + /** + * Determines if the instance is equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->equalTo('2018-07-25 12:45:16'); // true + * Carbon::parse('2018-07-25 12:45:16')->equalTo(Carbon::parse('2018-07-25 12:45:16')); // true + * Carbon::parse('2018-07-25 12:45:16')->equalTo('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function equalTo($date): bool; + + /** + * Set the current locale to the given, execute the passed function, reset the locale to previous one, + * then return the result of the closure (or null if the closure was void). + * + * @param string $locale locale ex. en + * @param callable $func + * + * @return mixed + */ + public static function executeWithLocale($locale, $func); + + /** + * Get the farthest date from the instance (second-precision). + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * + * @return static + */ + public function farthest($date1, $date2); + + /** + * Modify to the first occurrence of a given day of the week + * in the current month. If no dayOfWeek is provided, modify to the + * first day of the current month. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek + * + * @return static + */ + public function firstOfMonth($dayOfWeek = null); + + /** + * Modify to the first occurrence of a given day of the week + * in the current quarter. If no dayOfWeek is provided, modify to the + * first day of the current quarter. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek day of the week default null + * + * @return static + */ + public function firstOfQuarter($dayOfWeek = null); + + /** + * Modify to the first occurrence of a given day of the week + * in the current year. If no dayOfWeek is provided, modify to the + * first day of the current year. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek day of the week default null + * + * @return static + */ + public function firstOfYear($dayOfWeek = null); + + /** + * Get the difference in days as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInDays($date = null, $absolute = true); + + /** + * Get the difference in hours as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInHours($date = null, $absolute = true); + + /** + * Get the difference in minutes as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInMinutes($date = null, $absolute = true); + + /** + * Get the difference in months as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInMonths($date = null, $absolute = true); + + /** + * Get the difference in days as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealDays($date = null, $absolute = true); + + /** + * Get the difference in hours as float (microsecond-precision) using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealHours($date = null, $absolute = true); + + /** + * Get the difference in minutes as float (microsecond-precision) using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealMinutes($date = null, $absolute = true); + + /** + * Get the difference in months as float (microsecond-precision) using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealMonths($date = null, $absolute = true); + + /** + * Get the difference in seconds as float (microsecond-precision) using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealSeconds($date = null, $absolute = true); + + /** + * Get the difference in weeks as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealWeeks($date = null, $absolute = true); + + /** + * Get the difference in year as float (microsecond-precision) using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealYears($date = null, $absolute = true); + + /** + * Get the difference in seconds as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInSeconds($date = null, $absolute = true); + + /** + * Get the difference in weeks as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInWeeks($date = null, $absolute = true); + + /** + * Get the difference in year as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInYears($date = null, $absolute = true); + + /** + * Round the current instance second with given precision if specified. + * + * @param float|int|string|\DateInterval|null $precision + * + * @return CarbonInterface + */ + public function floor($precision = 1); + + /** + * Truncate the current instance at the given unit with given precision if specified. + * + * @param string $unit + * @param float|int $precision + * + * @return CarbonInterface + */ + public function floorUnit($unit, $precision = 1); + + /** + * Truncate the current instance week. + * + * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week + * + * @return CarbonInterface + */ + public function floorWeek($weekStartsAt = null); + + /** + * Format the instance with the current locale. You can set the current + * locale using setlocale() https://php.net/setlocale. + * + * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1. + * Use ->isoFormat() instead. + * Deprecated since 2.55.0 + * + * @param string $format + * + * @return string + */ + public function formatLocalized($format); + + /** + * @alias diffForHumans + * + * Get the difference in a human readable format in the current locale from current instance to an other + * instance given (or now if null given). + * + * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; + * if null passed, now will be used as comparison reference; + * if any other type, it will be converted to date and used as reference. + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * - 'other' entry (see above) + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single unit) + * @param int $options human diff options + * + * @return string + */ + public function from($other = null, $syntax = null, $short = false, $parts = 1, $options = null); + + /** + * Get the difference in a human readable format in the current locale from current + * instance to now. + * + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single unit) + * @param int $options human diff options + * + * @return string + */ + public function fromNow($syntax = null, $short = false, $parts = 1, $options = null); + + /** + * Create an instance from a serialized string. + * + * @param string $value + * + * @throws InvalidFormatException + * + * @return static + */ + public static function fromSerialized($value); + + /** + * Register a custom macro. + * + * @param object|callable $macro + * @param int $priority marco with higher priority is tried first + * + * @return void + */ + public static function genericMacro($macro, $priority = 0); + + /** + * Get a part of the Carbon object + * + * @param string $name + * + * @throws UnknownGetterException + * + * @return string|int|bool|DateTimeZone|null + */ + public function get($name); + + /** + * Returns the alternative number for a given date property if available in the current locale. + * + * @param string $key date property + * + * @return string + */ + public function getAltNumber(string $key): string; + + /** + * Returns the list of internally available locales and already loaded custom locales. + * (It will ignore custom translator dynamic loading.) + * + * @return array + */ + public static function getAvailableLocales(); + + /** + * Returns list of Language object for each available locale. This object allow you to get the ISO name, native + * name, region and variant of the locale. + * + * @return Language[] + */ + public static function getAvailableLocalesInfo(); + + /** + * Returns list of calendar formats for ISO formatting. + * + * @param string|null $locale current locale used if null + * + * @return array + */ + public function getCalendarFormats($locale = null); + + /** + * Get the days of the week + * + * @return array + */ + public static function getDays(); + + /** + * Return the number of days since the start of the week (using the current locale or the first parameter + * if explicitly given). + * + * @param int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week, + * if not provided, start of week is inferred from the locale + * (Sunday for en_US, Monday for de_DE, etc.) + * + * @return int + */ + public function getDaysFromStartOfWeek(?int $weekStartsAt = null): int; + + /** + * Get the fallback locale. + * + * @see https://symfony.com/doc/current/components/translation.html#fallback-locales + * + * @return string|null + */ + public static function getFallbackLocale(); + + /** + * List of replacements from date() format to isoFormat(). + * + * @return array + */ + public static function getFormatsToIsoReplacements(); + + /** + * Return default humanDiff() options (merged flags as integer). + * + * @return int + */ + public static function getHumanDiffOptions(); + + /** + * Returns list of locale formats for ISO formatting. + * + * @param string|null $locale current locale used if null + * + * @return array + */ + public function getIsoFormats($locale = null); + + /** + * Returns list of locale units for ISO formatting. + * + * @return array + */ + public static function getIsoUnits(); + + /** + * {@inheritdoc} + * + * @return array + */ + #[ReturnTypeWillChange] + public static function getLastErrors(); + + /** + * Get the raw callable macro registered globally or locally for a given name. + * + * @param string $name + * + * @return callable|null + */ + public function getLocalMacro($name); + + /** + * Get the translator of the current instance or the default if none set. + * + * @return \Symfony\Component\Translation\TranslatorInterface + */ + public function getLocalTranslator(); + + /** + * Get the current translator locale. + * + * @return string + */ + public static function getLocale(); + + /** + * Get the raw callable macro registered globally for a given name. + * + * @param string $name + * + * @return callable|null + */ + public static function getMacro($name); + + /** + * get midday/noon hour + * + * @return int + */ + public static function getMidDayAt(); + + /** + * Returns the offset hour and minute formatted with +/- and a given separator (":" by default). + * For example, if the time zone is 9 hours 30 minutes, you'll get "+09:30", with "@@" as first + * argument, "+09@@30", with "" as first argument, "+0930". Negative offset will return something + * like "-12:00". + * + * @param string $separator string to place between hours and minutes (":" by default) + * + * @return string + */ + public function getOffsetString($separator = ':'); + + /** + * Returns a unit of the instance padded with 0 by default or any other string if specified. + * + * @param string $unit Carbon unit name + * @param int $length Length of the output (2 by default) + * @param string $padString String to use for padding ("0" by default) + * @param int $padType Side(s) to pad (STR_PAD_LEFT by default) + * + * @return string + */ + public function getPaddedUnit($unit, $length = 2, $padString = '0', $padType = 0); + + /** + * Returns a timestamp rounded with the given precision (6 by default). + * + * @example getPreciseTimestamp() 1532087464437474 (microsecond maximum precision) + * @example getPreciseTimestamp(6) 1532087464437474 + * @example getPreciseTimestamp(5) 153208746443747 (1/100000 second precision) + * @example getPreciseTimestamp(4) 15320874644375 (1/10000 second precision) + * @example getPreciseTimestamp(3) 1532087464437 (millisecond precision) + * @example getPreciseTimestamp(2) 153208746444 (1/100 second precision) + * @example getPreciseTimestamp(1) 15320874644 (1/10 second precision) + * @example getPreciseTimestamp(0) 1532087464 (second precision) + * @example getPreciseTimestamp(-1) 153208746 (10 second precision) + * @example getPreciseTimestamp(-2) 15320875 (100 second precision) + * + * @param int $precision + * + * @return float + */ + public function getPreciseTimestamp($precision = 6); + + /** + * Returns current local settings. + * + * @return array + */ + public function getSettings(); + + /** + * Get the Carbon instance (real or mock) to be returned when a "now" + * instance is created. + * + * @return Closure|static the current instance used for testing + */ + public static function getTestNow(); + + /** + * Return a format from H:i to H:i:s.u according to given unit precision. + * + * @param string $unitPrecision "minute", "second", "millisecond" or "microsecond" + * + * @return string + */ + public static function getTimeFormatByPrecision($unitPrecision); + + /** + * Returns the timestamp with millisecond precision. + * + * @return int + */ + public function getTimestampMs(); + + /** + * Get the translation of the current week day name (with context for languages with multiple forms). + * + * @param string|null $context whole format string + * @param string $keySuffix "", "_short" or "_min" + * @param string|null $defaultValue default value if translation missing + * + * @return string + */ + public function getTranslatedDayName($context = null, $keySuffix = '', $defaultValue = null); + + /** + * Get the translation of the current abbreviated week day name (with context for languages with multiple forms). + * + * @param string|null $context whole format string + * + * @return string + */ + public function getTranslatedMinDayName($context = null); + + /** + * Get the translation of the current month day name (with context for languages with multiple forms). + * + * @param string|null $context whole format string + * @param string $keySuffix "" or "_short" + * @param string|null $defaultValue default value if translation missing + * + * @return string + */ + public function getTranslatedMonthName($context = null, $keySuffix = '', $defaultValue = null); + + /** + * Get the translation of the current short week day name (with context for languages with multiple forms). + * + * @param string|null $context whole format string + * + * @return string + */ + public function getTranslatedShortDayName($context = null); + + /** + * Get the translation of the current short month day name (with context for languages with multiple forms). + * + * @param string|null $context whole format string + * + * @return string + */ + public function getTranslatedShortMonthName($context = null); + + /** + * Returns raw translation message for a given key. + * + * @param string $key key to find + * @param string|null $locale current locale used if null + * @param string|null $default default value if translation returns the key + * @param \Symfony\Component\Translation\TranslatorInterface $translator an optional translator to use + * + * @return string + */ + public function getTranslationMessage(string $key, ?string $locale = null, ?string $default = null, $translator = null); + + /** + * Returns raw translation message for a given key. + * + * @param \Symfony\Component\Translation\TranslatorInterface $translator the translator to use + * @param string $key key to find + * @param string|null $locale current locale used if null + * @param string|null $default default value if translation returns the key + * + * @return string + */ + public static function getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null); + + /** + * Get the default translator instance in use. + * + * @return \Symfony\Component\Translation\TranslatorInterface + */ + public static function getTranslator(); + + /** + * Get the last day of week + * + * @return int + */ + public static function getWeekEndsAt(); + + /** + * Get the first day of week + * + * @return int + */ + public static function getWeekStartsAt(); + + /** + * Get weekend days + * + * @return array + */ + public static function getWeekendDays(); + + /** + * Determines if the instance is greater (after) than another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:15'); // true + * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function greaterThan($date): bool; + + /** + * Determines if the instance is greater (after) than or equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:15'); // true + * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:16'); // true + * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function greaterThanOrEqualTo($date): bool; + + /** + * Determines if the instance is greater (after) than another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:15'); // true + * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see greaterThan() + * + * @return bool + */ + public function gt($date): bool; + + /** + * Determines if the instance is greater (after) than or equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:15'); // true + * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:16'); // true + * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see greaterThanOrEqualTo() + * + * @return bool + */ + public function gte($date): bool; + + /** + * Checks if the (date)time string is in a given format. + * + * @example + * ``` + * Carbon::hasFormat('11:12:45', 'h:i:s'); // true + * Carbon::hasFormat('13:12:45', 'h:i:s'); // false + * ``` + * + * @param string $date + * @param string $format + * + * @return bool + */ + public static function hasFormat($date, $format); + + /** + * Checks if the (date)time string is in a given format. + * + * @example + * ``` + * Carbon::hasFormatWithModifiers('31/08/2015', 'd#m#Y'); // true + * Carbon::hasFormatWithModifiers('31/08/2015', 'm#d#Y'); // false + * ``` + * + * @param string $date + * @param string $format + * + * @return bool + */ + public static function hasFormatWithModifiers($date, $format): bool; + + /** + * Checks if macro is registered globally or locally. + * + * @param string $name + * + * @return bool + */ + public function hasLocalMacro($name); + + /** + * Return true if the current instance has its own translator. + * + * @return bool + */ + public function hasLocalTranslator(); + + /** + * Checks if macro is registered globally. + * + * @param string $name + * + * @return bool + */ + public static function hasMacro($name); + + /** + * Determine if a time string will produce a relative date. + * + * @param string $time + * + * @return bool true if time match a relative date, false if absolute or invalid time string + */ + public static function hasRelativeKeywords($time); + + /** + * Determine if there is a valid test instance set. A valid test instance + * is anything that is not null. + * + * @return bool true if there is a test instance, otherwise false + */ + public static function hasTestNow(); + + /** + * Create a Carbon instance from a DateTime one. + * + * @param DateTimeInterface $date + * + * @return static + */ + public static function instance($date); + + /** + * Returns true if the current date matches the given string. + * + * @example + * ``` + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2018')); // false + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019-06')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('06-02')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019-06-02')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('Sunday')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('June')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23:45')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23:00')); // false + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12h')); // true + * var_dump(Carbon::parse('2019-06-02 15:23:45')->is('3pm')); // true + * var_dump(Carbon::parse('2019-06-02 15:23:45')->is('3am')); // false + * ``` + * + * @param string $tester day name, month name, hour, date, etc. as string + * + * @return bool + */ + public function is(string $tester); + + /** + * Determines if the instance is greater (after) than another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:15'); // true + * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see greaterThan() + * + * @return bool + */ + public function isAfter($date): bool; + + /** + * Determines if the instance is less (before) than another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:15'); // false + * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see lessThan() + * + * @return bool + */ + public function isBefore($date): bool; + + /** + * Determines if the instance is between two others + * + * @example + * ``` + * Carbon::parse('2018-07-25')->isBetween('2018-07-14', '2018-08-01'); // true + * Carbon::parse('2018-07-25')->isBetween('2018-08-01', '2018-08-20'); // false + * Carbon::parse('2018-07-25')->isBetween('2018-07-25', '2018-08-01'); // true + * Carbon::parse('2018-07-25')->isBetween('2018-07-25', '2018-08-01', false); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * @param bool $equal Indicates if an equal to comparison should be done + * + * @return bool + */ + public function isBetween($date1, $date2, $equal = true): bool; + + /** + * Check if its the birthday. Compares the date/month values of the two dates. + * + * @example + * ``` + * Carbon::now()->subYears(5)->isBirthday(); // true + * Carbon::now()->subYears(5)->subDay()->isBirthday(); // false + * Carbon::parse('2019-06-05')->isBirthday(Carbon::parse('2001-06-05')); // true + * Carbon::parse('2019-06-05')->isBirthday(Carbon::parse('2001-06-06')); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day. + * + * @return bool + */ + public function isBirthday($date = null); + + /** + * Determines if the instance is in the current unit given. + * + * @example + * ``` + * Carbon::now()->isCurrentUnit('hour'); // true + * Carbon::now()->subHours(2)->isCurrentUnit('hour'); // false + * ``` + * + * @param string $unit The unit to test. + * + * @throws BadMethodCallException + * + * @return bool + */ + public function isCurrentUnit($unit); + + /** + * Checks if this day is a specific day of the week. + * + * @example + * ``` + * Carbon::parse('2019-07-17')->isDayOfWeek(Carbon::WEDNESDAY); // true + * Carbon::parse('2019-07-17')->isDayOfWeek(Carbon::FRIDAY); // false + * Carbon::parse('2019-07-17')->isDayOfWeek('Wednesday'); // true + * Carbon::parse('2019-07-17')->isDayOfWeek('Friday'); // false + * ``` + * + * @param int $dayOfWeek + * + * @return bool + */ + public function isDayOfWeek($dayOfWeek); + + /** + * Check if the instance is end of day. + * + * @example + * ``` + * Carbon::parse('2019-02-28 23:59:59.999999')->isEndOfDay(); // true + * Carbon::parse('2019-02-28 23:59:59.123456')->isEndOfDay(); // true + * Carbon::parse('2019-02-28 23:59:59')->isEndOfDay(); // true + * Carbon::parse('2019-02-28 23:59:58.999999')->isEndOfDay(); // false + * Carbon::parse('2019-02-28 23:59:59.999999')->isEndOfDay(true); // true + * Carbon::parse('2019-02-28 23:59:59.123456')->isEndOfDay(true); // false + * Carbon::parse('2019-02-28 23:59:59')->isEndOfDay(true); // false + * ``` + * + * @param bool $checkMicroseconds check time at microseconds precision + * + * @return bool + */ + public function isEndOfDay($checkMicroseconds = false); + + /** + * Returns true if the date was created using CarbonImmutable::endOfTime() + * + * @return bool + */ + public function isEndOfTime(): bool; + + /** + * Determines if the instance is in the future, ie. greater (after) than now. + * + * @example + * ``` + * Carbon::now()->addHours(5)->isFuture(); // true + * Carbon::now()->subHours(5)->isFuture(); // false + * ``` + * + * @return bool + */ + public function isFuture(); + + /** + * Returns true if the current class/instance is immutable. + * + * @return bool + */ + public static function isImmutable(); + + /** + * Check if today is the last day of the Month + * + * @example + * ``` + * Carbon::parse('2019-02-28')->isLastOfMonth(); // true + * Carbon::parse('2019-03-28')->isLastOfMonth(); // false + * Carbon::parse('2019-03-30')->isLastOfMonth(); // false + * Carbon::parse('2019-03-31')->isLastOfMonth(); // true + * Carbon::parse('2019-04-30')->isLastOfMonth(); // true + * ``` + * + * @return bool + */ + public function isLastOfMonth(); + + /** + * Determines if the instance is a leap year. + * + * @example + * ``` + * Carbon::parse('2020-01-01')->isLeapYear(); // true + * Carbon::parse('2019-01-01')->isLeapYear(); // false + * ``` + * + * @return bool + */ + public function isLeapYear(); + + /** + * Determines if the instance is a long year (using ISO 8601 year). + * + * @example + * ``` + * Carbon::parse('2015-01-01')->isLongIsoYear(); // true + * Carbon::parse('2016-01-01')->isLongIsoYear(); // true + * Carbon::parse('2016-01-03')->isLongIsoYear(); // false + * Carbon::parse('2019-12-29')->isLongIsoYear(); // false + * Carbon::parse('2019-12-30')->isLongIsoYear(); // true + * ``` + * + * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates + * + * @return bool + */ + public function isLongIsoYear(); + + /** + * Determines if the instance is a long year (using calendar year). + * + * ⚠️ This method completely ignores month and day to use the numeric year number, + * it's not correct if the exact date matters. For instance as `2019-12-30` is already + * in the first week of the 2020 year, if you want to know from this date if ISO week + * year 2020 is a long year, use `isLongIsoYear` instead. + * + * @example + * ``` + * Carbon::create(2015)->isLongYear(); // true + * Carbon::create(2016)->isLongYear(); // false + * ``` + * + * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates + * + * @return bool + */ + public function isLongYear(); + + /** + * Check if the instance is midday. + * + * @example + * ``` + * Carbon::parse('2019-02-28 11:59:59.999999')->isMidday(); // false + * Carbon::parse('2019-02-28 12:00:00')->isMidday(); // true + * Carbon::parse('2019-02-28 12:00:00.999999')->isMidday(); // true + * Carbon::parse('2019-02-28 12:00:01')->isMidday(); // false + * ``` + * + * @return bool + */ + public function isMidday(); + + /** + * Check if the instance is start of day / midnight. + * + * @example + * ``` + * Carbon::parse('2019-02-28 00:00:00')->isMidnight(); // true + * Carbon::parse('2019-02-28 00:00:00.999999')->isMidnight(); // true + * Carbon::parse('2019-02-28 00:00:01')->isMidnight(); // false + * ``` + * + * @return bool + */ + public function isMidnight(); + + /** + * Returns true if a property can be changed via setter. + * + * @param string $unit + * + * @return bool + */ + public static function isModifiableUnit($unit); + + /** + * Returns true if the current class/instance is mutable. + * + * @return bool + */ + public static function isMutable(); + + /** + * Determines if the instance is in the past, ie. less (before) than now. + * + * @example + * ``` + * Carbon::now()->subHours(5)->isPast(); // true + * Carbon::now()->addHours(5)->isPast(); // false + * ``` + * + * @return bool + */ + public function isPast(); + + /** + * Compares the formatted values of the two dates. + * + * @example + * ``` + * Carbon::parse('2019-06-13')->isSameAs('Y-d', Carbon::parse('2019-12-13')); // true + * Carbon::parse('2019-06-13')->isSameAs('Y-d', Carbon::parse('2019-06-14')); // false + * ``` + * + * @param string $format date formats to compare. + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date instance to compare with or null to use current day. + * + * @return bool + */ + public function isSameAs($format, $date = null); + + /** + * Checks if the passed in date is in the same month as the instance´s month. + * + * @example + * ``` + * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2019-01-01')); // true + * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2019-02-01')); // false + * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2018-01-01')); // false + * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2018-01-01'), false); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date. + * @param bool $ofSameYear Check if it is the same month in the same year. + * + * @return bool + */ + public function isSameMonth($date = null, $ofSameYear = true); + + /** + * Checks if the passed in date is in the same quarter as the instance quarter (and year if needed). + * + * @example + * ``` + * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2019-03-01')); // true + * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2019-04-01')); // false + * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2018-03-01')); // false + * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2018-03-01'), false); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date The instance to compare with or null to use current day. + * @param bool $ofSameYear Check if it is the same month in the same year. + * + * @return bool + */ + public function isSameQuarter($date = null, $ofSameYear = true); + + /** + * Determines if the instance is in the current unit given. + * + * @example + * ``` + * Carbon::parse('2019-01-13')->isSameUnit('year', Carbon::parse('2019-12-25')); // true + * Carbon::parse('2018-12-13')->isSameUnit('year', Carbon::parse('2019-12-25')); // false + * ``` + * + * @param string $unit singular unit string + * @param \Carbon\Carbon|\DateTimeInterface|null $date instance to compare with or null to use current day. + * + * @throws BadComparisonUnitException + * + * @return bool + */ + public function isSameUnit($unit, $date = null); + + /** + * Check if the instance is start of day / midnight. + * + * @example + * ``` + * Carbon::parse('2019-02-28 00:00:00')->isStartOfDay(); // true + * Carbon::parse('2019-02-28 00:00:00.999999')->isStartOfDay(); // true + * Carbon::parse('2019-02-28 00:00:01')->isStartOfDay(); // false + * Carbon::parse('2019-02-28 00:00:00.000000')->isStartOfDay(true); // true + * Carbon::parse('2019-02-28 00:00:00.000012')->isStartOfDay(true); // false + * ``` + * + * @param bool $checkMicroseconds check time at microseconds precision + * + * @return bool + */ + public function isStartOfDay($checkMicroseconds = false); + + /** + * Returns true if the date was created using CarbonImmutable::startOfTime() + * + * @return bool + */ + public function isStartOfTime(): bool; + + /** + * Returns true if the strict mode is globally in use, false else. + * (It can be overridden in specific instances.) + * + * @return bool + */ + public static function isStrictModeEnabled(); + + /** + * Determines if the instance is today. + * + * @example + * ``` + * Carbon::today()->isToday(); // true + * Carbon::tomorrow()->isToday(); // false + * ``` + * + * @return bool + */ + public function isToday(); + + /** + * Determines if the instance is tomorrow. + * + * @example + * ``` + * Carbon::tomorrow()->isTomorrow(); // true + * Carbon::yesterday()->isTomorrow(); // false + * ``` + * + * @return bool + */ + public function isTomorrow(); + + /** + * Determines if the instance is a weekday. + * + * @example + * ``` + * Carbon::parse('2019-07-14')->isWeekday(); // false + * Carbon::parse('2019-07-15')->isWeekday(); // true + * ``` + * + * @return bool + */ + public function isWeekday(); + + /** + * Determines if the instance is a weekend day. + * + * @example + * ``` + * Carbon::parse('2019-07-14')->isWeekend(); // true + * Carbon::parse('2019-07-15')->isWeekend(); // false + * ``` + * + * @return bool + */ + public function isWeekend(); + + /** + * Determines if the instance is yesterday. + * + * @example + * ``` + * Carbon::yesterday()->isYesterday(); // true + * Carbon::tomorrow()->isYesterday(); // false + * ``` + * + * @return bool + */ + public function isYesterday(); + + /** + * Format in the current language using ISO replacement patterns. + * + * @param string $format + * @param string|null $originalFormat provide context if a chunk has been passed alone + * + * @return string + */ + public function isoFormat(string $format, ?string $originalFormat = null): string; + + /** + * Get/set the week number using given first day of week and first + * day of year included in the first week. Or use ISO format if no settings + * given. + * + * @param int|null $week + * @param int|null $dayOfWeek + * @param int|null $dayOfYear + * + * @return int|static + */ + public function isoWeek($week = null, $dayOfWeek = null, $dayOfYear = null); + + /** + * Set/get the week number of year using given first day of week and first + * day of year included in the first week. Or use ISO format if no settings + * given. + * + * @param int|null $year if null, act as a getter, if not null, set the year and return current instance. + * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday) + * @param int|null $dayOfYear first day of year included in the week #1 + * + * @return int|static + */ + public function isoWeekYear($year = null, $dayOfWeek = null, $dayOfYear = null); + + /** + * Get/set the ISO weekday from 1 (Monday) to 7 (Sunday). + * + * @param int|null $value new value for weekday if using as setter. + * + * @return static|int + */ + public function isoWeekday($value = null); + + /** + * Get the number of weeks of the current week-year using given first day of week and first + * day of year included in the first week. Or use ISO format if no settings + * given. + * + * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday) + * @param int|null $dayOfYear first day of year included in the week #1 + * + * @return int + */ + public function isoWeeksInYear($dayOfWeek = null, $dayOfYear = null); + + /** + * Prepare the object for JSON serialization. + * + * @return array|string + */ + #[ReturnTypeWillChange] + public function jsonSerialize(); + + /** + * Modify to the last occurrence of a given day of the week + * in the current month. If no dayOfWeek is provided, modify to the + * last day of the current month. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek + * + * @return static + */ + public function lastOfMonth($dayOfWeek = null); + + /** + * Modify to the last occurrence of a given day of the week + * in the current quarter. If no dayOfWeek is provided, modify to the + * last day of the current quarter. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek day of the week default null + * + * @return static + */ + public function lastOfQuarter($dayOfWeek = null); + + /** + * Modify to the last occurrence of a given day of the week + * in the current year. If no dayOfWeek is provided, modify to the + * last day of the current year. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek day of the week default null + * + * @return static + */ + public function lastOfYear($dayOfWeek = null); + + /** + * Determines if the instance is less (before) than another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:15'); // false + * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function lessThan($date): bool; + + /** + * Determines if the instance is less (before) or equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:15'); // false + * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:16'); // true + * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function lessThanOrEqualTo($date): bool; + + /** + * Get/set the locale for the current instance. + * + * @param string|null $locale + * @param string ...$fallbackLocales + * + * @return $this|string + */ + public function locale(?string $locale = null, ...$fallbackLocales); + + /** + * Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow). + * Support is considered enabled if the 3 words are translated in the given locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasDiffOneDayWords($locale); + + /** + * Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasDiffSyntax($locale); + + /** + * Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow). + * Support is considered enabled if the 2 words are translated in the given locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasDiffTwoDayWords($locale); + + /** + * Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasPeriodSyntax($locale); + + /** + * Returns true if the given locale is internally supported and has short-units support. + * Support is considered enabled if either year, day or hour has a short variant translated. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasShortUnits($locale); + + /** + * Determines if the instance is less (before) than another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:15'); // false + * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see lessThan() + * + * @return bool + */ + public function lt($date): bool; + + /** + * Determines if the instance is less (before) or equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:15'); // false + * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:16'); // true + * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see lessThanOrEqualTo() + * + * @return bool + */ + public function lte($date): bool; + + /** + * Register a custom macro. + * + * @example + * ``` + * $userSettings = [ + * 'locale' => 'pt', + * 'timezone' => 'America/Sao_Paulo', + * ]; + * Carbon::macro('userFormat', function () use ($userSettings) { + * return $this->copy()->locale($userSettings['locale'])->tz($userSettings['timezone'])->calendar(); + * }); + * echo Carbon::yesterday()->hours(11)->userFormat(); + * ``` + * + * @param string $name + * @param object|callable $macro + * + * @return void + */ + public static function macro($name, $macro); + + /** + * Make a Carbon instance from given variable if possible. + * + * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals + * and recurrences). Throw an exception for invalid format, but otherwise return null. + * + * @param mixed $var + * + * @throws InvalidFormatException + * + * @return static|null + */ + public static function make($var); + + /** + * Get the maximum instance between a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return static + */ + public function max($date = null); + + /** + * Create a Carbon instance for the greatest supported date. + * + * @return static + */ + public static function maxValue(); + + /** + * Get the maximum instance between a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see max() + * + * @return static + */ + public function maximum($date = null); + + /** + * Return the meridiem of the current time in the current locale. + * + * @param bool $isLower if true, returns lowercase variant if available in the current locale. + * + * @return string + */ + public function meridiem(bool $isLower = false): string; + + /** + * Modify to midday, default to self::$midDayAt + * + * @return static + */ + public function midDay(); + + /** + * Get the minimum instance between a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return static + */ + public function min($date = null); + + /** + * Create a Carbon instance for the lowest supported date. + * + * @return static + */ + public static function minValue(); + + /** + * Get the minimum instance between a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see min() + * + * @return static + */ + public function minimum($date = null); + + /** + * Mix another object into the class. + * + * @example + * ``` + * Carbon::mixin(new class { + * public function addMoon() { + * return function () { + * return $this->addDays(30); + * }; + * } + * public function subMoon() { + * return function () { + * return $this->subDays(30); + * }; + * } + * }); + * $fullMoon = Carbon::create('2018-12-22'); + * $nextFullMoon = $fullMoon->addMoon(); + * $blackMoon = Carbon::create('2019-01-06'); + * $previousBlackMoon = $blackMoon->subMoon(); + * echo "$nextFullMoon\n"; + * echo "$previousBlackMoon\n"; + * ``` + * + * @param object|string $mixin + * + * @throws ReflectionException + * + * @return void + */ + public static function mixin($mixin); + + /** + * Calls \DateTime::modify if mutable or \DateTimeImmutable::modify else. + * + * @see https://php.net/manual/en/datetime.modify.php + * + * @return static|false + */ + #[ReturnTypeWillChange] + public function modify($modify); + + /** + * Determines if the instance is not equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->ne('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->ne(Carbon::parse('2018-07-25 12:45:16')); // false + * Carbon::parse('2018-07-25 12:45:16')->ne('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see notEqualTo() + * + * @return bool + */ + public function ne($date): bool; + + /** + * Modify to the next occurrence of a given modifier such as a day of + * the week. If no modifier is provided, modify to the next occurrence + * of the current day of the week. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param string|int|null $modifier + * + * @return static|false + */ + public function next($modifier = null); + + /** + * Go forward to the next weekday. + * + * @return static + */ + public function nextWeekday(); + + /** + * Go forward to the next weekend day. + * + * @return static + */ + public function nextWeekendDay(); + + /** + * Determines if the instance is not equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->notEqualTo('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->notEqualTo(Carbon::parse('2018-07-25 12:45:16')); // false + * Carbon::parse('2018-07-25 12:45:16')->notEqualTo('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function notEqualTo($date): bool; + + /** + * Get a Carbon instance for the current date and time. + * + * @param DateTimeZone|string|null $tz + * + * @return static + */ + public static function now($tz = null); + + /** + * Returns a present instance in the same timezone. + * + * @return static + */ + public function nowWithSameTz(); + + /** + * Modify to the given occurrence of a given day of the week + * in the current month. If the calculated occurrence is outside the scope + * of the current month, then return false and no modifications are made. + * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int $nth + * @param int $dayOfWeek + * + * @return mixed + */ + public function nthOfMonth($nth, $dayOfWeek); + + /** + * Modify to the given occurrence of a given day of the week + * in the current quarter. If the calculated occurrence is outside the scope + * of the current quarter, then return false and no modifications are made. + * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int $nth + * @param int $dayOfWeek + * + * @return mixed + */ + public function nthOfQuarter($nth, $dayOfWeek); + + /** + * Modify to the given occurrence of a given day of the week + * in the current year. If the calculated occurrence is outside the scope + * of the current year, then return false and no modifications are made. + * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int $nth + * @param int $dayOfWeek + * + * @return mixed + */ + public function nthOfYear($nth, $dayOfWeek); + + /** + * Return a property with its ordinal. + * + * @param string $key + * @param string|null $period + * + * @return string + */ + public function ordinal(string $key, ?string $period = null): string; + + /** + * Create a carbon instance from a string. + * + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * + * @param string|DateTimeInterface|null $time + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function parse($time = null, $tz = null); + + /** + * Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.). + * + * @param string $time date/time string in the given language (may also contain English). + * @param string|null $locale if locale is null or not specified, current global locale will be + * used instead. + * @param DateTimeZone|string|null $tz optional timezone for the new instance. + * + * @throws InvalidFormatException + * + * @return static + */ + public static function parseFromLocale($time, $locale = null, $tz = null); + + /** + * Returns standardized plural of a given singular/plural unit name (in English). + * + * @param string $unit + * + * @return string + */ + public static function pluralUnit(string $unit): string; + + /** + * Modify to the previous occurrence of a given modifier such as a day of + * the week. If no dayOfWeek is provided, modify to the previous occurrence + * of the current day of the week. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param string|int|null $modifier + * + * @return static|false + */ + public function previous($modifier = null); + + /** + * Go backward to the previous weekday. + * + * @return static + */ + public function previousWeekday(); + + /** + * Go backward to the previous weekend day. + * + * @return static + */ + public function previousWeekendDay(); + + /** + * Create a iterable CarbonPeriod object from current date to a given end date (and optional interval). + * + * @param \DateTimeInterface|Carbon|CarbonImmutable|null $end period end date + * @param int|\DateInterval|string|null $interval period default interval or number of the given $unit + * @param string|null $unit if specified, $interval must be an integer + * + * @return CarbonPeriod + */ + public function range($end = null, $interval = null, $unit = null); + + /** + * Call native PHP DateTime/DateTimeImmutable add() method. + * + * @param DateInterval $interval + * + * @return static + */ + public function rawAdd(DateInterval $interval); + + /** + * Create a Carbon instance from a specific format. + * + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|false|null $tz + * + * @throws InvalidFormatException + * + * @return static|false + */ + public static function rawCreateFromFormat($format, $time, $tz = null); + + /** + * @see https://php.net/manual/en/datetime.format.php + * + * @param string $format + * + * @return string + */ + public function rawFormat($format); + + /** + * Create a carbon instance from a string. + * + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * + * @param string|DateTimeInterface|null $time + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function rawParse($time = null, $tz = null); + + /** + * Call native PHP DateTime/DateTimeImmutable sub() method. + * + * @param DateInterval $interval + * + * @return static + */ + public function rawSub(DateInterval $interval); + + /** + * Remove all macros and generic macros. + */ + public static function resetMacros(); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @see settings + * + * Reset the month overflow behavior. + * + * @return void + */ + public static function resetMonthsOverflow(); + + /** + * Reset the format used to the default when type juggling a Carbon instance to a string + * + * @return void + */ + public static function resetToStringFormat(); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @see settings + * + * Reset the month overflow behavior. + * + * @return void + */ + public static function resetYearsOverflow(); + + /** + * Round the current instance second with given precision if specified. + * + * @param float|int|string|\DateInterval|null $precision + * @param string $function + * + * @return CarbonInterface + */ + public function round($precision = 1, $function = 'round'); + + /** + * Round the current instance at the given unit with given precision if specified and the given function. + * + * @param string $unit + * @param float|int $precision + * @param string $function + * + * @return CarbonInterface + */ + public function roundUnit($unit, $precision = 1, $function = 'round'); + + /** + * Round the current instance week. + * + * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week + * + * @return CarbonInterface + */ + public function roundWeek($weekStartsAt = null); + + /** + * The number of seconds since midnight. + * + * @return int + */ + public function secondsSinceMidnight(); + + /** + * The number of seconds until 23:59:59. + * + * @return int + */ + public function secondsUntilEndOfDay(); + + /** + * Return a serialized string of the instance. + * + * @return string + */ + public function serialize(); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather transform Carbon object before the serialization. + * + * JSON serialize all Carbon instances using the given callback. + * + * @param callable $callback + * + * @return void + */ + public static function serializeUsing($callback); + + /** + * Set a part of the Carbon object + * + * @param string|array $name + * @param string|int|DateTimeZone $value + * + * @throws ImmutableException|UnknownSetterException + * + * @return $this + */ + public function set($name, $value = null); + + /** + * Set the date with gregorian year, month and day numbers. + * + * @see https://php.net/manual/en/datetime.setdate.php + * + * @param int $year + * @param int $month + * @param int $day + * + * @return static + */ + #[ReturnTypeWillChange] + public function setDate($year, $month, $day); + + /** + * Set the year, month, and date for this instance to that of the passed instance. + * + * @param Carbon|DateTimeInterface $date now if null + * + * @return static + */ + public function setDateFrom($date = null); + + /** + * Set the date and time all together. + * + * @param int $year + * @param int $month + * @param int $day + * @param int $hour + * @param int $minute + * @param int $second + * @param int $microseconds + * + * @return static + */ + public function setDateTime($year, $month, $day, $hour, $minute, $second = 0, $microseconds = 0); + + /** + * Set the date and time for this instance to that of the passed instance. + * + * @param Carbon|DateTimeInterface $date + * + * @return static + */ + public function setDateTimeFrom($date = null); + + /** + * Set the day (keeping the current time) to the start of the week + the number of days passed as the first + * parameter. First day of week is driven by the locale unless explicitly set with the second parameter. + * + * @param int $numberOfDays number of days to add after the start of the current week + * @param int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week, + * if not provided, start of week is inferred from the locale + * (Sunday for en_US, Monday for de_DE, etc.) + * + * @return static + */ + public function setDaysFromStartOfWeek(int $numberOfDays, ?int $weekStartsAt = null); + + /** + * Set the fallback locale. + * + * @see https://symfony.com/doc/current/components/translation.html#fallback-locales + * + * @param string $locale + */ + public static function setFallbackLocale($locale); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @see settings + * + * @param int $humanDiffOptions + */ + public static function setHumanDiffOptions($humanDiffOptions); + + /** + * Set a date according to the ISO 8601 standard - using weeks and day offsets rather than specific dates. + * + * @see https://php.net/manual/en/datetime.setisodate.php + * + * @param int $year + * @param int $week + * @param int $day + * + * @return static + */ + #[ReturnTypeWillChange] + public function setISODate($year, $week, $day = 1); + + /** + * Set the translator for the current instance. + * + * @param \Symfony\Component\Translation\TranslatorInterface $translator + * + * @return $this + */ + public function setLocalTranslator(TranslatorInterface $translator); + + /** + * Set the current translator locale and indicate if the source locale file exists. + * Pass 'auto' as locale to use closest language from the current LC_TIME locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function setLocale($locale); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather consider mid-day is always 12pm, then if you need to test if it's an other + * hour, test it explicitly: + * $date->format('G') == 13 + * or to set explicitly to a given hour: + * $date->setTime(13, 0, 0, 0) + * + * Set midday/noon hour + * + * @param int $hour midday hour + * + * @return void + */ + public static function setMidDayAt($hour); + + /** + * Set a Carbon instance (real or mock) to be returned when a "now" + * instance is created. The provided instance will be returned + * specifically under the following conditions: + * - A call to the static now() method, ex. Carbon::now() + * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) + * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') + * - When a string containing the desired time is passed to Carbon::parse(). + * + * Note the timezone parameter was left out of the examples above and + * has no affect as the mock value will be returned regardless of its value. + * + * Only the moment is mocked with setTestNow(), the timezone will still be the one passed + * as parameter of date_default_timezone_get() as a fallback (see setTestNowAndTimezone()). + * + * To clear the test instance call this method using the default + * parameter of null. + * + * /!\ Use this method for unit tests only. + * + * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance + */ + public static function setTestNow($testNow = null); + + /** + * Set a Carbon instance (real or mock) to be returned when a "now" + * instance is created. The provided instance will be returned + * specifically under the following conditions: + * - A call to the static now() method, ex. Carbon::now() + * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) + * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') + * - When a string containing the desired time is passed to Carbon::parse(). + * + * It will also align default timezone (e.g. call date_default_timezone_set()) with + * the second argument or if null, with the timezone of the given date object. + * + * To clear the test instance call this method using the default + * parameter of null. + * + * /!\ Use this method for unit tests only. + * + * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance + */ + public static function setTestNowAndTimezone($testNow = null, $tz = null); + + /** + * Resets the current time of the DateTime object to a different time. + * + * @see https://php.net/manual/en/datetime.settime.php + * + * @param int $hour + * @param int $minute + * @param int $second + * @param int $microseconds + * + * @return static + */ + #[ReturnTypeWillChange] + public function setTime($hour, $minute, $second = 0, $microseconds = 0); + + /** + * Set the hour, minute, second and microseconds for this instance to that of the passed instance. + * + * @param Carbon|DateTimeInterface $date now if null + * + * @return static + */ + public function setTimeFrom($date = null); + + /** + * Set the time by time string. + * + * @param string $time + * + * @return static + */ + public function setTimeFromTimeString($time); + + /** + * Set the instance's timestamp. + * + * Timestamp input can be given as int, float or a string containing one or more numbers. + * + * @param float|int|string $unixTimestamp + * + * @return static + */ + #[ReturnTypeWillChange] + public function setTimestamp($unixTimestamp); + + /** + * Set the instance's timezone from a string or object. + * + * @param DateTimeZone|string $value + * + * @return static + */ + #[ReturnTypeWillChange] + public function setTimezone($value); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather let Carbon object being cast to string with DEFAULT_TO_STRING_FORMAT, and + * use other method or custom format passed to format() method if you need to dump another string + * format. + * + * Set the default format used when type juggling a Carbon instance to a string. + * + * @param string|Closure|null $format + * + * @return void + */ + public static function setToStringFormat($format); + + /** + * Set the default translator instance to use. + * + * @param \Symfony\Component\Translation\TranslatorInterface $translator + * + * @return void + */ + public static function setTranslator(TranslatorInterface $translator); + + /** + * Set specified unit to new given value. + * + * @param string $unit year, month, day, hour, minute, second or microsecond + * @param int $value new value for given unit + * + * @return static + */ + public function setUnit($unit, $value = null); + + /** + * Set any unit to a new value without overflowing current other unit given. + * + * @param string $valueUnit unit name to modify + * @param int $value new value for the input unit + * @param string $overflowUnit unit name to not overflow + * + * @return static + */ + public function setUnitNoOverflow($valueUnit, $value, $overflowUnit); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use UTF-8 language packages on every machine. + * + * Set if UTF8 will be used for localized date/time. + * + * @param bool $utf8 + */ + public static function setUtf8($utf8); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * Use $weekStartsAt optional parameter instead when using startOfWeek, floorWeek, ceilWeek + * or roundWeek method. You can also use the 'first_day_of_week' locale setting to change the + * start of week according to current locale selected and implicitly the end of week. + * + * Set the last day of week + * + * @param int|string $day week end day (or 'auto' to get the day before the first day of week + * from Carbon::getLocale() culture). + * + * @return void + */ + public static function setWeekEndsAt($day); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * Use $weekEndsAt optional parameter instead when using endOfWeek method. You can also use the + * 'first_day_of_week' locale setting to change the start of week according to current locale + * selected and implicitly the end of week. + * + * Set the first day of week + * + * @param int|string $day week start day (or 'auto' to get the first day of week from Carbon::getLocale() culture). + * + * @return void + */ + public static function setWeekStartsAt($day); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather consider week-end is always saturday and sunday, and if you have some custom + * week-end days to handle, give to those days an other name and create a macro for them: + * + * ``` + * Carbon::macro('isDayOff', function ($date) { + * return $date->isSunday() || $date->isMonday(); + * }); + * Carbon::macro('isNotDayOff', function ($date) { + * return !$date->isDayOff(); + * }); + * if ($someDate->isDayOff()) ... + * if ($someDate->isNotDayOff()) ... + * // Add 5 not-off days + * $count = 5; + * while ($someDate->isDayOff() || ($count-- > 0)) { + * $someDate->addDay(); + * } + * ``` + * + * Set weekend days + * + * @param array $days + * + * @return void + */ + public static function setWeekendDays($days); + + /** + * Set specific options. + * - strictMode: true|false|null + * - monthOverflow: true|false|null + * - yearOverflow: true|false|null + * - humanDiffOptions: int|null + * - toStringFormat: string|Closure|null + * - toJsonFormat: string|Closure|null + * - locale: string|null + * - timezone: \DateTimeZone|string|int|null + * - macros: array|null + * - genericMacros: array|null + * + * @param array $settings + * + * @return $this|static + */ + public function settings(array $settings); + + /** + * Set the instance's timezone from a string or object and add/subtract the offset difference. + * + * @param DateTimeZone|string $value + * + * @return static + */ + public function shiftTimezone($value); + + /** + * Get the month overflow global behavior (can be overridden in specific instances). + * + * @return bool + */ + public static function shouldOverflowMonths(); + + /** + * Get the month overflow global behavior (can be overridden in specific instances). + * + * @return bool + */ + public static function shouldOverflowYears(); + + /** + * @alias diffForHumans + * + * Get the difference in a human readable format in the current locale from current instance to an other + * instance given (or now if null given). + */ + public function since($other = null, $syntax = null, $short = false, $parts = 1, $options = null); + + /** + * Returns standardized singular of a given singular/plural unit name (in English). + * + * @param string $unit + * + * @return string + */ + public static function singularUnit(string $unit): string; + + /** + * Modify to start of current given unit. + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16.334455') + * ->startOf('month') + * ->endOf('week', Carbon::FRIDAY); + * ``` + * + * @param string $unit + * @param array $params + * + * @return static + */ + public function startOf($unit, ...$params); + + /** + * Resets the date to the first day of the century and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfCentury(); + * ``` + * + * @return static + */ + public function startOfCentury(); + + /** + * Resets the time to 00:00:00 start of day + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfDay(); + * ``` + * + * @return static + */ + public function startOfDay(); + + /** + * Resets the date to the first day of the decade and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfDecade(); + * ``` + * + * @return static + */ + public function startOfDecade(); + + /** + * Modify to start of current hour, minutes and seconds become 0 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfHour(); + * ``` + * + * @return static + */ + public function startOfHour(); + + /** + * Resets the date to the first day of the millennium and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfMillennium(); + * ``` + * + * @return static + */ + public function startOfMillennium(); + + /** + * Modify to start of current minute, seconds become 0 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfMinute(); + * ``` + * + * @return static + */ + public function startOfMinute(); + + /** + * Resets the date to the first day of the month and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfMonth(); + * ``` + * + * @return static + */ + public function startOfMonth(); + + /** + * Resets the date to the first day of the quarter and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfQuarter(); + * ``` + * + * @return static + */ + public function startOfQuarter(); + + /** + * Modify to start of current second, microseconds become 0 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16.334455') + * ->startOfSecond() + * ->format('H:i:s.u'); + * ``` + * + * @return static + */ + public function startOfSecond(); + + /** + * Resets the date to the first day of week (defined in $weekStartsAt) and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfWeek() . "\n"; + * echo Carbon::parse('2018-07-25 12:45:16')->locale('ar')->startOfWeek() . "\n"; + * echo Carbon::parse('2018-07-25 12:45:16')->startOfWeek(Carbon::SUNDAY) . "\n"; + * ``` + * + * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week + * + * @return static + */ + public function startOfWeek($weekStartsAt = null); + + /** + * Resets the date to the first day of the year and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfYear(); + * ``` + * + * @return static + */ + public function startOfYear(); + + /** + * Subtract given units or interval to the current instance. + * + * @example $date->sub('hour', 3) + * @example $date->sub(15, 'days') + * @example $date->sub(CarbonInterval::days(4)) + * + * @param string|DateInterval|Closure|CarbonConverterInterface $unit + * @param int $value + * @param bool|null $overflow + * + * @return static + */ + #[ReturnTypeWillChange] + public function sub($unit, $value = 1, $overflow = null); + + public function subRealUnit($unit, $value = 1); + + /** + * Subtract given units to the current instance. + * + * @param string $unit + * @param int $value + * @param bool|null $overflow + * + * @return static + */ + public function subUnit($unit, $value = 1, $overflow = null); + + /** + * Subtract any unit to a new value without overflowing current other unit given. + * + * @param string $valueUnit unit name to modify + * @param int $value amount to subtract to the input unit + * @param string $overflowUnit unit name to not overflow + * + * @return static + */ + public function subUnitNoOverflow($valueUnit, $value, $overflowUnit); + + /** + * Subtract given units or interval to the current instance. + * + * @see sub() + * + * @param string|DateInterval $unit + * @param int $value + * @param bool|null $overflow + * + * @return static + */ + public function subtract($unit, $value = 1, $overflow = null); + + /** + * Get the difference in a human readable format in the current locale from current instance to an other + * instance given (or now if null given). + * + * @return string + */ + public function timespan($other = null, $timezone = null); + + /** + * Set the instance's timestamp. + * + * Timestamp input can be given as int, float or a string containing one or more numbers. + * + * @param float|int|string $unixTimestamp + * + * @return static + */ + public function timestamp($unixTimestamp); + + /** + * @alias setTimezone + * + * @param DateTimeZone|string $value + * + * @return static + */ + public function timezone($value); + + /** + * Get the difference in a human readable format in the current locale from an other + * instance given (or now if null given) to current instance. + * + * When comparing a value in the past to default now: + * 1 hour from now + * 5 months from now + * + * When comparing a value in the future to default now: + * 1 hour ago + * 5 months ago + * + * When comparing a value in the past to another value: + * 1 hour after + * 5 months after + * + * When comparing a value in the future to another value: + * 1 hour before + * 5 months before + * + * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; + * if null passed, now will be used as comparison reference; + * if any other type, it will be converted to date and used as reference. + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * - 'other' entry (see above) + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single unit) + * @param int $options human diff options + * + * @return string + */ + public function to($other = null, $syntax = null, $short = false, $parts = 1, $options = null); + + /** + * Get default array representation. + * + * @example + * ``` + * var_dump(Carbon::now()->toArray()); + * ``` + * + * @return array + */ + public function toArray(); + + /** + * Format the instance as ATOM + * + * @example + * ``` + * echo Carbon::now()->toAtomString(); + * ``` + * + * @return string + */ + public function toAtomString(); + + /** + * Format the instance as COOKIE + * + * @example + * ``` + * echo Carbon::now()->toCookieString(); + * ``` + * + * @return string + */ + public function toCookieString(); + + /** + * @alias toDateTime + * + * Return native DateTime PHP object matching the current instance. + * + * @example + * ``` + * var_dump(Carbon::now()->toDate()); + * ``` + * + * @return DateTime + */ + public function toDate(); + + /** + * Format the instance as date + * + * @example + * ``` + * echo Carbon::now()->toDateString(); + * ``` + * + * @return string + */ + public function toDateString(); + + /** + * Return native DateTime PHP object matching the current instance. + * + * @example + * ``` + * var_dump(Carbon::now()->toDateTime()); + * ``` + * + * @return DateTime + */ + public function toDateTime(); + + /** + * Return native toDateTimeImmutable PHP object matching the current instance. + * + * @example + * ``` + * var_dump(Carbon::now()->toDateTimeImmutable()); + * ``` + * + * @return DateTimeImmutable + */ + public function toDateTimeImmutable(); + + /** + * Format the instance as date and time T-separated with no timezone + * + * @example + * ``` + * echo Carbon::now()->toDateTimeLocalString(); + * echo "\n"; + * echo Carbon::now()->toDateTimeLocalString('minute'); // You can specify precision among: minute, second, millisecond and microsecond + * ``` + * + * @param string $unitPrecision + * + * @return string + */ + public function toDateTimeLocalString($unitPrecision = 'second'); + + /** + * Format the instance as date and time + * + * @example + * ``` + * echo Carbon::now()->toDateTimeString(); + * ``` + * + * @param string $unitPrecision + * + * @return string + */ + public function toDateTimeString($unitPrecision = 'second'); + + /** + * Format the instance with day, date and time + * + * @example + * ``` + * echo Carbon::now()->toDayDateTimeString(); + * ``` + * + * @return string + */ + public function toDayDateTimeString(); + + /** + * Format the instance as a readable date + * + * @example + * ``` + * echo Carbon::now()->toFormattedDateString(); + * ``` + * + * @return string + */ + public function toFormattedDateString(); + + /** + * Format the instance with the day, and a readable date + * + * @example + * ``` + * echo Carbon::now()->toFormattedDayDateString(); + * ``` + * + * @return string + */ + public function toFormattedDayDateString(): string; + + /** + * Return the ISO-8601 string (ex: 1977-04-22T06:00:00Z, if $keepOffset truthy, offset will be kept: + * 1977-04-22T01:00:00-05:00). + * + * @example + * ``` + * echo Carbon::now('America/Toronto')->toISOString() . "\n"; + * echo Carbon::now('America/Toronto')->toISOString(true) . "\n"; + * ``` + * + * @param bool $keepOffset Pass true to keep the date offset. Else forced to UTC. + * + * @return null|string + */ + public function toISOString($keepOffset = false); + + /** + * Return a immutable copy of the instance. + * + * @return CarbonImmutable + */ + public function toImmutable(); + + /** + * Format the instance as ISO8601 + * + * @example + * ``` + * echo Carbon::now()->toIso8601String(); + * ``` + * + * @return string + */ + public function toIso8601String(); + + /** + * Convert the instance to UTC and return as Zulu ISO8601 + * + * @example + * ``` + * echo Carbon::now()->toIso8601ZuluString(); + * ``` + * + * @param string $unitPrecision + * + * @return string + */ + public function toIso8601ZuluString($unitPrecision = 'second'); + + /** + * Return the ISO-8601 string (ex: 1977-04-22T06:00:00Z) with UTC timezone. + * + * @example + * ``` + * echo Carbon::now('America/Toronto')->toJSON(); + * ``` + * + * @return null|string + */ + public function toJSON(); + + /** + * Return a mutable copy of the instance. + * + * @return Carbon + */ + public function toMutable(); + + /** + * Get the difference in a human readable format in the current locale from an other + * instance given to now + * + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single part) + * @param int $options human diff options + * + * @return string + */ + public function toNow($syntax = null, $short = false, $parts = 1, $options = null); + + /** + * Get default object representation. + * + * @example + * ``` + * var_dump(Carbon::now()->toObject()); + * ``` + * + * @return object + */ + public function toObject(); + + /** + * Create a iterable CarbonPeriod object from current date to a given end date (and optional interval). + * + * @param \DateTimeInterface|Carbon|CarbonImmutable|int|null $end period end date or recurrences count if int + * @param int|\DateInterval|string|null $interval period default interval or number of the given $unit + * @param string|null $unit if specified, $interval must be an integer + * + * @return CarbonPeriod + */ + public function toPeriod($end = null, $interval = null, $unit = null); + + /** + * Format the instance as RFC1036 + * + * @example + * ``` + * echo Carbon::now()->toRfc1036String(); + * ``` + * + * @return string + */ + public function toRfc1036String(); + + /** + * Format the instance as RFC1123 + * + * @example + * ``` + * echo Carbon::now()->toRfc1123String(); + * ``` + * + * @return string + */ + public function toRfc1123String(); + + /** + * Format the instance as RFC2822 + * + * @example + * ``` + * echo Carbon::now()->toRfc2822String(); + * ``` + * + * @return string + */ + public function toRfc2822String(); + + /** + * Format the instance as RFC3339 + * + * @param bool $extended + * + * @example + * ``` + * echo Carbon::now()->toRfc3339String() . "\n"; + * echo Carbon::now()->toRfc3339String(true) . "\n"; + * ``` + * + * @return string + */ + public function toRfc3339String($extended = false); + + /** + * Format the instance as RFC7231 + * + * @example + * ``` + * echo Carbon::now()->toRfc7231String(); + * ``` + * + * @return string + */ + public function toRfc7231String(); + + /** + * Format the instance as RFC822 + * + * @example + * ``` + * echo Carbon::now()->toRfc822String(); + * ``` + * + * @return string + */ + public function toRfc822String(); + + /** + * Format the instance as RFC850 + * + * @example + * ``` + * echo Carbon::now()->toRfc850String(); + * ``` + * + * @return string + */ + public function toRfc850String(); + + /** + * Format the instance as RSS + * + * @example + * ``` + * echo Carbon::now()->toRssString(); + * ``` + * + * @return string + */ + public function toRssString(); + + /** + * Returns english human readable complete date string. + * + * @example + * ``` + * echo Carbon::now()->toString(); + * ``` + * + * @return string + */ + public function toString(); + + /** + * Format the instance as time + * + * @example + * ``` + * echo Carbon::now()->toTimeString(); + * ``` + * + * @param string $unitPrecision + * + * @return string + */ + public function toTimeString($unitPrecision = 'second'); + + /** + * Format the instance as W3C + * + * @example + * ``` + * echo Carbon::now()->toW3cString(); + * ``` + * + * @return string + */ + public function toW3cString(); + + /** + * Create a Carbon instance for today. + * + * @param DateTimeZone|string|null $tz + * + * @return static + */ + public static function today($tz = null); + + /** + * Create a Carbon instance for tomorrow. + * + * @param DateTimeZone|string|null $tz + * + * @return static + */ + public static function tomorrow($tz = null); + + /** + * Translate using translation string or callback available. + * + * @param string $key + * @param array $parameters + * @param string|int|float|null $number + * @param \Symfony\Component\Translation\TranslatorInterface|null $translator + * @param bool $altNumbers + * + * @return string + */ + public function translate(string $key, array $parameters = [], $number = null, ?TranslatorInterface $translator = null, bool $altNumbers = false): string; + + /** + * Returns the alternative number for a given integer if available in the current locale. + * + * @param int $number + * + * @return string + */ + public function translateNumber(int $number): string; + + /** + * Translate a time string from a locale to an other. + * + * @param string $timeString date/time/duration string to translate (may also contain English) + * @param string|null $from input locale of the $timeString parameter (`Carbon::getLocale()` by default) + * @param string|null $to output locale of the result returned (`"en"` by default) + * @param int $mode specify what to translate with options: + * - self::TRANSLATE_ALL (default) + * - CarbonInterface::TRANSLATE_MONTHS + * - CarbonInterface::TRANSLATE_DAYS + * - CarbonInterface::TRANSLATE_UNITS + * - CarbonInterface::TRANSLATE_MERIDIEM + * You can use pipe to group: CarbonInterface::TRANSLATE_MONTHS | CarbonInterface::TRANSLATE_DAYS + * + * @return string + */ + public static function translateTimeString($timeString, $from = null, $to = null, $mode = self::TRANSLATE_ALL); + + /** + * Translate a time string from the current locale (`$date->locale()`) to an other. + * + * @param string $timeString time string to translate + * @param string|null $to output locale of the result returned ("en" by default) + * + * @return string + */ + public function translateTimeStringTo($timeString, $to = null); + + /** + * Translate using translation string or callback available. + * + * @param \Symfony\Component\Translation\TranslatorInterface $translator + * @param string $key + * @param array $parameters + * @param null $number + * + * @return string + */ + public static function translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null): string; + + /** + * Format as ->format() do (using date replacements patterns from https://php.net/manual/en/function.date.php) + * but translate words whenever possible (months, day names, etc.) using the current locale. + * + * @param string $format + * + * @return string + */ + public function translatedFormat(string $format): string; + + /** + * Set the timezone or returns the timezone name if no arguments passed. + * + * @param DateTimeZone|string $value + * + * @return static|string + */ + public function tz($value = null); + + /** + * @alias getTimestamp + * + * Returns the UNIX timestamp for the current date. + * + * @return int + */ + public function unix(); + + /** + * @alias to + * + * Get the difference in a human readable format in the current locale from an other + * instance given (or now if null given) to current instance. + * + * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; + * if null passed, now will be used as comparison reference; + * if any other type, it will be converted to date and used as reference. + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * - 'other' entry (see above) + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single unit) + * @param int $options human diff options + * + * @return string + */ + public function until($other = null, $syntax = null, $short = false, $parts = 1, $options = null); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @see settings + * + * Indicates if months should be calculated with overflow. + * + * @param bool $monthsOverflow + * + * @return void + */ + public static function useMonthsOverflow($monthsOverflow = true); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @see settings + * + * Enable the strict mode (or disable with passing false). + * + * @param bool $strictModeEnabled + */ + public static function useStrictMode($strictModeEnabled = true); + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @see settings + * + * Indicates if years should be calculated with overflow. + * + * @param bool $yearsOverflow + * + * @return void + */ + public static function useYearsOverflow($yearsOverflow = true); + + /** + * Set the instance's timezone to UTC. + * + * @return static + */ + public function utc(); + + /** + * Returns the minutes offset to UTC if no arguments passed, else set the timezone with given minutes shift passed. + * + * @param int|null $minuteOffset + * + * @return int|static + */ + public function utcOffset(?int $minuteOffset = null); + + /** + * Returns the milliseconds timestamps used amongst other by Date javascript objects. + * + * @return float + */ + public function valueOf(); + + /** + * Get/set the week number using given first day of week and first + * day of year included in the first week. Or use US format if no settings + * given (Sunday / Jan 6). + * + * @param int|null $week + * @param int|null $dayOfWeek + * @param int|null $dayOfYear + * + * @return int|static + */ + public function week($week = null, $dayOfWeek = null, $dayOfYear = null); + + /** + * Set/get the week number of year using given first day of week and first + * day of year included in the first week. Or use US format if no settings + * given (Sunday / Jan 6). + * + * @param int|null $year if null, act as a getter, if not null, set the year and return current instance. + * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday) + * @param int|null $dayOfYear first day of year included in the week #1 + * + * @return int|static + */ + public function weekYear($year = null, $dayOfWeek = null, $dayOfYear = null); + + /** + * Get/set the weekday from 0 (Sunday) to 6 (Saturday). + * + * @param int|null $value new value for weekday if using as setter. + * + * @return static|int + */ + public function weekday($value = null); + + /** + * Get the number of weeks of the current week-year using given first day of week and first + * day of year included in the first week. Or use US format if no settings + * given (Sunday / Jan 6). + * + * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday) + * @param int|null $dayOfYear first day of year included in the week #1 + * + * @return int + */ + public function weeksInYear($dayOfWeek = null, $dayOfYear = null); + + /** + * Temporarily sets a static date to be used within the callback. + * Using setTestNow to set the date, executing the callback, then + * clearing the test instance. + * + * /!\ Use this method for unit tests only. + * + * @template T + * + * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance + * @param Closure(): T $callback + * + * @return T + */ + public static function withTestNow($testNow, $callback); + + /** + * Create a Carbon instance for yesterday. + * + * @param DateTimeZone|string|null $tz + * + * @return static + */ + public static function yesterday($tz = null); + + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php b/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php new file mode 100644 index 0000000..8437c54 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php @@ -0,0 +1,3054 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Carbon\Exceptions\BadFluentConstructorException; +use Carbon\Exceptions\BadFluentSetterException; +use Carbon\Exceptions\InvalidCastException; +use Carbon\Exceptions\InvalidIntervalException; +use Carbon\Exceptions\OutOfRangeException; +use Carbon\Exceptions\ParseErrorException; +use Carbon\Exceptions\UnitNotConfiguredException; +use Carbon\Exceptions\UnknownGetterException; +use Carbon\Exceptions\UnknownSetterException; +use Carbon\Exceptions\UnknownUnitException; +use Carbon\Traits\IntervalRounding; +use Carbon\Traits\IntervalStep; +use Carbon\Traits\MagicParameter; +use Carbon\Traits\Mixin; +use Carbon\Traits\Options; +use Carbon\Traits\ToStringFormat; +use Closure; +use DateInterval; +use DateMalformedIntervalStringException; +use DateTimeInterface; +use DateTimeZone; +use Exception; +use InvalidArgumentException; +use ReflectionException; +use ReturnTypeWillChange; +use RuntimeException; +use Throwable; + +/** + * A simple API extension for DateInterval. + * The implementation provides helpers to handle weeks but only days are saved. + * Weeks are calculated based on the total days of the current instance. + * + * @property int $years Total years of the current interval. + * @property int $months Total months of the current interval. + * @property int $weeks Total weeks of the current interval calculated from the days. + * @property int $dayz Total days of the current interval (weeks * 7 + days). + * @property int $hours Total hours of the current interval. + * @property int $minutes Total minutes of the current interval. + * @property int $seconds Total seconds of the current interval. + * @property int $microseconds Total microseconds of the current interval. + * @property int $milliseconds Total milliseconds of the current interval. + * @property int $microExcludeMilli Remaining microseconds without the milliseconds. + * @property int $dayzExcludeWeeks Total days remaining in the final week of the current instance (days % 7). + * @property int $daysExcludeWeeks alias of dayzExcludeWeeks + * @property-read float $totalYears Number of years equivalent to the interval. + * @property-read float $totalMonths Number of months equivalent to the interval. + * @property-read float $totalWeeks Number of weeks equivalent to the interval. + * @property-read float $totalDays Number of days equivalent to the interval. + * @property-read float $totalDayz Alias for totalDays. + * @property-read float $totalHours Number of hours equivalent to the interval. + * @property-read float $totalMinutes Number of minutes equivalent to the interval. + * @property-read float $totalSeconds Number of seconds equivalent to the interval. + * @property-read float $totalMilliseconds Number of milliseconds equivalent to the interval. + * @property-read float $totalMicroseconds Number of microseconds equivalent to the interval. + * @property-read string $locale locale of the current instance + * + * @method static CarbonInterval years($years = 1) Create instance specifying a number of years or modify the number of years if called on an instance. + * @method static CarbonInterval year($years = 1) Alias for years() + * @method static CarbonInterval months($months = 1) Create instance specifying a number of months or modify the number of months if called on an instance. + * @method static CarbonInterval month($months = 1) Alias for months() + * @method static CarbonInterval weeks($weeks = 1) Create instance specifying a number of weeks or modify the number of weeks if called on an instance. + * @method static CarbonInterval week($weeks = 1) Alias for weeks() + * @method static CarbonInterval days($days = 1) Create instance specifying a number of days or modify the number of days if called on an instance. + * @method static CarbonInterval dayz($days = 1) Alias for days() + * @method static CarbonInterval daysExcludeWeeks($days = 1) Create instance specifying a number of days or modify the number of days (keeping the current number of weeks) if called on an instance. + * @method static CarbonInterval dayzExcludeWeeks($days = 1) Alias for daysExcludeWeeks() + * @method static CarbonInterval day($days = 1) Alias for days() + * @method static CarbonInterval hours($hours = 1) Create instance specifying a number of hours or modify the number of hours if called on an instance. + * @method static CarbonInterval hour($hours = 1) Alias for hours() + * @method static CarbonInterval minutes($minutes = 1) Create instance specifying a number of minutes or modify the number of minutes if called on an instance. + * @method static CarbonInterval minute($minutes = 1) Alias for minutes() + * @method static CarbonInterval seconds($seconds = 1) Create instance specifying a number of seconds or modify the number of seconds if called on an instance. + * @method static CarbonInterval second($seconds = 1) Alias for seconds() + * @method static CarbonInterval milliseconds($milliseconds = 1) Create instance specifying a number of milliseconds or modify the number of milliseconds if called on an instance. + * @method static CarbonInterval millisecond($milliseconds = 1) Alias for milliseconds() + * @method static CarbonInterval microseconds($microseconds = 1) Create instance specifying a number of microseconds or modify the number of microseconds if called on an instance. + * @method static CarbonInterval microsecond($microseconds = 1) Alias for microseconds() + * @method $this addYears(int $years) Add given number of years to the current interval + * @method $this subYears(int $years) Subtract given number of years to the current interval + * @method $this addMonths(int $months) Add given number of months to the current interval + * @method $this subMonths(int $months) Subtract given number of months to the current interval + * @method $this addWeeks(int|float $weeks) Add given number of weeks to the current interval + * @method $this subWeeks(int|float $weeks) Subtract given number of weeks to the current interval + * @method $this addDays(int|float $days) Add given number of days to the current interval + * @method $this subDays(int|float $days) Subtract given number of days to the current interval + * @method $this addHours(int|float $hours) Add given number of hours to the current interval + * @method $this subHours(int|float $hours) Subtract given number of hours to the current interval + * @method $this addMinutes(int|float $minutes) Add given number of minutes to the current interval + * @method $this subMinutes(int|float $minutes) Subtract given number of minutes to the current interval + * @method $this addSeconds(int|float $seconds) Add given number of seconds to the current interval + * @method $this subSeconds(int|float $seconds) Subtract given number of seconds to the current interval + * @method $this addMilliseconds(int|float $milliseconds) Add given number of milliseconds to the current interval + * @method $this subMilliseconds(int|float $milliseconds) Subtract given number of milliseconds to the current interval + * @method $this addMicroseconds(int|float $microseconds) Add given number of microseconds to the current interval + * @method $this subMicroseconds(int|float $microseconds) Subtract given number of microseconds to the current interval + * @method $this roundYear(int|float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method $this roundYears(int|float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method $this floorYear(int|float $precision = 1) Truncate the current instance year with given precision. + * @method $this floorYears(int|float $precision = 1) Truncate the current instance year with given precision. + * @method $this ceilYear(int|float $precision = 1) Ceil the current instance year with given precision. + * @method $this ceilYears(int|float $precision = 1) Ceil the current instance year with given precision. + * @method $this roundMonth(int|float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method $this roundMonths(int|float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method $this floorMonth(int|float $precision = 1) Truncate the current instance month with given precision. + * @method $this floorMonths(int|float $precision = 1) Truncate the current instance month with given precision. + * @method $this ceilMonth(int|float $precision = 1) Ceil the current instance month with given precision. + * @method $this ceilMonths(int|float $precision = 1) Ceil the current instance month with given precision. + * @method $this roundWeek(int|float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method $this roundWeeks(int|float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method $this floorWeek(int|float $precision = 1) Truncate the current instance day with given precision. + * @method $this floorWeeks(int|float $precision = 1) Truncate the current instance day with given precision. + * @method $this ceilWeek(int|float $precision = 1) Ceil the current instance day with given precision. + * @method $this ceilWeeks(int|float $precision = 1) Ceil the current instance day with given precision. + * @method $this roundDay(int|float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method $this roundDays(int|float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method $this floorDay(int|float $precision = 1) Truncate the current instance day with given precision. + * @method $this floorDays(int|float $precision = 1) Truncate the current instance day with given precision. + * @method $this ceilDay(int|float $precision = 1) Ceil the current instance day with given precision. + * @method $this ceilDays(int|float $precision = 1) Ceil the current instance day with given precision. + * @method $this roundHour(int|float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method $this roundHours(int|float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method $this floorHour(int|float $precision = 1) Truncate the current instance hour with given precision. + * @method $this floorHours(int|float $precision = 1) Truncate the current instance hour with given precision. + * @method $this ceilHour(int|float $precision = 1) Ceil the current instance hour with given precision. + * @method $this ceilHours(int|float $precision = 1) Ceil the current instance hour with given precision. + * @method $this roundMinute(int|float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method $this roundMinutes(int|float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method $this floorMinute(int|float $precision = 1) Truncate the current instance minute with given precision. + * @method $this floorMinutes(int|float $precision = 1) Truncate the current instance minute with given precision. + * @method $this ceilMinute(int|float $precision = 1) Ceil the current instance minute with given precision. + * @method $this ceilMinutes(int|float $precision = 1) Ceil the current instance minute with given precision. + * @method $this roundSecond(int|float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method $this roundSeconds(int|float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method $this floorSecond(int|float $precision = 1) Truncate the current instance second with given precision. + * @method $this floorSeconds(int|float $precision = 1) Truncate the current instance second with given precision. + * @method $this ceilSecond(int|float $precision = 1) Ceil the current instance second with given precision. + * @method $this ceilSeconds(int|float $precision = 1) Ceil the current instance second with given precision. + * @method $this roundMillennium(int|float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method $this roundMillennia(int|float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method $this floorMillennium(int|float $precision = 1) Truncate the current instance millennium with given precision. + * @method $this floorMillennia(int|float $precision = 1) Truncate the current instance millennium with given precision. + * @method $this ceilMillennium(int|float $precision = 1) Ceil the current instance millennium with given precision. + * @method $this ceilMillennia(int|float $precision = 1) Ceil the current instance millennium with given precision. + * @method $this roundCentury(int|float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method $this roundCenturies(int|float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method $this floorCentury(int|float $precision = 1) Truncate the current instance century with given precision. + * @method $this floorCenturies(int|float $precision = 1) Truncate the current instance century with given precision. + * @method $this ceilCentury(int|float $precision = 1) Ceil the current instance century with given precision. + * @method $this ceilCenturies(int|float $precision = 1) Ceil the current instance century with given precision. + * @method $this roundDecade(int|float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method $this roundDecades(int|float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method $this floorDecade(int|float $precision = 1) Truncate the current instance decade with given precision. + * @method $this floorDecades(int|float $precision = 1) Truncate the current instance decade with given precision. + * @method $this ceilDecade(int|float $precision = 1) Ceil the current instance decade with given precision. + * @method $this ceilDecades(int|float $precision = 1) Ceil the current instance decade with given precision. + * @method $this roundQuarter(int|float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method $this roundQuarters(int|float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method $this floorQuarter(int|float $precision = 1) Truncate the current instance quarter with given precision. + * @method $this floorQuarters(int|float $precision = 1) Truncate the current instance quarter with given precision. + * @method $this ceilQuarter(int|float $precision = 1) Ceil the current instance quarter with given precision. + * @method $this ceilQuarters(int|float $precision = 1) Ceil the current instance quarter with given precision. + * @method $this roundMillisecond(int|float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method $this roundMilliseconds(int|float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method $this floorMillisecond(int|float $precision = 1) Truncate the current instance millisecond with given precision. + * @method $this floorMilliseconds(int|float $precision = 1) Truncate the current instance millisecond with given precision. + * @method $this ceilMillisecond(int|float $precision = 1) Ceil the current instance millisecond with given precision. + * @method $this ceilMilliseconds(int|float $precision = 1) Ceil the current instance millisecond with given precision. + * @method $this roundMicrosecond(int|float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method $this roundMicroseconds(int|float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method $this floorMicrosecond(int|float $precision = 1) Truncate the current instance microsecond with given precision. + * @method $this floorMicroseconds(int|float $precision = 1) Truncate the current instance microsecond with given precision. + * @method $this ceilMicrosecond(int|float $precision = 1) Ceil the current instance microsecond with given precision. + * @method $this ceilMicroseconds(int|float $precision = 1) Ceil the current instance microsecond with given precision. + */ +class CarbonInterval extends DateInterval implements CarbonConverterInterface +{ + use IntervalRounding; + use IntervalStep; + use MagicParameter; + use Mixin { + Mixin::mixin as baseMixin; + } + use Options; + use ToStringFormat; + + /** + * Interval spec period designators + */ + public const PERIOD_PREFIX = 'P'; + public const PERIOD_YEARS = 'Y'; + public const PERIOD_MONTHS = 'M'; + public const PERIOD_DAYS = 'D'; + public const PERIOD_TIME_PREFIX = 'T'; + public const PERIOD_HOURS = 'H'; + public const PERIOD_MINUTES = 'M'; + public const PERIOD_SECONDS = 'S'; + + /** + * A translator to ... er ... translate stuff + * + * @var \Symfony\Component\Translation\TranslatorInterface + */ + protected static $translator; + + /** + * @var array|null + */ + protected static $cascadeFactors; + + /** + * @var array + */ + protected static $formats = [ + 'y' => 'y', + 'Y' => 'y', + 'o' => 'y', + 'm' => 'm', + 'n' => 'm', + 'W' => 'weeks', + 'd' => 'd', + 'j' => 'd', + 'z' => 'd', + 'h' => 'h', + 'g' => 'h', + 'H' => 'h', + 'G' => 'h', + 'i' => 'i', + 's' => 's', + 'u' => 'micro', + 'v' => 'milli', + ]; + + /** + * @var array|null + */ + private static $flipCascadeFactors; + + /** + * @var bool + */ + private static $floatSettersEnabled = false; + + /** + * The registered macros. + * + * @var array + */ + protected static $macros = []; + + /** + * Timezone handler for settings() method. + * + * @var mixed + */ + protected $tzName; + + /** + * Set the instance's timezone from a string or object. + * + * @param \DateTimeZone|string $tzName + * + * @return static + */ + public function setTimezone($tzName) + { + $this->tzName = $tzName; + + return $this; + } + + /** + * @internal + * + * Set the instance's timezone from a string or object and add/subtract the offset difference. + * + * @param \DateTimeZone|string $tzName + * + * @return static + */ + public function shiftTimezone($tzName) + { + $this->tzName = $tzName; + + return $this; + } + + /** + * Mapping of units and factors for cascading. + * + * Should only be modified by changing the factors or referenced constants. + * + * @return array + */ + public static function getCascadeFactors() + { + return static::$cascadeFactors ?: static::getDefaultCascadeFactors(); + } + + protected static function getDefaultCascadeFactors(): array + { + return [ + 'milliseconds' => [Carbon::MICROSECONDS_PER_MILLISECOND, 'microseconds'], + 'seconds' => [Carbon::MILLISECONDS_PER_SECOND, 'milliseconds'], + 'minutes' => [Carbon::SECONDS_PER_MINUTE, 'seconds'], + 'hours' => [Carbon::MINUTES_PER_HOUR, 'minutes'], + 'dayz' => [Carbon::HOURS_PER_DAY, 'hours'], + 'weeks' => [Carbon::DAYS_PER_WEEK, 'dayz'], + 'months' => [Carbon::WEEKS_PER_MONTH, 'weeks'], + 'years' => [Carbon::MONTHS_PER_YEAR, 'months'], + ]; + } + + private static function standardizeUnit($unit) + { + $unit = rtrim($unit, 'sz').'s'; + + return $unit === 'days' ? 'dayz' : $unit; + } + + private static function getFlipCascadeFactors() + { + if (!self::$flipCascadeFactors) { + self::$flipCascadeFactors = []; + + foreach (static::getCascadeFactors() as $to => [$factor, $from]) { + self::$flipCascadeFactors[self::standardizeUnit($from)] = [self::standardizeUnit($to), $factor]; + } + } + + return self::$flipCascadeFactors; + } + + /** + * Set default cascading factors for ->cascade() method. + * + * @param array $cascadeFactors + */ + public static function setCascadeFactors(array $cascadeFactors) + { + self::$flipCascadeFactors = null; + static::$cascadeFactors = $cascadeFactors; + } + + /** + * This option allow you to opt-in for the Carbon 3 behavior where float + * values will no longer be cast to integer (so truncated). + * + * ⚠️ This settings will be applied globally, which mean your whole application + * code including the third-party dependencies that also may use Carbon will + * adopt the new behavior. + */ + public static function enableFloatSetters(bool $floatSettersEnabled = true): void + { + self::$floatSettersEnabled = $floatSettersEnabled; + } + + /////////////////////////////////////////////////////////////////// + //////////////////////////// CONSTRUCTORS ///////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Create a new CarbonInterval instance. + * + * @param Closure|DateInterval|string|int|null $years + * @param int|float|null $months + * @param int|float|null $weeks + * @param int|float|null $days + * @param int|float|null $hours + * @param int|float|null $minutes + * @param int|float|null $seconds + * @param int|float|null $microseconds + * + * @throws Exception when the interval_spec (passed as $years) cannot be parsed as an interval. + */ + public function __construct($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null, $microseconds = null) + { + if ($years instanceof Closure) { + $this->step = $years; + $years = null; + } + + if ($years instanceof DateInterval) { + parent::__construct(static::getDateIntervalSpec($years)); + $this->f = $years->f; + self::copyNegativeUnits($years, $this); + + return; + } + + $spec = $years; + $isStringSpec = (\is_string($spec) && !preg_match('/^[\d.]/', $spec)); + + if (!$isStringSpec || (float) $years) { + $spec = static::PERIOD_PREFIX; + + $spec .= $years > 0 ? $years.static::PERIOD_YEARS : ''; + $spec .= $months > 0 ? $months.static::PERIOD_MONTHS : ''; + + $specDays = 0; + $specDays += $weeks > 0 ? $weeks * static::getDaysPerWeek() : 0; + $specDays += $days > 0 ? $days : 0; + + $spec .= $specDays > 0 ? $specDays.static::PERIOD_DAYS : ''; + + if ($hours > 0 || $minutes > 0 || $seconds > 0) { + $spec .= static::PERIOD_TIME_PREFIX; + $spec .= $hours > 0 ? $hours.static::PERIOD_HOURS : ''; + $spec .= $minutes > 0 ? $minutes.static::PERIOD_MINUTES : ''; + $spec .= $seconds > 0 ? $seconds.static::PERIOD_SECONDS : ''; + } + + if ($spec === static::PERIOD_PREFIX) { + // Allow the zero interval. + $spec .= '0'.static::PERIOD_YEARS; + } + } + + try { + parent::__construct($spec); + } catch (Throwable $exception) { + try { + parent::__construct('PT0S'); + + if ($isStringSpec) { + if (!preg_match('/^P + (?:(?[+-]?\d*(?:\.\d+)?)Y)? + (?:(?[+-]?\d*(?:\.\d+)?)M)? + (?:(?[+-]?\d*(?:\.\d+)?)W)? + (?:(?[+-]?\d*(?:\.\d+)?)D)? + (?:T + (?:(?[+-]?\d*(?:\.\d+)?)H)? + (?:(?[+-]?\d*(?:\.\d+)?)M)? + (?:(?[+-]?\d*(?:\.\d+)?)S)? + )? + $/x', $spec, $match)) { + throw new InvalidArgumentException("Invalid duration: $spec"); + } + + $years = (float) ($match['year'] ?? 0); + $this->assertSafeForInteger('year', $years); + $months = (float) ($match['month'] ?? 0); + $this->assertSafeForInteger('month', $months); + $weeks = (float) ($match['week'] ?? 0); + $this->assertSafeForInteger('week', $weeks); + $days = (float) ($match['day'] ?? 0); + $this->assertSafeForInteger('day', $days); + $hours = (float) ($match['hour'] ?? 0); + $this->assertSafeForInteger('hour', $hours); + $minutes = (float) ($match['minute'] ?? 0); + $this->assertSafeForInteger('minute', $minutes); + $seconds = (float) ($match['second'] ?? 0); + $this->assertSafeForInteger('second', $seconds); + } + + $totalDays = (($weeks * static::getDaysPerWeek()) + $days); + $this->assertSafeForInteger('days total (including weeks)', $totalDays); + + $this->y = (int) $years; + $this->m = (int) $months; + $this->d = (int) $totalDays; + $this->h = (int) $hours; + $this->i = (int) $minutes; + $this->s = (int) $seconds; + + if ( + ((float) $this->y) !== $years || + ((float) $this->m) !== $months || + ((float) $this->d) !== $totalDays || + ((float) $this->h) !== $hours || + ((float) $this->i) !== $minutes || + ((float) $this->s) !== $seconds + ) { + $this->add(static::fromString( + ($years - $this->y).' years '. + ($months - $this->m).' months '. + ($totalDays - $this->d).' days '. + ($hours - $this->h).' hours '. + ($minutes - $this->i).' minutes '. + ($seconds - $this->s).' seconds ' + )); + } + } catch (Throwable $secondException) { + throw $secondException instanceof OutOfRangeException ? $secondException : $exception; + } + } + + if ($microseconds !== null) { + $this->f = $microseconds / Carbon::MICROSECONDS_PER_SECOND; + } + } + + /** + * Returns the factor for a given source-to-target couple. + * + * @param string $source + * @param string $target + * + * @return int|float|null + */ + public static function getFactor($source, $target) + { + $source = self::standardizeUnit($source); + $target = self::standardizeUnit($target); + $factors = self::getFlipCascadeFactors(); + + if (isset($factors[$source])) { + [$to, $factor] = $factors[$source]; + + if ($to === $target) { + return $factor; + } + + return $factor * static::getFactor($to, $target); + } + + return null; + } + + /** + * Returns the factor for a given source-to-target couple if set, + * else try to find the appropriate constant as the factor, such as Carbon::DAYS_PER_WEEK. + * + * @param string $source + * @param string $target + * + * @return int|float|null + */ + public static function getFactorWithDefault($source, $target) + { + $factor = self::getFactor($source, $target); + + if ($factor) { + return $factor; + } + + static $defaults = [ + 'month' => ['year' => Carbon::MONTHS_PER_YEAR], + 'week' => ['month' => Carbon::WEEKS_PER_MONTH], + 'day' => ['week' => Carbon::DAYS_PER_WEEK], + 'hour' => ['day' => Carbon::HOURS_PER_DAY], + 'minute' => ['hour' => Carbon::MINUTES_PER_HOUR], + 'second' => ['minute' => Carbon::SECONDS_PER_MINUTE], + 'millisecond' => ['second' => Carbon::MILLISECONDS_PER_SECOND], + 'microsecond' => ['millisecond' => Carbon::MICROSECONDS_PER_MILLISECOND], + ]; + + return $defaults[$source][$target] ?? null; + } + + /** + * Returns current config for days per week. + * + * @return int|float + */ + public static function getDaysPerWeek() + { + return static::getFactor('dayz', 'weeks') ?: Carbon::DAYS_PER_WEEK; + } + + /** + * Returns current config for hours per day. + * + * @return int|float + */ + public static function getHoursPerDay() + { + return static::getFactor('hours', 'dayz') ?: Carbon::HOURS_PER_DAY; + } + + /** + * Returns current config for minutes per hour. + * + * @return int|float + */ + public static function getMinutesPerHour() + { + return static::getFactor('minutes', 'hours') ?: Carbon::MINUTES_PER_HOUR; + } + + /** + * Returns current config for seconds per minute. + * + * @return int|float + */ + public static function getSecondsPerMinute() + { + return static::getFactor('seconds', 'minutes') ?: Carbon::SECONDS_PER_MINUTE; + } + + /** + * Returns current config for microseconds per second. + * + * @return int|float + */ + public static function getMillisecondsPerSecond() + { + return static::getFactor('milliseconds', 'seconds') ?: Carbon::MILLISECONDS_PER_SECOND; + } + + /** + * Returns current config for microseconds per second. + * + * @return int|float + */ + public static function getMicrosecondsPerMillisecond() + { + return static::getFactor('microseconds', 'milliseconds') ?: Carbon::MICROSECONDS_PER_MILLISECOND; + } + + /** + * Create a new CarbonInterval instance from specific values. + * This is an alias for the constructor that allows better fluent + * syntax as it allows you to do CarbonInterval::create(1)->fn() rather than + * (new CarbonInterval(1))->fn(). + * + * @param int $years + * @param int $months + * @param int $weeks + * @param int $days + * @param int $hours + * @param int $minutes + * @param int $seconds + * @param int $microseconds + * + * @throws Exception when the interval_spec (passed as $years) cannot be parsed as an interval. + * + * @return static + */ + public static function create($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null, $microseconds = null) + { + return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds, $microseconds); + } + + /** + * Parse a string into a new CarbonInterval object according to the specified format. + * + * @example + * ``` + * echo Carboninterval::createFromFormat('H:i', '1:30'); + * ``` + * + * @param string $format Format of the $interval input string + * @param string|null $interval Input string to convert into an interval + * + * @throws \Carbon\Exceptions\ParseErrorException when the $interval cannot be parsed as an interval. + * + * @return static + */ + public static function createFromFormat(string $format, ?string $interval) + { + $instance = new static(0); + $length = mb_strlen($format); + + if (preg_match('/s([,.])([uv])$/', $format, $match)) { + $interval = explode($match[1], $interval); + $index = \count($interval) - 1; + $interval[$index] = str_pad($interval[$index], $match[2] === 'v' ? 3 : 6, '0'); + $interval = implode($match[1], $interval); + } + + $interval = $interval ?? ''; + + for ($index = 0; $index < $length; $index++) { + $expected = mb_substr($format, $index, 1); + $nextCharacter = mb_substr($interval, 0, 1); + $unit = static::$formats[$expected] ?? null; + + if ($unit) { + if (!preg_match('/^-?\d+/', $interval, $match)) { + throw new ParseErrorException('number', $nextCharacter); + } + + $interval = mb_substr($interval, mb_strlen($match[0])); + $instance->$unit += (int) ($match[0]); + + continue; + } + + if ($nextCharacter !== $expected) { + throw new ParseErrorException( + "'$expected'", + $nextCharacter, + 'Allowed substitutes for interval formats are '.implode(', ', array_keys(static::$formats))."\n". + 'See https://php.net/manual/en/function.date.php for their meaning' + ); + } + + $interval = mb_substr($interval, 1); + } + + if ($interval !== '') { + throw new ParseErrorException( + 'end of string', + $interval + ); + } + + return $instance; + } + + /** + * Get a copy of the instance. + * + * @return static + */ + public function copy() + { + $date = new static(0); + $date->copyProperties($this); + $date->step = $this->step; + + return $date; + } + + /** + * Get a copy of the instance. + * + * @return static + */ + public function clone() + { + return $this->copy(); + } + + /** + * Provide static helpers to create instances. Allows CarbonInterval::years(3). + * + * Note: This is done using the magic method to allow static and instance methods to + * have the same names. + * + * @param string $method magic method name called + * @param array $parameters parameters list + * + * @return static|null + */ + public static function __callStatic($method, $parameters) + { + try { + $interval = new static(0); + $localStrictModeEnabled = $interval->localStrictModeEnabled; + $interval->localStrictModeEnabled = true; + + $result = static::hasMacro($method) + ? static::bindMacroContext(null, function () use (&$method, &$parameters, &$interval) { + return $interval->callMacro($method, $parameters); + }) + : $interval->$method(...$parameters); + + $interval->localStrictModeEnabled = $localStrictModeEnabled; + + return $result; + } catch (BadFluentSetterException $exception) { + if (Carbon::isStrictModeEnabled()) { + throw new BadFluentConstructorException($method, 0, $exception); + } + + return null; + } + } + + /** + * Evaluate the PHP generated by var_export() and recreate the exported CarbonInterval instance. + * + * @param array $dump data as exported by var_export() + * + * @return static + */ + #[ReturnTypeWillChange] + public static function __set_state($dump) + { + /** @noinspection PhpVoidFunctionResultUsedInspection */ + /** @var DateInterval $dateInterval */ + $dateInterval = parent::__set_state($dump); + + return static::instance($dateInterval); + } + + /** + * Return the current context from inside a macro callee or a new one if static. + * + * @return static + */ + protected static function this() + { + return end(static::$macroContextStack) ?: new static(0); + } + + /** + * Creates a CarbonInterval from string. + * + * Format: + * + * Suffix | Unit | Example | DateInterval expression + * -------|---------|---------|------------------------ + * y | years | 1y | P1Y + * mo | months | 3mo | P3M + * w | weeks | 2w | P2W + * d | days | 28d | P28D + * h | hours | 4h | PT4H + * m | minutes | 12m | PT12M + * s | seconds | 59s | PT59S + * + * e. g. `1w 3d 4h 32m 23s` is converted to 10 days 4 hours 32 minutes and 23 seconds. + * + * Special cases: + * - An empty string will return a zero interval + * - Fractions are allowed for weeks, days, hours and minutes and will be converted + * and rounded to the next smaller value (caution: 0.5w = 4d) + * + * @param string $intervalDefinition + * + * @return static + */ + public static function fromString($intervalDefinition) + { + if (empty($intervalDefinition)) { + return new static(0); + } + + $years = 0; + $months = 0; + $weeks = 0; + $days = 0; + $hours = 0; + $minutes = 0; + $seconds = 0; + $milliseconds = 0; + $microseconds = 0; + + $pattern = '/(\d+(?:\.\d+)?)\h*([^\d\h]*)/i'; + preg_match_all($pattern, $intervalDefinition, $parts, PREG_SET_ORDER); + + while ([$part, $value, $unit] = array_shift($parts)) { + $intValue = (int) $value; + $fraction = (float) $value - $intValue; + + // Fix calculation precision + switch (round($fraction, 6)) { + case 1: + $fraction = 0; + $intValue++; + + break; + case 0: + $fraction = 0; + + break; + } + + switch ($unit === 'µs' ? 'µs' : strtolower($unit)) { + case 'millennia': + case 'millennium': + $years += $intValue * CarbonInterface::YEARS_PER_MILLENNIUM; + + break; + + case 'century': + case 'centuries': + $years += $intValue * CarbonInterface::YEARS_PER_CENTURY; + + break; + + case 'decade': + case 'decades': + $years += $intValue * CarbonInterface::YEARS_PER_DECADE; + + break; + + case 'year': + case 'years': + case 'y': + case 'yr': + case 'yrs': + $years += $intValue; + + break; + + case 'quarter': + case 'quarters': + $months += $intValue * CarbonInterface::MONTHS_PER_QUARTER; + + break; + + case 'month': + case 'months': + case 'mo': + case 'mos': + $months += $intValue; + + break; + + case 'week': + case 'weeks': + case 'w': + $weeks += $intValue; + + if ($fraction) { + $parts[] = [null, $fraction * static::getDaysPerWeek(), 'd']; + } + + break; + + case 'day': + case 'days': + case 'd': + $days += $intValue; + + if ($fraction) { + $parts[] = [null, $fraction * static::getHoursPerDay(), 'h']; + } + + break; + + case 'hour': + case 'hours': + case 'h': + $hours += $intValue; + + if ($fraction) { + $parts[] = [null, $fraction * static::getMinutesPerHour(), 'm']; + } + + break; + + case 'minute': + case 'minutes': + case 'm': + $minutes += $intValue; + + if ($fraction) { + $parts[] = [null, $fraction * static::getSecondsPerMinute(), 's']; + } + + break; + + case 'second': + case 'seconds': + case 's': + $seconds += $intValue; + + if ($fraction) { + $parts[] = [null, $fraction * static::getMillisecondsPerSecond(), 'ms']; + } + + break; + + case 'millisecond': + case 'milliseconds': + case 'milli': + case 'ms': + $milliseconds += $intValue; + + if ($fraction) { + $microseconds += round($fraction * static::getMicrosecondsPerMillisecond()); + } + + break; + + case 'microsecond': + case 'microseconds': + case 'micro': + case 'µs': + $microseconds += $intValue; + + break; + + default: + throw new InvalidIntervalException( + sprintf('Invalid part %s in definition %s', $part, $intervalDefinition) + ); + } + } + + return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds, $milliseconds * Carbon::MICROSECONDS_PER_MILLISECOND + $microseconds); + } + + /** + * Creates a CarbonInterval from string using a different locale. + * + * @param string $interval interval string in the given language (may also contain English). + * @param string|null $locale if locale is null or not specified, current global locale will be used instead. + * + * @return static + */ + public static function parseFromLocale($interval, $locale = null) + { + return static::fromString(Carbon::translateTimeString($interval, $locale ?: static::getLocale(), 'en')); + } + + private static function castIntervalToClass(DateInterval $interval, string $className, array $skip = []) + { + $mainClass = DateInterval::class; + + if (!is_a($className, $mainClass, true)) { + throw new InvalidCastException("$className is not a sub-class of $mainClass."); + } + + $microseconds = $interval->f; + $instance = new $className(static::getDateIntervalSpec($interval, false, $skip)); + + if ($microseconds) { + $instance->f = $microseconds; + } + + if ($interval instanceof self && is_a($className, self::class, true)) { + self::copyStep($interval, $instance); + } + + self::copyNegativeUnits($interval, $instance); + + return $instance; + } + + private static function copyNegativeUnits(DateInterval $from, DateInterval $to): void + { + $to->invert = $from->invert; + + foreach (['y', 'm', 'd', 'h', 'i', 's'] as $unit) { + if ($from->$unit < 0) { + $to->$unit *= -1; + } + } + } + + private static function copyStep(self $from, self $to): void + { + $to->setStep($from->getStep()); + } + + /** + * Cast the current instance into the given class. + * + * @param string $className The $className::instance() method will be called to cast the current object. + * + * @return DateInterval + */ + public function cast(string $className) + { + return self::castIntervalToClass($this, $className); + } + + /** + * Create a CarbonInterval instance from a DateInterval one. Can not instance + * DateInterval objects created from DateTime::diff() as you can't externally + * set the $days field. + * + * @param DateInterval $interval + * @param bool $skipCopy set to true to return the passed object + * (without copying it) if it's already of the + * current class + * + * @return static + */ + public static function instance(DateInterval $interval, array $skip = [], bool $skipCopy = false) + { + if ($skipCopy && $interval instanceof static) { + return $interval; + } + + return self::castIntervalToClass($interval, static::class, $skip); + } + + /** + * Make a CarbonInterval instance from given variable if possible. + * + * Always return a new instance. Parse only strings and only these likely to be intervals (skip dates + * and recurrences). Throw an exception for invalid format, but otherwise return null. + * + * @param mixed|int|DateInterval|string|Closure|null $interval interval or number of the given $unit + * @param string|null $unit if specified, $interval must be an integer + * @param bool $skipCopy set to true to return the passed object + * (without copying it) if it's already of the + * current class + * + * @return static|null + */ + public static function make($interval, $unit = null, bool $skipCopy = false) + { + if ($unit) { + $interval = "$interval ".Carbon::pluralUnit($unit); + } + + if ($interval instanceof DateInterval) { + return static::instance($interval, [], $skipCopy); + } + + if ($interval instanceof Closure) { + return new static($interval); + } + + if (!\is_string($interval)) { + return null; + } + + return static::makeFromString($interval); + } + + protected static function makeFromString(string $interval) + { + $interval = preg_replace('/\s+/', ' ', trim($interval)); + + if (preg_match('/^P[T\d]/', $interval)) { + return new static($interval); + } + + if (preg_match('/^(?:\h*\d+(?:\.\d+)?\h*[a-z]+)+$/i', $interval)) { + return static::fromString($interval); + } + + // @codeCoverageIgnoreStart + try { + /** @var static $interval */ + $interval = static::createFromDateString($interval); + } catch (DateMalformedIntervalStringException $e) { + return null; + } + // @codeCoverageIgnoreEnd + + return !$interval || $interval->isEmpty() ? null : $interval; + } + + protected function resolveInterval($interval) + { + if (!($interval instanceof self)) { + return self::make($interval); + } + + return $interval; + } + + /** + * Sets up a DateInterval from the relative parts of the string. + * + * @param string $time + * + * @return static + * + * @link https://php.net/manual/en/dateinterval.createfromdatestring.php + */ + #[ReturnTypeWillChange] + public static function createFromDateString($time) + { + $interval = @parent::createFromDateString(strtr($time, [ + ',' => ' ', + ' and ' => ' ', + ])); + + if ($interval instanceof DateInterval) { + $interval = static::instance($interval); + } + + return $interval; + } + + /////////////////////////////////////////////////////////////////// + ///////////////////////// GETTERS AND SETTERS ///////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Get a part of the CarbonInterval object. + * + * @param string $name + * + * @throws UnknownGetterException + * + * @return int|float|string + */ + public function get($name) + { + if (str_starts_with($name, 'total')) { + return $this->total(substr($name, 5)); + } + + switch ($name) { + case 'years': + return $this->y; + + case 'months': + return $this->m; + + case 'dayz': + return $this->d; + + case 'hours': + return $this->h; + + case 'minutes': + return $this->i; + + case 'seconds': + return $this->s; + + case 'milli': + case 'milliseconds': + return (int) (round($this->f * Carbon::MICROSECONDS_PER_SECOND) / Carbon::MICROSECONDS_PER_MILLISECOND); + + case 'micro': + case 'microseconds': + return (int) round($this->f * Carbon::MICROSECONDS_PER_SECOND); + + case 'microExcludeMilli': + return (int) round($this->f * Carbon::MICROSECONDS_PER_SECOND) % Carbon::MICROSECONDS_PER_MILLISECOND; + + case 'weeks': + return (int) ($this->d / (int) static::getDaysPerWeek()); + + case 'daysExcludeWeeks': + case 'dayzExcludeWeeks': + return $this->d % (int) static::getDaysPerWeek(); + + case 'locale': + return $this->getTranslatorLocale(); + + default: + throw new UnknownGetterException($name); + } + } + + /** + * Get a part of the CarbonInterval object. + * + * @param string $name + * + * @throws UnknownGetterException + * + * @return int|float|string + */ + public function __get($name) + { + return $this->get($name); + } + + /** + * Set a part of the CarbonInterval object. + * + * @param string|array $name + * @param int $value + * + * @throws UnknownSetterException + * + * @return $this + */ + public function set($name, $value = null) + { + $properties = \is_array($name) ? $name : [$name => $value]; + + foreach ($properties as $key => $value) { + switch (Carbon::singularUnit(rtrim($key, 'z'))) { + case 'year': + $this->checkIntegerValue($key, $value); + $this->y = $value; + $this->handleDecimalPart('year', $value, $this->y); + + break; + + case 'month': + $this->checkIntegerValue($key, $value); + $this->m = $value; + $this->handleDecimalPart('month', $value, $this->m); + + break; + + case 'week': + $this->checkIntegerValue($key, $value); + $days = $value * (int) static::getDaysPerWeek(); + $this->assertSafeForInteger('days total (including weeks)', $days); + $this->d = $days; + $this->handleDecimalPart('day', $days, $this->d); + + break; + + case 'day': + $this->checkIntegerValue($key, $value); + $this->d = $value; + $this->handleDecimalPart('day', $value, $this->d); + + break; + + case 'daysexcludeweek': + case 'dayzexcludeweek': + $this->checkIntegerValue($key, $value); + $days = $this->weeks * (int) static::getDaysPerWeek() + $value; + $this->assertSafeForInteger('days total (including weeks)', $days); + $this->d = $days; + $this->handleDecimalPart('day', $days, $this->d); + + break; + + case 'hour': + $this->checkIntegerValue($key, $value); + $this->h = $value; + $this->handleDecimalPart('hour', $value, $this->h); + + break; + + case 'minute': + $this->checkIntegerValue($key, $value); + $this->i = $value; + $this->handleDecimalPart('minute', $value, $this->i); + + break; + + case 'second': + $this->checkIntegerValue($key, $value); + $this->s = $value; + $this->handleDecimalPart('second', $value, $this->s); + + break; + + case 'milli': + case 'millisecond': + $this->microseconds = $value * Carbon::MICROSECONDS_PER_MILLISECOND + $this->microseconds % Carbon::MICROSECONDS_PER_MILLISECOND; + + break; + + case 'micro': + case 'microsecond': + $this->f = $value / Carbon::MICROSECONDS_PER_SECOND; + + break; + + default: + if ($this->localStrictModeEnabled ?? Carbon::isStrictModeEnabled()) { + throw new UnknownSetterException($key); + } + + $this->$key = $value; + } + } + + return $this; + } + + /** + * Set a part of the CarbonInterval object. + * + * @param string $name + * @param int $value + * + * @throws UnknownSetterException + */ + public function __set($name, $value) + { + $this->set($name, $value); + } + + /** + * Allow setting of weeks and days to be cumulative. + * + * @param int $weeks Number of weeks to set + * @param int $days Number of days to set + * + * @return static + */ + public function weeksAndDays($weeks, $days) + { + $this->dayz = ($weeks * static::getDaysPerWeek()) + $days; + + return $this; + } + + /** + * Returns true if the interval is empty for each unit. + * + * @return bool + */ + public function isEmpty() + { + return $this->years === 0 && + $this->months === 0 && + $this->dayz === 0 && + !$this->days && + $this->hours === 0 && + $this->minutes === 0 && + $this->seconds === 0 && + $this->microseconds === 0; + } + + /** + * Register a custom macro. + * + * @example + * ``` + * CarbonInterval::macro('twice', function () { + * return $this->times(2); + * }); + * echo CarbonInterval::hours(2)->twice(); + * ``` + * + * @param string $name + * @param object|callable $macro + * + * @return void + */ + public static function macro($name, $macro) + { + static::$macros[$name] = $macro; + } + + /** + * Register macros from a mixin object. + * + * @example + * ``` + * CarbonInterval::mixin(new class { + * public function daysToHours() { + * return function () { + * $this->hours += $this->days; + * $this->days = 0; + * + * return $this; + * }; + * } + * public function hoursToDays() { + * return function () { + * $this->days += $this->hours; + * $this->hours = 0; + * + * return $this; + * }; + * } + * }); + * echo CarbonInterval::hours(5)->hoursToDays() . "\n"; + * echo CarbonInterval::days(5)->daysToHours() . "\n"; + * ``` + * + * @param object|string $mixin + * + * @throws ReflectionException + * + * @return void + */ + public static function mixin($mixin) + { + static::baseMixin($mixin); + } + + /** + * Check if macro is registered. + * + * @param string $name + * + * @return bool + */ + public static function hasMacro($name) + { + return isset(static::$macros[$name]); + } + + /** + * Call given macro. + * + * @param string $name + * @param array $parameters + * + * @return mixed + */ + protected function callMacro($name, $parameters) + { + $macro = static::$macros[$name]; + + if ($macro instanceof Closure) { + $boundMacro = @$macro->bindTo($this, static::class) ?: @$macro->bindTo(null, static::class); + + return ($boundMacro ?: $macro)(...$parameters); + } + + return $macro(...$parameters); + } + + /** + * Allow fluent calls on the setters... CarbonInterval::years(3)->months(5)->day(). + * + * Note: This is done using the magic method to allow static and instance methods to + * have the same names. + * + * @param string $method magic method name called + * @param array $parameters parameters list + * + * @throws BadFluentSetterException|Throwable + * + * @return static + */ + public function __call($method, $parameters) + { + if (static::hasMacro($method)) { + return static::bindMacroContext($this, function () use (&$method, &$parameters) { + return $this->callMacro($method, $parameters); + }); + } + + $roundedValue = $this->callRoundMethod($method, $parameters); + + if ($roundedValue !== null) { + return $roundedValue; + } + + if (preg_match('/^(?add|sub)(?[A-Z].*)$/', $method, $match)) { + $value = $this->getMagicParameter($parameters, 0, Carbon::pluralUnit($match['unit']), 0); + + return $this->{$match['method']}($value, $match['unit']); + } + + $value = $this->getMagicParameter($parameters, 0, Carbon::pluralUnit($method), 1); + + try { + $this->set($method, $value); + } catch (UnknownSetterException $exception) { + if ($this->localStrictModeEnabled ?? Carbon::isStrictModeEnabled()) { + throw new BadFluentSetterException($method, 0, $exception); + } + } + + return $this; + } + + protected function getForHumansInitialVariables($syntax, $short) + { + if (\is_array($syntax)) { + return $syntax; + } + + if (\is_int($short)) { + return [ + 'parts' => $short, + 'short' => false, + ]; + } + + if (\is_bool($syntax)) { + return [ + 'short' => $syntax, + 'syntax' => CarbonInterface::DIFF_ABSOLUTE, + ]; + } + + return []; + } + + /** + * @param mixed $syntax + * @param mixed $short + * @param mixed $parts + * @param mixed $options + * + * @return array + */ + protected function getForHumansParameters($syntax = null, $short = false, $parts = -1, $options = null) + { + $optionalSpace = ' '; + $default = $this->getTranslationMessage('list.0') ?? $this->getTranslationMessage('list') ?? ' '; + $join = $default === '' ? '' : ' '; + $altNumbers = false; + $aUnit = false; + $minimumUnit = 's'; + $skip = []; + extract($this->getForHumansInitialVariables($syntax, $short)); + $skip = array_map('strtolower', array_filter((array) $skip, static function ($value) { + return \is_string($value) && $value !== ''; + })); + + if ($syntax === null) { + $syntax = CarbonInterface::DIFF_ABSOLUTE; + } + + if ($parts === -1) { + $parts = INF; + } + + if ($options === null) { + $options = static::getHumanDiffOptions(); + } + + if ($join === false) { + $join = ' '; + } elseif ($join === true) { + $join = [ + $default, + $this->getTranslationMessage('list.1') ?? $default, + ]; + } + + if ($altNumbers && $altNumbers !== true) { + $language = new Language($this->locale); + $altNumbers = \in_array($language->getCode(), (array) $altNumbers, true); + } + + if (\is_array($join)) { + [$default, $last] = $join; + + if ($default !== ' ') { + $optionalSpace = ''; + } + + $join = function ($list) use ($default, $last) { + if (\count($list) < 2) { + return implode('', $list); + } + + $end = array_pop($list); + + return implode($default, $list).$last.$end; + }; + } + + if (\is_string($join)) { + if ($join !== ' ') { + $optionalSpace = ''; + } + + $glue = $join; + $join = function ($list) use ($glue) { + return implode($glue, $list); + }; + } + + $interpolations = [ + ':optional-space' => $optionalSpace, + ]; + + return [$syntax, $short, $parts, $options, $join, $aUnit, $altNumbers, $interpolations, $minimumUnit, $skip]; + } + + protected static function getRoundingMethodFromOptions(int $options): ?string + { + if ($options & CarbonInterface::ROUND) { + return 'round'; + } + + if ($options & CarbonInterface::CEIL) { + return 'ceil'; + } + + if ($options & CarbonInterface::FLOOR) { + return 'floor'; + } + + return null; + } + + /** + * Returns interval values as an array where key are the unit names and values the counts. + * + * @return int[] + */ + public function toArray() + { + return [ + 'years' => $this->years, + 'months' => $this->months, + 'weeks' => $this->weeks, + 'days' => $this->daysExcludeWeeks, + 'hours' => $this->hours, + 'minutes' => $this->minutes, + 'seconds' => $this->seconds, + 'microseconds' => $this->microseconds, + ]; + } + + /** + * Returns interval non-zero values as an array where key are the unit names and values the counts. + * + * @return int[] + */ + public function getNonZeroValues() + { + return array_filter($this->toArray(), 'intval'); + } + + /** + * Returns interval values as an array where key are the unit names and values the counts + * from the biggest non-zero one the the smallest non-zero one. + * + * @return int[] + */ + public function getValuesSequence() + { + $nonZeroValues = $this->getNonZeroValues(); + + if ($nonZeroValues === []) { + return []; + } + + $keys = array_keys($nonZeroValues); + $firstKey = $keys[0]; + $lastKey = $keys[\count($keys) - 1]; + $values = []; + $record = false; + + foreach ($this->toArray() as $unit => $count) { + if ($unit === $firstKey) { + $record = true; + } + + if ($record) { + $values[$unit] = $count; + } + + if ($unit === $lastKey) { + $record = false; + } + } + + return $values; + } + + /** + * Get the current interval in a human readable format in the current locale. + * + * @example + * ``` + * echo CarbonInterval::fromString('4d 3h 40m')->forHumans() . "\n"; + * echo CarbonInterval::fromString('4d 3h 40m')->forHumans(['parts' => 2]) . "\n"; + * echo CarbonInterval::fromString('4d 3h 40m')->forHumans(['parts' => 3, 'join' => true]) . "\n"; + * echo CarbonInterval::fromString('4d 3h 40m')->forHumans(['short' => true]) . "\n"; + * echo CarbonInterval::fromString('1d 24h')->forHumans(['join' => ' or ']) . "\n"; + * echo CarbonInterval::fromString('1d 24h')->forHumans(['minimumUnit' => 'hour']) . "\n"; + * ``` + * + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'skip' entry, list of units to skip (array of strings or a single string, + * ` it can be the unit name (singular or plural) or its shortcut + * ` (y, m, w, d, h, min, s, ms, µs). + * - 'aUnit' entry, prefer "an hour" over "1 hour" if true + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * - 'minimumUnit' entry determines the smallest unit of time to display can be long or + * ` short form of the units, e.g. 'hour' or 'h' (default value: s) + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: -1: no limits) + * @param int $options human diff options + * + * @throws Exception + * + * @return string + */ + public function forHumans($syntax = null, $short = false, $parts = -1, $options = null) + { + [$syntax, $short, $parts, $options, $join, $aUnit, $altNumbers, $interpolations, $minimumUnit, $skip] = $this + ->getForHumansParameters($syntax, $short, $parts, $options); + + $interval = []; + + $syntax = (int) ($syntax ?? CarbonInterface::DIFF_ABSOLUTE); + $absolute = $syntax === CarbonInterface::DIFF_ABSOLUTE; + $relativeToNow = $syntax === CarbonInterface::DIFF_RELATIVE_TO_NOW; + $count = 1; + $unit = $short ? 's' : 'second'; + $isFuture = $this->invert === 1; + $transId = $relativeToNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before'); + $declensionMode = null; + + /** @var \Symfony\Component\Translation\Translator $translator */ + $translator = $this->getLocalTranslator(); + + $handleDeclensions = function ($unit, $count, $index = 0, $parts = 1) use ($interpolations, $transId, $translator, $altNumbers, $absolute, &$declensionMode) { + if (!$absolute) { + $declensionMode = $declensionMode ?? $this->translate($transId.'_mode'); + + if ($this->needsDeclension($declensionMode, $index, $parts)) { + // Some languages have special pluralization for past and future tense. + $key = $unit.'_'.$transId; + $result = $this->translate($key, $interpolations, $count, $translator, $altNumbers); + + if ($result !== $key) { + return $result; + } + } + } + + $result = $this->translate($unit, $interpolations, $count, $translator, $altNumbers); + + if ($result !== $unit) { + return $result; + } + + return null; + }; + + $intervalValues = $this; + $method = static::getRoundingMethodFromOptions($options); + + if ($method) { + $previousCount = INF; + + while ( + \count($intervalValues->getNonZeroValues()) > $parts && + ($count = \count($keys = array_keys($intervalValues->getValuesSequence()))) > 1 + ) { + $index = min($count, $previousCount - 1) - 2; + + if ($index < 0) { + break; + } + + $intervalValues = $this->copy()->roundUnit( + $keys[$index], + 1, + $method + ); + $previousCount = $count; + } + } + + $diffIntervalArray = [ + ['value' => $intervalValues->years, 'unit' => 'year', 'unitShort' => 'y'], + ['value' => $intervalValues->months, 'unit' => 'month', 'unitShort' => 'm'], + ['value' => $intervalValues->weeks, 'unit' => 'week', 'unitShort' => 'w'], + ['value' => $intervalValues->daysExcludeWeeks, 'unit' => 'day', 'unitShort' => 'd'], + ['value' => $intervalValues->hours, 'unit' => 'hour', 'unitShort' => 'h'], + ['value' => $intervalValues->minutes, 'unit' => 'minute', 'unitShort' => 'min'], + ['value' => $intervalValues->seconds, 'unit' => 'second', 'unitShort' => 's'], + ['value' => $intervalValues->milliseconds, 'unit' => 'millisecond', 'unitShort' => 'ms'], + ['value' => $intervalValues->microExcludeMilli, 'unit' => 'microsecond', 'unitShort' => 'µs'], + ]; + + if (!empty($skip)) { + foreach ($diffIntervalArray as $index => &$unitData) { + $nextIndex = $index + 1; + + if ($unitData['value'] && + isset($diffIntervalArray[$nextIndex]) && + \count(array_intersect([$unitData['unit'], $unitData['unit'].'s', $unitData['unitShort']], $skip)) + ) { + $diffIntervalArray[$nextIndex]['value'] += $unitData['value'] * + self::getFactorWithDefault($diffIntervalArray[$nextIndex]['unit'], $unitData['unit']); + $unitData['value'] = 0; + } + } + } + + $transChoice = function ($short, $unitData, $index, $parts) use ($absolute, $handleDeclensions, $translator, $aUnit, $altNumbers, $interpolations) { + $count = $unitData['value']; + + if ($short) { + $result = $handleDeclensions($unitData['unitShort'], $count, $index, $parts); + + if ($result !== null) { + return $result; + } + } elseif ($aUnit) { + $result = $handleDeclensions('a_'.$unitData['unit'], $count, $index, $parts); + + if ($result !== null) { + return $result; + } + } + + if (!$absolute) { + return $handleDeclensions($unitData['unit'], $count, $index, $parts); + } + + return $this->translate($unitData['unit'], $interpolations, $count, $translator, $altNumbers); + }; + + $fallbackUnit = ['second', 's']; + + foreach ($diffIntervalArray as $diffIntervalData) { + if ($diffIntervalData['value'] > 0) { + $unit = $short ? $diffIntervalData['unitShort'] : $diffIntervalData['unit']; + $count = $diffIntervalData['value']; + $interval[] = [$short, $diffIntervalData]; + } elseif ($options & CarbonInterface::SEQUENTIAL_PARTS_ONLY && \count($interval) > 0) { + break; + } + + // break the loop after we get the required number of parts in array + if (\count($interval) >= $parts) { + break; + } + + // break the loop after we have reached the minimum unit + if (\in_array($minimumUnit, [$diffIntervalData['unit'], $diffIntervalData['unitShort']], true)) { + $fallbackUnit = [$diffIntervalData['unit'], $diffIntervalData['unitShort']]; + + break; + } + } + + $actualParts = \count($interval); + + foreach ($interval as $index => &$item) { + $item = $transChoice($item[0], $item[1], $index, $actualParts); + } + + if (\count($interval) === 0) { + if ($relativeToNow && $options & CarbonInterface::JUST_NOW) { + $key = 'diff_now'; + $translation = $this->translate($key, $interpolations, null, $translator); + + if ($translation !== $key) { + return $translation; + } + } + + $count = $options & CarbonInterface::NO_ZERO_DIFF ? 1 : 0; + $unit = $fallbackUnit[$short ? 1 : 0]; + $interval[] = $this->translate($unit, $interpolations, $count, $translator, $altNumbers); + } + + // join the interval parts by a space + $time = $join($interval); + + unset($diffIntervalArray, $interval); + + if ($absolute) { + return $time; + } + + $isFuture = $this->invert === 1; + + $transId = $relativeToNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before'); + + if ($parts === 1) { + if ($relativeToNow && $unit === 'day') { + if ($count === 1 && $options & CarbonInterface::ONE_DAY_WORDS) { + $key = $isFuture ? 'diff_tomorrow' : 'diff_yesterday'; + $translation = $this->translate($key, $interpolations, null, $translator); + + if ($translation !== $key) { + return $translation; + } + } + + if ($count === 2 && $options & CarbonInterface::TWO_DAY_WORDS) { + $key = $isFuture ? 'diff_after_tomorrow' : 'diff_before_yesterday'; + $translation = $this->translate($key, $interpolations, null, $translator); + + if ($translation !== $key) { + return $translation; + } + } + } + + $aTime = $aUnit ? $handleDeclensions('a_'.$unit, $count) : null; + + $time = $aTime ?: $handleDeclensions($unit, $count) ?: $time; + } + + $time = [':time' => $time]; + + return $this->translate($transId, array_merge($time, $interpolations, $time), null, $translator); + } + + /** + * Format the instance as a string using the forHumans() function. + * + * @throws Exception + * + * @return string + */ + public function __toString() + { + $format = $this->localToStringFormat ?? static::$toStringFormat; + + if (!$format) { + return $this->forHumans(); + } + + if ($format instanceof Closure) { + return $format($this); + } + + return $this->format($format); + } + + /** + * Return native DateInterval PHP object matching the current instance. + * + * @example + * ``` + * var_dump(CarbonInterval::hours(2)->toDateInterval()); + * ``` + * + * @return DateInterval + */ + public function toDateInterval() + { + return self::castIntervalToClass($this, DateInterval::class); + } + + /** + * Convert the interval to a CarbonPeriod. + * + * @param DateTimeInterface|string|int ...$params Start date, [end date or recurrences] and optional settings. + * + * @return CarbonPeriod + */ + public function toPeriod(...$params) + { + if ($this->tzName) { + $tz = \is_string($this->tzName) ? new DateTimeZone($this->tzName) : $this->tzName; + + if ($tz instanceof DateTimeZone) { + array_unshift($params, $tz); + } + } + + return CarbonPeriod::create($this, ...$params); + } + + /** + * Invert the interval. + * + * @param bool|int $inverted if a parameter is passed, the passed value cast as 1 or 0 is used + * as the new value of the ->invert property. + * + * @return $this + */ + public function invert($inverted = null) + { + $this->invert = (\func_num_args() === 0 ? !$this->invert : $inverted) ? 1 : 0; + + return $this; + } + + protected function solveNegativeInterval() + { + if (!$this->isEmpty() && $this->years <= 0 && $this->months <= 0 && $this->dayz <= 0 && $this->hours <= 0 && $this->minutes <= 0 && $this->seconds <= 0 && $this->microseconds <= 0) { + $this->years *= -1; + $this->months *= -1; + $this->dayz *= -1; + $this->hours *= -1; + $this->minutes *= -1; + $this->seconds *= -1; + $this->microseconds *= -1; + $this->invert(); + } + + return $this; + } + + /** + * Add the passed interval to the current instance. + * + * @param string|DateInterval $unit + * @param int|float $value + * + * @return $this + */ + public function add($unit, $value = 1) + { + if (is_numeric($unit)) { + [$value, $unit] = [$unit, $value]; + } + + if (\is_string($unit) && !preg_match('/^\s*\d/', $unit)) { + $unit = "$value $unit"; + $value = 1; + } + + $interval = static::make($unit); + + if (!$interval) { + throw new InvalidIntervalException('This type of data cannot be added/subtracted.'); + } + + if ($value !== 1) { + $interval->times($value); + } + + $sign = ($this->invert === 1) !== ($interval->invert === 1) ? -1 : 1; + $this->years += $interval->y * $sign; + $this->months += $interval->m * $sign; + $this->dayz += ($interval->days === false ? $interval->d : $interval->days) * $sign; + $this->hours += $interval->h * $sign; + $this->minutes += $interval->i * $sign; + $this->seconds += $interval->s * $sign; + $this->microseconds += $interval->microseconds * $sign; + + $this->solveNegativeInterval(); + + return $this; + } + + /** + * Subtract the passed interval to the current instance. + * + * @param string|DateInterval $unit + * @param int|float $value + * + * @return $this + */ + public function sub($unit, $value = 1) + { + if (is_numeric($unit)) { + [$value, $unit] = [$unit, $value]; + } + + return $this->add($unit, -(float) $value); + } + + /** + * Subtract the passed interval to the current instance. + * + * @param string|DateInterval $unit + * @param int|float $value + * + * @return $this + */ + public function subtract($unit, $value = 1) + { + return $this->sub($unit, $value); + } + + /** + * Add given parameters to the current interval. + * + * @param int $years + * @param int $months + * @param int|float $weeks + * @param int|float $days + * @param int|float $hours + * @param int|float $minutes + * @param int|float $seconds + * @param int|float $microseconds + * + * @return $this + */ + public function plus( + $years = 0, + $months = 0, + $weeks = 0, + $days = 0, + $hours = 0, + $minutes = 0, + $seconds = 0, + $microseconds = 0 + ): self { + return $this->add(" + $years years $months months $weeks weeks $days days + $hours hours $minutes minutes $seconds seconds $microseconds microseconds + "); + } + + /** + * Add given parameters to the current interval. + * + * @param int $years + * @param int $months + * @param int|float $weeks + * @param int|float $days + * @param int|float $hours + * @param int|float $minutes + * @param int|float $seconds + * @param int|float $microseconds + * + * @return $this + */ + public function minus( + $years = 0, + $months = 0, + $weeks = 0, + $days = 0, + $hours = 0, + $minutes = 0, + $seconds = 0, + $microseconds = 0 + ): self { + return $this->sub(" + $years years $months months $weeks weeks $days days + $hours hours $minutes minutes $seconds seconds $microseconds microseconds + "); + } + + /** + * Multiply current instance given number of times. times() is naive, it multiplies each unit + * (so day can be greater than 31, hour can be greater than 23, etc.) and the result is rounded + * separately for each unit. + * + * Use times() when you want a fast and approximated calculation that does not cascade units. + * + * For a precise and cascaded calculation, + * + * @see multiply() + * + * @param float|int $factor + * + * @return $this + */ + public function times($factor) + { + if ($factor < 0) { + $this->invert = $this->invert ? 0 : 1; + $factor = -$factor; + } + + $this->years = (int) round($this->years * $factor); + $this->months = (int) round($this->months * $factor); + $this->dayz = (int) round($this->dayz * $factor); + $this->hours = (int) round($this->hours * $factor); + $this->minutes = (int) round($this->minutes * $factor); + $this->seconds = (int) round($this->seconds * $factor); + $this->microseconds = (int) round($this->microseconds * $factor); + + return $this; + } + + /** + * Divide current instance by a given divider. shares() is naive, it divides each unit separately + * and the result is rounded for each unit. So 5 hours and 20 minutes shared by 3 becomes 2 hours + * and 7 minutes. + * + * Use shares() when you want a fast and approximated calculation that does not cascade units. + * + * For a precise and cascaded calculation, + * + * @see divide() + * + * @param float|int $divider + * + * @return $this + */ + public function shares($divider) + { + return $this->times(1 / $divider); + } + + protected function copyProperties(self $interval, $ignoreSign = false) + { + $this->years = $interval->years; + $this->months = $interval->months; + $this->dayz = $interval->dayz; + $this->hours = $interval->hours; + $this->minutes = $interval->minutes; + $this->seconds = $interval->seconds; + $this->microseconds = $interval->microseconds; + + if (!$ignoreSign) { + $this->invert = $interval->invert; + } + + return $this; + } + + /** + * Multiply and cascade current instance by a given factor. + * + * @param float|int $factor + * + * @return $this + */ + public function multiply($factor) + { + if ($factor < 0) { + $this->invert = $this->invert ? 0 : 1; + $factor = -$factor; + } + + $yearPart = (int) floor($this->years * $factor); // Split calculation to prevent imprecision + + if ($yearPart) { + $this->years -= $yearPart / $factor; + } + + return $this->copyProperties( + static::create($yearPart) + ->microseconds(abs($this->totalMicroseconds) * $factor) + ->cascade(), + true + ); + } + + /** + * Divide and cascade current instance by a given divider. + * + * @param float|int $divider + * + * @return $this + */ + public function divide($divider) + { + return $this->multiply(1 / $divider); + } + + /** + * Get the interval_spec string of a date interval. + * + * @param DateInterval $interval + * + * @return string + */ + public static function getDateIntervalSpec(DateInterval $interval, bool $microseconds = false, array $skip = []) + { + $date = array_filter([ + static::PERIOD_YEARS => abs($interval->y), + static::PERIOD_MONTHS => abs($interval->m), + static::PERIOD_DAYS => abs($interval->d), + ]); + + if ( + $interval->days >= CarbonInterface::DAYS_PER_WEEK * CarbonInterface::WEEKS_PER_MONTH && + (!isset($date[static::PERIOD_YEARS]) || \count(array_intersect(['y', 'year', 'years'], $skip))) && + (!isset($date[static::PERIOD_MONTHS]) || \count(array_intersect(['m', 'month', 'months'], $skip))) + ) { + $date = [ + static::PERIOD_DAYS => abs($interval->days), + ]; + } + + $seconds = abs($interval->s); + if ($microseconds && $interval->f > 0) { + $seconds = sprintf('%d.%06d', $seconds, abs($interval->f) * 1000000); + } + + $time = array_filter([ + static::PERIOD_HOURS => abs($interval->h), + static::PERIOD_MINUTES => abs($interval->i), + static::PERIOD_SECONDS => $seconds, + ]); + + $specString = static::PERIOD_PREFIX; + + foreach ($date as $key => $value) { + $specString .= $value.$key; + } + + if (\count($time) > 0) { + $specString .= static::PERIOD_TIME_PREFIX; + foreach ($time as $key => $value) { + $specString .= $value.$key; + } + } + + return $specString === static::PERIOD_PREFIX ? 'PT0S' : $specString; + } + + /** + * Get the interval_spec string. + * + * @return string + */ + public function spec(bool $microseconds = false) + { + return static::getDateIntervalSpec($this, $microseconds); + } + + /** + * Comparing 2 date intervals. + * + * @param DateInterval $first + * @param DateInterval $second + * + * @return int + */ + public static function compareDateIntervals(DateInterval $first, DateInterval $second) + { + $current = Carbon::now(); + $passed = $current->avoidMutation()->add($second); + $current->add($first); + + if ($current < $passed) { + return -1; + } + if ($current > $passed) { + return 1; + } + + return 0; + } + + /** + * Comparing with passed interval. + * + * @param DateInterval $interval + * + * @return int + */ + public function compare(DateInterval $interval) + { + return static::compareDateIntervals($this, $interval); + } + + private function invertCascade(array $values) + { + return $this->set(array_map(function ($value) { + return -$value; + }, $values))->doCascade(true)->invert(); + } + + private function doCascade(bool $deep) + { + $originalData = $this->toArray(); + $originalData['milliseconds'] = (int) ($originalData['microseconds'] / static::getMicrosecondsPerMillisecond()); + $originalData['microseconds'] = $originalData['microseconds'] % static::getMicrosecondsPerMillisecond(); + $originalData['weeks'] = (int) ($this->d / static::getDaysPerWeek()); + $originalData['daysExcludeWeeks'] = fmod($this->d, static::getDaysPerWeek()); + unset($originalData['days']); + $newData = $originalData; + $previous = []; + + foreach (self::getFlipCascadeFactors() as $source => [$target, $factor]) { + foreach (['source', 'target'] as $key) { + if ($$key === 'dayz') { + $$key = 'daysExcludeWeeks'; + } + } + + $value = $newData[$source]; + $modulo = fmod($factor + fmod($value, $factor), $factor); + $newData[$source] = $modulo; + $newData[$target] += ($value - $modulo) / $factor; + + $decimalPart = fmod($newData[$source], 1); + + if ($decimalPart !== 0.0) { + $unit = $source; + + foreach ($previous as [$subUnit, $subFactor]) { + $newData[$unit] -= $decimalPart; + $newData[$subUnit] += $decimalPart * $subFactor; + $decimalPart = fmod($newData[$subUnit], 1); + + if ($decimalPart === 0.0) { + break; + } + + $unit = $subUnit; + } + } + + array_unshift($previous, [$source, $factor]); + } + + $positive = null; + + if (!$deep) { + foreach ($newData as $value) { + if ($value) { + if ($positive === null) { + $positive = ($value > 0); + + continue; + } + + if (($value > 0) !== $positive) { + return $this->invertCascade($originalData) + ->solveNegativeInterval(); + } + } + } + } + + return $this->set($newData) + ->solveNegativeInterval(); + } + + /** + * Convert overflowed values into bigger units. + * + * @return $this + */ + public function cascade() + { + return $this->doCascade(false); + } + + public function hasNegativeValues(): bool + { + foreach ($this->toArray() as $value) { + if ($value < 0) { + return true; + } + } + + return false; + } + + public function hasPositiveValues(): bool + { + foreach ($this->toArray() as $value) { + if ($value > 0) { + return true; + } + } + + return false; + } + + /** + * Get amount of given unit equivalent to the interval. + * + * @param string $unit + * + * @throws UnknownUnitException|UnitNotConfiguredException + * + * @return float + */ + public function total($unit) + { + $realUnit = $unit = strtolower($unit); + + if (\in_array($unit, ['days', 'weeks'])) { + $realUnit = 'dayz'; + } elseif (!\in_array($unit, ['microseconds', 'milliseconds', 'seconds', 'minutes', 'hours', 'dayz', 'months', 'years'])) { + throw new UnknownUnitException($unit); + } + + $result = 0; + $cumulativeFactor = 0; + $unitFound = false; + $factors = self::getFlipCascadeFactors(); + $daysPerWeek = (int) static::getDaysPerWeek(); + + $values = [ + 'years' => $this->years, + 'months' => $this->months, + 'weeks' => (int) ($this->d / $daysPerWeek), + 'dayz' => fmod($this->d, $daysPerWeek), + 'hours' => $this->hours, + 'minutes' => $this->minutes, + 'seconds' => $this->seconds, + 'milliseconds' => (int) ($this->microseconds / Carbon::MICROSECONDS_PER_MILLISECOND), + 'microseconds' => $this->microseconds % Carbon::MICROSECONDS_PER_MILLISECOND, + ]; + + if (isset($factors['dayz']) && $factors['dayz'][0] !== 'weeks') { + $values['dayz'] += $values['weeks'] * $daysPerWeek; + $values['weeks'] = 0; + } + + foreach ($factors as $source => [$target, $factor]) { + if ($source === $realUnit) { + $unitFound = true; + $value = $values[$source]; + $result += $value; + $cumulativeFactor = 1; + } + + if ($factor === false) { + if ($unitFound) { + break; + } + + $result = 0; + $cumulativeFactor = 0; + + continue; + } + + if ($target === $realUnit) { + $unitFound = true; + } + + if ($cumulativeFactor) { + $cumulativeFactor *= $factor; + $result += $values[$target] * $cumulativeFactor; + + continue; + } + + $value = $values[$source]; + + $result = ($result + $value) / $factor; + } + + if (isset($target) && !$cumulativeFactor) { + $result += $values[$target]; + } + + if (!$unitFound) { + throw new UnitNotConfiguredException($unit); + } + + if ($this->invert) { + $result *= -1; + } + + if ($unit === 'weeks') { + $result /= $daysPerWeek; + } + + // Cast as int numbers with no decimal part + return fmod($result, 1) === 0.0 ? (int) $result : $result; + } + + /** + * Determines if the instance is equal to another + * + * @param CarbonInterval|DateInterval|mixed $interval + * + * @see equalTo() + * + * @return bool + */ + public function eq($interval): bool + { + return $this->equalTo($interval); + } + + /** + * Determines if the instance is equal to another + * + * @param CarbonInterval|DateInterval|mixed $interval + * + * @return bool + */ + public function equalTo($interval): bool + { + $interval = $this->resolveInterval($interval); + + return $interval !== null && $this->totalMicroseconds === $interval->totalMicroseconds; + } + + /** + * Determines if the instance is not equal to another + * + * @param CarbonInterval|DateInterval|mixed $interval + * + * @see notEqualTo() + * + * @return bool + */ + public function ne($interval): bool + { + return $this->notEqualTo($interval); + } + + /** + * Determines if the instance is not equal to another + * + * @param CarbonInterval|DateInterval|mixed $interval + * + * @return bool + */ + public function notEqualTo($interval): bool + { + return !$this->eq($interval); + } + + /** + * Determines if the instance is greater (longer) than another + * + * @param CarbonInterval|DateInterval|mixed $interval + * + * @see greaterThan() + * + * @return bool + */ + public function gt($interval): bool + { + return $this->greaterThan($interval); + } + + /** + * Determines if the instance is greater (longer) than another + * + * @param CarbonInterval|DateInterval|mixed $interval + * + * @return bool + */ + public function greaterThan($interval): bool + { + $interval = $this->resolveInterval($interval); + + return $interval === null || $this->totalMicroseconds > $interval->totalMicroseconds; + } + + /** + * Determines if the instance is greater (longer) than or equal to another + * + * @param CarbonInterval|DateInterval|mixed $interval + * + * @see greaterThanOrEqualTo() + * + * @return bool + */ + public function gte($interval): bool + { + return $this->greaterThanOrEqualTo($interval); + } + + /** + * Determines if the instance is greater (longer) than or equal to another + * + * @param CarbonInterval|DateInterval|mixed $interval + * + * @return bool + */ + public function greaterThanOrEqualTo($interval): bool + { + return $this->greaterThan($interval) || $this->equalTo($interval); + } + + /** + * Determines if the instance is less (shorter) than another + * + * @param CarbonInterval|DateInterval|mixed $interval + * + * @see lessThan() + * + * @return bool + */ + public function lt($interval): bool + { + return $this->lessThan($interval); + } + + /** + * Determines if the instance is less (shorter) than another + * + * @param CarbonInterval|DateInterval|mixed $interval + * + * @return bool + */ + public function lessThan($interval): bool + { + $interval = $this->resolveInterval($interval); + + return $interval !== null && $this->totalMicroseconds < $interval->totalMicroseconds; + } + + /** + * Determines if the instance is less (shorter) than or equal to another + * + * @param CarbonInterval|DateInterval|mixed $interval + * + * @see lessThanOrEqualTo() + * + * @return bool + */ + public function lte($interval): bool + { + return $this->lessThanOrEqualTo($interval); + } + + /** + * Determines if the instance is less (shorter) than or equal to another + * + * @param CarbonInterval|DateInterval|mixed $interval + * + * @return bool + */ + public function lessThanOrEqualTo($interval): bool + { + return $this->lessThan($interval) || $this->equalTo($interval); + } + + /** + * Determines if the instance is between two others. + * + * The third argument allow you to specify if bounds are included or not (true by default) + * but for when you including/excluding bounds may produce different results in your application, + * we recommend to use the explicit methods ->betweenIncluded() or ->betweenExcluded() instead. + * + * @example + * ``` + * CarbonInterval::hours(48)->between(CarbonInterval::day(), CarbonInterval::days(3)); // true + * CarbonInterval::hours(48)->between(CarbonInterval::day(), CarbonInterval::hours(36)); // false + * CarbonInterval::hours(48)->between(CarbonInterval::day(), CarbonInterval::days(2)); // true + * CarbonInterval::hours(48)->between(CarbonInterval::day(), CarbonInterval::days(2), false); // false + * ``` + * + * @param CarbonInterval|DateInterval|mixed $interval1 + * @param CarbonInterval|DateInterval|mixed $interval2 + * @param bool $equal Indicates if an equal to comparison should be done + * + * @return bool + */ + public function between($interval1, $interval2, $equal = true): bool + { + return $equal + ? $this->greaterThanOrEqualTo($interval1) && $this->lessThanOrEqualTo($interval2) + : $this->greaterThan($interval1) && $this->lessThan($interval2); + } + + /** + * Determines if the instance is between two others, bounds excluded. + * + * @example + * ``` + * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::days(3)); // true + * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::hours(36)); // false + * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::days(2)); // true + * ``` + * + * @param CarbonInterval|DateInterval|mixed $interval1 + * @param CarbonInterval|DateInterval|mixed $interval2 + * + * @return bool + */ + public function betweenIncluded($interval1, $interval2): bool + { + return $this->between($interval1, $interval2, true); + } + + /** + * Determines if the instance is between two others, bounds excluded. + * + * @example + * ``` + * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::days(3)); // true + * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::hours(36)); // false + * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::days(2)); // false + * ``` + * + * @param CarbonInterval|DateInterval|mixed $interval1 + * @param CarbonInterval|DateInterval|mixed $interval2 + * + * @return bool + */ + public function betweenExcluded($interval1, $interval2): bool + { + return $this->between($interval1, $interval2, false); + } + + /** + * Determines if the instance is between two others + * + * @example + * ``` + * CarbonInterval::hours(48)->isBetween(CarbonInterval::day(), CarbonInterval::days(3)); // true + * CarbonInterval::hours(48)->isBetween(CarbonInterval::day(), CarbonInterval::hours(36)); // false + * CarbonInterval::hours(48)->isBetween(CarbonInterval::day(), CarbonInterval::days(2)); // true + * CarbonInterval::hours(48)->isBetween(CarbonInterval::day(), CarbonInterval::days(2), false); // false + * ``` + * + * @param CarbonInterval|DateInterval|mixed $interval1 + * @param CarbonInterval|DateInterval|mixed $interval2 + * @param bool $equal Indicates if an equal to comparison should be done + * + * @return bool + */ + public function isBetween($interval1, $interval2, $equal = true): bool + { + return $this->between($interval1, $interval2, $equal); + } + + /** + * Round the current instance at the given unit with given precision if specified and the given function. + * + * @param string $unit + * @param float|int|string|DateInterval|null $precision + * @param string $function + * + * @throws Exception + * + * @return $this + */ + public function roundUnit($unit, $precision = 1, $function = 'round') + { + if (static::getCascadeFactors() !== static::getDefaultCascadeFactors()) { + $value = $function($this->total($unit) / $precision) * $precision; + $inverted = $value < 0; + + return $this->copyProperties(self::fromString( + number_format(abs($value), 12, '.', '').' '.$unit + )->invert($inverted)->cascade()); + } + + $base = CarbonImmutable::parse('2000-01-01 00:00:00', 'UTC') + ->roundUnit($unit, $precision, $function); + $next = $base->add($this); + $inverted = $next < $base; + + if ($inverted) { + $next = $base->sub($this); + } + + $this->copyProperties( + $next + ->roundUnit($unit, $precision, $function) + ->diffAsCarbonInterval($base) + ); + + return $this->invert($inverted); + } + + /** + * Truncate the current instance at the given unit with given precision if specified. + * + * @param string $unit + * @param float|int|string|DateInterval|null $precision + * + * @throws Exception + * + * @return $this + */ + public function floorUnit($unit, $precision = 1) + { + return $this->roundUnit($unit, $precision, 'floor'); + } + + /** + * Ceil the current instance at the given unit with given precision if specified. + * + * @param string $unit + * @param float|int|string|DateInterval|null $precision + * + * @throws Exception + * + * @return $this + */ + public function ceilUnit($unit, $precision = 1) + { + return $this->roundUnit($unit, $precision, 'ceil'); + } + + /** + * Round the current instance second with given precision if specified. + * + * @param float|int|string|DateInterval|null $precision + * @param string $function + * + * @throws Exception + * + * @return $this + */ + public function round($precision = 1, $function = 'round') + { + return $this->roundWith($precision, $function); + } + + /** + * Round the current instance second with given precision if specified. + * + * @param float|int|string|DateInterval|null $precision + * + * @throws Exception + * + * @return $this + */ + public function floor($precision = 1) + { + return $this->round($precision, 'floor'); + } + + /** + * Ceil the current instance second with given precision if specified. + * + * @param float|int|string|DateInterval|null $precision + * + * @throws Exception + * + * @return $this + */ + public function ceil($precision = 1) + { + return $this->round($precision, 'ceil'); + } + + private function needsDeclension(string $mode, int $index, int $parts): bool + { + switch ($mode) { + case 'last': + return $index === $parts - 1; + default: + return true; + } + } + + private function checkIntegerValue(string $name, $value) + { + if (\is_int($value)) { + return; + } + + $this->assertSafeForInteger($name, $value); + + if (\is_float($value) && (((float) (int) $value) === $value)) { + return; + } + + if (!self::$floatSettersEnabled) { + $type = \gettype($value); + @trigger_error( + "Since 2.70.0, it's deprecated to pass $type value for $name.\n". + "It's truncated when stored as an integer interval unit.\n". + "From 3.0.0, decimal part will no longer be truncated and will be cascaded to smaller units.\n". + "- To maintain the current behavior, use explicit cast: $name((int) \$value)\n". + "- To adopt the new behavior globally, call CarbonInterval::enableFloatSetters()\n", + \E_USER_DEPRECATED + ); + } + } + + /** + * Throw an exception if precision loss when storing the given value as an integer would be >= 1.0. + */ + private function assertSafeForInteger(string $name, $value) + { + if ($value && !\is_int($value) && ($value >= 0x7fffffffffffffff || $value <= -0x7fffffffffffffff)) { + throw new OutOfRangeException($name, -0x7fffffffffffffff, 0x7fffffffffffffff, $value); + } + } + + private function handleDecimalPart(string $unit, $value, $integerValue) + { + if (self::$floatSettersEnabled) { + $floatValue = (float) $value; + $base = (float) $integerValue; + + if ($floatValue === $base) { + return; + } + + $units = [ + 'y' => 'year', + 'm' => 'month', + 'd' => 'day', + 'h' => 'hour', + 'i' => 'minute', + 's' => 'second', + ]; + $upper = true; + + foreach ($units as $property => $name) { + if ($name === $unit) { + $upper = false; + + continue; + } + + if (!$upper && $this->$property !== 0) { + throw new RuntimeException( + "You cannot set $unit to a float value as $name would be overridden, ". + 'set it first to 0 explicitly if you really want to erase its value' + ); + } + } + + $this->add($unit, $floatValue - $base); + } + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php b/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php new file mode 100644 index 0000000..d12a986 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php @@ -0,0 +1,2742 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Carbon\Exceptions\EndLessPeriodException; +use Carbon\Exceptions\InvalidCastException; +use Carbon\Exceptions\InvalidIntervalException; +use Carbon\Exceptions\InvalidPeriodDateException; +use Carbon\Exceptions\InvalidPeriodParameterException; +use Carbon\Exceptions\NotACarbonClassException; +use Carbon\Exceptions\NotAPeriodException; +use Carbon\Exceptions\UnknownGetterException; +use Carbon\Exceptions\UnknownMethodException; +use Carbon\Exceptions\UnreachableException; +use Carbon\Traits\IntervalRounding; +use Carbon\Traits\Mixin; +use Carbon\Traits\Options; +use Carbon\Traits\ToStringFormat; +use Closure; +use Countable; +use DateInterval; +use DatePeriod; +use DateTime; +use DateTimeImmutable; +use DateTimeInterface; +use DateTimeZone; +use InvalidArgumentException; +use Iterator; +use JsonSerializable; +use ReflectionException; +use ReturnTypeWillChange; +use RuntimeException; + +/** + * Substitution of DatePeriod with some modifications and many more features. + * + * @property-read int|float $recurrences number of recurrences (if end not set). + * @property-read bool $include_start_date rather the start date is included in the iteration. + * @property-read bool $include_end_date rather the end date is included in the iteration (if recurrences not set). + * @property-read CarbonInterface $start Period start date. + * @property-read CarbonInterface $current Current date from the iteration. + * @property-read CarbonInterface $end Period end date. + * @property-read CarbonInterval $interval Underlying date interval instance. Always present, one day by default. + * + * @method static static start($date, $inclusive = null) Create instance specifying start date or modify the start date if called on an instance. + * @method static static since($date, $inclusive = null) Alias for start(). + * @method static static sinceNow($inclusive = null) Create instance with start date set to now or set the start date to now if called on an instance. + * @method static static end($date = null, $inclusive = null) Create instance specifying end date or modify the end date if called on an instance. + * @method static static until($date = null, $inclusive = null) Alias for end(). + * @method static static untilNow($inclusive = null) Create instance with end date set to now or set the end date to now if called on an instance. + * @method static static dates($start, $end = null) Create instance with start and end dates or modify the start and end dates if called on an instance. + * @method static static between($start, $end = null) Create instance with start and end dates or modify the start and end dates if called on an instance. + * @method static static recurrences($recurrences = null) Create instance with maximum number of recurrences or modify the number of recurrences if called on an instance. + * @method static static times($recurrences = null) Alias for recurrences(). + * @method static static options($options = null) Create instance with options or modify the options if called on an instance. + * @method static static toggle($options, $state = null) Create instance with options toggled on or off, or toggle options if called on an instance. + * @method static static filter($callback, $name = null) Create instance with filter added to the stack or append a filter if called on an instance. + * @method static static push($callback, $name = null) Alias for filter(). + * @method static static prepend($callback, $name = null) Create instance with filter prepended to the stack or prepend a filter if called on an instance. + * @method static static filters(array $filters = []) Create instance with filters stack or replace the whole filters stack if called on an instance. + * @method static static interval($interval) Create instance with given date interval or modify the interval if called on an instance. + * @method static static each($interval) Create instance with given date interval or modify the interval if called on an instance. + * @method static static every($interval) Create instance with given date interval or modify the interval if called on an instance. + * @method static static step($interval) Create instance with given date interval or modify the interval if called on an instance. + * @method static static stepBy($interval) Create instance with given date interval or modify the interval if called on an instance. + * @method static static invert() Create instance with inverted date interval or invert the interval if called on an instance. + * @method static static years($years = 1) Create instance specifying a number of years for date interval or replace the interval by the given a number of years if called on an instance. + * @method static static year($years = 1) Alias for years(). + * @method static static months($months = 1) Create instance specifying a number of months for date interval or replace the interval by the given a number of months if called on an instance. + * @method static static month($months = 1) Alias for months(). + * @method static static weeks($weeks = 1) Create instance specifying a number of weeks for date interval or replace the interval by the given a number of weeks if called on an instance. + * @method static static week($weeks = 1) Alias for weeks(). + * @method static static days($days = 1) Create instance specifying a number of days for date interval or replace the interval by the given a number of days if called on an instance. + * @method static static dayz($days = 1) Alias for days(). + * @method static static day($days = 1) Alias for days(). + * @method static static hours($hours = 1) Create instance specifying a number of hours for date interval or replace the interval by the given a number of hours if called on an instance. + * @method static static hour($hours = 1) Alias for hours(). + * @method static static minutes($minutes = 1) Create instance specifying a number of minutes for date interval or replace the interval by the given a number of minutes if called on an instance. + * @method static static minute($minutes = 1) Alias for minutes(). + * @method static static seconds($seconds = 1) Create instance specifying a number of seconds for date interval or replace the interval by the given a number of seconds if called on an instance. + * @method static static second($seconds = 1) Alias for seconds(). + * @method static static milliseconds($milliseconds = 1) Create instance specifying a number of milliseconds for date interval or replace the interval by the given a number of milliseconds if called on an instance. + * @method static static millisecond($milliseconds = 1) Alias for milliseconds(). + * @method static static microseconds($microseconds = 1) Create instance specifying a number of microseconds for date interval or replace the interval by the given a number of microseconds if called on an instance. + * @method static static microsecond($microseconds = 1) Alias for microseconds(). + * @method $this roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method $this roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method $this floorYear(float $precision = 1) Truncate the current instance year with given precision. + * @method $this floorYears(float $precision = 1) Truncate the current instance year with given precision. + * @method $this ceilYear(float $precision = 1) Ceil the current instance year with given precision. + * @method $this ceilYears(float $precision = 1) Ceil the current instance year with given precision. + * @method $this roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method $this roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method $this floorMonth(float $precision = 1) Truncate the current instance month with given precision. + * @method $this floorMonths(float $precision = 1) Truncate the current instance month with given precision. + * @method $this ceilMonth(float $precision = 1) Ceil the current instance month with given precision. + * @method $this ceilMonths(float $precision = 1) Ceil the current instance month with given precision. + * @method $this roundWeek(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method $this roundWeeks(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method $this floorWeek(float $precision = 1) Truncate the current instance day with given precision. + * @method $this floorWeeks(float $precision = 1) Truncate the current instance day with given precision. + * @method $this ceilWeek(float $precision = 1) Ceil the current instance day with given precision. + * @method $this ceilWeeks(float $precision = 1) Ceil the current instance day with given precision. + * @method $this roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method $this roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method $this floorDay(float $precision = 1) Truncate the current instance day with given precision. + * @method $this floorDays(float $precision = 1) Truncate the current instance day with given precision. + * @method $this ceilDay(float $precision = 1) Ceil the current instance day with given precision. + * @method $this ceilDays(float $precision = 1) Ceil the current instance day with given precision. + * @method $this roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method $this roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method $this floorHour(float $precision = 1) Truncate the current instance hour with given precision. + * @method $this floorHours(float $precision = 1) Truncate the current instance hour with given precision. + * @method $this ceilHour(float $precision = 1) Ceil the current instance hour with given precision. + * @method $this ceilHours(float $precision = 1) Ceil the current instance hour with given precision. + * @method $this roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method $this roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method $this floorMinute(float $precision = 1) Truncate the current instance minute with given precision. + * @method $this floorMinutes(float $precision = 1) Truncate the current instance minute with given precision. + * @method $this ceilMinute(float $precision = 1) Ceil the current instance minute with given precision. + * @method $this ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision. + * @method $this roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method $this roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method $this floorSecond(float $precision = 1) Truncate the current instance second with given precision. + * @method $this floorSeconds(float $precision = 1) Truncate the current instance second with given precision. + * @method $this ceilSecond(float $precision = 1) Ceil the current instance second with given precision. + * @method $this ceilSeconds(float $precision = 1) Ceil the current instance second with given precision. + * @method $this roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method $this roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method $this floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision. + * @method $this floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision. + * @method $this ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision. + * @method $this ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision. + * @method $this roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method $this roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method $this floorCentury(float $precision = 1) Truncate the current instance century with given precision. + * @method $this floorCenturies(float $precision = 1) Truncate the current instance century with given precision. + * @method $this ceilCentury(float $precision = 1) Ceil the current instance century with given precision. + * @method $this ceilCenturies(float $precision = 1) Ceil the current instance century with given precision. + * @method $this roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method $this roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method $this floorDecade(float $precision = 1) Truncate the current instance decade with given precision. + * @method $this floorDecades(float $precision = 1) Truncate the current instance decade with given precision. + * @method $this ceilDecade(float $precision = 1) Ceil the current instance decade with given precision. + * @method $this ceilDecades(float $precision = 1) Ceil the current instance decade with given precision. + * @method $this roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method $this roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method $this floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision. + * @method $this floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision. + * @method $this ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision. + * @method $this ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision. + * @method $this roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method $this roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method $this floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method $this floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method $this ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method $this ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method $this roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method $this roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method $this floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method $this floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method $this ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method $this ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class CarbonPeriod implements Iterator, Countable, JsonSerializable +{ + use IntervalRounding; + use Mixin { + Mixin::mixin as baseMixin; + } + use Options; + use ToStringFormat; + + /** + * Built-in filter for limit by recurrences. + * + * @var callable + */ + public const RECURRENCES_FILTER = [self::class, 'filterRecurrences']; + + /** + * Built-in filter for limit to an end. + * + * @var callable + */ + public const END_DATE_FILTER = [self::class, 'filterEndDate']; + + /** + * Special value which can be returned by filters to end iteration. Also a filter. + * + * @var callable + */ + public const END_ITERATION = [self::class, 'endIteration']; + + /** + * Exclude start date from iteration. + * + * @var int + */ + public const EXCLUDE_START_DATE = 1; + + /** + * Exclude end date from iteration. + * + * @var int + */ + public const EXCLUDE_END_DATE = 2; + + /** + * Yield CarbonImmutable instances. + * + * @var int + */ + public const IMMUTABLE = 4; + + /** + * Number of maximum attempts before giving up on finding next valid date. + * + * @var int + */ + public const NEXT_MAX_ATTEMPTS = 1000; + + /** + * Number of maximum attempts before giving up on finding end date. + * + * @var int + */ + public const END_MAX_ATTEMPTS = 10000; + + /** + * Default date class of iteration items. + * + * @var string + */ + protected const DEFAULT_DATE_CLASS = Carbon::class; + + /** + * The registered macros. + * + * @var array + */ + protected static $macros = []; + + /** + * Date class of iteration items. + * + * @var string + */ + protected $dateClass = Carbon::class; + + /** + * Underlying date interval instance. Always present, one day by default. + * + * @var CarbonInterval + */ + protected $dateInterval; + + /** + * True once __construct is finished. + * + * @var bool + */ + protected $constructed = false; + + /** + * Whether current date interval was set by default. + * + * @var bool + */ + protected $isDefaultInterval; + + /** + * The filters stack. + * + * @var array + */ + protected $filters = []; + + /** + * Period start date. Applied on rewind. Always present, now by default. + * + * @var CarbonInterface + */ + protected $startDate; + + /** + * Period end date. For inverted interval should be before the start date. Applied via a filter. + * + * @var CarbonInterface|null + */ + protected $endDate; + + /** + * Limit for number of recurrences. Applied via a filter. + * + * @var int|null + */ + protected $recurrences; + + /** + * Iteration options. + * + * @var int + */ + protected $options; + + /** + * Index of current date. Always sequential, even if some dates are skipped by filters. + * Equal to null only before the first iteration. + * + * @var int + */ + protected $key; + + /** + * Current date. May temporarily hold unaccepted value when looking for a next valid date. + * Equal to null only before the first iteration. + * + * @var CarbonInterface + */ + protected $current; + + /** + * Timezone of current date. Taken from the start date. + * + * @var \DateTimeZone|null + */ + protected $timezone; + + /** + * The cached validation result for current date. + * + * @var bool|string|null + */ + protected $validationResult; + + /** + * Timezone handler for settings() method. + * + * @var mixed + */ + protected $tzName; + + /** + * Make a CarbonPeriod instance from given variable if possible. + * + * @param mixed $var + * + * @return static|null + */ + public static function make($var) + { + try { + return static::instance($var); + } catch (NotAPeriodException $e) { + return static::create($var); + } + } + + /** + * Create a new instance from a DatePeriod or CarbonPeriod object. + * + * @param CarbonPeriod|DatePeriod $period + * + * @return static + */ + public static function instance($period) + { + if ($period instanceof static) { + return $period->copy(); + } + + if ($period instanceof self) { + return new static( + $period->getStartDate(), + $period->getEndDate() ?: $period->getRecurrences(), + $period->getDateInterval(), + $period->getOptions() + ); + } + + if ($period instanceof DatePeriod) { + return new static( + $period->start, + $period->end ?: ($period->recurrences - 1), + $period->interval, + $period->include_start_date ? 0 : static::EXCLUDE_START_DATE + ); + } + + $class = static::class; + $type = \gettype($period); + + throw new NotAPeriodException( + 'Argument 1 passed to '.$class.'::'.__METHOD__.'() '. + 'must be an instance of DatePeriod or '.$class.', '. + ($type === 'object' ? 'instance of '.\get_class($period) : $type).' given.' + ); + } + + /** + * Create a new instance. + * + * @return static + */ + public static function create(...$params) + { + return static::createFromArray($params); + } + + /** + * Create a new instance from an array of parameters. + * + * @param array $params + * + * @return static + */ + public static function createFromArray(array $params) + { + return new static(...$params); + } + + /** + * Create CarbonPeriod from ISO 8601 string. + * + * @param string $iso + * @param int|null $options + * + * @return static + */ + public static function createFromIso($iso, $options = null) + { + $params = static::parseIso8601($iso); + + $instance = static::createFromArray($params); + + if ($options !== null) { + $instance->setOptions($options); + } + + return $instance; + } + + /** + * Return whether given interval contains non zero value of any time unit. + * + * @param \DateInterval $interval + * + * @return bool + */ + protected static function intervalHasTime(DateInterval $interval) + { + return $interval->h || $interval->i || $interval->s || $interval->f; + } + + /** + * Return whether given variable is an ISO 8601 specification. + * + * Note: Check is very basic, as actual validation will be done later when parsing. + * We just want to ensure that variable is not any other type of a valid parameter. + * + * @param mixed $var + * + * @return bool + */ + protected static function isIso8601($var) + { + if (!\is_string($var)) { + return false; + } + + // Match slash but not within a timezone name. + $part = '[a-z]+(?:[_-][a-z]+)*'; + + preg_match("#\b$part/$part\b|(/)#i", $var, $match); + + return isset($match[1]); + } + + /** + * Parse given ISO 8601 string into an array of arguments. + * + * @SuppressWarnings(PHPMD.ElseExpression) + * + * @param string $iso + * + * @return array + */ + protected static function parseIso8601($iso) + { + $result = []; + + $interval = null; + $start = null; + $end = null; + $dateClass = static::DEFAULT_DATE_CLASS; + + foreach (explode('/', $iso) as $key => $part) { + if ($key === 0 && preg_match('/^R(\d*|INF)$/', $part, $match)) { + $parsed = \strlen($match[1]) ? (($match[1] !== 'INF') ? (int) $match[1] : INF) : null; + } elseif ($interval === null && $parsed = CarbonInterval::make($part)) { + $interval = $part; + } elseif ($start === null && $parsed = $dateClass::make($part)) { + $start = $part; + } elseif ($end === null && $parsed = $dateClass::make(static::addMissingParts($start ?? '', $part))) { + $end = $part; + } else { + throw new InvalidPeriodParameterException("Invalid ISO 8601 specification: $iso."); + } + + $result[] = $parsed; + } + + return $result; + } + + /** + * Add missing parts of the target date from the source date. + * + * @param string $source + * @param string $target + * + * @return string + */ + protected static function addMissingParts($source, $target) + { + $pattern = '/'.preg_replace('/\d+/', '[0-9]+', preg_quote($target, '/')).'$/'; + + $result = preg_replace($pattern, $target, $source, 1, $count); + + return $count ? $result : $target; + } + + /** + * Register a custom macro. + * + * @example + * ``` + * CarbonPeriod::macro('middle', function () { + * return $this->getStartDate()->average($this->getEndDate()); + * }); + * echo CarbonPeriod::since('2011-05-12')->until('2011-06-03')->middle(); + * ``` + * + * @param string $name + * @param object|callable $macro + * + * @return void + */ + public static function macro($name, $macro) + { + static::$macros[$name] = $macro; + } + + /** + * Register macros from a mixin object. + * + * @example + * ``` + * CarbonPeriod::mixin(new class { + * public function addDays() { + * return function ($count = 1) { + * return $this->setStartDate( + * $this->getStartDate()->addDays($count) + * )->setEndDate( + * $this->getEndDate()->addDays($count) + * ); + * }; + * } + * public function subDays() { + * return function ($count = 1) { + * return $this->setStartDate( + * $this->getStartDate()->subDays($count) + * )->setEndDate( + * $this->getEndDate()->subDays($count) + * ); + * }; + * } + * }); + * echo CarbonPeriod::create('2000-01-01', '2000-02-01')->addDays(5)->subDays(3); + * ``` + * + * @param object|string $mixin + * + * @throws ReflectionException + * + * @return void + */ + public static function mixin($mixin) + { + static::baseMixin($mixin); + } + + /** + * Check if macro is registered. + * + * @param string $name + * + * @return bool + */ + public static function hasMacro($name) + { + return isset(static::$macros[$name]); + } + + /** + * Provide static proxy for instance aliases. + * + * @param string $method + * @param array $parameters + * + * @return mixed + */ + public static function __callStatic($method, $parameters) + { + $date = new static(); + + if (static::hasMacro($method)) { + return static::bindMacroContext(null, function () use (&$method, &$parameters, &$date) { + return $date->callMacro($method, $parameters); + }); + } + + return $date->$method(...$parameters); + } + + /** + * CarbonPeriod constructor. + * + * @SuppressWarnings(PHPMD.ElseExpression) + * + * @throws InvalidArgumentException + */ + public function __construct(...$arguments) + { + if (is_a($this->dateClass, DateTimeImmutable::class, true)) { + $this->options = static::IMMUTABLE; + } + + // Parse and assign arguments one by one. First argument may be an ISO 8601 spec, + // which will be first parsed into parts and then processed the same way. + + $argumentsCount = \count($arguments); + + if ($argumentsCount && static::isIso8601($iso = $arguments[0])) { + array_splice($arguments, 0, 1, static::parseIso8601($iso)); + } + + if ($argumentsCount === 1) { + if ($arguments[0] instanceof DatePeriod) { + $arguments = [ + $arguments[0]->start, + $arguments[0]->end ?: ($arguments[0]->recurrences - 1), + $arguments[0]->interval, + $arguments[0]->include_start_date ? 0 : static::EXCLUDE_START_DATE, + ]; + } elseif ($arguments[0] instanceof self) { + $arguments = [ + $arguments[0]->getStartDate(), + $arguments[0]->getEndDate() ?: $arguments[0]->getRecurrences(), + $arguments[0]->getDateInterval(), + $arguments[0]->getOptions(), + ]; + } + } + + $optionsSet = false; + + foreach ($arguments as $argument) { + $parsedDate = null; + + if ($argument instanceof DateTimeZone) { + $this->setTimezone($argument); + } elseif ($this->dateInterval === null && + ( + (\is_string($argument) && preg_match( + '/^(-?\d(\d(?![\/-])|[^\d\/-]([\/-])?)*|P[T\d].*|(?:\h*\d+(?:\.\d+)?\h*[a-z]+)+)$/i', + $argument + )) || + $argument instanceof DateInterval || + $argument instanceof Closure + ) && + $parsedInterval = @CarbonInterval::make($argument) + ) { + $this->setDateInterval($parsedInterval); + } elseif ($this->startDate === null && $parsedDate = $this->makeDateTime($argument)) { + $this->setStartDate($parsedDate); + } elseif ($this->endDate === null && ($parsedDate = $parsedDate ?? $this->makeDateTime($argument))) { + $this->setEndDate($parsedDate); + } elseif ($this->recurrences === null && $this->endDate === null && is_numeric($argument)) { + $this->setRecurrences($argument); + } elseif (!$optionsSet && (\is_int($argument) || $argument === null)) { + $optionsSet = true; + $this->setOptions(((int) $this->options) | ((int) $argument)); + } else { + throw new InvalidPeriodParameterException('Invalid constructor parameters.'); + } + } + + if ($this->startDate === null) { + $dateClass = $this->dateClass; + $this->setStartDate($dateClass::now()); + } + + if ($this->dateInterval === null) { + $this->setDateInterval(CarbonInterval::day()); + + $this->isDefaultInterval = true; + } + + if ($this->options === null) { + $this->setOptions(0); + } + + $this->constructed = true; + } + + /** + * Get a copy of the instance. + * + * @return static + */ + public function copy() + { + return clone $this; + } + + /** + * Prepare the instance to be set (self if mutable to be mutated, + * copy if immutable to generate a new instance). + * + * @return static + */ + protected function copyIfImmutable() + { + return $this; + } + + /** + * Get the getter for a property allowing both `DatePeriod` snakeCase and camelCase names. + * + * @param string $name + * + * @return callable|null + */ + protected function getGetter(string $name) + { + switch (strtolower(preg_replace('/[A-Z]/', '_$0', $name))) { + case 'start': + case 'start_date': + return [$this, 'getStartDate']; + case 'end': + case 'end_date': + return [$this, 'getEndDate']; + case 'interval': + case 'date_interval': + return [$this, 'getDateInterval']; + case 'recurrences': + return [$this, 'getRecurrences']; + case 'include_start_date': + return [$this, 'isStartIncluded']; + case 'include_end_date': + return [$this, 'isEndIncluded']; + case 'current': + return [$this, 'current']; + default: + return null; + } + } + + /** + * Get a property allowing both `DatePeriod` snakeCase and camelCase names. + * + * @param string $name + * + * @return bool|CarbonInterface|CarbonInterval|int|null + */ + public function get(string $name) + { + $getter = $this->getGetter($name); + + if ($getter) { + return $getter(); + } + + throw new UnknownGetterException($name); + } + + /** + * Get a property allowing both `DatePeriod` snakeCase and camelCase names. + * + * @param string $name + * + * @return bool|CarbonInterface|CarbonInterval|int|null + */ + public function __get(string $name) + { + return $this->get($name); + } + + /** + * Check if an attribute exists on the object + * + * @param string $name + * + * @return bool + */ + public function __isset(string $name): bool + { + return $this->getGetter($name) !== null; + } + + /** + * @alias copy + * + * Get a copy of the instance. + * + * @return static + */ + public function clone() + { + return clone $this; + } + + /** + * Set the iteration item class. + * + * @param string $dateClass + * + * @return static + */ + public function setDateClass(string $dateClass) + { + if (!is_a($dateClass, CarbonInterface::class, true)) { + throw new NotACarbonClassException($dateClass); + } + + $self = $this->copyIfImmutable(); + $self->dateClass = $dateClass; + + if (is_a($dateClass, Carbon::class, true)) { + $self->options = $self->options & ~static::IMMUTABLE; + } elseif (is_a($dateClass, CarbonImmutable::class, true)) { + $self->options = $self->options | static::IMMUTABLE; + } + + return $self; + } + + /** + * Returns iteration item date class. + * + * @return string + */ + public function getDateClass(): string + { + return $this->dateClass; + } + + /** + * Change the period date interval. + * + * @param DateInterval|string $interval + * + * @throws InvalidIntervalException + * + * @return static + */ + public function setDateInterval($interval) + { + if (!$interval = CarbonInterval::make($interval)) { + throw new InvalidIntervalException('Invalid interval.'); + } + + if ($interval->spec() === 'PT0S' && !$interval->f && !$interval->getStep()) { + throw new InvalidIntervalException('Empty interval is not accepted.'); + } + + $self = $this->copyIfImmutable(); + $self->dateInterval = $interval; + + $self->isDefaultInterval = false; + + $self->handleChangedParameters(); + + return $self; + } + + /** + * Invert the period date interval. + * + * @return static + */ + public function invertDateInterval() + { + return $this->setDateInterval($this->dateInterval->invert()); + } + + /** + * Set start and end date. + * + * @param DateTime|DateTimeInterface|string $start + * @param DateTime|DateTimeInterface|string|null $end + * + * @return static + */ + public function setDates($start, $end) + { + return $this->setStartDate($start)->setEndDate($end); + } + + /** + * Change the period options. + * + * @param int|null $options + * + * @throws InvalidArgumentException + * + * @return static + */ + public function setOptions($options) + { + if (!\is_int($options) && $options !== null) { + throw new InvalidPeriodParameterException('Invalid options.'); + } + + $self = $this->copyIfImmutable(); + $self->options = $options ?: 0; + + $self->handleChangedParameters(); + + return $self; + } + + /** + * Get the period options. + * + * @return int + */ + public function getOptions() + { + return $this->options; + } + + /** + * Toggle given options on or off. + * + * @param int $options + * @param bool|null $state + * + * @throws \InvalidArgumentException + * + * @return static + */ + public function toggleOptions($options, $state = null) + { + if ($state === null) { + $state = ($this->options & $options) !== $options; + } + + return $this->setOptions( + $state ? + $this->options | $options : + $this->options & ~$options + ); + } + + /** + * Toggle EXCLUDE_START_DATE option. + * + * @param bool $state + * + * @return static + */ + public function excludeStartDate($state = true) + { + return $this->toggleOptions(static::EXCLUDE_START_DATE, $state); + } + + /** + * Toggle EXCLUDE_END_DATE option. + * + * @param bool $state + * + * @return static + */ + public function excludeEndDate($state = true) + { + return $this->toggleOptions(static::EXCLUDE_END_DATE, $state); + } + + /** + * Get the underlying date interval. + * + * @return CarbonInterval + */ + public function getDateInterval() + { + return $this->dateInterval->copy(); + } + + /** + * Get start date of the period. + * + * @param string|null $rounding Optional rounding 'floor', 'ceil', 'round' using the period interval. + * + * @return CarbonInterface + */ + public function getStartDate(string $rounding = null) + { + $date = $this->startDate->avoidMutation(); + + return $rounding ? $date->round($this->getDateInterval(), $rounding) : $date; + } + + /** + * Get end date of the period. + * + * @param string|null $rounding Optional rounding 'floor', 'ceil', 'round' using the period interval. + * + * @return CarbonInterface|null + */ + public function getEndDate(string $rounding = null) + { + if (!$this->endDate) { + return null; + } + + $date = $this->endDate->avoidMutation(); + + return $rounding ? $date->round($this->getDateInterval(), $rounding) : $date; + } + + /** + * Get number of recurrences. + * + * @return int|float|null + */ + public function getRecurrences() + { + return $this->recurrences; + } + + /** + * Returns true if the start date should be excluded. + * + * @return bool + */ + public function isStartExcluded() + { + return ($this->options & static::EXCLUDE_START_DATE) !== 0; + } + + /** + * Returns true if the end date should be excluded. + * + * @return bool + */ + public function isEndExcluded() + { + return ($this->options & static::EXCLUDE_END_DATE) !== 0; + } + + /** + * Returns true if the start date should be included. + * + * @return bool + */ + public function isStartIncluded() + { + return !$this->isStartExcluded(); + } + + /** + * Returns true if the end date should be included. + * + * @return bool + */ + public function isEndIncluded() + { + return !$this->isEndExcluded(); + } + + /** + * Return the start if it's included by option, else return the start + 1 period interval. + * + * @return CarbonInterface + */ + public function getIncludedStartDate() + { + $start = $this->getStartDate(); + + if ($this->isStartExcluded()) { + return $start->add($this->getDateInterval()); + } + + return $start; + } + + /** + * Return the end if it's included by option, else return the end - 1 period interval. + * Warning: if the period has no fixed end, this method will iterate the period to calculate it. + * + * @return CarbonInterface + */ + public function getIncludedEndDate() + { + $end = $this->getEndDate(); + + if (!$end) { + return $this->calculateEnd(); + } + + if ($this->isEndExcluded()) { + return $end->sub($this->getDateInterval()); + } + + return $end; + } + + /** + * Add a filter to the stack. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * + * @param callable $callback + * @param string $name + * + * @return static + */ + public function addFilter($callback, $name = null) + { + $self = $this->copyIfImmutable(); + $tuple = $self->createFilterTuple(\func_get_args()); + + $self->filters[] = $tuple; + + $self->handleChangedParameters(); + + return $self; + } + + /** + * Prepend a filter to the stack. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * + * @param callable $callback + * @param string $name + * + * @return static + */ + public function prependFilter($callback, $name = null) + { + $self = $this->copyIfImmutable(); + $tuple = $self->createFilterTuple(\func_get_args()); + + array_unshift($self->filters, $tuple); + + $self->handleChangedParameters(); + + return $self; + } + + /** + * Remove a filter by instance or name. + * + * @param callable|string $filter + * + * @return static + */ + public function removeFilter($filter) + { + $self = $this->copyIfImmutable(); + $key = \is_callable($filter) ? 0 : 1; + + $self->filters = array_values(array_filter( + $this->filters, + function ($tuple) use ($key, $filter) { + return $tuple[$key] !== $filter; + } + )); + + $self->updateInternalState(); + + $self->handleChangedParameters(); + + return $self; + } + + /** + * Return whether given instance or name is in the filter stack. + * + * @param callable|string $filter + * + * @return bool + */ + public function hasFilter($filter) + { + $key = \is_callable($filter) ? 0 : 1; + + foreach ($this->filters as $tuple) { + if ($tuple[$key] === $filter) { + return true; + } + } + + return false; + } + + /** + * Get filters stack. + * + * @return array + */ + public function getFilters() + { + return $this->filters; + } + + /** + * Set filters stack. + * + * @param array $filters + * + * @return static + */ + public function setFilters(array $filters) + { + $self = $this->copyIfImmutable(); + $self->filters = $filters; + + $self->updateInternalState(); + + $self->handleChangedParameters(); + + return $self; + } + + /** + * Reset filters stack. + * + * @return static + */ + public function resetFilters() + { + $self = $this->copyIfImmutable(); + $self->filters = []; + + if ($self->endDate !== null) { + $self->filters[] = [static::END_DATE_FILTER, null]; + } + + if ($self->recurrences !== null) { + $self->filters[] = [static::RECURRENCES_FILTER, null]; + } + + $self->handleChangedParameters(); + + return $self; + } + + /** + * Add a recurrences filter (set maximum number of recurrences). + * + * @param int|float|null $recurrences + * + * @throws InvalidArgumentException + * + * @return static + */ + public function setRecurrences($recurrences) + { + if ((!is_numeric($recurrences) && $recurrences !== null) || $recurrences < 0) { + throw new InvalidPeriodParameterException('Invalid number of recurrences.'); + } + + if ($recurrences === null) { + return $this->removeFilter(static::RECURRENCES_FILTER); + } + + /** @var self $self */ + $self = $this->copyIfImmutable(); + $self->recurrences = $recurrences === INF ? INF : (int) $recurrences; + + if (!$self->hasFilter(static::RECURRENCES_FILTER)) { + return $self->addFilter(static::RECURRENCES_FILTER); + } + + $self->handleChangedParameters(); + + return $self; + } + + /** + * Change the period start date. + * + * @param DateTime|DateTimeInterface|string $date + * @param bool|null $inclusive + * + * @throws InvalidPeriodDateException + * + * @return static + */ + public function setStartDate($date, $inclusive = null) + { + if (!$this->isInfiniteDate($date) && !($date = ([$this->dateClass, 'make'])($date))) { + throw new InvalidPeriodDateException('Invalid start date.'); + } + + $self = $this->copyIfImmutable(); + $self->startDate = $date; + + if ($inclusive !== null) { + $self = $self->toggleOptions(static::EXCLUDE_START_DATE, !$inclusive); + } + + return $self; + } + + /** + * Change the period end date. + * + * @param DateTime|DateTimeInterface|string|null $date + * @param bool|null $inclusive + * + * @throws \InvalidArgumentException + * + * @return static + */ + public function setEndDate($date, $inclusive = null) + { + if ($date !== null && !$this->isInfiniteDate($date) && !$date = ([$this->dateClass, 'make'])($date)) { + throw new InvalidPeriodDateException('Invalid end date.'); + } + + if (!$date) { + return $this->removeFilter(static::END_DATE_FILTER); + } + + $self = $this->copyIfImmutable(); + $self->endDate = $date; + + if ($inclusive !== null) { + $self = $self->toggleOptions(static::EXCLUDE_END_DATE, !$inclusive); + } + + if (!$self->hasFilter(static::END_DATE_FILTER)) { + return $self->addFilter(static::END_DATE_FILTER); + } + + $self->handleChangedParameters(); + + return $self; + } + + /** + * Check if the current position is valid. + * + * @return bool + */ + #[ReturnTypeWillChange] + public function valid() + { + return $this->validateCurrentDate() === true; + } + + /** + * Return the current key. + * + * @return int|null + */ + #[ReturnTypeWillChange] + public function key() + { + return $this->valid() + ? $this->key + : null; + } + + /** + * Return the current date. + * + * @return CarbonInterface|null + */ + #[ReturnTypeWillChange] + public function current() + { + return $this->valid() + ? $this->prepareForReturn($this->current) + : null; + } + + /** + * Move forward to the next date. + * + * @throws RuntimeException + * + * @return void + */ + #[ReturnTypeWillChange] + public function next() + { + if ($this->current === null) { + $this->rewind(); + } + + if ($this->validationResult !== static::END_ITERATION) { + $this->key++; + + $this->incrementCurrentDateUntilValid(); + } + } + + /** + * Rewind to the start date. + * + * Iterating over a date in the UTC timezone avoids bug during backward DST change. + * + * @see https://bugs.php.net/bug.php?id=72255 + * @see https://bugs.php.net/bug.php?id=74274 + * @see https://wiki.php.net/rfc/datetime_and_daylight_saving_time + * + * @throws RuntimeException + * + * @return void + */ + #[ReturnTypeWillChange] + public function rewind() + { + $this->key = 0; + $this->current = ([$this->dateClass, 'make'])($this->startDate); + $settings = $this->getSettings(); + + if ($this->hasLocalTranslator()) { + $settings['locale'] = $this->getTranslatorLocale(); + } + + $this->current->settings($settings); + $this->timezone = static::intervalHasTime($this->dateInterval) ? $this->current->getTimezone() : null; + + if ($this->timezone) { + $this->current = $this->current->utc(); + } + + $this->validationResult = null; + + if ($this->isStartExcluded() || $this->validateCurrentDate() === false) { + $this->incrementCurrentDateUntilValid(); + } + } + + /** + * Skip iterations and returns iteration state (false if ended, true if still valid). + * + * @param int $count steps number to skip (1 by default) + * + * @return bool + */ + public function skip($count = 1) + { + for ($i = $count; $this->valid() && $i > 0; $i--) { + $this->next(); + } + + return $this->valid(); + } + + /** + * Format the date period as ISO 8601. + * + * @return string + */ + public function toIso8601String() + { + $parts = []; + + if ($this->recurrences !== null) { + $parts[] = 'R'.$this->recurrences; + } + + $parts[] = $this->startDate->toIso8601String(); + + $parts[] = $this->dateInterval->spec(); + + if ($this->endDate !== null) { + $parts[] = $this->endDate->toIso8601String(); + } + + return implode('/', $parts); + } + + /** + * Convert the date period into a string. + * + * @return string + */ + public function toString() + { + $format = $this->localToStringFormat ?? static::$toStringFormat; + + if ($format instanceof Closure) { + return $format($this); + } + + $translator = ([$this->dateClass, 'getTranslator'])(); + + $parts = []; + + $format = $format ?? ( + !$this->startDate->isStartOfDay() || ($this->endDate && !$this->endDate->isStartOfDay()) + ? 'Y-m-d H:i:s' + : 'Y-m-d' + ); + + if ($this->recurrences !== null) { + $parts[] = $this->translate('period_recurrences', [], $this->recurrences, $translator); + } + + $parts[] = $this->translate('period_interval', [':interval' => $this->dateInterval->forHumans([ + 'join' => true, + ])], null, $translator); + + $parts[] = $this->translate('period_start_date', [':date' => $this->startDate->rawFormat($format)], null, $translator); + + if ($this->endDate !== null) { + $parts[] = $this->translate('period_end_date', [':date' => $this->endDate->rawFormat($format)], null, $translator); + } + + $result = implode(' ', $parts); + + return mb_strtoupper(mb_substr($result, 0, 1)).mb_substr($result, 1); + } + + /** + * Format the date period as ISO 8601. + * + * @return string + */ + public function spec() + { + return $this->toIso8601String(); + } + + /** + * Cast the current instance into the given class. + * + * @param string $className The $className::instance() method will be called to cast the current object. + * + * @return DatePeriod + */ + public function cast(string $className) + { + if (!method_exists($className, 'instance')) { + if (is_a($className, DatePeriod::class, true)) { + return new $className( + $this->rawDate($this->getStartDate()), + $this->getDateInterval(), + $this->getEndDate() ? $this->rawDate($this->getIncludedEndDate()) : $this->getRecurrences(), + $this->isStartExcluded() ? DatePeriod::EXCLUDE_START_DATE : 0 + ); + } + + throw new InvalidCastException("$className has not the instance() method needed to cast the date."); + } + + return $className::instance($this); + } + + /** + * Return native DatePeriod PHP object matching the current instance. + * + * @example + * ``` + * var_dump(CarbonPeriod::create('2021-01-05', '2021-02-15')->toDatePeriod()); + * ``` + * + * @return DatePeriod + */ + public function toDatePeriod() + { + return $this->cast(DatePeriod::class); + } + + /** + * Return `true` if the period has no custom filter and is guaranteed to be endless. + * + * Note that we can't check if a period is endless as soon as it has custom filters + * because filters can emit `CarbonPeriod::END_ITERATION` to stop the iteration in + * a way we can't predict without actually iterating the period. + */ + public function isUnfilteredAndEndLess(): bool + { + foreach ($this->filters as $filter) { + switch ($filter) { + case [static::RECURRENCES_FILTER, null]: + if ($this->recurrences !== null && is_finite($this->recurrences)) { + return false; + } + + break; + + case [static::END_DATE_FILTER, null]: + if ($this->endDate !== null && !$this->endDate->isEndOfTime()) { + return false; + } + + break; + + default: + return false; + } + } + + return true; + } + + /** + * Convert the date period into an array without changing current iteration state. + * + * @return CarbonInterface[] + */ + public function toArray() + { + if ($this->isUnfilteredAndEndLess()) { + throw new EndLessPeriodException("Endless period can't be converted to array nor counted."); + } + + $state = [ + $this->key, + $this->current ? $this->current->avoidMutation() : null, + $this->validationResult, + ]; + + $result = iterator_to_array($this); + + [$this->key, $this->current, $this->validationResult] = $state; + + return $result; + } + + /** + * Count dates in the date period. + * + * @return int + */ + #[ReturnTypeWillChange] + public function count() + { + return \count($this->toArray()); + } + + /** + * Return the first date in the date period. + * + * @return CarbonInterface|null + */ + public function first() + { + if ($this->isUnfilteredAndEndLess()) { + foreach ($this as $date) { + $this->rewind(); + + return $date; + } + + return null; + } + + return ($this->toArray() ?: [])[0] ?? null; + } + + /** + * Return the last date in the date period. + * + * @return CarbonInterface|null + */ + public function last() + { + $array = $this->toArray(); + + return $array ? $array[\count($array) - 1] : null; + } + + /** + * Convert the date period into a string. + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Add aliases for setters. + * + * CarbonPeriod::days(3)->hours(5)->invert() + * ->sinceNow()->until('2010-01-10') + * ->filter(...) + * ->count() + * + * Note: We use magic method to let static and instance aliases with the same names. + * + * @param string $method + * @param array $parameters + * + * @return mixed + */ + public function __call($method, $parameters) + { + if (static::hasMacro($method)) { + return static::bindMacroContext($this, function () use (&$method, &$parameters) { + return $this->callMacro($method, $parameters); + }); + } + + $roundedValue = $this->callRoundMethod($method, $parameters); + + if ($roundedValue !== null) { + return $roundedValue; + } + + switch ($method) { + case 'start': + case 'since': + self::setDefaultParameters($parameters, [ + [0, 'date', null], + ]); + + return $this->setStartDate(...$parameters); + + case 'sinceNow': + return $this->setStartDate(new Carbon(), ...$parameters); + + case 'end': + case 'until': + self::setDefaultParameters($parameters, [ + [0, 'date', null], + ]); + + return $this->setEndDate(...$parameters); + + case 'untilNow': + return $this->setEndDate(new Carbon(), ...$parameters); + + case 'dates': + case 'between': + self::setDefaultParameters($parameters, [ + [0, 'start', null], + [1, 'end', null], + ]); + + return $this->setDates(...$parameters); + + case 'recurrences': + case 'times': + self::setDefaultParameters($parameters, [ + [0, 'recurrences', null], + ]); + + return $this->setRecurrences(...$parameters); + + case 'options': + self::setDefaultParameters($parameters, [ + [0, 'options', null], + ]); + + return $this->setOptions(...$parameters); + + case 'toggle': + self::setDefaultParameters($parameters, [ + [0, 'options', null], + ]); + + return $this->toggleOptions(...$parameters); + + case 'filter': + case 'push': + return $this->addFilter(...$parameters); + + case 'prepend': + return $this->prependFilter(...$parameters); + + case 'filters': + self::setDefaultParameters($parameters, [ + [0, 'filters', []], + ]); + + return $this->setFilters(...$parameters); + + case 'interval': + case 'each': + case 'every': + case 'step': + case 'stepBy': + return $this->setDateInterval(...$parameters); + + case 'invert': + return $this->invertDateInterval(); + + case 'years': + case 'year': + case 'months': + case 'month': + case 'weeks': + case 'week': + case 'days': + case 'dayz': + case 'day': + case 'hours': + case 'hour': + case 'minutes': + case 'minute': + case 'seconds': + case 'second': + case 'milliseconds': + case 'millisecond': + case 'microseconds': + case 'microsecond': + return $this->setDateInterval(( + // Override default P1D when instantiating via fluent setters. + [$this->isDefaultInterval ? new CarbonInterval('PT0S') : $this->dateInterval, $method] + )(...$parameters)); + } + + $dateClass = $this->dateClass; + + if ($this->localStrictModeEnabled ?? $dateClass::isStrictModeEnabled()) { + throw new UnknownMethodException($method); + } + + return $this; + } + + /** + * Set the instance's timezone from a string or object and apply it to start/end. + * + * @param \DateTimeZone|string $timezone + * + * @return static + */ + public function setTimezone($timezone) + { + $self = $this->copyIfImmutable(); + $self->tzName = $timezone; + $self->timezone = $timezone; + + if ($self->startDate) { + $self = $self->setStartDate($self->startDate->setTimezone($timezone)); + } + + if ($self->endDate) { + $self = $self->setEndDate($self->endDate->setTimezone($timezone)); + } + + return $self; + } + + /** + * Set the instance's timezone from a string or object and add/subtract the offset difference to start/end. + * + * @param \DateTimeZone|string $timezone + * + * @return static + */ + public function shiftTimezone($timezone) + { + $self = $this->copyIfImmutable(); + $self->tzName = $timezone; + $self->timezone = $timezone; + + if ($self->startDate) { + $self = $self->setStartDate($self->startDate->shiftTimezone($timezone)); + } + + if ($self->endDate) { + $self = $self->setEndDate($self->endDate->shiftTimezone($timezone)); + } + + return $self; + } + + /** + * Returns the end is set, else calculated from start an recurrences. + * + * @param string|null $rounding Optional rounding 'floor', 'ceil', 'round' using the period interval. + * + * @return CarbonInterface + */ + public function calculateEnd(string $rounding = null) + { + if ($end = $this->getEndDate($rounding)) { + return $end; + } + + if ($this->dateInterval->isEmpty()) { + return $this->getStartDate($rounding); + } + + $date = $this->getEndFromRecurrences() ?? $this->iterateUntilEnd(); + + if ($date && $rounding) { + $date = $date->avoidMutation()->round($this->getDateInterval(), $rounding); + } + + return $date; + } + + /** + * @return CarbonInterface|null + */ + private function getEndFromRecurrences() + { + if ($this->recurrences === null) { + throw new UnreachableException( + "Could not calculate period end without either explicit end or recurrences.\n". + "If you're looking for a forever-period, use ->setRecurrences(INF)." + ); + } + + if ($this->recurrences === INF) { + $start = $this->getStartDate(); + + return $start < $start->avoidMutation()->add($this->getDateInterval()) + ? CarbonImmutable::endOfTime() + : CarbonImmutable::startOfTime(); + } + + if ($this->filters === [[static::RECURRENCES_FILTER, null]]) { + return $this->getStartDate()->avoidMutation()->add( + $this->getDateInterval()->times( + $this->recurrences - ($this->isStartExcluded() ? 0 : 1) + ) + ); + } + + return null; + } + + /** + * @return CarbonInterface|null + */ + private function iterateUntilEnd() + { + $attempts = 0; + $date = null; + + foreach ($this as $date) { + if (++$attempts > static::END_MAX_ATTEMPTS) { + throw new UnreachableException( + 'Could not calculate period end after iterating '.static::END_MAX_ATTEMPTS.' times.' + ); + } + } + + return $date; + } + + /** + * Returns true if the current period overlaps the given one (if 1 parameter passed) + * or the period between 2 dates (if 2 parameters passed). + * + * @param CarbonPeriod|\DateTimeInterface|Carbon|CarbonImmutable|string $rangeOrRangeStart + * @param \DateTimeInterface|Carbon|CarbonImmutable|string|null $rangeEnd + * + * @return bool + */ + public function overlaps($rangeOrRangeStart, $rangeEnd = null) + { + $range = $rangeEnd ? static::create($rangeOrRangeStart, $rangeEnd) : $rangeOrRangeStart; + + if (!($range instanceof self)) { + $range = static::create($range); + } + + [$start, $end] = $this->orderCouple($this->getStartDate(), $this->calculateEnd()); + [$rangeStart, $rangeEnd] = $this->orderCouple($range->getStartDate(), $range->calculateEnd()); + + return $end > $rangeStart && $rangeEnd > $start; + } + + /** + * Execute a given function on each date of the period. + * + * @example + * ``` + * Carbon::create('2020-11-29')->daysUntil('2020-12-24')->forEach(function (Carbon $date) { + * echo $date->diffInDays('2020-12-25')." days before Christmas!\n"; + * }); + * ``` + * + * @param callable $callback + */ + public function forEach(callable $callback) + { + foreach ($this as $date) { + $callback($date); + } + } + + /** + * Execute a given function on each date of the period and yield the result of this function. + * + * @example + * ``` + * $period = Carbon::create('2020-11-29')->daysUntil('2020-12-24'); + * echo implode("\n", iterator_to_array($period->map(function (Carbon $date) { + * return $date->diffInDays('2020-12-25').' days before Christmas!'; + * }))); + * ``` + * + * @param callable $callback + * + * @return \Generator + */ + public function map(callable $callback) + { + foreach ($this as $date) { + yield $callback($date); + } + } + + /** + * Determines if the instance is equal to another. + * Warning: if options differ, instances will never be equal. + * + * @param mixed $period + * + * @see equalTo() + * + * @return bool + */ + public function eq($period): bool + { + return $this->equalTo($period); + } + + /** + * Determines if the instance is equal to another. + * Warning: if options differ, instances will never be equal. + * + * @param mixed $period + * + * @return bool + */ + public function equalTo($period): bool + { + if (!($period instanceof self)) { + $period = self::make($period); + } + + $end = $this->getEndDate(); + + return $period !== null + && $this->getDateInterval()->eq($period->getDateInterval()) + && $this->getStartDate()->eq($period->getStartDate()) + && ($end ? $end->eq($period->getEndDate()) : $this->getRecurrences() === $period->getRecurrences()) + && ($this->getOptions() & (~static::IMMUTABLE)) === ($period->getOptions() & (~static::IMMUTABLE)); + } + + /** + * Determines if the instance is not equal to another. + * Warning: if options differ, instances will never be equal. + * + * @param mixed $period + * + * @see notEqualTo() + * + * @return bool + */ + public function ne($period): bool + { + return $this->notEqualTo($period); + } + + /** + * Determines if the instance is not equal to another. + * Warning: if options differ, instances will never be equal. + * + * @param mixed $period + * + * @return bool + */ + public function notEqualTo($period): bool + { + return !$this->eq($period); + } + + /** + * Determines if the start date is before an other given date. + * (Rather start/end are included by options is ignored.) + * + * @param mixed $date + * + * @return bool + */ + public function startsBefore($date = null): bool + { + return $this->getStartDate()->lessThan($this->resolveCarbon($date)); + } + + /** + * Determines if the start date is before or the same as a given date. + * (Rather start/end are included by options is ignored.) + * + * @param mixed $date + * + * @return bool + */ + public function startsBeforeOrAt($date = null): bool + { + return $this->getStartDate()->lessThanOrEqualTo($this->resolveCarbon($date)); + } + + /** + * Determines if the start date is after an other given date. + * (Rather start/end are included by options is ignored.) + * + * @param mixed $date + * + * @return bool + */ + public function startsAfter($date = null): bool + { + return $this->getStartDate()->greaterThan($this->resolveCarbon($date)); + } + + /** + * Determines if the start date is after or the same as a given date. + * (Rather start/end are included by options is ignored.) + * + * @param mixed $date + * + * @return bool + */ + public function startsAfterOrAt($date = null): bool + { + return $this->getStartDate()->greaterThanOrEqualTo($this->resolveCarbon($date)); + } + + /** + * Determines if the start date is the same as a given date. + * (Rather start/end are included by options is ignored.) + * + * @param mixed $date + * + * @return bool + */ + public function startsAt($date = null): bool + { + return $this->getStartDate()->equalTo($this->resolveCarbon($date)); + } + + /** + * Determines if the end date is before an other given date. + * (Rather start/end are included by options is ignored.) + * + * @param mixed $date + * + * @return bool + */ + public function endsBefore($date = null): bool + { + return $this->calculateEnd()->lessThan($this->resolveCarbon($date)); + } + + /** + * Determines if the end date is before or the same as a given date. + * (Rather start/end are included by options is ignored.) + * + * @param mixed $date + * + * @return bool + */ + public function endsBeforeOrAt($date = null): bool + { + return $this->calculateEnd()->lessThanOrEqualTo($this->resolveCarbon($date)); + } + + /** + * Determines if the end date is after an other given date. + * (Rather start/end are included by options is ignored.) + * + * @param mixed $date + * + * @return bool + */ + public function endsAfter($date = null): bool + { + return $this->calculateEnd()->greaterThan($this->resolveCarbon($date)); + } + + /** + * Determines if the end date is after or the same as a given date. + * (Rather start/end are included by options is ignored.) + * + * @param mixed $date + * + * @return bool + */ + public function endsAfterOrAt($date = null): bool + { + return $this->calculateEnd()->greaterThanOrEqualTo($this->resolveCarbon($date)); + } + + /** + * Determines if the end date is the same as a given date. + * (Rather start/end are included by options is ignored.) + * + * @param mixed $date + * + * @return bool + */ + public function endsAt($date = null): bool + { + return $this->calculateEnd()->equalTo($this->resolveCarbon($date)); + } + + /** + * Return true if start date is now or later. + * (Rather start/end are included by options is ignored.) + * + * @return bool + */ + public function isStarted(): bool + { + return $this->startsBeforeOrAt(); + } + + /** + * Return true if end date is now or later. + * (Rather start/end are included by options is ignored.) + * + * @return bool + */ + public function isEnded(): bool + { + return $this->endsBeforeOrAt(); + } + + /** + * Return true if now is between start date (included) and end date (excluded). + * (Rather start/end are included by options is ignored.) + * + * @return bool + */ + public function isInProgress(): bool + { + return $this->isStarted() && !$this->isEnded(); + } + + /** + * Round the current instance at the given unit with given precision if specified and the given function. + * + * @param string $unit + * @param float|int|string|\DateInterval|null $precision + * @param string $function + * + * @return static + */ + public function roundUnit($unit, $precision = 1, $function = 'round') + { + $self = $this->copyIfImmutable(); + $self = $self->setStartDate($self->getStartDate()->roundUnit($unit, $precision, $function)); + + if ($self->endDate) { + $self = $self->setEndDate($self->getEndDate()->roundUnit($unit, $precision, $function)); + } + + return $self->setDateInterval($self->getDateInterval()->roundUnit($unit, $precision, $function)); + } + + /** + * Truncate the current instance at the given unit with given precision if specified. + * + * @param string $unit + * @param float|int|string|\DateInterval|null $precision + * + * @return static + */ + public function floorUnit($unit, $precision = 1) + { + return $this->roundUnit($unit, $precision, 'floor'); + } + + /** + * Ceil the current instance at the given unit with given precision if specified. + * + * @param string $unit + * @param float|int|string|\DateInterval|null $precision + * + * @return static + */ + public function ceilUnit($unit, $precision = 1) + { + return $this->roundUnit($unit, $precision, 'ceil'); + } + + /** + * Round the current instance second with given precision if specified (else period interval is used). + * + * @param float|int|string|\DateInterval|null $precision + * @param string $function + * + * @return static + */ + public function round($precision = null, $function = 'round') + { + return $this->roundWith( + $precision ?? $this->getDateInterval()->setLocalTranslator(TranslatorImmutable::get('en'))->forHumans(), + $function + ); + } + + /** + * Round the current instance second with given precision if specified (else period interval is used). + * + * @param float|int|string|\DateInterval|null $precision + * + * @return static + */ + public function floor($precision = null) + { + return $this->round($precision, 'floor'); + } + + /** + * Ceil the current instance second with given precision if specified (else period interval is used). + * + * @param float|int|string|\DateInterval|null $precision + * + * @return static + */ + public function ceil($precision = null) + { + return $this->round($precision, 'ceil'); + } + + /** + * Specify data which should be serialized to JSON. + * + * @link https://php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return CarbonInterface[] + */ + #[ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->toArray(); + } + + /** + * Return true if the given date is between start and end. + * + * @param \Carbon\Carbon|\Carbon\CarbonPeriod|\Carbon\CarbonInterval|\DateInterval|\DatePeriod|\DateTimeInterface|string|null $date + * + * @return bool + */ + public function contains($date = null): bool + { + $startMethod = 'startsBefore'.($this->isStartIncluded() ? 'OrAt' : ''); + $endMethod = 'endsAfter'.($this->isEndIncluded() ? 'OrAt' : ''); + + return $this->$startMethod($date) && $this->$endMethod($date); + } + + /** + * Return true if the current period follows a given other period (with no overlap). + * For instance, [2019-08-01 -> 2019-08-12] follows [2019-07-29 -> 2019-07-31] + * Note than in this example, follows() would be false if 2019-08-01 or 2019-07-31 was excluded by options. + * + * @param \Carbon\CarbonPeriod|\DatePeriod|string $period + * + * @return bool + */ + public function follows($period, ...$arguments): bool + { + $period = $this->resolveCarbonPeriod($period, ...$arguments); + + return $this->getIncludedStartDate()->equalTo($period->getIncludedEndDate()->add($period->getDateInterval())); + } + + /** + * Return true if the given other period follows the current one (with no overlap). + * For instance, [2019-07-29 -> 2019-07-31] is followed by [2019-08-01 -> 2019-08-12] + * Note than in this example, isFollowedBy() would be false if 2019-08-01 or 2019-07-31 was excluded by options. + * + * @param \Carbon\CarbonPeriod|\DatePeriod|string $period + * + * @return bool + */ + public function isFollowedBy($period, ...$arguments): bool + { + $period = $this->resolveCarbonPeriod($period, ...$arguments); + + return $period->follows($this); + } + + /** + * Return true if the given period either follows or is followed by the current one. + * + * @see follows() + * @see isFollowedBy() + * + * @param \Carbon\CarbonPeriod|\DatePeriod|string $period + * + * @return bool + */ + public function isConsecutiveWith($period, ...$arguments): bool + { + return $this->follows($period, ...$arguments) || $this->isFollowedBy($period, ...$arguments); + } + + /** + * Update properties after removing built-in filters. + * + * @return void + */ + protected function updateInternalState() + { + if (!$this->hasFilter(static::END_DATE_FILTER)) { + $this->endDate = null; + } + + if (!$this->hasFilter(static::RECURRENCES_FILTER)) { + $this->recurrences = null; + } + } + + /** + * Create a filter tuple from raw parameters. + * + * Will create an automatic filter callback for one of Carbon's is* methods. + * + * @param array $parameters + * + * @return array + */ + protected function createFilterTuple(array $parameters) + { + $method = array_shift($parameters); + + if (!$this->isCarbonPredicateMethod($method)) { + return [$method, array_shift($parameters)]; + } + + return [function ($date) use ($method, $parameters) { + return ([$date, $method])(...$parameters); + }, $method]; + } + + /** + * Return whether given callable is a string pointing to one of Carbon's is* methods + * and should be automatically converted to a filter callback. + * + * @param callable $callable + * + * @return bool + */ + protected function isCarbonPredicateMethod($callable) + { + return \is_string($callable) && str_starts_with($callable, 'is') && + (method_exists($this->dateClass, $callable) || ([$this->dateClass, 'hasMacro'])($callable)); + } + + /** + * Recurrences filter callback (limits number of recurrences). + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * + * @param \Carbon\Carbon $current + * @param int $key + * + * @return bool|string + */ + protected function filterRecurrences($current, $key) + { + if ($key < $this->recurrences) { + return true; + } + + return static::END_ITERATION; + } + + /** + * End date filter callback. + * + * @param \Carbon\Carbon $current + * + * @return bool|string + */ + protected function filterEndDate($current) + { + if (!$this->isEndExcluded() && $current == $this->endDate) { + return true; + } + + if ($this->dateInterval->invert ? $current > $this->endDate : $current < $this->endDate) { + return true; + } + + return static::END_ITERATION; + } + + /** + * End iteration filter callback. + * + * @return string + */ + protected function endIteration() + { + return static::END_ITERATION; + } + + /** + * Handle change of the parameters. + */ + protected function handleChangedParameters() + { + if (($this->getOptions() & static::IMMUTABLE) && $this->dateClass === Carbon::class) { + $this->dateClass = CarbonImmutable::class; + } elseif (!($this->getOptions() & static::IMMUTABLE) && $this->dateClass === CarbonImmutable::class) { + $this->dateClass = Carbon::class; + } + + $this->validationResult = null; + } + + /** + * Validate current date and stop iteration when necessary. + * + * Returns true when current date is valid, false if it is not, or static::END_ITERATION + * when iteration should be stopped. + * + * @return bool|string + */ + protected function validateCurrentDate() + { + if ($this->current === null) { + $this->rewind(); + } + + // Check after the first rewind to avoid repeating the initial validation. + return $this->validationResult ?? ($this->validationResult = $this->checkFilters()); + } + + /** + * Check whether current value and key pass all the filters. + * + * @return bool|string + */ + protected function checkFilters() + { + $current = $this->prepareForReturn($this->current); + + foreach ($this->filters as $tuple) { + $result = \call_user_func( + $tuple[0], + $current->avoidMutation(), + $this->key, + $this + ); + + if ($result === static::END_ITERATION) { + return static::END_ITERATION; + } + + if (!$result) { + return false; + } + } + + return true; + } + + /** + * Prepare given date to be returned to the external logic. + * + * @param CarbonInterface $date + * + * @return CarbonInterface + */ + protected function prepareForReturn(CarbonInterface $date) + { + $date = ([$this->dateClass, 'make'])($date); + + if ($this->timezone) { + $date = $date->setTimezone($this->timezone); + } + + return $date; + } + + /** + * Keep incrementing the current date until a valid date is found or the iteration is ended. + * + * @throws RuntimeException + * + * @return void + */ + protected function incrementCurrentDateUntilValid() + { + $attempts = 0; + + do { + $this->current = $this->current->add($this->dateInterval); + + $this->validationResult = null; + + if (++$attempts > static::NEXT_MAX_ATTEMPTS) { + throw new UnreachableException('Could not find next valid date.'); + } + } while ($this->validateCurrentDate() === false); + } + + /** + * Call given macro. + * + * @param string $name + * @param array $parameters + * + * @return mixed + */ + protected function callMacro($name, $parameters) + { + $macro = static::$macros[$name]; + + if ($macro instanceof Closure) { + $boundMacro = @$macro->bindTo($this, static::class) ?: @$macro->bindTo(null, static::class); + + return ($boundMacro ?: $macro)(...$parameters); + } + + return $macro(...$parameters); + } + + /** + * Return the Carbon instance passed through, a now instance in the same timezone + * if null given or parse the input if string given. + * + * @param \Carbon\Carbon|\Carbon\CarbonPeriod|\Carbon\CarbonInterval|\DateInterval|\DatePeriod|\DateTimeInterface|string|null $date + * + * @return \Carbon\CarbonInterface + */ + protected function resolveCarbon($date = null) + { + return $this->getStartDate()->nowWithSameTz()->carbonize($date); + } + + /** + * Resolve passed arguments or DatePeriod to a CarbonPeriod object. + * + * @param mixed $period + * @param mixed ...$arguments + * + * @return static + */ + protected function resolveCarbonPeriod($period, ...$arguments) + { + if ($period instanceof self) { + return $period; + } + + return $period instanceof DatePeriod + ? static::instance($period) + : static::create($period, ...$arguments); + } + + private function orderCouple($first, $second): array + { + return $first > $second ? [$second, $first] : [$first, $second]; + } + + private function makeDateTime($value): ?DateTimeInterface + { + if ($value instanceof DateTimeInterface) { + return $value; + } + + if (\is_string($value)) { + $value = trim($value); + + if (!preg_match('/^P[\dT]/', $value) && + !preg_match('/^R\d/', $value) && + preg_match('/[a-z\d]/i', $value) + ) { + $dateClass = $this->dateClass; + + return $dateClass::parse($value, $this->tzName); + } + } + + return null; + } + + private function isInfiniteDate($date): bool + { + return $date instanceof CarbonInterface && ($date->isEndOfTime() || $date->isStartOfTime()); + } + + private function rawDate($date): ?DateTimeInterface + { + if ($date === false || $date === null) { + return null; + } + + if ($date instanceof CarbonInterface) { + return $date->isMutable() + ? $date->toDateTime() + : $date->toDateTimeImmutable(); + } + + if (\in_array(\get_class($date), [DateTime::class, DateTimeImmutable::class], true)) { + return $date; + } + + $class = $date instanceof DateTime ? DateTime::class : DateTimeImmutable::class; + + return new $class($date->format('Y-m-d H:i:s.u'), $date->getTimezone()); + } + + private static function setDefaultParameters(array &$parameters, array $defaults): void + { + foreach ($defaults as [$index, $name, $value]) { + if (!\array_key_exists($index, $parameters) && !\array_key_exists($name, $parameters)) { + $parameters[$index] = $value; + } + } + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonPeriodImmutable.php b/vendor/nesbot/carbon/src/Carbon/CarbonPeriodImmutable.php new file mode 100644 index 0000000..f0d0ee2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/CarbonPeriodImmutable.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +class CarbonPeriodImmutable extends CarbonPeriod +{ + /** + * Default date class of iteration items. + * + * @var string + */ + protected const DEFAULT_DATE_CLASS = CarbonImmutable::class; + + /** + * Date class of iteration items. + * + * @var string + */ + protected $dateClass = CarbonImmutable::class; + + /** + * Prepare the instance to be set (self if mutable to be mutated, + * copy if immutable to generate a new instance). + * + * @return static + */ + protected function copyIfImmutable() + { + return $this->constructed ? clone $this : $this; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonTimeZone.php b/vendor/nesbot/carbon/src/Carbon/CarbonTimeZone.php new file mode 100644 index 0000000..c81899f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/CarbonTimeZone.php @@ -0,0 +1,320 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Carbon\Exceptions\InvalidCastException; +use Carbon\Exceptions\InvalidTimeZoneException; +use DateTimeInterface; +use DateTimeZone; +use Throwable; + +class CarbonTimeZone extends DateTimeZone +{ + public function __construct($timezone = null) + { + parent::__construct(static::getDateTimeZoneNameFromMixed($timezone)); + } + + protected static function parseNumericTimezone($timezone) + { + if ($timezone <= -100 || $timezone >= 100) { + throw new InvalidTimeZoneException('Absolute timezone offset cannot be greater than 100.'); + } + + return ($timezone >= 0 ? '+' : '').ltrim($timezone, '+').':00'; + } + + protected static function getDateTimeZoneNameFromMixed($timezone) + { + if ($timezone === null) { + return date_default_timezone_get(); + } + + if (\is_string($timezone)) { + $timezone = preg_replace('/^\s*([+-]\d+)(\d{2})\s*$/', '$1:$2', $timezone); + } + + if (is_numeric($timezone)) { + return static::parseNumericTimezone($timezone); + } + + return $timezone; + } + + protected static function getDateTimeZoneFromName(&$name) + { + return @timezone_open($name = (string) static::getDateTimeZoneNameFromMixed($name)); + } + + /** + * Cast the current instance into the given class. + * + * @param string $className The $className::instance() method will be called to cast the current object. + * + * @return DateTimeZone + */ + public function cast(string $className) + { + if (!method_exists($className, 'instance')) { + if (is_a($className, DateTimeZone::class, true)) { + return new $className($this->getName()); + } + + throw new InvalidCastException("$className has not the instance() method needed to cast the date."); + } + + return $className::instance($this); + } + + /** + * Create a CarbonTimeZone from mixed input. + * + * @param DateTimeZone|string|int|null $object original value to get CarbonTimeZone from it. + * @param DateTimeZone|string|int|null $objectDump dump of the object for error messages. + * + * @throws InvalidTimeZoneException + * + * @return false|static + */ + public static function instance($object = null, $objectDump = null) + { + $tz = $object; + + if ($tz instanceof static) { + return $tz; + } + + if ($tz === null) { + return new static(); + } + + if (!$tz instanceof DateTimeZone) { + $tz = static::getDateTimeZoneFromName($object); + } + + if ($tz !== false) { + return new static($tz->getName()); + } + + if (Carbon::isStrictModeEnabled()) { + throw new InvalidTimeZoneException('Unknown or bad timezone ('.($objectDump ?: $object).')'); + } + + return false; + } + + /** + * Returns abbreviated name of the current timezone according to DST setting. + * + * @param bool $dst + * + * @return string + */ + public function getAbbreviatedName($dst = false) + { + $name = $this->getName(); + + foreach ($this->listAbbreviations() as $abbreviation => $zones) { + foreach ($zones as $zone) { + if ($zone['timezone_id'] === $name && $zone['dst'] == $dst) { + return $abbreviation; + } + } + } + + return 'unknown'; + } + + /** + * @alias getAbbreviatedName + * + * Returns abbreviated name of the current timezone according to DST setting. + * + * @param bool $dst + * + * @return string + */ + public function getAbbr($dst = false) + { + return $this->getAbbreviatedName($dst); + } + + /** + * Get the offset as string "sHH:MM" (such as "+00:00" or "-12:30"). + * + * @param DateTimeInterface|null $date + * + * @return string + */ + public function toOffsetName(DateTimeInterface $date = null) + { + return static::getOffsetNameFromMinuteOffset( + $this->getOffset($date ?: Carbon::now($this)) / 60 + ); + } + + /** + * Returns a new CarbonTimeZone object using the offset string instead of region string. + * + * @param DateTimeInterface|null $date + * + * @return CarbonTimeZone + */ + public function toOffsetTimeZone(DateTimeInterface $date = null) + { + return new static($this->toOffsetName($date)); + } + + /** + * Returns the first region string (such as "America/Toronto") that matches the current timezone or + * false if no match is found. + * + * @see timezone_name_from_abbr native PHP function. + * + * @param DateTimeInterface|null $date + * @param int $isDst + * + * @return string|false + */ + public function toRegionName(DateTimeInterface $date = null, $isDst = 1) + { + $name = $this->getName(); + $firstChar = substr($name, 0, 1); + + if ($firstChar !== '+' && $firstChar !== '-') { + return $name; + } + + $date = $date ?: Carbon::now($this); + + // Integer construction no longer supported since PHP 8 + // @codeCoverageIgnoreStart + try { + $offset = @$this->getOffset($date) ?: 0; + } catch (Throwable $e) { + $offset = 0; + } + // @codeCoverageIgnoreEnd + + $name = @timezone_name_from_abbr('', $offset, $isDst); + + if ($name) { + return $name; + } + + foreach (timezone_identifiers_list() as $timezone) { + if (Carbon::instance($date)->tz($timezone)->getOffset() === $offset) { + return $timezone; + } + } + + return false; + } + + /** + * Returns a new CarbonTimeZone object using the region string instead of offset string. + * + * @param DateTimeInterface|null $date + * + * @return CarbonTimeZone|false + */ + public function toRegionTimeZone(DateTimeInterface $date = null) + { + $tz = $this->toRegionName($date); + + if ($tz !== false) { + return new static($tz); + } + + if (Carbon::isStrictModeEnabled()) { + throw new InvalidTimeZoneException('Unknown timezone for offset '.$this->getOffset($date ?: Carbon::now($this)).' seconds.'); + } + + return false; + } + + /** + * Cast to string (get timezone name). + * + * @return string + */ + public function __toString() + { + return $this->getName(); + } + + /** + * Return the type number: + * + * Type 1; A UTC offset, such as -0300 + * Type 2; A timezone abbreviation, such as GMT + * Type 3: A timezone identifier, such as Europe/London + */ + public function getType(): int + { + return preg_match('/"timezone_type";i:(\d)/', serialize($this), $match) ? (int) $match[1] : 3; + } + + /** + * Create a CarbonTimeZone from mixed input. + * + * @param DateTimeZone|string|int|null $object + * + * @return false|static + */ + public static function create($object = null) + { + return static::instance($object); + } + + /** + * Create a CarbonTimeZone from int/float hour offset. + * + * @param float $hourOffset number of hour of the timezone shift (can be decimal). + * + * @return false|static + */ + public static function createFromHourOffset(float $hourOffset) + { + return static::createFromMinuteOffset($hourOffset * Carbon::MINUTES_PER_HOUR); + } + + /** + * Create a CarbonTimeZone from int/float minute offset. + * + * @param float $minuteOffset number of total minutes of the timezone shift. + * + * @return false|static + */ + public static function createFromMinuteOffset(float $minuteOffset) + { + return static::instance(static::getOffsetNameFromMinuteOffset($minuteOffset)); + } + + /** + * Convert a total minutes offset into a standardized timezone offset string. + * + * @param float $minutes number of total minutes of the timezone shift. + * + * @return string + */ + public static function getOffsetNameFromMinuteOffset(float $minutes): string + { + $minutes = round($minutes); + $unsignedMinutes = abs($minutes); + + return ($minutes < 0 ? '-' : '+'). + str_pad((string) floor($unsignedMinutes / 60), 2, '0', STR_PAD_LEFT). + ':'. + str_pad((string) ($unsignedMinutes % 60), 2, '0', STR_PAD_LEFT); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Cli/Invoker.php b/vendor/nesbot/carbon/src/Carbon/Cli/Invoker.php new file mode 100644 index 0000000..4f35d6c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Cli/Invoker.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Cli; + +class Invoker +{ + public const CLI_CLASS_NAME = 'Carbon\\Cli'; + + protected function runWithCli(string $className, array $parameters): bool + { + $cli = new $className(); + + return $cli(...$parameters); + } + + public function __invoke(...$parameters): bool + { + if (class_exists(self::CLI_CLASS_NAME)) { + return $this->runWithCli(self::CLI_CLASS_NAME, $parameters); + } + + $function = (($parameters[1] ?? '') === 'install' ? ($parameters[2] ?? null) : null) ?: 'shell_exec'; + $function('composer require carbon-cli/carbon-cli --no-interaction'); + + echo 'Installation succeeded.'; + + return true; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadComparisonUnitException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadComparisonUnitException.php new file mode 100644 index 0000000..3ca8837 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadComparisonUnitException.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use Throwable; + +class BadComparisonUnitException extends UnitException +{ + /** + * The unit. + * + * @var string + */ + protected $unit; + + /** + * Constructor. + * + * @param string $unit + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($unit, $code = 0, Throwable $previous = null) + { + $this->unit = $unit; + + parent::__construct("Bad comparison unit: '$unit'", $code, $previous); + } + + /** + * Get the unit. + * + * @return string + */ + public function getUnit(): string + { + return $this->unit; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentConstructorException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentConstructorException.php new file mode 100644 index 0000000..2e222e5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentConstructorException.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use BadMethodCallException as BaseBadMethodCallException; +use Throwable; + +class BadFluentConstructorException extends BaseBadMethodCallException implements BadMethodCallException +{ + /** + * The method. + * + * @var string + */ + protected $method; + + /** + * Constructor. + * + * @param string $method + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($method, $code = 0, Throwable $previous = null) + { + $this->method = $method; + + parent::__construct(sprintf("Unknown fluent constructor '%s'.", $method), $code, $previous); + } + + /** + * Get the method. + * + * @return string + */ + public function getMethod(): string + { + return $this->method; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentSetterException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentSetterException.php new file mode 100644 index 0000000..4ceaa2e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentSetterException.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use BadMethodCallException as BaseBadMethodCallException; +use Throwable; + +class BadFluentSetterException extends BaseBadMethodCallException implements BadMethodCallException +{ + /** + * The setter. + * + * @var string + */ + protected $setter; + + /** + * Constructor. + * + * @param string $setter + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($setter, $code = 0, Throwable $previous = null) + { + $this->setter = $setter; + + parent::__construct(sprintf("Unknown fluent setter '%s'", $setter), $code, $previous); + } + + /** + * Get the setter. + * + * @return string + */ + public function getSetter(): string + { + return $this->setter; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadMethodCallException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadMethodCallException.php new file mode 100644 index 0000000..108206d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadMethodCallException.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +interface BadMethodCallException extends Exception +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/EndLessPeriodException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/EndLessPeriodException.php new file mode 100644 index 0000000..e104926 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/EndLessPeriodException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use RuntimeException as BaseRuntimeException; + +final class EndLessPeriodException extends BaseRuntimeException implements RuntimeException +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/Exception.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/Exception.php new file mode 100644 index 0000000..8ad747e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/Exception.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +interface Exception +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/ImmutableException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/ImmutableException.php new file mode 100644 index 0000000..db334c6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/ImmutableException.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use RuntimeException as BaseRuntimeException; +use Throwable; + +class ImmutableException extends BaseRuntimeException implements RuntimeException +{ + /** + * The value. + * + * @var string + */ + protected $value; + + /** + * Constructor. + * + * @param string $value the immutable type/value + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($value, $code = 0, Throwable $previous = null) + { + $this->value = $value; + parent::__construct("$value is immutable.", $code, $previous); + } + + /** + * Get the value. + * + * @return string + */ + public function getValue(): string + { + return $this->value; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidArgumentException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidArgumentException.php new file mode 100644 index 0000000..5b013cd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidArgumentException.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +interface InvalidArgumentException extends Exception +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidCastException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidCastException.php new file mode 100644 index 0000000..a421401 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidCastException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; + +class InvalidCastException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php new file mode 100644 index 0000000..c9ecb6b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; +use Throwable; + +class InvalidDateException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + /** + * The invalid field. + * + * @var string + */ + private $field; + + /** + * The invalid value. + * + * @var mixed + */ + private $value; + + /** + * Constructor. + * + * @param string $field + * @param mixed $value + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($field, $value, $code = 0, Throwable $previous = null) + { + $this->field = $field; + $this->value = $value; + parent::__construct($field.' : '.$value.' is not a valid value.', $code, $previous); + } + + /** + * Get the invalid field. + * + * @return string + */ + public function getField() + { + return $this->field; + } + + /** + * Get the invalid value. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidFormatException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidFormatException.php new file mode 100644 index 0000000..92d55fe --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidFormatException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; + +class InvalidFormatException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidIntervalException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidIntervalException.php new file mode 100644 index 0000000..69cf412 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidIntervalException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; + +class InvalidIntervalException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodDateException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodDateException.php new file mode 100644 index 0000000..9bd84a9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodDateException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; + +class InvalidPeriodDateException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodParameterException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodParameterException.php new file mode 100644 index 0000000..cf2c902 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodParameterException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; + +class InvalidPeriodParameterException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTimeZoneException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTimeZoneException.php new file mode 100644 index 0000000..f725955 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTimeZoneException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; + +class InvalidTimeZoneException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTypeException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTypeException.php new file mode 100644 index 0000000..2c8ec9b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTypeException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; + +class InvalidTypeException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/NotACarbonClassException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotACarbonClassException.php new file mode 100644 index 0000000..7a87632 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotACarbonClassException.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use Carbon\CarbonInterface; +use InvalidArgumentException as BaseInvalidArgumentException; +use Throwable; + +class NotACarbonClassException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + /** + * The className. + * + * @var string + */ + protected $className; + + /** + * Constructor. + * + * @param string $className + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($className, $code = 0, Throwable $previous = null) + { + $this->className = $className; + + parent::__construct(sprintf('Given class does not implement %s: %s', CarbonInterface::class, $className), $code, $previous); + } + + /** + * Get the className. + * + * @return string + */ + public function getClassName(): string + { + return $this->className; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/NotAPeriodException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotAPeriodException.php new file mode 100644 index 0000000..4edd7a4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotAPeriodException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; + +class NotAPeriodException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/NotLocaleAwareException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotLocaleAwareException.php new file mode 100644 index 0000000..f2c5468 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotLocaleAwareException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; +use Throwable; + +class NotLocaleAwareException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + /** + * Constructor. + * + * @param mixed $object + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($object, $code = 0, Throwable $previous = null) + { + $dump = \is_object($object) ? \get_class($object) : \gettype($object); + + parent::__construct("$dump does neither implements Symfony\Contracts\Translation\LocaleAwareInterface nor getLocale() method.", $code, $previous); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/OutOfRangeException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/OutOfRangeException.php new file mode 100644 index 0000000..2c586d0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/OutOfRangeException.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; +use Throwable; + +// This will extends OutOfRangeException instead of InvalidArgumentException since 3.0.0 +// use OutOfRangeException as BaseOutOfRangeException; + +class OutOfRangeException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + /** + * The unit or name of the value. + * + * @var string + */ + private $unit; + + /** + * The range minimum. + * + * @var mixed + */ + private $min; + + /** + * The range maximum. + * + * @var mixed + */ + private $max; + + /** + * The invalid value. + * + * @var mixed + */ + private $value; + + /** + * Constructor. + * + * @param string $unit + * @param mixed $min + * @param mixed $max + * @param mixed $value + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($unit, $min, $max, $value, $code = 0, Throwable $previous = null) + { + $this->unit = $unit; + $this->min = $min; + $this->max = $max; + $this->value = $value; + + parent::__construct("$unit must be between $min and $max, $value given", $code, $previous); + } + + /** + * @return mixed + */ + public function getMax() + { + return $this->max; + } + + /** + * @return mixed + */ + public function getMin() + { + return $this->min; + } + + /** + * @return mixed + */ + public function getUnit() + { + return $this->unit; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/ParseErrorException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/ParseErrorException.php new file mode 100644 index 0000000..5416fd1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/ParseErrorException.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; +use Throwable; + +class ParseErrorException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + /** + * The expected. + * + * @var string + */ + protected $expected; + + /** + * The actual. + * + * @var string + */ + protected $actual; + + /** + * The help message. + * + * @var string + */ + protected $help; + + /** + * Constructor. + * + * @param string $expected + * @param string $actual + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($expected, $actual, $help = '', $code = 0, Throwable $previous = null) + { + $this->expected = $expected; + $this->actual = $actual; + $this->help = $help; + + $actual = $actual === '' ? 'data is missing' : "get '$actual'"; + + parent::__construct(trim("Format expected $expected but $actual\n$help"), $code, $previous); + } + + /** + * Get the expected. + * + * @return string + */ + public function getExpected(): string + { + return $this->expected; + } + + /** + * Get the actual. + * + * @return string + */ + public function getActual(): string + { + return $this->actual; + } + + /** + * Get the help message. + * + * @return string + */ + public function getHelp(): string + { + return $this->help; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/RuntimeException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/RuntimeException.php new file mode 100644 index 0000000..ad196f7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/RuntimeException.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +interface RuntimeException extends Exception +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnitException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnitException.php new file mode 100644 index 0000000..ee99953 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnitException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; + +class UnitException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnitNotConfiguredException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnitNotConfiguredException.php new file mode 100644 index 0000000..0e72305 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnitNotConfiguredException.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use Throwable; + +class UnitNotConfiguredException extends UnitException +{ + /** + * The unit. + * + * @var string + */ + protected $unit; + + /** + * Constructor. + * + * @param string $unit + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($unit, $code = 0, Throwable $previous = null) + { + $this->unit = $unit; + + parent::__construct("Unit $unit have no configuration to get total from other units.", $code, $previous); + } + + /** + * Get the unit. + * + * @return string + */ + public function getUnit(): string + { + return $this->unit; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownGetterException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownGetterException.php new file mode 100644 index 0000000..5c50497 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownGetterException.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; +use Throwable; + +class UnknownGetterException extends BaseInvalidArgumentException implements InvalidArgumentException +{ + /** + * The getter. + * + * @var string + */ + protected $getter; + + /** + * Constructor. + * + * @param string $getter getter name + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($getter, $code = 0, Throwable $previous = null) + { + $this->getter = $getter; + + parent::__construct("Unknown getter '$getter'", $code, $previous); + } + + /** + * Get the getter. + * + * @return string + */ + public function getGetter(): string + { + return $this->getter; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownMethodException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownMethodException.php new file mode 100644 index 0000000..75273a7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownMethodException.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use BadMethodCallException as BaseBadMethodCallException; +use Throwable; + +class UnknownMethodException extends BaseBadMethodCallException implements BadMethodCallException +{ + /** + * The method. + * + * @var string + */ + protected $method; + + /** + * Constructor. + * + * @param string $method + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($method, $code = 0, Throwable $previous = null) + { + $this->method = $method; + + parent::__construct("Method $method does not exist.", $code, $previous); + } + + /** + * Get the method. + * + * @return string + */ + public function getMethod(): string + { + return $this->method; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownSetterException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownSetterException.php new file mode 100644 index 0000000..a795f5d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownSetterException.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use InvalidArgumentException as BaseInvalidArgumentException; +use Throwable; + +class UnknownSetterException extends BaseInvalidArgumentException implements BadMethodCallException +{ + /** + * The setter. + * + * @var string + */ + protected $setter; + + /** + * Constructor. + * + * @param string $setter setter name + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($setter, $code = 0, Throwable $previous = null) + { + $this->setter = $setter; + + parent::__construct("Unknown setter '$setter'", $code, $previous); + } + + /** + * Get the setter. + * + * @return string + */ + public function getSetter(): string + { + return $this->setter; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownUnitException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownUnitException.php new file mode 100644 index 0000000..ecd7f7a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownUnitException.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use Throwable; + +class UnknownUnitException extends UnitException +{ + /** + * The unit. + * + * @var string + */ + protected $unit; + + /** + * Constructor. + * + * @param string $unit + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($unit, $code = 0, Throwable $previous = null) + { + $this->unit = $unit; + + parent::__construct("Unknown unit '$unit'.", $code, $previous); + } + + /** + * Get the unit. + * + * @return string + */ + public function getUnit(): string + { + return $this->unit; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnreachableException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnreachableException.php new file mode 100644 index 0000000..1654ab1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnreachableException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use RuntimeException as BaseRuntimeException; + +class UnreachableException extends BaseRuntimeException implements RuntimeException +{ + // +} diff --git a/vendor/nesbot/carbon/src/Carbon/Factory.php b/vendor/nesbot/carbon/src/Carbon/Factory.php new file mode 100644 index 0000000..d497535 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Factory.php @@ -0,0 +1,326 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Closure; +use DateTimeInterface; +use ReflectionMethod; + +/** + * A factory to generate Carbon instances with common settings. + * + * + * + * @method bool canBeCreatedFromFormat($date, $format) Checks if the (date)time string is in a given format and valid to create a + * new instance. + * @method Carbon|false create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null) Create a new Carbon instance from a specific date and time. + * If any of $year, $month or $day are set to null their now() values will + * be used. + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * If $hour is not null then the default values for $minute and $second + * will be 0. + * @method Carbon createFromDate($year = null, $month = null, $day = null, $tz = null) Create a Carbon instance from just a date. The time portion is set to now. + * @method Carbon|false createFromFormat($format, $time, $tz = null) Create a Carbon instance from a specific format. + * @method Carbon|false createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null) Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()). + * @method Carbon|false createFromLocaleFormat($format, $locale, $time, $tz = null) Create a Carbon instance from a specific format and a string in a given language. + * @method Carbon|false createFromLocaleIsoFormat($format, $locale, $time, $tz = null) Create a Carbon instance from a specific ISO format and a string in a given language. + * @method Carbon createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null) Create a Carbon instance from just a time. The date portion is set to today. + * @method Carbon createFromTimeString($time, $tz = null) Create a Carbon instance from a time string. The date portion is set to today. + * @method Carbon createFromTimestamp($timestamp, $tz = null) Create a Carbon instance from a timestamp and set the timezone (use default one if not specified). + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method Carbon createFromTimestampMs($timestamp, $tz = null) Create a Carbon instance from a timestamp in milliseconds. + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method Carbon createFromTimestampMsUTC($timestamp) Create a Carbon instance from a timestamp in milliseconds. + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method Carbon createFromTimestampUTC($timestamp) Create a Carbon instance from an timestamp keeping the timezone to UTC. + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method Carbon createMidnightDate($year = null, $month = null, $day = null, $tz = null) Create a Carbon instance from just a date. The time portion is set to midnight. + * @method Carbon|false createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null) Create a new safe Carbon instance from a specific date and time. + * If any of $year, $month or $day are set to null their now() values will + * be used. + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * If $hour is not null then the default values for $minute and $second + * will be 0. + * If one of the set values is not valid, an InvalidDateException + * will be thrown. + * @method CarbonInterface createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null) Create a new Carbon instance from a specific date and time using strict validation. + * @method Carbon disableHumanDiffOption($humanDiffOption) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @method Carbon enableHumanDiffOption($humanDiffOption) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @method mixed executeWithLocale($locale, $func) Set the current locale to the given, execute the passed function, reset the locale to previous one, + * then return the result of the closure (or null if the closure was void). + * @method Carbon fromSerialized($value) Create an instance from a serialized string. + * @method void genericMacro($macro, $priority = 0) Register a custom macro. + * @method array getAvailableLocales() Returns the list of internally available locales and already loaded custom locales. + * (It will ignore custom translator dynamic loading.) + * @method Language[] getAvailableLocalesInfo() Returns list of Language object for each available locale. This object allow you to get the ISO name, native + * name, region and variant of the locale. + * @method array getDays() Get the days of the week + * @method string|null getFallbackLocale() Get the fallback locale. + * @method array getFormatsToIsoReplacements() List of replacements from date() format to isoFormat(). + * @method int getHumanDiffOptions() Return default humanDiff() options (merged flags as integer). + * @method array getIsoUnits() Returns list of locale units for ISO formatting. + * @method array getLastErrors() {@inheritdoc} + * @method string getLocale() Get the current translator locale. + * @method callable|null getMacro($name) Get the raw callable macro registered globally for a given name. + * @method int getMidDayAt() get midday/noon hour + * @method Closure|Carbon getTestNow() Get the Carbon instance (real or mock) to be returned when a "now" + * instance is created. + * @method string getTimeFormatByPrecision($unitPrecision) Return a format from H:i to H:i:s.u according to given unit precision. + * @method string getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null) Returns raw translation message for a given key. + * @method \Symfony\Component\Translation\TranslatorInterface getTranslator() Get the default translator instance in use. + * @method int getWeekEndsAt() Get the last day of week + * @method int getWeekStartsAt() Get the first day of week + * @method array getWeekendDays() Get weekend days + * @method bool hasFormat($date, $format) Checks if the (date)time string is in a given format. + * @method bool hasFormatWithModifiers($date, $format) Checks if the (date)time string is in a given format. + * @method bool hasMacro($name) Checks if macro is registered globally. + * @method bool hasRelativeKeywords($time) Determine if a time string will produce a relative date. + * @method bool hasTestNow() Determine if there is a valid test instance set. A valid test instance + * is anything that is not null. + * @method Carbon instance($date) Create a Carbon instance from a DateTime one. + * @method bool isImmutable() Returns true if the current class/instance is immutable. + * @method bool isModifiableUnit($unit) Returns true if a property can be changed via setter. + * @method bool isMutable() Returns true if the current class/instance is mutable. + * @method bool isStrictModeEnabled() Returns true if the strict mode is globally in use, false else. + * (It can be overridden in specific instances.) + * @method bool localeHasDiffOneDayWords($locale) Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow). + * Support is considered enabled if the 3 words are translated in the given locale. + * @method bool localeHasDiffSyntax($locale) Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * @method bool localeHasDiffTwoDayWords($locale) Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow). + * Support is considered enabled if the 2 words are translated in the given locale. + * @method bool localeHasPeriodSyntax($locale) Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * @method bool localeHasShortUnits($locale) Returns true if the given locale is internally supported and has short-units support. + * Support is considered enabled if either year, day or hour has a short variant translated. + * @method void macro($name, $macro) Register a custom macro. + * @method Carbon|null make($var) Make a Carbon instance from given variable if possible. + * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals + * and recurrences). Throw an exception for invalid format, but otherwise return null. + * @method Carbon maxValue() Create a Carbon instance for the greatest supported date. + * @method Carbon minValue() Create a Carbon instance for the lowest supported date. + * @method void mixin($mixin) Mix another object into the class. + * @method Carbon now($tz = null) Get a Carbon instance for the current date and time. + * @method Carbon parse($time = null, $tz = null) Create a carbon instance from a string. + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * @method Carbon parseFromLocale($time, $locale = null, $tz = null) Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.). + * @method string pluralUnit(string $unit) Returns standardized plural of a given singular/plural unit name (in English). + * @method Carbon|false rawCreateFromFormat($format, $time, $tz = null) Create a Carbon instance from a specific format. + * @method Carbon rawParse($time = null, $tz = null) Create a carbon instance from a string. + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * @method Carbon resetMacros() Remove all macros and generic macros. + * @method void resetMonthsOverflow() @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @method void resetToStringFormat() Reset the format used to the default when type juggling a Carbon instance to a string + * @method void resetYearsOverflow() @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @method void serializeUsing($callback) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather transform Carbon object before the serialization. + * JSON serialize all Carbon instances using the given callback. + * @method Carbon setFallbackLocale($locale) Set the fallback locale. + * @method Carbon setHumanDiffOptions($humanDiffOptions) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @method bool setLocale($locale) Set the current translator locale and indicate if the source locale file exists. + * Pass 'auto' as locale to use closest language from the current LC_TIME locale. + * @method void setMidDayAt($hour) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather consider mid-day is always 12pm, then if you need to test if it's an other + * hour, test it explicitly: + * $date->format('G') == 13 + * or to set explicitly to a given hour: + * $date->setTime(13, 0, 0, 0) + * Set midday/noon hour + * @method Carbon setTestNow($testNow = null) Set a Carbon instance (real or mock) to be returned when a "now" + * instance is created. The provided instance will be returned + * specifically under the following conditions: + * - A call to the static now() method, ex. Carbon::now() + * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) + * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') + * - When a string containing the desired time is passed to Carbon::parse(). + * Note the timezone parameter was left out of the examples above and + * has no affect as the mock value will be returned regardless of its value. + * Only the moment is mocked with setTestNow(), the timezone will still be the one passed + * as parameter of date_default_timezone_get() as a fallback (see setTestNowAndTimezone()). + * To clear the test instance call this method using the default + * parameter of null. + * /!\ Use this method for unit tests only. + * @method Carbon setTestNowAndTimezone($testNow = null, $tz = null) Set a Carbon instance (real or mock) to be returned when a "now" + * instance is created. The provided instance will be returned + * specifically under the following conditions: + * - A call to the static now() method, ex. Carbon::now() + * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) + * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') + * - When a string containing the desired time is passed to Carbon::parse(). + * It will also align default timezone (e.g. call date_default_timezone_set()) with + * the second argument or if null, with the timezone of the given date object. + * To clear the test instance call this method using the default + * parameter of null. + * /!\ Use this method for unit tests only. + * @method void setToStringFormat($format) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather let Carbon object being cast to string with DEFAULT_TO_STRING_FORMAT, and + * use other method or custom format passed to format() method if you need to dump another string + * format. + * Set the default format used when type juggling a Carbon instance to a string. + * @method void setTranslator(TranslatorInterface $translator) Set the default translator instance to use. + * @method Carbon setUtf8($utf8) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use UTF-8 language packages on every machine. + * Set if UTF8 will be used for localized date/time. + * @method void setWeekEndsAt($day) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * Use $weekStartsAt optional parameter instead when using startOfWeek, floorWeek, ceilWeek + * or roundWeek method. You can also use the 'first_day_of_week' locale setting to change the + * start of week according to current locale selected and implicitly the end of week. + * Set the last day of week + * @method void setWeekStartsAt($day) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * Use $weekEndsAt optional parameter instead when using endOfWeek method. You can also use the + * 'first_day_of_week' locale setting to change the start of week according to current locale + * selected and implicitly the end of week. + * Set the first day of week + * @method void setWeekendDays($days) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather consider week-end is always saturday and sunday, and if you have some custom + * week-end days to handle, give to those days an other name and create a macro for them: + * ``` + * Carbon::macro('isDayOff', function ($date) { + * return $date->isSunday() || $date->isMonday(); + * }); + * Carbon::macro('isNotDayOff', function ($date) { + * return !$date->isDayOff(); + * }); + * if ($someDate->isDayOff()) ... + * if ($someDate->isNotDayOff()) ... + * // Add 5 not-off days + * $count = 5; + * while ($someDate->isDayOff() || ($count-- > 0)) { + * $someDate->addDay(); + * } + * ``` + * Set weekend days + * @method bool shouldOverflowMonths() Get the month overflow global behavior (can be overridden in specific instances). + * @method bool shouldOverflowYears() Get the month overflow global behavior (can be overridden in specific instances). + * @method string singularUnit(string $unit) Returns standardized singular of a given singular/plural unit name (in English). + * @method Carbon today($tz = null) Create a Carbon instance for today. + * @method Carbon tomorrow($tz = null) Create a Carbon instance for tomorrow. + * @method string translateTimeString($timeString, $from = null, $to = null, $mode = CarbonInterface::TRANSLATE_ALL) Translate a time string from a locale to an other. + * @method string translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null) Translate using translation string or callback available. + * @method void useMonthsOverflow($monthsOverflow = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @method Carbon useStrictMode($strictModeEnabled = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @method void useYearsOverflow($yearsOverflow = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @method mixed withTestNow($testNow, $callback) Temporarily sets a static date to be used within the callback. + * Using setTestNow to set the date, executing the callback, then + * clearing the test instance. + * /!\ Use this method for unit tests only. + * @method Carbon yesterday($tz = null) Create a Carbon instance for yesterday. + * + * + */ +class Factory +{ + protected $className = Carbon::class; + + protected $settings = []; + + public function __construct(array $settings = [], ?string $className = null) + { + if ($className) { + $this->className = $className; + } + + $this->settings = $settings; + } + + public function getClassName() + { + return $this->className; + } + + public function setClassName(string $className) + { + $this->className = $className; + + return $this; + } + + public function className(string $className = null) + { + return $className === null ? $this->getClassName() : $this->setClassName($className); + } + + public function getSettings() + { + return $this->settings; + } + + public function setSettings(array $settings) + { + $this->settings = $settings; + + return $this; + } + + public function settings(array $settings = null) + { + return $settings === null ? $this->getSettings() : $this->setSettings($settings); + } + + public function mergeSettings(array $settings) + { + $this->settings = array_merge($this->settings, $settings); + + return $this; + } + + public function __call($name, $arguments) + { + $method = new ReflectionMethod($this->className, $name); + $settings = $this->settings; + + if ($settings && isset($settings['timezone'])) { + $tzParameters = array_filter($method->getParameters(), function ($parameter) { + return \in_array($parameter->getName(), ['tz', 'timezone'], true); + }); + + if (isset($arguments[0]) && \in_array($name, ['instance', 'make', 'create', 'parse'], true)) { + if ($arguments[0] instanceof DateTimeInterface) { + $settings['innerTimezone'] = $settings['timezone']; + } elseif (\is_string($arguments[0]) && date_parse($arguments[0])['is_localtime']) { + unset($settings['timezone'], $settings['innerTimezone']); + } + } elseif (\count($tzParameters)) { + array_splice($arguments, key($tzParameters), 0, [$settings['timezone']]); + unset($settings['timezone']); + } + } + + $result = $this->className::$name(...$arguments); + + return $result instanceof CarbonInterface && !empty($settings) + ? $result->settings($settings) + : $result; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/FactoryImmutable.php b/vendor/nesbot/carbon/src/Carbon/FactoryImmutable.php new file mode 100644 index 0000000..d88a1cf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/FactoryImmutable.php @@ -0,0 +1,259 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Closure; +use DateTimeImmutable; +use DateTimeZone; +use Psr\Clock\ClockInterface; + +/** + * A factory to generate CarbonImmutable instances with common settings. + * + * + * + * @method bool canBeCreatedFromFormat($date, $format) Checks if the (date)time string is in a given format and valid to create a + * new instance. + * @method CarbonImmutable|false create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null) Create a new Carbon instance from a specific date and time. + * If any of $year, $month or $day are set to null their now() values will + * be used. + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * If $hour is not null then the default values for $minute and $second + * will be 0. + * @method CarbonImmutable createFromDate($year = null, $month = null, $day = null, $tz = null) Create a Carbon instance from just a date. The time portion is set to now. + * @method CarbonImmutable|false createFromFormat($format, $time, $tz = null) Create a Carbon instance from a specific format. + * @method CarbonImmutable|false createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null) Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()). + * @method CarbonImmutable|false createFromLocaleFormat($format, $locale, $time, $tz = null) Create a Carbon instance from a specific format and a string in a given language. + * @method CarbonImmutable|false createFromLocaleIsoFormat($format, $locale, $time, $tz = null) Create a Carbon instance from a specific ISO format and a string in a given language. + * @method CarbonImmutable createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null) Create a Carbon instance from just a time. The date portion is set to today. + * @method CarbonImmutable createFromTimeString($time, $tz = null) Create a Carbon instance from a time string. The date portion is set to today. + * @method CarbonImmutable createFromTimestamp($timestamp, $tz = null) Create a Carbon instance from a timestamp and set the timezone (use default one if not specified). + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method CarbonImmutable createFromTimestampMs($timestamp, $tz = null) Create a Carbon instance from a timestamp in milliseconds. + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method CarbonImmutable createFromTimestampMsUTC($timestamp) Create a Carbon instance from a timestamp in milliseconds. + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method CarbonImmutable createFromTimestampUTC($timestamp) Create a Carbon instance from an timestamp keeping the timezone to UTC. + * Timestamp input can be given as int, float or a string containing one or more numbers. + * @method CarbonImmutable createMidnightDate($year = null, $month = null, $day = null, $tz = null) Create a Carbon instance from just a date. The time portion is set to midnight. + * @method CarbonImmutable|false createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null) Create a new safe Carbon instance from a specific date and time. + * If any of $year, $month or $day are set to null their now() values will + * be used. + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * If $hour is not null then the default values for $minute and $second + * will be 0. + * If one of the set values is not valid, an InvalidDateException + * will be thrown. + * @method CarbonInterface createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null) Create a new Carbon instance from a specific date and time using strict validation. + * @method CarbonImmutable disableHumanDiffOption($humanDiffOption) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @method CarbonImmutable enableHumanDiffOption($humanDiffOption) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @method mixed executeWithLocale($locale, $func) Set the current locale to the given, execute the passed function, reset the locale to previous one, + * then return the result of the closure (or null if the closure was void). + * @method CarbonImmutable fromSerialized($value) Create an instance from a serialized string. + * @method void genericMacro($macro, $priority = 0) Register a custom macro. + * @method array getAvailableLocales() Returns the list of internally available locales and already loaded custom locales. + * (It will ignore custom translator dynamic loading.) + * @method Language[] getAvailableLocalesInfo() Returns list of Language object for each available locale. This object allow you to get the ISO name, native + * name, region and variant of the locale. + * @method array getDays() Get the days of the week + * @method string|null getFallbackLocale() Get the fallback locale. + * @method array getFormatsToIsoReplacements() List of replacements from date() format to isoFormat(). + * @method int getHumanDiffOptions() Return default humanDiff() options (merged flags as integer). + * @method array getIsoUnits() Returns list of locale units for ISO formatting. + * @method array getLastErrors() {@inheritdoc} + * @method string getLocale() Get the current translator locale. + * @method callable|null getMacro($name) Get the raw callable macro registered globally for a given name. + * @method int getMidDayAt() get midday/noon hour + * @method Closure|CarbonImmutable getTestNow() Get the Carbon instance (real or mock) to be returned when a "now" + * instance is created. + * @method string getTimeFormatByPrecision($unitPrecision) Return a format from H:i to H:i:s.u according to given unit precision. + * @method string getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null) Returns raw translation message for a given key. + * @method \Symfony\Component\Translation\TranslatorInterface getTranslator() Get the default translator instance in use. + * @method int getWeekEndsAt() Get the last day of week + * @method int getWeekStartsAt() Get the first day of week + * @method array getWeekendDays() Get weekend days + * @method bool hasFormat($date, $format) Checks if the (date)time string is in a given format. + * @method bool hasFormatWithModifiers($date, $format) Checks if the (date)time string is in a given format. + * @method bool hasMacro($name) Checks if macro is registered globally. + * @method bool hasRelativeKeywords($time) Determine if a time string will produce a relative date. + * @method bool hasTestNow() Determine if there is a valid test instance set. A valid test instance + * is anything that is not null. + * @method CarbonImmutable instance($date) Create a Carbon instance from a DateTime one. + * @method bool isImmutable() Returns true if the current class/instance is immutable. + * @method bool isModifiableUnit($unit) Returns true if a property can be changed via setter. + * @method bool isMutable() Returns true if the current class/instance is mutable. + * @method bool isStrictModeEnabled() Returns true if the strict mode is globally in use, false else. + * (It can be overridden in specific instances.) + * @method bool localeHasDiffOneDayWords($locale) Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow). + * Support is considered enabled if the 3 words are translated in the given locale. + * @method bool localeHasDiffSyntax($locale) Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * @method bool localeHasDiffTwoDayWords($locale) Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow). + * Support is considered enabled if the 2 words are translated in the given locale. + * @method bool localeHasPeriodSyntax($locale) Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * @method bool localeHasShortUnits($locale) Returns true if the given locale is internally supported and has short-units support. + * Support is considered enabled if either year, day or hour has a short variant translated. + * @method void macro($name, $macro) Register a custom macro. + * @method CarbonImmutable|null make($var) Make a Carbon instance from given variable if possible. + * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals + * and recurrences). Throw an exception for invalid format, but otherwise return null. + * @method CarbonImmutable maxValue() Create a Carbon instance for the greatest supported date. + * @method CarbonImmutable minValue() Create a Carbon instance for the lowest supported date. + * @method void mixin($mixin) Mix another object into the class. + * @method CarbonImmutable parse($time = null, $tz = null) Create a carbon instance from a string. + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * @method CarbonImmutable parseFromLocale($time, $locale = null, $tz = null) Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.). + * @method string pluralUnit(string $unit) Returns standardized plural of a given singular/plural unit name (in English). + * @method CarbonImmutable|false rawCreateFromFormat($format, $time, $tz = null) Create a Carbon instance from a specific format. + * @method CarbonImmutable rawParse($time = null, $tz = null) Create a carbon instance from a string. + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * @method CarbonImmutable resetMacros() Remove all macros and generic macros. + * @method void resetMonthsOverflow() @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @method void resetToStringFormat() Reset the format used to the default when type juggling a Carbon instance to a string + * @method void resetYearsOverflow() @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @method void serializeUsing($callback) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather transform Carbon object before the serialization. + * JSON serialize all Carbon instances using the given callback. + * @method CarbonImmutable setFallbackLocale($locale) Set the fallback locale. + * @method CarbonImmutable setHumanDiffOptions($humanDiffOptions) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @method bool setLocale($locale) Set the current translator locale and indicate if the source locale file exists. + * Pass 'auto' as locale to use closest language from the current LC_TIME locale. + * @method void setMidDayAt($hour) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather consider mid-day is always 12pm, then if you need to test if it's an other + * hour, test it explicitly: + * $date->format('G') == 13 + * or to set explicitly to a given hour: + * $date->setTime(13, 0, 0, 0) + * Set midday/noon hour + * @method CarbonImmutable setTestNow($testNow = null) Set a Carbon instance (real or mock) to be returned when a "now" + * instance is created. The provided instance will be returned + * specifically under the following conditions: + * - A call to the static now() method, ex. Carbon::now() + * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) + * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') + * - When a string containing the desired time is passed to Carbon::parse(). + * Note the timezone parameter was left out of the examples above and + * has no affect as the mock value will be returned regardless of its value. + * Only the moment is mocked with setTestNow(), the timezone will still be the one passed + * as parameter of date_default_timezone_get() as a fallback (see setTestNowAndTimezone()). + * To clear the test instance call this method using the default + * parameter of null. + * /!\ Use this method for unit tests only. + * @method CarbonImmutable setTestNowAndTimezone($testNow = null, $tz = null) Set a Carbon instance (real or mock) to be returned when a "now" + * instance is created. The provided instance will be returned + * specifically under the following conditions: + * - A call to the static now() method, ex. Carbon::now() + * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) + * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') + * - When a string containing the desired time is passed to Carbon::parse(). + * It will also align default timezone (e.g. call date_default_timezone_set()) with + * the second argument or if null, with the timezone of the given date object. + * To clear the test instance call this method using the default + * parameter of null. + * /!\ Use this method for unit tests only. + * @method void setToStringFormat($format) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather let Carbon object being cast to string with DEFAULT_TO_STRING_FORMAT, and + * use other method or custom format passed to format() method if you need to dump another string + * format. + * Set the default format used when type juggling a Carbon instance to a string. + * @method void setTranslator(TranslatorInterface $translator) Set the default translator instance to use. + * @method CarbonImmutable setUtf8($utf8) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use UTF-8 language packages on every machine. + * Set if UTF8 will be used for localized date/time. + * @method void setWeekEndsAt($day) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * Use $weekStartsAt optional parameter instead when using startOfWeek, floorWeek, ceilWeek + * or roundWeek method. You can also use the 'first_day_of_week' locale setting to change the + * start of week according to current locale selected and implicitly the end of week. + * Set the last day of week + * @method void setWeekStartsAt($day) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * Use $weekEndsAt optional parameter instead when using endOfWeek method. You can also use the + * 'first_day_of_week' locale setting to change the start of week according to current locale + * selected and implicitly the end of week. + * Set the first day of week + * @method void setWeekendDays($days) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather consider week-end is always saturday and sunday, and if you have some custom + * week-end days to handle, give to those days an other name and create a macro for them: + * ``` + * Carbon::macro('isDayOff', function ($date) { + * return $date->isSunday() || $date->isMonday(); + * }); + * Carbon::macro('isNotDayOff', function ($date) { + * return !$date->isDayOff(); + * }); + * if ($someDate->isDayOff()) ... + * if ($someDate->isNotDayOff()) ... + * // Add 5 not-off days + * $count = 5; + * while ($someDate->isDayOff() || ($count-- > 0)) { + * $someDate->addDay(); + * } + * ``` + * Set weekend days + * @method bool shouldOverflowMonths() Get the month overflow global behavior (can be overridden in specific instances). + * @method bool shouldOverflowYears() Get the month overflow global behavior (can be overridden in specific instances). + * @method string singularUnit(string $unit) Returns standardized singular of a given singular/plural unit name (in English). + * @method CarbonImmutable today($tz = null) Create a Carbon instance for today. + * @method CarbonImmutable tomorrow($tz = null) Create a Carbon instance for tomorrow. + * @method string translateTimeString($timeString, $from = null, $to = null, $mode = CarbonInterface::TRANSLATE_ALL) Translate a time string from a locale to an other. + * @method string translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null) Translate using translation string or callback available. + * @method void useMonthsOverflow($monthsOverflow = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @method CarbonImmutable useStrictMode($strictModeEnabled = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @method void useYearsOverflow($yearsOverflow = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @method mixed withTestNow($testNow, $callback) Temporarily sets a static date to be used within the callback. + * Using setTestNow to set the date, executing the callback, then + * clearing the test instance. + * /!\ Use this method for unit tests only. + * @method CarbonImmutable yesterday($tz = null) Create a Carbon instance for yesterday. + * + * + */ +class FactoryImmutable extends Factory implements ClockInterface +{ + protected $className = CarbonImmutable::class; + + /** + * Get a Carbon instance for the current date and time. + * + * @param DateTimeZone|string|int|null $tz + * + * @return CarbonImmutable + */ + public function now($tz = null): DateTimeImmutable + { + $className = $this->className; + + return new $className(null, $tz); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/aa.php b/vendor/nesbot/carbon/src/Carbon/Lang/aa.php new file mode 100644 index 0000000..f3431e4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/aa.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/aa_DJ.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/aa_DJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/aa_DJ.php new file mode 100644 index 0000000..c6e23c0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/aa_DJ.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['Qunxa Garablu', 'Kudo', 'Ciggilta Kudo', 'Agda Baxisso', 'Caxah Alsa', 'Qasa Dirri', 'Qado Dirri', 'Liiqen', 'Waysu', 'Diteli', 'Ximoli', 'Kaxxa Garablu'], + 'months_short' => ['qun', 'nah', 'cig', 'agd', 'cax', 'qas', 'qad', 'leq', 'way', 'dit', 'xim', 'kax'], + 'weekdays' => ['Acaada', 'Etleeni', 'Talaata', 'Arbaqa', 'Kamiisi', 'Gumqata', 'Sabti'], + 'weekdays_short' => ['aca', 'etl', 'tal', 'arb', 'kam', 'gum', 'sab'], + 'weekdays_min' => ['aca', 'etl', 'tal', 'arb', 'kam', 'gum', 'sab'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['saaku', 'carra'], + + 'year' => ':count gaqambo', // less reliable + 'y' => ':count gaqambo', // less reliable + 'a_year' => ':count gaqambo', // less reliable + + 'month' => ':count àlsa', + 'm' => ':count àlsa', + 'a_month' => ':count àlsa', + + 'day' => ':count saaku', // less reliable + 'd' => ':count saaku', // less reliable + 'a_day' => ':count saaku', // less reliable + + 'hour' => ':count ayti', // less reliable + 'h' => ':count ayti', // less reliable + 'a_hour' => ':count ayti', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/aa_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ER.php new file mode 100644 index 0000000..f8f395b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ER.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Qunxa Garablu', 'Naharsi Kudo', 'Ciggilta Kudo', 'Agda Baxisso', 'Caxah Alsa', 'Qasa Dirri', 'Qado Dirri', 'Leqeeni', 'Waysu', 'Diteli', 'Ximoli', 'Kaxxa Garablu'], + 'months_short' => ['Qun', 'Nah', 'Cig', 'Agd', 'Cax', 'Qas', 'Qad', 'Leq', 'Way', 'Dit', 'Xim', 'Kax'], + 'weekdays' => ['Acaada', 'Etleeni', 'Talaata', 'Arbaqa', 'Kamiisi', 'Gumqata', 'Sabti'], + 'weekdays_short' => ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'], + 'weekdays_min' => ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['saaku', 'carra'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/aa_ER@saaho.php b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ER@saaho.php new file mode 100644 index 0000000..6461225 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ER@saaho.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Qunxa Garablu', 'Naharsi Kudo', 'Ciggilta Kudo', 'Agda Baxisso', 'Caxah Alsa', 'Qasa Dirri', 'Qado Dirri', 'Leqeeni', 'Waysu', 'Diteli', 'Ximoli', 'Kaxxa Garablu'], + 'months_short' => ['Qun', 'Nah', 'Cig', 'Agd', 'Cax', 'Qas', 'Qad', 'Leq', 'Way', 'Dit', 'Xim', 'Kax'], + 'weekdays' => ['Naba Sambat', 'Sani', 'Salus', 'Rabuq', 'Camus', 'Jumqata', 'Qunxa Sambat'], + 'weekdays_short' => ['Nab', 'San', 'Sal', 'Rab', 'Cam', 'Jum', 'Qun'], + 'weekdays_min' => ['Nab', 'San', 'Sal', 'Rab', 'Cam', 'Jum', 'Qun'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['saaku', 'carra'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/aa_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ET.php new file mode 100644 index 0000000..e55e591 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ET.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Qunxa Garablu', 'Kudo', 'Ciggilta Kudo', 'Agda Baxisso', 'Caxah Alsa', 'Qasa Dirri', 'Qado Dirri', 'Liiqen', 'Waysu', 'Diteli', 'Ximoli', 'Kaxxa Garablu'], + 'months_short' => ['Qun', 'Kud', 'Cig', 'Agd', 'Cax', 'Qas', 'Qad', 'Leq', 'Way', 'Dit', 'Xim', 'Kax'], + 'weekdays' => ['Acaada', 'Etleeni', 'Talaata', 'Arbaqa', 'Kamiisi', 'Gumqata', 'Sabti'], + 'weekdays_short' => ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'], + 'weekdays_min' => ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['saaku', 'carra'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/af.php b/vendor/nesbot/carbon/src/Carbon/Lang/af.php new file mode 100644 index 0000000..27771d7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/af.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - JD Isaacks + * - Pierre du Plessis + */ +return [ + 'year' => ':count jaar', + 'a_year' => '\'n jaar|:count jaar', + 'y' => ':count j.', + 'month' => ':count maand|:count maande', + 'a_month' => '\'n maand|:count maande', + 'm' => ':count maa.', + 'week' => ':count week|:count weke', + 'a_week' => '\'n week|:count weke', + 'w' => ':count w.', + 'day' => ':count dag|:count dae', + 'a_day' => '\'n dag|:count dae', + 'd' => ':count d.', + 'hour' => ':count uur', + 'a_hour' => '\'n uur|:count uur', + 'h' => ':count u.', + 'minute' => ':count minuut|:count minute', + 'a_minute' => '\'n minuut|:count minute', + 'min' => ':count min.', + 'second' => ':count sekond|:count sekondes', + 'a_second' => '\'n paar sekondes|:count sekondes', + 's' => ':count s.', + 'ago' => ':time gelede', + 'from_now' => 'oor :time', + 'after' => ':time na', + 'before' => ':time voor', + 'diff_now' => 'Nou', + 'diff_today' => 'Vandag', + 'diff_today_regexp' => 'Vandag(?:\\s+om)?', + 'diff_yesterday' => 'Gister', + 'diff_yesterday_regexp' => 'Gister(?:\\s+om)?', + 'diff_tomorrow' => 'Môre', + 'diff_tomorrow_regexp' => 'Môre(?:\\s+om)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Vandag om] LT', + 'nextDay' => '[Môre om] LT', + 'nextWeek' => 'dddd [om] LT', + 'lastDay' => '[Gister om] LT', + 'lastWeek' => '[Laas] dddd [om] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + return $number.(($number === 1 || $number === 8 || $number >= 20) ? 'ste' : 'de'); + }, + 'meridiem' => ['VM', 'NM'], + 'months' => ['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie', 'Julie', 'Augustus', 'September', 'Oktober', 'November', 'Desember'], + 'months_short' => ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'], + 'weekdays' => ['Sondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrydag', 'Saterdag'], + 'weekdays_short' => ['Son', 'Maa', 'Din', 'Woe', 'Don', 'Vry', 'Sat'], + 'weekdays_min' => ['So', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Sa'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' en '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/af_NA.php b/vendor/nesbot/carbon/src/Carbon/Lang/af_NA.php new file mode 100644 index 0000000..f2fcf05 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/af_NA.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/af.php', [ + 'meridiem' => ['v', 'n'], + 'weekdays' => ['Sondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrydag', 'Saterdag'], + 'weekdays_short' => ['So.', 'Ma.', 'Di.', 'Wo.', 'Do.', 'Vr.', 'Sa.'], + 'weekdays_min' => ['So.', 'Ma.', 'Di.', 'Wo.', 'Do.', 'Vr.', 'Sa.'], + 'months' => ['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie', 'Julie', 'Augustus', 'September', 'Oktober', 'November', 'Desember'], + 'months_short' => ['Jan.', 'Feb.', 'Mrt.', 'Apr.', 'Mei', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Des.'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-DD', + 'LL' => 'DD MMM YYYY', + 'LLL' => 'DD MMMM YYYY HH:mm', + 'LLLL' => 'dddd, DD MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/af_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/af_ZA.php new file mode 100644 index 0000000..27896bd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/af_ZA.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/af.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/agq.php b/vendor/nesbot/carbon/src/Carbon/Lang/agq.php new file mode 100644 index 0000000..7011464 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/agq.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['a.g', 'a.k'], + 'weekdays' => ['tsuʔntsɨ', 'tsuʔukpà', 'tsuʔughɔe', 'tsuʔutɔ̀mlò', 'tsuʔumè', 'tsuʔughɨ̂m', 'tsuʔndzɨkɔʔɔ'], + 'weekdays_short' => ['nts', 'kpa', 'ghɔ', 'tɔm', 'ume', 'ghɨ', 'dzk'], + 'weekdays_min' => ['nts', 'kpa', 'ghɔ', 'tɔm', 'ume', 'ghɨ', 'dzk'], + 'months' => ['ndzɔ̀ŋɔ̀nùm', 'ndzɔ̀ŋɔ̀kƗ̀zùʔ', 'ndzɔ̀ŋɔ̀tƗ̀dʉ̀ghà', 'ndzɔ̀ŋɔ̀tǎafʉ̄ghā', 'ndzɔ̀ŋèsèe', 'ndzɔ̀ŋɔ̀nzùghò', 'ndzɔ̀ŋɔ̀dùmlo', 'ndzɔ̀ŋɔ̀kwîfɔ̀e', 'ndzɔ̀ŋɔ̀tƗ̀fʉ̀ghàdzughù', 'ndzɔ̀ŋɔ̀ghǔuwelɔ̀m', 'ndzɔ̀ŋɔ̀chwaʔàkaa wo', 'ndzɔ̀ŋèfwòo'], + 'months_short' => ['nùm', 'kɨz', 'tɨd', 'taa', 'see', 'nzu', 'dum', 'fɔe', 'dzu', 'lɔm', 'kaa', 'fwo'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/agr.php b/vendor/nesbot/carbon/src/Carbon/Lang/agr.php new file mode 100644 index 0000000..8f036ae --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/agr.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/agr_PE.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/agr_PE.php b/vendor/nesbot/carbon/src/Carbon/Lang/agr_PE.php new file mode 100644 index 0000000..54a326a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/agr_PE.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - somosazucar.org libc-alpha@sourceware.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['Petsatin', 'Kupitin', 'Uyaitin', 'Tayutin', 'Kegketin', 'Tegmatin', 'Kuntutin', 'Yagkujutin', 'Daiktatin', 'Ipamtatin', 'Shinutin', 'Sakamtin'], + 'months_short' => ['Pet', 'Kup', 'Uya', 'Tay', 'Keg', 'Teg', 'Kun', 'Yag', 'Dait', 'Ipam', 'Shin', 'Sak'], + 'weekdays' => ['Tuntuamtin', 'Achutin', 'Kugkuktin', 'Saketin', 'Shimpitin', 'Imaptin', 'Bataetin'], + 'weekdays_short' => ['Tun', 'Ach', 'Kug', 'Sak', 'Shim', 'Im', 'Bat'], + 'weekdays_min' => ['Tun', 'Ach', 'Kug', 'Sak', 'Shim', 'Im', 'Bat'], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 7, + 'meridiem' => ['VM', 'NM'], + + 'year' => ':count yaya', // less reliable + 'y' => ':count yaya', // less reliable + 'a_year' => ':count yaya', // less reliable + + 'month' => ':count nantu', // less reliable + 'm' => ':count nantu', // less reliable + 'a_month' => ':count nantu', // less reliable + + 'day' => ':count nayaim', // less reliable + 'd' => ':count nayaim', // less reliable + 'a_day' => ':count nayaim', // less reliable + + 'hour' => ':count kuwiš', // less reliable + 'h' => ':count kuwiš', // less reliable + 'a_hour' => ':count kuwiš', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ak.php b/vendor/nesbot/carbon/src/Carbon/Lang/ak.php new file mode 100644 index 0000000..5a64be3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ak.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/ak_GH.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ak_GH.php b/vendor/nesbot/carbon/src/Carbon/Lang/ak_GH.php new file mode 100644 index 0000000..1381946 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ak_GH.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Sugar Labs // OLPC sugarlabs.org libc-alpha@sourceware.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'YYYY/MM/DD', + ], + 'months' => ['Sanda-Ɔpɛpɔn', 'Kwakwar-Ɔgyefuo', 'Ebɔw-Ɔbenem', 'Ebɔbira-Oforisuo', 'Esusow Aketseaba-Kɔtɔnimba', 'Obirade-Ayɛwohomumu', 'Ayɛwoho-Kitawonsa', 'Difuu-Ɔsandaa', 'Fankwa-Ɛbɔ', 'Ɔbɛsɛ-Ahinime', 'Ɔberɛfɛw-Obubuo', 'Mumu-Ɔpɛnimba'], + 'months_short' => ['S-Ɔ', 'K-Ɔ', 'E-Ɔ', 'E-O', 'E-K', 'O-A', 'A-K', 'D-Ɔ', 'F-Ɛ', 'Ɔ-A', 'Ɔ-O', 'M-Ɔ'], + 'weekdays' => ['Kwesida', 'Dwowda', 'Benada', 'Wukuda', 'Yawda', 'Fida', 'Memeneda'], + 'weekdays_short' => ['Kwe', 'Dwo', 'Ben', 'Wuk', 'Yaw', 'Fia', 'Mem'], + 'weekdays_min' => ['Kwe', 'Dwo', 'Ben', 'Wuk', 'Yaw', 'Fia', 'Mem'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['AN', 'EW'], + + 'year' => ':count afe', + 'y' => ':count afe', + 'a_year' => ':count afe', + + 'month' => ':count bosume', + 'm' => ':count bosume', + 'a_month' => ':count bosume', + + 'day' => ':count ɛda', + 'd' => ':count ɛda', + 'a_day' => ':count ɛda', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/am.php b/vendor/nesbot/carbon/src/Carbon/Lang/am.php new file mode 100644 index 0000000..63bf72d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/am.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/am_ET.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/am_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/am_ET.php new file mode 100644 index 0000000..ece8062 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/am_ET.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች', 'ኤፕሪል', 'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት', 'ሴፕቴምበር', 'ኦክቶበር', 'ኖቬምበር', 'ዲሴምበር'], + 'months_short' => ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕረ', 'ሜይ ', 'ጁን ', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክተ', 'ኖቬም', 'ዲሴም'], + 'weekdays' => ['እሑድ', 'ሰኞ', 'ማክሰኞ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ'], + 'weekdays_short' => ['እሑድ', 'ሰኞ ', 'ማክሰ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ'], + 'weekdays_min' => ['እሑድ', 'ሰኞ ', 'ማክሰ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ጡዋት', 'ከሰዓት'], + + 'year' => ':count አመት', + 'y' => ':count አመት', + 'a_year' => ':count አመት', + + 'month' => ':count ወር', + 'm' => ':count ወር', + 'a_month' => ':count ወር', + + 'week' => ':count ሳምንት', + 'w' => ':count ሳምንት', + 'a_week' => ':count ሳምንት', + + 'day' => ':count ቀን', + 'd' => ':count ቀን', + 'a_day' => ':count ቀን', + + 'hour' => ':count ሰዓት', + 'h' => ':count ሰዓት', + 'a_hour' => ':count ሰዓት', + + 'minute' => ':count ደቂቃ', + 'min' => ':count ደቂቃ', + 'a_minute' => ':count ደቂቃ', + + 'second' => ':count ሴኮንድ', + 's' => ':count ሴኮንድ', + 'a_second' => ':count ሴኮንድ', + + 'ago' => 'ከ:time በፊት', + 'from_now' => 'በ:time ውስጥ', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/an.php b/vendor/nesbot/carbon/src/Carbon/Lang/an.php new file mode 100644 index 0000000..565abf2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/an.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/an_ES.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/an_ES.php b/vendor/nesbot/carbon/src/Carbon/Lang/an_ES.php new file mode 100644 index 0000000..faf8ae0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/an_ES.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Softaragones Jordi Mallach Pérez, Juan Pablo Martínez bug-glibc-locales@gnu.org, softaragones@softaragones.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['chinero', 'febrero', 'marzo', 'abril', 'mayo', 'chunyo', 'chuliol', 'agosto', 'setiembre', 'octubre', 'noviembre', 'aviento'], + 'months_short' => ['chi', 'feb', 'mar', 'abr', 'may', 'chn', 'chl', 'ago', 'set', 'oct', 'nov', 'avi'], + 'weekdays' => ['domingo', 'luns', 'martes', 'mierques', 'chueves', 'viernes', 'sabado'], + 'weekdays_short' => ['dom', 'lun', 'mar', 'mie', 'chu', 'vie', 'sab'], + 'weekdays_min' => ['dom', 'lun', 'mar', 'mie', 'chu', 'vie', 'sab'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'year' => ':count año', + 'y' => ':count año', + 'a_year' => ':count año', + + 'month' => ':count mes', + 'm' => ':count mes', + 'a_month' => ':count mes', + + 'week' => ':count semana', + 'w' => ':count semana', + 'a_week' => ':count semana', + + 'day' => ':count día', + 'd' => ':count día', + 'a_day' => ':count día', + + 'hour' => ':count reloch', // less reliable + 'h' => ':count reloch', // less reliable + 'a_hour' => ':count reloch', // less reliable + + 'minute' => ':count minuto', + 'min' => ':count minuto', + 'a_minute' => ':count minuto', + + 'second' => ':count segundo', + 's' => ':count segundo', + 'a_second' => ':count segundo', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/anp.php b/vendor/nesbot/carbon/src/Carbon/Lang/anp.php new file mode 100644 index 0000000..b56c67b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/anp.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/anp_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/anp_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/anp_IN.php new file mode 100644 index 0000000..11069be --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/anp_IN.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bhashaghar@googlegroups.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रैल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितंबर', 'अक्टूबर', 'नवंबर', 'दिसंबर"'], + 'months_short' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रैल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितंबर', 'अक्टूबर', 'नवंबर', 'दिसंबर'], + 'weekdays' => ['रविवार', 'सोमवार', 'मंगलवार', 'बुधवार', 'बृहस्पतिवार', 'शुक्रवार', 'शनिवार'], + 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पति', 'शुक्र', 'शनि'], + 'weekdays_min' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पति', 'शुक्र', 'शनि'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar.php new file mode 100644 index 0000000..5f73f63 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Atef Ben Ali (atefBB) + * - Ibrahim AshShohail + * - MLTDev + * - Mohamed Sabil (mohamedsabil83) + * - Yazan Alnugnugh (yazan-alnugnugh) + */ +$months = [ + 'يناير', + 'فبراير', + 'مارس', + 'أبريل', + 'مايو', + 'يونيو', + 'يوليو', + 'أغسطس', + 'سبتمبر', + 'أكتوبر', + 'نوفمبر', + 'ديسمبر', +]; + +return [ + 'year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']), + 'a_year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']), + 'month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']), + 'a_month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']), + 'week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']), + 'a_week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']), + 'day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']), + 'a_day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']), + 'hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']), + 'a_hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']), + 'minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']), + 'a_minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']), + 'second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']), + 'a_second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']), + 'ago' => 'منذ :time', + 'from_now' => ':time من الآن', + 'after' => 'بعد :time', + 'before' => 'قبل :time', + 'diff_now' => 'الآن', + 'diff_today' => 'اليوم', + 'diff_today_regexp' => 'اليوم(?:\\s+عند)?(?:\\s+الساعة)?', + 'diff_yesterday' => 'أمس', + 'diff_yesterday_regexp' => 'أمس(?:\\s+عند)?(?:\\s+الساعة)?', + 'diff_tomorrow' => 'غداً', + 'diff_tomorrow_regexp' => 'غدًا(?:\\s+عند)?(?:\\s+الساعة)?', + 'diff_before_yesterday' => 'قبل الأمس', + 'diff_after_tomorrow' => 'بعد غد', + 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']), + 'period_interval' => 'كل :interval', + 'period_start_date' => 'من :date', + 'period_end_date' => 'إلى :date', + 'months' => $months, + 'months_short' => $months, + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'], + 'weekdays_min' => ['ح', 'اث', 'ثل', 'أر', 'خم', 'ج', 'س'], + 'list' => ['، ', ' و '], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[اليوم عند الساعة] LT', + 'nextDay' => '[غدًا عند الساعة] LT', + 'nextWeek' => 'dddd [عند الساعة] LT', + 'lastDay' => '[أمس عند الساعة] LT', + 'lastWeek' => 'dddd [عند الساعة] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['ص', 'م'], + 'weekend' => [5, 6], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_AE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_AE.php new file mode 100644 index 0000000..35a22b1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_AE.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + * - Abdullah-Alhariri + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'DD MMM, YYYY', + ], + 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'], + 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت '], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_BH.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_BH.php new file mode 100644 index 0000000..3518096 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_BH.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + * - Abdullah-Alhariri + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'DD MMM, YYYY', + ], + 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'], + 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_DJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_DJ.php new file mode 100644 index 0000000..e790b99 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_DJ.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ar.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_DZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_DZ.php new file mode 100644 index 0000000..aea4eee --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_DZ.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Authors: + * - Josh Soref + * - Noureddine LOUAHEDJ + * - JD Isaacks + * - Atef Ben Ali (atefBB) + * - Mohamed Sabil (mohamedsabil83) + */ +$months = [ + 'جانفي', + 'فيفري', + 'مارس', + 'أفريل', + 'ماي', + 'جوان', + 'جويلية', + 'أوت', + 'سبتمبر', + 'أكتوبر', + 'نوفمبر', + 'ديسمبر', +]; + +return [ + 'year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']), + 'a_year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']), + 'month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']), + 'a_month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']), + 'week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']), + 'a_week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']), + 'day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']), + 'a_day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']), + 'hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']), + 'a_hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']), + 'minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']), + 'a_minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']), + 'second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']), + 'a_second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']), + 'ago' => 'منذ :time', + 'from_now' => 'في :time', + 'after' => 'بعد :time', + 'before' => 'قبل :time', + 'diff_now' => 'الآن', + 'diff_today' => 'اليوم', + 'diff_today_regexp' => 'اليوم(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_yesterday' => 'أمس', + 'diff_yesterday_regexp' => 'أمس(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_tomorrow' => 'غداً', + 'diff_tomorrow_regexp' => 'غدا(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_before_yesterday' => 'قبل الأمس', + 'diff_after_tomorrow' => 'بعد غد', + 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']), + 'period_interval' => 'كل :interval', + 'period_start_date' => 'من :date', + 'period_end_date' => 'إلى :date', + 'months' => $months, + 'months_short' => $months, + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'], + 'weekdays_min' => ['أح', 'إث', 'ثلا', 'أر', 'خم', 'جم', 'سب'], + 'list' => ['، ', ' و '], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 4, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[اليوم على الساعة] LT', + 'nextDay' => '[غدا على الساعة] LT', + 'nextWeek' => 'dddd [على الساعة] LT', + 'lastDay' => '[أمس على الساعة] LT', + 'lastWeek' => 'dddd [على الساعة] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['ص', 'م'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_EG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_EG.php new file mode 100644 index 0000000..3518096 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_EG.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + * - Abdullah-Alhariri + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'DD MMM, YYYY', + ], + 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'], + 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_EH.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_EH.php new file mode 100644 index 0000000..e790b99 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_EH.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ar.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_ER.php new file mode 100644 index 0000000..e790b99 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_ER.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ar.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_IL.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_IL.php new file mode 100644 index 0000000..e790b99 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_IL.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ar.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_IN.php new file mode 100644 index 0000000..5fecf70 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_IN.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'], + 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_IQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_IQ.php new file mode 100644 index 0000000..2d42008 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_IQ.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + * - Abdullah-Alhariri + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'DD MMM, YYYY', + ], + 'months' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], + 'months_short' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_JO.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_JO.php new file mode 100644 index 0000000..2d42008 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_JO.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + * - Abdullah-Alhariri + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'DD MMM, YYYY', + ], + 'months' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], + 'months_short' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_KM.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_KM.php new file mode 100644 index 0000000..e790b99 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_KM.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ar.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_KW.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_KW.php new file mode 100644 index 0000000..b3fb1cf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_KW.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Authors: + * - Josh Soref + * - Nusret Parlak + * - JD Isaacks + * - Atef Ben Ali (atefBB) + * - Mohamed Sabil (mohamedsabil83) + * - Abdullah-Alhariri + */ +$months = [ + 'يناير', + 'فبراير', + 'مارس', + 'أبريل', + 'ماي', + 'يونيو', + 'يوليوز', + 'غشت', + 'شتنبر', + 'أكتوبر', + 'نونبر', + 'دجنبر', +]; + +return [ + 'year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']), + 'a_year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']), + 'month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']), + 'a_month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']), + 'week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']), + 'a_week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']), + 'day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']), + 'a_day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']), + 'hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']), + 'a_hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']), + 'minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']), + 'a_minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']), + 'second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']), + 'a_second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']), + 'ago' => 'منذ :time', + 'from_now' => 'في :time', + 'after' => 'بعد :time', + 'before' => 'قبل :time', + 'diff_now' => 'الآن', + 'diff_today' => 'اليوم', + 'diff_today_regexp' => 'اليوم(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_yesterday' => 'أمس', + 'diff_yesterday_regexp' => 'أمس(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_tomorrow' => 'غداً', + 'diff_tomorrow_regexp' => 'غدا(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_before_yesterday' => 'قبل الأمس', + 'diff_after_tomorrow' => 'بعد غد', + 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']), + 'period_interval' => 'كل :interval', + 'period_start_date' => 'من :date', + 'period_end_date' => 'إلى :date', + 'months' => $months, + 'months_short' => $months, + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'list' => ['، ', ' و '], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[اليوم على الساعة] LT', + 'nextDay' => '[غدا على الساعة] LT', + 'nextWeek' => 'dddd [على الساعة] LT', + 'lastDay' => '[أمس على الساعة] LT', + 'lastWeek' => 'dddd [على الساعة] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['ص', 'م'], + 'weekend' => [5, 6], + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_LB.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_LB.php new file mode 100644 index 0000000..2792745 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_LB.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + * - Abdullah-Alhariri + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'DD MMM, YYYY', + ], + 'months' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], + 'months_short' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_LY.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_LY.php new file mode 100644 index 0000000..1f0af49 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_LY.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Atef Ben Ali (atefBB) + * - Ibrahim AshShohail + * - MLTDev + */ + +$months = [ + 'يناير', + 'فبراير', + 'مارس', + 'أبريل', + 'مايو', + 'يونيو', + 'يوليو', + 'أغسطس', + 'سبتمبر', + 'أكتوبر', + 'نوفمبر', + 'ديسمبر', +]; + +return [ + 'year' => implode('|', [':count سنة', 'سنة', 'سنتين', ':count سنوات', ':count سنة']), + 'a_year' => implode('|', [':count سنة', 'سنة', 'سنتين', ':count سنوات', ':count سنة']), + 'month' => implode('|', [':count شهر', 'شهر', 'شهرين', ':count أشهر', ':count شهر']), + 'a_month' => implode('|', [':count شهر', 'شهر', 'شهرين', ':count أشهر', ':count شهر']), + 'week' => implode('|', [':count أسبوع', 'أسبوع', 'أسبوعين', ':count أسابيع', ':count أسبوع']), + 'a_week' => implode('|', [':count أسبوع', 'أسبوع', 'أسبوعين', ':count أسابيع', ':count أسبوع']), + 'day' => implode('|', [':count يوم', 'يوم', 'يومين', ':count أيام', ':count يوم']), + 'a_day' => implode('|', [':count يوم', 'يوم', 'يومين', ':count أيام', ':count يوم']), + 'hour' => implode('|', [':count ساعة', 'ساعة', 'ساعتين', ':count ساعات', ':count ساعة']), + 'a_hour' => implode('|', [':count ساعة', 'ساعة', 'ساعتين', ':count ساعات', ':count ساعة']), + 'minute' => implode('|', [':count دقيقة', 'دقيقة', 'دقيقتين', ':count دقائق', ':count دقيقة']), + 'a_minute' => implode('|', [':count دقيقة', 'دقيقة', 'دقيقتين', ':count دقائق', ':count دقيقة']), + 'second' => implode('|', [':count ثانية', 'ثانية', 'ثانيتين', ':count ثواني', ':count ثانية']), + 'a_second' => implode('|', [':count ثانية', 'ثانية', 'ثانيتين', ':count ثواني', ':count ثانية']), + 'ago' => 'منذ :time', + 'from_now' => ':time من الآن', + 'after' => 'بعد :time', + 'before' => 'قبل :time', + 'diff_now' => 'الآن', + 'diff_today' => 'اليوم', + 'diff_today_regexp' => 'اليوم(?:\\s+عند)?(?:\\s+الساعة)?', + 'diff_yesterday' => 'أمس', + 'diff_yesterday_regexp' => 'أمس(?:\\s+عند)?(?:\\s+الساعة)?', + 'diff_tomorrow' => 'غداً', + 'diff_tomorrow_regexp' => 'غدًا(?:\\s+عند)?(?:\\s+الساعة)?', + 'diff_before_yesterday' => 'قبل الأمس', + 'diff_after_tomorrow' => 'بعد غد', + 'period_recurrences' => implode('|', ['مرة', 'مرة', ':count مرتين', ':count مرات', ':count مرة']), + 'period_interval' => 'كل :interval', + 'period_start_date' => 'من :date', + 'period_end_date' => 'إلى :date', + 'months' => $months, + 'months_short' => $months, + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'], + 'weekdays_min' => ['ح', 'اث', 'ثل', 'أر', 'خم', 'ج', 'س'], + 'list' => ['، ', ' و '], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[اليوم عند الساعة] LT', + 'nextDay' => '[غدًا عند الساعة] LT', + 'nextWeek' => 'dddd [عند الساعة] LT', + 'lastDay' => '[أمس عند الساعة] LT', + 'lastWeek' => 'dddd [عند الساعة] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['ص', 'م'], + 'weekend' => [5, 6], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_MA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_MA.php new file mode 100644 index 0000000..047ae05 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_MA.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Authors: + * - Josh Soref + * - JD Isaacks + * - Atef Ben Ali (atefBB) + * - Mohamed Sabil (mohamedsabil83) + */ +$months = [ + 'يناير', + 'فبراير', + 'مارس', + 'أبريل', + 'ماي', + 'يونيو', + 'يوليوز', + 'غشت', + 'شتنبر', + 'أكتوبر', + 'نونبر', + 'دجنبر', +]; + +return [ + 'year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']), + 'a_year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']), + 'month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']), + 'a_month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']), + 'week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']), + 'a_week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']), + 'day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']), + 'a_day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']), + 'hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']), + 'a_hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']), + 'minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']), + 'a_minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']), + 'second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']), + 'a_second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']), + 'ago' => 'منذ :time', + 'from_now' => 'في :time', + 'after' => 'بعد :time', + 'before' => 'قبل :time', + 'diff_now' => 'الآن', + 'diff_today' => 'اليوم', + 'diff_today_regexp' => 'اليوم(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_yesterday' => 'أمس', + 'diff_yesterday_regexp' => 'أمس(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_tomorrow' => 'غداً', + 'diff_tomorrow_regexp' => 'غدا(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_before_yesterday' => 'قبل الأمس', + 'diff_after_tomorrow' => 'بعد غد', + 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']), + 'period_interval' => 'كل :interval', + 'period_start_date' => 'من :date', + 'period_end_date' => 'إلى :date', + 'months' => $months, + 'months_short' => $months, + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'list' => ['، ', ' و '], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[اليوم على الساعة] LT', + 'nextDay' => '[غدا على الساعة] LT', + 'nextWeek' => 'dddd [على الساعة] LT', + 'lastDay' => '[أمس على الساعة] LT', + 'lastWeek' => 'dddd [على الساعة] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['ص', 'م'], + 'weekend' => [5, 6], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_MR.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_MR.php new file mode 100644 index 0000000..e790b99 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_MR.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ar.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_OM.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_OM.php new file mode 100644 index 0000000..3518096 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_OM.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + * - Abdullah-Alhariri + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'DD MMM, YYYY', + ], + 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'], + 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_PS.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_PS.php new file mode 100644 index 0000000..503c60d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_PS.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Abdullah-Alhariri + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_QA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_QA.php new file mode 100644 index 0000000..3518096 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_QA.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + * - Abdullah-Alhariri + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'DD MMM, YYYY', + ], + 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'], + 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_SA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SA.php new file mode 100644 index 0000000..550b0c7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SA.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Authors: + * - Josh Soref + * - JD Isaacks + * - Atef Ben Ali (atefBB) + * - Mohamed Sabil (mohamedsabil83) + * - Abdullah-Alhariri + */ +$months = [ + 'يناير', + 'فبراير', + 'مارس', + 'أبريل', + 'مايو', + 'يونيو', + 'يوليو', + 'أغسطس', + 'سبتمبر', + 'أكتوبر', + 'نوفمبر', + 'ديسمبر', +]; + +return [ + 'year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']), + 'a_year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']), + 'month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']), + 'a_month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']), + 'week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']), + 'a_week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']), + 'day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']), + 'a_day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']), + 'hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']), + 'a_hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']), + 'minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']), + 'a_minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']), + 'second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']), + 'a_second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']), + 'ago' => 'منذ :time', + 'from_now' => 'في :time', + 'after' => 'بعد :time', + 'before' => 'قبل :time', + 'diff_now' => 'الآن', + 'diff_today' => 'اليوم', + 'diff_today_regexp' => 'اليوم(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_yesterday' => 'أمس', + 'diff_yesterday_regexp' => 'أمس(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_tomorrow' => 'غداً', + 'diff_tomorrow_regexp' => 'غدا(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_before_yesterday' => 'قبل الأمس', + 'diff_after_tomorrow' => 'بعد غد', + 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']), + 'period_interval' => 'كل :interval', + 'period_start_date' => 'من :date', + 'period_end_date' => 'إلى :date', + 'months' => $months, + 'months_short' => $months, + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'list' => ['، ', ' و '], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[اليوم على الساعة] LT', + 'nextDay' => '[غدا على الساعة] LT', + 'nextWeek' => 'dddd [على الساعة] LT', + 'lastDay' => '[أمس على الساعة] LT', + 'lastWeek' => 'dddd [على الساعة] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['ص', 'م'], + 'weekend' => [5, 6], + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_SD.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SD.php new file mode 100644 index 0000000..3518096 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SD.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + * - Abdullah-Alhariri + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'DD MMM, YYYY', + ], + 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'], + 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_SO.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SO.php new file mode 100644 index 0000000..e790b99 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SO.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ar.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_SS.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SS.php new file mode 100644 index 0000000..32f3282 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SS.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'DD MMM, YYYY', + ], + 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'], + 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_SY.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SY.php new file mode 100644 index 0000000..2d42008 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SY.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + * - Abdullah-Alhariri + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'DD MMM, YYYY', + ], + 'months' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], + 'months_short' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php new file mode 100644 index 0000000..c2d4b43 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Abdellah Chadidi + * - Atef Ben Ali (atefBB) + * - Mohamed Sabil (mohamedsabil83) + */ +// Same for long and short +$months = [ + // @TODO add shakl to months + 'يناير', + 'فبراير', + 'مارس', + 'أبريل', + 'مايو', + 'يونيو', + 'يوليو', + 'أغسطس', + 'سبتمبر', + 'أكتوبر', + 'نوفمبر', + 'ديسمبر', +]; + +return [ + 'year' => implode('|', ['{0}:count سَنَة', '{1}سَنَة', '{2}سَنَتَيْن', ']2,11[:count سَنَوَات', ']10,Inf[:count سَنَة']), + 'a_year' => implode('|', ['{0}:count سَنَة', '{1}سَنَة', '{2}سَنَتَيْن', ']2,11[:count سَنَوَات', ']10,Inf[:count سَنَة']), + 'month' => implode('|', ['{0}:count شَهْرَ', '{1}شَهْرَ', '{2}شَهْرَيْن', ']2,11[:count أَشْهُر', ']10,Inf[:count شَهْرَ']), + 'a_month' => implode('|', ['{0}:count شَهْرَ', '{1}شَهْرَ', '{2}شَهْرَيْن', ']2,11[:count أَشْهُر', ']10,Inf[:count شَهْرَ']), + 'week' => implode('|', ['{0}:count أُسْبُوع', '{1}أُسْبُوع', '{2}أُسْبُوعَيْن', ']2,11[:count أَسَابِيع', ']10,Inf[:count أُسْبُوع']), + 'a_week' => implode('|', ['{0}:count أُسْبُوع', '{1}أُسْبُوع', '{2}أُسْبُوعَيْن', ']2,11[:count أَسَابِيع', ']10,Inf[:count أُسْبُوع']), + 'day' => implode('|', ['{0}:count يَوْم', '{1}يَوْم', '{2}يَوْمَيْن', ']2,11[:count أَيَّام', ']10,Inf[:count يَوْم']), + 'a_day' => implode('|', ['{0}:count يَوْم', '{1}يَوْم', '{2}يَوْمَيْن', ']2,11[:count أَيَّام', ']10,Inf[:count يَوْم']), + 'hour' => implode('|', ['{0}:count سَاعَة', '{1}سَاعَة', '{2}سَاعَتَيْن', ']2,11[:count سَاعَات', ']10,Inf[:count سَاعَة']), + 'a_hour' => implode('|', ['{0}:count سَاعَة', '{1}سَاعَة', '{2}سَاعَتَيْن', ']2,11[:count سَاعَات', ']10,Inf[:count سَاعَة']), + 'minute' => implode('|', ['{0}:count دَقِيقَة', '{1}دَقِيقَة', '{2}دَقِيقَتَيْن', ']2,11[:count دَقَائِق', ']10,Inf[:count دَقِيقَة']), + 'a_minute' => implode('|', ['{0}:count دَقِيقَة', '{1}دَقِيقَة', '{2}دَقِيقَتَيْن', ']2,11[:count دَقَائِق', ']10,Inf[:count دَقِيقَة']), + 'second' => implode('|', ['{0}:count ثَانِيَة', '{1}ثَانِيَة', '{2}ثَانِيَتَيْن', ']2,11[:count ثَوَان', ']10,Inf[:count ثَانِيَة']), + 'a_second' => implode('|', ['{0}:count ثَانِيَة', '{1}ثَانِيَة', '{2}ثَانِيَتَيْن', ']2,11[:count ثَوَان', ']10,Inf[:count ثَانِيَة']), + 'ago' => 'مُنْذُ :time', + 'from_now' => 'مِنَ الْآن :time', + 'after' => 'بَعْدَ :time', + 'before' => 'قَبْلَ :time', + + // @TODO add shakl to translations below + 'diff_now' => 'الآن', + 'diff_today' => 'اليوم', + 'diff_today_regexp' => 'اليوم(?:\\s+عند)?(?:\\s+الساعة)?', + 'diff_yesterday' => 'أمس', + 'diff_yesterday_regexp' => 'أمس(?:\\s+عند)?(?:\\s+الساعة)?', + 'diff_tomorrow' => 'غداً', + 'diff_tomorrow_regexp' => 'غدًا(?:\\s+عند)?(?:\\s+الساعة)?', + 'diff_before_yesterday' => 'قبل الأمس', + 'diff_after_tomorrow' => 'بعد غد', + 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']), + 'period_interval' => 'كل :interval', + 'period_start_date' => 'من :date', + 'period_end_date' => 'إلى :date', + 'months' => $months, + 'months_short' => $months, + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'], + 'weekdays_min' => ['ح', 'اث', 'ثل', 'أر', 'خم', 'ج', 'س'], + 'list' => ['، ', ' و '], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[اليوم عند الساعة] LT', + 'nextDay' => '[غدًا عند الساعة] LT', + 'nextWeek' => 'dddd [عند الساعة] LT', + 'lastDay' => '[أمس عند الساعة] LT', + 'lastWeek' => 'dddd [عند الساعة] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['ص', 'م'], + 'weekend' => [5, 6], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_TD.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_TD.php new file mode 100644 index 0000000..e790b99 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_TD.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ar.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_TN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_TN.php new file mode 100644 index 0000000..f096678 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_TN.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Authors: + * - JD Isaacks + * - Atef Ben Ali (atefBB) + * - Mohamed Sabil (mohamedsabil83) + */ +$months = [ + 'جانفي', + 'فيفري', + 'مارس', + 'أفريل', + 'ماي', + 'جوان', + 'جويلية', + 'أوت', + 'سبتمبر', + 'أكتوبر', + 'نوفمبر', + 'ديسمبر', +]; + +return [ + 'year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']), + 'a_year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']), + 'month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']), + 'a_month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']), + 'week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']), + 'a_week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']), + 'day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']), + 'a_day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']), + 'hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']), + 'a_hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']), + 'minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']), + 'a_minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']), + 'second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']), + 'a_second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']), + 'ago' => 'منذ :time', + 'from_now' => 'في :time', + 'after' => 'بعد :time', + 'before' => 'قبل :time', + 'diff_now' => 'الآن', + 'diff_today' => 'اليوم', + 'diff_today_regexp' => 'اليوم(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_yesterday' => 'أمس', + 'diff_yesterday_regexp' => 'أمس(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_tomorrow' => 'غداً', + 'diff_tomorrow_regexp' => 'غدا(?:\\s+على)?(?:\\s+الساعة)?', + 'diff_before_yesterday' => 'قبل الأمس', + 'diff_after_tomorrow' => 'بعد غد', + 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']), + 'period_interval' => 'كل :interval', + 'period_start_date' => 'من :date', + 'period_end_date' => 'إلى :date', + 'months' => $months, + 'months_short' => $months, + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'list' => ['، ', ' و '], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[اليوم على الساعة] LT', + 'nextDay' => '[غدا على الساعة] LT', + 'nextWeek' => 'dddd [على الساعة] LT', + 'lastDay' => '[أمس على الساعة] LT', + 'lastWeek' => 'dddd [على الساعة] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['ص', 'م'], + 'weekend' => [5, 6], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_YE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_YE.php new file mode 100644 index 0000000..169fe88 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_YE.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + * - Abdullah-Alhariri + */ +return array_replace_recursive(require __DIR__.'/ar.php', [ + 'formats' => [ + 'L' => 'DD MMM, YYYY', + ], + 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'], + 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'], + 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], + 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/as.php b/vendor/nesbot/carbon/src/Carbon/Lang/as.php new file mode 100644 index 0000000..04bc3df --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/as.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/as_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/as_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/as_IN.php new file mode 100644 index 0000000..5fbc3db --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/as_IN.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Amitakhya Phukan, Red Hat bug-glibc@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D-MM-YYYY', + ], + 'months' => ['জানুৱাৰী', 'ফেব্ৰুৱাৰী', 'মাৰ্চ', 'এপ্ৰিল', 'মে', 'জুন', 'জুলাই', 'আগষ্ট', 'ছেপ্তেম্বৰ', 'অক্টোবৰ', 'নৱেম্বৰ', 'ডিচেম্বৰ'], + 'months_short' => ['জানু', 'ফেব্ৰু', 'মাৰ্চ', 'এপ্ৰিল', 'মে', 'জুন', 'জুলাই', 'আগ', 'সেপ্ট', 'অক্টো', 'নভে', 'ডিসে'], + 'weekdays' => ['দেওবাৰ', 'সোমবাৰ', 'মঙ্গলবাৰ', 'বুধবাৰ', 'বৃহষ্পতিবাৰ', 'শুক্ৰবাৰ', 'শনিবাৰ'], + 'weekdays_short' => ['দেও', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহষ্পতি', 'শুক্ৰ', 'শনি'], + 'weekdays_min' => ['দেও', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহষ্পতি', 'শুক্ৰ', 'শনি'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['পূৰ্ব্বাহ্ন', 'অপৰাহ্ন'], + + 'year' => ':count বছৰ', + 'y' => ':count বছৰ', + 'a_year' => ':count বছৰ', + + 'month' => ':count মাহ', + 'm' => ':count মাহ', + 'a_month' => ':count মাহ', + + 'week' => ':count সপ্তাহ', + 'w' => ':count সপ্তাহ', + 'a_week' => ':count সপ্তাহ', + + 'day' => ':count বাৰ', + 'd' => ':count বাৰ', + 'a_day' => ':count বাৰ', + + 'hour' => ':count ঘণ্টা', + 'h' => ':count ঘণ্টা', + 'a_hour' => ':count ঘণ্টা', + + 'minute' => ':count মিনিট', + 'min' => ':count মিনিট', + 'a_minute' => ':count মিনিট', + + 'second' => ':count দ্বিতীয়', + 's' => ':count দ্বিতীয়', + 'a_second' => ':count দ্বিতীয়', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/asa.php b/vendor/nesbot/carbon/src/Carbon/Lang/asa.php new file mode 100644 index 0000000..03bb483 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/asa.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['icheheavo', 'ichamthi'], + 'weekdays' => ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi', 'Ijumaa', 'Jumamosi'], + 'weekdays_short' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Ijm', 'Jmo'], + 'weekdays_min' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Ijm', 'Jmo'], + 'months' => ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'], + 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Dec'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ast.php b/vendor/nesbot/carbon/src/Carbon/Lang/ast.php new file mode 100644 index 0000000..d9bdebe --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ast.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Jordi Mallach jordi@gnu.org + * - Adolfo Jayme-Barrientos (fitojb) + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['de xineru', 'de febreru', 'de marzu', 'd’abril', 'de mayu', 'de xunu', 'de xunetu', 'd’agostu', 'de setiembre', 'd’ochobre', 'de payares', 'd’avientu'], + 'months_short' => ['xin', 'feb', 'mar', 'abr', 'may', 'xun', 'xnt', 'ago', 'set', 'och', 'pay', 'avi'], + 'weekdays' => ['domingu', 'llunes', 'martes', 'miércoles', 'xueves', 'vienres', 'sábadu'], + 'weekdays_short' => ['dom', 'llu', 'mar', 'mié', 'xue', 'vie', 'sáb'], + 'weekdays_min' => ['dom', 'llu', 'mar', 'mié', 'xue', 'vie', 'sáb'], + + 'year' => ':count añu|:count años', + 'y' => ':count añu|:count años', + 'a_year' => 'un añu|:count años', + + 'month' => ':count mes', + 'm' => ':count mes', + 'a_month' => 'un mes|:count mes', + + 'week' => ':count selmana|:count selmanes', + 'w' => ':count selmana|:count selmanes', + 'a_week' => 'una selmana|:count selmanes', + + 'day' => ':count día|:count díes', + 'd' => ':count día|:count díes', + 'a_day' => 'un día|:count díes', + + 'hour' => ':count hora|:count hores', + 'h' => ':count hora|:count hores', + 'a_hour' => 'una hora|:count hores', + + 'minute' => ':count minutu|:count minutos', + 'min' => ':count minutu|:count minutos', + 'a_minute' => 'un minutu|:count minutos', + + 'second' => ':count segundu|:count segundos', + 's' => ':count segundu|:count segundos', + 'a_second' => 'un segundu|:count segundos', + + 'ago' => 'hai :time', + 'from_now' => 'en :time', + 'after' => ':time dempués', + 'before' => ':time enantes', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ast_ES.php b/vendor/nesbot/carbon/src/Carbon/Lang/ast_ES.php new file mode 100644 index 0000000..04d7562 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ast_ES.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ast.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ayc.php b/vendor/nesbot/carbon/src/Carbon/Lang/ayc.php new file mode 100644 index 0000000..d6a6f63 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ayc.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/ayc_PE.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ayc_PE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ayc_PE.php new file mode 100644 index 0000000..ff18504 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ayc_PE.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - runasimipi.org libc-alpha@sourceware.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['inïru', 'phiwriru', 'marsu', 'awrila', 'mayu', 'junyu', 'julyu', 'awustu', 'sitimri', 'uktuwri', 'nuwimri', 'risimri'], + 'months_short' => ['ini', 'phi', 'mar', 'awr', 'may', 'jun', 'jul', 'awu', 'sit', 'ukt', 'nuw', 'ris'], + 'weekdays' => ['tuminku', 'lunisa', 'martisa', 'mirkulisa', 'juywisa', 'wirnisa', 'sawäru'], + 'weekdays_short' => ['tum', 'lun', 'mar', 'mir', 'juy', 'wir', 'saw'], + 'weekdays_min' => ['tum', 'lun', 'mar', 'mir', 'juy', 'wir', 'saw'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['VM', 'NM'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/az.php b/vendor/nesbot/carbon/src/Carbon/Lang/az.php new file mode 100644 index 0000000..1e92106 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/az.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - Kunal Marwaha + * - François B + * - JD Isaacks + * - Orxan + * - Şəhriyar İmanov + * - Baran Şengül + */ +return [ + 'year' => ':count il', + 'a_year' => '{1}bir il|]1,Inf[:count il', + 'y' => ':count il', + 'month' => ':count ay', + 'a_month' => '{1}bir ay|]1,Inf[:count ay', + 'm' => ':count ay', + 'week' => ':count həftə', + 'a_week' => '{1}bir həftə|]1,Inf[:count həftə', + 'w' => ':count h.', + 'day' => ':count gün', + 'a_day' => '{1}bir gün|]1,Inf[:count gün', + 'd' => ':count g.', + 'hour' => ':count saat', + 'a_hour' => '{1}bir saat|]1,Inf[:count saat', + 'h' => ':count saat', + 'minute' => ':count d.', + 'a_minute' => '{1}bir dəqiqə|]1,Inf[:count dəqiqə', + 'min' => ':count dəqiqə', + 'second' => ':count san.', + 'a_second' => '{1}birneçə saniyə|]1,Inf[:count saniyə', + 's' => ':count saniyə', + 'ago' => ':time əvvəl', + 'from_now' => ':time sonra', + 'after' => ':time sonra', + 'before' => ':time əvvəl', + 'diff_now' => 'indi', + 'diff_today' => 'bugün', + 'diff_today_regexp' => 'bugün(?:\\s+saat)?', + 'diff_yesterday' => 'dünən', + 'diff_tomorrow' => 'sabah', + 'diff_tomorrow_regexp' => 'sabah(?:\\s+saat)?', + 'diff_before_yesterday' => 'srağagün', + 'diff_after_tomorrow' => 'birisi gün', + 'period_recurrences' => ':count dəfədən bir', + 'period_interval' => 'hər :interval', + 'period_start_date' => ':date tarixindən başlayaraq', + 'period_end_date' => ':date tarixinədək', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[bugün saat] LT', + 'nextDay' => '[sabah saat] LT', + 'nextWeek' => '[gələn həftə] dddd [saat] LT', + 'lastDay' => '[dünən] LT', + 'lastWeek' => '[keçən həftə] dddd [saat] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + if ($number === 0) { // special case for zero + return "$number-ıncı"; + } + + static $suffixes = [ + 1 => '-inci', + 5 => '-inci', + 8 => '-inci', + 70 => '-inci', + 80 => '-inci', + 2 => '-nci', + 7 => '-nci', + 20 => '-nci', + 50 => '-nci', + 3 => '-üncü', + 4 => '-üncü', + 100 => '-üncü', + 6 => '-ncı', + 9 => '-uncu', + 10 => '-uncu', + 30 => '-uncu', + 60 => '-ıncı', + 90 => '-ıncı', + ]; + + $lastDigit = $number % 10; + + return $number.($suffixes[$lastDigit] ?? $suffixes[$number % 100 - $lastDigit] ?? $suffixes[$number >= 100 ? 100 : -1] ?? ''); + }, + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'gecə'; + } + if ($hour < 12) { + return 'səhər'; + } + if ($hour < 17) { + return 'gündüz'; + } + + return 'axşam'; + }, + 'months' => ['yanvar', 'fevral', 'mart', 'aprel', 'may', 'iyun', 'iyul', 'avqust', 'sentyabr', 'oktyabr', 'noyabr', 'dekabr'], + 'months_short' => ['yan', 'fev', 'mar', 'apr', 'may', 'iyn', 'iyl', 'avq', 'sen', 'okt', 'noy', 'dek'], + 'months_standalone' => ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'İyun', 'İyul', 'Avqust', 'Sentyabr', 'Oktyabr', 'Noyabr', 'Dekabr'], + 'weekdays' => ['bazar', 'bazar ertəsi', 'çərşənbə axşamı', 'çərşənbə', 'cümə axşamı', 'cümə', 'şənbə'], + 'weekdays_short' => ['baz', 'bze', 'çax', 'çər', 'cax', 'cüm', 'şən'], + 'weekdays_min' => ['bz', 'be', 'ça', 'çə', 'ca', 'cü', 'şə'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' və '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/az_AZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/az_AZ.php new file mode 100644 index 0000000..2acf881 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/az_AZ.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Pablo Saratxaga pablo@mandrakesoft.com + */ +return array_replace_recursive(require __DIR__.'/az.php', [ + 'months_short' => ['Yan', 'Fev', 'Mar', 'Apr', 'May', 'İyn', 'İyl', 'Avq', 'Sen', 'Okt', 'Noy', 'Dek'], + 'weekdays' => ['bazar günü', 'bazar ertəsi', 'çərşənbə axşamı', 'çərşənbə', 'cümə axşamı', 'cümə', 'şənbə'], + 'weekdays_short' => ['baz', 'ber', 'çax', 'çər', 'cax', 'cüm', 'şnb'], + 'weekdays_min' => ['baz', 'ber', 'çax', 'çər', 'cax', 'cüm', 'şnb'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/az_Cyrl.php b/vendor/nesbot/carbon/src/Carbon/Lang/az_Cyrl.php new file mode 100644 index 0000000..28fc62f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/az_Cyrl.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/az.php', [ + 'weekdays' => ['базар', 'базар ертәси', 'чәршәнбә ахшамы', 'чәршәнбә', 'ҹүмә ахшамы', 'ҹүмә', 'шәнбә'], + 'weekdays_short' => ['Б.', 'Б.Е.', 'Ч.А.', 'Ч.', 'Ҹ.А.', 'Ҹ.', 'Ш.'], + 'weekdays_min' => ['Б.', 'Б.Е.', 'Ч.А.', 'Ч.', 'Ҹ.А.', 'Ҹ.', 'Ш.'], + 'months' => ['јанвар', 'феврал', 'март', 'апрел', 'май', 'ијун', 'ијул', 'август', 'сентјабр', 'октјабр', 'нојабр', 'декабр'], + 'months_short' => ['јан', 'фев', 'мар', 'апр', 'май', 'ијн', 'ијл', 'авг', 'сен', 'окт', 'ној', 'дек'], + 'months_standalone' => ['Јанвар', 'Феврал', 'Март', 'Апрел', 'Май', 'Ијун', 'Ијул', 'Август', 'Сентјабр', 'Октјабр', 'Нојабр', 'Декабр'], + 'meridiem' => ['а', 'п'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/az_IR.php b/vendor/nesbot/carbon/src/Carbon/Lang/az_IR.php new file mode 100644 index 0000000..991a0ef --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/az_IR.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Mousa Moradi mousamk@gmail.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'OY/OM/OD', + ], + 'months' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مئی', 'ژوئن', 'جولای', 'آقۇست', 'سپتامبر', 'اوْکتوْبر', 'نوْوامبر', 'دسامبر'], + 'months_short' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مئی', 'ژوئن', 'جولای', 'آقۇست', 'سپتامبر', 'اوْکتوْبر', 'نوْوامبر', 'دسامبر'], + 'weekdays' => ['یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چارشنبه', 'جۆمعه آخشامی', 'جۆمعه', 'شنبه'], + 'weekdays_short' => ['یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چارشنبه', 'جۆمعه آخشامی', 'جۆمعه', 'شنبه'], + 'weekdays_min' => ['یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چارشنبه', 'جۆمعه آخشامی', 'جۆمعه', 'شنبه'], + 'first_day_of_week' => 6, + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰۴', '۰۵', '۰۶', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱۴', '۱۵', '۱۶', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲۴', '۲۵', '۲۶', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳۴', '۳۵', '۳۶', '۳۷', '۳۸', '۳۹', '۴۰', '۴۱', '۴۲', '۴۳', '۴۴', '۴۵', '۴۶', '۴۷', '۴۸', '۴۹', '۵۰', '۵۱', '۵۲', '۵۳', '۵۴', '۵۵', '۵۶', '۵۷', '۵۸', '۵۹', '۶۰', '۶۱', '۶۲', '۶۳', '۶۴', '۶۵', '۶۶', '۶۷', '۶۸', '۶۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷۴', '۷۵', '۷۶', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸۴', '۸۵', '۸۶', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹۴', '۹۵', '۹۶', '۹۷', '۹۸', '۹۹'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/az_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/az_Latn.php new file mode 100644 index 0000000..0be3391 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/az_Latn.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/az.php', [ + 'meridiem' => ['a', 'p'], + 'weekdays' => ['bazar', 'bazar ertəsi', 'çərşənbə axşamı', 'çərşənbə', 'cümə axşamı', 'cümə', 'şənbə'], + 'weekdays_short' => ['B.', 'B.E.', 'Ç.A.', 'Ç.', 'C.A.', 'C.', 'Ş.'], + 'weekdays_min' => ['B.', 'B.E.', 'Ç.A.', 'Ç.', 'C.A.', 'C.', 'Ş.'], + 'months' => ['yanvar', 'fevral', 'mart', 'aprel', 'may', 'iyun', 'iyul', 'avqust', 'sentyabr', 'oktyabr', 'noyabr', 'dekabr'], + 'months_short' => ['yan', 'fev', 'mar', 'apr', 'may', 'iyn', 'iyl', 'avq', 'sen', 'okt', 'noy', 'dek'], + 'months_standalone' => ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'İyun', 'İyul', 'Avqust', 'Sentyabr', 'Oktyabr', 'Noyabr', 'Dekabr'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'D MMMM YYYY, dddd HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bas.php b/vendor/nesbot/carbon/src/Carbon/Lang/bas.php new file mode 100644 index 0000000..41bfa1d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bas.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['I bikɛ̂glà', 'I ɓugajɔp'], + 'weekdays' => ['ŋgwà nɔ̂y', 'ŋgwà njaŋgumba', 'ŋgwà ûm', 'ŋgwà ŋgê', 'ŋgwà mbɔk', 'ŋgwà kɔɔ', 'ŋgwà jôn'], + 'weekdays_short' => ['nɔy', 'nja', 'uum', 'ŋge', 'mbɔ', 'kɔɔ', 'jon'], + 'weekdays_min' => ['nɔy', 'nja', 'uum', 'ŋge', 'mbɔ', 'kɔɔ', 'jon'], + 'months' => ['Kɔndɔŋ', 'Màcɛ̂l', 'Màtùmb', 'Màtop', 'M̀puyɛ', 'Hìlòndɛ̀', 'Njèbà', 'Hìkaŋ', 'Dìpɔ̀s', 'Bìòôm', 'Màyɛsèp', 'Lìbuy li ńyèe'], + 'months_short' => ['kɔn', 'mac', 'mat', 'mto', 'mpu', 'hil', 'nje', 'hik', 'dip', 'bio', 'may', 'liɓ'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + + 'second' => ':count móndî', // less reliable + 's' => ':count móndî', // less reliable + 'a_second' => ':count móndî', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/be.php b/vendor/nesbot/carbon/src/Carbon/Lang/be.php new file mode 100644 index 0000000..ee73636 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/be.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Carbon\CarbonInterface; +use Symfony\Component\Translation\PluralizationRules; + +// @codeCoverageIgnoreStart +if (class_exists(PluralizationRules::class)) { + PluralizationRules::set(static function ($number) { + return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); + }, 'be'); +} +// @codeCoverageIgnoreEnd + +/* + * Authors: + * - Josh Soref + * - SobakaSlava + * - François B + * - Serhan Apaydın + * - JD Isaacks + * - AbadonnaAbbys + * - Siomkin Alexander + */ +return [ + 'year' => ':count год|:count гады|:count гадоў', + 'a_year' => '{1}год|:count год|:count гады|:count гадоў', + 'y' => ':count год|:count гады|:count гадоў', + 'month' => ':count месяц|:count месяцы|:count месяцаў', + 'a_month' => '{1}месяц|:count месяц|:count месяцы|:count месяцаў', + 'm' => ':count месяц|:count месяцы|:count месяцаў', + 'week' => ':count тыдзень|:count тыдні|:count тыдняў', + 'a_week' => '{1}тыдзень|:count тыдзень|:count тыдні|:count тыдняў', + 'w' => ':count тыдзень|:count тыдні|:count тыдняў', + 'day' => ':count дзень|:count дні|:count дзён', + 'a_day' => '{1}дзень|:count дзень|:count дні|:count дзён', + 'd' => ':count дн', + 'hour' => ':count гадзіну|:count гадзіны|:count гадзін', + 'a_hour' => '{1}гадзіна|:count гадзіна|:count гадзіны|:count гадзін', + 'h' => ':count гадзіна|:count гадзіны|:count гадзін', + 'minute' => ':count хвіліна|:count хвіліны|:count хвілін', + 'a_minute' => '{1}хвіліна|:count хвіліна|:count хвіліны|:count хвілін', + 'min' => ':count хв', + 'second' => ':count секунда|:count секунды|:count секунд', + 'a_second' => '{1}некалькі секунд|:count секунда|:count секунды|:count секунд', + 's' => ':count сек', + + 'hour_ago' => ':count гадзіну|:count гадзіны|:count гадзін', + 'a_hour_ago' => '{1}гадзіну|:count гадзіну|:count гадзіны|:count гадзін', + 'h_ago' => ':count гадзіну|:count гадзіны|:count гадзін', + 'minute_ago' => ':count хвіліну|:count хвіліны|:count хвілін', + 'a_minute_ago' => '{1}хвіліну|:count хвіліну|:count хвіліны|:count хвілін', + 'min_ago' => ':count хвіліну|:count хвіліны|:count хвілін', + 'second_ago' => ':count секунду|:count секунды|:count секунд', + 'a_second_ago' => '{1}некалькі секунд|:count секунду|:count секунды|:count секунд', + 's_ago' => ':count секунду|:count секунды|:count секунд', + + 'hour_from_now' => ':count гадзіну|:count гадзіны|:count гадзін', + 'a_hour_from_now' => '{1}гадзіну|:count гадзіну|:count гадзіны|:count гадзін', + 'h_from_now' => ':count гадзіну|:count гадзіны|:count гадзін', + 'minute_from_now' => ':count хвіліну|:count хвіліны|:count хвілін', + 'a_minute_from_now' => '{1}хвіліну|:count хвіліну|:count хвіліны|:count хвілін', + 'min_from_now' => ':count хвіліну|:count хвіліны|:count хвілін', + 'second_from_now' => ':count секунду|:count секунды|:count секунд', + 'a_second_from_now' => '{1}некалькі секунд|:count секунду|:count секунды|:count секунд', + 's_from_now' => ':count секунду|:count секунды|:count секунд', + + 'hour_after' => ':count гадзіну|:count гадзіны|:count гадзін', + 'a_hour_after' => '{1}гадзіну|:count гадзіну|:count гадзіны|:count гадзін', + 'h_after' => ':count гадзіну|:count гадзіны|:count гадзін', + 'minute_after' => ':count хвіліну|:count хвіліны|:count хвілін', + 'a_minute_after' => '{1}хвіліну|:count хвіліну|:count хвіліны|:count хвілін', + 'min_after' => ':count хвіліну|:count хвіліны|:count хвілін', + 'second_after' => ':count секунду|:count секунды|:count секунд', + 'a_second_after' => '{1}некалькі секунд|:count секунду|:count секунды|:count секунд', + 's_after' => ':count секунду|:count секунды|:count секунд', + + 'hour_before' => ':count гадзіну|:count гадзіны|:count гадзін', + 'a_hour_before' => '{1}гадзіну|:count гадзіну|:count гадзіны|:count гадзін', + 'h_before' => ':count гадзіну|:count гадзіны|:count гадзін', + 'minute_before' => ':count хвіліну|:count хвіліны|:count хвілін', + 'a_minute_before' => '{1}хвіліну|:count хвіліну|:count хвіліны|:count хвілін', + 'min_before' => ':count хвіліну|:count хвіліны|:count хвілін', + 'second_before' => ':count секунду|:count секунды|:count секунд', + 'a_second_before' => '{1}некалькі секунд|:count секунду|:count секунды|:count секунд', + 's_before' => ':count секунду|:count секунды|:count секунд', + + 'ago' => ':time таму', + 'from_now' => 'праз :time', + 'after' => ':time пасля', + 'before' => ':time да', + 'diff_now' => 'цяпер', + 'diff_today' => 'Сёння', + 'diff_today_regexp' => 'Сёння(?:\\s+ў)?', + 'diff_yesterday' => 'учора', + 'diff_yesterday_regexp' => 'Учора(?:\\s+ў)?', + 'diff_tomorrow' => 'заўтра', + 'diff_tomorrow_regexp' => 'Заўтра(?:\\s+ў)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY г.', + 'LLL' => 'D MMMM YYYY г., HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY г., HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Сёння ў] LT', + 'nextDay' => '[Заўтра ў] LT', + 'nextWeek' => '[У] dddd [ў] LT', + 'lastDay' => '[Учора ў] LT', + 'lastWeek' => function (CarbonInterface $current) { + switch ($current->dayOfWeek) { + case 1: + case 2: + case 4: + return '[У мінулы] dddd [ў] LT'; + default: + return '[У мінулую] dddd [ў] LT'; + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => function ($number, $period) { + switch ($period) { + case 'M': + case 'd': + case 'DDD': + case 'w': + case 'W': + return ($number % 10 === 2 || $number % 10 === 3) && ($number % 100 !== 12 && $number % 100 !== 13) ? $number.'-і' : $number.'-ы'; + case 'D': + return $number.'-га'; + default: + return $number; + } + }, + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'ночы'; + } + if ($hour < 12) { + return 'раніцы'; + } + if ($hour < 17) { + return 'дня'; + } + + return 'вечара'; + }, + 'months' => ['студзеня', 'лютага', 'сакавіка', 'красавіка', 'траўня', 'чэрвеня', 'ліпеня', 'жніўня', 'верасня', 'кастрычніка', 'лістапада', 'снежня'], + 'months_standalone' => ['студзень', 'люты', 'сакавік', 'красавік', 'травень', 'чэрвень', 'ліпень', 'жнівень', 'верасень', 'кастрычнік', 'лістапад', 'снежань'], + 'months_short' => ['студ', 'лют', 'сак', 'крас', 'трав', 'чэрв', 'ліп', 'жнів', 'вер', 'каст', 'ліст', 'снеж'], + 'months_regexp' => '/(DD?o?\.?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/', + 'weekdays' => ['нядзелю', 'панядзелак', 'аўторак', 'сераду', 'чацвер', 'пятніцу', 'суботу'], + 'weekdays_standalone' => ['нядзеля', 'панядзелак', 'аўторак', 'серада', 'чацвер', 'пятніца', 'субота'], + 'weekdays_short' => ['нд', 'пн', 'ат', 'ср', 'чц', 'пт', 'сб'], + 'weekdays_min' => ['нд', 'пн', 'ат', 'ср', 'чц', 'пт', 'сб'], + 'weekdays_regexp' => '/\[ ?[Ууў] ?(?:мінулую|наступную)? ?\] ?dddd/', + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' і '], + 'months_short_standalone' => ['сту', 'лют', 'сак', 'кра', 'май', 'чэр', 'ліп', 'жні', 'вер', 'кас', 'ліс', 'сне'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/be_BY.php b/vendor/nesbot/carbon/src/Carbon/Lang/be_BY.php new file mode 100644 index 0000000..26684b4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/be_BY.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/be.php', [ + 'months' => ['студзеня', 'лютага', 'сакавіка', 'красавіка', 'мая', 'чэрвеня', 'ліпеня', 'жніўня', 'верасня', 'кастрычніка', 'лістапада', 'снежня'], + 'months_short' => ['сту', 'лют', 'сак', 'кра', 'мая', 'чэр', 'ліп', 'жні', 'вер', 'кас', 'ліс', 'сне'], + 'weekdays' => ['Нядзеля', 'Панядзелак', 'Аўторак', 'Серада', 'Чацвер', 'Пятніца', 'Субота'], + 'weekdays_short' => ['Няд', 'Пан', 'Аўт', 'Срд', 'Чцв', 'Пят', 'Суб'], + 'weekdays_min' => ['Няд', 'Пан', 'Аўт', 'Срд', 'Чцв', 'Пят', 'Суб'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/be_BY@latin.php b/vendor/nesbot/carbon/src/Carbon/Lang/be_BY@latin.php new file mode 100644 index 0000000..517ce83 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/be_BY@latin.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['studzienia', 'lutaha', 'sakavika', 'krasavika', 'maja', 'červienia', 'lipienia', 'žniŭnia', 'vieraśnia', 'kastryčnika', 'listapada', 'śniežnia'], + 'months_short' => ['Stu', 'Lut', 'Sak', 'Kra', 'Maj', 'Čer', 'Lip', 'Žni', 'Vie', 'Kas', 'Lis', 'Śni'], + 'weekdays' => ['Niadziela', 'Paniadziełak', 'Aŭtorak', 'Sierada', 'Čaćvier', 'Piatnica', 'Subota'], + 'weekdays_short' => ['Nia', 'Pan', 'Aŭt', 'Sie', 'Čać', 'Pia', 'Sub'], + 'weekdays_min' => ['Nia', 'Pan', 'Aŭt', 'Sie', 'Čać', 'Pia', 'Sub'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bem.php b/vendor/nesbot/carbon/src/Carbon/Lang/bem.php new file mode 100644 index 0000000..1c3ef03 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bem.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/bem_ZM.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bem_ZM.php b/vendor/nesbot/carbon/src/Carbon/Lang/bem_ZM.php new file mode 100644 index 0000000..620b579 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bem_ZM.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - ANLoc Martin Benjamin locales@africanlocalization.net + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'MM/DD/YYYY', + ], + 'months' => ['Januari', 'Februari', 'Machi', 'Epreo', 'Mei', 'Juni', 'Julai', 'Ogasti', 'Septemba', 'Oktoba', 'Novemba', 'Disemba'], + 'months_short' => ['Jan', 'Feb', 'Mac', 'Epr', 'Mei', 'Jun', 'Jul', 'Oga', 'Sep', 'Okt', 'Nov', 'Dis'], + 'weekdays' => ['Pa Mulungu', 'Palichimo', 'Palichibuli', 'Palichitatu', 'Palichine', 'Palichisano', 'Pachibelushi'], + 'weekdays_short' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + 'weekdays_min' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['uluchelo', 'akasuba'], + + 'year' => 'myaka :count', + 'y' => 'myaka :count', + 'a_year' => 'myaka :count', + + 'month' => 'myeshi :count', + 'm' => 'myeshi :count', + 'a_month' => 'myeshi :count', + + 'week' => 'umulungu :count', + 'w' => 'umulungu :count', + 'a_week' => 'umulungu :count', + + 'day' => 'inshiku :count', + 'd' => 'inshiku :count', + 'a_day' => 'inshiku :count', + + 'hour' => 'awala :count', + 'h' => 'awala :count', + 'a_hour' => 'awala :count', + + 'minute' => 'miniti :count', + 'min' => 'miniti :count', + 'a_minute' => 'miniti :count', + + 'second' => 'sekondi :count', + 's' => 'sekondi :count', + 'a_second' => 'sekondi :count', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ber.php b/vendor/nesbot/carbon/src/Carbon/Lang/ber.php new file mode 100644 index 0000000..685603c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ber.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/ber_DZ.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ber_DZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/ber_DZ.php new file mode 100644 index 0000000..38de10a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ber_DZ.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Pablo Saratxaga pablo@mandrakesoft.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['yanvar', 'fevral', 'mart', 'aprel', 'may', 'iyun', 'iyul', 'avqust', 'sentyabr', 'oktyabr', 'noyabr', 'dekabr'], + 'months_short' => ['Yan', 'Fev', 'Mar', 'Apr', 'May', 'İyn', 'İyl', 'Avq', 'Sen', 'Okt', 'Noy', 'Dek'], + 'weekdays' => ['bazar günü', 'birinci gün', 'ikinci gün', 'üçüncü gün', 'dördüncü gün', 'beşinci gün', 'altıncı gün'], + 'weekdays_short' => ['baz', 'bir', 'iki', 'üçü', 'dör', 'beş', 'alt'], + 'weekdays_min' => ['baz', 'bir', 'iki', 'üçü', 'dör', 'beş', 'alt'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ber_MA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ber_MA.php new file mode 100644 index 0000000..38de10a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ber_MA.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Pablo Saratxaga pablo@mandrakesoft.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['yanvar', 'fevral', 'mart', 'aprel', 'may', 'iyun', 'iyul', 'avqust', 'sentyabr', 'oktyabr', 'noyabr', 'dekabr'], + 'months_short' => ['Yan', 'Fev', 'Mar', 'Apr', 'May', 'İyn', 'İyl', 'Avq', 'Sen', 'Okt', 'Noy', 'Dek'], + 'weekdays' => ['bazar günü', 'birinci gün', 'ikinci gün', 'üçüncü gün', 'dördüncü gün', 'beşinci gün', 'altıncı gün'], + 'weekdays_short' => ['baz', 'bir', 'iki', 'üçü', 'dör', 'beş', 'alt'], + 'weekdays_min' => ['baz', 'bir', 'iki', 'üçü', 'dör', 'beş', 'alt'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bez.php b/vendor/nesbot/carbon/src/Carbon/Lang/bez.php new file mode 100644 index 0000000..d59c5ef --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bez.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['pamilau', 'pamunyi'], + 'weekdays' => ['pa mulungu', 'pa shahuviluha', 'pa hivili', 'pa hidatu', 'pa hitayi', 'pa hihanu', 'pa shahulembela'], + 'weekdays_short' => ['Mul', 'Vil', 'Hiv', 'Hid', 'Hit', 'Hih', 'Lem'], + 'weekdays_min' => ['Mul', 'Vil', 'Hiv', 'Hid', 'Hit', 'Hih', 'Lem'], + 'months' => ['pa mwedzi gwa hutala', 'pa mwedzi gwa wuvili', 'pa mwedzi gwa wudatu', 'pa mwedzi gwa wutai', 'pa mwedzi gwa wuhanu', 'pa mwedzi gwa sita', 'pa mwedzi gwa saba', 'pa mwedzi gwa nane', 'pa mwedzi gwa tisa', 'pa mwedzi gwa kumi', 'pa mwedzi gwa kumi na moja', 'pa mwedzi gwa kumi na mbili'], + 'months_short' => ['Hut', 'Vil', 'Dat', 'Tai', 'Han', 'Sit', 'Sab', 'Nan', 'Tis', 'Kum', 'Kmj', 'Kmb'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bg.php b/vendor/nesbot/carbon/src/Carbon/Lang/bg.php new file mode 100644 index 0000000..f768074 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bg.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - François B + * - Serhan Apaydın + * - JD Isaacks + * - Glavić + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count година|:count години', + 'a_year' => 'година|:count години', + 'y' => ':count година|:count години', + 'month' => ':count месец|:count месеца', + 'a_month' => 'месец|:count месеца', + 'm' => ':count месец|:count месеца', + 'week' => ':count седмица|:count седмици', + 'a_week' => 'седмица|:count седмици', + 'w' => ':count седмица|:count седмици', + 'day' => ':count ден|:count дни', + 'a_day' => 'ден|:count дни', + 'd' => ':count ден|:count дни', + 'hour' => ':count час|:count часа', + 'a_hour' => 'час|:count часа', + 'h' => ':count час|:count часа', + 'minute' => ':count минута|:count минути', + 'a_minute' => 'минута|:count минути', + 'min' => ':count минута|:count минути', + 'second' => ':count секунда|:count секунди', + 'a_second' => 'няколко секунди|:count секунди', + 's' => ':count секунда|:count секунди', + 'ago' => 'преди :time', + 'from_now' => 'след :time', + 'after' => 'след :time', + 'before' => 'преди :time', + 'diff_now' => 'сега', + 'diff_today' => 'Днес', + 'diff_today_regexp' => 'Днес(?:\\s+в)?', + 'diff_yesterday' => 'вчера', + 'diff_yesterday_regexp' => 'Вчера(?:\\s+в)?', + 'diff_tomorrow' => 'утре', + 'diff_tomorrow_regexp' => 'Утре(?:\\s+в)?', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'D.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY H:mm', + 'LLLL' => 'dddd, D MMMM YYYY H:mm', + ], + 'calendar' => [ + 'sameDay' => '[Днес в] LT', + 'nextDay' => '[Утре в] LT', + 'nextWeek' => 'dddd [в] LT', + 'lastDay' => '[Вчера в] LT', + 'lastWeek' => function (CarbonInterface $current) { + switch ($current->dayOfWeek) { + case 0: + case 3: + case 6: + return '[В изминалата] dddd [в] LT'; + default: + return '[В изминалия] dddd [в] LT'; + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + $lastDigit = $number % 10; + $last2Digits = $number % 100; + if ($number === 0) { + return "$number-ев"; + } + if ($last2Digits === 0) { + return "$number-ен"; + } + if ($last2Digits > 10 && $last2Digits < 20) { + return "$number-ти"; + } + if ($lastDigit === 1) { + return "$number-ви"; + } + if ($lastDigit === 2) { + return "$number-ри"; + } + if ($lastDigit === 7 || $lastDigit === 8) { + return "$number-ми"; + } + + return "$number-ти"; + }, + 'months' => ['януари', 'февруари', 'март', 'април', 'май', 'юни', 'юли', 'август', 'септември', 'октомври', 'ноември', 'декември'], + 'months_short' => ['яну', 'фев', 'мар', 'апр', 'май', 'юни', 'юли', 'авг', 'сеп', 'окт', 'ное', 'дек'], + 'weekdays' => ['неделя', 'понеделник', 'вторник', 'сряда', 'четвъртък', 'петък', 'събота'], + 'weekdays_short' => ['нед', 'пон', 'вто', 'сря', 'чет', 'пет', 'съб'], + 'weekdays_min' => ['нд', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' и '], + 'meridiem' => ['преди обяд', 'следобед'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bg_BG.php b/vendor/nesbot/carbon/src/Carbon/Lang/bg_BG.php new file mode 100644 index 0000000..b53874d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bg_BG.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/bg.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bhb.php b/vendor/nesbot/carbon/src/Carbon/Lang/bhb.php new file mode 100644 index 0000000..49f0803 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bhb.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/bhb_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bhb_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/bhb_IN.php new file mode 100644 index 0000000..ab557cb --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bhb_IN.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Samsung Electronics Co., Ltd. alexey.merzlyakov@samsung.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + 'weekdays' => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + 'weekdays_short' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + 'weekdays_min' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bho.php b/vendor/nesbot/carbon/src/Carbon/Lang/bho.php new file mode 100644 index 0000000..e9ed0b6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bho.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/bho_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bho_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/bho_IN.php new file mode 100644 index 0000000..bc54f36 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bho_IN.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bhashaghar@googlegroups.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रैल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर"'], + 'months_short' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रैल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर"'], + 'weekdays' => ['रविवार', 'सोमवार', 'मंगलवार', 'बुधवार', 'गुरुवार', 'शुक्रवार', 'शनिवार'], + 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'गुरु', 'शुक्र', 'शनि'], + 'weekdays_min' => ['रवि', 'सोम', 'मंगल', 'बुध', 'गुरु', 'शुक्र', 'शनि'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], + + 'hour' => ':count मौसम', + 'h' => ':count मौसम', + 'a_hour' => ':count मौसम', + + 'minute' => ':count कला', + 'min' => ':count कला', + 'a_minute' => ':count कला', + + 'second' => ':count सोमार', + 's' => ':count सोमार', + 'a_second' => ':count सोमार', + + 'year' => ':count साल', + 'y' => ':count साल', + 'a_year' => ':count साल', + + 'month' => ':count महिना', + 'm' => ':count महिना', + 'a_month' => ':count महिना', + + 'week' => ':count सप्ताह', + 'w' => ':count सप्ताह', + 'a_week' => ':count सप्ताह', + + 'day' => ':count दिन', + 'd' => ':count दिन', + 'a_day' => ':count दिन', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bi.php b/vendor/nesbot/carbon/src/Carbon/Lang/bi.php new file mode 100644 index 0000000..dd08128 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bi.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/bi_VU.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bi_VU.php b/vendor/nesbot/carbon/src/Carbon/Lang/bi_VU.php new file mode 100644 index 0000000..1fe7770 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bi_VU.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com & maninder1.s@samsung.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'dddd DD MMM YYYY', + ], + 'months' => ['jenuware', 'febwari', 'maj', 'epril', 'mei', 'jun', 'julae', 'ogis', 'septemba', 'oktoba', 'novemba', 'disemba'], + 'months_short' => ['jen', 'feb', 'maj', 'epr', 'mei', 'jun', 'jul', 'ogi', 'sep', 'okt', 'nov', 'dis'], + 'weekdays' => ['sande', 'mande', 'maj', 'wota', 'fraede', 'sarede'], + 'weekdays_short' => ['san', 'man', 'maj', 'wot', 'fra', 'sar'], + 'weekdays_min' => ['san', 'man', 'maj', 'wot', 'fra', 'sar'], + + 'year' => ':count seven', // less reliable + 'y' => ':count seven', // less reliable + 'a_year' => ':count seven', // less reliable + + 'month' => ':count mi', // less reliable + 'm' => ':count mi', // less reliable + 'a_month' => ':count mi', // less reliable + + 'week' => ':count sarede', // less reliable + 'w' => ':count sarede', // less reliable + 'a_week' => ':count sarede', // less reliable + + 'day' => ':count betde', // less reliable + 'd' => ':count betde', // less reliable + 'a_day' => ':count betde', // less reliable + + 'hour' => ':count klok', // less reliable + 'h' => ':count klok', // less reliable + 'a_hour' => ':count klok', // less reliable + + 'minute' => ':count smol', // less reliable + 'min' => ':count smol', // less reliable + 'a_minute' => ':count smol', // less reliable + + 'second' => ':count tu', // less reliable + 's' => ':count tu', // less reliable + 'a_second' => ':count tu', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bm.php b/vendor/nesbot/carbon/src/Carbon/Lang/bm.php new file mode 100644 index 0000000..92822d2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bm.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Estelle Comment + */ +return [ + 'year' => 'san :count', + 'a_year' => '{1}san kelen|san :count', + 'y' => 'san :count', + 'month' => 'kalo :count', + 'a_month' => '{1}kalo kelen|kalo :count', + 'm' => 'k. :count', + 'week' => 'dɔgɔkun :count', + 'a_week' => 'dɔgɔkun kelen', + 'w' => 'd. :count', + 'day' => 'tile :count', + 'd' => 't. :count', + 'a_day' => '{1}tile kelen|tile :count', + 'hour' => 'lɛrɛ :count', + 'a_hour' => '{1}lɛrɛ kelen|lɛrɛ :count', + 'h' => 'l. :count', + 'minute' => 'miniti :count', + 'a_minute' => '{1}miniti kelen|miniti :count', + 'min' => 'm. :count', + 'second' => 'sekondi :count', + 'a_second' => '{1}sanga dama dama|sekondi :count', + 's' => 'sek. :count', + 'ago' => 'a bɛ :time bɔ', + 'from_now' => ':time kɔnɔ', + 'diff_today' => 'Bi', + 'diff_yesterday' => 'Kunu', + 'diff_yesterday_regexp' => 'Kunu(?:\\s+lɛrɛ)?', + 'diff_tomorrow' => 'Sini', + 'diff_tomorrow_regexp' => 'Sini(?:\\s+lɛrɛ)?', + 'diff_today_regexp' => 'Bi(?:\\s+lɛrɛ)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'MMMM [tile] D [san] YYYY', + 'LLL' => 'MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm', + 'LLLL' => 'dddd MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Bi lɛrɛ] LT', + 'nextDay' => '[Sini lɛrɛ] LT', + 'nextWeek' => 'dddd [don lɛrɛ] LT', + 'lastDay' => '[Kunu lɛrɛ] LT', + 'lastWeek' => 'dddd [tɛmɛnen lɛrɛ] LT', + 'sameElse' => 'L', + ], + 'months' => ['Zanwuyekalo', 'Fewuruyekalo', 'Marisikalo', 'Awirilikalo', 'Mɛkalo', 'Zuwɛnkalo', 'Zuluyekalo', 'Utikalo', 'Sɛtanburukalo', 'ɔkutɔburukalo', 'Nowanburukalo', 'Desanburukalo'], + 'months_short' => ['Zan', 'Few', 'Mar', 'Awi', 'Mɛ', 'Zuw', 'Zul', 'Uti', 'Sɛt', 'ɔku', 'Now', 'Des'], + 'weekdays' => ['Kari', 'Ntɛnɛn', 'Tarata', 'Araba', 'Alamisa', 'Juma', 'Sibiri'], + 'weekdays_short' => ['Kar', 'Ntɛ', 'Tar', 'Ara', 'Ala', 'Jum', 'Sib'], + 'weekdays_min' => ['Ka', 'Nt', 'Ta', 'Ar', 'Al', 'Ju', 'Si'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' ni '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bn.php b/vendor/nesbot/carbon/src/Carbon/Lang/bn.php new file mode 100644 index 0000000..8e14789 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bn.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - Shakib Hossain + * - Raju + * - Aniruddha Adhikary + * - JD Isaacks + * - Saiful Islam + * - Faisal Islam + */ +return [ + 'year' => ':count বছর', + 'a_year' => 'এক বছর|:count বছর', + 'y' => '১ বছর|:count বছর', + 'month' => ':count মাস', + 'a_month' => 'এক মাস|:count মাস', + 'm' => '১ মাস|:count মাস', + 'week' => ':count সপ্তাহ', + 'a_week' => '১ সপ্তাহ|:count সপ্তাহ', + 'w' => '১ সপ্তাহ|:count সপ্তাহ', + 'day' => ':count দিন', + 'a_day' => 'এক দিন|:count দিন', + 'd' => '১ দিন|:count দিন', + 'hour' => ':count ঘন্টা', + 'a_hour' => 'এক ঘন্টা|:count ঘন্টা', + 'h' => '১ ঘন্টা|:count ঘন্টা', + 'minute' => ':count মিনিট', + 'a_minute' => 'এক মিনিট|:count মিনিট', + 'min' => '১ মিনিট|:count মিনিট', + 'second' => ':count সেকেন্ড', + 'a_second' => 'কয়েক সেকেন্ড|:count সেকেন্ড', + 's' => '১ সেকেন্ড|:count সেকেন্ড', + 'ago' => ':time আগে', + 'from_now' => ':time পরে', + 'after' => ':time পরে', + 'before' => ':time আগে', + 'diff_now' => 'এখন', + 'diff_today' => 'আজ', + 'diff_yesterday' => 'গতকাল', + 'diff_tomorrow' => 'আগামীকাল', + 'period_recurrences' => ':count বার|:count বার', + 'period_interval' => 'প্রতি :interval', + 'period_start_date' => ':date থেকে', + 'period_end_date' => ':date পর্যন্ত', + 'formats' => [ + 'LT' => 'A Oh:Om সময়', + 'LTS' => 'A Oh:Om:Os সময়', + 'L' => 'OD/OM/OY', + 'LL' => 'OD MMMM OY', + 'LLL' => 'OD MMMM OY, A Oh:Om সময়', + 'LLLL' => 'dddd, OD MMMM OY, A Oh:Om সময়', + ], + 'calendar' => [ + 'sameDay' => '[আজ] LT', + 'nextDay' => '[আগামীকাল] LT', + 'nextWeek' => 'dddd, LT', + 'lastDay' => '[গতকাল] LT', + 'lastWeek' => '[গত] dddd, LT', + 'sameElse' => 'L', + ], + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'রাত'; + } + if ($hour < 10) { + return 'সকাল'; + } + if ($hour < 17) { + return 'দুপুর'; + } + if ($hour < 20) { + return 'বিকাল'; + } + + return 'রাত'; + }, + 'months' => ['জানুয়ারী', 'ফেব্রুয়ারি', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগস্ট', 'সেপ্টেম্বর', 'অক্টোবর', 'নভেম্বর', 'ডিসেম্বর'], + 'months_short' => ['জানু', 'ফেব', 'মার্চ', 'এপ্র', 'মে', 'জুন', 'জুল', 'আগ', 'সেপ্ট', 'অক্টো', 'নভে', 'ডিসে'], + 'weekdays' => ['রবিবার', 'সোমবার', 'মঙ্গলবার', 'বুধবার', 'বৃহস্পতিবার', 'শুক্রবার', 'শনিবার'], + 'weekdays_short' => ['রবি', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহস্পতি', 'শুক্র', 'শনি'], + 'weekdays_min' => ['রবি', 'সোম', 'মঙ্গ', 'বুধ', 'বৃহঃ', 'শুক্র', 'শনি'], + 'list' => [', ', ' এবং '], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, + 'weekdays_standalone' => ['রবিবার', 'সোমবার', 'মঙ্গলবার', 'বুধবার', 'বৃহষ্পতিবার', 'শুক্রবার', 'শনিবার'], + 'weekdays_min_standalone' => ['রঃ', 'সোঃ', 'মঃ', 'বুঃ', 'বৃঃ', 'শুঃ', 'শনি'], + 'months_short_standalone' => ['জানুয়ারী', 'ফেব্রুয়ারী', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগস্ট', 'সেপ্টেম্বর', 'অক্টোবর', 'নভেম্বর', 'ডিসেম্বর'], + 'alt_numbers' => ['০', '১', '২', '৩', '৪', '৫', '৬', '৭', '৮', '৯'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bn_BD.php b/vendor/nesbot/carbon/src/Carbon/Lang/bn_BD.php new file mode 100644 index 0000000..b5b28dd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bn_BD.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ankur Group, Taneem Ahmed, Jamil Ahmed + */ +return array_replace_recursive(require __DIR__.'/bn.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['জানুয়ারী', 'ফেব্রুয়ারী', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগস্ট', 'সেপ্টেম্বর', 'অক্টোবর', 'নভেম্বর', 'ডিসেম্বর'], + 'months_short' => ['জানু', 'ফেব', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগস্ট', 'সেপ্টেম্বর', 'অক্টোবর', 'নভেম্বর', 'ডিসেম্বর'], + 'weekdays' => ['রবিবার', 'সোমবার', 'মঙ্গলবার', 'বুধবার', 'বৃহস্পতিবার', 'শুক্রবার', 'শনিবার'], + 'weekdays_short' => ['রবি', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহঃ', 'শুক্র', 'শনি'], + 'weekdays_min' => ['রবি', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহঃ', 'শুক্র', 'শনি'], + 'first_day_of_week' => 5, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bn_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/bn_IN.php new file mode 100644 index 0000000..8b3a50e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bn_IN.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/bn.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['জানুয়ারী', 'ফেব্রুয়ারী', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগস্ট', 'সেপ্টেম্বর', 'অক্টোবর', 'নভেম্বর', 'ডিসেম্বর'], + 'months_short' => ['জানু', 'ফেব', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগস্ট', 'সেপ্টেম্বর', 'অক্টোবর', 'নভেম্বর', 'ডিসেম্বর'], + 'weekdays' => ['রবিবার', 'সোমবার', 'মঙ্গলবার', 'বুধবার', 'বৃহস্পতিবার', 'শুক্রবার', 'শনিবার'], + 'weekdays_short' => ['রবি', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহস্পতি', 'শুক্র', 'শনি'], + 'weekdays_min' => ['রবি', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহস্পতি', 'শুক্র', 'শনি'], + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bo.php b/vendor/nesbot/carbon/src/Carbon/Lang/bo.php new file mode 100644 index 0000000..99e1bf4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bo.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - JD Isaacks + */ +return [ + 'year' => '{1}ལོ་གཅིག|]1,Inf[:count ལོ', + 'month' => '{1}ཟླ་བ་གཅིག|]1,Inf[:count ཟླ་བ', + 'week' => ':count བདུན་ཕྲག', + 'day' => '{1}ཉིན་གཅིག|]1,Inf[:count ཉིན་', + 'hour' => '{1}ཆུ་ཚོད་གཅིག|]1,Inf[:count ཆུ་ཚོད', + 'minute' => '{1}སྐར་མ་གཅིག|]1,Inf[:count སྐར་མ', + 'second' => '{1}ལམ་སང|]1,Inf[:count སྐར་ཆ།', + 'ago' => ':time སྔན་ལ', + 'from_now' => ':time ལ་', + 'diff_yesterday' => 'ཁ་སང', + 'diff_today' => 'དི་རིང', + 'diff_tomorrow' => 'སང་ཉིན', + 'formats' => [ + 'LT' => 'A h:mm', + 'LTS' => 'A h:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY, A h:mm', + 'LLLL' => 'dddd, D MMMM YYYY, A h:mm', + ], + 'calendar' => [ + 'sameDay' => '[དི་རིང] LT', + 'nextDay' => '[སང་ཉིན] LT', + 'nextWeek' => '[བདུན་ཕྲག་རྗེས་མ], LT', + 'lastDay' => '[ཁ་སང] LT', + 'lastWeek' => '[བདུན་ཕྲག་མཐའ་མ] dddd, LT', + 'sameElse' => 'L', + ], + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'མཚན་མོ'; + } + if ($hour < 10) { + return 'ཞོགས་ཀས'; + } + if ($hour < 17) { + return 'ཉིན་གུང'; + } + if ($hour < 20) { + return 'དགོང་དག'; + } + + return 'མཚན་མོ'; + }, + 'months' => ['ཟླ་བ་དང་པོ', 'ཟླ་བ་གཉིས་པ', 'ཟླ་བ་གསུམ་པ', 'ཟླ་བ་བཞི་པ', 'ཟླ་བ་ལྔ་པ', 'ཟླ་བ་དྲུག་པ', 'ཟླ་བ་བདུན་པ', 'ཟླ་བ་བརྒྱད་པ', 'ཟླ་བ་དགུ་པ', 'ཟླ་བ་བཅུ་པ', 'ཟླ་བ་བཅུ་གཅིག་པ', 'ཟླ་བ་བཅུ་གཉིས་པ'], + 'months_short' => ['ཟླ་བ་དང་པོ', 'ཟླ་བ་གཉིས་པ', 'ཟླ་བ་གསུམ་པ', 'ཟླ་བ་བཞི་པ', 'ཟླ་བ་ལྔ་པ', 'ཟླ་བ་དྲུག་པ', 'ཟླ་བ་བདུན་པ', 'ཟླ་བ་བརྒྱད་པ', 'ཟླ་བ་དགུ་པ', 'ཟླ་བ་བཅུ་པ', 'ཟླ་བ་བཅུ་གཅིག་པ', 'ཟླ་བ་བཅུ་གཉིས་པ'], + 'weekdays' => ['གཟའ་ཉི་མ་', 'གཟའ་ཟླ་བ་', 'གཟའ་མིག་དམར་', 'གཟའ་ལྷག་པ་', 'གཟའ་ཕུར་བུ', 'གཟའ་པ་སངས་', 'གཟའ་སྤེན་པ་'], + 'weekdays_short' => ['ཉི་མ་', 'ཟླ་བ་', 'མིག་དམར་', 'ལྷག་པ་', 'ཕུར་བུ', 'པ་སངས་', 'སྤེན་པ་'], + 'weekdays_min' => ['ཉི་མ་', 'ཟླ་བ་', 'མིག་དམར་', 'ལྷག་པ་', 'ཕུར་བུ', 'པ་སངས་', 'སྤེན་པ་'], + 'list' => [', ', ' ཨནད་ '], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, + 'months_standalone' => ['ཟླ་བ་དང་པོ་', 'ཟླ་བ་གཉིས་པ་', 'ཟླ་བ་གསུམ་པ་', 'ཟླ་བ་བཞི་པ་', 'ཟླ་བ་ལྔ་པ་', 'ཟླ་བ་དྲུག་པ་', 'ཟླ་བ་བདུན་པ་', 'ཟླ་བ་བརྒྱད་པ་', 'ཟླ་བ་དགུ་པ་', 'ཟླ་བ་བཅུ་པ་', 'ཟླ་བ་བཅུ་གཅིག་པ་', 'ཟླ་བ་བཅུ་གཉིས་པ་'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bo_CN.php b/vendor/nesbot/carbon/src/Carbon/Lang/bo_CN.php new file mode 100644 index 0000000..380abb1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bo_CN.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/bo.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bo_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/bo_IN.php new file mode 100644 index 0000000..ca50d04 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bo_IN.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/bo.php', [ + 'meridiem' => ['སྔ་དྲོ་', 'ཕྱི་དྲོ་'], + 'weekdays' => ['གཟའ་ཉི་མ་', 'གཟའ་ཟླ་བ་', 'གཟའ་མིག་དམར་', 'གཟའ་ལྷག་པ་', 'གཟའ་ཕུར་བུ་', 'གཟའ་པ་སངས་', 'གཟའ་སྤེན་པ་'], + 'weekdays_short' => ['ཉི་མ་', 'ཟླ་བ་', 'མིག་དམར་', 'ལྷག་པ་', 'ཕུར་བུ་', 'པ་སངས་', 'སྤེན་པ་'], + 'weekdays_min' => ['ཉི་མ་', 'ཟླ་བ་', 'མིག་དམར་', 'ལྷག་པ་', 'ཕུར་བུ་', 'པ་སངས་', 'སྤེན་པ་'], + 'months' => ['ཟླ་བ་དང་པོ', 'ཟླ་བ་གཉིས་པ', 'ཟླ་བ་གསུམ་པ', 'ཟླ་བ་བཞི་པ', 'ཟླ་བ་ལྔ་པ', 'ཟླ་བ་དྲུག་པ', 'ཟླ་བ་བདུན་པ', 'ཟླ་བ་བརྒྱད་པ', 'ཟླ་བ་དགུ་པ', 'ཟླ་བ་བཅུ་པ', 'ཟླ་བ་བཅུ་གཅིག་པ', 'ཟླ་བ་བཅུ་གཉིས་པ'], + 'months_short' => ['ཟླ་༡', 'ཟླ་༢', 'ཟླ་༣', 'ཟླ་༤', 'ཟླ་༥', 'ཟླ་༦', 'ཟླ་༧', 'ཟླ་༨', 'ཟླ་༩', 'ཟླ་༡༠', 'ཟླ་༡༡', 'ཟླ་༡༢'], + 'months_standalone' => ['ཟླ་བ་དང་པོ་', 'ཟླ་བ་གཉིས་པ་', 'ཟླ་བ་གསུམ་པ་', 'ཟླ་བ་བཞི་པ་', 'ཟླ་བ་ལྔ་པ་', 'ཟླ་བ་དྲུག་པ་', 'ཟླ་བ་བདུན་པ་', 'ཟླ་བ་བརྒྱད་པ་', 'ཟླ་བ་དགུ་པ་', 'ཟླ་བ་བཅུ་པ་', 'ཟླ་བ་བཅུ་གཅིག་པ་', 'ཟླ་བ་བཅུ་གཉིས་པ་'], + 'weekend' => [0, 0], + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'YYYY-MM-DD', + 'LL' => 'YYYY ལོའི་MMMཚེས་D', + 'LLL' => 'སྤྱི་ལོ་YYYY MMMMའི་ཚེས་D h:mm a', + 'LLLL' => 'YYYY MMMMའི་ཚེས་D, dddd h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/br.php b/vendor/nesbot/carbon/src/Carbon/Lang/br.php new file mode 100644 index 0000000..583472f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/br.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Serhan Apaydın + * - JD Isaacks + */ +return [ + 'year' => '{1}:count bloaz|{3,4,5,9}:count bloaz|[0,Inf[:count vloaz', + 'a_year' => '{1}ur bloaz|{3,4,5,9}:count bloaz|[0,Inf[:count vloaz', + 'month' => '{1}:count miz|{2}:count viz|[0,Inf[:count miz', + 'a_month' => '{1}ur miz|{2}:count viz|[0,Inf[:count miz', + 'week' => ':count sizhun', + 'a_week' => '{1}ur sizhun|:count sizhun', + 'day' => '{1}:count devezh|{2}:count zevezh|[0,Inf[:count devezh', + 'a_day' => '{1}un devezh|{2}:count zevezh|[0,Inf[:count devezh', + 'hour' => ':count eur', + 'a_hour' => '{1}un eur|:count eur', + 'minute' => '{1}:count vunutenn|{2}:count vunutenn|[0,Inf[:count munutenn', + 'a_minute' => '{1}ur vunutenn|{2}:count vunutenn|[0,Inf[:count munutenn', + 'second' => ':count eilenn', + 'a_second' => '{1}un nebeud segondennoù|[0,Inf[:count eilenn', + 'ago' => ':time \'zo', + 'from_now' => 'a-benn :time', + 'diff_now' => 'bremañ', + 'diff_today' => 'Hiziv', + 'diff_today_regexp' => 'Hiziv(?:\\s+da)?', + 'diff_yesterday' => 'decʼh', + 'diff_yesterday_regexp' => 'Dec\'h(?:\\s+da)?', + 'diff_tomorrow' => 'warcʼhoazh', + 'diff_tomorrow_regexp' => 'Warc\'hoazh(?:\\s+da)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D [a viz] MMMM YYYY', + 'LLL' => 'D [a viz] MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D [a viz] MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Hiziv da] LT', + 'nextDay' => '[Warc\'hoazh da] LT', + 'nextWeek' => 'dddd [da] LT', + 'lastDay' => '[Dec\'h da] LT', + 'lastWeek' => 'dddd [paset da] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + return $number.($number === 1 ? 'añ' : 'vet'); + }, + 'months' => ['Genver', 'C\'hwevrer', 'Meurzh', 'Ebrel', 'Mae', 'Mezheven', 'Gouere', 'Eost', 'Gwengolo', 'Here', 'Du', 'Kerzu'], + 'months_short' => ['Gen', 'C\'hwe', 'Meu', 'Ebr', 'Mae', 'Eve', 'Gou', 'Eos', 'Gwe', 'Her', 'Du', 'Ker'], + 'weekdays' => ['Sul', 'Lun', 'Meurzh', 'Merc\'her', 'Yaou', 'Gwener', 'Sadorn'], + 'weekdays_short' => ['Sul', 'Lun', 'Meu', 'Mer', 'Yao', 'Gwe', 'Sad'], + 'weekdays_min' => ['Su', 'Lu', 'Me', 'Mer', 'Ya', 'Gw', 'Sa'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' hag '], + 'meridiem' => ['A.M.', 'G.M.'], + + 'y' => ':count bl.', + 'd' => ':count d', + 'h' => ':count e', + 'min' => ':count min', + 's' => ':count s', +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/br_FR.php b/vendor/nesbot/carbon/src/Carbon/Lang/br_FR.php new file mode 100644 index 0000000..7f54185 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/br_FR.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/br.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/brx.php b/vendor/nesbot/carbon/src/Carbon/Lang/brx.php new file mode 100644 index 0000000..a0a7bf9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/brx.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/brx_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/brx_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/brx_IN.php new file mode 100644 index 0000000..2d80ced --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/brx_IN.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Red Hat Pune bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'M/D/YY', + ], + 'months' => ['जानुवारी', 'फेब्रुवारी', 'मार्स', 'एफ्रिल', 'मे', 'जुन', 'जुलाइ', 'आगस्थ', 'सेबथेज्ब़र', 'अखथबर', 'नबेज्ब़र', 'दिसेज्ब़र'], + 'months_short' => ['जानुवारी', 'फेब्रुवारी', 'मार्स', 'एप्रिल', 'मे', 'जुन', 'जुलाइ', 'आगस्थ', 'सेबथेज्ब़र', 'अखथबर', 'नबेज्ब़र', 'दिसेज्ब़र'], + 'weekdays' => ['रबिबार', 'सोबार', 'मंगलबार', 'बुदबार', 'बिसथिबार', 'सुखुरबार', 'सुनिबार'], + 'weekdays_short' => ['रबि', 'सम', 'मंगल', 'बुद', 'बिसथि', 'सुखुर', 'सुनि'], + 'weekdays_min' => ['रबि', 'सम', 'मंगल', 'बुद', 'बिसथि', 'सुखुर', 'सुनि'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['फुं.', 'बेलासे.'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bs.php b/vendor/nesbot/carbon/src/Carbon/Lang/bs.php new file mode 100644 index 0000000..e5d6808 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bs.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bokideckonja + * - Josh Soref + * - François B + * - shaishavgandhi05 + * - Serhan Apaydın + * - JD Isaacks + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count godina|:count godine|:count godina', + 'y' => ':count godina|:count godine|:count godina', + 'month' => ':count mjesec|:count mjeseca|:count mjeseci', + 'm' => ':count mjesec|:count mjeseca|:count mjeseci', + 'week' => ':count sedmice|:count sedmicu|:count sedmica', + 'w' => ':count sedmice|:count sedmicu|:count sedmica', + 'day' => ':count dan|:count dana|:count dana', + 'd' => ':count dan|:count dana|:count dana', + 'hour' => ':count sat|:count sata|:count sati', + 'h' => ':count sat|:count sata|:count sati', + 'minute' => ':count minut|:count minuta|:count minuta', + 'min' => ':count minut|:count minuta|:count minuta', + 'second' => ':count sekund|:count sekunda|:count sekundi', + 's' => ':count sekund|:count sekunda|:count sekundi', + 'ago' => 'prije :time', + 'from_now' => 'za :time', + 'after' => 'nakon :time', + 'before' => ':time ranije', + 'diff_now' => 'sada', + 'diff_today' => 'danas', + 'diff_today_regexp' => 'danas(?:\\s+u)?', + 'diff_yesterday' => 'jučer', + 'diff_yesterday_regexp' => 'jučer(?:\\s+u)?', + 'diff_tomorrow' => 'sutra', + 'diff_tomorrow_regexp' => 'sutra(?:\\s+u)?', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY H:mm', + 'LLLL' => 'dddd, D. MMMM YYYY H:mm', + ], + 'calendar' => [ + 'sameDay' => '[danas u] LT', + 'nextDay' => '[sutra u] LT', + 'nextWeek' => function (CarbonInterface $current) { + switch ($current->dayOfWeek) { + case 0: + return '[u] [nedjelju] [u] LT'; + case 3: + return '[u] [srijedu] [u] LT'; + case 6: + return '[u] [subotu] [u] LT'; + default: + return '[u] dddd [u] LT'; + } + }, + 'lastDay' => '[jučer u] LT', + 'lastWeek' => function (CarbonInterface $current) { + switch ($current->dayOfWeek) { + case 0: + case 3: + return '[prošlu] dddd [u] LT'; + case 6: + return '[prošle] [subote] [u] LT'; + default: + return '[prošli] dddd [u] LT'; + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'months' => ['januar', 'februar', 'mart', 'april', 'maj', 'juni', 'juli', 'august', 'septembar', 'oktobar', 'novembar', 'decembar'], + 'months_short' => ['jan.', 'feb.', 'mar.', 'apr.', 'maj.', 'jun.', 'jul.', 'aug.', 'sep.', 'okt.', 'nov.', 'dec.'], + 'weekdays' => ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota'], + 'weekdays_short' => ['ned.', 'pon.', 'uto.', 'sri.', 'čet.', 'pet.', 'sub.'], + 'weekdays_min' => ['ne', 'po', 'ut', 'sr', 'če', 'pe', 'su'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' i '], + 'meridiem' => ['prijepodne', 'popodne'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php b/vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php new file mode 100644 index 0000000..0a59117 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/bs.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bs_Cyrl.php b/vendor/nesbot/carbon/src/Carbon/Lang/bs_Cyrl.php new file mode 100644 index 0000000..e1a1744 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bs_Cyrl.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/bs.php', [ + 'meridiem' => ['пре подне', 'поподне'], + 'weekdays' => ['недјеља', 'понедјељак', 'уторак', 'сриједа', 'четвртак', 'петак', 'субота'], + 'weekdays_short' => ['нед', 'пон', 'уто', 'сри', 'чет', 'пет', 'суб'], + 'weekdays_min' => ['нед', 'пон', 'уто', 'сри', 'чет', 'пет', 'суб'], + 'months' => ['јануар', 'фебруар', 'март', 'април', 'мај', 'јуни', 'јули', 'аугуст', 'септембар', 'октобар', 'новембар', 'децембар'], + 'months_short' => ['јан', 'феб', 'мар', 'апр', 'мај', 'јун', 'јул', 'ауг', 'сеп', 'окт', 'нов', 'дец'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D.M.YYYY.', + 'LL' => 'DD.MM.YYYY.', + 'LLL' => 'DD. MMMM YYYY. HH:mm', + 'LLLL' => 'dddd, DD. MMMM YYYY. HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bs_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/bs_Latn.php new file mode 100644 index 0000000..b4e363e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bs_Latn.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/bs.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/byn.php b/vendor/nesbot/carbon/src/Carbon/Lang/byn.php new file mode 100644 index 0000000..7125f3d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/byn.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/byn_ER.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/byn_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/byn_ER.php new file mode 100644 index 0000000..ad67533 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/byn_ER.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['ልደትሪ', 'ካብኽብቲ', 'ክብላ', 'ፋጅኺሪ', 'ክቢቅሪ', 'ምኪኤል ትጓ̅ኒሪ', 'ኰርኩ', 'ማርያም ትሪ', 'ያኸኒ መሳቅለሪ', 'መተሉ', 'ምኪኤል መሽወሪ', 'ተሕሳስሪ'], + 'months_short' => ['ልደት', 'ካብኽ', 'ክብላ', 'ፋጅኺ', 'ክቢቅ', 'ም/ት', 'ኰር', 'ማርያ', 'ያኸኒ', 'መተሉ', 'ም/ም', 'ተሕሳ'], + 'weekdays' => ['ሰንበር ቅዳዅ', 'ሰኑ', 'ሰሊጝ', 'ለጓ ወሪ ለብዋ', 'ኣምድ', 'ኣርብ', 'ሰንበር ሽጓዅ'], + 'weekdays_short' => ['ሰ/ቅ', 'ሰኑ', 'ሰሊጝ', 'ለጓ', 'ኣምድ', 'ኣርብ', 'ሰ/ሽ'], + 'weekdays_min' => ['ሰ/ቅ', 'ሰኑ', 'ሰሊጝ', 'ለጓ', 'ኣምድ', 'ኣርብ', 'ሰ/ሽ'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ፋዱስ ጃብ', 'ፋዱስ ደምቢ'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca.php new file mode 100644 index 0000000..b8b1994 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - mestremuten + * - François B + * - Marc Ordinas i Llopis + * - Pere Orga + * - JD Isaacks + * - Quentí + * - Víctor Díaz + * - Xavi + * - qcardona + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count any|:count anys', + 'a_year' => 'un any|:count anys', + 'y' => ':count any|:count anys', + 'month' => ':count mes|:count mesos', + 'a_month' => 'un mes|:count mesos', + 'm' => ':count mes|:count mesos', + 'week' => ':count setmana|:count setmanes', + 'a_week' => 'una setmana|:count setmanes', + 'w' => ':count setmana|:count setmanes', + 'day' => ':count dia|:count dies', + 'a_day' => 'un dia|:count dies', + 'd' => ':count d', + 'hour' => ':count hora|:count hores', + 'a_hour' => 'una hora|:count hores', + 'h' => ':count h', + 'minute' => ':count minut|:count minuts', + 'a_minute' => 'un minut|:count minuts', + 'min' => ':count min', + 'second' => ':count segon|:count segons', + 'a_second' => 'uns segons|:count segons', + 's' => ':count s', + 'ago' => 'fa :time', + 'from_now' => 'd\'aquí a :time', + 'after' => ':time després', + 'before' => ':time abans', + 'diff_now' => 'ara mateix', + 'diff_today' => 'avui', + 'diff_today_regexp' => 'avui(?:\\s+a)?(?:\\s+les)?', + 'diff_yesterday' => 'ahir', + 'diff_yesterday_regexp' => 'ahir(?:\\s+a)?(?:\\s+les)?', + 'diff_tomorrow' => 'demà', + 'diff_tomorrow_regexp' => 'demà(?:\\s+a)?(?:\\s+les)?', + 'diff_before_yesterday' => 'abans d\'ahir', + 'diff_after_tomorrow' => 'demà passat', + 'period_recurrences' => ':count cop|:count cops', + 'period_interval' => 'cada :interval', + 'period_start_date' => 'de :date', + 'period_end_date' => 'fins a :date', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM [de] YYYY', + 'LLL' => 'D MMMM [de] YYYY [a les] H:mm', + 'LLLL' => 'dddd D MMMM [de] YYYY [a les] H:mm', + ], + 'calendar' => [ + 'sameDay' => function (CarbonInterface $current) { + return '[avui a '.($current->hour !== 1 ? 'les' : 'la').'] LT'; + }, + 'nextDay' => function (CarbonInterface $current) { + return '[demà a '.($current->hour !== 1 ? 'les' : 'la').'] LT'; + }, + 'nextWeek' => function (CarbonInterface $current) { + return 'dddd [a '.($current->hour !== 1 ? 'les' : 'la').'] LT'; + }, + 'lastDay' => function (CarbonInterface $current) { + return '[ahir a '.($current->hour !== 1 ? 'les' : 'la').'] LT'; + }, + 'lastWeek' => function (CarbonInterface $current) { + return '[el] dddd [passat a '.($current->hour !== 1 ? 'les' : 'la').'] LT'; + }, + 'sameElse' => 'L', + ], + 'ordinal' => function ($number, $period) { + return $number.( + ($period === 'w' || $period === 'W') ? 'a' : ( + ($number === 1) ? 'r' : ( + ($number === 2) ? 'n' : ( + ($number === 3) ? 'r' : ( + ($number === 4) ? 't' : 'è' + ) + ) + ) + ) + ); + }, + 'months' => ['de gener', 'de febrer', 'de març', 'd\'abril', 'de maig', 'de juny', 'de juliol', 'd\'agost', 'de setembre', 'd\'octubre', 'de novembre', 'de desembre'], + 'months_standalone' => ['gener', 'febrer', 'març', 'abril', 'maig', 'juny', 'juliol', 'agost', 'setembre', 'octubre', 'novembre', 'desembre'], + 'months_short' => ['de gen.', 'de febr.', 'de març', 'd\'abr.', 'de maig', 'de juny', 'de jul.', 'd\'ag.', 'de set.', 'd\'oct.', 'de nov.', 'de des.'], + 'months_short_standalone' => ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny', 'jul.', 'ag.', 'set.', 'oct.', 'nov.', 'des.'], + 'months_regexp' => '/(D[oD]?[\s,]+MMMM?|L{2,4}|l{2,4})/', + 'weekdays' => ['diumenge', 'dilluns', 'dimarts', 'dimecres', 'dijous', 'divendres', 'dissabte'], + 'weekdays_short' => ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'], + 'weekdays_min' => ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' i '], + 'meridiem' => ['a. m.', 'p. m.'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca_AD.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca_AD.php new file mode 100644 index 0000000..861acd2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca_AD.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ca.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca_ES.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca_ES.php new file mode 100644 index 0000000..5004978 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca_ES.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ca.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca_ES_Valencia.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca_ES_Valencia.php new file mode 100644 index 0000000..1c16421 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca_ES_Valencia.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Component\Translation\PluralizationRules; + +// @codeCoverageIgnoreStart +if (class_exists(PluralizationRules::class)) { + PluralizationRules::set(static function ($number) { + return PluralizationRules::get($number, 'ca'); + }, 'ca_ES_Valencia'); +} +// @codeCoverageIgnoreEnd + +return array_replace_recursive(require __DIR__.'/ca.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca_FR.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca_FR.php new file mode 100644 index 0000000..861acd2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca_FR.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ca.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca_IT.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca_IT.php new file mode 100644 index 0000000..861acd2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca_IT.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ca.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ccp.php b/vendor/nesbot/carbon/src/Carbon/Lang/ccp.php new file mode 100644 index 0000000..99c1dca --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ccp.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'weekdays' => ['𑄢𑄧𑄝𑄨𑄝𑄢𑄴', '𑄥𑄧𑄟𑄴𑄝𑄢𑄴', '𑄟𑄧𑄁𑄉𑄧𑄣𑄴𑄝𑄢𑄴', '𑄝𑄪𑄖𑄴𑄝𑄢𑄴', '𑄝𑄳𑄢𑄨𑄥𑄪𑄛𑄴𑄝𑄢𑄴', '𑄥𑄪𑄇𑄴𑄇𑄮𑄢𑄴𑄝𑄢𑄴', '𑄥𑄧𑄚𑄨𑄝𑄢𑄴'], + 'weekdays_short' => ['𑄢𑄧𑄝𑄨', '𑄥𑄧𑄟𑄴', '𑄟𑄧𑄁𑄉𑄧𑄣𑄴', '𑄝𑄪𑄖𑄴', '𑄝𑄳𑄢𑄨𑄥𑄪𑄛𑄴', '𑄥𑄪𑄇𑄴𑄇𑄮𑄢𑄴', '𑄥𑄧𑄚𑄨'], + 'weekdays_min' => ['𑄢𑄧𑄝𑄨', '𑄥𑄧𑄟𑄴', '𑄟𑄧𑄁𑄉𑄧𑄣𑄴', '𑄝𑄪𑄖𑄴', '𑄝𑄳𑄢𑄨𑄥𑄪𑄛𑄴', '𑄥𑄪𑄇𑄴𑄇𑄮𑄢𑄴', '𑄥𑄧𑄚𑄨'], + 'months' => ['𑄎𑄚𑄪𑄠𑄢𑄨', '𑄜𑄬𑄛𑄴𑄝𑄳𑄢𑄪𑄠𑄢𑄨', '𑄟𑄢𑄴𑄌𑄧', '𑄃𑄬𑄛𑄳𑄢𑄨𑄣𑄴', '𑄟𑄬', '𑄎𑄪𑄚𑄴', '𑄎𑄪𑄣𑄭', '𑄃𑄉𑄧𑄌𑄴𑄑𑄴', '𑄥𑄬𑄛𑄴𑄑𑄬𑄟𑄴𑄝𑄧𑄢𑄴', '𑄃𑄧𑄇𑄴𑄑𑄬𑄝𑄧𑄢𑄴', '𑄚𑄧𑄞𑄬𑄟𑄴𑄝𑄧𑄢𑄴', '𑄓𑄨𑄥𑄬𑄟𑄴𑄝𑄧𑄢𑄴'], + 'months_short' => ['𑄎𑄚𑄪', '𑄜𑄬𑄛𑄴', '𑄟𑄢𑄴𑄌𑄧', '𑄃𑄬𑄛𑄳𑄢𑄨𑄣𑄴', '𑄟𑄬', '𑄎𑄪𑄚𑄴', '𑄎𑄪𑄣𑄭', '𑄃𑄉𑄧𑄌𑄴𑄑𑄴', '𑄥𑄬𑄛𑄴𑄑𑄬𑄟𑄴𑄝𑄧𑄢𑄴', '𑄃𑄧𑄇𑄴𑄑𑄮𑄝𑄧𑄢𑄴', '𑄚𑄧𑄞𑄬𑄟𑄴𑄝𑄧𑄢𑄴', '𑄓𑄨𑄥𑄬𑄟𑄴𑄝𑄢𑄴'], + 'months_short_standalone' => ['𑄎𑄚𑄪𑄠𑄢𑄨', '𑄜𑄬𑄛𑄴𑄝𑄳𑄢𑄪𑄠𑄢𑄨', '𑄟𑄢𑄴𑄌𑄧', '𑄃𑄬𑄛𑄳𑄢𑄨𑄣𑄴', '𑄟𑄬', '𑄎𑄪𑄚𑄴', '𑄎𑄪𑄣𑄭', '𑄃𑄉𑄧𑄌𑄴𑄑𑄴', '𑄥𑄬𑄛𑄴𑄑𑄬𑄟𑄴𑄝𑄧𑄢𑄴', '𑄃𑄧𑄇𑄴𑄑𑄮𑄝𑄧𑄢𑄴', '𑄚𑄧𑄞𑄬𑄟𑄴𑄝𑄧𑄢𑄴', '𑄓𑄨𑄥𑄬𑄟𑄴𑄝𑄧𑄢𑄴'], + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM, YYYY h:mm a', + 'LLLL' => 'dddd, D MMMM, YYYY h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ccp_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ccp_IN.php new file mode 100644 index 0000000..c1fa8af --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ccp_IN.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ccp.php', [ + 'weekend' => [0, 0], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ce.php b/vendor/nesbot/carbon/src/Carbon/Lang/ce.php new file mode 100644 index 0000000..f99f6ff --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ce.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/ce_RU.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ce_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/ce_RU.php new file mode 100644 index 0000000..f769856 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ce_RU.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - ANCHR + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'YYYY.DD.MM', + ], + 'months' => ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'], + 'months_short' => ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'], + 'weekdays' => ['КӀиранан де', 'Оршотан де', 'Шинарин де', 'Кхаарин де', 'Еарин де', 'ПӀераскан де', 'Шот де'], + 'weekdays_short' => ['КӀ', 'Ор', 'Ши', 'Кх', 'Еа', 'ПӀ', 'Шо'], + 'weekdays_min' => ['КӀ', 'Ор', 'Ши', 'Кх', 'Еа', 'ПӀ', 'Шо'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'year' => ':count шо', + 'y' => ':count шо', + 'a_year' => ':count шо', + + 'month' => ':count бутт', + 'm' => ':count бутт', + 'a_month' => ':count бутт', + + 'week' => ':count кӏира', + 'w' => ':count кӏира', + 'a_week' => ':count кӏира', + + 'day' => ':count де', + 'd' => ':count де', + 'a_day' => ':count де', + + 'hour' => ':count сахьт', + 'h' => ':count сахьт', + 'a_hour' => ':count сахьт', + + 'minute' => ':count минот', + 'min' => ':count минот', + 'a_minute' => ':count минот', + + 'second' => ':count секунд', + 's' => ':count секунд', + 'a_second' => ':count секунд', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cgg.php b/vendor/nesbot/carbon/src/Carbon/Lang/cgg.php new file mode 100644 index 0000000..09bcc1c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cgg.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'weekdays' => ['Sande', 'Orwokubanza', 'Orwakabiri', 'Orwakashatu', 'Orwakana', 'Orwakataano', 'Orwamukaaga'], + 'weekdays_short' => ['SAN', 'ORK', 'OKB', 'OKS', 'OKN', 'OKT', 'OMK'], + 'weekdays_min' => ['SAN', 'ORK', 'OKB', 'OKS', 'OKN', 'OKT', 'OMK'], + 'months' => ['Okwokubanza', 'Okwakabiri', 'Okwakashatu', 'Okwakana', 'Okwakataana', 'Okwamukaaga', 'Okwamushanju', 'Okwamunaana', 'Okwamwenda', 'Okwaikumi', 'Okwaikumi na kumwe', 'Okwaikumi na ibiri'], + 'months_short' => ['KBZ', 'KBR', 'KST', 'KKN', 'KTN', 'KMK', 'KMS', 'KMN', 'KMW', 'KKM', 'KNK', 'KNB'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + + 'day' => ':count ruhanga', // less reliable + 'd' => ':count ruhanga', // less reliable + 'a_day' => ':count ruhanga', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/chr.php b/vendor/nesbot/carbon/src/Carbon/Lang/chr.php new file mode 100644 index 0000000..e26190f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/chr.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/chr_US.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/chr_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/chr_US.php new file mode 100644 index 0000000..371353e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/chr_US.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Cherokee Nation Joseph Erb josepherb7@gmail.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'MM/DD/YYYY', + ], + 'months' => ['ᎤᏃᎸᏔᏅ', 'ᎧᎦᎵ', 'ᎠᏅᏱ', 'ᎧᏬᏂ', 'ᎠᏂᏍᎬᏘ', 'ᏕᎭᎷᏱ', 'ᎫᏰᏉᏂ', 'ᎦᎶᏂ', 'ᏚᎵᏍᏗ', 'ᏚᏂᏅᏗ', 'ᏅᏓᏕᏆ', 'ᎥᏍᎩᏱ'], + 'months_short' => ['ᎤᏃ', 'ᎧᎦ', 'ᎠᏅ', 'ᎧᏬ', 'ᎠᏂ', 'ᏕᎭ', 'ᎫᏰ', 'ᎦᎶ', 'ᏚᎵ', 'ᏚᏂ', 'ᏅᏓ', 'ᎥᏍ'], + 'weekdays' => ['ᎤᎾᏙᏓᏆᏍᎬ', 'ᎤᎾᏙᏓᏉᏅᎯ', 'ᏔᎵᏁᎢᎦ', 'ᏦᎢᏁᎢᎦ', 'ᏅᎩᏁᎢᎦ', 'ᏧᎾᎩᎶᏍᏗ', 'ᎤᎾᏙᏓᏈᏕᎾ'], + 'weekdays_short' => ['ᏆᏍᎬ', 'ᏉᏅᎯ', 'ᏔᎵᏁ', 'ᏦᎢᏁ', 'ᏅᎩᏁ', 'ᏧᎾᎩ', 'ᏈᏕᎾ'], + 'weekdays_min' => ['ᏆᏍᎬ', 'ᏉᏅᎯ', 'ᏔᎵᏁ', 'ᏦᎢᏁ', 'ᏅᎩᏁ', 'ᏧᎾᎩ', 'ᏈᏕᎾ'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ᏌᎾᎴ', 'ᏒᎯᏱᎢᏗᏢ', 'ꮜꮎꮄ', 'ꮢꭿᏹꭲꮧꮲ'], + + 'second' => ':count ᏐᎢ', // less reliable + 's' => ':count ᏐᎢ', // less reliable + 'a_second' => ':count ᏐᎢ', // less reliable + + 'year' => ':count ᏑᏕᏘᏴᏓ', + 'y' => ':count ᏑᏕᏘᏴᏓ', + 'a_year' => ':count ᏑᏕᏘᏴᏓ', + + 'month' => ':count ᏏᏅᏙ', + 'm' => ':count ᏏᏅᏙ', + 'a_month' => ':count ᏏᏅᏙ', + + 'week' => ':count ᏑᎾᏙᏓᏆᏍᏗ', + 'w' => ':count ᏑᎾᏙᏓᏆᏍᏗ', + 'a_week' => ':count ᏑᎾᏙᏓᏆᏍᏗ', + + 'day' => ':count ᎢᎦ', + 'd' => ':count ᎢᎦ', + 'a_day' => ':count ᎢᎦ', + + 'hour' => ':count ᏑᏟᎶᏛ', + 'h' => ':count ᏑᏟᎶᏛ', + 'a_hour' => ':count ᏑᏟᎶᏛ', + + 'minute' => ':count ᎢᏯᏔᏬᏍᏔᏅ', + 'min' => ':count ᎢᏯᏔᏬᏍᏔᏅ', + 'a_minute' => ':count ᎢᏯᏔᏬᏍᏔᏅ', + + 'ago' => ':time ᏥᎨᏒ', + 'from_now' => 'ᎾᎿ :time', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ckb.php b/vendor/nesbot/carbon/src/Carbon/Lang/ckb.php new file mode 100644 index 0000000..acf4dc2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ckb.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Swara Mohammed + */ +$months = [ + 'ڕێبەندان', + 'ڕەشەمە', + 'نەورۆز', + 'گوڵان', + 'جۆزەردان', + 'پوشپەڕ', + 'گەلاوێژ', + 'خەرمانان', + 'ڕەزبەر', + 'گەڵاڕێزان', + 'سەرماوەرز', + 'بەفرانبار', +]; + +return [ + 'year' => implode('|', ['{0}:count ساڵێک', '{1}ساڵێک', '{2}دوو ساڵ', ']2,11[:count ساڵ', ']10,Inf[:count ساڵ']), + 'a_year' => implode('|', ['{0}:count ساڵێک', '{1}ساڵێک', '{2}دوو ساڵ', ']2,11[:count ساڵ', ']10,Inf[:count ساڵ']), + 'month' => implode('|', ['{0}:count مانگێک', '{1}مانگێک', '{2}دوو مانگ', ']2,11[:count مانگ', ']10,Inf[:count مانگ']), + 'a_month' => implode('|', ['{0}:count مانگێک', '{1}مانگێک', '{2}دوو مانگ', ']2,11[:count مانگ', ']10,Inf[:count مانگ']), + 'week' => implode('|', ['{0}:count هەفتەیەک', '{1}هەفتەیەک', '{2}دوو هەفتە', ']2,11[:count هەفتە', ']10,Inf[:count هەفتە']), + 'a_week' => implode('|', ['{0}:count هەفتەیەک', '{1}هەفتەیەک', '{2}دوو هەفتە', ']2,11[:count هەفتە', ']10,Inf[:count هەفتە']), + 'day' => implode('|', ['{0}:count ڕۆژێک', '{1}ڕۆژێک', '{2}دوو ڕۆژ', ']2,11[:count ڕۆژ', ']10,Inf[:count ڕۆژ']), + 'a_day' => implode('|', ['{0}:count ڕۆژێک', '{1}ڕۆژێک', '{2}دوو ڕۆژ', ']2,11[:count ڕۆژ', ']10,Inf[:count ڕۆژ']), + 'hour' => implode('|', ['{0}:count کاتژمێرێک', '{1}کاتژمێرێک', '{2}دوو کاتژمێر', ']2,11[:count کاتژمێر', ']10,Inf[:count کاتژمێر']), + 'a_hour' => implode('|', ['{0}:count کاتژمێرێک', '{1}کاتژمێرێک', '{2}دوو کاتژمێر', ']2,11[:count کاتژمێر', ']10,Inf[:count کاتژمێر']), + 'minute' => implode('|', ['{0}:count خولەکێک', '{1}خولەکێک', '{2}دوو خولەک', ']2,11[:count خولەک', ']10,Inf[:count خولەک']), + 'a_minute' => implode('|', ['{0}:count خولەکێک', '{1}خولەکێک', '{2}دوو خولەک', ']2,11[:count خولەک', ']10,Inf[:count خولەک']), + 'second' => implode('|', ['{0}:count چرکەیەک', '{1}چرکەیەک', '{2}دوو چرکە', ']2,11[:count چرکە', ']10,Inf[:count چرکە']), + 'a_second' => implode('|', ['{0}:count چرکەیەک', '{1}چرکەیەک', '{2}دوو چرکە', ']2,11[:count چرکە', ']10,Inf[:count چرکە']), + 'ago' => 'پێش :time', + 'from_now' => ':time لە ئێستاوە', + 'after' => 'دوای :time', + 'before' => 'پێش :time', + 'diff_now' => 'ئێستا', + 'diff_today' => 'ئەمڕۆ', + 'diff_today_regexp' => 'ڕۆژ(?:\\s+لە)?(?:\\s+کاتژمێر)?', + 'diff_yesterday' => 'دوێنێ', + 'diff_yesterday_regexp' => 'دوێنێ(?:\\s+لە)?(?:\\s+کاتژمێر)?', + 'diff_tomorrow' => 'سبەینێ', + 'diff_tomorrow_regexp' => 'سبەینێ(?:\\s+لە)?(?:\\s+کاتژمێر)?', + 'diff_before_yesterday' => 'پێش دوێنێ', + 'diff_after_tomorrow' => 'دوای سبەینێ', + 'period_recurrences' => implode('|', ['{0}جار', '{1}جار', '{2}:count دووجار', ']2,11[:count جار', ']10,Inf[:count جار']), + 'period_interval' => 'هەموو :interval', + 'period_start_date' => 'لە :date', + 'period_end_date' => 'بۆ :date', + 'months' => $months, + 'months_short' => $months, + 'weekdays' => ['یەکشەممە', 'دووشەممە', 'سێشەممە', 'چوارشەممە', 'پێنجشەممە', 'هەینی', 'شەممە'], + 'weekdays_short' => ['یەکشەممە', 'دووشەممە', 'سێشەممە', 'چوارشەممە', 'پێنجشەممە', 'هەینی', 'شەممە'], + 'weekdays_min' => ['یەکشەممە', 'دووشەممە', 'سێشەممە', 'چوارشەممە', 'پێنجشەممە', 'هەینی', 'شەممە'], + 'list' => ['، ', ' و '], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[ئەمڕۆ لە کاتژمێر] LT', + 'nextDay' => '[سبەینێ لە کاتژمێر] LT', + 'nextWeek' => 'dddd [لە کاتژمێر] LT', + 'lastDay' => '[دوێنێ لە کاتژمێر] LT', + 'lastWeek' => 'dddd [لە کاتژمێر] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['پ.ن', 'د.ن'], + 'weekend' => [5, 6], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cmn.php b/vendor/nesbot/carbon/src/Carbon/Lang/cmn.php new file mode 100644 index 0000000..80b1d69 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cmn.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/cmn_TW.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cmn_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/cmn_TW.php new file mode 100644 index 0000000..7e43f9d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cmn_TW.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'YYYY年MM月DD號', + ], + 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + 'months_short' => [' 1月', ' 2月', ' 3月', ' 4月', ' 5月', ' 6月', ' 7月', ' 8月', ' 9月', '10月', '11月', '12月'], + 'weekdays' => ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], + 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'], + 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'], + 'meridiem' => ['上午', '下午'], + + 'year' => ':count 年', + 'y' => ':count 年', + 'a_year' => ':count 年', + + 'month' => ':count 月', + 'm' => ':count 月', + 'a_month' => ':count 月', + + 'week' => ':count 周', + 'w' => ':count 周', + 'a_week' => ':count 周', + + 'day' => ':count 白天', + 'd' => ':count 白天', + 'a_day' => ':count 白天', + + 'hour' => ':count 小时', + 'h' => ':count 小时', + 'a_hour' => ':count 小时', + + 'minute' => ':count 分钟', + 'min' => ':count 分钟', + 'a_minute' => ':count 分钟', + + 'second' => ':count 秒', + 's' => ':count 秒', + 'a_second' => ':count 秒', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/crh.php b/vendor/nesbot/carbon/src/Carbon/Lang/crh.php new file mode 100644 index 0000000..a1d7ce6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/crh.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/crh_UA.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/crh_UA.php b/vendor/nesbot/carbon/src/Carbon/Lang/crh_UA.php new file mode 100644 index 0000000..0513933 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/crh_UA.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Reşat SABIQ tilde.birlik@gmail.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'Mayıs', 'İyun', 'İyul', 'Avgust', 'Sentâbr', 'Oktâbr', 'Noyabr', 'Dekabr'], + 'months_short' => ['Yan', 'Fev', 'Mar', 'Apr', 'May', 'İyn', 'İyl', 'Avg', 'Sen', 'Okt', 'Noy', 'Dek'], + 'weekdays' => ['Bazar', 'Bazarertesi', 'Salı', 'Çarşembe', 'Cumaaqşamı', 'Cuma', 'Cumaertesi'], + 'weekdays_short' => ['Baz', 'Ber', 'Sal', 'Çar', 'Caq', 'Cum', 'Cer'], + 'weekdays_min' => ['Baz', 'Ber', 'Sal', 'Çar', 'Caq', 'Cum', 'Cer'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ÜE', 'ÜS'], + + 'year' => ':count yıl', + 'y' => ':count yıl', + 'a_year' => ':count yıl', + + 'month' => ':count ay', + 'm' => ':count ay', + 'a_month' => ':count ay', + + 'week' => ':count afta', + 'w' => ':count afta', + 'a_week' => ':count afta', + + 'day' => ':count kün', + 'd' => ':count kün', + 'a_day' => ':count kün', + + 'hour' => ':count saat', + 'h' => ':count saat', + 'a_hour' => ':count saat', + + 'minute' => ':count daqqa', + 'min' => ':count daqqa', + 'a_minute' => ':count daqqa', + + 'second' => ':count ekinci', + 's' => ':count ekinci', + 'a_second' => ':count ekinci', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cs.php b/vendor/nesbot/carbon/src/Carbon/Lang/cs.php new file mode 100644 index 0000000..c01e3cc --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cs.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Philippe Vaucher + * - Jakub Tesinsky + * - Martin Suja + * - Nikos Timiopulos + * - Bohuslav Blín + * - Tsutomu Kuroda + * - tjku + * - Lukas Svoboda + * - Max Melentiev + * - Juanito Fatas + * - Akira Matsuda + * - Christopher Dell + * - Václav Pávek + * - CodeSkills + * - Tlapi + * - newman101 + * - Petr Kadlec + * - tommaskraus + * - Karel Sommer (calvera) + */ +$za = function ($time) { + return 'za '.strtr($time, [ + 'hodina' => 'hodinu', + 'minuta' => 'minutu', + 'sekunda' => 'sekundu', + ]); +}; + +$pred = function ($time) { + $time = strtr($time, [ + 'hodina' => 'hodinou', + 'minuta' => 'minutou', + 'sekunda' => 'sekundou', + ]); + $time = preg_replace('/hodiny?(?!\w)/', 'hodinami', $time); + $time = preg_replace('/minuty?(?!\w)/', 'minutami', $time); + $time = preg_replace('/sekundy?(?!\w)/', 'sekundami', $time); + + return "před $time"; +}; + +return [ + 'year' => ':count rok|:count roky|:count let', + 'y' => ':count rok|:count roky|:count let', + 'a_year' => 'rok|:count roky|:count let', + 'month' => ':count měsíc|:count měsíce|:count měsíců', + 'm' => ':count měs.', + 'a_month' => 'měsíc|:count měsíce|:count měsíců', + 'week' => ':count týden|:count týdny|:count týdnů', + 'w' => ':count týd.', + 'a_week' => 'týden|:count týdny|:count týdnů', + 'day' => ':count den|:count dny|:count dní', + 'd' => ':count den|:count dny|:count dní', + 'a_day' => 'den|:count dny|:count dní', + 'hour' => ':count hodina|:count hodiny|:count hodin', + 'h' => ':count hod.', + 'a_hour' => 'hodina|:count hodiny|:count hodin', + 'minute' => ':count minuta|:count minuty|:count minut', + 'min' => ':count min.', + 'a_minute' => 'minuta|:count minuty|:count minut', + 'second' => ':count sekunda|:count sekundy|:count sekund', + 's' => ':count sek.', + 'a_second' => 'pár sekund|:count sekundy|:count sekund', + + 'month_ago' => ':count měsícem|:count měsíci|:count měsíci', + 'a_month_ago' => 'měsícem|:count měsíci|:count měsíci', + 'day_ago' => ':count dnem|:count dny|:count dny', + 'a_day_ago' => 'dnem|:count dny|:count dny', + 'week_ago' => ':count týdnem|:count týdny|:count týdny', + 'a_week_ago' => 'týdnem|:count týdny|:count týdny', + 'year_ago' => ':count rokem|:count roky|:count lety', + 'y_ago' => ':count rok.|:count rok.|:count let.', + 'a_year_ago' => 'rokem|:count roky|:count lety', + + 'month_before' => ':count měsícem|:count měsíci|:count měsíci', + 'a_month_before' => 'měsícem|:count měsíci|:count měsíci', + 'day_before' => ':count dnem|:count dny|:count dny', + 'a_day_before' => 'dnem|:count dny|:count dny', + 'week_before' => ':count týdnem|:count týdny|:count týdny', + 'a_week_before' => 'týdnem|:count týdny|:count týdny', + 'year_before' => ':count rokem|:count roky|:count lety', + 'y_before' => ':count rok.|:count rok.|:count let.', + 'a_year_before' => 'rokem|:count roky|:count lety', + + 'ago' => $pred, + 'from_now' => $za, + 'before' => $pred, + 'after' => $za, + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'months' => ['ledna', 'února', 'března', 'dubna', 'května', 'června', 'července', 'srpna', 'září', 'října', 'listopadu', 'prosince'], + 'months_standalone' => ['leden', 'únor', 'březen', 'duben', 'květen', 'červen', 'červenec', 'srpen', 'září', 'říjen', 'listopad', 'prosinec'], + 'months_short' => ['led', 'úno', 'bře', 'dub', 'kvě', 'čvn', 'čvc', 'srp', 'zář', 'říj', 'lis', 'pro'], + 'weekdays' => ['neděle', 'pondělí', 'úterý', 'středa', 'čtvrtek', 'pátek', 'sobota'], + 'weekdays_short' => ['ned', 'pon', 'úte', 'stř', 'čtv', 'pát', 'sob'], + 'weekdays_min' => ['ne', 'po', 'út', 'st', 'čt', 'pá', 'so'], + 'list' => [', ', ' a '], + 'diff_now' => 'nyní', + 'diff_yesterday' => 'včera', + 'diff_tomorrow' => 'zítra', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD. MM. YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY HH:mm', + 'LLLL' => 'dddd D. MMMM YYYY HH:mm', + ], + 'meridiem' => ['dopoledne', 'odpoledne'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cs_CZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/cs_CZ.php new file mode 100644 index 0000000..ea2517e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cs_CZ.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/cs.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/csb.php b/vendor/nesbot/carbon/src/Carbon/Lang/csb.php new file mode 100644 index 0000000..a35d281 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/csb.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/csb_PL.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/csb_PL.php b/vendor/nesbot/carbon/src/Carbon/Lang/csb_PL.php new file mode 100644 index 0000000..25e0ca8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/csb_PL.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - csb_PL locale Michal Ostrowski bug-glibc-locales@gnu.org + */ +return [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-DD', + 'LL' => 'MMMM DD, YYYY', + 'LLL' => 'DD MMM HH:mm', + 'LLLL' => 'MMMM DD, YYYY HH:mm', + ], + 'months' => ['stëcznika', 'gromicznika', 'strëmiannika', 'łżëkwiata', 'maja', 'czerwińca', 'lëpińca', 'zélnika', 'séwnika', 'rujana', 'lëstopadnika', 'gòdnika'], + 'months_short' => ['stë', 'gro', 'str', 'łżë', 'maj', 'cze', 'lëp', 'zél', 'séw', 'ruj', 'lës', 'gòd'], + 'weekdays' => ['niedzela', 'pòniedzôłk', 'wtórk', 'strzoda', 'czwiôrtk', 'piątk', 'sobòta'], + 'weekdays_short' => ['nie', 'pòn', 'wtó', 'str', 'czw', 'pią', 'sob'], + 'weekdays_min' => ['nie', 'pòn', 'wtó', 'str', 'czw', 'pią', 'sob'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' a téż '], + 'two_words_connector' => ' a téż ', + 'year' => ':count rok', + 'month' => ':count miesiąc', + 'week' => ':count tidzéń', + 'day' => ':count dzéń', + 'hour' => ':count gòdzëna', + 'minute' => ':count minuta', + 'second' => ':count sekunda', +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cu.php b/vendor/nesbot/carbon/src/Carbon/Lang/cu.php new file mode 100644 index 0000000..d6d1312 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cu.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'months' => ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10', 'M11', 'M12'], + 'months_short' => ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10', 'M11', 'M12'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-DD', + 'LL' => 'YYYY MMM D', + 'LLL' => 'YYYY MMMM D HH:mm', + 'LLLL' => 'YYYY MMMM D, dddd HH:mm', + ], + + 'year' => ':count лѣто', + 'y' => ':count лѣто', + 'a_year' => ':count лѣто', + + 'month' => ':count мѣсѧць', + 'm' => ':count мѣсѧць', + 'a_month' => ':count мѣсѧць', + + 'week' => ':count сєдмица', + 'w' => ':count сєдмица', + 'a_week' => ':count сєдмица', + + 'day' => ':count дьнь', + 'd' => ':count дьнь', + 'a_day' => ':count дьнь', + + 'hour' => ':count година', + 'h' => ':count година', + 'a_hour' => ':count година', + + 'minute' => ':count малъ', // less reliable + 'min' => ':count малъ', // less reliable + 'a_minute' => ':count малъ', // less reliable + + 'second' => ':count въторъ', + 's' => ':count въторъ', + 'a_second' => ':count въторъ', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cv.php b/vendor/nesbot/carbon/src/Carbon/Lang/cv.php new file mode 100644 index 0000000..8aeb73a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cv.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - François B + * - JD Isaacks + */ +return [ + 'year' => ':count ҫул', + 'a_year' => '{1}пӗр ҫул|:count ҫул', + 'month' => ':count уйӑх', + 'a_month' => '{1}пӗр уйӑх|:count уйӑх', + 'week' => ':count эрне', + 'a_week' => '{1}пӗр эрне|:count эрне', + 'day' => ':count кун', + 'a_day' => '{1}пӗр кун|:count кун', + 'hour' => ':count сехет', + 'a_hour' => '{1}пӗр сехет|:count сехет', + 'minute' => ':count минут', + 'a_minute' => '{1}пӗр минут|:count минут', + 'second' => ':count ҫеккунт', + 'a_second' => '{1}пӗр-ик ҫеккунт|:count ҫеккунт', + 'ago' => ':time каялла', + 'from_now' => function ($time) { + return $time.(preg_match('/сехет$/u', $time) ? 'рен' : (preg_match('/ҫул/u', $time) ? 'тан' : 'ран')); + }, + 'diff_yesterday' => 'Ӗнер', + 'diff_today' => 'Паян', + 'diff_tomorrow' => 'Ыран', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD-MM-YYYY', + 'LL' => 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]', + 'LLL' => 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm', + 'LLLL' => 'dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Паян] LT [сехетре]', + 'nextDay' => '[Ыран] LT [сехетре]', + 'nextWeek' => '[Ҫитес] dddd LT [сехетре]', + 'lastDay' => '[Ӗнер] LT [сехетре]', + 'lastWeek' => '[Иртнӗ] dddd LT [сехетре]', + 'sameElse' => 'L', + ], + 'ordinal' => ':number-мӗш', + 'months' => ['кӑрлач', 'нарӑс', 'пуш', 'ака', 'май', 'ҫӗртме', 'утӑ', 'ҫурла', 'авӑн', 'юпа', 'чӳк', 'раштав'], + 'months_short' => ['кӑр', 'нар', 'пуш', 'ака', 'май', 'ҫӗр', 'утӑ', 'ҫур', 'авн', 'юпа', 'чӳк', 'раш'], + 'weekdays' => ['вырсарникун', 'тунтикун', 'ытларикун', 'юнкун', 'кӗҫнерникун', 'эрнекун', 'шӑматкун'], + 'weekdays_short' => ['выр', 'тун', 'ытл', 'юн', 'кӗҫ', 'эрн', 'шӑм'], + 'weekdays_min' => ['вр', 'тн', 'ыт', 'юн', 'кҫ', 'эр', 'шм'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' тата '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cv_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/cv_RU.php new file mode 100644 index 0000000..197bd8d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cv_RU.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/cv.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cy.php b/vendor/nesbot/carbon/src/Carbon/Lang/cy.php new file mode 100644 index 0000000..119274f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cy.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - JD Isaacks + * - Daniel Monaghan + */ +return [ + 'year' => '{1}blwyddyn|]1,Inf[:count flynedd', + 'y' => ':countbl', + 'month' => '{1}mis|]1,Inf[:count mis', + 'm' => ':countmi', + 'week' => ':count wythnos', + 'w' => ':countw', + 'day' => '{1}diwrnod|]1,Inf[:count diwrnod', + 'd' => ':countd', + 'hour' => '{1}awr|]1,Inf[:count awr', + 'h' => ':counth', + 'minute' => '{1}munud|]1,Inf[:count munud', + 'min' => ':countm', + 'second' => '{1}ychydig eiliadau|]1,Inf[:count eiliad', + 's' => ':counts', + 'ago' => ':time yn ôl', + 'from_now' => 'mewn :time', + 'after' => ':time ar ôl', + 'before' => ':time o\'r blaen', + 'diff_now' => 'nawr', + 'diff_today' => 'Heddiw', + 'diff_today_regexp' => 'Heddiw(?:\\s+am)?', + 'diff_yesterday' => 'ddoe', + 'diff_yesterday_regexp' => 'Ddoe(?:\\s+am)?', + 'diff_tomorrow' => 'yfory', + 'diff_tomorrow_regexp' => 'Yfory(?:\\s+am)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Heddiw am] LT', + 'nextDay' => '[Yfory am] LT', + 'nextWeek' => 'dddd [am] LT', + 'lastDay' => '[Ddoe am] LT', + 'lastWeek' => 'dddd [diwethaf am] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + return $number.( + $number > 20 + ? (\in_array((int) $number, [40, 50, 60, 80, 100], true) ? 'fed' : 'ain') + : ([ + '', 'af', 'il', 'ydd', 'ydd', 'ed', 'ed', 'ed', 'fed', 'fed', 'fed', // 1af to 10fed + 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'fed', // 11eg to 20fed + ])[$number] ?? '' + ); + }, + 'months' => ['Ionawr', 'Chwefror', 'Mawrth', 'Ebrill', 'Mai', 'Mehefin', 'Gorffennaf', 'Awst', 'Medi', 'Hydref', 'Tachwedd', 'Rhagfyr'], + 'months_short' => ['Ion', 'Chwe', 'Maw', 'Ebr', 'Mai', 'Meh', 'Gor', 'Aws', 'Med', 'Hyd', 'Tach', 'Rhag'], + 'weekdays' => ['Dydd Sul', 'Dydd Llun', 'Dydd Mawrth', 'Dydd Mercher', 'Dydd Iau', 'Dydd Gwener', 'Dydd Sadwrn'], + 'weekdays_short' => ['Sul', 'Llun', 'Maw', 'Mer', 'Iau', 'Gwe', 'Sad'], + 'weekdays_min' => ['Su', 'Ll', 'Ma', 'Me', 'Ia', 'Gw', 'Sa'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' a '], + 'meridiem' => ['yb', 'yh'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cy_GB.php b/vendor/nesbot/carbon/src/Carbon/Lang/cy_GB.php new file mode 100644 index 0000000..2c8148d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cy_GB.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/cy.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/da.php b/vendor/nesbot/carbon/src/Carbon/Lang/da.php new file mode 100644 index 0000000..322f91d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/da.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Rune Mønnike + * - François B + * - codenhagen + * - JD Isaacks + * - Jens Herlevsen + * - Ulrik McArdle (mcardle) + * - Frederik Sauer (FrittenKeeZ) + * - Janus Bahs Jacquet (kokoshneta) + */ +return [ + 'year' => ':count år|:count år', + 'a_year' => 'et år|:count år', + 'y' => ':count år|:count år', + 'month' => ':count måned|:count måneder', + 'a_month' => 'en måned|:count måneder', + 'm' => ':count mdr.', + 'week' => ':count uge|:count uger', + 'a_week' => 'en uge|:count uger', + 'w' => ':count u.', + 'day' => ':count dag|:count dage', + 'a_day' => ':count dag|:count dage', + 'd' => ':count d.', + 'hour' => ':count time|:count timer', + 'a_hour' => 'en time|:count timer', + 'h' => ':count t.', + 'minute' => ':count minut|:count minutter', + 'a_minute' => 'et minut|:count minutter', + 'min' => ':count min.', + 'second' => ':count sekund|:count sekunder', + 'a_second' => 'få sekunder|:count sekunder', + 's' => ':count s.', + 'ago' => 'for :time siden', + 'from_now' => 'om :time', + 'after' => ':time efter', + 'before' => ':time før', + 'diff_now' => 'nu', + 'diff_today' => 'i dag', + 'diff_today_regexp' => 'i dag(?:\\s+kl.)?', + 'diff_yesterday' => 'i går', + 'diff_yesterday_regexp' => 'i går(?:\\s+kl.)?', + 'diff_tomorrow' => 'i morgen', + 'diff_tomorrow_regexp' => 'i morgen(?:\\s+kl.)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY HH:mm', + 'LLLL' => 'dddd [d.] D. MMMM YYYY [kl.] HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[i dag kl.] LT', + 'nextDay' => '[i morgen kl.] LT', + 'nextWeek' => 'på dddd [kl.] LT', + 'lastDay' => '[i går kl.] LT', + 'lastWeek' => '[i] dddd[s kl.] LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'months' => ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'december'], + 'months_short' => ['jan.', 'feb.', 'mar.', 'apr.', 'maj.', 'jun.', 'jul.', 'aug.', 'sep.', 'okt.', 'nov.', 'dec.'], + 'weekdays' => ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag'], + 'weekdays_short' => ['søn.', 'man.', 'tir.', 'ons.', 'tor.', 'fre.', 'lør.'], + 'weekdays_min' => ['sø', 'ma', 'ti', 'on', 'to', 'fr', 'lø'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' og '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/da_DK.php b/vendor/nesbot/carbon/src/Carbon/Lang/da_DK.php new file mode 100644 index 0000000..392c484 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/da_DK.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/da.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/da_GL.php b/vendor/nesbot/carbon/src/Carbon/Lang/da_GL.php new file mode 100644 index 0000000..ea5698b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/da_GL.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/da.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + 'LL' => 'D. MMM YYYY', + 'LLL' => 'D. MMMM YYYY HH.mm', + 'LLLL' => 'dddd [den] D. MMMM YYYY HH.mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dav.php b/vendor/nesbot/carbon/src/Carbon/Lang/dav.php new file mode 100644 index 0000000..e95ec4b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dav.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Luma lwa K', 'luma lwa p'], + 'weekdays' => ['Ituku ja jumwa', 'Kuramuka jimweri', 'Kuramuka kawi', 'Kuramuka kadadu', 'Kuramuka kana', 'Kuramuka kasanu', 'Kifula nguwo'], + 'weekdays_short' => ['Jum', 'Jim', 'Kaw', 'Kad', 'Kan', 'Kas', 'Ngu'], + 'weekdays_min' => ['Jum', 'Jim', 'Kaw', 'Kad', 'Kan', 'Kas', 'Ngu'], + 'months' => ['Mori ghwa imbiri', 'Mori ghwa kawi', 'Mori ghwa kadadu', 'Mori ghwa kana', 'Mori ghwa kasanu', 'Mori ghwa karandadu', 'Mori ghwa mfungade', 'Mori ghwa wunyanya', 'Mori ghwa ikenda', 'Mori ghwa ikumi', 'Mori ghwa ikumi na imweri', 'Mori ghwa ikumi na iwi'], + 'months_short' => ['Imb', 'Kaw', 'Kad', 'Kan', 'Kas', 'Kar', 'Mfu', 'Wun', 'Ike', 'Iku', 'Imw', 'Iwi'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de.php b/vendor/nesbot/carbon/src/Carbon/Lang/de.php new file mode 100644 index 0000000..3b70750 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/de.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Michael Hohl + * - sheriffmarley + * - dennisoderwald + * - Timo + * - Karag2006 + * - Pete Scopes (pdscopes) + */ +return [ + 'year' => ':count Jahr|:count Jahre', + 'a_year' => 'ein Jahr|:count Jahre', + 'y' => ':count J.', + 'month' => ':count Monat|:count Monate', + 'a_month' => 'ein Monat|:count Monate', + 'm' => ':count Mon.', + 'week' => ':count Woche|:count Wochen', + 'a_week' => 'eine Woche|:count Wochen', + 'w' => ':count Wo.', + 'day' => ':count Tag|:count Tage', + 'a_day' => 'ein Tag|:count Tage', + 'd' => ':count Tg.', + 'hour' => ':count Stunde|:count Stunden', + 'a_hour' => 'eine Stunde|:count Stunden', + 'h' => ':count Std.', + 'minute' => ':count Minute|:count Minuten', + 'a_minute' => 'eine Minute|:count Minuten', + 'min' => ':count Min.', + 'second' => ':count Sekunde|:count Sekunden', + 'a_second' => 'ein paar Sekunden|:count Sekunden', + 's' => ':count Sek.', + 'millisecond' => ':count Millisekunde|:count Millisekunden', + 'a_millisecond' => 'eine Millisekunde|:count Millisekunden', + 'ms' => ':countms', + 'microsecond' => ':count Mikrosekunde|:count Mikrosekunden', + 'a_microsecond' => 'eine Mikrosekunde|:count Mikrosekunden', + 'µs' => ':countµs', + 'ago' => 'vor :time', + 'from_now' => 'in :time', + 'after' => ':time später', + 'before' => ':time zuvor', + + 'year_from_now' => ':count Jahr|:count Jahren', + 'month_from_now' => ':count Monat|:count Monaten', + 'week_from_now' => ':count Woche|:count Wochen', + 'day_from_now' => ':count Tag|:count Tagen', + 'year_ago' => ':count Jahr|:count Jahren', + 'month_ago' => ':count Monat|:count Monaten', + 'week_ago' => ':count Woche|:count Wochen', + 'day_ago' => ':count Tag|:count Tagen', + 'a_year_from_now' => 'ein Jahr|:count Jahren', + 'a_month_from_now' => 'ein Monat|:count Monaten', + 'a_week_from_now' => 'eine Woche|:count Wochen', + 'a_day_from_now' => 'ein Tag|:count Tagen', + 'a_year_ago' => 'ein Jahr|:count Jahren', + 'a_month_ago' => 'ein Monat|:count Monaten', + 'a_week_ago' => 'eine Woche|:count Wochen', + 'a_day_ago' => 'ein Tag|:count Tagen', + + 'diff_now' => 'Gerade eben', + 'diff_today' => 'heute', + 'diff_today_regexp' => 'heute(?:\\s+um)?', + 'diff_yesterday' => 'Gestern', + 'diff_yesterday_regexp' => 'gestern(?:\\s+um)?', + 'diff_tomorrow' => 'Morgen', + 'diff_tomorrow_regexp' => 'morgen(?:\\s+um)?', + 'diff_before_yesterday' => 'Vorgestern', + 'diff_after_tomorrow' => 'Übermorgen', + + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D. MMMM YYYY HH:mm', + ], + + 'calendar' => [ + 'sameDay' => '[heute um] LT [Uhr]', + 'nextDay' => '[morgen um] LT [Uhr]', + 'nextWeek' => 'dddd [um] LT [Uhr]', + 'lastDay' => '[gestern um] LT [Uhr]', + 'lastWeek' => '[letzten] dddd [um] LT [Uhr]', + 'sameElse' => 'L', + ], + + 'months' => ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], + 'months_short' => ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'], + 'weekdays' => ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'], + 'weekdays_short' => ['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.'], + 'weekdays_min' => ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'], + 'ordinal' => ':number.', + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' und '], + 'ordinal_words' => [ + 'of' => 'im', + 'first' => 'erster', + 'second' => 'zweiter', + 'third' => 'dritter', + 'fourth' => 'vierten', + 'fifth' => 'fünfter', + 'last' => 'letzten', + ], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_AT.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_AT.php new file mode 100644 index 0000000..a2ea4c0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_AT.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - sheriffmarley + * - Timo + * - Michael Hohl + * - Namoshek + * - Bernhard Baumrock (BernhardBaumrock) + */ +return array_replace_recursive(require __DIR__.'/de.php', [ + 'months' => [ + 0 => 'Jänner', + ], + 'months_short' => [ + 0 => 'Jän', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_BE.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_BE.php new file mode 100644 index 0000000..8ed8dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_BE.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/de.php', [ + 'formats' => [ + 'L' => 'YYYY-MM-DD', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_CH.php new file mode 100644 index 0000000..a869ab4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_CH.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - sheriffmarley + * - Timo + * - Michael Hohl + */ +return array_replace_recursive(require __DIR__.'/de.php', [ + 'weekdays_short' => ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_DE.php new file mode 100644 index 0000000..fb1209d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_DE.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org + */ +return require __DIR__.'/de.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_IT.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_IT.php new file mode 100644 index 0000000..604a856 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_IT.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Matthias Dieter Wallno:fer libc-locales@sourceware.org + */ +return require __DIR__.'/de.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_LI.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_LI.php new file mode 100644 index 0000000..03e606a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_LI.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/de.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_LU.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_LU.php new file mode 100644 index 0000000..8ed8dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_LU.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/de.php', [ + 'formats' => [ + 'L' => 'YYYY-MM-DD', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dje.php b/vendor/nesbot/carbon/src/Carbon/Lang/dje.php new file mode 100644 index 0000000..74b7ac1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dje.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Subbaahi', 'Zaarikay b'], + 'weekdays' => ['Alhadi', 'Atinni', 'Atalaata', 'Alarba', 'Alhamisi', 'Alzuma', 'Asibti'], + 'weekdays_short' => ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'], + 'weekdays_min' => ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'], + 'months' => ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me', 'Žuweŋ', 'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur', 'Deesanbur'], + 'months_short' => ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy', 'Ut', 'Sek', 'Okt', 'Noo', 'Dee'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + + 'year' => ':count hari', // less reliable + 'y' => ':count hari', // less reliable + 'a_year' => ':count hari', // less reliable + + 'week' => ':count alzuma', // less reliable + 'w' => ':count alzuma', // less reliable + 'a_week' => ':count alzuma', // less reliable + + 'second' => ':count atinni', // less reliable + 's' => ':count atinni', // less reliable + 'a_second' => ':count atinni', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/doi.php b/vendor/nesbot/carbon/src/Carbon/Lang/doi.php new file mode 100644 index 0000000..cb679c5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/doi.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/doi_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/doi_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/doi_IN.php new file mode 100644 index 0000000..d359721 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/doi_IN.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Red Hat Pune libc-alpha@sourceware.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['जनवरी', 'फरवरी', 'मार्च', 'एप्रैल', 'मेई', 'जून', 'जूलै', 'अगस्त', 'सितंबर', 'अक्तूबर', 'नवंबर', 'दिसंबर'], + 'months_short' => ['जनवरी', 'फरवरी', 'मार्च', 'एप्रैल', 'मेई', 'जून', 'जूलै', 'अगस्त', 'सितंबर', 'अक्तूबर', 'नवंबर', 'दिसंबर'], + 'weekdays' => ['ऐतबार', 'सोमबार', 'मंगलबर', 'बुधबार', 'बीरबार', 'शुक्करबार', 'श्नीचरबार'], + 'weekdays_short' => ['ऐत', 'सोम', 'मंगल', 'बुध', 'बीर', 'शुक्कर', 'श्नीचर'], + 'weekdays_min' => ['ऐत', 'सोम', 'मंगल', 'बुध', 'बीर', 'शुक्कर', 'श्नीचर'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['सञं', 'सबेर'], + + 'second' => ':count सङार', // less reliable + 's' => ':count सङार', // less reliable + 'a_second' => ':count सङार', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dsb.php b/vendor/nesbot/carbon/src/Carbon/Lang/dsb.php new file mode 100644 index 0000000..1d214d5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dsb.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/dsb_DE.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dsb_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/dsb_DE.php new file mode 100644 index 0000000..1b94187 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dsb_DE.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Information from Michael Wolf bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'DD. MMMM YYYY', + 'LLL' => 'DD. MMMM, HH:mm [góź.]', + 'LLLL' => 'dddd, DD. MMMM YYYY, HH:mm [góź.]', + ], + 'months' => ['januara', 'februara', 'měrca', 'apryla', 'maja', 'junija', 'julija', 'awgusta', 'septembra', 'oktobra', 'nowembra', 'decembra'], + 'months_short' => ['Jan', 'Feb', 'Měr', 'Apr', 'Maj', 'Jun', 'Jul', 'Awg', 'Sep', 'Okt', 'Now', 'Dec'], + 'weekdays' => ['Njeźela', 'Pónjeźele', 'Wałtora', 'Srjoda', 'Stwórtk', 'Pětk', 'Sobota'], + 'weekdays_short' => ['Nj', 'Pó', 'Wa', 'Sr', 'St', 'Pě', 'So'], + 'weekdays_min' => ['Nj', 'Pó', 'Wa', 'Sr', 'St', 'Pě', 'So'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'year' => ':count lěto', + 'y' => ':count lěto', + 'a_year' => ':count lěto', + + 'month' => ':count mjasec', + 'm' => ':count mjasec', + 'a_month' => ':count mjasec', + + 'week' => ':count tyźeń', + 'w' => ':count tyźeń', + 'a_week' => ':count tyźeń', + + 'day' => ':count źeń', + 'd' => ':count źeń', + 'a_day' => ':count źeń', + + 'hour' => ':count góźina', + 'h' => ':count góźina', + 'a_hour' => ':count góźina', + + 'minute' => ':count minuta', + 'min' => ':count minuta', + 'a_minute' => ':count minuta', + + 'second' => ':count drugi', + 's' => ':count drugi', + 'a_second' => ':count drugi', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dua.php b/vendor/nesbot/carbon/src/Carbon/Lang/dua.php new file mode 100644 index 0000000..55e5c7c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dua.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['idiɓa', 'ebyámu'], + 'weekdays' => ['éti', 'mɔ́sú', 'kwasú', 'mukɔ́sú', 'ŋgisú', 'ɗónɛsú', 'esaɓasú'], + 'weekdays_short' => ['ét', 'mɔ́s', 'kwa', 'muk', 'ŋgi', 'ɗón', 'esa'], + 'weekdays_min' => ['ét', 'mɔ́s', 'kwa', 'muk', 'ŋgi', 'ɗón', 'esa'], + 'months' => ['dimɔ́di', 'ŋgɔndɛ', 'sɔŋɛ', 'diɓáɓá', 'emiasele', 'esɔpɛsɔpɛ', 'madiɓɛ́díɓɛ́', 'diŋgindi', 'nyɛtɛki', 'mayésɛ́', 'tiníní', 'eláŋgɛ́'], + 'months_short' => ['di', 'ŋgɔn', 'sɔŋ', 'diɓ', 'emi', 'esɔ', 'mad', 'diŋ', 'nyɛt', 'may', 'tin', 'elá'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + + 'year' => ':count ma mbu', // less reliable + 'y' => ':count ma mbu', // less reliable + 'a_year' => ':count ma mbu', // less reliable + + 'month' => ':count myo̱di', // less reliable + 'm' => ':count myo̱di', // less reliable + 'a_month' => ':count myo̱di', // less reliable + + 'week' => ':count woki', // less reliable + 'w' => ':count woki', // less reliable + 'a_week' => ':count woki', // less reliable + + 'day' => ':count buńa', // less reliable + 'd' => ':count buńa', // less reliable + 'a_day' => ':count buńa', // less reliable + + 'hour' => ':count ma awa', // less reliable + 'h' => ':count ma awa', // less reliable + 'a_hour' => ':count ma awa', // less reliable + + 'minute' => ':count minuti', // less reliable + 'min' => ':count minuti', // less reliable + 'a_minute' => ':count minuti', // less reliable + + 'second' => ':count maba', // less reliable + 's' => ':count maba', // less reliable + 'a_second' => ':count maba', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dv.php b/vendor/nesbot/carbon/src/Carbon/Lang/dv.php new file mode 100644 index 0000000..4b8d7e1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dv.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +$months = [ + 'ޖެނުއަރީ', + 'ފެބްރުއަރީ', + 'މާރިޗު', + 'އޭޕްރީލު', + 'މޭ', + 'ޖޫން', + 'ޖުލައި', + 'އޯގަސްޓު', + 'ސެޕްޓެމްބަރު', + 'އޮކްޓޯބަރު', + 'ނޮވެމްބަރު', + 'ޑިސެމްބަރު', +]; + +$weekdays = [ + 'އާދިއްތަ', + 'ހޯމަ', + 'އަންގާރަ', + 'ބުދަ', + 'ބުރާސްފަތި', + 'ހުކުރު', + 'ހޮނިހިރު', +]; + +/* + * Authors: + * - Josh Soref + * - Jawish Hameed + */ +return [ + 'year' => ':count '.'އަހަރު', + 'a_year' => '{1}'.'އަހަރެއް'.'|:count '.'އަހަރު', + 'month' => ':count '.'މަސް', + 'a_month' => '{1}'.'މަހެއް'.'|:count '.'މަސް', + 'week' => ':count '.'ހަފްތާ', + 'a_week' => '{1}'.'ސިކުންތުކޮޅެއް'.'|:count '.'ހަފްތާ', + 'day' => ':count '.'ދުވަސް', + 'a_day' => '{1}'.'ދުވަހެއް'.'|:count '.'ދުވަސް', + 'hour' => ':count '.'ގަޑިއިރު', + 'a_hour' => '{1}'.'ގަޑިއިރެއް'.'|:count '.'ގަޑިއިރު', + 'minute' => ':count '.'މިނިޓު', + 'a_minute' => '{1}'.'މިނިޓެއް'.'|:count '.'މިނިޓު', + 'second' => ':count '.'ސިކުންތު', + 'a_second' => '{1}'.'ސިކުންތުކޮޅެއް'.'|:count '.'ސިކުންތު', + 'ago' => 'ކުރިން :time', + 'from_now' => 'ތެރޭގައި :time', + 'after' => ':time ފަހުން', + 'before' => ':time ކުރި', + 'diff_yesterday' => 'އިއްޔެ', + 'diff_today' => 'މިއަދު', + 'diff_tomorrow' => 'މާދަމާ', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[މިއަދު] LT', + 'nextDay' => '[މާދަމާ] LT', + 'nextWeek' => 'dddd LT', + 'lastDay' => '[އިއްޔެ] LT', + 'lastWeek' => '[ފާއިތުވި] dddd LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['މކ', 'މފ'], + 'months' => $months, + 'months_short' => $months, + 'weekdays' => $weekdays, + 'weekdays_short' => $weekdays, + 'weekdays_min' => ['އާދި', 'ހޯމަ', 'އަން', 'ބުދަ', 'ބުރާ', 'ހުކު', 'ހޮނި'], + 'list' => [', ', ' އަދި '], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dv_MV.php b/vendor/nesbot/carbon/src/Carbon/Lang/dv_MV.php new file mode 100644 index 0000000..2668d5b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dv_MV.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ahmed Ali + */ + +$months = [ + 'ޖެނުއަރީ', + 'ފެބްރުއަރީ', + 'މާރިޗު', + 'އޭޕްރީލު', + 'މޭ', + 'ޖޫން', + 'ޖުލައި', + 'އޯގަސްޓު', + 'ސެޕްޓެމްބަރު', + 'އޮކްޓޯބަރު', + 'ނޮވެމްބަރު', + 'ޑިސެމްބަރު', +]; + +$weekdays = [ + 'އާދިއްތަ', + 'ހޯމަ', + 'އަންގާރަ', + 'ބުދަ', + 'ބުރާސްފަތި', + 'ހުކުރު', + 'ހޮނިހިރު', +]; + +return [ + 'year' => '{0}އަހަރެއް|[1,Inf]:count އަހަރު', + 'y' => '{0}އަހަރެއް|[1,Inf]:count އަހަރު', + 'month' => '{0}މައްސަރެއް|[1,Inf]:count މަސް', + 'm' => '{0}މައްސަރެއް|[1,Inf]:count މަސް', + 'week' => '{0}ހަފްތާއެއް|[1,Inf]:count ހަފްތާ', + 'w' => '{0}ހަފްތާއެއް|[1,Inf]:count ހަފްތާ', + 'day' => '{0}ދުވަސް|[1,Inf]:count ދުވަސް', + 'd' => '{0}ދުވަސް|[1,Inf]:count ދުވަސް', + 'hour' => '{0}ގަޑިއިރެއް|[1,Inf]:count ގަޑި', + 'h' => '{0}ގަޑިއިރެއް|[1,Inf]:count ގަޑި', + 'minute' => '{0}މިނެޓެއް|[1,Inf]:count މިނެޓް', + 'min' => '{0}މިނެޓެއް|[1,Inf]:count މިނެޓް', + 'second' => '{0}ސިކުންތެއް|[1,Inf]:count ސިކުންތު', + 's' => '{0}ސިކުންތެއް|[1,Inf]:count ސިކުންތު', + 'ago' => ':time ކުރިން', + 'from_now' => ':time ފަހުން', + 'after' => ':time ފަހުން', + 'before' => ':time ކުރި', + 'diff_yesterday' => 'އިއްޔެ', + 'diff_today' => 'މިއަދު', + 'diff_tomorrow' => 'މާދަމާ', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[މިއަދު] LT', + 'nextDay' => '[މާދަމާ] LT', + 'nextWeek' => 'dddd LT', + 'lastDay' => '[އިއްޔެ] LT', + 'lastWeek' => '[ފާއިތުވި] dddd LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['މކ', 'މފ'], + 'months' => $months, + 'months_short' => $months, + 'weekdays' => $weekdays, + 'weekdays_short' => $weekdays, + 'weekdays_min' => ['އާދި', 'ހޯމަ', 'އަން', 'ބުދަ', 'ބުރާ', 'ހުކު', 'ހޮނި'], + 'list' => [', ', ' އަދި '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dyo.php b/vendor/nesbot/carbon/src/Carbon/Lang/dyo.php new file mode 100644 index 0000000..33082e6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dyo.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'weekdays' => ['Dimas', 'Teneŋ', 'Talata', 'Alarbay', 'Aramisay', 'Arjuma', 'Sibiti'], + 'weekdays_short' => ['Dim', 'Ten', 'Tal', 'Ala', 'Ara', 'Arj', 'Sib'], + 'weekdays_min' => ['Dim', 'Ten', 'Tal', 'Ala', 'Ara', 'Arj', 'Sib'], + 'months' => ['Sanvie', 'Fébirie', 'Mars', 'Aburil', 'Mee', 'Sueŋ', 'Súuyee', 'Ut', 'Settembar', 'Oktobar', 'Novembar', 'Disambar'], + 'months_short' => ['Sa', 'Fe', 'Ma', 'Ab', 'Me', 'Su', 'Sú', 'Ut', 'Se', 'Ok', 'No', 'De'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dz.php b/vendor/nesbot/carbon/src/Carbon/Lang/dz.php new file mode 100644 index 0000000..cc17e69 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dz.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/dz_BT.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dz_BT.php b/vendor/nesbot/carbon/src/Carbon/Lang/dz_BT.php new file mode 100644 index 0000000..bfbcaf4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dz_BT.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Sherubtse College bug-glibc@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'པསྱི་ལོYYཟལMMཚེསDD', + ], + 'months' => ['ཟླ་བ་དང་པ་', 'ཟླ་བ་གཉིས་པ་', 'ཟླ་བ་གསུམ་པ་', 'ཟླ་བ་བཞི་པ་', 'ཟླ་བ་ལྔ་ཕ་', 'ཟླ་བ་དྲུག་པ་', 'ཟླ་བ་བདུནཔ་', 'ཟླ་བ་བརྒྱད་པ་', 'ཟླ་བ་དགུ་པ་', 'ཟླ་བ་བཅུ་པ་', 'ཟླ་བ་བཅུ་གཅིག་པ་', 'ཟླ་བ་བཅུ་གཉིས་པ་'], + 'months_short' => ['ཟླ་༡', 'ཟླ་༢', 'ཟླ་༣', 'ཟླ་༤', 'ཟླ་༥', 'ཟླ་༦', 'ཟླ་༧', 'ཟླ་༨', 'ཟླ་༩', 'ཟླ་༡༠', 'ཟླ་༡༡', 'ཟླ་༡༢'], + 'weekdays' => ['གཟའ་ཟླ་བ་', 'གཟའ་མིག་དམར་', 'གཟའ་ལྷག་ཕ་', 'གཟའ་པུར་བུ་', 'གཟའ་པ་སངས་', 'གཟའ་སྤེན་ཕ་', 'གཟའ་ཉི་མ་'], + 'weekdays_short' => ['ཟླ་', 'མིར་', 'ལྷག་', 'པུར་', 'སངས་', 'སྤེན་', 'ཉི་'], + 'weekdays_min' => ['ཟླ་', 'མིར་', 'ལྷག་', 'པུར་', 'སངས་', 'སྤེན་', 'ཉི་'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ངས་ཆ', 'ཕྱི་ཆ'], + + 'year' => ':count ཆརཔ', // less reliable + 'y' => ':count ཆརཔ', // less reliable + 'a_year' => ':count ཆརཔ', // less reliable + + 'month' => ':count ཟླ་བ', // less reliable + 'm' => ':count ཟླ་བ', // less reliable + 'a_month' => ':count ཟླ་བ', // less reliable + + 'day' => ':count ཉི', // less reliable + 'd' => ':count ཉི', // less reliable + 'a_day' => ':count ཉི', // less reliable + + 'second' => ':count ཆ', // less reliable + 's' => ':count ཆ', // less reliable + 'a_second' => ':count ཆ', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ebu.php b/vendor/nesbot/carbon/src/Carbon/Lang/ebu.php new file mode 100644 index 0000000..f60bc6f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ebu.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['KI', 'UT'], + 'weekdays' => ['Kiumia', 'Njumatatu', 'Njumaine', 'Njumatano', 'Aramithi', 'Njumaa', 'NJumamothii'], + 'weekdays_short' => ['Kma', 'Tat', 'Ine', 'Tan', 'Arm', 'Maa', 'NMM'], + 'weekdays_min' => ['Kma', 'Tat', 'Ine', 'Tan', 'Arm', 'Maa', 'NMM'], + 'months' => ['Mweri wa mbere', 'Mweri wa kaĩri', 'Mweri wa kathatũ', 'Mweri wa kana', 'Mweri wa gatano', 'Mweri wa gatantatũ', 'Mweri wa mũgwanja', 'Mweri wa kanana', 'Mweri wa kenda', 'Mweri wa ikũmi', 'Mweri wa ikũmi na ũmwe', 'Mweri wa ikũmi na Kaĩrĩ'], + 'months_short' => ['Mbe', 'Kai', 'Kat', 'Kan', 'Gat', 'Gan', 'Mug', 'Knn', 'Ken', 'Iku', 'Imw', 'Igi'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ee.php b/vendor/nesbot/carbon/src/Carbon/Lang/ee.php new file mode 100644 index 0000000..f96c5c9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ee.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['ŋ', 'ɣ'], + 'weekdays' => ['kɔsiɖa', 'dzoɖa', 'blaɖa', 'kuɖa', 'yawoɖa', 'fiɖa', 'memleɖa'], + 'weekdays_short' => ['kɔs', 'dzo', 'bla', 'kuɖ', 'yaw', 'fiɖ', 'mem'], + 'weekdays_min' => ['kɔs', 'dzo', 'bla', 'kuɖ', 'yaw', 'fiɖ', 'mem'], + 'months' => ['dzove', 'dzodze', 'tedoxe', 'afɔfĩe', 'dama', 'masa', 'siamlɔm', 'deasiamime', 'anyɔnyɔ', 'kele', 'adeɛmekpɔxe', 'dzome'], + 'months_short' => ['dzv', 'dzd', 'ted', 'afɔ', 'dam', 'mas', 'sia', 'dea', 'any', 'kel', 'ade', 'dzm'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'a [ga] h:mm', + 'LTS' => 'a [ga] h:mm:ss', + 'L' => 'M/D/YYYY', + 'LL' => 'MMM D [lia], YYYY', + 'LLL' => 'a [ga] h:mm MMMM D [lia] YYYY', + 'LLLL' => 'a [ga] h:mm dddd, MMMM D [lia] YYYY', + ], + + 'year' => 'ƒe :count', + 'y' => 'ƒe :count', + 'a_year' => 'ƒe :count', + + 'month' => 'ɣleti :count', + 'm' => 'ɣleti :count', + 'a_month' => 'ɣleti :count', + + 'week' => 'kwasiɖa :count', + 'w' => 'kwasiɖa :count', + 'a_week' => 'kwasiɖa :count', + + 'day' => 'ŋkeke :count', + 'd' => 'ŋkeke :count', + 'a_day' => 'ŋkeke :count', + + 'hour' => 'gaƒoƒo :count', + 'h' => 'gaƒoƒo :count', + 'a_hour' => 'gaƒoƒo :count', + + 'minute' => 'miniti :count', // less reliable + 'min' => 'miniti :count', // less reliable + 'a_minute' => 'miniti :count', // less reliable + + 'second' => 'sɛkɛnd :count', // less reliable + 's' => 'sɛkɛnd :count', // less reliable + 'a_second' => 'sɛkɛnd :count', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ee_TG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ee_TG.php new file mode 100644 index 0000000..7a8b36c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ee_TG.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ee.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'LLL' => 'HH:mm MMMM D [lia] YYYY', + 'LLLL' => 'HH:mm dddd, MMMM D [lia] YYYY', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/el.php b/vendor/nesbot/carbon/src/Carbon/Lang/el.php new file mode 100644 index 0000000..7c40f9c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/el.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Alessandro Di Felice + * - François B + * - Tim Fish + * - Gabriel Monteagudo + * - JD Isaacks + * - yiannisdesp + * - Ilias Kasmeridis (iliaskasm) + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count χρόνος|:count χρόνια', + 'a_year' => 'ένας χρόνος|:count χρόνια', + 'y' => ':count χρ.', + 'month' => ':count μήνας|:count μήνες', + 'a_month' => 'ένας μήνας|:count μήνες', + 'm' => ':count μήν.', + 'week' => ':count εβδομάδα|:count εβδομάδες', + 'a_week' => 'μια εβδομάδα|:count εβδομάδες', + 'w' => ':count εβδ.', + 'day' => ':count μέρα|:count μέρες', + 'a_day' => 'μία μέρα|:count μέρες', + 'd' => ':count μέρ.', + 'hour' => ':count ώρα|:count ώρες', + 'a_hour' => 'μία ώρα|:count ώρες', + 'h' => ':count ώρα|:count ώρες', + 'minute' => ':count λεπτό|:count λεπτά', + 'a_minute' => 'ένα λεπτό|:count λεπτά', + 'min' => ':count λεπ.', + 'second' => ':count δευτερόλεπτο|:count δευτερόλεπτα', + 'a_second' => 'λίγα δευτερόλεπτα|:count δευτερόλεπτα', + 's' => ':count δευ.', + 'ago' => 'πριν :time', + 'from_now' => 'σε :time', + 'after' => ':time μετά', + 'before' => ':time πριν', + 'diff_now' => 'τώρα', + 'diff_today' => 'Σήμερα', + 'diff_today_regexp' => 'Σήμερα(?:\\s+{})?', + 'diff_yesterday' => 'χθες', + 'diff_yesterday_regexp' => 'Χθες(?:\\s+{})?', + 'diff_tomorrow' => 'αύριο', + 'diff_tomorrow_regexp' => 'Αύριο(?:\\s+{})?', + 'formats' => [ + 'LT' => 'h:mm A', + 'LTS' => 'h:mm:ss A', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm A', + 'LLLL' => 'dddd, D MMMM YYYY h:mm A', + ], + 'calendar' => [ + 'sameDay' => '[Σήμερα {}] LT', + 'nextDay' => '[Αύριο {}] LT', + 'nextWeek' => 'dddd [{}] LT', + 'lastDay' => '[Χθες {}] LT', + 'lastWeek' => function (CarbonInterface $current) { + switch ($current->dayOfWeek) { + case 6: + return '[το προηγούμενο] dddd [{}] LT'; + default: + return '[την προηγούμενη] dddd [{}] LT'; + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => ':numberη', + 'meridiem' => ['ΠΜ', 'ΜΜ', 'πμ', 'μμ'], + 'months' => ['Ιανουαρίου', 'Φεβρουαρίου', 'Μαρτίου', 'Απριλίου', 'Μαΐου', 'Ιουνίου', 'Ιουλίου', 'Αυγούστου', 'Σεπτεμβρίου', 'Οκτωβρίου', 'Νοεμβρίου', 'Δεκεμβρίου'], + 'months_standalone' => ['Ιανουάριος', 'Φεβρουάριος', 'Μάρτιος', 'Απρίλιος', 'Μάιος', 'Ιούνιος', 'Ιούλιος', 'Αύγουστος', 'Σεπτέμβριος', 'Οκτώβριος', 'Νοέμβριος', 'Δεκέμβριος'], + 'months_regexp' => '/(D[oD]?[\s,]+MMMM|L{2,4}|l{2,4})/', + 'months_short' => ['Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μαϊ', 'Ιουν', 'Ιουλ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ'], + 'weekdays' => ['Κυριακή', 'Δευτέρα', 'Τρίτη', 'Τετάρτη', 'Πέμπτη', 'Παρασκευή', 'Σάββατο'], + 'weekdays_short' => ['Κυρ', 'Δευ', 'Τρι', 'Τετ', 'Πεμ', 'Παρ', 'Σαβ'], + 'weekdays_min' => ['Κυ', 'Δε', 'Τρ', 'Τε', 'Πε', 'Πα', 'Σα'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' και '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/el_CY.php b/vendor/nesbot/carbon/src/Carbon/Lang/el_CY.php new file mode 100644 index 0000000..8a693c1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/el_CY.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Greek Debian Translation Team bug-glibc@gnu.org + */ +return array_replace_recursive(require __DIR__.'/el.php', [ + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/el_GR.php b/vendor/nesbot/carbon/src/Carbon/Lang/el_GR.php new file mode 100644 index 0000000..df196af --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/el_GR.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/el.php', [ + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en.php b/vendor/nesbot/carbon/src/Carbon/Lang/en.php new file mode 100644 index 0000000..f81f617 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Milos Sakovic + * - Paul + * - Pete Scopes (pdscopes) + */ +return [ + /* + * {1}, {0} and ]1,Inf[ are not needed as it's the default for English pluralization. + * But as some languages are using en.php as a fallback, it's better to specify it + * explicitly so those languages also fallback to English pluralization when a unit + * is missing. + */ + 'year' => '{1}:count year|{0}:count years|]1,Inf[:count years', + 'a_year' => '{1}a year|{0}:count years|]1,Inf[:count years', + 'y' => '{1}:countyr|{0}:countyrs|]1,Inf[:countyrs', + 'month' => '{1}:count month|{0}:count months|]1,Inf[:count months', + 'a_month' => '{1}a month|{0}:count months|]1,Inf[:count months', + 'm' => '{1}:countmo|{0}:countmos|]1,Inf[:countmos', + 'week' => '{1}:count week|{0}:count weeks|]1,Inf[:count weeks', + 'a_week' => '{1}a week|{0}:count weeks|]1,Inf[:count weeks', + 'w' => ':countw', + 'day' => '{1}:count day|{0}:count days|]1,Inf[:count days', + 'a_day' => '{1}a day|{0}:count days|]1,Inf[:count days', + 'd' => ':countd', + 'hour' => '{1}:count hour|{0}:count hours|]1,Inf[:count hours', + 'a_hour' => '{1}an hour|{0}:count hours|]1,Inf[:count hours', + 'h' => ':counth', + 'minute' => '{1}:count minute|{0}:count minutes|]1,Inf[:count minutes', + 'a_minute' => '{1}a minute|{0}:count minutes|]1,Inf[:count minutes', + 'min' => ':countm', + 'second' => '{1}:count second|{0}:count seconds|]1,Inf[:count seconds', + 'a_second' => '{1}a few seconds|{0}:count seconds|]1,Inf[:count seconds', + 's' => ':counts', + 'millisecond' => '{1}:count millisecond|{0}:count milliseconds|]1,Inf[:count milliseconds', + 'a_millisecond' => '{1}a millisecond|{0}:count milliseconds|]1,Inf[:count milliseconds', + 'ms' => ':countms', + 'microsecond' => '{1}:count microsecond|{0}:count microseconds|]1,Inf[:count microseconds', + 'a_microsecond' => '{1}a microsecond|{0}:count microseconds|]1,Inf[:count microseconds', + 'µs' => ':countµs', + 'ago' => ':time ago', + 'from_now' => ':time from now', + 'after' => ':time after', + 'before' => ':time before', + 'diff_now' => 'just now', + 'diff_today' => 'today', + 'diff_yesterday' => 'yesterday', + 'diff_tomorrow' => 'tomorrow', + 'diff_before_yesterday' => 'before yesterday', + 'diff_after_tomorrow' => 'after tomorrow', + 'period_recurrences' => '{1}once|{0}:count times|]1,Inf[:count times', + 'period_interval' => 'every :interval', + 'period_start_date' => 'from :date', + 'period_end_date' => 'to :date', + 'months' => ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + 'weekdays' => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + 'weekdays_short' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + 'weekdays_min' => ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + 'ordinal' => function ($number) { + $lastDigit = $number % 10; + + return $number.( + ((int) ($number % 100 / 10) === 1) ? 'th' : ( + ($lastDigit === 1) ? 'st' : ( + ($lastDigit === 2) ? 'nd' : ( + ($lastDigit === 3) ? 'rd' : 'th' + ) + ) + ) + ); + }, + 'list' => [', ', ' and '], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_001.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_001.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_001.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_150.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_150.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_150.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AG.php new file mode 100644 index 0000000..2c1c64f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AG.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AI.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AI.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AS.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AS.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AT.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AT.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AT.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AU.php new file mode 100644 index 0000000..f16bd4f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AU.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Kunal Marwaha + * - François B + * - Mayank Badola + * - JD Isaacks + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'from_now' => 'in :time', + 'formats' => [ + 'LT' => 'h:mm A', + 'LTS' => 'h:mm:ss A', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm A', + 'LLLL' => 'dddd, D MMMM YYYY h:mm A', + ], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BB.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BB.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BB.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BE.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BE.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BI.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BI.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BM.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BM.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BS.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BS.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BW.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BW.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BZ.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BZ.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CA.php new file mode 100644 index 0000000..e656086 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CA.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Zhan Tong Zhang + * - Mayank Badola + * - JD Isaacks + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'from_now' => 'in :time', + 'formats' => [ + 'LT' => 'h:mm A', + 'LTS' => 'h:mm:ss A', + 'L' => 'YYYY-MM-DD', + 'LL' => 'MMMM D, YYYY', + 'LLL' => 'MMMM D, YYYY h:mm A', + 'LLLL' => 'dddd, MMMM D, YYYY h:mm A', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CC.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CC.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CH.php new file mode 100644 index 0000000..10d9cd8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CH.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CK.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CK.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CM.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CM.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CX.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CX.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CX.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CY.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CY.php new file mode 100644 index 0000000..a44c350 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CY.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - NehaGautam + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'from_now' => 'in :time', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD-MM-YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_DE.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_DE.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_DG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_DG.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_DG.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_DK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_DK.php new file mode 100644 index 0000000..9e8a8c6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_DK.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Danish Standards Association bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'YYYY-MM-DD', + ], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_DM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_DM.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_DM.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ER.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ER.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_FI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_FI.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_FI.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_FJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_FJ.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_FJ.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_FK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_FK.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_FK.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_FM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_FM.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_FM.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GB.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GB.php new file mode 100644 index 0000000..67d9fd6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GB.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Mayank Badola + * - JD Isaacks + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'from_now' => 'in :time', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GD.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GD.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GD.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GG.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GG.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GH.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GH.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GI.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GI.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GM.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GM.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GU.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GU.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GY.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GY.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GY.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_HK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_HK.php new file mode 100644 index 0000000..34aae98 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_HK.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IE.php new file mode 100644 index 0000000..c8d3c2f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IE.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Martin McWhorter + * - François B + * - Chris Cartlidge + * - JD Isaacks + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'from_now' => 'in :time', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD-MM-YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IL.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IL.php new file mode 100644 index 0000000..e607924 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IL.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Yoav Amit + * - François B + * - Mayank Badola + * - JD Isaacks + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'from_now' => 'in :time', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IM.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IM.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IN.php new file mode 100644 index 0000000..00414e9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IN.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YY', + 'LL' => 'MMMM DD, YYYY', + 'LLL' => 'DD MMM HH:mm', + 'LLLL' => 'MMMM DD, YYYY HH:mm', + ], + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IO.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IO.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IO.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ISO.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ISO.php new file mode 100644 index 0000000..11457b0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ISO.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-dd', + 'LL' => 'YYYY MMM D', + 'LLL' => 'YYYY MMMM D HH:mm', + 'LLLL' => 'dddd, YYYY MMMM DD HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_JE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_JE.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_JE.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_JM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_JM.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_JM.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_KE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_KE.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_KE.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_KI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_KI.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_KI.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_KN.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_KN.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_KN.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_KY.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_KY.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_KY.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_LC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_LC.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_LC.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_LR.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_LR.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_LR.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_LS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_LS.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_LS.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MG.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MG.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MH.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MH.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MO.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MO.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MO.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MP.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MP.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MP.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MS.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MS.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MT.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MT.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MT.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MU.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MU.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MW.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MW.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MY.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MY.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MY.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NA.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NA.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NA.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NF.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NF.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NF.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NG.php new file mode 100644 index 0000000..67bceaa --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NG.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NL.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NL.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NL.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NR.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NR.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NR.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NU.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NU.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NZ.php new file mode 100644 index 0000000..6a206a0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NZ.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Mayank Badola + * - Luke McGregor + * - JD Isaacks + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'from_now' => 'in :time', + 'formats' => [ + 'LT' => 'h:mm A', + 'LTS' => 'h:mm:ss A', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm A', + 'LLLL' => 'dddd, D MMMM YYYY h:mm A', + ], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PG.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PG.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PH.php new file mode 100644 index 0000000..34aae98 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PH.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PK.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PK.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PN.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PN.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PN.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PR.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PR.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PR.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PW.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PW.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_RW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_RW.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_RW.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SB.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SB.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SB.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SC.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SC.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SD.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SD.php new file mode 100644 index 0000000..c4e2557 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SD.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 6, + 'weekend' => [5, 6], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SE.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SE.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SG.php new file mode 100644 index 0000000..5ee9524 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SG.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'from_now' => 'in :time', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SH.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SH.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SI.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SI.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SL.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SL.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SL.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SS.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SS.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SX.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SX.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SX.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SZ.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SZ.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TC.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TC.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TK.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TK.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TO.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TO.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TO.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TT.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TT.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TT.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TV.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TV.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TV.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TZ.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TZ.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_UG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_UG.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_UG.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_UM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_UM.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_UM.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_US.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_US.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_US_Posix.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_US_Posix.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_US_Posix.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_VC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_VC.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_VC.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_VG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_VG.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_VG.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_VI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_VI.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_VI.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_VU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_VU.php new file mode 100644 index 0000000..e2dd81d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_VU.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_WS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_WS.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_WS.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZA.php new file mode 100644 index 0000000..48ea947 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZA.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YY', + 'LL' => 'MMMM DD, YYYY', + 'LLL' => 'DD MMM HH:mm', + 'LLLL' => 'MMMM DD, YYYY HH:mm', + ], + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ZM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZM.php new file mode 100644 index 0000000..d8a8cb5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZM.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - ANLoc Martin Benjamin locales@africanlocalization.net + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ZW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZW.php new file mode 100644 index 0000000..f086dc6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZW.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/en.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/eo.php b/vendor/nesbot/carbon/src/Carbon/Lang/eo.php new file mode 100644 index 0000000..7c2efba --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/eo.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - François B + * - Mia Nordentoft + * - JD Isaacks + */ +return [ + 'year' => ':count jaro|:count jaroj', + 'a_year' => 'jaro|:count jaroj', + 'y' => ':count j.', + 'month' => ':count monato|:count monatoj', + 'a_month' => 'monato|:count monatoj', + 'm' => ':count mo.', + 'week' => ':count semajno|:count semajnoj', + 'a_week' => 'semajno|:count semajnoj', + 'w' => ':count sem.', + 'day' => ':count tago|:count tagoj', + 'a_day' => 'tago|:count tagoj', + 'd' => ':count t.', + 'hour' => ':count horo|:count horoj', + 'a_hour' => 'horo|:count horoj', + 'h' => ':count h.', + 'minute' => ':count minuto|:count minutoj', + 'a_minute' => 'minuto|:count minutoj', + 'min' => ':count min.', + 'second' => ':count sekundo|:count sekundoj', + 'a_second' => 'sekundoj|:count sekundoj', + 's' => ':count sek.', + 'ago' => 'antaŭ :time', + 'from_now' => 'post :time', + 'after' => ':time poste', + 'before' => ':time antaŭe', + 'diff_yesterday' => 'Hieraŭ', + 'diff_yesterday_regexp' => 'Hieraŭ(?:\\s+je)?', + 'diff_today' => 'Hodiaŭ', + 'diff_today_regexp' => 'Hodiaŭ(?:\\s+je)?', + 'diff_tomorrow' => 'Morgaŭ', + 'diff_tomorrow_regexp' => 'Morgaŭ(?:\\s+je)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-DD', + 'LL' => 'D[-a de] MMMM, YYYY', + 'LLL' => 'D[-a de] MMMM, YYYY HH:mm', + 'LLLL' => 'dddd, [la] D[-a de] MMMM, YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Hodiaŭ je] LT', + 'nextDay' => '[Morgaŭ je] LT', + 'nextWeek' => 'dddd [je] LT', + 'lastDay' => '[Hieraŭ je] LT', + 'lastWeek' => '[pasinta] dddd [je] LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':numbera', + 'meridiem' => ['a.t.m.', 'p.t.m.'], + 'months' => ['januaro', 'februaro', 'marto', 'aprilo', 'majo', 'junio', 'julio', 'aŭgusto', 'septembro', 'oktobro', 'novembro', 'decembro'], + 'months_short' => ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aŭg', 'sep', 'okt', 'nov', 'dec'], + 'weekdays' => ['dimanĉo', 'lundo', 'mardo', 'merkredo', 'ĵaŭdo', 'vendredo', 'sabato'], + 'weekdays_short' => ['dim', 'lun', 'mard', 'merk', 'ĵaŭ', 'ven', 'sab'], + 'weekdays_min' => ['di', 'lu', 'ma', 'me', 'ĵa', 've', 'sa'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' kaj '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es.php b/vendor/nesbot/carbon/src/Carbon/Lang/es.php new file mode 100644 index 0000000..1c4fcfd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Kunal Marwaha + * - kostas + * - François B + * - Tim Fish + * - Claire Coloma + * - Steven Heinrich + * - JD Isaacks + * - Raphael Amorim + * - Jorge Y. Castillo + * - Víctor Díaz + * - Diego + * - Sebastian Thierer + * - quinterocesar + * - Daniel Commesse Liévanos (danielcommesse) + * - Pete Scopes (pdscopes) + * - gam04 + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count año|:count años', + 'a_year' => 'un año|:count años', + 'y' => ':count año|:count años', + 'month' => ':count mes|:count meses', + 'a_month' => 'un mes|:count meses', + 'm' => ':count mes|:count meses', + 'week' => ':count semana|:count semanas', + 'a_week' => 'una semana|:count semanas', + 'w' => ':countsem', + 'day' => ':count día|:count días', + 'a_day' => 'un día|:count días', + 'd' => ':countd', + 'hour' => ':count hora|:count horas', + 'a_hour' => 'una hora|:count horas', + 'h' => ':counth', + 'minute' => ':count minuto|:count minutos', + 'a_minute' => 'un minuto|:count minutos', + 'min' => ':countm', + 'second' => ':count segundo|:count segundos', + 'a_second' => 'unos segundos|:count segundos', + 's' => ':counts', + 'millisecond' => ':count milisegundo|:count milisegundos', + 'a_millisecond' => 'un milisegundo|:count milisegundos', + 'ms' => ':countms', + 'microsecond' => ':count microsegundo|:count microsegundos', + 'a_microsecond' => 'un microsegundo|:count microsegundos', + 'µs' => ':countµs', + 'ago' => 'hace :time', + 'from_now' => 'en :time', + 'after' => ':time después', + 'before' => ':time antes', + 'diff_now' => 'ahora mismo', + 'diff_today' => 'hoy', + 'diff_today_regexp' => 'hoy(?:\\s+a)?(?:\\s+las)?', + 'diff_yesterday' => 'ayer', + 'diff_yesterday_regexp' => 'ayer(?:\\s+a)?(?:\\s+las)?', + 'diff_tomorrow' => 'mañana', + 'diff_tomorrow_regexp' => 'mañana(?:\\s+a)?(?:\\s+las)?', + 'diff_before_yesterday' => 'anteayer', + 'diff_after_tomorrow' => 'pasado mañana', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D [de] MMMM [de] YYYY', + 'LLL' => 'D [de] MMMM [de] YYYY H:mm', + 'LLLL' => 'dddd, D [de] MMMM [de] YYYY H:mm', + ], + 'calendar' => [ + 'sameDay' => function (CarbonInterface $current) { + return '[hoy a la'.($current->hour !== 1 ? 's' : '').'] LT'; + }, + 'nextDay' => function (CarbonInterface $current) { + return '[mañana a la'.($current->hour !== 1 ? 's' : '').'] LT'; + }, + 'nextWeek' => function (CarbonInterface $current) { + return 'dddd [a la'.($current->hour !== 1 ? 's' : '').'] LT'; + }, + 'lastDay' => function (CarbonInterface $current) { + return '[ayer a la'.($current->hour !== 1 ? 's' : '').'] LT'; + }, + 'lastWeek' => function (CarbonInterface $current) { + return '[el] dddd [pasado a la'.($current->hour !== 1 ? 's' : '').'] LT'; + }, + 'sameElse' => 'L', + ], + 'months' => ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'], + 'months_short' => ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'], + 'mmm_suffix' => '.', + 'ordinal' => ':numberº', + 'weekdays' => ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'], + 'weekdays_short' => ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'], + 'weekdays_min' => ['do', 'lu', 'ma', 'mi', 'ju', 'vi', 'sá'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' y '], + 'meridiem' => ['a. m.', 'p. m.'], + 'ordinal_words' => [ + 'of' => 'de', + 'first' => 'primer', + 'second' => 'segundo', + 'third' => 'tercer', + 'fourth' => 'cuarto', + 'fifth' => 'quinto', + 'last' => 'último', + ], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_419.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_419.php new file mode 100644 index 0000000..a74806e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_419.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_AR.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_AR.php new file mode 100644 index 0000000..a74806e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_AR.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_BO.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_BO.php new file mode 100644 index 0000000..c9b8432 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_BO.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_BR.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_BR.php new file mode 100644 index 0000000..378d054 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_BR.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_BZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_BZ.php new file mode 100644 index 0000000..378d054 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_BZ.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_CL.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_CL.php new file mode 100644 index 0000000..a74806e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_CL.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_CO.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_CO.php new file mode 100644 index 0000000..a74806e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_CO.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_CR.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_CR.php new file mode 100644 index 0000000..553fc09 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_CR.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_CU.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_CU.php new file mode 100644 index 0000000..f02e1a6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_CU.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_DO.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_DO.php new file mode 100644 index 0000000..0f855ba --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_DO.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - kostas + * - François B + * - Tim Fish + * - Chiel Robben + * - Claire Coloma + * - Steven Heinrich + * - JD Isaacks + * - Raphael Amorim + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'diff_before_yesterday' => 'anteayer', + 'formats' => [ + 'LT' => 'h:mm A', + 'LTS' => 'h:mm:ss A', + 'LLL' => 'D [de] MMMM [de] YYYY h:mm A', + 'LLLL' => 'dddd, D [de] MMMM [de] YYYY h:mm A', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_EA.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_EA.php new file mode 100644 index 0000000..f02e1a6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_EA.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_EC.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_EC.php new file mode 100644 index 0000000..a74806e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_EC.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_ES.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_ES.php new file mode 100644 index 0000000..19217c2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_ES.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return require __DIR__.'/es.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_GQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_GQ.php new file mode 100644 index 0000000..f02e1a6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_GQ.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_GT.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_GT.php new file mode 100644 index 0000000..a74806e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_GT.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_HN.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_HN.php new file mode 100644 index 0000000..a74806e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_HN.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_IC.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_IC.php new file mode 100644 index 0000000..f02e1a6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_IC.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_MX.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_MX.php new file mode 100644 index 0000000..61e14cf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_MX.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'diff_before_yesterday' => 'antier', + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_NI.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_NI.php new file mode 100644 index 0000000..6b964c1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_NI.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_PA.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_PA.php new file mode 100644 index 0000000..a74806e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_PA.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_PE.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_PE.php new file mode 100644 index 0000000..a74806e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_PE.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_PH.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_PH.php new file mode 100644 index 0000000..deae06a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_PH.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'D/M/yy', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D [de] MMMM [de] YYYY h:mm a', + 'LLLL' => 'dddd, D [de] MMMM [de] YYYY h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_PR.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_PR.php new file mode 100644 index 0000000..6b964c1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_PR.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_PY.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_PY.php new file mode 100644 index 0000000..a74806e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_PY.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_SV.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_SV.php new file mode 100644 index 0000000..00db08e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_SV.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'months' => ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'], + 'months_short' => ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'], + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_US.php new file mode 100644 index 0000000..f333136 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_US.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Kunal Marwaha + * - Josh Soref + * - Jørn Ølmheim + * - Craig Patik + * - bustta + * - François B + * - Tim Fish + * - Claire Coloma + * - Steven Heinrich + * - JD Isaacks + * - Raphael Amorim + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'diff_before_yesterday' => 'anteayer', + 'formats' => [ + 'LT' => 'h:mm A', + 'LTS' => 'h:mm:ss A', + 'L' => 'MM/DD/YYYY', + 'LL' => 'MMMM [de] D [de] YYYY', + 'LLL' => 'MMMM [de] D [de] YYYY h:mm A', + 'LLLL' => 'dddd, MMMM [de] D [de] YYYY h:mm A', + ], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_UY.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_UY.php new file mode 100644 index 0000000..39baff8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_UY.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'months' => ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'], + 'months_short' => ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'set', 'oct', 'nov', 'dic'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_VE.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_VE.php new file mode 100644 index 0000000..a74806e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_VE.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/es.php', [ + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/et.php b/vendor/nesbot/carbon/src/Carbon/Lang/et.php new file mode 100644 index 0000000..f49c880 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/et.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Philippe Vaucher + * - Andres Ivanov + * - Tsutomu Kuroda + * - tjku + * - Max Melentiev + * - Juanito Fatas + * - RM87 + * - Akira Matsuda + * - Christopher Dell + * - Enrique Vidal + * - Simone Carletti + * - Aaron Patterson + * - Esko Lehtme + * - Mart Karu + * - Nicolás Hock Isaza + * - Kevin Valdek + * - Zahhar Kirillov + * - João Magalhães + * - Ingmar + * - Illimar Tambek + * - Mihkel + */ +return [ + 'year' => ':count aasta|:count aastat', + 'y' => ':count a', + 'month' => ':count kuu|:count kuud', + 'm' => ':count k', + 'week' => ':count nädal|:count nädalat', + 'w' => ':count näd', + 'day' => ':count päev|:count päeva', + 'd' => ':count p', + 'hour' => ':count tund|:count tundi', + 'h' => ':count t', + 'minute' => ':count minut|:count minutit', + 'min' => ':count min', + 'second' => ':count sekund|:count sekundit', + 's' => ':count s', + 'ago' => ':time tagasi', + 'from_now' => ':time pärast', + 'after' => ':time pärast', + 'before' => ':time enne', + 'year_from_now' => ':count aasta', + 'month_from_now' => ':count kuu', + 'week_from_now' => ':count nädala', + 'day_from_now' => ':count päeva', + 'hour_from_now' => ':count tunni', + 'minute_from_now' => ':count minuti', + 'second_from_now' => ':count sekundi', + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'diff_now' => 'nüüd', + 'diff_today' => 'täna', + 'diff_yesterday' => 'eile', + 'diff_tomorrow' => 'homme', + 'diff_before_yesterday' => 'üleeile', + 'diff_after_tomorrow' => 'ülehomme', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D. MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[täna] LT', + 'nextDay' => '[homme] LT', + 'lastDay' => '[eile] LT', + 'nextWeek' => 'dddd LT', + 'lastWeek' => '[eelmine] dddd LT', + 'sameElse' => 'L', + ], + 'months' => ['jaanuar', 'veebruar', 'märts', 'aprill', 'mai', 'juuni', 'juuli', 'august', 'september', 'oktoober', 'november', 'detsember'], + 'months_short' => ['jaan', 'veebr', 'märts', 'apr', 'mai', 'juuni', 'juuli', 'aug', 'sept', 'okt', 'nov', 'dets'], + 'weekdays' => ['pühapäev', 'esmaspäev', 'teisipäev', 'kolmapäev', 'neljapäev', 'reede', 'laupäev'], + 'weekdays_short' => ['P', 'E', 'T', 'K', 'N', 'R', 'L'], + 'weekdays_min' => ['P', 'E', 'T', 'K', 'N', 'R', 'L'], + 'list' => [', ', ' ja '], + 'meridiem' => ['enne lõunat', 'pärast lõunat'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/et_EE.php b/vendor/nesbot/carbon/src/Carbon/Lang/et_EE.php new file mode 100644 index 0000000..0f112b3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/et_EE.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/et.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/eu.php b/vendor/nesbot/carbon/src/Carbon/Lang/eu.php new file mode 100644 index 0000000..a543f1a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/eu.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - François B + * - JD Isaacks + */ +return [ + 'year' => 'urte bat|:count urte', + 'y' => 'Urte 1|:count urte', + 'month' => 'hilabete bat|:count hilabete', + 'm' => 'Hile 1|:count hile', + 'week' => 'Aste 1|:count aste', + 'w' => 'Aste 1|:count aste', + 'day' => 'egun bat|:count egun', + 'd' => 'Egun 1|:count egun', + 'hour' => 'ordu bat|:count ordu', + 'h' => 'Ordu 1|:count ordu', + 'minute' => 'minutu bat|:count minutu', + 'min' => 'Minutu 1|:count minutu', + 'second' => 'segundo batzuk|:count segundo', + 's' => 'Segundu 1|:count segundu', + 'ago' => 'duela :time', + 'from_now' => ':time barru', + 'after' => ':time geroago', + 'before' => ':time lehenago', + 'diff_now' => 'orain', + 'diff_today' => 'gaur', + 'diff_yesterday' => 'atzo', + 'diff_tomorrow' => 'bihar', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-DD', + 'LL' => 'YYYY[ko] MMMM[ren] D[a]', + 'LLL' => 'YYYY[ko] MMMM[ren] D[a] HH:mm', + 'LLLL' => 'dddd, YYYY[ko] MMMM[ren] D[a] HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[gaur] LT[etan]', + 'nextDay' => '[bihar] LT[etan]', + 'nextWeek' => 'dddd LT[etan]', + 'lastDay' => '[atzo] LT[etan]', + 'lastWeek' => '[aurreko] dddd LT[etan]', + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'months' => ['urtarrila', 'otsaila', 'martxoa', 'apirila', 'maiatza', 'ekaina', 'uztaila', 'abuztua', 'iraila', 'urria', 'azaroa', 'abendua'], + 'months_short' => ['urt.', 'ots.', 'mar.', 'api.', 'mai.', 'eka.', 'uzt.', 'abu.', 'ira.', 'urr.', 'aza.', 'abe.'], + 'weekdays' => ['igandea', 'astelehena', 'asteartea', 'asteazkena', 'osteguna', 'ostirala', 'larunbata'], + 'weekdays_short' => ['ig.', 'al.', 'ar.', 'az.', 'og.', 'ol.', 'lr.'], + 'weekdays_min' => ['ig', 'al', 'ar', 'az', 'og', 'ol', 'lr'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' eta '], + 'meridiem' => ['g', 'a'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/eu_ES.php b/vendor/nesbot/carbon/src/Carbon/Lang/eu_ES.php new file mode 100644 index 0000000..0d1e82a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/eu_ES.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/eu.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ewo.php b/vendor/nesbot/carbon/src/Carbon/Lang/ewo.php new file mode 100644 index 0000000..7808ab5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ewo.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['kíkíríg', 'ngəgógəle'], + 'weekdays' => ['sɔ́ndɔ', 'mɔ́ndi', 'sɔ́ndɔ məlú mə́bɛ̌', 'sɔ́ndɔ məlú mə́lɛ́', 'sɔ́ndɔ məlú mə́nyi', 'fúladé', 'séradé'], + 'weekdays_short' => ['sɔ́n', 'mɔ́n', 'smb', 'sml', 'smn', 'fúl', 'sér'], + 'weekdays_min' => ['sɔ́n', 'mɔ́n', 'smb', 'sml', 'smn', 'fúl', 'sér'], + 'months' => ['ngɔn osú', 'ngɔn bɛ̌', 'ngɔn lála', 'ngɔn nyina', 'ngɔn tána', 'ngɔn saməna', 'ngɔn zamgbála', 'ngɔn mwom', 'ngɔn ebulú', 'ngɔn awóm', 'ngɔn awóm ai dziá', 'ngɔn awóm ai bɛ̌'], + 'months_short' => ['ngo', 'ngb', 'ngl', 'ngn', 'ngt', 'ngs', 'ngz', 'ngm', 'nge', 'nga', 'ngad', 'ngab'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + + // Too unreliable + /* + 'year' => ':count mbu', // less reliable + 'y' => ':count mbu', // less reliable + 'a_year' => ':count mbu', // less reliable + + 'month' => ':count ngòn', // less reliable + 'm' => ':count ngòn', // less reliable + 'a_month' => ':count ngòn', // less reliable + + 'week' => ':count mësë', // less reliable + 'w' => ':count mësë', // less reliable + 'a_week' => ':count mësë', // less reliable + + 'day' => ':count mësë', // less reliable + 'd' => ':count mësë', // less reliable + 'a_day' => ':count mësë', // less reliable + + 'hour' => ':count awola', // less reliable + 'h' => ':count awola', // less reliable + 'a_hour' => ':count awola', // less reliable + + 'minute' => ':count awola', // less reliable + 'min' => ':count awola', // less reliable + 'a_minute' => ':count awola', // less reliable + */ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fa.php b/vendor/nesbot/carbon/src/Carbon/Lang/fa.php new file mode 100644 index 0000000..72e0308 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fa.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - François B + * - Nasser Ghiasi + * - JD Isaacks + * - Hossein Jabbari + * - nimamo + * - hafezdivandari + * - Hassan Pezeshk (hpez) + */ +return [ + 'year' => ':count سال', + 'a_year' => 'یک سال'.'|:count '.'سال', + 'y' => ':count سال', + 'month' => ':count ماه', + 'a_month' => 'یک ماه'.'|:count '.'ماه', + 'm' => ':count ماه', + 'week' => ':count هفته', + 'a_week' => 'یک هفته'.'|:count '.'هفته', + 'w' => ':count هفته', + 'day' => ':count روز', + 'a_day' => 'یک روز'.'|:count '.'روز', + 'd' => ':count روز', + 'hour' => ':count ساعت', + 'a_hour' => 'یک ساعت'.'|:count '.'ساعت', + 'h' => ':count ساعت', + 'minute' => ':count دقیقه', + 'a_minute' => 'یک دقیقه'.'|:count '.'دقیقه', + 'min' => ':count دقیقه', + 'second' => ':count ثانیه', + 's' => ':count ثانیه', + 'ago' => ':time پیش', + 'from_now' => ':time دیگر', + 'after' => ':time پس از', + 'before' => ':time پیش از', + 'diff_now' => 'اکنون', + 'diff_today' => 'امروز', + 'diff_today_regexp' => 'امروز(?:\\s+ساعت)?', + 'diff_yesterday' => 'دیروز', + 'diff_yesterday_regexp' => 'دیروز(?:\\s+ساعت)?', + 'diff_tomorrow' => 'فردا', + 'diff_tomorrow_regexp' => 'فردا(?:\\s+ساعت)?', + 'formats' => [ + 'LT' => 'OH:Om', + 'LTS' => 'OH:Om:Os', + 'L' => 'OD/OM/OY', + 'LL' => 'OD MMMM OY', + 'LLL' => 'OD MMMM OY OH:Om', + 'LLLL' => 'dddd, OD MMMM OY OH:Om', + ], + 'calendar' => [ + 'sameDay' => '[امروز ساعت] LT', + 'nextDay' => '[فردا ساعت] LT', + 'nextWeek' => 'dddd [ساعت] LT', + 'lastDay' => '[دیروز ساعت] LT', + 'lastWeek' => 'dddd [پیش] [ساعت] LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':timeم', + 'meridiem' => ['قبل از ظهر', 'بعد از ظهر'], + 'months' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر', 'نوامبر', 'دسامبر'], + 'months_short' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر', 'نوامبر', 'دسامبر'], + 'weekdays' => ['یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'], + 'weekdays_short' => ['یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'], + 'weekdays_min' => ['ی', 'د', 'س', 'چ', 'پ', 'ج', 'ش'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'list' => ['، ', ' و '], + 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰۴', '۰۵', '۰۶', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱۴', '۱۵', '۱۶', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲۴', '۲۵', '۲۶', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳۴', '۳۵', '۳۶', '۳۷', '۳۸', '۳۹', '۴۰', '۴۱', '۴۲', '۴۳', '۴۴', '۴۵', '۴۶', '۴۷', '۴۸', '۴۹', '۵۰', '۵۱', '۵۲', '۵۳', '۵۴', '۵۵', '۵۶', '۵۷', '۵۸', '۵۹', '۶۰', '۶۱', '۶۲', '۶۳', '۶۴', '۶۵', '۶۶', '۶۷', '۶۸', '۶۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷۴', '۷۵', '۷۶', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸۴', '۸۵', '۸۶', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹۴', '۹۵', '۹۶', '۹۷', '۹۸', '۹۹'], + 'months_short_standalone' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر', 'نوامبر', 'دسامبر'], + 'weekend' => [5, 5], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fa_AF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fa_AF.php new file mode 100644 index 0000000..6947100 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fa_AF.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fa.php', [ + 'meridiem' => ['ق', 'ب'], + 'weekend' => [4, 5], + 'formats' => [ + 'L' => 'OY/OM/OD', + 'LL' => 'OD MMM OY', + 'LLL' => 'OD MMMM OY،‏ H:mm', + 'LLLL' => 'dddd OD MMMM OY،‏ H:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fa_IR.php b/vendor/nesbot/carbon/src/Carbon/Lang/fa_IR.php new file mode 100644 index 0000000..08d0182 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fa_IR.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fa.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ff.php b/vendor/nesbot/carbon/src/Carbon/Lang/ff.php new file mode 100644 index 0000000..9525c95 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ff.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'months' => ['siilo', 'colte', 'mbooy', 'seeɗto', 'duujal', 'korse', 'morso', 'juko', 'siilto', 'yarkomaa', 'jolal', 'bowte'], + 'months_short' => ['sii', 'col', 'mbo', 'see', 'duu', 'kor', 'mor', 'juk', 'slt', 'yar', 'jol', 'bow'], + 'weekdays' => ['dewo', 'aaɓnde', 'mawbaare', 'njeslaare', 'naasaande', 'mawnde', 'hoore-biir'], + 'weekdays_short' => ['dew', 'aaɓ', 'maw', 'nje', 'naa', 'mwd', 'hbi'], + 'weekdays_min' => ['dew', 'aaɓ', 'maw', 'nje', 'naa', 'mwd', 'hbi'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['subaka', 'kikiiɗe'], + + 'year' => ':count baret', // less reliable + 'y' => ':count baret', // less reliable + 'a_year' => ':count baret', // less reliable + + 'month' => ':count lewru', // less reliable + 'm' => ':count lewru', // less reliable + 'a_month' => ':count lewru', // less reliable + + 'week' => ':count naange', // less reliable + 'w' => ':count naange', // less reliable + 'a_week' => ':count naange', // less reliable + + 'day' => ':count dian', // less reliable + 'd' => ':count dian', // less reliable + 'a_day' => ':count dian', // less reliable + + 'hour' => ':count montor', // less reliable + 'h' => ':count montor', // less reliable + 'a_hour' => ':count montor', // less reliable + + 'minute' => ':count tokossuoum', // less reliable + 'min' => ':count tokossuoum', // less reliable + 'a_minute' => ':count tokossuoum', // less reliable + + 'second' => ':count tenen', // less reliable + 's' => ':count tenen', // less reliable + 'a_second' => ':count tenen', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ff_CM.php b/vendor/nesbot/carbon/src/Carbon/Lang/ff_CM.php new file mode 100644 index 0000000..b797ac0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ff_CM.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ff.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ff_GN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ff_GN.php new file mode 100644 index 0000000..b797ac0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ff_GN.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ff.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ff_MR.php b/vendor/nesbot/carbon/src/Carbon/Lang/ff_MR.php new file mode 100644 index 0000000..2f4c29f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ff_MR.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ff.php', [ + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd D MMMM YYYY h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ff_SN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ff_SN.php new file mode 100644 index 0000000..1e4c8b6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ff_SN.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Pular-Fulfulde.org Ibrahima Sarr admin@pulaar-fulfulde.org + */ +return require __DIR__.'/ff.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fi.php b/vendor/nesbot/carbon/src/Carbon/Lang/fi.php new file mode 100644 index 0000000..edf2d6d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fi.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Philippe Vaucher + * - Janne Warén + * - digitalfrost + * - Tsutomu Kuroda + * - Roope Salmi + * - tjku + * - Max Melentiev + * - Sami Haahtinen + * - Teemu Leisti + * - Artem Ignatyev + * - Akira Matsuda + * - Christopher Dell + * - Enrique Vidal + * - Simone Carletti + * - Robert Bjarnason + * - Aaron Patterson + * - Nicolás Hock Isaza + * - Tom Hughes + * - Sven Fuchs + * - Petri Kivikangas + * - Nizar Jouini + * - Marko Seppae + * - Tomi Mynttinen (Pikseli) + * - Petteri (powergrip) + */ +return [ + 'year' => ':count vuosi|:count vuotta', + 'y' => ':count v', + 'month' => ':count kuukausi|:count kuukautta', + 'm' => ':count kk', + 'week' => ':count viikko|:count viikkoa', + 'w' => ':count vk', + 'day' => ':count päivä|:count päivää', + 'd' => ':count pv', + 'hour' => ':count tunti|:count tuntia', + 'h' => ':count t', + 'minute' => ':count minuutti|:count minuuttia', + 'min' => ':count min', + 'second' => ':count sekunti|:count sekuntia', + 'a_second' => 'muutama sekunti|:count sekuntia', + 's' => ':count s', + 'ago' => ':time sitten', + 'from_now' => ':time päästä', + 'year_from_now' => ':count vuoden', + 'month_from_now' => ':count kuukauden', + 'week_from_now' => ':count viikon', + 'day_from_now' => ':count päivän', + 'hour_from_now' => ':count tunnin', + 'minute_from_now' => ':count minuutin', + 'second_from_now' => ':count sekunnin', + 'after' => ':time sen jälkeen', + 'before' => ':time ennen', + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' ja '], + 'diff_now' => 'nyt', + 'diff_yesterday' => 'eilen', + 'diff_tomorrow' => 'huomenna', + 'formats' => [ + 'LT' => 'HH.mm', + 'LTS' => 'HH.mm:ss', + 'L' => 'D.M.YYYY', + 'LL' => 'dddd D. MMMM[ta] YYYY', + 'll' => 'ddd D. MMM YYYY', + 'LLL' => 'D.MM. HH.mm', + 'LLLL' => 'D. MMMM[ta] YYYY HH.mm', + 'llll' => 'D. MMM YY HH.mm', + ], + 'weekdays' => ['sunnuntai', 'maanantai', 'tiistai', 'keskiviikko', 'torstai', 'perjantai', 'lauantai'], + 'weekdays_short' => ['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la'], + 'weekdays_min' => ['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la'], + 'months' => ['tammikuu', 'helmikuu', 'maaliskuu', 'huhtikuu', 'toukokuu', 'kesäkuu', 'heinäkuu', 'elokuu', 'syyskuu', 'lokakuu', 'marraskuu', 'joulukuu'], + 'months_short' => ['tammi', 'helmi', 'maalis', 'huhti', 'touko', 'kesä', 'heinä', 'elo', 'syys', 'loka', 'marras', 'joulu'], + 'meridiem' => ['aamupäivä', 'iltapäivä'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fi_FI.php b/vendor/nesbot/carbon/src/Carbon/Lang/fi_FI.php new file mode 100644 index 0000000..920f1ca --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fi_FI.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fi.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fil.php b/vendor/nesbot/carbon/src/Carbon/Lang/fil.php new file mode 100644 index 0000000..61114e3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fil.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/fil_PH.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fil_PH.php b/vendor/nesbot/carbon/src/Carbon/Lang/fil_PH.php new file mode 100644 index 0000000..bcf1580 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fil_PH.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Rene Torres Rene Torres, Pablo Saratxaga rgtorre@rocketmail.com, pablo@mandrakesoft.com + * - Jaycee Mariano (alohajaycee) + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'MM/DD/YY', + ], + 'months' => ['Enero', 'Pebrero', 'Marso', 'Abril', 'Mayo', 'Hunyo', 'Hulyo', 'Agosto', 'Setyembre', 'Oktubre', 'Nobyembre', 'Disyembre'], + 'months_short' => ['Ene', 'Peb', 'Mar', 'Abr', 'May', 'Hun', 'Hul', 'Ago', 'Set', 'Okt', 'Nob', 'Dis'], + 'weekdays' => ['Linggo', 'Lunes', 'Martes', 'Miyerkoles', 'Huwebes', 'Biyernes', 'Sabado'], + 'weekdays_short' => ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'], + 'weekdays_min' => ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['N.U.', 'N.H.'], + + 'before' => ':time bago', + 'after' => ':time pagkatapos', + + 'year' => ':count taon', + 'y' => ':count taon', + 'a_year' => ':count taon', + + 'month' => ':count buwan', + 'm' => ':count buwan', + 'a_month' => ':count buwan', + + 'week' => ':count linggo', + 'w' => ':count linggo', + 'a_week' => ':count linggo', + + 'day' => ':count araw', + 'd' => ':count araw', + 'a_day' => ':count araw', + + 'hour' => ':count oras', + 'h' => ':count oras', + 'a_hour' => ':count oras', + + 'minute' => ':count minuto', + 'min' => ':count minuto', + 'a_minute' => ':count minuto', + + 'second' => ':count segundo', + 's' => ':count segundo', + 'a_second' => ':count segundo', + + 'ago' => ':time ang nakalipas', + 'from_now' => 'sa :time', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fo.php b/vendor/nesbot/carbon/src/Carbon/Lang/fo.php new file mode 100644 index 0000000..6a14a6f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fo.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Kristian Sakarisson + * - François B + * - JD Isaacks + * - Sverri Mohr Olsen + */ +return [ + 'year' => 'eitt ár|:count ár', + 'y' => ':count ár|:count ár', + 'month' => 'ein mánaði|:count mánaðir', + 'm' => ':count mánaður|:count mánaðir', + 'week' => ':count vika|:count vikur', + 'w' => ':count vika|:count vikur', + 'day' => 'ein dagur|:count dagar', + 'd' => ':count dag|:count dagar', + 'hour' => 'ein tími|:count tímar', + 'h' => ':count tími|:count tímar', + 'minute' => 'ein minutt|:count minuttir', + 'min' => ':count minutt|:count minuttir', + 'second' => 'fá sekund|:count sekundir', + 's' => ':count sekund|:count sekundir', + 'ago' => ':time síðani', + 'from_now' => 'um :time', + 'after' => ':time aftaná', + 'before' => ':time áðrenn', + 'diff_today' => 'Í', + 'diff_yesterday' => 'Í', + 'diff_yesterday_regexp' => 'Í(?:\\s+gjár)?(?:\\s+kl.)?', + 'diff_tomorrow' => 'Í', + 'diff_tomorrow_regexp' => 'Í(?:\\s+morgin)?(?:\\s+kl.)?', + 'diff_today_regexp' => 'Í(?:\\s+dag)?(?:\\s+kl.)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D. MMMM, YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Í dag kl.] LT', + 'nextDay' => '[Í morgin kl.] LT', + 'nextWeek' => 'dddd [kl.] LT', + 'lastDay' => '[Í gjár kl.] LT', + 'lastWeek' => '[síðstu] dddd [kl] LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'months' => ['januar', 'februar', 'mars', 'apríl', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'], + 'months_short' => ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'], + 'weekdays' => ['sunnudagur', 'mánadagur', 'týsdagur', 'mikudagur', 'hósdagur', 'fríggjadagur', 'leygardagur'], + 'weekdays_short' => ['sun', 'mán', 'týs', 'mik', 'hós', 'frí', 'ley'], + 'weekdays_min' => ['su', 'má', 'tý', 'mi', 'hó', 'fr', 'le'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' og '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fo_DK.php b/vendor/nesbot/carbon/src/Carbon/Lang/fo_DK.php new file mode 100644 index 0000000..657f2c5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fo_DK.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fo.php', [ + 'formats' => [ + 'L' => 'DD.MM.yy', + 'LL' => 'DD.MM.YYYY', + 'LLL' => 'D. MMMM YYYY, HH:mm', + 'LLLL' => 'dddd, D. MMMM YYYY, HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fo_FO.php b/vendor/nesbot/carbon/src/Carbon/Lang/fo_FO.php new file mode 100644 index 0000000..6d73616 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fo_FO.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fo.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr.php new file mode 100644 index 0000000..f4c7247 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Dieter Sting + * - François B + * - Maxime VALY + * - JD Isaacks + * - Dieter Sting + * - François B + * - JD Isaacks + * - Sebastian Thierer + * - Fastfuel + * - Pete Scopes (pdscopes) + */ +return [ + 'year' => ':count an|:count ans', + 'a_year' => 'un an|:count ans', + 'y' => ':count an|:count ans', + 'month' => ':count mois|:count mois', + 'a_month' => 'un mois|:count mois', + 'm' => ':count mois', + 'week' => ':count semaine|:count semaines', + 'a_week' => 'une semaine|:count semaines', + 'w' => ':count sem.', + 'day' => ':count jour|:count jours', + 'a_day' => 'un jour|:count jours', + 'd' => ':count j', + 'hour' => ':count heure|:count heures', + 'a_hour' => 'une heure|:count heures', + 'h' => ':count h', + 'minute' => ':count minute|:count minutes', + 'a_minute' => 'une minute|:count minutes', + 'min' => ':count min', + 'second' => ':count seconde|:count secondes', + 'a_second' => 'quelques secondes|:count secondes', + 's' => ':count s', + 'millisecond' => ':count milliseconde|:count millisecondes', + 'a_millisecond' => 'une milliseconde|:count millisecondes', + 'ms' => ':countms', + 'microsecond' => ':count microseconde|:count microsecondes', + 'a_microsecond' => 'une microseconde|:count microsecondes', + 'µs' => ':countµs', + 'ago' => 'il y a :time', + 'from_now' => 'dans :time', + 'after' => ':time après', + 'before' => ':time avant', + 'diff_now' => "à l'instant", + 'diff_today' => "aujourd'hui", + 'diff_today_regexp' => "aujourd'hui(?:\s+à)?", + 'diff_yesterday' => 'hier', + 'diff_yesterday_regexp' => 'hier(?:\s+à)?', + 'diff_tomorrow' => 'demain', + 'diff_tomorrow_regexp' => 'demain(?:\s+à)?', + 'diff_before_yesterday' => 'avant-hier', + 'diff_after_tomorrow' => 'après-demain', + 'period_recurrences' => ':count fois', + 'period_interval' => 'tous les :interval', + 'period_start_date' => 'de :date', + 'period_end_date' => 'à :date', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Aujourd’hui à] LT', + 'nextDay' => '[Demain à] LT', + 'nextWeek' => 'dddd [à] LT', + 'lastDay' => '[Hier à] LT', + 'lastWeek' => 'dddd [dernier à] LT', + 'sameElse' => 'L', + ], + 'months' => ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'], + 'months_short' => ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'], + 'weekdays' => ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'], + 'weekdays_short' => ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'], + 'weekdays_min' => ['di', 'lu', 'ma', 'me', 'je', 've', 'sa'], + 'ordinal' => function ($number, $period) { + switch ($period) { + // In French, only the first has to be ordinal, other number remains cardinal + // @link https://fr.wikihow.com/%C3%A9crire-la-date-en-fran%C3%A7ais + case 'D': + return $number.($number === 1 ? 'er' : ''); + + default: + case 'M': + case 'Q': + case 'DDD': + case 'd': + return $number.($number === 1 ? 'er' : 'e'); + + // Words with feminine grammatical gender: semaine + case 'w': + case 'W': + return $number.($number === 1 ? 're' : 'e'); + } + }, + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' et '], + 'ordinal_words' => [ + 'of' => 'de', + 'first' => 'premier', + 'second' => 'deuxième', + 'third' => 'troisième', + 'fourth' => 'quatrième', + 'fifth' => 'cinquième', + 'last' => 'dernier', + ], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_BE.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BE.php new file mode 100644 index 0000000..f6cafe8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BE.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'months_short' => ['jan', 'fév', 'mar', 'avr', 'mai', 'jun', 'jui', 'aoû', 'sep', 'oct', 'nov', 'déc'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_BF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BF.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BF.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_BI.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BI.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BI.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_BJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BJ.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BJ.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_BL.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BL.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BL.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CA.php new file mode 100644 index 0000000..c9f6346 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CA.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Dieter Sting + * - François B + * - Maxime VALY + * - JD Isaacks + */ +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'formats' => [ + 'L' => 'YYYY-MM-DD', + ], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CD.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CD.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CD.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CF.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CF.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CG.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CG.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CG.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CH.php new file mode 100644 index 0000000..8674c27 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CH.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Dieter Sting + * - François B + * - Gaspard Bucher + * - Maxime VALY + * - JD Isaacks + */ +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CI.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CI.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CI.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CM.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CM.php new file mode 100644 index 0000000..67d3787 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CM.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'meridiem' => ['mat.', 'soir'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_DJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_DJ.php new file mode 100644 index 0000000..2f06086 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_DJ.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'first_day_of_week' => 6, + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd D MMMM YYYY h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_DZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_DZ.php new file mode 100644 index 0000000..ae8db5f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_DZ.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'first_day_of_week' => 6, + 'weekend' => [5, 6], + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd D MMMM YYYY h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_FR.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_FR.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_FR.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_GA.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GA.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GA.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_GF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GF.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GF.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_GN.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GN.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GN.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_GP.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GP.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GP.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_GQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GQ.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GQ.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_HT.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_HT.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_HT.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_KM.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_KM.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_KM.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_LU.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_LU.php new file mode 100644 index 0000000..8e37d85 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_LU.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months_short' => ['jan', 'fév', 'mar', 'avr', 'mai', 'jun', 'jui', 'aoû', 'sep', 'oct', 'nov', 'déc'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MA.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MA.php new file mode 100644 index 0000000..1bf034d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MA.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'first_day_of_week' => 6, + 'weekend' => [5, 6], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MC.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MC.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MC.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MF.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MF.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MG.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MG.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MG.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_ML.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_ML.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_ML.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MQ.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MQ.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MR.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MR.php new file mode 100644 index 0000000..37cf83f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MR.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd D MMMM YYYY h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MU.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MU.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MU.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_NC.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_NC.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_NC.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_NE.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_NE.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_NE.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_PF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_PF.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_PF.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_PM.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_PM.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_PM.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_RE.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_RE.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_RE.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_RW.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_RW.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_RW.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_SC.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_SC.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_SC.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_SN.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_SN.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_SN.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_SY.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_SY.php new file mode 100644 index 0000000..ae8db5f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_SY.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'first_day_of_week' => 6, + 'weekend' => [5, 6], + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd D MMMM YYYY h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_TD.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_TD.php new file mode 100644 index 0000000..37cf83f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_TD.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd D MMMM YYYY h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_TG.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_TG.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_TG.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_TN.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_TN.php new file mode 100644 index 0000000..6905e7a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_TN.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'weekend' => [5, 6], + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd D MMMM YYYY h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_VU.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_VU.php new file mode 100644 index 0000000..37cf83f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_VU.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fr.php', [ + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd D MMMM YYYY h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_WF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_WF.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_WF.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_YT.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_YT.php new file mode 100644 index 0000000..ec3ee35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_YT.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/fr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fur.php b/vendor/nesbot/carbon/src/Carbon/Lang/fur.php new file mode 100644 index 0000000..36c2564 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fur.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/fur_IT.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fur_IT.php b/vendor/nesbot/carbon/src/Carbon/Lang/fur_IT.php new file mode 100644 index 0000000..0147a59 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fur_IT.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Pablo Saratxaga pablo@mandrakesoft.com + */ +return [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD. MM. YY', + 'LL' => 'DD di MMMM dal YYYY', + 'LLL' => 'DD di MMM HH:mm', + 'LLLL' => 'DD di MMMM dal YYYY HH:mm', + ], + 'months' => ['zenâr', 'fevrâr', 'març', 'avrîl', 'mai', 'jugn', 'lui', 'avost', 'setembar', 'otubar', 'novembar', 'dicembar'], + 'months_short' => ['zen', 'fev', 'mar', 'avr', 'mai', 'jug', 'lui', 'avo', 'set', 'otu', 'nov', 'dic'], + 'weekdays' => ['domenie', 'lunis', 'martars', 'miercus', 'joibe', 'vinars', 'sabide'], + 'weekdays_short' => ['dom', 'lun', 'mar', 'mie', 'joi', 'vin', 'sab'], + 'weekdays_min' => ['dom', 'lun', 'mar', 'mie', 'joi', 'vin', 'sab'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'year' => ':count an', + 'month' => ':count mês', + 'week' => ':count setemane', + 'day' => ':count zornade', + 'hour' => ':count ore', + 'minute' => ':count minût', + 'second' => ':count secont', +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fy.php b/vendor/nesbot/carbon/src/Carbon/Lang/fy.php new file mode 100644 index 0000000..c1b5439 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fy.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Tim Fish + * - JD Isaacks + */ +return [ + 'year' => ':count jier|:count jierren', + 'a_year' => 'ien jier|:count jierren', + 'y' => ':count j', + 'month' => ':count moanne|:count moannen', + 'a_month' => 'ien moanne|:count moannen', + 'm' => ':count moa.', + 'week' => ':count wike|:count wiken', + 'a_week' => 'in wike|:count wiken', + 'a' => ':count w.', + 'day' => ':count dei|:count dagen', + 'a_day' => 'ien dei|:count dagen', + 'd' => ':count d.', + 'hour' => ':count oere|:count oeren', + 'a_hour' => 'ien oere|:count oeren', + 'h' => ':count o.', + 'minute' => ':count minút|:count minuten', + 'a_minute' => 'ien minút|:count minuten', + 'min' => ':count min.', + 'second' => ':count sekonde|:count sekonden', + 'a_second' => 'in pear sekonden|:count sekonden', + 's' => ':count s.', + 'ago' => ':time lyn', + 'from_now' => 'oer :time', + 'diff_yesterday' => 'juster', + 'diff_yesterday_regexp' => 'juster(?:\\s+om)?', + 'diff_today' => 'hjoed', + 'diff_today_regexp' => 'hjoed(?:\\s+om)?', + 'diff_tomorrow' => 'moarn', + 'diff_tomorrow_regexp' => 'moarn(?:\\s+om)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD-MM-YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[hjoed om] LT', + 'nextDay' => '[moarn om] LT', + 'nextWeek' => 'dddd [om] LT', + 'lastDay' => '[juster om] LT', + 'lastWeek' => '[ôfrûne] dddd [om] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + return $number.(($number === 1 || $number === 8 || $number >= 20) ? 'ste' : 'de'); + }, + 'months' => ['jannewaris', 'febrewaris', 'maart', 'april', 'maaie', 'juny', 'july', 'augustus', 'septimber', 'oktober', 'novimber', 'desimber'], + 'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'], + 'mmm_suffix' => '.', + 'weekdays' => ['snein', 'moandei', 'tiisdei', 'woansdei', 'tongersdei', 'freed', 'sneon'], + 'weekdays_short' => ['si.', 'mo.', 'ti.', 'wo.', 'to.', 'fr.', 'so.'], + 'weekdays_min' => ['Si', 'Mo', 'Ti', 'Wo', 'To', 'Fr', 'So'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' en '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fy_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/fy_DE.php new file mode 100644 index 0000000..8559d5c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fy_DE.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - information from Kenneth Christiansen Kenneth Christiansen, Pablo Saratxaga kenneth@gnu.org, pablo@mandriva.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['Jaunuwoa', 'Februwoa', 'Moaz', 'Aprell', 'Mai', 'Juni', 'Juli', 'August', 'Septamba', 'Oktoba', 'Nowamba', 'Dezamba'], + 'months_short' => ['Jan', 'Feb', 'Moz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Now', 'Dez'], + 'weekdays' => ['Sinndag', 'Mondag', 'Dingsdag', 'Meddwäakj', 'Donnadag', 'Friedag', 'Sinnowend'], + 'weekdays_short' => ['Sdg', 'Mdg', 'Dsg', 'Mwk', 'Ddg', 'Fdg', 'Swd'], + 'weekdays_min' => ['Sdg', 'Mdg', 'Dsg', 'Mwk', 'Ddg', 'Fdg', 'Swd'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fy_NL.php b/vendor/nesbot/carbon/src/Carbon/Lang/fy_NL.php new file mode 100644 index 0000000..01cc96c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fy_NL.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/fy.php', [ + 'formats' => [ + 'L' => 'DD-MM-YY', + ], + 'months' => ['Jannewaris', 'Febrewaris', 'Maart', 'April', 'Maaie', 'Juny', 'July', 'Augustus', 'Septimber', 'Oktober', 'Novimber', 'Desimber'], + 'months_short' => ['Jan', 'Feb', 'Mrt', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'], + 'weekdays' => ['Snein', 'Moandei', 'Tiisdei', 'Woansdei', 'Tongersdei', 'Freed', 'Sneon'], + 'weekdays_short' => ['Sn', 'Mo', 'Ti', 'Wo', 'To', 'Fr', 'Sn'], + 'weekdays_min' => ['Sn', 'Mo', 'Ti', 'Wo', 'To', 'Fr', 'Sn'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ga.php b/vendor/nesbot/carbon/src/Carbon/Lang/ga.php new file mode 100644 index 0000000..9f07a26 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ga.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Thanks to André Silva : https://github.com/askpt + */ + +return [ + 'year' => ':count bliain', + 'a_year' => '{1}bliain|:count bliain', + 'y' => ':countb', + 'month' => ':count mí', + 'a_month' => '{1}mí|:count mí', + 'm' => ':countm', + 'week' => ':count sheachtain', + 'a_week' => '{1}sheachtain|:count sheachtain', + 'w' => ':countsh', + 'day' => ':count lá', + 'a_day' => '{1}lá|:count lá', + 'd' => ':countl', + 'hour' => ':count uair an chloig', + 'a_hour' => '{1}uair an chloig|:count uair an chloig', + 'h' => ':countu', + 'minute' => ':count nóiméad', + 'a_minute' => '{1}nóiméad|:count nóiméad', + 'min' => ':countn', + 'second' => ':count soicind', + 'a_second' => '{1}cúpla soicind|:count soicind', + 's' => ':countso', + 'ago' => ':time ó shin', + 'from_now' => 'i :time', + 'after' => ':time tar éis', + 'before' => ':time roimh', + 'diff_now' => 'anois', + 'diff_today' => 'Inniu', + 'diff_today_regexp' => 'Inniu(?:\\s+ag)?', + 'diff_yesterday' => 'inné', + 'diff_yesterday_regexp' => 'Inné(?:\\s+aig)?', + 'diff_tomorrow' => 'amárach', + 'diff_tomorrow_regexp' => 'Amárach(?:\\s+ag)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Inniu ag] LT', + 'nextDay' => '[Amárach ag] LT', + 'nextWeek' => 'dddd [ag] LT', + 'lastDay' => '[Inné aig] LT', + 'lastWeek' => 'dddd [seo caite] [ag] LT', + 'sameElse' => 'L', + ], + 'months' => ['Eanáir', 'Feabhra', 'Márta', 'Aibreán', 'Bealtaine', 'Méitheamh', 'Iúil', 'Lúnasa', 'Meán Fómhair', 'Deaireadh Fómhair', 'Samhain', 'Nollaig'], + 'months_short' => ['Eaná', 'Feab', 'Márt', 'Aibr', 'Beal', 'Méit', 'Iúil', 'Lúna', 'Meán', 'Deai', 'Samh', 'Noll'], + 'weekdays' => ['Dé Domhnaigh', 'Dé Luain', 'Dé Máirt', 'Dé Céadaoin', 'Déardaoin', 'Dé hAoine', 'Dé Satharn'], + 'weekdays_short' => ['Dom', 'Lua', 'Mái', 'Céa', 'Déa', 'hAo', 'Sat'], + 'weekdays_min' => ['Do', 'Lu', 'Má', 'Ce', 'Dé', 'hA', 'Sa'], + 'ordinal' => function ($number) { + return $number.($number === 1 ? 'd' : ($number % 10 === 2 ? 'na' : 'mh')); + }, + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' agus '], + 'meridiem' => ['r.n.', 'i.n.'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ga_IE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ga_IE.php new file mode 100644 index 0000000..57b0c4f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ga_IE.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ga.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gd.php b/vendor/nesbot/carbon/src/Carbon/Lang/gd.php new file mode 100644 index 0000000..63d064d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gd.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Jon Ashdown + */ +return [ + 'year' => ':count bliadhna', + 'a_year' => '{1}bliadhna|:count bliadhna', + 'y' => ':count b.', + 'month' => ':count mìosan', + 'a_month' => '{1}mìos|:count mìosan', + 'm' => ':count ms.', + 'week' => ':count seachdainean', + 'a_week' => '{1}seachdain|:count seachdainean', + 'w' => ':count s.', + 'day' => ':count latha', + 'a_day' => '{1}latha|:count latha', + 'd' => ':count l.', + 'hour' => ':count uairean', + 'a_hour' => '{1}uair|:count uairean', + 'h' => ':count u.', + 'minute' => ':count mionaidean', + 'a_minute' => '{1}mionaid|:count mionaidean', + 'min' => ':count md.', + 'second' => ':count diogan', + 'a_second' => '{1}beagan diogan|:count diogan', + 's' => ':count d.', + 'ago' => 'bho chionn :time', + 'from_now' => 'ann an :time', + 'diff_yesterday' => 'An-dè', + 'diff_yesterday_regexp' => 'An-dè(?:\\s+aig)?', + 'diff_today' => 'An-diugh', + 'diff_today_regexp' => 'An-diugh(?:\\s+aig)?', + 'diff_tomorrow' => 'A-màireach', + 'diff_tomorrow_regexp' => 'A-màireach(?:\\s+aig)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[An-diugh aig] LT', + 'nextDay' => '[A-màireach aig] LT', + 'nextWeek' => 'dddd [aig] LT', + 'lastDay' => '[An-dè aig] LT', + 'lastWeek' => 'dddd [seo chaidh] [aig] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + return $number.($number === 1 ? 'd' : ($number % 10 === 2 ? 'na' : 'mh')); + }, + 'months' => ['Am Faoilleach', 'An Gearran', 'Am Màrt', 'An Giblean', 'An Cèitean', 'An t-Ògmhios', 'An t-Iuchar', 'An Lùnastal', 'An t-Sultain', 'An Dàmhair', 'An t-Samhain', 'An Dùbhlachd'], + 'months_short' => ['Faoi', 'Gear', 'Màrt', 'Gibl', 'Cèit', 'Ògmh', 'Iuch', 'Lùn', 'Sult', 'Dàmh', 'Samh', 'Dùbh'], + 'weekdays' => ['Didòmhnaich', 'Diluain', 'Dimàirt', 'Diciadain', 'Diardaoin', 'Dihaoine', 'Disathairne'], + 'weekdays_short' => ['Did', 'Dil', 'Dim', 'Dic', 'Dia', 'Dih', 'Dis'], + 'weekdays_min' => ['Dò', 'Lu', 'Mà', 'Ci', 'Ar', 'Ha', 'Sa'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' agus '], + 'meridiem' => ['m', 'f'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gd_GB.php b/vendor/nesbot/carbon/src/Carbon/Lang/gd_GB.php new file mode 100644 index 0000000..4fc26b3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gd_GB.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/gd.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gez.php b/vendor/nesbot/carbon/src/Carbon/Lang/gez.php new file mode 100644 index 0000000..b8a2f0e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gez.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/gez_ER.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gez_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/gez_ER.php new file mode 100644 index 0000000..f19d1df --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gez_ER.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['ጠሐረ', 'ከተተ', 'መገበ', 'አኀዘ', 'ግንባት', 'ሠንየ', 'ሐመለ', 'ነሐሰ', 'ከረመ', 'ጠቀመ', 'ኀደረ', 'ኀሠሠ'], + 'months_short' => ['ጠሐረ', 'ከተተ', 'መገበ', 'አኀዘ', 'ግንባ', 'ሠንየ', 'ሐመለ', 'ነሐሰ', 'ከረመ', 'ጠቀመ', 'ኀደረ', 'ኀሠሠ'], + 'weekdays' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚት'], + 'weekdays_short' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚ'], + 'weekdays_min' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚ'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ጽባሕ', 'ምሴት'], + + 'month' => ':count ወርሕ', // less reliable + 'm' => ':count ወርሕ', // less reliable + 'a_month' => ':count ወርሕ', // less reliable + + 'week' => ':count ሰብዑ', // less reliable + 'w' => ':count ሰብዑ', // less reliable + 'a_week' => ':count ሰብዑ', // less reliable + + 'hour' => ':count አንትሙ', // less reliable + 'h' => ':count አንትሙ', // less reliable + 'a_hour' => ':count አንትሙ', // less reliable + + 'minute' => ':count ንኡስ', // less reliable + 'min' => ':count ንኡስ', // less reliable + 'a_minute' => ':count ንኡስ', // less reliable + + 'year' => ':count ዓመት', + 'y' => ':count ዓመት', + 'a_year' => ':count ዓመት', + + 'day' => ':count ዕለት', + 'd' => ':count ዕለት', + 'a_day' => ':count ዕለት', + + 'second' => ':count ካልእ', + 's' => ':count ካልእ', + 'a_second' => ':count ካልእ', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gez_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/gez_ET.php new file mode 100644 index 0000000..3933009 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gez_ET.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች', 'ኤፕረል', 'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት', 'ሴፕቴምበር', 'ኦክተውበር', 'ኖቬምበር', 'ዲሴምበር'], + 'months_short' => ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕረ', 'ሜይ ', 'ጁን ', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክተ', 'ኖቬም', 'ዲሴም'], + 'weekdays' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚት'], + 'weekdays_short' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚ'], + 'weekdays_min' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚ'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ጽባሕ', 'ምሴት'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gl.php b/vendor/nesbot/carbon/src/Carbon/Lang/gl.php new file mode 100644 index 0000000..088b0f2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gl.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Fidel Pita + * - JD Isaacks + * - Diego Vilariño + * - Sebastian Thierer + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count ano|:count anos', + 'a_year' => 'un ano|:count anos', + 'y' => ':count a.', + 'month' => ':count mes|:count meses', + 'a_month' => 'un mes|:count meses', + 'm' => ':count mes.', + 'week' => ':count semana|:count semanas', + 'a_week' => 'unha semana|:count semanas', + 'w' => ':count sem.', + 'day' => ':count día|:count días', + 'a_day' => 'un día|:count días', + 'd' => ':count d.', + 'hour' => ':count hora|:count horas', + 'a_hour' => 'unha hora|:count horas', + 'h' => ':count h.', + 'minute' => ':count minuto|:count minutos', + 'a_minute' => 'un minuto|:count minutos', + 'min' => ':count min.', + 'second' => ':count segundo|:count segundos', + 'a_second' => 'uns segundos|:count segundos', + 's' => ':count seg.', + 'ago' => 'hai :time', + 'from_now' => function ($time) { + if (str_starts_with($time, 'un')) { + return "n$time"; + } + + return "en $time"; + }, + 'diff_now' => 'agora', + 'diff_today' => 'hoxe', + 'diff_today_regexp' => 'hoxe(?:\\s+ás)?', + 'diff_yesterday' => 'onte', + 'diff_yesterday_regexp' => 'onte(?:\\s+á)?', + 'diff_tomorrow' => 'mañá', + 'diff_tomorrow_regexp' => 'mañá(?:\\s+ás)?', + 'after' => ':time despois', + 'before' => ':time antes', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D [de] MMMM [de] YYYY', + 'LLL' => 'D [de] MMMM [de] YYYY H:mm', + 'LLLL' => 'dddd, D [de] MMMM [de] YYYY H:mm', + ], + 'calendar' => [ + 'sameDay' => function (CarbonInterface $current) { + return '[hoxe '.($current->hour !== 1 ? 'ás' : 'á').'] LT'; + }, + 'nextDay' => function (CarbonInterface $current) { + return '[mañá '.($current->hour !== 1 ? 'ás' : 'á').'] LT'; + }, + 'nextWeek' => function (CarbonInterface $current) { + return 'dddd ['.($current->hour !== 1 ? 'ás' : 'á').'] LT'; + }, + 'lastDay' => function (CarbonInterface $current) { + return '[onte '.($current->hour !== 1 ? 'á' : 'a').'] LT'; + }, + 'lastWeek' => function (CarbonInterface $current) { + return '[o] dddd [pasado '.($current->hour !== 1 ? 'ás' : 'á').'] LT'; + }, + 'sameElse' => 'L', + ], + 'ordinal' => ':numberº', + 'months' => ['xaneiro', 'febreiro', 'marzo', 'abril', 'maio', 'xuño', 'xullo', 'agosto', 'setembro', 'outubro', 'novembro', 'decembro'], + 'months_short' => ['xan.', 'feb.', 'mar.', 'abr.', 'mai.', 'xuñ.', 'xul.', 'ago.', 'set.', 'out.', 'nov.', 'dec.'], + 'weekdays' => ['domingo', 'luns', 'martes', 'mércores', 'xoves', 'venres', 'sábado'], + 'weekdays_short' => ['dom.', 'lun.', 'mar.', 'mér.', 'xov.', 'ven.', 'sáb.'], + 'weekdays_min' => ['do', 'lu', 'ma', 'mé', 'xo', 've', 'sá'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' e '], + 'meridiem' => ['a.m.', 'p.m.'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gl_ES.php b/vendor/nesbot/carbon/src/Carbon/Lang/gl_ES.php new file mode 100644 index 0000000..9d6c1d9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gl_ES.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/gl.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gom.php b/vendor/nesbot/carbon/src/Carbon/Lang/gom.php new file mode 100644 index 0000000..2a0584f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gom.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/gom_Latn.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gom_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/gom_Latn.php new file mode 100644 index 0000000..612bb88 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gom_Latn.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'year' => ':count voros|:count vorsam', + 'y' => ':countv', + 'month' => ':count mhoino|:count mhoine', + 'm' => ':countmh', + 'week' => ':count satolleacho|:count satolleache', + 'w' => ':countsa|:countsa', + 'day' => ':count dis', + 'd' => ':countd', + 'hour' => ':count hor|:count horam', + 'h' => ':counth', + 'minute' => ':count minute|:count mintam', + 'min' => ':countm', + 'second' => ':count second', + 's' => ':counts', + + 'diff_today' => 'Aiz', + 'diff_yesterday' => 'Kal', + 'diff_tomorrow' => 'Faleam', + 'formats' => [ + 'LT' => 'A h:mm [vazta]', + 'LTS' => 'A h:mm:ss [vazta]', + 'L' => 'DD-MM-YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY A h:mm [vazta]', + 'LLLL' => 'dddd, MMMM[achea] Do, YYYY, A h:mm [vazta]', + 'llll' => 'ddd, D MMM YYYY, A h:mm [vazta]', + ], + + 'calendar' => [ + 'sameDay' => '[Aiz] LT', + 'nextDay' => '[Faleam] LT', + 'nextWeek' => '[Ieta to] dddd[,] LT', + 'lastDay' => '[Kal] LT', + 'lastWeek' => '[Fatlo] dddd[,] LT', + 'sameElse' => 'L', + ], + + 'months' => ['Janer', 'Febrer', 'Mars', 'Abril', 'Mai', 'Jun', 'Julai', 'Agost', 'Setembr', 'Otubr', 'Novembr', 'Dezembr'], + 'months_short' => ['Jan.', 'Feb.', 'Mars', 'Abr.', 'Mai', 'Jun', 'Jul.', 'Ago.', 'Set.', 'Otu.', 'Nov.', 'Dez.'], + 'weekdays' => ['Aitar', 'Somar', 'Mongllar', 'Budvar', 'Brestar', 'Sukrar', 'Son\'var'], + 'weekdays_short' => ['Ait.', 'Som.', 'Mon.', 'Bud.', 'Bre.', 'Suk.', 'Son.'], + 'weekdays_min' => ['Ai', 'Sm', 'Mo', 'Bu', 'Br', 'Su', 'Sn'], + + 'ordinal' => function ($number, $period) { + return $number.($period === 'D' ? 'er' : ''); + }, + + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'rati'; + } + if ($hour < 12) { + return 'sokalli'; + } + if ($hour < 16) { + return 'donparam'; + } + if ($hour < 20) { + return 'sanje'; + } + + return 'rati'; + }, + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' ani '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gsw.php b/vendor/nesbot/carbon/src/Carbon/Lang/gsw.php new file mode 100644 index 0000000..c5c850e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gsw.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Christopher Dell + * - Akira Matsuda + * - Enrique Vidal + * - Simone Carletti + * - Henning Kiel + * - Aaron Patterson + * - Florian Hanke + */ +return [ + 'year' => ':count Johr', + 'month' => ':count Monet', + 'week' => ':count Woche', + 'day' => ':count Tag', + 'hour' => ':count Schtund', + 'minute' => ':count Minute', + 'second' => ':count Sekunde', + 'weekdays' => ['Sunntig', 'Mäntig', 'Ziischtig', 'Mittwuch', 'Dunschtig', 'Friitig', 'Samschtig'], + 'weekdays_short' => ['Su', 'Mä', 'Zi', 'Mi', 'Du', 'Fr', 'Sa'], + 'weekdays_min' => ['Su', 'Mä', 'Zi', 'Mi', 'Du', 'Fr', 'Sa'], + 'months' => ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'Auguscht', 'September', 'Oktober', 'November', 'Dezember'], + 'months_short' => ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'], + 'meridiem' => ['am Vormittag', 'am Namittag'], + 'ordinal' => ':number.', + 'list' => [', ', ' und '], + 'diff_now' => 'now', + 'diff_yesterday' => 'geschter', + 'diff_tomorrow' => 'moorn', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'Do MMMM YYYY', + 'LLL' => 'Do MMMM, HH:mm [Uhr]', + 'LLLL' => 'dddd, Do MMMM YYYY, HH:mm [Uhr]', + ], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gsw_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/gsw_CH.php new file mode 100644 index 0000000..594eb25 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gsw_CH.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/gsw.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gsw_FR.php b/vendor/nesbot/carbon/src/Carbon/Lang/gsw_FR.php new file mode 100644 index 0000000..3581dcf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gsw_FR.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/gsw.php', [ + 'meridiem' => ['vorm.', 'nam.'], + 'months' => ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'Auguscht', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LLL' => 'Do MMMM YYYY HH:mm', + 'LLLL' => 'dddd, Do MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gsw_LI.php b/vendor/nesbot/carbon/src/Carbon/Lang/gsw_LI.php new file mode 100644 index 0000000..3581dcf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gsw_LI.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/gsw.php', [ + 'meridiem' => ['vorm.', 'nam.'], + 'months' => ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'Auguscht', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LLL' => 'Do MMMM YYYY HH:mm', + 'LLLL' => 'dddd, Do MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gu.php b/vendor/nesbot/carbon/src/Carbon/Lang/gu.php new file mode 100644 index 0000000..8bc4311 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gu.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - Kaushik Thanki + * - Josh Soref + */ +return [ + 'year' => 'એક વર્ષ|:count વર્ષ', + 'y' => ':countવર્ષ|:countવર્ષો', + 'month' => 'એક મહિનો|:count મહિના', + 'm' => ':countમહિનો|:countમહિના', + 'week' => ':count અઠવાડિયું|:count અઠવાડિયા', + 'w' => ':countઅઠ.|:countઅઠ.', + 'day' => 'એક દિવસ|:count દિવસ', + 'd' => ':countદિ.|:countદિ.', + 'hour' => 'એક કલાક|:count કલાક', + 'h' => ':countક.|:countક.', + 'minute' => 'એક મિનિટ|:count મિનિટ', + 'min' => ':countમિ.|:countમિ.', + 'second' => 'અમુક પળો|:count સેકંડ', + 's' => ':countસે.|:countસે.', + 'ago' => ':time પેહલા', + 'from_now' => ':time મા', + 'after' => ':time પછી', + 'before' => ':time પહેલા', + 'diff_now' => 'હમણાં', + 'diff_today' => 'આજ', + 'diff_yesterday' => 'ગઇકાલે', + 'diff_tomorrow' => 'કાલે', + 'formats' => [ + 'LT' => 'A h:mm વાગ્યે', + 'LTS' => 'A h:mm:ss વાગ્યે', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY, A h:mm વાગ્યે', + 'LLLL' => 'dddd, D MMMM YYYY, A h:mm વાગ્યે', + ], + 'calendar' => [ + 'sameDay' => '[આજ] LT', + 'nextDay' => '[કાલે] LT', + 'nextWeek' => 'dddd, LT', + 'lastDay' => '[ગઇકાલે] LT', + 'lastWeek' => '[પાછલા] dddd, LT', + 'sameElse' => 'L', + ], + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'રાત'; + } + if ($hour < 10) { + return 'સવાર'; + } + if ($hour < 17) { + return 'બપોર'; + } + if ($hour < 20) { + return 'સાંજ'; + } + + return 'રાત'; + }, + 'months' => ['જાન્યુઆરી', 'ફેબ્રુઆરી', 'માર્ચ', 'એપ્રિલ', 'મે', 'જૂન', 'જુલાઈ', 'ઑગસ્ટ', 'સપ્ટેમ્બર', 'ઑક્ટ્બર', 'નવેમ્બર', 'ડિસેમ્બર'], + 'months_short' => ['જાન્યુ.', 'ફેબ્રુ.', 'માર્ચ', 'એપ્રિ.', 'મે', 'જૂન', 'જુલા.', 'ઑગ.', 'સપ્ટે.', 'ઑક્ટ્.', 'નવે.', 'ડિસે.'], + 'weekdays' => ['રવિવાર', 'સોમવાર', 'મંગળવાર', 'બુધ્વાર', 'ગુરુવાર', 'શુક્રવાર', 'શનિવાર'], + 'weekdays_short' => ['રવિ', 'સોમ', 'મંગળ', 'બુધ્', 'ગુરુ', 'શુક્ર', 'શનિ'], + 'weekdays_min' => ['ર', 'સો', 'મં', 'બુ', 'ગુ', 'શુ', 'શ'], + 'list' => [', ', ' અને '], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, + 'weekend' => [0, 0], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gu_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/gu_IN.php new file mode 100644 index 0000000..02654b1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gu_IN.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/gu.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/guz.php b/vendor/nesbot/carbon/src/Carbon/Lang/guz.php new file mode 100644 index 0000000..6230165 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/guz.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Ma', 'Mo'], + 'weekdays' => ['Chumapiri', 'Chumatato', 'Chumaine', 'Chumatano', 'Aramisi', 'Ichuma', 'Esabato'], + 'weekdays_short' => ['Cpr', 'Ctt', 'Cmn', 'Cmt', 'Ars', 'Icm', 'Est'], + 'weekdays_min' => ['Cpr', 'Ctt', 'Cmn', 'Cmt', 'Ars', 'Icm', 'Est'], + 'months' => ['Chanuari', 'Feburari', 'Machi', 'Apiriri', 'Mei', 'Juni', 'Chulai', 'Agosti', 'Septemba', 'Okitoba', 'Nobemba', 'Disemba'], + 'months_short' => ['Can', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Cul', 'Agt', 'Sep', 'Okt', 'Nob', 'Dis'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + + 'month' => ':count omotunyi', // less reliable + 'm' => ':count omotunyi', // less reliable + 'a_month' => ':count omotunyi', // less reliable + + 'week' => ':count isano naibere', // less reliable + 'w' => ':count isano naibere', // less reliable + 'a_week' => ':count isano naibere', // less reliable + + 'second' => ':count ibere', // less reliable + 's' => ':count ibere', // less reliable + 'a_second' => ':count ibere', // less reliable + + 'year' => ':count omwaka', + 'y' => ':count omwaka', + 'a_year' => ':count omwaka', + + 'day' => ':count rituko', + 'd' => ':count rituko', + 'a_day' => ':count rituko', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gv.php b/vendor/nesbot/carbon/src/Carbon/Lang/gv.php new file mode 100644 index 0000000..7c52b94 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gv.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/gv_GB.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gv_GB.php b/vendor/nesbot/carbon/src/Carbon/Lang/gv_GB.php new file mode 100644 index 0000000..6b1168f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gv_GB.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Alastair McKinstry bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['Jerrey-geuree', 'Toshiaght-arree', 'Mayrnt', 'Averil', 'Boaldyn', 'Mean-souree', 'Jerrey-souree', 'Luanistyn', 'Mean-fouyir', 'Jerrey-fouyir', 'Mee Houney', 'Mee ny Nollick'], + 'months_short' => ['J-guer', 'T-arree', 'Mayrnt', 'Avrril', 'Boaldyn', 'M-souree', 'J-souree', 'Luanistyn', 'M-fouyir', 'J-fouyir', 'M.Houney', 'M.Nollick'], + 'weekdays' => ['Jedoonee', 'Jelhein', 'Jemayrt', 'Jercean', 'Jerdein', 'Jeheiney', 'Jesarn'], + 'weekdays_short' => ['Jed', 'Jel', 'Jem', 'Jerc', 'Jerd', 'Jeh', 'Jes'], + 'weekdays_min' => ['Jed', 'Jel', 'Jem', 'Jerc', 'Jerd', 'Jeh', 'Jes'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'year' => ':count blein', + 'y' => ':count blein', + 'a_year' => ':count blein', + + 'month' => ':count mee', + 'm' => ':count mee', + 'a_month' => ':count mee', + + 'week' => ':count shiaghtin', + 'w' => ':count shiaghtin', + 'a_week' => ':count shiaghtin', + + 'day' => ':count laa', + 'd' => ':count laa', + 'a_day' => ':count laa', + + 'hour' => ':count oor', + 'h' => ':count oor', + 'a_hour' => ':count oor', + + 'minute' => ':count feer veg', + 'min' => ':count feer veg', + 'a_minute' => ':count feer veg', + + 'second' => ':count derrey', + 's' => ':count derrey', + 'a_second' => ':count derrey', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ha.php b/vendor/nesbot/carbon/src/Carbon/Lang/ha.php new file mode 100644 index 0000000..cd8e34d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ha.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - pablo@mandriva.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM, YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM, YYYY HH:mm', + ], + 'months' => ['Janairu', 'Faburairu', 'Maris', 'Afirilu', 'Mayu', 'Yuni', 'Yuli', 'Agusta', 'Satumba', 'Oktoba', 'Nuwamba', 'Disamba'], + 'months_short' => ['Jan', 'Fab', 'Mar', 'Afi', 'May', 'Yun', 'Yul', 'Agu', 'Sat', 'Okt', 'Nuw', 'Dis'], + 'weekdays' => ['Lahadi', 'Litini', 'Talata', 'Laraba', 'Alhamis', 'Jumaʼa', 'Asabar'], + 'weekdays_short' => ['Lah', 'Lit', 'Tal', 'Lar', 'Alh', 'Jum', 'Asa'], + 'weekdays_min' => ['Lh', 'Li', 'Ta', 'Lr', 'Al', 'Ju', 'As'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'year' => 'shekara :count', + 'y' => 'shekara :count', + 'a_year' => 'shekara :count', + + 'month' => ':count wátàa', + 'm' => ':count wátàa', + 'a_month' => ':count wátàa', + + 'week' => ':count mako', + 'w' => ':count mako', + 'a_week' => ':count mako', + + 'day' => ':count rana', + 'd' => ':count rana', + 'a_day' => ':count rana', + + 'hour' => ':count áwàa', + 'h' => ':count áwàa', + 'a_hour' => ':count áwàa', + + 'minute' => 'minti :count', + 'min' => 'minti :count', + 'a_minute' => 'minti :count', + + 'second' => ':count ná bíyú', + 's' => ':count ná bíyú', + 'a_second' => ':count ná bíyú', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ha_GH.php b/vendor/nesbot/carbon/src/Carbon/Lang/ha_GH.php new file mode 100644 index 0000000..f9f99a7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ha_GH.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ha.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ha_NE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ha_NE.php new file mode 100644 index 0000000..f9f99a7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ha_NE.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ha.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ha_NG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ha_NG.php new file mode 100644 index 0000000..f9f99a7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ha_NG.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ha.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hak.php b/vendor/nesbot/carbon/src/Carbon/Lang/hak.php new file mode 100644 index 0000000..6c3260e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hak.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/hak_TW.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hak_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/hak_TW.php new file mode 100644 index 0000000..fe23986 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hak_TW.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'YYYY年MM月DD日', + ], + 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + 'months_short' => [' 1月', ' 2月', ' 3月', ' 4月', ' 5月', ' 6月', ' 7月', ' 8月', ' 9月', '10月', '11月', '12月'], + 'weekdays' => ['禮拜日', '禮拜一', '禮拜二', '禮拜三', '禮拜四', '禮拜五', '禮拜六'], + 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'], + 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['上晝', '下晝'], + + 'year' => ':count ngien11', + 'y' => ':count ngien11', + 'a_year' => ':count ngien11', + + 'month' => ':count ngie̍t', + 'm' => ':count ngie̍t', + 'a_month' => ':count ngie̍t', + + 'week' => ':count lî-pai', + 'w' => ':count lî-pai', + 'a_week' => ':count lî-pai', + + 'day' => ':count ngit', + 'd' => ':count ngit', + 'a_day' => ':count ngit', + + 'hour' => ':count sṳ̀', + 'h' => ':count sṳ̀', + 'a_hour' => ':count sṳ̀', + + 'minute' => ':count fûn', + 'min' => ':count fûn', + 'a_minute' => ':count fûn', + + 'second' => ':count miéu', + 's' => ':count miéu', + 'a_second' => ':count miéu', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/haw.php b/vendor/nesbot/carbon/src/Carbon/Lang/haw.php new file mode 100644 index 0000000..cdd3686 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/haw.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'months' => ['Ianuali', 'Pepeluali', 'Malaki', 'ʻApelila', 'Mei', 'Iune', 'Iulai', 'ʻAukake', 'Kepakemapa', 'ʻOkakopa', 'Nowemapa', 'Kekemapa'], + 'months_short' => ['Ian.', 'Pep.', 'Mal.', 'ʻAp.', 'Mei', 'Iun.', 'Iul.', 'ʻAu.', 'Kep.', 'ʻOk.', 'Now.', 'Kek.'], + 'weekdays' => ['Lāpule', 'Poʻakahi', 'Poʻalua', 'Poʻakolu', 'Poʻahā', 'Poʻalima', 'Poʻaono'], + 'weekdays_short' => ['LP', 'P1', 'P2', 'P3', 'P4', 'P5', 'P6'], + 'weekdays_min' => ['S', 'M', 'T', 'W', 'T', 'F', 'S'], + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd, D MMMM YYYY h:mm a', + ], + + 'year' => ':count makahiki', + 'y' => ':count makahiki', + 'a_year' => ':count makahiki', + + 'month' => ':count mahina', + 'm' => ':count mahina', + 'a_month' => ':count mahina', + + 'week' => ':count pule', + 'w' => ':count pule', + 'a_week' => ':count pule', + + 'day' => ':count lā', + 'd' => ':count lā', + 'a_day' => ':count lā', + + 'hour' => ':count hola', + 'h' => ':count hola', + 'a_hour' => ':count hola', + + 'minute' => ':count minuke', + 'min' => ':count minuke', + 'a_minute' => ':count minuke', + + 'second' => ':count lua', + 's' => ':count lua', + 'a_second' => ':count lua', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/he.php b/vendor/nesbot/carbon/src/Carbon/Lang/he.php new file mode 100644 index 0000000..c3fb3e9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/he.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Daniel Cohen Gindi + * - JD Isaacks + * - Itai Nathaniel + * - GabMic + * - Yaakov Dahan (yakidahan) + */ +return [ + 'year' => 'שנה|{2}שנתיים|:count שנים', + 'y' => 'שנה|:count שנ׳', + 'month' => 'חודש|{2}חודשיים|:count חודשים', + 'm' => 'חודש|:count חו׳', + 'week' => 'שבוע|{2}שבועיים|:count שבועות', + 'w' => 'שבוע|:count שב׳', + 'day' => 'יום|{2}יומיים|:count ימים', + 'd' => 'יום|:count ימ׳', + 'hour' => 'שעה|{2}שעתיים|:count שעות', + 'h' => 'שעה|:count שע׳', + 'minute' => 'דקה|{2}שתי דקות|:count דקות', + 'min' => 'דקה|:count דק׳', + 'second' => 'שנייה|:count שניות', + 'a_second' => 'כמה שניות|:count שניות', + 's' => 'שניה|:count שנ׳', + 'ago' => 'לפני :time', + 'from_now' => 'בעוד :time מעכשיו', + 'after' => 'אחרי :time', + 'before' => 'לפני :time', + 'diff_now' => 'עכשיו', + 'diff_today' => 'היום', + 'diff_today_regexp' => 'היום(?:\\s+ב־)?', + 'diff_yesterday' => 'אתמול', + 'diff_yesterday_regexp' => 'אתמול(?:\\s+ב־)?', + 'diff_tomorrow' => 'מחר', + 'diff_tomorrow_regexp' => 'מחר(?:\\s+ב־)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D [ב]MMMM YYYY', + 'LLL' => 'D [ב]MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D [ב]MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[היום ב־]LT', + 'nextDay' => '[מחר ב־]LT', + 'nextWeek' => 'dddd [בשעה] LT', + 'lastDay' => '[אתמול ב־]LT', + 'lastWeek' => '[ביום] dddd [האחרון בשעה] LT', + 'sameElse' => 'L', + ], + 'meridiem' => function ($hour, $minute, $isLower) { + if ($hour < 5) { + return 'לפנות בוקר'; + } + if ($hour < 10) { + return 'בבוקר'; + } + if ($hour < 12) { + return $isLower ? 'לפנה"צ' : 'לפני הצהריים'; + } + if ($hour < 18) { + return $isLower ? 'אחה"צ' : 'אחרי הצהריים'; + } + + return 'בערב'; + }, + 'months' => ['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'], + 'months_short' => ['ינו׳', 'פבר׳', 'מרץ', 'אפר׳', 'מאי', 'יוני', 'יולי', 'אוג׳', 'ספט׳', 'אוק׳', 'נוב׳', 'דצמ׳'], + 'weekdays' => ['ראשון', 'שני', 'שלישי', 'רביעי', 'חמישי', 'שישי', 'שבת'], + 'weekdays_short' => ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳', 'ש׳'], + 'weekdays_min' => ['א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ש'], + 'list' => [', ', ' ו -'], + 'weekend' => [5, 6], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/he_IL.php b/vendor/nesbot/carbon/src/Carbon/Lang/he_IL.php new file mode 100644 index 0000000..14fab3e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/he_IL.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/he.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hi.php b/vendor/nesbot/carbon/src/Carbon/Lang/hi.php new file mode 100644 index 0000000..70c57a2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hi.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - abhimanyu003 + * - Josh Soref + * - JD Isaacks + */ +return [ + 'year' => 'एक वर्ष|:count वर्ष', + 'y' => '1 वर्ष|:count वर्षों', + 'month' => 'एक महीने|:count महीने', + 'm' => '1 माह|:count महीने', + 'week' => '1 सप्ताह|:count सप्ताह', + 'w' => '1 सप्ताह|:count सप्ताह', + 'day' => 'एक दिन|:count दिन', + 'd' => '1 दिन|:count दिनों', + 'hour' => 'एक घंटा|:count घंटे', + 'h' => '1 घंटा|:count घंटे', + 'minute' => 'एक मिनट|:count मिनट', + 'min' => '1 मिनट|:count मिनटों', + 'second' => 'कुछ ही क्षण|:count सेकंड', + 's' => '1 सेकंड|:count सेकंड', + 'ago' => ':time पहले', + 'from_now' => ':time में', + 'after' => ':time के बाद', + 'before' => ':time के पहले', + 'diff_now' => 'अब', + 'diff_today' => 'आज', + 'diff_yesterday' => 'कल', + 'diff_tomorrow' => 'कल', + 'formats' => [ + 'LT' => 'A h:mm बजे', + 'LTS' => 'A h:mm:ss बजे', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY, A h:mm बजे', + 'LLLL' => 'dddd, D MMMM YYYY, A h:mm बजे', + ], + 'calendar' => [ + 'sameDay' => '[आज] LT', + 'nextDay' => '[कल] LT', + 'nextWeek' => 'dddd, LT', + 'lastDay' => '[कल] LT', + 'lastWeek' => '[पिछले] dddd, LT', + 'sameElse' => 'L', + ], + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'रात'; + } + if ($hour < 10) { + return 'सुबह'; + } + if ($hour < 17) { + return 'दोपहर'; + } + if ($hour < 20) { + return 'शाम'; + } + + return 'रात'; + }, + 'months' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रैल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'], + 'months_short' => ['जन.', 'फ़र.', 'मार्च', 'अप्रै.', 'मई', 'जून', 'जुल.', 'अग.', 'सित.', 'अक्टू.', 'नव.', 'दिस.'], + 'weekdays' => ['रविवार', 'सोमवार', 'मंगलवार', 'बुधवार', 'गुरूवार', 'शुक्रवार', 'शनिवार'], + 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'गुरू', 'शुक्र', 'शनि'], + 'weekdays_min' => ['र', 'सो', 'मं', 'बु', 'गु', 'शु', 'श'], + 'list' => [', ', ' और '], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, + 'weekend' => [0, 0], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hi_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/hi_IN.php new file mode 100644 index 0000000..749dd97 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hi_IN.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/hi.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hif.php b/vendor/nesbot/carbon/src/Carbon/Lang/hif.php new file mode 100644 index 0000000..65791dd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hif.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/hif_FJ.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hif_FJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/hif_FJ.php new file mode 100644 index 0000000..30ad5e7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hif_FJ.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'dddd DD MMM YYYY', + ], + 'months' => ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + 'weekdays' => ['Ravivar', 'Somvar', 'Mangalvar', 'Budhvar', 'Guruvar', 'Shukravar', 'Shanivar'], + 'weekdays_short' => ['Ravi', 'Som', 'Mangal', 'Budh', 'Guru', 'Shukra', 'Shani'], + 'weekdays_min' => ['Ravi', 'Som', 'Mangal', 'Budh', 'Guru', 'Shukra', 'Shani'], + 'meridiem' => ['Purvahan', 'Aparaahna'], + + 'hour' => ':count minit', // less reliable + 'h' => ':count minit', // less reliable + 'a_hour' => ':count minit', // less reliable + + 'year' => ':count saal', + 'y' => ':count saal', + 'a_year' => ':count saal', + + 'month' => ':count Mahina', + 'm' => ':count Mahina', + 'a_month' => ':count Mahina', + + 'week' => ':count Hafta', + 'w' => ':count Hafta', + 'a_week' => ':count Hafta', + + 'day' => ':count Din', + 'd' => ':count Din', + 'a_day' => ':count Din', + + 'minute' => ':count Minit', + 'min' => ':count Minit', + 'a_minute' => ':count Minit', + + 'second' => ':count Second', + 's' => ':count Second', + 'a_second' => ':count Second', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hne.php b/vendor/nesbot/carbon/src/Carbon/Lang/hne.php new file mode 100644 index 0000000..4bcb05c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hne.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/hne_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hne_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/hne_IN.php new file mode 100644 index 0000000..a5ca758 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hne_IN.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Red Hat, Pune bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['जनवरी', 'फरवरी', 'मार्च', 'अपरेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितमबर', 'अकटूबर', 'नवमबर', 'दिसमबर'], + 'months_short' => ['जन', 'फर', 'मार्च', 'अप', 'मई', 'जून', 'जुला', 'अग', 'सित', 'अकटू', 'नव', 'दिस'], + 'weekdays' => ['इतवार', 'सोमवार', 'मंगलवार', 'बुधवार', 'बिरसपत', 'सुकरवार', 'सनिवार'], + 'weekdays_short' => ['इत', 'सोम', 'मंग', 'बुध', 'बिर', 'सुक', 'सनि'], + 'weekdays_min' => ['इत', 'सोम', 'मंग', 'बुध', 'बिर', 'सुक', 'सनि'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['बिहिनियाँ', 'मंझनियाँ'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hr.php b/vendor/nesbot/carbon/src/Carbon/Lang/hr.php new file mode 100644 index 0000000..cfd85fd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hr.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - François B + * - Tim Fish + * - shaishavgandhi05 + * - Serhan Apaydın + * - JD Isaacks + * - tomhorvat + * - Josh Soref + * - François B + * - shaishavgandhi05 + * - Serhan Apaydın + * - JD Isaacks + * - tomhorvat + * - Stjepan Majdak + * - Vanja Retkovac (vr00) + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count godinu|:count godine|:count godina', + 'y' => ':count god.|:count god.|:count god.', + 'month' => ':count mjesec|:count mjeseca|:count mjeseci', + 'm' => ':count mj.|:count mj.|:count mj.', + 'week' => ':count tjedan|:count tjedna|:count tjedana', + 'w' => ':count tj.|:count tj.|:count tj.', + 'day' => ':count dan|:count dana|:count dana', + 'd' => ':count d.|:count d.|:count d.', + 'hour' => ':count sat|:count sata|:count sati', + 'h' => ':count sat|:count sata|:count sati', + 'minute' => ':count minutu|:count minute|:count minuta', + 'min' => ':count min.|:count min.|:count min.', + 'second' => ':count sekundu|:count sekunde|:count sekundi', + 'a_second' => 'nekoliko sekundi|:count sekunde|:count sekundi', + 's' => ':count sek.|:count sek.|:count sek.', + 'ago' => 'prije :time', + 'from_now' => 'za :time', + 'after' => ':time poslije', + 'before' => ':time prije', + 'diff_now' => 'sad', + 'diff_today' => 'danas', + 'diff_today_regexp' => 'danas(?:\\s+u)?', + 'diff_yesterday' => 'jučer', + 'diff_yesterday_regexp' => 'jučer(?:\\s+u)?', + 'diff_tomorrow' => 'sutra', + 'diff_tomorrow_regexp' => 'sutra(?:\\s+u)?', + 'diff_before_yesterday' => 'prekjučer', + 'diff_after_tomorrow' => 'prekosutra', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'D. M. YYYY.', + 'LL' => 'D. MMMM YYYY.', + 'LLL' => 'D. MMMM YYYY. H:mm', + 'LLLL' => 'dddd, D. MMMM YYYY. H:mm', + ], + 'calendar' => [ + 'sameDay' => '[danas u] LT', + 'nextDay' => '[sutra u] LT', + 'nextWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[u] [nedjelju] [u] LT'; + case 3: + return '[u] [srijedu] [u] LT'; + case 6: + return '[u] [subotu] [u] LT'; + default: + return '[u] dddd [u] LT'; + } + }, + 'lastDay' => '[jučer u] LT', + 'lastWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + case 3: + return '[prošlu] dddd [u] LT'; + case 6: + return '[prošle] [subote] [u] LT'; + default: + return '[prošli] dddd [u] LT'; + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'months' => ['siječnja', 'veljače', 'ožujka', 'travnja', 'svibnja', 'lipnja', 'srpnja', 'kolovoza', 'rujna', 'listopada', 'studenoga', 'prosinca'], + 'months_standalone' => ['siječanj', 'veljača', 'ožujak', 'travanj', 'svibanj', 'lipanj', 'srpanj', 'kolovoz', 'rujan', 'listopad', 'studeni', 'prosinac'], + 'months_short' => ['sij.', 'velj.', 'ožu.', 'tra.', 'svi.', 'lip.', 'srp.', 'kol.', 'ruj.', 'lis.', 'stu.', 'pro.'], + 'months_regexp' => '/(D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/', + 'weekdays' => ['nedjelju', 'ponedjeljak', 'utorak', 'srijedu', 'četvrtak', 'petak', 'subotu'], + 'weekdays_standalone' => ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota'], + 'weekdays_short' => ['ned.', 'pon.', 'uto.', 'sri.', 'čet.', 'pet.', 'sub.'], + 'weekdays_min' => ['ne', 'po', 'ut', 'sr', 'če', 'pe', 'su'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' i '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hr_BA.php b/vendor/nesbot/carbon/src/Carbon/Lang/hr_BA.php new file mode 100644 index 0000000..7763a45 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hr_BA.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - DarkoDevelop + */ +return array_replace_recursive(require __DIR__.'/hr.php', [ + 'weekdays' => ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota'], + 'weekdays_short' => ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'], + 'weekdays_min' => ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'], + 'months' => ['siječnja', 'veljače', 'ožujka', 'travnja', 'svibnja', 'lipnja', 'srpnja', 'kolovoza', 'rujna', 'listopada', 'studenoga', 'prosinca'], + 'months_short' => ['sij', 'velj', 'ožu', 'tra', 'svi', 'lip', 'srp', 'kol', 'ruj', 'lis', 'stu', 'pro'], + 'months_standalone' => ['siječanj', 'veljača', 'ožujak', 'travanj', 'svibanj', 'lipanj', 'srpanj', 'kolovoz', 'rujan', 'listopad', 'studeni', 'prosinac'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D. M. yy.', + 'LL' => 'D. MMM YYYY.', + 'LLL' => 'D. MMMM YYYY. HH:mm', + 'LLLL' => 'dddd, D. MMMM YYYY. HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hr_HR.php b/vendor/nesbot/carbon/src/Carbon/Lang/hr_HR.php new file mode 100644 index 0000000..db74d8c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hr_HR.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/hr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hsb.php b/vendor/nesbot/carbon/src/Carbon/Lang/hsb.php new file mode 100644 index 0000000..3537b8b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hsb.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/hsb_DE.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hsb_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/hsb_DE.php new file mode 100644 index 0000000..6ba2271 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hsb_DE.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Information from Michael Wolf Andrzej Krzysztofowicz ankry@mif.pg.gda.pl + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'DD. MMMM YYYY', + 'LLL' => 'DD. MMMM, HH:mm [hodź.]', + 'LLLL' => 'dddd, DD. MMMM YYYY, HH:mm [hodź.]', + ], + 'months' => ['januara', 'februara', 'měrca', 'apryla', 'meje', 'junija', 'julija', 'awgusta', 'septembra', 'oktobra', 'nowembra', 'decembra'], + 'months_short' => ['Jan', 'Feb', 'Měr', 'Apr', 'Mej', 'Jun', 'Jul', 'Awg', 'Sep', 'Okt', 'Now', 'Dec'], + 'weekdays' => ['Njedźela', 'Póndźela', 'Wutora', 'Srjeda', 'Štvórtk', 'Pjatk', 'Sobota'], + 'weekdays_short' => ['Nj', 'Pó', 'Wu', 'Sr', 'Št', 'Pj', 'So'], + 'weekdays_min' => ['Nj', 'Pó', 'Wu', 'Sr', 'Št', 'Pj', 'So'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'year' => ':count lěto', + 'y' => ':count lěto', + 'a_year' => ':count lěto', + + 'month' => ':count měsac', + 'm' => ':count měsac', + 'a_month' => ':count měsac', + + 'week' => ':count tydźeń', + 'w' => ':count tydźeń', + 'a_week' => ':count tydźeń', + + 'day' => ':count dźeń', + 'd' => ':count dźeń', + 'a_day' => ':count dźeń', + + 'hour' => ':count hodźina', + 'h' => ':count hodźina', + 'a_hour' => ':count hodźina', + + 'minute' => ':count chwila', + 'min' => ':count chwila', + 'a_minute' => ':count chwila', + + 'second' => ':count druhi', + 's' => ':count druhi', + 'a_second' => ':count druhi', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ht.php b/vendor/nesbot/carbon/src/Carbon/Lang/ht.php new file mode 100644 index 0000000..ebd12ad --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ht.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/ht_HT.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ht_HT.php b/vendor/nesbot/carbon/src/Carbon/Lang/ht_HT.php new file mode 100644 index 0000000..139b813 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ht_HT.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Sugar Labs // OLPC sugarlabs.org libc-alpha@sourceware.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['janvye', 'fevriye', 'mas', 'avril', 'me', 'jen', 'jiyè', 'out', 'septanm', 'oktòb', 'novanm', 'desanm'], + 'months_short' => ['jan', 'fev', 'mas', 'avr', 'me', 'jen', 'jiy', 'out', 'sep', 'okt', 'nov', 'des'], + 'weekdays' => ['dimanch', 'lendi', 'madi', 'mèkredi', 'jedi', 'vandredi', 'samdi'], + 'weekdays_short' => ['dim', 'len', 'mad', 'mèk', 'jed', 'van', 'sam'], + 'weekdays_min' => ['dim', 'len', 'mad', 'mèk', 'jed', 'van', 'sam'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'year' => ':count lane', + 'y' => ':count lane', + 'a_year' => ':count lane', + + 'month' => 'mwa :count', + 'm' => 'mwa :count', + 'a_month' => 'mwa :count', + + 'week' => 'semèn :count', + 'w' => 'semèn :count', + 'a_week' => 'semèn :count', + + 'day' => ':count jou', + 'd' => ':count jou', + 'a_day' => ':count jou', + + 'hour' => ':count lè', + 'h' => ':count lè', + 'a_hour' => ':count lè', + + 'minute' => ':count minit', + 'min' => ':count minit', + 'a_minute' => ':count minit', + + 'second' => ':count segonn', + 's' => ':count segonn', + 'a_second' => ':count segonn', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hu.php b/vendor/nesbot/carbon/src/Carbon/Lang/hu.php new file mode 100644 index 0000000..b7583ee --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hu.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Adam Brunner + * - Brett Johnson + * - balping + */ + +use Carbon\CarbonInterface; + +$huWeekEndings = ['vasárnap', 'hétfőn', 'kedden', 'szerdán', 'csütörtökön', 'pénteken', 'szombaton']; + +return [ + 'year' => ':count év', + 'y' => ':count év', + 'month' => ':count hónap', + 'm' => ':count hónap', + 'week' => ':count hét', + 'w' => ':count hét', + 'day' => ':count nap', + 'd' => ':count nap', + 'hour' => ':count óra', + 'h' => ':count óra', + 'minute' => ':count perc', + 'min' => ':count perc', + 'second' => ':count másodperc', + 's' => ':count másodperc', + 'ago' => ':time', + 'from_now' => ':time múlva', + 'after' => ':time később', + 'before' => ':time korábban', + 'year_ago' => ':count éve', + 'y_ago' => ':count éve', + 'month_ago' => ':count hónapja', + 'm_ago' => ':count hónapja', + 'week_ago' => ':count hete', + 'w_ago' => ':count hete', + 'day_ago' => ':count napja', + 'd_ago' => ':count napja', + 'hour_ago' => ':count órája', + 'h_ago' => ':count órája', + 'minute_ago' => ':count perce', + 'min_ago' => ':count perce', + 'second_ago' => ':count másodperce', + 's_ago' => ':count másodperce', + 'year_after' => ':count évvel', + 'y_after' => ':count évvel', + 'month_after' => ':count hónappal', + 'm_after' => ':count hónappal', + 'week_after' => ':count héttel', + 'w_after' => ':count héttel', + 'day_after' => ':count nappal', + 'd_after' => ':count nappal', + 'hour_after' => ':count órával', + 'h_after' => ':count órával', + 'minute_after' => ':count perccel', + 'min_after' => ':count perccel', + 'second_after' => ':count másodperccel', + 's_after' => ':count másodperccel', + 'year_before' => ':count évvel', + 'y_before' => ':count évvel', + 'month_before' => ':count hónappal', + 'm_before' => ':count hónappal', + 'week_before' => ':count héttel', + 'w_before' => ':count héttel', + 'day_before' => ':count nappal', + 'd_before' => ':count nappal', + 'hour_before' => ':count órával', + 'h_before' => ':count órával', + 'minute_before' => ':count perccel', + 'min_before' => ':count perccel', + 'second_before' => ':count másodperccel', + 's_before' => ':count másodperccel', + 'months' => ['január', 'február', 'március', 'április', 'május', 'június', 'július', 'augusztus', 'szeptember', 'október', 'november', 'december'], + 'months_short' => ['jan.', 'febr.', 'márc.', 'ápr.', 'máj.', 'jún.', 'júl.', 'aug.', 'szept.', 'okt.', 'nov.', 'dec.'], + 'weekdays' => ['vasárnap', 'hétfő', 'kedd', 'szerda', 'csütörtök', 'péntek', 'szombat'], + 'weekdays_short' => ['vas', 'hét', 'kedd', 'sze', 'csüt', 'pén', 'szo'], + 'weekdays_min' => ['v', 'h', 'k', 'sze', 'cs', 'p', 'sz'], + 'ordinal' => ':number.', + 'diff_now' => 'most', + 'diff_today' => 'ma', + 'diff_yesterday' => 'tegnap', + 'diff_tomorrow' => 'holnap', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'YYYY.MM.DD.', + 'LL' => 'YYYY. MMMM D.', + 'LLL' => 'YYYY. MMMM D. H:mm', + 'LLLL' => 'YYYY. MMMM D., dddd H:mm', + ], + 'calendar' => [ + 'sameDay' => '[ma] LT[-kor]', + 'nextDay' => '[holnap] LT[-kor]', + 'nextWeek' => function (CarbonInterface $date) use ($huWeekEndings) { + return '['.$huWeekEndings[$date->dayOfWeek].'] LT[-kor]'; + }, + 'lastDay' => '[tegnap] LT[-kor]', + 'lastWeek' => function (CarbonInterface $date) use ($huWeekEndings) { + return '[múlt '.$huWeekEndings[$date->dayOfWeek].'] LT[-kor]'; + }, + 'sameElse' => 'L', + ], + 'meridiem' => ['DE', 'DU'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' és '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hu_HU.php b/vendor/nesbot/carbon/src/Carbon/Lang/hu_HU.php new file mode 100644 index 0000000..b1c4854 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hu_HU.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/hu.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hy.php b/vendor/nesbot/carbon/src/Carbon/Lang/hy.php new file mode 100644 index 0000000..8b12994 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hy.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - mhamlet + */ +return [ + 'year' => ':count տարի', + 'a_year' => 'տարի|:count տարի', + 'y' => ':countտ', + 'month' => ':count ամիս', + 'a_month' => 'ամիս|:count ամիս', + 'm' => ':countամ', + 'week' => ':count շաբաթ', + 'a_week' => 'շաբաթ|:count շաբաթ', + 'w' => ':countշ', + 'day' => ':count օր', + 'a_day' => 'օր|:count օր', + 'd' => ':countօր', + 'hour' => ':count ժամ', + 'a_hour' => 'ժամ|:count ժամ', + 'h' => ':countժ', + 'minute' => ':count րոպե', + 'a_minute' => 'րոպե|:count րոպե', + 'min' => ':countր', + 'second' => ':count վայրկյան', + 'a_second' => 'մի քանի վայրկյան|:count վայրկյան', + 's' => ':countվրկ', + 'ago' => ':time առաջ', + 'from_now' => ':timeից', + 'after' => ':time հետո', + 'before' => ':time առաջ', + 'diff_now' => 'հիմա', + 'diff_today' => 'այսօր', + 'diff_yesterday' => 'երեկ', + 'diff_tomorrow' => 'վաղը', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY թ.', + 'LLL' => 'D MMMM YYYY թ., HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY թ., HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[այսօր] LT', + 'nextDay' => '[վաղը] LT', + 'nextWeek' => 'dddd [օրը ժամը] LT', + 'lastDay' => '[երեկ] LT', + 'lastWeek' => '[անցած] dddd [օրը ժամը] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number, $period) { + switch ($period) { + case 'DDD': + case 'w': + case 'W': + case 'DDDo': + return $number.($number === 1 ? '-ին' : '-րդ'); + default: + return $number; + } + }, + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'գիշերվա'; + } + if ($hour < 12) { + return 'առավոտվա'; + } + if ($hour < 17) { + return 'ցերեկվա'; + } + + return 'երեկոյան'; + }, + 'months' => ['հունվարի', 'փետրվարի', 'մարտի', 'ապրիլի', 'մայիսի', 'հունիսի', 'հուլիսի', 'օգոստոսի', 'սեպտեմբերի', 'հոկտեմբերի', 'նոյեմբերի', 'դեկտեմբերի'], + 'months_standalone' => ['հունվար', 'փետրվար', 'մարտ', 'ապրիլ', 'մայիս', 'հունիս', 'հուլիս', 'օգոստոս', 'սեպտեմբեր', 'հոկտեմբեր', 'նոյեմբեր', 'դեկտեմբեր'], + 'months_short' => ['հնվ', 'փտր', 'մրտ', 'ապր', 'մյս', 'հնս', 'հլս', 'օգս', 'սպտ', 'հկտ', 'նմբ', 'դկտ'], + 'months_regexp' => '/(D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/', + 'weekdays' => ['կիրակի', 'երկուշաբթի', 'երեքշաբթի', 'չորեքշաբթի', 'հինգշաբթի', 'ուրբաթ', 'շաբաթ'], + 'weekdays_short' => ['կրկ', 'երկ', 'երք', 'չրք', 'հնգ', 'ուրբ', 'շբթ'], + 'weekdays_min' => ['կրկ', 'երկ', 'երք', 'չրք', 'հնգ', 'ուրբ', 'շբթ'], + 'list' => [', ', ' եւ '], + 'first_day_of_week' => 1, +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hy_AM.php b/vendor/nesbot/carbon/src/Carbon/Lang/hy_AM.php new file mode 100644 index 0000000..4587df5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hy_AM.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - François B + * - Tim Fish + * - Serhan Apaydın + * - JD Isaacks + */ +return array_replace_recursive(require __DIR__.'/hy.php', [ + 'from_now' => ':time հետո', + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/i18n.php b/vendor/nesbot/carbon/src/Carbon/Lang/i18n.php new file mode 100644 index 0000000..e65449b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/i18n.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'YYYY-MM-DD', + ], + 'months' => ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'], + 'months_short' => ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'], + 'weekdays' => ['1', '2', '3', '4', '5', '6', '7'], + 'weekdays_short' => ['1', '2', '3', '4', '5', '6', '7'], + 'weekdays_min' => ['1', '2', '3', '4', '5', '6', '7'], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ia.php b/vendor/nesbot/carbon/src/Carbon/Lang/ia.php new file mode 100644 index 0000000..0a0d5e6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ia.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/ia_FR.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ia_FR.php b/vendor/nesbot/carbon/src/Carbon/Lang/ia_FR.php new file mode 100644 index 0000000..de4b2fa --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ia_FR.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Fedora Project Nik Kalach nikka@fedoraproject.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['januario', 'februario', 'martio', 'april', 'maio', 'junio', 'julio', 'augusto', 'septembre', 'octobre', 'novembre', 'decembre'], + 'months_short' => ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'], + 'weekdays' => ['dominica', 'lunedi', 'martedi', 'mercuridi', 'jovedi', 'venerdi', 'sabbato'], + 'weekdays_short' => ['dom', 'lun', 'mar', 'mer', 'jov', 'ven', 'sab'], + 'weekdays_min' => ['dom', 'lun', 'mar', 'mer', 'jov', 'ven', 'sab'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'year' => 'anno :count', + 'y' => 'anno :count', + 'a_year' => 'anno :count', + + 'month' => ':count mense', + 'm' => ':count mense', + 'a_month' => ':count mense', + + 'week' => ':count septimana', + 'w' => ':count septimana', + 'a_week' => ':count septimana', + + 'day' => ':count die', + 'd' => ':count die', + 'a_day' => ':count die', + + 'hour' => ':count hora', + 'h' => ':count hora', + 'a_hour' => ':count hora', + + 'minute' => ':count minuscule', + 'min' => ':count minuscule', + 'a_minute' => ':count minuscule', + + 'second' => ':count secunda', + 's' => ':count secunda', + 'a_second' => ':count secunda', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/id.php b/vendor/nesbot/carbon/src/Carbon/Lang/id.php new file mode 100644 index 0000000..afaf78f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/id.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - du + * - JD Isaacks + * - Nafies Luthfi + * - Raymundus Jati Primanda (mundusjp) + * - diankur313 + * - a-wip0 + */ +return [ + 'year' => ':count tahun', + 'a_year' => '{1}setahun|]1,Inf[:count tahun', + 'y' => ':countthn', + 'month' => ':count bulan', + 'a_month' => '{1}sebulan|]1,Inf[:count bulan', + 'm' => ':countbln', + 'week' => ':count minggu', + 'a_week' => '{1}seminggu|]1,Inf[:count minggu', + 'w' => ':countmgg', + 'day' => ':count hari', + 'a_day' => '{1}sehari|]1,Inf[:count hari', + 'd' => ':counthr', + 'hour' => ':count jam', + 'a_hour' => '{1}sejam|]1,Inf[:count jam', + 'h' => ':countj', + 'minute' => ':count menit', + 'a_minute' => '{1}semenit|]1,Inf[:count menit', + 'min' => ':countmnt', + 'second' => ':count detik', + 'a_second' => '{1}beberapa detik|]1,Inf[:count detik', + 's' => ':countdt', + 'ago' => ':time yang lalu', + 'from_now' => ':time dari sekarang', + 'after' => ':time setelahnya', + 'before' => ':time sebelumnya', + 'diff_now' => 'sekarang', + 'diff_today' => 'Hari', + 'diff_today_regexp' => 'Hari(?:\\s+ini)?(?:\\s+pukul)?', + 'diff_yesterday' => 'kemarin', + 'diff_yesterday_regexp' => 'Kemarin(?:\\s+pukul)?', + 'diff_tomorrow' => 'besok', + 'diff_tomorrow_regexp' => 'Besok(?:\\s+pukul)?', + 'formats' => [ + 'LT' => 'HH.mm', + 'LTS' => 'HH.mm.ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY [pukul] HH.mm', + 'LLLL' => 'dddd, D MMMM YYYY [pukul] HH.mm', + ], + 'calendar' => [ + 'sameDay' => '[Hari ini pukul] LT', + 'nextDay' => '[Besok pukul] LT', + 'nextWeek' => 'dddd [pukul] LT', + 'lastDay' => '[Kemarin pukul] LT', + 'lastWeek' => 'dddd [lalu pukul] LT', + 'sameElse' => 'L', + ], + 'meridiem' => function ($hour) { + if ($hour < 11) { + return 'pagi'; + } + if ($hour < 15) { + return 'siang'; + } + if ($hour < 19) { + return 'sore'; + } + + return 'malam'; + }, + 'months' => ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agt', 'Sep', 'Okt', 'Nov', 'Des'], + 'weekdays' => ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'], + 'weekdays_short' => ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'], + 'weekdays_min' => ['Mg', 'Sn', 'Sl', 'Rb', 'Km', 'Jm', 'Sb'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' dan '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/id_ID.php b/vendor/nesbot/carbon/src/Carbon/Lang/id_ID.php new file mode 100644 index 0000000..d5953a1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/id_ID.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/id.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ig.php b/vendor/nesbot/carbon/src/Carbon/Lang/ig.php new file mode 100644 index 0000000..de51e9c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ig.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/ig_NG.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ig_NG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ig_NG.php new file mode 100644 index 0000000..0034e35 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ig_NG.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - pablo@mandriva.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['Jenụwarị', 'Febrụwarị', 'Maachị', 'Eprel', 'Mee', 'Juun', 'Julaị', 'Ọgọọst', 'Septemba', 'Ọktoba', 'Novemba', 'Disemba'], + 'months_short' => ['Jen', 'Feb', 'Maa', 'Epr', 'Mee', 'Juu', 'Jul', 'Ọgọ', 'Sep', 'Ọkt', 'Nov', 'Dis'], + 'weekdays' => ['sọnde', 'mọnde', 'tuzde', 'wenzde', 'tọsde', 'fraịde', 'satọde'], + 'weekdays_short' => ['sọn', 'mọn', 'tuz', 'wen', 'tọs', 'fra', 'sat'], + 'weekdays_min' => ['sọn', 'mọn', 'tuz', 'wen', 'tọs', 'fra', 'sat'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'year' => 'afo :count', + 'y' => 'afo :count', + 'a_year' => 'afo :count', + + 'month' => 'önwa :count', + 'm' => 'önwa :count', + 'a_month' => 'önwa :count', + + 'week' => 'izu :count', + 'w' => 'izu :count', + 'a_week' => 'izu :count', + + 'day' => 'ụbọchị :count', + 'd' => 'ụbọchị :count', + 'a_day' => 'ụbọchị :count', + + 'hour' => 'awa :count', + 'h' => 'awa :count', + 'a_hour' => 'awa :count', + + 'minute' => 'minit :count', + 'min' => 'minit :count', + 'a_minute' => 'minit :count', + + 'second' => 'sekọnd :count', + 's' => 'sekọnd :count', + 'a_second' => 'sekọnd :count', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ii.php b/vendor/nesbot/carbon/src/Carbon/Lang/ii.php new file mode 100644 index 0000000..a4246c2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ii.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['ꎸꄑ', 'ꁯꋒ'], + 'weekdays' => ['ꑭꆏꑍ', 'ꆏꊂꋍ', 'ꆏꊂꑍ', 'ꆏꊂꌕ', 'ꆏꊂꇖ', 'ꆏꊂꉬ', 'ꆏꊂꃘ'], + 'weekdays_short' => ['ꑭꆏ', 'ꆏꋍ', 'ꆏꑍ', 'ꆏꌕ', 'ꆏꇖ', 'ꆏꉬ', 'ꆏꃘ'], + 'weekdays_min' => ['ꑭꆏ', 'ꆏꋍ', 'ꆏꑍ', 'ꆏꌕ', 'ꆏꇖ', 'ꆏꉬ', 'ꆏꃘ'], + 'months' => null, + 'months_short' => ['ꋍꆪ', 'ꑍꆪ', 'ꌕꆪ', 'ꇖꆪ', 'ꉬꆪ', 'ꃘꆪ', 'ꏃꆪ', 'ꉆꆪ', 'ꈬꆪ', 'ꊰꆪ', 'ꊰꊪꆪ', 'ꊰꑋꆪ'], + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'YYYY-MM-dd', + 'LL' => 'YYYY MMM D', + 'LLL' => 'YYYY MMMM D h:mm a', + 'LLLL' => 'YYYY MMMM D, dddd h:mm a', + ], + + 'year' => ':count ꒉ', // less reliable + 'y' => ':count ꒉ', // less reliable + 'a_year' => ':count ꒉ', // less reliable + + 'month' => ':count ꆪ', + 'm' => ':count ꆪ', + 'a_month' => ':count ꆪ', + + 'week' => ':count ꏃ', // less reliable + 'w' => ':count ꏃ', // less reliable + 'a_week' => ':count ꏃ', // less reliable + + 'day' => ':count ꏜ', // less reliable + 'd' => ':count ꏜ', // less reliable + 'a_day' => ':count ꏜ', // less reliable + + 'hour' => ':count ꄮꈉ', + 'h' => ':count ꄮꈉ', + 'a_hour' => ':count ꄮꈉ', + + 'minute' => ':count ꀄꊭ', // less reliable + 'min' => ':count ꀄꊭ', // less reliable + 'a_minute' => ':count ꀄꊭ', // less reliable + + 'second' => ':count ꇅ', // less reliable + 's' => ':count ꇅ', // less reliable + 'a_second' => ':count ꇅ', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ik.php b/vendor/nesbot/carbon/src/Carbon/Lang/ik.php new file mode 100644 index 0000000..7a13aa2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ik.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/ik_CA.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ik_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ik_CA.php new file mode 100644 index 0000000..bb2a109 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ik_CA.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - pablo@mandriva.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['Siqiññaatchiaq', 'Siqiññaasrugruk', 'Paniqsiqsiivik', 'Qilġich Tatqiat', 'Suppivik', 'Iġñivik', 'Itchavik', 'Tiññivik', 'Amiġaiqsivik', 'Sikkuvik', 'Nippivik', 'Siqiñġiḷaq'], + 'months_short' => ['Sñt', 'Sñs', 'Pan', 'Qil', 'Sup', 'Iġñ', 'Itc', 'Tiñ', 'Ami', 'Sik', 'Nip', 'Siq'], + 'weekdays' => ['Minġuiqsioiq', 'Savałłiq', 'Ilaqtchiioiq', 'Qitchiioiq', 'Sisamiioiq', 'Tallimmiioiq', 'Maqinġuoiq'], + 'weekdays_short' => ['Min', 'Sav', 'Ila', 'Qit', 'Sis', 'Tal', 'Maq'], + 'weekdays_min' => ['Min', 'Sav', 'Ila', 'Qit', 'Sis', 'Tal', 'Maq'], + 'day_of_first_week_of_year' => 1, + + 'year' => ':count ukiuq', + 'y' => ':count ukiuq', + 'a_year' => ':count ukiuq', + + 'month' => ':count Tatqiat', + 'm' => ':count Tatqiat', + 'a_month' => ':count Tatqiat', + + 'week' => ':count tatqiat', // less reliable + 'w' => ':count tatqiat', // less reliable + 'a_week' => ':count tatqiat', // less reliable + + 'day' => ':count siqiñiq', // less reliable + 'd' => ':count siqiñiq', // less reliable + 'a_day' => ':count siqiñiq', // less reliable + + 'hour' => ':count Siḷa', // less reliable + 'h' => ':count Siḷa', // less reliable + 'a_hour' => ':count Siḷa', // less reliable + + 'second' => ':count iġñiq', // less reliable + 's' => ':count iġñiq', // less reliable + 'a_second' => ':count iġñiq', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/in.php b/vendor/nesbot/carbon/src/Carbon/Lang/in.php new file mode 100644 index 0000000..d5953a1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/in.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/id.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/is.php b/vendor/nesbot/carbon/src/Carbon/Lang/is.php new file mode 100644 index 0000000..9990168 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/is.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Kristján Ingi Geirsson + */ +return [ + 'year' => '1 ár|:count ár', + 'y' => '1 ár|:count ár', + 'month' => '1 mánuður|:count mánuðir', + 'm' => '1 mánuður|:count mánuðir', + 'week' => '1 vika|:count vikur', + 'w' => '1 vika|:count vikur', + 'day' => '1 dagur|:count dagar', + 'd' => '1 dagur|:count dagar', + 'hour' => '1 klukkutími|:count klukkutímar', + 'h' => '1 klukkutími|:count klukkutímar', + 'minute' => '1 mínúta|:count mínútur', + 'min' => '1 mínúta|:count mínútur', + 'second' => '1 sekúnda|:count sekúndur', + 's' => '1 sekúnda|:count sekúndur', + 'ago' => ':time síðan', + 'from_now' => ':time síðan', + 'after' => ':time eftir', + 'before' => ':time fyrir', + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' og '], + 'meridiem' => ['fh', 'eh'], + 'diff_now' => 'núna', + 'diff_yesterday' => 'í gær', + 'diff_tomorrow' => 'á morgun', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM [kl.] HH:mm', + 'LLLL' => 'dddd D. MMMM YYYY [kl.] HH:mm', + ], + 'weekdays' => ['sunnudaginn', 'mánudaginn', 'þriðjudaginn', 'miðvikudaginn', 'fimmtudaginn', 'föstudaginn', 'laugardaginn'], + 'weekdays_short' => ['sun', 'mán', 'þri', 'mið', 'fim', 'fös', 'lau'], + 'weekdays_min' => ['sun', 'mán', 'þri', 'mið', 'fim', 'fös', 'lau'], + 'months' => ['janúar', 'febrúar', 'mars', 'apríl', 'maí', 'júní', 'júlí', 'ágúst', 'september', 'október', 'nóvember', 'desember'], + 'months_short' => ['jan', 'feb', 'mar', 'apr', 'maí', 'jún', 'júl', 'ágú', 'sep', 'okt', 'nóv', 'des'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/is_IS.php b/vendor/nesbot/carbon/src/Carbon/Lang/is_IS.php new file mode 100644 index 0000000..4d35c44 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/is_IS.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/is.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/it.php b/vendor/nesbot/carbon/src/Carbon/Lang/it.php new file mode 100644 index 0000000..49875d7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/it.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ash + * - François B + * - Marco Perrando + * - Massimiliano Caniparoli + * - JD Isaacks + * - Andrea Martini + * - Francesco Marasco + * - Tizianoz93 + * - Davide Casiraghi (davide-casiraghi) + * - Pete Scopes (pdscopes) + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count anno|:count anni', + 'a_year' => 'un anno|:count anni', + 'y' => ':count anno|:count anni', + 'month' => ':count mese|:count mesi', + 'a_month' => 'un mese|:count mesi', + 'm' => ':count mese|:count mesi', + 'week' => ':count settimana|:count settimane', + 'a_week' => 'una settimana|:count settimane', + 'w' => ':count set.', + 'day' => ':count giorno|:count giorni', + 'a_day' => 'un giorno|:count giorni', + 'd' => ':count g|:count gg', + 'hour' => ':count ora|:count ore', + 'a_hour' => 'un\'ora|:count ore', + 'h' => ':count h', + 'minute' => ':count minuto|:count minuti', + 'a_minute' => 'un minuto|:count minuti', + 'min' => ':count min.', + 'second' => ':count secondo|:count secondi', + 'a_second' => 'alcuni secondi|:count secondi', + 's' => ':count sec.', + 'millisecond' => ':count millisecondo|:count millisecondi', + 'a_millisecond' => 'un millisecondo|:count millisecondi', + 'ms' => ':countms', + 'microsecond' => ':count microsecondo|:count microsecondi', + 'a_microsecond' => 'un microsecondo|:count microsecondi', + 'µs' => ':countµs', + 'ago' => ':time fa', + 'from_now' => function ($time) { + return (preg_match('/^\d.+$/', $time) ? 'tra' : 'in')." $time"; + }, + 'after' => ':time dopo', + 'before' => ':time prima', + 'diff_now' => 'proprio ora', + 'diff_today' => 'Oggi', + 'diff_today_regexp' => 'Oggi(?:\\s+alle)?', + 'diff_yesterday' => 'ieri', + 'diff_yesterday_regexp' => 'Ieri(?:\\s+alle)?', + 'diff_tomorrow' => 'domani', + 'diff_tomorrow_regexp' => 'Domani(?:\\s+alle)?', + 'diff_before_yesterday' => 'l\'altro ieri', + 'diff_after_tomorrow' => 'dopodomani', + 'period_interval' => 'ogni :interval', + 'period_start_date' => 'dal :date', + 'period_end_date' => 'al :date', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Oggi alle] LT', + 'nextDay' => '[Domani alle] LT', + 'nextWeek' => 'dddd [alle] LT', + 'lastDay' => '[Ieri alle] LT', + 'lastWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[la scorsa] dddd [alle] LT'; + default: + return '[lo scorso] dddd [alle] LT'; + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => ':numberº', + 'months' => ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno', 'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'], + 'months_short' => ['gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug', 'ago', 'set', 'ott', 'nov', 'dic'], + 'weekdays' => ['domenica', 'lunedì', 'martedì', 'mercoledì', 'giovedì', 'venerdì', 'sabato'], + 'weekdays_short' => ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'], + 'weekdays_min' => ['do', 'lu', 'ma', 'me', 'gi', 've', 'sa'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' e '], + 'ordinal_words' => [ + 'of' => 'di', + 'first' => 'primo', + 'second' => 'secondo', + 'third' => 'terzo', + 'fourth' => 'quarto', + 'fifth' => 'quinto', + 'last' => 'ultimo', + ], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/it_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/it_CH.php new file mode 100644 index 0000000..c23cc50 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/it_CH.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Propaganistas + */ +return array_replace_recursive(require __DIR__.'/it.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/it_IT.php b/vendor/nesbot/carbon/src/Carbon/Lang/it_IT.php new file mode 100644 index 0000000..a5d1981 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/it_IT.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return require __DIR__.'/it.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/it_SM.php b/vendor/nesbot/carbon/src/Carbon/Lang/it_SM.php new file mode 100644 index 0000000..5e8fc92 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/it_SM.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/it.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/it_VA.php b/vendor/nesbot/carbon/src/Carbon/Lang/it_VA.php new file mode 100644 index 0000000..5e8fc92 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/it_VA.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/it.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/iu.php b/vendor/nesbot/carbon/src/Carbon/Lang/iu.php new file mode 100644 index 0000000..4fa9742 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/iu.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/iu_CA.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/iu_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/iu_CA.php new file mode 100644 index 0000000..6ab7e14 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/iu_CA.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Pablo Saratxaga pablo@mandriva.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'MM/DD/YY', + ], + 'months' => ['ᔮᓄᐊᓕ', 'ᕕᕗᐊᓕ', 'ᒪᔅᓯ', 'ᐃᐳᓗ', 'ᒪᐃ', 'ᔪᓂ', 'ᔪᓚᐃ', 'ᐊᒋᓯ', 'ᓯᑎᕙ', 'ᐊᑦᑐᕙ', 'ᓄᕕᕙ', 'ᑎᓯᕝᕙ'], + 'months_short' => ['ᔮᓄ', 'ᕕᕗ', 'ᒪᔅ', 'ᐃᐳ', 'ᒪᐃ', 'ᔪᓂ', 'ᔪᓚ', 'ᐊᒋ', 'ᓯᑎ', 'ᐊᑦ', 'ᓄᕕ', 'ᑎᓯ'], + 'weekdays' => ['ᓈᑦᑎᖑᔭᕐᕕᒃ', 'ᓇᒡᒐᔾᔭᐅ', 'ᓇᒡᒐᔾᔭᐅᓕᖅᑭᑦ', 'ᐱᖓᓲᓕᖅᓯᐅᑦ', 'ᕿᑎᖅᑰᑦ', 'ᐅᓪᓗᕈᓘᑐᐃᓇᖅ', 'ᓯᕙᑖᕕᒃ'], + 'weekdays_short' => ['ᓈ', 'ᓇ', 'ᓕ', 'ᐱ', 'ᕿ', 'ᐅ', 'ᓯ'], + 'weekdays_min' => ['ᓈ', 'ᓇ', 'ᓕ', 'ᐱ', 'ᕿ', 'ᐅ', 'ᓯ'], + 'day_of_first_week_of_year' => 1, + + 'year' => ':count ᐅᑭᐅᖅ', + 'y' => ':count ᐅᑭᐅᖅ', + 'a_year' => ':count ᐅᑭᐅᖅ', + + 'month' => ':count qaammat', + 'm' => ':count qaammat', + 'a_month' => ':count qaammat', + + 'week' => ':count sapaatip akunnera', + 'w' => ':count sapaatip akunnera', + 'a_week' => ':count sapaatip akunnera', + + 'day' => ':count ulloq', + 'd' => ':count ulloq', + 'a_day' => ':count ulloq', + + 'hour' => ':count ikarraq', + 'h' => ':count ikarraq', + 'a_hour' => ':count ikarraq', + + 'minute' => ':count titiqqaralaaq', // less reliable + 'min' => ':count titiqqaralaaq', // less reliable + 'a_minute' => ':count titiqqaralaaq', // less reliable + + 'second' => ':count marluk', // less reliable + 's' => ':count marluk', // less reliable + 'a_second' => ':count marluk', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/iw.php b/vendor/nesbot/carbon/src/Carbon/Lang/iw.php new file mode 100644 index 0000000..a26e350 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/iw.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'months' => ['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'], + 'months_short' => ['ינו׳', 'פבר׳', 'מרץ', 'אפר׳', 'מאי', 'יוני', 'יולי', 'אוג׳', 'ספט׳', 'אוק׳', 'נוב׳', 'דצמ׳'], + 'weekdays' => ['יום ראשון', 'יום שני', 'יום שלישי', 'יום רביעי', 'יום חמישי', 'יום שישי', 'יום שבת'], + 'weekdays_short' => ['יום א׳', 'יום ב׳', 'יום ג׳', 'יום ד׳', 'יום ה׳', 'יום ו׳', 'שבת'], + 'weekdays_min' => ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳', 'ש׳'], + 'meridiem' => ['לפנה״צ', 'אחה״צ'], + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'D.M.YYYY', + 'LL' => 'D בMMM YYYY', + 'LLL' => 'D בMMMM YYYY H:mm', + 'LLLL' => 'dddd, D בMMMM YYYY H:mm', + ], + + 'year' => ':count שנה', + 'y' => ':count שנה', + 'a_year' => ':count שנה', + + 'month' => ':count חודש', + 'm' => ':count חודש', + 'a_month' => ':count חודש', + + 'week' => ':count שבוע', + 'w' => ':count שבוע', + 'a_week' => ':count שבוע', + + 'day' => ':count יום', + 'd' => ':count יום', + 'a_day' => ':count יום', + + 'hour' => ':count שעה', + 'h' => ':count שעה', + 'a_hour' => ':count שעה', + + 'minute' => ':count דקה', + 'min' => ':count דקה', + 'a_minute' => ':count דקה', + + 'second' => ':count שניה', + 's' => ':count שניה', + 'a_second' => ':count שניה', + + 'ago' => 'לפני :time', + 'from_now' => 'בעוד :time', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ja.php b/vendor/nesbot/carbon/src/Carbon/Lang/ja.php new file mode 100644 index 0000000..1ca6751 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ja.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Takuya Sawada + * - Atsushi Tanaka + * - François B + * - Jason Katz-Brown + * - Serhan Apaydın + * - XueWei + * - JD Isaacks + * - toyama satoshi + * - atakigawa + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count年', + 'y' => ':count年', + 'month' => ':countヶ月', + 'm' => ':countヶ月', + 'week' => ':count週間', + 'w' => ':count週間', + 'day' => ':count日', + 'd' => ':count日', + 'hour' => ':count時間', + 'h' => ':count時間', + 'minute' => ':count分', + 'min' => ':count分', + 'second' => ':count秒', + 'a_second' => '{1}数秒|]1,Inf[:count秒', + 's' => ':count秒', + 'ago' => ':time前', + 'from_now' => ':time後', + 'after' => ':time後', + 'before' => ':time前', + 'diff_now' => '今', + 'diff_today' => '今日', + 'diff_yesterday' => '昨日', + 'diff_tomorrow' => '明日', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY/MM/DD', + 'LL' => 'YYYY年M月D日', + 'LLL' => 'YYYY年M月D日 HH:mm', + 'LLLL' => 'YYYY年M月D日 dddd HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[今日] LT', + 'nextDay' => '[明日] LT', + 'nextWeek' => function (CarbonInterface $current, CarbonInterface $other) { + if ($other->week !== $current->week) { + return '[来週]dddd LT'; + } + + return 'dddd LT'; + }, + 'lastDay' => '[昨日] LT', + 'lastWeek' => function (CarbonInterface $current, CarbonInterface $other) { + if ($other->week !== $current->week) { + return '[先週]dddd LT'; + } + + return 'dddd LT'; + }, + 'sameElse' => 'L', + ], + 'ordinal' => function ($number, $period) { + switch ($period) { + case 'd': + case 'D': + case 'DDD': + return $number.'日'; + default: + return $number; + } + }, + 'meridiem' => ['午前', '午後'], + 'months' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], + 'months_short' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], + 'weekdays' => ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'], + 'weekdays_short' => ['日', '月', '火', '水', '木', '金', '土'], + 'weekdays_min' => ['日', '月', '火', '水', '木', '金', '土'], + 'list' => '、', + 'alt_numbers' => ['〇', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十', '二十一', '二十二', '二十三', '二十四', '二十五', '二十六', '二十七', '二十八', '二十九', '三十', '三十一', '三十二', '三十三', '三十四', '三十五', '三十六', '三十七', '三十八', '三十九', '四十', '四十一', '四十二', '四十三', '四十四', '四十五', '四十六', '四十七', '四十八', '四十九', '五十', '五十一', '五十二', '五十三', '五十四', '五十五', '五十六', '五十七', '五十八', '五十九', '六十', '六十一', '六十二', '六十三', '六十四', '六十五', '六十六', '六十七', '六十八', '六十九', '七十', '七十一', '七十二', '七十三', '七十四', '七十五', '七十六', '七十七', '七十八', '七十九', '八十', '八十一', '八十二', '八十三', '八十四', '八十五', '八十六', '八十七', '八十八', '八十九', '九十', '九十一', '九十二', '九十三', '九十四', '九十五', '九十六', '九十七', '九十八', '九十九'], + 'alt_numbers_pow' => [ + 10000 => '万', + 1000 => '千', + 100 => '百', + ], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ja_JP.php b/vendor/nesbot/carbon/src/Carbon/Lang/ja_JP.php new file mode 100644 index 0000000..c283625 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ja_JP.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ja.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/jgo.php b/vendor/nesbot/carbon/src/Carbon/Lang/jgo.php new file mode 100644 index 0000000..6a1e77a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/jgo.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/jmc.php b/vendor/nesbot/carbon/src/Carbon/Lang/jmc.php new file mode 100644 index 0000000..ed92e8e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/jmc.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['utuko', 'kyiukonyi'], + 'weekdays' => ['Jumapilyi', 'Jumatatuu', 'Jumanne', 'Jumatanu', 'Alhamisi', 'Ijumaa', 'Jumamosi'], + 'weekdays_short' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'], + 'weekdays_min' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'], + 'months' => ['Januari', 'Februari', 'Machi', 'Aprilyi', 'Mei', 'Junyi', 'Julyai', 'Agusti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'], + 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/jv.php b/vendor/nesbot/carbon/src/Carbon/Lang/jv.php new file mode 100644 index 0000000..bcbe044 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/jv.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - tgfjt + * - JD Isaacks + */ +return [ + 'year' => '{1}setaun|]1,Inf[:count taun', + 'month' => '{1}sewulan|]1,Inf[:count wulan', + 'week' => '{1}sakminggu|]1,Inf[:count minggu', + 'day' => '{1}sedinten|]1,Inf[:count dinten', + 'hour' => '{1}setunggal jam|]1,Inf[:count jam', + 'minute' => '{1}setunggal menit|]1,Inf[:count menit', + 'second' => '{1}sawetawis detik|]1,Inf[:count detik', + 'ago' => ':time ingkang kepengker', + 'from_now' => 'wonten ing :time', + 'diff_today' => 'Dinten', + 'diff_yesterday' => 'Kala', + 'diff_yesterday_regexp' => 'Kala(?:\\s+wingi)?(?:\\s+pukul)?', + 'diff_tomorrow' => 'Mbenjang', + 'diff_tomorrow_regexp' => 'Mbenjang(?:\\s+pukul)?', + 'diff_today_regexp' => 'Dinten(?:\\s+puniko)?(?:\\s+pukul)?', + 'formats' => [ + 'LT' => 'HH.mm', + 'LTS' => 'HH.mm.ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY [pukul] HH.mm', + 'LLLL' => 'dddd, D MMMM YYYY [pukul] HH.mm', + ], + 'calendar' => [ + 'sameDay' => '[Dinten puniko pukul] LT', + 'nextDay' => '[Mbenjang pukul] LT', + 'nextWeek' => 'dddd [pukul] LT', + 'lastDay' => '[Kala wingi pukul] LT', + 'lastWeek' => 'dddd [kepengker pukul] LT', + 'sameElse' => 'L', + ], + 'meridiem' => function ($hour) { + if ($hour < 11) { + return 'enjing'; + } + if ($hour < 15) { + return 'siyang'; + } + if ($hour < 19) { + return 'sonten'; + } + + return 'ndalu'; + }, + 'months' => ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'Nopember', 'Desember'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Ags', 'Sep', 'Okt', 'Nop', 'Des'], + 'weekdays' => ['Minggu', 'Senen', 'Seloso', 'Rebu', 'Kemis', 'Jemuwah', 'Septu'], + 'weekdays_short' => ['Min', 'Sen', 'Sel', 'Reb', 'Kem', 'Jem', 'Sep'], + 'weekdays_min' => ['Mg', 'Sn', 'Sl', 'Rb', 'Km', 'Jm', 'Sp'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' lan '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ka.php b/vendor/nesbot/carbon/src/Carbon/Lang/ka.php new file mode 100644 index 0000000..a5d563d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ka.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Tornike Razmadze + * - François B + * - Lasha Dolidze + * - Tim Fish + * - JD Isaacks + * - Tornike Razmadze + * - François B + * - Lasha Dolidze + * - JD Isaacks + * - LONGMAN + * - Avtandil Kikabidze (akalongman) + * - Levan Velijanashvili (Stichoza) + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count წელი', + 'y' => ':count წელი', + 'a_year' => '{1}წელი|]1,Inf[:count წელი', + 'month' => ':count თვე', + 'm' => ':count თვე', + 'a_month' => '{1}თვე|]1,Inf[:count თვე', + 'week' => ':count კვირა', + 'w' => ':count კვირა', + 'a_week' => '{1}კვირა|]1,Inf[:count კვირა', + 'day' => ':count დღე', + 'd' => ':count დღე', + 'a_day' => '{1}დღე|]1,Inf[:count დღე', + 'hour' => ':count საათი', + 'h' => ':count საათი', + 'a_hour' => '{1}საათი|]1,Inf[:count საათი', + 'minute' => ':count წუთი', + 'min' => ':count წუთი', + 'a_minute' => '{1}წუთი|]1,Inf[:count წუთი', + 'second' => ':count წამი', + 's' => ':count წამი', + 'a_second' => '{1}რამდენიმე წამი|]1,Inf[:count წამი', + 'ago' => function ($time) { + $replacements = [ + // year + 'წელი' => 'წლის', + // month + 'თვე' => 'თვის', + // week + 'კვირა' => 'კვირის', + // day + 'დღე' => 'დღის', + // hour + 'საათი' => 'საათის', + // minute + 'წუთი' => 'წუთის', + // second + 'წამი' => 'წამის', + ]; + $time = strtr($time, array_flip($replacements)); + $time = strtr($time, $replacements); + + return "$time წინ"; + }, + 'from_now' => function ($time) { + $replacements = [ + // year + 'წელი' => 'წელიწადში', + // week + 'კვირა' => 'კვირაში', + // day + 'დღე' => 'დღეში', + // month + 'თვე' => 'თვეში', + // hour + 'საათი' => 'საათში', + // minute + 'წუთი' => 'წუთში', + // second + 'წამი' => 'წამში', + ]; + $time = strtr($time, array_flip($replacements)); + $time = strtr($time, $replacements); + + return $time; + }, + 'after' => function ($time) { + $replacements = [ + // year + 'წელი' => 'წლის', + // month + 'თვე' => 'თვის', + // week + 'კვირა' => 'კვირის', + // day + 'დღე' => 'დღის', + // hour + 'საათი' => 'საათის', + // minute + 'წუთი' => 'წუთის', + // second + 'წამი' => 'წამის', + ]; + $time = strtr($time, array_flip($replacements)); + $time = strtr($time, $replacements); + + return "$time შემდეგ"; + }, + 'before' => function ($time) { + $replacements = [ + // year + 'წელი' => 'წლით', + // month + 'თვე' => 'თვით', + // week + 'კვირა' => 'კვირით', + // day + 'დღე' => 'დღით', + // hour + 'საათი' => 'საათით', + // minute + 'წუთი' => 'წუთით', + // second + 'წამი' => 'წამით', + ]; + $time = strtr($time, array_flip($replacements)); + $time = strtr($time, $replacements); + + return "$time ადრე"; + }, + 'diff_now' => 'ახლა', + 'diff_today' => 'დღეს', + 'diff_yesterday' => 'გუშინ', + 'diff_tomorrow' => 'ხვალ', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[დღეს], LT[-ზე]', + 'nextDay' => '[ხვალ], LT[-ზე]', + 'nextWeek' => function (CarbonInterface $current, CarbonInterface $other) { + return ($current->isSameWeek($other) ? '' : '[შემდეგ] ').'dddd, LT[-ზე]'; + }, + 'lastDay' => '[გუშინ], LT[-ზე]', + 'lastWeek' => '[წინა] dddd, LT-ზე', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + if ($number === 0) { + return $number; + } + if ($number === 1) { + return $number.'-ლი'; + } + if (($number < 20) || ($number <= 100 && ($number % 20 === 0)) || ($number % 100 === 0)) { + return 'მე-'.$number; + } + + return $number.'-ე'; + }, + 'months' => ['იანვარი', 'თებერვალი', 'მარტი', 'აპრილი', 'მაისი', 'ივნისი', 'ივლისი', 'აგვისტო', 'სექტემბერი', 'ოქტომბერი', 'ნოემბერი', 'დეკემბერი'], + 'months_standalone' => ['იანვარს', 'თებერვალს', 'მარტს', 'აპრილს', 'მაისს', 'ივნისს', 'ივლისს', 'აგვისტოს', 'სექტემბერს', 'ოქტომბერს', 'ნოემბერს', 'დეკემბერს'], + 'months_short' => ['იან', 'თებ', 'მარ', 'აპრ', 'მაი', 'ივნ', 'ივლ', 'აგვ', 'სექ', 'ოქტ', 'ნოე', 'დეკ'], + 'months_regexp' => '/(D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/', + 'weekdays' => ['კვირას', 'ორშაბათს', 'სამშაბათს', 'ოთხშაბათს', 'ხუთშაბათს', 'პარასკევს', 'შაბათს'], + 'weekdays_standalone' => ['კვირა', 'ორშაბათი', 'სამშაბათი', 'ოთხშაბათი', 'ხუთშაბათი', 'პარასკევი', 'შაბათი'], + 'weekdays_short' => ['კვი', 'ორშ', 'სამ', 'ოთხ', 'ხუთ', 'პარ', 'შაბ'], + 'weekdays_min' => ['კვ', 'ორ', 'სა', 'ოთ', 'ხუ', 'პა', 'შა'], + 'weekdays_regexp' => '/^([^d].*|.*[^d])$/', + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' და '], + 'meridiem' => function ($hour) { + if ($hour >= 4) { + if ($hour < 11) { + return 'დილის'; + } + + if ($hour < 16) { + return 'შუადღის'; + } + + if ($hour < 22) { + return 'საღამოს'; + } + } + + return 'ღამის'; + }, +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ka_GE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ka_GE.php new file mode 100644 index 0000000..a26d930 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ka_GE.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ka.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kab.php b/vendor/nesbot/carbon/src/Carbon/Lang/kab.php new file mode 100644 index 0000000..94d6473 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kab.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/kab_DZ.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kab_DZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/kab_DZ.php new file mode 100644 index 0000000..796660b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kab_DZ.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - belkacem77@gmail.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Yennayer', 'Fuṛar', 'Meɣres', 'Yebrir', 'Mayyu', 'Yunyu', 'Yulyu', 'ɣuct', 'Ctembeṛ', 'Tubeṛ', 'Wambeṛ', 'Dujembeṛ'], + 'months_short' => ['Yen', 'Fur', 'Meɣ', 'Yeb', 'May', 'Yun', 'Yul', 'ɣuc', 'Cte', 'Tub', 'Wam', 'Duj'], + 'weekdays' => ['Acer', 'Arim', 'Aram', 'Ahad', 'Amhad', 'Sem', 'Sed'], + 'weekdays_short' => ['Ace', 'Ari', 'Ara', 'Aha', 'Amh', 'Sem', 'Sed'], + 'weekdays_min' => ['Ace', 'Ari', 'Ara', 'Aha', 'Amh', 'Sem', 'Sed'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['FT', 'MD'], + + 'year' => ':count n yiseggasen', + 'y' => ':count n yiseggasen', + 'a_year' => ':count n yiseggasen', + + 'month' => ':count n wayyuren', + 'm' => ':count n wayyuren', + 'a_month' => ':count n wayyuren', + + 'week' => ':count n ledwaṛ', // less reliable + 'w' => ':count n ledwaṛ', // less reliable + 'a_week' => ':count n ledwaṛ', // less reliable + + 'day' => ':count n wussan', + 'd' => ':count n wussan', + 'a_day' => ':count n wussan', + + 'hour' => ':count n tsaɛtin', + 'h' => ':count n tsaɛtin', + 'a_hour' => ':count n tsaɛtin', + + 'minute' => ':count n tedqiqin', + 'min' => ':count n tedqiqin', + 'a_minute' => ':count n tedqiqin', + + 'second' => ':count tasdidt', // less reliable + 's' => ':count tasdidt', // less reliable + 'a_second' => ':count tasdidt', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kam.php b/vendor/nesbot/carbon/src/Carbon/Lang/kam.php new file mode 100644 index 0000000..0fc70d7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kam.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Ĩyakwakya', 'Ĩyawĩoo'], + 'weekdays' => ['Wa kyumwa', 'Wa kwambĩlĩlya', 'Wa kelĩ', 'Wa katatũ', 'Wa kana', 'Wa katano', 'Wa thanthatũ'], + 'weekdays_short' => ['Wky', 'Wkw', 'Wkl', 'Wtũ', 'Wkn', 'Wtn', 'Wth'], + 'weekdays_min' => ['Wky', 'Wkw', 'Wkl', 'Wtũ', 'Wkn', 'Wtn', 'Wth'], + 'months' => ['Mwai wa mbee', 'Mwai wa kelĩ', 'Mwai wa katatũ', 'Mwai wa kana', 'Mwai wa katano', 'Mwai wa thanthatũ', 'Mwai wa muonza', 'Mwai wa nyaanya', 'Mwai wa kenda', 'Mwai wa ĩkumi', 'Mwai wa ĩkumi na ĩmwe', 'Mwai wa ĩkumi na ilĩ'], + 'months_short' => ['Mbe', 'Kel', 'Ktũ', 'Kan', 'Ktn', 'Tha', 'Moo', 'Nya', 'Knd', 'Ĩku', 'Ĩkm', 'Ĩkl'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + + // Too unreliable + /* + 'year' => ':count mbua', // less reliable + 'y' => ':count mbua', // less reliable + 'a_year' => ':count mbua', // less reliable + + 'month' => ':count ndakitali', // less reliable + 'm' => ':count ndakitali', // less reliable + 'a_month' => ':count ndakitali', // less reliable + + 'day' => ':count wia', // less reliable + 'd' => ':count wia', // less reliable + 'a_day' => ':count wia', // less reliable + + 'hour' => ':count orasan', // less reliable + 'h' => ':count orasan', // less reliable + 'a_hour' => ':count orasan', // less reliable + + 'minute' => ':count orasan', // less reliable + 'min' => ':count orasan', // less reliable + 'a_minute' => ':count orasan', // less reliable + */ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kde.php b/vendor/nesbot/carbon/src/Carbon/Lang/kde.php new file mode 100644 index 0000000..fbcc9f3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kde.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Muhi', 'Chilo'], + 'weekdays' => ['Liduva lyapili', 'Liduva lyatatu', 'Liduva lyanchechi', 'Liduva lyannyano', 'Liduva lyannyano na linji', 'Liduva lyannyano na mavili', 'Liduva litandi'], + 'weekdays_short' => ['Ll2', 'Ll3', 'Ll4', 'Ll5', 'Ll6', 'Ll7', 'Ll1'], + 'weekdays_min' => ['Ll2', 'Ll3', 'Ll4', 'Ll5', 'Ll6', 'Ll7', 'Ll1'], + 'months' => ['Mwedi Ntandi', 'Mwedi wa Pili', 'Mwedi wa Tatu', 'Mwedi wa Nchechi', 'Mwedi wa Nnyano', 'Mwedi wa Nnyano na Umo', 'Mwedi wa Nnyano na Mivili', 'Mwedi wa Nnyano na Mitatu', 'Mwedi wa Nnyano na Nchechi', 'Mwedi wa Nnyano na Nnyano', 'Mwedi wa Nnyano na Nnyano na U', 'Mwedi wa Nnyano na Nnyano na M'], + 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kea.php b/vendor/nesbot/carbon/src/Carbon/Lang/kea.php new file mode 100644 index 0000000..8b6c21b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kea.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['a', 'p'], + 'weekdays' => ['dumingu', 'sigunda-fera', 'tersa-fera', 'kuarta-fera', 'kinta-fera', 'sesta-fera', 'sabadu'], + 'weekdays_short' => ['dum', 'sig', 'ter', 'kua', 'kin', 'ses', 'sab'], + 'weekdays_min' => ['du', 'si', 'te', 'ku', 'ki', 'se', 'sa'], + 'weekdays_standalone' => ['dumingu', 'sigunda-fera', 'tersa-fera', 'kuarta-fera', 'kinta-fera', 'sesta-fera', 'sábadu'], + 'months' => ['Janeru', 'Febreru', 'Marsu', 'Abril', 'Maiu', 'Junhu', 'Julhu', 'Agostu', 'Setenbru', 'Otubru', 'Nuvenbru', 'Dizenbru'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Otu', 'Nuv', 'Diz'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D [di] MMMM [di] YYYY HH:mm', + 'LLLL' => 'dddd, D [di] MMMM [di] YYYY HH:mm', + ], + + 'year' => ':count otunu', // less reliable + 'y' => ':count otunu', // less reliable + 'a_year' => ':count otunu', // less reliable + + 'week' => ':count día dumingu', // less reliable + 'w' => ':count día dumingu', // less reliable + 'a_week' => ':count día dumingu', // less reliable + + 'day' => ':count diâ', // less reliable + 'd' => ':count diâ', // less reliable + 'a_day' => ':count diâ', // less reliable + + 'minute' => ':count sugundu', // less reliable + 'min' => ':count sugundu', // less reliable + 'a_minute' => ':count sugundu', // less reliable + + 'second' => ':count dós', // less reliable + 's' => ':count dós', // less reliable + 'a_second' => ':count dós', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/khq.php b/vendor/nesbot/carbon/src/Carbon/Lang/khq.php new file mode 100644 index 0000000..7a834cf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/khq.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Adduha', 'Aluula'], + 'weekdays' => ['Alhadi', 'Atini', 'Atalata', 'Alarba', 'Alhamiisa', 'Aljuma', 'Assabdu'], + 'weekdays_short' => ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alj', 'Ass'], + 'weekdays_min' => ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alj', 'Ass'], + 'months' => ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me', 'Žuweŋ', 'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur', 'Deesanbur'], + 'months_short' => ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy', 'Ut', 'Sek', 'Okt', 'Noo', 'Dee'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ki.php b/vendor/nesbot/carbon/src/Carbon/Lang/ki.php new file mode 100644 index 0000000..d86afc5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ki.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Kiroko', 'Hwaĩ-inĩ'], + 'weekdays' => ['Kiumia', 'Njumatatũ', 'Njumaine', 'Njumatana', 'Aramithi', 'Njumaa', 'Njumamothi'], + 'weekdays_short' => ['KMA', 'NTT', 'NMN', 'NMT', 'ART', 'NMA', 'NMM'], + 'weekdays_min' => ['KMA', 'NTT', 'NMN', 'NMT', 'ART', 'NMA', 'NMM'], + 'months' => ['Njenuarĩ', 'Mwere wa kerĩ', 'Mwere wa gatatũ', 'Mwere wa kana', 'Mwere wa gatano', 'Mwere wa gatandatũ', 'Mwere wa mũgwanja', 'Mwere wa kanana', 'Mwere wa kenda', 'Mwere wa ikũmi', 'Mwere wa ikũmi na ũmwe', 'Ndithemba'], + 'months_short' => ['JEN', 'WKR', 'WGT', 'WKN', 'WTN', 'WTD', 'WMJ', 'WNN', 'WKD', 'WIK', 'WMW', 'DIT'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + + 'year' => ':count mĩaka', // less reliable + 'y' => ':count mĩaka', // less reliable + 'a_year' => ':count mĩaka', // less reliable + + 'month' => ':count mweri', // less reliable + 'm' => ':count mweri', // less reliable + 'a_month' => ':count mweri', // less reliable + + 'week' => ':count kiumia', // less reliable + 'w' => ':count kiumia', // less reliable + 'a_week' => ':count kiumia', // less reliable + + 'day' => ':count mũthenya', // less reliable + 'd' => ':count mũthenya', // less reliable + 'a_day' => ':count mũthenya', // less reliable + + 'hour' => ':count thaa', // less reliable + 'h' => ':count thaa', // less reliable + 'a_hour' => ':count thaa', // less reliable + + 'minute' => ':count mundu', // less reliable + 'min' => ':count mundu', // less reliable + 'a_minute' => ':count mundu', // less reliable + + 'second' => ':count igego', // less reliable + 's' => ':count igego', // less reliable + 'a_second' => ':count igego', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kk.php b/vendor/nesbot/carbon/src/Carbon/Lang/kk.php new file mode 100644 index 0000000..59fa9af --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kk.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - François B + * - Talat Uspanov + * - Нурлан Рахимжанов + * - Toleugazy Kali + */ +return [ + 'year' => ':count жыл', + 'a_year' => '{1}бір жыл|:count жыл', + 'y' => ':count ж.', + 'month' => ':count ай', + 'a_month' => '{1}бір ай|:count ай', + 'm' => ':count ай', + 'week' => ':count апта', + 'a_week' => '{1}бір апта', + 'w' => ':count ап.', + 'day' => ':count күн', + 'a_day' => '{1}бір күн|:count күн', + 'd' => ':count к.', + 'hour' => ':count сағат', + 'a_hour' => '{1}бір сағат|:count сағат', + 'h' => ':count са.', + 'minute' => ':count минут', + 'a_minute' => '{1}бір минут|:count минут', + 'min' => ':count м.', + 'second' => ':count секунд', + 'a_second' => '{1}бірнеше секунд|:count секунд', + 's' => ':count се.', + 'ago' => ':time бұрын', + 'from_now' => ':time ішінде', + 'after' => ':time кейін', + 'before' => ':time бұрын', + 'diff_now' => 'қазір', + 'diff_today' => 'Бүгін', + 'diff_today_regexp' => 'Бүгін(?:\\s+сағат)?', + 'diff_yesterday' => 'кеше', + 'diff_yesterday_regexp' => 'Кеше(?:\\s+сағат)?', + 'diff_tomorrow' => 'ертең', + 'diff_tomorrow_regexp' => 'Ертең(?:\\s+сағат)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Бүгін сағат] LT', + 'nextDay' => '[Ертең сағат] LT', + 'nextWeek' => 'dddd [сағат] LT', + 'lastDay' => '[Кеше сағат] LT', + 'lastWeek' => '[Өткен аптаның] dddd [сағат] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + static $suffixes = [ + 0 => '-ші', + 1 => '-ші', + 2 => '-ші', + 3 => '-ші', + 4 => '-ші', + 5 => '-ші', + 6 => '-шы', + 7 => '-ші', + 8 => '-ші', + 9 => '-шы', + 10 => '-шы', + 20 => '-шы', + 30 => '-шы', + 40 => '-шы', + 50 => '-ші', + 60 => '-шы', + 70 => '-ші', + 80 => '-ші', + 90 => '-шы', + 100 => '-ші', + ]; + + return $number.($suffixes[$number] ?? $suffixes[$number % 10] ?? $suffixes[$number >= 100 ? 100 : -1] ?? ''); + }, + 'months' => ['қаңтар', 'ақпан', 'наурыз', 'сәуір', 'мамыр', 'маусым', 'шілде', 'тамыз', 'қыркүйек', 'қазан', 'қараша', 'желтоқсан'], + 'months_short' => ['қаң', 'ақп', 'нау', 'сәу', 'мам', 'мау', 'шіл', 'там', 'қыр', 'қаз', 'қар', 'жел'], + 'weekdays' => ['жексенбі', 'дүйсенбі', 'сейсенбі', 'сәрсенбі', 'бейсенбі', 'жұма', 'сенбі'], + 'weekdays_short' => ['жек', 'дүй', 'сей', 'сәр', 'бей', 'жұм', 'сен'], + 'weekdays_min' => ['жк', 'дй', 'сй', 'ср', 'бй', 'жм', 'сн'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' және '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kk_KZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/kk_KZ.php new file mode 100644 index 0000000..7dc5ebc --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kk_KZ.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/kk.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kkj.php b/vendor/nesbot/carbon/src/Carbon/Lang/kkj.php new file mode 100644 index 0000000..6a1e77a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kkj.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kl.php b/vendor/nesbot/carbon/src/Carbon/Lang/kl.php new file mode 100644 index 0000000..7329a07 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kl.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/kl_GL.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kl_GL.php b/vendor/nesbot/carbon/src/Carbon/Lang/kl_GL.php new file mode 100644 index 0000000..4fed720 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kl_GL.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Danish Standards Association bug-glibc-locales@gnu.org + * - John Eyðstein Johannesen (mashema) + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY HH:mm', + 'LLLL' => 'dddd [d.] D. MMMM YYYY [kl.] HH:mm', + ], + 'months' => ['januaarip', 'februaarip', 'marsip', 'apriilip', 'maajip', 'juunip', 'juulip', 'aggustip', 'septembarip', 'oktobarip', 'novembarip', 'decembarip'], + 'months_short' => ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'], + 'weekdays' => ['sapaat', 'ataasinngorneq', 'marlunngorneq', 'pingasunngorneq', 'sisamanngorneq', 'tallimanngorneq', 'arfininngorneq'], + 'weekdays_short' => ['sap', 'ata', 'mar', 'pin', 'sis', 'tal', 'arf'], + 'weekdays_min' => ['sap', 'ata', 'mar', 'pin', 'sis', 'tal', 'arf'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'year' => '{1}ukioq :count|{0}:count ukiut|]1,Inf[ukiut :count', + 'a_year' => '{1}ukioq|{0}:count ukiut|]1,Inf[ukiut :count', + 'y' => '{1}:countyr|{0}:countyrs|]1,Inf[:countyrs', + + 'month' => '{1}qaammat :count|{0}:count qaammatit|]1,Inf[qaammatit :count', + 'a_month' => '{1}qaammat|{0}:count qaammatit|]1,Inf[qaammatit :count', + 'm' => '{1}:countmo|{0}:countmos|]1,Inf[:countmos', + + 'week' => '{1}:count sap. ak.|{0}:count sap. ak.|]1,Inf[:count sap. ak.', + 'a_week' => '{1}a sap. ak.|{0}:count sap. ak.|]1,Inf[:count sap. ak.', + 'w' => ':countw', + + 'day' => '{1}:count ulloq|{0}:count ullut|]1,Inf[:count ullut', + 'a_day' => '{1}a ulloq|{0}:count ullut|]1,Inf[:count ullut', + 'd' => ':countd', + + 'hour' => '{1}:count tiimi|{0}:count tiimit|]1,Inf[:count tiimit', + 'a_hour' => '{1}tiimi|{0}:count tiimit|]1,Inf[:count tiimit', + 'h' => ':counth', + + 'minute' => '{1}:count minutsi|{0}:count minutsit|]1,Inf[:count minutsit', + 'a_minute' => '{1}a minutsi|{0}:count minutsit|]1,Inf[:count minutsit', + 'min' => ':countm', + + 'second' => '{1}:count sikunti|{0}:count sikuntit|]1,Inf[:count sikuntit', + 'a_second' => '{1}sikunti|{0}:count sikuntit|]1,Inf[:count sikuntit', + 's' => ':counts', + + 'ago' => ':time matuma siorna', + +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kln.php b/vendor/nesbot/carbon/src/Carbon/Lang/kln.php new file mode 100644 index 0000000..b9c3996 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kln.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['krn', 'koosk'], + 'weekdays' => ['Kotisap', 'Kotaai', 'Koaeng’', 'Kosomok', 'Koang’wan', 'Komuut', 'Kolo'], + 'weekdays_short' => ['Kts', 'Kot', 'Koo', 'Kos', 'Koa', 'Kom', 'Kol'], + 'weekdays_min' => ['Kts', 'Kot', 'Koo', 'Kos', 'Koa', 'Kom', 'Kol'], + 'months' => ['Mulgul', 'Ng’atyaato', 'Kiptaamo', 'Iwootkuut', 'Mamuut', 'Paagi', 'Ng’eiyeet', 'Rooptui', 'Bureet', 'Epeeso', 'Kipsuunde ne taai', 'Kipsuunde nebo aeng’'], + 'months_short' => ['Mul', 'Ngat', 'Taa', 'Iwo', 'Mam', 'Paa', 'Nge', 'Roo', 'Bur', 'Epe', 'Kpt', 'Kpa'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + + 'year' => ':count maghatiat', // less reliable + 'y' => ':count maghatiat', // less reliable + 'a_year' => ':count maghatiat', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/km.php b/vendor/nesbot/carbon/src/Carbon/Lang/km.php new file mode 100644 index 0000000..da790ac --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/km.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Kruy Vanna + * - Sereysethy Touch + * - JD Isaacks + * - Sovichet Tep + */ +return [ + 'year' => '{1}មួយឆ្នាំ|]1,Inf[:count ឆ្នាំ', + 'y' => ':count ឆ្នាំ', + 'month' => '{1}មួយខែ|]1,Inf[:count ខែ', + 'm' => ':count ខែ', + 'week' => ':count សប្ដាហ៍', + 'w' => ':count សប្ដាហ៍', + 'day' => '{1}មួយថ្ងៃ|]1,Inf[:count ថ្ងៃ', + 'd' => ':count ថ្ងៃ', + 'hour' => '{1}មួយម៉ោង|]1,Inf[:count ម៉ោង', + 'h' => ':count ម៉ោង', + 'minute' => '{1}មួយនាទី|]1,Inf[:count នាទី', + 'min' => ':count នាទី', + 'second' => '{1}ប៉ុន្មានវិនាទី|]1,Inf[:count វិនាទី', + 's' => ':count វិនាទី', + 'ago' => ':timeមុន', + 'from_now' => ':timeទៀត', + 'after' => 'នៅ​ក្រោយ :time', + 'before' => 'នៅ​មុន :time', + 'diff_now' => 'ឥឡូវ', + 'diff_today' => 'ថ្ងៃនេះ', + 'diff_today_regexp' => 'ថ្ងៃនេះ(?:\\s+ម៉ោង)?', + 'diff_yesterday' => 'ម្សិលមិញ', + 'diff_yesterday_regexp' => 'ម្សិលមិញ(?:\\s+ម៉ោង)?', + 'diff_tomorrow' => 'ថ្ងៃ​ស្អែក', + 'diff_tomorrow_regexp' => 'ស្អែក(?:\\s+ម៉ោង)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[ថ្ងៃនេះ ម៉ោង] LT', + 'nextDay' => '[ស្អែក ម៉ោង] LT', + 'nextWeek' => 'dddd [ម៉ោង] LT', + 'lastDay' => '[ម្សិលមិញ ម៉ោង] LT', + 'lastWeek' => 'dddd [សប្តាហ៍មុន] [ម៉ោង] LT', + 'sameElse' => 'L', + ], + 'ordinal' => 'ទី:number', + 'meridiem' => ['ព្រឹក', 'ល្ងាច'], + 'months' => ['មករា', 'កុម្ភៈ', 'មីនា', 'មេសា', 'ឧសភា', 'មិថុនា', 'កក្កដា', 'សីហា', 'កញ្ញា', 'តុលា', 'វិច្ឆិកា', 'ធ្នូ'], + 'months_short' => ['មករា', 'កុម្ភៈ', 'មីនា', 'មេសា', 'ឧសភា', 'មិថុនា', 'កក្កដា', 'សីហា', 'កញ្ញា', 'តុលា', 'វិច្ឆិកា', 'ធ្នូ'], + 'weekdays' => ['អាទិត្យ', 'ច័ន្ទ', 'អង្គារ', 'ពុធ', 'ព្រហស្បតិ៍', 'សុក្រ', 'សៅរ៍'], + 'weekdays_short' => ['អា', 'ច', 'អ', 'ព', 'ព្រ', 'សុ', 'ស'], + 'weekdays_min' => ['អា', 'ច', 'អ', 'ព', 'ព្រ', 'សុ', 'ស'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', 'និង '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/km_KH.php b/vendor/nesbot/carbon/src/Carbon/Lang/km_KH.php new file mode 100644 index 0000000..92e5fdb --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/km_KH.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/km.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kn.php b/vendor/nesbot/carbon/src/Carbon/Lang/kn.php new file mode 100644 index 0000000..0d2ad08 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kn.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - MOHAN M U + * - François B + * - rajeevnaikte + */ +return [ + 'year' => '{1}ಒಂದು ವರ್ಷ|]1,Inf[:count ವರ್ಷ', + 'month' => '{1}ಒಂದು ತಿಂಗಳು|]1,Inf[:count ತಿಂಗಳು', + 'week' => '{1}ಒಂದು ವಾರ|]1,Inf[:count ವಾರಗಳು', + 'day' => '{1}ಒಂದು ದಿನ|]1,Inf[:count ದಿನ', + 'hour' => '{1}ಒಂದು ಗಂಟೆ|]1,Inf[:count ಗಂಟೆ', + 'minute' => '{1}ಒಂದು ನಿಮಿಷ|]1,Inf[:count ನಿಮಿಷ', + 'second' => '{1}ಕೆಲವು ಕ್ಷಣಗಳು|]1,Inf[:count ಸೆಕೆಂಡುಗಳು', + 'ago' => ':time ಹಿಂದೆ', + 'from_now' => ':time ನಂತರ', + 'diff_now' => 'ಈಗ', + 'diff_today' => 'ಇಂದು', + 'diff_yesterday' => 'ನಿನ್ನೆ', + 'diff_tomorrow' => 'ನಾಳೆ', + 'formats' => [ + 'LT' => 'A h:mm', + 'LTS' => 'A h:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY, A h:mm', + 'LLLL' => 'dddd, D MMMM YYYY, A h:mm', + ], + 'calendar' => [ + 'sameDay' => '[ಇಂದು] LT', + 'nextDay' => '[ನಾಳೆ] LT', + 'nextWeek' => 'dddd, LT', + 'lastDay' => '[ನಿನ್ನೆ] LT', + 'lastWeek' => '[ಕೊನೆಯ] dddd, LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':numberನೇ', + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'ರಾತ್ರಿ'; + } + if ($hour < 10) { + return 'ಬೆಳಿಗ್ಗೆ'; + } + if ($hour < 17) { + return 'ಮಧ್ಯಾಹ್ನ'; + } + if ($hour < 20) { + return 'ಸಂಜೆ'; + } + + return 'ರಾತ್ರಿ'; + }, + 'months' => ['ಜನವರಿ', 'ಫೆಬ್ರವರಿ', 'ಮಾರ್ಚ್', 'ಏಪ್ರಿಲ್', 'ಮೇ', 'ಜೂನ್', 'ಜುಲೈ', 'ಆಗಸ್ಟ್', 'ಸೆಪ್ಟೆಂಬರ್', 'ಅಕ್ಟೋಬರ್', 'ನವೆಂಬರ್', 'ಡಿಸೆಂಬರ್'], + 'months_short' => ['ಜನ', 'ಫೆಬ್ರ', 'ಮಾರ್ಚ್', 'ಏಪ್ರಿಲ್', 'ಮೇ', 'ಜೂನ್', 'ಜುಲೈ', 'ಆಗಸ್ಟ್', 'ಸೆಪ್ಟೆಂ', 'ಅಕ್ಟೋ', 'ನವೆಂ', 'ಡಿಸೆಂ'], + 'weekdays' => ['ಭಾನುವಾರ', 'ಸೋಮವಾರ', 'ಮಂಗಳವಾರ', 'ಬುಧವಾರ', 'ಗುರುವಾರ', 'ಶುಕ್ರವಾರ', 'ಶನಿವಾರ'], + 'weekdays_short' => ['ಭಾನು', 'ಸೋಮ', 'ಮಂಗಳ', 'ಬುಧ', 'ಗುರು', 'ಶುಕ್ರ', 'ಶನಿ'], + 'weekdays_min' => ['ಭಾ', 'ಸೋ', 'ಮಂ', 'ಬು', 'ಗು', 'ಶು', 'ಶ'], + 'list' => ', ', + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, + 'weekend' => [0, 0], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kn_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/kn_IN.php new file mode 100644 index 0000000..30e3d88 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kn_IN.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/kn.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ko.php b/vendor/nesbot/carbon/src/Carbon/Lang/ko.php new file mode 100644 index 0000000..4fa6237 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ko.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Kunal Marwaha + * - FourwingsY + * - François B + * - Jason Katz-Brown + * - Seokjun Kim + * - Junho Kim + * - JD Isaacks + * - Juwon Kim + */ +return [ + 'year' => ':count년', + 'a_year' => '{1}일년|]1,Inf[:count년', + 'y' => ':count년', + 'month' => ':count개월', + 'a_month' => '{1}한달|]1,Inf[:count개월', + 'm' => ':count개월', + 'week' => ':count주', + 'a_week' => '{1}일주일|]1,Inf[:count 주', + 'w' => ':count주일', + 'day' => ':count일', + 'a_day' => '{1}하루|]1,Inf[:count일', + 'd' => ':count일', + 'hour' => ':count시간', + 'a_hour' => '{1}한시간|]1,Inf[:count시간', + 'h' => ':count시간', + 'minute' => ':count분', + 'a_minute' => '{1}일분|]1,Inf[:count분', + 'min' => ':count분', + 'second' => ':count초', + 'a_second' => '{1}몇초|]1,Inf[:count초', + 's' => ':count초', + 'ago' => ':time 전', + 'from_now' => ':time 후', + 'after' => ':time 후', + 'before' => ':time 전', + 'diff_now' => '지금', + 'diff_today' => '오늘', + 'diff_yesterday' => '어제', + 'diff_tomorrow' => '내일', + 'formats' => [ + 'LT' => 'A h:mm', + 'LTS' => 'A h:mm:ss', + 'L' => 'YYYY.MM.DD.', + 'LL' => 'YYYY년 MMMM D일', + 'LLL' => 'YYYY년 MMMM D일 A h:mm', + 'LLLL' => 'YYYY년 MMMM D일 dddd A h:mm', + ], + 'calendar' => [ + 'sameDay' => '오늘 LT', + 'nextDay' => '내일 LT', + 'nextWeek' => 'dddd LT', + 'lastDay' => '어제 LT', + 'lastWeek' => '지난주 dddd LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number, $period) { + switch ($period) { + case 'd': + case 'D': + case 'DDD': + return $number.'일'; + case 'M': + return $number.'월'; + case 'w': + case 'W': + return $number.'주'; + default: + return $number; + } + }, + 'meridiem' => ['오전', '오후'], + 'months' => ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'], + 'months_short' => ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'], + 'weekdays' => ['일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일'], + 'weekdays_short' => ['일', '월', '화', '수', '목', '금', '토'], + 'weekdays_min' => ['일', '월', '화', '수', '목', '금', '토'], + 'list' => ' ', +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ko_KP.php b/vendor/nesbot/carbon/src/Carbon/Lang/ko_KP.php new file mode 100644 index 0000000..4ba802b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ko_KP.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ko.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ko_KR.php b/vendor/nesbot/carbon/src/Carbon/Lang/ko_KR.php new file mode 100644 index 0000000..9d873a2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ko_KR.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ko.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kok.php b/vendor/nesbot/carbon/src/Carbon/Lang/kok.php new file mode 100644 index 0000000..4adcddc --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kok.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/kok_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kok_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/kok_IN.php new file mode 100644 index 0000000..92ba844 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kok_IN.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Red Hat, Pune bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D-M-YY', + ], + 'months' => ['जानेवारी', 'फेब्रुवारी', 'मार्च', 'एप्रिल', 'मे', 'जून', 'जुलै', 'ओगस्ट', 'सेप्टेंबर', 'ओक्टोबर', 'नोव्हेंबर', 'डिसेंबर'], + 'months_short' => ['जानेवारी', 'फेब्रुवारी', 'मार्च', 'एप्रिल', 'मे', 'जून', 'जुलै', 'ओगस्ट', 'सेप्टेंबर', 'ओक्टोबर', 'नोव्हेंबर', 'डिसेंबर'], + 'weekdays' => ['आयतार', 'सोमार', 'मंगळवार', 'बुधवार', 'बेरेसतार', 'शुकरार', 'शेनवार'], + 'weekdays_short' => ['आयतार', 'सोमार', 'मंगळवार', 'बुधवार', 'बेरेसतार', 'शुकरार', 'शेनवार'], + 'weekdays_min' => ['आयतार', 'सोमार', 'मंगळवार', 'बुधवार', 'बेरेसतार', 'शुकरार', 'शेनवार'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['म.पू.', 'म.नं.'], + + 'year' => ':count वैशाकु', // less reliable + 'y' => ':count वैशाकु', // less reliable + 'a_year' => ':count वैशाकु', // less reliable + + 'week' => ':count आदित्यवार', // less reliable + 'w' => ':count आदित्यवार', // less reliable + 'a_week' => ':count आदित्यवार', // less reliable + + 'minute' => ':count नोंद', // less reliable + 'min' => ':count नोंद', // less reliable + 'a_minute' => ':count नोंद', // less reliable + + 'second' => ':count तेंको', // less reliable + 's' => ':count तेंको', // less reliable + 'a_second' => ':count तेंको', // less reliable + + 'month' => ':count मैनो', + 'm' => ':count मैनो', + 'a_month' => ':count मैनो', + + 'day' => ':count दिवसु', + 'd' => ':count दिवसु', + 'a_day' => ':count दिवसु', + + 'hour' => ':count घंते', + 'h' => ':count घंते', + 'a_hour' => ':count घंते', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ks.php b/vendor/nesbot/carbon/src/Carbon/Lang/ks.php new file mode 100644 index 0000000..9876079 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ks.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/ks_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN.php new file mode 100644 index 0000000..ce9d5d4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Red Hat, Pune bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'M/D/YY', + ], + 'months' => ['جنؤری', 'فرؤری', 'مارٕچ', 'اپریل', 'میٔ', 'جوٗن', 'جوٗلایی', 'اگست', 'ستمبر', 'اکتوٗبر', 'نومبر', 'دسمبر'], + 'months_short' => ['جنؤری', 'فرؤری', 'مارٕچ', 'اپریل', 'میٔ', 'جوٗن', 'جوٗلایی', 'اگست', 'ستمبر', 'اکتوٗبر', 'نومبر', 'دسمبر'], + 'weekdays' => ['آتهوار', 'ژءندروار', 'بوءںوار', 'بودهوار', 'برىسوار', 'جمع', 'بٹوار'], + 'weekdays_short' => ['آتهوار', 'ژءنتروار', 'بوءںوار', 'بودهوار', 'برىسوار', 'جمع', 'بٹوار'], + 'weekdays_min' => ['آتهوار', 'ژءنتروار', 'بوءںوار', 'بودهوار', 'برىسوار', 'جمع', 'بٹوار'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['دوپھربرونھ', 'دوپھرپتھ'], + + 'year' => ':count آب', // less reliable + 'y' => ':count آب', // less reliable + 'a_year' => ':count آب', // less reliable + + 'month' => ':count रान्', // less reliable + 'm' => ':count रान्', // less reliable + 'a_month' => ':count रान्', // less reliable + + 'week' => ':count آتھٕوار', // less reliable + 'w' => ':count آتھٕوار', // less reliable + 'a_week' => ':count آتھٕوار', // less reliable + + 'hour' => ':count سۄن', // less reliable + 'h' => ':count سۄن', // less reliable + 'a_hour' => ':count سۄن', // less reliable + + 'minute' => ':count فَن', // less reliable + 'min' => ':count فَن', // less reliable + 'a_minute' => ':count فَن', // less reliable + + 'second' => ':count दोʼयुम', // less reliable + 's' => ':count दोʼयुम', // less reliable + 'a_second' => ':count दोʼयुम', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN@devanagari.php b/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN@devanagari.php new file mode 100644 index 0000000..a2ae8b6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN@devanagari.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - ks-gnome-trans-commits@lists.code.indlinux.net + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'M/D/YY', + ], + 'months' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'], + 'months_short' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'], + 'weekdays' => ['आथवार', 'चॅ़दुरवार', 'बोमवार', 'ब्वदवार', 'ब्रसवार', 'शोकुरवार', 'बटुवार'], + 'weekdays_short' => ['आथ ', 'चॅ़दुर', 'बोम', 'ब्वद', 'ब्रस', 'शोकुर', 'बटु'], + 'weekdays_min' => ['आथ ', 'चॅ़दुर', 'बोम', 'ब्वद', 'ब्रस', 'शोकुर', 'बटु'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ksb.php b/vendor/nesbot/carbon/src/Carbon/Lang/ksb.php new file mode 100644 index 0000000..aaa0061 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ksb.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['makeo', 'nyiaghuo'], + 'weekdays' => ['Jumaapii', 'Jumaatatu', 'Jumaane', 'Jumaatano', 'Alhamisi', 'Ijumaa', 'Jumaamosi'], + 'weekdays_short' => ['Jpi', 'Jtt', 'Jmn', 'Jtn', 'Alh', 'Iju', 'Jmo'], + 'weekdays_min' => ['Jpi', 'Jtt', 'Jmn', 'Jtn', 'Alh', 'Iju', 'Jmo'], + 'months' => ['Januali', 'Febluali', 'Machi', 'Aplili', 'Mei', 'Juni', 'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'], + 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ksf.php b/vendor/nesbot/carbon/src/Carbon/Lang/ksf.php new file mode 100644 index 0000000..84a5967 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ksf.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['sárúwá', 'cɛɛ́nko'], + 'weekdays' => ['sɔ́ndǝ', 'lǝndí', 'maadí', 'mɛkrɛdí', 'jǝǝdí', 'júmbá', 'samdí'], + 'weekdays_short' => ['sɔ́n', 'lǝn', 'maa', 'mɛk', 'jǝǝ', 'júm', 'sam'], + 'weekdays_min' => ['sɔ́n', 'lǝn', 'maa', 'mɛk', 'jǝǝ', 'júm', 'sam'], + 'months' => ['ŋwíí a ntɔ́ntɔ', 'ŋwíí akǝ bɛ́ɛ', 'ŋwíí akǝ ráá', 'ŋwíí akǝ nin', 'ŋwíí akǝ táan', 'ŋwíí akǝ táafɔk', 'ŋwíí akǝ táabɛɛ', 'ŋwíí akǝ táaraa', 'ŋwíí akǝ táanin', 'ŋwíí akǝ ntɛk', 'ŋwíí akǝ ntɛk di bɔ́k', 'ŋwíí akǝ ntɛk di bɛ́ɛ'], + 'months_short' => ['ŋ1', 'ŋ2', 'ŋ3', 'ŋ4', 'ŋ5', 'ŋ6', 'ŋ7', 'ŋ8', 'ŋ9', 'ŋ10', 'ŋ11', 'ŋ12'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ksh.php b/vendor/nesbot/carbon/src/Carbon/Lang/ksh.php new file mode 100644 index 0000000..95457e2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ksh.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['v.M.', 'n.M.'], + 'weekdays' => ['Sunndaach', 'Mohndaach', 'Dinnsdaach', 'Metwoch', 'Dunnersdaach', 'Friidaach', 'Samsdaach'], + 'weekdays_short' => ['Su.', 'Mo.', 'Di.', 'Me.', 'Du.', 'Fr.', 'Sa.'], + 'weekdays_min' => ['Su', 'Mo', 'Di', 'Me', 'Du', 'Fr', 'Sa'], + 'months' => ['Jannewa', 'Fäbrowa', 'Määz', 'Aprell', 'Mai', 'Juuni', 'Juuli', 'Oujoß', 'Septämber', 'Oktohber', 'Novämber', 'Dezämber'], + 'months_short' => ['Jan', 'Fäb', 'Mäz', 'Apr', 'Mai', 'Jun', 'Jul', 'Ouj', 'Säp', 'Okt', 'Nov', 'Dez'], + 'months_short_standalone' => ['Jan.', 'Fäb.', 'Mäz.', 'Apr.', 'Mai', 'Jun.', 'Jul.', 'Ouj.', 'Säp.', 'Okt.', 'Nov.', 'Dez.'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D. M. YYYY', + 'LL' => 'D. MMM. YYYY', + 'LLL' => 'D. MMMM YYYY HH:mm', + 'LLLL' => 'dddd, [dä] D. MMMM YYYY HH:mm', + ], + + 'year' => ':count Johr', + 'y' => ':count Johr', + 'a_year' => ':count Johr', + + 'month' => ':count Moohnd', + 'm' => ':count Moohnd', + 'a_month' => ':count Moohnd', + + 'week' => ':count woch', + 'w' => ':count woch', + 'a_week' => ':count woch', + + 'day' => ':count Daach', + 'd' => ':count Daach', + 'a_day' => ':count Daach', + + 'hour' => ':count Uhr', + 'h' => ':count Uhr', + 'a_hour' => ':count Uhr', + + 'minute' => ':count Menutt', + 'min' => ':count Menutt', + 'a_minute' => ':count Menutt', + + 'second' => ':count Sekůndt', + 's' => ':count Sekůndt', + 'a_second' => ':count Sekůndt', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ku.php b/vendor/nesbot/carbon/src/Carbon/Lang/ku.php new file mode 100644 index 0000000..189960c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ku.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Unicode, Inc. + */ + +return [ + 'ago' => 'berî :time', + 'from_now' => 'di :time de', + 'after' => ':time piştî', + 'before' => ':time berê', + 'year' => ':count sal', + 'year_ago' => ':count salê|:count salan', + 'year_from_now' => 'salekê|:count salan', + 'month' => ':count meh', + 'week' => ':count hefte', + 'day' => ':count roj', + 'hour' => ':count saet', + 'minute' => ':count deqîqe', + 'second' => ':count saniye', + 'months' => ['rêbendanê', 'reşemiyê', 'adarê', 'avrêlê', 'gulanê', 'pûşperê', 'tîrmehê', 'gelawêjê', 'rezberê', 'kewçêrê', 'sermawezê', 'berfanbarê'], + 'months_standalone' => ['rêbendan', 'reşemî', 'adar', 'avrêl', 'gulan', 'pûşper', 'tîrmeh', 'gelawêj', 'rezber', 'kewçêr', 'sermawez', 'berfanbar'], + 'months_short' => ['rêb', 'reş', 'ada', 'avr', 'gul', 'pûş', 'tîr', 'gel', 'rez', 'kew', 'ser', 'ber'], + 'weekdays' => ['yekşem', 'duşem', 'sêşem', 'çarşem', 'pêncşem', 'în', 'şemî'], + 'weekdays_short' => ['yş', 'dş', 'sş', 'çş', 'pş', 'în', 'ş'], + 'weekdays_min' => ['Y', 'D', 'S', 'Ç', 'P', 'Î', 'Ş'], + 'list' => [', ', ' û '], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ku_TR.php b/vendor/nesbot/carbon/src/Carbon/Lang/ku_TR.php new file mode 100644 index 0000000..4243a82 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ku_TR.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ku.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kw.php b/vendor/nesbot/carbon/src/Carbon/Lang/kw.php new file mode 100644 index 0000000..26e242e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kw.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/kw_GB.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kw_GB.php b/vendor/nesbot/carbon/src/Carbon/Lang/kw_GB.php new file mode 100644 index 0000000..00bf52b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kw_GB.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Alastair McKinstry bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['mis Genver', 'mis Hwevrer', 'mis Meurth', 'mis Ebrel', 'mis Me', 'mis Metheven', 'mis Gortheren', 'mis Est', 'mis Gwynngala', 'mis Hedra', 'mis Du', 'mis Kevardhu'], + 'months_short' => ['Gen', 'Hwe', 'Meu', 'Ebr', 'Me', 'Met', 'Gor', 'Est', 'Gwn', 'Hed', 'Du', 'Kev'], + 'weekdays' => ['De Sul', 'De Lun', 'De Merth', 'De Merher', 'De Yow', 'De Gwener', 'De Sadorn'], + 'weekdays_short' => ['Sul', 'Lun', 'Mth', 'Mhr', 'Yow', 'Gwe', 'Sad'], + 'weekdays_min' => ['Sul', 'Lun', 'Mth', 'Mhr', 'Yow', 'Gwe', 'Sad'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'year' => ':count bledhen', + 'y' => ':count bledhen', + 'a_year' => ':count bledhen', + + 'month' => ':count mis', + 'm' => ':count mis', + 'a_month' => ':count mis', + + 'week' => ':count seythen', + 'w' => ':count seythen', + 'a_week' => ':count seythen', + + 'day' => ':count dydh', + 'd' => ':count dydh', + 'a_day' => ':count dydh', + + 'hour' => ':count eur', + 'h' => ':count eur', + 'a_hour' => ':count eur', + + 'minute' => ':count mynysen', + 'min' => ':count mynysen', + 'a_minute' => ':count mynysen', + + 'second' => ':count pryjwyth', + 's' => ':count pryjwyth', + 'a_second' => ':count pryjwyth', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ky.php b/vendor/nesbot/carbon/src/Carbon/Lang/ky.php new file mode 100644 index 0000000..e0d1af1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ky.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - acutexyz + * - Josh Soref + * - François B + * - Chyngyz Arystan uulu + * - Chyngyz + * - acutexyz + * - Josh Soref + * - François B + * - Chyngyz Arystan uulu + */ +return [ + 'year' => ':count жыл', + 'a_year' => '{1}бир жыл|:count жыл', + 'y' => ':count жыл', + 'month' => ':count ай', + 'a_month' => '{1}бир ай|:count ай', + 'm' => ':count ай', + 'week' => ':count апта', + 'a_week' => '{1}бир апта|:count апта', + 'w' => ':count апт.', + 'day' => ':count күн', + 'a_day' => '{1}бир күн|:count күн', + 'd' => ':count күн', + 'hour' => ':count саат', + 'a_hour' => '{1}бир саат|:count саат', + 'h' => ':count саат.', + 'minute' => ':count мүнөт', + 'a_minute' => '{1}бир мүнөт|:count мүнөт', + 'min' => ':count мүн.', + 'second' => ':count секунд', + 'a_second' => '{1}бирнече секунд|:count секунд', + 's' => ':count сек.', + 'ago' => ':time мурун', + 'from_now' => ':time ичинде', + 'diff_now' => 'азыр', + 'diff_today' => 'Бүгүн', + 'diff_today_regexp' => 'Бүгүн(?:\\s+саат)?', + 'diff_yesterday' => 'кечээ', + 'diff_yesterday_regexp' => 'Кече(?:\\s+саат)?', + 'diff_tomorrow' => 'эртең', + 'diff_tomorrow_regexp' => 'Эртең(?:\\s+саат)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Бүгүн саат] LT', + 'nextDay' => '[Эртең саат] LT', + 'nextWeek' => 'dddd [саат] LT', + 'lastDay' => '[Кече саат] LT', + 'lastWeek' => '[Өткен аптанын] dddd [күнү] [саат] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + static $suffixes = [ + 0 => '-чү', + 1 => '-чи', + 2 => '-чи', + 3 => '-чү', + 4 => '-чү', + 5 => '-чи', + 6 => '-чы', + 7 => '-чи', + 8 => '-чи', + 9 => '-чу', + 10 => '-чу', + 20 => '-чы', + 30 => '-чу', + 40 => '-чы', + 50 => '-чү', + 60 => '-чы', + 70 => '-чи', + 80 => '-чи', + 90 => '-чу', + 100 => '-чү', + ]; + + return $number.($suffixes[$number] ?? $suffixes[$number % 10] ?? $suffixes[$number >= 100 ? 100 : -1] ?? ''); + }, + 'months' => ['январь', 'февраль', 'март', 'апрель', 'май', 'июнь', 'июль', 'август', 'сентябрь', 'октябрь', 'ноябрь', 'декабрь'], + 'months_short' => ['янв', 'фев', 'март', 'апр', 'май', 'июнь', 'июль', 'авг', 'сен', 'окт', 'ноя', 'дек'], + 'weekdays' => ['Жекшемби', 'Дүйшөмбү', 'Шейшемби', 'Шаршемби', 'Бейшемби', 'Жума', 'Ишемби'], + 'weekdays_short' => ['Жек', 'Дүй', 'Шей', 'Шар', 'Бей', 'Жум', 'Ише'], + 'weekdays_min' => ['Жк', 'Дй', 'Шй', 'Шр', 'Бй', 'Жм', 'Иш'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => ' ', + 'meridiem' => ['таңкы', 'түштөн кийинки'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ky_KG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ky_KG.php new file mode 100644 index 0000000..9923a31 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ky_KG.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ky.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lag.php b/vendor/nesbot/carbon/src/Carbon/Lang/lag.php new file mode 100644 index 0000000..f3f57f6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lag.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['TOO', 'MUU'], + 'weekdays' => ['Jumapíiri', 'Jumatátu', 'Jumaíne', 'Jumatáano', 'Alamíisi', 'Ijumáa', 'Jumamóosi'], + 'weekdays_short' => ['Píili', 'Táatu', 'Íne', 'Táano', 'Alh', 'Ijm', 'Móosi'], + 'weekdays_min' => ['Píili', 'Táatu', 'Íne', 'Táano', 'Alh', 'Ijm', 'Móosi'], + 'months' => ['Kʉfúngatɨ', 'Kʉnaanɨ', 'Kʉkeenda', 'Kwiikumi', 'Kwiinyambála', 'Kwiidwaata', 'Kʉmʉʉnchɨ', 'Kʉvɨɨrɨ', 'Kʉsaatʉ', 'Kwiinyi', 'Kʉsaano', 'Kʉsasatʉ'], + 'months_short' => ['Fúngatɨ', 'Naanɨ', 'Keenda', 'Ikúmi', 'Inyambala', 'Idwaata', 'Mʉʉnchɨ', 'Vɨɨrɨ', 'Saatʉ', 'Inyi', 'Saano', 'Sasatʉ'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lb.php b/vendor/nesbot/carbon/src/Carbon/Lang/lb.php new file mode 100644 index 0000000..7636655 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lb.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Philippe Vaucher + * - Tsutomu Kuroda + * - dan-nl + * - Simon Lelorrain (slelorrain) + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count Joer', + 'y' => ':countJ', + 'month' => ':count Mount|:count Méint', + 'm' => ':countMo', + 'week' => ':count Woch|:count Wochen', + 'w' => ':countWo|:countWo', + 'day' => ':count Dag|:count Deeg', + 'd' => ':countD', + 'hour' => ':count Stonn|:count Stonnen', + 'h' => ':countSto', + 'minute' => ':count Minutt|:count Minutten', + 'min' => ':countM', + 'second' => ':count Sekonn|:count Sekonnen', + 's' => ':countSek', + + 'ago' => 'virun :time', + 'from_now' => 'an :time', + 'before' => ':time virdrun', + 'after' => ':time duerno', + + 'diff_today' => 'Haut', + 'diff_yesterday' => 'Gëschter', + 'diff_yesterday_regexp' => 'Gëschter(?:\\s+um)?', + 'diff_tomorrow' => 'Muer', + 'diff_tomorrow_regexp' => 'Muer(?:\\s+um)?', + 'diff_today_regexp' => 'Haut(?:\\s+um)?', + 'formats' => [ + 'LT' => 'H:mm [Auer]', + 'LTS' => 'H:mm:ss [Auer]', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY H:mm [Auer]', + 'LLLL' => 'dddd, D. MMMM YYYY H:mm [Auer]', + ], + + 'calendar' => [ + 'sameDay' => '[Haut um] LT', + 'nextDay' => '[Muer um] LT', + 'nextWeek' => 'dddd [um] LT', + 'lastDay' => '[Gëschter um] LT', + 'lastWeek' => function (CarbonInterface $date) { + // Different date string for 'Dënschdeg' (Tuesday) and 'Donneschdeg' (Thursday) due to phonological rule + switch ($date->dayOfWeek) { + case 2: + case 4: + return '[Leschten] dddd [um] LT'; + default: + return '[Leschte] dddd [um] LT'; + } + }, + 'sameElse' => 'L', + ], + + 'months' => ['Januar', 'Februar', 'Mäerz', 'Abrëll', 'Mee', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], + 'months_short' => ['Jan.', 'Febr.', 'Mrz.', 'Abr.', 'Mee', 'Jun.', 'Jul.', 'Aug.', 'Sept.', 'Okt.', 'Nov.', 'Dez.'], + 'weekdays' => ['Sonndeg', 'Méindeg', 'Dënschdeg', 'Mëttwoch', 'Donneschdeg', 'Freideg', 'Samschdeg'], + 'weekdays_short' => ['So.', 'Mé.', 'Dë.', 'Më.', 'Do.', 'Fr.', 'Sa.'], + 'weekdays_min' => ['So', 'Mé', 'Dë', 'Më', 'Do', 'Fr', 'Sa'], + 'ordinal' => ':number.', + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' an '], + 'meridiem' => ['moies', 'mëttes'], + 'weekdays_short_standalone' => ['Son', 'Méi', 'Dën', 'Mët', 'Don', 'Fre', 'Sam'], + 'months_short_standalone' => ['Jan', 'Feb', 'Mäe', 'Abr', 'Mee', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lb_LU.php b/vendor/nesbot/carbon/src/Carbon/Lang/lb_LU.php new file mode 100644 index 0000000..414bd4d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lb_LU.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/lb.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lg.php b/vendor/nesbot/carbon/src/Carbon/Lang/lg.php new file mode 100644 index 0000000..48bc68b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lg.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/lg_UG.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lg_UG.php b/vendor/nesbot/carbon/src/Carbon/Lang/lg_UG.php new file mode 100644 index 0000000..aa02214 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lg_UG.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Akademe ya Luganda Kizito Birabwa kompyuta@kizito.uklinux.net + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['Janwaliyo', 'Febwaliyo', 'Marisi', 'Apuli', 'Maayi', 'Juuni', 'Julaayi', 'Agusito', 'Sebuttemba', 'Okitobba', 'Novemba', 'Desemba'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apu', 'Maa', 'Juu', 'Jul', 'Agu', 'Seb', 'Oki', 'Nov', 'Des'], + 'weekdays' => ['Sabiiti', 'Balaza', 'Lwakubiri', 'Lwakusatu', 'Lwakuna', 'Lwakutaano', 'Lwamukaaga'], + 'weekdays_short' => ['Sab', 'Bal', 'Lw2', 'Lw3', 'Lw4', 'Lw5', 'Lw6'], + 'weekdays_min' => ['Sab', 'Bal', 'Lw2', 'Lw3', 'Lw4', 'Lw5', 'Lw6'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'month' => ':count njuba', // less reliable + 'm' => ':count njuba', // less reliable + 'a_month' => ':count njuba', // less reliable + + 'year' => ':count mwaaka', + 'y' => ':count mwaaka', + 'a_year' => ':count mwaaka', + + 'week' => ':count sabbiiti', + 'w' => ':count sabbiiti', + 'a_week' => ':count sabbiiti', + + 'day' => ':count lunaku', + 'd' => ':count lunaku', + 'a_day' => ':count lunaku', + + 'hour' => 'saawa :count', + 'h' => 'saawa :count', + 'a_hour' => 'saawa :count', + + 'minute' => 'ddakiika :count', + 'min' => 'ddakiika :count', + 'a_minute' => 'ddakiika :count', + + 'second' => ':count kyʼokubiri', + 's' => ':count kyʼokubiri', + 'a_second' => ':count kyʼokubiri', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/li.php b/vendor/nesbot/carbon/src/Carbon/Lang/li.php new file mode 100644 index 0000000..86c3009 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/li.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/li_NL.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/li_NL.php b/vendor/nesbot/carbon/src/Carbon/Lang/li_NL.php new file mode 100644 index 0000000..6c5feb7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/li_NL.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - information from Kenneth Christiansen Kenneth Christiansen, Pablo Saratxaga kenneth@gnu.org, pablo@mandriva.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['jannewarie', 'fibberwarie', 'miert', 'eprèl', 'meij', 'junie', 'julie', 'augustus', 'september', 'oktober', 'november', 'desember'], + 'months_short' => ['jan', 'fib', 'mie', 'epr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'], + 'weekdays' => ['zóndig', 'maondig', 'daensdig', 'goonsdig', 'dónderdig', 'vriedig', 'zaoterdig'], + 'weekdays_short' => ['zón', 'mao', 'dae', 'goo', 'dón', 'vri', 'zao'], + 'weekdays_min' => ['zón', 'mao', 'dae', 'goo', 'dón', 'vri', 'zao'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'minute' => ':count momênt', // less reliable + 'min' => ':count momênt', // less reliable + 'a_minute' => ':count momênt', // less reliable + + 'year' => ':count jaor', + 'y' => ':count jaor', + 'a_year' => ':count jaor', + + 'month' => ':count maond', + 'm' => ':count maond', + 'a_month' => ':count maond', + + 'week' => ':count waek', + 'w' => ':count waek', + 'a_week' => ':count waek', + + 'day' => ':count daag', + 'd' => ':count daag', + 'a_day' => ':count daag', + + 'hour' => ':count oer', + 'h' => ':count oer', + 'a_hour' => ':count oer', + + 'second' => ':count Secónd', + 's' => ':count Secónd', + 'a_second' => ':count Secónd', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lij.php b/vendor/nesbot/carbon/src/Carbon/Lang/lij.php new file mode 100644 index 0000000..45732b5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lij.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/lij_IT.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lij_IT.php b/vendor/nesbot/carbon/src/Carbon/Lang/lij_IT.php new file mode 100644 index 0000000..f8726fd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lij_IT.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Gastaldi alessio.gastaldi@libero.it + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['zenâ', 'fevrâ', 'marzo', 'avrî', 'mazzo', 'zûgno', 'lûggio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dixembre'], + 'months_short' => ['zen', 'fev', 'mar', 'arv', 'maz', 'zûg', 'lûg', 'ago', 'set', 'ött', 'nov', 'dix'], + 'weekdays' => ['domenega', 'lûnedì', 'martedì', 'mercUrdì', 'zêggia', 'venardì', 'sabbo'], + 'weekdays_short' => ['dom', 'lûn', 'mar', 'mer', 'zêu', 'ven', 'sab'], + 'weekdays_min' => ['dom', 'lûn', 'mar', 'mer', 'zêu', 'ven', 'sab'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'year' => ':count etæ', // less reliable + 'y' => ':count etæ', // less reliable + 'a_year' => ':count etæ', // less reliable + + 'month' => ':count meize', + 'm' => ':count meize', + 'a_month' => ':count meize', + + 'week' => ':count settemannha', + 'w' => ':count settemannha', + 'a_week' => ':count settemannha', + + 'day' => ':count giorno', + 'd' => ':count giorno', + 'a_day' => ':count giorno', + + 'hour' => ':count reléuio', // less reliable + 'h' => ':count reléuio', // less reliable + 'a_hour' => ':count reléuio', // less reliable + + 'minute' => ':count menûo', + 'min' => ':count menûo', + 'a_minute' => ':count menûo', + + 'second' => ':count segondo', + 's' => ':count segondo', + 'a_second' => ':count segondo', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lkt.php b/vendor/nesbot/carbon/src/Carbon/Lang/lkt.php new file mode 100644 index 0000000..ae73a97 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lkt.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + + 'month' => ':count haŋwí', // less reliable + 'm' => ':count haŋwí', // less reliable + 'a_month' => ':count haŋwí', // less reliable + + 'week' => ':count šakówiŋ', // less reliable + 'w' => ':count šakówiŋ', // less reliable + 'a_week' => ':count šakówiŋ', // less reliable + + 'hour' => ':count maza škaŋškaŋ', // less reliable + 'h' => ':count maza škaŋškaŋ', // less reliable + 'a_hour' => ':count maza škaŋškaŋ', // less reliable + + 'minute' => ':count číkʼala', // less reliable + 'min' => ':count číkʼala', // less reliable + 'a_minute' => ':count číkʼala', // less reliable + + 'year' => ':count waníyetu', + 'y' => ':count waníyetu', + 'a_year' => ':count waníyetu', + + 'day' => ':count aŋpétu', + 'd' => ':count aŋpétu', + 'a_day' => ':count aŋpétu', + + 'second' => ':count icinuŋpa', + 's' => ':count icinuŋpa', + 'a_second' => ':count icinuŋpa', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ln.php b/vendor/nesbot/carbon/src/Carbon/Lang/ln.php new file mode 100644 index 0000000..9d5c35d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ln.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ubuntu René Manassé GALEKWA renemanasse@gmail.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'months' => ['sánzá ya yambo', 'sánzá ya míbalé', 'sánzá ya mísáto', 'sánzá ya mínei', 'sánzá ya mítáno', 'sánzá ya motóbá', 'sánzá ya nsambo', 'sánzá ya mwambe', 'sánzá ya libwa', 'sánzá ya zómi', 'sánzá ya zómi na mɔ̌kɔ́', 'sánzá ya zómi na míbalé'], + 'months_short' => ['yan', 'fbl', 'msi', 'apl', 'mai', 'yun', 'yul', 'agt', 'stb', 'ɔtb', 'nvb', 'dsb'], + 'weekdays' => ['Lomíngo', 'Mosálá mɔ̌kɔ́', 'Misálá míbalé', 'Misálá mísáto', 'Misálá mínei', 'Misálá mítáno', 'Mpɔ́sɔ'], + 'weekdays_short' => ['m1.', 'm2.', 'm3.', 'm4.', 'm5.', 'm6.', 'm7.'], + 'weekdays_min' => ['m1.', 'm2.', 'm3.', 'm4.', 'm5.', 'm6.', 'm7.'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'year' => 'mbula :count', + 'y' => 'mbula :count', + 'a_year' => 'mbula :count', + + 'month' => 'sánzá :count', + 'm' => 'sánzá :count', + 'a_month' => 'sánzá :count', + + 'week' => 'mpɔ́sɔ :count', + 'w' => 'mpɔ́sɔ :count', + 'a_week' => 'mpɔ́sɔ :count', + + 'day' => 'mokɔlɔ :count', + 'd' => 'mokɔlɔ :count', + 'a_day' => 'mokɔlɔ :count', + + 'hour' => 'ngonga :count', + 'h' => 'ngonga :count', + 'a_hour' => 'ngonga :count', + + 'minute' => 'miniti :count', + 'min' => 'miniti :count', + 'a_minute' => 'miniti :count', + + 'second' => 'segɔnde :count', + 's' => 'segɔnde :count', + 'a_second' => 'segɔnde :count', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ln_AO.php b/vendor/nesbot/carbon/src/Carbon/Lang/ln_AO.php new file mode 100644 index 0000000..7fdb7f1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ln_AO.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ln.php', [ + 'weekdays' => ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé', 'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno', 'mpɔ́sɔ'], + 'weekdays_short' => ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'], + 'weekdays_min' => ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'], + 'meridiem' => ['ntɔ́ngɔ́', 'mpókwa'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ln_CD.php b/vendor/nesbot/carbon/src/Carbon/Lang/ln_CD.php new file mode 100644 index 0000000..13635fc --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ln_CD.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ubuntu René Manassé GALEKWA renemanasse@gmail.com + */ +return require __DIR__.'/ln.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ln_CF.php b/vendor/nesbot/carbon/src/Carbon/Lang/ln_CF.php new file mode 100644 index 0000000..7fdb7f1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ln_CF.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ln.php', [ + 'weekdays' => ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé', 'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno', 'mpɔ́sɔ'], + 'weekdays_short' => ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'], + 'weekdays_min' => ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'], + 'meridiem' => ['ntɔ́ngɔ́', 'mpókwa'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ln_CG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ln_CG.php new file mode 100644 index 0000000..7fdb7f1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ln_CG.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ln.php', [ + 'weekdays' => ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé', 'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno', 'mpɔ́sɔ'], + 'weekdays_short' => ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'], + 'weekdays_min' => ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'], + 'meridiem' => ['ntɔ́ngɔ́', 'mpókwa'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lo.php b/vendor/nesbot/carbon/src/Carbon/Lang/lo.php new file mode 100644 index 0000000..48715f5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lo.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - ryanhart2 + */ +return [ + 'year' => ':count ປີ', + 'y' => ':count ປີ', + 'month' => ':count ເດືອນ', + 'm' => ':count ດ. ', + 'week' => ':count ອາທິດ', + 'w' => ':count ອທ. ', + 'day' => ':count ມື້', + 'd' => ':count ມື້', + 'hour' => ':count ຊົ່ວໂມງ', + 'h' => ':count ຊມ. ', + 'minute' => ':count ນາທີ', + 'min' => ':count ນທ. ', + 'second' => '{1}ບໍ່ເທົ່າໃດວິນາທີ|]1,Inf[:count ວິນາທີ', + 's' => ':count ວິ. ', + 'ago' => ':timeຜ່ານມາ', + 'from_now' => 'ອີກ :time', + 'diff_now' => 'ຕອນນີ້', + 'diff_today' => 'ມື້ນີ້ເວລາ', + 'diff_yesterday' => 'ມື້ວານນີ້ເວລາ', + 'diff_tomorrow' => 'ມື້ອື່ນເວລາ', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'ວັນdddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[ມື້ນີ້ເວລາ] LT', + 'nextDay' => '[ມື້ອື່ນເວລາ] LT', + 'nextWeek' => '[ວັນ]dddd[ໜ້າເວລາ] LT', + 'lastDay' => '[ມື້ວານນີ້ເວລາ] LT', + 'lastWeek' => '[ວັນ]dddd[ແລ້ວນີ້ເວລາ] LT', + 'sameElse' => 'L', + ], + 'ordinal' => 'ທີ່:number', + 'meridiem' => ['ຕອນເຊົ້າ', 'ຕອນແລງ'], + 'months' => ['ມັງກອນ', 'ກຸມພາ', 'ມີນາ', 'ເມສາ', 'ພຶດສະພາ', 'ມິຖຸນາ', 'ກໍລະກົດ', 'ສິງຫາ', 'ກັນຍາ', 'ຕຸລາ', 'ພະຈິກ', 'ທັນວາ'], + 'months_short' => ['ມັງກອນ', 'ກຸມພາ', 'ມີນາ', 'ເມສາ', 'ພຶດສະພາ', 'ມິຖຸນາ', 'ກໍລະກົດ', 'ສິງຫາ', 'ກັນຍາ', 'ຕຸລາ', 'ພະຈິກ', 'ທັນວາ'], + 'weekdays' => ['ອາທິດ', 'ຈັນ', 'ອັງຄານ', 'ພຸດ', 'ພະຫັດ', 'ສຸກ', 'ເສົາ'], + 'weekdays_short' => ['ທິດ', 'ຈັນ', 'ອັງຄານ', 'ພຸດ', 'ພະຫັດ', 'ສຸກ', 'ເສົາ'], + 'weekdays_min' => ['ທ', 'ຈ', 'ອຄ', 'ພ', 'ພຫ', 'ສກ', 'ສ'], + 'list' => [', ', 'ແລະ '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lo_LA.php b/vendor/nesbot/carbon/src/Carbon/Lang/lo_LA.php new file mode 100644 index 0000000..9b7fd9b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lo_LA.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/lo.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lrc.php b/vendor/nesbot/carbon/src/Carbon/Lang/lrc.php new file mode 100644 index 0000000..546e679 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lrc.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + + 'minute' => ':count هنر', // less reliable + 'min' => ':count هنر', // less reliable + 'a_minute' => ':count هنر', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lrc_IQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/lrc_IQ.php new file mode 100644 index 0000000..d42f5e9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lrc_IQ.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/lrc.php', [ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lt.php b/vendor/nesbot/carbon/src/Carbon/Lang/lt.php new file mode 100644 index 0000000..7d1b6f7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lt.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Philippe Vaucher + * - Tsutomu Kuroda + * - tjku + * - valdas406 + * - Justas Palumickas + * - Max Melentiev + * - Andrius Janauskas + * - Juanito Fatas + * - Akira Matsuda + * - Christopher Dell + * - Enrique Vidal + * - Simone Carletti + * - Aaron Patterson + * - Nicolás Hock Isaza + * - Laurynas Butkus + * - Sven Fuchs + * - Dominykas Tijūnaitis + * - Justinas Bolys + * - Ričardas + * - Kirill Chalkin + * - Rolandas + * - Justinas (Gamesh) + */ +return [ + 'year' => ':count metai|:count metai|:count metų', + 'y' => ':count m.', + 'month' => ':count mėnuo|:count mėnesiai|:count mėnesį', + 'm' => ':count mėn.', + 'week' => ':count savaitė|:count savaitės|:count savaitę', + 'w' => ':count sav.', + 'day' => ':count diena|:count dienos|:count dienų', + 'd' => ':count d.', + 'hour' => ':count valanda|:count valandos|:count valandų', + 'h' => ':count val.', + 'minute' => ':count minutė|:count minutės|:count minutę', + 'min' => ':count min.', + 'second' => ':count sekundė|:count sekundės|:count sekundžių', + 's' => ':count sek.', + + 'year_ago' => ':count metus|:count metus|:count metų', + 'month_ago' => ':count mėnesį|:count mėnesius|:count mėnesių', + 'week_ago' => ':count savaitę|:count savaites|:count savaičių', + 'day_ago' => ':count dieną|:count dienas|:count dienų', + 'hour_ago' => ':count valandą|:count valandas|:count valandų', + 'minute_ago' => ':count minutę|:count minutes|:count minučių', + 'second_ago' => ':count sekundę|:count sekundes|:count sekundžių', + + 'year_from_now' => ':count metų', + 'month_from_now' => ':count mėnesio|:count mėnesių|:count mėnesių', + 'week_from_now' => ':count savaitės|:count savaičių|:count savaičių', + 'day_from_now' => ':count dienos|:count dienų|:count dienų', + 'hour_from_now' => ':count valandos|:count valandų|:count valandų', + 'minute_from_now' => ':count minutės|:count minučių|:count minučių', + 'second_from_now' => ':count sekundės|:count sekundžių|:count sekundžių', + + 'year_after' => ':count metų', + 'month_after' => ':count mėnesio|:count mėnesių|:count mėnesių', + 'week_after' => ':count savaitės|:count savaičių|:count savaičių', + 'day_after' => ':count dienos|:count dienų|:count dienų', + 'hour_after' => ':count valandos|:count valandų|:count valandų', + 'minute_after' => ':count minutės|:count minučių|:count minučių', + 'second_after' => ':count sekundės|:count sekundžių|:count sekundžių', + + 'ago' => 'prieš :time', + 'from_now' => ':time nuo dabar', + 'after' => 'po :time', + 'before' => 'už :time', + + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'diff_now' => 'ką tik', + 'diff_today' => 'Šiandien', + 'diff_yesterday' => 'vakar', + 'diff_yesterday_regexp' => 'Vakar', + 'diff_tomorrow' => 'rytoj', + 'diff_tomorrow_regexp' => 'Rytoj', + 'diff_before_yesterday' => 'užvakar', + 'diff_after_tomorrow' => 'poryt', + + 'period_recurrences' => 'kartą|:count kartų', + 'period_interval' => 'kiekvieną :interval', + 'period_start_date' => 'nuo :date', + 'period_end_date' => 'iki :date', + + 'months' => ['sausio', 'vasario', 'kovo', 'balandžio', 'gegužės', 'birželio', 'liepos', 'rugpjūčio', 'rugsėjo', 'spalio', 'lapkričio', 'gruodžio'], + 'months_standalone' => ['sausis', 'vasaris', 'kovas', 'balandis', 'gegužė', 'birželis', 'liepa', 'rugpjūtis', 'rugsėjis', 'spalis', 'lapkritis', 'gruodis'], + 'months_regexp' => '/(L{2,4}|D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|MMMM?(\[[^\[\]]*\]|\s)+D[oD]?)/', + 'months_short' => ['sau', 'vas', 'kov', 'bal', 'geg', 'bir', 'lie', 'rgp', 'rgs', 'spa', 'lap', 'gru'], + 'weekdays' => ['sekmadienį', 'pirmadienį', 'antradienį', 'trečiadienį', 'ketvirtadienį', 'penktadienį', 'šeštadienį'], + 'weekdays_standalone' => ['sekmadienis', 'pirmadienis', 'antradienis', 'trečiadienis', 'ketvirtadienis', 'penktadienis', 'šeštadienis'], + 'weekdays_short' => ['sek', 'pir', 'ant', 'tre', 'ket', 'pen', 'šeš'], + 'weekdays_min' => ['se', 'pi', 'an', 'tr', 'ke', 'pe', 'še'], + 'list' => [', ', ' ir '], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-DD', + 'LL' => 'MMMM DD, YYYY', + 'LLL' => 'DD MMM HH:mm', + 'LLLL' => 'MMMM DD, YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Šiandien] LT', + 'nextDay' => '[Rytoj] LT', + 'nextWeek' => 'dddd LT', + 'lastDay' => '[Vakar] LT', + 'lastWeek' => '[Paskutinį] dddd LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + switch ($number) { + case 0: + return '0-is'; + case 3: + return '3-ias'; + default: + return "$number-as"; + } + }, + 'meridiem' => ['priešpiet', 'popiet'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lt_LT.php b/vendor/nesbot/carbon/src/Carbon/Lang/lt_LT.php new file mode 100644 index 0000000..f772d38 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lt_LT.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/lt.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lu.php b/vendor/nesbot/carbon/src/Carbon/Lang/lu.php new file mode 100644 index 0000000..c8cd83a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lu.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Dinda', 'Dilolo'], + 'weekdays' => ['Lumingu', 'Nkodya', 'Ndàayà', 'Ndangù', 'Njòwa', 'Ngòvya', 'Lubingu'], + 'weekdays_short' => ['Lum', 'Nko', 'Ndy', 'Ndg', 'Njw', 'Ngv', 'Lub'], + 'weekdays_min' => ['Lum', 'Nko', 'Ndy', 'Ndg', 'Njw', 'Ngv', 'Lub'], + 'months' => ['Ciongo', 'Lùishi', 'Lusòlo', 'Mùuyà', 'Lumùngùlù', 'Lufuimi', 'Kabàlàshìpù', 'Lùshìkà', 'Lutongolo', 'Lungùdi', 'Kaswèkèsè', 'Ciswà'], + 'months_short' => ['Cio', 'Lui', 'Lus', 'Muu', 'Lum', 'Luf', 'Kab', 'Lush', 'Lut', 'Lun', 'Kas', 'Cis'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/luo.php b/vendor/nesbot/carbon/src/Carbon/Lang/luo.php new file mode 100644 index 0000000..b55af73 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/luo.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['OD', 'OT'], + 'weekdays' => ['Jumapil', 'Wuok Tich', 'Tich Ariyo', 'Tich Adek', 'Tich Ang’wen', 'Tich Abich', 'Ngeso'], + 'weekdays_short' => ['JMP', 'WUT', 'TAR', 'TAD', 'TAN', 'TAB', 'NGS'], + 'weekdays_min' => ['JMP', 'WUT', 'TAR', 'TAD', 'TAN', 'TAB', 'NGS'], + 'months' => ['Dwe mar Achiel', 'Dwe mar Ariyo', 'Dwe mar Adek', 'Dwe mar Ang’wen', 'Dwe mar Abich', 'Dwe mar Auchiel', 'Dwe mar Abiriyo', 'Dwe mar Aboro', 'Dwe mar Ochiko', 'Dwe mar Apar', 'Dwe mar gi achiel', 'Dwe mar Apar gi ariyo'], + 'months_short' => ['DAC', 'DAR', 'DAD', 'DAN', 'DAH', 'DAU', 'DAO', 'DAB', 'DOC', 'DAP', 'DGI', 'DAG'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + + 'year' => 'higni :count', + 'y' => 'higni :count', + 'a_year' => ':higni :count', + + 'month' => 'dweche :count', + 'm' => 'dweche :count', + 'a_month' => 'dweche :count', + + 'week' => 'jumbe :count', + 'w' => 'jumbe :count', + 'a_week' => 'jumbe :count', + + 'day' => 'ndalo :count', + 'd' => 'ndalo :count', + 'a_day' => 'ndalo :count', + + 'hour' => 'seche :count', + 'h' => 'seche :count', + 'a_hour' => 'seche :count', + + 'minute' => 'dakika :count', + 'min' => 'dakika :count', + 'a_minute' => 'dakika :count', + + 'second' => 'nus dakika :count', + 's' => 'nus dakika :count', + 'a_second' => 'nus dakika :count', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/luy.php b/vendor/nesbot/carbon/src/Carbon/Lang/luy.php new file mode 100644 index 0000000..2b37e3e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/luy.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'weekdays' => ['Jumapiri', 'Jumatatu', 'Jumanne', 'Jumatano', 'Murwa wa Kanne', 'Murwa wa Katano', 'Jumamosi'], + 'weekdays_short' => ['J2', 'J3', 'J4', 'J5', 'Al', 'Ij', 'J1'], + 'weekdays_min' => ['J2', 'J3', 'J4', 'J5', 'Al', 'Ij', 'J1'], + 'months' => ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + + // Too unreliable + /* + 'year' => ':count liliino', // less reliable + 'y' => ':count liliino', // less reliable + 'a_year' => ':count liliino', // less reliable + + 'month' => ':count kumwesi', // less reliable + 'm' => ':count kumwesi', // less reliable + 'a_month' => ':count kumwesi', // less reliable + + 'week' => ':count olutambi', // less reliable + 'w' => ':count olutambi', // less reliable + 'a_week' => ':count olutambi', // less reliable + + 'day' => ':count luno', // less reliable + 'd' => ':count luno', // less reliable + 'a_day' => ':count luno', // less reliable + + 'hour' => ':count ekengele', // less reliable + 'h' => ':count ekengele', // less reliable + 'a_hour' => ':count ekengele', // less reliable + + 'minute' => ':count omundu', // less reliable + 'min' => ':count omundu', // less reliable + 'a_minute' => ':count omundu', // less reliable + + 'second' => ':count liliino', // less reliable + 's' => ':count liliino', // less reliable + 'a_second' => ':count liliino', // less reliable + */ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lv.php b/vendor/nesbot/carbon/src/Carbon/Lang/lv.php new file mode 100644 index 0000000..d5cba7c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lv.php @@ -0,0 +1,183 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Carbon\CarbonInterface; + +/** + * This file is part of the Carbon package. + * + * (c) Brian Nesbitt + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Philippe Vaucher + * - pirminis + * - Tsutomu Kuroda + * - tjku + * - Andris Zāģeris + * - Max Melentiev + * - Edgars Beigarts + * - Juanito Fatas + * - Vitauts Stočka + * - Akira Matsuda + * - Christopher Dell + * - Enrique Vidal + * - Simone Carletti + * - Aaron Patterson + * - Kaspars Bankovskis + * - Nicolás Hock Isaza + * - Viesturs Kavacs (Kavacky) + * - zakse + * - Janis Eglitis (janiseglitis) + * - Guntars + * - Juris Sudmalis + */ +$daysOfWeek = ['svētdiena', 'pirmdiena', 'otrdiena', 'trešdiena', 'ceturtdiena', 'piektdiena', 'sestdiena']; +$daysOfWeekLocativum = ['svētdien', 'pirmdien', 'otrdien', 'trešdien', 'ceturtdien', 'piektdien', 'sestdien']; + +$transformDiff = function ($input) { + return strtr($input, [ + // Nominative => "pirms/pēc" Dative + 'gads' => 'gada', + 'gadi' => 'gadiem', + 'gadu' => 'gadiem', + 'mēnesis' => 'mēneša', + 'mēneši' => 'mēnešiem', + 'mēnešu' => 'mēnešiem', + 'nedēļa' => 'nedēļas', + 'nedēļas' => 'nedēļām', + 'nedēļu' => 'nedēļām', + 'diena' => 'dienas', + 'dienas' => 'dienām', + 'dienu' => 'dienām', + 'stunda' => 'stundas', + 'stundas' => 'stundām', + 'stundu' => 'stundām', + 'minūte' => 'minūtes', + 'minūtes' => 'minūtēm', + 'minūšu' => 'minūtēm', + 'sekunde' => 'sekundes', + 'sekundes' => 'sekundēm', + 'sekunžu' => 'sekundēm', + ]); +}; + +return [ + 'ago' => function ($time) use ($transformDiff) { + return 'pirms '.$transformDiff($time); + }, + 'from_now' => function ($time) use ($transformDiff) { + return 'pēc '.$transformDiff($time); + }, + + 'year' => '0 gadu|:count gads|:count gadi', + 'y' => ':count g.', + 'a_year' => '{1}gads|0 gadu|:count gads|:count gadi', + 'month' => '0 mēnešu|:count mēnesis|:count mēneši', + 'm' => ':count mēn.', + 'a_month' => '{1}mēnesis|0 mēnešu|:count mēnesis|:count mēneši', + 'week' => '0 nedēļu|:count nedēļa|:count nedēļas', + 'w' => ':count ned.', + 'a_week' => '{1}nedēļa|0 nedēļu|:count nedēļa|:count nedēļas', + 'day' => '0 dienu|:count diena|:count dienas', + 'd' => ':count d.', + 'a_day' => '{1}diena|0 dienu|:count diena|:count dienas', + 'hour' => '0 stundu|:count stunda|:count stundas', + 'h' => ':count st.', + 'a_hour' => '{1}stunda|0 stundu|:count stunda|:count stundas', + 'minute' => '0 minūšu|:count minūte|:count minūtes', + 'min' => ':count min.', + 'a_minute' => '{1}minūte|0 minūšu|:count minūte|:count minūtes', + 'second' => '0 sekunžu|:count sekunde|:count sekundes', + 's' => ':count sek.', + 'a_second' => '{1}sekunde|0 sekunžu|:count sekunde|:count sekundes', + + 'after' => ':time vēlāk', + 'year_after' => '0 gadus|:count gadu|:count gadus', + 'a_year_after' => '{1}gadu|0 gadus|:count gadu|:count gadus', + 'month_after' => '0 mēnešus|:count mēnesi|:count mēnešus', + 'a_month_after' => '{1}mēnesi|0 mēnešus|:count mēnesi|:count mēnešus', + 'week_after' => '0 nedēļas|:count nedēļu|:count nedēļas', + 'a_week_after' => '{1}nedēļu|0 nedēļas|:count nedēļu|:count nedēļas', + 'day_after' => '0 dienas|:count dienu|:count dienas', + 'a_day_after' => '{1}dienu|0 dienas|:count dienu|:count dienas', + 'hour_after' => '0 stundas|:count stundu|:count stundas', + 'a_hour_after' => '{1}stundu|0 stundas|:count stundu|:count stundas', + 'minute_after' => '0 minūtes|:count minūti|:count minūtes', + 'a_minute_after' => '{1}minūti|0 minūtes|:count minūti|:count minūtes', + 'second_after' => '0 sekundes|:count sekundi|:count sekundes', + 'a_second_after' => '{1}sekundi|0 sekundes|:count sekundi|:count sekundes', + + 'before' => ':time agrāk', + 'year_before' => '0 gadus|:count gadu|:count gadus', + 'a_year_before' => '{1}gadu|0 gadus|:count gadu|:count gadus', + 'month_before' => '0 mēnešus|:count mēnesi|:count mēnešus', + 'a_month_before' => '{1}mēnesi|0 mēnešus|:count mēnesi|:count mēnešus', + 'week_before' => '0 nedēļas|:count nedēļu|:count nedēļas', + 'a_week_before' => '{1}nedēļu|0 nedēļas|:count nedēļu|:count nedēļas', + 'day_before' => '0 dienas|:count dienu|:count dienas', + 'a_day_before' => '{1}dienu|0 dienas|:count dienu|:count dienas', + 'hour_before' => '0 stundas|:count stundu|:count stundas', + 'a_hour_before' => '{1}stundu|0 stundas|:count stundu|:count stundas', + 'minute_before' => '0 minūtes|:count minūti|:count minūtes', + 'a_minute_before' => '{1}minūti|0 minūtes|:count minūti|:count minūtes', + 'second_before' => '0 sekundes|:count sekundi|:count sekundes', + 'a_second_before' => '{1}sekundi|0 sekundes|:count sekundi|:count sekundes', + + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' un '], + + 'diff_now' => 'tagad', + 'diff_today' => 'šodien', + 'diff_yesterday' => 'vakar', + 'diff_before_yesterday' => 'aizvakar', + 'diff_tomorrow' => 'rīt', + 'diff_after_tomorrow' => 'parīt', + + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY.', + 'LL' => 'YYYY. [gada] D. MMMM', + 'LLL' => 'DD.MM.YYYY., HH:mm', + 'LLLL' => 'YYYY. [gada] D. MMMM, HH:mm', + ], + + 'calendar' => [ + 'sameDay' => '[šodien] [plkst.] LT', + 'nextDay' => '[rīt] [plkst.] LT', + 'nextWeek' => function (CarbonInterface $current, CarbonInterface $other) use ($daysOfWeekLocativum) { + if ($current->week !== $other->week) { + return '[nākošo] ['.$daysOfWeekLocativum[$current->dayOfWeek].'] [plkst.] LT'; + } + + return '['.$daysOfWeekLocativum[$current->dayOfWeek].'] [plkst.] LT'; + }, + 'lastDay' => '[vakar] [plkst.] LT', + 'lastWeek' => function (CarbonInterface $current) use ($daysOfWeekLocativum) { + return '[pagājušo] ['.$daysOfWeekLocativum[$current->dayOfWeek].'] [plkst.] LT'; + }, + 'sameElse' => 'L', + ], + + 'weekdays' => $daysOfWeek, + 'weekdays_short' => ['Sv.', 'P.', 'O.', 'T.', 'C.', 'Pk.', 'S.'], + 'weekdays_min' => ['Sv.', 'P.', 'O.', 'T.', 'C.', 'Pk.', 'S.'], + 'months' => ['janvāris', 'februāris', 'marts', 'aprīlis', 'maijs', 'jūnijs', 'jūlijs', 'augusts', 'septembris', 'oktobris', 'novembris', 'decembris'], + 'months_standalone' => ['janvārī', 'februārī', 'martā', 'aprīlī', 'maijā', 'jūnijā', 'jūlijā', 'augustā', 'septembrī', 'oktobrī', 'novembrī', 'decembrī'], + 'months_short' => ['janv.', 'febr.', 'martā', 'apr.', 'maijā', 'jūn.', 'jūl.', 'aug.', 'sept.', 'okt.', 'nov.', 'dec.'], + 'meridiem' => ['priekšpusdiena', 'pēcpusdiena'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lv_LV.php b/vendor/nesbot/carbon/src/Carbon/Lang/lv_LV.php new file mode 100644 index 0000000..ee91c36 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lv_LV.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/lv.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lzh.php b/vendor/nesbot/carbon/src/Carbon/Lang/lzh.php new file mode 100644 index 0000000..1180c6b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lzh.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/lzh_TW.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lzh_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/lzh_TW.php new file mode 100644 index 0000000..3b1493e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lzh_TW.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'OY[年]MMMMOD[日]', + ], + 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + 'months_short' => [' 一 ', ' 二 ', ' 三 ', ' 四 ', ' 五 ', ' 六 ', ' 七 ', ' 八 ', ' 九 ', ' 十 ', '十一', '十二'], + 'weekdays' => ['週日', '週一', '週二', '週三', '週四', '週五', '週六'], + 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'], + 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'], + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['〇', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '廿', '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '卅', '卅一'], + 'meridiem' => ['朝', '暮'], + + 'year' => ':count 夏', // less reliable + 'y' => ':count 夏', // less reliable + 'a_year' => ':count 夏', // less reliable + + 'month' => ':count 月', // less reliable + 'm' => ':count 月', // less reliable + 'a_month' => ':count 月', // less reliable + + 'hour' => ':count 氧', // less reliable + 'h' => ':count 氧', // less reliable + 'a_hour' => ':count 氧', // less reliable + + 'minute' => ':count 點', // less reliable + 'min' => ':count 點', // less reliable + 'a_minute' => ':count 點', // less reliable + + 'second' => ':count 楚', // less reliable + 's' => ':count 楚', // less reliable + 'a_second' => ':count 楚', // less reliable + + 'week' => ':count 星期', + 'w' => ':count 星期', + 'a_week' => ':count 星期', + + 'day' => ':count 日(曆法)', + 'd' => ':count 日(曆法)', + 'a_day' => ':count 日(曆法)', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mag.php b/vendor/nesbot/carbon/src/Carbon/Lang/mag.php new file mode 100644 index 0000000..7532436 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mag.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/mag_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mag_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mag_IN.php new file mode 100644 index 0000000..193f67a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mag_IN.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bhashaghar@googlegroups.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'], + 'months_short' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'], + 'weekdays' => ['एतवार', 'सोमार', 'मंगर', 'बुध', 'बिफे', 'सूक', 'सनिचर'], + 'weekdays_short' => ['एतवार', 'सोमार', 'मंगर', 'बुध', 'बिफे', 'सूक', 'सनिचर'], + 'weekdays_min' => ['एतवार', 'सोमार', 'मंगर', 'बुध', 'बिफे', 'सूक', 'सनिचर'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mai.php b/vendor/nesbot/carbon/src/Carbon/Lang/mai.php new file mode 100644 index 0000000..792b973 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mai.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/mai_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mai_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mai_IN.php new file mode 100644 index 0000000..03049d4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mai_IN.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Maithili Computing Research Center, Pune, India rajeshkajha@yahoo.com,akhilesh.k@samusng.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['बैसाख', 'जेठ', 'अषाढ़', 'सावोन', 'भादो', 'आसिन', 'कातिक', 'अगहन', 'पूस', 'माघ', 'फागुन', 'चैति'], + 'months_short' => ['बैसाख', 'जेठ', 'अषाढ़', 'सावोन', 'भादो', 'आसिन', 'कातिक', 'अगहन', 'पूस', 'माघ', 'फागुन', 'चैति'], + 'weekdays' => ['रविदिन', 'सोमदिन', 'मंगलदिन', 'बुधदिन', 'बृहस्पतीदिन', 'शुक्रदिन', 'शनीदिन'], + 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पती', 'शुक्र', 'शनी'], + 'weekdays_min' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पती', 'शुक्र', 'शनी'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], + + 'year' => ':count ऋतु', // less reliable + 'y' => ':count ऋतु', // less reliable + 'a_year' => ':count ऋतु', // less reliable + + 'month' => ':count महिना', + 'm' => ':count महिना', + 'a_month' => ':count महिना', + + 'week' => ':count श्रेणी:क्यालेन्डर', // less reliable + 'w' => ':count श्रेणी:क्यालेन्डर', // less reliable + 'a_week' => ':count श्रेणी:क्यालेन्डर', // less reliable + + 'day' => ':count दिन', + 'd' => ':count दिन', + 'a_day' => ':count दिन', + + 'hour' => ':count घण्टा', + 'h' => ':count घण्टा', + 'a_hour' => ':count घण्टा', + + 'minute' => ':count समय', // less reliable + 'min' => ':count समय', // less reliable + 'a_minute' => ':count समय', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mas.php b/vendor/nesbot/carbon/src/Carbon/Lang/mas.php new file mode 100644 index 0000000..cbd610c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mas.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Ɛnkakɛnyá', 'Ɛndámâ'], + 'weekdays' => ['Jumapílí', 'Jumatátu', 'Jumane', 'Jumatánɔ', 'Alaámisi', 'Jumáa', 'Jumamósi'], + 'weekdays_short' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'], + 'weekdays_min' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'], + 'months' => ['Oladalʉ́', 'Arát', 'Ɔɛnɨ́ɔɨŋɔk', 'Olodoyíóríê inkókúâ', 'Oloilépūnyīē inkókúâ', 'Kújúɔrɔk', 'Mórusásin', 'Ɔlɔ́ɨ́bɔ́rárɛ', 'Kúshîn', 'Olgísan', 'Pʉshʉ́ka', 'Ntʉ́ŋʉ́s'], + 'months_short' => ['Dal', 'Ará', 'Ɔɛn', 'Doy', 'Lép', 'Rok', 'Sás', 'Bɔ́r', 'Kús', 'Gís', 'Shʉ́', 'Ntʉ́'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + + 'year' => ':count olameyu', // less reliable + 'y' => ':count olameyu', // less reliable + 'a_year' => ':count olameyu', // less reliable + + 'week' => ':count engolongeare orwiki', // less reliable + 'w' => ':count engolongeare orwiki', // less reliable + 'a_week' => ':count engolongeare orwiki', // less reliable + + 'hour' => ':count esahabu', // less reliable + 'h' => ':count esahabu', // less reliable + 'a_hour' => ':count esahabu', // less reliable + + 'second' => ':count are', // less reliable + 's' => ':count are', // less reliable + 'a_second' => ':count are', // less reliable + + 'month' => ':count olapa', + 'm' => ':count olapa', + 'a_month' => ':count olapa', + + 'day' => ':count enkolongʼ', + 'd' => ':count enkolongʼ', + 'a_day' => ':count enkolongʼ', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mas_TZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/mas_TZ.php new file mode 100644 index 0000000..56e2905 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mas_TZ.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/mas.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mer.php b/vendor/nesbot/carbon/src/Carbon/Lang/mer.php new file mode 100644 index 0000000..2e14597 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mer.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['RŨ', 'ŨG'], + 'weekdays' => ['Kiumia', 'Muramuko', 'Wairi', 'Wethatu', 'Wena', 'Wetano', 'Jumamosi'], + 'weekdays_short' => ['KIU', 'MRA', 'WAI', 'WET', 'WEN', 'WTN', 'JUM'], + 'weekdays_min' => ['KIU', 'MRA', 'WAI', 'WET', 'WEN', 'WTN', 'JUM'], + 'months' => ['Januarĩ', 'Feburuarĩ', 'Machi', 'Ĩpurũ', 'Mĩĩ', 'Njuni', 'Njuraĩ', 'Agasti', 'Septemba', 'Oktũba', 'Novemba', 'Dicemba'], + 'months_short' => ['JAN', 'FEB', 'MAC', 'ĨPU', 'MĨĨ', 'NJU', 'NJR', 'AGA', 'SPT', 'OKT', 'NOV', 'DEC'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + + 'year' => ':count murume', // less reliable + 'y' => ':count murume', // less reliable + 'a_year' => ':count murume', // less reliable + + 'month' => ':count muchaara', // less reliable + 'm' => ':count muchaara', // less reliable + 'a_month' => ':count muchaara', // less reliable + + 'minute' => ':count monto', // less reliable + 'min' => ':count monto', // less reliable + 'a_minute' => ':count monto', // less reliable + + 'second' => ':count gikeno', // less reliable + 's' => ':count gikeno', // less reliable + 'a_second' => ':count gikeno', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mfe.php b/vendor/nesbot/carbon/src/Carbon/Lang/mfe.php new file mode 100644 index 0000000..4d6e6b6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mfe.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/mfe_MU.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mfe_MU.php b/vendor/nesbot/carbon/src/Carbon/Lang/mfe_MU.php new file mode 100644 index 0000000..2d27b45 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mfe_MU.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['zanvie', 'fevriye', 'mars', 'avril', 'me', 'zin', 'zilye', 'out', 'septam', 'oktob', 'novam', 'desam'], + 'months_short' => ['zan', 'fev', 'mar', 'avr', 'me', 'zin', 'zil', 'out', 'sep', 'okt', 'nov', 'des'], + 'weekdays' => ['dimans', 'lindi', 'mardi', 'merkredi', 'zedi', 'vandredi', 'samdi'], + 'weekdays_short' => ['dim', 'lin', 'mar', 'mer', 'ze', 'van', 'sam'], + 'weekdays_min' => ['dim', 'lin', 'mar', 'mer', 'ze', 'van', 'sam'], + + 'year' => ':count banané', + 'y' => ':count banané', + 'a_year' => ':count banané', + + 'month' => ':count mwa', + 'm' => ':count mwa', + 'a_month' => ':count mwa', + + 'week' => ':count sémenn', + 'w' => ':count sémenn', + 'a_week' => ':count sémenn', + + 'day' => ':count zour', + 'd' => ':count zour', + 'a_day' => ':count zour', + + 'hour' => ':count -er-tan', + 'h' => ':count -er-tan', + 'a_hour' => ':count -er-tan', + + 'minute' => ':count minitt', + 'min' => ':count minitt', + 'a_minute' => ':count minitt', + + 'second' => ':count déziém', + 's' => ':count déziém', + 'a_second' => ':count déziém', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mg.php b/vendor/nesbot/carbon/src/Carbon/Lang/mg.php new file mode 100644 index 0000000..40bc2a8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mg.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/mg_MG.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mg_MG.php b/vendor/nesbot/carbon/src/Carbon/Lang/mg_MG.php new file mode 100644 index 0000000..6a14535 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mg_MG.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - The Debian Project modified by GNU//Linux Malagasy Rado Ramarotafika,Do-Risika RAFIEFERANTSIARONJY rado@linuxmg.org,dourix@free.fr + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['Janoary', 'Febroary', 'Martsa', 'Aprily', 'Mey', 'Jona', 'Jolay', 'Aogositra', 'Septambra', 'Oktobra', 'Novambra', 'Desambra'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mey', 'Jon', 'Jol', 'Aog', 'Sep', 'Okt', 'Nov', 'Des'], + 'weekdays' => ['alahady', 'alatsinainy', 'talata', 'alarobia', 'alakamisy', 'zoma', 'sabotsy'], + 'weekdays_short' => ['lhd', 'lts', 'tlt', 'lrb', 'lkm', 'zom', 'sab'], + 'weekdays_min' => ['lhd', 'lts', 'tlt', 'lrb', 'lkm', 'zom', 'sab'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'minute' => ':count minitra', // less reliable + 'min' => ':count minitra', // less reliable + 'a_minute' => ':count minitra', // less reliable + + 'year' => ':count taona', + 'y' => ':count taona', + 'a_year' => ':count taona', + + 'month' => ':count volana', + 'm' => ':count volana', + 'a_month' => ':count volana', + + 'week' => ':count herinandro', + 'w' => ':count herinandro', + 'a_week' => ':count herinandro', + + 'day' => ':count andro', + 'd' => ':count andro', + 'a_day' => ':count andro', + + 'hour' => ':count ora', + 'h' => ':count ora', + 'a_hour' => ':count ora', + + 'second' => ':count segondra', + 's' => ':count segondra', + 'a_second' => ':count segondra', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mgh.php b/vendor/nesbot/carbon/src/Carbon/Lang/mgh.php new file mode 100644 index 0000000..2a80960 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mgh.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['wichishu', 'mchochil’l'], + 'weekdays' => ['Sabato', 'Jumatatu', 'Jumanne', 'Jumatano', 'Arahamisi', 'Ijumaa', 'Jumamosi'], + 'weekdays_short' => ['Sab', 'Jtt', 'Jnn', 'Jtn', 'Ara', 'Iju', 'Jmo'], + 'weekdays_min' => ['Sab', 'Jtt', 'Jnn', 'Jtn', 'Ara', 'Iju', 'Jmo'], + 'months' => ['Mweri wo kwanza', 'Mweri wo unayeli', 'Mweri wo uneraru', 'Mweri wo unecheshe', 'Mweri wo unethanu', 'Mweri wo thanu na mocha', 'Mweri wo saba', 'Mweri wo nane', 'Mweri wo tisa', 'Mweri wo kumi', 'Mweri wo kumi na moja', 'Mweri wo kumi na yel’li'], + 'months_short' => ['Kwa', 'Una', 'Rar', 'Che', 'Tha', 'Moc', 'Sab', 'Nan', 'Tis', 'Kum', 'Moj', 'Yel'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mgo.php b/vendor/nesbot/carbon/src/Carbon/Lang/mgo.php new file mode 100644 index 0000000..a126c9f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mgo.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'weekdays' => ['Aneg 1', 'Aneg 2', 'Aneg 3', 'Aneg 4', 'Aneg 5', 'Aneg 6', 'Aneg 7'], + 'weekdays_short' => ['Aneg 1', 'Aneg 2', 'Aneg 3', 'Aneg 4', 'Aneg 5', 'Aneg 6', 'Aneg 7'], + 'weekdays_min' => ['1', '2', '3', '4', '5', '6', '7'], + 'months' => ['iməg mbegtug', 'imeg àbùbì', 'imeg mbəŋchubi', 'iməg ngwə̀t', 'iməg fog', 'iməg ichiibɔd', 'iməg àdùmbə̀ŋ', 'iməg ichika', 'iməg kud', 'iməg tèsiʼe', 'iməg zò', 'iməg krizmed'], + 'months_short' => ['mbegtug', 'imeg àbùbì', 'imeg mbəŋchubi', 'iməg ngwə̀t', 'iməg fog', 'iməg ichiibɔd', 'iməg àdùmbə̀ŋ', 'iməg ichika', 'iməg kud', 'iməg tèsiʼe', 'iməg zò', 'iməg krizmed'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-dd', + 'LL' => 'YYYY MMM D', + 'LLL' => 'YYYY MMMM D HH:mm', + 'LLLL' => 'dddd, YYYY MMMM DD HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mhr.php b/vendor/nesbot/carbon/src/Carbon/Lang/mhr.php new file mode 100644 index 0000000..6bbc9f6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mhr.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/mhr_RU.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mhr_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/mhr_RU.php new file mode 100644 index 0000000..309ead9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mhr_RU.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - PeshSajSoft Ltd. Vyacheslav Kileev slavakileev@yandex.ru + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'YYYY.MM.DD', + ], + 'months' => ['Шорыкйол', 'Пургыж', 'Ӱярня', 'Вӱдшор', 'Ага', 'Пеледыш', 'Сӱрем', 'Сорла', 'Идым', 'Шыжа', 'Кылме', 'Теле'], + 'months_short' => ['Шрк', 'Пгж', 'Ӱрн', 'Вшр', 'Ага', 'Пдш', 'Срм', 'Срл', 'Идм', 'Шыж', 'Клм', 'Тел'], + 'weekdays' => ['Рушарня', 'Шочмо', 'Кушкыжмо', 'Вӱргече', 'Изарня', 'Кугарня', 'Шуматкече'], + 'weekdays_short' => ['Ршр', 'Шчм', 'Кжм', 'Вгч', 'Изр', 'Кгр', 'Шмт'], + 'weekdays_min' => ['Ршр', 'Шчм', 'Кжм', 'Вгч', 'Изр', 'Кгр', 'Шмт'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'year' => ':count идалык', + 'y' => ':count идалык', + 'a_year' => ':count идалык', + + 'month' => ':count Тылзе', + 'm' => ':count Тылзе', + 'a_month' => ':count Тылзе', + + 'week' => ':count арня', + 'w' => ':count арня', + 'a_week' => ':count арня', + + 'day' => ':count кече', + 'd' => ':count кече', + 'a_day' => ':count кече', + + 'hour' => ':count час', + 'h' => ':count час', + 'a_hour' => ':count час', + + 'minute' => ':count минут', + 'min' => ':count минут', + 'a_minute' => ':count минут', + + 'second' => ':count кокымшан', + 's' => ':count кокымшан', + 'a_second' => ':count кокымшан', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mi.php b/vendor/nesbot/carbon/src/Carbon/Lang/mi.php new file mode 100644 index 0000000..b7f51ec --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mi.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - John Corrigan + * - François B + */ +return [ + 'year' => ':count tau', + 'a_year' => '{1}he tau|:count tau', + 'month' => ':count marama', + 'a_month' => '{1}he marama|:count marama', + 'week' => ':count wiki', + 'a_week' => '{1}he wiki|:count wiki', + 'day' => ':count ra', + 'a_day' => '{1}he ra|:count ra', + 'hour' => ':count haora', + 'a_hour' => '{1}te haora|:count haora', + 'minute' => ':count meneti', + 'a_minute' => '{1}he meneti|:count meneti', + 'second' => ':count hēkona', + 'a_second' => '{1}te hēkona ruarua|:count hēkona', + 'ago' => ':time i mua', + 'from_now' => 'i roto i :time', + 'diff_yesterday' => 'inanahi', + 'diff_yesterday_regexp' => 'inanahi(?:\\s+i)?', + 'diff_today' => 'i teie', + 'diff_today_regexp' => 'i teie(?:\\s+mahana,)?(?:\\s+i)?', + 'diff_tomorrow' => 'apopo', + 'diff_tomorrow_regexp' => 'apopo(?:\\s+i)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY [i] HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY [i] HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[i teie mahana, i] LT', + 'nextDay' => '[apopo i] LT', + 'nextWeek' => 'dddd [i] LT', + 'lastDay' => '[inanahi i] LT', + 'lastWeek' => 'dddd [whakamutunga i] LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':numberº', + 'months' => ['Kohi-tāte', 'Hui-tanguru', 'Poutū-te-rangi', 'Paenga-whāwhā', 'Haratua', 'Pipiri', 'Hōngoingoi', 'Here-turi-kōkā', 'Mahuru', 'Whiringa-ā-nuku', 'Whiringa-ā-rangi', 'Hakihea'], + 'months_short' => ['Kohi', 'Hui', 'Pou', 'Pae', 'Hara', 'Pipi', 'Hōngoi', 'Here', 'Mahu', 'Whi-nu', 'Whi-ra', 'Haki'], + 'weekdays' => ['Rātapu', 'Mane', 'Tūrei', 'Wenerei', 'Tāite', 'Paraire', 'Hātarei'], + 'weekdays_short' => ['Ta', 'Ma', 'Tū', 'We', 'Tāi', 'Pa', 'Hā'], + 'weekdays_min' => ['Ta', 'Ma', 'Tū', 'We', 'Tāi', 'Pa', 'Hā'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' me te '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mi_NZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/mi_NZ.php new file mode 100644 index 0000000..6b964e3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mi_NZ.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/mi.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/miq.php b/vendor/nesbot/carbon/src/Carbon/Lang/miq.php new file mode 100644 index 0000000..51e5a98 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/miq.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/miq_NI.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/miq_NI.php b/vendor/nesbot/carbon/src/Carbon/Lang/miq_NI.php new file mode 100644 index 0000000..57faa31 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/miq_NI.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['siakwa kati', 'kuswa kati', 'kakamuk kati', 'lî wainhka kati', 'lih mairin kati', 'lî kati', 'pastara kati', 'sikla kati', 'wîs kati', 'waupasa kati', 'yahbra kati', 'trisu kati'], + 'months_short' => ['siakwa kati', 'kuswa kati', 'kakamuk kati', 'lî wainhka kati', 'lih mairin kati', 'lî kati', 'pastara kati', 'sikla kati', 'wîs kati', 'waupasa kati', 'yahbra kati', 'trisu kati'], + 'weekdays' => ['sandi', 'mundi', 'tiusdi', 'wensde', 'tausde', 'praidi', 'satadi'], + 'weekdays_short' => ['san', 'mun', 'tius', 'wens', 'taus', 'prai', 'sat'], + 'weekdays_min' => ['san', 'mun', 'tius', 'wens', 'taus', 'prai', 'sat'], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 7, + 'meridiem' => ['VM', 'NM'], + + 'month' => ':count kati', // less reliable + 'm' => ':count kati', // less reliable + 'a_month' => ':count kati', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mjw.php b/vendor/nesbot/carbon/src/Carbon/Lang/mjw.php new file mode 100644 index 0000000..617154c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mjw.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/mjw_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mjw_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mjw_IN.php new file mode 100644 index 0000000..58ed0d1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mjw_IN.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Jor Teron bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['Arkoi', 'Thangthang', 'There', 'Jangmi', 'Aru', 'Vosik', 'Jakhong', 'Paipai', 'Chiti', 'Phere', 'Phaikuni', 'Matijong'], + 'months_short' => ['Ark', 'Thang', 'The', 'Jang', 'Aru', 'Vos', 'Jak', 'Pai', 'Chi', 'Phe', 'Phai', 'Mati'], + 'weekdays' => ['Bhomkuru', 'Urmi', 'Durmi', 'Thelang', 'Theman', 'Bhomta', 'Bhomti'], + 'weekdays_short' => ['Bhom', 'Ur', 'Dur', 'Tkel', 'Tkem', 'Bhta', 'Bhti'], + 'weekdays_min' => ['Bhom', 'Ur', 'Dur', 'Tkel', 'Tkem', 'Bhta', 'Bhti'], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mk.php b/vendor/nesbot/carbon/src/Carbon/Lang/mk.php new file mode 100644 index 0000000..d822de0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mk.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Sashko Todorov + * - Josh Soref + * - François B + * - Serhan Apaydın + * - Borislav Mickov + * - JD Isaacks + * - Tomi Atanasoski + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count година|:count години', + 'a_year' => 'година|:count години', + 'y' => ':count год.', + 'month' => ':count месец|:count месеци', + 'a_month' => 'месец|:count месеци', + 'm' => ':count месец|:count месеци', + 'week' => ':count седмица|:count седмици', + 'a_week' => 'седмица|:count седмици', + 'w' => ':count седмица|:count седмици', + 'day' => ':count ден|:count дена', + 'a_day' => 'ден|:count дена', + 'd' => ':count ден|:count дена', + 'hour' => ':count час|:count часа', + 'a_hour' => 'час|:count часа', + 'h' => ':count час|:count часа', + 'minute' => ':count минута|:count минути', + 'a_minute' => 'минута|:count минути', + 'min' => ':count мин.', + 'second' => ':count секунда|:count секунди', + 'a_second' => 'неколку секунди|:count секунди', + 's' => ':count сек.', + 'ago' => 'пред :time', + 'from_now' => 'после :time', + 'after' => 'по :time', + 'before' => 'пред :time', + 'diff_now' => 'сега', + 'diff_today' => 'Денес', + 'diff_today_regexp' => 'Денес(?:\\s+во)?', + 'diff_yesterday' => 'вчера', + 'diff_yesterday_regexp' => 'Вчера(?:\\s+во)?', + 'diff_tomorrow' => 'утре', + 'diff_tomorrow_regexp' => 'Утре(?:\\s+во)?', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'D.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY H:mm', + 'LLLL' => 'dddd, D MMMM YYYY H:mm', + ], + 'calendar' => [ + 'sameDay' => '[Денес во] LT', + 'nextDay' => '[Утре во] LT', + 'nextWeek' => '[Во] dddd [во] LT', + 'lastDay' => '[Вчера во] LT', + 'lastWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + case 3: + case 6: + return '[Изминатата] dddd [во] LT'; + default: + return '[Изминатиот] dddd [во] LT'; + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + $lastDigit = $number % 10; + $last2Digits = $number % 100; + if ($number === 0) { + return $number.'-ев'; + } + if ($last2Digits === 0) { + return $number.'-ен'; + } + if ($last2Digits > 10 && $last2Digits < 20) { + return $number.'-ти'; + } + if ($lastDigit === 1) { + return $number.'-ви'; + } + if ($lastDigit === 2) { + return $number.'-ри'; + } + if ($lastDigit === 7 || $lastDigit === 8) { + return $number.'-ми'; + } + + return $number.'-ти'; + }, + 'months' => ['јануари', 'февруари', 'март', 'април', 'мај', 'јуни', 'јули', 'август', 'септември', 'октомври', 'ноември', 'декември'], + 'months_short' => ['јан', 'фев', 'мар', 'апр', 'мај', 'јун', 'јул', 'авг', 'сеп', 'окт', 'ное', 'дек'], + 'weekdays' => ['недела', 'понеделник', 'вторник', 'среда', 'четврток', 'петок', 'сабота'], + 'weekdays_short' => ['нед', 'пон', 'вто', 'сре', 'чет', 'пет', 'саб'], + 'weekdays_min' => ['нe', 'пo', 'вт', 'ср', 'че', 'пе', 'сa'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' и '], + 'meridiem' => ['АМ', 'ПМ'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mk_MK.php b/vendor/nesbot/carbon/src/Carbon/Lang/mk_MK.php new file mode 100644 index 0000000..95e2ff9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mk_MK.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/mk.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ml.php b/vendor/nesbot/carbon/src/Carbon/Lang/ml.php new file mode 100644 index 0000000..1abd6c4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ml.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - JD Isaacks + */ +return [ + 'year' => ':count വർഷം', + 'a_year' => 'ഒരു വർഷം|:count വർഷം', + 'month' => ':count മാസം', + 'a_month' => 'ഒരു മാസം|:count മാസം', + 'week' => ':count ആഴ്ച', + 'a_week' => 'ഒരാഴ്ച|:count ആഴ്ച', + 'day' => ':count ദിവസം', + 'a_day' => 'ഒരു ദിവസം|:count ദിവസം', + 'hour' => ':count മണിക്കൂർ', + 'a_hour' => 'ഒരു മണിക്കൂർ|:count മണിക്കൂർ', + 'minute' => ':count മിനിറ്റ്', + 'a_minute' => 'ഒരു മിനിറ്റ്|:count മിനിറ്റ്', + 'second' => ':count സെക്കൻഡ്', + 'a_second' => 'അൽപ നിമിഷങ്ങൾ|:count സെക്കൻഡ്', + 'ago' => ':time മുൻപ്', + 'from_now' => ':time കഴിഞ്ഞ്', + 'diff_now' => 'ഇപ്പോൾ', + 'diff_today' => 'ഇന്ന്', + 'diff_yesterday' => 'ഇന്നലെ', + 'diff_tomorrow' => 'നാളെ', + 'formats' => [ + 'LT' => 'A h:mm -നു', + 'LTS' => 'A h:mm:ss -നു', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY, A h:mm -നു', + 'LLLL' => 'dddd, D MMMM YYYY, A h:mm -നു', + ], + 'calendar' => [ + 'sameDay' => '[ഇന്ന്] LT', + 'nextDay' => '[നാളെ] LT', + 'nextWeek' => 'dddd, LT', + 'lastDay' => '[ഇന്നലെ] LT', + 'lastWeek' => '[കഴിഞ്ഞ] dddd, LT', + 'sameElse' => 'L', + ], + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'രാത്രി'; + } + if ($hour < 12) { + return 'രാവിലെ'; + } + if ($hour < 17) { + return 'ഉച്ച കഴിഞ്ഞ്'; + } + if ($hour < 20) { + return 'വൈകുന്നേരം'; + } + + return 'രാത്രി'; + }, + 'months' => ['ജനുവരി', 'ഫെബ്രുവരി', 'മാർച്ച്', 'ഏപ്രിൽ', 'മേയ്', 'ജൂൺ', 'ജൂലൈ', 'ഓഗസ്റ്റ്', 'സെപ്റ്റംബർ', 'ഒക്ടോബർ', 'നവംബർ', 'ഡിസംബർ'], + 'months_short' => ['ജനു.', 'ഫെബ്രു.', 'മാർ.', 'ഏപ്രി.', 'മേയ്', 'ജൂൺ', 'ജൂലൈ.', 'ഓഗ.', 'സെപ്റ്റ.', 'ഒക്ടോ.', 'നവം.', 'ഡിസം.'], + 'weekdays' => ['ഞായറാഴ്ച', 'തിങ്കളാഴ്ച', 'ചൊവ്വാഴ്ച', 'ബുധനാഴ്ച', 'വ്യാഴാഴ്ച', 'വെള്ളിയാഴ്ച', 'ശനിയാഴ്ച'], + 'weekdays_short' => ['ഞായർ', 'തിങ്കൾ', 'ചൊവ്വ', 'ബുധൻ', 'വ്യാഴം', 'വെള്ളി', 'ശനി'], + 'weekdays_min' => ['ഞാ', 'തി', 'ചൊ', 'ബു', 'വ്യാ', 'വെ', 'ശ'], + 'list' => ', ', + 'weekend' => [0, 0], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ml_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ml_IN.php new file mode 100644 index 0000000..000e795 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ml_IN.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ml.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mn.php b/vendor/nesbot/carbon/src/Carbon/Lang/mn.php new file mode 100644 index 0000000..38c6434 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mn.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Philippe Vaucher + * - Tsutomu Kuroda + * - tjku + * - Max Melentiev + * - Zolzaya Erdenebaatar + * - Tom Hughes + * - Akira Matsuda + * - Christopher Dell + * - Michael Kessler + * - Enrique Vidal + * - Simone Carletti + * - Aaron Patterson + * - Nicolás Hock Isaza + * - Ochirkhuyag + * - Batmandakh + * - lucifer-crybaby + */ +return [ + 'year' => ':count жил', + 'y' => ':count жил', + 'month' => ':count сар', + 'm' => ':count сар', + 'week' => ':count долоо хоног', + 'w' => ':count долоо хоног', + 'day' => ':count өдөр', + 'd' => ':count өдөр', + 'hour' => ':count цаг', + 'h' => ':countц', + 'minute' => ':count минут', + 'min' => ':countм', + 'second' => ':count секунд', + 's' => ':countс', + + 'ago_mode' => 'last', + 'ago' => ':time өмнө', + 'year_ago' => ':count жилийн', + 'y_ago' => ':count жилийн', + 'month_ago' => ':count сарын', + 'm_ago' => ':count сарын', + 'day_ago' => ':count хоногийн', + 'd_ago' => ':count хоногийн', + 'week_ago' => ':count долоо хоногийн', + 'w_ago' => ':count долоо хоногийн', + 'hour_ago' => ':count цагийн', + 'minute_ago' => ':count минутын', + 'second_ago' => ':count секундын', + + 'from_now_mode' => 'last', + 'from_now' => 'одоогоос :time', + 'year_from_now' => ':count жилийн дараа', + 'y_from_now' => ':count жилийн дараа', + 'month_from_now' => ':count сарын дараа', + 'm_from_now' => ':count сарын дараа', + 'day_from_now' => ':count хоногийн дараа', + 'd_from_now' => ':count хоногийн дараа', + 'hour_from_now' => ':count цагийн дараа', + 'minute_from_now' => ':count минутын дараа', + 'second_from_now' => ':count секундын дараа', + + 'after_mode' => 'last', + 'after' => ':time дараа', + 'year_after' => ':count жилийн', + 'y_after' => ':count жилийн', + 'month_after' => ':count сарын', + 'm_after' => ':count сарын', + 'day_after' => ':count хоногийн', + 'd_after' => ':count хоногийн', + 'hour_after' => ':count цагийн', + 'minute_after' => ':count минутын', + 'second_after' => ':count секундын', + + 'before_mode' => 'last', + 'before' => ':time өмнө', + 'year_before' => ':count жилийн', + 'y_before' => ':count жилийн', + 'month_before' => ':count сарын', + 'm_before' => ':count сарын', + 'day_before' => ':count хоногийн', + 'd_before' => ':count хоногийн', + 'hour_before' => ':count цагийн', + 'minute_before' => ':count минутын', + 'second_before' => ':count секундын', + + 'list' => ', ', + 'diff_now' => 'одоо', + 'diff_yesterday' => 'өчигдөр', + 'diff_tomorrow' => 'маргааш', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-DD', + 'LL' => 'YYYY MMMM DD', + 'LLL' => 'YY-MM-DD, HH:mm', + 'LLLL' => 'YYYY MMMM DD, HH:mm', + ], + 'weekdays' => ['Ням', 'Даваа', 'Мягмар', 'Лхагва', 'Пүрэв', 'Баасан', 'Бямба'], + 'weekdays_short' => ['Ня', 'Да', 'Мя', 'Лх', 'Пү', 'Ба', 'Бя'], + 'weekdays_min' => ['Ня', 'Да', 'Мя', 'Лх', 'Пү', 'Ба', 'Бя'], + 'months' => ['1 сар', '2 сар', '3 сар', '4 сар', '5 сар', '6 сар', '7 сар', '8 сар', '9 сар', '10 сар', '11 сар', '12 сар'], + 'months_short' => ['1 сар', '2 сар', '3 сар', '4 сар', '5 сар', '6 сар', '7 сар', '8 сар', '9 сар', '10 сар', '11 сар', '12 сар'], + 'meridiem' => ['өглөө', 'орой'], + 'first_day_of_week' => 1, +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mn_MN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mn_MN.php new file mode 100644 index 0000000..e5ce426 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mn_MN.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/mn.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mni.php b/vendor/nesbot/carbon/src/Carbon/Lang/mni.php new file mode 100644 index 0000000..cafa2f8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mni.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/mni_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mni_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mni_IN.php new file mode 100644 index 0000000..45d430e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mni_IN.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Red Hat Pune libc-alpha@sourceware.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['জানুৱারি', 'ফেব্রুৱারি', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগষ্ট', 'সেপ্তেম্বর', 'ওক্তোবর', 'নবেম্বর', 'ডিসেম্বর'], + 'months_short' => ['জান', 'ফেব', 'মার', 'এপ্রি', 'মে', 'জুন', 'জুল', 'আগ', 'সেপ', 'ওক্ত', 'নবে', 'ডিস'], + 'weekdays' => ['নোংমাইজিং', 'নিংথৌকাবা', 'লৈবাকপোকপা', 'য়ুমশকৈশা', 'শগোলশেন', 'ইরাই', 'থাংজ'], + 'weekdays_short' => ['নোং', 'নিং', 'লৈবাক', 'য়ুম', 'শগোল', 'ইরা', 'থাং'], + 'weekdays_min' => ['নোং', 'নিং', 'লৈবাক', 'য়ুম', 'শগোল', 'ইরা', 'থাং'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['এ.ম.', 'প.ম.'], + + 'year' => ':count ইসিং', // less reliable + 'y' => ':count ইসিং', // less reliable + 'a_year' => ':count ইসিং', // less reliable + + 'second' => ':count ꯅꯤꯡꯊꯧꯀꯥꯕ', // less reliable + 's' => ':count ꯅꯤꯡꯊꯧꯀꯥꯕ', // less reliable + 'a_second' => ':count ꯅꯤꯡꯊꯧꯀꯥꯕ', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mo.php b/vendor/nesbot/carbon/src/Carbon/Lang/mo.php new file mode 100644 index 0000000..102afcd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mo.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ro.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mr.php b/vendor/nesbot/carbon/src/Carbon/Lang/mr.php new file mode 100644 index 0000000..4aaeafd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mr.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Vikram-enyota + */ +return [ + 'year' => ':count वर्ष', + 'y' => ':count वर्ष', + 'month' => ':count महिना|:count महिने', + 'm' => ':count महिना|:count महिने', + 'week' => ':count आठवडा|:count आठवडे', + 'w' => ':count आठवडा|:count आठवडे', + 'day' => ':count दिवस', + 'd' => ':count दिवस', + 'hour' => ':count तास', + 'h' => ':count तास', + 'minute' => ':count मिनिटे', + 'min' => ':count मिनिटे', + 'second' => ':count सेकंद', + 's' => ':count सेकंद', + + 'ago' => ':timeपूर्वी', + 'from_now' => ':timeमध्ये', + 'before' => ':timeपूर्वी', + 'after' => ':timeनंतर', + + 'diff_now' => 'आत्ता', + 'diff_today' => 'आज', + 'diff_yesterday' => 'काल', + 'diff_tomorrow' => 'उद्या', + + 'formats' => [ + 'LT' => 'A h:mm वाजता', + 'LTS' => 'A h:mm:ss वाजता', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY, A h:mm वाजता', + 'LLLL' => 'dddd, D MMMM YYYY, A h:mm वाजता', + ], + + 'calendar' => [ + 'sameDay' => '[आज] LT', + 'nextDay' => '[उद्या] LT', + 'nextWeek' => 'dddd, LT', + 'lastDay' => '[काल] LT', + 'lastWeek' => '[मागील] dddd, LT', + 'sameElse' => 'L', + ], + + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'रात्री'; + } + if ($hour < 10) { + return 'सकाळी'; + } + if ($hour < 17) { + return 'दुपारी'; + } + if ($hour < 20) { + return 'सायंकाळी'; + } + + return 'रात्री'; + }, + + 'months' => ['जानेवारी', 'फेब्रुवारी', 'मार्च', 'एप्रिल', 'मे', 'जून', 'जुलै', 'ऑगस्ट', 'सप्टेंबर', 'ऑक्टोबर', 'नोव्हेंबर', 'डिसेंबर'], + 'months_short' => ['जाने.', 'फेब्रु.', 'मार्च.', 'एप्रि.', 'मे.', 'जून.', 'जुलै.', 'ऑग.', 'सप्टें.', 'ऑक्टो.', 'नोव्हें.', 'डिसें.'], + 'weekdays' => ['रविवार', 'सोमवार', 'मंगळवार', 'बुधवार', 'गुरूवार', 'शुक्रवार', 'शनिवार'], + 'weekdays_short' => ['रवि', 'सोम', 'मंगळ', 'बुध', 'गुरू', 'शुक्र', 'शनि'], + 'weekdays_min' => ['र', 'सो', 'मं', 'बु', 'गु', 'शु', 'श'], + 'list' => [', ', ' आणि '], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, + 'weekend' => [0, 0], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mr_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mr_IN.php new file mode 100644 index 0000000..7bca919 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mr_IN.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/mr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ms.php b/vendor/nesbot/carbon/src/Carbon/Lang/ms.php new file mode 100644 index 0000000..c9e8085 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ms.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - Azri Jamil + * - JD Isaacks + * - Josh Soref + * - Azri Jamil + * - Hariadi Hinta + * - Ashraf Kamarudin + */ +return [ + 'year' => ':count tahun', + 'a_year' => '{1}setahun|]1,Inf[:count tahun', + 'y' => ':count tahun', + 'month' => ':count bulan', + 'a_month' => '{1}sebulan|]1,Inf[:count bulan', + 'm' => ':count bulan', + 'week' => ':count minggu', + 'a_week' => '{1}seminggu|]1,Inf[:count minggu', + 'w' => ':count minggu', + 'day' => ':count hari', + 'a_day' => '{1}sehari|]1,Inf[:count hari', + 'd' => ':count hari', + 'hour' => ':count jam', + 'a_hour' => '{1}sejam|]1,Inf[:count jam', + 'h' => ':count jam', + 'minute' => ':count minit', + 'a_minute' => '{1}seminit|]1,Inf[:count minit', + 'min' => ':count minit', + 'second' => ':count saat', + 'a_second' => '{1}beberapa saat|]1,Inf[:count saat', + 'millisecond' => ':count milisaat', + 'a_millisecond' => '{1}semilisaat|]1,Inf[:count milliseconds', + 'microsecond' => ':count mikrodetik', + 'a_microsecond' => '{1}semikrodetik|]1,Inf[:count mikrodetik', + 's' => ':count saat', + 'ago' => ':time yang lepas', + 'from_now' => ':time dari sekarang', + 'after' => ':time kemudian', + 'before' => ':time sebelum', + 'diff_now' => 'sekarang', + 'diff_today' => 'Hari', + 'diff_today_regexp' => 'Hari(?:\\s+ini)?(?:\\s+pukul)?', + 'diff_yesterday' => 'semalam', + 'diff_yesterday_regexp' => 'Semalam(?:\\s+pukul)?', + 'diff_tomorrow' => 'esok', + 'diff_tomorrow_regexp' => 'Esok(?:\\s+pukul)?', + 'diff_before_yesterday' => 'kelmarin', + 'diff_after_tomorrow' => 'lusa', + 'formats' => [ + 'LT' => 'HH.mm', + 'LTS' => 'HH.mm.ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY [pukul] HH.mm', + 'LLLL' => 'dddd, D MMMM YYYY [pukul] HH.mm', + ], + 'calendar' => [ + 'sameDay' => '[Hari ini pukul] LT', + 'nextDay' => '[Esok pukul] LT', + 'nextWeek' => 'dddd [pukul] LT', + 'lastDay' => '[Kelmarin pukul] LT', + 'lastWeek' => 'dddd [lepas pukul] LT', + 'sameElse' => 'L', + ], + 'meridiem' => function ($hour) { + if ($hour < 1) { + return 'tengah malam'; + } + + if ($hour < 12) { + return 'pagi'; + } + + if ($hour < 13) { + return 'tengah hari'; + } + + if ($hour < 19) { + return 'petang'; + } + + return 'malam'; + }, + 'months' => ['Januari', 'Februari', 'Mac', 'April', 'Mei', 'Jun', 'Julai', 'Ogos', 'September', 'Oktober', 'November', 'Disember'], + 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ogs', 'Sep', 'Okt', 'Nov', 'Dis'], + 'weekdays' => ['Ahad', 'Isnin', 'Selasa', 'Rabu', 'Khamis', 'Jumaat', 'Sabtu'], + 'weekdays_short' => ['Ahd', 'Isn', 'Sel', 'Rab', 'Kha', 'Jum', 'Sab'], + 'weekdays_min' => ['Ah', 'Is', 'Sl', 'Rb', 'Km', 'Jm', 'Sb'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' dan '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ms_BN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ms_BN.php new file mode 100644 index 0000000..ef837a2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ms_BN.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ms.php', [ + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'D/MM/yy', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY, h:mm a', + 'LLLL' => 'dd MMMM YYYY, h:mm a', + ], + 'meridiem' => ['a', 'p'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ms_MY.php b/vendor/nesbot/carbon/src/Carbon/Lang/ms_MY.php new file mode 100644 index 0000000..970d604 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ms_MY.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - Azri Jamil + * - JD Isaacks + */ +return require __DIR__.'/ms.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ms_SG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ms_SG.php new file mode 100644 index 0000000..77cb83d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ms_SG.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ms.php', [ + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'D/MM/yy', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY, h:mm a', + 'LLLL' => 'dddd, D MMMM YYYY, h:mm a', + ], + 'meridiem' => ['a', 'p'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mt.php b/vendor/nesbot/carbon/src/Carbon/Lang/mt.php new file mode 100644 index 0000000..e8aadcc --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mt.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Alessandro Maruccia + */ +return [ + 'year' => 'sena|:count sni|:count sni|:count sni', + 'y' => 'sa sena|:count snin|:count snin|:count snin', + 'month' => 'xahar|:count xhur|:count xhur|:count xhur', + 'm' => ':count xahar|:count xhur|:count xhur|:count xhur', + 'week' => 'gimgħa|:count ġimgħat|:count ġimgħat|:count ġimgħat', + 'w' => 'ġimgħa|:count ġimgħat|:count ġimgħat|:count ġimgħat', + 'day' => 'ġurnata|:count ġranet|:count ġranet|:count ġranet', + 'd' => 'ġurnata|:count ġranet|:count ġranet|:count ġranet', + 'hour' => 'siegħa|:count siegħat|:count siegħat|:count siegħat', + 'h' => 'siegħa|:count sigħat|:count sigħat|:count sigħat', + 'minute' => 'minuta|:count minuti|:count minuti|:count minuti', + 'min' => 'min.|:count min.|:count min.|:count min.', + 'second' => 'ftit sekondi|:count sekondi|:count sekondi|:count sekondi', + 's' => 'sek.|:count sek.|:count sek.|:count sek.', + 'ago' => ':time ilu', + 'from_now' => 'f’ :time', + 'diff_now' => 'issa', + 'diff_today' => 'Illum', + 'diff_today_regexp' => 'Illum(?:\\s+fil-)?', + 'diff_yesterday' => 'lbieraħ', + 'diff_yesterday_regexp' => 'Il-bieraħ(?:\\s+fil-)?', + 'diff_tomorrow' => 'għada', + 'diff_tomorrow_regexp' => 'Għada(?:\\s+fil-)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Illum fil-]LT', + 'nextDay' => '[Għada fil-]LT', + 'nextWeek' => 'dddd [fil-]LT', + 'lastDay' => '[Il-bieraħ fil-]LT', + 'lastWeek' => 'dddd [li għadda] [fil-]LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':numberº', + 'months' => ['Jannar', 'Frar', 'Marzu', 'April', 'Mejju', 'Ġunju', 'Lulju', 'Awwissu', 'Settembru', 'Ottubru', 'Novembru', 'Diċembru'], + 'months_short' => ['Jan', 'Fra', 'Mar', 'Apr', 'Mej', 'Ġun', 'Lul', 'Aww', 'Set', 'Ott', 'Nov', 'Diċ'], + 'weekdays' => ['Il-Ħadd', 'It-Tnejn', 'It-Tlieta', 'L-Erbgħa', 'Il-Ħamis', 'Il-Ġimgħa', 'Is-Sibt'], + 'weekdays_short' => ['Ħad', 'Tne', 'Tli', 'Erb', 'Ħam', 'Ġim', 'Sib'], + 'weekdays_min' => ['Ħa', 'Tn', 'Tl', 'Er', 'Ħa', 'Ġi', 'Si'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' u '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mt_MT.php b/vendor/nesbot/carbon/src/Carbon/Lang/mt_MT.php new file mode 100644 index 0000000..9534f68 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mt_MT.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/mt.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mua.php b/vendor/nesbot/carbon/src/Carbon/Lang/mua.php new file mode 100644 index 0000000..a3a3c6f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mua.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['comme', 'lilli'], + 'weekdays' => ['Com’yakke', 'Comlaaɗii', 'Comzyiiɗii', 'Comkolle', 'Comkaldǝɓlii', 'Comgaisuu', 'Comzyeɓsuu'], + 'weekdays_short' => ['Cya', 'Cla', 'Czi', 'Cko', 'Cka', 'Cga', 'Cze'], + 'weekdays_min' => ['Cya', 'Cla', 'Czi', 'Cko', 'Cka', 'Cga', 'Cze'], + 'months' => ['Fĩi Loo', 'Cokcwaklaŋne', 'Cokcwaklii', 'Fĩi Marfoo', 'Madǝǝuutǝbijaŋ', 'Mamǝŋgwãafahbii', 'Mamǝŋgwãalii', 'Madǝmbii', 'Fĩi Dǝɓlii', 'Fĩi Mundaŋ', 'Fĩi Gwahlle', 'Fĩi Yuru'], + 'months_short' => ['FLO', 'CLA', 'CKI', 'FMF', 'MAD', 'MBI', 'MLI', 'MAM', 'FDE', 'FMU', 'FGW', 'FYU'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/my.php b/vendor/nesbot/carbon/src/Carbon/Lang/my.php new file mode 100644 index 0000000..bbdfba4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/my.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - JD Isaacks + * - Nay Lin Aung + */ +return [ + 'year' => '{1}တစ်နှစ်|]1,Inf[:count နှစ်', + 'y' => ':count နှစ်', + 'month' => '{1}တစ်လ|]1,Inf[:count လ', + 'm' => ':count လ', + 'week' => ':count ပတ်', + 'w' => ':count ပတ်', + 'day' => '{1}တစ်ရက်|]1,Inf[:count ရက်', + 'd' => ':count ရက်', + 'hour' => '{1}တစ်နာရီ|]1,Inf[:count နာရီ', + 'h' => ':count နာရီ', + 'minute' => '{1}တစ်မိနစ်|]1,Inf[:count မိနစ်', + 'min' => ':count မိနစ်', + 'second' => '{1}စက္ကန်.အနည်းငယ်|]1,Inf[:count စက္ကန့်', + 's' => ':count စက္ကန့်', + 'ago' => 'လွန်ခဲ့သော :time က', + 'from_now' => 'လာမည့် :time မှာ', + 'after' => ':time ကြာပြီးနောက်', + 'before' => ':time မတိုင်ခင်', + 'diff_now' => 'အခုလေးတင်', + 'diff_today' => 'ယနေ.', + 'diff_yesterday' => 'မနေ့က', + 'diff_yesterday_regexp' => 'မနေ.က', + 'diff_tomorrow' => 'မနက်ဖြန်', + 'diff_before_yesterday' => 'တမြန်နေ့က', + 'diff_after_tomorrow' => 'တဘက်ခါ', + 'period_recurrences' => ':count ကြိမ်', + 'formats' => [ + 'LT' => 'Oh:Om A', + 'LTS' => 'Oh:Om:Os A', + 'L' => 'OD/OM/OY', + 'LL' => 'OD MMMM OY', + 'LLL' => 'OD MMMM OY Oh:Om A', + 'LLLL' => 'dddd OD MMMM OY Oh:Om A', + ], + 'calendar' => [ + 'sameDay' => '[ယနေ.] LT [မှာ]', + 'nextDay' => '[မနက်ဖြန်] LT [မှာ]', + 'nextWeek' => 'dddd LT [မှာ]', + 'lastDay' => '[မနေ.က] LT [မှာ]', + 'lastWeek' => '[ပြီးခဲ့သော] dddd LT [မှာ]', + 'sameElse' => 'L', + ], + 'months' => ['ဇန်နဝါရီ', 'ဖေဖော်ဝါရီ', 'မတ်', 'ဧပြီ', 'မေ', 'ဇွန်', 'ဇူလိုင်', 'သြဂုတ်', 'စက်တင်ဘာ', 'အောက်တိုဘာ', 'နိုဝင်ဘာ', 'ဒီဇင်ဘာ'], + 'months_short' => ['ဇန်', 'ဖေ', 'မတ်', 'ပြီ', 'မေ', 'ဇွန်', 'လိုင်', 'သြ', 'စက်', 'အောက်', 'နို', 'ဒီ'], + 'weekdays' => ['တနင်္ဂနွေ', 'တနင်္လာ', 'အင်္ဂါ', 'ဗုဒ္ဓဟူး', 'ကြာသပတေး', 'သောကြာ', 'စနေ'], + 'weekdays_short' => ['နွေ', 'လာ', 'ဂါ', 'ဟူး', 'ကြာ', 'သော', 'နေ'], + 'weekdays_min' => ['နွေ', 'လာ', 'ဂါ', 'ဟူး', 'ကြာ', 'သော', 'နေ'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'alt_numbers' => ['၀၀', '၀၁', '၀၂', '၀၃', '၀၄', '၀၅', '၀၆', '၀၇', '၀၈', '၀၉', '၁၀', '၁၁', '၁၂', '၁၃', '၁၄', '၁၅', '၁၆', '၁၇', '၁၈', '၁၉', '၂၀', '၂၁', '၂၂', '၂၃', '၂၄', '၂၅', '၂၆', '၂၇', '၂၈', '၂၉', '၃၀', '၃၁', '၃၂', '၃၃', '၃၄', '၃၅', '၃၆', '၃၇', '၃၈', '၃၉', '၄၀', '၄၁', '၄၂', '၄၃', '၄၄', '၄၅', '၄၆', '၄၇', '၄၈', '၄၉', '၅၀', '၅၁', '၅၂', '၅၃', '၅၄', '၅၅', '၅၆', '၅၇', '၅၈', '၅၉', '၆၀', '၆၁', '၆၂', '၆၃', '၆၄', '၆၅', '၆၆', '၆၇', '၆၈', '၆၉', '၇၀', '၇၁', '၇၂', '၇၃', '၇၄', '၇၅', '၇၆', '၇၇', '၇၈', '၇၉', '၈၀', '၈၁', '၈၂', '၈၃', '၈၄', '၈၅', '၈၆', '၈၇', '၈၈', '၈၉', '၉၀', '၉၁', '၉၂', '၉၃', '၉၄', '၉၅', '၉၆', '၉၇', '၉၈', '၉၉'], + 'meridiem' => ['နံနက်', 'ညနေ'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/my_MM.php b/vendor/nesbot/carbon/src/Carbon/Lang/my_MM.php new file mode 100644 index 0000000..a0108dd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/my_MM.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/my.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mzn.php b/vendor/nesbot/carbon/src/Carbon/Lang/mzn.php new file mode 100644 index 0000000..70f5f23 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mzn.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fa.php', [ + 'months' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر', 'نوامبر', 'دسامبر'], + 'months_short' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر', 'نوامبر', 'دسامبر'], + 'first_day_of_week' => 6, + 'weekend' => [5, 5], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-dd', + 'LL' => 'YYYY MMM D', + 'LLL' => 'YYYY MMMM D HH:mm', + 'LLLL' => 'YYYY MMMM D, dddd HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nan.php b/vendor/nesbot/carbon/src/Carbon/Lang/nan.php new file mode 100644 index 0000000..0affece --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nan.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/nan_TW.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW.php new file mode 100644 index 0000000..5c50aa4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'YYYY年MM月DD日', + ], + 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + 'months_short' => [' 1月', ' 2月', ' 3月', ' 4月', ' 5月', ' 6月', ' 7月', ' 8月', ' 9月', '10月', '11月', '12月'], + 'weekdays' => ['禮拜日', '禮拜一', '禮拜二', '禮拜三', '禮拜四', '禮拜五', '禮拜六'], + 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'], + 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['頂晡', '下晡'], + + 'year' => ':count 年', + 'y' => ':count 年', + 'a_year' => ':count 年', + + 'month' => ':count goe̍h', + 'm' => ':count goe̍h', + 'a_month' => ':count goe̍h', + + 'week' => ':count lé-pài', + 'w' => ':count lé-pài', + 'a_week' => ':count lé-pài', + + 'day' => ':count 日', + 'd' => ':count 日', + 'a_day' => ':count 日', + + 'hour' => ':count tiám-cheng', + 'h' => ':count tiám-cheng', + 'a_hour' => ':count tiám-cheng', + + 'minute' => ':count Hun-cheng', + 'min' => ':count Hun-cheng', + 'a_minute' => ':count Hun-cheng', + + 'second' => ':count Bió', + 's' => ':count Bió', + 'a_second' => ':count Bió', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW@latin.php b/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW@latin.php new file mode 100644 index 0000000..99ca2a4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW@latin.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Arne Goetje arne@canonical.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'YYYY-MM-DD', + ], + 'months' => ['1goe̍h', '2goe̍h', '3goe̍h', '4goe̍h', '5goe̍h', '6goe̍h', '7goe̍h', '8goe̍h', '9goe̍h', '10goe̍h', '11goe̍h', '12goe̍h'], + 'months_short' => ['1g', '2g', '3g', '4g', '5g', '6g', '7g', '8g', '9g', '10g', '11g', '12g'], + 'weekdays' => ['lé-pài-ji̍t', 'pài-it', 'pài-jī', 'pài-saⁿ', 'pài-sì', 'pài-gō͘', 'pài-la̍k'], + 'weekdays_short' => ['lp', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6'], + 'weekdays_min' => ['lp', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['téng-po͘', 'ē-po͘'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/naq.php b/vendor/nesbot/carbon/src/Carbon/Lang/naq.php new file mode 100644 index 0000000..fbd9be9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/naq.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['ǁgoagas', 'ǃuias'], + 'weekdays' => ['Sontaxtsees', 'Mantaxtsees', 'Denstaxtsees', 'Wunstaxtsees', 'Dondertaxtsees', 'Fraitaxtsees', 'Satertaxtsees'], + 'weekdays_short' => ['Son', 'Ma', 'De', 'Wu', 'Do', 'Fr', 'Sat'], + 'weekdays_min' => ['Son', 'Ma', 'De', 'Wu', 'Do', 'Fr', 'Sat'], + 'months' => ['ǃKhanni', 'ǃKhanǀgôab', 'ǀKhuuǁkhâb', 'ǃHôaǂkhaib', 'ǃKhaitsâb', 'Gamaǀaeb', 'ǂKhoesaob', 'Aoǁkhuumûǁkhâb', 'Taraǀkhuumûǁkhâb', 'ǂNûǁnâiseb', 'ǀHooǂgaeb', 'Hôasoreǁkhâb'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd, D MMMM YYYY h:mm a', + ], + + 'year' => ':count kurigu', + 'y' => ':count kurigu', + 'a_year' => ':count kurigu', + + 'month' => ':count ǁaub', // less reliable + 'm' => ':count ǁaub', // less reliable + 'a_month' => ':count ǁaub', // less reliable + + 'week' => ':count hû', // less reliable + 'w' => ':count hû', // less reliable + 'a_week' => ':count hû', // less reliable + + 'day' => ':count ǀhobas', // less reliable + 'd' => ':count ǀhobas', // less reliable + 'a_day' => ':count ǀhobas', // less reliable + + 'hour' => ':count ǂgaes', // less reliable + 'h' => ':count ǂgaes', // less reliable + 'a_hour' => ':count ǂgaes', // less reliable + + 'minute' => ':count minutga', // less reliable + 'min' => ':count minutga', // less reliable + 'a_minute' => ':count minutga', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nb.php b/vendor/nesbot/carbon/src/Carbon/Lang/nb.php new file mode 100644 index 0000000..371ee84 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nb.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Alexander Tømmerås + * - Sigurd Gartmann + * - JD Isaacks + */ +return [ + 'year' => ':count år|:count år', + 'a_year' => 'ett år|:count år', + 'y' => ':count år|:count år', + 'month' => ':count måned|:count måneder', + 'a_month' => 'en måned|:count måneder', + 'm' => ':count md.', + 'week' => ':count uke|:count uker', + 'a_week' => 'en uke|:count uker', + 'w' => ':count u.', + 'day' => ':count dag|:count dager', + 'a_day' => 'en dag|:count dager', + 'd' => ':count d.', + 'hour' => ':count time|:count timer', + 'a_hour' => 'en time|:count timer', + 'h' => ':count t', + 'minute' => ':count minutt|:count minutter', + 'a_minute' => 'ett minutt|:count minutter', + 'min' => ':count min', + 'second' => ':count sekund|:count sekunder', + 'a_second' => 'noen sekunder|:count sekunder', + 's' => ':count sek', + 'ago' => ':time siden', + 'from_now' => 'om :time', + 'after' => ':time etter', + 'before' => ':time før', + 'diff_now' => 'akkurat nå', + 'diff_today' => 'i dag', + 'diff_today_regexp' => 'i dag(?:\\s+kl.)?', + 'diff_yesterday' => 'i går', + 'diff_yesterday_regexp' => 'i går(?:\\s+kl.)?', + 'diff_tomorrow' => 'i morgen', + 'diff_tomorrow_regexp' => 'i morgen(?:\\s+kl.)?', + 'diff_before_yesterday' => 'i forgårs', + 'diff_after_tomorrow' => 'i overmorgen', + 'period_recurrences' => 'en gang|:count ganger', + 'period_interval' => 'hver :interval', + 'period_start_date' => 'fra :date', + 'period_end_date' => 'til :date', + 'months' => ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'], + 'months_short' => ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'], + 'weekdays' => ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag'], + 'weekdays_short' => ['søn', 'man', 'tir', 'ons', 'tor', 'fre', 'lør'], + 'weekdays_min' => ['sø', 'ma', 'ti', 'on', 'to', 'fr', 'lø'], + 'ordinal' => ':number.', + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY [kl.] HH:mm', + 'LLLL' => 'dddd D. MMMM YYYY [kl.] HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[i dag kl.] LT', + 'nextDay' => '[i morgen kl.] LT', + 'nextWeek' => 'dddd [kl.] LT', + 'lastDay' => '[i går kl.] LT', + 'lastWeek' => '[forrige] dddd [kl.] LT', + 'sameElse' => 'L', + ], + 'list' => [', ', ' og '], + 'meridiem' => ['a.m.', 'p.m.'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nb_NO.php b/vendor/nesbot/carbon/src/Carbon/Lang/nb_NO.php new file mode 100644 index 0000000..31678c5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nb_NO.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/nb.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nb_SJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/nb_SJ.php new file mode 100644 index 0000000..ce0210b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nb_SJ.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/nb.php', [ + 'formats' => [ + 'LL' => 'D. MMM YYYY', + 'LLL' => 'D. MMMM YYYY, HH:mm', + 'LLLL' => 'dddd D. MMMM YYYY, HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nd.php b/vendor/nesbot/carbon/src/Carbon/Lang/nd.php new file mode 100644 index 0000000..f75d9a7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nd.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'weekdays' => ['Sonto', 'Mvulo', 'Sibili', 'Sithathu', 'Sine', 'Sihlanu', 'Mgqibelo'], + 'weekdays_short' => ['Son', 'Mvu', 'Sib', 'Sit', 'Sin', 'Sih', 'Mgq'], + 'weekdays_min' => ['Son', 'Mvu', 'Sib', 'Sit', 'Sin', 'Sih', 'Mgq'], + 'months' => ['Zibandlela', 'Nhlolanja', 'Mbimbitho', 'Mabasa', 'Nkwenkwezi', 'Nhlangula', 'Ntulikazi', 'Ncwabakazi', 'Mpandula', 'Mfumfu', 'Lwezi', 'Mpalakazi'], + 'months_short' => ['Zib', 'Nhlo', 'Mbi', 'Mab', 'Nkw', 'Nhla', 'Ntu', 'Ncw', 'Mpan', 'Mfu', 'Lwe', 'Mpal'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + + 'year' => 'okweminyaka engu-:count', // less reliable + 'y' => 'okweminyaka engu-:count', // less reliable + 'a_year' => 'okweminyaka engu-:count', // less reliable + + 'month' => 'inyanga ezingu-:count', + 'm' => 'inyanga ezingu-:count', + 'a_month' => 'inyanga ezingu-:count', + + 'week' => 'amaviki angu-:count', + 'w' => 'amaviki angu-:count', + 'a_week' => 'amaviki angu-:count', + + 'day' => 'kwamalanga angu-:count', + 'd' => 'kwamalanga angu-:count', + 'a_day' => 'kwamalanga angu-:count', + + 'hour' => 'amahola angu-:count', + 'h' => 'amahola angu-:count', + 'a_hour' => 'amahola angu-:count', + + 'minute' => 'imizuzu engu-:count', + 'min' => 'imizuzu engu-:count', + 'a_minute' => 'imizuzu engu-:count', + + 'second' => 'imizuzwana engu-:count', + 's' => 'imizuzwana engu-:count', + 'a_second' => 'imizuzwana engu-:count', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nds.php b/vendor/nesbot/carbon/src/Carbon/Lang/nds.php new file mode 100644 index 0000000..c0b3775 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nds.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/nds_DE.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nds_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/nds_DE.php new file mode 100644 index 0000000..a6c57a9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nds_DE.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - information from Kenneth Christiansen Kenneth Christiansen, Pablo Saratxaga kenneth@gnu.org, pablo@mandrakesoft.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['Jannuaar', 'Feberwaar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], + 'months_short' => ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'], + 'weekdays' => ['Sünndag', 'Maandag', 'Dingsdag', 'Middeweek', 'Dunnersdag', 'Freedag', 'Sünnavend'], + 'weekdays_short' => ['Sdag', 'Maan', 'Ding', 'Midd', 'Dunn', 'Free', 'Svd.'], + 'weekdays_min' => ['Sd', 'Ma', 'Di', 'Mi', 'Du', 'Fr', 'Sa'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'year' => ':count Johr', + 'y' => ':countJ', + 'a_year' => '{1}een Johr|:count Johr', + + 'month' => ':count Maand', + 'm' => ':countM', + 'a_month' => '{1}een Maand|:count Maand', + + 'week' => ':count Week|:count Weken', + 'w' => ':countW', + 'a_week' => '{1}een Week|:count Week|:count Weken', + + 'day' => ':count Dag|:count Daag', + 'd' => ':countD', + 'a_day' => '{1}een Dag|:count Dag|:count Daag', + + 'hour' => ':count Stünn|:count Stünnen', + 'h' => ':countSt', + 'a_hour' => '{1}een Stünn|:count Stünn|:count Stünnen', + + 'minute' => ':count Minuut|:count Minuten', + 'min' => ':countm', + 'a_minute' => '{1}een Minuut|:count Minuut|:count Minuten', + + 'second' => ':count Sekunn|:count Sekunnen', + 's' => ':counts', + 'a_second' => 'en poor Sekunnen|:count Sekunn|:count Sekunnen', + + 'ago' => 'vör :time', + 'from_now' => 'in :time', + 'before' => ':time vörher', + 'after' => ':time later', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nds_NL.php b/vendor/nesbot/carbon/src/Carbon/Lang/nds_NL.php new file mode 100644 index 0000000..de2c57b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nds_NL.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - information from Kenneth Christiansen Kenneth Christiansen, Pablo Saratxaga kenneth@gnu.org, pablo@mandrakesoft.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['Jaunuwoa', 'Februwoa', 'Moaz', 'Aprell', 'Mai', 'Juni', 'Juli', 'August', 'Septamba', 'Oktoba', 'Nowamba', 'Dezamba'], + 'months_short' => ['Jan', 'Feb', 'Moz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Now', 'Dez'], + 'weekdays' => ['Sinndag', 'Mondag', 'Dingsdag', 'Meddwäakj', 'Donnadag', 'Friedag', 'Sinnowend'], + 'weekdays_short' => ['Sdg', 'Mdg', 'Dsg', 'Mwk', 'Ddg', 'Fdg', 'Swd'], + 'weekdays_min' => ['Sdg', 'Mdg', 'Dsg', 'Mwk', 'Ddg', 'Fdg', 'Swd'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ne.php b/vendor/nesbot/carbon/src/Carbon/Lang/ne.php new file mode 100644 index 0000000..d4caf0e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ne.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - nootanghimire + * - Josh Soref + * - Nj Subedi + * - JD Isaacks + */ +return [ + 'year' => 'एक बर्ष|:count बर्ष', + 'y' => ':count वर्ष', + 'month' => 'एक महिना|:count महिना', + 'm' => ':count महिना', + 'week' => ':count हप्ता', + 'w' => ':count हप्ता', + 'day' => 'एक दिन|:count दिन', + 'd' => ':count दिन', + 'hour' => 'एक घण्टा|:count घण्टा', + 'h' => ':count घण्टा', + 'minute' => 'एक मिनेट|:count मिनेट', + 'min' => ':count मिनेट', + 'second' => 'केही क्षण|:count सेकेण्ड', + 's' => ':count सेकेण्ड', + 'ago' => ':time अगाडि', + 'from_now' => ':timeमा', + 'after' => ':time पछि', + 'before' => ':time अघि', + 'diff_now' => 'अहिले', + 'diff_today' => 'आज', + 'diff_yesterday' => 'हिजो', + 'diff_tomorrow' => 'भोलि', + 'formats' => [ + 'LT' => 'Aको h:mm बजे', + 'LTS' => 'Aको h:mm:ss बजे', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY, Aको h:mm बजे', + 'LLLL' => 'dddd, D MMMM YYYY, Aको h:mm बजे', + ], + 'calendar' => [ + 'sameDay' => '[आज] LT', + 'nextDay' => '[भोलि] LT', + 'nextWeek' => '[आउँदो] dddd[,] LT', + 'lastDay' => '[हिजो] LT', + 'lastWeek' => '[गएको] dddd[,] LT', + 'sameElse' => 'L', + ], + 'meridiem' => function ($hour) { + if ($hour < 3) { + return 'राति'; + } + if ($hour < 12) { + return 'बिहान'; + } + if ($hour < 16) { + return 'दिउँसो'; + } + if ($hour < 20) { + return 'साँझ'; + } + + return 'राति'; + }, + 'months' => ['जनवरी', 'फेब्रुवरी', 'मार्च', 'अप्रिल', 'मई', 'जुन', 'जुलाई', 'अगष्ट', 'सेप्टेम्बर', 'अक्टोबर', 'नोभेम्बर', 'डिसेम्बर'], + 'months_short' => ['जन.', 'फेब्रु.', 'मार्च', 'अप्रि.', 'मई', 'जुन', 'जुलाई.', 'अग.', 'सेप्ट.', 'अक्टो.', 'नोभे.', 'डिसे.'], + 'weekdays' => ['आइतबार', 'सोमबार', 'मङ्गलबार', 'बुधबार', 'बिहिबार', 'शुक्रबार', 'शनिबार'], + 'weekdays_short' => ['आइत.', 'सोम.', 'मङ्गल.', 'बुध.', 'बिहि.', 'शुक्र.', 'शनि.'], + 'weekdays_min' => ['आ.', 'सो.', 'मं.', 'बु.', 'बि.', 'शु.', 'श.'], + 'list' => [', ', ' र '], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ne_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ne_IN.php new file mode 100644 index 0000000..f68d00e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ne_IN.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ne.php', [ + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'yy/M/d', + 'LL' => 'YYYY MMM D', + 'LLL' => 'YYYY MMMM D, h:mm a', + 'LLLL' => 'YYYY MMMM D, dddd, h:mm a', + ], + 'months' => ['जनवरी', 'फेब्रुअरी', 'मार्च', 'अप्रिल', 'मे', 'जुन', 'जुलाई', 'अगस्ट', 'सेप्टेम्बर', 'अक्टोबर', 'नोभेम्बर', 'डिसेम्बर'], + 'months_short' => ['जनवरी', 'फेब्रुअरी', 'मार्च', 'अप्रिल', 'मे', 'जुन', 'जुलाई', 'अगस्ट', 'सेप्टेम्बर', 'अक्टोबर', 'नोभेम्बर', 'डिसेम्बर'], + 'weekend' => [0, 0], + 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ne_NP.php b/vendor/nesbot/carbon/src/Carbon/Lang/ne_NP.php new file mode 100644 index 0000000..27840c0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ne_NP.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ne.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nhn.php b/vendor/nesbot/carbon/src/Carbon/Lang/nhn.php new file mode 100644 index 0000000..5a85831 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nhn.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/nhn_MX.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nhn_MX.php b/vendor/nesbot/carbon/src/Carbon/Lang/nhn_MX.php new file mode 100644 index 0000000..9db88a1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nhn_MX.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP libc-alpha@sourceware.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'], + 'months_short' => ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'], + 'weekdays' => ['teoilhuitl', 'ceilhuitl', 'omeilhuitl', 'yeilhuitl', 'nahuilhuitl', 'macuililhuitl', 'chicuaceilhuitl'], + 'weekdays_short' => ['teo', 'cei', 'ome', 'yei', 'nau', 'mac', 'chi'], + 'weekdays_min' => ['teo', 'cei', 'ome', 'yei', 'nau', 'mac', 'chi'], + 'day_of_first_week_of_year' => 1, + + 'month' => ':count metztli', // less reliable + 'm' => ':count metztli', // less reliable + 'a_month' => ':count metztli', // less reliable + + 'week' => ':count tonalli', // less reliable + 'w' => ':count tonalli', // less reliable + 'a_week' => ':count tonalli', // less reliable + + 'day' => ':count tonatih', // less reliable + 'd' => ':count tonatih', // less reliable + 'a_day' => ':count tonatih', // less reliable + + 'minute' => ':count toltecayotl', // less reliable + 'min' => ':count toltecayotl', // less reliable + 'a_minute' => ':count toltecayotl', // less reliable + + 'second' => ':count ome', // less reliable + 's' => ':count ome', // less reliable + 'a_second' => ':count ome', // less reliable + + 'year' => ':count xihuitl', + 'y' => ':count xihuitl', + 'a_year' => ':count xihuitl', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/niu.php b/vendor/nesbot/carbon/src/Carbon/Lang/niu.php new file mode 100644 index 0000000..bd9be8a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/niu.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/niu_NU.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/niu_NU.php b/vendor/nesbot/carbon/src/Carbon/Lang/niu_NU.php new file mode 100644 index 0000000..6e7a697 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/niu_NU.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RockET Systems Emani Fakaotimanava-Lui emani@niue.nu + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['Ianuali', 'Fepuali', 'Masi', 'Apelila', 'Me', 'Iuni', 'Iulai', 'Aokuso', 'Sepetema', 'Oketopa', 'Novema', 'Tesemo'], + 'months_short' => ['Ian', 'Fep', 'Mas', 'Ape', 'Me', 'Iun', 'Iul', 'Aok', 'Sep', 'Oke', 'Nov', 'Tes'], + 'weekdays' => ['Aho Tapu', 'Aho Gofua', 'Aho Ua', 'Aho Lotu', 'Aho Tuloto', 'Aho Falaile', 'Aho Faiumu'], + 'weekdays_short' => ['Tapu', 'Gofua', 'Ua', 'Lotu', 'Tuloto', 'Falaile', 'Faiumu'], + 'weekdays_min' => ['Tapu', 'Gofua', 'Ua', 'Lotu', 'Tuloto', 'Falaile', 'Faiumu'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'year' => ':count tau', + 'y' => ':count tau', + 'a_year' => ':count tau', + + 'month' => ':count mahina', + 'm' => ':count mahina', + 'a_month' => ':count mahina', + + 'week' => ':count faahi tapu', + 'w' => ':count faahi tapu', + 'a_week' => ':count faahi tapu', + + 'day' => ':count aho', + 'd' => ':count aho', + 'a_day' => ':count aho', + + 'hour' => ':count e tulā', + 'h' => ':count e tulā', + 'a_hour' => ':count e tulā', + + 'minute' => ':count minuti', + 'min' => ':count minuti', + 'a_minute' => ':count minuti', + + 'second' => ':count sekone', + 's' => ':count sekone', + 'a_second' => ':count sekone', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl.php new file mode 100644 index 0000000..2d73770 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Roy + * - Stephan + * - François B + * - Tim Fish + * - Kevin Huang + * - Jacob Middag + * - JD Isaacks + * - Roy + * - Stephan + * - François B + * - Tim Fish + * - Jacob Middag + * - JD Isaacks + * - Propaganistas + * - MegaXLR + * - adriaanzon + * - MonkeyPhysics + * - JeroenG + * - RikSomers + * - proclame + * - Rik de Groot (hwdegroot) + */ +return [ + 'year' => ':count jaar|:count jaar', + 'a_year' => 'een jaar|:count jaar', + 'y' => ':countj', + 'month' => ':count maand|:count maanden', + 'a_month' => 'een maand|:count maanden', + 'm' => ':countmnd', + 'week' => ':count week|:count weken', + 'a_week' => 'een week|:count weken', + 'w' => ':countw', + 'day' => ':count dag|:count dagen', + 'a_day' => 'een dag|:count dagen', + 'd' => ':countd', + 'hour' => ':count uur|:count uur', + 'a_hour' => 'een uur|:count uur', + 'h' => ':countu', + 'minute' => ':count minuut|:count minuten', + 'a_minute' => 'een minuut|:count minuten', + 'min' => ':countmin', + 'second' => ':count seconde|:count seconden', + 'a_second' => 'een paar seconden|:count seconden', + 's' => ':counts', + 'ago' => ':time geleden', + 'from_now' => 'over :time', + 'after' => ':time later', + 'before' => ':time eerder', + 'diff_now' => 'nu', + 'diff_today' => 'vandaag', + 'diff_today_regexp' => 'vandaag(?:\\s+om)?', + 'diff_yesterday' => 'gisteren', + 'diff_yesterday_regexp' => 'gisteren(?:\\s+om)?', + 'diff_tomorrow' => 'morgen', + 'diff_tomorrow_regexp' => 'morgen(?:\\s+om)?', + 'diff_after_tomorrow' => 'overmorgen', + 'diff_before_yesterday' => 'eergisteren', + 'period_recurrences' => ':count keer', + 'period_interval' => function (string $interval = '') { + /** @var string $output */ + $output = preg_replace('/^(een|één|1)\s+/u', '', $interval); + + if (preg_match('/^(een|één|1)( jaar|j| uur|u)/u', $interval)) { + return "elk $output"; + } + + return "elke $output"; + }, + 'period_start_date' => 'van :date', + 'period_end_date' => 'tot :date', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD-MM-YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[vandaag om] LT', + 'nextDay' => '[morgen om] LT', + 'nextWeek' => 'dddd [om] LT', + 'lastDay' => '[gisteren om] LT', + 'lastWeek' => '[afgelopen] dddd [om] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + return $number.(($number === 1 || $number === 8 || $number >= 20) ? 'ste' : 'de'); + }, + 'months' => ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'], + 'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'], + 'mmm_suffix' => '.', + 'weekdays' => ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'], + 'weekdays_short' => ['zo.', 'ma.', 'di.', 'wo.', 'do.', 'vr.', 'za.'], + 'weekdays_min' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' en '], + 'meridiem' => ['\'s ochtends', '\'s middags'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_AW.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_AW.php new file mode 100644 index 0000000..5ec136d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_AW.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/nl.php', [ + 'formats' => [ + 'L' => 'DD-MM-YY', + ], + 'months' => ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'], + 'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'], + 'weekdays' => ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'], + 'weekdays_short' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], + 'weekdays_min' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_BE.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_BE.php new file mode 100644 index 0000000..037f5b4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_BE.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Roy + * - Stephan + * - François B + * - Tim Fish + * - Kevin Huang + * - Jacob Middag + * - JD Isaacks + * - Propaganistas + */ +return array_replace_recursive(require __DIR__.'/nl.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_BQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_BQ.php new file mode 100644 index 0000000..c269197 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_BQ.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/nl.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_CW.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_CW.php new file mode 100644 index 0000000..c269197 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_CW.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/nl.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_NL.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_NL.php new file mode 100644 index 0000000..14e4853 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_NL.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/nl.php', [ + 'months' => ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'], + 'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'], + 'weekdays' => ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'], + 'weekdays_short' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], + 'weekdays_min' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_SR.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_SR.php new file mode 100644 index 0000000..c269197 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_SR.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/nl.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_SX.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_SX.php new file mode 100644 index 0000000..c269197 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_SX.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/nl.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nmg.php b/vendor/nesbot/carbon/src/Carbon/Lang/nmg.php new file mode 100644 index 0000000..4d1df6e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nmg.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['maná', 'kugú'], + 'weekdays' => ['sɔ́ndɔ', 'mɔ́ndɔ', 'sɔ́ndɔ mafú mába', 'sɔ́ndɔ mafú málal', 'sɔ́ndɔ mafú mána', 'mabágá má sukul', 'sásadi'], + 'weekdays_short' => ['sɔ́n', 'mɔ́n', 'smb', 'sml', 'smn', 'mbs', 'sas'], + 'weekdays_min' => ['sɔ́n', 'mɔ́n', 'smb', 'sml', 'smn', 'mbs', 'sas'], + 'months' => ['ngwɛn matáhra', 'ngwɛn ńmba', 'ngwɛn ńlal', 'ngwɛn ńna', 'ngwɛn ńtan', 'ngwɛn ńtuó', 'ngwɛn hɛmbuɛrí', 'ngwɛn lɔmbi', 'ngwɛn rɛbvuâ', 'ngwɛn wum', 'ngwɛn wum navǔr', 'krísimin'], + 'months_short' => ['ng1', 'ng2', 'ng3', 'ng4', 'ng5', 'ng6', 'ng7', 'ng8', 'ng9', 'ng10', 'ng11', 'kris'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nn.php b/vendor/nesbot/carbon/src/Carbon/Lang/nn.php new file mode 100644 index 0000000..041f7b2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nn.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Alexander Tømmerås + * - Øystein + * - JD Isaacks + * - Gaute Hvoslef Kvalnes (gaute) + */ +return [ + 'year' => ':count år', + 'a_year' => 'eit år|:count år', + 'y' => ':count år', + 'month' => ':count månad|:count månader', + 'a_month' => 'ein månad|:count månader', + 'm' => ':count md', + 'week' => ':count veke|:count veker', + 'a_week' => 'ei veke|:count veker', + 'w' => ':countv', + 'day' => ':count dag|:count dagar', + 'a_day' => 'ein dag|:count dagar', + 'd' => ':countd', + 'hour' => ':count time|:count timar', + 'a_hour' => 'ein time|:count timar', + 'h' => ':countt', + 'minute' => ':count minutt', + 'a_minute' => 'eit minutt|:count minutt', + 'min' => ':countm', + 'second' => ':count sekund', + 'a_second' => 'nokre sekund|:count sekund', + 's' => ':counts', + 'ago' => ':time sidan', + 'from_now' => 'om :time', + 'after' => ':time etter', + 'before' => ':time før', + 'diff_today' => 'I dag', + 'diff_yesterday' => 'I går', + 'diff_yesterday_regexp' => 'I går(?:\\s+klokka)?', + 'diff_tomorrow' => 'I morgon', + 'diff_tomorrow_regexp' => 'I morgon(?:\\s+klokka)?', + 'diff_today_regexp' => 'I dag(?:\\s+klokka)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY [kl.] H:mm', + 'LLLL' => 'dddd D. MMMM YYYY [kl.] HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[I dag klokka] LT', + 'nextDay' => '[I morgon klokka] LT', + 'nextWeek' => 'dddd [klokka] LT', + 'lastDay' => '[I går klokka] LT', + 'lastWeek' => '[Føregåande] dddd [klokka] LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'months' => ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'], + 'months_short' => ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'], + 'weekdays' => ['sundag', 'måndag', 'tysdag', 'onsdag', 'torsdag', 'fredag', 'laurdag'], + 'weekdays_short' => ['sun', 'mån', 'tys', 'ons', 'tor', 'fre', 'lau'], + 'weekdays_min' => ['su', 'må', 'ty', 'on', 'to', 'fr', 'la'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' og '], + 'meridiem' => ['f.m.', 'e.m.'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nn_NO.php b/vendor/nesbot/carbon/src/Carbon/Lang/nn_NO.php new file mode 100644 index 0000000..8e16871 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nn_NO.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/nn.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nnh.php b/vendor/nesbot/carbon/src/Carbon/Lang/nnh.php new file mode 100644 index 0000000..007d239 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nnh.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['mbaʼámbaʼ', 'ncwònzém'], + 'weekdays' => null, + 'weekdays_short' => ['lyɛʼɛ́ sẅíŋtè', 'mvfò lyɛ̌ʼ', 'mbɔ́ɔntè mvfò lyɛ̌ʼ', 'tsètsɛ̀ɛ lyɛ̌ʼ', 'mbɔ́ɔntè tsetsɛ̀ɛ lyɛ̌ʼ', 'mvfò màga lyɛ̌ʼ', 'màga lyɛ̌ʼ'], + 'weekdays_min' => null, + 'months' => null, + 'months_short' => ['saŋ tsetsɛ̀ɛ lùm', 'saŋ kàg ngwóŋ', 'saŋ lepyè shúm', 'saŋ cÿó', 'saŋ tsɛ̀ɛ cÿó', 'saŋ njÿoláʼ', 'saŋ tyɛ̀b tyɛ̀b mbʉ̀ŋ', 'saŋ mbʉ̀ŋ', 'saŋ ngwɔ̀ʼ mbÿɛ', 'saŋ tàŋa tsetsáʼ', 'saŋ mejwoŋó', 'saŋ lùm'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/yy', + 'LL' => 'D MMM, YYYY', + 'LLL' => '[lyɛ]̌ʼ d [na] MMMM, YYYY HH:mm', + 'LLLL' => 'dddd , [lyɛ]̌ʼ d [na] MMMM, YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/no.php b/vendor/nesbot/carbon/src/Carbon/Lang/no.php new file mode 100644 index 0000000..f4497c7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/no.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Daniel S. Billing + * - Paul + * - Jimmie Johansson + * - Jens Herlevsen + */ +return array_replace_recursive(require __DIR__.'/nb.php', [ + 'formats' => [ + 'LLL' => 'D. MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D. MMMM YYYY [kl.] HH:mm', + ], + 'calendar' => [ + 'nextWeek' => 'på dddd [kl.] LT', + 'lastWeek' => '[i] dddd[s kl.] LT', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nr.php b/vendor/nesbot/carbon/src/Carbon/Lang/nr.php new file mode 100644 index 0000000..1bc999f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nr.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/nr_ZA.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nr_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/nr_ZA.php new file mode 100644 index 0000000..f9a7be8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nr_ZA.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Janabari', 'uFeberbari', 'uMatjhi', 'u-Apreli', 'Meyi', 'Juni', 'Julayi', 'Arhostosi', 'Septemba', 'Oktoba', 'Usinyikhaba', 'Disemba'], + 'months_short' => ['Jan', 'Feb', 'Mat', 'Apr', 'Mey', 'Jun', 'Jul', 'Arh', 'Sep', 'Okt', 'Usi', 'Dis'], + 'weekdays' => ['uSonto', 'uMvulo', 'uLesibili', 'lesithathu', 'uLesine', 'ngoLesihlanu', 'umGqibelo'], + 'weekdays_short' => ['Son', 'Mvu', 'Bil', 'Tha', 'Ne', 'Hla', 'Gqi'], + 'weekdays_min' => ['Son', 'Mvu', 'Bil', 'Tha', 'Ne', 'Hla', 'Gqi'], + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nso.php b/vendor/nesbot/carbon/src/Carbon/Lang/nso.php new file mode 100644 index 0000000..2a6cabb --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nso.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/nso_ZA.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nso_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/nso_ZA.php new file mode 100644 index 0000000..b08fe6d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nso_ZA.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Janaware', 'Febereware', 'Matšhe', 'Aprele', 'Mei', 'June', 'Julae', 'Agostose', 'Setemere', 'Oktobere', 'Nofemere', 'Disemere'], + 'months_short' => ['Jan', 'Feb', 'Mat', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Set', 'Okt', 'Nof', 'Dis'], + 'weekdays' => ['LaMorena', 'Mošupologo', 'Labobedi', 'Laboraro', 'Labone', 'Labohlano', 'Mokibelo'], + 'weekdays_short' => ['Son', 'Moš', 'Bed', 'Rar', 'Ne', 'Hla', 'Mok'], + 'weekdays_min' => ['Son', 'Moš', 'Bed', 'Rar', 'Ne', 'Hla', 'Mok'], + 'day_of_first_week_of_year' => 1, + + 'year' => ':count ngwaga', + 'y' => ':count ngwaga', + 'a_year' => ':count ngwaga', + + 'month' => ':count Kgwedi', + 'm' => ':count Kgwedi', + 'a_month' => ':count Kgwedi', + + 'week' => ':count Beke', + 'w' => ':count Beke', + 'a_week' => ':count Beke', + + 'day' => ':count Letšatši', + 'd' => ':count Letšatši', + 'a_day' => ':count Letšatši', + + 'hour' => ':count Iri', + 'h' => ':count Iri', + 'a_hour' => ':count Iri', + + 'minute' => ':count Motsotso', + 'min' => ':count Motsotso', + 'a_minute' => ':count Motsotso', + + 'second' => ':count motsotswana', + 's' => ':count motsotswana', + 'a_second' => ':count motsotswana', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nus.php b/vendor/nesbot/carbon/src/Carbon/Lang/nus.php new file mode 100644 index 0000000..789bc39 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nus.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['RW', 'TŊ'], + 'weekdays' => ['Cäŋ kuɔth', 'Jiec la̱t', 'Rɛw lätni', 'Diɔ̱k lätni', 'Ŋuaan lätni', 'Dhieec lätni', 'Bäkɛl lätni'], + 'weekdays_short' => ['Cäŋ', 'Jiec', 'Rɛw', 'Diɔ̱k', 'Ŋuaan', 'Dhieec', 'Bäkɛl'], + 'weekdays_min' => ['Cäŋ', 'Jiec', 'Rɛw', 'Diɔ̱k', 'Ŋuaan', 'Dhieec', 'Bäkɛl'], + 'months' => ['Tiop thar pɛt', 'Pɛt', 'Duɔ̱ɔ̱ŋ', 'Guak', 'Duät', 'Kornyoot', 'Pay yie̱tni', 'Tho̱o̱r', 'Tɛɛr', 'Laath', 'Kur', 'Tio̱p in di̱i̱t'], + 'months_short' => ['Tiop', 'Pɛt', 'Duɔ̱ɔ̱', 'Guak', 'Duä', 'Kor', 'Pay', 'Thoo', 'Tɛɛ', 'Laa', 'Kur', 'Tid'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'D/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd D MMMM YYYY h:mm a', + ], + + 'year' => ':count jiök', // less reliable + 'y' => ':count jiök', // less reliable + 'a_year' => ':count jiök', // less reliable + + 'month' => ':count pay', // less reliable + 'm' => ':count pay', // less reliable + 'a_month' => ':count pay', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nyn.php b/vendor/nesbot/carbon/src/Carbon/Lang/nyn.php new file mode 100644 index 0000000..8660ea4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nyn.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'weekdays' => ['Sande', 'Orwokubanza', 'Orwakabiri', 'Orwakashatu', 'Orwakana', 'Orwakataano', 'Orwamukaaga'], + 'weekdays_short' => ['SAN', 'ORK', 'OKB', 'OKS', 'OKN', 'OKT', 'OMK'], + 'weekdays_min' => ['SAN', 'ORK', 'OKB', 'OKS', 'OKN', 'OKT', 'OMK'], + 'months' => ['Okwokubanza', 'Okwakabiri', 'Okwakashatu', 'Okwakana', 'Okwakataana', 'Okwamukaaga', 'Okwamushanju', 'Okwamunaana', 'Okwamwenda', 'Okwaikumi', 'Okwaikumi na kumwe', 'Okwaikumi na ibiri'], + 'months_short' => ['KBZ', 'KBR', 'KST', 'KKN', 'KTN', 'KMK', 'KMS', 'KMN', 'KMW', 'KKM', 'KNK', 'KNB'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/oc.php b/vendor/nesbot/carbon/src/Carbon/Lang/oc.php new file mode 100644 index 0000000..c9411d6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/oc.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Quentí + */ +// @codeCoverageIgnoreStart +use Symfony\Component\Translation\PluralizationRules; + +if (class_exists('Symfony\\Component\\Translation\\PluralizationRules')) { + PluralizationRules::set(static function ($number) { + return $number == 1 ? 0 : 1; + }, 'oc'); +} +// @codeCoverageIgnoreEnd + +return [ + 'year' => ':count an|:count ans', + 'a_year' => 'un an|:count ans', + 'y' => ':count an|:count ans', + 'month' => ':count mes|:count meses', + 'a_month' => 'un mes|:count meses', + 'm' => ':count mes|:count meses', + 'week' => ':count setmana|:count setmanas', + 'a_week' => 'una setmana|:count setmanas', + 'w' => ':count setmana|:count setmanas', + 'day' => ':count jorn|:count jorns', + 'a_day' => 'un jorn|:count jorns', + 'd' => ':count jorn|:count jorns', + 'hour' => ':count ora|:count oras', + 'a_hour' => 'una ora|:count oras', + 'h' => ':count ora|:count oras', + 'minute' => ':count minuta|:count minutas', + 'a_minute' => 'una minuta|:count minutas', + 'min' => ':count minuta|:count minutas', + 'second' => ':count segonda|:count segondas', + 'a_second' => 'una segonda|:count segondas', + 's' => ':count segonda|:count segondas', + 'ago' => 'fa :time', + 'from_now' => 'd\'aquí :time', + 'after' => ':time aprèp', + 'before' => ':time abans', + 'diff_now' => 'ara meteis', + 'diff_today' => 'Uèi', + 'diff_today_regexp' => 'Uèi(?:\\s+a)?', + 'diff_yesterday' => 'ièr', + 'diff_yesterday_regexp' => 'Ièr(?:\\s+a)?', + 'diff_tomorrow' => 'deman', + 'diff_tomorrow_regexp' => 'Deman(?:\\s+a)?', + 'diff_before_yesterday' => 'ièr delà', + 'diff_after_tomorrow' => 'deman passat', + 'period_recurrences' => ':count còp|:count còps', + 'period_interval' => 'cada :interval', + 'period_start_date' => 'de :date', + 'period_end_date' => 'fins a :date', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM [de] YYYY', + 'LLL' => 'D MMMM [de] YYYY [a] H:mm', + 'LLLL' => 'dddd D MMMM [de] YYYY [a] H:mm', + ], + 'calendar' => [ + 'sameDay' => '[Uèi a] LT', + 'nextDay' => '[Deman a] LT', + 'nextWeek' => 'dddd [a] LT', + 'lastDay' => '[Ièr a] LT', + 'lastWeek' => 'dddd [passat a] LT', + 'sameElse' => 'L', + ], + 'months' => ['de genièr', 'de febrièr', 'de març', 'd\'abrial', 'de mai', 'de junh', 'de julhet', 'd\'agost', 'de setembre', 'd’octòbre', 'de novembre', 'de decembre'], + 'months_standalone' => ['genièr', 'febrièr', 'març', 'abrial', 'mai', 'junh', 'julh', 'agost', 'setembre', 'octòbre', 'novembre', 'decembre'], + 'months_short' => ['gen.', 'feb.', 'març', 'abr.', 'mai', 'junh', 'julh', 'ago.', 'sep.', 'oct.', 'nov.', 'dec.'], + 'weekdays' => ['dimenge', 'diluns', 'dimars', 'dimècres', 'dijòus', 'divendres', 'dissabte'], + 'weekdays_short' => ['dg', 'dl', 'dm', 'dc', 'dj', 'dv', 'ds'], + 'weekdays_min' => ['dg', 'dl', 'dm', 'dc', 'dj', 'dv', 'ds'], + 'ordinal' => function ($number, string $period = '') { + $ordinal = [1 => 'èr', 2 => 'nd'][(int) $number] ?? 'en'; + + // feminine for year, week, hour, minute, second + if (preg_match('/^[yYwWhHgGis]$/', $period)) { + $ordinal .= 'a'; + } + + return $number.$ordinal; + }, + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' e '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/oc_FR.php b/vendor/nesbot/carbon/src/Carbon/Lang/oc_FR.php new file mode 100644 index 0000000..01eb5c1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/oc_FR.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/oc.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/om.php b/vendor/nesbot/carbon/src/Carbon/Lang/om.php new file mode 100644 index 0000000..b8d5a0b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/om.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation & Sagalee Oromoo Publishing Co. Inc. locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'dd-MMM-YYYY', + 'LLL' => 'dd MMMM YYYY HH:mm', + 'LLLL' => 'dddd, MMMM D, YYYY HH:mm', + ], + 'months' => ['Amajjii', 'Guraandhala', 'Bitooteessa', 'Elba', 'Caamsa', 'Waxabajjii', 'Adooleessa', 'Hagayya', 'Fuulbana', 'Onkololeessa', 'Sadaasa', 'Muddee'], + 'months_short' => ['Ama', 'Gur', 'Bit', 'Elb', 'Cam', 'Wax', 'Ado', 'Hag', 'Ful', 'Onk', 'Sad', 'Mud'], + 'weekdays' => ['Dilbata', 'Wiixata', 'Qibxata', 'Roobii', 'Kamiisa', 'Jimaata', 'Sanbata'], + 'weekdays_short' => ['Dil', 'Wix', 'Qib', 'Rob', 'Kam', 'Jim', 'San'], + 'weekdays_min' => ['Dil', 'Wix', 'Qib', 'Rob', 'Kam', 'Jim', 'San'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['WD', 'WB'], + + 'year' => 'wggoota :count', + 'y' => 'wggoota :count', + 'a_year' => 'wggoota :count', + + 'month' => 'ji’a :count', + 'm' => 'ji’a :count', + 'a_month' => 'ji’a :count', + + 'week' => 'torban :count', + 'w' => 'torban :count', + 'a_week' => 'torban :count', + + 'day' => 'guyyaa :count', + 'd' => 'guyyaa :count', + 'a_day' => 'guyyaa :count', + + 'hour' => 'saʼaatii :count', + 'h' => 'saʼaatii :count', + 'a_hour' => 'saʼaatii :count', + + 'minute' => 'daqiiqaa :count', + 'min' => 'daqiiqaa :count', + 'a_minute' => 'daqiiqaa :count', + + 'second' => 'sekoondii :count', + 's' => 'sekoondii :count', + 'a_second' => 'sekoondii :count', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/om_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/om_ET.php new file mode 100644 index 0000000..044760e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/om_ET.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/om.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/om_KE.php b/vendor/nesbot/carbon/src/Carbon/Lang/om_KE.php new file mode 100644 index 0000000..f5a4d1c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/om_KE.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/om.php', [ + 'day_of_first_week_of_year' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/or.php b/vendor/nesbot/carbon/src/Carbon/Lang/or.php new file mode 100644 index 0000000..3aa7173 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/or.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/or_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/or_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/or_IN.php new file mode 100644 index 0000000..57a89f5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/or_IN.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM AP Linux Technology Center, Yamato Software Laboratory bug-glibc@gnu.org + */ +return [ + 'diff_now' => 'ବର୍ତ୍ତମାନ', + 'diff_yesterday' => 'ଗତକାଲି', + 'diff_tomorrow' => 'ଆସନ୍ତାକାଲି', + 'formats' => [ + 'LT' => 'Oh:Om A', + 'LTS' => 'Oh:Om:Os A', + 'L' => 'OD-OM-OY', + 'LL' => 'OD MMMM OY', + 'LLL' => 'OD MMMM OY Oh:Om A', + 'LLLL' => 'dddd OD MMMM OY Oh:Om A', + ], + 'months' => ['ଜାନୁଆରୀ', 'ଫେବୃଆରୀ', 'ମାର୍ଚ୍ଚ', 'ଅପ୍ରେଲ', 'ମଇ', 'ଜୁନ', 'ଜୁଲାଇ', 'ଅଗଷ୍ଟ', 'ସେପ୍ଟେମ୍ବର', 'ଅକ୍ଟୋବର', 'ନଭେମ୍ବର', 'ଡିସେମ୍ବର'], + 'months_short' => ['ଜାନୁଆରୀ', 'ଫେବୃଆରୀ', 'ମାର୍ଚ୍ଚ', 'ଅପ୍ରେଲ', 'ମଇ', 'ଜୁନ', 'ଜୁଲାଇ', 'ଅଗଷ୍ଟ', 'ସେପ୍ଟେମ୍ବର', 'ଅକ୍ଟୋବର', 'ନଭେମ୍ବର', 'ଡିସେମ୍ବର'], + 'weekdays' => ['ରବିବାର', 'ସୋମବାର', 'ମଙ୍ଗଳବାର', 'ବୁଧବାର', 'ଗୁରୁବାର', 'ଶୁକ୍ରବାର', 'ଶନିବାର'], + 'weekdays_short' => ['ରବି', 'ସୋମ', 'ମଙ୍ଗଳ', 'ବୁଧ', 'ଗୁରୁ', 'ଶୁକ୍ର', 'ଶନି'], + 'weekdays_min' => ['ରବି', 'ସୋମ', 'ମଙ୍ଗଳ', 'ବୁଧ', 'ଗୁରୁ', 'ଶୁକ୍ର', 'ଶନି'], + 'day_of_first_week_of_year' => 1, + 'alt_numbers' => ['୦', '୧', '୨', '୩', '୪', '୫', '୬', '୭', '୮', '୯', '୧୦', '୧୧', '୧୨', '୧୩', '୧୪', '୧୫', '୧୬', '୧୭', '୧୮', '୧୯', '୨୦', '୨୧', '୨୨', '୨୩', '୨୪', '୨୫', '୨୬', '୨୭', '୨୮', '୨୯', '୩୦', '୩୧', '୩୨', '୩୩', '୩୪', '୩୫', '୩୬', '୩୭', '୩୮', '୩୯', '୪୦', '୪୧', '୪୨', '୪୩', '୪୪', '୪୫', '୪୬', '୪୭', '୪୮', '୪୯', '୫୦', '୫୧', '୫୨', '୫୩', '୫୪', '୫୫', '୫୬', '୫୭', '୫୮', '୫୯', '୬୦', '୬୧', '୬୨', '୬୩', '୬୪', '୬୫', '୬୬', '୬୭', '୬୮', '୬୯', '୭୦', '୭୧', '୭୨', '୭୩', '୭୪', '୭୫', '୭୬', '୭୭', '୭୮', '୭୯', '୮୦', '୮୧', '୮୨', '୮୩', '୮୪', '୮୫', '୮୬', '୮୭', '୮୮', '୮୯', '୯୦', '୯୧', '୯୨', '୯୩', '୯୪', '୯୫', '୯୬', '୯୭', '୯୮', '୯୯'], + 'year' => ':count ବର୍ଷ', + 'y' => ':count ବ.', + 'month' => ':count ମାସ', + 'm' => ':count ମା.', + 'week' => ':count ସପ୍ତାହ', + 'w' => ':count ସପ୍ତା.', + 'day' => ':count ଦିନ', + 'd' => ':count ଦିନ', + 'hour' => ':count ଘଣ୍ତ', + 'h' => ':count ଘ.', + 'minute' => ':count ମିନଟ', + 'min' => ':count ମି.', + 'second' => ':count ସେକଣ୍ଢ', + 's' => ':count ସେ.', + 'ago' => ':time ପୂର୍ବେ', + 'from_now' => ':timeରେ', +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/os.php b/vendor/nesbot/carbon/src/Carbon/Lang/os.php new file mode 100644 index 0000000..5f55e8a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/os.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/os_RU.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/os_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/os_RU.php new file mode 100644 index 0000000..9592d15 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/os_RU.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['январы', 'февралы', 'мартъийы', 'апрелы', 'майы', 'июны', 'июлы', 'августы', 'сентябры', 'октябры', 'ноябры', 'декабры'], + 'months_short' => ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'], + 'weekdays' => ['Хуыцаубон', 'Къуырисæр', 'Дыццæг', 'Æртыццæг', 'Цыппæрæм', 'Майрæмбон', 'Сабат'], + 'weekdays_short' => ['Хцб', 'Крс', 'Дцг', 'Æрт', 'Цпр', 'Мрб', 'Сбт'], + 'weekdays_min' => ['Хцб', 'Крс', 'Дцг', 'Æрт', 'Цпр', 'Мрб', 'Сбт'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'minute' => ':count гыццыл', // less reliable + 'min' => ':count гыццыл', // less reliable + 'a_minute' => ':count гыццыл', // less reliable + + 'second' => ':count æндæр', // less reliable + 's' => ':count æндæр', // less reliable + 'a_second' => ':count æндæр', // less reliable + + 'year' => ':count аз', + 'y' => ':count аз', + 'a_year' => ':count аз', + + 'month' => ':count мӕй', + 'm' => ':count мӕй', + 'a_month' => ':count мӕй', + + 'week' => ':count къуыри', + 'w' => ':count къуыри', + 'a_week' => ':count къуыри', + + 'day' => ':count бон', + 'd' => ':count бон', + 'a_day' => ':count бон', + + 'hour' => ':count сахат', + 'h' => ':count сахат', + 'a_hour' => ':count сахат', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pa.php b/vendor/nesbot/carbon/src/Carbon/Lang/pa.php new file mode 100644 index 0000000..48b2033 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pa.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Philippe Vaucher + * - Tsutomu Kuroda + * - Punjab + */ +return [ + 'year' => 'ਇੱਕ ਸਾਲ|:count ਸਾਲ', + 'month' => 'ਇੱਕ ਮਹੀਨਾ|:count ਮਹੀਨੇ', + 'week' => 'ਹਫਤਾ|:count ਹਫ਼ਤੇ', + 'day' => 'ਇੱਕ ਦਿਨ|:count ਦਿਨ', + 'hour' => 'ਇੱਕ ਘੰਟਾ|:count ਘੰਟੇ', + 'minute' => 'ਇਕ ਮਿੰਟ|:count ਮਿੰਟ', + 'second' => 'ਕੁਝ ਸਕਿੰਟ|:count ਸਕਿੰਟ', + 'ago' => ':time ਪਹਿਲਾਂ', + 'from_now' => ':time ਵਿੱਚ', + 'before' => ':time ਤੋਂ ਪਹਿਲਾਂ', + 'after' => ':time ਤੋਂ ਬਾਅਦ', + 'diff_now' => 'ਹੁਣ', + 'diff_today' => 'ਅਜ', + 'diff_yesterday' => 'ਕਲ', + 'diff_tomorrow' => 'ਕਲ', + 'formats' => [ + 'LT' => 'A h:mm ਵਜੇ', + 'LTS' => 'A h:mm:ss ਵਜੇ', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY, A h:mm ਵਜੇ', + 'LLLL' => 'dddd, D MMMM YYYY, A h:mm ਵਜੇ', + ], + 'calendar' => [ + 'sameDay' => '[ਅਜ] LT', + 'nextDay' => '[ਕਲ] LT', + 'nextWeek' => '[ਅਗਲਾ] dddd, LT', + 'lastDay' => '[ਕਲ] LT', + 'lastWeek' => '[ਪਿਛਲੇ] dddd, LT', + 'sameElse' => 'L', + ], + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'ਰਾਤ'; + } + if ($hour < 10) { + return 'ਸਵੇਰ'; + } + if ($hour < 17) { + return 'ਦੁਪਹਿਰ'; + } + if ($hour < 20) { + return 'ਸ਼ਾਮ'; + } + + return 'ਰਾਤ'; + }, + 'months' => ['ਜਨਵਰੀ', 'ਫ਼ਰਵਰੀ', 'ਮਾਰਚ', 'ਅਪ੍ਰੈਲ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾਈ', 'ਅਗਸਤ', 'ਸਤੰਬਰ', 'ਅਕਤੂਬਰ', 'ਨਵੰਬਰ', 'ਦਸੰਬਰ'], + 'months_short' => ['ਜਨਵਰੀ', 'ਫ਼ਰਵਰੀ', 'ਮਾਰਚ', 'ਅਪ੍ਰੈਲ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾਈ', 'ਅਗਸਤ', 'ਸਤੰਬਰ', 'ਅਕਤੂਬਰ', 'ਨਵੰਬਰ', 'ਦਸੰਬਰ'], + 'weekdays' => ['ਐਤਵਾਰ', 'ਸੋਮਵਾਰ', 'ਮੰਗਲਵਾਰ', 'ਬੁਧਵਾਰ', 'ਵੀਰਵਾਰ', 'ਸ਼ੁੱਕਰਵਾਰ', 'ਸ਼ਨੀਚਰਵਾਰ'], + 'weekdays_short' => ['ਐਤ', 'ਸੋਮ', 'ਮੰਗਲ', 'ਬੁਧ', 'ਵੀਰ', 'ਸ਼ੁਕਰ', 'ਸ਼ਨੀ'], + 'weekdays_min' => ['ਐਤ', 'ਸੋਮ', 'ਮੰਗਲ', 'ਬੁਧ', 'ਵੀਰ', 'ਸ਼ੁਕਰ', 'ਸ਼ਨੀ'], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' ਅਤੇ '], + 'weekend' => [0, 0], + 'alt_numbers' => ['੦', '੧', '੨', '੩', '੪', '੫', '੬', '੭', '੮', '੯'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pa_Arab.php b/vendor/nesbot/carbon/src/Carbon/Lang/pa_Arab.php new file mode 100644 index 0000000..39b0653 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pa_Arab.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ur.php', [ + 'weekdays' => ['اتوار', 'پیر', 'منگل', 'بُدھ', 'جمعرات', 'جمعہ', 'ہفتہ'], + 'weekdays_short' => ['اتوار', 'پیر', 'منگل', 'بُدھ', 'جمعرات', 'جمعہ', 'ہفتہ'], + 'weekdays_min' => ['اتوار', 'پیر', 'منگل', 'بُدھ', 'جمعرات', 'جمعہ', 'ہفتہ'], + 'months' => ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئ', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر', 'نومبر', 'دسمبر'], + 'months_short' => ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئ', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر', 'نومبر', 'دسمبر'], + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd, DD MMMM YYYY h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pa_Guru.php b/vendor/nesbot/carbon/src/Carbon/Lang/pa_Guru.php new file mode 100644 index 0000000..7adff5c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pa_Guru.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/pa.php', [ + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'D/M/yy', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY, h:mm a', + 'LLLL' => 'dddd, D MMMM YYYY, h:mm a', + ], + 'months' => ['ਜਨਵਰੀ', 'ਫ਼ਰਵਰੀ', 'ਮਾਰਚ', 'ਅਪ੍ਰੈਲ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾਈ', 'ਅਗਸਤ', 'ਸਤੰਬਰ', 'ਅਕਤੂਬਰ', 'ਨਵੰਬਰ', 'ਦਸੰਬਰ'], + 'months_short' => ['ਜਨ', 'ਫ਼ਰ', 'ਮਾਰਚ', 'ਅਪ੍ਰੈ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾ', 'ਅਗ', 'ਸਤੰ', 'ਅਕਤੂ', 'ਨਵੰ', 'ਦਸੰ'], + 'weekdays' => ['ਐਤਵਾਰ', 'ਸੋਮਵਾਰ', 'ਮੰਗਲਵਾਰ', 'ਬੁੱਧਵਾਰ', 'ਵੀਰਵਾਰ', 'ਸ਼ੁੱਕਰਵਾਰ', 'ਸ਼ਨਿੱਚਰਵਾਰ'], + 'weekdays_short' => ['ਐਤ', 'ਸੋਮ', 'ਮੰਗਲ', 'ਬੁੱਧ', 'ਵੀਰ', 'ਸ਼ੁੱਕਰ', 'ਸ਼ਨਿੱਚਰ'], + 'weekdays_min' => ['ਐਤ', 'ਸੋਮ', 'ਮੰਗ', 'ਬੁੱਧ', 'ਵੀਰ', 'ਸ਼ੁੱਕ', 'ਸ਼ਨਿੱ'], + 'weekend' => [0, 0], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pa_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/pa_IN.php new file mode 100644 index 0000000..ca67642 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pa_IN.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Guo Xiang Tan + * - Josh Soref + * - Ash + * - harpreetkhalsagtbit + */ +return require __DIR__.'/pa.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pa_PK.php b/vendor/nesbot/carbon/src/Carbon/Lang/pa_PK.php new file mode 100644 index 0000000..f9af11c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pa_PK.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['جنوري', 'فروري', 'مارچ', 'اپريل', 'مٓی', 'جون', 'جولاي', 'اگست', 'ستمبر', 'اكتوبر', 'نومبر', 'دسمبر'], + 'months_short' => ['جنوري', 'فروري', 'مارچ', 'اپريل', 'مٓی', 'جون', 'جولاي', 'اگست', 'ستمبر', 'اكتوبر', 'نومبر', 'دسمبر'], + 'weekdays' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'], + 'weekdays_short' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'], + 'weekdays_min' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ص', 'ش'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pap.php b/vendor/nesbot/carbon/src/Carbon/Lang/pap.php new file mode 100644 index 0000000..b4c1706 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pap.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return [ + 'formats' => [ + 'LT' => 'HH.mm', + 'LTS' => 'HH.mm:ss', + 'L' => 'DD-MM-YY', + 'LL' => 'MMMM [di] DD, YYYY', + 'LLL' => 'DD MMM HH.mm', + 'LLLL' => 'MMMM DD, YYYY HH.mm', + ], + 'months' => ['yanüari', 'febrüari', 'mart', 'aprel', 'mei', 'yüni', 'yüli', 'ougùstùs', 'sèptèmber', 'oktober', 'novèmber', 'desèmber'], + 'months_short' => ['yan', 'feb', 'mar', 'apr', 'mei', 'yün', 'yül', 'oug', 'sèp', 'okt', 'nov', 'des'], + 'weekdays' => ['djadomingo', 'djaluna', 'djamars', 'djawebs', 'djarason', 'djabierne', 'djasabra'], + 'weekdays_short' => ['do', 'lu', 'ma', 'we', 'ra', 'bi', 'sa'], + 'weekdays_min' => ['do', 'lu', 'ma', 'we', 'ra', 'bi', 'sa'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'year' => ':count aña', + 'month' => ':count luna', + 'week' => ':count siman', + 'day' => ':count dia', + 'hour' => ':count ora', + 'minute' => ':count minüt', + 'second' => ':count sekònde', + 'list' => [', ', ' i '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pap_AW.php b/vendor/nesbot/carbon/src/Carbon/Lang/pap_AW.php new file mode 100644 index 0000000..e9a48ff --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pap_AW.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - information from native speaker Pablo Saratxaga pablo@mandrakesoft.com + */ +return require __DIR__.'/pap.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pap_CW.php b/vendor/nesbot/carbon/src/Carbon/Lang/pap_CW.php new file mode 100644 index 0000000..e9a48ff --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pap_CW.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - information from native speaker Pablo Saratxaga pablo@mandrakesoft.com + */ +return require __DIR__.'/pap.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pl.php b/vendor/nesbot/carbon/src/Carbon/Lang/pl.php new file mode 100644 index 0000000..b720535 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pl.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Wacław Jacek + * - François B + * - Tim Fish + * - Serhan Apaydın + * - Massimiliano Caniparoli + * - JD Isaacks + * - Jakub Szwacz + * - Jan + * - Paul + * - damlys + * - Marek (marast78) + * - Peter (UnrulyNatives) + * - Qrzysio + * - Jan (aso824) + * - diverpl + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count rok|:count lata|:count lat', + 'a_year' => 'rok|:count lata|:count lat', + 'y' => ':count r|:count l|:count l', + 'month' => ':count miesiąc|:count miesiące|:count miesięcy', + 'a_month' => 'miesiąc|:count miesiące|:count miesięcy', + 'm' => ':count mies.', + 'week' => ':count tydzień|:count tygodnie|:count tygodni', + 'a_week' => 'tydzień|:count tygodnie|:count tygodni', + 'w' => ':count tyg.', + 'day' => ':count dzień|:count dni|:count dni', + 'a_day' => 'dzień|:count dni|:count dni', + 'd' => ':count d', + 'hour' => ':count godzina|:count godziny|:count godzin', + 'a_hour' => 'godzina|:count godziny|:count godzin', + 'h' => ':count godz.', + 'minute' => ':count minuta|:count minuty|:count minut', + 'a_minute' => 'minuta|:count minuty|:count minut', + 'min' => ':count min', + 'second' => ':count sekunda|:count sekundy|:count sekund', + 'a_second' => '{1}kilka sekund|:count sekunda|:count sekundy|:count sekund', + 's' => ':count sek.', + 'ago' => ':time temu', + 'from_now' => static function ($time) { + return 'za '.strtr($time, [ + 'godzina' => 'godzinę', + 'minuta' => 'minutę', + 'sekunda' => 'sekundę', + ]); + }, + 'after' => ':time po', + 'before' => ':time przed', + 'diff_now' => 'teraz', + 'diff_today' => 'Dziś', + 'diff_today_regexp' => 'Dziś(?:\\s+o)?', + 'diff_yesterday' => 'wczoraj', + 'diff_yesterday_regexp' => 'Wczoraj(?:\\s+o)?', + 'diff_tomorrow' => 'jutro', + 'diff_tomorrow_regexp' => 'Jutro(?:\\s+o)?', + 'diff_before_yesterday' => 'przedwczoraj', + 'diff_after_tomorrow' => 'pojutrze', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Dziś o] LT', + 'nextDay' => '[Jutro o] LT', + 'nextWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[W niedzielę o] LT'; + case 2: + return '[We wtorek o] LT'; + case 3: + return '[W środę o] LT'; + case 6: + return '[W sobotę o] LT'; + default: + return '[W] dddd [o] LT'; + } + }, + 'lastDay' => '[Wczoraj o] LT', + 'lastWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[W zeszłą niedzielę o] LT'; + case 3: + return '[W zeszłą środę o] LT'; + case 6: + return '[W zeszłą sobotę o] LT'; + default: + return '[W zeszły] dddd [o] LT'; + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'months' => ['stycznia', 'lutego', 'marca', 'kwietnia', 'maja', 'czerwca', 'lipca', 'sierpnia', 'września', 'października', 'listopada', 'grudnia'], + 'months_standalone' => ['styczeń', 'luty', 'marzec', 'kwiecień', 'maj', 'czerwiec', 'lipiec', 'sierpień', 'wrzesień', 'październik', 'listopad', 'grudzień'], + 'months_short' => ['sty', 'lut', 'mar', 'kwi', 'maj', 'cze', 'lip', 'sie', 'wrz', 'paź', 'lis', 'gru'], + 'months_regexp' => '/(DD?o?\.?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/', + 'weekdays' => ['niedziela', 'poniedziałek', 'wtorek', 'środa', 'czwartek', 'piątek', 'sobota'], + 'weekdays_short' => ['ndz', 'pon', 'wt', 'śr', 'czw', 'pt', 'sob'], + 'weekdays_min' => ['Nd', 'Pn', 'Wt', 'Śr', 'Cz', 'Pt', 'So'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' i '], + 'meridiem' => ['przed południem', 'po południu'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pl_PL.php b/vendor/nesbot/carbon/src/Carbon/Lang/pl_PL.php new file mode 100644 index 0000000..222bcdb --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pl_PL.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/pl.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/prg.php b/vendor/nesbot/carbon/src/Carbon/Lang/prg.php new file mode 100644 index 0000000..6e63f4a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/prg.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'months' => ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10', 'M11', 'M12'], + 'months_short' => ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10', 'M11', 'M12'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-dd', + 'LL' => 'YYYY MMM D', + 'LLL' => 'YYYY MMMM D HH:mm', + 'LLLL' => 'YYYY MMMM D, dddd HH:mm', + ], + + 'year' => ':count meta', + 'y' => ':count meta', + 'a_year' => ':count meta', + + 'month' => ':count mēniks', // less reliable + 'm' => ':count mēniks', // less reliable + 'a_month' => ':count mēniks', // less reliable + + 'week' => ':count sawaītin', // less reliable + 'w' => ':count sawaītin', // less reliable + 'a_week' => ':count sawaītin', // less reliable + + 'day' => ':count di', + 'd' => ':count di', + 'a_day' => ':count di', + + 'hour' => ':count bruktēt', // less reliable + 'h' => ':count bruktēt', // less reliable + 'a_hour' => ':count bruktēt', // less reliable + + 'minute' => ':count līkuts', // less reliable + 'min' => ':count līkuts', // less reliable + 'a_minute' => ':count līkuts', // less reliable + + 'second' => ':count kitan', // less reliable + 's' => ':count kitan', // less reliable + 'a_second' => ':count kitan', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ps.php b/vendor/nesbot/carbon/src/Carbon/Lang/ps.php new file mode 100644 index 0000000..a928b28 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ps.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Muhammad Nasir Rahimi + * - Nassim Nasibullah (spinzar) + */ +return [ + 'year' => ':count کال|:count کاله', + 'y' => ':countکال|:countکاله', + 'month' => ':count مياشت|:count مياشتي', + 'm' => ':countمياشت|:countمياشتي', + 'week' => ':count اونۍ|:count اونۍ', + 'w' => ':countاونۍ|:countاونۍ', + 'day' => ':count ورځ|:count ورځي', + 'd' => ':countورځ|:countورځي', + 'hour' => ':count ساعت|:count ساعته', + 'h' => ':countساعت|:countساعته', + 'minute' => ':count دقيقه|:count دقيقې', + 'min' => ':countدقيقه|:countدقيقې', + 'second' => ':count ثانيه|:count ثانيې', + 's' => ':countثانيه|:countثانيې', + 'ago' => ':time دمخه', + 'from_now' => ':time له اوس څخه', + 'after' => ':time وروسته', + 'before' => ':time دمخه', + 'list' => ['، ', ' او '], + 'meridiem' => ['غ.م.', 'غ.و.'], + 'weekdays' => ['اتوار', 'ګل', 'نهه', 'شورو', 'زيارت', 'جمعه', 'خالي'], + 'weekdays_short' => ['ا', 'ګ', 'ن', 'ش', 'ز', 'ج', 'خ'], + 'weekdays_min' => ['ا', 'ګ', 'ن', 'ش', 'ز', 'ج', 'خ'], + 'months' => ['جنوري', 'فبروري', 'مارچ', 'اپریل', 'مۍ', 'جون', 'جولای', 'اگست', 'سېپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'], + 'months_short' => ['جنوري', 'فبروري', 'مارچ', 'اپریل', 'مۍ', 'جون', 'جولای', 'اگست', 'سېپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'], + 'months_standalone' => ['جنوري', 'فېبروري', 'مارچ', 'اپریل', 'مۍ', 'جون', 'جولای', 'اگست', 'سپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'], + 'months_short_standalone' => ['جنوري', 'فبروري', 'مارچ', 'اپریل', 'مۍ', 'جون', 'جولای', 'اگست', 'سپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'], + 'first_day_of_week' => 6, + 'weekend' => [4, 5], + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'YYYY/M/d', + 'LL' => 'YYYY MMM D', + 'LLL' => 'د YYYY د MMMM D H:mm', + 'LLLL' => 'dddd د YYYY د MMMM D H:mm', + ], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ps_AF.php b/vendor/nesbot/carbon/src/Carbon/Lang/ps_AF.php new file mode 100644 index 0000000..6ec5180 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ps_AF.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ps.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt.php new file mode 100644 index 0000000..bb6359b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Cassiano Montanari + * - Matt Pope + * - François B + * - Prodis + * - JD Isaacks + * - Raphael Amorim + * - João Magalhães + * - victortobias + * - Paulo Freitas + * - Sebastian Thierer + * - Claudson Martins (claudsonm) + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count ano|:count anos', + 'a_year' => 'um ano|:count anos', + 'y' => ':counta', + 'month' => ':count mês|:count meses', + 'a_month' => 'um mês|:count meses', + 'm' => ':countm', + 'week' => ':count semana|:count semanas', + 'a_week' => 'uma semana|:count semanas', + 'w' => ':countsem', + 'day' => ':count dia|:count dias', + 'a_day' => 'um dia|:count dias', + 'd' => ':countd', + 'hour' => ':count hora|:count horas', + 'a_hour' => 'uma hora|:count horas', + 'h' => ':counth', + 'minute' => ':count minuto|:count minutos', + 'a_minute' => 'um minuto|:count minutos', + 'min' => ':countmin', + 'second' => ':count segundo|:count segundos', + 'a_second' => 'alguns segundos|:count segundos', + 's' => ':counts', + 'millisecond' => ':count milissegundo|:count milissegundos', + 'a_millisecond' => 'um milissegundo|:count milissegundos', + 'ms' => ':countms', + 'microsecond' => ':count microssegundo|:count microssegundos', + 'a_microsecond' => 'um microssegundo|:count microssegundos', + 'µs' => ':countµs', + 'ago' => 'há :time', + 'from_now' => 'em :time', + 'after' => ':time depois', + 'before' => ':time antes', + 'diff_now' => 'agora', + 'diff_today' => 'Hoje', + 'diff_today_regexp' => 'Hoje(?:\\s+às)?', + 'diff_yesterday' => 'ontem', + 'diff_yesterday_regexp' => 'Ontem(?:\\s+às)?', + 'diff_tomorrow' => 'amanhã', + 'diff_tomorrow_regexp' => 'Amanhã(?:\\s+às)?', + 'diff_before_yesterday' => 'anteontem', + 'diff_after_tomorrow' => 'depois de amanhã', + 'period_recurrences' => 'uma vez|:count vezes', + 'period_interval' => 'cada :interval', + 'period_start_date' => 'de :date', + 'period_end_date' => 'até :date', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D [de] MMMM [de] YYYY', + 'LLL' => 'D [de] MMMM [de] YYYY HH:mm', + 'LLLL' => 'dddd, D [de] MMMM [de] YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Hoje às] LT', + 'nextDay' => '[Amanhã às] LT', + 'nextWeek' => 'dddd [às] LT', + 'lastDay' => '[Ontem às] LT', + 'lastWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + case 6: + return '[Último] dddd [às] LT'; + default: + return '[Última] dddd [às] LT'; + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => ':numberº', + 'months' => ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'], + 'months_short' => ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov', 'dez'], + 'weekdays' => ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado'], + 'weekdays_short' => ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'], + 'weekdays_min' => ['Do', '2ª', '3ª', '4ª', '5ª', '6ª', 'Sá'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' e '], + 'ordinal_words' => [ + 'of' => 'de', + 'first' => 'primeira', + 'second' => 'segunda', + 'third' => 'terceira', + 'fourth' => 'quarta', + 'fifth' => 'quinta', + 'last' => 'última', + ], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_AO.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_AO.php new file mode 100644 index 0000000..22c01ec --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_AO.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/pt.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php new file mode 100644 index 0000000..e917c5c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Cassiano Montanari + * - Eduardo Dalla Vecchia + * - David Rodrigues + * - Matt Pope + * - François B + * - Prodis + * - Marlon Maxwel + * - JD Isaacks + * - Raphael Amorim + * - Rafael Raupp + * - felipeleite1 + * - swalker + * - Lucas Macedo + * - Paulo Freitas + * - Sebastian Thierer + */ +return array_replace_recursive(require __DIR__.'/pt.php', [ + 'period_recurrences' => 'uma|:count vez', + 'period_interval' => 'toda :interval', + 'formats' => [ + 'LLL' => 'D [de] MMMM [de] YYYY [às] HH:mm', + 'LLLL' => 'dddd, D [de] MMMM [de] YYYY [às] HH:mm', + ], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_CH.php new file mode 100644 index 0000000..22c01ec --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_CH.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/pt.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_CV.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_CV.php new file mode 100644 index 0000000..22c01ec --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_CV.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/pt.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_GQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_GQ.php new file mode 100644 index 0000000..22c01ec --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_GQ.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/pt.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_GW.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_GW.php new file mode 100644 index 0000000..22c01ec --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_GW.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/pt.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_LU.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_LU.php new file mode 100644 index 0000000..22c01ec --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_LU.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/pt.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_MO.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_MO.php new file mode 100644 index 0000000..f2b5eab --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_MO.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/pt.php', [ + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'LLL' => 'D [de] MMMM [de] YYYY, h:mm a', + 'LLLL' => 'dddd, D [de] MMMM [de] YYYY, h:mm a', + ], + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_MZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_MZ.php new file mode 100644 index 0000000..fbc0c97 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_MZ.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/pt.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_PT.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_PT.php new file mode 100644 index 0000000..2a76fc1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_PT.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RAP bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/pt.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'], + 'months_short' => ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov', 'dez'], + 'weekdays' => ['domingo', 'segunda', 'terça', 'quarta', 'quinta', 'sexta', 'sábado'], + 'weekdays_short' => ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'], + 'weekdays_min' => ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_ST.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_ST.php new file mode 100644 index 0000000..22c01ec --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_ST.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/pt.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_TL.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_TL.php new file mode 100644 index 0000000..22c01ec --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_TL.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/pt.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/qu.php b/vendor/nesbot/carbon/src/Carbon/Lang/qu.php new file mode 100644 index 0000000..65278cd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/qu.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/es_UY.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM, YYYY HH:mm', + ], + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/qu_BO.php b/vendor/nesbot/carbon/src/Carbon/Lang/qu_BO.php new file mode 100644 index 0000000..d5db6bf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/qu_BO.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/qu.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/qu_EC.php b/vendor/nesbot/carbon/src/Carbon/Lang/qu_EC.php new file mode 100644 index 0000000..d5db6bf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/qu_EC.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/qu.php', [ + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/quz.php b/vendor/nesbot/carbon/src/Carbon/Lang/quz.php new file mode 100644 index 0000000..1640c02 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/quz.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/quz_PE.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/quz_PE.php b/vendor/nesbot/carbon/src/Carbon/Lang/quz_PE.php new file mode 100644 index 0000000..d322918 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/quz_PE.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Sugar Labs // OLPC sugarlabs.org libc-alpha@sourceware.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['iniru', 'phiwriru', 'marsu', 'awril', 'mayu', 'huniyu', 'huliyu', 'agustu', 'siptiyimri', 'uktuwri', 'nuwiyimri', 'tisiyimri'], + 'months_short' => ['ini', 'phi', 'mar', 'awr', 'may', 'hun', 'hul', 'agu', 'sip', 'ukt', 'nuw', 'tis'], + 'weekdays' => ['tuminku', 'lunis', 'martis', 'miyirkulis', 'juywis', 'wiyirnis', 'sawatu'], + 'weekdays_short' => ['tum', 'lun', 'mar', 'miy', 'juy', 'wiy', 'saw'], + 'weekdays_min' => ['tum', 'lun', 'mar', 'miy', 'juy', 'wiy', 'saw'], + 'day_of_first_week_of_year' => 1, + + 'minute' => ':count uchuy', // less reliable + 'min' => ':count uchuy', // less reliable + 'a_minute' => ':count uchuy', // less reliable + + 'year' => ':count wata', + 'y' => ':count wata', + 'a_year' => ':count wata', + + 'month' => ':count killa', + 'm' => ':count killa', + 'a_month' => ':count killa', + + 'week' => ':count simana', + 'w' => ':count simana', + 'a_week' => ':count simana', + + 'day' => ':count pʼunchaw', + 'd' => ':count pʼunchaw', + 'a_day' => ':count pʼunchaw', + + 'hour' => ':count ura', + 'h' => ':count ura', + 'a_hour' => ':count ura', + + 'second' => ':count iskay ñiqin', + 's' => ':count iskay ñiqin', + 'a_second' => ':count iskay ñiqin', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/raj.php b/vendor/nesbot/carbon/src/Carbon/Lang/raj.php new file mode 100644 index 0000000..26138c9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/raj.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/raj_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/raj_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/raj_IN.php new file mode 100644 index 0000000..7b4589c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/raj_IN.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - meghrajsuthar03@gmail.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रैल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितंबर', 'अक्टूबर', 'नवंबर', 'दिसंबर'], + 'months_short' => ['जन', 'फर', 'मार्च', 'अप्रै', 'मई', 'जून', 'जुल', 'अग', 'सित', 'अक्टू', 'नव', 'दिस'], + 'weekdays' => ['रविवार', 'सोमवार', 'मंगल्लवार', 'बुधवार', 'बृहस्पतिवार', 'शुक्रवार', 'शनिवार'], + 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पति', 'शुक्र', 'शनि'], + 'weekdays_min' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पति', 'शुक्र', 'शनि'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], + + 'year' => ':count आंहू', // less reliable + 'y' => ':count आंहू', // less reliable + 'a_year' => ':count आंहू', // less reliable + + 'month' => ':count सूरज', // less reliable + 'm' => ':count सूरज', // less reliable + 'a_month' => ':count सूरज', // less reliable + + 'week' => ':count निवाज', // less reliable + 'w' => ':count निवाज', // less reliable + 'a_week' => ':count निवाज', // less reliable + + 'day' => ':count अेक', // less reliable + 'd' => ':count अेक', // less reliable + 'a_day' => ':count अेक', // less reliable + + 'hour' => ':count दुनियांण', // less reliable + 'h' => ':count दुनियांण', // less reliable + 'a_hour' => ':count दुनियांण', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/rm.php b/vendor/nesbot/carbon/src/Carbon/Lang/rm.php new file mode 100644 index 0000000..1843f45 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/rm.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Philippe Vaucher + * - tjku + * - Max Melentiev + * - Juanito Fatas + * - Tsutomu Kuroda + * - Akira Matsuda + * - Christopher Dell + * - Enrique Vidal + * - Simone Carletti + * - Aaron Patterson + * - Nicolás Hock Isaza + * - sebastian de castelberg + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'Do MMMM YYYY', + 'LLL' => 'Do MMMM, HH:mm [Uhr]', + 'LLLL' => 'dddd, Do MMMM YYYY, HH:mm [Uhr]', + ], + 'year' => ':count onn|:count onns', + 'month' => ':count mais', + 'week' => ':count emna|:count emnas', + 'day' => ':count di|:count dis', + 'hour' => ':count oura|:count ouras', + 'minute' => ':count minuta|:count minutas', + 'second' => ':count secunda|:count secundas', + 'weekdays' => ['dumengia', 'glindesdi', 'mardi', 'mesemna', 'gievgia', 'venderdi', 'sonda'], + 'weekdays_short' => ['du', 'gli', 'ma', 'me', 'gie', 've', 'so'], + 'weekdays_min' => ['du', 'gli', 'ma', 'me', 'gie', 've', 'so'], + 'months' => ['schaner', 'favrer', 'mars', 'avrigl', 'matg', 'zercladur', 'fanadur', 'avust', 'settember', 'october', 'november', 'december'], + 'months_short' => ['schan', 'favr', 'mars', 'avr', 'matg', 'zercl', 'fan', 'avust', 'sett', 'oct', 'nov', 'dec'], + 'meridiem' => ['avantmezdi', 'suentermezdi'], + 'list' => [', ', ' e '], + 'first_day_of_week' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/rn.php b/vendor/nesbot/carbon/src/Carbon/Lang/rn.php new file mode 100644 index 0000000..8ab958e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/rn.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Z.MU.', 'Z.MW.'], + 'weekdays' => ['Ku w’indwi', 'Ku wa mbere', 'Ku wa kabiri', 'Ku wa gatatu', 'Ku wa kane', 'Ku wa gatanu', 'Ku wa gatandatu'], + 'weekdays_short' => ['cu.', 'mbe.', 'kab.', 'gtu.', 'kan.', 'gnu.', 'gnd.'], + 'weekdays_min' => ['cu.', 'mbe.', 'kab.', 'gtu.', 'kan.', 'gnu.', 'gnd.'], + 'months' => ['Nzero', 'Ruhuhuma', 'Ntwarante', 'Ndamukiza', 'Rusama', 'Ruheshi', 'Mukakaro', 'Nyandagaro', 'Nyakanga', 'Gitugutu', 'Munyonyo', 'Kigarama'], + 'months_short' => ['Mut.', 'Gas.', 'Wer.', 'Mat.', 'Gic.', 'Kam.', 'Nya.', 'Kan.', 'Nze.', 'Ukw.', 'Ugu.', 'Uku.'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + + 'year' => 'imyaka :count', + 'y' => 'imyaka :count', + 'a_year' => 'imyaka :count', + + 'month' => 'amezi :count', + 'm' => 'amezi :count', + 'a_month' => 'amezi :count', + + 'week' => 'indwi :count', + 'w' => 'indwi :count', + 'a_week' => 'indwi :count', + + 'day' => 'imisi :count', + 'd' => 'imisi :count', + 'a_day' => 'imisi :count', + + 'hour' => 'amasaha :count', + 'h' => 'amasaha :count', + 'a_hour' => 'amasaha :count', + + 'minute' => 'iminuta :count', + 'min' => 'iminuta :count', + 'a_minute' => 'iminuta :count', + + 'second' => 'inguvu :count', // less reliable + 's' => 'inguvu :count', // less reliable + 'a_second' => 'inguvu :count', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ro.php b/vendor/nesbot/carbon/src/Carbon/Lang/ro.php new file mode 100644 index 0000000..868a327 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ro.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - JD Isaacks + * - Cătălin Georgescu + * - Valentin Ivaşcu (oriceon) + */ +return [ + 'year' => ':count an|:count ani|:count ani', + 'a_year' => 'un an|:count ani|:count ani', + 'y' => ':count a.', + 'month' => ':count lună|:count luni|:count luni', + 'a_month' => 'o lună|:count luni|:count luni', + 'm' => ':count l.', + 'week' => ':count săptămână|:count săptămâni|:count săptămâni', + 'a_week' => 'o săptămână|:count săptămâni|:count săptămâni', + 'w' => ':count săp.', + 'day' => ':count zi|:count zile|:count zile', + 'a_day' => 'o zi|:count zile|:count zile', + 'd' => ':count z.', + 'hour' => ':count oră|:count ore|:count ore', + 'a_hour' => 'o oră|:count ore|:count ore', + 'h' => ':count o.', + 'minute' => ':count minut|:count minute|:count minute', + 'a_minute' => 'un minut|:count minute|:count minute', + 'min' => ':count m.', + 'second' => ':count secundă|:count secunde|:count secunde', + 'a_second' => 'câteva secunde|:count secunde|:count secunde', + 's' => ':count sec.', + 'ago' => ':time în urmă', + 'from_now' => 'peste :time', + 'after' => 'peste :time', + 'before' => 'acum :time', + 'diff_now' => 'acum', + 'diff_today' => 'azi', + 'diff_today_regexp' => 'azi(?:\\s+la)?', + 'diff_yesterday' => 'ieri', + 'diff_yesterday_regexp' => 'ieri(?:\\s+la)?', + 'diff_tomorrow' => 'mâine', + 'diff_tomorrow_regexp' => 'mâine(?:\\s+la)?', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY H:mm', + 'LLLL' => 'dddd, D MMMM YYYY H:mm', + ], + 'calendar' => [ + 'sameDay' => '[azi la] LT', + 'nextDay' => '[mâine la] LT', + 'nextWeek' => 'dddd [la] LT', + 'lastDay' => '[ieri la] LT', + 'lastWeek' => '[fosta] dddd [la] LT', + 'sameElse' => 'L', + ], + 'months' => ['ianuarie', 'februarie', 'martie', 'aprilie', 'mai', 'iunie', 'iulie', 'august', 'septembrie', 'octombrie', 'noiembrie', 'decembrie'], + 'months_short' => ['ian.', 'feb.', 'mar.', 'apr.', 'mai', 'iun.', 'iul.', 'aug.', 'sept.', 'oct.', 'nov.', 'dec.'], + 'weekdays' => ['duminică', 'luni', 'marți', 'miercuri', 'joi', 'vineri', 'sâmbătă'], + 'weekdays_short' => ['dum', 'lun', 'mar', 'mie', 'joi', 'vin', 'sâm'], + 'weekdays_min' => ['du', 'lu', 'ma', 'mi', 'jo', 'vi', 'sâ'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' și '], + 'meridiem' => ['a.m.', 'p.m.'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ro_MD.php b/vendor/nesbot/carbon/src/Carbon/Lang/ro_MD.php new file mode 100644 index 0000000..ad1d2fa --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ro_MD.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ro.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY, HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY, HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ro_RO.php b/vendor/nesbot/carbon/src/Carbon/Lang/ro_RO.php new file mode 100644 index 0000000..102afcd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ro_RO.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ro.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/rof.php b/vendor/nesbot/carbon/src/Carbon/Lang/rof.php new file mode 100644 index 0000000..205fc26 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/rof.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['kang’ama', 'kingoto'], + 'weekdays' => ['Ijumapili', 'Ijumatatu', 'Ijumanne', 'Ijumatano', 'Alhamisi', 'Ijumaa', 'Ijumamosi'], + 'weekdays_short' => ['Ijp', 'Ijt', 'Ijn', 'Ijtn', 'Alh', 'Iju', 'Ijm'], + 'weekdays_min' => ['Ijp', 'Ijt', 'Ijn', 'Ijtn', 'Alh', 'Iju', 'Ijm'], + 'months' => ['Mweri wa kwanza', 'Mweri wa kaili', 'Mweri wa katatu', 'Mweri wa kaana', 'Mweri wa tanu', 'Mweri wa sita', 'Mweri wa saba', 'Mweri wa nane', 'Mweri wa tisa', 'Mweri wa ikumi', 'Mweri wa ikumi na moja', 'Mweri wa ikumi na mbili'], + 'months_short' => ['M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9', 'M10', 'M11', 'M12'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru.php new file mode 100644 index 0000000..673b043 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru.php @@ -0,0 +1,191 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Bari Badamshin + * - Jørn Ølmheim + * - François B + * - Tim Fish + * - Коренберг Марк (imac) + * - Serhan Apaydın + * - RomeroMsk + * - vsn4ik + * - JD Isaacks + * - Bari Badamshin + * - Jørn Ølmheim + * - François B + * - Коренберг Марк (imac) + * - Serhan Apaydın + * - RomeroMsk + * - vsn4ik + * - JD Isaacks + * - Fellzo + * - andrey-helldar + * - Pavel Skripkin (psxx) + * - AlexWalkerson + * - Vladislav UnsealedOne + * - dima-bzz + */ + +use Carbon\CarbonInterface; + +$transformDiff = function ($input) { + return strtr($input, [ + 'неделя' => 'неделю', + 'секунда' => 'секунду', + 'минута' => 'минуту', + ]); +}; + +return [ + 'year' => ':count год|:count года|:count лет', + 'y' => ':count г.|:count г.|:count л.', + 'a_year' => '{1}год|:count год|:count года|:count лет', + 'month' => ':count месяц|:count месяца|:count месяцев', + 'm' => ':count мес.', + 'a_month' => '{1}месяц|:count месяц|:count месяца|:count месяцев', + 'week' => ':count неделя|:count недели|:count недель', + 'w' => ':count нед.', + 'a_week' => '{1}неделя|:count неделю|:count недели|:count недель', + 'day' => ':count день|:count дня|:count дней', + 'd' => ':count д.', + 'a_day' => '{1}день|:count день|:count дня|:count дней', + 'hour' => ':count час|:count часа|:count часов', + 'h' => ':count ч.', + 'a_hour' => '{1}час|:count час|:count часа|:count часов', + 'minute' => ':count минута|:count минуты|:count минут', + 'min' => ':count мин.', + 'a_minute' => '{1}минута|:count минута|:count минуты|:count минут', + 'second' => ':count секунда|:count секунды|:count секунд', + 's' => ':count сек.', + 'a_second' => '{1}несколько секунд|:count секунду|:count секунды|:count секунд', + 'ago' => function ($time) use ($transformDiff) { + return $transformDiff($time).' назад'; + }, + 'from_now' => function ($time) use ($transformDiff) { + return 'через '.$transformDiff($time); + }, + 'after' => function ($time) use ($transformDiff) { + return $transformDiff($time).' после'; + }, + 'before' => function ($time) use ($transformDiff) { + return $transformDiff($time).' до'; + }, + 'diff_now' => 'только что', + 'diff_today' => 'Сегодня,', + 'diff_today_regexp' => 'Сегодня,?(?:\\s+в)?', + 'diff_yesterday' => 'вчера', + 'diff_yesterday_regexp' => 'Вчера,?(?:\\s+в)?', + 'diff_tomorrow' => 'завтра', + 'diff_tomorrow_regexp' => 'Завтра,?(?:\\s+в)?', + 'diff_before_yesterday' => 'позавчера', + 'diff_after_tomorrow' => 'послезавтра', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY г.', + 'LLL' => 'D MMMM YYYY г., H:mm', + 'LLLL' => 'dddd, D MMMM YYYY г., H:mm', + ], + 'calendar' => [ + 'sameDay' => '[Сегодня, в] LT', + 'nextDay' => '[Завтра, в] LT', + 'nextWeek' => function (CarbonInterface $current, CarbonInterface $other) { + if ($current->week !== $other->week) { + switch ($current->dayOfWeek) { + case 0: + return '[В следующее] dddd, [в] LT'; + case 1: + case 2: + case 4: + return '[В следующий] dddd, [в] LT'; + case 3: + case 5: + case 6: + return '[В следующую] dddd, [в] LT'; + } + } + + if ($current->dayOfWeek === 2) { + return '[Во] dddd, [в] LT'; + } + + return '[В] dddd, [в] LT'; + }, + 'lastDay' => '[Вчера, в] LT', + 'lastWeek' => function (CarbonInterface $current, CarbonInterface $other) { + if ($current->week !== $other->week) { + switch ($current->dayOfWeek) { + case 0: + return '[В прошлое] dddd, [в] LT'; + case 1: + case 2: + case 4: + return '[В прошлый] dddd, [в] LT'; + case 3: + case 5: + case 6: + return '[В прошлую] dddd, [в] LT'; + } + } + + if ($current->dayOfWeek === 2) { + return '[Во] dddd, [в] LT'; + } + + return '[В] dddd, [в] LT'; + }, + 'sameElse' => 'L', + ], + 'ordinal' => function ($number, $period) { + switch ($period) { + case 'M': + case 'd': + case 'DDD': + return $number.'-й'; + case 'D': + return $number.'-го'; + case 'w': + case 'W': + return $number.'-я'; + default: + return $number; + } + }, + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'ночи'; + } + if ($hour < 12) { + return 'утра'; + } + if ($hour < 17) { + return 'дня'; + } + + return 'вечера'; + }, + 'months' => ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'], + 'months_standalone' => ['январь', 'февраль', 'март', 'апрель', 'май', 'июнь', 'июль', 'август', 'сентябрь', 'октябрь', 'ноябрь', 'декабрь'], + 'months_short' => ['янв', 'фев', 'мар', 'апр', 'мая', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'], + 'months_short_standalone' => ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'], + 'months_regexp' => '/(DD?o?\.?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/', + 'weekdays' => ['воскресенье', 'понедельник', 'вторник', 'среду', 'четверг', 'пятницу', 'субботу'], + 'weekdays_standalone' => ['воскресенье', 'понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота'], + 'weekdays_short' => ['вск', 'пнд', 'втр', 'срд', 'чтв', 'птн', 'сбт'], + 'weekdays_min' => ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'], + 'weekdays_regexp' => '/\[\s*(В|в)\s*((?:прошлую|следующую|эту)\s*)?\]\s*dddd/', + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' и '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru_BY.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru_BY.php new file mode 100644 index 0000000..8ca7df3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru_BY.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ru.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru_KG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru_KG.php new file mode 100644 index 0000000..8ca7df3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru_KG.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ru.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru_KZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru_KZ.php new file mode 100644 index 0000000..8ca7df3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru_KZ.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ru.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru_MD.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru_MD.php new file mode 100644 index 0000000..8ca7df3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru_MD.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ru.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru_RU.php new file mode 100644 index 0000000..8ca7df3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru_RU.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ru.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru_UA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru_UA.php new file mode 100644 index 0000000..db958d6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru_UA.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - RFC 2319 bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/ru.php', [ + 'weekdays' => ['воскресенье', 'понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота'], + 'weekdays_short' => ['вск', 'пнд', 'вто', 'срд', 'чтв', 'птн', 'суб'], + 'weekdays_min' => ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'су'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/rw.php b/vendor/nesbot/carbon/src/Carbon/Lang/rw.php new file mode 100644 index 0000000..bc4a347 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/rw.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/rw_RW.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/rw_RW.php b/vendor/nesbot/carbon/src/Carbon/Lang/rw_RW.php new file mode 100644 index 0000000..9b3e068 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/rw_RW.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Rwanda Steve Murphy murf@e-tools.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['Mutarama', 'Gashyantare', 'Werurwe', 'Mata', 'Gicuransi', 'Kamena', 'Nyakanga', 'Kanama', 'Nzeli', 'Ukwakira', 'Ugushyingo', 'Ukuboza'], + 'months_short' => ['Mut', 'Gas', 'Wer', 'Mat', 'Gic', 'Kam', 'Nya', 'Kan', 'Nze', 'Ukw', 'Ugu', 'Uku'], + 'weekdays' => ['Ku cyumweru', 'Kuwa mbere', 'Kuwa kabiri', 'Kuwa gatatu', 'Kuwa kane', 'Kuwa gatanu', 'Kuwa gatandatu'], + 'weekdays_short' => ['Mwe', 'Mbe', 'Kab', 'Gtu', 'Kan', 'Gnu', 'Gnd'], + 'weekdays_min' => ['Mwe', 'Mbe', 'Kab', 'Gtu', 'Kan', 'Gnu', 'Gnd'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'second' => ':count vuna', // less reliable + 's' => ':count vuna', // less reliable + 'a_second' => ':count vuna', // less reliable + + 'year' => 'aka :count', + 'y' => 'aka :count', + 'a_year' => 'aka :count', + + 'month' => 'ezi :count', + 'm' => 'ezi :count', + 'a_month' => 'ezi :count', + + 'week' => ':count icyumweru', + 'w' => ':count icyumweru', + 'a_week' => ':count icyumweru', + + 'day' => ':count nsi', + 'd' => ':count nsi', + 'a_day' => ':count nsi', + + 'hour' => 'saha :count', + 'h' => 'saha :count', + 'a_hour' => 'saha :count', + + 'minute' => ':count -nzinya', + 'min' => ':count -nzinya', + 'a_minute' => ':count -nzinya', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/rwk.php b/vendor/nesbot/carbon/src/Carbon/Lang/rwk.php new file mode 100644 index 0000000..ed92e8e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/rwk.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['utuko', 'kyiukonyi'], + 'weekdays' => ['Jumapilyi', 'Jumatatuu', 'Jumanne', 'Jumatanu', 'Alhamisi', 'Ijumaa', 'Jumamosi'], + 'weekdays_short' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'], + 'weekdays_min' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'], + 'months' => ['Januari', 'Februari', 'Machi', 'Aprilyi', 'Mei', 'Junyi', 'Julyai', 'Agusti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'], + 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sa.php b/vendor/nesbot/carbon/src/Carbon/Lang/sa.php new file mode 100644 index 0000000..1357c03 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sa.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/sa_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sa_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/sa_IN.php new file mode 100644 index 0000000..cfda9a6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sa_IN.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - The Debian project Christian Perrier bubulle@debian.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D-MM-YY', + ], + 'months' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'], + 'months_short' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'], + 'weekdays' => ['रविवासर:', 'सोमवासर:', 'मंगलवासर:', 'बुधवासर:', 'बृहस्पतिवासरः', 'शुक्रवासर', 'शनिवासर:'], + 'weekdays_short' => ['रविः', 'सोम:', 'मंगल:', 'बुध:', 'बृहस्पतिः', 'शुक्र', 'शनि:'], + 'weekdays_min' => ['रविः', 'सोम:', 'मंगल:', 'बुध:', 'बृहस्पतिः', 'शुक्र', 'शनि:'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], + + 'minute' => ':count होरा', // less reliable + 'min' => ':count होरा', // less reliable + 'a_minute' => ':count होरा', // less reliable + + 'year' => ':count वर्ष', + 'y' => ':count वर्ष', + 'a_year' => ':count वर्ष', + + 'month' => ':count मास', + 'm' => ':count मास', + 'a_month' => ':count मास', + + 'week' => ':count सप्ताहः saptahaĥ', + 'w' => ':count सप्ताहः saptahaĥ', + 'a_week' => ':count सप्ताहः saptahaĥ', + + 'day' => ':count दिन', + 'd' => ':count दिन', + 'a_day' => ':count दिन', + + 'hour' => ':count घण्टा', + 'h' => ':count घण्टा', + 'a_hour' => ':count घण्टा', + + 'second' => ':count द्वितीयः', + 's' => ':count द्वितीयः', + 'a_second' => ':count द्वितीयः', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sah.php b/vendor/nesbot/carbon/src/Carbon/Lang/sah.php new file mode 100644 index 0000000..b828824 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sah.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/sah_RU.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sah_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/sah_RU.php new file mode 100644 index 0000000..94cc0cb --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sah_RU.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Valery Timiriliyev Valery Timiriliyev timiriliyev@gmail.com + */ +return array_replace_recursive(require __DIR__.'/ru.php', [ + 'formats' => [ + 'L' => 'YYYY.MM.DD', + ], + 'months' => ['тохсунньу', 'олунньу', 'кулун тутар', 'муус устар', 'ыам ыйын', 'бэс ыйын', 'от ыйын', 'атырдьах ыйын', 'балаҕан ыйын', 'алтынньы', 'сэтинньи', 'ахсынньы'], + 'months_short' => ['тохс', 'олун', 'кул', 'муус', 'ыам', 'бэс', 'от', 'атыр', 'бал', 'алт', 'сэт', 'ахс'], + 'weekdays' => ['баскыһыанньа', 'бэнидиэнньик', 'оптуорунньук', 'сэрэдэ', 'чэппиэр', 'бээтинсэ', 'субуота'], + 'weekdays_short' => ['бс', 'бн', 'оп', 'ср', 'чп', 'бт', 'сб'], + 'weekdays_min' => ['бс', 'бн', 'оп', 'ср', 'чп', 'бт', 'сб'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/saq.php b/vendor/nesbot/carbon/src/Carbon/Lang/saq.php new file mode 100644 index 0000000..ff8bf60 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/saq.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Tesiran', 'Teipa'], + 'weekdays' => ['Mderot ee are', 'Mderot ee kuni', 'Mderot ee ong’wan', 'Mderot ee inet', 'Mderot ee ile', 'Mderot ee sapa', 'Mderot ee kwe'], + 'weekdays_short' => ['Are', 'Kun', 'Ong', 'Ine', 'Ile', 'Sap', 'Kwe'], + 'weekdays_min' => ['Are', 'Kun', 'Ong', 'Ine', 'Ile', 'Sap', 'Kwe'], + 'months' => ['Lapa le obo', 'Lapa le waare', 'Lapa le okuni', 'Lapa le ong’wan', 'Lapa le imet', 'Lapa le ile', 'Lapa le sapa', 'Lapa le isiet', 'Lapa le saal', 'Lapa le tomon', 'Lapa le tomon obo', 'Lapa le tomon waare'], + 'months_short' => ['Obo', 'Waa', 'Oku', 'Ong', 'Ime', 'Ile', 'Sap', 'Isi', 'Saa', 'Tom', 'Tob', 'Tow'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sat.php b/vendor/nesbot/carbon/src/Carbon/Lang/sat.php new file mode 100644 index 0000000..c9914c6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sat.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/sat_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sat_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/sat_IN.php new file mode 100644 index 0000000..632b1af --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sat_IN.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Red Hat Pune libc-alpha@sourceware.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रेल', 'मई', 'जुन', 'जुलाई', 'अगस्त', 'सितम्बर', 'अखथबर', 'नवम्बर', 'दिसम्बर'], + 'months_short' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रेल', 'मई', 'जुन', 'जुलाई', 'अगस्त', 'सितम्बर', 'अखथबर', 'नवम्बर', 'दिसम्बर'], + 'weekdays' => ['सिंगेमाँहाँ', 'ओतेमाँहाँ', 'बालेमाँहाँ', 'सागुनमाँहाँ', 'सारदीमाँहाँ', 'जारुममाँहाँ', 'ञुहुममाँहाँ'], + 'weekdays_short' => ['सिंगे', 'ओते', 'बाले', 'सागुन', 'सारदी', 'जारुम', 'ञुहुम'], + 'weekdays_min' => ['सिंगे', 'ओते', 'बाले', 'सागुन', 'सारदी', 'जारुम', 'ञुहुम'], + 'day_of_first_week_of_year' => 1, + + 'month' => ':count ńindạ cando', // less reliable + 'm' => ':count ńindạ cando', // less reliable + 'a_month' => ':count ńindạ cando', // less reliable + + 'week' => ':count mãhã', // less reliable + 'w' => ':count mãhã', // less reliable + 'a_week' => ':count mãhã', // less reliable + + 'hour' => ':count ᱥᱳᱱᱚ', // less reliable + 'h' => ':count ᱥᱳᱱᱚ', // less reliable + 'a_hour' => ':count ᱥᱳᱱᱚ', // less reliable + + 'minute' => ':count ᱯᱤᱞᱪᱩ', // less reliable + 'min' => ':count ᱯᱤᱞᱪᱩ', // less reliable + 'a_minute' => ':count ᱯᱤᱞᱪᱩ', // less reliable + + 'second' => ':count ar', // less reliable + 's' => ':count ar', // less reliable + 'a_second' => ':count ar', // less reliable + + 'year' => ':count ne̲s', + 'y' => ':count ne̲s', + 'a_year' => ':count ne̲s', + + 'day' => ':count ᱫᱤᱱ', + 'd' => ':count ᱫᱤᱱ', + 'a_day' => ':count ᱫᱤᱱ', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sbp.php b/vendor/nesbot/carbon/src/Carbon/Lang/sbp.php new file mode 100644 index 0000000..e29ca37 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sbp.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Lwamilawu', 'Pashamihe'], + 'weekdays' => ['Mulungu', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alahamisi', 'Ijumaa', 'Jumamosi'], + 'weekdays_short' => ['Mul', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'], + 'weekdays_min' => ['Mul', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'], + 'months' => ['Mupalangulwa', 'Mwitope', 'Mushende', 'Munyi', 'Mushende Magali', 'Mujimbi', 'Mushipepo', 'Mupuguto', 'Munyense', 'Mokhu', 'Musongandembwe', 'Muhaano'], + 'months_short' => ['Mup', 'Mwi', 'Msh', 'Mun', 'Mag', 'Muj', 'Msp', 'Mpg', 'Mye', 'Mok', 'Mus', 'Muh'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sc.php b/vendor/nesbot/carbon/src/Carbon/Lang/sc.php new file mode 100644 index 0000000..7178cf4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sc.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/sc_IT.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sc_IT.php b/vendor/nesbot/carbon/src/Carbon/Lang/sc_IT.php new file mode 100644 index 0000000..5d1e4ce --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sc_IT.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Sardinian Translators Team Massimeddu Cireddu massimeddu@gmail.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD. MM. YY', + ], + 'months' => ['Ghennàrgiu', 'Freàrgiu', 'Martzu', 'Abrile', 'Maju', 'Làmpadas', 'Argiolas//Trìulas', 'Austu', 'Cabudanni', 'Santugaine//Ladàmine', 'Onniasantu//Santandria', 'Nadale//Idas'], + 'months_short' => ['Ghe', 'Fre', 'Mar', 'Abr', 'Maj', 'Làm', 'Arg', 'Aus', 'Cab', 'Lad', 'Onn', 'Nad'], + 'weekdays' => ['Domìnigu', 'Lunis', 'Martis', 'Mèrcuris', 'Giòbia', 'Chenàbura', 'Sàbadu'], + 'weekdays_short' => ['Dom', 'Lun', 'Mar', 'Mèr', 'Giò', 'Che', 'Sàb'], + 'weekdays_min' => ['Dom', 'Lun', 'Mar', 'Mèr', 'Giò', 'Che', 'Sàb'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'minute' => ':count mementu', // less reliable + 'min' => ':count mementu', // less reliable + 'a_minute' => ':count mementu', // less reliable + + 'year' => ':count annu', + 'y' => ':count annu', + 'a_year' => ':count annu', + + 'month' => ':count mese', + 'm' => ':count mese', + 'a_month' => ':count mese', + + 'week' => ':count chida', + 'w' => ':count chida', + 'a_week' => ':count chida', + + 'day' => ':count dí', + 'd' => ':count dí', + 'a_day' => ':count dí', + + 'hour' => ':count ora', + 'h' => ':count ora', + 'a_hour' => ':count ora', + + 'second' => ':count secundu', + 's' => ':count secundu', + 'a_second' => ':count secundu', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sd.php b/vendor/nesbot/carbon/src/Carbon/Lang/sd.php new file mode 100644 index 0000000..0022c5a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sd.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +$months = [ + 'جنوري', + 'فيبروري', + 'مارچ', + 'اپريل', + 'مئي', + 'جون', + 'جولاءِ', + 'آگسٽ', + 'سيپٽمبر', + 'آڪٽوبر', + 'نومبر', + 'ڊسمبر', +]; + +$weekdays = [ + 'آچر', + 'سومر', + 'اڱارو', + 'اربع', + 'خميس', + 'جمع', + 'ڇنڇر', +]; + +/* + * Authors: + * - Narain Sagar + * - Sawood Alam + * - Narain Sagar + */ +return [ + 'year' => '{1}'.'هڪ سال'.'|:count '.'سال', + 'month' => '{1}'.'هڪ مهينو'.'|:count '.'مهينا', + 'week' => '{1}'.'ھڪ ھفتو'.'|:count '.'هفتا', + 'day' => '{1}'.'هڪ ڏينهن'.'|:count '.'ڏينهن', + 'hour' => '{1}'.'هڪ ڪلاڪ'.'|:count '.'ڪلاڪ', + 'minute' => '{1}'.'هڪ منٽ'.'|:count '.'منٽ', + 'second' => '{1}'.'چند سيڪنڊ'.'|:count '.'سيڪنڊ', + 'ago' => ':time اڳ', + 'from_now' => ':time پوء', + 'diff_yesterday' => 'ڪالهه', + 'diff_today' => 'اڄ', + 'diff_tomorrow' => 'سڀاڻي', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd، D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[اڄ] LT', + 'nextDay' => '[سڀاڻي] LT', + 'nextWeek' => 'dddd [اڳين هفتي تي] LT', + 'lastDay' => '[ڪالهه] LT', + 'lastWeek' => '[گزريل هفتي] dddd [تي] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['صبح', 'شام'], + 'months' => $months, + 'months_short' => $months, + 'weekdays' => $weekdays, + 'weekdays_short' => $weekdays, + 'weekdays_min' => $weekdays, + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => ['، ', ' ۽ '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sd_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/sd_IN.php new file mode 100644 index 0000000..de1dad0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sd_IN.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Red Hat, Pune bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/sd.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['جنوري', 'فبروري', 'مارچ', 'اپريل', 'مي', 'جون', 'جولاءِ', 'آگسٽ', 'سيپٽيمبر', 'آڪٽوبر', 'نومبر', 'ڊسمبر'], + 'months_short' => ['جنوري', 'فبروري', 'مارچ', 'اپريل', 'مي', 'جون', 'جولاءِ', 'آگسٽ', 'سيپٽيمبر', 'آڪٽوبر', 'نومبر', 'ڊسمبر'], + 'weekdays' => ['آرتوارُ', 'سومرُ', 'منگلُ', 'ٻُڌرُ', 'وسپت', 'جُمو', 'ڇنڇر'], + 'weekdays_short' => ['آرتوارُ', 'سومرُ', 'منگلُ', 'ٻُڌرُ', 'وسپت', 'جُمو', 'ڇنڇر'], + 'weekdays_min' => ['آرتوارُ', 'سومرُ', 'منگلُ', 'ٻُڌرُ', 'وسپت', 'جُمو', 'ڇنڇر'], + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sd_IN@devanagari.php b/vendor/nesbot/carbon/src/Carbon/Lang/sd_IN@devanagari.php new file mode 100644 index 0000000..061fcc1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sd_IN@devanagari.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Red Hat, Pune bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/sd.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['जनवरी', 'फबरवरी', 'मार्चि', 'अप्रेल', 'मे', 'जूनि', 'जूलाइ', 'आगस्टु', 'सेप्टेंबरू', 'आक्टूबरू', 'नवंबरू', 'ॾिसंबरू'], + 'months_short' => ['जनवरी', 'फबरवरी', 'मार्चि', 'अप्रेल', 'मे', 'जूनि', 'जूलाइ', 'आगस्टु', 'सेप्टेंबरू', 'आक्टूबरू', 'नवंबरू', 'ॾिसंबरू'], + 'weekdays' => ['आर्तवारू', 'सूमरू', 'मंगलू', 'ॿुधरू', 'विस्पति', 'जुमो', 'छंछस'], + 'weekdays_short' => ['आर्तवारू', 'सूमरू', 'मंगलू', 'ॿुधरू', 'विस्पति', 'जुमो', 'छंछस'], + 'weekdays_min' => ['आर्तवारू', 'सूमरू', 'मंगलू', 'ॿुधरू', 'विस्पति', 'जुमो', 'छंछस'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['म.पू.', 'म.नं.'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/se.php b/vendor/nesbot/carbon/src/Carbon/Lang/se.php new file mode 100644 index 0000000..7c4b92a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/se.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Karamell + */ +return [ + 'year' => '{1}:count jahki|:count jagit', + 'a_year' => '{1}okta jahki|:count jagit', + 'y' => ':count j.', + 'month' => '{1}:count mánnu|:count mánut', + 'a_month' => '{1}okta mánnu|:count mánut', + 'm' => ':count mán.', + 'week' => '{1}:count vahkku|:count vahkku', + 'a_week' => '{1}okta vahkku|:count vahkku', + 'w' => ':count v.', + 'day' => '{1}:count beaivi|:count beaivvit', + 'a_day' => '{1}okta beaivi|:count beaivvit', + 'd' => ':count b.', + 'hour' => '{1}:count diimmu|:count diimmut', + 'a_hour' => '{1}okta diimmu|:count diimmut', + 'h' => ':count d.', + 'minute' => '{1}:count minuhta|:count minuhtat', + 'a_minute' => '{1}okta minuhta|:count minuhtat', + 'min' => ':count min.', + 'second' => '{1}:count sekunddat|:count sekunddat', + 'a_second' => '{1}moadde sekunddat|:count sekunddat', + 's' => ':count s.', + 'ago' => 'maŋit :time', + 'from_now' => ':time geažes', + 'diff_yesterday' => 'ikte', + 'diff_yesterday_regexp' => 'ikte(?:\\s+ti)?', + 'diff_today' => 'otne', + 'diff_today_regexp' => 'otne(?:\\s+ti)?', + 'diff_tomorrow' => 'ihttin', + 'diff_tomorrow_regexp' => 'ihttin(?:\\s+ti)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'MMMM D. [b.] YYYY', + 'LLL' => 'MMMM D. [b.] YYYY [ti.] HH:mm', + 'LLLL' => 'dddd, MMMM D. [b.] YYYY [ti.] HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[otne ti] LT', + 'nextDay' => '[ihttin ti] LT', + 'nextWeek' => 'dddd [ti] LT', + 'lastDay' => '[ikte ti] LT', + 'lastWeek' => '[ovddit] dddd [ti] LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'months' => ['ođđajagemánnu', 'guovvamánnu', 'njukčamánnu', 'cuoŋománnu', 'miessemánnu', 'geassemánnu', 'suoidnemánnu', 'borgemánnu', 'čakčamánnu', 'golggotmánnu', 'skábmamánnu', 'juovlamánnu'], + 'months_short' => ['ođđj', 'guov', 'njuk', 'cuo', 'mies', 'geas', 'suoi', 'borg', 'čakč', 'golg', 'skáb', 'juov'], + 'weekdays' => ['sotnabeaivi', 'vuossárga', 'maŋŋebárga', 'gaskavahkku', 'duorastat', 'bearjadat', 'lávvardat'], + 'weekdays_short' => ['sotn', 'vuos', 'maŋ', 'gask', 'duor', 'bear', 'láv'], + 'weekdays_min' => ['s', 'v', 'm', 'g', 'd', 'b', 'L'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' ja '], + 'meridiem' => ['i.b.', 'e.b.'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/se_FI.php b/vendor/nesbot/carbon/src/Carbon/Lang/se_FI.php new file mode 100644 index 0000000..cf01805 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/se_FI.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/se.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'months' => ['ođđajagemánnu', 'guovvamánnu', 'njukčamánnu', 'cuoŋománnu', 'miessemánnu', 'geassemánnu', 'suoidnemánnu', 'borgemánnu', 'čakčamánnu', 'golggotmánnu', 'skábmamánnu', 'juovlamánnu'], + 'months_short' => ['ođđj', 'guov', 'njuk', 'cuoŋ', 'mies', 'geas', 'suoi', 'borg', 'čakč', 'golg', 'skáb', 'juov'], + 'weekdays' => ['sotnabeaivi', 'mánnodat', 'disdat', 'gaskavahkku', 'duorastat', 'bearjadat', 'lávvordat'], + 'weekdays_short' => ['so', 'má', 'di', 'ga', 'du', 'be', 'lá'], + 'weekdays_min' => ['so', 'má', 'di', 'ga', 'du', 'be', 'lá'], + 'meridiem' => ['i', 'e'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/se_NO.php b/vendor/nesbot/carbon/src/Carbon/Lang/se_NO.php new file mode 100644 index 0000000..177c7e9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/se_NO.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/se.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/se_SE.php b/vendor/nesbot/carbon/src/Carbon/Lang/se_SE.php new file mode 100644 index 0000000..177c7e9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/se_SE.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/se.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/seh.php b/vendor/nesbot/carbon/src/Carbon/Lang/seh.php new file mode 100644 index 0000000..babf9af --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/seh.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'weekdays' => ['Dimingu', 'Chiposi', 'Chipiri', 'Chitatu', 'Chinai', 'Chishanu', 'Sabudu'], + 'weekdays_short' => ['Dim', 'Pos', 'Pir', 'Tat', 'Nai', 'Sha', 'Sab'], + 'weekdays_min' => ['Dim', 'Pos', 'Pir', 'Tat', 'Nai', 'Sha', 'Sab'], + 'months' => ['Janeiro', 'Fevreiro', 'Marco', 'Abril', 'Maio', 'Junho', 'Julho', 'Augusto', 'Setembro', 'Otubro', 'Novembro', 'Decembro'], + 'months_short' => ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Aug', 'Set', 'Otu', 'Nov', 'Dec'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'd [de] MMM [de] YYYY', + 'LLL' => 'd [de] MMMM [de] YYYY HH:mm', + 'LLLL' => 'dddd, d [de] MMMM [de] YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ses.php b/vendor/nesbot/carbon/src/Carbon/Lang/ses.php new file mode 100644 index 0000000..e1099e6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ses.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Adduha', 'Aluula'], + 'weekdays' => ['Alhadi', 'Atinni', 'Atalaata', 'Alarba', 'Alhamiisa', 'Alzuma', 'Asibti'], + 'weekdays_short' => ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'], + 'weekdays_min' => ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'], + 'months' => ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me', 'Žuweŋ', 'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur', 'Deesanbur'], + 'months_short' => ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy', 'Ut', 'Sek', 'Okt', 'Noo', 'Dee'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + + 'month' => ':count alaada', // less reliable + 'm' => ':count alaada', // less reliable + 'a_month' => ':count alaada', // less reliable + + 'hour' => ':count ɲaajin', // less reliable + 'h' => ':count ɲaajin', // less reliable + 'a_hour' => ':count ɲaajin', // less reliable + + 'minute' => ':count zarbu', // less reliable + 'min' => ':count zarbu', // less reliable + 'a_minute' => ':count zarbu', // less reliable + + 'year' => ':count jiiri', + 'y' => ':count jiiri', + 'a_year' => ':count jiiri', + + 'week' => ':count jirbiiyye', + 'w' => ':count jirbiiyye', + 'a_week' => ':count jirbiiyye', + + 'day' => ':count zaari', + 'd' => ':count zaari', + 'a_day' => ':count zaari', + + 'second' => ':count ihinkante', + 's' => ':count ihinkante', + 'a_second' => ':count ihinkante', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sg.php b/vendor/nesbot/carbon/src/Carbon/Lang/sg.php new file mode 100644 index 0000000..9264e89 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sg.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['ND', 'LK'], + 'weekdays' => ['Bikua-ôko', 'Bïkua-ûse', 'Bïkua-ptâ', 'Bïkua-usïö', 'Bïkua-okü', 'Lâpôsö', 'Lâyenga'], + 'weekdays_short' => ['Bk1', 'Bk2', 'Bk3', 'Bk4', 'Bk5', 'Lâp', 'Lây'], + 'weekdays_min' => ['Bk1', 'Bk2', 'Bk3', 'Bk4', 'Bk5', 'Lâp', 'Lây'], + 'months' => ['Nyenye', 'Fulundïgi', 'Mbängü', 'Ngubùe', 'Bêläwü', 'Föndo', 'Lengua', 'Kükürü', 'Mvuka', 'Ngberere', 'Nabändüru', 'Kakauka'], + 'months_short' => ['Nye', 'Ful', 'Mbä', 'Ngu', 'Bêl', 'Fön', 'Len', 'Kük', 'Mvu', 'Ngb', 'Nab', 'Kak'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + + 'year' => ':count dā', // less reliable + 'y' => ':count dā', // less reliable + 'a_year' => ':count dā', // less reliable + + 'week' => ':count bïkua-okü', // less reliable + 'w' => ':count bïkua-okü', // less reliable + 'a_week' => ':count bïkua-okü', // less reliable + + 'day' => ':count ziggawâ', // less reliable + 'd' => ':count ziggawâ', // less reliable + 'a_day' => ':count ziggawâ', // less reliable + + 'hour' => ':count yângâködörö', // less reliable + 'h' => ':count yângâködörö', // less reliable + 'a_hour' => ':count yângâködörö', // less reliable + + 'second' => ':count bïkua-ôko', // less reliable + 's' => ':count bïkua-ôko', // less reliable + 'a_second' => ':count bïkua-ôko', // less reliable + + 'month' => ':count Nze tî ngu', + 'm' => ':count Nze tî ngu', + 'a_month' => ':count Nze tî ngu', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sgs.php b/vendor/nesbot/carbon/src/Carbon/Lang/sgs.php new file mode 100644 index 0000000..864b989 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sgs.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/sgs_LT.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sgs_LT.php b/vendor/nesbot/carbon/src/Carbon/Lang/sgs_LT.php new file mode 100644 index 0000000..aa9e942 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sgs_LT.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Arnas Udovičius bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'YYYY.MM.DD', + ], + 'months' => ['sausė', 'vasarė', 'kuova', 'balondė', 'gegožės', 'bėrželė', 'lëpas', 'rogpjūtė', 'siejės', 'spalė', 'lapkrėstė', 'grůdė'], + 'months_short' => ['Sau', 'Vas', 'Kuo', 'Bal', 'Geg', 'Bėr', 'Lëp', 'Rgp', 'Sie', 'Spa', 'Lap', 'Grd'], + 'weekdays' => ['nedielės dëna', 'panedielis', 'oterninks', 'sereda', 'četvergs', 'petnīčė', 'sobata'], + 'weekdays_short' => ['Nd', 'Pn', 'Ot', 'Sr', 'Čt', 'Pt', 'Sb'], + 'weekdays_min' => ['Nd', 'Pn', 'Ot', 'Sr', 'Čt', 'Pt', 'Sb'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'minute' => ':count mažos', // less reliable + 'min' => ':count mažos', // less reliable + 'a_minute' => ':count mažos', // less reliable + + 'year' => ':count metā', + 'y' => ':count metā', + 'a_year' => ':count metā', + + 'month' => ':count mienou', + 'm' => ':count mienou', + 'a_month' => ':count mienou', + + 'week' => ':count nedielė', + 'w' => ':count nedielė', + 'a_week' => ':count nedielė', + + 'day' => ':count dīna', + 'd' => ':count dīna', + 'a_day' => ':count dīna', + + 'hour' => ':count adīna', + 'h' => ':count adīna', + 'a_hour' => ':count adīna', + + 'second' => ':count Sekondė', + 's' => ':count Sekondė', + 'a_second' => ':count Sekondė', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sh.php b/vendor/nesbot/carbon/src/Carbon/Lang/sh.php new file mode 100644 index 0000000..e03b506 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sh.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +// @codeCoverageIgnoreStart +use Symfony\Component\Translation\PluralizationRules; + +if (class_exists('Symfony\\Component\\Translation\\PluralizationRules')) { + PluralizationRules::set(static function ($number) { + return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); + }, 'sh'); +} +// @codeCoverageIgnoreEnd + +/* + * Authors: + * - Томица Кораћ + * - Enrique Vidal + * - Christopher Dell + * - dmilisic + * - danijel + * - Miroslav Matkovic (mikki021) + */ +return [ + 'diff_now' => 'sada', + 'diff_yesterday' => 'juče', + 'diff_tomorrow' => 'sutra', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'MMMM D, YYYY', + 'LLL' => 'DD MMM HH:mm', + 'LLLL' => 'MMMM DD, YYYY HH:mm', + ], + 'year' => ':count godina|:count godine|:count godina', + 'y' => ':count g.', + 'month' => ':count mesec|:count meseca|:count meseci', + 'm' => ':count m.', + 'week' => ':count nedelja|:count nedelje|:count nedelja', + 'w' => ':count n.', + 'day' => ':count dan|:count dana|:count dana', + 'd' => ':count d.', + 'hour' => ':count sat|:count sata|:count sati', + 'h' => ':count č.', + 'minute' => ':count minut|:count minuta|:count minuta', + 'min' => ':count min.', + 'second' => ':count sekund|:count sekunde|:count sekundi', + 's' => ':count s.', + 'ago' => 'pre :time', + 'from_now' => 'za :time', + 'after' => 'nakon :time', + 'before' => ':time raniјe', + 'weekdays' => ['Nedelja', 'Ponedeljak', 'Utorak', 'Sreda', 'Četvrtak', 'Petak', 'Subota'], + 'weekdays_short' => ['Ned', 'Pon', 'Uto', 'Sre', 'Čet', 'Pet', 'Sub'], + 'weekdays_min' => ['Ned', 'Pon', 'Uto', 'Sre', 'Čet', 'Pet', 'Sub'], + 'months' => ['Januar', 'Februar', 'Mart', 'April', 'Maj', 'Jun', 'Jul', 'Avgust', 'Septembar', 'Oktobar', 'Novembar', 'Decembar'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Avg', 'Sep', 'Okt', 'Nov', 'Dec'], + 'list' => [', ', ' i '], + 'meridiem' => ['pre podne', 'po podne'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shi.php b/vendor/nesbot/carbon/src/Carbon/Lang/shi.php new file mode 100644 index 0000000..7815186 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/shi.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['ⵜⵉⴼⴰⵡⵜ', 'ⵜⴰⴷⴳⴳⵯⴰⵜ'], + 'weekdays' => ['ⴰⵙⴰⵎⴰⵙ', 'ⴰⵢⵏⴰⵙ', 'ⴰⵙⵉⵏⴰⵙ', 'ⴰⴽⵕⴰⵙ', 'ⴰⴽⵡⴰⵙ', 'ⵙⵉⵎⵡⴰⵙ', 'ⴰⵙⵉⴹⵢⴰⵙ'], + 'weekdays_short' => ['ⴰⵙⴰ', 'ⴰⵢⵏ', 'ⴰⵙⵉ', 'ⴰⴽⵕ', 'ⴰⴽⵡ', 'ⴰⵙⵉⵎ', 'ⴰⵙⵉⴹ'], + 'weekdays_min' => ['ⴰⵙⴰ', 'ⴰⵢⵏ', 'ⴰⵙⵉ', 'ⴰⴽⵕ', 'ⴰⴽⵡ', 'ⴰⵙⵉⵎ', 'ⴰⵙⵉⴹ'], + 'months' => ['ⵉⵏⵏⴰⵢⵔ', 'ⴱⵕⴰⵢⵕ', 'ⵎⴰⵕⵚ', 'ⵉⴱⵔⵉⵔ', 'ⵎⴰⵢⵢⵓ', 'ⵢⵓⵏⵢⵓ', 'ⵢⵓⵍⵢⵓⵣ', 'ⵖⵓⵛⵜ', 'ⵛⵓⵜⴰⵏⴱⵉⵔ', 'ⴽⵜⵓⴱⵔ', 'ⵏⵓⵡⴰⵏⴱⵉⵔ', 'ⴷⵓⵊⴰⵏⴱⵉⵔ'], + 'months_short' => ['ⵉⵏⵏ', 'ⴱⵕⴰ', 'ⵎⴰⵕ', 'ⵉⴱⵔ', 'ⵎⴰⵢ', 'ⵢⵓⵏ', 'ⵢⵓⵍ', 'ⵖⵓⵛ', 'ⵛⵓⵜ', 'ⴽⵜⵓ', 'ⵏⵓⵡ', 'ⴷⵓⵊ'], + 'first_day_of_week' => 6, + 'weekend' => [5, 6], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + + 'year' => ':count aseggwas', + 'y' => ':count aseggwas', + 'a_year' => ':count aseggwas', + + 'month' => ':count ayyur', + 'm' => ':count ayyur', + 'a_month' => ':count ayyur', + + 'week' => ':count imalass', + 'w' => ':count imalass', + 'a_week' => ':count imalass', + + 'day' => ':count ass', + 'd' => ':count ass', + 'a_day' => ':count ass', + + 'hour' => ':count urɣ', // less reliable + 'h' => ':count urɣ', // less reliable + 'a_hour' => ':count urɣ', // less reliable + + 'minute' => ':count ⴰⵎⵥⵉ', // less reliable + 'min' => ':count ⴰⵎⵥⵉ', // less reliable + 'a_minute' => ':count ⴰⵎⵥⵉ', // less reliable + + 'second' => ':count sin', // less reliable + 's' => ':count sin', // less reliable + 'a_second' => ':count sin', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shi_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/shi_Latn.php new file mode 100644 index 0000000..cddfb24 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/shi_Latn.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/shi.php', [ + 'meridiem' => ['tifawt', 'tadggʷat'], + 'weekdays' => ['asamas', 'aynas', 'asinas', 'akṛas', 'akwas', 'asimwas', 'asiḍyas'], + 'weekdays_short' => ['asa', 'ayn', 'asi', 'akṛ', 'akw', 'asim', 'asiḍ'], + 'weekdays_min' => ['asa', 'ayn', 'asi', 'akṛ', 'akw', 'asim', 'asiḍ'], + 'months' => ['innayr', 'bṛayṛ', 'maṛṣ', 'ibrir', 'mayyu', 'yunyu', 'yulyuz', 'ɣuct', 'cutanbir', 'ktubr', 'nuwanbir', 'dujanbir'], + 'months_short' => ['inn', 'bṛa', 'maṛ', 'ibr', 'may', 'yun', 'yul', 'ɣuc', 'cut', 'ktu', 'nuw', 'duj'], + 'first_day_of_week' => 6, + 'weekend' => [5, 6], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + + 'minute' => ':count agur', // less reliable + 'min' => ':count agur', // less reliable + 'a_minute' => ':count agur', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shi_Tfng.php b/vendor/nesbot/carbon/src/Carbon/Lang/shi_Tfng.php new file mode 100644 index 0000000..f3df1f2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/shi_Tfng.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/shi.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shn.php b/vendor/nesbot/carbon/src/Carbon/Lang/shn.php new file mode 100644 index 0000000..fe7b1ea --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/shn.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/shn_MM.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shn_MM.php b/vendor/nesbot/carbon/src/Carbon/Lang/shn_MM.php new file mode 100644 index 0000000..f399acf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/shn_MM.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - ubuntu Myanmar LoCo Team https://ubuntu-mm.net Bone Pyae Sone bone.burma@mail.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'OY MMM OD dddd', + ], + 'months' => ['လိူၼ်ၵမ်', 'လိူၼ်သၢမ်', 'လိူၼ်သီ', 'လိူၼ်ႁႃႈ', 'လိူၼ်ႁူၵ်း', 'လိူၼ်ၸဵတ်း', 'လိူၼ်ပႅတ်ႇ', 'လိူၼ်ၵဝ်ႈ', 'လိူၼ်သိပ်း', 'လိူၼ်သိပ်းဢိတ်း', 'လိူၼ်သိပ်းဢိတ်းသွင်', 'လိူၼ်ၸဵင်'], + 'months_short' => ['လိူၼ်ၵမ်', 'လိူၼ်သၢမ်', 'လိူၼ်သီ', 'လိူၼ်ႁႃႈ', 'လိူၼ်ႁူၵ်း', 'လိူၼ်ၸဵတ်း', 'လိူၼ်ပႅတ်ႇ', 'လိူၼ်ၵဝ်ႈ', 'လိူၼ်သိပ်း', 'လိူၼ်သိပ်းဢိတ်း', 'လိူၼ်သိပ်းဢိတ်းသွင်', 'လိူၼ်ၸဵင်'], + 'weekdays' => ['ဝၼ်းဢႃးတိတ်ႉ', 'ဝၼ်းၸၼ်', 'ဝၼ်း​ဢၢင်း​ၵၢၼ်း', 'ဝၼ်းပူတ်ႉ', 'ဝၼ်းၽတ်း', 'ဝၼ်းသုၵ်း', 'ဝၼ်းသဝ်'], + 'weekdays_short' => ['တိတ့်', 'ၸၼ်', 'ၵၢၼ်း', 'ပုတ့်', 'ၽတ်း', 'သုၵ်း', 'သဝ်'], + 'weekdays_min' => ['တိတ့်', 'ၸၼ်', 'ၵၢၼ်း', 'ပုတ့်', 'ၽတ်း', 'သုၵ်း', 'သဝ်'], + 'alt_numbers' => ['႐႐', '႐႑', '႐႒', '႐႓', '႐႔', '႐႕', '႐႖', '႐႗', '႐႘', '႐႙', '႑႐', '႑႑', '႑႒', '႑႓', '႑႔', '႑႕', '႑႖', '႑႗', '႑႘', '႑႙', '႒႐', '႒႑', '႒႒', '႒႓', '႒႔', '႒႕', '႒႖', '႒႗', '႒႘', '႒႙', '႓႐', '႓႑', '႓႒', '႓႓', '႓႔', '႓႕', '႓႖', '႓႗', '႓႘', '႓႙', '႔႐', '႔႑', '႔႒', '႔႓', '႔႔', '႔႕', '႔႖', '႔႗', '႔႘', '႔႙', '႕႐', '႕႑', '႕႒', '႕႓', '႕႔', '႕႕', '႕႖', '႕႗', '႕႘', '႕႙', '႖႐', '႖႑', '႖႒', '႖႓', '႖႔', '႖႕', '႖႖', '႖႗', '႖႘', '႖႙', '႗႐', '႗႑', '႗႒', '႗႓', '႗႔', '႗႕', '႗႖', '႗႗', '႗႘', '႗႙', '႘႐', '႘႑', '႘႒', '႘႓', '႘႔', '႘႕', '႘႖', '႘႗', '႘႘', '႘႙', '႙႐', '႙႑', '႙႒', '႙႓', '႙႔', '႙႕', '႙႖', '႙႗', '႙႘', '႙႙'], + 'meridiem' => ['ၵၢင်ၼႂ်', 'တၢမ်းၶမ်ႈ'], + + 'month' => ':count လိူၼ်', // less reliable + 'm' => ':count လိူၼ်', // less reliable + 'a_month' => ':count လိူၼ်', // less reliable + + 'week' => ':count ဝၼ်း', // less reliable + 'w' => ':count ဝၼ်း', // less reliable + 'a_week' => ':count ဝၼ်း', // less reliable + + 'hour' => ':count ຕີ', // less reliable + 'h' => ':count ຕີ', // less reliable + 'a_hour' => ':count ຕີ', // less reliable + + 'minute' => ':count ເດັກ', // less reliable + 'min' => ':count ເດັກ', // less reliable + 'a_minute' => ':count ເດັກ', // less reliable + + 'second' => ':count ဢိုၼ်ႇ', // less reliable + 's' => ':count ဢိုၼ်ႇ', // less reliable + 'a_second' => ':count ဢိုၼ်ႇ', // less reliable + + 'year' => ':count ပီ', + 'y' => ':count ပီ', + 'a_year' => ':count ပီ', + + 'day' => ':count ກາງວັນ', + 'd' => ':count ກາງວັນ', + 'a_day' => ':count ກາງວັນ', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shs.php b/vendor/nesbot/carbon/src/Carbon/Lang/shs.php new file mode 100644 index 0000000..8d2e1d7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/shs.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/shs_CA.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shs_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/shs_CA.php new file mode 100644 index 0000000..08d385e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/shs_CA.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Neskie Manuel bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['Pellkwet̓min', 'Pelctsipwen̓ten', 'Pellsqépts', 'Peslléwten', 'Pell7ell7é7llqten', 'Pelltspéntsk', 'Pelltqwelq̓wél̓t', 'Pellct̓éxel̓cten', 'Pesqelqlélten', 'Pesllwélsten', 'Pellc7ell7é7llcwten̓', 'Pelltetétq̓em'], + 'months_short' => ['Kwe', 'Tsi', 'Sqe', 'Éwt', 'Ell', 'Tsp', 'Tqw', 'Ct̓é', 'Qel', 'Wél', 'U7l', 'Tet'], + 'weekdays' => ['Sxetspesq̓t', 'Spetkesq̓t', 'Selesq̓t', 'Skellesq̓t', 'Smesesq̓t', 'Stselkstesq̓t', 'Stqmekstesq̓t'], + 'weekdays_short' => ['Sxe', 'Spe', 'Sel', 'Ske', 'Sme', 'Sts', 'Stq'], + 'weekdays_min' => ['Sxe', 'Spe', 'Sel', 'Ske', 'Sme', 'Sts', 'Stq'], + 'day_of_first_week_of_year' => 1, + + 'year' => ':count sqlélten', // less reliable + 'y' => ':count sqlélten', // less reliable + 'a_year' => ':count sqlélten', // less reliable + + 'month' => ':count swewll', // less reliable + 'm' => ':count swewll', // less reliable + 'a_month' => ':count swewll', // less reliable + + 'hour' => ':count seqwlút', // less reliable + 'h' => ':count seqwlút', // less reliable + 'a_hour' => ':count seqwlút', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/si.php b/vendor/nesbot/carbon/src/Carbon/Lang/si.php new file mode 100644 index 0000000..636bf69 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/si.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Serhan Apaydın + * - JD Isaacks + * - Malinda Weerasinghe (MalindaWMD) + */ +return [ + 'year' => '{1}වසර 1|වසර :count', + 'a_year' => '{1}වසරක්|වසර :count', + 'month' => '{1}මාස 1|මාස :count', + 'a_month' => '{1}මාසය|මාස :count', + 'week' => '{1}සති 1|සති :count', + 'a_week' => '{1}සතියක්|සති :count', + 'day' => '{1}දින 1|දින :count', + 'a_day' => '{1}දිනක්|දින :count', + 'hour' => '{1}පැය 1|පැය :count', + 'a_hour' => '{1}පැයක්|පැය :count', + 'minute' => '{1}මිනිත්තු 1|මිනිත්තු :count', + 'a_minute' => '{1}මිනිත්තුවක්|මිනිත්තු :count', + 'second' => '{1}තත්පර 1|තත්පර :count', + 'a_second' => '{1}තත්පර කිහිපයකට|තත්පර :count', + 'ago' => ':time කට පෙර', + 'from_now' => function ($time) { + if (preg_match('/දින \d/u', $time)) { + return $time.' න්'; + } + + return $time.' කින්'; + }, + 'before' => ':time කට පෙර', + 'after' => function ($time) { + if (preg_match('/දින \d/u', $time)) { + return $time.' න්'; + } + + return $time.' කින්'; + }, + 'diff_now' => 'දැන්', + 'diff_today' => 'අද', + 'diff_yesterday' => 'ඊයේ', + 'diff_tomorrow' => 'හෙට', + 'formats' => [ + 'LT' => 'a h:mm', + 'LTS' => 'a h:mm:ss', + 'L' => 'YYYY/MM/DD', + 'LL' => 'YYYY MMMM D', + 'LLL' => 'YYYY MMMM D, a h:mm', + 'LLLL' => 'YYYY MMMM D [වැනි] dddd, a h:mm:ss', + ], + 'calendar' => [ + 'sameDay' => '[අද] LT[ට]', + 'nextDay' => '[හෙට] LT[ට]', + 'nextWeek' => 'dddd LT[ට]', + 'lastDay' => '[ඊයේ] LT[ට]', + 'lastWeek' => '[පසුගිය] dddd LT[ට]', + 'sameElse' => 'L', + ], + 'ordinal' => ':number වැනි', + 'meridiem' => ['පෙර වරු', 'පස් වරු', 'පෙ.ව.', 'ප.ව.'], + 'months' => ['ජනවාරි', 'පෙබරවාරි', 'මාර්තු', 'අප්‍රේල්', 'මැයි', 'ජූනි', 'ජූලි', 'අගෝස්තු', 'සැප්තැම්බර්', 'ඔක්තෝබර්', 'නොවැම්බර්', 'දෙසැම්බර්'], + 'months_short' => ['ජන', 'පෙබ', 'මාර්', 'අප්', 'මැයි', 'ජූනි', 'ජූලි', 'අගෝ', 'සැප්', 'ඔක්', 'නොවැ', 'දෙසැ'], + 'weekdays' => ['ඉරිදා', 'සඳුදා', 'අඟහරුවාදා', 'බදාදා', 'බ්‍රහස්පතින්දා', 'සිකුරාදා', 'සෙනසුරාදා'], + 'weekdays_short' => ['ඉරි', 'සඳු', 'අඟ', 'බදා', 'බ්‍රහ', 'සිකු', 'සෙන'], + 'weekdays_min' => ['ඉ', 'ස', 'අ', 'බ', 'බ්‍ර', 'සි', 'සෙ'], + 'first_day_of_week' => 1, +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/si_LK.php b/vendor/nesbot/carbon/src/Carbon/Lang/si_LK.php new file mode 100644 index 0000000..81c44e0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/si_LK.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/si.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sid.php b/vendor/nesbot/carbon/src/Carbon/Lang/sid.php new file mode 100644 index 0000000..b1c6521 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sid.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/sid_ET.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sid_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/sid_ET.php new file mode 100644 index 0000000..1296f9b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sid_ET.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + 'weekdays' => ['Sambata', 'Sanyo', 'Maakisanyo', 'Roowe', 'Hamuse', 'Arbe', 'Qidaame'], + 'weekdays_short' => ['Sam', 'San', 'Mak', 'Row', 'Ham', 'Arb', 'Qid'], + 'weekdays_min' => ['Sam', 'San', 'Mak', 'Row', 'Ham', 'Arb', 'Qid'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['soodo', 'hawwaro'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sk.php b/vendor/nesbot/carbon/src/Carbon/Lang/sk.php new file mode 100644 index 0000000..f9702e9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sk.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Philippe Vaucher + * - Martin Suja + * - Tsutomu Kuroda + * - tjku + * - Max Melentiev + * - Juanito Fatas + * - Ivan Stana + * - Akira Matsuda + * - Christopher Dell + * - James McKinney + * - Enrique Vidal + * - Simone Carletti + * - Aaron Patterson + * - Jozef Fulop + * - Nicolás Hock Isaza + * - Tom Hughes + * - Simon Hürlimann (CyT) + * - jofi + * - Jakub ADAMEC + * - Marek Adamický + * - AlterwebStudio + */ + +use Carbon\CarbonInterface; + +$fromNow = function ($time) { + return 'o '.strtr($time, [ + 'hodina' => 'hodinu', + 'minúta' => 'minútu', + 'sekunda' => 'sekundu', + ]); +}; + +$ago = function ($time) { + $replacements = [ + '/\bhodina\b/' => 'hodinou', + '/\bminúta\b/' => 'minútou', + '/\bsekunda\b/' => 'sekundou', + '/\bdeň\b/u' => 'dňom', + '/\btýždeň\b/u' => 'týždňom', + '/\bmesiac\b/' => 'mesiacom', + '/\brok\b/' => 'rokom', + ]; + + $replacementsPlural = [ + '/\bhodiny\b/' => 'hodinami', + '/\bminúty\b/' => 'minútami', + '/\bsekundy\b/' => 'sekundami', + '/\bdni\b/' => 'dňami', + '/\btýždne\b/' => 'týždňami', + '/\bmesiace\b/' => 'mesiacmi', + '/\broky\b/' => 'rokmi', + ]; + + foreach ($replacements + $replacementsPlural as $pattern => $replacement) { + $time = preg_replace($pattern, $replacement, $time); + } + + return "pred $time"; +}; + +return [ + 'year' => ':count rok|:count roky|:count rokov', + 'a_year' => 'rok|:count roky|:count rokov', + 'y' => ':count r', + 'month' => ':count mesiac|:count mesiace|:count mesiacov', + 'a_month' => 'mesiac|:count mesiace|:count mesiacov', + 'm' => ':count m', + 'week' => ':count týždeň|:count týždne|:count týždňov', + 'a_week' => 'týždeň|:count týždne|:count týždňov', + 'w' => ':count t', + 'day' => ':count deň|:count dni|:count dní', + 'a_day' => 'deň|:count dni|:count dní', + 'd' => ':count d', + 'hour' => ':count hodina|:count hodiny|:count hodín', + 'a_hour' => 'hodina|:count hodiny|:count hodín', + 'h' => ':count h', + 'minute' => ':count minúta|:count minúty|:count minút', + 'a_minute' => 'minúta|:count minúty|:count minút', + 'min' => ':count min', + 'second' => ':count sekunda|:count sekundy|:count sekúnd', + 'a_second' => 'sekunda|:count sekundy|:count sekúnd', + 's' => ':count s', + 'millisecond' => ':count milisekunda|:count milisekundy|:count milisekúnd', + 'a_millisecond' => 'milisekunda|:count milisekundy|:count milisekúnd', + 'ms' => ':count ms', + 'microsecond' => ':count mikrosekunda|:count mikrosekundy|:count mikrosekúnd', + 'a_microsecond' => 'mikrosekunda|:count mikrosekundy|:count mikrosekúnd', + 'µs' => ':count µs', + + 'ago' => $ago, + 'from_now' => $fromNow, + 'before' => ':time pred', + 'after' => ':time po', + + 'hour_after' => ':count hodinu|:count hodiny|:count hodín', + 'minute_after' => ':count minútu|:count minúty|:count minút', + 'second_after' => ':count sekundu|:count sekundy|:count sekúnd', + + 'hour_before' => ':count hodinu|:count hodiny|:count hodín', + 'minute_before' => ':count minútu|:count minúty|:count minút', + 'second_before' => ':count sekundu|:count sekundy|:count sekúnd', + + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' a '], + 'diff_now' => 'teraz', + 'diff_yesterday' => 'včera', + 'diff_tomorrow' => 'zajtra', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'DD. MMMM YYYY', + 'LLL' => 'D. M. HH:mm', + 'LLLL' => 'dddd D. MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[dnes o] LT', + 'nextDay' => '[zajtra o] LT', + 'lastDay' => '[včera o] LT', + 'nextWeek' => 'dddd [o] LT', + 'lastWeek' => static function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 1: + case 2: + case 4: + case 5: + return '[minulý] dddd [o] LT'; //pondelok/utorok/štvrtok/piatok + default: + return '[minulá] dddd [o] LT'; + } + }, + 'sameElse' => 'L', + ], + 'weekdays' => ['nedeľa', 'pondelok', 'utorok', 'streda', 'štvrtok', 'piatok', 'sobota'], + 'weekdays_short' => ['ned', 'pon', 'uto', 'str', 'štv', 'pia', 'sob'], + 'weekdays_min' => ['ne', 'po', 'ut', 'st', 'št', 'pi', 'so'], + 'months' => ['január', 'február', 'marec', 'apríl', 'máj', 'jún', 'júl', 'august', 'september', 'október', 'november', 'december'], + 'months_short' => ['jan', 'feb', 'mar', 'apr', 'máj', 'jún', 'júl', 'aug', 'sep', 'okt', 'nov', 'dec'], + 'meridiem' => ['dopoludnia', 'popoludní'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sk_SK.php b/vendor/nesbot/carbon/src/Carbon/Lang/sk_SK.php new file mode 100644 index 0000000..0515601 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sk_SK.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/sk.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sl.php b/vendor/nesbot/carbon/src/Carbon/Lang/sl.php new file mode 100644 index 0000000..1f1d1b3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sl.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Philippe Vaucher + * - Tsutomu Kuroda + * - tjku + * - Max Melentiev + * - Juanito Fatas + * - Akira Matsuda + * - Christopher Dell + * - Enrique Vidal + * - Simone Carletti + * - Aaron Patterson + * - Nicolás Hock Isaza + * - Miha Rebernik + * - Gal Jakič (morpheus7CS) + * - Glavić + * - Anže Časar + * - Lovro Tramšek (Lovro1107) + * - burut13 + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count leto|:count leti|:count leta|:count let', + 'y' => ':count leto|:count leti|:count leta|:count let', + 'month' => ':count mesec|:count meseca|:count mesece|:count mesecev', + 'm' => ':count mes.', + 'week' => ':count teden|:count tedna|:count tedne|:count tednov', + 'w' => ':count ted.', + 'day' => ':count dan|:count dni|:count dni|:count dni', + 'd' => ':count dan|:count dni|:count dni|:count dni', + 'hour' => ':count ura|:count uri|:count ure|:count ur', + 'h' => ':count h', + 'minute' => ':count minuta|:count minuti|:count minute|:count minut', + 'min' => ':count min.', + 'second' => ':count sekunda|:count sekundi|:count sekunde|:count sekund', + 'a_second' => '{1}nekaj sekund|:count sekunda|:count sekundi|:count sekunde|:count sekund', + 's' => ':count s', + + 'year_ago' => ':count letom|:count letoma|:count leti|:count leti', + 'y_ago' => ':count letom|:count letoma|:count leti|:count leti', + 'month_ago' => ':count mesecem|:count mesecema|:count meseci|:count meseci', + 'week_ago' => ':count tednom|:count tednoma|:count tedni|:count tedni', + 'day_ago' => ':count dnem|:count dnevoma|:count dnevi|:count dnevi', + 'd_ago' => ':count dnem|:count dnevoma|:count dnevi|:count dnevi', + 'hour_ago' => ':count uro|:count urama|:count urami|:count urami', + 'minute_ago' => ':count minuto|:count minutama|:count minutami|:count minutami', + 'second_ago' => ':count sekundo|:count sekundama|:count sekundami|:count sekundami', + + 'day_from_now' => ':count dan|:count dneva|:count dni|:count dni', + 'd_from_now' => ':count dan|:count dneva|:count dni|:count dni', + 'hour_from_now' => ':count uro|:count uri|:count ure|:count ur', + 'minute_from_now' => ':count minuto|:count minuti|:count minute|:count minut', + 'second_from_now' => ':count sekundo|:count sekundi|:count sekunde|:count sekund', + + 'ago' => 'pred :time', + 'from_now' => 'čez :time', + 'after' => ':time kasneje', + 'before' => ':time prej', + + 'diff_now' => 'ravnokar', + 'diff_today' => 'danes', + 'diff_today_regexp' => 'danes(?:\\s+ob)?', + 'diff_yesterday' => 'včeraj', + 'diff_yesterday_regexp' => 'včeraj(?:\\s+ob)?', + 'diff_tomorrow' => 'jutri', + 'diff_tomorrow_regexp' => 'jutri(?:\\s+ob)?', + 'diff_before_yesterday' => 'predvčerajšnjim', + 'diff_after_tomorrow' => 'pojutrišnjem', + + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'period_start_date' => 'od :date', + 'period_end_date' => 'do :date', + + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY H:mm', + 'LLLL' => 'dddd, D. MMMM YYYY H:mm', + ], + 'calendar' => [ + 'sameDay' => '[danes ob] LT', + 'nextDay' => '[jutri ob] LT', + 'nextWeek' => 'dddd [ob] LT', + 'lastDay' => '[včeraj ob] LT', + 'lastWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[preteklo] [nedeljo] [ob] LT'; + case 1: + return '[pretekli] [ponedeljek] [ob] LT'; + case 2: + return '[pretekli] [torek] [ob] LT'; + case 3: + return '[preteklo] [sredo] [ob] LT'; + case 4: + return '[pretekli] [četrtek] [ob] LT'; + case 5: + return '[pretekli] [petek] [ob] LT'; + case 6: + return '[preteklo] [soboto] [ob] LT'; + } + }, + 'sameElse' => 'L', + ], + 'months' => ['januar', 'februar', 'marec', 'april', 'maj', 'junij', 'julij', 'avgust', 'september', 'oktober', 'november', 'december'], + 'months_short' => ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'avg', 'sep', 'okt', 'nov', 'dec'], + 'weekdays' => ['nedelja', 'ponedeljek', 'torek', 'sreda', 'četrtek', 'petek', 'sobota'], + 'weekdays_short' => ['ned', 'pon', 'tor', 'sre', 'čet', 'pet', 'sob'], + 'weekdays_min' => ['ne', 'po', 'to', 'sr', 'če', 'pe', 'so'], + 'list' => [', ', ' in '], + 'meridiem' => ['dopoldan', 'popoldan'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sl_SI.php b/vendor/nesbot/carbon/src/Carbon/Lang/sl_SI.php new file mode 100644 index 0000000..5dad8c8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sl_SI.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/sl.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sm.php b/vendor/nesbot/carbon/src/Carbon/Lang/sm.php new file mode 100644 index 0000000..e8c118a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sm.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/sm_WS.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sm_WS.php b/vendor/nesbot/carbon/src/Carbon/Lang/sm_WS.php new file mode 100644 index 0000000..f066068 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sm_WS.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Ianuari', 'Fepuari', 'Mati', 'Aperila', 'Me', 'Iuni', 'Iulai', 'Auguso', 'Setema', 'Oketopa', 'Novema', 'Tesema'], + 'months_short' => ['Ian', 'Fep', 'Mat', 'Ape', 'Me', 'Iun', 'Iul', 'Aug', 'Set', 'Oke', 'Nov', 'Tes'], + 'weekdays' => ['Aso Sa', 'Aso Gafua', 'Aso Lua', 'Aso Lulu', 'Aso Tofi', 'Aso Farail', 'Aso To\'ana\'i'], + 'weekdays_short' => ['Aso Sa', 'Aso Gaf', 'Aso Lua', 'Aso Lul', 'Aso Tof', 'Aso Far', 'Aso To\''], + 'weekdays_min' => ['Aso Sa', 'Aso Gaf', 'Aso Lua', 'Aso Lul', 'Aso Tof', 'Aso Far', 'Aso To\''], + + 'hour' => ':count uati', // less reliable + 'h' => ':count uati', // less reliable + 'a_hour' => ':count uati', // less reliable + + 'minute' => ':count itiiti', // less reliable + 'min' => ':count itiiti', // less reliable + 'a_minute' => ':count itiiti', // less reliable + + 'second' => ':count lua', // less reliable + 's' => ':count lua', // less reliable + 'a_second' => ':count lua', // less reliable + + 'year' => ':count tausaga', + 'y' => ':count tausaga', + 'a_year' => ':count tausaga', + + 'month' => ':count māsina', + 'm' => ':count māsina', + 'a_month' => ':count māsina', + + 'week' => ':count vaiaso', + 'w' => ':count vaiaso', + 'a_week' => ':count vaiaso', + + 'day' => ':count aso', + 'd' => ':count aso', + 'a_day' => ':count aso', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/smn.php b/vendor/nesbot/carbon/src/Carbon/Lang/smn.php new file mode 100644 index 0000000..20add02 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/smn.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['ip.', 'ep.'], + 'weekdays' => ['pasepeeivi', 'vuossaargâ', 'majebaargâ', 'koskoho', 'tuorâstuv', 'vástuppeeivi', 'lávurduv'], + 'weekdays_short' => ['pas', 'vuo', 'maj', 'kos', 'tuo', 'vás', 'láv'], + 'weekdays_min' => ['pa', 'vu', 'ma', 'ko', 'tu', 'vá', 'lá'], + 'weekdays_standalone' => ['pasepeivi', 'vuossargâ', 'majebargâ', 'koskokko', 'tuorâstâh', 'vástuppeivi', 'lávurdâh'], + 'months' => ['uđđâivemáánu', 'kuovâmáánu', 'njuhčâmáánu', 'cuáŋuimáánu', 'vyesimáánu', 'kesimáánu', 'syeinimáánu', 'porgemáánu', 'čohčâmáánu', 'roovvâdmáánu', 'skammâmáánu', 'juovlâmáánu'], + 'months_short' => ['uđiv', 'kuovâ', 'njuhčâ', 'cuáŋui', 'vyesi', 'kesi', 'syeini', 'porge', 'čohčâ', 'roovvâd', 'skammâ', 'juovlâ'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'H.mm', + 'LTS' => 'H.mm.ss', + 'L' => 'D.M.YYYY', + 'LL' => 'MMM D. YYYY', + 'LLL' => 'MMMM D. YYYY H.mm', + 'LLLL' => 'dddd, MMMM D. YYYY H.mm', + ], + + 'hour' => ':count äigi', // less reliable + 'h' => ':count äigi', // less reliable + 'a_hour' => ':count äigi', // less reliable + + 'year' => ':count ihe', + 'y' => ':count ihe', + 'a_year' => ':count ihe', + + 'month' => ':count mánuppaje', + 'm' => ':count mánuppaje', + 'a_month' => ':count mánuppaje', + + 'week' => ':count okko', + 'w' => ':count okko', + 'a_week' => ':count okko', + + 'day' => ':count peivi', + 'd' => ':count peivi', + 'a_day' => ':count peivi', + + 'minute' => ':count miinut', + 'min' => ':count miinut', + 'a_minute' => ':count miinut', + + 'second' => ':count nubbe', + 's' => ':count nubbe', + 'a_second' => ':count nubbe', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sn.php b/vendor/nesbot/carbon/src/Carbon/Lang/sn.php new file mode 100644 index 0000000..4f25028 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sn.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['a', 'p'], + 'weekdays' => ['Svondo', 'Muvhuro', 'Chipiri', 'Chitatu', 'China', 'Chishanu', 'Mugovera'], + 'weekdays_short' => ['Svo', 'Muv', 'Chp', 'Cht', 'Chn', 'Chs', 'Mug'], + 'weekdays_min' => ['Sv', 'Mu', 'Cp', 'Ct', 'Cn', 'Cs', 'Mg'], + 'months' => ['Ndira', 'Kukadzi', 'Kurume', 'Kubvumbi', 'Chivabvu', 'Chikumi', 'Chikunguru', 'Nyamavhuvhu', 'Gunyana', 'Gumiguru', 'Mbudzi', 'Zvita'], + 'months_short' => ['Ndi', 'Kuk', 'Kur', 'Kub', 'Chv', 'Chk', 'Chg', 'Nya', 'Gun', 'Gum', 'Mbu', 'Zvi'], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-dd', + 'LL' => 'YYYY MMM D', + 'LLL' => 'YYYY MMMM D HH:mm', + 'LLLL' => 'YYYY MMMM D, dddd HH:mm', + ], + + 'year' => 'makore :count', + 'y' => 'makore :count', + 'a_year' => 'makore :count', + + 'month' => 'mwedzi :count', + 'm' => 'mwedzi :count', + 'a_month' => 'mwedzi :count', + + 'week' => 'vhiki :count', + 'w' => 'vhiki :count', + 'a_week' => 'vhiki :count', + + 'day' => 'mazuva :count', + 'd' => 'mazuva :count', + 'a_day' => 'mazuva :count', + + 'hour' => 'maawa :count', + 'h' => 'maawa :count', + 'a_hour' => 'maawa :count', + + 'minute' => 'minitsi :count', + 'min' => 'minitsi :count', + 'a_minute' => 'minitsi :count', + + 'second' => 'sekonzi :count', + 's' => 'sekonzi :count', + 'a_second' => 'sekonzi :count', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/so.php b/vendor/nesbot/carbon/src/Carbon/Lang/so.php new file mode 100644 index 0000000..5785271 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/so.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Author: + * - Abdifatah Abdilahi(@abdifatahz) + */ +return [ + 'year' => ':count sanad|:count sanadood', + 'a_year' => 'sanad|:count sanadood', + 'y' => '{1}:countsn|{0}:countsns|]1,Inf[:countsn', + 'month' => ':count bil|:count bilood', + 'a_month' => 'bil|:count bilood', + 'm' => ':countbil', + 'week' => ':count isbuuc', + 'a_week' => 'isbuuc|:count isbuuc', + 'w' => ':countis', + 'day' => ':count maalin|:count maalmood', + 'a_day' => 'maalin|:count maalmood', + 'd' => ':countml', + 'hour' => ':count saac', + 'a_hour' => 'saacad|:count saac', + 'h' => ':countsc', + 'minute' => ':count daqiiqo', + 'a_minute' => 'daqiiqo|:count daqiiqo', + 'min' => ':countdq', + 'second' => ':count ilbidhiqsi', + 'a_second' => 'xooga ilbidhiqsiyo|:count ilbidhiqsi', + 's' => ':countil', + 'ago' => ':time kahor', + 'from_now' => ':time gudahood', + 'after' => ':time kedib', + 'before' => ':time kahor', + 'diff_now' => 'hada', + 'diff_today' => 'maanta', + 'diff_today_regexp' => 'maanta(?:\s+markay\s+(?:tahay|ahayd))?', + 'diff_yesterday' => 'shalayto', + 'diff_yesterday_regexp' => 'shalayto(?:\s+markay\s+ahayd)?', + 'diff_tomorrow' => 'beri', + 'diff_tomorrow_regexp' => 'beri(?:\s+markay\s+tahay)?', + 'diff_before_yesterday' => 'doraato', + 'diff_after_tomorrow' => 'saadanbe', + 'period_recurrences' => 'mar|:count jeer', + 'period_interval' => ':interval kasta', + 'period_start_date' => 'laga bilaabo :date', + 'period_end_date' => 'ilaa :date', + 'months' => ['Janaayo', 'Febraayo', 'Abriil', 'Maajo', 'Juun', 'Luuliyo', 'Agoosto', 'Sebteembar', 'Oktoobar', 'Nofeembar', 'Diseembar'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Abr', 'Mjo', 'Jun', 'Lyo', 'Agt', 'Seb', 'Okt', 'Nof', 'Dis'], + 'weekdays' => ['Axad', 'Isniin', 'Talaada', 'Arbaca', 'Khamiis', 'Jimce', 'Sabti'], + 'weekdays_short' => ['Axd', 'Isn', 'Tal', 'Arb', 'Kha', 'Jim', 'Sbt'], + 'weekdays_min' => ['Ax', 'Is', 'Ta', 'Ar', 'Kh', 'Ji', 'Sa'], + 'list' => [', ', ' and '], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'calendar' => [ + 'sameDay' => '[Maanta markay tahay] LT', + 'nextDay' => '[Beri markay tahay] LT', + 'nextWeek' => 'dddd [markay tahay] LT', + 'lastDay' => '[Shalay markay ahayd] LT', + 'lastWeek' => '[Hore] dddd [Markay ahayd] LT', + 'sameElse' => 'L', + ], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/so_DJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/so_DJ.php new file mode 100644 index 0000000..273dda8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/so_DJ.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/so.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/so_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/so_ET.php new file mode 100644 index 0000000..7b69971 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/so_ET.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return require __DIR__.'/so.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/so_KE.php b/vendor/nesbot/carbon/src/Carbon/Lang/so_KE.php new file mode 100644 index 0000000..7b69971 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/so_KE.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return require __DIR__.'/so.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/so_SO.php b/vendor/nesbot/carbon/src/Carbon/Lang/so_SO.php new file mode 100644 index 0000000..7b69971 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/so_SO.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return require __DIR__.'/so.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sq.php b/vendor/nesbot/carbon/src/Carbon/Lang/sq.php new file mode 100644 index 0000000..ffa592e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sq.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - JD Isaacks + * - Fadion Dashi + */ +return [ + 'year' => ':count vit|:count vjet', + 'a_year' => 'një vit|:count vite', + 'y' => ':count v.', + 'month' => ':count muaj', + 'a_month' => 'një muaj|:count muaj', + 'm' => ':count muaj', + 'week' => ':count javë', + 'a_week' => ':count javë|:count javë', + 'w' => ':count j.', + 'day' => ':count ditë', + 'a_day' => 'një ditë|:count ditë', + 'd' => ':count d.', + 'hour' => ':count orë', + 'a_hour' => 'një orë|:count orë', + 'h' => ':count o.', + 'minute' => ':count minutë|:count minuta', + 'a_minute' => 'një minutë|:count minuta', + 'min' => ':count min.', + 'second' => ':count sekondë|:count sekonda', + 'a_second' => 'disa sekonda|:count sekonda', + 's' => ':count s.', + 'ago' => ':time më parë', + 'from_now' => 'në :time', + 'after' => ':time pas', + 'before' => ':time para', + 'diff_now' => 'tani', + 'diff_today' => 'Sot', + 'diff_today_regexp' => 'Sot(?:\\s+në)?', + 'diff_yesterday' => 'dje', + 'diff_yesterday_regexp' => 'Dje(?:\\s+në)?', + 'diff_tomorrow' => 'nesër', + 'diff_tomorrow_regexp' => 'Nesër(?:\\s+në)?', + 'diff_before_yesterday' => 'pardje', + 'diff_after_tomorrow' => 'pasnesër', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Sot në] LT', + 'nextDay' => '[Nesër në] LT', + 'nextWeek' => 'dddd [në] LT', + 'lastDay' => '[Dje në] LT', + 'lastWeek' => 'dddd [e kaluar në] LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'meridiem' => ['PD', 'MD'], + 'months' => ['janar', 'shkurt', 'mars', 'prill', 'maj', 'qershor', 'korrik', 'gusht', 'shtator', 'tetor', 'nëntor', 'dhjetor'], + 'months_short' => ['jan', 'shk', 'mar', 'pri', 'maj', 'qer', 'kor', 'gus', 'sht', 'tet', 'nën', 'dhj'], + 'weekdays' => ['e diel', 'e hënë', 'e martë', 'e mërkurë', 'e enjte', 'e premte', 'e shtunë'], + 'weekdays_short' => ['die', 'hën', 'mar', 'mër', 'enj', 'pre', 'sht'], + 'weekdays_min' => ['d', 'h', 'ma', 'më', 'e', 'p', 'sh'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' dhe '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sq_AL.php b/vendor/nesbot/carbon/src/Carbon/Lang/sq_AL.php new file mode 100644 index 0000000..ea5df3f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sq_AL.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/sq.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sq_MK.php b/vendor/nesbot/carbon/src/Carbon/Lang/sq_MK.php new file mode 100644 index 0000000..62f752c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sq_MK.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/sq.php', [ + 'formats' => [ + 'L' => 'D.M.YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY, HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY, HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sq_XK.php b/vendor/nesbot/carbon/src/Carbon/Lang/sq_XK.php new file mode 100644 index 0000000..62f752c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sq_XK.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/sq.php', [ + 'formats' => [ + 'L' => 'D.M.YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY, HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY, HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr.php new file mode 100644 index 0000000..68ba663 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - François B + * - shaishavgandhi05 + * - Serhan Apaydın + * - JD Isaacks + * - Glavić + * - Milos Sakovic + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count godina|:count godine|:count godina', + 'y' => ':count g.', + 'month' => ':count mesec|:count meseca|:count meseci', + 'm' => ':count mj.', + 'week' => ':count nedelja|:count nedelje|:count nedelja', + 'w' => ':count ned.', + 'day' => ':count dan|:count dana|:count dana', + 'd' => ':count d.', + 'hour' => ':count sat|:count sata|:count sati', + 'h' => ':count č.', + 'minute' => ':count minut|:count minuta|:count minuta', + 'min' => ':count min.', + 'second' => ':count sekundu|:count sekunde|:count sekundi', + 's' => ':count sek.', + 'ago' => 'pre :time', + 'from_now' => 'za :time', + 'after' => 'nakon :time', + 'before' => 'pre :time', + + 'year_from_now' => ':count godinu|:count godine|:count godina', + 'year_ago' => ':count godinu|:count godine|:count godina', + 'week_from_now' => ':count nedelju|:count nedelje|:count nedelja', + 'week_ago' => ':count nedelju|:count nedelje|:count nedelja', + + 'diff_now' => 'upravo sada', + 'diff_today' => 'danas', + 'diff_today_regexp' => 'danas(?:\\s+u)?', + 'diff_yesterday' => 'juče', + 'diff_yesterday_regexp' => 'juče(?:\\s+u)?', + 'diff_tomorrow' => 'sutra', + 'diff_tomorrow_regexp' => 'sutra(?:\\s+u)?', + 'diff_before_yesterday' => 'prekjuče', + 'diff_after_tomorrow' => 'preksutra', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY H:mm', + 'LLLL' => 'dddd, D. MMMM YYYY H:mm', + ], + 'calendar' => [ + 'sameDay' => '[danas u] LT', + 'nextDay' => '[sutra u] LT', + 'nextWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[u nedelju u] LT'; + case 3: + return '[u sredu u] LT'; + case 6: + return '[u subotu u] LT'; + default: + return '[u] dddd [u] LT'; + } + }, + 'lastDay' => '[juče u] LT', + 'lastWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[prošle nedelje u] LT'; + case 1: + return '[prošlog ponedeljka u] LT'; + case 2: + return '[prošlog utorka u] LT'; + case 3: + return '[prošle srede u] LT'; + case 4: + return '[prošlog četvrtka u] LT'; + case 5: + return '[prošlog petka u] LT'; + default: + return '[prošle subote u] LT'; + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'months' => ['januar', 'februar', 'mart', 'april', 'maj', 'jun', 'jul', 'avgust', 'septembar', 'oktobar', 'novembar', 'decembar'], + 'months_short' => ['jan.', 'feb.', 'mar.', 'apr.', 'maj', 'jun', 'jul', 'avg.', 'sep.', 'okt.', 'nov.', 'dec.'], + 'weekdays' => ['nedelja', 'ponedeljak', 'utorak', 'sreda', 'četvrtak', 'petak', 'subota'], + 'weekdays_short' => ['ned.', 'pon.', 'uto.', 'sre.', 'čet.', 'pet.', 'sub.'], + 'weekdays_min' => ['ne', 'po', 'ut', 'sr', 'če', 'pe', 'su'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' i '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php new file mode 100644 index 0000000..8becbc5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - François B + * - shaishavgandhi05 + * - Serhan Apaydın + * - JD Isaacks + * - Glavić + * - Nikola Zeravcic + * - Milos Sakovic + */ + +use Carbon\CarbonInterface; + +return [ + 'year' => ':count година|:count године|:count година', + 'y' => ':count г.', + 'month' => ':count месец|:count месеца|:count месеци', + 'm' => ':count м.', + 'week' => ':count недеља|:count недеље|:count недеља', + 'w' => ':count нед.', + 'day' => ':count дан|:count дана|:count дана', + 'd' => ':count д.', + 'hour' => ':count сат|:count сата|:count сати', + 'h' => ':count ч.', + 'minute' => ':count минут|:count минута|:count минута', + 'min' => ':count мин.', + 'second' => ':count секунд|:count секунде|:count секунди', + 's' => ':count сек.', + 'ago' => 'пре :time', + 'from_now' => 'за :time', + 'after' => ':time након', + 'before' => ':time пре', + 'year_from_now' => ':count годину|:count године|:count година', + 'year_ago' => ':count годину|:count године|:count година', + 'week_from_now' => ':count недељу|:count недеље|:count недеља', + 'week_ago' => ':count недељу|:count недеље|:count недеља', + 'diff_now' => 'управо сада', + 'diff_today' => 'данас', + 'diff_today_regexp' => 'данас(?:\\s+у)?', + 'diff_yesterday' => 'јуче', + 'diff_yesterday_regexp' => 'јуче(?:\\s+у)?', + 'diff_tomorrow' => 'сутра', + 'diff_tomorrow_regexp' => 'сутра(?:\\s+у)?', + 'diff_before_yesterday' => 'прекјуче', + 'diff_after_tomorrow' => 'прекосутра', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY H:mm', + 'LLLL' => 'dddd, D. MMMM YYYY H:mm', + ], + 'calendar' => [ + 'sameDay' => '[данас у] LT', + 'nextDay' => '[сутра у] LT', + 'nextWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[у недељу у] LT'; + case 3: + return '[у среду у] LT'; + case 6: + return '[у суботу у] LT'; + default: + return '[у] dddd [у] LT'; + } + }, + 'lastDay' => '[јуче у] LT', + 'lastWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[прошле недеље у] LT'; + case 1: + return '[прошлог понедељка у] LT'; + case 2: + return '[прошлог уторка у] LT'; + case 3: + return '[прошле среде у] LT'; + case 4: + return '[прошлог четвртка у] LT'; + case 5: + return '[прошлог петка у] LT'; + default: + return '[прошле суботе у] LT'; + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'months' => ['јануар', 'фебруар', 'март', 'април', 'мај', 'јун', 'јул', 'август', 'септембар', 'октобар', 'новембар', 'децембар'], + 'months_short' => ['јан.', 'феб.', 'мар.', 'апр.', 'мај', 'јун', 'јул', 'авг.', 'сеп.', 'окт.', 'нов.', 'дец.'], + 'weekdays' => ['недеља', 'понедељак', 'уторак', 'среда', 'четвртак', 'петак', 'субота'], + 'weekdays_short' => ['нед.', 'пон.', 'уто.', 'сре.', 'чет.', 'пет.', 'суб.'], + 'weekdays_min' => ['не', 'по', 'ут', 'ср', 'че', 'пе', 'су'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' и '], + 'meridiem' => ['АМ', 'ПМ'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_BA.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_BA.php new file mode 100644 index 0000000..4b29a45 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_BA.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Component\Translation\PluralizationRules; + +// @codeCoverageIgnoreStart +if (class_exists(PluralizationRules::class)) { + PluralizationRules::set(static function ($number) { + return PluralizationRules::get($number, 'sr'); + }, 'sr_Cyrl_BA'); +} +// @codeCoverageIgnoreEnd + +return array_replace_recursive(require __DIR__.'/sr_Cyrl.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D.M.yy.', + 'LL' => 'DD.MM.YYYY.', + 'LLL' => 'DD. MMMM YYYY. HH:mm', + 'LLLL' => 'dddd, DD. MMMM YYYY. HH:mm', + ], + 'weekdays' => ['недјеља', 'понедељак', 'уторак', 'сриједа', 'четвртак', 'петак', 'субота'], + 'weekdays_short' => ['нед.', 'пон.', 'ут.', 'ср.', 'чет.', 'пет.', 'суб.'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php new file mode 100644 index 0000000..28d22fd --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Glavić + * - Milos Sakovic + */ + +use Carbon\CarbonInterface; +use Symfony\Component\Translation\PluralizationRules; + +// @codeCoverageIgnoreStart +if (class_exists(PluralizationRules::class)) { + PluralizationRules::set(static function ($number) { + return PluralizationRules::get($number, 'sr'); + }, 'sr_Cyrl_ME'); +} +// @codeCoverageIgnoreEnd + +return [ + 'year' => ':count година|:count године|:count година', + 'y' => ':count г.', + 'month' => ':count мјесец|:count мјесеца|:count мјесеци', + 'm' => ':count мј.', + 'week' => ':count недјеља|:count недјеље|:count недјеља', + 'w' => ':count нед.', + 'day' => ':count дан|:count дана|:count дана', + 'd' => ':count д.', + 'hour' => ':count сат|:count сата|:count сати', + 'h' => ':count ч.', + 'minute' => ':count минут|:count минута|:count минута', + 'min' => ':count мин.', + 'second' => ':count секунд|:count секунде|:count секунди', + 's' => ':count сек.', + 'ago' => 'прије :time', + 'from_now' => 'за :time', + 'after' => ':time након', + 'before' => ':time прије', + + 'year_from_now' => ':count годину|:count године|:count година', + 'year_ago' => ':count годину|:count године|:count година', + + 'week_from_now' => ':count недјељу|:count недјеље|:count недјеља', + 'week_ago' => ':count недјељу|:count недјеље|:count недјеља', + + 'diff_now' => 'управо сада', + 'diff_today' => 'данас', + 'diff_today_regexp' => 'данас(?:\\s+у)?', + 'diff_yesterday' => 'јуче', + 'diff_yesterday_regexp' => 'јуче(?:\\s+у)?', + 'diff_tomorrow' => 'сутра', + 'diff_tomorrow_regexp' => 'сутра(?:\\s+у)?', + 'diff_before_yesterday' => 'прекјуче', + 'diff_after_tomorrow' => 'прекосјутра', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM YYYY', + 'LLL' => 'D. MMMM YYYY H:mm', + 'LLLL' => 'dddd, D. MMMM YYYY H:mm', + ], + 'calendar' => [ + 'sameDay' => '[данас у] LT', + 'nextDay' => '[сутра у] LT', + 'nextWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[у недељу у] LT'; + case 3: + return '[у среду у] LT'; + case 6: + return '[у суботу у] LT'; + default: + return '[у] dddd [у] LT'; + } + }, + 'lastDay' => '[јуче у] LT', + 'lastWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[прошле недеље у] LT'; + case 1: + return '[прошлог понедељка у] LT'; + case 2: + return '[прошлог уторка у] LT'; + case 3: + return '[прошле среде у] LT'; + case 4: + return '[прошлог четвртка у] LT'; + case 5: + return '[прошлог петка у] LT'; + default: + return '[прошле суботе у] LT'; + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'months' => ['јануар', 'фебруар', 'март', 'април', 'мај', 'јун', 'јул', 'август', 'септембар', 'октобар', 'новембар', 'децембар'], + 'months_short' => ['јан.', 'феб.', 'мар.', 'апр.', 'мај', 'јун', 'јул', 'авг.', 'сеп.', 'окт.', 'нов.', 'дец.'], + 'weekdays' => ['недеља', 'понедељак', 'уторак', 'среда', 'четвртак', 'петак', 'субота'], + 'weekdays_short' => ['нед.', 'пон.', 'уто.', 'сре.', 'чет.', 'пет.', 'суб.'], + 'weekdays_min' => ['не', 'по', 'ут', 'ср', 'че', 'пе', 'су'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' и '], + 'meridiem' => ['АМ', 'ПМ'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_XK.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_XK.php new file mode 100644 index 0000000..d6e29b8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_XK.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Component\Translation\PluralizationRules; + +// @codeCoverageIgnoreStart +if (class_exists(PluralizationRules::class)) { + PluralizationRules::set(static function ($number) { + return PluralizationRules::get($number, 'sr'); + }, 'sr_Cyrl_XK'); +} +// @codeCoverageIgnoreEnd + +return array_replace_recursive(require __DIR__.'/sr_Cyrl_BA.php', [ + 'weekdays' => ['недеља', 'понедељак', 'уторак', 'среда', 'четвртак', 'петак', 'субота'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn.php new file mode 100644 index 0000000..9971674 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/sr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_BA.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_BA.php new file mode 100644 index 0000000..95b2770 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_BA.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Component\Translation\PluralizationRules; + +// @codeCoverageIgnoreStart +if (class_exists(PluralizationRules::class)) { + PluralizationRules::set(static function ($number) { + return PluralizationRules::get($number, 'sr'); + }, 'sr_Latn_BA'); +} +// @codeCoverageIgnoreEnd + +return array_replace_recursive(require __DIR__.'/sr_Latn.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D.M.yy.', + 'LL' => 'DD.MM.YYYY.', + 'LLL' => 'DD. MMMM YYYY. HH:mm', + 'LLLL' => 'dddd, DD. MMMM YYYY. HH:mm', + ], + 'weekdays' => ['nedjelja', 'ponedeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota'], + 'weekdays_short' => ['ned.', 'pon.', 'ut.', 'sr.', 'čet.', 'pet.', 'sub.'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php new file mode 100644 index 0000000..5b8f2d0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Glavić + * - Milos Sakovic + */ + +use Carbon\CarbonInterface; +use Symfony\Component\Translation\PluralizationRules; + +// @codeCoverageIgnoreStart +if (class_exists(PluralizationRules::class)) { + PluralizationRules::set(static function ($number) { + return PluralizationRules::get($number, 'sr'); + }, 'sr_Latn_ME'); +} +// @codeCoverageIgnoreEnd + +return array_replace_recursive(require __DIR__.'/sr.php', [ + 'month' => ':count mjesec|:count mjeseca|:count mjeseci', + 'week' => ':count nedjelja|:count nedjelje|:count nedjelja', + 'second' => ':count sekund|:count sekunde|:count sekundi', + 'ago' => 'prije :time', + 'from_now' => 'za :time', + 'after' => ':time nakon', + 'before' => ':time prije', + 'week_from_now' => ':count nedjelju|:count nedjelje|:count nedjelja', + 'week_ago' => ':count nedjelju|:count nedjelje|:count nedjelja', + 'second_ago' => ':count sekund|:count sekunde|:count sekundi', + 'diff_tomorrow' => 'sjutra', + 'calendar' => [ + 'nextDay' => '[sjutra u] LT', + 'nextWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[u nedjelju u] LT'; + case 3: + return '[u srijedu u] LT'; + case 6: + return '[u subotu u] LT'; + default: + return '[u] dddd [u] LT'; + } + }, + 'lastWeek' => function (CarbonInterface $date) { + switch ($date->dayOfWeek) { + case 0: + return '[prošle nedjelje u] LT'; + case 1: + return '[prošle nedjelje u] LT'; + case 2: + return '[prošlog utorka u] LT'; + case 3: + return '[prošle srijede u] LT'; + case 4: + return '[prošlog četvrtka u] LT'; + case 5: + return '[prošlog petka u] LT'; + default: + return '[prošle subote u] LT'; + } + }, + ], + 'weekdays' => ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota'], + 'weekdays_short' => ['ned.', 'pon.', 'uto.', 'sri.', 'čet.', 'pet.', 'sub.'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_XK.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_XK.php new file mode 100644 index 0000000..5278e2e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_XK.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Component\Translation\PluralizationRules; + +// @codeCoverageIgnoreStart +if (class_exists(PluralizationRules::class)) { + PluralizationRules::set(static function ($number) { + return PluralizationRules::get($number, 'sr'); + }, 'sr_Latn_XK'); +} +// @codeCoverageIgnoreEnd + +return array_replace_recursive(require __DIR__.'/sr_Latn_BA.php', [ + 'weekdays' => ['nedelja', 'ponedeljak', 'utorak', 'sreda', 'četvrtak', 'petak', 'subota'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_ME.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_ME.php new file mode 100644 index 0000000..d7c65b9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_ME.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/sr_Latn_ME.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_RS.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_RS.php new file mode 100644 index 0000000..bc5e04b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_RS.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - sr_YU, sr_CS locale Danilo Segan bug-glibc-locales@gnu.org + */ +return require __DIR__.'/sr_Cyrl.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_RS@latin.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_RS@latin.php new file mode 100644 index 0000000..9971674 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_RS@latin.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/sr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ss.php b/vendor/nesbot/carbon/src/Carbon/Lang/ss.php new file mode 100644 index 0000000..1c52c9b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ss.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Nicolai Davies + */ +return [ + 'year' => '{1}umnyaka|:count iminyaka', + 'month' => '{1}inyanga|:count tinyanga', + 'week' => '{1}:count liviki|:count emaviki', + 'day' => '{1}lilanga|:count emalanga', + 'hour' => '{1}lihora|:count emahora', + 'minute' => '{1}umzuzu|:count emizuzu', + 'second' => '{1}emizuzwana lomcane|:count mzuzwana', + 'ago' => 'wenteka nga :time', + 'from_now' => 'nga :time', + 'diff_yesterday' => 'Itolo', + 'diff_yesterday_regexp' => 'Itolo(?:\\s+nga)?', + 'diff_today' => 'Namuhla', + 'diff_today_regexp' => 'Namuhla(?:\\s+nga)?', + 'diff_tomorrow' => 'Kusasa', + 'diff_tomorrow_regexp' => 'Kusasa(?:\\s+nga)?', + 'formats' => [ + 'LT' => 'h:mm A', + 'LTS' => 'h:mm:ss A', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm A', + 'LLLL' => 'dddd, D MMMM YYYY h:mm A', + ], + 'calendar' => [ + 'sameDay' => '[Namuhla nga] LT', + 'nextDay' => '[Kusasa nga] LT', + 'nextWeek' => 'dddd [nga] LT', + 'lastDay' => '[Itolo nga] LT', + 'lastWeek' => 'dddd [leliphelile] [nga] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + $lastDigit = $number % 10; + + return $number.( + ((int) ($number % 100 / 10) === 1) ? 'e' : ( + ($lastDigit === 1 || $lastDigit === 2) ? 'a' : 'e' + ) + ); + }, + 'meridiem' => function ($hour) { + if ($hour < 11) { + return 'ekuseni'; + } + if ($hour < 15) { + return 'emini'; + } + if ($hour < 19) { + return 'entsambama'; + } + + return 'ebusuku'; + }, + 'months' => ['Bhimbidvwane', 'Indlovana', 'Indlov\'lenkhulu', 'Mabasa', 'Inkhwekhweti', 'Inhlaba', 'Kholwane', 'Ingci', 'Inyoni', 'Imphala', 'Lweti', 'Ingongoni'], + 'months_short' => ['Bhi', 'Ina', 'Inu', 'Mab', 'Ink', 'Inh', 'Kho', 'Igc', 'Iny', 'Imp', 'Lwe', 'Igo'], + 'weekdays' => ['Lisontfo', 'Umsombuluko', 'Lesibili', 'Lesitsatfu', 'Lesine', 'Lesihlanu', 'Umgcibelo'], + 'weekdays_short' => ['Lis', 'Umb', 'Lsb', 'Les', 'Lsi', 'Lsh', 'Umg'], + 'weekdays_min' => ['Li', 'Us', 'Lb', 'Lt', 'Ls', 'Lh', 'Ug'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ss_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ss_ZA.php new file mode 100644 index 0000000..ba89527 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ss_ZA.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/ss.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/st.php b/vendor/nesbot/carbon/src/Carbon/Lang/st.php new file mode 100644 index 0000000..b065445 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/st.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/st_ZA.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/st_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/st_ZA.php new file mode 100644 index 0000000..5bce7f2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/st_ZA.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Pherekgong', 'Hlakola', 'Tlhakubele', 'Mmese', 'Motsheanong', 'Phupjane', 'Phupu', 'Phato', 'Leotse', 'Mphalane', 'Pudungwana', 'Tshitwe'], + 'months_short' => ['Phe', 'Hla', 'TlH', 'Mme', 'Mot', 'Jan', 'Upu', 'Pha', 'Leo', 'Mph', 'Pud', 'Tsh'], + 'weekdays' => ['Sontaha', 'Mantaha', 'Labobedi', 'Laboraro', 'Labone', 'Labohlano', 'Moqebelo'], + 'weekdays_short' => ['Son', 'Mma', 'Bed', 'Rar', 'Ne', 'Hla', 'Moq'], + 'weekdays_min' => ['Son', 'Mma', 'Bed', 'Rar', 'Ne', 'Hla', 'Moq'], + 'day_of_first_week_of_year' => 1, + + 'week' => ':count Sontaha', // less reliable + 'w' => ':count Sontaha', // less reliable + 'a_week' => ':count Sontaha', // less reliable + + 'day' => ':count letsatsi', // less reliable + 'd' => ':count letsatsi', // less reliable + 'a_day' => ':count letsatsi', // less reliable + + 'hour' => ':count sešupanako', // less reliable + 'h' => ':count sešupanako', // less reliable + 'a_hour' => ':count sešupanako', // less reliable + + 'minute' => ':count menyane', // less reliable + 'min' => ':count menyane', // less reliable + 'a_minute' => ':count menyane', // less reliable + + 'second' => ':count thusa', // less reliable + 's' => ':count thusa', // less reliable + 'a_second' => ':count thusa', // less reliable + + 'year' => ':count selemo', + 'y' => ':count selemo', + 'a_year' => ':count selemo', + + 'month' => ':count kgwedi', + 'm' => ':count kgwedi', + 'a_month' => ':count kgwedi', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sv.php b/vendor/nesbot/carbon/src/Carbon/Lang/sv.php new file mode 100644 index 0000000..1706c71 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sv.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Kristoffer Snabb + * - JD Isaacks + * - Jens Herlevsen + * - Nightpine + * - Anders Nygren (litemerafrukt) + */ +return [ + 'year' => ':count år', + 'a_year' => 'ett år|:count år', + 'y' => ':count år', + 'month' => ':count månad|:count månader', + 'a_month' => 'en månad|:count månader', + 'm' => ':count mån', + 'week' => ':count vecka|:count veckor', + 'a_week' => 'en vecka|:count veckor', + 'w' => ':count v', + 'day' => ':count dag|:count dagar', + 'a_day' => 'en dag|:count dagar', + 'd' => ':count dgr', + 'hour' => ':count timme|:count timmar', + 'a_hour' => 'en timme|:count timmar', + 'h' => ':count tim', + 'minute' => ':count minut|:count minuter', + 'a_minute' => 'en minut|:count minuter', + 'min' => ':count min', + 'second' => ':count sekund|:count sekunder', + 'a_second' => 'några sekunder|:count sekunder', + 's' => ':count s', + 'ago' => 'för :time sedan', + 'from_now' => 'om :time', + 'after' => ':time efter', + 'before' => ':time före', + 'diff_now' => 'nu', + 'diff_today' => 'I dag', + 'diff_yesterday' => 'i går', + 'diff_yesterday_regexp' => 'I går', + 'diff_tomorrow' => 'i morgon', + 'diff_tomorrow_regexp' => 'I morgon', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-DD', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY [kl.] HH:mm', + 'LLLL' => 'dddd D MMMM YYYY [kl.] HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[I dag] LT', + 'nextDay' => '[I morgon] LT', + 'nextWeek' => '[På] dddd LT', + 'lastDay' => '[I går] LT', + 'lastWeek' => '[I] dddd[s] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + $lastDigit = $number % 10; + + return $number.( + ((int) ($number % 100 / 10) === 1) ? 'e' : ( + ($lastDigit === 1 || $lastDigit === 2) ? 'a' : 'e' + ) + ); + }, + 'months' => ['januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli', 'augusti', 'september', 'oktober', 'november', 'december'], + 'months_short' => ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'], + 'weekdays' => ['söndag', 'måndag', 'tisdag', 'onsdag', 'torsdag', 'fredag', 'lördag'], + 'weekdays_short' => ['sön', 'mån', 'tis', 'ons', 'tors', 'fre', 'lör'], + 'weekdays_min' => ['sö', 'må', 'ti', 'on', 'to', 'fr', 'lö'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' och '], + 'meridiem' => ['fm', 'em'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sv_AX.php b/vendor/nesbot/carbon/src/Carbon/Lang/sv_AX.php new file mode 100644 index 0000000..70cc558 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sv_AX.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/sv.php', [ + 'formats' => [ + 'L' => 'YYYY-MM-dd', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sv_FI.php b/vendor/nesbot/carbon/src/Carbon/Lang/sv_FI.php new file mode 100644 index 0000000..d7182c8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sv_FI.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/sv.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sv_SE.php b/vendor/nesbot/carbon/src/Carbon/Lang/sv_SE.php new file mode 100644 index 0000000..d7182c8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sv_SE.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/sv.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sw.php b/vendor/nesbot/carbon/src/Carbon/Lang/sw.php new file mode 100644 index 0000000..f8630d5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sw.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - leyluj + * - Josh Soref + * - ryanhart2 + */ +return [ + 'year' => 'mwaka :count|miaka :count', + 'a_year' => 'mwaka mmoja|miaka :count', + 'y' => 'mwaka :count|miaka :count', + 'month' => 'mwezi :count|miezi :count', + 'a_month' => 'mwezi mmoja|miezi :count', + 'm' => 'mwezi :count|miezi :count', + 'week' => 'wiki :count', + 'a_week' => 'wiki mmoja|wiki :count', + 'w' => 'w. :count', + 'day' => 'siku :count', + 'a_day' => 'siku moja|masiku :count', + 'd' => 'si. :count', + 'hour' => 'saa :count|masaa :count', + 'a_hour' => 'saa limoja|masaa :count', + 'h' => 'saa :count|masaa :count', + 'minute' => 'dakika :count', + 'a_minute' => 'dakika moja|dakika :count', + 'min' => 'd. :count', + 'second' => 'sekunde :count', + 'a_second' => 'hivi punde|sekunde :count', + 's' => 'se. :count', + 'ago' => 'tokea :time', + 'from_now' => ':time baadaye', + 'after' => ':time baada', + 'before' => ':time kabla', + 'diff_now' => 'sasa hivi', + 'diff_today' => 'leo', + 'diff_today_regexp' => 'leo(?:\\s+saa)?', + 'diff_yesterday' => 'jana', + 'diff_tomorrow' => 'kesho', + 'diff_tomorrow_regexp' => 'kesho(?:\\s+saa)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[leo saa] LT', + 'nextDay' => '[kesho saa] LT', + 'nextWeek' => '[wiki ijayo] dddd [saat] LT', + 'lastDay' => '[jana] LT', + 'lastWeek' => '[wiki iliyopita] dddd [saat] LT', + 'sameElse' => 'L', + ], + 'months' => ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'], + 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'], + 'weekdays' => ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi', 'Ijumaa', 'Jumamosi'], + 'weekdays_short' => ['Jpl', 'Jtat', 'Jnne', 'Jtan', 'Alh', 'Ijm', 'Jmos'], + 'weekdays_min' => ['J2', 'J3', 'J4', 'J5', 'Al', 'Ij', 'J1'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' na '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sw_CD.php b/vendor/nesbot/carbon/src/Carbon/Lang/sw_CD.php new file mode 100644 index 0000000..ec9117b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sw_CD.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/sw.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sw_KE.php b/vendor/nesbot/carbon/src/Carbon/Lang/sw_KE.php new file mode 100644 index 0000000..2ace0db --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sw_KE.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Kamusi Project Martin Benjamin locales@kamusi.org + */ +return array_replace_recursive(require __DIR__.'/sw.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'], + 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'], + 'weekdays' => ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi', 'Ijumaa', 'Jumamosi'], + 'weekdays_short' => ['J2', 'J3', 'J4', 'J5', 'Alh', 'Ij', 'J1'], + 'weekdays_min' => ['J2', 'J3', 'J4', 'J5', 'Alh', 'Ij', 'J1'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['asubuhi', 'alasiri'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sw_TZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/sw_TZ.php new file mode 100644 index 0000000..fab3cd6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sw_TZ.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Kamusi Project Martin Benjamin locales@kamusi.org + */ +return array_replace_recursive(require __DIR__.'/sw.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'], + 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'], + 'weekdays' => ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi', 'Ijumaa', 'Jumamosi'], + 'weekdays_short' => ['J2', 'J3', 'J4', 'J5', 'Alh', 'Ij', 'J1'], + 'weekdays_min' => ['J2', 'J3', 'J4', 'J5', 'Alh', 'Ij', 'J1'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['asubuhi', 'alasiri'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sw_UG.php b/vendor/nesbot/carbon/src/Carbon/Lang/sw_UG.php new file mode 100644 index 0000000..ec9117b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sw_UG.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/sw.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/szl.php b/vendor/nesbot/carbon/src/Carbon/Lang/szl.php new file mode 100644 index 0000000..4429c4f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/szl.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/szl_PL.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/szl_PL.php b/vendor/nesbot/carbon/src/Carbon/Lang/szl_PL.php new file mode 100644 index 0000000..9adddcf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/szl_PL.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - szl_PL locale Przemyslaw Buczkowski libc-alpha@sourceware.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['styczyń', 'luty', 'merc', 'kwjeciyń', 'moj', 'czyrwjyń', 'lipjyń', 'siyrpjyń', 'wrzesiyń', 'październik', 'listopad', 'grudziyń'], + 'months_short' => ['sty', 'lut', 'mer', 'kwj', 'moj', 'czy', 'lip', 'siy', 'wrz', 'paź', 'lis', 'gru'], + 'weekdays' => ['niydziela', 'pyńdziŏek', 'wtŏrek', 'strzŏda', 'sztwortek', 'pjōntek', 'sobŏta'], + 'weekdays_short' => ['niy', 'pyń', 'wtŏ', 'str', 'szt', 'pjō', 'sob'], + 'weekdays_min' => ['niy', 'pyń', 'wtŏ', 'str', 'szt', 'pjō', 'sob'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'year' => ':count rok', + 'y' => ':count rok', + 'a_year' => ':count rok', + + 'month' => ':count mjeśůnc', + 'm' => ':count mjeśůnc', + 'a_month' => ':count mjeśůnc', + + 'week' => ':count tydźyń', + 'w' => ':count tydźyń', + 'a_week' => ':count tydźyń', + + 'day' => ':count dźyń', + 'd' => ':count dźyń', + 'a_day' => ':count dźyń', + + 'hour' => ':count godzina', + 'h' => ':count godzina', + 'a_hour' => ':count godzina', + + 'minute' => ':count minuta', + 'min' => ':count minuta', + 'a_minute' => ':count minuta', + + 'second' => ':count sekůnda', + 's' => ':count sekůnda', + 'a_second' => ':count sekůnda', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ta.php b/vendor/nesbot/carbon/src/Carbon/Lang/ta.php new file mode 100644 index 0000000..c1d89cb --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ta.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - François B + * - JD Isaacks + * - Satheez + */ +return [ + 'year' => ':count வருடம்|:count ஆண்டுகள்', + 'a_year' => 'ஒரு வருடம்|:count ஆண்டுகள்', + 'y' => ':count வருட.|:count ஆண்.', + 'month' => ':count மாதம்|:count மாதங்கள்', + 'a_month' => 'ஒரு மாதம்|:count மாதங்கள்', + 'm' => ':count மாத.', + 'week' => ':count வாரம்|:count வாரங்கள்', + 'a_week' => 'ஒரு வாரம்|:count வாரங்கள்', + 'w' => ':count வார.', + 'day' => ':count நாள்|:count நாட்கள்', + 'a_day' => 'ஒரு நாள்|:count நாட்கள்', + 'd' => ':count நாள்|:count நாட்.', + 'hour' => ':count மணி நேரம்|:count மணி நேரம்', + 'a_hour' => 'ஒரு மணி நேரம்|:count மணி நேரம்', + 'h' => ':count மணி.', + 'minute' => ':count நிமிடம்|:count நிமிடங்கள்', + 'a_minute' => 'ஒரு நிமிடம்|:count நிமிடங்கள்', + 'min' => ':count நிமி.', + 'second' => ':count சில விநாடிகள்|:count விநாடிகள்', + 'a_second' => 'ஒரு சில விநாடிகள்|:count விநாடிகள்', + 's' => ':count விநா.', + 'ago' => ':time முன்', + 'from_now' => ':time இல்', + 'before' => ':time முன்', + 'after' => ':time பின்', + 'diff_now' => 'இப்போது', + 'diff_today' => 'இன்று', + 'diff_yesterday' => 'நேற்று', + 'diff_tomorrow' => 'நாளை', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY, HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY, HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[இன்று] LT', + 'nextDay' => '[நாளை] LT', + 'nextWeek' => 'dddd, LT', + 'lastDay' => '[நேற்று] LT', + 'lastWeek' => '[கடந்த வாரம்] dddd, LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':numberவது', + 'meridiem' => function ($hour) { + if ($hour < 2) { + return ' யாமம்'; + } + if ($hour < 6) { + return ' வைகறை'; + } + if ($hour < 10) { + return ' காலை'; + } + if ($hour < 14) { + return ' நண்பகல்'; + } + if ($hour < 18) { + return ' எற்பாடு'; + } + if ($hour < 22) { + return ' மாலை'; + } + + return ' யாமம்'; + }, + 'months' => ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆகஸ்ட்', 'செப்டெம்பர்', 'அக்டோபர்', 'நவம்பர்', 'டிசம்பர்'], + 'months_short' => ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆகஸ்ட்', 'செப்டெம்பர்', 'அக்டோபர்', 'நவம்பர்', 'டிசம்பர்'], + 'weekdays' => ['ஞாயிற்றுக்கிழமை', 'திங்கட்கிழமை', 'செவ்வாய்கிழமை', 'புதன்கிழமை', 'வியாழக்கிழமை', 'வெள்ளிக்கிழமை', 'சனிக்கிழமை'], + 'weekdays_short' => ['ஞாயிறு', 'திங்கள்', 'செவ்வாய்', 'புதன்', 'வியாழன்', 'வெள்ளி', 'சனி'], + 'weekdays_min' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' மற்றும் '], + 'weekend' => [0, 0], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ta_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ta_IN.php new file mode 100644 index 0000000..492d4c5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ta_IN.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/ta.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆகஸ்ட்', 'செப்டம்பர்', 'அக்டோபர்', 'நவம்பர்', 'டிசம்பர்'], + 'months_short' => ['ஜன.', 'பிப்.', 'மார்.', 'ஏப்.', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக.', 'செப்.', 'அக்.', 'நவ.', 'டிச.'], + 'weekdays' => ['ஞாயிறு', 'திங்கள்', 'செவ்வாய்', 'புதன்', 'வியாழன்', 'வெள்ளி', 'சனி'], + 'weekdays_short' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'], + 'weekdays_min' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['காலை', 'மாலை'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ta_LK.php b/vendor/nesbot/carbon/src/Carbon/Lang/ta_LK.php new file mode 100644 index 0000000..8e2afbf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ta_LK.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - J.Yogaraj 94-777-315206 yogaraj.ubuntu@gmail.com + */ +return array_replace_recursive(require __DIR__.'/ta.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆகஸ்ட்', 'செப்டம்பர்', 'அக்டோபர்', 'நவம்பர்', 'டிசம்பர்'], + 'months_short' => ['ஜன', 'பிப்', 'மார்', 'ஏப்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக', 'செப்', 'அக்', 'நவ', 'டிச'], + 'weekdays' => ['ஞாயிறு', 'திங்கள்', 'செவ்வாய்', 'புதன்', 'வியாழன்', 'வெள்ளி', 'சனி'], + 'weekdays_short' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'], + 'weekdays_min' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['காலை', 'மாலை'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ta_MY.php b/vendor/nesbot/carbon/src/Carbon/Lang/ta_MY.php new file mode 100644 index 0000000..a6cd8b5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ta_MY.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ta.php', [ + 'formats' => [ + 'LT' => 'a h:mm', + 'LTS' => 'a h:mm:ss', + 'L' => 'D/M/yy', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM, YYYY, a h:mm', + 'LLLL' => 'dddd, D MMMM, YYYY, a h:mm', + ], + 'months' => ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆகஸ்ட்', 'செப்டம்பர்', 'அக்டோபர்', 'நவம்பர்', 'டிசம்பர்'], + 'months_short' => ['ஜன.', 'பிப்.', 'மார்.', 'ஏப்.', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக.', 'செப்.', 'அக்.', 'நவ.', 'டிச.'], + 'weekdays' => ['ஞாயிறு', 'திங்கள்', 'செவ்வாய்', 'புதன்', 'வியாழன்', 'வெள்ளி', 'சனி'], + 'weekdays_short' => ['ஞாயி.', 'திங்.', 'செவ்.', 'புத.', 'வியா.', 'வெள்.', 'சனி'], + 'weekdays_min' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'], + 'first_day_of_week' => 1, + 'meridiem' => ['மு.ப', 'பி.ப'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ta_SG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ta_SG.php new file mode 100644 index 0000000..7dbedee --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ta_SG.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ta.php', [ + 'formats' => [ + 'LT' => 'a h:mm', + 'LTS' => 'a h:mm:ss', + 'L' => 'D/M/yy', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM, YYYY, a h:mm', + 'LLLL' => 'dddd, D MMMM, YYYY, a h:mm', + ], + 'months' => ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆகஸ்ட்', 'செப்டம்பர்', 'அக்டோபர்', 'நவம்பர்', 'டிசம்பர்'], + 'months_short' => ['ஜன.', 'பிப்.', 'மார்.', 'ஏப்.', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக.', 'செப்.', 'அக்.', 'நவ.', 'டிச.'], + 'weekdays' => ['ஞாயிறு', 'திங்கள்', 'செவ்வாய்', 'புதன்', 'வியாழன்', 'வெள்ளி', 'சனி'], + 'weekdays_short' => ['ஞாயி.', 'திங்.', 'செவ்.', 'புத.', 'வியா.', 'வெள்.', 'சனி'], + 'weekdays_min' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'], + 'meridiem' => ['மு.ப', 'பி.ப'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tcy.php b/vendor/nesbot/carbon/src/Carbon/Lang/tcy.php new file mode 100644 index 0000000..2eb9905 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tcy.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/tcy_IN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tcy_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/tcy_IN.php new file mode 100644 index 0000000..2ff20e0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tcy_IN.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IndLinux.org, Samsung Electronics Co., Ltd. alexey.merzlyakov@samsung.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['ಜನವರಿ', 'ಫೆಬ್ರುವರಿ', 'ಮಾರ್ಚ್', 'ಏಪ್ರಿಲ್‌‌', 'ಮೇ', 'ಜೂನ್', 'ಜುಲೈ', 'ಆಗಸ್ಟ್', 'ಸೆಪ್ಟೆಂಬರ್‌', 'ಅಕ್ಟೋಬರ್', 'ನವೆಂಬರ್', 'ಡಿಸೆಂಬರ್'], + 'months_short' => ['ಜ', 'ಫೆ', 'ಮಾ', 'ಏ', 'ಮೇ', 'ಜೂ', 'ಜು', 'ಆ', 'ಸೆ', 'ಅ', 'ನ', 'ಡಿ'], + 'weekdays' => ['ಐಥಾರ', 'ಸೋಮಾರ', 'ಅಂಗರೆ', 'ಬುಧಾರ', 'ಗುರುವಾರ', 'ಶುಕ್ರರ', 'ಶನಿವಾರ'], + 'weekdays_short' => ['ಐ', 'ಸೋ', 'ಅಂ', 'ಬು', 'ಗು', 'ಶು', 'ಶ'], + 'weekdays_min' => ['ಐ', 'ಸೋ', 'ಅಂ', 'ಬು', 'ಗು', 'ಶು', 'ಶ'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ಕಾಂಡೆ', 'ಬಯ್ಯ'], + + 'year' => ':count ನೀರ್', // less reliable + 'y' => ':count ನೀರ್', // less reliable + 'a_year' => ':count ನೀರ್', // less reliable + + 'month' => ':count ಮೀನ್', // less reliable + 'm' => ':count ಮೀನ್', // less reliable + 'a_month' => ':count ಮೀನ್', // less reliable + + 'day' => ':count ಸುಗ್ಗಿ', // less reliable + 'd' => ':count ಸುಗ್ಗಿ', // less reliable + 'a_day' => ':count ಸುಗ್ಗಿ', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/te.php b/vendor/nesbot/carbon/src/Carbon/Lang/te.php new file mode 100644 index 0000000..ac38218 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/te.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Kunal Marwaha + * - Josh Soref + * - François B + * - kc + */ +return [ + 'year' => ':count సంవత్సరం|:count సంవత్సరాలు', + 'a_year' => 'ఒక సంవత్సరం|:count సంవత్సరాలు', + 'y' => ':count సం.', + 'month' => ':count నెల|:count నెలలు', + 'a_month' => 'ఒక నెల|:count నెలలు', + 'm' => ':count నెల|:count నెల.', + 'week' => ':count వారం|:count వారాలు', + 'a_week' => 'ఒక వారం|:count వారాలు', + 'w' => ':count వార.|:count వారా.', + 'day' => ':count రోజు|:count రోజులు', + 'a_day' => 'ఒక రోజు|:count రోజులు', + 'd' => ':count రోజు|:count రోజు.', + 'hour' => ':count గంట|:count గంటలు', + 'a_hour' => 'ఒక గంట|:count గంటలు', + 'h' => ':count గం.', + 'minute' => ':count నిమిషం|:count నిమిషాలు', + 'a_minute' => 'ఒక నిమిషం|:count నిమిషాలు', + 'min' => ':count నిమి.', + 'second' => ':count సెకను|:count సెకన్లు', + 'a_second' => 'కొన్ని క్షణాలు|:count సెకన్లు', + 's' => ':count సెక.', + 'ago' => ':time క్రితం', + 'from_now' => ':time లో', + 'diff_now' => 'ప్రస్తుతం', + 'diff_today' => 'నేడు', + 'diff_yesterday' => 'నిన్న', + 'diff_tomorrow' => 'రేపు', + 'formats' => [ + 'LT' => 'A h:mm', + 'LTS' => 'A h:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY, A h:mm', + 'LLLL' => 'dddd, D MMMM YYYY, A h:mm', + ], + 'calendar' => [ + 'sameDay' => '[నేడు] LT', + 'nextDay' => '[రేపు] LT', + 'nextWeek' => 'dddd, LT', + 'lastDay' => '[నిన్న] LT', + 'lastWeek' => '[గత] dddd, LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':numberవ', + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'రాత్రి'; + } + if ($hour < 10) { + return 'ఉదయం'; + } + if ($hour < 17) { + return 'మధ్యాహ్నం'; + } + if ($hour < 20) { + return 'సాయంత్రం'; + } + + return ' రాత్రి'; + }, + 'months' => ['జనవరి', 'ఫిబ్రవరి', 'మార్చి', 'ఏప్రిల్', 'మే', 'జూన్', 'జూలై', 'ఆగస్టు', 'సెప్టెంబర్', 'అక్టోబర్', 'నవంబర్', 'డిసెంబర్'], + 'months_short' => ['జన.', 'ఫిబ్ర.', 'మార్చి', 'ఏప్రి.', 'మే', 'జూన్', 'జూలై', 'ఆగ.', 'సెప్.', 'అక్టో.', 'నవ.', 'డిసె.'], + 'weekdays' => ['ఆదివారం', 'సోమవారం', 'మంగళవారం', 'బుధవారం', 'గురువారం', 'శుక్రవారం', 'శనివారం'], + 'weekdays_short' => ['ఆది', 'సోమ', 'మంగళ', 'బుధ', 'గురు', 'శుక్ర', 'శని'], + 'weekdays_min' => ['ఆ', 'సో', 'మం', 'బు', 'గు', 'శు', 'శ'], + 'list' => ', ', + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, + 'weekend' => [0, 0], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/te_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/te_IN.php new file mode 100644 index 0000000..3963f8d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/te_IN.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/te.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/teo.php b/vendor/nesbot/carbon/src/Carbon/Lang/teo.php new file mode 100644 index 0000000..ca30c37 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/teo.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ta.php', [ + 'meridiem' => ['Taparachu', 'Ebongi'], + 'weekdays' => ['Nakaejuma', 'Nakaebarasa', 'Nakaare', 'Nakauni', 'Nakaung’on', 'Nakakany', 'Nakasabiti'], + 'weekdays_short' => ['Jum', 'Bar', 'Aar', 'Uni', 'Ung', 'Kan', 'Sab'], + 'weekdays_min' => ['Jum', 'Bar', 'Aar', 'Uni', 'Ung', 'Kan', 'Sab'], + 'months' => ['Orara', 'Omuk', 'Okwamg’', 'Odung’el', 'Omaruk', 'Omodok’king’ol', 'Ojola', 'Opedel', 'Osokosokoma', 'Otibar', 'Olabor', 'Opoo'], + 'months_short' => ['Rar', 'Muk', 'Kwa', 'Dun', 'Mar', 'Mod', 'Jol', 'Ped', 'Sok', 'Tib', 'Lab', 'Poo'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/teo_KE.php b/vendor/nesbot/carbon/src/Carbon/Lang/teo_KE.php new file mode 100644 index 0000000..010a04f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/teo_KE.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/teo.php', [ + 'first_day_of_week' => 0, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tet.php b/vendor/nesbot/carbon/src/Carbon/Lang/tet.php new file mode 100644 index 0000000..d0544d4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tet.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Joshua Brooks + * - François B + */ +return [ + 'year' => 'tinan :count', + 'a_year' => '{1}tinan ida|tinan :count', + 'month' => 'fulan :count', + 'a_month' => '{1}fulan ida|fulan :count', + 'week' => 'semana :count', + 'a_week' => '{1}semana ida|semana :count', + 'day' => 'loron :count', + 'a_day' => '{1}loron ida|loron :count', + 'hour' => 'oras :count', + 'a_hour' => '{1}oras ida|oras :count', + 'minute' => 'minutu :count', + 'a_minute' => '{1}minutu ida|minutu :count', + 'second' => 'segundu :count', + 'a_second' => '{1}segundu balun|segundu :count', + 'ago' => ':time liuba', + 'from_now' => 'iha :time', + 'diff_yesterday' => 'Horiseik', + 'diff_yesterday_regexp' => 'Horiseik(?:\\s+iha)?', + 'diff_today' => 'Ohin', + 'diff_today_regexp' => 'Ohin(?:\\s+iha)?', + 'diff_tomorrow' => 'Aban', + 'diff_tomorrow_regexp' => 'Aban(?:\\s+iha)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Ohin iha] LT', + 'nextDay' => '[Aban iha] LT', + 'nextWeek' => 'dddd [iha] LT', + 'lastDay' => '[Horiseik iha] LT', + 'lastWeek' => 'dddd [semana kotuk] [iha] LT', + 'sameElse' => 'L', + ], + 'ordinal' => ':numberº', + 'months' => ['Janeiru', 'Fevereiru', 'Marsu', 'Abril', 'Maiu', 'Juñu', 'Jullu', 'Agustu', 'Setembru', 'Outubru', 'Novembru', 'Dezembru'], + 'months_short' => ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'], + 'weekdays' => ['Domingu', 'Segunda', 'Tersa', 'Kuarta', 'Kinta', 'Sesta', 'Sabadu'], + 'weekdays_short' => ['Dom', 'Seg', 'Ters', 'Kua', 'Kint', 'Sest', 'Sab'], + 'weekdays_min' => ['Do', 'Seg', 'Te', 'Ku', 'Ki', 'Ses', 'Sa'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tg.php b/vendor/nesbot/carbon/src/Carbon/Lang/tg.php new file mode 100644 index 0000000..b7df893 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tg.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Orif N. Jr + */ +return [ + 'year' => '{1}як сол|:count сол', + 'month' => '{1}як моҳ|:count моҳ', + 'week' => '{1}як ҳафта|:count ҳафта', + 'day' => '{1}як рӯз|:count рӯз', + 'hour' => '{1}як соат|:count соат', + 'minute' => '{1}як дақиқа|:count дақиқа', + 'second' => '{1}якчанд сония|:count сония', + 'ago' => ':time пеш', + 'from_now' => 'баъди :time', + 'diff_today' => 'Имрӯз', + 'diff_yesterday' => 'Дирӯз', + 'diff_yesterday_regexp' => 'Дирӯз(?:\\s+соати)?', + 'diff_tomorrow' => 'Пагоҳ', + 'diff_tomorrow_regexp' => 'Пагоҳ(?:\\s+соати)?', + 'diff_today_regexp' => 'Имрӯз(?:\\s+соати)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Имрӯз соати] LT', + 'nextDay' => '[Пагоҳ соати] LT', + 'nextWeek' => 'dddd[и] [ҳафтаи оянда соати] LT', + 'lastDay' => '[Дирӯз соати] LT', + 'lastWeek' => 'dddd[и] [ҳафтаи гузашта соати] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number) { + if ($number === 0) { // special case for zero + return "$number-ıncı"; + } + + static $suffixes = [ + 0 => '-ум', + 1 => '-ум', + 2 => '-юм', + 3 => '-юм', + 4 => '-ум', + 5 => '-ум', + 6 => '-ум', + 7 => '-ум', + 8 => '-ум', + 9 => '-ум', + 10 => '-ум', + 12 => '-ум', + 13 => '-ум', + 20 => '-ум', + 30 => '-юм', + 40 => '-ум', + 50 => '-ум', + 60 => '-ум', + 70 => '-ум', + 80 => '-ум', + 90 => '-ум', + 100 => '-ум', + ]; + + return $number.($suffixes[$number] ?? $suffixes[$number % 10] ?? $suffixes[$number >= 100 ? 100 : -1] ?? ''); + }, + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'шаб'; + } + if ($hour < 11) { + return 'субҳ'; + } + if ($hour < 16) { + return 'рӯз'; + } + if ($hour < 19) { + return 'бегоҳ'; + } + + return 'шаб'; + }, + 'months' => ['январ', 'феврал', 'март', 'апрел', 'май', 'июн', 'июл', 'август', 'сентябр', 'октябр', 'ноябр', 'декабр'], + 'months_short' => ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'], + 'weekdays' => ['якшанбе', 'душанбе', 'сешанбе', 'чоршанбе', 'панҷшанбе', 'ҷумъа', 'шанбе'], + 'weekdays_short' => ['яшб', 'дшб', 'сшб', 'чшб', 'пшб', 'ҷум', 'шнб'], + 'weekdays_min' => ['яш', 'дш', 'сш', 'чш', 'пш', 'ҷм', 'шб'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' ва '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tg_TJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/tg_TJ.php new file mode 100644 index 0000000..badc7d1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tg_TJ.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/tg.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/th.php b/vendor/nesbot/carbon/src/Carbon/Lang/th.php new file mode 100644 index 0000000..6397f6e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/th.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Nate Whittaker + * - John MacAslan + * - Chanintorn Asavavichairoj + * - JD Isaacks + * - ROKAISAKKON + * - RO'KAISAKKON + * - Andreas Möller + * - nithisa + */ +return [ + 'year' => ':count ปี', + 'y' => ':count ปี', + 'month' => ':count เดือน', + 'm' => ':count เดือน', + 'week' => ':count สัปดาห์', + 'w' => ':count สัปดาห์', + 'day' => ':count วัน', + 'd' => ':count วัน', + 'hour' => ':count ชั่วโมง', + 'h' => ':count ชั่วโมง', + 'minute' => ':count นาที', + 'min' => ':count นาที', + 'second' => ':count วินาที', + 'a_second' => '{1}ไม่กี่วินาที|]1,Inf[:count วินาที', + 's' => ':count วินาที', + 'ago' => ':timeที่แล้ว', + 'from_now' => 'อีก :time', + 'after' => ':timeหลังจากนี้', + 'before' => ':timeก่อน', + 'diff_now' => 'ขณะนี้', + 'diff_today' => 'วันนี้', + 'diff_today_regexp' => 'วันนี้(?:\\s+เวลา)?', + 'diff_yesterday' => 'เมื่อวาน', + 'diff_yesterday_regexp' => 'เมื่อวานนี้(?:\\s+เวลา)?', + 'diff_tomorrow' => 'พรุ่งนี้', + 'diff_tomorrow_regexp' => 'พรุ่งนี้(?:\\s+เวลา)?', + 'formats' => [ + 'LT' => 'H:mm', + 'LTS' => 'H:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY เวลา H:mm', + 'LLLL' => 'วันddddที่ D MMMM YYYY เวลา H:mm', + ], + 'calendar' => [ + 'sameDay' => '[วันนี้ เวลา] LT', + 'nextDay' => '[พรุ่งนี้ เวลา] LT', + 'nextWeek' => 'dddd[หน้า เวลา] LT', + 'lastDay' => '[เมื่อวานนี้ เวลา] LT', + 'lastWeek' => '[วัน]dddd[ที่แล้ว เวลา] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['ก่อนเที่ยง', 'หลังเที่ยง'], + 'months' => ['มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม'], + 'months_short' => ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'], + 'weekdays' => ['อาทิตย์', 'จันทร์', 'อังคาร', 'พุธ', 'พฤหัสบดี', 'ศุกร์', 'เสาร์'], + 'weekdays_short' => ['อาทิตย์', 'จันทร์', 'อังคาร', 'พุธ', 'พฤหัส', 'ศุกร์', 'เสาร์'], + 'weekdays_min' => ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'], + 'list' => [', ', ' และ '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/th_TH.php b/vendor/nesbot/carbon/src/Carbon/Lang/th_TH.php new file mode 100644 index 0000000..b9f94b2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/th_TH.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/th.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/the.php b/vendor/nesbot/carbon/src/Carbon/Lang/the.php new file mode 100644 index 0000000..85f8333 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/the.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/the_NP.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/the_NP.php b/vendor/nesbot/carbon/src/Carbon/Lang/the_NP.php new file mode 100644 index 0000000..34da162 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/the_NP.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Chitwanix OS Development info@chitwanix.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'dddd DD MMM YYYY', + ], + 'months' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'], + 'months_short' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'], + 'weekdays' => ['आइतबार', 'सोमबार', 'मंगलबार', 'बुधबार', 'बिहिबार', 'शुक्रबार', 'शनिबार'], + 'weekdays_short' => ['आइत', 'सोम', 'मंगल', 'बुध', 'बिहि', 'शुक्र', 'शनि'], + 'weekdays_min' => ['आइत', 'सोम', 'मंगल', 'बुध', 'बिहि', 'शुक्र', 'शनि'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['पूर्वाह्न', 'अपराह्न'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ti.php b/vendor/nesbot/carbon/src/Carbon/Lang/ti.php new file mode 100644 index 0000000..ffd3236 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ti.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/ti_ER.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ti_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/ti_ER.php new file mode 100644 index 0000000..310c51c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ti_ER.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['ጥሪ', 'ለካቲት', 'መጋቢት', 'ሚያዝያ', 'ግንቦት', 'ሰነ', 'ሓምለ', 'ነሓሰ', 'መስከረም', 'ጥቅምቲ', 'ሕዳር', 'ታሕሳስ'], + 'months_short' => ['ጥሪ ', 'ለካቲ', 'መጋቢ', 'ሚያዝ', 'ግንቦ', 'ሰነ ', 'ሓምለ', 'ነሓሰ', 'መስከ', 'ጥቅም', 'ሕዳር', 'ታሕሳ'], + 'weekdays' => ['ሰንበት', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'], + 'weekdays_short' => ['ሰንበ', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'], + 'weekdays_min' => ['ሰንበ', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ንጉሆ ሰዓተ', 'ድሕር ሰዓት'], + + 'year' => ':count ዓመት', + 'y' => ':count ዓመት', + 'a_year' => ':count ዓመት', + + 'month' => 'ወርሒ :count', + 'm' => 'ወርሒ :count', + 'a_month' => 'ወርሒ :count', + + 'week' => ':count ሰሙን', + 'w' => ':count ሰሙን', + 'a_week' => ':count ሰሙን', + + 'day' => ':count መዓልቲ', + 'd' => ':count መዓልቲ', + 'a_day' => ':count መዓልቲ', + + 'hour' => ':count ሰዓት', + 'h' => ':count ሰዓት', + 'a_hour' => ':count ሰዓት', + + 'minute' => ':count ደቒቕ', + 'min' => ':count ደቒቕ', + 'a_minute' => ':count ደቒቕ', + + 'second' => ':count ሰከንድ', + 's' => ':count ሰከንድ', + 'a_second' => ':count ሰከንድ', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ti_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/ti_ET.php new file mode 100644 index 0000000..024217f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ti_ET.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች', 'ኤፕረል', 'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት', 'ሴፕቴምበር', 'ኦክተውበር', 'ኖቬምበር', 'ዲሴምበር'], + 'months_short' => ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕረ', 'ሜይ ', 'ጁን ', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክተ', 'ኖቬም', 'ዲሴም'], + 'weekdays' => ['ሰንበት', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'], + 'weekdays_short' => ['ሰንበ', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'], + 'weekdays_min' => ['ሰንበ', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ንጉሆ ሰዓተ', 'ድሕር ሰዓት'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tig.php b/vendor/nesbot/carbon/src/Carbon/Lang/tig.php new file mode 100644 index 0000000..186fe71 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tig.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/tig_ER.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tig_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/tig_ER.php new file mode 100644 index 0000000..46887b0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tig_ER.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['ጥሪ', 'ለካቲት', 'መጋቢት', 'ሚያዝያ', 'ግንቦት', 'ሰነ', 'ሓምለ', 'ነሓሰ', 'መስከረም', 'ጥቅምቲ', 'ሕዳር', 'ታሕሳስ'], + 'months_short' => ['ጥሪ ', 'ለካቲ', 'መጋቢ', 'ሚያዝ', 'ግንቦ', 'ሰነ ', 'ሓምለ', 'ነሓሰ', 'መስከ', 'ጥቅም', 'ሕዳር', 'ታሕሳ'], + 'weekdays' => ['ሰንበት ዓባይ', 'ሰኖ', 'ታላሸኖ', 'ኣረርባዓ', 'ከሚሽ', 'ጅምዓት', 'ሰንበት ንኢሽ'], + 'weekdays_short' => ['ሰ//ዓ', 'ሰኖ ', 'ታላሸ', 'ኣረር', 'ከሚሽ', 'ጅምዓ', 'ሰ//ን'], + 'weekdays_min' => ['ሰ//ዓ', 'ሰኖ ', 'ታላሸ', 'ኣረር', 'ከሚሽ', 'ጅምዓ', 'ሰ//ን'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ቀደም ሰር ምዕል', 'ሓቆ ሰር ምዕል'], + + 'year' => ':count ማይ', // less reliable + 'y' => ':count ማይ', // less reliable + 'a_year' => ':count ማይ', // less reliable + + 'month' => ':count ሸምሽ', // less reliable + 'm' => ':count ሸምሽ', // less reliable + 'a_month' => ':count ሸምሽ', // less reliable + + 'week' => ':count ሰቡዕ', // less reliable + 'w' => ':count ሰቡዕ', // less reliable + 'a_week' => ':count ሰቡዕ', // less reliable + + 'day' => ':count ዎሮ', // less reliable + 'd' => ':count ዎሮ', // less reliable + 'a_day' => ':count ዎሮ', // less reliable + + 'hour' => ':count ሰዓት', // less reliable + 'h' => ':count ሰዓት', // less reliable + 'a_hour' => ':count ሰዓት', // less reliable + + 'minute' => ':count ካልኣይት', // less reliable + 'min' => ':count ካልኣይት', // less reliable + 'a_minute' => ':count ካልኣይት', // less reliable + + 'second' => ':count ካልኣይ', + 's' => ':count ካልኣይ', + 'a_second' => ':count ካልኣይ', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tk.php b/vendor/nesbot/carbon/src/Carbon/Lang/tk.php new file mode 100644 index 0000000..d8f7d19 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tk.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/tk_TM.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tk_TM.php b/vendor/nesbot/carbon/src/Carbon/Lang/tk_TM.php new file mode 100644 index 0000000..f949a43 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tk_TM.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Authors: + * - Ghorban M. Tavakoly Pablo Saratxaga & Ghorban M. Tavakoly pablo@walon.org & gmt314@yahoo.com + * - SuperManPHP + * - Maksat Meredow (isadma) + */ +$transformDiff = function ($input) { + return strtr($input, [ + 'sekunt' => 'sekunt', + 'hepde' => 'hepde', + ]); +}; + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['Ýanwar', 'Fewral', 'Mart', 'Aprel', 'Maý', 'Iýun', 'Iýul', 'Awgust', 'Sentýabr', 'Oktýabr', 'Noýabr', 'Dekabr'], + 'months_short' => ['Ýan', 'Few', 'Mar', 'Apr', 'Maý', 'Iýn', 'Iýl', 'Awg', 'Sen', 'Okt', 'Noý', 'Dek'], + 'weekdays' => ['Duşenbe', 'Sişenbe', 'Çarşenbe', 'Penşenbe', 'Anna', 'Şenbe', 'Ýekşenbe'], + 'weekdays_short' => ['Duş', 'Siş', 'Çar', 'Pen', 'Ann', 'Şen', 'Ýek'], + 'weekdays_min' => ['Du', 'Si', 'Ça', 'Pe', 'An', 'Şe', 'Ýe'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + + 'year' => ':count ýyl', + 'y' => ':count ýyl', + 'a_year' => ':count ýyl', + + 'month' => ':count aý', + 'm' => ':count aý', + 'a_month' => ':count aý', + + 'week' => ':count hepde', + 'w' => ':count hepde', + 'a_week' => ':count hepde', + + 'day' => ':count gün', + 'd' => ':count gün', + 'a_day' => ':count gün', + + 'hour' => ':count sagat', + 'h' => ':count sagat', + 'a_hour' => ':count sagat', + + 'minute' => ':count minut', + 'min' => ':count minut', + 'a_minute' => ':count minut', + + 'second' => ':count sekunt', + 's' => ':count sekunt', + 'a_second' => ':count sekunt', + + 'ago' => function ($time) use ($transformDiff) { + return $transformDiff($time).' ozal'; + }, + 'from_now' => function ($time) use ($transformDiff) { + return $transformDiff($time).' soňra'; + }, + 'after' => function ($time) use ($transformDiff) { + return $transformDiff($time).' soň'; + }, + 'before' => function ($time) use ($transformDiff) { + return $transformDiff($time).' öň'; + }, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tl.php b/vendor/nesbot/carbon/src/Carbon/Lang/tl.php new file mode 100644 index 0000000..410a266 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tl.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'year' => ':count taon', + 'a_year' => '{1}isang taon|:count taon', + 'month' => ':count buwan', + 'a_month' => '{1}isang buwan|:count buwan', + 'week' => ':count linggo', + 'a_week' => '{1}isang linggo|:count linggo', + 'day' => ':count araw', + 'a_day' => '{1}isang araw|:count araw', + 'hour' => ':count oras', + 'a_hour' => '{1}isang oras|:count oras', + 'minute' => ':count minuto', + 'a_minute' => '{1}isang minuto|:count minuto', + 'min' => ':count min.', + 'second' => ':count segundo', + 'a_second' => '{1}ilang segundo|:count segundo', + 's' => ':count seg.', + 'ago' => ':time ang nakalipas', + 'from_now' => 'sa loob ng :time', + 'diff_now' => 'ngayon', + 'diff_today' => 'ngayong', + 'diff_today_regexp' => 'ngayong(?:\\s+araw)?', + 'diff_yesterday' => 'kahapon', + 'diff_tomorrow' => 'bukas', + 'diff_tomorrow_regexp' => 'Bukas(?:\\s+ng)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'MM/D/YYYY', + 'LL' => 'MMMM D, YYYY', + 'LLL' => 'MMMM D, YYYY HH:mm', + 'LLLL' => 'dddd, MMMM DD, YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => 'LT [ngayong araw]', + 'nextDay' => '[Bukas ng] LT', + 'nextWeek' => 'LT [sa susunod na] dddd', + 'lastDay' => 'LT [kahapon]', + 'lastWeek' => 'LT [noong nakaraang] dddd', + 'sameElse' => 'L', + ], + 'months' => ['Enero', 'Pebrero', 'Marso', 'Abril', 'Mayo', 'Hunyo', 'Hulyo', 'Agosto', 'Setyembre', 'Oktubre', 'Nobyembre', 'Disyembre'], + 'months_short' => ['Ene', 'Peb', 'Mar', 'Abr', 'May', 'Hun', 'Hul', 'Ago', 'Set', 'Okt', 'Nob', 'Dis'], + 'weekdays' => ['Linggo', 'Lunes', 'Martes', 'Miyerkules', 'Huwebes', 'Biyernes', 'Sabado'], + 'weekdays_short' => ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'], + 'weekdays_min' => ['Li', 'Lu', 'Ma', 'Mi', 'Hu', 'Bi', 'Sab'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' at '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tl_PH.php b/vendor/nesbot/carbon/src/Carbon/Lang/tl_PH.php new file mode 100644 index 0000000..95f508c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tl_PH.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Ian De La Cruz + * - JD Isaacks + */ +return require __DIR__.'/tl.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tlh.php b/vendor/nesbot/carbon/src/Carbon/Lang/tlh.php new file mode 100644 index 0000000..fbf9e6f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tlh.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Serhan Apaydın + * - Dominika + */ +return [ + 'year' => '{1}wa’ DIS|:count DIS', + 'month' => '{1}wa’ jar|:count jar', + 'week' => '{1}wa’ hogh|:count hogh', + 'day' => '{1}wa’ jaj|:count jaj', + 'hour' => '{1}wa’ rep|:count rep', + 'minute' => '{1}wa’ tup|:count tup', + 'second' => '{1}puS lup|:count lup', + 'ago' => function ($time) { + $output = strtr($time, [ + 'jaj' => 'Hu’', + 'jar' => 'wen', + 'DIS' => 'ben', + ]); + + return $output === $time ? "$time ret" : $output; + }, + 'from_now' => function ($time) { + $output = strtr($time, [ + 'jaj' => 'leS', + 'jar' => 'waQ', + 'DIS' => 'nem', + ]); + + return $output === $time ? "$time pIq" : $output; + }, + 'diff_yesterday' => 'wa’Hu’', + 'diff_today' => 'DaHjaj', + 'diff_tomorrow' => 'wa’leS', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[DaHjaj] LT', + 'nextDay' => '[wa’leS] LT', + 'nextWeek' => 'LLL', + 'lastDay' => '[wa’Hu’] LT', + 'lastWeek' => 'LLL', + 'sameElse' => 'L', + ], + 'ordinal' => ':number.', + 'months' => ['tera’ jar wa’', 'tera’ jar cha’', 'tera’ jar wej', 'tera’ jar loS', 'tera’ jar vagh', 'tera’ jar jav', 'tera’ jar Soch', 'tera’ jar chorgh', 'tera’ jar Hut', 'tera’ jar wa’maH', 'tera’ jar wa’maH wa’', 'tera’ jar wa’maH cha’'], + 'months_short' => ['jar wa’', 'jar cha’', 'jar wej', 'jar loS', 'jar vagh', 'jar jav', 'jar Soch', 'jar chorgh', 'jar Hut', 'jar wa’maH', 'jar wa’maH wa’', 'jar wa’maH cha’'], + 'weekdays' => ['lojmItjaj', 'DaSjaj', 'povjaj', 'ghItlhjaj', 'loghjaj', 'buqjaj', 'ghInjaj'], + 'weekdays_short' => ['lojmItjaj', 'DaSjaj', 'povjaj', 'ghItlhjaj', 'loghjaj', 'buqjaj', 'ghInjaj'], + 'weekdays_min' => ['lojmItjaj', 'DaSjaj', 'povjaj', 'ghItlhjaj', 'loghjaj', 'buqjaj', 'ghInjaj'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' ’ej '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tn.php b/vendor/nesbot/carbon/src/Carbon/Lang/tn.php new file mode 100644 index 0000000..f29bdf6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tn.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/tn_ZA.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tn_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/tn_ZA.php new file mode 100644 index 0000000..aada7db --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tn_ZA.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Ferikgong', 'Tlhakole', 'Mopitlwe', 'Moranang', 'Motsheganong', 'Seetebosigo', 'Phukwi', 'Phatwe', 'Lwetse', 'Diphalane', 'Ngwanatsele', 'Sedimonthole'], + 'months_short' => ['Fer', 'Tlh', 'Mop', 'Mor', 'Mot', 'See', 'Phu', 'Pha', 'Lwe', 'Dip', 'Ngw', 'Sed'], + 'weekdays' => ['laTshipi', 'Mosupologo', 'Labobedi', 'Laboraro', 'Labone', 'Labotlhano', 'Lamatlhatso'], + 'weekdays_short' => ['Tsh', 'Mos', 'Bed', 'Rar', 'Ne', 'Tlh', 'Mat'], + 'weekdays_min' => ['Tsh', 'Mos', 'Bed', 'Rar', 'Ne', 'Tlh', 'Mat'], + 'day_of_first_week_of_year' => 1, + + 'year' => 'dingwaga di le :count', + 'y' => 'dingwaga di le :count', + 'a_year' => 'dingwaga di le :count', + + 'month' => 'dikgwedi di le :count', + 'm' => 'dikgwedi di le :count', + 'a_month' => 'dikgwedi di le :count', + + 'week' => 'dibeke di le :count', + 'w' => 'dibeke di le :count', + 'a_week' => 'dibeke di le :count', + + 'day' => 'malatsi :count', + 'd' => 'malatsi :count', + 'a_day' => 'malatsi :count', + + 'hour' => 'diura di le :count', + 'h' => 'diura di le :count', + 'a_hour' => 'diura di le :count', + + 'minute' => 'metsotso e le :count', + 'min' => 'metsotso e le :count', + 'a_minute' => 'metsotso e le :count', + + 'second' => 'metsotswana e le :count', + 's' => 'metsotswana e le :count', + 'a_second' => 'metsotswana e le :count', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/to.php b/vendor/nesbot/carbon/src/Carbon/Lang/to.php new file mode 100644 index 0000000..20581bb --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/to.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/to_TO.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/to_TO.php b/vendor/nesbot/carbon/src/Carbon/Lang/to_TO.php new file mode 100644 index 0000000..335c69a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/to_TO.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - International Components for Unicode akhilesh.k@samsung.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'dddd DD MMM YYYY', + ], + 'months' => ['Sānuali', 'Fēpueli', 'Maʻasi', 'ʻEpeleli', 'Mē', 'Sune', 'Siulai', 'ʻAokosi', 'Sepitema', 'ʻOkatopa', 'Nōvema', 'Tīsema'], + 'months_short' => ['Sān', 'Fēp', 'Maʻa', 'ʻEpe', 'Mē', 'Sun', 'Siu', 'ʻAok', 'Sep', 'ʻOka', 'Nōv', 'Tīs'], + 'weekdays' => ['Sāpate', 'Mōnite', 'Tūsite', 'Pulelulu', 'Tuʻapulelulu', 'Falaite', 'Tokonaki'], + 'weekdays_short' => ['Sāp', 'Mōn', 'Tūs', 'Pul', 'Tuʻa', 'Fal', 'Tok'], + 'weekdays_min' => ['Sāp', 'Mōn', 'Tūs', 'Pul', 'Tuʻa', 'Fal', 'Tok'], + 'meridiem' => ['hengihengi', 'efiafi'], + + 'year' => ':count fitu', // less reliable + 'y' => ':count fitu', // less reliable + 'a_year' => ':count fitu', // less reliable + + 'month' => ':count mahina', // less reliable + 'm' => ':count mahina', // less reliable + 'a_month' => ':count mahina', // less reliable + + 'week' => ':count Sapate', // less reliable + 'w' => ':count Sapate', // less reliable + 'a_week' => ':count Sapate', // less reliable + + 'day' => ':count ʻaho', // less reliable + 'd' => ':count ʻaho', // less reliable + 'a_day' => ':count ʻaho', // less reliable + + 'hour' => ':count houa', + 'h' => ':count houa', + 'a_hour' => ':count houa', + + 'minute' => ':count miniti', + 'min' => ':count miniti', + 'a_minute' => ':count miniti', + + 'second' => ':count sekoni', + 's' => ':count sekoni', + 'a_second' => ':count sekoni', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tpi.php b/vendor/nesbot/carbon/src/Carbon/Lang/tpi.php new file mode 100644 index 0000000..7d38dae --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tpi.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/tpi_PG.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tpi_PG.php b/vendor/nesbot/carbon/src/Carbon/Lang/tpi_PG.php new file mode 100644 index 0000000..5f58c44 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tpi_PG.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Janueri', 'Februeri', 'Mas', 'Epril', 'Me', 'Jun', 'Julai', 'Ogas', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'], + 'months_short' => ['Jan', 'Feb', 'Mas', 'Epr', 'Me', 'Jun', 'Jul', 'Oga', 'Sep', 'Okt', 'Nov', 'Des'], + 'weekdays' => ['Sande', 'Mande', 'Tunde', 'Trinde', 'Fonde', 'Fraide', 'Sarere'], + 'weekdays_short' => ['San', 'Man', 'Tun', 'Tri', 'Fon', 'Fra', 'Sar'], + 'weekdays_min' => ['San', 'Man', 'Tun', 'Tri', 'Fon', 'Fra', 'Sar'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['biknait', 'apinun'], + + 'year' => 'yia :count', + 'y' => 'yia :count', + 'a_year' => 'yia :count', + + 'month' => ':count mun', + 'm' => ':count mun', + 'a_month' => ':count mun', + + 'week' => ':count wik', + 'w' => ':count wik', + 'a_week' => ':count wik', + + 'day' => ':count de', + 'd' => ':count de', + 'a_day' => ':count de', + + 'hour' => ':count aua', + 'h' => ':count aua', + 'a_hour' => ':count aua', + + 'minute' => ':count minit', + 'min' => ':count minit', + 'a_minute' => ':count minit', + + 'second' => ':count namba tu', + 's' => ':count namba tu', + 'a_second' => ':count namba tu', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tr.php b/vendor/nesbot/carbon/src/Carbon/Lang/tr.php new file mode 100644 index 0000000..f5d9f4c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tr.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - Alan Agius + * - Erhan Gundogan + * - François B + * - JD Isaacks + * - Murat Yüksel + * - Baran Şengül + * - Selami (selamialtin) + * - TeomanBey + */ +return [ + 'year' => ':count yıl', + 'a_year' => '{1}bir yıl|]1,Inf[:count yıl', + 'y' => ':county', + 'month' => ':count ay', + 'a_month' => '{1}bir ay|]1,Inf[:count ay', + 'm' => ':countay', + 'week' => ':count hafta', + 'a_week' => '{1}bir hafta|]1,Inf[:count hafta', + 'w' => ':counth', + 'day' => ':count gün', + 'a_day' => '{1}bir gün|]1,Inf[:count gün', + 'd' => ':countg', + 'hour' => ':count saat', + 'a_hour' => '{1}bir saat|]1,Inf[:count saat', + 'h' => ':countsa', + 'minute' => ':count dakika', + 'a_minute' => '{1}bir dakika|]1,Inf[:count dakika', + 'min' => ':countdk', + 'second' => ':count saniye', + 'a_second' => '{1}birkaç saniye|]1,Inf[:count saniye', + 's' => ':countsn', + 'ago' => ':time önce', + 'from_now' => ':time sonra', + 'after' => ':time sonra', + 'before' => ':time önce', + 'diff_now' => 'şimdi', + 'diff_today' => 'bugün', + 'diff_today_regexp' => 'bugün(?:\\s+saat)?', + 'diff_yesterday' => 'dün', + 'diff_tomorrow' => 'yarın', + 'diff_tomorrow_regexp' => 'yarın(?:\\s+saat)?', + 'diff_before_yesterday' => 'evvelsi gün', + 'diff_after_tomorrow' => 'öbür gün', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[bugün saat] LT', + 'nextDay' => '[yarın saat] LT', + 'nextWeek' => '[gelecek] dddd [saat] LT', + 'lastDay' => '[dün] LT', + 'lastWeek' => '[geçen] dddd [saat] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number, $period) { + switch ($period) { + case 'd': + case 'D': + case 'Do': + case 'DD': + return $number; + default: + if ($number === 0) { // special case for zero + return "$number'ıncı"; + } + + static $suffixes = [ + 1 => '\'inci', + 5 => '\'inci', + 8 => '\'inci', + 70 => '\'inci', + 80 => '\'inci', + 2 => '\'nci', + 7 => '\'nci', + 20 => '\'nci', + 50 => '\'nci', + 3 => '\'üncü', + 4 => '\'üncü', + 100 => '\'üncü', + 6 => '\'ncı', + 9 => '\'uncu', + 10 => '\'uncu', + 30 => '\'uncu', + 60 => '\'ıncı', + 90 => '\'ıncı', + ]; + + $lastDigit = $number % 10; + + return $number.($suffixes[$lastDigit] ?? $suffixes[$number % 100 - $lastDigit] ?? $suffixes[$number >= 100 ? 100 : -1] ?? ''); + } + }, + 'meridiem' => ['ÖÖ', 'ÖS', 'öö', 'ös'], + 'months' => ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'], + 'months_short' => ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'], + 'weekdays' => ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi'], + 'weekdays_short' => ['Paz', 'Pts', 'Sal', 'Çar', 'Per', 'Cum', 'Cts'], + 'weekdays_min' => ['Pz', 'Pt', 'Sa', 'Ça', 'Pe', 'Cu', 'Ct'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' ve '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tr_CY.php b/vendor/nesbot/carbon/src/Carbon/Lang/tr_CY.php new file mode 100644 index 0000000..23f1144 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tr_CY.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/tr.php', [ + 'weekdays_short' => ['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt'], + 'weekdays_min' => ['Pa', 'Pt', 'Sa', 'Ça', 'Pe', 'Cu', 'Ct'], + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'D.MM.YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'D MMMM YYYY dddd h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tr_TR.php b/vendor/nesbot/carbon/src/Carbon/Lang/tr_TR.php new file mode 100644 index 0000000..9e99482 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tr_TR.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/tr.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ts.php b/vendor/nesbot/carbon/src/Carbon/Lang/ts.php new file mode 100644 index 0000000..525736b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ts.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/ts_ZA.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ts_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ts_ZA.php new file mode 100644 index 0000000..37a24ec --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ts_ZA.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Sunguti', 'Nyenyenyani', 'Nyenyankulu', 'Dzivamisoko', 'Mudyaxihi', 'Khotavuxika', 'Mawuwani', 'Mhawuri', 'Ndzhati', 'Nhlangula', 'Hukuri', 'N\'wendzamhala'], + 'months_short' => ['Sun', 'Yan', 'Kul', 'Dzi', 'Mud', 'Kho', 'Maw', 'Mha', 'Ndz', 'Nhl', 'Huk', 'N\'w'], + 'weekdays' => ['Sonto', 'Musumbhunuku', 'Ravumbirhi', 'Ravunharhu', 'Ravumune', 'Ravuntlhanu', 'Mugqivela'], + 'weekdays_short' => ['Son', 'Mus', 'Bir', 'Har', 'Ne', 'Tlh', 'Mug'], + 'weekdays_min' => ['Son', 'Mus', 'Bir', 'Har', 'Ne', 'Tlh', 'Mug'], + 'day_of_first_week_of_year' => 1, + + 'year' => 'malembe ya :count', + 'y' => 'malembe ya :count', + 'a_year' => 'malembe ya :count', + + 'month' => 'tin’hweti ta :count', + 'm' => 'tin’hweti ta :count', + 'a_month' => 'tin’hweti ta :count', + + 'week' => 'mavhiki ya :count', + 'w' => 'mavhiki ya :count', + 'a_week' => 'mavhiki ya :count', + + 'day' => 'masiku :count', + 'd' => 'masiku :count', + 'a_day' => 'masiku :count', + + 'hour' => 'tiawara ta :count', + 'h' => 'tiawara ta :count', + 'a_hour' => 'tiawara ta :count', + + 'minute' => 'timinete ta :count', + 'min' => 'timinete ta :count', + 'a_minute' => 'timinete ta :count', + + 'second' => 'tisekoni ta :count', + 's' => 'tisekoni ta :count', + 'a_second' => 'tisekoni ta :count', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tt.php b/vendor/nesbot/carbon/src/Carbon/Lang/tt.php new file mode 100644 index 0000000..d67d896 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tt.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/tt_RU.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tt_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/tt_RU.php new file mode 100644 index 0000000..38e42d0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tt_RU.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Rinat Norkin Pablo Saratxaga, Rinat Norkin pablo@mandrakesoft.com, rinat@taif.ru + */ +return [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'DD MMM, HH:mm', + 'LLLL' => 'DD MMMM YYYY, HH:mm', + ], + 'months' => ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'], + 'months_short' => ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'], + 'weekdays' => ['якшәмбе', 'дышәмбе', 'сишәмбе', 'чәршәәмбе', 'пәнҗешмбе', 'җомга', 'шимбә'], + 'weekdays_short' => ['якш', 'дыш', 'сиш', 'чәрш', 'пәнҗ', 'җом', 'шим'], + 'weekdays_min' => ['якш', 'дыш', 'сиш', 'чәрш', 'пәнҗ', 'җом', 'шим'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'year' => ':count ел', + 'month' => ':count ай', + 'week' => ':count атна', + 'day' => ':count көн', + 'hour' => ':count сәгать', + 'minute' => ':count минут', + 'second' => ':count секунд', +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tt_RU@iqtelif.php b/vendor/nesbot/carbon/src/Carbon/Lang/tt_RU@iqtelif.php new file mode 100644 index 0000000..16b8efb --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tt_RU@iqtelif.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Reshat Sabiq tatar.iqtelif.i18n@gmail.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD.MM.YYYY', + ], + 'months' => ['Ğınwar', 'Fiwral\'', 'Mart', 'April', 'May', 'Yün', 'Yül', 'Awgust', 'Sintebír', 'Üktebír', 'Noyebír', 'Dikebír'], + 'months_short' => ['Ğın', 'Fiw', 'Mar', 'Apr', 'May', 'Yün', 'Yül', 'Awg', 'Sin', 'Ükt', 'Noy', 'Dik'], + 'weekdays' => ['Yekşembí', 'Düşembí', 'Sişembí', 'Çerşembí', 'Pencíşembí', 'Comğa', 'Şimbe'], + 'weekdays_short' => ['Yek', 'Düş', 'Siş', 'Çer', 'Pen', 'Com', 'Şim'], + 'weekdays_min' => ['Yek', 'Düş', 'Siş', 'Çer', 'Pen', 'Com', 'Şim'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ÖA', 'ÖS'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/twq.php b/vendor/nesbot/carbon/src/Carbon/Lang/twq.php new file mode 100644 index 0000000..5cbb46e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/twq.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/ses.php', [ + 'meridiem' => ['Subbaahi', 'Zaarikay b'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tzl.php b/vendor/nesbot/carbon/src/Carbon/Lang/tzl.php new file mode 100644 index 0000000..50bf26d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tzl.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'year' => '[0,1]:count ar|:count ars', + 'y' => '[0,1]:count ar|:count ars', + 'month' => '[0,1]:count mes|:count mesen', + 'm' => '[0,1]:count mes|:count mesen', + 'week' => '[0,1]:count seifetziua|:count seifetziuas', + 'w' => '[0,1]:count seifetziua|:count seifetziuas', + 'day' => '[0,1]:count ziua|:count ziuas', + 'd' => '[0,1]:count ziua|:count ziuas', + 'hour' => '[0,1]:count þora|:count þoras', + 'h' => '[0,1]:count þora|:count þoras', + 'minute' => '[0,1]:count míut|:count míuts', + 'min' => '[0,1]:count míut|:count míuts', + 'second' => ':count secunds', + 's' => ':count secunds', + + 'ago' => 'ja :time', + 'from_now' => 'osprei :time', + + 'diff_yesterday' => 'ieiri', + 'diff_yesterday_regexp' => 'ieiri(?:\\s+à)?', + 'diff_today' => 'oxhi', + 'diff_today_regexp' => 'oxhi(?:\\s+à)?', + 'diff_tomorrow' => 'demà', + 'diff_tomorrow_regexp' => 'demà(?:\\s+à)?', + + 'formats' => [ + 'LT' => 'HH.mm', + 'LTS' => 'HH.mm.ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D. MMMM [dallas] YYYY', + 'LLL' => 'D. MMMM [dallas] YYYY HH.mm', + 'LLLL' => 'dddd, [li] D. MMMM [dallas] YYYY HH.mm', + ], + + 'calendar' => [ + 'sameDay' => '[oxhi à] LT', + 'nextDay' => '[demà à] LT', + 'nextWeek' => 'dddd [à] LT', + 'lastDay' => '[ieiri à] LT', + 'lastWeek' => '[sür el] dddd [lasteu à] LT', + 'sameElse' => 'L', + ], + + 'meridiem' => ["D'A", "D'O"], + 'months' => ['Januar', 'Fevraglh', 'Març', 'Avrïu', 'Mai', 'Gün', 'Julia', 'Guscht', 'Setemvar', 'Listopäts', 'Noemvar', 'Zecemvar'], + 'months_short' => ['Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Gün', 'Jul', 'Gus', 'Set', 'Lis', 'Noe', 'Zec'], + 'weekdays' => ['Súladi', 'Lúneçi', 'Maitzi', 'Márcuri', 'Xhúadi', 'Viénerçi', 'Sáturi'], + 'weekdays_short' => ['Súl', 'Lún', 'Mai', 'Már', 'Xhú', 'Vié', 'Sát'], + 'weekdays_min' => ['Sú', 'Lú', 'Ma', 'Má', 'Xh', 'Vi', 'Sá'], + 'ordinal' => ':number.', + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tzm.php b/vendor/nesbot/carbon/src/Carbon/Lang/tzm.php new file mode 100644 index 0000000..2a1a0f2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tzm.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - JD Isaacks + */ +return [ + 'year' => '{1}ⴰⵙⴳⴰⵙ|:count ⵉⵙⴳⴰⵙⵏ', + 'month' => '{1}ⴰⵢoⵓⵔ|:count ⵉⵢⵢⵉⵔⵏ', + 'week' => ':count ⵉⵎⴰⵍⴰⵙⵙ', + 'day' => '{1}ⴰⵙⵙ|:count oⵙⵙⴰⵏ', + 'hour' => '{1}ⵙⴰⵄⴰ|:count ⵜⴰⵙⵙⴰⵄⵉⵏ', + 'minute' => '{1}ⵎⵉⵏⵓⴺ|:count ⵎⵉⵏⵓⴺ', + 'second' => '{1}ⵉⵎⵉⴽ|:count ⵉⵎⵉⴽ', + 'ago' => 'ⵢⴰⵏ :time', + 'from_now' => 'ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ :time', + 'diff_today' => 'ⴰⵙⴷⵅ', + 'diff_yesterday' => 'ⴰⵚⴰⵏⵜ', + 'diff_yesterday_regexp' => 'ⴰⵚⴰⵏⵜ(?:\\s+ⴴ)?', + 'diff_tomorrow' => 'ⴰⵙⴽⴰ', + 'diff_tomorrow_regexp' => 'ⴰⵙⴽⴰ(?:\\s+ⴴ)?', + 'diff_today_regexp' => 'ⴰⵙⴷⵅ(?:\\s+ⴴ)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[ⴰⵙⴷⵅ ⴴ] LT', + 'nextDay' => '[ⴰⵙⴽⴰ ⴴ] LT', + 'nextWeek' => 'dddd [ⴴ] LT', + 'lastDay' => '[ⴰⵚⴰⵏⵜ ⴴ] LT', + 'lastWeek' => 'dddd [ⴴ] LT', + 'sameElse' => 'L', + ], + 'months' => ['ⵉⵏⵏⴰⵢⵔ', 'ⴱⵕⴰⵢⵕ', 'ⵎⴰⵕⵚ', 'ⵉⴱⵔⵉⵔ', 'ⵎⴰⵢⵢⵓ', 'ⵢⵓⵏⵢⵓ', 'ⵢⵓⵍⵢⵓⵣ', 'ⵖⵓⵛⵜ', 'ⵛⵓⵜⴰⵏⴱⵉⵔ', 'ⴽⵟⵓⴱⵕ', 'ⵏⵓⵡⴰⵏⴱⵉⵔ', 'ⴷⵓⵊⵏⴱⵉⵔ'], + 'months_short' => ['ⵉⵏⵏⴰⵢⵔ', 'ⴱⵕⴰⵢⵕ', 'ⵎⴰⵕⵚ', 'ⵉⴱⵔⵉⵔ', 'ⵎⴰⵢⵢⵓ', 'ⵢⵓⵏⵢⵓ', 'ⵢⵓⵍⵢⵓⵣ', 'ⵖⵓⵛⵜ', 'ⵛⵓⵜⴰⵏⴱⵉⵔ', 'ⴽⵟⵓⴱⵕ', 'ⵏⵓⵡⴰⵏⴱⵉⵔ', 'ⴷⵓⵊⵏⴱⵉⵔ'], + 'weekdays' => ['ⴰⵙⴰⵎⴰⵙ', 'ⴰⵢⵏⴰⵙ', 'ⴰⵙⵉⵏⴰⵙ', 'ⴰⴽⵔⴰⵙ', 'ⴰⴽⵡⴰⵙ', 'ⴰⵙⵉⵎⵡⴰⵙ', 'ⴰⵙⵉⴹⵢⴰⵙ'], + 'weekdays_short' => ['ⴰⵙⴰⵎⴰⵙ', 'ⴰⵢⵏⴰⵙ', 'ⴰⵙⵉⵏⴰⵙ', 'ⴰⴽⵔⴰⵙ', 'ⴰⴽⵡⴰⵙ', 'ⴰⵙⵉⵎⵡⴰⵙ', 'ⴰⵙⵉⴹⵢⴰⵙ'], + 'weekdays_min' => ['ⴰⵙⴰⵎⴰⵙ', 'ⴰⵢⵏⴰⵙ', 'ⴰⵙⵉⵏⴰⵙ', 'ⴰⴽⵔⴰⵙ', 'ⴰⴽⵡⴰⵙ', 'ⴰⵙⵉⵎⵡⴰⵙ', 'ⴰⵙⵉⴹⵢⴰⵙ'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, + 'weekend' => [5, 6], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tzm_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/tzm_Latn.php new file mode 100644 index 0000000..5840d20 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tzm_Latn.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - JD Isaacks + */ +return [ + 'year' => '{1}:count asgas|:count isgasn', + 'a_year' => 'asgas|:count isgasn', + 'month' => '{1}:count ayowr|:count iyyirn', + 'a_month' => 'ayowr|:count iyyirn', + 'week' => ':count imalass', + 'a_week' => ':imalass', + 'day' => '{1}:count ass|:count ossan', + 'a_day' => 'ass|:count ossan', + 'hour' => '{1}:count saɛa|:count tassaɛin', + 'a_hour' => '{1}saɛa|:count tassaɛin', + 'minute' => ':count minuḍ', + 'a_minute' => '{1}minuḍ|:count minuḍ', + 'second' => ':count imik', + 'a_second' => '{1}imik|:count imik', + 'ago' => 'yan :time', + 'from_now' => 'dadkh s yan :time', + 'diff_yesterday' => 'assant', + 'diff_yesterday_regexp' => 'assant(?:\\s+g)?', + 'diff_today' => 'asdkh', + 'diff_today_regexp' => 'asdkh(?:\\s+g)?', + 'diff_tomorrow' => 'aska', + 'diff_tomorrow_regexp' => 'aska(?:\\s+g)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[asdkh g] LT', + 'nextDay' => '[aska g] LT', + 'nextWeek' => 'dddd [g] LT', + 'lastDay' => '[assant g] LT', + 'lastWeek' => 'dddd [g] LT', + 'sameElse' => 'L', + ], + 'months' => ['innayr', 'brˤayrˤ', 'marˤsˤ', 'ibrir', 'mayyw', 'ywnyw', 'ywlywz', 'ɣwšt', 'šwtanbir', 'ktˤwbrˤ', 'nwwanbir', 'dwjnbir'], + 'months_short' => ['innayr', 'brˤayrˤ', 'marˤsˤ', 'ibrir', 'mayyw', 'ywnyw', 'ywlywz', 'ɣwšt', 'šwtanbir', 'ktˤwbrˤ', 'nwwanbir', 'dwjnbir'], + 'weekdays' => ['asamas', 'aynas', 'asinas', 'akras', 'akwas', 'asimwas', 'asiḍyas'], + 'weekdays_short' => ['asamas', 'aynas', 'asinas', 'akras', 'akwas', 'asimwas', 'asiḍyas'], + 'weekdays_min' => ['asamas', 'aynas', 'asinas', 'akras', 'akwas', 'asimwas', 'asiḍyas'], + 'meridiem' => ['Zdat azal', 'Ḍeffir aza'], + 'first_day_of_week' => 6, + 'day_of_first_week_of_year' => 1, +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ug.php b/vendor/nesbot/carbon/src/Carbon/Lang/ug.php new file mode 100644 index 0000000..259b99a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ug.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Philippe Vaucher + * - Tsutomu Kuroda + * - yasinn + */ +return [ + 'year' => '{1}'.'بىر يىل'.'|:count '.'يىل', + 'month' => '{1}'.'بىر ئاي'.'|:count '.'ئاي', + 'week' => '{1}'.'بىر ھەپتە'.'|:count '.'ھەپتە', + 'day' => '{1}'.'بىر كۈن'.'|:count '.'كۈن', + 'hour' => '{1}'.'بىر سائەت'.'|:count '.'سائەت', + 'minute' => '{1}'.'بىر مىنۇت'.'|:count '.'مىنۇت', + 'second' => '{1}'.'نەچچە سېكونت'.'|:count '.'سېكونت', + 'ago' => ':time بۇرۇن', + 'from_now' => ':time كېيىن', + 'diff_today' => 'بۈگۈن', + 'diff_yesterday' => 'تۆنۈگۈن', + 'diff_tomorrow' => 'ئەتە', + 'diff_tomorrow_regexp' => 'ئەتە(?:\\s+سائەت)?', + 'diff_today_regexp' => 'بۈگۈن(?:\\s+سائەت)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-DD', + 'LL' => 'YYYY-يىلىM-ئاينىڭD-كۈنى', + 'LLL' => 'YYYY-يىلىM-ئاينىڭD-كۈنى، HH:mm', + 'LLLL' => 'dddd، YYYY-يىلىM-ئاينىڭD-كۈنى، HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[بۈگۈن سائەت] LT', + 'nextDay' => '[ئەتە سائەت] LT', + 'nextWeek' => '[كېلەركى] dddd [سائەت] LT', + 'lastDay' => '[تۆنۈگۈن] LT', + 'lastWeek' => '[ئالدىنقى] dddd [سائەت] LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number, $period) { + switch ($period) { + case 'd': + case 'D': + case 'DDD': + return $number.'-كۈنى'; + case 'w': + case 'W': + return $number.'-ھەپتە'; + default: + return $number; + } + }, + 'meridiem' => function ($hour, $minute) { + $time = $hour * 100 + $minute; + if ($time < 600) { + return 'يېرىم كېچە'; + } + if ($time < 900) { + return 'سەھەر'; + } + if ($time < 1130) { + return 'چۈشتىن بۇرۇن'; + } + if ($time < 1230) { + return 'چۈش'; + } + if ($time < 1800) { + return 'چۈشتىن كېيىن'; + } + + return 'كەچ'; + }, + 'months' => ['يانۋار', 'فېۋرال', 'مارت', 'ئاپرېل', 'ماي', 'ئىيۇن', 'ئىيۇل', 'ئاۋغۇست', 'سېنتەبىر', 'ئۆكتەبىر', 'نويابىر', 'دېكابىر'], + 'months_short' => ['يانۋار', 'فېۋرال', 'مارت', 'ئاپرېل', 'ماي', 'ئىيۇن', 'ئىيۇل', 'ئاۋغۇست', 'سېنتەبىر', 'ئۆكتەبىر', 'نويابىر', 'دېكابىر'], + 'weekdays' => ['يەكشەنبە', 'دۈشەنبە', 'سەيشەنبە', 'چارشەنبە', 'پەيشەنبە', 'جۈمە', 'شەنبە'], + 'weekdays_short' => ['يە', 'دۈ', 'سە', 'چا', 'پە', 'جۈ', 'شە'], + 'weekdays_min' => ['يە', 'دۈ', 'سە', 'چا', 'پە', 'جۈ', 'شە'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' ۋە '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ug_CN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ug_CN.php new file mode 100644 index 0000000..deb828c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ug_CN.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Kunal Marwaha + * - Alim Boyaq + */ +return require __DIR__.'/ug.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uk.php b/vendor/nesbot/carbon/src/Carbon/Lang/uk.php new file mode 100644 index 0000000..4217d16 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/uk.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Carbon\CarbonInterface; + +$processHoursFunction = function (CarbonInterface $date, string $format) { + return $format.'о'.($date->hour === 11 ? 'б' : '').'] LT'; +}; + +/* + * Authors: + * - Kunal Marwaha + * - Josh Soref + * - François B + * - Tim Fish + * - Serhan Apaydın + * - Max Mykhailenko + * - JD Isaacks + * - Max Kovpak + * - AucT + * - Philippe Vaucher + * - Ilya Shaplyko + * - Vadym Ievsieiev + * - Denys Kurets + * - Igor Kasyanchuk + * - Tsutomu Kuroda + * - tjku + * - Max Melentiev + * - Oleh + * - epaminond + * - Juanito Fatas + * - Vitalii Khustochka + * - Akira Matsuda + * - Christopher Dell + * - Enrique Vidal + * - Simone Carletti + * - Aaron Patterson + * - Andriy Tyurnikov + * - Nicolás Hock Isaza + * - Iwakura Taro + * - Andrii Ponomarov + * - alecrabbit + * - vystepanenko + * - AlexWalkerson + * - Andre Havryliuk (Andrend) + * - Max Datsenko (datsenko-md) + */ +return [ + 'year' => ':count рік|:count роки|:count років', + 'y' => ':countр|:countрр|:countрр', + 'a_year' => '{1}рік|:count рік|:count роки|:count років', + 'month' => ':count місяць|:count місяці|:count місяців', + 'm' => ':countм', + 'a_month' => '{1}місяць|:count місяць|:count місяці|:count місяців', + 'week' => ':count тиждень|:count тижні|:count тижнів', + 'w' => ':countт', + 'a_week' => '{1}тиждень|:count тиждень|:count тижні|:count тижнів', + 'day' => ':count день|:count дні|:count днів', + 'd' => ':countд', + 'a_day' => '{1}день|:count день|:count дні|:count днів', + 'hour' => ':count година|:count години|:count годин', + 'h' => ':countг', + 'a_hour' => '{1}година|:count година|:count години|:count годин', + 'minute' => ':count хвилина|:count хвилини|:count хвилин', + 'min' => ':countхв', + 'a_minute' => '{1}хвилина|:count хвилина|:count хвилини|:count хвилин', + 'second' => ':count секунда|:count секунди|:count секунд', + 's' => ':countсек', + 'a_second' => '{1}декілька секунд|:count секунда|:count секунди|:count секунд', + + 'hour_ago' => ':count годину|:count години|:count годин', + 'a_hour_ago' => '{1}годину|:count годину|:count години|:count годин', + 'minute_ago' => ':count хвилину|:count хвилини|:count хвилин', + 'a_minute_ago' => '{1}хвилину|:count хвилину|:count хвилини|:count хвилин', + 'second_ago' => ':count секунду|:count секунди|:count секунд', + 'a_second_ago' => '{1}декілька секунд|:count секунду|:count секунди|:count секунд', + + 'hour_from_now' => ':count годину|:count години|:count годин', + 'a_hour_from_now' => '{1}годину|:count годину|:count години|:count годин', + 'minute_from_now' => ':count хвилину|:count хвилини|:count хвилин', + 'a_minute_from_now' => '{1}хвилину|:count хвилину|:count хвилини|:count хвилин', + 'second_from_now' => ':count секунду|:count секунди|:count секунд', + 'a_second_from_now' => '{1}декілька секунд|:count секунду|:count секунди|:count секунд', + + 'hour_after' => ':count годину|:count години|:count годин', + 'a_hour_after' => '{1}годину|:count годину|:count години|:count годин', + 'minute_after' => ':count хвилину|:count хвилини|:count хвилин', + 'a_minute_after' => '{1}хвилину|:count хвилину|:count хвилини|:count хвилин', + 'second_after' => ':count секунду|:count секунди|:count секунд', + 'a_second_after' => '{1}декілька секунд|:count секунду|:count секунди|:count секунд', + + 'hour_before' => ':count годину|:count години|:count годин', + 'a_hour_before' => '{1}годину|:count годину|:count години|:count годин', + 'minute_before' => ':count хвилину|:count хвилини|:count хвилин', + 'a_minute_before' => '{1}хвилину|:count хвилину|:count хвилини|:count хвилин', + 'second_before' => ':count секунду|:count секунди|:count секунд', + 'a_second_before' => '{1}декілька секунд|:count секунду|:count секунди|:count секунд', + + 'ago' => ':time тому', + 'from_now' => 'за :time', + 'after' => ':time після', + 'before' => ':time до', + 'diff_now' => 'щойно', + 'diff_today' => 'Сьогодні', + 'diff_today_regexp' => 'Сьогодні(?:\\s+о)?', + 'diff_yesterday' => 'вчора', + 'diff_yesterday_regexp' => 'Вчора(?:\\s+о)?', + 'diff_tomorrow' => 'завтра', + 'diff_tomorrow_regexp' => 'Завтра(?:\\s+о)?', + 'diff_before_yesterday' => 'позавчора', + 'diff_after_tomorrow' => 'післязавтра', + 'period_recurrences' => 'один раз|:count рази|:count разів', + 'period_interval' => 'кожні :interval', + 'period_start_date' => 'з :date', + 'period_end_date' => 'до :date', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY, HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY, HH:mm', + ], + 'calendar' => [ + 'sameDay' => function (CarbonInterface $date) use ($processHoursFunction) { + return $processHoursFunction($date, '[Сьогодні '); + }, + 'nextDay' => function (CarbonInterface $date) use ($processHoursFunction) { + return $processHoursFunction($date, '[Завтра '); + }, + 'nextWeek' => function (CarbonInterface $date) use ($processHoursFunction) { + return $processHoursFunction($date, '[У] dddd ['); + }, + 'lastDay' => function (CarbonInterface $date) use ($processHoursFunction) { + return $processHoursFunction($date, '[Вчора '); + }, + 'lastWeek' => function (CarbonInterface $date) use ($processHoursFunction) { + switch ($date->dayOfWeek) { + case 0: + case 3: + case 5: + case 6: + return $processHoursFunction($date, '[Минулої] dddd ['); + default: + return $processHoursFunction($date, '[Минулого] dddd ['); + } + }, + 'sameElse' => 'L', + ], + 'ordinal' => function ($number, $period) { + switch ($period) { + case 'M': + case 'd': + case 'DDD': + case 'w': + case 'W': + return $number.'-й'; + case 'D': + return $number.'-го'; + default: + return $number; + } + }, + 'meridiem' => function ($hour) { + if ($hour < 4) { + return 'ночі'; + } + if ($hour < 12) { + return 'ранку'; + } + if ($hour < 17) { + return 'дня'; + } + + return 'вечора'; + }, + 'months' => ['січня', 'лютого', 'березня', 'квітня', 'травня', 'червня', 'липня', 'серпня', 'вересня', 'жовтня', 'листопада', 'грудня'], + 'months_standalone' => ['січень', 'лютий', 'березень', 'квітень', 'травень', 'червень', 'липень', 'серпень', 'вересень', 'жовтень', 'листопад', 'грудень'], + 'months_short' => ['січ', 'лют', 'бер', 'кві', 'тра', 'чер', 'лип', 'сер', 'вер', 'жов', 'лис', 'гру'], + 'months_regexp' => '/(D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/', + 'weekdays' => function (CarbonInterface $date, $format, $index) { + static $words = [ + 'nominative' => ['неділя', 'понеділок', 'вівторок', 'середа', 'четвер', 'п’ятниця', 'субота'], + 'accusative' => ['неділю', 'понеділок', 'вівторок', 'середу', 'четвер', 'п’ятницю', 'суботу'], + 'genitive' => ['неділі', 'понеділка', 'вівторка', 'середи', 'четверга', 'п’ятниці', 'суботи'], + ]; + + $format = $format ?? ''; + $nounCase = preg_match('/(\[(В|в|У|у)\])\s+dddd/u', $format) + ? 'accusative' + : ( + preg_match('/\[?(?:минулої|наступної)?\s*\]\s+dddd/u', $format) + ? 'genitive' + : 'nominative' + ); + + return $words[$nounCase][$index] ?? null; + }, + 'weekdays_short' => ['нд', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'], + 'weekdays_min' => ['нд', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' i '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uk_UA.php b/vendor/nesbot/carbon/src/Carbon/Lang/uk_UA.php new file mode 100644 index 0000000..bd11d86 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/uk_UA.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/uk.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/unm.php b/vendor/nesbot/carbon/src/Carbon/Lang/unm.php new file mode 100644 index 0000000..d3f19f0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/unm.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/unm_US.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/unm_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/unm_US.php new file mode 100644 index 0000000..fa5c374 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/unm_US.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['enikwsi', 'chkwali', 'xamokhwite', 'kwetayoxe', 'tainipen', 'kichinipen', 'lainipen', 'winaminke', 'kichitahkok', 'puksit', 'wini', 'muxkotae'], + 'months_short' => ['eni', 'chk', 'xam', 'kwe', 'tai', 'nip', 'lai', 'win', 'tah', 'puk', 'kun', 'mux'], + 'weekdays' => ['kentuwei', 'manteke', 'tusteke', 'lelai', 'tasteke', 'pelaiteke', 'sateteke'], + 'weekdays_short' => ['ken', 'man', 'tus', 'lel', 'tas', 'pel', 'sat'], + 'weekdays_min' => ['ken', 'man', 'tus', 'lel', 'tas', 'pel', 'sat'], + 'day_of_first_week_of_year' => 1, + + // Too unreliable + /* + 'year' => ':count kaxtëne', + 'y' => ':count kaxtëne', + 'a_year' => ':count kaxtëne', + + 'month' => ':count piskewëni kishux', // less reliable + 'm' => ':count piskewëni kishux', // less reliable + 'a_month' => ':count piskewëni kishux', // less reliable + + 'week' => ':count kishku', // less reliable + 'w' => ':count kishku', // less reliable + 'a_week' => ':count kishku', // less reliable + + 'day' => ':count kishku', + 'd' => ':count kishku', + 'a_day' => ':count kishku', + + 'hour' => ':count xkuk', // less reliable + 'h' => ':count xkuk', // less reliable + 'a_hour' => ':count xkuk', // less reliable + + 'minute' => ':count txituwàk', // less reliable + 'min' => ':count txituwàk', // less reliable + 'a_minute' => ':count txituwàk', // less reliable + + 'second' => ':count nisha', // less reliable + 's' => ':count nisha', // less reliable + 'a_second' => ':count nisha', // less reliable + */ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ur.php b/vendor/nesbot/carbon/src/Carbon/Lang/ur.php new file mode 100644 index 0000000..dc16c2c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ur.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +$months = [ + 'جنوری', + 'فروری', + 'مارچ', + 'اپریل', + 'مئی', + 'جون', + 'جولائی', + 'اگست', + 'ستمبر', + 'اکتوبر', + 'نومبر', + 'دسمبر', +]; + +$weekdays = [ + 'اتوار', + 'پیر', + 'منگل', + 'بدھ', + 'جمعرات', + 'جمعہ', + 'ہفتہ', +]; + +/* + * Authors: + * - Sawood Alam + * - Mehshan + * - Philippe Vaucher + * - Tsutomu Kuroda + * - tjku + * - Zaid Akram + * - Max Melentiev + * - hafezdivandari + * - Hossein Jabbari + * - nimamo + */ +return [ + 'year' => 'ایک سال|:count سال', + 'month' => 'ایک ماہ|:count ماہ', + 'week' => ':count ہفتے', + 'day' => 'ایک دن|:count دن', + 'hour' => 'ایک گھنٹہ|:count گھنٹے', + 'minute' => 'ایک منٹ|:count منٹ', + 'second' => 'چند سیکنڈ|:count سیکنڈ', + 'ago' => ':time قبل', + 'from_now' => ':time بعد', + 'after' => ':time بعد', + 'before' => ':time پہلے', + 'diff_now' => 'اب', + 'diff_today' => 'آج', + 'diff_today_regexp' => 'آج(?:\\s+بوقت)?', + 'diff_yesterday' => 'گزشتہ کل', + 'diff_yesterday_regexp' => 'گذشتہ(?:\\s+روز)?(?:\\s+بوقت)?', + 'diff_tomorrow' => 'آئندہ کل', + 'diff_tomorrow_regexp' => 'کل(?:\\s+بوقت)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd، D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[آج بوقت] LT', + 'nextDay' => '[کل بوقت] LT', + 'nextWeek' => 'dddd [بوقت] LT', + 'lastDay' => '[گذشتہ روز بوقت] LT', + 'lastWeek' => '[گذشتہ] dddd [بوقت] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['صبح', 'شام'], + 'months' => $months, + 'months_short' => $months, + 'weekdays' => $weekdays, + 'weekdays_short' => $weekdays, + 'weekdays_min' => $weekdays, + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => ['، ', ' اور '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ur_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ur_IN.php new file mode 100644 index 0000000..f81c84d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ur_IN.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Red Hat, Pune bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/ur.php', [ + 'formats' => [ + 'L' => 'D/M/YY', + ], + 'months' => ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر', 'نومبر', 'دسمبر'], + 'months_short' => ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر', 'نومبر', 'دسمبر'], + 'weekdays' => ['اتوار', 'پیر', 'منگل', 'بدھ', 'جمعرات', 'جمعہ', 'سنیچر'], + 'weekdays_short' => ['اتوار', 'پیر', 'منگل', 'بدھ', 'جمعرات', 'جمعہ', 'سنیچر'], + 'weekdays_min' => ['اتوار', 'پیر', 'منگل', 'بدھ', 'جمعرات', 'جمعہ', 'سنیچر'], + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ur_PK.php b/vendor/nesbot/carbon/src/Carbon/Lang/ur_PK.php new file mode 100644 index 0000000..8cd593d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ur_PK.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/ur.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر', 'نومبر', 'دسمبر'], + 'months_short' => ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر', 'نومبر', 'دسمبر'], + 'weekdays' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'], + 'weekdays_short' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'], + 'weekdays_min' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ص', 'ش'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz.php new file mode 100644 index 0000000..61f3b64 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Dmitriy Shabanov + * - JD Isaacks + * - Inoyatulloh + * - Jamshid + * - aarkhipov + * - Philippe Vaucher + * - felixthemagnificent + * - Tsutomu Kuroda + * - tjku + * - Max Melentiev + * - Juanito Fatas + * - Alisher Ulugbekov + * - Ergashev Adizbek + */ +return [ + 'year' => ':count йил', + 'a_year' => '{1}бир йил|:count йил', + 'y' => ':count й', + 'month' => ':count ой', + 'a_month' => '{1}бир ой|:count ой', + 'm' => ':count о', + 'week' => ':count ҳафта', + 'a_week' => '{1}бир ҳафта|:count ҳафта', + 'w' => ':count ҳ', + 'day' => ':count кун', + 'a_day' => '{1}бир кун|:count кун', + 'd' => ':count к', + 'hour' => ':count соат', + 'a_hour' => '{1}бир соат|:count соат', + 'h' => ':count с', + 'minute' => ':count дақиқа', + 'a_minute' => '{1}бир дақиқа|:count дақиқа', + 'min' => ':count д', + 'second' => ':count сония', + 'a_second' => '{1}сония|:count сония', + 's' => ':count с', + 'ago' => ':time аввал', + 'from_now' => 'Якин :time ичида', + 'after' => ':timeдан кейин', + 'before' => ':time олдин', + 'diff_now' => 'ҳозир', + 'diff_today' => 'Бугун', + 'diff_today_regexp' => 'Бугун(?:\\s+соат)?', + 'diff_yesterday' => 'Кеча', + 'diff_yesterday_regexp' => 'Кеча(?:\\s+соат)?', + 'diff_tomorrow' => 'Эртага', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'D MMMM YYYY, dddd HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Бугун соат] LT [да]', + 'nextDay' => '[Эртага] LT [да]', + 'nextWeek' => 'dddd [куни соат] LT [да]', + 'lastDay' => '[Кеча соат] LT [да]', + 'lastWeek' => '[Утган] dddd [куни соат] LT [да]', + 'sameElse' => 'L', + ], + 'months' => ['январ', 'феврал', 'март', 'апрел', 'май', 'июн', 'июл', 'август', 'сентябр', 'октябр', 'ноябр', 'декабр'], + 'months_short' => ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'], + 'weekdays' => ['якшанба', 'душанба', 'сешанба', 'чоршанба', 'пайшанба', 'жума', 'шанба'], + 'weekdays_short' => ['якш', 'душ', 'сеш', 'чор', 'пай', 'жум', 'шан'], + 'weekdays_min' => ['як', 'ду', 'се', 'чо', 'па', 'жу', 'ша'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['эрталаб', 'кечаси'], + 'list' => [', ', ' ва '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz_Arab.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz_Arab.php new file mode 100644 index 0000000..ffb5131 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz_Arab.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/fa.php', [ + 'weekdays' => ['یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'], + 'weekdays_short' => ['ی.', 'د.', 'س.', 'چ.', 'پ.', 'ج.', 'ش.'], + 'weekdays_min' => ['ی.', 'د.', 'س.', 'چ.', 'پ.', 'ج.', 'ش.'], + 'months' => ['جنوری', 'فبروری', 'مارچ', 'اپریل', 'می', 'جون', 'جولای', 'اگست', 'سپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'], + 'months_short' => ['جنو', 'فبر', 'مار', 'اپر', 'می', 'جون', 'جول', 'اگس', 'سپت', 'اکت', 'نوم', 'دسم'], + 'first_day_of_week' => 6, + 'weekend' => [4, 5], + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-dd', + 'LL' => 'YYYY MMM D', + 'LLL' => 'YYYY MMMM D HH:mm', + 'LLLL' => 'YYYY MMMM D, dddd HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz_Cyrl.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz_Cyrl.php new file mode 100644 index 0000000..89e9971 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz_Cyrl.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/uz.php', [ + 'formats' => [ + 'L' => 'DD/MM/yy', + 'LL' => 'D MMM, YYYY', + 'LLL' => 'D MMMM, YYYY HH:mm', + 'LLLL' => 'dddd, DD MMMM, YYYY HH:mm', + ], + 'meridiem' => ['ТО', 'ТК'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz_Latn.php new file mode 100644 index 0000000..ecceeaa --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz_Latn.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Josh Soref + * - Rasulbek + * - Ilyosjon Kamoldinov (ilyosjon09) + */ +return [ + 'year' => ':count yil', + 'a_year' => '{1}bir yil|:count yil', + 'y' => ':count y', + 'month' => ':count oy', + 'a_month' => '{1}bir oy|:count oy', + 'm' => ':count o', + 'week' => ':count hafta', + 'a_week' => '{1}bir hafta|:count hafta', + 'w' => ':count h', + 'day' => ':count kun', + 'a_day' => '{1}bir kun|:count kun', + 'd' => ':count k', + 'hour' => ':count soat', + 'a_hour' => '{1}bir soat|:count soat', + 'h' => ':count soat', + 'minute' => ':count daqiqa', + 'a_minute' => '{1}bir daqiqa|:count daqiqa', + 'min' => ':count d', + 'second' => ':count soniya', + 'a_second' => '{1}soniya|:count soniya', + 's' => ':count son.', + 'ago' => ':time avval', + 'from_now' => 'Yaqin :time ichida', + 'after' => ':timedan keyin', + 'before' => ':time oldin', + 'diff_yesterday' => 'Kecha', + 'diff_yesterday_regexp' => 'Kecha(?:\\s+soat)?', + 'diff_today' => 'Bugun', + 'diff_today_regexp' => 'Bugun(?:\\s+soat)?', + 'diff_tomorrow' => 'Ertaga', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'D MMMM YYYY, dddd HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Bugun soat] LT [da]', + 'nextDay' => '[Ertaga] LT [da]', + 'nextWeek' => 'dddd [kuni soat] LT [da]', + 'lastDay' => '[Kecha soat] LT [da]', + 'lastWeek' => '[O\'tgan] dddd [kuni soat] LT [da]', + 'sameElse' => 'L', + ], + 'months' => ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'Iyun', 'Iyul', 'Avgust', 'Sentabr', 'Oktabr', 'Noyabr', 'Dekabr'], + 'months_short' => ['Yan', 'Fev', 'Mar', 'Apr', 'May', 'Iyun', 'Iyul', 'Avg', 'Sen', 'Okt', 'Noy', 'Dek'], + 'weekdays' => ['Yakshanba', 'Dushanba', 'Seshanba', 'Chorshanba', 'Payshanba', 'Juma', 'Shanba'], + 'weekdays_short' => ['Yak', 'Dush', 'Sesh', 'Chor', 'Pay', 'Jum', 'Shan'], + 'weekdays_min' => ['Ya', 'Du', 'Se', 'Cho', 'Pa', 'Ju', 'Sha'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' va '], + 'meridiem' => ['TO', 'TK'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ.php new file mode 100644 index 0000000..d41bfee --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Bobir Ismailov Bobir Ismailov, Pablo Saratxaga, Mashrab Kuvatov bobir_is@yahoo.com, pablo@mandrakesoft.com, kmashrab@uni-bremen.de + */ +return array_replace_recursive(require __DIR__.'/uz_Latn.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'Iyun', 'Iyul', 'Avgust', 'Sentabr', 'Oktabr', 'Noyabr', 'Dekabr'], + 'months_short' => ['Yan', 'Fev', 'Mar', 'Apr', 'May', 'Iyn', 'Iyl', 'Avg', 'Sen', 'Okt', 'Noy', 'Dek'], + 'weekdays' => ['Yakshanba', 'Dushanba', 'Seshanba', 'Chorshanba', 'Payshanba', 'Juma', 'Shanba'], + 'weekdays_short' => ['Yak', 'Du', 'Se', 'Cho', 'Pay', 'Ju', 'Sha'], + 'weekdays_min' => ['Yak', 'Du', 'Se', 'Cho', 'Pay', 'Ju', 'Sha'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ@cyrillic.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ@cyrillic.php new file mode 100644 index 0000000..2fa967c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ@cyrillic.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Mashrab Kuvatov Mashrab Kuvatov, Pablo Saratxaga kmashrab@uni-bremen.de, pablo@mandrakesoft.com + */ +return array_replace_recursive(require __DIR__.'/uz.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['Январ', 'Феврал', 'Март', 'Апрел', 'Май', 'Июн', 'Июл', 'Август', 'Сентябр', 'Октябр', 'Ноябр', 'Декабр'], + 'months_short' => ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'], + 'weekdays' => ['Якшанба', 'Душанба', 'Сешанба', 'Чоршанба', 'Пайшанба', 'Жума', 'Шанба'], + 'weekdays_short' => ['Якш', 'Душ', 'Сеш', 'Чор', 'Пай', 'Жум', 'Шан'], + 'weekdays_min' => ['Якш', 'Душ', 'Сеш', 'Чор', 'Пай', 'Жум', 'Шан'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vai.php b/vendor/nesbot/carbon/src/Carbon/Lang/vai.php new file mode 100644 index 0000000..3c378df --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/vai.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'weekdays' => ['ꕞꕌꔵ', 'ꗳꗡꘉ', 'ꕚꕞꕚ', 'ꕉꕞꕒ', 'ꕉꔤꕆꕢ', 'ꕉꔤꕀꕮ', 'ꔻꔬꔳ'], + 'weekdays_short' => ['ꕞꕌꔵ', 'ꗳꗡꘉ', 'ꕚꕞꕚ', 'ꕉꕞꕒ', 'ꕉꔤꕆꕢ', 'ꕉꔤꕀꕮ', 'ꔻꔬꔳ'], + 'weekdays_min' => ['ꕞꕌꔵ', 'ꗳꗡꘉ', 'ꕚꕞꕚ', 'ꕉꕞꕒ', 'ꕉꔤꕆꕢ', 'ꕉꔤꕀꕮ', 'ꔻꔬꔳ'], + 'months' => ['ꖨꖕ ꕪꕴ ꔞꔀꕮꕊ', 'ꕒꕡꖝꖕ', 'ꕾꖺ', 'ꖢꖕ', 'ꖑꕱ', 'ꖱꘋ', 'ꖱꕞꔤ', 'ꗛꔕ', 'ꕢꕌ', 'ꕭꖃ', 'ꔞꘋꕔꕿ ꕸꖃꗏ', 'ꖨꖕ ꕪꕴ ꗏꖺꕮꕊ'], + 'months_short' => ['ꖨꖕꔞ', 'ꕒꕡ', 'ꕾꖺ', 'ꖢꖕ', 'ꖑꕱ', 'ꖱꘋ', 'ꖱꕞ', 'ꗛꔕ', 'ꕢꕌ', 'ꕭꖃ', 'ꔞꘋ', 'ꖨꖕꗏ'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd, D MMMM YYYY h:mm a', + ], + + 'year' => ':count ꕀ', // less reliable + 'y' => ':count ꕀ', // less reliable + 'a_year' => ':count ꕀ', // less reliable + + 'second' => ':count ꗱꕞꕯꕊ', // less reliable + 's' => ':count ꗱꕞꕯꕊ', // less reliable + 'a_second' => ':count ꗱꕞꕯꕊ', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vai_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/vai_Latn.php new file mode 100644 index 0000000..51e83cc --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/vai_Latn.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'weekdays' => ['lahadi', 'tɛɛnɛɛ', 'talata', 'alaba', 'aimisa', 'aijima', 'siɓiti'], + 'weekdays_short' => ['lahadi', 'tɛɛnɛɛ', 'talata', 'alaba', 'aimisa', 'aijima', 'siɓiti'], + 'weekdays_min' => ['lahadi', 'tɛɛnɛɛ', 'talata', 'alaba', 'aimisa', 'aijima', 'siɓiti'], + 'months' => ['luukao kemã', 'ɓandaɓu', 'vɔɔ', 'fulu', 'goo', '6', '7', 'kɔnde', 'saah', 'galo', 'kenpkato ɓololɔ', 'luukao lɔma'], + 'months_short' => ['luukao kemã', 'ɓandaɓu', 'vɔɔ', 'fulu', 'goo', '6', '7', 'kɔnde', 'saah', 'galo', 'kenpkato ɓololɔ', 'luukao lɔma'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'h:mm a', + 'LTS' => 'h:mm:ss a', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm a', + 'LLLL' => 'dddd, D MMMM YYYY h:mm a', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vai_Vaii.php b/vendor/nesbot/carbon/src/Carbon/Lang/vai_Vaii.php new file mode 100644 index 0000000..b4bb533 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/vai_Vaii.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/vai.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ve.php b/vendor/nesbot/carbon/src/Carbon/Lang/ve.php new file mode 100644 index 0000000..7f10aeb --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ve.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/ve_ZA.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ve_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ve_ZA.php new file mode 100644 index 0000000..5eb2b91 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ve_ZA.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Phando', 'Luhuhi', 'Ṱhafamuhwe', 'Lambamai', 'Shundunthule', 'Fulwi', 'Fulwana', 'Ṱhangule', 'Khubvumedzi', 'Tshimedzi', 'Ḽara', 'Nyendavhusiku'], + 'months_short' => ['Pha', 'Luh', 'Fam', 'Lam', 'Shu', 'Lwi', 'Lwa', 'Ngu', 'Khu', 'Tsh', 'Ḽar', 'Nye'], + 'weekdays' => ['Swondaha', 'Musumbuluwo', 'Ḽavhuvhili', 'Ḽavhuraru', 'Ḽavhuṋa', 'Ḽavhuṱanu', 'Mugivhela'], + 'weekdays_short' => ['Swo', 'Mus', 'Vhi', 'Rar', 'ṋa', 'Ṱan', 'Mug'], + 'weekdays_min' => ['Swo', 'Mus', 'Vhi', 'Rar', 'ṋa', 'Ṱan', 'Mug'], + 'day_of_first_week_of_year' => 1, + + // Too unreliable + /* + 'day' => ':count vhege', // less reliable + 'd' => ':count vhege', // less reliable + 'a_day' => ':count vhege', // less reliable + + 'hour' => ':count watshi', // less reliable + 'h' => ':count watshi', // less reliable + 'a_hour' => ':count watshi', // less reliable + + 'minute' => ':count watshi', // less reliable + 'min' => ':count watshi', // less reliable + 'a_minute' => ':count watshi', // less reliable + + 'second' => ':count Mu', // less reliable + 's' => ':count Mu', // less reliable + 'a_second' => ':count Mu', // less reliable + + 'week' => ':count vhege', + 'w' => ':count vhege', + 'a_week' => ':count vhege', + */ +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vi.php b/vendor/nesbot/carbon/src/Carbon/Lang/vi.php new file mode 100644 index 0000000..73e2852 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/vi.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Andre Polykanine A.K.A. Menelion Elensúlë + * - JD Isaacks + */ +return [ + 'year' => ':count năm', + 'a_year' => '{1}một năm|]1, Inf[:count năm', + 'y' => ':count năm', + 'month' => ':count tháng', + 'a_month' => '{1}một tháng|]1, Inf[:count tháng', + 'm' => ':count tháng', + 'week' => ':count tuần', + 'a_week' => '{1}một tuần|]1, Inf[:count tuần', + 'w' => ':count tuần', + 'day' => ':count ngày', + 'a_day' => '{1}một ngày|]1, Inf[:count ngày', + 'd' => ':count ngày', + 'hour' => ':count giờ', + 'a_hour' => '{1}một giờ|]1, Inf[:count giờ', + 'h' => ':count giờ', + 'minute' => ':count phút', + 'a_minute' => '{1}một phút|]1, Inf[:count phút', + 'min' => ':count phút', + 'second' => ':count giây', + 'a_second' => '{1}vài giây|]1, Inf[:count giây', + 's' => ':count giây', + 'ago' => ':time trước', + 'from_now' => ':time tới', + 'after' => ':time sau', + 'before' => ':time trước', + 'diff_now' => 'bây giờ', + 'diff_today' => 'Hôm', + 'diff_today_regexp' => 'Hôm(?:\\s+nay)?(?:\\s+lúc)?', + 'diff_yesterday' => 'Hôm qua', + 'diff_yesterday_regexp' => 'Hôm(?:\\s+qua)?(?:\\s+lúc)?', + 'diff_tomorrow' => 'Ngày mai', + 'diff_tomorrow_regexp' => 'Ngày(?:\\s+mai)?(?:\\s+lúc)?', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM [năm] YYYY', + 'LLL' => 'D MMMM [năm] YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM [năm] YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[Hôm nay lúc] LT', + 'nextDay' => '[Ngày mai lúc] LT', + 'nextWeek' => 'dddd [tuần tới lúc] LT', + 'lastDay' => '[Hôm qua lúc] LT', + 'lastWeek' => 'dddd [tuần trước lúc] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['SA', 'CH'], + 'months' => ['tháng 1', 'tháng 2', 'tháng 3', 'tháng 4', 'tháng 5', 'tháng 6', 'tháng 7', 'tháng 8', 'tháng 9', 'tháng 10', 'tháng 11', 'tháng 12'], + 'months_short' => ['Th01', 'Th02', 'Th03', 'Th04', 'Th05', 'Th06', 'Th07', 'Th08', 'Th09', 'Th10', 'Th11', 'Th12'], + 'weekdays' => ['chủ nhật', 'thứ hai', 'thứ ba', 'thứ tư', 'thứ năm', 'thứ sáu', 'thứ bảy'], + 'weekdays_short' => ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'], + 'weekdays_min' => ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => [', ', ' và '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vi_VN.php b/vendor/nesbot/carbon/src/Carbon/Lang/vi_VN.php new file mode 100644 index 0000000..18d8987 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/vi_VN.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/vi.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vo.php b/vendor/nesbot/carbon/src/Carbon/Lang/vo.php new file mode 100644 index 0000000..e273033 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/vo.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'months' => ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10', 'M11', 'M12'], + 'months_short' => ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10', 'M11', 'M12'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY-MM-dd', + 'LL' => 'YYYY MMM D', + 'LLL' => 'YYYY MMMM D HH:mm', + 'LLLL' => 'YYYY MMMM D, dddd HH:mm', + ], + + 'year' => ':count yel', + 'y' => ':count yel', + 'a_year' => ':count yel', + + 'month' => ':count mul', + 'm' => ':count mul', + 'a_month' => ':count mul', + + 'week' => ':count vig', + 'w' => ':count vig', + 'a_week' => ':count vig', + + 'day' => ':count del', + 'd' => ':count del', + 'a_day' => ':count del', + + 'hour' => ':count düp', + 'h' => ':count düp', + 'a_hour' => ':count düp', + + 'minute' => ':count minut', + 'min' => ':count minut', + 'a_minute' => ':count minut', + + 'second' => ':count sekun', + 's' => ':count sekun', + 'a_second' => ':count sekun', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vun.php b/vendor/nesbot/carbon/src/Carbon/Lang/vun.php new file mode 100644 index 0000000..ed92e8e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/vun.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['utuko', 'kyiukonyi'], + 'weekdays' => ['Jumapilyi', 'Jumatatuu', 'Jumanne', 'Jumatanu', 'Alhamisi', 'Ijumaa', 'Jumamosi'], + 'weekdays_short' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'], + 'weekdays_min' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'], + 'months' => ['Januari', 'Februari', 'Machi', 'Aprilyi', 'Mei', 'Junyi', 'Julyai', 'Agusti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'], + 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wa.php b/vendor/nesbot/carbon/src/Carbon/Lang/wa.php new file mode 100644 index 0000000..f6dc4cc --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/wa.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/wa_BE.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wa_BE.php b/vendor/nesbot/carbon/src/Carbon/Lang/wa_BE.php new file mode 100644 index 0000000..a76d80d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/wa_BE.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Djan SACRE Pablo Saratxaga pablo@mandrakesoft.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['di djanvî', 'di fevrî', 'di måss', 'd’ avri', 'di may', 'di djun', 'di djulete', 'd’ awousse', 'di setimbe', 'd’ octôbe', 'di nôvimbe', 'di decimbe'], + 'months_short' => ['dja', 'fev', 'mås', 'avr', 'may', 'djn', 'djl', 'awo', 'set', 'oct', 'nôv', 'dec'], + 'weekdays' => ['dimegne', 'londi', 'mårdi', 'mierkidi', 'djudi', 'vénrdi', 'semdi'], + 'weekdays_short' => ['dim', 'lon', 'mår', 'mie', 'dju', 'vén', 'sem'], + 'weekdays_min' => ['dim', 'lon', 'mår', 'mie', 'dju', 'vén', 'sem'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'year' => ':count anêye', + 'y' => ':count anêye', + 'a_year' => ':count anêye', + + 'month' => ':count meûs', + 'm' => ':count meûs', + 'a_month' => ':count meûs', + + 'week' => ':count samwinne', + 'w' => ':count samwinne', + 'a_week' => ':count samwinne', + + 'day' => ':count djoû', + 'd' => ':count djoû', + 'a_day' => ':count djoû', + + 'hour' => ':count eure', + 'h' => ':count eure', + 'a_hour' => ':count eure', + + 'minute' => ':count munute', + 'min' => ':count munute', + 'a_minute' => ':count munute', + + 'second' => ':count Sigonde', + 's' => ':count Sigonde', + 'a_second' => ':count Sigonde', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wae.php b/vendor/nesbot/carbon/src/Carbon/Lang/wae.php new file mode 100644 index 0000000..bf57f23 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/wae.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/wae_CH.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wae_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/wae_CH.php new file mode 100644 index 0000000..2af50b4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/wae_CH.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Walser Translation Team ml@translate-wae.ch + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'YYYY-MM-DD', + ], + 'months' => ['Jenner', 'Hornig', 'Märze', 'Abrille', 'Meije', 'Bráčet', 'Heiwet', 'Öigšte', 'Herbštmánet', 'Wímánet', 'Wintermánet', 'Chrištmánet'], + 'months_short' => ['Jen', 'Hor', 'Mär', 'Abr', 'Mei', 'Brá', 'Hei', 'Öig', 'Her', 'Wím', 'Win', 'Chr'], + 'weekdays' => ['Suntag', 'Mäntag', 'Zischtag', 'Mittwuch', 'Frontag', 'Fritag', 'Samschtag'], + 'weekdays_short' => ['Sun', 'Män', 'Zis', 'Mit', 'Fro', 'Fri', 'Sam'], + 'weekdays_min' => ['Sun', 'Män', 'Zis', 'Mit', 'Fro', 'Fri', 'Sam'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + + 'month' => ':count Maano', // less reliable + 'm' => ':count Maano', // less reliable + 'a_month' => ':count Maano', // less reliable +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wal.php b/vendor/nesbot/carbon/src/Carbon/Lang/wal.php new file mode 100644 index 0000000..e8ec40f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/wal.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/wal_ET.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wal_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/wal_ET.php new file mode 100644 index 0000000..a4e619a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/wal_ET.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Ge'ez Frontier Foundation locales@geez.org + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች', 'ኤፕረል', 'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት', 'ሴፕቴምበር', 'ኦክተውበር', 'ኖቬምበር', 'ዲሴምበር'], + 'months_short' => ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕረ', 'ሜይ ', 'ጁን ', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክተ', 'ኖቬም', 'ዲሴም'], + 'weekdays' => ['ወጋ', 'ሳይኖ', 'ማቆሳኛ', 'አሩዋ', 'ሃሙሳ', 'አርባ', 'ቄራ'], + 'weekdays_short' => ['ወጋ ', 'ሳይኖ', 'ማቆሳ', 'አሩዋ', 'ሃሙሳ', 'አርባ', 'ቄራ '], + 'weekdays_min' => ['ወጋ ', 'ሳይኖ', 'ማቆሳ', 'አሩዋ', 'ሃሙሳ', 'አርባ', 'ቄራ '], + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['ማለዶ', 'ቃማ'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wo.php b/vendor/nesbot/carbon/src/Carbon/Lang/wo.php new file mode 100644 index 0000000..74b95df --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/wo.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/wo_SN.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wo_SN.php b/vendor/nesbot/carbon/src/Carbon/Lang/wo_SN.php new file mode 100644 index 0000000..f8a85b3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/wo_SN.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - The Debian Project Christian Perrier bubulle@debian.org + */ +return [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD.MM.YYYY', + 'LL' => 'MMMM DD, YYYY', + 'LLL' => 'DD MMM HH:mm', + 'LLLL' => 'MMMM DD, YYYY HH:mm', + ], + 'months' => ['sanwiy\'e', 'feebriy\'e', 'mars', 'awril', 'me', 'suwen', 'sulet', 'uut', 'septaambar', 'oktoobar', 'nowaambar', 'desaambar'], + 'months_short' => ['san', 'fee', 'mar', 'awr', 'me ', 'suw', 'sul', 'uut', 'sep', 'okt', 'now', 'des'], + 'weekdays' => ['dib\'eer', 'altine', 'talaata', 'allarba', 'alxames', 'ajjuma', 'gaawu'], + 'weekdays_short' => ['dib', 'alt', 'tal', 'all', 'alx', 'ajj', 'gaa'], + 'weekdays_min' => ['dib', 'alt', 'tal', 'all', 'alx', 'ajj', 'gaa'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'year' => ':count at', + 'month' => ':count wèr', + 'week' => ':count ayubés', + 'day' => ':count bés', + 'hour' => ':count waxtu', + 'minute' => ':count simili', + 'second' => ':count saa', +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/xh.php b/vendor/nesbot/carbon/src/Carbon/Lang/xh.php new file mode 100644 index 0000000..e88c78d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/xh.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/xh_ZA.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/xh_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/xh_ZA.php new file mode 100644 index 0000000..910f831 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/xh_ZA.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['eyoMqungu', 'eyoMdumba', 'eyoKwindla', 'uTshazimpuzi', 'uCanzibe', 'eyeSilimela', 'eyeKhala', 'eyeThupa', 'eyoMsintsi', 'eyeDwarha', 'eyeNkanga', 'eyoMnga'], + 'months_short' => ['Mqu', 'Mdu', 'Kwi', 'Tsh', 'Can', 'Sil', 'Kha', 'Thu', 'Msi', 'Dwa', 'Nka', 'Mng'], + 'weekdays' => ['iCawa', 'uMvulo', 'lwesiBini', 'lwesiThathu', 'ulweSine', 'lwesiHlanu', 'uMgqibelo'], + 'weekdays_short' => ['Caw', 'Mvu', 'Bin', 'Tha', 'Sin', 'Hla', 'Mgq'], + 'weekdays_min' => ['Caw', 'Mvu', 'Bin', 'Tha', 'Sin', 'Hla', 'Mgq'], + 'day_of_first_week_of_year' => 1, + + 'year' => ':count ihlobo', // less reliable + 'y' => ':count ihlobo', // less reliable + 'a_year' => ':count ihlobo', // less reliable + + 'hour' => ':count iwotshi', // less reliable + 'h' => ':count iwotshi', // less reliable + 'a_hour' => ':count iwotshi', // less reliable + + 'minute' => ':count ingqalelo', // less reliable + 'min' => ':count ingqalelo', // less reliable + 'a_minute' => ':count ingqalelo', // less reliable + + 'second' => ':count nceda', // less reliable + 's' => ':count nceda', // less reliable + 'a_second' => ':count nceda', // less reliable + + 'month' => ':count inyanga', + 'm' => ':count inyanga', + 'a_month' => ':count inyanga', + + 'week' => ':count veki', + 'w' => ':count veki', + 'a_week' => ':count veki', + + 'day' => ':count imini', + 'd' => ':count imini', + 'a_day' => ':count imini', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/xog.php b/vendor/nesbot/carbon/src/Carbon/Lang/xog.php new file mode 100644 index 0000000..eb55b4a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/xog.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['Munkyo', 'Eigulo'], + 'weekdays' => ['Sabiiti', 'Balaza', 'Owokubili', 'Owokusatu', 'Olokuna', 'Olokutaanu', 'Olomukaaga'], + 'weekdays_short' => ['Sabi', 'Bala', 'Kubi', 'Kusa', 'Kuna', 'Kuta', 'Muka'], + 'weekdays_min' => ['Sabi', 'Bala', 'Kubi', 'Kusa', 'Kuna', 'Kuta', 'Muka'], + 'months' => ['Janwaliyo', 'Febwaliyo', 'Marisi', 'Apuli', 'Maayi', 'Juuni', 'Julaayi', 'Agusito', 'Sebuttemba', 'Okitobba', 'Novemba', 'Desemba'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apu', 'Maa', 'Juu', 'Jul', 'Agu', 'Seb', 'Oki', 'Nov', 'Des'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yav.php b/vendor/nesbot/carbon/src/Carbon/Lang/yav.php new file mode 100644 index 0000000..225a20d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yav.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/en.php', [ + 'meridiem' => ['kiɛmɛ́ɛm', 'kisɛ́ndɛ'], + 'weekdays' => ['sɔ́ndiɛ', 'móndie', 'muányáŋmóndie', 'metúkpíápɛ', 'kúpélimetúkpiapɛ', 'feléte', 'séselé'], + 'weekdays_short' => ['sd', 'md', 'mw', 'et', 'kl', 'fl', 'ss'], + 'weekdays_min' => ['sd', 'md', 'mw', 'et', 'kl', 'fl', 'ss'], + 'months' => ['pikítíkítie, oólí ú kutúan', 'siɛyɛ́, oóli ú kándíɛ', 'ɔnsúmbɔl, oóli ú kátátúɛ', 'mesiŋ, oóli ú kénie', 'ensil, oóli ú kátánuɛ', 'ɔsɔn', 'efute', 'pisuyú', 'imɛŋ i puɔs', 'imɛŋ i putúk,oóli ú kátíɛ', 'makandikɛ', 'pilɔndɔ́'], + 'months_short' => ['o.1', 'o.2', 'o.3', 'o.4', 'o.5', 'o.6', 'o.7', 'o.8', 'o.9', 'o.10', 'o.11', 'o.12'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'D/M/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yi.php b/vendor/nesbot/carbon/src/Carbon/Lang/yi.php new file mode 100644 index 0000000..8f32022 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yi.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/yi_US.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yi_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/yi_US.php new file mode 100644 index 0000000..f764d36 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yi_US.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - http://www.uyip.org/ Pablo Saratxaga pablo@mandrakesoft.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['יאַנואַר', 'פֿעברואַר', 'מערץ', 'אַפּריל', 'מיי', 'יוני', 'יולי', 'אויגוסט', 'סעפּטעמבער', 'אקטאבער', 'נאוועמבער', 'דעצעמבער'], + 'months_short' => ['יאַנ', 'פֿעב', 'מאַר', 'אַפּר', 'מײַ ', 'יונ', 'יול', 'אױג', 'סעפּ', 'אָקט', 'נאָװ', 'דעצ'], + 'weekdays' => ['זונטיק', 'מאָנטיק', 'דינסטיק', 'מיטװאָך', 'דאָנערשטיק', 'פֿרײַטיק', 'שבת'], + 'weekdays_short' => ['זונ\'', 'מאָנ\'', 'דינ\'', 'מיט\'', 'דאָנ\'', 'פֿרײַ\'', 'שבת'], + 'weekdays_min' => ['זונ\'', 'מאָנ\'', 'דינ\'', 'מיט\'', 'דאָנ\'', 'פֿרײַ\'', 'שבת'], + 'day_of_first_week_of_year' => 1, + + 'year' => ':count יאר', + 'y' => ':count יאר', + 'a_year' => ':count יאר', + + 'month' => ':count חודש', + 'm' => ':count חודש', + 'a_month' => ':count חודש', + + 'week' => ':count וואָך', + 'w' => ':count וואָך', + 'a_week' => ':count וואָך', + + 'day' => ':count טאָג', + 'd' => ':count טאָג', + 'a_day' => ':count טאָג', + + 'hour' => ':count שעה', + 'h' => ':count שעה', + 'a_hour' => ':count שעה', + + 'minute' => ':count מינוט', + 'min' => ':count מינוט', + 'a_minute' => ':count מינוט', + + 'second' => ':count סעקונדע', + 's' => ':count סעקונדע', + 'a_second' => ':count סעקונדע', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yo.php b/vendor/nesbot/carbon/src/Carbon/Lang/yo.php new file mode 100644 index 0000000..0a82981 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yo.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - François B + * - Atolagbe Abisoye + */ +return [ + 'year' => 'ọdún :count', + 'a_year' => '{1}ọdún kan|ọdún :count', + 'month' => 'osù :count', + 'a_month' => '{1}osù kan|osù :count', + 'week' => 'ọsẹ :count', + 'a_week' => '{1}ọsẹ kan|ọsẹ :count', + 'day' => 'ọjọ́ :count', + 'a_day' => '{1}ọjọ́ kan|ọjọ́ :count', + 'hour' => 'wákati :count', + 'a_hour' => '{1}wákati kan|wákati :count', + 'minute' => 'ìsẹjú :count', + 'a_minute' => '{1}ìsẹjú kan|ìsẹjú :count', + 'second' => 'iaayá :count', + 'a_second' => '{1}ìsẹjú aayá die|aayá :count', + 'ago' => ':time kọjá', + 'from_now' => 'ní :time', + 'diff_yesterday' => 'Àna', + 'diff_yesterday_regexp' => 'Àna(?:\\s+ni)?', + 'diff_today' => 'Ònì', + 'diff_today_regexp' => 'Ònì(?:\\s+ni)?', + 'diff_tomorrow' => 'Ọ̀la', + 'diff_tomorrow_regexp' => 'Ọ̀la(?:\\s+ni)?', + 'formats' => [ + 'LT' => 'h:mm A', + 'LTS' => 'h:mm:ss A', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY h:mm A', + 'LLLL' => 'dddd, D MMMM YYYY h:mm A', + ], + 'calendar' => [ + 'sameDay' => '[Ònì ni] LT', + 'nextDay' => '[Ọ̀la ni] LT', + 'nextWeek' => 'dddd [Ọsẹ̀ tón\'bọ] [ni] LT', + 'lastDay' => '[Àna ni] LT', + 'lastWeek' => 'dddd [Ọsẹ̀ tólọ́] [ni] LT', + 'sameElse' => 'L', + ], + 'ordinal' => 'ọjọ́ :number', + 'months' => ['Sẹ́rẹ́', 'Èrèlè', 'Ẹrẹ̀nà', 'Ìgbé', 'Èbibi', 'Òkùdu', 'Agẹmo', 'Ògún', 'Owewe', 'Ọ̀wàrà', 'Bélú', 'Ọ̀pẹ̀̀'], + 'months_short' => ['Sẹ́r', 'Èrl', 'Ẹrn', 'Ìgb', 'Èbi', 'Òkù', 'Agẹ', 'Ògú', 'Owe', 'Ọ̀wà', 'Bél', 'Ọ̀pẹ̀̀'], + 'weekdays' => ['Àìkú', 'Ajé', 'Ìsẹ́gun', 'Ọjọ́rú', 'Ọjọ́bọ', 'Ẹtì', 'Àbámẹ́ta'], + 'weekdays_short' => ['Àìk', 'Ajé', 'Ìsẹ́', 'Ọjr', 'Ọjb', 'Ẹtì', 'Àbá'], + 'weekdays_min' => ['Àì', 'Aj', 'Ìs', 'Ọr', 'Ọb', 'Ẹt', 'Àb'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'meridiem' => ['Àárọ̀', 'Ọ̀sán'], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yo_BJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/yo_BJ.php new file mode 100644 index 0000000..12b9e81 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yo_BJ.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array_replace_recursive(require __DIR__.'/yo.php', [ + 'meridiem' => ['Àárɔ̀', 'Ɔ̀sán'], + 'weekdays' => ['Ɔjɔ́ Àìkú', 'Ɔjɔ́ Ajé', 'Ɔjɔ́ Ìsɛ́gun', 'Ɔjɔ́rú', 'Ɔjɔ́bɔ', 'Ɔjɔ́ Ɛtì', 'Ɔjɔ́ Àbámɛ́ta'], + 'weekdays_short' => ['Àìkú', 'Ajé', 'Ìsɛ́gun', 'Ɔjɔ́rú', 'Ɔjɔ́bɔ', 'Ɛtì', 'Àbámɛ́ta'], + 'weekdays_min' => ['Àìkú', 'Ajé', 'Ìsɛ́gun', 'Ɔjɔ́rú', 'Ɔjɔ́bɔ', 'Ɛtì', 'Àbámɛ́ta'], + 'months' => ['Oshù Shɛ́rɛ́', 'Oshù Èrèlè', 'Oshù Ɛrɛ̀nà', 'Oshù Ìgbé', 'Oshù Ɛ̀bibi', 'Oshù Òkúdu', 'Oshù Agɛmɔ', 'Oshù Ògún', 'Oshù Owewe', 'Oshù Ɔ̀wàrà', 'Oshù Bélú', 'Oshù Ɔ̀pɛ̀'], + 'months_short' => ['Shɛ́rɛ́', 'Èrèlè', 'Ɛrɛ̀nà', 'Ìgbé', 'Ɛ̀bibi', 'Òkúdu', 'Agɛmɔ', 'Ògún', 'Owewe', 'Ɔ̀wàrà', 'Bélú', 'Ɔ̀pɛ̀'], + 'first_day_of_week' => 1, + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd, D MMMM YYYY HH:mm', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yo_NG.php b/vendor/nesbot/carbon/src/Carbon/Lang/yo_NG.php new file mode 100644 index 0000000..6860bc1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yo_NG.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/yo.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yue.php b/vendor/nesbot/carbon/src/Carbon/Lang/yue.php new file mode 100644 index 0000000..ce233a4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yue.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/yue_HK.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yue_HK.php b/vendor/nesbot/carbon/src/Carbon/Lang/yue_HK.php new file mode 100644 index 0000000..4e7d5c3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yue_HK.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/zh_HK.php', [ + 'formats' => [ + 'L' => 'YYYY年MM月DD日 dddd', + ], + 'months' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], + 'months_short' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], + 'weekdays' => ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], + 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'], + 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'], + 'first_day_of_week' => 0, + 'day_of_first_week_of_year' => 1, + 'meridiem' => ['上午', '下午'], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yue_Hans.php b/vendor/nesbot/carbon/src/Carbon/Lang/yue_Hans.php new file mode 100644 index 0000000..db913ca --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yue_Hans.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/zh_Hans.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yue_Hant.php b/vendor/nesbot/carbon/src/Carbon/Lang/yue_Hant.php new file mode 100644 index 0000000..e2526f1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yue_Hant.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/zh_Hant.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yuw.php b/vendor/nesbot/carbon/src/Carbon/Lang/yuw.php new file mode 100644 index 0000000..8efdc93 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yuw.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/yuw_PG.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yuw_PG.php b/vendor/nesbot/carbon/src/Carbon/Lang/yuw_PG.php new file mode 100644 index 0000000..b99ad2e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/yuw_PG.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Information from native speakers Hannah Sarvasy nungon.localization@gmail.com + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YY', + ], + 'months' => ['jenuari', 'febuari', 'mas', 'epril', 'mei', 'jun', 'julai', 'ögus', 'septemba', 'öktoba', 'nöwemba', 'diksemba'], + 'months_short' => ['jen', 'feb', 'mas', 'epr', 'mei', 'jun', 'jul', 'ögu', 'sep', 'ökt', 'nöw', 'dis'], + 'weekdays' => ['sönda', 'mönda', 'sinda', 'mitiwö', 'sogipbono', 'nenggo', 'söndanggie'], + 'weekdays_short' => ['sön', 'mön', 'sin', 'mit', 'soi', 'nen', 'sab'], + 'weekdays_min' => ['sön', 'mön', 'sin', 'mit', 'soi', 'nen', 'sab'], + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zgh.php b/vendor/nesbot/carbon/src/Carbon/Lang/zgh.php new file mode 100644 index 0000000..4d2c3b3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zgh.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - BAKTETE Miloud + */ +return [ + 'year' => ':count ⵓⵙⴳⴳⵯⴰⵙ|:count ⵉⵙⴳⴳⵓⵙⴰ', + 'a_year' => 'ⵓⵙⴳⴳⵯⴰⵙ|:count ⵉⵙⴳⴳⵓⵙⴰ', + 'y' => ':count ⵓⵙⴳⴳⵯⴰⵙ|:count ⵉⵙⴳⴳⵓⵙⴰ', + 'month' => ':count ⵡⴰⵢⵢⵓⵔ|:count ⴰⵢⵢⵓⵔⵏ', + 'a_month' => 'ⵉⴷⵊ ⵡⴰⵢⵢⵓⵔ|:count ⴰⵢⵢⵓⵔⵏ', + 'm' => ':count ⴰⵢⵢⵓⵔⵏ', + 'week' => ':count ⵉⵎⴰⵍⴰⵙⵙ|:count ⵉⵎⴰⵍⴰⵙⵙⵏ', + 'a_week' => 'ⵉⵛⵜ ⵉⵎⴰⵍⴰⵙⵙ|:count ⵉⵎⴰⵍⴰⵙⵙⵏ', + 'w' => ':count ⵉⵎⴰⵍⴰⵙⵙ.', + 'day' => ':count ⵡⴰⵙⵙ|:count ⵓⵙⵙⴰⵏ', + 'a_day' => 'ⵉⴷⵊ ⵡⴰⵙⵙ|:count ⵓⵙⵙⴰⵏ', + 'd' => ':count ⵓ', + 'hour' => ':count ⵜⵙⵔⴰⴳⵜ|:count ⵜⵉⵙⵔⴰⴳⵉⵏ', + 'a_hour' => 'ⵉⵛⵜ ⵜⵙⵔⴰⴳⵜ|:count ⵜⵉⵙⵔⴰⴳⵉⵏ', + 'h' => ':count ⵜ', + 'minute' => ':count ⵜⵓⵙⴷⵉⴷⵜ|:count ⵜⵓⵙⴷⵉⴷⵉⵏ', + 'a_minute' => 'ⵉⵛⵜ ⵜⵓⵙⴷⵉⴷⵜ|:count ⵜⵓⵙⴷⵉⴷⵉⵏ', + 'min' => ':count ⵜⵓⵙ', + 'second' => ':count ⵜⵙⵉⵏⵜ|:count ⵜⵉⵙⵉⵏⴰ', + 'a_second' => 'ⴽⵔⴰ ⵜⵉⵙⵉⵏⴰ|:count ⵜⵉⵙⵉⵏⴰ', + 's' => ':count ⵜ', + 'ago' => 'ⵣⴳ :time', + 'from_now' => 'ⴷⴳ :time', + 'after' => ':time ⴰⵡⴰⵔ', + 'before' => ':time ⴷⴰⵜ', + 'diff_now' => 'ⴰⴷⵡⴰⵍⵉ', + 'diff_today' => 'ⴰⵙⵙ', + 'diff_today_regexp' => 'ⴰⵙⵙ(?:\\s+ⴰ/ⴰⴷ)?(?:\\s+ⴳ)?', + 'diff_yesterday' => 'ⴰⵙⵙⵏⵏⴰⵟ', + 'diff_yesterday_regexp' => 'ⴰⵙⵙⵏⵏⴰⵟ(?:\\s+ⴳ)?', + 'diff_tomorrow' => 'ⴰⵙⴽⴽⴰ', + 'diff_tomorrow_regexp' => 'ⴰⵙⴽⴽⴰ(?:\\s+ⴳ)?', + 'diff_before_yesterday' => 'ⴼⵔ ⵉⴹⵏⵏⴰⵟ', + 'diff_after_tomorrow' => 'ⵏⴰⴼ ⵓⵙⴽⴽⴰ', + 'period_recurrences' => ':count ⵜⵉⴽⴽⴰⵍ', + 'period_interval' => 'ⴽⵓ :interval', + 'period_start_date' => 'ⴳ :date', + 'period_end_date' => 'ⵉ :date', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'DD/MM/YYYY', + 'LL' => 'D MMMM YYYY', + 'LLL' => 'D MMMM YYYY HH:mm', + 'LLLL' => 'dddd D MMMM YYYY HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[ⴰⵙⵙ ⴰ/ⴰⴷ ⴳ] LT', + 'nextDay' => '[ⴰⵙⴽⴽⴰ ⴳ] LT', + 'nextWeek' => 'dddd [ⴳ] LT', + 'lastDay' => '[ⴰⵙⵙⵏⵏⴰⵟ ⴳ] LT', + 'lastWeek' => 'dddd [ⴰⵎⴳⴳⴰⵔⵓ ⴳ] LT', + 'sameElse' => 'L', + ], + 'meridiem' => ['ⵜⵉⴼⴰⵡⵜ', 'ⵜⴰⴷⴳⴳⵯⴰⵜ'], + 'months' => ['ⵉⵏⵏⴰⵢⵔ', 'ⴱⵕⴰⵢⵕ', 'ⵎⴰⵕⵚ', 'ⵉⴱⵔⵉⵔ', 'ⵎⴰⵢⵢⵓ', 'ⵢⵓⵏⵢⵓ', 'ⵢⵓⵍⵢⵓⵣ', 'ⵖⵓⵛⵜ', 'ⵛⵓⵜⴰⵏⴱⵉⵔ', 'ⴽⵟⵓⴱⵕ', 'ⵏⵓⵡⴰⵏⴱⵉⵔ', 'ⴷⵓⵊⴰⵏⴱⵉⵔ'], + 'months_short' => ['ⵉⵏⵏ', 'ⴱⵕⴰ', 'ⵎⴰⵕ', 'ⵉⴱⵔ', 'ⵎⴰⵢ', 'ⵢⵓⵏ', 'ⵢⵓⵍ', 'ⵖⵓⵛ', 'ⵛⵓⵜ', 'ⴽⵟⵓ', 'ⵏⵓⵡ', 'ⴷⵓⵊ'], + 'weekdays' => ['ⵓⵙⴰⵎⴰⵙ', 'ⵡⴰⵢⵏⴰⵙ', 'ⵓⵙⵉⵏⴰⵙ', 'ⵡⴰⴽⵕⴰⵙ', 'ⵓⴽⵡⴰⵙ', 'ⵓⵙⵉⵎⵡⴰⵙ', 'ⵓⵙⵉⴹⵢⴰⵙ'], + 'weekdays_short' => ['ⵓⵙⴰ', 'ⵡⴰⵢ', 'ⵓⵙⵉ', 'ⵡⴰⴽ', 'ⵓⴽⵡ', 'ⵓⵙⵉⵎ', 'ⵓⵙⵉⴹ'], + 'weekdays_min' => ['ⵓⵙⴰ', 'ⵡⴰⵢ', 'ⵓⵙⵉ', 'ⵡⴰⴽ', 'ⵓⴽⵡ', 'ⵓⵙⵉⵎ', 'ⵓⵙⵉⴹ'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 1, + 'list' => [', ', ' ⴷ '], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh.php new file mode 100644 index 0000000..1187c3d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - xuri + * - sycuato + * - bokideckonja + * - Luo Ning + * - William Yang (williamyang233) + */ +return array_merge(require __DIR__.'/zh_Hans.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY/MM/DD', + 'LL' => 'YYYY年M月D日', + 'LLL' => 'YYYY年M月D日 A h点mm分', + 'LLLL' => 'YYYY年M月D日dddd A h点mm分', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_CN.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_CN.php new file mode 100644 index 0000000..9c05d5a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_CN.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - monkeycon + * - François B + * - Jason Katz-Brown + * - Serhan Apaydın + * - Matt Johnson + * - JD Isaacks + * - Zeno Zeng + * - Chris Hemp + * - shankesgk2 + */ +return array_merge(require __DIR__.'/zh.php', [ + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY/MM/DD', + 'LL' => 'YYYY年M月D日', + 'LLL' => 'YYYY年M月D日Ah点mm分', + 'LLLL' => 'YYYY年M月D日ddddAh点mm分', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_HK.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_HK.php new file mode 100644 index 0000000..c3ee9fc --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_HK.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/zh_Hant_HK.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans.php new file mode 100644 index 0000000..9b91785 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - monkeycon + * - François B + * - Jason Katz-Brown + * - Konstantin Konev + * - Chris Lam + * - Serhan Apaydın + * - Gary Lo + * - JD Isaacks + * - Chris Hemp + * - shankesgk2 + * - Daniel Cheung (danvim) + */ +return [ + 'year' => ':count:optional-space年', + 'y' => ':count:optional-space年', + 'month' => ':count:optional-space个月', + 'm' => ':count:optional-space个月', + 'week' => ':count:optional-space周', + 'w' => ':count:optional-space周', + 'day' => ':count:optional-space天', + 'd' => ':count:optional-space天', + 'hour' => ':count:optional-space小时', + 'h' => ':count:optional-space小时', + 'minute' => ':count:optional-space分钟', + 'min' => ':count:optional-space分钟', + 'second' => ':count:optional-space秒', + 'a_second' => '{1}几秒|]1,Inf[:count:optional-space秒', + 's' => ':count:optional-space秒', + 'ago' => ':time前', + 'from_now' => ':time后', + 'after' => ':time后', + 'before' => ':time前', + 'diff_now' => '现在', + 'diff_today' => '今天', + 'diff_yesterday' => '昨天', + 'diff_tomorrow' => '明天', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY/MM/DD', + 'LL' => 'YYYY年M月D日', + 'LLL' => 'YYYY年M月D日 HH:mm', + 'LLLL' => 'YYYY年M月D日dddd HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[今天]LT', + 'nextDay' => '[明天]LT', + 'nextWeek' => '[下]ddddLT', + 'lastDay' => '[昨天]LT', + 'lastWeek' => '[上]ddddLT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number, $period) { + switch ($period) { + case 'd': + case 'D': + case 'DDD': + return $number.'日'; + case 'M': + return $number.'月'; + case 'w': + case 'W': + return $number.'周'; + default: + return $number; + } + }, + 'meridiem' => function ($hour, $minute) { + $time = $hour * 100 + $minute; + if ($time < 600) { + return '凌晨'; + } + if ($time < 900) { + return '早上'; + } + if ($time < 1130) { + return '上午'; + } + if ($time < 1230) { + return '中午'; + } + if ($time < 1800) { + return '下午'; + } + + return '晚上'; + }, + 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + 'months_short' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], + 'weekdays' => ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], + 'weekdays_short' => ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], + 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => '', +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_HK.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_HK.php new file mode 100644 index 0000000..db913ca --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_HK.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/zh_Hans.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_MO.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_MO.php new file mode 100644 index 0000000..db913ca --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_MO.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/zh_Hans.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_SG.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_SG.php new file mode 100644 index 0000000..db913ca --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_SG.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/zh_Hans.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant.php new file mode 100644 index 0000000..a27b610 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Adam + * - monkeycon + * - François B + * - Jason Katz-Brown + * - Chris Lam + * - Serhan Apaydın + * - Gary Lo + * - JD Isaacks + * - Chris Hemp + * - Eddie + * - KID + * - shankesgk2 + * - Daniel Cheung (danvim) + */ +return [ + 'year' => ':count:optional-space年', + 'y' => ':count:optional-space年', + 'month' => ':count:optional-space個月', + 'm' => ':count:optional-space月', + 'week' => ':count:optional-space週', + 'w' => ':count:optional-space週', + 'day' => ':count:optional-space天', + 'd' => ':count:optional-space天', + 'hour' => ':count:optional-space小時', + 'h' => ':count:optional-space小時', + 'minute' => ':count:optional-space分鐘', + 'min' => ':count:optional-space分鐘', + 'second' => ':count:optional-space秒', + 'a_second' => '{1}幾秒|]1,Inf[:count:optional-space秒', + 's' => ':count:optional-space秒', + 'ago' => ':time前', + 'from_now' => ':time後', + 'after' => ':time後', + 'before' => ':time前', + 'diff_now' => '現在', + 'diff_today' => '今天', + 'diff_yesterday' => '昨天', + 'diff_tomorrow' => '明天', + 'formats' => [ + 'LT' => 'HH:mm', + 'LTS' => 'HH:mm:ss', + 'L' => 'YYYY/MM/DD', + 'LL' => 'YYYY年M月D日', + 'LLL' => 'YYYY年M月D日 HH:mm', + 'LLLL' => 'YYYY年M月D日dddd HH:mm', + ], + 'calendar' => [ + 'sameDay' => '[今天] LT', + 'nextDay' => '[明天] LT', + 'nextWeek' => '[下]dddd LT', + 'lastDay' => '[昨天] LT', + 'lastWeek' => '[上]dddd LT', + 'sameElse' => 'L', + ], + 'ordinal' => function ($number, $period) { + switch ($period) { + case 'd': + case 'D': + case 'DDD': + return $number.'日'; + case 'M': + return $number.'月'; + case 'w': + case 'W': + return $number.'周'; + default: + return $number; + } + }, + 'meridiem' => function ($hour, $minute) { + $time = $hour * 100 + $minute; + if ($time < 600) { + return '凌晨'; + } + if ($time < 900) { + return '早上'; + } + if ($time < 1130) { + return '上午'; + } + if ($time < 1230) { + return '中午'; + } + if ($time < 1800) { + return '下午'; + } + + return '晚上'; + }, + 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + 'months_short' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], + 'weekdays' => ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], + 'weekdays_short' => ['週日', '週一', '週二', '週三', '週四', '週五', '週六'], + 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'], + 'first_day_of_week' => 1, + 'day_of_first_week_of_year' => 4, + 'list' => '', +]; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_HK.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_HK.php new file mode 100644 index 0000000..e2526f1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_HK.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/zh_Hant.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_MO.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_MO.php new file mode 100644 index 0000000..e2526f1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_MO.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/zh_Hant.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_TW.php new file mode 100644 index 0000000..e2526f1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_TW.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/zh_Hant.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_MO.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_MO.php new file mode 100644 index 0000000..1c86d47 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_MO.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - tarunvelli + * - Eddie + * - KID + * - shankesgk2 + */ +return array_replace_recursive(require __DIR__.'/zh_Hant.php', [ + 'after' => ':time后', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_SG.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_SG.php new file mode 100644 index 0000000..c451a56 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_SG.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/zh.php', [ + 'formats' => [ + 'L' => 'YYYY年MM月DD日', + ], + 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + 'months_short' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + 'weekdays' => ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], + 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'], + 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'], + 'day_of_first_week_of_year' => 1, +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php new file mode 100644 index 0000000..c6789ed --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/zh_Hant_TW.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_YUE.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_YUE.php new file mode 100644 index 0000000..b0d9ba8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_YUE.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org + */ +return array_replace_recursive(require __DIR__.'/zh.php', [ + 'formats' => [ + 'L' => 'YYYY-MM-DD', + ], +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zu.php b/vendor/nesbot/carbon/src/Carbon/Lang/zu.php new file mode 100644 index 0000000..9a6cce0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zu.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Unknown default region, use the first alphabetically. + */ +return require __DIR__.'/zu_ZA.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zu_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/zu_ZA.php new file mode 100644 index 0000000..6bfb72f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zu_ZA.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Authors: + * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za + */ +return array_replace_recursive(require __DIR__.'/en.php', [ + 'formats' => [ + 'L' => 'DD/MM/YYYY', + ], + 'months' => ['Januwari', 'Februwari', 'Mashi', 'Ephreli', 'Meyi', 'Juni', 'Julayi', 'Agasti', 'Septhemba', 'Okthoba', 'Novemba', 'Disemba'], + 'months_short' => ['Jan', 'Feb', 'Mas', 'Eph', 'Mey', 'Jun', 'Jul', 'Aga', 'Sep', 'Okt', 'Nov', 'Dis'], + 'weekdays' => ['iSonto', 'uMsombuluko', 'uLwesibili', 'uLwesithathu', 'uLwesine', 'uLwesihlanu', 'uMgqibelo'], + 'weekdays_short' => ['Son', 'Mso', 'Bil', 'Tha', 'Sin', 'Hla', 'Mgq'], + 'weekdays_min' => ['Son', 'Mso', 'Bil', 'Tha', 'Sin', 'Hla', 'Mgq'], + 'day_of_first_week_of_year' => 1, + + 'year' => 'kweminyaka engu-:count', + 'y' => 'kweminyaka engu-:count', + 'a_year' => 'kweminyaka engu-:count', + + 'month' => 'izinyanga ezingu-:count', + 'm' => 'izinyanga ezingu-:count', + 'a_month' => 'izinyanga ezingu-:count', + + 'week' => 'lwamasonto angu-:count', + 'w' => 'lwamasonto angu-:count', + 'a_week' => 'lwamasonto angu-:count', + + 'day' => 'ezingaba ngu-:count', + 'd' => 'ezingaba ngu-:count', + 'a_day' => 'ezingaba ngu-:count', + + 'hour' => 'amahora angu-:count', + 'h' => 'amahora angu-:count', + 'a_hour' => 'amahora angu-:count', + + 'minute' => 'ngemizuzu engu-:count', + 'min' => 'ngemizuzu engu-:count', + 'a_minute' => 'ngemizuzu engu-:count', + + 'second' => 'imizuzwana engu-:count', + 's' => 'imizuzwana engu-:count', + 'a_second' => 'imizuzwana engu-:count', +]); diff --git a/vendor/nesbot/carbon/src/Carbon/Language.php b/vendor/nesbot/carbon/src/Carbon/Language.php new file mode 100644 index 0000000..1fb5baf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Language.php @@ -0,0 +1,342 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use JsonSerializable; +use ReturnTypeWillChange; + +class Language implements JsonSerializable +{ + /** + * @var array + */ + protected static $languagesNames; + + /** + * @var array + */ + protected static $regionsNames; + + /** + * @var string + */ + protected $id; + + /** + * @var string + */ + protected $code; + + /** + * @var string|null + */ + protected $variant; + + /** + * @var string|null + */ + protected $region; + + /** + * @var array + */ + protected $names; + + /** + * @var string + */ + protected $isoName; + + /** + * @var string + */ + protected $nativeName; + + public function __construct(string $id) + { + $this->id = str_replace('-', '_', $id); + $parts = explode('_', $this->id); + $this->code = $parts[0]; + + if (isset($parts[1])) { + if (!preg_match('/^[A-Z]+$/', $parts[1])) { + $this->variant = $parts[1]; + $parts[1] = $parts[2] ?? null; + } + if ($parts[1]) { + $this->region = $parts[1]; + } + } + } + + /** + * Get the list of the known languages. + * + * @return array + */ + public static function all() + { + if (!static::$languagesNames) { + static::$languagesNames = require __DIR__.'/List/languages.php'; + } + + return static::$languagesNames; + } + + /** + * Get the list of the known regions. + * + * @return array + */ + public static function regions() + { + if (!static::$regionsNames) { + static::$regionsNames = require __DIR__.'/List/regions.php'; + } + + return static::$regionsNames; + } + + /** + * Get both isoName and nativeName as an array. + * + * @return array + */ + public function getNames(): array + { + if (!$this->names) { + $this->names = static::all()[$this->code] ?? [ + 'isoName' => $this->code, + 'nativeName' => $this->code, + ]; + } + + return $this->names; + } + + /** + * Returns the original locale ID. + * + * @return string + */ + public function getId(): string + { + return $this->id; + } + + /** + * Returns the code of the locale "en"/"fr". + * + * @return string + */ + public function getCode(): string + { + return $this->code; + } + + /** + * Returns the variant code such as cyrl/latn. + * + * @return string|null + */ + public function getVariant(): ?string + { + return $this->variant; + } + + /** + * Returns the variant such as Cyrillic/Latin. + * + * @return string|null + */ + public function getVariantName(): ?string + { + if ($this->variant === 'Latn') { + return 'Latin'; + } + + if ($this->variant === 'Cyrl') { + return 'Cyrillic'; + } + + return $this->variant; + } + + /** + * Returns the region part of the locale. + * + * @return string|null + */ + public function getRegion(): ?string + { + return $this->region; + } + + /** + * Returns the region name for the current language. + * + * @return string|null + */ + public function getRegionName(): ?string + { + return $this->region ? (static::regions()[$this->region] ?? $this->region) : null; + } + + /** + * Returns the long ISO language name. + * + * @return string + */ + public function getFullIsoName(): string + { + if (!$this->isoName) { + $this->isoName = $this->getNames()['isoName']; + } + + return $this->isoName; + } + + /** + * Set the ISO language name. + * + * @param string $isoName + */ + public function setIsoName(string $isoName): self + { + $this->isoName = $isoName; + + return $this; + } + + /** + * Return the full name of the language in this language. + * + * @return string + */ + public function getFullNativeName(): string + { + if (!$this->nativeName) { + $this->nativeName = $this->getNames()['nativeName']; + } + + return $this->nativeName; + } + + /** + * Set the name of the language in this language. + * + * @param string $nativeName + */ + public function setNativeName(string $nativeName): self + { + $this->nativeName = $nativeName; + + return $this; + } + + /** + * Returns the short ISO language name. + * + * @return string + */ + public function getIsoName(): string + { + $name = $this->getFullIsoName(); + + return trim(strstr($name, ',', true) ?: $name); + } + + /** + * Get the short name of the language in this language. + * + * @return string + */ + public function getNativeName(): string + { + $name = $this->getFullNativeName(); + + return trim(strstr($name, ',', true) ?: $name); + } + + /** + * Get a string with short ISO name, region in parentheses if applicable, variant in parentheses if applicable. + * + * @return string + */ + public function getIsoDescription() + { + $region = $this->getRegionName(); + $variant = $this->getVariantName(); + + return $this->getIsoName().($region ? ' ('.$region.')' : '').($variant ? ' ('.$variant.')' : ''); + } + + /** + * Get a string with short native name, region in parentheses if applicable, variant in parentheses if applicable. + * + * @return string + */ + public function getNativeDescription() + { + $region = $this->getRegionName(); + $variant = $this->getVariantName(); + + return $this->getNativeName().($region ? ' ('.$region.')' : '').($variant ? ' ('.$variant.')' : ''); + } + + /** + * Get a string with long ISO name, region in parentheses if applicable, variant in parentheses if applicable. + * + * @return string + */ + public function getFullIsoDescription() + { + $region = $this->getRegionName(); + $variant = $this->getVariantName(); + + return $this->getFullIsoName().($region ? ' ('.$region.')' : '').($variant ? ' ('.$variant.')' : ''); + } + + /** + * Get a string with long native name, region in parentheses if applicable, variant in parentheses if applicable. + * + * @return string + */ + public function getFullNativeDescription() + { + $region = $this->getRegionName(); + $variant = $this->getVariantName(); + + return $this->getFullNativeName().($region ? ' ('.$region.')' : '').($variant ? ' ('.$variant.')' : ''); + } + + /** + * Returns the original locale ID. + * + * @return string + */ + public function __toString() + { + return $this->getId(); + } + + /** + * Get a string with short ISO name, region in parentheses if applicable, variant in parentheses if applicable. + * + * @return string + */ + #[ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getIsoDescription(); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php b/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php new file mode 100644 index 0000000..84e241e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Laravel; + +use Carbon\Carbon; +use Carbon\CarbonImmutable; +use Carbon\CarbonInterval; +use Carbon\CarbonPeriod; +use Illuminate\Contracts\Events\Dispatcher as DispatcherContract; +use Illuminate\Events\Dispatcher; +use Illuminate\Events\EventDispatcher; +use Illuminate\Support\Carbon as IlluminateCarbon; +use Illuminate\Support\Facades\Date; +use Throwable; + +class ServiceProvider extends \Illuminate\Support\ServiceProvider +{ + /** @var callable|null */ + protected $appGetter = null; + + /** @var callable|null */ + protected $localeGetter = null; + + public function setAppGetter(?callable $appGetter): void + { + $this->appGetter = $appGetter; + } + + public function setLocaleGetter(?callable $localeGetter): void + { + $this->localeGetter = $localeGetter; + } + + public function boot() + { + $this->updateLocale(); + + if (!$this->app->bound('events')) { + return; + } + + $service = $this; + $events = $this->app['events']; + + if ($this->isEventDispatcher($events)) { + $events->listen(class_exists('Illuminate\Foundation\Events\LocaleUpdated') ? 'Illuminate\Foundation\Events\LocaleUpdated' : 'locale.changed', function () use ($service) { + $service->updateLocale(); + }); + } + } + + public function updateLocale() + { + $locale = $this->getLocale(); + + if ($locale === null) { + return; + } + + Carbon::setLocale($locale); + CarbonImmutable::setLocale($locale); + CarbonPeriod::setLocale($locale); + CarbonInterval::setLocale($locale); + + if (class_exists(IlluminateCarbon::class)) { + IlluminateCarbon::setLocale($locale); + } + + if (class_exists(Date::class)) { + try { + $root = Date::getFacadeRoot(); + $root->setLocale($locale); + } catch (Throwable $e) { + // Non Carbon class in use in Date facade + } + } + } + + public function register() + { + // Needed for Laravel < 5.3 compatibility + } + + protected function getLocale() + { + if ($this->localeGetter) { + return ($this->localeGetter)(); + } + + $app = $this->getApp(); + $app = $app && method_exists($app, 'getLocale') + ? $app + : $this->getGlobalApp('translator'); + + return $app ? $app->getLocale() : null; + } + + protected function getApp() + { + if ($this->appGetter) { + return ($this->appGetter)(); + } + + return $this->app ?? $this->getGlobalApp(); + } + + protected function getGlobalApp(...$args) + { + return \function_exists('app') ? \app(...$args) : null; + } + + protected function isEventDispatcher($instance) + { + return $instance instanceof EventDispatcher + || $instance instanceof Dispatcher + || $instance instanceof DispatcherContract; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/List/languages.php b/vendor/nesbot/carbon/src/Carbon/List/languages.php new file mode 100644 index 0000000..5b5d9a1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/List/languages.php @@ -0,0 +1,1239 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + /* + * ISO 639-2 + */ + 'ab' => [ + 'isoName' => 'Abkhazian', + 'nativeName' => 'аҧсуа бызшәа, аҧсшәа', + ], + 'aa' => [ + 'isoName' => 'Afar', + 'nativeName' => 'Afaraf', + ], + 'af' => [ + 'isoName' => 'Afrikaans', + 'nativeName' => 'Afrikaans', + ], + 'ak' => [ + 'isoName' => 'Akan', + 'nativeName' => 'Akan', + ], + 'sq' => [ + 'isoName' => 'Albanian', + 'nativeName' => 'Shqip', + ], + 'am' => [ + 'isoName' => 'Amharic', + 'nativeName' => 'አማርኛ', + ], + 'ar' => [ + 'isoName' => 'Arabic', + 'nativeName' => 'العربية', + ], + 'an' => [ + 'isoName' => 'Aragonese', + 'nativeName' => 'aragonés', + ], + 'hy' => [ + 'isoName' => 'Armenian', + 'nativeName' => 'Հայերեն', + ], + 'as' => [ + 'isoName' => 'Assamese', + 'nativeName' => 'অসমীয়া', + ], + 'av' => [ + 'isoName' => 'Avaric', + 'nativeName' => 'авар мацӀ, магӀарул мацӀ', + ], + 'ae' => [ + 'isoName' => 'Avestan', + 'nativeName' => 'avesta', + ], + 'ay' => [ + 'isoName' => 'Aymara', + 'nativeName' => 'aymar aru', + ], + 'az' => [ + 'isoName' => 'Azerbaijani', + 'nativeName' => 'azərbaycan dili', + ], + 'bm' => [ + 'isoName' => 'Bambara', + 'nativeName' => 'bamanankan', + ], + 'ba' => [ + 'isoName' => 'Bashkir', + 'nativeName' => 'башҡорт теле', + ], + 'eu' => [ + 'isoName' => 'Basque', + 'nativeName' => 'euskara, euskera', + ], + 'be' => [ + 'isoName' => 'Belarusian', + 'nativeName' => 'беларуская мова', + ], + 'bn' => [ + 'isoName' => 'Bengali', + 'nativeName' => 'বাংলা', + ], + 'bh' => [ + 'isoName' => 'Bihari languages', + 'nativeName' => 'भोजपुरी', + ], + 'bi' => [ + 'isoName' => 'Bislama', + 'nativeName' => 'Bislama', + ], + 'bs' => [ + 'isoName' => 'Bosnian', + 'nativeName' => 'bosanski jezik', + ], + 'br' => [ + 'isoName' => 'Breton', + 'nativeName' => 'brezhoneg', + ], + 'bg' => [ + 'isoName' => 'Bulgarian', + 'nativeName' => 'български език', + ], + 'my' => [ + 'isoName' => 'Burmese', + 'nativeName' => 'ဗမာစာ', + ], + 'ca' => [ + 'isoName' => 'Catalan, Valencian', + 'nativeName' => 'català, valencià', + ], + 'ch' => [ + 'isoName' => 'Chamorro', + 'nativeName' => 'Chamoru', + ], + 'ce' => [ + 'isoName' => 'Chechen', + 'nativeName' => 'нохчийн мотт', + ], + 'ny' => [ + 'isoName' => 'Chichewa, Chewa, Nyanja', + 'nativeName' => 'chiCheŵa, chinyanja', + ], + 'zh' => [ + 'isoName' => 'Chinese', + 'nativeName' => '中文 (Zhōngwén), 汉语, 漢語', + ], + 'cv' => [ + 'isoName' => 'Chuvash', + 'nativeName' => 'чӑваш чӗлхи', + ], + 'kw' => [ + 'isoName' => 'Cornish', + 'nativeName' => 'Kernewek', + ], + 'co' => [ + 'isoName' => 'Corsican', + 'nativeName' => 'corsu, lingua corsa', + ], + 'cr' => [ + 'isoName' => 'Cree', + 'nativeName' => 'ᓀᐦᐃᔭᐍᐏᐣ', + ], + 'hr' => [ + 'isoName' => 'Croatian', + 'nativeName' => 'hrvatski jezik', + ], + 'cs' => [ + 'isoName' => 'Czech', + 'nativeName' => 'čeština, český jazyk', + ], + 'da' => [ + 'isoName' => 'Danish', + 'nativeName' => 'dansk', + ], + 'dv' => [ + 'isoName' => 'Divehi, Dhivehi, Maldivian', + 'nativeName' => 'ދިވެހި', + ], + 'nl' => [ + 'isoName' => 'Dutch, Flemish', + 'nativeName' => 'Nederlands, Vlaams', + ], + 'dz' => [ + 'isoName' => 'Dzongkha', + 'nativeName' => 'རྫོང་ཁ', + ], + 'en' => [ + 'isoName' => 'English', + 'nativeName' => 'English', + ], + 'eo' => [ + 'isoName' => 'Esperanto', + 'nativeName' => 'Esperanto', + ], + 'et' => [ + 'isoName' => 'Estonian', + 'nativeName' => 'eesti, eesti keel', + ], + 'ee' => [ + 'isoName' => 'Ewe', + 'nativeName' => 'Eʋegbe', + ], + 'fo' => [ + 'isoName' => 'Faroese', + 'nativeName' => 'føroyskt', + ], + 'fj' => [ + 'isoName' => 'Fijian', + 'nativeName' => 'vosa Vakaviti', + ], + 'fi' => [ + 'isoName' => 'Finnish', + 'nativeName' => 'suomi, suomen kieli', + ], + 'fr' => [ + 'isoName' => 'French', + 'nativeName' => 'français', + ], + 'ff' => [ + 'isoName' => 'Fulah', + 'nativeName' => 'Fulfulde, Pulaar, Pular', + ], + 'gl' => [ + 'isoName' => 'Galician', + 'nativeName' => 'Galego', + ], + 'ka' => [ + 'isoName' => 'Georgian', + 'nativeName' => 'ქართული', + ], + 'de' => [ + 'isoName' => 'German', + 'nativeName' => 'Deutsch', + ], + 'el' => [ + 'isoName' => 'Greek (modern)', + 'nativeName' => 'ελληνικά', + ], + 'gn' => [ + 'isoName' => 'Guaraní', + 'nativeName' => 'Avañe\'ẽ', + ], + 'gu' => [ + 'isoName' => 'Gujarati', + 'nativeName' => 'ગુજરાતી', + ], + 'ht' => [ + 'isoName' => 'Haitian, Haitian Creole', + 'nativeName' => 'Kreyòl ayisyen', + ], + 'ha' => [ + 'isoName' => 'Hausa', + 'nativeName' => '(Hausa) هَوُسَ', + ], + 'he' => [ + 'isoName' => 'Hebrew (modern)', + 'nativeName' => 'עברית', + ], + 'hz' => [ + 'isoName' => 'Herero', + 'nativeName' => 'Otjiherero', + ], + 'hi' => [ + 'isoName' => 'Hindi', + 'nativeName' => 'हिन्दी, हिंदी', + ], + 'ho' => [ + 'isoName' => 'Hiri Motu', + 'nativeName' => 'Hiri Motu', + ], + 'hu' => [ + 'isoName' => 'Hungarian', + 'nativeName' => 'magyar', + ], + 'ia' => [ + 'isoName' => 'Interlingua', + 'nativeName' => 'Interlingua', + ], + 'id' => [ + 'isoName' => 'Indonesian', + 'nativeName' => 'Bahasa Indonesia', + ], + 'ie' => [ + 'isoName' => 'Interlingue', + 'nativeName' => 'Originally called Occidental; then Interlingue after WWII', + ], + 'ga' => [ + 'isoName' => 'Irish', + 'nativeName' => 'Gaeilge', + ], + 'ig' => [ + 'isoName' => 'Igbo', + 'nativeName' => 'Asụsụ Igbo', + ], + 'ik' => [ + 'isoName' => 'Inupiaq', + 'nativeName' => 'Iñupiaq, Iñupiatun', + ], + 'io' => [ + 'isoName' => 'Ido', + 'nativeName' => 'Ido', + ], + 'is' => [ + 'isoName' => 'Icelandic', + 'nativeName' => 'Íslenska', + ], + 'it' => [ + 'isoName' => 'Italian', + 'nativeName' => 'Italiano', + ], + 'iu' => [ + 'isoName' => 'Inuktitut', + 'nativeName' => 'ᐃᓄᒃᑎᑐᑦ', + ], + 'ja' => [ + 'isoName' => 'Japanese', + 'nativeName' => '日本語 (にほんご)', + ], + 'jv' => [ + 'isoName' => 'Javanese', + 'nativeName' => 'ꦧꦱꦗꦮ, Basa Jawa', + ], + 'kl' => [ + 'isoName' => 'Kalaallisut, Greenlandic', + 'nativeName' => 'kalaallisut, kalaallit oqaasii', + ], + 'kn' => [ + 'isoName' => 'Kannada', + 'nativeName' => 'ಕನ್ನಡ', + ], + 'kr' => [ + 'isoName' => 'Kanuri', + 'nativeName' => 'Kanuri', + ], + 'ks' => [ + 'isoName' => 'Kashmiri', + 'nativeName' => 'कश्मीरी, كشميري‎', + ], + 'kk' => [ + 'isoName' => 'Kazakh', + 'nativeName' => 'қазақ тілі', + ], + 'km' => [ + 'isoName' => 'Central Khmer', + 'nativeName' => 'ខ្មែរ, ខេមរភាសា, ភាសាខ្មែរ', + ], + 'ki' => [ + 'isoName' => 'Kikuyu, Gikuyu', + 'nativeName' => 'Gĩkũyũ', + ], + 'rw' => [ + 'isoName' => 'Kinyarwanda', + 'nativeName' => 'Ikinyarwanda', + ], + 'ky' => [ + 'isoName' => 'Kirghiz, Kyrgyz', + 'nativeName' => 'Кыргызча, Кыргыз тили', + ], + 'kv' => [ + 'isoName' => 'Komi', + 'nativeName' => 'коми кыв', + ], + 'kg' => [ + 'isoName' => 'Kongo', + 'nativeName' => 'Kikongo', + ], + 'ko' => [ + 'isoName' => 'Korean', + 'nativeName' => '한국어', + ], + 'ku' => [ + 'isoName' => 'Kurdish', + 'nativeName' => 'Kurdî, کوردی‎', + ], + 'kj' => [ + 'isoName' => 'Kuanyama, Kwanyama', + 'nativeName' => 'Kuanyama', + ], + 'la' => [ + 'isoName' => 'Latin', + 'nativeName' => 'latine, lingua latina', + ], + 'lb' => [ + 'isoName' => 'Luxembourgish, Letzeburgesch', + 'nativeName' => 'Lëtzebuergesch', + ], + 'lg' => [ + 'isoName' => 'Ganda', + 'nativeName' => 'Luganda', + ], + 'li' => [ + 'isoName' => 'Limburgan, Limburger, Limburgish', + 'nativeName' => 'Limburgs', + ], + 'ln' => [ + 'isoName' => 'Lingala', + 'nativeName' => 'Lingála', + ], + 'lo' => [ + 'isoName' => 'Lao', + 'nativeName' => 'ພາສາລາວ', + ], + 'lt' => [ + 'isoName' => 'Lithuanian', + 'nativeName' => 'lietuvių kalba', + ], + 'lu' => [ + 'isoName' => 'Luba-Katanga', + 'nativeName' => 'Kiluba', + ], + 'lv' => [ + 'isoName' => 'Latvian', + 'nativeName' => 'latviešu valoda', + ], + 'gv' => [ + 'isoName' => 'Manx', + 'nativeName' => 'Gaelg, Gailck', + ], + 'mk' => [ + 'isoName' => 'Macedonian', + 'nativeName' => 'македонски јазик', + ], + 'mg' => [ + 'isoName' => 'Malagasy', + 'nativeName' => 'fiteny malagasy', + ], + 'ms' => [ + 'isoName' => 'Malay', + 'nativeName' => 'Bahasa Melayu, بهاس ملايو‎', + ], + 'ml' => [ + 'isoName' => 'Malayalam', + 'nativeName' => 'മലയാളം', + ], + 'mt' => [ + 'isoName' => 'Maltese', + 'nativeName' => 'Malti', + ], + 'mi' => [ + 'isoName' => 'Maori', + 'nativeName' => 'te reo Māori', + ], + 'mr' => [ + 'isoName' => 'Marathi', + 'nativeName' => 'मराठी', + ], + 'mh' => [ + 'isoName' => 'Marshallese', + 'nativeName' => 'Kajin M̧ajeļ', + ], + 'mn' => [ + 'isoName' => 'Mongolian', + 'nativeName' => 'Монгол хэл', + ], + 'na' => [ + 'isoName' => 'Nauru', + 'nativeName' => 'Dorerin Naoero', + ], + 'nv' => [ + 'isoName' => 'Navajo, Navaho', + 'nativeName' => 'Diné bizaad', + ], + 'nd' => [ + 'isoName' => 'North Ndebele', + 'nativeName' => 'isiNdebele', + ], + 'ne' => [ + 'isoName' => 'Nepali', + 'nativeName' => 'नेपाली', + ], + 'ng' => [ + 'isoName' => 'Ndonga', + 'nativeName' => 'Owambo', + ], + 'nb' => [ + 'isoName' => 'Norwegian Bokmål', + 'nativeName' => 'Norsk Bokmål', + ], + 'nn' => [ + 'isoName' => 'Norwegian Nynorsk', + 'nativeName' => 'Norsk Nynorsk', + ], + 'no' => [ + 'isoName' => 'Norwegian', + 'nativeName' => 'Norsk', + ], + 'ii' => [ + 'isoName' => 'Sichuan Yi, Nuosu', + 'nativeName' => 'ꆈꌠ꒿ Nuosuhxop', + ], + 'nr' => [ + 'isoName' => 'South Ndebele', + 'nativeName' => 'isiNdebele', + ], + 'oc' => [ + 'isoName' => 'Occitan', + 'nativeName' => 'occitan, lenga d\'òc', + ], + 'oj' => [ + 'isoName' => 'Ojibwa', + 'nativeName' => 'ᐊᓂᔑᓈᐯᒧᐎᓐ', + ], + 'cu' => [ + 'isoName' => 'Church Slavic, Church Slavonic, Old Church Slavonic, Old Slavonic, Old Bulgarian', + 'nativeName' => 'ѩзыкъ словѣньскъ', + ], + 'om' => [ + 'isoName' => 'Oromo', + 'nativeName' => 'Afaan Oromoo', + ], + 'or' => [ + 'isoName' => 'Oriya', + 'nativeName' => 'ଓଡ଼ିଆ', + ], + 'os' => [ + 'isoName' => 'Ossetian, Ossetic', + 'nativeName' => 'ирон æвзаг', + ], + 'pa' => [ + 'isoName' => 'Panjabi, Punjabi', + 'nativeName' => 'ਪੰਜਾਬੀ', + ], + 'pi' => [ + 'isoName' => 'Pali', + 'nativeName' => 'पाऴि', + ], + 'fa' => [ + 'isoName' => 'Persian', + 'nativeName' => 'فارسی', + ], + 'pl' => [ + 'isoName' => 'Polish', + 'nativeName' => 'język polski, polszczyzna', + ], + 'ps' => [ + 'isoName' => 'Pashto, Pushto', + 'nativeName' => 'پښتو', + ], + 'pt' => [ + 'isoName' => 'Portuguese', + 'nativeName' => 'Português', + ], + 'qu' => [ + 'isoName' => 'Quechua', + 'nativeName' => 'Runa Simi, Kichwa', + ], + 'rm' => [ + 'isoName' => 'Romansh', + 'nativeName' => 'Rumantsch Grischun', + ], + 'rn' => [ + 'isoName' => 'Rundi', + 'nativeName' => 'Ikirundi', + ], + 'ro' => [ + 'isoName' => 'Romanian, Moldavian, Moldovan', + 'nativeName' => 'Română', + ], + 'ru' => [ + 'isoName' => 'Russian', + 'nativeName' => 'русский', + ], + 'sa' => [ + 'isoName' => 'Sanskrit', + 'nativeName' => 'संस्कृतम्', + ], + 'sc' => [ + 'isoName' => 'Sardinian', + 'nativeName' => 'sardu', + ], + 'sd' => [ + 'isoName' => 'Sindhi', + 'nativeName' => 'सिन्धी, سنڌي، سندھی‎', + ], + 'se' => [ + 'isoName' => 'Northern Sami', + 'nativeName' => 'Davvisámegiella', + ], + 'sm' => [ + 'isoName' => 'Samoan', + 'nativeName' => 'gagana fa\'a Samoa', + ], + 'sg' => [ + 'isoName' => 'Sango', + 'nativeName' => 'yângâ tî sängö', + ], + 'sr' => [ + 'isoName' => 'Serbian', + 'nativeName' => 'српски језик', + ], + 'gd' => [ + 'isoName' => 'Gaelic, Scottish Gaelic', + 'nativeName' => 'Gàidhlig', + ], + 'sn' => [ + 'isoName' => 'Shona', + 'nativeName' => 'chiShona', + ], + 'si' => [ + 'isoName' => 'Sinhala, Sinhalese', + 'nativeName' => 'සිංහල', + ], + 'sk' => [ + 'isoName' => 'Slovak', + 'nativeName' => 'Slovenčina, Slovenský Jazyk', + ], + 'sl' => [ + 'isoName' => 'Slovene', + 'nativeName' => 'Slovenski Jezik, Slovenščina', + ], + 'so' => [ + 'isoName' => 'Somali', + 'nativeName' => 'Soomaaliga, af Soomaali', + ], + 'st' => [ + 'isoName' => 'Southern Sotho', + 'nativeName' => 'Sesotho', + ], + 'es' => [ + 'isoName' => 'Spanish, Castilian', + 'nativeName' => 'Español', + ], + 'su' => [ + 'isoName' => 'Sundanese', + 'nativeName' => 'Basa Sunda', + ], + 'sw' => [ + 'isoName' => 'Swahili', + 'nativeName' => 'Kiswahili', + ], + 'ss' => [ + 'isoName' => 'Swati', + 'nativeName' => 'SiSwati', + ], + 'sv' => [ + 'isoName' => 'Swedish', + 'nativeName' => 'Svenska', + ], + 'ta' => [ + 'isoName' => 'Tamil', + 'nativeName' => 'தமிழ்', + ], + 'te' => [ + 'isoName' => 'Telugu', + 'nativeName' => 'తెలుగు', + ], + 'tg' => [ + 'isoName' => 'Tajik', + 'nativeName' => 'тоҷикӣ, toçikī, تاجیکی‎', + ], + 'th' => [ + 'isoName' => 'Thai', + 'nativeName' => 'ไทย', + ], + 'ti' => [ + 'isoName' => 'Tigrinya', + 'nativeName' => 'ትግርኛ', + ], + 'bo' => [ + 'isoName' => 'Tibetan', + 'nativeName' => 'བོད་ཡིག', + ], + 'tk' => [ + 'isoName' => 'Turkmen', + 'nativeName' => 'Türkmen, Түркмен', + ], + 'tl' => [ + 'isoName' => 'Tagalog', + 'nativeName' => 'Wikang Tagalog', + ], + 'tn' => [ + 'isoName' => 'Tswana', + 'nativeName' => 'Setswana', + ], + 'to' => [ + 'isoName' => 'Tongan (Tonga Islands)', + 'nativeName' => 'Faka Tonga', + ], + 'tr' => [ + 'isoName' => 'Turkish', + 'nativeName' => 'Türkçe', + ], + 'ts' => [ + 'isoName' => 'Tsonga', + 'nativeName' => 'Xitsonga', + ], + 'tt' => [ + 'isoName' => 'Tatar', + 'nativeName' => 'татар теле, tatar tele', + ], + 'tw' => [ + 'isoName' => 'Twi', + 'nativeName' => 'Twi', + ], + 'ty' => [ + 'isoName' => 'Tahitian', + 'nativeName' => 'Reo Tahiti', + ], + 'ug' => [ + 'isoName' => 'Uighur, Uyghur', + 'nativeName' => 'Uyƣurqə, ‫ئۇيغۇرچ', + ], + 'uk' => [ + 'isoName' => 'Ukrainian', + 'nativeName' => 'Українська', + ], + 'ur' => [ + 'isoName' => 'Urdu', + 'nativeName' => 'اردو', + ], + 'uz' => [ + 'isoName' => 'Uzbek', + 'nativeName' => 'Oʻzbek, Ўзбек, أۇزبېك‎', + ], + 've' => [ + 'isoName' => 'Venda', + 'nativeName' => 'Tshivenḓa', + ], + 'vi' => [ + 'isoName' => 'Vietnamese', + 'nativeName' => 'Tiếng Việt', + ], + 'vo' => [ + 'isoName' => 'Volapük', + 'nativeName' => 'Volapük', + ], + 'wa' => [ + 'isoName' => 'Walloon', + 'nativeName' => 'Walon', + ], + 'cy' => [ + 'isoName' => 'Welsh', + 'nativeName' => 'Cymraeg', + ], + 'wo' => [ + 'isoName' => 'Wolof', + 'nativeName' => 'Wollof', + ], + 'fy' => [ + 'isoName' => 'Western Frisian', + 'nativeName' => 'Frysk', + ], + 'xh' => [ + 'isoName' => 'Xhosa', + 'nativeName' => 'isiXhosa', + ], + 'yi' => [ + 'isoName' => 'Yiddish', + 'nativeName' => 'ייִדיש', + ], + 'yo' => [ + 'isoName' => 'Yoruba', + 'nativeName' => 'Yorùbá', + ], + 'za' => [ + 'isoName' => 'Zhuang, Chuang', + 'nativeName' => 'Saɯ cueŋƅ, Saw cuengh', + ], + 'zu' => [ + 'isoName' => 'Zulu', + 'nativeName' => 'isiZulu', + ], + /* + * Add ISO 639-3 languages available in Carbon + */ + 'agq' => [ + 'isoName' => 'Aghem', + 'nativeName' => 'Aghem', + ], + 'agr' => [ + 'isoName' => 'Aguaruna', + 'nativeName' => 'Aguaruna', + ], + 'anp' => [ + 'isoName' => 'Angika', + 'nativeName' => 'Angika', + ], + 'asa' => [ + 'isoName' => 'Asu', + 'nativeName' => 'Asu', + ], + 'ast' => [ + 'isoName' => 'Asturian', + 'nativeName' => 'Asturian', + ], + 'ayc' => [ + 'isoName' => 'Southern Aymara', + 'nativeName' => 'Southern Aymara', + ], + 'bas' => [ + 'isoName' => 'Basaa', + 'nativeName' => 'Basaa', + ], + 'bem' => [ + 'isoName' => 'Bemba', + 'nativeName' => 'Bemba', + ], + 'bez' => [ + 'isoName' => 'Bena', + 'nativeName' => 'Bena', + ], + 'bhb' => [ + 'isoName' => 'Bhili', + 'nativeName' => 'Bhili', + ], + 'bho' => [ + 'isoName' => 'Bhojpuri', + 'nativeName' => 'Bhojpuri', + ], + 'brx' => [ + 'isoName' => 'Bodo', + 'nativeName' => 'Bodo', + ], + 'byn' => [ + 'isoName' => 'Bilin', + 'nativeName' => 'Bilin', + ], + 'ccp' => [ + 'isoName' => 'Chakma', + 'nativeName' => 'Chakma', + ], + 'cgg' => [ + 'isoName' => 'Chiga', + 'nativeName' => 'Chiga', + ], + 'chr' => [ + 'isoName' => 'Cherokee', + 'nativeName' => 'Cherokee', + ], + 'cmn' => [ + 'isoName' => 'Chinese', + 'nativeName' => 'Chinese', + ], + 'crh' => [ + 'isoName' => 'Crimean Turkish', + 'nativeName' => 'Crimean Turkish', + ], + 'csb' => [ + 'isoName' => 'Kashubian', + 'nativeName' => 'Kashubian', + ], + 'dav' => [ + 'isoName' => 'Taita', + 'nativeName' => 'Taita', + ], + 'dje' => [ + 'isoName' => 'Zarma', + 'nativeName' => 'Zarma', + ], + 'doi' => [ + 'isoName' => 'Dogri (macrolanguage)', + 'nativeName' => 'Dogri (macrolanguage)', + ], + 'dsb' => [ + 'isoName' => 'Lower Sorbian', + 'nativeName' => 'Lower Sorbian', + ], + 'dua' => [ + 'isoName' => 'Duala', + 'nativeName' => 'Duala', + ], + 'dyo' => [ + 'isoName' => 'Jola-Fonyi', + 'nativeName' => 'Jola-Fonyi', + ], + 'ebu' => [ + 'isoName' => 'Embu', + 'nativeName' => 'Embu', + ], + 'ewo' => [ + 'isoName' => 'Ewondo', + 'nativeName' => 'Ewondo', + ], + 'fil' => [ + 'isoName' => 'Filipino', + 'nativeName' => 'Filipino', + ], + 'fur' => [ + 'isoName' => 'Friulian', + 'nativeName' => 'Friulian', + ], + 'gez' => [ + 'isoName' => 'Geez', + 'nativeName' => 'Geez', + ], + 'gom' => [ + 'isoName' => 'Konkani, Goan', + 'nativeName' => 'ಕೊಂಕಣಿ', + ], + 'gsw' => [ + 'isoName' => 'Swiss German', + 'nativeName' => 'Swiss German', + ], + 'guz' => [ + 'isoName' => 'Gusii', + 'nativeName' => 'Gusii', + ], + 'hak' => [ + 'isoName' => 'Hakka Chinese', + 'nativeName' => 'Hakka Chinese', + ], + 'haw' => [ + 'isoName' => 'Hawaiian', + 'nativeName' => 'Hawaiian', + ], + 'hif' => [ + 'isoName' => 'Fiji Hindi', + 'nativeName' => 'Fiji Hindi', + ], + 'hne' => [ + 'isoName' => 'Chhattisgarhi', + 'nativeName' => 'Chhattisgarhi', + ], + 'hsb' => [ + 'isoName' => 'Upper Sorbian', + 'nativeName' => 'Upper Sorbian', + ], + 'jgo' => [ + 'isoName' => 'Ngomba', + 'nativeName' => 'Ngomba', + ], + 'jmc' => [ + 'isoName' => 'Machame', + 'nativeName' => 'Machame', + ], + 'kab' => [ + 'isoName' => 'Kabyle', + 'nativeName' => 'Kabyle', + ], + 'kam' => [ + 'isoName' => 'Kamba', + 'nativeName' => 'Kamba', + ], + 'kde' => [ + 'isoName' => 'Makonde', + 'nativeName' => 'Makonde', + ], + 'kea' => [ + 'isoName' => 'Kabuverdianu', + 'nativeName' => 'Kabuverdianu', + ], + 'khq' => [ + 'isoName' => 'Koyra Chiini', + 'nativeName' => 'Koyra Chiini', + ], + 'kkj' => [ + 'isoName' => 'Kako', + 'nativeName' => 'Kako', + ], + 'kln' => [ + 'isoName' => 'Kalenjin', + 'nativeName' => 'Kalenjin', + ], + 'kok' => [ + 'isoName' => 'Konkani', + 'nativeName' => 'Konkani', + ], + 'ksb' => [ + 'isoName' => 'Shambala', + 'nativeName' => 'Shambala', + ], + 'ksf' => [ + 'isoName' => 'Bafia', + 'nativeName' => 'Bafia', + ], + 'ksh' => [ + 'isoName' => 'Colognian', + 'nativeName' => 'Colognian', + ], + 'lag' => [ + 'isoName' => 'Langi', + 'nativeName' => 'Langi', + ], + 'lij' => [ + 'isoName' => 'Ligurian', + 'nativeName' => 'Ligurian', + ], + 'lkt' => [ + 'isoName' => 'Lakota', + 'nativeName' => 'Lakota', + ], + 'lrc' => [ + 'isoName' => 'Northern Luri', + 'nativeName' => 'Northern Luri', + ], + 'luo' => [ + 'isoName' => 'Luo', + 'nativeName' => 'Luo', + ], + 'luy' => [ + 'isoName' => 'Luyia', + 'nativeName' => 'Luyia', + ], + 'lzh' => [ + 'isoName' => 'Literary Chinese', + 'nativeName' => 'Literary Chinese', + ], + 'mag' => [ + 'isoName' => 'Magahi', + 'nativeName' => 'Magahi', + ], + 'mai' => [ + 'isoName' => 'Maithili', + 'nativeName' => 'Maithili', + ], + 'mas' => [ + 'isoName' => 'Masai', + 'nativeName' => 'Masai', + ], + 'mer' => [ + 'isoName' => 'Meru', + 'nativeName' => 'Meru', + ], + 'mfe' => [ + 'isoName' => 'Morisyen', + 'nativeName' => 'Morisyen', + ], + 'mgh' => [ + 'isoName' => 'Makhuwa-Meetto', + 'nativeName' => 'Makhuwa-Meetto', + ], + 'mgo' => [ + 'isoName' => 'Metaʼ', + 'nativeName' => 'Metaʼ', + ], + 'mhr' => [ + 'isoName' => 'Eastern Mari', + 'nativeName' => 'Eastern Mari', + ], + 'miq' => [ + 'isoName' => 'Mískito', + 'nativeName' => 'Mískito', + ], + 'mjw' => [ + 'isoName' => 'Karbi', + 'nativeName' => 'Karbi', + ], + 'mni' => [ + 'isoName' => 'Manipuri', + 'nativeName' => 'Manipuri', + ], + 'mua' => [ + 'isoName' => 'Mundang', + 'nativeName' => 'Mundang', + ], + 'mzn' => [ + 'isoName' => 'Mazanderani', + 'nativeName' => 'Mazanderani', + ], + 'nan' => [ + 'isoName' => 'Min Nan Chinese', + 'nativeName' => 'Min Nan Chinese', + ], + 'naq' => [ + 'isoName' => 'Nama', + 'nativeName' => 'Nama', + ], + 'nds' => [ + 'isoName' => 'Low German', + 'nativeName' => 'Low German', + ], + 'nhn' => [ + 'isoName' => 'Central Nahuatl', + 'nativeName' => 'Central Nahuatl', + ], + 'niu' => [ + 'isoName' => 'Niuean', + 'nativeName' => 'Niuean', + ], + 'nmg' => [ + 'isoName' => 'Kwasio', + 'nativeName' => 'Kwasio', + ], + 'nnh' => [ + 'isoName' => 'Ngiemboon', + 'nativeName' => 'Ngiemboon', + ], + 'nso' => [ + 'isoName' => 'Northern Sotho', + 'nativeName' => 'Northern Sotho', + ], + 'nus' => [ + 'isoName' => 'Nuer', + 'nativeName' => 'Nuer', + ], + 'nyn' => [ + 'isoName' => 'Nyankole', + 'nativeName' => 'Nyankole', + ], + 'pap' => [ + 'isoName' => 'Papiamento', + 'nativeName' => 'Papiamento', + ], + 'prg' => [ + 'isoName' => 'Prussian', + 'nativeName' => 'Prussian', + ], + 'quz' => [ + 'isoName' => 'Cusco Quechua', + 'nativeName' => 'Cusco Quechua', + ], + 'raj' => [ + 'isoName' => 'Rajasthani', + 'nativeName' => 'Rajasthani', + ], + 'rof' => [ + 'isoName' => 'Rombo', + 'nativeName' => 'Rombo', + ], + 'rwk' => [ + 'isoName' => 'Rwa', + 'nativeName' => 'Rwa', + ], + 'sah' => [ + 'isoName' => 'Sakha', + 'nativeName' => 'Sakha', + ], + 'saq' => [ + 'isoName' => 'Samburu', + 'nativeName' => 'Samburu', + ], + 'sat' => [ + 'isoName' => 'Santali', + 'nativeName' => 'Santali', + ], + 'sbp' => [ + 'isoName' => 'Sangu', + 'nativeName' => 'Sangu', + ], + 'scr' => [ + 'isoName' => 'Serbo Croatian', + 'nativeName' => 'Serbo Croatian', + ], + 'seh' => [ + 'isoName' => 'Sena', + 'nativeName' => 'Sena', + ], + 'ses' => [ + 'isoName' => 'Koyraboro Senni', + 'nativeName' => 'Koyraboro Senni', + ], + 'sgs' => [ + 'isoName' => 'Samogitian', + 'nativeName' => 'Samogitian', + ], + 'shi' => [ + 'isoName' => 'Tachelhit', + 'nativeName' => 'Tachelhit', + ], + 'shn' => [ + 'isoName' => 'Shan', + 'nativeName' => 'Shan', + ], + 'shs' => [ + 'isoName' => 'Shuswap', + 'nativeName' => 'Shuswap', + ], + 'sid' => [ + 'isoName' => 'Sidamo', + 'nativeName' => 'Sidamo', + ], + 'smn' => [ + 'isoName' => 'Inari Sami', + 'nativeName' => 'Inari Sami', + ], + 'szl' => [ + 'isoName' => 'Silesian', + 'nativeName' => 'Silesian', + ], + 'tcy' => [ + 'isoName' => 'Tulu', + 'nativeName' => 'Tulu', + ], + 'teo' => [ + 'isoName' => 'Teso', + 'nativeName' => 'Teso', + ], + 'tet' => [ + 'isoName' => 'Tetum', + 'nativeName' => 'Tetum', + ], + 'the' => [ + 'isoName' => 'Chitwania Tharu', + 'nativeName' => 'Chitwania Tharu', + ], + 'tig' => [ + 'isoName' => 'Tigre', + 'nativeName' => 'Tigre', + ], + 'tlh' => [ + 'isoName' => 'Klingon', + 'nativeName' => 'tlhIngan Hol', + ], + 'tpi' => [ + 'isoName' => 'Tok Pisin', + 'nativeName' => 'Tok Pisin', + ], + 'twq' => [ + 'isoName' => 'Tasawaq', + 'nativeName' => 'Tasawaq', + ], + 'tzl' => [ + 'isoName' => 'Talossan', + 'nativeName' => 'Talossan', + ], + 'tzm' => [ + 'isoName' => 'Tamazight, Central Atlas', + 'nativeName' => 'ⵜⵎⴰⵣⵉⵖⵜ', + ], + 'unm' => [ + 'isoName' => 'Unami', + 'nativeName' => 'Unami', + ], + 'vai' => [ + 'isoName' => 'Vai', + 'nativeName' => 'Vai', + ], + 'vun' => [ + 'isoName' => 'Vunjo', + 'nativeName' => 'Vunjo', + ], + 'wae' => [ + 'isoName' => 'Walser', + 'nativeName' => 'Walser', + ], + 'wal' => [ + 'isoName' => 'Wolaytta', + 'nativeName' => 'Wolaytta', + ], + 'xog' => [ + 'isoName' => 'Soga', + 'nativeName' => 'Soga', + ], + 'yav' => [ + 'isoName' => 'Yangben', + 'nativeName' => 'Yangben', + ], + 'yue' => [ + 'isoName' => 'Cantonese', + 'nativeName' => 'Cantonese', + ], + 'yuw' => [ + 'isoName' => 'Yau (Morobe Province)', + 'nativeName' => 'Yau (Morobe Province)', + ], + 'zgh' => [ + 'isoName' => 'Standard Moroccan Tamazight', + 'nativeName' => 'Standard Moroccan Tamazight', + ], +]; diff --git a/vendor/nesbot/carbon/src/Carbon/List/regions.php b/vendor/nesbot/carbon/src/Carbon/List/regions.php new file mode 100644 index 0000000..8ab8a9e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/List/regions.php @@ -0,0 +1,265 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * ISO 3166-2 + */ +return [ + 'AD' => 'Andorra', + 'AE' => 'United Arab Emirates', + 'AF' => 'Afghanistan', + 'AG' => 'Antigua and Barbuda', + 'AI' => 'Anguilla', + 'AL' => 'Albania', + 'AM' => 'Armenia', + 'AO' => 'Angola', + 'AQ' => 'Antarctica', + 'AR' => 'Argentina', + 'AS' => 'American Samoa', + 'AT' => 'Austria', + 'AU' => 'Australia', + 'AW' => 'Aruba', + 'AX' => 'Åland Islands', + 'AZ' => 'Azerbaijan', + 'BA' => 'Bosnia and Herzegovina', + 'BB' => 'Barbados', + 'BD' => 'Bangladesh', + 'BE' => 'Belgium', + 'BF' => 'Burkina Faso', + 'BG' => 'Bulgaria', + 'BH' => 'Bahrain', + 'BI' => 'Burundi', + 'BJ' => 'Benin', + 'BL' => 'Saint Barthélemy', + 'BM' => 'Bermuda', + 'BN' => 'Brunei Darussalam', + 'BO' => 'Bolivia (Plurinational State of)', + 'BQ' => 'Bonaire, Sint Eustatius and Saba', + 'BR' => 'Brazil', + 'BS' => 'Bahamas', + 'BT' => 'Bhutan', + 'BV' => 'Bouvet Island', + 'BW' => 'Botswana', + 'BY' => 'Belarus', + 'BZ' => 'Belize', + 'CA' => 'Canada', + 'CC' => 'Cocos (Keeling) Islands', + 'CD' => 'Congo, Democratic Republic of the', + 'CF' => 'Central African Republic', + 'CG' => 'Congo', + 'CH' => 'Switzerland', + 'CI' => 'Côte d\'Ivoire', + 'CK' => 'Cook Islands', + 'CL' => 'Chile', + 'CM' => 'Cameroon', + 'CN' => 'China', + 'CO' => 'Colombia', + 'CR' => 'Costa Rica', + 'CU' => 'Cuba', + 'CV' => 'Cabo Verde', + 'CW' => 'Curaçao', + 'CX' => 'Christmas Island', + 'CY' => 'Cyprus', + 'CZ' => 'Czechia', + 'DE' => 'Germany', + 'DJ' => 'Djibouti', + 'DK' => 'Denmark', + 'DM' => 'Dominica', + 'DO' => 'Dominican Republic', + 'DZ' => 'Algeria', + 'EC' => 'Ecuador', + 'EE' => 'Estonia', + 'EG' => 'Egypt', + 'EH' => 'Western Sahara', + 'ER' => 'Eritrea', + 'ES' => 'Spain', + 'ET' => 'Ethiopia', + 'FI' => 'Finland', + 'FJ' => 'Fiji', + 'FK' => 'Falkland Islands (Malvinas)', + 'FM' => 'Micronesia (Federated States of)', + 'FO' => 'Faroe Islands', + 'FR' => 'France', + 'GA' => 'Gabon', + 'GB' => 'United Kingdom of Great Britain and Northern Ireland', + 'GD' => 'Grenada', + 'GE' => 'Georgia', + 'GF' => 'French Guiana', + 'GG' => 'Guernsey', + 'GH' => 'Ghana', + 'GI' => 'Gibraltar', + 'GL' => 'Greenland', + 'GM' => 'Gambia', + 'GN' => 'Guinea', + 'GP' => 'Guadeloupe', + 'GQ' => 'Equatorial Guinea', + 'GR' => 'Greece', + 'GS' => 'South Georgia and the South Sandwich Islands', + 'GT' => 'Guatemala', + 'GU' => 'Guam', + 'GW' => 'Guinea-Bissau', + 'GY' => 'Guyana', + 'HK' => 'Hong Kong', + 'HM' => 'Heard Island and McDonald Islands', + 'HN' => 'Honduras', + 'HR' => 'Croatia', + 'HT' => 'Haiti', + 'HU' => 'Hungary', + 'ID' => 'Indonesia', + 'IE' => 'Ireland', + 'IL' => 'Israel', + 'IM' => 'Isle of Man', + 'IN' => 'India', + 'IO' => 'British Indian Ocean Territory', + 'IQ' => 'Iraq', + 'IR' => 'Iran (Islamic Republic of)', + 'IS' => 'Iceland', + 'IT' => 'Italy', + 'JE' => 'Jersey', + 'JM' => 'Jamaica', + 'JO' => 'Jordan', + 'JP' => 'Japan', + 'KE' => 'Kenya', + 'KG' => 'Kyrgyzstan', + 'KH' => 'Cambodia', + 'KI' => 'Kiribati', + 'KM' => 'Comoros', + 'KN' => 'Saint Kitts and Nevis', + 'KP' => 'Korea (Democratic People\'s Republic of)', + 'KR' => 'Korea, Republic of', + 'KW' => 'Kuwait', + 'KY' => 'Cayman Islands', + 'KZ' => 'Kazakhstan', + 'LA' => 'Lao People\'s Democratic Republic', + 'LB' => 'Lebanon', + 'LC' => 'Saint Lucia', + 'LI' => 'Liechtenstein', + 'LK' => 'Sri Lanka', + 'LR' => 'Liberia', + 'LS' => 'Lesotho', + 'LT' => 'Lithuania', + 'LU' => 'Luxembourg', + 'LV' => 'Latvia', + 'LY' => 'Libya', + 'MA' => 'Morocco', + 'MC' => 'Monaco', + 'MD' => 'Moldova, Republic of', + 'ME' => 'Montenegro', + 'MF' => 'Saint Martin (French part)', + 'MG' => 'Madagascar', + 'MH' => 'Marshall Islands', + 'MK' => 'Macedonia, the former Yugoslav Republic of', + 'ML' => 'Mali', + 'MM' => 'Myanmar', + 'MN' => 'Mongolia', + 'MO' => 'Macao', + 'MP' => 'Northern Mariana Islands', + 'MQ' => 'Martinique', + 'MR' => 'Mauritania', + 'MS' => 'Montserrat', + 'MT' => 'Malta', + 'MU' => 'Mauritius', + 'MV' => 'Maldives', + 'MW' => 'Malawi', + 'MX' => 'Mexico', + 'MY' => 'Malaysia', + 'MZ' => 'Mozambique', + 'NA' => 'Namibia', + 'NC' => 'New Caledonia', + 'NE' => 'Niger', + 'NF' => 'Norfolk Island', + 'NG' => 'Nigeria', + 'NI' => 'Nicaragua', + 'NL' => 'Netherlands', + 'NO' => 'Norway', + 'NP' => 'Nepal', + 'NR' => 'Nauru', + 'NU' => 'Niue', + 'NZ' => 'New Zealand', + 'OM' => 'Oman', + 'PA' => 'Panama', + 'PE' => 'Peru', + 'PF' => 'French Polynesia', + 'PG' => 'Papua New Guinea', + 'PH' => 'Philippines', + 'PK' => 'Pakistan', + 'PL' => 'Poland', + 'PM' => 'Saint Pierre and Miquelon', + 'PN' => 'Pitcairn', + 'PR' => 'Puerto Rico', + 'PS' => 'Palestine, State of', + 'PT' => 'Portugal', + 'PW' => 'Palau', + 'PY' => 'Paraguay', + 'QA' => 'Qatar', + 'RE' => 'Réunion', + 'RO' => 'Romania', + 'RS' => 'Serbia', + 'RU' => 'Russian Federation', + 'RW' => 'Rwanda', + 'SA' => 'Saudi Arabia', + 'SB' => 'Solomon Islands', + 'SC' => 'Seychelles', + 'SD' => 'Sudan', + 'SE' => 'Sweden', + 'SG' => 'Singapore', + 'SH' => 'Saint Helena, Ascension and Tristan da Cunha', + 'SI' => 'Slovenia', + 'SJ' => 'Svalbard and Jan Mayen', + 'SK' => 'Slovakia', + 'SL' => 'Sierra Leone', + 'SM' => 'San Marino', + 'SN' => 'Senegal', + 'SO' => 'Somalia', + 'SR' => 'Suriname', + 'SS' => 'South Sudan', + 'ST' => 'Sao Tome and Principe', + 'SV' => 'El Salvador', + 'SX' => 'Sint Maarten (Dutch part)', + 'SY' => 'Syrian Arab Republic', + 'SZ' => 'Eswatini', + 'TC' => 'Turks and Caicos Islands', + 'TD' => 'Chad', + 'TF' => 'French Southern Territories', + 'TG' => 'Togo', + 'TH' => 'Thailand', + 'TJ' => 'Tajikistan', + 'TK' => 'Tokelau', + 'TL' => 'Timor-Leste', + 'TM' => 'Turkmenistan', + 'TN' => 'Tunisia', + 'TO' => 'Tonga', + 'TR' => 'Turkey', + 'TT' => 'Trinidad and Tobago', + 'TV' => 'Tuvalu', + 'TW' => 'Taiwan, Province of China', + 'TZ' => 'Tanzania, United Republic of', + 'UA' => 'Ukraine', + 'UG' => 'Uganda', + 'UM' => 'United States Minor Outlying Islands', + 'US' => 'United States of America', + 'UY' => 'Uruguay', + 'UZ' => 'Uzbekistan', + 'VA' => 'Holy See', + 'VC' => 'Saint Vincent and the Grenadines', + 'VE' => 'Venezuela (Bolivarian Republic of)', + 'VG' => 'Virgin Islands (British)', + 'VI' => 'Virgin Islands (U.S.)', + 'VN' => 'Viet Nam', + 'VU' => 'Vanuatu', + 'WF' => 'Wallis and Futuna', + 'WS' => 'Samoa', + 'YE' => 'Yemen', + 'YT' => 'Mayotte', + 'ZA' => 'South Africa', + 'ZM' => 'Zambia', + 'ZW' => 'Zimbabwe', +]; diff --git a/vendor/nesbot/carbon/src/Carbon/MessageFormatter/MessageFormatterMapper.php b/vendor/nesbot/carbon/src/Carbon/MessageFormatter/MessageFormatterMapper.php new file mode 100644 index 0000000..c054808 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/MessageFormatter/MessageFormatterMapper.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\MessageFormatter; + +use ReflectionMethod; +use Symfony\Component\Translation\Formatter\MessageFormatter; +use Symfony\Component\Translation\Formatter\MessageFormatterInterface; + +// @codeCoverageIgnoreStart +$transMethod = new ReflectionMethod(MessageFormatterInterface::class, 'format'); + +require $transMethod->getParameters()[0]->hasType() + ? __DIR__.'/../../../lazy/Carbon/MessageFormatter/MessageFormatterMapperStrongType.php' + : __DIR__.'/../../../lazy/Carbon/MessageFormatter/MessageFormatterMapperWeakType.php'; +// @codeCoverageIgnoreEnd + +final class MessageFormatterMapper extends LazyMessageFormatter +{ + /** + * Wrapped formatter. + * + * @var MessageFormatterInterface + */ + protected $formatter; + + public function __construct(?MessageFormatterInterface $formatter = null) + { + $this->formatter = $formatter ?? new MessageFormatter(); + } + + protected function transformLocale(?string $locale): ?string + { + return $locale ? preg_replace('/[_@][A-Za-z][a-z]{2,}/', '', $locale) : $locale; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/AbstractMacro.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/AbstractMacro.php new file mode 100644 index 0000000..fde67b3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/PHPStan/AbstractMacro.php @@ -0,0 +1,286 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\PHPStan; + +use Closure; +use InvalidArgumentException; +use PHPStan\BetterReflection\Reflection\Adapter\ReflectionParameter as AdapterReflectionParameter; +use PHPStan\BetterReflection\Reflection\Adapter\ReflectionType as AdapterReflectionType; +use PHPStan\BetterReflection\Reflection\ReflectionClass as BetterReflectionClass; +use PHPStan\BetterReflection\Reflection\ReflectionFunction as BetterReflectionFunction; +use PHPStan\BetterReflection\Reflection\ReflectionParameter as BetterReflectionParameter; +use PHPStan\Reflection\Php\BuiltinMethodReflection; +use PHPStan\TrinaryLogic; +use ReflectionClass; +use ReflectionFunction; +use ReflectionMethod; +use ReflectionParameter; +use ReflectionType; +use stdClass; +use Throwable; + +abstract class AbstractMacro implements BuiltinMethodReflection +{ + /** + * The reflection function/method. + * + * @var ReflectionFunction|ReflectionMethod + */ + protected $reflectionFunction; + + /** + * The class name. + * + * @var class-string + */ + private $className; + + /** + * The method name. + * + * @var string + */ + private $methodName; + + /** + * The parameters. + * + * @var ReflectionParameter[] + */ + private $parameters; + + /** + * The is static. + * + * @var bool + */ + private $static = false; + + /** + * Macro constructor. + * + * @param class-string $className + * @param string $methodName + * @param callable $macro + */ + public function __construct(string $className, string $methodName, $macro) + { + $this->className = $className; + $this->methodName = $methodName; + $rawReflectionFunction = \is_array($macro) + ? new ReflectionMethod($macro[0], $macro[1]) + : new ReflectionFunction($macro); + $this->reflectionFunction = self::hasModernParser() + ? $this->getReflectionFunction($macro) + : $rawReflectionFunction; // @codeCoverageIgnore + $this->parameters = array_map( + function ($parameter) { + if ($parameter instanceof BetterReflectionParameter) { + return new AdapterReflectionParameter($parameter); + } + + return $parameter; // @codeCoverageIgnore + }, + $this->reflectionFunction->getParameters() + ); + + if ($rawReflectionFunction->isClosure()) { + try { + $closure = $rawReflectionFunction->getClosure(); + $boundClosure = Closure::bind($closure, new stdClass()); + $this->static = (!$boundClosure || (new ReflectionFunction($boundClosure))->getClosureThis() === null); + } catch (Throwable $e) { + $this->static = true; + } + } + } + + private function getReflectionFunction($spec) + { + if (\is_array($spec) && \count($spec) === 2 && \is_string($spec[1])) { + \assert($spec[1] !== ''); + + if (\is_object($spec[0])) { + return BetterReflectionClass::createFromInstance($spec[0]) + ->getMethod($spec[1]); + } + + return BetterReflectionClass::createFromName($spec[0]) + ->getMethod($spec[1]); + } + + if (\is_string($spec)) { + return BetterReflectionFunction::createFromName($spec); + } + + if ($spec instanceof Closure) { + return BetterReflectionFunction::createFromClosure($spec); + } + + throw new InvalidArgumentException('Could not create reflection from the spec given'); // @codeCoverageIgnore + } + + /** + * {@inheritdoc} + */ + public function getDeclaringClass(): ReflectionClass + { + return new ReflectionClass($this->className); + } + + /** + * {@inheritdoc} + */ + public function isPrivate(): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isPublic(): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function isFinal(): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isInternal(): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isAbstract(): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isStatic(): bool + { + return $this->static; + } + + /** + * {@inheritdoc} + */ + public function getDocComment(): ?string + { + return $this->reflectionFunction->getDocComment() ?: null; + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return $this->methodName; + } + + /** + * {@inheritdoc} + */ + public function getParameters(): array + { + return $this->parameters; + } + + /** + * {@inheritdoc} + */ + public function getReturnType(): ?ReflectionType + { + $type = $this->reflectionFunction->getReturnType(); + + if ($type instanceof ReflectionType) { + return $type; // @codeCoverageIgnore + } + + return self::adaptType($type); + } + + /** + * {@inheritdoc} + */ + public function isDeprecated(): TrinaryLogic + { + return TrinaryLogic::createFromBoolean( + $this->reflectionFunction->isDeprecated() || + preg_match('/@deprecated/i', $this->getDocComment() ?: '') + ); + } + + /** + * {@inheritdoc} + */ + public function isVariadic(): bool + { + return $this->reflectionFunction->isVariadic(); + } + + /** + * {@inheritdoc} + */ + public function getPrototype(): BuiltinMethodReflection + { + return $this; + } + + public function getTentativeReturnType(): ?ReflectionType + { + return null; + } + + public function returnsByReference(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + private static function adaptType($type) + { + $method = method_exists(AdapterReflectionType::class, 'fromTypeOrNull') + ? 'fromTypeOrNull' + : 'fromReturnTypeOrNull'; // @codeCoverageIgnore + + return AdapterReflectionType::$method($type); + } + + private static function hasModernParser(): bool + { + static $modernParser = null; + + if ($modernParser !== null) { + return $modernParser; + } + + $modernParser = method_exists(AdapterReflectionType::class, 'fromTypeOrNull'); + + return $modernParser; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/Macro.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/Macro.php new file mode 100644 index 0000000..de3e51f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/PHPStan/Macro.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\PHPStan; + +use PHPStan\BetterReflection\Reflection\Adapter; +use PHPStan\Reflection\Php\BuiltinMethodReflection; +use ReflectionMethod; + +$method = new ReflectionMethod(BuiltinMethodReflection::class, 'getReflection'); + +require $method->hasReturnType() && $method->getReturnType()->getName() === Adapter\ReflectionMethod::class + ? __DIR__.'/../../../lazy/Carbon/PHPStan/AbstractMacroStatic.php' + : __DIR__.'/../../../lazy/Carbon/PHPStan/AbstractMacroBuiltin.php'; + +$method = new ReflectionMethod(BuiltinMethodReflection::class, 'getFileName'); + +require $method->hasReturnType() + ? __DIR__.'/../../../lazy/Carbon/PHPStan/MacroStrongType.php' + : __DIR__.'/../../../lazy/Carbon/PHPStan/MacroWeakType.php'; + +final class Macro extends LazyMacro +{ +} diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroExtension.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroExtension.php new file mode 100644 index 0000000..2cd6fce --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroExtension.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\PHPStan; + +use PHPStan\Reflection\Assertions; +use PHPStan\Reflection\ClassReflection; +use PHPStan\Reflection\MethodReflection; +use PHPStan\Reflection\MethodsClassReflectionExtension; +use PHPStan\Reflection\Php\PhpMethodReflectionFactory; +use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Type\TypehintHelper; + +/** + * Class MacroExtension. + * + * @codeCoverageIgnore Pure PHPStan wrapper. + */ +final class MacroExtension implements MethodsClassReflectionExtension +{ + /** + * @var PhpMethodReflectionFactory + */ + protected $methodReflectionFactory; + + /** + * @var MacroScanner + */ + protected $scanner; + + /** + * Extension constructor. + * + * @param PhpMethodReflectionFactory $methodReflectionFactory + * @param ReflectionProvider $reflectionProvider + */ + public function __construct( + PhpMethodReflectionFactory $methodReflectionFactory, + ReflectionProvider $reflectionProvider + ) { + $this->scanner = new MacroScanner($reflectionProvider); + $this->methodReflectionFactory = $methodReflectionFactory; + } + + /** + * {@inheritdoc} + */ + public function hasMethod(ClassReflection $classReflection, string $methodName): bool + { + return $this->scanner->hasMethod($classReflection->getName(), $methodName); + } + + /** + * {@inheritdoc} + */ + public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection + { + $builtinMacro = $this->scanner->getMethod($classReflection->getName(), $methodName); + $supportAssertions = class_exists(Assertions::class); + + return $this->methodReflectionFactory->create( + $classReflection, + null, + $builtinMacro, + $classReflection->getActiveTemplateTypeMap(), + [], + TypehintHelper::decideTypeFromReflection($builtinMacro->getReturnType()), + null, + null, + $builtinMacro->isDeprecated()->yes(), + $builtinMacro->isInternal(), + $builtinMacro->isFinal(), + $supportAssertions ? null : $builtinMacro->getDocComment(), + $supportAssertions ? Assertions::createEmpty() : null, + null, + $builtinMacro->getDocComment(), + [] + ); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroScanner.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroScanner.php new file mode 100644 index 0000000..eb8957d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroScanner.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\PHPStan; + +use Carbon\CarbonInterface; +use PHPStan\Reflection\ReflectionProvider; +use ReflectionClass; +use ReflectionException; + +final class MacroScanner +{ + /** + * @var \PHPStan\Reflection\ReflectionProvider + */ + private $reflectionProvider; + + /** + * MacroScanner constructor. + * + * @param \PHPStan\Reflection\ReflectionProvider $reflectionProvider + */ + public function __construct(ReflectionProvider $reflectionProvider) + { + $this->reflectionProvider = $reflectionProvider; + } + + /** + * Return true if the given pair class-method is a Carbon macro. + * + * @param class-string $className + * @param string $methodName + * + * @return bool + */ + public function hasMethod(string $className, string $methodName): bool + { + $classReflection = $this->reflectionProvider->getClass($className); + + if ( + $classReflection->getName() !== CarbonInterface::class && + !$classReflection->isSubclassOf(CarbonInterface::class) + ) { + return false; + } + + return \is_callable([$className, 'hasMacro']) && + $className::hasMacro($methodName); + } + + /** + * Return the Macro for a given pair class-method. + * + * @param class-string $className + * @param string $methodName + * + * @throws ReflectionException + * + * @return Macro + */ + public function getMethod(string $className, string $methodName): Macro + { + $reflectionClass = new ReflectionClass($className); + $property = $reflectionClass->getProperty('globalMacros'); + + $property->setAccessible(true); + $macro = $property->getValue()[$methodName]; + + return new Macro( + $className, + $methodName, + $macro + ); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Boundaries.php b/vendor/nesbot/carbon/src/Carbon/Traits/Boundaries.php new file mode 100644 index 0000000..71bbb72 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Boundaries.php @@ -0,0 +1,443 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\Exceptions\UnknownUnitException; + +/** + * Trait Boundaries. + * + * startOf, endOf and derived method for each unit. + * + * Depends on the following properties: + * + * @property int $year + * @property int $month + * @property int $daysInMonth + * @property int $quarter + * + * Depends on the following methods: + * + * @method $this setTime(int $hour, int $minute, int $second = 0, int $microseconds = 0) + * @method $this setDate(int $year, int $month, int $day) + * @method $this addMonths(int $value = 1) + */ +trait Boundaries +{ + /** + * Resets the time to 00:00:00 start of day + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfDay(); + * ``` + * + * @return static + */ + public function startOfDay() + { + return $this->setTime(0, 0, 0, 0); + } + + /** + * Resets the time to 23:59:59.999999 end of day + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfDay(); + * ``` + * + * @return static + */ + public function endOfDay() + { + return $this->setTime(static::HOURS_PER_DAY - 1, static::MINUTES_PER_HOUR - 1, static::SECONDS_PER_MINUTE - 1, static::MICROSECONDS_PER_SECOND - 1); + } + + /** + * Resets the date to the first day of the month and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfMonth(); + * ``` + * + * @return static + */ + public function startOfMonth() + { + return $this->setDate($this->year, $this->month, 1)->startOfDay(); + } + + /** + * Resets the date to end of the month and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfMonth(); + * ``` + * + * @return static + */ + public function endOfMonth() + { + return $this->setDate($this->year, $this->month, $this->daysInMonth)->endOfDay(); + } + + /** + * Resets the date to the first day of the quarter and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfQuarter(); + * ``` + * + * @return static + */ + public function startOfQuarter() + { + $month = ($this->quarter - 1) * static::MONTHS_PER_QUARTER + 1; + + return $this->setDate($this->year, $month, 1)->startOfDay(); + } + + /** + * Resets the date to end of the quarter and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfQuarter(); + * ``` + * + * @return static + */ + public function endOfQuarter() + { + return $this->startOfQuarter()->addMonths(static::MONTHS_PER_QUARTER - 1)->endOfMonth(); + } + + /** + * Resets the date to the first day of the year and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfYear(); + * ``` + * + * @return static + */ + public function startOfYear() + { + return $this->setDate($this->year, 1, 1)->startOfDay(); + } + + /** + * Resets the date to end of the year and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfYear(); + * ``` + * + * @return static + */ + public function endOfYear() + { + return $this->setDate($this->year, 12, 31)->endOfDay(); + } + + /** + * Resets the date to the first day of the decade and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfDecade(); + * ``` + * + * @return static + */ + public function startOfDecade() + { + $year = $this->year - $this->year % static::YEARS_PER_DECADE; + + return $this->setDate($year, 1, 1)->startOfDay(); + } + + /** + * Resets the date to end of the decade and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfDecade(); + * ``` + * + * @return static + */ + public function endOfDecade() + { + $year = $this->year - $this->year % static::YEARS_PER_DECADE + static::YEARS_PER_DECADE - 1; + + return $this->setDate($year, 12, 31)->endOfDay(); + } + + /** + * Resets the date to the first day of the century and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfCentury(); + * ``` + * + * @return static + */ + public function startOfCentury() + { + $year = $this->year - ($this->year - 1) % static::YEARS_PER_CENTURY; + + return $this->setDate($year, 1, 1)->startOfDay(); + } + + /** + * Resets the date to end of the century and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfCentury(); + * ``` + * + * @return static + */ + public function endOfCentury() + { + $year = $this->year - 1 - ($this->year - 1) % static::YEARS_PER_CENTURY + static::YEARS_PER_CENTURY; + + return $this->setDate($year, 12, 31)->endOfDay(); + } + + /** + * Resets the date to the first day of the millennium and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfMillennium(); + * ``` + * + * @return static + */ + public function startOfMillennium() + { + $year = $this->year - ($this->year - 1) % static::YEARS_PER_MILLENNIUM; + + return $this->setDate($year, 1, 1)->startOfDay(); + } + + /** + * Resets the date to end of the millennium and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfMillennium(); + * ``` + * + * @return static + */ + public function endOfMillennium() + { + $year = $this->year - 1 - ($this->year - 1) % static::YEARS_PER_MILLENNIUM + static::YEARS_PER_MILLENNIUM; + + return $this->setDate($year, 12, 31)->endOfDay(); + } + + /** + * Resets the date to the first day of week (defined in $weekStartsAt) and the time to 00:00:00 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfWeek() . "\n"; + * echo Carbon::parse('2018-07-25 12:45:16')->locale('ar')->startOfWeek() . "\n"; + * echo Carbon::parse('2018-07-25 12:45:16')->startOfWeek(Carbon::SUNDAY) . "\n"; + * ``` + * + * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week + * + * @return static + */ + public function startOfWeek($weekStartsAt = null) + { + return $this->subDays((7 + $this->dayOfWeek - ($weekStartsAt ?? $this->firstWeekDay)) % 7)->startOfDay(); + } + + /** + * Resets the date to end of week (defined in $weekEndsAt) and time to 23:59:59.999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfWeek() . "\n"; + * echo Carbon::parse('2018-07-25 12:45:16')->locale('ar')->endOfWeek() . "\n"; + * echo Carbon::parse('2018-07-25 12:45:16')->endOfWeek(Carbon::SATURDAY) . "\n"; + * ``` + * + * @param int $weekEndsAt optional start allow you to specify the day of week to use to end the week + * + * @return static + */ + public function endOfWeek($weekEndsAt = null) + { + return $this->addDays((7 - $this->dayOfWeek + ($weekEndsAt ?? $this->lastWeekDay)) % 7)->endOfDay(); + } + + /** + * Modify to start of current hour, minutes and seconds become 0 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfHour(); + * ``` + * + * @return static + */ + public function startOfHour() + { + return $this->setTime($this->hour, 0, 0, 0); + } + + /** + * Modify to end of current hour, minutes and seconds become 59 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfHour(); + * ``` + * + * @return static + */ + public function endOfHour() + { + return $this->setTime($this->hour, static::MINUTES_PER_HOUR - 1, static::SECONDS_PER_MINUTE - 1, static::MICROSECONDS_PER_SECOND - 1); + } + + /** + * Modify to start of current minute, seconds become 0 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->startOfMinute(); + * ``` + * + * @return static + */ + public function startOfMinute() + { + return $this->setTime($this->hour, $this->minute, 0, 0); + } + + /** + * Modify to end of current minute, seconds become 59 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16')->endOfMinute(); + * ``` + * + * @return static + */ + public function endOfMinute() + { + return $this->setTime($this->hour, $this->minute, static::SECONDS_PER_MINUTE - 1, static::MICROSECONDS_PER_SECOND - 1); + } + + /** + * Modify to start of current second, microseconds become 0 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16.334455') + * ->startOfSecond() + * ->format('H:i:s.u'); + * ``` + * + * @return static + */ + public function startOfSecond() + { + return $this->setTime($this->hour, $this->minute, $this->second, 0); + } + + /** + * Modify to end of current second, microseconds become 999999 + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16.334455') + * ->endOfSecond() + * ->format('H:i:s.u'); + * ``` + * + * @return static + */ + public function endOfSecond() + { + return $this->setTime($this->hour, $this->minute, $this->second, static::MICROSECONDS_PER_SECOND - 1); + } + + /** + * Modify to start of current given unit. + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16.334455') + * ->startOf('month') + * ->endOf('week', Carbon::FRIDAY); + * ``` + * + * @param string $unit + * @param array $params + * + * @return static + */ + public function startOf($unit, ...$params) + { + $ucfUnit = ucfirst(static::singularUnit($unit)); + $method = "startOf$ucfUnit"; + if (!method_exists($this, $method)) { + throw new UnknownUnitException($unit); + } + + return $this->$method(...$params); + } + + /** + * Modify to end of current given unit. + * + * @example + * ``` + * echo Carbon::parse('2018-07-25 12:45:16.334455') + * ->startOf('month') + * ->endOf('week', Carbon::FRIDAY); + * ``` + * + * @param string $unit + * @param array $params + * + * @return static + */ + public function endOf($unit, ...$params) + { + $ucfUnit = ucfirst(static::singularUnit($unit)); + $method = "endOf$ucfUnit"; + if (!method_exists($this, $method)) { + throw new UnknownUnitException($unit); + } + + return $this->$method(...$params); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Cast.php b/vendor/nesbot/carbon/src/Carbon/Traits/Cast.php new file mode 100644 index 0000000..5f7c7c0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Cast.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\Exceptions\InvalidCastException; +use DateTimeInterface; + +/** + * Trait Cast. + * + * Utils to cast into an other class. + */ +trait Cast +{ + /** + * Cast the current instance into the given class. + * + * @param string $className The $className::instance() method will be called to cast the current object. + * + * @return DateTimeInterface + */ + public function cast(string $className) + { + if (!method_exists($className, 'instance')) { + if (is_a($className, DateTimeInterface::class, true)) { + return new $className($this->rawFormat('Y-m-d H:i:s.u'), $this->getTimezone()); + } + + throw new InvalidCastException("$className has not the instance() method needed to cast the date."); + } + + return $className::instance($this); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Comparison.php b/vendor/nesbot/carbon/src/Carbon/Traits/Comparison.php new file mode 100644 index 0000000..daee19c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Comparison.php @@ -0,0 +1,1129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use BadMethodCallException; +use Carbon\CarbonInterface; +use Carbon\Exceptions\BadComparisonUnitException; +use InvalidArgumentException; + +/** + * Trait Comparison. + * + * Comparison utils and testers. All the following methods return booleans. + * nowWithSameTz + * + * Depends on the following methods: + * + * @method static resolveCarbon($date) + * @method static copy() + * @method static nowWithSameTz() + * @method static static yesterday($timezone = null) + * @method static static tomorrow($timezone = null) + */ +trait Comparison +{ + /** @var bool */ + protected $endOfTime = false; + + /** @var bool */ + protected $startOfTime = false; + + /** + * Determines if the instance is equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->eq('2018-07-25 12:45:16'); // true + * Carbon::parse('2018-07-25 12:45:16')->eq(Carbon::parse('2018-07-25 12:45:16')); // true + * Carbon::parse('2018-07-25 12:45:16')->eq('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see equalTo() + * + * @return bool + */ + public function eq($date): bool + { + return $this->equalTo($date); + } + + /** + * Determines if the instance is equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->equalTo('2018-07-25 12:45:16'); // true + * Carbon::parse('2018-07-25 12:45:16')->equalTo(Carbon::parse('2018-07-25 12:45:16')); // true + * Carbon::parse('2018-07-25 12:45:16')->equalTo('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function equalTo($date): bool + { + $this->discourageNull($date); + $this->discourageBoolean($date); + + return $this == $this->resolveCarbon($date); + } + + /** + * Determines if the instance is not equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->ne('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->ne(Carbon::parse('2018-07-25 12:45:16')); // false + * Carbon::parse('2018-07-25 12:45:16')->ne('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see notEqualTo() + * + * @return bool + */ + public function ne($date): bool + { + return $this->notEqualTo($date); + } + + /** + * Determines if the instance is not equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->notEqualTo('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->notEqualTo(Carbon::parse('2018-07-25 12:45:16')); // false + * Carbon::parse('2018-07-25 12:45:16')->notEqualTo('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function notEqualTo($date): bool + { + return !$this->equalTo($date); + } + + /** + * Determines if the instance is greater (after) than another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:15'); // true + * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see greaterThan() + * + * @return bool + */ + public function gt($date): bool + { + return $this->greaterThan($date); + } + + /** + * Determines if the instance is greater (after) than another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:15'); // true + * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function greaterThan($date): bool + { + $this->discourageNull($date); + $this->discourageBoolean($date); + + return $this > $this->resolveCarbon($date); + } + + /** + * Determines if the instance is greater (after) than another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:15'); // true + * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see greaterThan() + * + * @return bool + */ + public function isAfter($date): bool + { + return $this->greaterThan($date); + } + + /** + * Determines if the instance is greater (after) than or equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:15'); // true + * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:16'); // true + * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see greaterThanOrEqualTo() + * + * @return bool + */ + public function gte($date): bool + { + return $this->greaterThanOrEqualTo($date); + } + + /** + * Determines if the instance is greater (after) than or equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:15'); // true + * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:16'); // true + * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:17'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function greaterThanOrEqualTo($date): bool + { + $this->discourageNull($date); + $this->discourageBoolean($date); + + return $this >= $this->resolveCarbon($date); + } + + /** + * Determines if the instance is less (before) than another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:15'); // false + * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see lessThan() + * + * @return bool + */ + public function lt($date): bool + { + return $this->lessThan($date); + } + + /** + * Determines if the instance is less (before) than another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:15'); // false + * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function lessThan($date): bool + { + $this->discourageNull($date); + $this->discourageBoolean($date); + + return $this < $this->resolveCarbon($date); + } + + /** + * Determines if the instance is less (before) than another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:15'); // false + * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:16'); // false + * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see lessThan() + * + * @return bool + */ + public function isBefore($date): bool + { + return $this->lessThan($date); + } + + /** + * Determines if the instance is less (before) or equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:15'); // false + * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:16'); // true + * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see lessThanOrEqualTo() + * + * @return bool + */ + public function lte($date): bool + { + return $this->lessThanOrEqualTo($date); + } + + /** + * Determines if the instance is less (before) or equal to another + * + * @example + * ``` + * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:15'); // false + * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:16'); // true + * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:17'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function lessThanOrEqualTo($date): bool + { + $this->discourageNull($date); + $this->discourageBoolean($date); + + return $this <= $this->resolveCarbon($date); + } + + /** + * Determines if the instance is between two others. + * + * The third argument allow you to specify if bounds are included or not (true by default) + * but for when you including/excluding bounds may produce different results in your application, + * we recommend to use the explicit methods ->betweenIncluded() or ->betweenExcluded() instead. + * + * @example + * ``` + * Carbon::parse('2018-07-25')->between('2018-07-14', '2018-08-01'); // true + * Carbon::parse('2018-07-25')->between('2018-08-01', '2018-08-20'); // false + * Carbon::parse('2018-07-25')->between('2018-07-25', '2018-08-01'); // true + * Carbon::parse('2018-07-25')->between('2018-07-25', '2018-08-01', false); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * @param bool $equal Indicates if an equal to comparison should be done + * + * @return bool + */ + public function between($date1, $date2, $equal = true): bool + { + $date1 = $this->resolveCarbon($date1); + $date2 = $this->resolveCarbon($date2); + + if ($date1->greaterThan($date2)) { + [$date1, $date2] = [$date2, $date1]; + } + + if ($equal) { + return $this >= $date1 && $this <= $date2; + } + + return $this > $date1 && $this < $date2; + } + + /** + * Determines if the instance is between two others, bounds included. + * + * @example + * ``` + * Carbon::parse('2018-07-25')->betweenIncluded('2018-07-14', '2018-08-01'); // true + * Carbon::parse('2018-07-25')->betweenIncluded('2018-08-01', '2018-08-20'); // false + * Carbon::parse('2018-07-25')->betweenIncluded('2018-07-25', '2018-08-01'); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * + * @return bool + */ + public function betweenIncluded($date1, $date2): bool + { + return $this->between($date1, $date2, true); + } + + /** + * Determines if the instance is between two others, bounds excluded. + * + * @example + * ``` + * Carbon::parse('2018-07-25')->betweenExcluded('2018-07-14', '2018-08-01'); // true + * Carbon::parse('2018-07-25')->betweenExcluded('2018-08-01', '2018-08-20'); // false + * Carbon::parse('2018-07-25')->betweenExcluded('2018-07-25', '2018-08-01'); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * + * @return bool + */ + public function betweenExcluded($date1, $date2): bool + { + return $this->between($date1, $date2, false); + } + + /** + * Determines if the instance is between two others + * + * @example + * ``` + * Carbon::parse('2018-07-25')->isBetween('2018-07-14', '2018-08-01'); // true + * Carbon::parse('2018-07-25')->isBetween('2018-08-01', '2018-08-20'); // false + * Carbon::parse('2018-07-25')->isBetween('2018-07-25', '2018-08-01'); // true + * Carbon::parse('2018-07-25')->isBetween('2018-07-25', '2018-08-01', false); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * @param bool $equal Indicates if an equal to comparison should be done + * + * @return bool + */ + public function isBetween($date1, $date2, $equal = true): bool + { + return $this->between($date1, $date2, $equal); + } + + /** + * Determines if the instance is a weekday. + * + * @example + * ``` + * Carbon::parse('2019-07-14')->isWeekday(); // false + * Carbon::parse('2019-07-15')->isWeekday(); // true + * ``` + * + * @return bool + */ + public function isWeekday() + { + return !$this->isWeekend(); + } + + /** + * Determines if the instance is a weekend day. + * + * @example + * ``` + * Carbon::parse('2019-07-14')->isWeekend(); // true + * Carbon::parse('2019-07-15')->isWeekend(); // false + * ``` + * + * @return bool + */ + public function isWeekend() + { + return \in_array($this->dayOfWeek, static::$weekendDays, true); + } + + /** + * Determines if the instance is yesterday. + * + * @example + * ``` + * Carbon::yesterday()->isYesterday(); // true + * Carbon::tomorrow()->isYesterday(); // false + * ``` + * + * @return bool + */ + public function isYesterday() + { + return $this->toDateString() === static::yesterday($this->getTimezone())->toDateString(); + } + + /** + * Determines if the instance is today. + * + * @example + * ``` + * Carbon::today()->isToday(); // true + * Carbon::tomorrow()->isToday(); // false + * ``` + * + * @return bool + */ + public function isToday() + { + return $this->toDateString() === $this->nowWithSameTz()->toDateString(); + } + + /** + * Determines if the instance is tomorrow. + * + * @example + * ``` + * Carbon::tomorrow()->isTomorrow(); // true + * Carbon::yesterday()->isTomorrow(); // false + * ``` + * + * @return bool + */ + public function isTomorrow() + { + return $this->toDateString() === static::tomorrow($this->getTimezone())->toDateString(); + } + + /** + * Determines if the instance is in the future, ie. greater (after) than now. + * + * @example + * ``` + * Carbon::now()->addHours(5)->isFuture(); // true + * Carbon::now()->subHours(5)->isFuture(); // false + * ``` + * + * @return bool + */ + public function isFuture() + { + return $this->greaterThan($this->nowWithSameTz()); + } + + /** + * Determines if the instance is in the past, ie. less (before) than now. + * + * @example + * ``` + * Carbon::now()->subHours(5)->isPast(); // true + * Carbon::now()->addHours(5)->isPast(); // false + * ``` + * + * @return bool + */ + public function isPast() + { + return $this->lessThan($this->nowWithSameTz()); + } + + /** + * Determines if the instance is a leap year. + * + * @example + * ``` + * Carbon::parse('2020-01-01')->isLeapYear(); // true + * Carbon::parse('2019-01-01')->isLeapYear(); // false + * ``` + * + * @return bool + */ + public function isLeapYear() + { + return $this->rawFormat('L') === '1'; + } + + /** + * Determines if the instance is a long year (using calendar year). + * + * ⚠️ This method completely ignores month and day to use the numeric year number, + * it's not correct if the exact date matters. For instance as `2019-12-30` is already + * in the first week of the 2020 year, if you want to know from this date if ISO week + * year 2020 is a long year, use `isLongIsoYear` instead. + * + * @example + * ``` + * Carbon::create(2015)->isLongYear(); // true + * Carbon::create(2016)->isLongYear(); // false + * ``` + * + * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates + * + * @return bool + */ + public function isLongYear() + { + return static::create($this->year, 12, 28, 0, 0, 0, $this->tz)->weekOfYear === 53; + } + + /** + * Determines if the instance is a long year (using ISO 8601 year). + * + * @example + * ``` + * Carbon::parse('2015-01-01')->isLongIsoYear(); // true + * Carbon::parse('2016-01-01')->isLongIsoYear(); // true + * Carbon::parse('2016-01-03')->isLongIsoYear(); // false + * Carbon::parse('2019-12-29')->isLongIsoYear(); // false + * Carbon::parse('2019-12-30')->isLongIsoYear(); // true + * ``` + * + * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates + * + * @return bool + */ + public function isLongIsoYear() + { + return static::create($this->isoWeekYear, 12, 28, 0, 0, 0, $this->tz)->weekOfYear === 53; + } + + /** + * Compares the formatted values of the two dates. + * + * @example + * ``` + * Carbon::parse('2019-06-13')->isSameAs('Y-d', Carbon::parse('2019-12-13')); // true + * Carbon::parse('2019-06-13')->isSameAs('Y-d', Carbon::parse('2019-06-14')); // false + * ``` + * + * @param string $format date formats to compare. + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date instance to compare with or null to use current day. + * + * @return bool + */ + public function isSameAs($format, $date = null) + { + return $this->rawFormat($format) === $this->resolveCarbon($date)->rawFormat($format); + } + + /** + * Determines if the instance is in the current unit given. + * + * @example + * ``` + * Carbon::parse('2019-01-13')->isSameUnit('year', Carbon::parse('2019-12-25')); // true + * Carbon::parse('2018-12-13')->isSameUnit('year', Carbon::parse('2019-12-25')); // false + * ``` + * + * @param string $unit singular unit string + * @param \Carbon\Carbon|\DateTimeInterface|null $date instance to compare with or null to use current day. + * + * @throws BadComparisonUnitException + * + * @return bool + */ + public function isSameUnit($unit, $date = null) + { + $units = [ + // @call isSameUnit + 'year' => 'Y', + // @call isSameUnit + 'week' => 'o-W', + // @call isSameUnit + 'day' => 'Y-m-d', + // @call isSameUnit + 'hour' => 'Y-m-d H', + // @call isSameUnit + 'minute' => 'Y-m-d H:i', + // @call isSameUnit + 'second' => 'Y-m-d H:i:s', + // @call isSameUnit + 'micro' => 'Y-m-d H:i:s.u', + // @call isSameUnit + 'microsecond' => 'Y-m-d H:i:s.u', + ]; + + if (isset($units[$unit])) { + return $this->isSameAs($units[$unit], $date); + } + + if (isset($this->$unit)) { + return $this->resolveCarbon($date)->$unit === $this->$unit; + } + + if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) { + throw new BadComparisonUnitException($unit); + } + + return false; + } + + /** + * Determines if the instance is in the current unit given. + * + * @example + * ``` + * Carbon::now()->isCurrentUnit('hour'); // true + * Carbon::now()->subHours(2)->isCurrentUnit('hour'); // false + * ``` + * + * @param string $unit The unit to test. + * + * @throws BadMethodCallException + * + * @return bool + */ + public function isCurrentUnit($unit) + { + return $this->{'isSame'.ucfirst($unit)}(); + } + + /** + * Checks if the passed in date is in the same quarter as the instance quarter (and year if needed). + * + * @example + * ``` + * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2019-03-01')); // true + * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2019-04-01')); // false + * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2018-03-01')); // false + * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2018-03-01'), false); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date The instance to compare with or null to use current day. + * @param bool $ofSameYear Check if it is the same month in the same year. + * + * @return bool + */ + public function isSameQuarter($date = null, $ofSameYear = true) + { + $date = $this->resolveCarbon($date); + + return $this->quarter === $date->quarter && (!$ofSameYear || $this->isSameYear($date)); + } + + /** + * Checks if the passed in date is in the same month as the instance´s month. + * + * @example + * ``` + * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2019-01-01')); // true + * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2019-02-01')); // false + * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2018-01-01')); // false + * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2018-01-01'), false); // true + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date. + * @param bool $ofSameYear Check if it is the same month in the same year. + * + * @return bool + */ + public function isSameMonth($date = null, $ofSameYear = true) + { + return $this->isSameAs($ofSameYear ? 'Y-m' : 'm', $date); + } + + /** + * Checks if this day is a specific day of the week. + * + * @example + * ``` + * Carbon::parse('2019-07-17')->isDayOfWeek(Carbon::WEDNESDAY); // true + * Carbon::parse('2019-07-17')->isDayOfWeek(Carbon::FRIDAY); // false + * Carbon::parse('2019-07-17')->isDayOfWeek('Wednesday'); // true + * Carbon::parse('2019-07-17')->isDayOfWeek('Friday'); // false + * ``` + * + * @param int $dayOfWeek + * + * @return bool + */ + public function isDayOfWeek($dayOfWeek) + { + if (\is_string($dayOfWeek) && \defined($constant = static::class.'::'.strtoupper($dayOfWeek))) { + $dayOfWeek = \constant($constant); + } + + return $this->dayOfWeek === $dayOfWeek; + } + + /** + * Check if its the birthday. Compares the date/month values of the two dates. + * + * @example + * ``` + * Carbon::now()->subYears(5)->isBirthday(); // true + * Carbon::now()->subYears(5)->subDay()->isBirthday(); // false + * Carbon::parse('2019-06-05')->isBirthday(Carbon::parse('2001-06-05')); // true + * Carbon::parse('2019-06-05')->isBirthday(Carbon::parse('2001-06-06')); // false + * ``` + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day. + * + * @return bool + */ + public function isBirthday($date = null) + { + return $this->isSameAs('md', $date); + } + + /** + * Check if today is the last day of the Month + * + * @example + * ``` + * Carbon::parse('2019-02-28')->isLastOfMonth(); // true + * Carbon::parse('2019-03-28')->isLastOfMonth(); // false + * Carbon::parse('2019-03-30')->isLastOfMonth(); // false + * Carbon::parse('2019-03-31')->isLastOfMonth(); // true + * Carbon::parse('2019-04-30')->isLastOfMonth(); // true + * ``` + * + * @return bool + */ + public function isLastOfMonth() + { + return $this->day === $this->daysInMonth; + } + + /** + * Check if the instance is start of day / midnight. + * + * @example + * ``` + * Carbon::parse('2019-02-28 00:00:00')->isStartOfDay(); // true + * Carbon::parse('2019-02-28 00:00:00.999999')->isStartOfDay(); // true + * Carbon::parse('2019-02-28 00:00:01')->isStartOfDay(); // false + * Carbon::parse('2019-02-28 00:00:00.000000')->isStartOfDay(true); // true + * Carbon::parse('2019-02-28 00:00:00.000012')->isStartOfDay(true); // false + * ``` + * + * @param bool $checkMicroseconds check time at microseconds precision + * + * @return bool + */ + public function isStartOfDay($checkMicroseconds = false) + { + /* @var CarbonInterface $this */ + return $checkMicroseconds + ? $this->rawFormat('H:i:s.u') === '00:00:00.000000' + : $this->rawFormat('H:i:s') === '00:00:00'; + } + + /** + * Check if the instance is end of day. + * + * @example + * ``` + * Carbon::parse('2019-02-28 23:59:59.999999')->isEndOfDay(); // true + * Carbon::parse('2019-02-28 23:59:59.123456')->isEndOfDay(); // true + * Carbon::parse('2019-02-28 23:59:59')->isEndOfDay(); // true + * Carbon::parse('2019-02-28 23:59:58.999999')->isEndOfDay(); // false + * Carbon::parse('2019-02-28 23:59:59.999999')->isEndOfDay(true); // true + * Carbon::parse('2019-02-28 23:59:59.123456')->isEndOfDay(true); // false + * Carbon::parse('2019-02-28 23:59:59')->isEndOfDay(true); // false + * ``` + * + * @param bool $checkMicroseconds check time at microseconds precision + * + * @return bool + */ + public function isEndOfDay($checkMicroseconds = false) + { + /* @var CarbonInterface $this */ + return $checkMicroseconds + ? $this->rawFormat('H:i:s.u') === '23:59:59.999999' + : $this->rawFormat('H:i:s') === '23:59:59'; + } + + /** + * Check if the instance is start of day / midnight. + * + * @example + * ``` + * Carbon::parse('2019-02-28 00:00:00')->isMidnight(); // true + * Carbon::parse('2019-02-28 00:00:00.999999')->isMidnight(); // true + * Carbon::parse('2019-02-28 00:00:01')->isMidnight(); // false + * ``` + * + * @return bool + */ + public function isMidnight() + { + return $this->isStartOfDay(); + } + + /** + * Check if the instance is midday. + * + * @example + * ``` + * Carbon::parse('2019-02-28 11:59:59.999999')->isMidday(); // false + * Carbon::parse('2019-02-28 12:00:00')->isMidday(); // true + * Carbon::parse('2019-02-28 12:00:00.999999')->isMidday(); // true + * Carbon::parse('2019-02-28 12:00:01')->isMidday(); // false + * ``` + * + * @return bool + */ + public function isMidday() + { + /* @var CarbonInterface $this */ + return $this->rawFormat('G:i:s') === static::$midDayAt.':00:00'; + } + + /** + * Checks if the (date)time string is in a given format. + * + * @example + * ``` + * Carbon::hasFormat('11:12:45', 'h:i:s'); // true + * Carbon::hasFormat('13:12:45', 'h:i:s'); // false + * ``` + * + * @param string $date + * @param string $format + * + * @return bool + */ + public static function hasFormat($date, $format) + { + // createFromFormat() is known to handle edge cases silently. + // E.g. "1975-5-1" (Y-n-j) will still be parsed correctly when "Y-m-d" is supplied as the format. + // To ensure we're really testing against our desired format, perform an additional regex validation. + + return self::matchFormatPattern((string) $date, preg_quote((string) $format, '/'), static::$regexFormats); + } + + /** + * Checks if the (date)time string is in a given format. + * + * @example + * ``` + * Carbon::hasFormatWithModifiers('31/08/2015', 'd#m#Y'); // true + * Carbon::hasFormatWithModifiers('31/08/2015', 'm#d#Y'); // false + * ``` + * + * @param string $date + * @param string $format + * + * @return bool + */ + public static function hasFormatWithModifiers($date, $format): bool + { + return self::matchFormatPattern((string) $date, (string) $format, array_merge(static::$regexFormats, static::$regexFormatModifiers)); + } + + /** + * Checks if the (date)time string is in a given format and valid to create a + * new instance. + * + * @example + * ``` + * Carbon::canBeCreatedFromFormat('11:12:45', 'h:i:s'); // true + * Carbon::canBeCreatedFromFormat('13:12:45', 'h:i:s'); // false + * ``` + * + * @param string $date + * @param string $format + * + * @return bool + */ + public static function canBeCreatedFromFormat($date, $format) + { + try { + // Try to create a DateTime object. Throws an InvalidArgumentException if the provided time string + // doesn't match the format in any way. + if (!static::rawCreateFromFormat($format, $date)) { + return false; + } + } catch (InvalidArgumentException $e) { + return false; + } + + return static::hasFormatWithModifiers($date, $format); + } + + /** + * Returns true if the current date matches the given string. + * + * @example + * ``` + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2018')); // false + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019-06')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('06-02')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019-06-02')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('Sunday')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('June')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23:45')); // true + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23:00')); // false + * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12h')); // true + * var_dump(Carbon::parse('2019-06-02 15:23:45')->is('3pm')); // true + * var_dump(Carbon::parse('2019-06-02 15:23:45')->is('3am')); // false + * ``` + * + * @param string $tester day name, month name, hour, date, etc. as string + * + * @return bool + */ + public function is(string $tester) + { + $tester = trim($tester); + + if (preg_match('/^\d+$/', $tester)) { + return $this->year === (int) $tester; + } + + if (preg_match('/^(?:Jan|January|Feb|February|Mar|March|Apr|April|May|Jun|June|Jul|July|Aug|August|Sep|September|Oct|October|Nov|November|Dec|December)$/i', $tester)) { + return $this->isSameMonth(static::parse($tester), false); + } + + if (preg_match('/^\d{3,}-\d{1,2}$/', $tester)) { + return $this->isSameMonth(static::parse($tester)); + } + + if (preg_match('/^\d{1,2}-\d{1,2}$/', $tester)) { + return $this->isSameDay(static::parse($this->year.'-'.$tester)); + } + + $modifier = preg_replace('/(\d)h$/i', '$1:00', $tester); + + /* @var CarbonInterface $max */ + $median = static::parse('5555-06-15 12:30:30.555555')->modify($modifier); + $current = $this->avoidMutation(); + /* @var CarbonInterface $other */ + $other = $this->avoidMutation()->modify($modifier); + + if ($current->eq($other)) { + return true; + } + + if (preg_match('/\d:\d{1,2}:\d{1,2}$/', $tester)) { + return $current->startOfSecond()->eq($other); + } + + if (preg_match('/\d:\d{1,2}$/', $tester)) { + return $current->startOfMinute()->eq($other); + } + + if (preg_match('/\d(?:h|am|pm)$/', $tester)) { + return $current->startOfHour()->eq($other); + } + + if (preg_match( + '/^(?:january|february|march|april|may|june|july|august|september|october|november|december)(?:\s+\d+)?$/i', + $tester + )) { + return $current->startOfMonth()->eq($other->startOfMonth()); + } + + $units = [ + 'month' => [1, 'year'], + 'day' => [1, 'month'], + 'hour' => [0, 'day'], + 'minute' => [0, 'hour'], + 'second' => [0, 'minute'], + 'microsecond' => [0, 'second'], + ]; + + foreach ($units as $unit => [$minimum, $startUnit]) { + if ($minimum === $median->$unit) { + $current = $current->startOf($startUnit); + + break; + } + } + + return $current->eq($other); + } + + /** + * Checks if the (date)time string is in a given format with + * given list of pattern replacements. + * + * @example + * ``` + * Carbon::hasFormat('11:12:45', 'h:i:s'); // true + * Carbon::hasFormat('13:12:45', 'h:i:s'); // false + * ``` + * + * @param string $date + * @param string $format + * @param array $replacements + * + * @return bool + */ + private static function matchFormatPattern(string $date, string $format, array $replacements): bool + { + // Preg quote, but remove escaped backslashes since we'll deal with escaped characters in the format string. + $regex = str_replace('\\\\', '\\', $format); + // Replace not-escaped letters + $regex = preg_replace_callback( + '/(?startOfTime ?? false; + } + + /** + * Returns true if the date was created using CarbonImmutable::endOfTime() + * + * @return bool + */ + public function isEndOfTime(): bool + { + return $this->endOfTime ?? false; + } + + private function discourageNull($value): void + { + if ($value === null) { + @trigger_error("Since 2.61.0, it's deprecated to compare a date to null, meaning of such comparison is ambiguous and will no longer be possible in 3.0.0, you should explicitly pass 'now' or make an other check to eliminate null values.", \E_USER_DEPRECATED); + } + } + + private function discourageBoolean($value): void + { + if (\is_bool($value)) { + @trigger_error("Since 2.61.0, it's deprecated to compare a date to true or false, meaning of such comparison is ambiguous and will no longer be possible in 3.0.0, you should explicitly pass 'now' or make an other check to eliminate boolean values.", \E_USER_DEPRECATED); + } + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Converter.php b/vendor/nesbot/carbon/src/Carbon/Traits/Converter.php new file mode 100644 index 0000000..fff8a60 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Converter.php @@ -0,0 +1,639 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\Carbon; +use Carbon\CarbonImmutable; +use Carbon\CarbonInterface; +use Carbon\CarbonInterval; +use Carbon\CarbonPeriod; +use Carbon\CarbonPeriodImmutable; +use Carbon\Exceptions\UnitException; +use Closure; +use DateTime; +use DateTimeImmutable; +use ReturnTypeWillChange; + +/** + * Trait Converter. + * + * Change date into different string formats and types and + * handle the string cast. + * + * Depends on the following methods: + * + * @method static copy() + */ +trait Converter +{ + use ToStringFormat; + + /** + * Returns the formatted date string on success or FALSE on failure. + * + * @see https://php.net/manual/en/datetime.format.php + * + * @param string $format + * + * @return string + */ + #[ReturnTypeWillChange] + public function format($format) + { + $function = $this->localFormatFunction ?: static::$formatFunction; + + if (!$function) { + return $this->rawFormat($format); + } + + if (\is_string($function) && method_exists($this, $function)) { + $function = [$this, $function]; + } + + return $function(...\func_get_args()); + } + + /** + * @see https://php.net/manual/en/datetime.format.php + * + * @param string $format + * + * @return string + */ + public function rawFormat($format) + { + return parent::format($format); + } + + /** + * Format the instance as a string using the set format + * + * @example + * ``` + * echo Carbon::now(); // Carbon instances can be cast to string + * ``` + * + * @return string + */ + public function __toString() + { + $format = $this->localToStringFormat ?? static::$toStringFormat; + + return $format instanceof Closure + ? $format($this) + : $this->rawFormat($format ?: ( + \defined('static::DEFAULT_TO_STRING_FORMAT') + ? static::DEFAULT_TO_STRING_FORMAT + : CarbonInterface::DEFAULT_TO_STRING_FORMAT + )); + } + + /** + * Format the instance as date + * + * @example + * ``` + * echo Carbon::now()->toDateString(); + * ``` + * + * @return string + */ + public function toDateString() + { + return $this->rawFormat('Y-m-d'); + } + + /** + * Format the instance as a readable date + * + * @example + * ``` + * echo Carbon::now()->toFormattedDateString(); + * ``` + * + * @return string + */ + public function toFormattedDateString() + { + return $this->rawFormat('M j, Y'); + } + + /** + * Format the instance with the day, and a readable date + * + * @example + * ``` + * echo Carbon::now()->toFormattedDayDateString(); + * ``` + * + * @return string + */ + public function toFormattedDayDateString(): string + { + return $this->rawFormat('D, M j, Y'); + } + + /** + * Format the instance as time + * + * @example + * ``` + * echo Carbon::now()->toTimeString(); + * ``` + * + * @param string $unitPrecision + * + * @return string + */ + public function toTimeString($unitPrecision = 'second') + { + return $this->rawFormat(static::getTimeFormatByPrecision($unitPrecision)); + } + + /** + * Format the instance as date and time + * + * @example + * ``` + * echo Carbon::now()->toDateTimeString(); + * ``` + * + * @param string $unitPrecision + * + * @return string + */ + public function toDateTimeString($unitPrecision = 'second') + { + return $this->rawFormat('Y-m-d '.static::getTimeFormatByPrecision($unitPrecision)); + } + + /** + * Return a format from H:i to H:i:s.u according to given unit precision. + * + * @param string $unitPrecision "minute", "second", "millisecond" or "microsecond" + * + * @return string + */ + public static function getTimeFormatByPrecision($unitPrecision) + { + switch (static::singularUnit($unitPrecision)) { + case 'minute': + return 'H:i'; + case 'second': + return 'H:i:s'; + case 'm': + case 'millisecond': + return 'H:i:s.v'; + case 'µ': + case 'microsecond': + return 'H:i:s.u'; + } + + throw new UnitException('Precision unit expected among: minute, second, millisecond and microsecond.'); + } + + /** + * Format the instance as date and time T-separated with no timezone + * + * @example + * ``` + * echo Carbon::now()->toDateTimeLocalString(); + * echo "\n"; + * echo Carbon::now()->toDateTimeLocalString('minute'); // You can specify precision among: minute, second, millisecond and microsecond + * ``` + * + * @param string $unitPrecision + * + * @return string + */ + public function toDateTimeLocalString($unitPrecision = 'second') + { + return $this->rawFormat('Y-m-d\T'.static::getTimeFormatByPrecision($unitPrecision)); + } + + /** + * Format the instance with day, date and time + * + * @example + * ``` + * echo Carbon::now()->toDayDateTimeString(); + * ``` + * + * @return string + */ + public function toDayDateTimeString() + { + return $this->rawFormat('D, M j, Y g:i A'); + } + + /** + * Format the instance as ATOM + * + * @example + * ``` + * echo Carbon::now()->toAtomString(); + * ``` + * + * @return string + */ + public function toAtomString() + { + return $this->rawFormat(DateTime::ATOM); + } + + /** + * Format the instance as COOKIE + * + * @example + * ``` + * echo Carbon::now()->toCookieString(); + * ``` + * + * @return string + */ + public function toCookieString() + { + return $this->rawFormat(DateTime::COOKIE); + } + + /** + * Format the instance as ISO8601 + * + * @example + * ``` + * echo Carbon::now()->toIso8601String(); + * ``` + * + * @return string + */ + public function toIso8601String() + { + return $this->toAtomString(); + } + + /** + * Format the instance as RFC822 + * + * @example + * ``` + * echo Carbon::now()->toRfc822String(); + * ``` + * + * @return string + */ + public function toRfc822String() + { + return $this->rawFormat(DateTime::RFC822); + } + + /** + * Convert the instance to UTC and return as Zulu ISO8601 + * + * @example + * ``` + * echo Carbon::now()->toIso8601ZuluString(); + * ``` + * + * @param string $unitPrecision + * + * @return string + */ + public function toIso8601ZuluString($unitPrecision = 'second') + { + return $this->avoidMutation() + ->utc() + ->rawFormat('Y-m-d\T'.static::getTimeFormatByPrecision($unitPrecision).'\Z'); + } + + /** + * Format the instance as RFC850 + * + * @example + * ``` + * echo Carbon::now()->toRfc850String(); + * ``` + * + * @return string + */ + public function toRfc850String() + { + return $this->rawFormat(DateTime::RFC850); + } + + /** + * Format the instance as RFC1036 + * + * @example + * ``` + * echo Carbon::now()->toRfc1036String(); + * ``` + * + * @return string + */ + public function toRfc1036String() + { + return $this->rawFormat(DateTime::RFC1036); + } + + /** + * Format the instance as RFC1123 + * + * @example + * ``` + * echo Carbon::now()->toRfc1123String(); + * ``` + * + * @return string + */ + public function toRfc1123String() + { + return $this->rawFormat(DateTime::RFC1123); + } + + /** + * Format the instance as RFC2822 + * + * @example + * ``` + * echo Carbon::now()->toRfc2822String(); + * ``` + * + * @return string + */ + public function toRfc2822String() + { + return $this->rawFormat(DateTime::RFC2822); + } + + /** + * Format the instance as RFC3339 + * + * @param bool $extended + * + * @example + * ``` + * echo Carbon::now()->toRfc3339String() . "\n"; + * echo Carbon::now()->toRfc3339String(true) . "\n"; + * ``` + * + * @return string + */ + public function toRfc3339String($extended = false) + { + $format = DateTime::RFC3339; + if ($extended) { + $format = DateTime::RFC3339_EXTENDED; + } + + return $this->rawFormat($format); + } + + /** + * Format the instance as RSS + * + * @example + * ``` + * echo Carbon::now()->toRssString(); + * ``` + * + * @return string + */ + public function toRssString() + { + return $this->rawFormat(DateTime::RSS); + } + + /** + * Format the instance as W3C + * + * @example + * ``` + * echo Carbon::now()->toW3cString(); + * ``` + * + * @return string + */ + public function toW3cString() + { + return $this->rawFormat(DateTime::W3C); + } + + /** + * Format the instance as RFC7231 + * + * @example + * ``` + * echo Carbon::now()->toRfc7231String(); + * ``` + * + * @return string + */ + public function toRfc7231String() + { + return $this->avoidMutation() + ->setTimezone('GMT') + ->rawFormat(\defined('static::RFC7231_FORMAT') ? static::RFC7231_FORMAT : CarbonInterface::RFC7231_FORMAT); + } + + /** + * Get default array representation. + * + * @example + * ``` + * var_dump(Carbon::now()->toArray()); + * ``` + * + * @return array + */ + public function toArray() + { + return [ + 'year' => $this->year, + 'month' => $this->month, + 'day' => $this->day, + 'dayOfWeek' => $this->dayOfWeek, + 'dayOfYear' => $this->dayOfYear, + 'hour' => $this->hour, + 'minute' => $this->minute, + 'second' => $this->second, + 'micro' => $this->micro, + 'timestamp' => $this->timestamp, + 'formatted' => $this->rawFormat(\defined('static::DEFAULT_TO_STRING_FORMAT') ? static::DEFAULT_TO_STRING_FORMAT : CarbonInterface::DEFAULT_TO_STRING_FORMAT), + 'timezone' => $this->timezone, + ]; + } + + /** + * Get default object representation. + * + * @example + * ``` + * var_dump(Carbon::now()->toObject()); + * ``` + * + * @return object + */ + public function toObject() + { + return (object) $this->toArray(); + } + + /** + * Returns english human readable complete date string. + * + * @example + * ``` + * echo Carbon::now()->toString(); + * ``` + * + * @return string + */ + public function toString() + { + return $this->avoidMutation()->locale('en')->isoFormat('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); + } + + /** + * Return the ISO-8601 string (ex: 1977-04-22T06:00:00Z, if $keepOffset truthy, offset will be kept: + * 1977-04-22T01:00:00-05:00). + * + * @example + * ``` + * echo Carbon::now('America/Toronto')->toISOString() . "\n"; + * echo Carbon::now('America/Toronto')->toISOString(true) . "\n"; + * ``` + * + * @param bool $keepOffset Pass true to keep the date offset. Else forced to UTC. + * + * @return null|string + */ + public function toISOString($keepOffset = false) + { + if (!$this->isValid()) { + return null; + } + + $yearFormat = $this->year < 0 || $this->year > 9999 ? 'YYYYYY' : 'YYYY'; + $tzFormat = $keepOffset ? 'Z' : '[Z]'; + $date = $keepOffset ? $this : $this->avoidMutation()->utc(); + + return $date->isoFormat("$yearFormat-MM-DD[T]HH:mm:ss.SSSSSS$tzFormat"); + } + + /** + * Return the ISO-8601 string (ex: 1977-04-22T06:00:00Z) with UTC timezone. + * + * @example + * ``` + * echo Carbon::now('America/Toronto')->toJSON(); + * ``` + * + * @return null|string + */ + public function toJSON() + { + return $this->toISOString(); + } + + /** + * Return native DateTime PHP object matching the current instance. + * + * @example + * ``` + * var_dump(Carbon::now()->toDateTime()); + * ``` + * + * @return DateTime + */ + public function toDateTime() + { + return new DateTime($this->rawFormat('Y-m-d H:i:s.u'), $this->getTimezone()); + } + + /** + * Return native toDateTimeImmutable PHP object matching the current instance. + * + * @example + * ``` + * var_dump(Carbon::now()->toDateTimeImmutable()); + * ``` + * + * @return DateTimeImmutable + */ + public function toDateTimeImmutable() + { + return new DateTimeImmutable($this->rawFormat('Y-m-d H:i:s.u'), $this->getTimezone()); + } + + /** + * @alias toDateTime + * + * Return native DateTime PHP object matching the current instance. + * + * @example + * ``` + * var_dump(Carbon::now()->toDate()); + * ``` + * + * @return DateTime + */ + public function toDate() + { + return $this->toDateTime(); + } + + /** + * Create a iterable CarbonPeriod object from current date to a given end date (and optional interval). + * + * @param \DateTimeInterface|Carbon|CarbonImmutable|int|null $end period end date or recurrences count if int + * @param int|\DateInterval|string|null $interval period default interval or number of the given $unit + * @param string|null $unit if specified, $interval must be an integer + * + * @return CarbonPeriod + */ + public function toPeriod($end = null, $interval = null, $unit = null) + { + if ($unit) { + $interval = CarbonInterval::make("$interval ".static::pluralUnit($unit)); + } + + $period = ($this->isMutable() ? new CarbonPeriod() : new CarbonPeriodImmutable()) + ->setDateClass(static::class) + ->setStartDate($this); + + if ($interval) { + $period = $period->setDateInterval($interval); + } + + if (\is_int($end) || (\is_string($end) && ctype_digit($end))) { + $period = $period->setRecurrences($end); + } elseif ($end) { + $period = $period->setEndDate($end); + } + + return $period; + } + + /** + * Create a iterable CarbonPeriod object from current date to a given end date (and optional interval). + * + * @param \DateTimeInterface|Carbon|CarbonImmutable|null $end period end date + * @param int|\DateInterval|string|null $interval period default interval or number of the given $unit + * @param string|null $unit if specified, $interval must be an integer + * + * @return CarbonPeriod + */ + public function range($end = null, $interval = null, $unit = null) + { + return $this->toPeriod($end, $interval, $unit); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php b/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php new file mode 100644 index 0000000..0d611ea --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php @@ -0,0 +1,977 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\Carbon; +use Carbon\CarbonImmutable; +use Carbon\CarbonInterface; +use Carbon\Exceptions\InvalidDateException; +use Carbon\Exceptions\InvalidFormatException; +use Carbon\Exceptions\OutOfRangeException; +use Carbon\Translator; +use Closure; +use DateMalformedStringException; +use DateTimeImmutable; +use DateTimeInterface; +use DateTimeZone; +use Exception; +use ReturnTypeWillChange; + +/** + * Trait Creator. + * + * Static creators. + * + * Depends on the following methods: + * + * @method static Carbon|CarbonImmutable getTestNow() + */ +trait Creator +{ + use ObjectInitialisation; + + /** + * The errors that can occur. + * + * @var array + */ + protected static $lastErrors; + + /** + * Create a new Carbon instance. + * + * Please see the testing aids section (specifically static::setTestNow()) + * for more on the possibility of this constructor returning a test instance. + * + * @param DateTimeInterface|string|null $time + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + */ + public function __construct($time = null, $tz = null) + { + if ($time instanceof DateTimeInterface) { + $time = $this->constructTimezoneFromDateTime($time, $tz)->format('Y-m-d H:i:s.u'); + } + + if (is_numeric($time) && (!\is_string($time) || !preg_match('/^\d{1,14}$/', $time))) { + $time = static::createFromTimestampUTC($time)->format('Y-m-d\TH:i:s.uP'); + } + + // If the class has a test now set and we are trying to create a now() + // instance then override as required + $isNow = empty($time) || $time === 'now'; + + if (method_exists(static::class, 'hasTestNow') && + method_exists(static::class, 'getTestNow') && + static::hasTestNow() && + ($isNow || static::hasRelativeKeywords($time)) + ) { + static::mockConstructorParameters($time, $tz); + } + + // Work-around for PHP bug https://bugs.php.net/bug.php?id=67127 + if (!str_contains((string) .1, '.')) { + $locale = setlocale(LC_NUMERIC, '0'); // @codeCoverageIgnore + setlocale(LC_NUMERIC, 'C'); // @codeCoverageIgnore + } + + try { + parent::__construct($time ?: 'now', static::safeCreateDateTimeZone($tz) ?: null); + } catch (Exception $exception) { + throw new InvalidFormatException($exception->getMessage(), 0, $exception); + } + + $this->constructedObjectId = spl_object_hash($this); + + if (isset($locale)) { + setlocale(LC_NUMERIC, $locale); // @codeCoverageIgnore + } + + self::setLastErrors(parent::getLastErrors()); + } + + /** + * Get timezone from a datetime instance. + * + * @param DateTimeInterface $date + * @param DateTimeZone|string|null $tz + * + * @return DateTimeInterface + */ + private function constructTimezoneFromDateTime(DateTimeInterface $date, &$tz) + { + if ($tz !== null) { + $safeTz = static::safeCreateDateTimeZone($tz); + + if ($safeTz) { + return ($date instanceof DateTimeImmutable ? $date : clone $date)->setTimezone($safeTz); + } + + return $date; + } + + $tz = $date->getTimezone(); + + return $date; + } + + /** + * Update constructedObjectId on cloned. + */ + public function __clone() + { + $this->constructedObjectId = spl_object_hash($this); + } + + /** + * Create a Carbon instance from a DateTime one. + * + * @param DateTimeInterface $date + * + * @return static + */ + public static function instance($date) + { + if ($date instanceof static) { + return clone $date; + } + + static::expectDateTime($date); + + $instance = new static($date->format('Y-m-d H:i:s.u'), $date->getTimezone()); + + if ($date instanceof CarbonInterface) { + $settings = $date->getSettings(); + + if (!$date->hasLocalTranslator()) { + unset($settings['locale']); + } + + $instance->settings($settings); + } + + return $instance; + } + + /** + * Create a carbon instance from a string. + * + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * + * @param string|DateTimeInterface|null $time + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function rawParse($time = null, $tz = null) + { + if ($time instanceof DateTimeInterface) { + return static::instance($time); + } + + try { + return new static($time, $tz); + } catch (Exception $exception) { + // @codeCoverageIgnoreStart + try { + $date = @static::now($tz)->change($time); + } catch (DateMalformedStringException $ignoredException) { + $date = null; + } + // @codeCoverageIgnoreEnd + + if (!$date) { + throw new InvalidFormatException("Could not parse '$time': ".$exception->getMessage(), 0, $exception); + } + + return $date; + } + } + + /** + * Create a carbon instance from a string. + * + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * + * @param string|DateTimeInterface|null $time + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function parse($time = null, $tz = null) + { + $function = static::$parseFunction; + + if (!$function) { + return static::rawParse($time, $tz); + } + + if (\is_string($function) && method_exists(static::class, $function)) { + $function = [static::class, $function]; + } + + return $function(...\func_get_args()); + } + + /** + * Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.). + * + * @param string $time date/time string in the given language (may also contain English). + * @param string|null $locale if locale is null or not specified, current global locale will be + * used instead. + * @param DateTimeZone|string|null $tz optional timezone for the new instance. + * + * @throws InvalidFormatException + * + * @return static + */ + public static function parseFromLocale($time, $locale = null, $tz = null) + { + return static::rawParse(static::translateTimeString($time, $locale, 'en'), $tz); + } + + /** + * Get a Carbon instance for the current date and time. + * + * @param DateTimeZone|string|null $tz + * + * @return static + */ + public static function now($tz = null) + { + return new static(null, $tz); + } + + /** + * Create a Carbon instance for today. + * + * @param DateTimeZone|string|null $tz + * + * @return static + */ + public static function today($tz = null) + { + return static::rawParse('today', $tz); + } + + /** + * Create a Carbon instance for tomorrow. + * + * @param DateTimeZone|string|null $tz + * + * @return static + */ + public static function tomorrow($tz = null) + { + return static::rawParse('tomorrow', $tz); + } + + /** + * Create a Carbon instance for yesterday. + * + * @param DateTimeZone|string|null $tz + * + * @return static + */ + public static function yesterday($tz = null) + { + return static::rawParse('yesterday', $tz); + } + + /** + * Create a Carbon instance for the greatest supported date. + * + * @return static + */ + public static function maxValue() + { + if (self::$PHPIntSize === 4) { + // 32 bit + return static::createFromTimestamp(PHP_INT_MAX); // @codeCoverageIgnore + } + + // 64 bit + return static::create(9999, 12, 31, 23, 59, 59); + } + + /** + * Create a Carbon instance for the lowest supported date. + * + * @return static + */ + public static function minValue() + { + if (self::$PHPIntSize === 4) { + // 32 bit + return static::createFromTimestamp(~PHP_INT_MAX); // @codeCoverageIgnore + } + + // 64 bit + return static::create(1, 1, 1, 0, 0, 0); + } + + private static function assertBetween($unit, $value, $min, $max) + { + if (static::isStrictModeEnabled() && ($value < $min || $value > $max)) { + throw new OutOfRangeException($unit, $min, $max, $value); + } + } + + private static function createNowInstance($tz) + { + if (!static::hasTestNow()) { + return static::now($tz); + } + + $now = static::getTestNow(); + + if ($now instanceof Closure) { + return $now(static::now($tz)); + } + + return $now->avoidMutation()->tz($tz); + } + + /** + * Create a new Carbon instance from a specific date and time. + * + * If any of $year, $month or $day are set to null their now() values will + * be used. + * + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * + * If $hour is not null then the default values for $minute and $second + * will be 0. + * + * @param DateTimeInterface|int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static|false + */ + public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null) + { + if ((\is_string($year) && !is_numeric($year)) || $year instanceof DateTimeInterface) { + return static::parse($year, $tz ?: (\is_string($month) || $month instanceof DateTimeZone ? $month : null)); + } + + $defaults = null; + $getDefault = function ($unit) use ($tz, &$defaults) { + if ($defaults === null) { + $now = self::createNowInstance($tz); + + $defaults = array_combine([ + 'year', + 'month', + 'day', + 'hour', + 'minute', + 'second', + ], explode('-', $now->rawFormat('Y-n-j-G-i-s.u'))); + } + + return $defaults[$unit]; + }; + + $year = $year ?? $getDefault('year'); + $month = $month ?? $getDefault('month'); + $day = $day ?? $getDefault('day'); + $hour = $hour ?? $getDefault('hour'); + $minute = $minute ?? $getDefault('minute'); + $second = (float) ($second ?? $getDefault('second')); + + self::assertBetween('month', $month, 0, 99); + self::assertBetween('day', $day, 0, 99); + self::assertBetween('hour', $hour, 0, 99); + self::assertBetween('minute', $minute, 0, 99); + self::assertBetween('second', $second, 0, 99); + + $fixYear = null; + + if ($year < 0) { + $fixYear = $year; + $year = 0; + } elseif ($year > 9999) { + $fixYear = $year - 9999; + $year = 9999; + } + + $second = ($second < 10 ? '0' : '').number_format($second, 6); + $instance = static::rawCreateFromFormat('!Y-n-j G:i:s.u', sprintf('%s-%s-%s %s:%02s:%02s', $year, $month, $day, $hour, $minute, $second), $tz); + + if ($fixYear !== null) { + $instance = $instance->addYears($fixYear); + } + + return $instance; + } + + /** + * Create a new safe Carbon instance from a specific date and time. + * + * If any of $year, $month or $day are set to null their now() values will + * be used. + * + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * + * If $hour is not null then the default values for $minute and $second + * will be 0. + * + * If one of the set values is not valid, an InvalidDateException + * will be thrown. + * + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|null $tz + * + * @throws InvalidDateException + * + * @return static|false + */ + public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null) + { + $fields = static::getRangesByUnit(); + + foreach ($fields as $field => $range) { + if ($$field !== null && (!\is_int($$field) || $$field < $range[0] || $$field > $range[1])) { + if (static::isStrictModeEnabled()) { + throw new InvalidDateException($field, $$field); + } + + return false; + } + } + + $instance = static::create($year, $month, $day, $hour, $minute, $second, $tz); + + foreach (array_reverse($fields) as $field => $range) { + if ($$field !== null && (!\is_int($$field) || $$field !== $instance->$field)) { + if (static::isStrictModeEnabled()) { + throw new InvalidDateException($field, $$field); + } + + return false; + } + } + + return $instance; + } + + /** + * Create a new Carbon instance from a specific date and time using strict validation. + * + * @see create() + * + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null): self + { + $initialStrictMode = static::isStrictModeEnabled(); + static::useStrictMode(true); + + try { + $date = static::create($year, $month, $day, $hour, $minute, $second, $tz); + } finally { + static::useStrictMode($initialStrictMode); + } + + return $date; + } + + /** + * Create a Carbon instance from just a date. The time portion is set to now. + * + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function createFromDate($year = null, $month = null, $day = null, $tz = null) + { + return static::create($year, $month, $day, null, null, null, $tz); + } + + /** + * Create a Carbon instance from just a date. The time portion is set to midnight. + * + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function createMidnightDate($year = null, $month = null, $day = null, $tz = null) + { + return static::create($year, $month, $day, 0, 0, 0, $tz); + } + + /** + * Create a Carbon instance from just a time. The date portion is set to today. + * + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null) + { + return static::create(null, null, null, $hour, $minute, $second, $tz); + } + + /** + * Create a Carbon instance from a time string. The date portion is set to today. + * + * @param string $time + * @param DateTimeZone|string|null $tz + * + * @throws InvalidFormatException + * + * @return static + */ + public static function createFromTimeString($time, $tz = null) + { + return static::today($tz)->setTimeFromTimeString($time); + } + + /** + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|false|null $originalTz + * + * @return DateTimeInterface|false + */ + private static function createFromFormatAndTimezone($format, $time, $originalTz) + { + // Work-around for https://bugs.php.net/bug.php?id=75577 + // @codeCoverageIgnoreStart + if (version_compare(PHP_VERSION, '7.3.0-dev', '<')) { + $format = str_replace('.v', '.u', $format); + } + // @codeCoverageIgnoreEnd + + if ($originalTz === null) { + return parent::createFromFormat($format, (string) $time); + } + + $tz = \is_int($originalTz) + ? @timezone_name_from_abbr('', (int) ($originalTz * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE), 1) + : $originalTz; + + $tz = static::safeCreateDateTimeZone($tz, $originalTz); + + if ($tz === false) { + return false; + } + + return parent::createFromFormat($format, (string) $time, $tz); + } + + /** + * Create a Carbon instance from a specific format. + * + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|false|null $tz + * + * @throws InvalidFormatException + * + * @return static|false + */ + public static function rawCreateFromFormat($format, $time, $tz = null) + { + // Work-around for https://bugs.php.net/bug.php?id=80141 + $format = preg_replace('/(?getTimezone(); + } + + $mock = $mock->copy(); + + // Prepend mock datetime only if the format does not contain non escaped unix epoch reset flag. + if (!preg_match("/{$nonEscaped}[!|]/", $format)) { + if (preg_match('/[HhGgisvuB]/', $format)) { + $mock = $mock->setTime(0, 0); + } + + $format = static::MOCK_DATETIME_FORMAT.' '.$format; + $time = ($mock instanceof self ? $mock->rawFormat(static::MOCK_DATETIME_FORMAT) : $mock->format(static::MOCK_DATETIME_FORMAT)).' '.$time; + } + + // Regenerate date from the modified format to base result on the mocked instance instead of now. + $date = self::createFromFormatAndTimezone($format, $time, $tz); + } + + if ($date instanceof DateTimeInterface) { + $instance = static::instance($date); + $instance::setLastErrors($lastErrors); + + return $instance; + } + + if (static::isStrictModeEnabled()) { + throw new InvalidFormatException(implode(PHP_EOL, $lastErrors['errors'])); + } + + return false; + } + + /** + * Create a Carbon instance from a specific format. + * + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|false|null $tz + * + * @throws InvalidFormatException + * + * @return static|false + */ + #[ReturnTypeWillChange] + public static function createFromFormat($format, $time, $tz = null) + { + $function = static::$createFromFormatFunction; + + if (!$function) { + return static::rawCreateFromFormat($format, $time, $tz); + } + + if (\is_string($function) && method_exists(static::class, $function)) { + $function = [static::class, $function]; + } + + return $function(...\func_get_args()); + } + + /** + * Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()). + * + * @param string $format Datetime format + * @param string $time + * @param DateTimeZone|string|false|null $tz optional timezone + * @param string|null $locale locale to be used for LTS, LT, LL, LLL, etc. macro-formats (en by fault, unneeded if no such macro-format in use) + * @param \Symfony\Component\Translation\TranslatorInterface $translator optional custom translator to use for macro-formats + * + * @throws InvalidFormatException + * + * @return static|false + */ + public static function createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null) + { + $format = preg_replace_callback('/(? static::getTranslationMessageWith($translator, 'formats.LT', $locale, 'h:mm A'), + 'LTS' => static::getTranslationMessageWith($translator, 'formats.LTS', $locale, 'h:mm:ss A'), + 'L' => static::getTranslationMessageWith($translator, 'formats.L', $locale, 'MM/DD/YYYY'), + 'LL' => static::getTranslationMessageWith($translator, 'formats.LL', $locale, 'MMMM D, YYYY'), + 'LLL' => static::getTranslationMessageWith($translator, 'formats.LLL', $locale, 'MMMM D, YYYY h:mm A'), + 'LLLL' => static::getTranslationMessageWith($translator, 'formats.LLLL', $locale, 'dddd, MMMM D, YYYY h:mm A'), + ]; + } + + return $formats[$code] ?? preg_replace_callback( + '/MMMM|MM|DD|dddd/', + function ($code) { + return mb_substr($code[0], 1); + }, + $formats[strtoupper($code)] ?? '' + ); + }, $format); + + $format = preg_replace_callback('/(? 'd', + 'OM' => 'M', + 'OY' => 'Y', + 'OH' => 'G', + 'Oh' => 'g', + 'Om' => 'i', + 'Os' => 's', + 'D' => 'd', + 'DD' => 'd', + 'Do' => 'd', + 'd' => '!', + 'dd' => '!', + 'ddd' => 'D', + 'dddd' => 'D', + 'DDD' => 'z', + 'DDDD' => 'z', + 'DDDo' => 'z', + 'e' => '!', + 'E' => '!', + 'H' => 'G', + 'HH' => 'H', + 'h' => 'g', + 'hh' => 'h', + 'k' => 'G', + 'kk' => 'G', + 'hmm' => 'gi', + 'hmmss' => 'gis', + 'Hmm' => 'Gi', + 'Hmmss' => 'Gis', + 'm' => 'i', + 'mm' => 'i', + 'a' => 'a', + 'A' => 'a', + 's' => 's', + 'ss' => 's', + 'S' => '*', + 'SS' => '*', + 'SSS' => '*', + 'SSSS' => '*', + 'SSSSS' => '*', + 'SSSSSS' => 'u', + 'SSSSSSS' => 'u*', + 'SSSSSSSS' => 'u*', + 'SSSSSSSSS' => 'u*', + 'M' => 'm', + 'MM' => 'm', + 'MMM' => 'M', + 'MMMM' => 'M', + 'Mo' => 'm', + 'Q' => '!', + 'Qo' => '!', + 'G' => '!', + 'GG' => '!', + 'GGG' => '!', + 'GGGG' => '!', + 'GGGGG' => '!', + 'g' => '!', + 'gg' => '!', + 'ggg' => '!', + 'gggg' => '!', + 'ggggg' => '!', + 'W' => '!', + 'WW' => '!', + 'Wo' => '!', + 'w' => '!', + 'ww' => '!', + 'wo' => '!', + 'x' => 'U???', + 'X' => 'U', + 'Y' => 'Y', + 'YY' => 'y', + 'YYYY' => 'Y', + 'YYYYY' => 'Y', + 'YYYYYY' => 'Y', + 'z' => 'e', + 'zz' => 'e', + 'Z' => 'e', + 'ZZ' => 'e', + ]; + } + + $format = $replacements[$code] ?? '?'; + + if ($format === '!') { + throw new InvalidFormatException("Format $code not supported for creation."); + } + + return $format; + }, $format); + + return static::rawCreateFromFormat($format, $time, $tz); + } + + /** + * Create a Carbon instance from a specific format and a string in a given language. + * + * @param string $format Datetime format + * @param string $locale + * @param string $time + * @param DateTimeZone|string|false|null $tz + * + * @throws InvalidFormatException + * + * @return static|false + */ + public static function createFromLocaleFormat($format, $locale, $time, $tz = null) + { + $format = preg_replace_callback( + '/(?:\\\\[a-zA-Z]|[bfkqCEJKQRV]){2,}/', + static function (array $match) use ($locale): string { + $word = str_replace('\\', '', $match[0]); + $translatedWord = static::translateTimeString($word, $locale, 'en'); + + return $word === $translatedWord + ? $match[0] + : preg_replace('/[a-zA-Z]/', '\\\\$0', $translatedWord); + }, + $format + ); + + return static::rawCreateFromFormat($format, static::translateTimeString($time, $locale, 'en'), $tz); + } + + /** + * Create a Carbon instance from a specific ISO format and a string in a given language. + * + * @param string $format Datetime ISO format + * @param string $locale + * @param string $time + * @param DateTimeZone|string|false|null $tz + * + * @throws InvalidFormatException + * + * @return static|false + */ + public static function createFromLocaleIsoFormat($format, $locale, $time, $tz = null) + { + $time = static::translateTimeString($time, $locale, 'en', CarbonInterface::TRANSLATE_MONTHS | CarbonInterface::TRANSLATE_DAYS | CarbonInterface::TRANSLATE_MERIDIEM); + + return static::createFromIsoFormat($format, $time, $tz, $locale); + } + + /** + * Make a Carbon instance from given variable if possible. + * + * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals + * and recurrences). Throw an exception for invalid format, but otherwise return null. + * + * @param mixed $var + * + * @throws InvalidFormatException + * + * @return static|null + */ + public static function make($var) + { + if ($var instanceof DateTimeInterface) { + return static::instance($var); + } + + $date = null; + + if (\is_string($var)) { + $var = trim($var); + + if (!preg_match('/^P[\dT]/', $var) && + !preg_match('/^R\d/', $var) && + preg_match('/[a-z\d]/i', $var) + ) { + $date = static::parse($var); + } + } + + return $date; + } + + /** + * Set last errors. + * + * @param array|bool $lastErrors + * + * @return void + */ + private static function setLastErrors($lastErrors) + { + if (\is_array($lastErrors) || $lastErrors === false) { + static::$lastErrors = \is_array($lastErrors) ? $lastErrors : [ + 'warning_count' => 0, + 'warnings' => [], + 'error_count' => 0, + 'errors' => [], + ]; + } + } + + /** + * {@inheritdoc} + * + * @return array + */ + #[ReturnTypeWillChange] + public static function getLastErrors() + { + return static::$lastErrors; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Date.php b/vendor/nesbot/carbon/src/Carbon/Traits/Date.php new file mode 100644 index 0000000..8ae5c17 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Date.php @@ -0,0 +1,2747 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use BadMethodCallException; +use Carbon\Carbon; +use Carbon\CarbonInterface; +use Carbon\CarbonPeriod; +use Carbon\CarbonTimeZone; +use Carbon\Exceptions\BadComparisonUnitException; +use Carbon\Exceptions\ImmutableException; +use Carbon\Exceptions\InvalidTimeZoneException; +use Carbon\Exceptions\InvalidTypeException; +use Carbon\Exceptions\UnknownGetterException; +use Carbon\Exceptions\UnknownMethodException; +use Carbon\Exceptions\UnknownSetterException; +use Carbon\Exceptions\UnknownUnitException; +use Closure; +use DateInterval; +use DatePeriod; +use DateTime; +use DateTimeImmutable; +use DateTimeInterface; +use DateTimeZone; +use InvalidArgumentException; +use ReflectionException; +use ReturnTypeWillChange; +use Throwable; + +/** + * A simple API extension for DateTime. + * + * @mixin DeprecatedProperties + * + * + * + * @property int $year + * @property int $yearIso + * @property int $month + * @property int $day + * @property int $hour + * @property int $minute + * @property int $second + * @property int $micro + * @property int $microsecond + * @property int|float|string $timestamp seconds since the Unix Epoch + * @property string $englishDayOfWeek the day of week in English + * @property string $shortEnglishDayOfWeek the abbreviated day of week in English + * @property string $englishMonth the month in English + * @property string $shortEnglishMonth the abbreviated month in English + * @property int $milliseconds + * @property int $millisecond + * @property int $milli + * @property int $week 1 through 53 + * @property int $isoWeek 1 through 53 + * @property int $weekYear year according to week format + * @property int $isoWeekYear year according to ISO week format + * @property int $dayOfYear 1 through 366 + * @property int $age does a diffInYears() with default parameters + * @property int $offset the timezone offset in seconds from UTC + * @property int $offsetMinutes the timezone offset in minutes from UTC + * @property int $offsetHours the timezone offset in hours from UTC + * @property CarbonTimeZone $timezone the current timezone + * @property CarbonTimeZone $tz alias of $timezone + * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) + * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) + * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday + * @property-read int $daysInMonth number of days in the given month + * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark) + * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark) + * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name + * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName + * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language + * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language + * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language + * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + * @property-read int $noZeroHour current hour from 1 to 24 + * @property-read int $weeksInYear 51 through 53 + * @property-read int $isoWeeksInYear 51 through 53 + * @property-read int $weekOfMonth 1 through 5 + * @property-read int $weekNumberInMonth 1 through 5 + * @property-read int $firstWeekDay 0 through 6 + * @property-read int $lastWeekDay 0 through 6 + * @property-read int $daysInYear 365 or 366 + * @property-read int $quarter the quarter of this instance, 1 - 4 + * @property-read int $decade the decade of this instance + * @property-read int $century the century of this instance + * @property-read int $millennium the millennium of this instance + * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise + * @property-read bool $local checks if the timezone is local, true if local, false otherwise + * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise + * @property-read string $timezoneName the current timezone name + * @property-read string $tzName alias of $timezoneName + * @property-read string $locale locale of the current instance + * + * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) + * @method bool isLocal() Check if the current instance has non-UTC timezone. + * @method bool isValid() Check if the current instance is a valid date. + * @method bool isDST() Check if the current instance is in a daylight saving time. + * @method bool isSunday() Checks if the instance day is sunday. + * @method bool isMonday() Checks if the instance day is monday. + * @method bool isTuesday() Checks if the instance day is tuesday. + * @method bool isWednesday() Checks if the instance day is wednesday. + * @method bool isThursday() Checks if the instance day is thursday. + * @method bool isFriday() Checks if the instance day is friday. + * @method bool isSaturday() Checks if the instance day is saturday. + * @method bool isSameYear(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment. + * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year. + * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year. + * @method bool isSameWeek(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment. + * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week. + * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week. + * @method bool isSameDay(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment. + * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day. + * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day. + * @method bool isSameHour(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment. + * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour. + * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour. + * @method bool isSameMinute(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment. + * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute. + * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute. + * @method bool isSameSecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment. + * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second. + * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second. + * @method bool isSameMicro(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment. + * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond. + * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond. + * @method bool isSameMicrosecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment. + * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond. + * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond. + * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment. + * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month. + * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month. + * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment. + * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter. + * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter. + * @method bool isSameDecade(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment. + * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade. + * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade. + * @method bool isSameCentury(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment. + * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century. + * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century. + * @method bool isSameMillennium(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone). + * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment. + * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium. + * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium. + * @method CarbonInterface years(int $value) Set current instance year to the given value. + * @method CarbonInterface year(int $value) Set current instance year to the given value. + * @method CarbonInterface setYears(int $value) Set current instance year to the given value. + * @method CarbonInterface setYear(int $value) Set current instance year to the given value. + * @method CarbonInterface months(int $value) Set current instance month to the given value. + * @method CarbonInterface month(int $value) Set current instance month to the given value. + * @method CarbonInterface setMonths(int $value) Set current instance month to the given value. + * @method CarbonInterface setMonth(int $value) Set current instance month to the given value. + * @method CarbonInterface days(int $value) Set current instance day to the given value. + * @method CarbonInterface day(int $value) Set current instance day to the given value. + * @method CarbonInterface setDays(int $value) Set current instance day to the given value. + * @method CarbonInterface setDay(int $value) Set current instance day to the given value. + * @method CarbonInterface hours(int $value) Set current instance hour to the given value. + * @method CarbonInterface hour(int $value) Set current instance hour to the given value. + * @method CarbonInterface setHours(int $value) Set current instance hour to the given value. + * @method CarbonInterface setHour(int $value) Set current instance hour to the given value. + * @method CarbonInterface minutes(int $value) Set current instance minute to the given value. + * @method CarbonInterface minute(int $value) Set current instance minute to the given value. + * @method CarbonInterface setMinutes(int $value) Set current instance minute to the given value. + * @method CarbonInterface setMinute(int $value) Set current instance minute to the given value. + * @method CarbonInterface seconds(int $value) Set current instance second to the given value. + * @method CarbonInterface second(int $value) Set current instance second to the given value. + * @method CarbonInterface setSeconds(int $value) Set current instance second to the given value. + * @method CarbonInterface setSecond(int $value) Set current instance second to the given value. + * @method CarbonInterface millis(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface milli(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface setMillis(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface setMilli(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface milliseconds(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface millisecond(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface setMilliseconds(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface setMillisecond(int $value) Set current instance millisecond to the given value. + * @method CarbonInterface micros(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface micro(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface setMicros(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface setMicro(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface microseconds(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface microsecond(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface setMicroseconds(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface setMicrosecond(int $value) Set current instance microsecond to the given value. + * @method CarbonInterface addYears(int $value = 1) Add years (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addYear() Add one year to the instance (using date interval). + * @method CarbonInterface subYears(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subYear() Sub one year to the instance (using date interval). + * @method CarbonInterface addYearsWithOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subYearsWithOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addYearsWithoutOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearsWithoutOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearsWithNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearsWithNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearsNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearsNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonths(int $value = 1) Add months (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMonth() Add one month to the instance (using date interval). + * @method CarbonInterface subMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMonth() Sub one month to the instance (using date interval). + * @method CarbonInterface addMonthsWithOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subMonthsWithOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addMonthsWithoutOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthsWithoutOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthsWithNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthsWithNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthsNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthsNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDays(int $value = 1) Add days (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addDay() Add one day to the instance (using date interval). + * @method CarbonInterface subDays(int $value = 1) Sub days (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subDay() Sub one day to the instance (using date interval). + * @method CarbonInterface addHours(int $value = 1) Add hours (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addHour() Add one hour to the instance (using date interval). + * @method CarbonInterface subHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subHour() Sub one hour to the instance (using date interval). + * @method CarbonInterface addMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMinute() Add one minute to the instance (using date interval). + * @method CarbonInterface subMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMinute() Sub one minute to the instance (using date interval). + * @method CarbonInterface addSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addSecond() Add one second to the instance (using date interval). + * @method CarbonInterface subSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subSecond() Sub one second to the instance (using date interval). + * @method CarbonInterface addMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMilli() Add one millisecond to the instance (using date interval). + * @method CarbonInterface subMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMilli() Sub one millisecond to the instance (using date interval). + * @method CarbonInterface addMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMillisecond() Add one millisecond to the instance (using date interval). + * @method CarbonInterface subMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMillisecond() Sub one millisecond to the instance (using date interval). + * @method CarbonInterface addMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMicro() Add one microsecond to the instance (using date interval). + * @method CarbonInterface subMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMicro() Sub one microsecond to the instance (using date interval). + * @method CarbonInterface addMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMicrosecond() Add one microsecond to the instance (using date interval). + * @method CarbonInterface subMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMicrosecond() Sub one microsecond to the instance (using date interval). + * @method CarbonInterface addMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addMillennium() Add one millennium to the instance (using date interval). + * @method CarbonInterface subMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subMillennium() Sub one millennium to the instance (using date interval). + * @method CarbonInterface addMillenniaWithOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subMillenniaWithOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addMillenniaWithoutOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniaWithoutOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniaWithNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniaWithNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniaNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniaNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addCentury() Add one century to the instance (using date interval). + * @method CarbonInterface subCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subCentury() Sub one century to the instance (using date interval). + * @method CarbonInterface addCenturiesWithOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subCenturiesWithOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addCenturiesWithoutOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturiesWithoutOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturiesWithNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturiesWithNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturiesNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturiesNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addDecade() Add one decade to the instance (using date interval). + * @method CarbonInterface subDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subDecade() Sub one decade to the instance (using date interval). + * @method CarbonInterface addDecadesWithOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subDecadesWithOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addDecadesWithoutOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadesWithoutOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadesWithNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadesWithNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadesNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadesNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addQuarter() Add one quarter to the instance (using date interval). + * @method CarbonInterface subQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subQuarter() Sub one quarter to the instance (using date interval). + * @method CarbonInterface addQuartersWithOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subQuartersWithOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed. + * @method CarbonInterface addQuartersWithoutOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuartersWithoutOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuartersWithNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuartersWithNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuartersNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuartersNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden. + * @method CarbonInterface addWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addWeek() Add one week to the instance (using date interval). + * @method CarbonInterface subWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subWeek() Sub one week to the instance (using date interval). + * @method CarbonInterface addWeekdays(int $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface addWeekday() Add one weekday to the instance (using date interval). + * @method CarbonInterface subWeekdays(int $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval). + * @method CarbonInterface subWeekday() Sub one weekday to the instance (using date interval). + * @method CarbonInterface addRealMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMicro() Add one microsecond to the instance (using timestamp). + * @method CarbonInterface subRealMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMicro() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method CarbonInterface addRealMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMicrosecond() Add one microsecond to the instance (using timestamp). + * @method CarbonInterface subRealMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMicrosecond() Sub one microsecond to the instance (using timestamp). + * @method CarbonPeriod microsecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given. + * @method CarbonInterface addRealMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMilli() Add one millisecond to the instance (using timestamp). + * @method CarbonInterface subRealMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMilli() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method CarbonInterface addRealMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMillisecond() Add one millisecond to the instance (using timestamp). + * @method CarbonInterface subRealMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMillisecond() Sub one millisecond to the instance (using timestamp). + * @method CarbonPeriod millisecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given. + * @method CarbonInterface addRealSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealSecond() Add one second to the instance (using timestamp). + * @method CarbonInterface subRealSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealSecond() Sub one second to the instance (using timestamp). + * @method CarbonPeriod secondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given. + * @method CarbonInterface addRealMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMinute() Add one minute to the instance (using timestamp). + * @method CarbonInterface subRealMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMinute() Sub one minute to the instance (using timestamp). + * @method CarbonPeriod minutesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given. + * @method CarbonInterface addRealHours(int $value = 1) Add hours (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealHour() Add one hour to the instance (using timestamp). + * @method CarbonInterface subRealHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealHour() Sub one hour to the instance (using timestamp). + * @method CarbonPeriod hoursUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given. + * @method CarbonInterface addRealDays(int $value = 1) Add days (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealDay() Add one day to the instance (using timestamp). + * @method CarbonInterface subRealDays(int $value = 1) Sub days (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealDay() Sub one day to the instance (using timestamp). + * @method CarbonPeriod daysUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given. + * @method CarbonInterface addRealWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealWeek() Add one week to the instance (using timestamp). + * @method CarbonInterface subRealWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealWeek() Sub one week to the instance (using timestamp). + * @method CarbonPeriod weeksUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given. + * @method CarbonInterface addRealMonths(int $value = 1) Add months (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMonth() Add one month to the instance (using timestamp). + * @method CarbonInterface subRealMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMonth() Sub one month to the instance (using timestamp). + * @method CarbonPeriod monthsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given. + * @method CarbonInterface addRealQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealQuarter() Add one quarter to the instance (using timestamp). + * @method CarbonInterface subRealQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealQuarter() Sub one quarter to the instance (using timestamp). + * @method CarbonPeriod quartersUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given. + * @method CarbonInterface addRealYears(int $value = 1) Add years (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealYear() Add one year to the instance (using timestamp). + * @method CarbonInterface subRealYears(int $value = 1) Sub years (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealYear() Sub one year to the instance (using timestamp). + * @method CarbonPeriod yearsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given. + * @method CarbonInterface addRealDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealDecade() Add one decade to the instance (using timestamp). + * @method CarbonInterface subRealDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealDecade() Sub one decade to the instance (using timestamp). + * @method CarbonPeriod decadesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given. + * @method CarbonInterface addRealCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealCentury() Add one century to the instance (using timestamp). + * @method CarbonInterface subRealCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealCentury() Sub one century to the instance (using timestamp). + * @method CarbonPeriod centuriesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given. + * @method CarbonInterface addRealMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface addRealMillennium() Add one millennium to the instance (using timestamp). + * @method CarbonInterface subRealMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp). + * @method CarbonInterface subRealMillennium() Sub one millennium to the instance (using timestamp). + * @method CarbonPeriod millenniaUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given. + * @method CarbonInterface roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method CarbonInterface roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function. + * @method CarbonInterface floorYear(float $precision = 1) Truncate the current instance year with given precision. + * @method CarbonInterface floorYears(float $precision = 1) Truncate the current instance year with given precision. + * @method CarbonInterface ceilYear(float $precision = 1) Ceil the current instance year with given precision. + * @method CarbonInterface ceilYears(float $precision = 1) Ceil the current instance year with given precision. + * @method CarbonInterface roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method CarbonInterface roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function. + * @method CarbonInterface floorMonth(float $precision = 1) Truncate the current instance month with given precision. + * @method CarbonInterface floorMonths(float $precision = 1) Truncate the current instance month with given precision. + * @method CarbonInterface ceilMonth(float $precision = 1) Ceil the current instance month with given precision. + * @method CarbonInterface ceilMonths(float $precision = 1) Ceil the current instance month with given precision. + * @method CarbonInterface roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method CarbonInterface roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function. + * @method CarbonInterface floorDay(float $precision = 1) Truncate the current instance day with given precision. + * @method CarbonInterface floorDays(float $precision = 1) Truncate the current instance day with given precision. + * @method CarbonInterface ceilDay(float $precision = 1) Ceil the current instance day with given precision. + * @method CarbonInterface ceilDays(float $precision = 1) Ceil the current instance day with given precision. + * @method CarbonInterface roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method CarbonInterface roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function. + * @method CarbonInterface floorHour(float $precision = 1) Truncate the current instance hour with given precision. + * @method CarbonInterface floorHours(float $precision = 1) Truncate the current instance hour with given precision. + * @method CarbonInterface ceilHour(float $precision = 1) Ceil the current instance hour with given precision. + * @method CarbonInterface ceilHours(float $precision = 1) Ceil the current instance hour with given precision. + * @method CarbonInterface roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method CarbonInterface roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function. + * @method CarbonInterface floorMinute(float $precision = 1) Truncate the current instance minute with given precision. + * @method CarbonInterface floorMinutes(float $precision = 1) Truncate the current instance minute with given precision. + * @method CarbonInterface ceilMinute(float $precision = 1) Ceil the current instance minute with given precision. + * @method CarbonInterface ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision. + * @method CarbonInterface roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method CarbonInterface roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function. + * @method CarbonInterface floorSecond(float $precision = 1) Truncate the current instance second with given precision. + * @method CarbonInterface floorSeconds(float $precision = 1) Truncate the current instance second with given precision. + * @method CarbonInterface ceilSecond(float $precision = 1) Ceil the current instance second with given precision. + * @method CarbonInterface ceilSeconds(float $precision = 1) Ceil the current instance second with given precision. + * @method CarbonInterface roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method CarbonInterface roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function. + * @method CarbonInterface floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision. + * @method CarbonInterface floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision. + * @method CarbonInterface ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision. + * @method CarbonInterface ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision. + * @method CarbonInterface roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method CarbonInterface roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function. + * @method CarbonInterface floorCentury(float $precision = 1) Truncate the current instance century with given precision. + * @method CarbonInterface floorCenturies(float $precision = 1) Truncate the current instance century with given precision. + * @method CarbonInterface ceilCentury(float $precision = 1) Ceil the current instance century with given precision. + * @method CarbonInterface ceilCenturies(float $precision = 1) Ceil the current instance century with given precision. + * @method CarbonInterface roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method CarbonInterface roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function. + * @method CarbonInterface floorDecade(float $precision = 1) Truncate the current instance decade with given precision. + * @method CarbonInterface floorDecades(float $precision = 1) Truncate the current instance decade with given precision. + * @method CarbonInterface ceilDecade(float $precision = 1) Ceil the current instance decade with given precision. + * @method CarbonInterface ceilDecades(float $precision = 1) Ceil the current instance decade with given precision. + * @method CarbonInterface roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method CarbonInterface roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function. + * @method CarbonInterface floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision. + * @method CarbonInterface floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision. + * @method CarbonInterface ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision. + * @method CarbonInterface ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision. + * @method CarbonInterface roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method CarbonInterface roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function. + * @method CarbonInterface floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method CarbonInterface floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision. + * @method CarbonInterface ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method CarbonInterface ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision. + * @method CarbonInterface roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method CarbonInterface roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function. + * @method CarbonInterface floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method CarbonInterface floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision. + * @method CarbonInterface ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method CarbonInterface ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision. + * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.) + * + * + */ +trait Date +{ + use Boundaries; + use Comparison; + use Converter; + use Creator; + use Difference; + use Macro; + use MagicParameter; + use Modifiers; + use Mutability; + use ObjectInitialisation; + use Options; + use Rounding; + use Serialization; + use Test; + use Timestamp; + use Units; + use Week; + + /** + * Names of days of the week. + * + * @var array + */ + protected static $days = [ + // @call isDayOfWeek + CarbonInterface::SUNDAY => 'Sunday', + // @call isDayOfWeek + CarbonInterface::MONDAY => 'Monday', + // @call isDayOfWeek + CarbonInterface::TUESDAY => 'Tuesday', + // @call isDayOfWeek + CarbonInterface::WEDNESDAY => 'Wednesday', + // @call isDayOfWeek + CarbonInterface::THURSDAY => 'Thursday', + // @call isDayOfWeek + CarbonInterface::FRIDAY => 'Friday', + // @call isDayOfWeek + CarbonInterface::SATURDAY => 'Saturday', + ]; + + /** + * Will UTF8 encoding be used to print localized date/time ? + * + * @var bool + */ + protected static $utf8 = false; + + /** + * List of unit and magic methods associated as doc-comments. + * + * @var array + */ + protected static $units = [ + // @call setUnit + // @call addUnit + 'year', + // @call setUnit + // @call addUnit + 'month', + // @call setUnit + // @call addUnit + 'day', + // @call setUnit + // @call addUnit + 'hour', + // @call setUnit + // @call addUnit + 'minute', + // @call setUnit + // @call addUnit + 'second', + // @call setUnit + // @call addUnit + 'milli', + // @call setUnit + // @call addUnit + 'millisecond', + // @call setUnit + // @call addUnit + 'micro', + // @call setUnit + // @call addUnit + 'microsecond', + ]; + + /** + * Creates a DateTimeZone from a string, DateTimeZone or integer offset. + * + * @param DateTimeZone|string|int|null $object original value to get CarbonTimeZone from it. + * @param DateTimeZone|string|int|null $objectDump dump of the object for error messages. + * + * @throws InvalidTimeZoneException + * + * @return CarbonTimeZone|false + */ + protected static function safeCreateDateTimeZone($object, $objectDump = null) + { + return CarbonTimeZone::instance($object, $objectDump); + } + + /** + * Get the TimeZone associated with the Carbon instance (as CarbonTimeZone). + * + * @return CarbonTimeZone + * + * @link https://php.net/manual/en/datetime.gettimezone.php + */ + #[ReturnTypeWillChange] + public function getTimezone() + { + return CarbonTimeZone::instance(parent::getTimezone()); + } + + /** + * List of minimum and maximums for each unit. + * + * @param int $daysInMonth + * + * @return array + */ + protected static function getRangesByUnit(int $daysInMonth = 31): array + { + return [ + // @call roundUnit + 'year' => [1, 9999], + // @call roundUnit + 'month' => [1, static::MONTHS_PER_YEAR], + // @call roundUnit + 'day' => [1, $daysInMonth], + // @call roundUnit + 'hour' => [0, static::HOURS_PER_DAY - 1], + // @call roundUnit + 'minute' => [0, static::MINUTES_PER_HOUR - 1], + // @call roundUnit + 'second' => [0, static::SECONDS_PER_MINUTE - 1], + ]; + } + + /** + * Get a copy of the instance. + * + * @return static + */ + public function copy() + { + return clone $this; + } + + /** + * @alias copy + * + * Get a copy of the instance. + * + * @return static + */ + public function clone() + { + return clone $this; + } + + /** + * Clone the current instance if it's mutable. + * + * This method is convenient to ensure you don't mutate the initial object + * but avoid to make a useless copy of it if it's already immutable. + * + * @return static + */ + public function avoidMutation(): self + { + if ($this instanceof DateTimeImmutable) { + return $this; + } + + return clone $this; + } + + /** + * Returns a present instance in the same timezone. + * + * @return static + */ + public function nowWithSameTz() + { + return static::now($this->getTimezone()); + } + + /** + * Throws an exception if the given object is not a DateTime and does not implement DateTimeInterface. + * + * @param mixed $date + * @param string|array $other + * + * @throws InvalidTypeException + */ + protected static function expectDateTime($date, $other = []) + { + $message = 'Expected '; + foreach ((array) $other as $expect) { + $message .= "$expect, "; + } + + if (!$date instanceof DateTime && !$date instanceof DateTimeInterface) { + throw new InvalidTypeException( + $message.'DateTime or DateTimeInterface, '. + (\is_object($date) ? \get_class($date) : \gettype($date)).' given' + ); + } + } + + /** + * Return the Carbon instance passed through, a now instance in the same timezone + * if null given or parse the input if string given. + * + * @param Carbon|DateTimeInterface|string|null $date + * + * @return static + */ + protected function resolveCarbon($date = null) + { + if (!$date) { + return $this->nowWithSameTz(); + } + + if (\is_string($date)) { + return static::parse($date, $this->getTimezone()); + } + + static::expectDateTime($date, ['null', 'string']); + + return $date instanceof self ? $date : static::instance($date); + } + + /** + * Return the Carbon instance passed through, a now instance in UTC + * if null given or parse the input if string given (using current timezone + * then switching to UTC). + * + * @param Carbon|DateTimeInterface|string|null $date + * + * @return static + */ + protected function resolveUTC($date = null): self + { + if (!$date) { + return static::now('UTC'); + } + + if (\is_string($date)) { + return static::parse($date, $this->getTimezone())->utc(); + } + + static::expectDateTime($date, ['null', 'string']); + + return $date instanceof self ? $date : static::instance($date)->utc(); + } + + /** + * Return the Carbon instance passed through, a now instance in the same timezone + * if null given or parse the input if string given. + * + * @param Carbon|\Carbon\CarbonPeriod|\Carbon\CarbonInterval|\DateInterval|\DatePeriod|DateTimeInterface|string|null $date + * + * @return static + */ + public function carbonize($date = null) + { + if ($date instanceof DateInterval) { + return $this->avoidMutation()->add($date); + } + + if ($date instanceof DatePeriod || $date instanceof CarbonPeriod) { + $date = $date->getStartDate(); + } + + return $this->resolveCarbon($date); + } + + /////////////////////////////////////////////////////////////////// + ///////////////////////// GETTERS AND SETTERS ///////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Get a part of the Carbon object + * + * @param string $name + * + * @throws UnknownGetterException + * + * @return string|int|bool|DateTimeZone|null + */ + public function __get($name) + { + return $this->get($name); + } + + /** + * Get a part of the Carbon object + * + * @param string $name + * + * @throws UnknownGetterException + * + * @return string|int|bool|DateTimeZone|null + */ + public function get($name) + { + static $formats = [ + // @property int + 'year' => 'Y', + // @property int + 'yearIso' => 'o', + // @property int + // @call isSameUnit + 'month' => 'n', + // @property int + 'day' => 'j', + // @property int + 'hour' => 'G', + // @property int + 'minute' => 'i', + // @property int + 'second' => 's', + // @property int + 'micro' => 'u', + // @property int + 'microsecond' => 'u', + // @property-read int 0 (for Sunday) through 6 (for Saturday) + 'dayOfWeek' => 'w', + // @property-read int 1 (for Monday) through 7 (for Sunday) + 'dayOfWeekIso' => 'N', + // @property-read int ISO-8601 week number of year, weeks starting on Monday + 'weekOfYear' => 'W', + // @property-read int number of days in the given month + 'daysInMonth' => 't', + // @property int|float|string seconds since the Unix Epoch + 'timestamp' => 'U', + // @property-read string "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark) + 'latinMeridiem' => 'a', + // @property-read string "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark) + 'latinUpperMeridiem' => 'A', + // @property string the day of week in English + 'englishDayOfWeek' => 'l', + // @property string the abbreviated day of week in English + 'shortEnglishDayOfWeek' => 'D', + // @property string the month in English + 'englishMonth' => 'F', + // @property string the abbreviated month in English + 'shortEnglishMonth' => 'M', + // @property string the day of week in current locale LC_TIME + // @deprecated + // reason: It uses OS language package and strftime() which is deprecated since PHP 8.1. + // replacement: Use ->isoFormat('MMM') instead. + // since: 2.55.0 + 'localeDayOfWeek' => '%A', + // @property string the abbreviated day of week in current locale LC_TIME + // @deprecated + // reason: It uses OS language package and strftime() which is deprecated since PHP 8.1. + // replacement: Use ->isoFormat('dddd') instead. + // since: 2.55.0 + 'shortLocaleDayOfWeek' => '%a', + // @property string the month in current locale LC_TIME + // @deprecated + // reason: It uses OS language package and strftime() which is deprecated since PHP 8.1. + // replacement: Use ->isoFormat('ddd') instead. + // since: 2.55.0 + 'localeMonth' => '%B', + // @property string the abbreviated month in current locale LC_TIME + // @deprecated + // reason: It uses OS language package and strftime() which is deprecated since PHP 8.1. + // replacement: Use ->isoFormat('MMMM') instead. + // since: 2.55.0 + 'shortLocaleMonth' => '%b', + // @property-read string $timezoneAbbreviatedName the current timezone abbreviated name + 'timezoneAbbreviatedName' => 'T', + // @property-read string $tzAbbrName alias of $timezoneAbbreviatedName + 'tzAbbrName' => 'T', + ]; + + switch (true) { + case isset($formats[$name]): + $format = $formats[$name]; + $method = str_starts_with($format, '%') ? 'formatLocalized' : 'rawFormat'; + $value = $this->$method($format); + + return is_numeric($value) ? (int) $value : $value; + + // @property-read string long name of weekday translated according to Carbon locale, in english if no translation available for current language + case $name === 'dayName': + return $this->getTranslatedDayName(); + // @property-read string short name of weekday translated according to Carbon locale, in english if no translation available for current language + case $name === 'shortDayName': + return $this->getTranslatedShortDayName(); + // @property-read string very short name of weekday translated according to Carbon locale, in english if no translation available for current language + case $name === 'minDayName': + return $this->getTranslatedMinDayName(); + // @property-read string long name of month translated according to Carbon locale, in english if no translation available for current language + case $name === 'monthName': + return $this->getTranslatedMonthName(); + // @property-read string short name of month translated according to Carbon locale, in english if no translation available for current language + case $name === 'shortMonthName': + return $this->getTranslatedShortMonthName(); + // @property-read string lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + case $name === 'meridiem': + return $this->meridiem(true); + // @property-read string uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language + case $name === 'upperMeridiem': + return $this->meridiem(); + // @property-read int current hour from 1 to 24 + case $name === 'noZeroHour': + return $this->hour ?: 24; + // @property int + case $name === 'milliseconds': + // @property int + case $name === 'millisecond': + // @property int + case $name === 'milli': + return (int) floor(((int) $this->rawFormat('u')) / 1000); + + // @property int 1 through 53 + case $name === 'week': + return (int) $this->week(); + + // @property int 1 through 53 + case $name === 'isoWeek': + return (int) $this->isoWeek(); + + // @property int year according to week format + case $name === 'weekYear': + return (int) $this->weekYear(); + + // @property int year according to ISO week format + case $name === 'isoWeekYear': + return (int) $this->isoWeekYear(); + + // @property-read int 51 through 53 + case $name === 'weeksInYear': + return $this->weeksInYear(); + + // @property-read int 51 through 53 + case $name === 'isoWeeksInYear': + return $this->isoWeeksInYear(); + + // @property-read int 1 through 5 + case $name === 'weekOfMonth': + return (int) ceil($this->day / static::DAYS_PER_WEEK); + + // @property-read int 1 through 5 + case $name === 'weekNumberInMonth': + return (int) ceil(($this->day + $this->avoidMutation()->startOfMonth()->dayOfWeekIso - 1) / static::DAYS_PER_WEEK); + + // @property-read int 0 through 6 + case $name === 'firstWeekDay': + return $this->localTranslator ? ($this->getTranslationMessage('first_day_of_week') ?? 0) : static::getWeekStartsAt(); + + // @property-read int 0 through 6 + case $name === 'lastWeekDay': + return $this->localTranslator ? (($this->getTranslationMessage('first_day_of_week') ?? 0) + static::DAYS_PER_WEEK - 1) % static::DAYS_PER_WEEK : static::getWeekEndsAt(); + + // @property int 1 through 366 + case $name === 'dayOfYear': + return 1 + (int) ($this->rawFormat('z')); + + // @property-read int 365 or 366 + case $name === 'daysInYear': + return $this->isLeapYear() ? 366 : 365; + + // @property int does a diffInYears() with default parameters + case $name === 'age': + return $this->diffInYears(); + + // @property-read int the quarter of this instance, 1 - 4 + // @call isSameUnit + case $name === 'quarter': + return (int) ceil($this->month / static::MONTHS_PER_QUARTER); + + // @property-read int the decade of this instance + // @call isSameUnit + case $name === 'decade': + return (int) ceil($this->year / static::YEARS_PER_DECADE); + + // @property-read int the century of this instance + // @call isSameUnit + case $name === 'century': + $factor = 1; + $year = $this->year; + if ($year < 0) { + $year = -$year; + $factor = -1; + } + + return (int) ($factor * ceil($year / static::YEARS_PER_CENTURY)); + + // @property-read int the millennium of this instance + // @call isSameUnit + case $name === 'millennium': + $factor = 1; + $year = $this->year; + if ($year < 0) { + $year = -$year; + $factor = -1; + } + + return (int) ($factor * ceil($year / static::YEARS_PER_MILLENNIUM)); + + // @property int the timezone offset in seconds from UTC + case $name === 'offset': + return $this->getOffset(); + + // @property int the timezone offset in minutes from UTC + case $name === 'offsetMinutes': + return $this->getOffset() / static::SECONDS_PER_MINUTE; + + // @property int the timezone offset in hours from UTC + case $name === 'offsetHours': + return $this->getOffset() / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR; + + // @property-read bool daylight savings time indicator, true if DST, false otherwise + case $name === 'dst': + return $this->rawFormat('I') === '1'; + + // @property-read bool checks if the timezone is local, true if local, false otherwise + case $name === 'local': + return $this->getOffset() === $this->avoidMutation()->setTimezone(date_default_timezone_get())->getOffset(); + + // @property-read bool checks if the timezone is UTC, true if UTC, false otherwise + case $name === 'utc': + return $this->getOffset() === 0; + + // @property CarbonTimeZone $timezone the current timezone + // @property CarbonTimeZone $tz alias of $timezone + case $name === 'timezone' || $name === 'tz': + return CarbonTimeZone::instance($this->getTimezone()); + + // @property-read string $timezoneName the current timezone name + // @property-read string $tzName alias of $timezoneName + case $name === 'timezoneName' || $name === 'tzName': + return $this->getTimezone()->getName(); + + // @property-read string locale of the current instance + case $name === 'locale': + return $this->getTranslatorLocale(); + + default: + $macro = $this->getLocalMacro('get'.ucfirst($name)); + + if ($macro) { + return $this->executeCallableWithContext($macro); + } + + throw new UnknownGetterException($name); + } + } + + /** + * Check if an attribute exists on the object + * + * @param string $name + * + * @return bool + */ + public function __isset($name) + { + try { + $this->__get($name); + } catch (UnknownGetterException | ReflectionException $e) { + return false; + } + + return true; + } + + /** + * Set a part of the Carbon object + * + * @param string $name + * @param string|int|DateTimeZone $value + * + * @throws UnknownSetterException|ReflectionException + * + * @return void + */ + public function __set($name, $value) + { + if ($this->constructedObjectId === spl_object_hash($this)) { + $this->set($name, $value); + + return; + } + + $this->$name = $value; + } + + /** + * Set a part of the Carbon object + * + * @param string|array $name + * @param string|int|DateTimeZone $value + * + * @throws ImmutableException|UnknownSetterException + * + * @return $this + */ + public function set($name, $value = null) + { + if ($this->isImmutable()) { + throw new ImmutableException(sprintf('%s class', static::class)); + } + + if (\is_array($name)) { + foreach ($name as $key => $value) { + $this->set($key, $value); + } + + return $this; + } + + switch ($name) { + case 'milliseconds': + case 'millisecond': + case 'milli': + case 'microseconds': + case 'microsecond': + case 'micro': + if (str_starts_with($name, 'milli')) { + $value *= 1000; + } + + while ($value < 0) { + $this->subSecond(); + $value += static::MICROSECONDS_PER_SECOND; + } + + while ($value >= static::MICROSECONDS_PER_SECOND) { + $this->addSecond(); + $value -= static::MICROSECONDS_PER_SECOND; + } + + $this->modify($this->rawFormat('H:i:s.').str_pad((string) round($value), 6, '0', STR_PAD_LEFT)); + + break; + + case 'year': + case 'month': + case 'day': + case 'hour': + case 'minute': + case 'second': + [$year, $month, $day, $hour, $minute, $second] = array_map('intval', explode('-', $this->rawFormat('Y-n-j-G-i-s'))); + $$name = $value; + $this->setDateTime($year, $month, $day, $hour, $minute, $second); + + break; + + case 'week': + $this->week($value); + + break; + + case 'isoWeek': + $this->isoWeek($value); + + break; + + case 'weekYear': + $this->weekYear($value); + + break; + + case 'isoWeekYear': + $this->isoWeekYear($value); + + break; + + case 'dayOfYear': + $this->addDays($value - $this->dayOfYear); + + break; + + case 'timestamp': + $this->setTimestamp($value); + + break; + + case 'offset': + $this->setTimezone(static::safeCreateDateTimeZone($value / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR)); + + break; + + case 'offsetMinutes': + $this->setTimezone(static::safeCreateDateTimeZone($value / static::MINUTES_PER_HOUR)); + + break; + + case 'offsetHours': + $this->setTimezone(static::safeCreateDateTimeZone($value)); + + break; + + case 'timezone': + case 'tz': + $this->setTimezone($value); + + break; + + default: + $macro = $this->getLocalMacro('set'.ucfirst($name)); + + if ($macro) { + $this->executeCallableWithContext($macro, $value); + + break; + } + + if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) { + throw new UnknownSetterException($name); + } + + $this->$name = $value; + } + + return $this; + } + + protected function getTranslatedFormByRegExp($baseKey, $keySuffix, $context, $subKey, $defaultValue) + { + $key = $baseKey.$keySuffix; + $standaloneKey = "{$key}_standalone"; + $baseTranslation = $this->getTranslationMessage($key); + + if ($baseTranslation instanceof Closure) { + return $baseTranslation($this, $context, $subKey) ?: $defaultValue; + } + + if ( + $this->getTranslationMessage("$standaloneKey.$subKey") && + (!$context || (($regExp = $this->getTranslationMessage("{$baseKey}_regexp")) && !preg_match($regExp, $context))) + ) { + $key = $standaloneKey; + } + + return $this->getTranslationMessage("$key.$subKey", null, $defaultValue); + } + + /** + * Get the translation of the current week day name (with context for languages with multiple forms). + * + * @param string|null $context whole format string + * @param string $keySuffix "", "_short" or "_min" + * @param string|null $defaultValue default value if translation missing + * + * @return string + */ + public function getTranslatedDayName($context = null, $keySuffix = '', $defaultValue = null) + { + return $this->getTranslatedFormByRegExp('weekdays', $keySuffix, $context, $this->dayOfWeek, $defaultValue ?: $this->englishDayOfWeek); + } + + /** + * Get the translation of the current short week day name (with context for languages with multiple forms). + * + * @param string|null $context whole format string + * + * @return string + */ + public function getTranslatedShortDayName($context = null) + { + return $this->getTranslatedDayName($context, '_short', $this->shortEnglishDayOfWeek); + } + + /** + * Get the translation of the current abbreviated week day name (with context for languages with multiple forms). + * + * @param string|null $context whole format string + * + * @return string + */ + public function getTranslatedMinDayName($context = null) + { + return $this->getTranslatedDayName($context, '_min', $this->shortEnglishDayOfWeek); + } + + /** + * Get the translation of the current month day name (with context for languages with multiple forms). + * + * @param string|null $context whole format string + * @param string $keySuffix "" or "_short" + * @param string|null $defaultValue default value if translation missing + * + * @return string + */ + public function getTranslatedMonthName($context = null, $keySuffix = '', $defaultValue = null) + { + return $this->getTranslatedFormByRegExp('months', $keySuffix, $context, $this->month - 1, $defaultValue ?: $this->englishMonth); + } + + /** + * Get the translation of the current short month day name (with context for languages with multiple forms). + * + * @param string|null $context whole format string + * + * @return string + */ + public function getTranslatedShortMonthName($context = null) + { + return $this->getTranslatedMonthName($context, '_short', $this->shortEnglishMonth); + } + + /** + * Get/set the day of year. + * + * @param int|null $value new value for day of year if using as setter. + * + * @return static|int + */ + public function dayOfYear($value = null) + { + $dayOfYear = $this->dayOfYear; + + return $value === null ? $dayOfYear : $this->addDays($value - $dayOfYear); + } + + /** + * Get/set the weekday from 0 (Sunday) to 6 (Saturday). + * + * @param int|null $value new value for weekday if using as setter. + * + * @return static|int + */ + public function weekday($value = null) + { + if ($value === null) { + return $this->dayOfWeek; + } + + $firstDay = (int) ($this->getTranslationMessage('first_day_of_week') ?? 0); + $dayOfWeek = ($this->dayOfWeek + 7 - $firstDay) % 7; + + return $this->addDays((($value + 7 - $firstDay) % 7) - $dayOfWeek); + } + + /** + * Get/set the ISO weekday from 1 (Monday) to 7 (Sunday). + * + * @param int|null $value new value for weekday if using as setter. + * + * @return static|int + */ + public function isoWeekday($value = null) + { + $dayOfWeekIso = $this->dayOfWeekIso; + + return $value === null ? $dayOfWeekIso : $this->addDays($value - $dayOfWeekIso); + } + + /** + * Return the number of days since the start of the week (using the current locale or the first parameter + * if explicitly given). + * + * @param int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week, + * if not provided, start of week is inferred from the locale + * (Sunday for en_US, Monday for de_DE, etc.) + * + * @return int + */ + public function getDaysFromStartOfWeek(int $weekStartsAt = null): int + { + $firstDay = (int) ($weekStartsAt ?? $this->getTranslationMessage('first_day_of_week') ?? 0); + + return ($this->dayOfWeek + 7 - $firstDay) % 7; + } + + /** + * Set the day (keeping the current time) to the start of the week + the number of days passed as the first + * parameter. First day of week is driven by the locale unless explicitly set with the second parameter. + * + * @param int $numberOfDays number of days to add after the start of the current week + * @param int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week, + * if not provided, start of week is inferred from the locale + * (Sunday for en_US, Monday for de_DE, etc.) + * + * @return static + */ + public function setDaysFromStartOfWeek(int $numberOfDays, int $weekStartsAt = null) + { + return $this->addDays($numberOfDays - $this->getDaysFromStartOfWeek($weekStartsAt)); + } + + /** + * Set any unit to a new value without overflowing current other unit given. + * + * @param string $valueUnit unit name to modify + * @param int $value new value for the input unit + * @param string $overflowUnit unit name to not overflow + * + * @return static + */ + public function setUnitNoOverflow($valueUnit, $value, $overflowUnit) + { + try { + $original = $this->avoidMutation(); + /** @var static $date */ + $date = $this->$valueUnit($value); + $end = $original->avoidMutation()->endOf($overflowUnit); + $start = $original->avoidMutation()->startOf($overflowUnit); + if ($date < $start) { + $date = $date->setDateTimeFrom($start); + } elseif ($date > $end) { + $date = $date->setDateTimeFrom($end); + } + + return $date; + } catch (BadMethodCallException | ReflectionException $exception) { + throw new UnknownUnitException($valueUnit, 0, $exception); + } + } + + /** + * Add any unit to a new value without overflowing current other unit given. + * + * @param string $valueUnit unit name to modify + * @param int $value amount to add to the input unit + * @param string $overflowUnit unit name to not overflow + * + * @return static + */ + public function addUnitNoOverflow($valueUnit, $value, $overflowUnit) + { + return $this->setUnitNoOverflow($valueUnit, $this->$valueUnit + $value, $overflowUnit); + } + + /** + * Subtract any unit to a new value without overflowing current other unit given. + * + * @param string $valueUnit unit name to modify + * @param int $value amount to subtract to the input unit + * @param string $overflowUnit unit name to not overflow + * + * @return static + */ + public function subUnitNoOverflow($valueUnit, $value, $overflowUnit) + { + return $this->setUnitNoOverflow($valueUnit, $this->$valueUnit - $value, $overflowUnit); + } + + /** + * Returns the minutes offset to UTC if no arguments passed, else set the timezone with given minutes shift passed. + * + * @param int|null $minuteOffset + * + * @return int|static + */ + public function utcOffset(int $minuteOffset = null) + { + if (\func_num_args() < 1) { + return $this->offsetMinutes; + } + + return $this->setTimezone(CarbonTimeZone::createFromMinuteOffset($minuteOffset)); + } + + /** + * Set the date with gregorian year, month and day numbers. + * + * @see https://php.net/manual/en/datetime.setdate.php + * + * @param int $year + * @param int $month + * @param int $day + * + * @return static + */ + #[ReturnTypeWillChange] + public function setDate($year, $month, $day) + { + return parent::setDate((int) $year, (int) $month, (int) $day); + } + + /** + * Set a date according to the ISO 8601 standard - using weeks and day offsets rather than specific dates. + * + * @see https://php.net/manual/en/datetime.setisodate.php + * + * @param int $year + * @param int $week + * @param int $day + * + * @return static + */ + #[ReturnTypeWillChange] + public function setISODate($year, $week, $day = 1) + { + return parent::setISODate((int) $year, (int) $week, (int) $day); + } + + /** + * Set the date and time all together. + * + * @param int $year + * @param int $month + * @param int $day + * @param int $hour + * @param int $minute + * @param int $second + * @param int $microseconds + * + * @return static + */ + public function setDateTime($year, $month, $day, $hour, $minute, $second = 0, $microseconds = 0) + { + return $this->setDate($year, $month, $day)->setTime((int) $hour, (int) $minute, (int) $second, (int) $microseconds); + } + + /** + * Resets the current time of the DateTime object to a different time. + * + * @see https://php.net/manual/en/datetime.settime.php + * + * @param int $hour + * @param int $minute + * @param int $second + * @param int $microseconds + * + * @return static + */ + #[ReturnTypeWillChange] + public function setTime($hour, $minute, $second = 0, $microseconds = 0) + { + return parent::setTime((int) $hour, (int) $minute, (int) $second, (int) $microseconds); + } + + /** + * Set the instance's timestamp. + * + * Timestamp input can be given as int, float or a string containing one or more numbers. + * + * @param float|int|string $unixTimestamp + * + * @return static + */ + #[ReturnTypeWillChange] + public function setTimestamp($unixTimestamp) + { + [$timestamp, $microseconds] = self::getIntegerAndDecimalParts($unixTimestamp); + + return parent::setTimestamp((int) $timestamp)->setMicroseconds((int) $microseconds); + } + + /** + * Set the time by time string. + * + * @param string $time + * + * @return static + */ + public function setTimeFromTimeString($time) + { + if (!str_contains($time, ':')) { + $time .= ':0'; + } + + return $this->modify($time); + } + + /** + * @alias setTimezone + * + * @param DateTimeZone|string $value + * + * @return static + */ + public function timezone($value) + { + return $this->setTimezone($value); + } + + /** + * Set the timezone or returns the timezone name if no arguments passed. + * + * @param DateTimeZone|string $value + * + * @return static|string + */ + public function tz($value = null) + { + if (\func_num_args() < 1) { + return $this->tzName; + } + + return $this->setTimezone($value); + } + + /** + * Set the instance's timezone from a string or object. + * + * @param DateTimeZone|string $value + * + * @return static + */ + #[ReturnTypeWillChange] + public function setTimezone($value) + { + $tz = static::safeCreateDateTimeZone($value); + + if ($tz === false && !self::isStrictModeEnabled()) { + $tz = new CarbonTimeZone(); + } + + return parent::setTimezone($tz); + } + + /** + * Set the instance's timezone from a string or object and add/subtract the offset difference. + * + * @param DateTimeZone|string $value + * + * @return static + */ + public function shiftTimezone($value) + { + $dateTimeString = $this->format('Y-m-d H:i:s.u'); + + return $this + ->setTimezone($value) + ->modify($dateTimeString); + } + + /** + * Set the instance's timezone to UTC. + * + * @return static + */ + public function utc() + { + return $this->setTimezone('UTC'); + } + + /** + * Set the year, month, and date for this instance to that of the passed instance. + * + * @param Carbon|DateTimeInterface $date now if null + * + * @return static + */ + public function setDateFrom($date = null) + { + $date = $this->resolveCarbon($date); + + return $this->setDate($date->year, $date->month, $date->day); + } + + /** + * Set the hour, minute, second and microseconds for this instance to that of the passed instance. + * + * @param Carbon|DateTimeInterface $date now if null + * + * @return static + */ + public function setTimeFrom($date = null) + { + $date = $this->resolveCarbon($date); + + return $this->setTime($date->hour, $date->minute, $date->second, $date->microsecond); + } + + /** + * Set the date and time for this instance to that of the passed instance. + * + * @param Carbon|DateTimeInterface $date + * + * @return static + */ + public function setDateTimeFrom($date = null) + { + $date = $this->resolveCarbon($date); + + return $this->modify($date->rawFormat('Y-m-d H:i:s.u')); + } + + /** + * Get the days of the week + * + * @return array + */ + public static function getDays() + { + return static::$days; + } + + /////////////////////////////////////////////////////////////////// + /////////////////////// WEEK SPECIAL DAYS ///////////////////////// + /////////////////////////////////////////////////////////////////// + + private static function getFirstDayOfWeek(): int + { + return (int) static::getTranslationMessageWith( + static::getTranslator(), + 'first_day_of_week' + ); + } + + /** + * Get the first day of week + * + * @return int + */ + public static function getWeekStartsAt() + { + if (static::$weekStartsAt === static::WEEK_DAY_AUTO) { + return self::getFirstDayOfWeek(); + } + + return static::$weekStartsAt; + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * Use $weekEndsAt optional parameter instead when using endOfWeek method. You can also use the + * 'first_day_of_week' locale setting to change the start of week according to current locale + * selected and implicitly the end of week. + * + * Set the first day of week + * + * @param int|string $day week start day (or 'auto' to get the first day of week from Carbon::getLocale() culture). + * + * @return void + */ + public static function setWeekStartsAt($day) + { + static::$weekStartsAt = $day === static::WEEK_DAY_AUTO ? $day : max(0, (7 + $day) % 7); + } + + /** + * Get the last day of week + * + * @return int + */ + public static function getWeekEndsAt() + { + if (static::$weekStartsAt === static::WEEK_DAY_AUTO) { + return (int) (static::DAYS_PER_WEEK - 1 + self::getFirstDayOfWeek()) % static::DAYS_PER_WEEK; + } + + return static::$weekEndsAt; + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * Use $weekStartsAt optional parameter instead when using startOfWeek, floorWeek, ceilWeek + * or roundWeek method. You can also use the 'first_day_of_week' locale setting to change the + * start of week according to current locale selected and implicitly the end of week. + * + * Set the last day of week + * + * @param int|string $day week end day (or 'auto' to get the day before the first day of week + * from Carbon::getLocale() culture). + * + * @return void + */ + public static function setWeekEndsAt($day) + { + static::$weekEndsAt = $day === static::WEEK_DAY_AUTO ? $day : max(0, (7 + $day) % 7); + } + + /** + * Get weekend days + * + * @return array + */ + public static function getWeekendDays() + { + return static::$weekendDays; + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather consider week-end is always saturday and sunday, and if you have some custom + * week-end days to handle, give to those days an other name and create a macro for them: + * + * ``` + * Carbon::macro('isDayOff', function ($date) { + * return $date->isSunday() || $date->isMonday(); + * }); + * Carbon::macro('isNotDayOff', function ($date) { + * return !$date->isDayOff(); + * }); + * if ($someDate->isDayOff()) ... + * if ($someDate->isNotDayOff()) ... + * // Add 5 not-off days + * $count = 5; + * while ($someDate->isDayOff() || ($count-- > 0)) { + * $someDate->addDay(); + * } + * ``` + * + * Set weekend days + * + * @param array $days + * + * @return void + */ + public static function setWeekendDays($days) + { + static::$weekendDays = $days; + } + + /** + * Determine if a time string will produce a relative date. + * + * @param string $time + * + * @return bool true if time match a relative date, false if absolute or invalid time string + */ + public static function hasRelativeKeywords($time) + { + if (!$time || strtotime($time) === false) { + return false; + } + + $date1 = new DateTime('2000-01-01T00:00:00Z'); + $date1->modify($time); + $date2 = new DateTime('2001-12-25T00:00:00Z'); + $date2->modify($time); + + return $date1 != $date2; + } + + /////////////////////////////////////////////////////////////////// + /////////////////////// STRING FORMATTING ///////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use UTF-8 language packages on every machine. + * + * Set if UTF8 will be used for localized date/time. + * + * @param bool $utf8 + */ + public static function setUtf8($utf8) + { + static::$utf8 = $utf8; + } + + /** + * Format the instance with the current locale. You can set the current + * locale using setlocale() https://php.net/setlocale. + * + * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1. + * Use ->isoFormat() instead. + * Deprecated since 2.55.0 + * + * @param string $format + * + * @return string + */ + public function formatLocalized($format) + { + // Check for Windows to find and replace the %e modifier correctly. + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + $format = preg_replace('#(?toDateTimeString()); + $formatted = ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) + ? strftime($format, $time) + : @strftime($format, $time); + + return static::$utf8 + ? ( + \function_exists('mb_convert_encoding') + ? mb_convert_encoding($formatted, 'UTF-8', mb_list_encodings()) + : utf8_encode($formatted) + ) + : $formatted; + } + + /** + * Returns list of locale formats for ISO formatting. + * + * @param string|null $locale current locale used if null + * + * @return array + */ + public function getIsoFormats($locale = null) + { + return [ + 'LT' => $this->getTranslationMessage('formats.LT', $locale, 'h:mm A'), + 'LTS' => $this->getTranslationMessage('formats.LTS', $locale, 'h:mm:ss A'), + 'L' => $this->getTranslationMessage('formats.L', $locale, 'MM/DD/YYYY'), + 'LL' => $this->getTranslationMessage('formats.LL', $locale, 'MMMM D, YYYY'), + 'LLL' => $this->getTranslationMessage('formats.LLL', $locale, 'MMMM D, YYYY h:mm A'), + 'LLLL' => $this->getTranslationMessage('formats.LLLL', $locale, 'dddd, MMMM D, YYYY h:mm A'), + 'l' => $this->getTranslationMessage('formats.l', $locale), + 'll' => $this->getTranslationMessage('formats.ll', $locale), + 'lll' => $this->getTranslationMessage('formats.lll', $locale), + 'llll' => $this->getTranslationMessage('formats.llll', $locale), + ]; + } + + /** + * Returns list of calendar formats for ISO formatting. + * + * @param string|null $locale current locale used if null + * + * @return array + */ + public function getCalendarFormats($locale = null) + { + return [ + 'sameDay' => $this->getTranslationMessage('calendar.sameDay', $locale, '[Today at] LT'), + 'nextDay' => $this->getTranslationMessage('calendar.nextDay', $locale, '[Tomorrow at] LT'), + 'nextWeek' => $this->getTranslationMessage('calendar.nextWeek', $locale, 'dddd [at] LT'), + 'lastDay' => $this->getTranslationMessage('calendar.lastDay', $locale, '[Yesterday at] LT'), + 'lastWeek' => $this->getTranslationMessage('calendar.lastWeek', $locale, '[Last] dddd [at] LT'), + 'sameElse' => $this->getTranslationMessage('calendar.sameElse', $locale, 'L'), + ]; + } + + /** + * Returns list of locale units for ISO formatting. + * + * @return array + */ + public static function getIsoUnits() + { + static $units = null; + + if ($units === null) { + $units = [ + 'OD' => ['getAltNumber', ['day']], + 'OM' => ['getAltNumber', ['month']], + 'OY' => ['getAltNumber', ['year']], + 'OH' => ['getAltNumber', ['hour']], + 'Oh' => ['getAltNumber', ['h']], + 'Om' => ['getAltNumber', ['minute']], + 'Os' => ['getAltNumber', ['second']], + 'D' => 'day', + 'DD' => ['rawFormat', ['d']], + 'Do' => ['ordinal', ['day', 'D']], + 'd' => 'dayOfWeek', + 'dd' => function (CarbonInterface $date, $originalFormat = null) { + return $date->getTranslatedMinDayName($originalFormat); + }, + 'ddd' => function (CarbonInterface $date, $originalFormat = null) { + return $date->getTranslatedShortDayName($originalFormat); + }, + 'dddd' => function (CarbonInterface $date, $originalFormat = null) { + return $date->getTranslatedDayName($originalFormat); + }, + 'DDD' => 'dayOfYear', + 'DDDD' => ['getPaddedUnit', ['dayOfYear', 3]], + 'DDDo' => ['ordinal', ['dayOfYear', 'DDD']], + 'e' => ['weekday', []], + 'E' => 'dayOfWeekIso', + 'H' => ['rawFormat', ['G']], + 'HH' => ['rawFormat', ['H']], + 'h' => ['rawFormat', ['g']], + 'hh' => ['rawFormat', ['h']], + 'k' => 'noZeroHour', + 'kk' => ['getPaddedUnit', ['noZeroHour']], + 'hmm' => ['rawFormat', ['gi']], + 'hmmss' => ['rawFormat', ['gis']], + 'Hmm' => ['rawFormat', ['Gi']], + 'Hmmss' => ['rawFormat', ['Gis']], + 'm' => 'minute', + 'mm' => ['rawFormat', ['i']], + 'a' => 'meridiem', + 'A' => 'upperMeridiem', + 's' => 'second', + 'ss' => ['getPaddedUnit', ['second']], + 'S' => function (CarbonInterface $date) { + return (string) floor($date->micro / 100000); + }, + 'SS' => function (CarbonInterface $date) { + return str_pad((string) floor($date->micro / 10000), 2, '0', STR_PAD_LEFT); + }, + 'SSS' => function (CarbonInterface $date) { + return str_pad((string) floor($date->micro / 1000), 3, '0', STR_PAD_LEFT); + }, + 'SSSS' => function (CarbonInterface $date) { + return str_pad((string) floor($date->micro / 100), 4, '0', STR_PAD_LEFT); + }, + 'SSSSS' => function (CarbonInterface $date) { + return str_pad((string) floor($date->micro / 10), 5, '0', STR_PAD_LEFT); + }, + 'SSSSSS' => ['getPaddedUnit', ['micro', 6]], + 'SSSSSSS' => function (CarbonInterface $date) { + return str_pad((string) floor($date->micro * 10), 7, '0', STR_PAD_LEFT); + }, + 'SSSSSSSS' => function (CarbonInterface $date) { + return str_pad((string) floor($date->micro * 100), 8, '0', STR_PAD_LEFT); + }, + 'SSSSSSSSS' => function (CarbonInterface $date) { + return str_pad((string) floor($date->micro * 1000), 9, '0', STR_PAD_LEFT); + }, + 'M' => 'month', + 'MM' => ['rawFormat', ['m']], + 'MMM' => function (CarbonInterface $date, $originalFormat = null) { + $month = $date->getTranslatedShortMonthName($originalFormat); + $suffix = $date->getTranslationMessage('mmm_suffix'); + if ($suffix && $month !== $date->monthName) { + $month .= $suffix; + } + + return $month; + }, + 'MMMM' => function (CarbonInterface $date, $originalFormat = null) { + return $date->getTranslatedMonthName($originalFormat); + }, + 'Mo' => ['ordinal', ['month', 'M']], + 'Q' => 'quarter', + 'Qo' => ['ordinal', ['quarter', 'M']], + 'G' => 'isoWeekYear', + 'GG' => ['getPaddedUnit', ['isoWeekYear']], + 'GGG' => ['getPaddedUnit', ['isoWeekYear', 3]], + 'GGGG' => ['getPaddedUnit', ['isoWeekYear', 4]], + 'GGGGG' => ['getPaddedUnit', ['isoWeekYear', 5]], + 'g' => 'weekYear', + 'gg' => ['getPaddedUnit', ['weekYear']], + 'ggg' => ['getPaddedUnit', ['weekYear', 3]], + 'gggg' => ['getPaddedUnit', ['weekYear', 4]], + 'ggggg' => ['getPaddedUnit', ['weekYear', 5]], + 'W' => 'isoWeek', + 'WW' => ['getPaddedUnit', ['isoWeek']], + 'Wo' => ['ordinal', ['isoWeek', 'W']], + 'w' => 'week', + 'ww' => ['getPaddedUnit', ['week']], + 'wo' => ['ordinal', ['week', 'w']], + 'x' => ['valueOf', []], + 'X' => 'timestamp', + 'Y' => 'year', + 'YY' => ['rawFormat', ['y']], + 'YYYY' => ['getPaddedUnit', ['year', 4]], + 'YYYYY' => ['getPaddedUnit', ['year', 5]], + 'YYYYYY' => function (CarbonInterface $date) { + return ($date->year < 0 ? '' : '+').$date->getPaddedUnit('year', 6); + }, + 'z' => ['rawFormat', ['T']], + 'zz' => 'tzName', + 'Z' => ['getOffsetString', []], + 'ZZ' => ['getOffsetString', ['']], + ]; + } + + return $units; + } + + /** + * Returns a unit of the instance padded with 0 by default or any other string if specified. + * + * @param string $unit Carbon unit name + * @param int $length Length of the output (2 by default) + * @param string $padString String to use for padding ("0" by default) + * @param int $padType Side(s) to pad (STR_PAD_LEFT by default) + * + * @return string + */ + public function getPaddedUnit($unit, $length = 2, $padString = '0', $padType = STR_PAD_LEFT) + { + return ($this->$unit < 0 ? '-' : '').str_pad((string) abs($this->$unit), $length, $padString, $padType); + } + + /** + * Return a property with its ordinal. + * + * @param string $key + * @param string|null $period + * + * @return string + */ + public function ordinal(string $key, ?string $period = null): string + { + $number = $this->$key; + $result = $this->translate('ordinal', [ + ':number' => $number, + ':period' => (string) $period, + ]); + + return (string) ($result === 'ordinal' ? $number : $result); + } + + /** + * Return the meridiem of the current time in the current locale. + * + * @param bool $isLower if true, returns lowercase variant if available in the current locale. + * + * @return string + */ + public function meridiem(bool $isLower = false): string + { + $hour = $this->hour; + $index = $hour < 12 ? 0 : 1; + + if ($isLower) { + $key = 'meridiem.'.($index + 2); + $result = $this->translate($key); + + if ($result !== $key) { + return $result; + } + } + + $key = "meridiem.$index"; + $result = $this->translate($key); + if ($result === $key) { + $result = $this->translate('meridiem', [ + ':hour' => $this->hour, + ':minute' => $this->minute, + ':isLower' => $isLower, + ]); + + if ($result === 'meridiem') { + return $isLower ? $this->latinMeridiem : $this->latinUpperMeridiem; + } + } elseif ($isLower) { + $result = mb_strtolower($result); + } + + return $result; + } + + /** + * Returns the alternative number for a given date property if available in the current locale. + * + * @param string $key date property + * + * @return string + */ + public function getAltNumber(string $key): string + { + return $this->translateNumber(\strlen($key) > 1 ? $this->$key : $this->rawFormat('h')); + } + + /** + * Format in the current language using ISO replacement patterns. + * + * @param string $format + * @param string|null $originalFormat provide context if a chunk has been passed alone + * + * @return string + */ + public function isoFormat(string $format, ?string $originalFormat = null): string + { + $result = ''; + $length = mb_strlen($format); + $originalFormat = $originalFormat ?: $format; + $inEscaped = false; + $formats = null; + $units = null; + + for ($i = 0; $i < $length; $i++) { + $char = mb_substr($format, $i, 1); + + if ($char === '\\') { + $result .= mb_substr($format, ++$i, 1); + + continue; + } + + if ($char === '[' && !$inEscaped) { + $inEscaped = true; + + continue; + } + + if ($char === ']' && $inEscaped) { + $inEscaped = false; + + continue; + } + + if ($inEscaped) { + $result .= $char; + + continue; + } + + $input = mb_substr($format, $i); + + if (preg_match('/^(LTS|LT|l{1,4}|L{1,4})/', $input, $match)) { + if ($formats === null) { + $formats = $this->getIsoFormats(); + } + + $code = $match[0]; + $sequence = $formats[$code] ?? preg_replace_callback( + '/MMMM|MM|DD|dddd/', + function ($code) { + return mb_substr($code[0], 1); + }, + $formats[strtoupper($code)] ?? '' + ); + $rest = mb_substr($format, $i + mb_strlen($code)); + $format = mb_substr($format, 0, $i).$sequence.$rest; + $length = mb_strlen($format); + $input = $sequence.$rest; + } + + if (preg_match('/^'.CarbonInterface::ISO_FORMAT_REGEXP.'/', $input, $match)) { + $code = $match[0]; + + if ($units === null) { + $units = static::getIsoUnits(); + } + + $sequence = $units[$code] ?? ''; + + if ($sequence instanceof Closure) { + $sequence = $sequence($this, $originalFormat); + } elseif (\is_array($sequence)) { + try { + $sequence = $this->{$sequence[0]}(...$sequence[1]); + } catch (ReflectionException | InvalidArgumentException | BadMethodCallException $e) { + $sequence = ''; + } + } elseif (\is_string($sequence)) { + $sequence = $this->$sequence ?? $code; + } + + $format = mb_substr($format, 0, $i).$sequence.mb_substr($format, $i + mb_strlen($code)); + $i += mb_strlen((string) $sequence) - 1; + $length = mb_strlen($format); + $char = $sequence; + } + + $result .= $char; + } + + return $result; + } + + /** + * List of replacements from date() format to isoFormat(). + * + * @return array + */ + public static function getFormatsToIsoReplacements() + { + static $replacements = null; + + if ($replacements === null) { + $replacements = [ + 'd' => true, + 'D' => 'ddd', + 'j' => true, + 'l' => 'dddd', + 'N' => true, + 'S' => function ($date) { + $day = $date->rawFormat('j'); + + return str_replace((string) $day, '', $date->isoFormat('Do')); + }, + 'w' => true, + 'z' => true, + 'W' => true, + 'F' => 'MMMM', + 'm' => true, + 'M' => 'MMM', + 'n' => true, + 't' => true, + 'L' => true, + 'o' => true, + 'Y' => true, + 'y' => true, + 'a' => 'a', + 'A' => 'A', + 'B' => true, + 'g' => true, + 'G' => true, + 'h' => true, + 'H' => true, + 'i' => true, + 's' => true, + 'u' => true, + 'v' => true, + 'E' => true, + 'I' => true, + 'O' => true, + 'P' => true, + 'Z' => true, + 'c' => true, + 'r' => true, + 'U' => true, + 'T' => true, + ]; + } + + return $replacements; + } + + /** + * Format as ->format() do (using date replacements patterns from https://php.net/manual/en/function.date.php) + * but translate words whenever possible (months, day names, etc.) using the current locale. + * + * @param string $format + * + * @return string + */ + public function translatedFormat(string $format): string + { + $replacements = static::getFormatsToIsoReplacements(); + $context = ''; + $isoFormat = ''; + $length = mb_strlen($format); + + for ($i = 0; $i < $length; $i++) { + $char = mb_substr($format, $i, 1); + + if ($char === '\\') { + $replacement = mb_substr($format, $i, 2); + $isoFormat .= $replacement; + $i++; + + continue; + } + + if (!isset($replacements[$char])) { + $replacement = preg_match('/^[A-Za-z]$/', $char) ? "\\$char" : $char; + $isoFormat .= $replacement; + $context .= $replacement; + + continue; + } + + $replacement = $replacements[$char]; + + if ($replacement === true) { + static $contextReplacements = null; + + if ($contextReplacements === null) { + $contextReplacements = [ + 'm' => 'MM', + 'd' => 'DD', + 't' => 'D', + 'j' => 'D', + 'N' => 'e', + 'w' => 'e', + 'n' => 'M', + 'o' => 'YYYY', + 'Y' => 'YYYY', + 'y' => 'YY', + 'g' => 'h', + 'G' => 'H', + 'h' => 'hh', + 'H' => 'HH', + 'i' => 'mm', + 's' => 'ss', + ]; + } + + $isoFormat .= '['.$this->rawFormat($char).']'; + $context .= $contextReplacements[$char] ?? ' '; + + continue; + } + + if ($replacement instanceof Closure) { + $replacement = '['.$replacement($this).']'; + $isoFormat .= $replacement; + $context .= $replacement; + + continue; + } + + $isoFormat .= $replacement; + $context .= $replacement; + } + + return $this->isoFormat($isoFormat, $context); + } + + /** + * Returns the offset hour and minute formatted with +/- and a given separator (":" by default). + * For example, if the time zone is 9 hours 30 minutes, you'll get "+09:30", with "@@" as first + * argument, "+09@@30", with "" as first argument, "+0930". Negative offset will return something + * like "-12:00". + * + * @param string $separator string to place between hours and minutes (":" by default) + * + * @return string + */ + public function getOffsetString($separator = ':') + { + $second = $this->getOffset(); + $symbol = $second < 0 ? '-' : '+'; + $minute = abs($second) / static::SECONDS_PER_MINUTE; + $hour = str_pad((string) floor($minute / static::MINUTES_PER_HOUR), 2, '0', STR_PAD_LEFT); + $minute = str_pad((string) (((int) $minute) % static::MINUTES_PER_HOUR), 2, '0', STR_PAD_LEFT); + + return "$symbol$hour$separator$minute"; + } + + protected static function executeStaticCallable($macro, ...$parameters) + { + return static::bindMacroContext(null, function () use (&$macro, &$parameters) { + if ($macro instanceof Closure) { + $boundMacro = @Closure::bind($macro, null, static::class); + + return ($boundMacro ?: $macro)(...$parameters); + } + + return $macro(...$parameters); + }); + } + + /** + * Dynamically handle calls to the class. + * + * @param string $method magic method name called + * @param array $parameters parameters list + * + * @throws BadMethodCallException + * + * @return mixed + */ + public static function __callStatic($method, $parameters) + { + if (!static::hasMacro($method)) { + foreach (static::getGenericMacros() as $callback) { + try { + return static::executeStaticCallable($callback, $method, ...$parameters); + } catch (BadMethodCallException $exception) { + continue; + } + } + if (static::isStrictModeEnabled()) { + throw new UnknownMethodException(sprintf('%s::%s', static::class, $method)); + } + + return null; + } + + return static::executeStaticCallable(static::$globalMacros[$method], ...$parameters); + } + + /** + * Set specified unit to new given value. + * + * @param string $unit year, month, day, hour, minute, second or microsecond + * @param int $value new value for given unit + * + * @return static + */ + public function setUnit($unit, $value = null) + { + $unit = static::singularUnit($unit); + $dateUnits = ['year', 'month', 'day']; + if (\in_array($unit, $dateUnits)) { + return $this->setDate(...array_map(function ($name) use ($unit, $value) { + return (int) ($name === $unit ? $value : $this->$name); + }, $dateUnits)); + } + + $units = ['hour', 'minute', 'second', 'micro']; + if ($unit === 'millisecond' || $unit === 'milli') { + $value *= 1000; + $unit = 'micro'; + } elseif ($unit === 'microsecond') { + $unit = 'micro'; + } + + return $this->setTime(...array_map(function ($name) use ($unit, $value) { + return (int) ($name === $unit ? $value : $this->$name); + }, $units)); + } + + /** + * Returns standardized singular of a given singular/plural unit name (in English). + * + * @param string $unit + * + * @return string + */ + public static function singularUnit(string $unit): string + { + $unit = rtrim(mb_strtolower($unit), 's'); + + if ($unit === 'centurie') { + return 'century'; + } + + if ($unit === 'millennia') { + return 'millennium'; + } + + return $unit; + } + + /** + * Returns standardized plural of a given singular/plural unit name (in English). + * + * @param string $unit + * + * @return string + */ + public static function pluralUnit(string $unit): string + { + $unit = rtrim(strtolower($unit), 's'); + + if ($unit === 'century') { + return 'centuries'; + } + + if ($unit === 'millennium' || $unit === 'millennia') { + return 'millennia'; + } + + return "{$unit}s"; + } + + protected function executeCallable($macro, ...$parameters) + { + if ($macro instanceof Closure) { + $boundMacro = @$macro->bindTo($this, static::class) ?: @$macro->bindTo(null, static::class); + + return ($boundMacro ?: $macro)(...$parameters); + } + + return $macro(...$parameters); + } + + protected function executeCallableWithContext($macro, ...$parameters) + { + return static::bindMacroContext($this, function () use (&$macro, &$parameters) { + return $this->executeCallable($macro, ...$parameters); + }); + } + + protected static function getGenericMacros() + { + foreach (static::$globalGenericMacros as $list) { + foreach ($list as $macro) { + yield $macro; + } + } + } + + /** + * Dynamically handle calls to the class. + * + * @param string $method magic method name called + * @param array $parameters parameters list + * + * @throws UnknownMethodException|BadMethodCallException|ReflectionException|Throwable + * + * @return mixed + */ + public function __call($method, $parameters) + { + $diffSizes = [ + // @mode diffForHumans + 'short' => true, + // @mode diffForHumans + 'long' => false, + ]; + $diffSyntaxModes = [ + // @call diffForHumans + 'Absolute' => CarbonInterface::DIFF_ABSOLUTE, + // @call diffForHumans + 'Relative' => CarbonInterface::DIFF_RELATIVE_AUTO, + // @call diffForHumans + 'RelativeToNow' => CarbonInterface::DIFF_RELATIVE_TO_NOW, + // @call diffForHumans + 'RelativeToOther' => CarbonInterface::DIFF_RELATIVE_TO_OTHER, + ]; + $sizePattern = implode('|', array_keys($diffSizes)); + $syntaxPattern = implode('|', array_keys($diffSyntaxModes)); + + if (preg_match("/^(?$sizePattern)(?$syntaxPattern)DiffForHumans$/", $method, $match)) { + $dates = array_filter($parameters, function ($parameter) { + return $parameter instanceof DateTimeInterface; + }); + $other = null; + + if (\count($dates)) { + $key = key($dates); + $other = current($dates); + array_splice($parameters, $key, 1); + } + + return $this->diffForHumans($other, $diffSyntaxModes[$match['syntax']], $diffSizes[$match['size']], ...$parameters); + } + + $roundedValue = $this->callRoundMethod($method, $parameters); + + if ($roundedValue !== null) { + return $roundedValue; + } + + $unit = rtrim($method, 's'); + + if (str_starts_with($unit, 'is')) { + $word = substr($unit, 2); + + if (\in_array($word, static::$days, true)) { + return $this->isDayOfWeek($word); + } + + switch ($word) { + // @call is Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.) + case 'Utc': + case 'UTC': + return $this->utc; + // @call is Check if the current instance has non-UTC timezone. + case 'Local': + return $this->local; + // @call is Check if the current instance is a valid date. + case 'Valid': + return $this->year !== 0; + // @call is Check if the current instance is in a daylight saving time. + case 'DST': + return $this->dst; + } + } + + $action = substr($unit, 0, 3); + $overflow = null; + + if ($action === 'set') { + $unit = strtolower(substr($unit, 3)); + } + + if (\in_array($unit, static::$units, true)) { + return $this->setUnit($unit, ...$parameters); + } + + if ($action === 'add' || $action === 'sub') { + $unit = substr($unit, 3); + + if (str_starts_with($unit, 'Real')) { + $unit = static::singularUnit(substr($unit, 4)); + + return $this->{"{$action}RealUnit"}($unit, ...$parameters); + } + + if (preg_match('/^(Month|Quarter|Year|Decade|Century|Centurie|Millennium|Millennia)s?(No|With|Without|WithNo)Overflow$/', $unit, $match)) { + $unit = $match[1]; + $overflow = $match[2] === 'With'; + } + + $unit = static::singularUnit($unit); + } + + if (static::isModifiableUnit($unit)) { + return $this->{"{$action}Unit"}($unit, $this->getMagicParameter($parameters, 0, 'value', 1), $overflow); + } + + $sixFirstLetters = substr($unit, 0, 6); + $factor = -1; + + if ($sixFirstLetters === 'isLast') { + $sixFirstLetters = 'isNext'; + $factor = 1; + } + + if ($sixFirstLetters === 'isNext') { + $lowerUnit = strtolower(substr($unit, 6)); + + if (static::isModifiableUnit($lowerUnit)) { + return $this->copy()->addUnit($lowerUnit, $factor, false)->isSameUnit($lowerUnit, ...$parameters); + } + } + + if ($sixFirstLetters === 'isSame') { + try { + return $this->isSameUnit(strtolower(substr($unit, 6)), ...$parameters); + } catch (BadComparisonUnitException $exception) { + // Try next + } + } + + if (str_starts_with($unit, 'isCurrent')) { + try { + return $this->isCurrentUnit(strtolower(substr($unit, 9))); + } catch (BadComparisonUnitException | BadMethodCallException $exception) { + // Try next + } + } + + if (str_ends_with($method, 'Until')) { + try { + $unit = static::singularUnit(substr($method, 0, -5)); + + return $this->range( + $this->getMagicParameter($parameters, 0, 'endDate', $this), + $this->getMagicParameter($parameters, 1, 'factor', 1), + $unit + ); + } catch (InvalidArgumentException $exception) { + // Try macros + } + } + + return static::bindMacroContext($this, function () use (&$method, &$parameters) { + $macro = $this->getLocalMacro($method); + + if (!$macro) { + foreach ([$this->localGenericMacros ?: [], static::getGenericMacros()] as $list) { + foreach ($list as $callback) { + try { + return $this->executeCallable($callback, $method, ...$parameters); + } catch (BadMethodCallException $exception) { + continue; + } + } + } + + if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) { + throw new UnknownMethodException($method); + } + + return null; + } + + return $this->executeCallable($macro, ...$parameters); + }); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedProperties.php b/vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedProperties.php new file mode 100644 index 0000000..5acc6f5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedProperties.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +trait DeprecatedProperties +{ + /** + * the day of week in current locale LC_TIME + * + * @var string + * + * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1. + * Use ->isoFormat('MMM') instead. + * Deprecated since 2.55.0 + */ + public $localeDayOfWeek; + + /** + * the abbreviated day of week in current locale LC_TIME + * + * @var string + * + * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1. + * Use ->isoFormat('dddd') instead. + * Deprecated since 2.55.0 + */ + public $shortLocaleDayOfWeek; + + /** + * the month in current locale LC_TIME + * + * @var string + * + * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1. + * Use ->isoFormat('ddd') instead. + * Deprecated since 2.55.0 + */ + public $localeMonth; + + /** + * the abbreviated month in current locale LC_TIME + * + * @var string + * + * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1. + * Use ->isoFormat('MMMM') instead. + * Deprecated since 2.55.0 + */ + public $shortLocaleMonth; +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Difference.php b/vendor/nesbot/carbon/src/Carbon/Traits/Difference.php new file mode 100644 index 0000000..ab5b65d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Difference.php @@ -0,0 +1,1182 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\Carbon; +use Carbon\CarbonImmutable; +use Carbon\CarbonInterface; +use Carbon\CarbonInterval; +use Carbon\CarbonPeriod; +use Carbon\Translator; +use Closure; +use DateInterval; +use DateTimeInterface; +use ReturnTypeWillChange; + +/** + * Trait Difference. + * + * Depends on the following methods: + * + * @method bool lessThan($date) + * @method static copy() + * @method static resolveCarbon($date = null) + * @method static Translator translator() + */ +trait Difference +{ + /** + * @codeCoverageIgnore + * + * @param CarbonInterval $diff + */ + protected static function fixNegativeMicroseconds(CarbonInterval $diff) + { + if ($diff->s !== 0 || $diff->i !== 0 || $diff->h !== 0 || $diff->d !== 0 || $diff->m !== 0 || $diff->y !== 0) { + $diff->f = (round($diff->f * 1000000) + 1000000) / 1000000; + $diff->s--; + + if ($diff->s < 0) { + $diff->s += 60; + $diff->i--; + + if ($diff->i < 0) { + $diff->i += 60; + $diff->h--; + + if ($diff->h < 0) { + $diff->h += 24; + $diff->d--; + + if ($diff->d < 0) { + $diff->d += 30; + $diff->m--; + + if ($diff->m < 0) { + $diff->m += 12; + $diff->y--; + } + } + } + } + } + + return; + } + + $diff->f *= -1; + $diff->invert(); + } + + /** + * @param DateInterval $diff + * @param bool $absolute + * + * @return CarbonInterval + */ + protected static function fixDiffInterval(DateInterval $diff, $absolute, array $skip = []) + { + $diff = CarbonInterval::instance($diff, $skip); + + // Work-around for https://bugs.php.net/bug.php?id=77145 + // @codeCoverageIgnoreStart + if ($diff->f > 0 && $diff->y === -1 && $diff->m === 11 && $diff->d >= 27 && $diff->h === 23 && $diff->i === 59 && $diff->s === 59) { + $diff->y = 0; + $diff->m = 0; + $diff->d = 0; + $diff->h = 0; + $diff->i = 0; + $diff->s = 0; + $diff->f = (1000000 - round($diff->f * 1000000)) / 1000000; + $diff->invert(); + } elseif ($diff->f < 0) { + static::fixNegativeMicroseconds($diff); + } + // @codeCoverageIgnoreEnd + + if ($absolute && $diff->invert) { + $diff->invert(); + } + + return $diff; + } + + /** + * Get the difference as a DateInterval instance. + * Return relative interval (negative if $absolute flag is not set to true and the given date is before + * current one). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return DateInterval + */ + #[ReturnTypeWillChange] + public function diff($date = null, $absolute = false) + { + $other = $this->resolveCarbon($date); + + // Work-around for https://bugs.php.net/bug.php?id=81458 + // It was initially introduced for https://bugs.php.net/bug.php?id=80998 + // The very specific case of 80998 was fixed in PHP 8.1beta3, but it introduced 81458 + // So we still need to keep this for now + // @codeCoverageIgnoreStart + if (version_compare(PHP_VERSION, '8.1.0-dev', '>=') && $other->tz !== $this->tz) { + $other = $other->avoidMutation()->tz($this->tz); + } + // @codeCoverageIgnoreEnd + + return parent::diff($other, (bool) $absolute); + } + + /** + * Get the difference as a CarbonInterval instance. + * Return relative interval (negative if $absolute flag is not set to true and the given date is before + * current one). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return CarbonInterval + */ + public function diffAsCarbonInterval($date = null, $absolute = true, array $skip = []) + { + return static::fixDiffInterval($this->diff($this->resolveCarbon($date), $absolute), $absolute, $skip); + } + + /** + * Get the difference in years + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInYears($date = null, $absolute = true) + { + return (int) $this->diff($this->resolveCarbon($date), $absolute)->format('%r%y'); + } + + /** + * Get the difference in quarters rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInQuarters($date = null, $absolute = true) + { + return (int) ($this->diffInMonths($date, $absolute) / static::MONTHS_PER_QUARTER); + } + + /** + * Get the difference in months rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInMonths($date = null, $absolute = true) + { + $date = $this->resolveCarbon($date)->avoidMutation()->tz($this->tz); + + [$yearStart, $monthStart, $dayStart] = explode('-', $this->format('Y-m-dHisu')); + [$yearEnd, $monthEnd, $dayEnd] = explode('-', $date->format('Y-m-dHisu')); + + $diff = (((int) $yearEnd) - ((int) $yearStart)) * static::MONTHS_PER_YEAR + + ((int) $monthEnd) - ((int) $monthStart); + + if ($diff > 0) { + $diff -= ($dayStart > $dayEnd ? 1 : 0); + } elseif ($diff < 0) { + $diff += ($dayStart < $dayEnd ? 1 : 0); + } + + return $absolute ? abs($diff) : $diff; + } + + /** + * Get the difference in weeks rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInWeeks($date = null, $absolute = true) + { + return (int) ($this->diffInDays($date, $absolute) / static::DAYS_PER_WEEK); + } + + /** + * Get the difference in days rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInDays($date = null, $absolute = true) + { + return $this->getIntervalDayDiff($this->diff($this->resolveCarbon($date), $absolute)); + } + + /** + * Get the difference in days using a filter closure rounded down. + * + * @param Closure $callback + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInDaysFiltered(Closure $callback, $date = null, $absolute = true) + { + return $this->diffFiltered(CarbonInterval::day(), $callback, $date, $absolute); + } + + /** + * Get the difference in hours using a filter closure rounded down. + * + * @param Closure $callback + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInHoursFiltered(Closure $callback, $date = null, $absolute = true) + { + return $this->diffFiltered(CarbonInterval::hour(), $callback, $date, $absolute); + } + + /** + * Get the difference by the given interval using a filter closure. + * + * @param CarbonInterval $ci An interval to traverse by + * @param Closure $callback + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffFiltered(CarbonInterval $ci, Closure $callback, $date = null, $absolute = true) + { + $start = $this; + $end = $this->resolveCarbon($date); + $inverse = false; + + if ($end < $start) { + $start = $end; + $end = $this; + $inverse = true; + } + + $options = CarbonPeriod::EXCLUDE_END_DATE | ($this->isMutable() ? 0 : CarbonPeriod::IMMUTABLE); + $diff = $ci->toPeriod($start, $end, $options)->filter($callback)->count(); + + return $inverse && !$absolute ? -$diff : $diff; + } + + /** + * Get the difference in weekdays rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInWeekdays($date = null, $absolute = true) + { + return $this->diffInDaysFiltered(static function (CarbonInterface $date) { + return $date->isWeekday(); + }, $this->resolveCarbon($date)->avoidMutation()->modify($this->format('H:i:s.u')), $absolute); + } + + /** + * Get the difference in weekend days using a filter rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInWeekendDays($date = null, $absolute = true) + { + return $this->diffInDaysFiltered(static function (CarbonInterface $date) { + return $date->isWeekend(); + }, $this->resolveCarbon($date)->avoidMutation()->modify($this->format('H:i:s.u')), $absolute); + } + + /** + * Get the difference in hours rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInHours($date = null, $absolute = true) + { + return (int) ($this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR); + } + + /** + * Get the difference in hours rounded down using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealHours($date = null, $absolute = true) + { + return (int) ($this->diffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR); + } + + /** + * Get the difference in minutes rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInMinutes($date = null, $absolute = true) + { + return (int) ($this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE); + } + + /** + * Get the difference in minutes rounded down using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealMinutes($date = null, $absolute = true) + { + return (int) ($this->diffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE); + } + + /** + * Get the difference in seconds rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInSeconds($date = null, $absolute = true) + { + $diff = $this->diff($date); + + if ($diff->days === 0) { + $diff = static::fixDiffInterval($diff, $absolute); + } + + $value = (((($diff->m || $diff->y ? $diff->days : $diff->d) * static::HOURS_PER_DAY) + + $diff->h) * static::MINUTES_PER_HOUR + + $diff->i) * static::SECONDS_PER_MINUTE + + $diff->s; + + return $absolute || !$diff->invert ? $value : -$value; + } + + /** + * Get the difference in microseconds. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInMicroseconds($date = null, $absolute = true) + { + $diff = $this->diff($date); + $value = (int) round(((((($diff->m || $diff->y ? $diff->days : $diff->d) * static::HOURS_PER_DAY) + + $diff->h) * static::MINUTES_PER_HOUR + + $diff->i) * static::SECONDS_PER_MINUTE + + ($diff->f + $diff->s)) * static::MICROSECONDS_PER_SECOND); + + return $absolute || !$diff->invert ? $value : -$value; + } + + /** + * Get the difference in milliseconds rounded down. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInMilliseconds($date = null, $absolute = true) + { + return (int) ($this->diffInMicroseconds($date, $absolute) / static::MICROSECONDS_PER_MILLISECOND); + } + + /** + * Get the difference in seconds using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealSeconds($date = null, $absolute = true) + { + /** @var CarbonInterface $date */ + $date = $this->resolveCarbon($date); + $value = $date->getTimestamp() - $this->getTimestamp(); + + return $absolute ? abs($value) : $value; + } + + /** + * Get the difference in microseconds using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealMicroseconds($date = null, $absolute = true) + { + /** @var CarbonInterface $date */ + $date = $this->resolveCarbon($date); + $value = ($date->timestamp - $this->timestamp) * static::MICROSECONDS_PER_SECOND + + $date->micro - $this->micro; + + return $absolute ? abs($value) : $value; + } + + /** + * Get the difference in milliseconds rounded down using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealMilliseconds($date = null, $absolute = true) + { + return (int) ($this->diffInRealMicroseconds($date, $absolute) / static::MICROSECONDS_PER_MILLISECOND); + } + + /** + * Get the difference in seconds as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInSeconds($date = null, $absolute = true) + { + return (float) ($this->diffInMicroseconds($date, $absolute) / static::MICROSECONDS_PER_SECOND); + } + + /** + * Get the difference in minutes as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInMinutes($date = null, $absolute = true) + { + return $this->floatDiffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE; + } + + /** + * Get the difference in hours as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInHours($date = null, $absolute = true) + { + return $this->floatDiffInMinutes($date, $absolute) / static::MINUTES_PER_HOUR; + } + + /** + * Get the difference in days as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInDays($date = null, $absolute = true) + { + $hoursDiff = $this->floatDiffInHours($date, $absolute); + $interval = $this->diff($date, $absolute); + + if ($interval->y === 0 && $interval->m === 0 && $interval->d === 0) { + return $hoursDiff / static::HOURS_PER_DAY; + } + + $daysDiff = $this->getIntervalDayDiff($interval); + + return $daysDiff + fmod($hoursDiff, static::HOURS_PER_DAY) / static::HOURS_PER_DAY; + } + + /** + * Get the difference in weeks as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInWeeks($date = null, $absolute = true) + { + return $this->floatDiffInDays($date, $absolute) / static::DAYS_PER_WEEK; + } + + /** + * Get the difference in months as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInMonths($date = null, $absolute = true) + { + $start = $this; + $end = $this->resolveCarbon($date); + $ascending = ($start <= $end); + $sign = $absolute || $ascending ? 1 : -1; + if (!$ascending) { + [$start, $end] = [$end, $start]; + } + $monthsDiff = $start->diffInMonths($end); + /** @var Carbon|CarbonImmutable $floorEnd */ + $floorEnd = $start->avoidMutation()->addMonths($monthsDiff); + + if ($floorEnd >= $end) { + return $sign * $monthsDiff; + } + + /** @var Carbon|CarbonImmutable $startOfMonthAfterFloorEnd */ + $startOfMonthAfterFloorEnd = $floorEnd->avoidMutation()->addMonth()->startOfMonth(); + + if ($startOfMonthAfterFloorEnd > $end) { + return $sign * ($monthsDiff + $floorEnd->floatDiffInDays($end) / $floorEnd->daysInMonth); + } + + return $sign * ($monthsDiff + $floorEnd->floatDiffInDays($startOfMonthAfterFloorEnd) / $floorEnd->daysInMonth + $startOfMonthAfterFloorEnd->floatDiffInDays($end) / $end->daysInMonth); + } + + /** + * Get the difference in year as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInYears($date = null, $absolute = true) + { + $start = $this; + $end = $this->resolveCarbon($date); + $ascending = ($start <= $end); + $sign = $absolute || $ascending ? 1 : -1; + if (!$ascending) { + [$start, $end] = [$end, $start]; + } + $yearsDiff = $start->diffInYears($end); + /** @var Carbon|CarbonImmutable $floorEnd */ + $floorEnd = $start->avoidMutation()->addYears($yearsDiff); + + if ($floorEnd >= $end) { + return $sign * $yearsDiff; + } + + /** @var Carbon|CarbonImmutable $startOfYearAfterFloorEnd */ + $startOfYearAfterFloorEnd = $floorEnd->avoidMutation()->addYear()->startOfYear(); + + if ($startOfYearAfterFloorEnd > $end) { + return $sign * ($yearsDiff + $floorEnd->floatDiffInDays($end) / $floorEnd->daysInYear); + } + + return $sign * ($yearsDiff + $floorEnd->floatDiffInDays($startOfYearAfterFloorEnd) / $floorEnd->daysInYear + $startOfYearAfterFloorEnd->floatDiffInDays($end) / $end->daysInYear); + } + + /** + * Get the difference in seconds as float (microsecond-precision) using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealSeconds($date = null, $absolute = true) + { + return $this->diffInRealMicroseconds($date, $absolute) / static::MICROSECONDS_PER_SECOND; + } + + /** + * Get the difference in minutes as float (microsecond-precision) using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealMinutes($date = null, $absolute = true) + { + return $this->floatDiffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE; + } + + /** + * Get the difference in hours as float (microsecond-precision) using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealHours($date = null, $absolute = true) + { + return $this->floatDiffInRealMinutes($date, $absolute) / static::MINUTES_PER_HOUR; + } + + /** + * Get the difference in days as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealDays($date = null, $absolute = true) + { + $date = $this->resolveUTC($date); + $utc = $this->avoidMutation()->utc(); + $hoursDiff = $utc->floatDiffInRealHours($date, $absolute); + + return ($hoursDiff < 0 ? -1 : 1) * $utc->diffInDays($date) + fmod($hoursDiff, static::HOURS_PER_DAY) / static::HOURS_PER_DAY; + } + + /** + * Get the difference in weeks as float (microsecond-precision). + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealWeeks($date = null, $absolute = true) + { + return $this->floatDiffInRealDays($date, $absolute) / static::DAYS_PER_WEEK; + } + + /** + * Get the difference in months as float (microsecond-precision) using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealMonths($date = null, $absolute = true) + { + $start = $this; + $end = $this->resolveCarbon($date); + $ascending = ($start <= $end); + $sign = $absolute || $ascending ? 1 : -1; + if (!$ascending) { + [$start, $end] = [$end, $start]; + } + $monthsDiff = $start->diffInMonths($end); + /** @var Carbon|CarbonImmutable $floorEnd */ + $floorEnd = $start->avoidMutation()->addMonths($monthsDiff); + + if ($floorEnd >= $end) { + return $sign * $monthsDiff; + } + + /** @var Carbon|CarbonImmutable $startOfMonthAfterFloorEnd */ + $startOfMonthAfterFloorEnd = $floorEnd->avoidMutation()->addMonth()->startOfMonth(); + + if ($startOfMonthAfterFloorEnd > $end) { + return $sign * ($monthsDiff + $floorEnd->floatDiffInRealDays($end) / $floorEnd->daysInMonth); + } + + return $sign * ($monthsDiff + $floorEnd->floatDiffInRealDays($startOfMonthAfterFloorEnd) / $floorEnd->daysInMonth + $startOfMonthAfterFloorEnd->floatDiffInRealDays($end) / $end->daysInMonth); + } + + /** + * Get the difference in year as float (microsecond-precision) using timestamps. + * + * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return float + */ + public function floatDiffInRealYears($date = null, $absolute = true) + { + $start = $this; + $end = $this->resolveCarbon($date); + $ascending = ($start <= $end); + $sign = $absolute || $ascending ? 1 : -1; + if (!$ascending) { + [$start, $end] = [$end, $start]; + } + $yearsDiff = $start->diffInYears($end); + /** @var Carbon|CarbonImmutable $floorEnd */ + $floorEnd = $start->avoidMutation()->addYears($yearsDiff); + + if ($floorEnd >= $end) { + return $sign * $yearsDiff; + } + + /** @var Carbon|CarbonImmutable $startOfYearAfterFloorEnd */ + $startOfYearAfterFloorEnd = $floorEnd->avoidMutation()->addYear()->startOfYear(); + + if ($startOfYearAfterFloorEnd > $end) { + return $sign * ($yearsDiff + $floorEnd->floatDiffInRealDays($end) / $floorEnd->daysInYear); + } + + return $sign * ($yearsDiff + $floorEnd->floatDiffInRealDays($startOfYearAfterFloorEnd) / $floorEnd->daysInYear + $startOfYearAfterFloorEnd->floatDiffInRealDays($end) / $end->daysInYear); + } + + /** + * The number of seconds since midnight. + * + * @return int + */ + public function secondsSinceMidnight() + { + return $this->diffInSeconds($this->avoidMutation()->startOfDay()); + } + + /** + * The number of seconds until 23:59:59. + * + * @return int + */ + public function secondsUntilEndOfDay() + { + return $this->diffInSeconds($this->avoidMutation()->endOfDay()); + } + + /** + * Get the difference in a human readable format in the current locale from current instance to an other + * instance given (or now if null given). + * + * @example + * ``` + * echo Carbon::tomorrow()->diffForHumans() . "\n"; + * echo Carbon::tomorrow()->diffForHumans(['parts' => 2]) . "\n"; + * echo Carbon::tomorrow()->diffForHumans(['parts' => 3, 'join' => true]) . "\n"; + * echo Carbon::tomorrow()->diffForHumans(Carbon::yesterday()) . "\n"; + * echo Carbon::tomorrow()->diffForHumans(Carbon::yesterday(), ['short' => true]) . "\n"; + * ``` + * + * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; + * if null passed, now will be used as comparison reference; + * if any other type, it will be converted to date and used as reference. + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'skip' entry, list of units to skip (array of strings or a single string, + * ` it can be the unit name (singular or plural) or its shortcut + * ` (y, m, w, d, h, min, s, ms, µs). + * - 'aUnit' entry, prefer "an hour" over "1 hour" if true + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * - 'other' entry (see above) + * - 'minimumUnit' entry determines the smallest unit of time to display can be long or + * ` short form of the units, e.g. 'hour' or 'h' (default value: s) + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single unit) + * @param int $options human diff options + * + * @return string + */ + public function diffForHumans($other = null, $syntax = null, $short = false, $parts = 1, $options = null) + { + /* @var CarbonInterface $this */ + if (\is_array($other)) { + $other['syntax'] = \array_key_exists('syntax', $other) ? $other['syntax'] : $syntax; + $syntax = $other; + $other = $syntax['other'] ?? null; + } + + $intSyntax = &$syntax; + if (\is_array($syntax)) { + $syntax['syntax'] = $syntax['syntax'] ?? null; + $intSyntax = &$syntax['syntax']; + } + $intSyntax = (int) ($intSyntax ?? static::DIFF_RELATIVE_AUTO); + $intSyntax = $intSyntax === static::DIFF_RELATIVE_AUTO && $other === null ? static::DIFF_RELATIVE_TO_NOW : $intSyntax; + + $parts = min(7, max(1, (int) $parts)); + $skip = \is_array($syntax) ? ($syntax['skip'] ?? []) : []; + + return $this->diffAsCarbonInterval($other, false, (array) $skip) + ->setLocalTranslator($this->getLocalTranslator()) + ->forHumans($syntax, (bool) $short, $parts, $options ?? $this->localHumanDiffOptions ?? static::getHumanDiffOptions()); + } + + /** + * @alias diffForHumans + * + * Get the difference in a human readable format in the current locale from current instance to an other + * instance given (or now if null given). + * + * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; + * if null passed, now will be used as comparison reference; + * if any other type, it will be converted to date and used as reference. + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * - 'other' entry (see above) + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single unit) + * @param int $options human diff options + * + * @return string + */ + public function from($other = null, $syntax = null, $short = false, $parts = 1, $options = null) + { + return $this->diffForHumans($other, $syntax, $short, $parts, $options); + } + + /** + * @alias diffForHumans + * + * Get the difference in a human readable format in the current locale from current instance to an other + * instance given (or now if null given). + */ + public function since($other = null, $syntax = null, $short = false, $parts = 1, $options = null) + { + return $this->diffForHumans($other, $syntax, $short, $parts, $options); + } + + /** + * Get the difference in a human readable format in the current locale from an other + * instance given (or now if null given) to current instance. + * + * When comparing a value in the past to default now: + * 1 hour from now + * 5 months from now + * + * When comparing a value in the future to default now: + * 1 hour ago + * 5 months ago + * + * When comparing a value in the past to another value: + * 1 hour after + * 5 months after + * + * When comparing a value in the future to another value: + * 1 hour before + * 5 months before + * + * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; + * if null passed, now will be used as comparison reference; + * if any other type, it will be converted to date and used as reference. + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * - 'other' entry (see above) + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single unit) + * @param int $options human diff options + * + * @return string + */ + public function to($other = null, $syntax = null, $short = false, $parts = 1, $options = null) + { + if (!$syntax && !$other) { + $syntax = CarbonInterface::DIFF_RELATIVE_TO_NOW; + } + + return $this->resolveCarbon($other)->diffForHumans($this, $syntax, $short, $parts, $options); + } + + /** + * @alias to + * + * Get the difference in a human readable format in the current locale from an other + * instance given (or now if null given) to current instance. + * + * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below; + * if null passed, now will be used as comparison reference; + * if any other type, it will be converted to date and used as reference. + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * - 'other' entry (see above) + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single unit) + * @param int $options human diff options + * + * @return string + */ + public function until($other = null, $syntax = null, $short = false, $parts = 1, $options = null) + { + return $this->to($other, $syntax, $short, $parts, $options); + } + + /** + * Get the difference in a human readable format in the current locale from current + * instance to now. + * + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single unit) + * @param int $options human diff options + * + * @return string + */ + public function fromNow($syntax = null, $short = false, $parts = 1, $options = null) + { + $other = null; + + if ($syntax instanceof DateTimeInterface) { + [$other, $syntax, $short, $parts, $options] = array_pad(\func_get_args(), 5, null); + } + + return $this->from($other, $syntax, $short, $parts, $options); + } + + /** + * Get the difference in a human readable format in the current locale from an other + * instance given to now + * + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single part) + * @param int $options human diff options + * + * @return string + */ + public function toNow($syntax = null, $short = false, $parts = 1, $options = null) + { + return $this->to(null, $syntax, $short, $parts, $options); + } + + /** + * Get the difference in a human readable format in the current locale from an other + * instance given to now + * + * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains: + * - 'syntax' entry (see below) + * - 'short' entry (see below) + * - 'parts' entry (see below) + * - 'options' entry (see below) + * - 'join' entry determines how to join multiple parts of the string + * ` - if $join is a string, it's used as a joiner glue + * ` - if $join is a callable/closure, it get the list of string and should return a string + * ` - if $join is an array, the first item will be the default glue, and the second item + * ` will be used instead of the glue for the last item + * ` - if $join is true, it will be guessed from the locale ('list' translation file entry) + * ` - if $join is missing, a space will be used as glue + * if int passed, it add modifiers: + * Possible values: + * - CarbonInterface::DIFF_ABSOLUTE no modifiers + * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier + * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier + * Default value: CarbonInterface::DIFF_ABSOLUTE + * @param bool $short displays short format of time units + * @param int $parts maximum number of parts to display (default value: 1: single part) + * @param int $options human diff options + * + * @return string + */ + public function ago($syntax = null, $short = false, $parts = 1, $options = null) + { + $other = null; + + if ($syntax instanceof DateTimeInterface) { + [$other, $syntax, $short, $parts, $options] = array_pad(\func_get_args(), 5, null); + } + + return $this->from($other, $syntax, $short, $parts, $options); + } + + /** + * Get the difference in a human readable format in the current locale from current instance to an other + * instance given (or now if null given). + * + * @return string + */ + public function timespan($other = null, $timezone = null) + { + if (!$other instanceof DateTimeInterface) { + $other = static::parse($other, $timezone); + } + + return $this->diffForHumans($other, [ + 'join' => ', ', + 'syntax' => CarbonInterface::DIFF_ABSOLUTE, + 'options' => CarbonInterface::NO_ZERO_DIFF, + 'parts' => -1, + ]); + } + + /** + * Returns either day of week + time (e.g. "Last Friday at 3:30 PM") if reference time is within 7 days, + * or a calendar date (e.g. "10/29/2017") otherwise. + * + * Language, date and time formats will change according to the current locale. + * + * @param Carbon|\DateTimeInterface|string|null $referenceTime + * @param array $formats + * + * @return string + */ + public function calendar($referenceTime = null, array $formats = []) + { + /** @var CarbonInterface $current */ + $current = $this->avoidMutation()->startOfDay(); + /** @var CarbonInterface $other */ + $other = $this->resolveCarbon($referenceTime)->avoidMutation()->setTimezone($this->getTimezone())->startOfDay(); + $diff = $other->diffInDays($current, false); + $format = $diff < -6 ? 'sameElse' : ( + $diff < -1 ? 'lastWeek' : ( + $diff < 0 ? 'lastDay' : ( + $diff < 1 ? 'sameDay' : ( + $diff < 2 ? 'nextDay' : ( + $diff < 7 ? 'nextWeek' : 'sameElse' + ) + ) + ) + ) + ); + $format = array_merge($this->getCalendarFormats(), $formats)[$format]; + if ($format instanceof Closure) { + $format = $format($current, $other) ?? ''; + } + + return $this->isoFormat((string) $format); + } + + private function getIntervalDayDiff(DateInterval $interval): int + { + $daysDiff = (int) $interval->format('%a'); + $sign = $interval->format('%r') === '-' ? -1 : 1; + + if (\is_int($interval->days) && + $interval->y === 0 && + $interval->m === 0 && + version_compare(PHP_VERSION, '8.1.0-dev', '<') && + abs($interval->d - $daysDiff) === 1 + ) { + $daysDiff = abs($interval->d); // @codeCoverageIgnore + } + + return $daysDiff * $sign; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/IntervalRounding.php b/vendor/nesbot/carbon/src/Carbon/Traits/IntervalRounding.php new file mode 100644 index 0000000..f069c28 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/IntervalRounding.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\CarbonInterval; +use Carbon\Exceptions\InvalidIntervalException; +use DateInterval; + +/** + * Trait to call rounding methods to interval or the interval of a period. + */ +trait IntervalRounding +{ + protected function callRoundMethod(string $method, array $parameters) + { + $action = substr($method, 0, 4); + + if ($action !== 'ceil') { + $action = substr($method, 0, 5); + } + + if (\in_array($action, ['round', 'floor', 'ceil'])) { + return $this->{$action.'Unit'}(substr($method, \strlen($action)), ...$parameters); + } + + return null; + } + + protected function roundWith($precision, $function) + { + $unit = 'second'; + + if ($precision instanceof DateInterval) { + $precision = (string) CarbonInterval::instance($precision, [], true); + } + + if (\is_string($precision) && preg_match('/^\s*(?\d+)?\s*(?\w+)(?\W.*)?$/', $precision, $match)) { + if (trim($match['other'] ?? '') !== '') { + throw new InvalidIntervalException('Rounding is only possible with single unit intervals.'); + } + + $precision = (int) ($match['precision'] ?: 1); + $unit = $match['unit']; + } + + return $this->roundUnit($unit, $precision, $function); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/IntervalStep.php b/vendor/nesbot/carbon/src/Carbon/Traits/IntervalStep.php new file mode 100644 index 0000000..82d7c32 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/IntervalStep.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\Carbon; +use Carbon\CarbonImmutable; +use Carbon\CarbonInterface; +use Closure; +use DateTimeImmutable; +use DateTimeInterface; + +trait IntervalStep +{ + /** + * Step to apply instead of a fixed interval to get the new date. + * + * @var Closure|null + */ + protected $step; + + /** + * Get the dynamic step in use. + * + * @return Closure + */ + public function getStep(): ?Closure + { + return $this->step; + } + + /** + * Set a step to apply instead of a fixed interval to get the new date. + * + * Or pass null to switch to fixed interval. + * + * @param Closure|null $step + */ + public function setStep(?Closure $step): void + { + $this->step = $step; + } + + /** + * Take a date and apply either the step if set, or the current interval else. + * + * The interval/step is applied negatively (typically subtraction instead of addition) if $negated is true. + * + * @param DateTimeInterface $dateTime + * @param bool $negated + * + * @return CarbonInterface + */ + public function convertDate(DateTimeInterface $dateTime, bool $negated = false): CarbonInterface + { + /** @var CarbonInterface $carbonDate */ + $carbonDate = $dateTime instanceof CarbonInterface ? $dateTime : $this->resolveCarbon($dateTime); + + if ($this->step) { + return $carbonDate->setDateTimeFrom(($this->step)($carbonDate->avoidMutation(), $negated)); + } + + if ($negated) { + return $carbonDate->rawSub($this); + } + + return $carbonDate->rawAdd($this); + } + + /** + * Convert DateTimeImmutable instance to CarbonImmutable instance and DateTime instance to Carbon instance. + * + * @param DateTimeInterface $dateTime + * + * @return Carbon|CarbonImmutable + */ + private function resolveCarbon(DateTimeInterface $dateTime) + { + if ($dateTime instanceof DateTimeImmutable) { + return CarbonImmutable::instance($dateTime); + } + + return Carbon::instance($dateTime); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Localization.php b/vendor/nesbot/carbon/src/Carbon/Traits/Localization.php new file mode 100644 index 0000000..46aff11 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Localization.php @@ -0,0 +1,840 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\CarbonInterface; +use Carbon\Exceptions\InvalidTypeException; +use Carbon\Exceptions\NotLocaleAwareException; +use Carbon\Language; +use Carbon\Translator; +use Carbon\TranslatorStrongTypeInterface; +use Closure; +use Symfony\Component\Translation\TranslatorBagInterface; +use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\LocaleAwareInterface; +use Symfony\Contracts\Translation\TranslatorInterface as ContractsTranslatorInterface; + +// @codeCoverageIgnoreStart +if (interface_exists('Symfony\\Contracts\\Translation\\TranslatorInterface') && + !interface_exists('Symfony\\Component\\Translation\\TranslatorInterface') +) { + class_alias( + 'Symfony\\Contracts\\Translation\\TranslatorInterface', + 'Symfony\\Component\\Translation\\TranslatorInterface' + ); +} +// @codeCoverageIgnoreEnd + +/** + * Trait Localization. + * + * Embed default and locale translators and translation base methods. + */ +trait Localization +{ + /** + * Default translator. + * + * @var \Symfony\Component\Translation\TranslatorInterface + */ + protected static $translator; + + /** + * Specific translator of the current instance. + * + * @var \Symfony\Component\Translation\TranslatorInterface + */ + protected $localTranslator; + + /** + * Options for diffForHumans(). + * + * @var int + */ + protected static $humanDiffOptions = CarbonInterface::NO_ZERO_DIFF; + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @see settings + * + * @param int $humanDiffOptions + */ + public static function setHumanDiffOptions($humanDiffOptions) + { + static::$humanDiffOptions = $humanDiffOptions; + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @see settings + * + * @param int $humanDiffOption + */ + public static function enableHumanDiffOption($humanDiffOption) + { + static::$humanDiffOptions = static::getHumanDiffOptions() | $humanDiffOption; + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @see settings + * + * @param int $humanDiffOption + */ + public static function disableHumanDiffOption($humanDiffOption) + { + static::$humanDiffOptions = static::getHumanDiffOptions() & ~$humanDiffOption; + } + + /** + * Return default humanDiff() options (merged flags as integer). + * + * @return int + */ + public static function getHumanDiffOptions() + { + return static::$humanDiffOptions; + } + + /** + * Get the default translator instance in use. + * + * @return \Symfony\Component\Translation\TranslatorInterface + */ + public static function getTranslator() + { + return static::translator(); + } + + /** + * Set the default translator instance to use. + * + * @param \Symfony\Component\Translation\TranslatorInterface $translator + * + * @return void + */ + public static function setTranslator(TranslatorInterface $translator) + { + static::$translator = $translator; + } + + /** + * Return true if the current instance has its own translator. + * + * @return bool + */ + public function hasLocalTranslator() + { + return isset($this->localTranslator); + } + + /** + * Get the translator of the current instance or the default if none set. + * + * @return \Symfony\Component\Translation\TranslatorInterface + */ + public function getLocalTranslator() + { + return $this->localTranslator ?: static::translator(); + } + + /** + * Set the translator for the current instance. + * + * @param \Symfony\Component\Translation\TranslatorInterface $translator + * + * @return $this + */ + public function setLocalTranslator(TranslatorInterface $translator) + { + $this->localTranslator = $translator; + + return $this; + } + + /** + * Returns raw translation message for a given key. + * + * @param \Symfony\Component\Translation\TranslatorInterface $translator the translator to use + * @param string $key key to find + * @param string|null $locale current locale used if null + * @param string|null $default default value if translation returns the key + * + * @return string + */ + public static function getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null) + { + if (!($translator instanceof TranslatorBagInterface && $translator instanceof TranslatorInterface)) { + throw new InvalidTypeException( + 'Translator does not implement '.TranslatorInterface::class.' and '.TranslatorBagInterface::class.'. '. + (\is_object($translator) ? \get_class($translator) : \gettype($translator)).' has been given.' + ); + } + + if (!$locale && $translator instanceof LocaleAwareInterface) { + $locale = $translator->getLocale(); + } + + $result = self::getFromCatalogue($translator, $translator->getCatalogue($locale), $key); + + return $result === $key ? $default : $result; + } + + /** + * Returns raw translation message for a given key. + * + * @param string $key key to find + * @param string|null $locale current locale used if null + * @param string|null $default default value if translation returns the key + * @param \Symfony\Component\Translation\TranslatorInterface $translator an optional translator to use + * + * @return string + */ + public function getTranslationMessage(string $key, ?string $locale = null, ?string $default = null, $translator = null) + { + return static::getTranslationMessageWith($translator ?: $this->getLocalTranslator(), $key, $locale, $default); + } + + /** + * Translate using translation string or callback available. + * + * @param \Symfony\Component\Translation\TranslatorInterface $translator + * @param string $key + * @param array $parameters + * @param null $number + * + * @return string + */ + public static function translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null): string + { + $message = static::getTranslationMessageWith($translator, $key, null, $key); + if ($message instanceof Closure) { + return (string) $message(...array_values($parameters)); + } + + if ($number !== null) { + $parameters['%count%'] = $number; + } + if (isset($parameters['%count%'])) { + $parameters[':count'] = $parameters['%count%']; + } + + // @codeCoverageIgnoreStart + $choice = $translator instanceof ContractsTranslatorInterface + ? $translator->trans($key, $parameters) + : $translator->transChoice($key, $number, $parameters); + // @codeCoverageIgnoreEnd + + return (string) $choice; + } + + /** + * Translate using translation string or callback available. + * + * @param string $key + * @param array $parameters + * @param string|int|float|null $number + * @param \Symfony\Component\Translation\TranslatorInterface|null $translator + * @param bool $altNumbers + * + * @return string + */ + public function translate(string $key, array $parameters = [], $number = null, ?TranslatorInterface $translator = null, bool $altNumbers = false): string + { + $translation = static::translateWith($translator ?: $this->getLocalTranslator(), $key, $parameters, $number); + + if ($number !== null && $altNumbers) { + return str_replace($number, $this->translateNumber($number), $translation); + } + + return $translation; + } + + /** + * Returns the alternative number for a given integer if available in the current locale. + * + * @param int $number + * + * @return string + */ + public function translateNumber(int $number): string + { + $translateKey = "alt_numbers.$number"; + $symbol = $this->translate($translateKey); + + if ($symbol !== $translateKey) { + return $symbol; + } + + if ($number > 99 && $this->translate('alt_numbers.99') !== 'alt_numbers.99') { + $start = ''; + foreach ([10000, 1000, 100] as $exp) { + $key = "alt_numbers_pow.$exp"; + if ($number >= $exp && $number < $exp * 10 && ($pow = $this->translate($key)) !== $key) { + $unit = floor($number / $exp); + $number -= $unit * $exp; + $start .= ($unit > 1 ? $this->translate("alt_numbers.$unit") : '').$pow; + } + } + $result = ''; + while ($number) { + $chunk = $number % 100; + $result = $this->translate("alt_numbers.$chunk").$result; + $number = floor($number / 100); + } + + return "$start$result"; + } + + if ($number > 9 && $this->translate('alt_numbers.9') !== 'alt_numbers.9') { + $result = ''; + while ($number) { + $chunk = $number % 10; + $result = $this->translate("alt_numbers.$chunk").$result; + $number = floor($number / 10); + } + + return $result; + } + + return (string) $number; + } + + /** + * Translate a time string from a locale to an other. + * + * @param string $timeString date/time/duration string to translate (may also contain English) + * @param string|null $from input locale of the $timeString parameter (`Carbon::getLocale()` by default) + * @param string|null $to output locale of the result returned (`"en"` by default) + * @param int $mode specify what to translate with options: + * - CarbonInterface::TRANSLATE_ALL (default) + * - CarbonInterface::TRANSLATE_MONTHS + * - CarbonInterface::TRANSLATE_DAYS + * - CarbonInterface::TRANSLATE_UNITS + * - CarbonInterface::TRANSLATE_MERIDIEM + * You can use pipe to group: CarbonInterface::TRANSLATE_MONTHS | CarbonInterface::TRANSLATE_DAYS + * + * @return string + */ + public static function translateTimeString($timeString, $from = null, $to = null, $mode = CarbonInterface::TRANSLATE_ALL) + { + // Fallback source and destination locales + $from = $from ?: static::getLocale(); + $to = $to ?: 'en'; + + if ($from === $to) { + return $timeString; + } + + // Standardize apostrophe + $timeString = strtr($timeString, ['’' => "'"]); + + $fromTranslations = []; + $toTranslations = []; + + foreach (['from', 'to'] as $key) { + $language = $$key; + $translator = Translator::get($language); + $translations = $translator->getMessages(); + + if (!isset($translations[$language])) { + return $timeString; + } + + $translationKey = $key.'Translations'; + $messages = $translations[$language]; + $months = $messages['months'] ?? []; + $weekdays = $messages['weekdays'] ?? []; + $meridiem = $messages['meridiem'] ?? ['AM', 'PM']; + + if (isset($messages['ordinal_words'])) { + $timeString = self::replaceOrdinalWords( + $timeString, + $key === 'from' ? array_flip($messages['ordinal_words']) : $messages['ordinal_words'] + ); + } + + if ($key === 'from') { + foreach (['months', 'weekdays'] as $variable) { + $list = $messages[$variable.'_standalone'] ?? null; + + if ($list) { + foreach ($$variable as $index => &$name) { + $name .= '|'.$messages[$variable.'_standalone'][$index]; + } + } + } + } + + $$translationKey = array_merge( + $mode & CarbonInterface::TRANSLATE_MONTHS ? static::getTranslationArray($months, 12, $timeString) : [], + $mode & CarbonInterface::TRANSLATE_MONTHS ? static::getTranslationArray($messages['months_short'] ?? [], 12, $timeString) : [], + $mode & CarbonInterface::TRANSLATE_DAYS ? static::getTranslationArray($weekdays, 7, $timeString) : [], + $mode & CarbonInterface::TRANSLATE_DAYS ? static::getTranslationArray($messages['weekdays_short'] ?? [], 7, $timeString) : [], + $mode & CarbonInterface::TRANSLATE_DIFF ? static::translateWordsByKeys([ + 'diff_now', + 'diff_today', + 'diff_yesterday', + 'diff_tomorrow', + 'diff_before_yesterday', + 'diff_after_tomorrow', + ], $messages, $key) : [], + $mode & CarbonInterface::TRANSLATE_UNITS ? static::translateWordsByKeys([ + 'year', + 'month', + 'week', + 'day', + 'hour', + 'minute', + 'second', + ], $messages, $key) : [], + $mode & CarbonInterface::TRANSLATE_MERIDIEM ? array_map(function ($hour) use ($meridiem) { + if (\is_array($meridiem)) { + return $meridiem[$hour < 12 ? 0 : 1]; + } + + return $meridiem($hour, 0, false); + }, range(0, 23)) : [] + ); + } + + return substr(preg_replace_callback('/(?<=[\d\s+.\/,_-])('.implode('|', $fromTranslations).')(?=[\d\s+.\/,_-])/iu', function ($match) use ($fromTranslations, $toTranslations) { + [$chunk] = $match; + + foreach ($fromTranslations as $index => $word) { + if (preg_match("/^$word\$/iu", $chunk)) { + return $toTranslations[$index] ?? ''; + } + } + + return $chunk; // @codeCoverageIgnore + }, " $timeString "), 1, -1); + } + + /** + * Translate a time string from the current locale (`$date->locale()`) to an other. + * + * @param string $timeString time string to translate + * @param string|null $to output locale of the result returned ("en" by default) + * + * @return string + */ + public function translateTimeStringTo($timeString, $to = null) + { + return static::translateTimeString($timeString, $this->getTranslatorLocale(), $to); + } + + /** + * Get/set the locale for the current instance. + * + * @param string|null $locale + * @param string ...$fallbackLocales + * + * @return $this|string + */ + public function locale(string $locale = null, ...$fallbackLocales) + { + if ($locale === null) { + return $this->getTranslatorLocale(); + } + + if (!$this->localTranslator || $this->getTranslatorLocale($this->localTranslator) !== $locale) { + $translator = Translator::get($locale); + + if (!empty($fallbackLocales)) { + $translator->setFallbackLocales($fallbackLocales); + + foreach ($fallbackLocales as $fallbackLocale) { + $messages = Translator::get($fallbackLocale)->getMessages(); + + if (isset($messages[$fallbackLocale])) { + $translator->setMessages($fallbackLocale, $messages[$fallbackLocale]); + } + } + } + + $this->localTranslator = $translator; + } + + return $this; + } + + /** + * Get the current translator locale. + * + * @return string + */ + public static function getLocale() + { + return static::getLocaleAwareTranslator()->getLocale(); + } + + /** + * Set the current translator locale and indicate if the source locale file exists. + * Pass 'auto' as locale to use closest language from the current LC_TIME locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function setLocale($locale) + { + return static::getLocaleAwareTranslator()->setLocale($locale) !== false; + } + + /** + * Set the fallback locale. + * + * @see https://symfony.com/doc/current/components/translation.html#fallback-locales + * + * @param string $locale + */ + public static function setFallbackLocale($locale) + { + $translator = static::getTranslator(); + + if (method_exists($translator, 'setFallbackLocales')) { + $translator->setFallbackLocales([$locale]); + + if ($translator instanceof Translator) { + $preferredLocale = $translator->getLocale(); + $translator->setMessages($preferredLocale, array_replace_recursive( + $translator->getMessages()[$locale] ?? [], + Translator::get($locale)->getMessages()[$locale] ?? [], + $translator->getMessages($preferredLocale) + )); + } + } + } + + /** + * Get the fallback locale. + * + * @see https://symfony.com/doc/current/components/translation.html#fallback-locales + * + * @return string|null + */ + public static function getFallbackLocale() + { + $translator = static::getTranslator(); + + if (method_exists($translator, 'getFallbackLocales')) { + return $translator->getFallbackLocales()[0] ?? null; + } + + return null; + } + + /** + * Set the current locale to the given, execute the passed function, reset the locale to previous one, + * then return the result of the closure (or null if the closure was void). + * + * @param string $locale locale ex. en + * @param callable $func + * + * @return mixed + */ + public static function executeWithLocale($locale, $func) + { + $currentLocale = static::getLocale(); + $result = $func(static::setLocale($locale) ? static::getLocale() : false, static::translator()); + static::setLocale($currentLocale); + + return $result; + } + + /** + * Returns true if the given locale is internally supported and has short-units support. + * Support is considered enabled if either year, day or hour has a short variant translated. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasShortUnits($locale) + { + return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { + return ($newLocale && (($y = static::translateWith($translator, 'y')) !== 'y' && $y !== static::translateWith($translator, 'year'))) || ( + ($y = static::translateWith($translator, 'd')) !== 'd' && + $y !== static::translateWith($translator, 'day') + ) || ( + ($y = static::translateWith($translator, 'h')) !== 'h' && + $y !== static::translateWith($translator, 'hour') + ); + }); + } + + /** + * Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasDiffSyntax($locale) + { + return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { + if (!$newLocale) { + return false; + } + + foreach (['ago', 'from_now', 'before', 'after'] as $key) { + if ($translator instanceof TranslatorBagInterface && + self::getFromCatalogue($translator, $translator->getCatalogue($newLocale), $key) instanceof Closure + ) { + continue; + } + + if ($translator->trans($key) === $key) { + return false; + } + } + + return true; + }); + } + + /** + * Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow). + * Support is considered enabled if the 3 words are translated in the given locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasDiffOneDayWords($locale) + { + return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { + return $newLocale && + $translator->trans('diff_now') !== 'diff_now' && + $translator->trans('diff_yesterday') !== 'diff_yesterday' && + $translator->trans('diff_tomorrow') !== 'diff_tomorrow'; + }); + } + + /** + * Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow). + * Support is considered enabled if the 2 words are translated in the given locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasDiffTwoDayWords($locale) + { + return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { + return $newLocale && + $translator->trans('diff_before_yesterday') !== 'diff_before_yesterday' && + $translator->trans('diff_after_tomorrow') !== 'diff_after_tomorrow'; + }); + } + + /** + * Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasPeriodSyntax($locale) + { + return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { + return $newLocale && + $translator->trans('period_recurrences') !== 'period_recurrences' && + $translator->trans('period_interval') !== 'period_interval' && + $translator->trans('period_start_date') !== 'period_start_date' && + $translator->trans('period_end_date') !== 'period_end_date'; + }); + } + + /** + * Returns the list of internally available locales and already loaded custom locales. + * (It will ignore custom translator dynamic loading.) + * + * @return array + */ + public static function getAvailableLocales() + { + $translator = static::getLocaleAwareTranslator(); + + return $translator instanceof Translator + ? $translator->getAvailableLocales() + : [$translator->getLocale()]; + } + + /** + * Returns list of Language object for each available locale. This object allow you to get the ISO name, native + * name, region and variant of the locale. + * + * @return Language[] + */ + public static function getAvailableLocalesInfo() + { + $languages = []; + foreach (static::getAvailableLocales() as $id) { + $languages[$id] = new Language($id); + } + + return $languages; + } + + /** + * Initialize the default translator instance if necessary. + * + * @return \Symfony\Component\Translation\TranslatorInterface + */ + protected static function translator() + { + if (static::$translator === null) { + static::$translator = Translator::get(); + } + + return static::$translator; + } + + /** + * Get the locale of a given translator. + * + * If null or omitted, current local translator is used. + * If no local translator is in use, current global translator is used. + * + * @param null $translator + * + * @return string|null + */ + protected function getTranslatorLocale($translator = null): ?string + { + if (\func_num_args() === 0) { + $translator = $this->getLocalTranslator(); + } + + $translator = static::getLocaleAwareTranslator($translator); + + return $translator ? $translator->getLocale() : null; + } + + /** + * Throw an error if passed object is not LocaleAwareInterface. + * + * @param LocaleAwareInterface|null $translator + * + * @return LocaleAwareInterface|null + */ + protected static function getLocaleAwareTranslator($translator = null) + { + if (\func_num_args() === 0) { + $translator = static::translator(); + } + + if ($translator && !($translator instanceof LocaleAwareInterface || method_exists($translator, 'getLocale'))) { + throw new NotLocaleAwareException($translator); // @codeCoverageIgnore + } + + return $translator; + } + + /** + * @param mixed $translator + * @param \Symfony\Component\Translation\MessageCatalogueInterface $catalogue + * + * @return mixed + */ + private static function getFromCatalogue($translator, $catalogue, string $id, string $domain = 'messages') + { + return $translator instanceof TranslatorStrongTypeInterface + ? $translator->getFromCatalogue($catalogue, $id, $domain) // @codeCoverageIgnore + : $catalogue->get($id, $domain); + } + + /** + * Return the word cleaned from its translation codes. + * + * @param string $word + * + * @return string + */ + private static function cleanWordFromTranslationString($word) + { + $word = str_replace([':count', '%count', ':time'], '', $word); + $word = strtr($word, ['’' => "'"]); + $word = preg_replace('/({\d+(,(\d+|Inf))?}|[\[\]]\d+(,(\d+|Inf))?[\[\]])/', '', $word); + + return trim($word); + } + + /** + * Translate a list of words. + * + * @param string[] $keys keys to translate. + * @param string[] $messages messages bag handling translations. + * @param string $key 'to' (to get the translation) or 'from' (to get the detection RegExp pattern). + * + * @return string[] + */ + private static function translateWordsByKeys($keys, $messages, $key): array + { + return array_map(function ($wordKey) use ($messages, $key) { + $message = $key === 'from' && isset($messages[$wordKey.'_regexp']) + ? $messages[$wordKey.'_regexp'] + : ($messages[$wordKey] ?? null); + + if (!$message) { + return '>>DO NOT REPLACE<<'; + } + + $parts = explode('|', $message); + + return $key === 'to' + ? self::cleanWordFromTranslationString(end($parts)) + : '(?:'.implode('|', array_map([static::class, 'cleanWordFromTranslationString'], $parts)).')'; + }, $keys); + } + + /** + * Get an array of translations based on the current date. + * + * @param callable $translation + * @param int $length + * @param string $timeString + * + * @return string[] + */ + private static function getTranslationArray($translation, $length, $timeString): array + { + $filler = '>>DO NOT REPLACE<<'; + + if (\is_array($translation)) { + return array_pad($translation, $length, $filler); + } + + $list = []; + $date = static::now(); + + for ($i = 0; $i < $length; $i++) { + $list[] = $translation($date, $timeString, $i) ?? $filler; + } + + return $list; + } + + private static function replaceOrdinalWords(string $timeString, array $ordinalWords): string + { + return preg_replace_callback('/(? + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +/** + * Trait Macros. + * + * Allows users to register macros within the Carbon class. + */ +trait Macro +{ + use Mixin; + + /** + * The registered macros. + * + * @var array + */ + protected static $globalMacros = []; + + /** + * The registered generic macros. + * + * @var array + */ + protected static $globalGenericMacros = []; + + /** + * Register a custom macro. + * + * @example + * ``` + * $userSettings = [ + * 'locale' => 'pt', + * 'timezone' => 'America/Sao_Paulo', + * ]; + * Carbon::macro('userFormat', function () use ($userSettings) { + * return $this->copy()->locale($userSettings['locale'])->tz($userSettings['timezone'])->calendar(); + * }); + * echo Carbon::yesterday()->hours(11)->userFormat(); + * ``` + * + * @param string $name + * @param object|callable $macro + * + * @return void + */ + public static function macro($name, $macro) + { + static::$globalMacros[$name] = $macro; + } + + /** + * Remove all macros and generic macros. + */ + public static function resetMacros() + { + static::$globalMacros = []; + static::$globalGenericMacros = []; + } + + /** + * Register a custom macro. + * + * @param object|callable $macro + * @param int $priority marco with higher priority is tried first + * + * @return void + */ + public static function genericMacro($macro, $priority = 0) + { + if (!isset(static::$globalGenericMacros[$priority])) { + static::$globalGenericMacros[$priority] = []; + krsort(static::$globalGenericMacros, SORT_NUMERIC); + } + + static::$globalGenericMacros[$priority][] = $macro; + } + + /** + * Checks if macro is registered globally. + * + * @param string $name + * + * @return bool + */ + public static function hasMacro($name) + { + return isset(static::$globalMacros[$name]); + } + + /** + * Get the raw callable macro registered globally for a given name. + * + * @param string $name + * + * @return callable|null + */ + public static function getMacro($name) + { + return static::$globalMacros[$name] ?? null; + } + + /** + * Checks if macro is registered globally or locally. + * + * @param string $name + * + * @return bool + */ + public function hasLocalMacro($name) + { + return ($this->localMacros && isset($this->localMacros[$name])) || static::hasMacro($name); + } + + /** + * Get the raw callable macro registered globally or locally for a given name. + * + * @param string $name + * + * @return callable|null + */ + public function getLocalMacro($name) + { + return ($this->localMacros ?? [])[$name] ?? static::getMacro($name); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/MagicParameter.php b/vendor/nesbot/carbon/src/Carbon/Traits/MagicParameter.php new file mode 100644 index 0000000..310a44d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/MagicParameter.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +/** + * Trait MagicParameter. + * + * Allows to retrieve parameter in magic calls by index or name. + */ +trait MagicParameter +{ + private function getMagicParameter(array $parameters, int $index, string $key, $default) + { + if (\array_key_exists($index, $parameters)) { + return $parameters[$index]; + } + + if (\array_key_exists($key, $parameters)) { + return $parameters[$key]; + } + + return $default; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Mixin.php b/vendor/nesbot/carbon/src/Carbon/Traits/Mixin.php new file mode 100644 index 0000000..5822454 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Mixin.php @@ -0,0 +1,226 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\CarbonInterface; +use Carbon\CarbonInterval; +use Carbon\CarbonPeriod; +use Closure; +use Generator; +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; +use Throwable; + +/** + * Trait Mixin. + * + * Allows mixing in entire classes with multiple macros. + */ +trait Mixin +{ + /** + * Stack of macro instance contexts. + * + * @var array + */ + protected static $macroContextStack = []; + + /** + * Mix another object into the class. + * + * @example + * ``` + * Carbon::mixin(new class { + * public function addMoon() { + * return function () { + * return $this->addDays(30); + * }; + * } + * public function subMoon() { + * return function () { + * return $this->subDays(30); + * }; + * } + * }); + * $fullMoon = Carbon::create('2018-12-22'); + * $nextFullMoon = $fullMoon->addMoon(); + * $blackMoon = Carbon::create('2019-01-06'); + * $previousBlackMoon = $blackMoon->subMoon(); + * echo "$nextFullMoon\n"; + * echo "$previousBlackMoon\n"; + * ``` + * + * @param object|string $mixin + * + * @throws ReflectionException + * + * @return void + */ + public static function mixin($mixin) + { + \is_string($mixin) && trait_exists($mixin) + ? self::loadMixinTrait($mixin) + : self::loadMixinClass($mixin); + } + + /** + * @param object|string $mixin + * + * @throws ReflectionException + */ + private static function loadMixinClass($mixin) + { + $methods = (new ReflectionClass($mixin))->getMethods( + ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED + ); + + foreach ($methods as $method) { + if ($method->isConstructor() || $method->isDestructor()) { + continue; + } + + $method->setAccessible(true); + + static::macro($method->name, $method->invoke($mixin)); + } + } + + /** + * @param string $trait + */ + private static function loadMixinTrait($trait) + { + $context = eval(self::getAnonymousClassCodeForTrait($trait)); + $className = \get_class($context); + $baseClass = static::class; + + foreach (self::getMixableMethods($context) as $name) { + $closureBase = Closure::fromCallable([$context, $name]); + + static::macro($name, function (...$parameters) use ($closureBase, $className, $baseClass) { + $downContext = isset($this) ? ($this) : new $baseClass(); + $context = isset($this) ? $this->cast($className) : new $className(); + + try { + // @ is required to handle error if not converted into exceptions + $closure = @$closureBase->bindTo($context); + } catch (Throwable $throwable) { // @codeCoverageIgnore + $closure = $closureBase; // @codeCoverageIgnore + } + + // in case of errors not converted into exceptions + $closure = $closure ?: $closureBase; + + $result = $closure(...$parameters); + + if (!($result instanceof $className)) { + return $result; + } + + if ($downContext instanceof CarbonInterface && $result instanceof CarbonInterface) { + if ($context !== $result) { + $downContext = $downContext->copy(); + } + + return $downContext + ->setTimezone($result->getTimezone()) + ->modify($result->format('Y-m-d H:i:s.u')) + ->settings($result->getSettings()); + } + + if ($downContext instanceof CarbonInterval && $result instanceof CarbonInterval) { + if ($context !== $result) { + $downContext = $downContext->copy(); + } + + $downContext->copyProperties($result); + self::copyStep($downContext, $result); + self::copyNegativeUnits($downContext, $result); + + return $downContext->settings($result->getSettings()); + } + + if ($downContext instanceof CarbonPeriod && $result instanceof CarbonPeriod) { + if ($context !== $result) { + $downContext = $downContext->copy(); + } + + return $downContext + ->setDates($result->getStartDate(), $result->getEndDate()) + ->setRecurrences($result->getRecurrences()) + ->setOptions($result->getOptions()) + ->settings($result->getSettings()); + } + + return $result; + }); + } + } + + private static function getAnonymousClassCodeForTrait(string $trait) + { + return 'return new class() extends '.static::class.' {use '.$trait.';};'; + } + + private static function getMixableMethods(self $context): Generator + { + foreach (get_class_methods($context) as $name) { + if (method_exists(static::class, $name)) { + continue; + } + + yield $name; + } + } + + /** + * Stack a Carbon context from inside calls of self::this() and execute a given action. + * + * @param static|null $context + * @param callable $callable + * + * @throws Throwable + * + * @return mixed + */ + protected static function bindMacroContext($context, callable $callable) + { + static::$macroContextStack[] = $context; + + try { + return $callable(); + } finally { + array_pop(static::$macroContextStack); + } + } + + /** + * Return the current context from inside a macro callee or a null if static. + * + * @return static|null + */ + protected static function context() + { + return end(static::$macroContextStack) ?: null; + } + + /** + * Return the current context from inside a macro callee or a new one if static. + * + * @return static + */ + protected static function this() + { + return end(static::$macroContextStack) ?: new static(); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Modifiers.php b/vendor/nesbot/carbon/src/Carbon/Traits/Modifiers.php new file mode 100644 index 0000000..39343d8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Modifiers.php @@ -0,0 +1,472 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\CarbonInterface; +use ReturnTypeWillChange; + +/** + * Trait Modifiers. + * + * Returns dates relative to current date using modifier short-hand. + */ +trait Modifiers +{ + /** + * Midday/noon hour. + * + * @var int + */ + protected static $midDayAt = 12; + + /** + * get midday/noon hour + * + * @return int + */ + public static function getMidDayAt() + { + return static::$midDayAt; + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather consider mid-day is always 12pm, then if you need to test if it's an other + * hour, test it explicitly: + * $date->format('G') == 13 + * or to set explicitly to a given hour: + * $date->setTime(13, 0, 0, 0) + * + * Set midday/noon hour + * + * @param int $hour midday hour + * + * @return void + */ + public static function setMidDayAt($hour) + { + static::$midDayAt = $hour; + } + + /** + * Modify to midday, default to self::$midDayAt + * + * @return static + */ + public function midDay() + { + return $this->setTime(static::$midDayAt, 0, 0, 0); + } + + /** + * Modify to the next occurrence of a given modifier such as a day of + * the week. If no modifier is provided, modify to the next occurrence + * of the current day of the week. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param string|int|null $modifier + * + * @return static|false + */ + public function next($modifier = null) + { + if ($modifier === null) { + $modifier = $this->dayOfWeek; + } + + return $this->change( + 'next '.(\is_string($modifier) ? $modifier : static::$days[$modifier]) + ); + } + + /** + * Go forward or backward to the next week- or weekend-day. + * + * @param bool $weekday + * @param bool $forward + * + * @return static + */ + private function nextOrPreviousDay($weekday = true, $forward = true) + { + /** @var CarbonInterface $date */ + $date = $this; + $step = $forward ? 1 : -1; + + do { + $date = $date->addDays($step); + } while ($weekday ? $date->isWeekend() : $date->isWeekday()); + + return $date; + } + + /** + * Go forward to the next weekday. + * + * @return static + */ + public function nextWeekday() + { + return $this->nextOrPreviousDay(); + } + + /** + * Go backward to the previous weekday. + * + * @return static + */ + public function previousWeekday() + { + return $this->nextOrPreviousDay(true, false); + } + + /** + * Go forward to the next weekend day. + * + * @return static + */ + public function nextWeekendDay() + { + return $this->nextOrPreviousDay(false); + } + + /** + * Go backward to the previous weekend day. + * + * @return static + */ + public function previousWeekendDay() + { + return $this->nextOrPreviousDay(false, false); + } + + /** + * Modify to the previous occurrence of a given modifier such as a day of + * the week. If no dayOfWeek is provided, modify to the previous occurrence + * of the current day of the week. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param string|int|null $modifier + * + * @return static|false + */ + public function previous($modifier = null) + { + if ($modifier === null) { + $modifier = $this->dayOfWeek; + } + + return $this->change( + 'last '.(\is_string($modifier) ? $modifier : static::$days[$modifier]) + ); + } + + /** + * Modify to the first occurrence of a given day of the week + * in the current month. If no dayOfWeek is provided, modify to the + * first day of the current month. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek + * + * @return static + */ + public function firstOfMonth($dayOfWeek = null) + { + $date = $this->startOfDay(); + + if ($dayOfWeek === null) { + return $date->day(1); + } + + return $date->modify('first '.static::$days[$dayOfWeek].' of '.$date->rawFormat('F').' '.$date->year); + } + + /** + * Modify to the last occurrence of a given day of the week + * in the current month. If no dayOfWeek is provided, modify to the + * last day of the current month. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek + * + * @return static + */ + public function lastOfMonth($dayOfWeek = null) + { + $date = $this->startOfDay(); + + if ($dayOfWeek === null) { + return $date->day($date->daysInMonth); + } + + return $date->modify('last '.static::$days[$dayOfWeek].' of '.$date->rawFormat('F').' '.$date->year); + } + + /** + * Modify to the given occurrence of a given day of the week + * in the current month. If the calculated occurrence is outside the scope + * of the current month, then return false and no modifications are made. + * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int $nth + * @param int $dayOfWeek + * + * @return mixed + */ + public function nthOfMonth($nth, $dayOfWeek) + { + $date = $this->avoidMutation()->firstOfMonth(); + $check = $date->rawFormat('Y-m'); + $date = $date->modify('+'.$nth.' '.static::$days[$dayOfWeek]); + + return $date->rawFormat('Y-m') === $check ? $this->modify((string) $date) : false; + } + + /** + * Modify to the first occurrence of a given day of the week + * in the current quarter. If no dayOfWeek is provided, modify to the + * first day of the current quarter. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek day of the week default null + * + * @return static + */ + public function firstOfQuarter($dayOfWeek = null) + { + return $this->setDate($this->year, $this->quarter * static::MONTHS_PER_QUARTER - 2, 1)->firstOfMonth($dayOfWeek); + } + + /** + * Modify to the last occurrence of a given day of the week + * in the current quarter. If no dayOfWeek is provided, modify to the + * last day of the current quarter. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek day of the week default null + * + * @return static + */ + public function lastOfQuarter($dayOfWeek = null) + { + return $this->setDate($this->year, $this->quarter * static::MONTHS_PER_QUARTER, 1)->lastOfMonth($dayOfWeek); + } + + /** + * Modify to the given occurrence of a given day of the week + * in the current quarter. If the calculated occurrence is outside the scope + * of the current quarter, then return false and no modifications are made. + * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int $nth + * @param int $dayOfWeek + * + * @return mixed + */ + public function nthOfQuarter($nth, $dayOfWeek) + { + $date = $this->avoidMutation()->day(1)->month($this->quarter * static::MONTHS_PER_QUARTER); + $lastMonth = $date->month; + $year = $date->year; + $date = $date->firstOfQuarter()->modify('+'.$nth.' '.static::$days[$dayOfWeek]); + + return ($lastMonth < $date->month || $year !== $date->year) ? false : $this->modify((string) $date); + } + + /** + * Modify to the first occurrence of a given day of the week + * in the current year. If no dayOfWeek is provided, modify to the + * first day of the current year. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek day of the week default null + * + * @return static + */ + public function firstOfYear($dayOfWeek = null) + { + return $this->month(1)->firstOfMonth($dayOfWeek); + } + + /** + * Modify to the last occurrence of a given day of the week + * in the current year. If no dayOfWeek is provided, modify to the + * last day of the current year. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek day of the week default null + * + * @return static + */ + public function lastOfYear($dayOfWeek = null) + { + return $this->month(static::MONTHS_PER_YEAR)->lastOfMonth($dayOfWeek); + } + + /** + * Modify to the given occurrence of a given day of the week + * in the current year. If the calculated occurrence is outside the scope + * of the current year, then return false and no modifications are made. + * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int $nth + * @param int $dayOfWeek + * + * @return mixed + */ + public function nthOfYear($nth, $dayOfWeek) + { + $date = $this->avoidMutation()->firstOfYear()->modify('+'.$nth.' '.static::$days[$dayOfWeek]); + + return $this->year === $date->year ? $this->modify((string) $date) : false; + } + + /** + * Modify the current instance to the average of a given instance (default now) and the current instance + * (second-precision). + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date + * + * @return static + */ + public function average($date = null) + { + return $this->addRealMicroseconds((int) ($this->diffInRealMicroseconds($this->resolveCarbon($date), false) / 2)); + } + + /** + * Get the closest date from the instance (second-precision). + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * + * @return static + */ + public function closest($date1, $date2) + { + return $this->diffInRealMicroseconds($date1) < $this->diffInRealMicroseconds($date2) ? $date1 : $date2; + } + + /** + * Get the farthest date from the instance (second-precision). + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * + * @return static + */ + public function farthest($date1, $date2) + { + return $this->diffInRealMicroseconds($date1) > $this->diffInRealMicroseconds($date2) ? $date1 : $date2; + } + + /** + * Get the minimum instance between a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return static + */ + public function min($date = null) + { + $date = $this->resolveCarbon($date); + + return $this->lt($date) ? $this : $date; + } + + /** + * Get the minimum instance between a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see min() + * + * @return static + */ + public function minimum($date = null) + { + return $this->min($date); + } + + /** + * Get the maximum instance between a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return static + */ + public function max($date = null) + { + $date = $this->resolveCarbon($date); + + return $this->gt($date) ? $this : $date; + } + + /** + * Get the maximum instance between a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see max() + * + * @return static + */ + public function maximum($date = null) + { + return $this->max($date); + } + + /** + * Calls \DateTime::modify if mutable or \DateTimeImmutable::modify else. + * + * @see https://php.net/manual/en/datetime.modify.php + * + * @return static|false + */ + #[ReturnTypeWillChange] + public function modify($modify) + { + return parent::modify((string) $modify); + } + + /** + * Similar to native modify() method of DateTime but can handle more grammars. + * + * @example + * ``` + * echo Carbon::now()->change('next 2pm'); + * ``` + * + * @link https://php.net/manual/en/datetime.modify.php + * + * @param string $modifier + * + * @return static|false + */ + public function change($modifier) + { + return $this->modify(preg_replace_callback('/^(next|previous|last)\s+(\d{1,2}(h|am|pm|:\d{1,2}(:\d{1,2})?))$/i', function ($match) { + $match[2] = str_replace('h', ':00', $match[2]); + $test = $this->avoidMutation()->modify($match[2]); + $method = $match[1] === 'next' ? 'lt' : 'gt'; + $match[1] = $test->$method($this) ? $match[1].' day' : 'today'; + + return $match[1].' '.$match[2]; + }, strtr(trim($modifier), [ + ' at ' => ' ', + 'just now' => 'now', + 'after tomorrow' => 'tomorrow +1 day', + 'before yesterday' => 'yesterday -1 day', + ]))); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Mutability.php b/vendor/nesbot/carbon/src/Carbon/Traits/Mutability.php new file mode 100644 index 0000000..561c867 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Mutability.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\Carbon; +use Carbon\CarbonImmutable; + +/** + * Trait Mutability. + * + * Utils to know if the current object is mutable or immutable and convert it. + */ +trait Mutability +{ + use Cast; + + /** + * Returns true if the current class/instance is mutable. + * + * @return bool + */ + public static function isMutable() + { + return false; + } + + /** + * Returns true if the current class/instance is immutable. + * + * @return bool + */ + public static function isImmutable() + { + return !static::isMutable(); + } + + /** + * Return a mutable copy of the instance. + * + * @return Carbon + */ + public function toMutable() + { + /** @var Carbon $date */ + $date = $this->cast(Carbon::class); + + return $date; + } + + /** + * Return a immutable copy of the instance. + * + * @return CarbonImmutable + */ + public function toImmutable() + { + /** @var CarbonImmutable $date */ + $date = $this->cast(CarbonImmutable::class); + + return $date; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/ObjectInitialisation.php b/vendor/nesbot/carbon/src/Carbon/Traits/ObjectInitialisation.php new file mode 100644 index 0000000..c77a102 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/ObjectInitialisation.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +trait ObjectInitialisation +{ + /** + * True when parent::__construct has been called. + * + * @var string + */ + protected $constructedObjectId; +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Options.php b/vendor/nesbot/carbon/src/Carbon/Traits/Options.php new file mode 100644 index 0000000..ffad4f1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Options.php @@ -0,0 +1,471 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\CarbonInterface; +use DateTimeInterface; +use Throwable; + +/** + * Trait Options. + * + * Embed base methods to change settings of Carbon classes. + * + * Depends on the following methods: + * + * @method static shiftTimezone($timezone) Set the timezone + */ +trait Options +{ + use Localization; + + /** + * Customizable PHP_INT_SIZE override. + * + * @var int + */ + public static $PHPIntSize = PHP_INT_SIZE; + + /** + * First day of week. + * + * @var int|string + */ + protected static $weekStartsAt = CarbonInterface::MONDAY; + + /** + * Last day of week. + * + * @var int|string + */ + protected static $weekEndsAt = CarbonInterface::SUNDAY; + + /** + * Days of weekend. + * + * @var array + */ + protected static $weekendDays = [ + CarbonInterface::SATURDAY, + CarbonInterface::SUNDAY, + ]; + + /** + * Format regex patterns. + * + * @var array + */ + protected static $regexFormats = [ + 'd' => '(3[01]|[12][0-9]|0[1-9])', + 'D' => '(Sun|Mon|Tue|Wed|Thu|Fri|Sat)', + 'j' => '([123][0-9]|[1-9])', + 'l' => '([a-zA-Z]{2,})', + 'N' => '([1-7])', + 'S' => '(st|nd|rd|th)', + 'w' => '([0-6])', + 'z' => '(36[0-5]|3[0-5][0-9]|[12][0-9]{2}|[1-9]?[0-9])', + 'W' => '(5[012]|[1-4][0-9]|0?[1-9])', + 'F' => '([a-zA-Z]{2,})', + 'm' => '(1[012]|0[1-9])', + 'M' => '([a-zA-Z]{3})', + 'n' => '(1[012]|[1-9])', + 't' => '(2[89]|3[01])', + 'L' => '(0|1)', + 'o' => '([1-9][0-9]{0,4})', + 'Y' => '([1-9]?[0-9]{4})', + 'y' => '([0-9]{2})', + 'a' => '(am|pm)', + 'A' => '(AM|PM)', + 'B' => '([0-9]{3})', + 'g' => '(1[012]|[1-9])', + 'G' => '(2[0-3]|1?[0-9])', + 'h' => '(1[012]|0[1-9])', + 'H' => '(2[0-3]|[01][0-9])', + 'i' => '([0-5][0-9])', + 's' => '([0-5][0-9])', + 'u' => '([0-9]{1,6})', + 'v' => '([0-9]{1,3})', + 'e' => '([a-zA-Z]{1,5})|([a-zA-Z]*\\/[a-zA-Z]*)', + 'I' => '(0|1)', + 'O' => '([+-](1[0123]|0[0-9])[0134][05])', + 'P' => '([+-](1[0123]|0[0-9]):[0134][05])', + 'p' => '(Z|[+-](1[0123]|0[0-9]):[0134][05])', + 'T' => '([a-zA-Z]{1,5})', + 'Z' => '(-?[1-5]?[0-9]{1,4})', + 'U' => '([0-9]*)', + + // The formats below are combinations of the above formats. + 'c' => '(([1-9]?[0-9]{4})-(1[012]|0[1-9])-(3[01]|[12][0-9]|0[1-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])[+-](1[012]|0[0-9]):([0134][05]))', // Y-m-dTH:i:sP + 'r' => '(([a-zA-Z]{3}), ([123][0-9]|0[1-9]) ([a-zA-Z]{3}) ([1-9]?[0-9]{4}) (2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]) [+-](1[012]|0[0-9])([0134][05]))', // D, d M Y H:i:s O + ]; + + /** + * Format modifiers (such as available in createFromFormat) regex patterns. + * + * @var array + */ + protected static $regexFormatModifiers = [ + '*' => '.+', + ' ' => '[ ]', + '#' => '[;:\\/.,()-]', + '?' => '([^a]|[a])', + '!' => '', + '|' => '', + '+' => '', + ]; + + /** + * Indicates if months should be calculated with overflow. + * Global setting. + * + * @var bool + */ + protected static $monthsOverflow = true; + + /** + * Indicates if years should be calculated with overflow. + * Global setting. + * + * @var bool + */ + protected static $yearsOverflow = true; + + /** + * Indicates if the strict mode is in use. + * Global setting. + * + * @var bool + */ + protected static $strictModeEnabled = true; + + /** + * Function to call instead of format. + * + * @var string|callable|null + */ + protected static $formatFunction; + + /** + * Function to call instead of createFromFormat. + * + * @var string|callable|null + */ + protected static $createFromFormatFunction; + + /** + * Function to call instead of parse. + * + * @var string|callable|null + */ + protected static $parseFunction; + + /** + * Indicates if months should be calculated with overflow. + * Specific setting. + * + * @var bool|null + */ + protected $localMonthsOverflow; + + /** + * Indicates if years should be calculated with overflow. + * Specific setting. + * + * @var bool|null + */ + protected $localYearsOverflow; + + /** + * Indicates if the strict mode is in use. + * Specific setting. + * + * @var bool|null + */ + protected $localStrictModeEnabled; + + /** + * Options for diffForHumans and forHumans methods. + * + * @var bool|null + */ + protected $localHumanDiffOptions; + + /** + * Format to use on string cast. + * + * @var string|null + */ + protected $localToStringFormat; + + /** + * Format to use on JSON serialization. + * + * @var string|null + */ + protected $localSerializer; + + /** + * Instance-specific macros. + * + * @var array|null + */ + protected $localMacros; + + /** + * Instance-specific generic macros. + * + * @var array|null + */ + protected $localGenericMacros; + + /** + * Function to call instead of format. + * + * @var string|callable|null + */ + protected $localFormatFunction; + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * @see settings + * + * Enable the strict mode (or disable with passing false). + * + * @param bool $strictModeEnabled + */ + public static function useStrictMode($strictModeEnabled = true) + { + static::$strictModeEnabled = $strictModeEnabled; + } + + /** + * Returns true if the strict mode is globally in use, false else. + * (It can be overridden in specific instances.) + * + * @return bool + */ + public static function isStrictModeEnabled() + { + return static::$strictModeEnabled; + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @see settings + * + * Indicates if months should be calculated with overflow. + * + * @param bool $monthsOverflow + * + * @return void + */ + public static function useMonthsOverflow($monthsOverflow = true) + { + static::$monthsOverflow = $monthsOverflow; + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @see settings + * + * Reset the month overflow behavior. + * + * @return void + */ + public static function resetMonthsOverflow() + { + static::$monthsOverflow = true; + } + + /** + * Get the month overflow global behavior (can be overridden in specific instances). + * + * @return bool + */ + public static function shouldOverflowMonths() + { + return static::$monthsOverflow; + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @see settings + * + * Indicates if years should be calculated with overflow. + * + * @param bool $yearsOverflow + * + * @return void + */ + public static function useYearsOverflow($yearsOverflow = true) + { + static::$yearsOverflow = $yearsOverflow; + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather use the ->settings() method. + * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants + * are available for quarters, years, decade, centuries, millennia (singular and plural forms). + * @see settings + * + * Reset the month overflow behavior. + * + * @return void + */ + public static function resetYearsOverflow() + { + static::$yearsOverflow = true; + } + + /** + * Get the month overflow global behavior (can be overridden in specific instances). + * + * @return bool + */ + public static function shouldOverflowYears() + { + return static::$yearsOverflow; + } + + /** + * Set specific options. + * - strictMode: true|false|null + * - monthOverflow: true|false|null + * - yearOverflow: true|false|null + * - humanDiffOptions: int|null + * - toStringFormat: string|Closure|null + * - toJsonFormat: string|Closure|null + * - locale: string|null + * - timezone: \DateTimeZone|string|int|null + * - macros: array|null + * - genericMacros: array|null + * + * @param array $settings + * + * @return $this|static + */ + public function settings(array $settings) + { + $this->localStrictModeEnabled = $settings['strictMode'] ?? null; + $this->localMonthsOverflow = $settings['monthOverflow'] ?? null; + $this->localYearsOverflow = $settings['yearOverflow'] ?? null; + $this->localHumanDiffOptions = $settings['humanDiffOptions'] ?? null; + $this->localToStringFormat = $settings['toStringFormat'] ?? null; + $this->localSerializer = $settings['toJsonFormat'] ?? null; + $this->localMacros = $settings['macros'] ?? null; + $this->localGenericMacros = $settings['genericMacros'] ?? null; + $this->localFormatFunction = $settings['formatFunction'] ?? null; + + if (isset($settings['locale'])) { + $locales = $settings['locale']; + + if (!\is_array($locales)) { + $locales = [$locales]; + } + + $this->locale(...$locales); + } + + if (isset($settings['innerTimezone'])) { + return $this->setTimezone($settings['innerTimezone']); + } + + if (isset($settings['timezone'])) { + return $this->shiftTimezone($settings['timezone']); + } + + return $this; + } + + /** + * Returns current local settings. + * + * @return array + */ + public function getSettings() + { + $settings = []; + $map = [ + 'localStrictModeEnabled' => 'strictMode', + 'localMonthsOverflow' => 'monthOverflow', + 'localYearsOverflow' => 'yearOverflow', + 'localHumanDiffOptions' => 'humanDiffOptions', + 'localToStringFormat' => 'toStringFormat', + 'localSerializer' => 'toJsonFormat', + 'localMacros' => 'macros', + 'localGenericMacros' => 'genericMacros', + 'locale' => 'locale', + 'tzName' => 'timezone', + 'localFormatFunction' => 'formatFunction', + ]; + + foreach ($map as $property => $key) { + $value = $this->$property ?? null; + + if ($value !== null && ($key !== 'locale' || $value !== 'en' || $this->localTranslator)) { + $settings[$key] = $value; + } + } + + return $settings; + } + + /** + * Show truthy properties on var_dump(). + * + * @return array + */ + public function __debugInfo() + { + $infos = array_filter(get_object_vars($this), static function ($var) { + return $var; + }); + + foreach (['dumpProperties', 'constructedObjectId', 'constructed'] as $property) { + if (isset($infos[$property])) { + unset($infos[$property]); + } + } + + $this->addExtraDebugInfos($infos); + + return $infos; + } + + protected function addExtraDebugInfos(&$infos): void + { + if ($this instanceof DateTimeInterface) { + try { + if (!isset($infos['date'])) { + $infos['date'] = $this->format(CarbonInterface::MOCK_DATETIME_FORMAT); + } + + if (!isset($infos['timezone'])) { + $infos['timezone'] = $this->tzName; + } + } catch (Throwable $exception) { + // noop + } + } + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Rounding.php b/vendor/nesbot/carbon/src/Carbon/Traits/Rounding.php new file mode 100644 index 0000000..85ff5a7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Rounding.php @@ -0,0 +1,254 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\CarbonInterface; +use Carbon\Exceptions\UnknownUnitException; + +/** + * Trait Rounding. + * + * Round, ceil, floor units. + * + * Depends on the following methods: + * + * @method static copy() + * @method static startOfWeek(int $weekStartsAt = null) + */ +trait Rounding +{ + use IntervalRounding; + + /** + * Round the current instance at the given unit with given precision if specified and the given function. + * + * @param string $unit + * @param float|int $precision + * @param string $function + * + * @return CarbonInterface + */ + public function roundUnit($unit, $precision = 1, $function = 'round') + { + $metaUnits = [ + // @call roundUnit + 'millennium' => [static::YEARS_PER_MILLENNIUM, 'year'], + // @call roundUnit + 'century' => [static::YEARS_PER_CENTURY, 'year'], + // @call roundUnit + 'decade' => [static::YEARS_PER_DECADE, 'year'], + // @call roundUnit + 'quarter' => [static::MONTHS_PER_QUARTER, 'month'], + // @call roundUnit + 'millisecond' => [1000, 'microsecond'], + ]; + $normalizedUnit = static::singularUnit($unit); + $ranges = array_merge(static::getRangesByUnit($this->daysInMonth), [ + // @call roundUnit + 'microsecond' => [0, 999999], + ]); + $factor = 1; + + if ($normalizedUnit === 'week') { + $normalizedUnit = 'day'; + $precision *= static::DAYS_PER_WEEK; + } + + if (isset($metaUnits[$normalizedUnit])) { + [$factor, $normalizedUnit] = $metaUnits[$normalizedUnit]; + } + + $precision *= $factor; + + if (!isset($ranges[$normalizedUnit])) { + throw new UnknownUnitException($unit); + } + + $found = false; + $fraction = 0; + $arguments = null; + $initialValue = null; + $factor = $this->year < 0 ? -1 : 1; + $changes = []; + $minimumInc = null; + + foreach ($ranges as $unit => [$minimum, $maximum]) { + if ($normalizedUnit === $unit) { + $arguments = [$this->$unit, $minimum]; + $initialValue = $this->$unit; + $fraction = $precision - floor($precision); + $found = true; + + continue; + } + + if ($found) { + $delta = $maximum + 1 - $minimum; + $factor /= $delta; + $fraction *= $delta; + $inc = ($this->$unit - $minimum) * $factor; + + if ($inc !== 0.0) { + $minimumInc = $minimumInc ?? ($arguments[0] / pow(2, 52)); + + // If value is still the same when adding a non-zero increment/decrement, + // it means precision got lost in the addition + if (abs($inc) < $minimumInc) { + $inc = $minimumInc * ($inc < 0 ? -1 : 1); + } + + // If greater than $precision, assume precision loss caused an overflow + if ($function !== 'floor' || abs($arguments[0] + $inc - $initialValue) >= $precision) { + $arguments[0] += $inc; + } + } + + $changes[$unit] = round( + $minimum + ($fraction ? $fraction * $function(($this->$unit - $minimum) / $fraction) : 0) + ); + + // Cannot use modulo as it lose double precision + while ($changes[$unit] >= $delta) { + $changes[$unit] -= $delta; + } + + $fraction -= floor($fraction); + } + } + + [$value, $minimum] = $arguments; + $normalizedValue = floor($function(($value - $minimum) / $precision) * $precision + $minimum); + + /** @var CarbonInterface $result */ + $result = $this; + + foreach ($changes as $unit => $value) { + $result = $result->$unit($value); + } + + return $result->$normalizedUnit($normalizedValue); + } + + /** + * Truncate the current instance at the given unit with given precision if specified. + * + * @param string $unit + * @param float|int $precision + * + * @return CarbonInterface + */ + public function floorUnit($unit, $precision = 1) + { + return $this->roundUnit($unit, $precision, 'floor'); + } + + /** + * Ceil the current instance at the given unit with given precision if specified. + * + * @param string $unit + * @param float|int $precision + * + * @return CarbonInterface + */ + public function ceilUnit($unit, $precision = 1) + { + return $this->roundUnit($unit, $precision, 'ceil'); + } + + /** + * Round the current instance second with given precision if specified. + * + * @param float|int|string|\DateInterval|null $precision + * @param string $function + * + * @return CarbonInterface + */ + public function round($precision = 1, $function = 'round') + { + return $this->roundWith($precision, $function); + } + + /** + * Round the current instance second with given precision if specified. + * + * @param float|int|string|\DateInterval|null $precision + * + * @return CarbonInterface + */ + public function floor($precision = 1) + { + return $this->round($precision, 'floor'); + } + + /** + * Ceil the current instance second with given precision if specified. + * + * @param float|int|string|\DateInterval|null $precision + * + * @return CarbonInterface + */ + public function ceil($precision = 1) + { + return $this->round($precision, 'ceil'); + } + + /** + * Round the current instance week. + * + * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week + * + * @return CarbonInterface + */ + public function roundWeek($weekStartsAt = null) + { + return $this->closest( + $this->avoidMutation()->floorWeek($weekStartsAt), + $this->avoidMutation()->ceilWeek($weekStartsAt) + ); + } + + /** + * Truncate the current instance week. + * + * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week + * + * @return CarbonInterface + */ + public function floorWeek($weekStartsAt = null) + { + return $this->startOfWeek($weekStartsAt); + } + + /** + * Ceil the current instance week. + * + * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week + * + * @return CarbonInterface + */ + public function ceilWeek($weekStartsAt = null) + { + if ($this->isMutable()) { + $startOfWeek = $this->avoidMutation()->startOfWeek($weekStartsAt); + + return $startOfWeek != $this ? + $this->startOfWeek($weekStartsAt)->addWeek() : + $this; + } + + $startOfWeek = $this->startOfWeek($weekStartsAt); + + return $startOfWeek != $this ? + $startOfWeek->addWeek() : + $this->avoidMutation(); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Serialization.php b/vendor/nesbot/carbon/src/Carbon/Traits/Serialization.php new file mode 100644 index 0000000..c1d5c5e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Serialization.php @@ -0,0 +1,326 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\Exceptions\InvalidFormatException; +use ReturnTypeWillChange; +use Throwable; + +/** + * Trait Serialization. + * + * Serialization and JSON stuff. + * + * Depends on the following properties: + * + * @property int $year + * @property int $month + * @property int $daysInMonth + * @property int $quarter + * + * Depends on the following methods: + * + * @method string|static locale(string $locale = null, string ...$fallbackLocales) + * @method string toJSON() + */ +trait Serialization +{ + use ObjectInitialisation; + + /** + * The custom Carbon JSON serializer. + * + * @var callable|null + */ + protected static $serializer; + + /** + * List of key to use for dump/serialization. + * + * @var string[] + */ + protected $dumpProperties = ['date', 'timezone_type', 'timezone']; + + /** + * Locale to dump comes here before serialization. + * + * @var string|null + */ + protected $dumpLocale; + + /** + * Embed date properties to dump in a dedicated variables so it won't overlap native + * DateTime ones. + * + * @var array|null + */ + protected $dumpDateProperties; + + /** + * Return a serialized string of the instance. + * + * @return string + */ + public function serialize() + { + return serialize($this); + } + + /** + * Create an instance from a serialized string. + * + * @param string $value + * + * @throws InvalidFormatException + * + * @return static + */ + public static function fromSerialized($value) + { + $instance = @unserialize((string) $value); + + if (!$instance instanceof static) { + throw new InvalidFormatException("Invalid serialized value: $value"); + } + + return $instance; + } + + /** + * The __set_state handler. + * + * @param string|array $dump + * + * @return static + */ + #[ReturnTypeWillChange] + public static function __set_state($dump) + { + if (\is_string($dump)) { + return static::parse($dump); + } + + /** @var \DateTimeInterface $date */ + $date = get_parent_class(static::class) && method_exists(parent::class, '__set_state') + ? parent::__set_state((array) $dump) + : (object) $dump; + + return static::instance($date); + } + + /** + * Returns the list of properties to dump on serialize() called on. + * + * Only used by PHP < 7.4. + * + * @return array + */ + public function __sleep() + { + $properties = $this->getSleepProperties(); + + if ($this->localTranslator ?? null) { + $properties[] = 'dumpLocale'; + $this->dumpLocale = $this->locale ?? null; + } + + return $properties; + } + + /** + * Returns the values to dump on serialize() called on. + * + * Only used by PHP >= 7.4. + * + * @return array + */ + public function __serialize(): array + { + // @codeCoverageIgnoreStart + if (isset($this->timezone_type, $this->timezone, $this->date)) { + return [ + 'date' => $this->date ?? null, + 'timezone_type' => $this->timezone_type, + 'timezone' => $this->timezone ?? null, + ]; + } + // @codeCoverageIgnoreEnd + + $timezone = $this->getTimezone(); + $export = [ + 'date' => $this->format('Y-m-d H:i:s.u'), + 'timezone_type' => $timezone->getType(), + 'timezone' => $timezone->getName(), + ]; + + // @codeCoverageIgnoreStart + if (\extension_loaded('msgpack') && isset($this->constructedObjectId)) { + $export['dumpDateProperties'] = [ + 'date' => $this->format('Y-m-d H:i:s.u'), + 'timezone' => serialize($this->timezone ?? null), + ]; + } + // @codeCoverageIgnoreEnd + + if ($this->localTranslator ?? null) { + $export['dumpLocale'] = $this->locale ?? null; + } + + return $export; + } + + /** + * Set locale if specified on unserialize() called. + * + * Only used by PHP < 7.4. + * + * @return void + */ + #[ReturnTypeWillChange] + public function __wakeup() + { + if (parent::class && method_exists(parent::class, '__wakeup')) { + // @codeCoverageIgnoreStart + try { + parent::__wakeup(); + } catch (Throwable $exception) { + try { + // FatalError occurs when calling msgpack_unpack() in PHP 7.4 or later. + ['date' => $date, 'timezone' => $timezone] = $this->dumpDateProperties; + parent::__construct($date, unserialize($timezone)); + } catch (Throwable $ignoredException) { + throw $exception; + } + } + // @codeCoverageIgnoreEnd + } + + $this->constructedObjectId = spl_object_hash($this); + + if (isset($this->dumpLocale)) { + $this->locale($this->dumpLocale); + $this->dumpLocale = null; + } + + $this->cleanupDumpProperties(); + } + + /** + * Set locale if specified on unserialize() called. + * + * Only used by PHP >= 7.4. + * + * @return void + */ + public function __unserialize(array $data): void + { + // @codeCoverageIgnoreStart + try { + $this->__construct($data['date'] ?? null, $data['timezone'] ?? null); + } catch (Throwable $exception) { + if (!isset($data['dumpDateProperties']['date'], $data['dumpDateProperties']['timezone'])) { + throw $exception; + } + + try { + // FatalError occurs when calling msgpack_unpack() in PHP 7.4 or later. + ['date' => $date, 'timezone' => $timezone] = $data['dumpDateProperties']; + $this->__construct($date, unserialize($timezone)); + } catch (Throwable $ignoredException) { + throw $exception; + } + } + // @codeCoverageIgnoreEnd + + if (isset($data['dumpLocale'])) { + $this->locale($data['dumpLocale']); + } + } + + /** + * Prepare the object for JSON serialization. + * + * @return array|string + */ + #[ReturnTypeWillChange] + public function jsonSerialize() + { + $serializer = $this->localSerializer ?? static::$serializer; + + if ($serializer) { + return \is_string($serializer) + ? $this->rawFormat($serializer) + : $serializer($this); + } + + return $this->toJSON(); + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather transform Carbon object before the serialization. + * + * JSON serialize all Carbon instances using the given callback. + * + * @param callable $callback + * + * @return void + */ + public static function serializeUsing($callback) + { + static::$serializer = $callback; + } + + /** + * Cleanup properties attached to the public scope of DateTime when a dump of the date is requested. + * foreach ($date as $_) {} + * serializer($date) + * var_export($date) + * get_object_vars($date) + */ + public function cleanupDumpProperties() + { + // @codeCoverageIgnoreStart + if (PHP_VERSION < 8.2) { + foreach ($this->dumpProperties as $property) { + if (isset($this->$property)) { + unset($this->$property); + } + } + } + // @codeCoverageIgnoreEnd + + return $this; + } + + private function getSleepProperties(): array + { + $properties = $this->dumpProperties; + + // @codeCoverageIgnoreStart + if (!\extension_loaded('msgpack')) { + return $properties; + } + + if (isset($this->constructedObjectId)) { + $this->dumpDateProperties = [ + 'date' => $this->format('Y-m-d H:i:s.u'), + 'timezone' => serialize($this->timezone ?? null), + ]; + + $properties[] = 'dumpDateProperties'; + } + + return $properties; + // @codeCoverageIgnoreEnd + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Test.php b/vendor/nesbot/carbon/src/Carbon/Traits/Test.php new file mode 100644 index 0000000..f23c72e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Test.php @@ -0,0 +1,228 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\CarbonInterface; +use Carbon\CarbonTimeZone; +use Closure; +use DateTimeImmutable; +use DateTimeInterface; +use InvalidArgumentException; +use Throwable; + +trait Test +{ + /////////////////////////////////////////////////////////////////// + ///////////////////////// TESTING AIDS //////////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * A test Carbon instance to be returned when now instances are created. + * + * @var Closure|static|null + */ + protected static $testNow; + + /** + * The timezone to resto to when clearing the time mock. + * + * @var string|null + */ + protected static $testDefaultTimezone; + + /** + * Set a Carbon instance (real or mock) to be returned when a "now" + * instance is created. The provided instance will be returned + * specifically under the following conditions: + * - A call to the static now() method, ex. Carbon::now() + * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) + * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') + * - When a string containing the desired time is passed to Carbon::parse(). + * + * Note the timezone parameter was left out of the examples above and + * has no affect as the mock value will be returned regardless of its value. + * + * Only the moment is mocked with setTestNow(), the timezone will still be the one passed + * as parameter of date_default_timezone_get() as a fallback (see setTestNowAndTimezone()). + * + * To clear the test instance call this method using the default + * parameter of null. + * + * /!\ Use this method for unit tests only. + * + * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance + */ + public static function setTestNow($testNow = null) + { + static::$testNow = $testNow instanceof self || $testNow instanceof Closure + ? $testNow + : static::make($testNow); + } + + /** + * Set a Carbon instance (real or mock) to be returned when a "now" + * instance is created. The provided instance will be returned + * specifically under the following conditions: + * - A call to the static now() method, ex. Carbon::now() + * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) + * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') + * - When a string containing the desired time is passed to Carbon::parse(). + * + * It will also align default timezone (e.g. call date_default_timezone_set()) with + * the second argument or if null, with the timezone of the given date object. + * + * To clear the test instance call this method using the default + * parameter of null. + * + * /!\ Use this method for unit tests only. + * + * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance + */ + public static function setTestNowAndTimezone($testNow = null, $tz = null) + { + if ($testNow) { + self::$testDefaultTimezone = self::$testDefaultTimezone ?? date_default_timezone_get(); + } + + $useDateInstanceTimezone = $testNow instanceof DateTimeInterface; + + if ($useDateInstanceTimezone) { + self::setDefaultTimezone($testNow->getTimezone()->getName(), $testNow); + } + + static::setTestNow($testNow); + + if (!$useDateInstanceTimezone) { + $now = static::getMockedTestNow(\func_num_args() === 1 ? null : $tz); + $tzName = $now ? $now->tzName : null; + self::setDefaultTimezone($tzName ?? self::$testDefaultTimezone ?? 'UTC', $now); + } + + if (!$testNow) { + self::$testDefaultTimezone = null; + } + } + + /** + * Temporarily sets a static date to be used within the callback. + * Using setTestNow to set the date, executing the callback, then + * clearing the test instance. + * + * /!\ Use this method for unit tests only. + * + * @template T + * + * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance + * @param Closure(): T $callback + * + * @return T + */ + public static function withTestNow($testNow, $callback) + { + static::setTestNow($testNow); + + try { + $result = $callback(); + } finally { + static::setTestNow(); + } + + return $result; + } + + /** + * Get the Carbon instance (real or mock) to be returned when a "now" + * instance is created. + * + * @return Closure|static the current instance used for testing + */ + public static function getTestNow() + { + return static::$testNow; + } + + /** + * Determine if there is a valid test instance set. A valid test instance + * is anything that is not null. + * + * @return bool true if there is a test instance, otherwise false + */ + public static function hasTestNow() + { + return static::getTestNow() !== null; + } + + /** + * Get the mocked date passed in setTestNow() and if it's a Closure, execute it. + * + * @param string|\DateTimeZone $tz + * + * @return \Carbon\CarbonImmutable|\Carbon\Carbon|null + */ + protected static function getMockedTestNow($tz) + { + $testNow = static::getTestNow(); + + if ($testNow instanceof Closure) { + $realNow = new DateTimeImmutable('now'); + $testNow = $testNow(static::parse( + $realNow->format('Y-m-d H:i:s.u'), + $tz ?: $realNow->getTimezone() + )); + } + /* @var \Carbon\CarbonImmutable|\Carbon\Carbon|null $testNow */ + + return $testNow instanceof CarbonInterface + ? $testNow->avoidMutation()->tz($tz) + : $testNow; + } + + protected static function mockConstructorParameters(&$time, $tz) + { + /** @var \Carbon\CarbonImmutable|\Carbon\Carbon $testInstance */ + $testInstance = clone static::getMockedTestNow($tz); + + if (static::hasRelativeKeywords($time)) { + $testInstance = $testInstance->modify($time); + } + + $time = $testInstance instanceof self + ? $testInstance->rawFormat(static::MOCK_DATETIME_FORMAT) + : $testInstance->format(static::MOCK_DATETIME_FORMAT); + } + + private static function setDefaultTimezone($timezone, DateTimeInterface $date = null) + { + $previous = null; + $success = false; + + try { + $success = date_default_timezone_set($timezone); + } catch (Throwable $exception) { + $previous = $exception; + } + + if (!$success) { + $suggestion = @CarbonTimeZone::create($timezone)->toRegionName($date); + + throw new InvalidArgumentException( + "Timezone ID '$timezone' is invalid". + ($suggestion && $suggestion !== $timezone ? ", did you mean '$suggestion'?" : '.')."\n". + "It must be one of the IDs from DateTimeZone::listIdentifiers(),\n". + 'For the record, hours/minutes offset are relevant only for a particular moment, '. + 'but not as a default timezone.', + 0, + $previous + ); + } + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Timestamp.php b/vendor/nesbot/carbon/src/Carbon/Traits/Timestamp.php new file mode 100644 index 0000000..88a465c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Timestamp.php @@ -0,0 +1,198 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +/** + * Trait Timestamp. + */ +trait Timestamp +{ + /** + * Create a Carbon instance from a timestamp and set the timezone (use default one if not specified). + * + * Timestamp input can be given as int, float or a string containing one or more numbers. + * + * @param float|int|string $timestamp + * @param \DateTimeZone|string|null $tz + * + * @return static + */ + public static function createFromTimestamp($timestamp, $tz = null) + { + return static::createFromTimestampUTC($timestamp)->setTimezone($tz); + } + + /** + * Create a Carbon instance from an timestamp keeping the timezone to UTC. + * + * Timestamp input can be given as int, float or a string containing one or more numbers. + * + * @param float|int|string $timestamp + * + * @return static + */ + public static function createFromTimestampUTC($timestamp) + { + [$integer, $decimal] = self::getIntegerAndDecimalParts($timestamp); + $delta = floor($decimal / static::MICROSECONDS_PER_SECOND); + $integer += $delta; + $decimal -= $delta * static::MICROSECONDS_PER_SECOND; + $decimal = str_pad((string) $decimal, 6, '0', STR_PAD_LEFT); + + return static::rawCreateFromFormat('U u', "$integer $decimal"); + } + + /** + * Create a Carbon instance from a timestamp in milliseconds. + * + * Timestamp input can be given as int, float or a string containing one or more numbers. + * + * @param float|int|string $timestamp + * + * @return static + */ + public static function createFromTimestampMsUTC($timestamp) + { + [$milliseconds, $microseconds] = self::getIntegerAndDecimalParts($timestamp, 3); + $sign = $milliseconds < 0 || ($milliseconds === 0.0 && $microseconds < 0) ? -1 : 1; + $milliseconds = abs($milliseconds); + $microseconds = $sign * abs($microseconds) + static::MICROSECONDS_PER_MILLISECOND * ($milliseconds % static::MILLISECONDS_PER_SECOND); + $seconds = $sign * floor($milliseconds / static::MILLISECONDS_PER_SECOND); + $delta = floor($microseconds / static::MICROSECONDS_PER_SECOND); + $seconds += $delta; + $microseconds -= $delta * static::MICROSECONDS_PER_SECOND; + $microseconds = str_pad($microseconds, 6, '0', STR_PAD_LEFT); + + return static::rawCreateFromFormat('U u', "$seconds $microseconds"); + } + + /** + * Create a Carbon instance from a timestamp in milliseconds. + * + * Timestamp input can be given as int, float or a string containing one or more numbers. + * + * @param float|int|string $timestamp + * @param \DateTimeZone|string|null $tz + * + * @return static + */ + public static function createFromTimestampMs($timestamp, $tz = null) + { + return static::createFromTimestampMsUTC($timestamp) + ->setTimezone($tz); + } + + /** + * Set the instance's timestamp. + * + * Timestamp input can be given as int, float or a string containing one or more numbers. + * + * @param float|int|string $unixTimestamp + * + * @return static + */ + public function timestamp($unixTimestamp) + { + return $this->setTimestamp($unixTimestamp); + } + + /** + * Returns a timestamp rounded with the given precision (6 by default). + * + * @example getPreciseTimestamp() 1532087464437474 (microsecond maximum precision) + * @example getPreciseTimestamp(6) 1532087464437474 + * @example getPreciseTimestamp(5) 153208746443747 (1/100000 second precision) + * @example getPreciseTimestamp(4) 15320874644375 (1/10000 second precision) + * @example getPreciseTimestamp(3) 1532087464437 (millisecond precision) + * @example getPreciseTimestamp(2) 153208746444 (1/100 second precision) + * @example getPreciseTimestamp(1) 15320874644 (1/10 second precision) + * @example getPreciseTimestamp(0) 1532087464 (second precision) + * @example getPreciseTimestamp(-1) 153208746 (10 second precision) + * @example getPreciseTimestamp(-2) 15320875 (100 second precision) + * + * @param int $precision + * + * @return float + */ + public function getPreciseTimestamp($precision = 6) + { + return round(((float) $this->rawFormat('Uu')) / pow(10, 6 - $precision)); + } + + /** + * Returns the milliseconds timestamps used amongst other by Date javascript objects. + * + * @return float + */ + public function valueOf() + { + return $this->getPreciseTimestamp(3); + } + + /** + * Returns the timestamp with millisecond precision. + * + * @return int + */ + public function getTimestampMs() + { + return (int) $this->getPreciseTimestamp(3); + } + + /** + * @alias getTimestamp + * + * Returns the UNIX timestamp for the current date. + * + * @return int + */ + public function unix() + { + return $this->getTimestamp(); + } + + /** + * Return an array with integer part digits and decimals digits split from one or more positive numbers + * (such as timestamps) as string with the given number of decimals (6 by default). + * + * By splitting integer and decimal, this method obtain a better precision than + * number_format when the input is a string. + * + * @param float|int|string $numbers one or more numbers + * @param int $decimals number of decimals precision (6 by default) + * + * @return array 0-index is integer part, 1-index is decimal part digits + */ + private static function getIntegerAndDecimalParts($numbers, $decimals = 6) + { + if (\is_int($numbers) || \is_float($numbers)) { + $numbers = number_format($numbers, $decimals, '.', ''); + } + + $sign = str_starts_with($numbers, '-') ? -1 : 1; + $integer = 0; + $decimal = 0; + + foreach (preg_split('`[^\d.]+`', $numbers) as $chunk) { + [$integerPart, $decimalPart] = explode('.', "$chunk."); + + $integer += (int) $integerPart; + $decimal += (float) ("0.$decimalPart"); + } + + $overflow = floor($decimal); + $integer += $overflow; + $decimal -= $overflow; + + return [$sign * $integer, $decimal === 0.0 ? 0.0 : $sign * round($decimal * pow(10, $decimals))]; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/ToStringFormat.php b/vendor/nesbot/carbon/src/Carbon/Traits/ToStringFormat.php new file mode 100644 index 0000000..a81164f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/ToStringFormat.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Closure; + +/** + * Trait ToStringFormat. + * + * Handle global format customization for string cast of the object. + */ +trait ToStringFormat +{ + /** + * Format to use for __toString method when type juggling occurs. + * + * @var string|Closure|null + */ + protected static $toStringFormat; + + /** + * Reset the format used to the default when type juggling a Carbon instance to a string + * + * @return void + */ + public static function resetToStringFormat() + { + static::setToStringFormat(null); + } + + /** + * @deprecated To avoid conflict between different third-party libraries, static setters should not be used. + * You should rather let Carbon object being cast to string with DEFAULT_TO_STRING_FORMAT, and + * use other method or custom format passed to format() method if you need to dump another string + * format. + * + * Set the default format used when type juggling a Carbon instance to a string. + * + * @param string|Closure|null $format + * + * @return void + */ + public static function setToStringFormat($format) + { + static::$toStringFormat = $format; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Units.php b/vendor/nesbot/carbon/src/Carbon/Traits/Units.php new file mode 100644 index 0000000..5be14ec --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Units.php @@ -0,0 +1,412 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +use Carbon\CarbonConverterInterface; +use Carbon\CarbonInterface; +use Carbon\CarbonInterval; +use Carbon\Exceptions\UnitException; +use Closure; +use DateInterval; +use DateMalformedStringException; +use ReturnTypeWillChange; + +/** + * Trait Units. + * + * Add, subtract and set units. + */ +trait Units +{ + /** + * Add seconds to the instance using timestamp. Positive $value travels + * forward while negative $value travels into the past. + * + * @param string $unit + * @param int $value + * + * @return static + */ + public function addRealUnit($unit, $value = 1) + { + switch ($unit) { + // @call addRealUnit + case 'micro': + + // @call addRealUnit + case 'microsecond': + /* @var CarbonInterface $this */ + $diff = $this->microsecond + $value; + $time = $this->getTimestamp(); + $seconds = (int) floor($diff / static::MICROSECONDS_PER_SECOND); + $time += $seconds; + $diff -= $seconds * static::MICROSECONDS_PER_SECOND; + $microtime = str_pad((string) $diff, 6, '0', STR_PAD_LEFT); + $tz = $this->tz; + + return $this->tz('UTC')->modify("@$time.$microtime")->tz($tz); + + // @call addRealUnit + case 'milli': + // @call addRealUnit + case 'millisecond': + return $this->addRealUnit('microsecond', $value * static::MICROSECONDS_PER_MILLISECOND); + + // @call addRealUnit + case 'second': + break; + + // @call addRealUnit + case 'minute': + $value *= static::SECONDS_PER_MINUTE; + + break; + + // @call addRealUnit + case 'hour': + $value *= static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; + + break; + + // @call addRealUnit + case 'day': + $value *= static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; + + break; + + // @call addRealUnit + case 'week': + $value *= static::DAYS_PER_WEEK * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; + + break; + + // @call addRealUnit + case 'month': + $value *= 30 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; + + break; + + // @call addRealUnit + case 'quarter': + $value *= static::MONTHS_PER_QUARTER * 30 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; + + break; + + // @call addRealUnit + case 'year': + $value *= 365 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; + + break; + + // @call addRealUnit + case 'decade': + $value *= static::YEARS_PER_DECADE * 365 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; + + break; + + // @call addRealUnit + case 'century': + $value *= static::YEARS_PER_CENTURY * 365 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; + + break; + + // @call addRealUnit + case 'millennium': + $value *= static::YEARS_PER_MILLENNIUM * 365 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE; + + break; + + default: + if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) { + throw new UnitException("Invalid unit for real timestamp add/sub: '$unit'"); + } + + return $this; + } + + /* @var CarbonInterface $this */ + return $this->setTimestamp((int) ($this->getTimestamp() + $value)); + } + + public function subRealUnit($unit, $value = 1) + { + return $this->addRealUnit($unit, -$value); + } + + /** + * Returns true if a property can be changed via setter. + * + * @param string $unit + * + * @return bool + */ + public static function isModifiableUnit($unit) + { + static $modifiableUnits = [ + // @call addUnit + 'millennium', + // @call addUnit + 'century', + // @call addUnit + 'decade', + // @call addUnit + 'quarter', + // @call addUnit + 'week', + // @call addUnit + 'weekday', + ]; + + return \in_array($unit, $modifiableUnits, true) || \in_array($unit, static::$units, true); + } + + /** + * Call native PHP DateTime/DateTimeImmutable add() method. + * + * @param DateInterval $interval + * + * @return static + */ + public function rawAdd(DateInterval $interval) + { + return parent::add($interval); + } + + /** + * Add given units or interval to the current instance. + * + * @example $date->add('hour', 3) + * @example $date->add(15, 'days') + * @example $date->add(CarbonInterval::days(4)) + * + * @param string|DateInterval|Closure|CarbonConverterInterface $unit + * @param int $value + * @param bool|null $overflow + * + * @return static + */ + #[ReturnTypeWillChange] + public function add($unit, $value = 1, $overflow = null) + { + if (\is_string($unit) && \func_num_args() === 1) { + $unit = CarbonInterval::make($unit, [], true); + } + + if ($unit instanceof CarbonConverterInterface) { + return $this->resolveCarbon($unit->convertDate($this, false)); + } + + if ($unit instanceof Closure) { + return $this->resolveCarbon($unit($this, false)); + } + + if ($unit instanceof DateInterval) { + return parent::add($unit); + } + + if (is_numeric($unit)) { + [$value, $unit] = [$unit, $value]; + } + + return $this->addUnit($unit, $value, $overflow); + } + + /** + * Add given units to the current instance. + * + * @param string $unit + * @param int $value + * @param bool|null $overflow + * + * @return static + */ + public function addUnit($unit, $value = 1, $overflow = null) + { + $originalArgs = \func_get_args(); + + $date = $this; + + if (!is_numeric($value) || !(float) $value) { + return $date->isMutable() ? $date : $date->avoidMutation(); + } + + $unit = self::singularUnit($unit); + $metaUnits = [ + 'millennium' => [static::YEARS_PER_MILLENNIUM, 'year'], + 'century' => [static::YEARS_PER_CENTURY, 'year'], + 'decade' => [static::YEARS_PER_DECADE, 'year'], + 'quarter' => [static::MONTHS_PER_QUARTER, 'month'], + ]; + + if (isset($metaUnits[$unit])) { + [$factor, $unit] = $metaUnits[$unit]; + $value *= $factor; + } + + if ($unit === 'weekday') { + $weekendDays = static::getWeekendDays(); + + if ($weekendDays !== [static::SATURDAY, static::SUNDAY]) { + $absoluteValue = abs($value); + $sign = $value / max(1, $absoluteValue); + $weekDaysCount = 7 - min(6, \count(array_unique($weekendDays))); + $weeks = floor($absoluteValue / $weekDaysCount); + + for ($diff = $absoluteValue % $weekDaysCount; $diff; $diff--) { + /** @var static $date */ + $date = $date->addDays($sign); + + while (\in_array($date->dayOfWeek, $weekendDays, true)) { + $date = $date->addDays($sign); + } + } + + $value = $weeks * $sign; + $unit = 'week'; + } + + $timeString = $date->toTimeString(); + } elseif ($canOverflow = (\in_array($unit, [ + 'month', + 'year', + ]) && ($overflow === false || ( + $overflow === null && + ($ucUnit = ucfirst($unit).'s') && + !($this->{'local'.$ucUnit.'Overflow'} ?? static::{'shouldOverflow'.$ucUnit}()) + )))) { + $day = $date->day; + } + + $value = (int) $value; + + if ($unit === 'milli' || $unit === 'millisecond') { + $unit = 'microsecond'; + $value *= static::MICROSECONDS_PER_MILLISECOND; + } + + // Work-around for bug https://bugs.php.net/bug.php?id=75642 + if ($unit === 'micro' || $unit === 'microsecond') { + $microseconds = $this->micro + $value; + $second = (int) floor($microseconds / static::MICROSECONDS_PER_SECOND); + $microseconds %= static::MICROSECONDS_PER_SECOND; + if ($microseconds < 0) { + $microseconds += static::MICROSECONDS_PER_SECOND; + } + $date = $date->microseconds($microseconds); + $unit = 'second'; + $value = $second; + } + + try { + $date = $date->modify("$value $unit"); + + if (isset($timeString)) { + $date = $date->setTimeFromTimeString($timeString); + } elseif (isset($canOverflow, $day) && $canOverflow && $day !== $date->day) { + $date = $date->modify('last day of previous month'); + } + } catch (DateMalformedStringException $ignoredException) { // @codeCoverageIgnore + $date = null; // @codeCoverageIgnore + } + + if (!$date) { + throw new UnitException('Unable to add unit '.var_export($originalArgs, true)); + } + + return $date; + } + + /** + * Subtract given units to the current instance. + * + * @param string $unit + * @param int $value + * @param bool|null $overflow + * + * @return static + */ + public function subUnit($unit, $value = 1, $overflow = null) + { + return $this->addUnit($unit, -$value, $overflow); + } + + /** + * Call native PHP DateTime/DateTimeImmutable sub() method. + * + * @param DateInterval $interval + * + * @return static + */ + public function rawSub(DateInterval $interval) + { + return parent::sub($interval); + } + + /** + * Subtract given units or interval to the current instance. + * + * @example $date->sub('hour', 3) + * @example $date->sub(15, 'days') + * @example $date->sub(CarbonInterval::days(4)) + * + * @param string|DateInterval|Closure|CarbonConverterInterface $unit + * @param int $value + * @param bool|null $overflow + * + * @return static + */ + #[ReturnTypeWillChange] + public function sub($unit, $value = 1, $overflow = null) + { + if (\is_string($unit) && \func_num_args() === 1) { + $unit = CarbonInterval::make($unit, [], true); + } + + if ($unit instanceof CarbonConverterInterface) { + return $this->resolveCarbon($unit->convertDate($this, true)); + } + + if ($unit instanceof Closure) { + return $this->resolveCarbon($unit($this, true)); + } + + if ($unit instanceof DateInterval) { + return parent::sub($unit); + } + + if (is_numeric($unit)) { + [$value, $unit] = [$unit, $value]; + } + + return $this->addUnit($unit, -(float) $value, $overflow); + } + + /** + * Subtract given units or interval to the current instance. + * + * @see sub() + * + * @param string|DateInterval $unit + * @param int $value + * @param bool|null $overflow + * + * @return static + */ + public function subtract($unit, $value = 1, $overflow = null) + { + if (\is_string($unit) && \func_num_args() === 1) { + $unit = CarbonInterval::make($unit, [], true); + } + + return $this->sub($unit, $value, $overflow); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Week.php b/vendor/nesbot/carbon/src/Carbon/Traits/Week.php new file mode 100644 index 0000000..6f14814 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Traits/Week.php @@ -0,0 +1,219 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Traits; + +/** + * Trait Week. + * + * week and ISO week number, year and count in year. + * + * Depends on the following properties: + * + * @property int $daysInYear + * @property int $dayOfWeek + * @property int $dayOfYear + * @property int $year + * + * Depends on the following methods: + * + * @method static addWeeks(int $weeks = 1) + * @method static copy() + * @method static dayOfYear(int $dayOfYear) + * @method string getTranslationMessage(string $key, ?string $locale = null, ?string $default = null, $translator = null) + * @method static next(int|string $day = null) + * @method static startOfWeek(int $day = 1) + * @method static subWeeks(int $weeks = 1) + * @method static year(int $year = null) + */ +trait Week +{ + /** + * Set/get the week number of year using given first day of week and first + * day of year included in the first week. Or use ISO format if no settings + * given. + * + * @param int|null $year if null, act as a getter, if not null, set the year and return current instance. + * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday) + * @param int|null $dayOfYear first day of year included in the week #1 + * + * @return int|static + */ + public function isoWeekYear($year = null, $dayOfWeek = null, $dayOfYear = null) + { + return $this->weekYear( + $year, + $dayOfWeek ?? 1, + $dayOfYear ?? 4 + ); + } + + /** + * Set/get the week number of year using given first day of week and first + * day of year included in the first week. Or use US format if no settings + * given (Sunday / Jan 6). + * + * @param int|null $year if null, act as a getter, if not null, set the year and return current instance. + * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday) + * @param int|null $dayOfYear first day of year included in the week #1 + * + * @return int|static + */ + public function weekYear($year = null, $dayOfWeek = null, $dayOfYear = null) + { + $dayOfWeek = $dayOfWeek ?? $this->getTranslationMessage('first_day_of_week') ?? 0; + $dayOfYear = $dayOfYear ?? $this->getTranslationMessage('day_of_first_week_of_year') ?? 1; + + if ($year !== null) { + $year = (int) round($year); + + if ($this->weekYear(null, $dayOfWeek, $dayOfYear) === $year) { + return $this->avoidMutation(); + } + + $week = $this->week(null, $dayOfWeek, $dayOfYear); + $day = $this->dayOfWeek; + $date = $this->year($year); + switch ($date->weekYear(null, $dayOfWeek, $dayOfYear) - $year) { + case 1: + $date = $date->subWeeks(26); + + break; + case -1: + $date = $date->addWeeks(26); + + break; + } + + $date = $date->addWeeks($week - $date->week(null, $dayOfWeek, $dayOfYear))->startOfWeek($dayOfWeek); + + if ($date->dayOfWeek === $day) { + return $date; + } + + return $date->next($day); + } + + $year = $this->year; + $day = $this->dayOfYear; + $date = $this->avoidMutation()->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek); + + if ($date->year === $year && $day < $date->dayOfYear) { + return $year - 1; + } + + $date = $this->avoidMutation()->addYear()->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek); + + if ($date->year === $year && $day >= $date->dayOfYear) { + return $year + 1; + } + + return $year; + } + + /** + * Get the number of weeks of the current week-year using given first day of week and first + * day of year included in the first week. Or use ISO format if no settings + * given. + * + * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday) + * @param int|null $dayOfYear first day of year included in the week #1 + * + * @return int + */ + public function isoWeeksInYear($dayOfWeek = null, $dayOfYear = null) + { + return $this->weeksInYear( + $dayOfWeek ?? 1, + $dayOfYear ?? 4 + ); + } + + /** + * Get the number of weeks of the current week-year using given first day of week and first + * day of year included in the first week. Or use US format if no settings + * given (Sunday / Jan 6). + * + * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday) + * @param int|null $dayOfYear first day of year included in the week #1 + * + * @return int + */ + public function weeksInYear($dayOfWeek = null, $dayOfYear = null) + { + $dayOfWeek = $dayOfWeek ?? $this->getTranslationMessage('first_day_of_week') ?? 0; + $dayOfYear = $dayOfYear ?? $this->getTranslationMessage('day_of_first_week_of_year') ?? 1; + $year = $this->year; + $start = $this->avoidMutation()->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek); + $startDay = $start->dayOfYear; + if ($start->year !== $year) { + $startDay -= $start->daysInYear; + } + $end = $this->avoidMutation()->addYear()->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek); + $endDay = $end->dayOfYear; + if ($end->year !== $year) { + $endDay += $this->daysInYear; + } + + return (int) round(($endDay - $startDay) / 7); + } + + /** + * Get/set the week number using given first day of week and first + * day of year included in the first week. Or use US format if no settings + * given (Sunday / Jan 6). + * + * @param int|null $week + * @param int|null $dayOfWeek + * @param int|null $dayOfYear + * + * @return int|static + */ + public function week($week = null, $dayOfWeek = null, $dayOfYear = null) + { + $date = $this; + $dayOfWeek = $dayOfWeek ?? $this->getTranslationMessage('first_day_of_week') ?? 0; + $dayOfYear = $dayOfYear ?? $this->getTranslationMessage('day_of_first_week_of_year') ?? 1; + + if ($week !== null) { + return $date->addWeeks(round($week) - $this->week(null, $dayOfWeek, $dayOfYear)); + } + + $start = $date->avoidMutation()->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek); + $end = $date->avoidMutation()->startOfWeek($dayOfWeek); + if ($start > $end) { + $start = $start->subWeeks(26)->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek); + } + $week = (int) ($start->diffInDays($end) / 7 + 1); + + return $week > $end->weeksInYear($dayOfWeek, $dayOfYear) ? 1 : $week; + } + + /** + * Get/set the week number using given first day of week and first + * day of year included in the first week. Or use ISO format if no settings + * given. + * + * @param int|null $week + * @param int|null $dayOfWeek + * @param int|null $dayOfYear + * + * @return int|static + */ + public function isoWeek($week = null, $dayOfWeek = null, $dayOfYear = null) + { + return $this->week( + $week, + $dayOfWeek ?? 1, + $dayOfYear ?? 4 + ); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Translator.php b/vendor/nesbot/carbon/src/Carbon/Translator.php new file mode 100644 index 0000000..491c9e7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Translator.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use ReflectionMethod; +use Symfony\Component\Translation; +use Symfony\Contracts\Translation\TranslatorInterface; + +$transMethod = new ReflectionMethod( + class_exists(TranslatorInterface::class) + ? TranslatorInterface::class + : Translation\Translator::class, + 'trans' +); + +require $transMethod->hasReturnType() + ? __DIR__.'/../../lazy/Carbon/TranslatorStrongType.php' + : __DIR__.'/../../lazy/Carbon/TranslatorWeakType.php'; + +class Translator extends LazyTranslator +{ + // Proxy dynamically loaded LazyTranslator in a static way +} diff --git a/vendor/nesbot/carbon/src/Carbon/TranslatorImmutable.php b/vendor/nesbot/carbon/src/Carbon/TranslatorImmutable.php new file mode 100644 index 0000000..ce6b2f9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/TranslatorImmutable.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Carbon\Exceptions\ImmutableException; +use Symfony\Component\Config\ConfigCacheFactoryInterface; +use Symfony\Component\Translation\Formatter\MessageFormatterInterface; + +class TranslatorImmutable extends Translator +{ + /** @var bool */ + private $constructed = false; + + public function __construct($locale, MessageFormatterInterface $formatter = null, $cacheDir = null, $debug = false) + { + parent::__construct($locale, $formatter, $cacheDir, $debug); + $this->constructed = true; + } + + /** + * @codeCoverageIgnore + */ + public function setDirectories(array $directories) + { + $this->disallowMutation(__METHOD__); + + return parent::setDirectories($directories); + } + + public function setLocale($locale) + { + $this->disallowMutation(__METHOD__); + + return parent::setLocale($locale); + } + + /** + * @codeCoverageIgnore + */ + public function setMessages($locale, $messages) + { + $this->disallowMutation(__METHOD__); + + return parent::setMessages($locale, $messages); + } + + /** + * @codeCoverageIgnore + */ + public function setTranslations($messages) + { + $this->disallowMutation(__METHOD__); + + return parent::setTranslations($messages); + } + + /** + * @codeCoverageIgnore + */ + public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory): void + { + $this->disallowMutation(__METHOD__); + + parent::setConfigCacheFactory($configCacheFactory); + } + + public function resetMessages($locale = null) + { + $this->disallowMutation(__METHOD__); + + return parent::resetMessages($locale); + } + + /** + * @codeCoverageIgnore + */ + public function setFallbackLocales(array $locales) + { + $this->disallowMutation(__METHOD__); + + parent::setFallbackLocales($locales); + } + + private function disallowMutation($method) + { + if ($this->constructed) { + throw new ImmutableException($method.' not allowed on '.static::class); + } + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/TranslatorStrongTypeInterface.php b/vendor/nesbot/carbon/src/Carbon/TranslatorStrongTypeInterface.php new file mode 100644 index 0000000..ef4dee8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/TranslatorStrongTypeInterface.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Symfony\Component\Translation\MessageCatalogueInterface; + +/** + * Mark translator using strong type from symfony/translation >= 6. + */ +interface TranslatorStrongTypeInterface +{ + public function getFromCatalogue(MessageCatalogueInterface $catalogue, string $id, string $domain = 'messages'); +} diff --git a/vendor/nikic/fast-route/.gitignore b/vendor/nikic/fast-route/.gitignore new file mode 100644 index 0000000..e378a07 --- /dev/null +++ b/vendor/nikic/fast-route/.gitignore @@ -0,0 +1,5 @@ +/vendor/ +.idea/ + +# ignore lock file since we have no extra dependencies +composer.lock diff --git a/vendor/nikic/fast-route/.hhconfig b/vendor/nikic/fast-route/.hhconfig new file mode 100644 index 0000000..0c2153c --- /dev/null +++ b/vendor/nikic/fast-route/.hhconfig @@ -0,0 +1 @@ +assume_php=false diff --git a/vendor/nikic/fast-route/.travis.yml b/vendor/nikic/fast-route/.travis.yml new file mode 100644 index 0000000..10f8381 --- /dev/null +++ b/vendor/nikic/fast-route/.travis.yml @@ -0,0 +1,20 @@ +sudo: false +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - 7.1 + - 7.2 + - hhvm + +script: + - ./vendor/bin/phpunit + +before_install: + - travis_retry composer self-update + +install: + - composer install diff --git a/vendor/nikic/fast-route/FastRoute.hhi b/vendor/nikic/fast-route/FastRoute.hhi new file mode 100644 index 0000000..8d50738 --- /dev/null +++ b/vendor/nikic/fast-route/FastRoute.hhi @@ -0,0 +1,126 @@ +; + } + + class RouteCollector { + public function __construct(RouteParser $routeParser, DataGenerator $dataGenerator); + public function addRoute(mixed $httpMethod, string $route, mixed $handler): void; + public function getData(): array; + } + + class Route { + public function __construct(string $httpMethod, mixed $handler, string $regex, array $variables); + public function matches(string $str): bool; + } + + interface DataGenerator { + public function addRoute(string $httpMethod, array $routeData, mixed $handler); + public function getData(): array; + } + + interface Dispatcher { + const int NOT_FOUND = 0; + const int FOUND = 1; + const int METHOD_NOT_ALLOWED = 2; + public function dispatch(string $httpMethod, string $uri): array; + } + + function simpleDispatcher( + (function(RouteCollector): void) $routeDefinitionCallback, + shape( + ?'routeParser' => classname, + ?'dataGenerator' => classname, + ?'dispatcher' => classname, + ?'routeCollector' => classname, + ) $options = shape()): Dispatcher; + + function cachedDispatcher( + (function(RouteCollector): void) $routeDefinitionCallback, + shape( + ?'routeParser' => classname, + ?'dataGenerator' => classname, + ?'dispatcher' => classname, + ?'routeCollector' => classname, + ?'cacheDisabled' => bool, + ?'cacheFile' => string, + ) $options = shape()): Dispatcher; +} + +namespace FastRoute\DataGenerator { + abstract class RegexBasedAbstract implements \FastRoute\DataGenerator { + protected abstract function getApproxChunkSize(); + protected abstract function processChunk($regexToRoutesMap); + + public function addRoute(string $httpMethod, array $routeData, mixed $handler): void; + public function getData(): array; + } + + class CharCountBased extends RegexBasedAbstract { + protected function getApproxChunkSize(): int; + protected function processChunk(array $regexToRoutesMap): array; + } + + class GroupCountBased extends RegexBasedAbstract { + protected function getApproxChunkSize(): int; + protected function processChunk(array $regexToRoutesMap): array; + } + + class GroupPosBased extends RegexBasedAbstract { + protected function getApproxChunkSize(): int; + protected function processChunk(array $regexToRoutesMap): array; + } + + class MarkBased extends RegexBasedAbstract { + protected function getApproxChunkSize(): int; + protected function processChunk(array $regexToRoutesMap): array; + } +} + +namespace FastRoute\Dispatcher { + abstract class RegexBasedAbstract implements \FastRoute\Dispatcher { + protected abstract function dispatchVariableRoute(array $routeData, string $uri): array; + + public function dispatch(string $httpMethod, string $uri): array; + } + + class GroupPosBased extends RegexBasedAbstract { + public function __construct(array $data); + protected function dispatchVariableRoute(array $routeData, string $uri): array; + } + + class GroupCountBased extends RegexBasedAbstract { + public function __construct(array $data); + protected function dispatchVariableRoute(array $routeData, string $uri): array; + } + + class CharCountBased extends RegexBasedAbstract { + public function __construct(array $data); + protected function dispatchVariableRoute(array $routeData, string $uri): array; + } + + class MarkBased extends RegexBasedAbstract { + public function __construct(array $data); + protected function dispatchVariableRoute(array $routeData, string $uri): array; + } +} + +namespace FastRoute\RouteParser { + class Std implements \FastRoute\RouteParser { + const string VARIABLE_REGEX = <<<'REGEX' +\{ + \s* ([a-zA-Z][a-zA-Z0-9_]*) \s* + (?: + : \s* ([^{}]*(?:\{(?-1)\}[^{}]*)*) + )? +\} +REGEX; + const string DEFAULT_DISPATCH_REGEX = '[^/]+'; + public function parse(string $route): array; + } +} diff --git a/vendor/nikic/fast-route/LICENSE b/vendor/nikic/fast-route/LICENSE new file mode 100644 index 0000000..478e764 --- /dev/null +++ b/vendor/nikic/fast-route/LICENSE @@ -0,0 +1,31 @@ +Copyright (c) 2013 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. diff --git a/vendor/nikic/fast-route/README.md b/vendor/nikic/fast-route/README.md new file mode 100644 index 0000000..91bd466 --- /dev/null +++ b/vendor/nikic/fast-route/README.md @@ -0,0 +1,313 @@ +FastRoute - Fast request router for PHP +======================================= + +This library provides a fast implementation of a regular expression based router. [Blog post explaining how the +implementation works and why it is fast.][blog_post] + +Install +------- + +To install with composer: + +```sh +composer require nikic/fast-route +``` + +Requires PHP 5.4 or newer. + +Usage +----- + +Here's a basic usage example: + +```php +addRoute('GET', '/users', 'get_all_users_handler'); + // {id} must be a number (\d+) + $r->addRoute('GET', '/user/{id:\d+}', 'get_user_handler'); + // The /{title} suffix is optional + $r->addRoute('GET', '/articles/{id:\d+}[/{title}]', 'get_article_handler'); +}); + +// Fetch method and URI from somewhere +$httpMethod = $_SERVER['REQUEST_METHOD']; +$uri = $_SERVER['REQUEST_URI']; + +// Strip query string (?foo=bar) and decode URI +if (false !== $pos = strpos($uri, '?')) { + $uri = substr($uri, 0, $pos); +} +$uri = rawurldecode($uri); + +$routeInfo = $dispatcher->dispatch($httpMethod, $uri); +switch ($routeInfo[0]) { + case FastRoute\Dispatcher::NOT_FOUND: + // ... 404 Not Found + break; + case FastRoute\Dispatcher::METHOD_NOT_ALLOWED: + $allowedMethods = $routeInfo[1]; + // ... 405 Method Not Allowed + break; + case FastRoute\Dispatcher::FOUND: + $handler = $routeInfo[1]; + $vars = $routeInfo[2]; + // ... call $handler with $vars + break; +} +``` + +### Defining routes + +The routes are defined by calling the `FastRoute\simpleDispatcher()` function, which accepts +a callable taking a `FastRoute\RouteCollector` instance. The routes are added by calling +`addRoute()` on the collector instance: + +```php +$r->addRoute($method, $routePattern, $handler); +``` + +The `$method` is an uppercase HTTP method string for which a certain route should match. It +is possible to specify multiple valid methods using an array: + +```php +// These two calls +$r->addRoute('GET', '/test', 'handler'); +$r->addRoute('POST', '/test', 'handler'); +// Are equivalent to this one call +$r->addRoute(['GET', 'POST'], '/test', 'handler'); +``` + +By default the `$routePattern` uses a syntax where `{foo}` specifies a placeholder with name `foo` +and matching the regex `[^/]+`. To adjust the pattern the placeholder matches, you can specify +a custom pattern by writing `{bar:[0-9]+}`. Some examples: + +```php +// Matches /user/42, but not /user/xyz +$r->addRoute('GET', '/user/{id:\d+}', 'handler'); + +// Matches /user/foobar, but not /user/foo/bar +$r->addRoute('GET', '/user/{name}', 'handler'); + +// Matches /user/foo/bar as well +$r->addRoute('GET', '/user/{name:.+}', 'handler'); +``` + +Custom patterns for route placeholders cannot use capturing groups. For example `{lang:(en|de)}` +is not a valid placeholder, because `()` is a capturing group. Instead you can use either +`{lang:en|de}` or `{lang:(?:en|de)}`. + +Furthermore parts of the route enclosed in `[...]` are considered optional, so that `/foo[bar]` +will match both `/foo` and `/foobar`. Optional parts are only supported in a trailing position, +not in the middle of a route. + +```php +// This route +$r->addRoute('GET', '/user/{id:\d+}[/{name}]', 'handler'); +// Is equivalent to these two routes +$r->addRoute('GET', '/user/{id:\d+}', 'handler'); +$r->addRoute('GET', '/user/{id:\d+}/{name}', 'handler'); + +// Multiple nested optional parts are possible as well +$r->addRoute('GET', '/user[/{id:\d+}[/{name}]]', 'handler'); + +// This route is NOT valid, because optional parts can only occur at the end +$r->addRoute('GET', '/user[/{id:\d+}]/{name}', 'handler'); +``` + +The `$handler` parameter does not necessarily have to be a callback, it could also be a controller +class name or any other kind of data you wish to associate with the route. FastRoute only tells you +which handler corresponds to your URI, how you interpret it is up to you. + +#### Shorcut methods for common request methods + +For the `GET`, `POST`, `PUT`, `PATCH`, `DELETE` and `HEAD` request methods shortcut methods are available. For example: + +```php +$r->get('/get-route', 'get_handler'); +$r->post('/post-route', 'post_handler'); +``` + +Is equivalent to: + +```php +$r->addRoute('GET', '/get-route', 'get_handler'); +$r->addRoute('POST', '/post-route', 'post_handler'); +``` + +#### Route Groups + +Additionally, you can specify routes inside of a group. All routes defined inside a group will have a common prefix. + +For example, defining your routes as: + +```php +$r->addGroup('/admin', function (RouteCollector $r) { + $r->addRoute('GET', '/do-something', 'handler'); + $r->addRoute('GET', '/do-another-thing', 'handler'); + $r->addRoute('GET', '/do-something-else', 'handler'); +}); +``` + +Will have the same result as: + + ```php +$r->addRoute('GET', '/admin/do-something', 'handler'); +$r->addRoute('GET', '/admin/do-another-thing', 'handler'); +$r->addRoute('GET', '/admin/do-something-else', 'handler'); + ``` + +Nested groups are also supported, in which case the prefixes of all the nested groups are combined. + +### Caching + +The reason `simpleDispatcher` accepts a callback for defining the routes is to allow seamless +caching. By using `cachedDispatcher` instead of `simpleDispatcher` you can cache the generated +routing data and construct the dispatcher from the cached information: + +```php +addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); + $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1'); + $r->addRoute('GET', '/user/{name}', 'handler2'); +}, [ + 'cacheFile' => __DIR__ . '/route.cache', /* required */ + 'cacheDisabled' => IS_DEBUG_ENABLED, /* optional, enabled by default */ +]); +``` + +The second parameter to the function is an options array, which can be used to specify the cache +file location, among other things. + +### Dispatching a URI + +A URI is dispatched by calling the `dispatch()` method of the created dispatcher. This method +accepts the HTTP method and a URI. Getting those two bits of information (and normalizing them +appropriately) is your job - this library is not bound to the PHP web SAPIs. + +The `dispatch()` method returns an array whose first element contains a status code. It is one +of `Dispatcher::NOT_FOUND`, `Dispatcher::METHOD_NOT_ALLOWED` and `Dispatcher::FOUND`. For the +method not allowed status the second array element contains a list of HTTP methods allowed for +the supplied URI. For example: + + [FastRoute\Dispatcher::METHOD_NOT_ALLOWED, ['GET', 'POST']] + +> **NOTE:** The HTTP specification requires that a `405 Method Not Allowed` response include the +`Allow:` header to detail available methods for the requested resource. Applications using FastRoute +should use the second array element to add this header when relaying a 405 response. + +For the found status the second array element is the handler that was associated with the route +and the third array element is a dictionary of placeholder names to their values. For example: + + /* Routing against GET /user/nikic/42 */ + + [FastRoute\Dispatcher::FOUND, 'handler0', ['name' => 'nikic', 'id' => '42']] + +### Overriding the route parser and dispatcher + +The routing process makes use of three components: A route parser, a data generator and a +dispatcher. The three components adhere to the following interfaces: + +```php + 'FastRoute\\RouteParser\\Std', + 'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased', + 'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased', +]); +``` + +The above options array corresponds to the defaults. By replacing `GroupCountBased` by +`GroupPosBased` you could switch to a different dispatching strategy. + +### A Note on HEAD Requests + +The HTTP spec requires servers to [support both GET and HEAD methods][2616-511]: + +> The methods GET and HEAD MUST be supported by all general-purpose servers + +To avoid forcing users to manually register HEAD routes for each resource we fallback to matching an +available GET route for a given resource. The PHP web SAPI transparently removes the entity body +from HEAD responses so this behavior has no effect on the vast majority of users. + +However, implementers using FastRoute outside the web SAPI environment (e.g. a custom server) MUST +NOT send entity bodies generated in response to HEAD requests. If you are a non-SAPI user this is +*your responsibility*; FastRoute has no purview to prevent you from breaking HTTP in such cases. + +Finally, note that applications MAY always specify their own HEAD method route for a given +resource to bypass this behavior entirely. + +### Credits + +This library is based on a router that [Levi Morrison][levi] implemented for the Aerys server. + +A large number of tests, as well as HTTP compliance considerations, were provided by [Daniel Lowrey][rdlowrey]. + + +[2616-511]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1 "RFC 2616 Section 5.1.1" +[blog_post]: http://nikic.github.io/2014/02/18/Fast-request-routing-using-regular-expressions.html +[levi]: https://github.com/morrisonlevi +[rdlowrey]: https://github.com/rdlowrey diff --git a/vendor/nikic/fast-route/composer.json b/vendor/nikic/fast-route/composer.json new file mode 100644 index 0000000..fb446a2 --- /dev/null +++ b/vendor/nikic/fast-route/composer.json @@ -0,0 +1,24 @@ +{ + "name": "nikic/fast-route", + "description": "Fast request router for PHP", + "keywords": ["routing", "router"], + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "autoload": { + "psr-4": { + "FastRoute\\": "src/" + }, + "files": ["src/functions.php"] + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" + } +} diff --git a/vendor/nikic/fast-route/phpunit.xml b/vendor/nikic/fast-route/phpunit.xml new file mode 100644 index 0000000..3c807b6 --- /dev/null +++ b/vendor/nikic/fast-route/phpunit.xml @@ -0,0 +1,24 @@ + + + + + + ./test/ + + + + + + ./src/ + + + diff --git a/vendor/nikic/fast-route/psalm.xml b/vendor/nikic/fast-route/psalm.xml new file mode 100644 index 0000000..0dca5d7 --- /dev/null +++ b/vendor/nikic/fast-route/psalm.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/nikic/fast-route/src/BadRouteException.php b/vendor/nikic/fast-route/src/BadRouteException.php new file mode 100644 index 0000000..62262ec --- /dev/null +++ b/vendor/nikic/fast-route/src/BadRouteException.php @@ -0,0 +1,7 @@ + $route) { + $suffixLen++; + $suffix .= "\t"; + + $regexes[] = '(?:' . $regex . '/(\t{' . $suffixLen . '})\t{' . ($count - $suffixLen) . '})'; + $routeMap[$suffix] = [$route->handler, $route->variables]; + } + + $regex = '~^(?|' . implode('|', $regexes) . ')$~'; + return ['regex' => $regex, 'suffix' => '/' . $suffix, 'routeMap' => $routeMap]; + } +} diff --git a/vendor/nikic/fast-route/src/DataGenerator/GroupCountBased.php b/vendor/nikic/fast-route/src/DataGenerator/GroupCountBased.php new file mode 100644 index 0000000..54d9a05 --- /dev/null +++ b/vendor/nikic/fast-route/src/DataGenerator/GroupCountBased.php @@ -0,0 +1,30 @@ + $route) { + $numVariables = count($route->variables); + $numGroups = max($numGroups, $numVariables); + + $regexes[] = $regex . str_repeat('()', $numGroups - $numVariables); + $routeMap[$numGroups + 1] = [$route->handler, $route->variables]; + + ++$numGroups; + } + + $regex = '~^(?|' . implode('|', $regexes) . ')$~'; + return ['regex' => $regex, 'routeMap' => $routeMap]; + } +} diff --git a/vendor/nikic/fast-route/src/DataGenerator/GroupPosBased.php b/vendor/nikic/fast-route/src/DataGenerator/GroupPosBased.php new file mode 100644 index 0000000..fc4dc0a --- /dev/null +++ b/vendor/nikic/fast-route/src/DataGenerator/GroupPosBased.php @@ -0,0 +1,27 @@ + $route) { + $regexes[] = $regex; + $routeMap[$offset] = [$route->handler, $route->variables]; + + $offset += count($route->variables); + } + + $regex = '~^(?:' . implode('|', $regexes) . ')$~'; + return ['regex' => $regex, 'routeMap' => $routeMap]; + } +} diff --git a/vendor/nikic/fast-route/src/DataGenerator/MarkBased.php b/vendor/nikic/fast-route/src/DataGenerator/MarkBased.php new file mode 100644 index 0000000..0aebed9 --- /dev/null +++ b/vendor/nikic/fast-route/src/DataGenerator/MarkBased.php @@ -0,0 +1,27 @@ + $route) { + $regexes[] = $regex . '(*MARK:' . $markName . ')'; + $routeMap[$markName] = [$route->handler, $route->variables]; + + ++$markName; + } + + $regex = '~^(?|' . implode('|', $regexes) . ')$~'; + return ['regex' => $regex, 'routeMap' => $routeMap]; + } +} diff --git a/vendor/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php b/vendor/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php new file mode 100644 index 0000000..6457290 --- /dev/null +++ b/vendor/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php @@ -0,0 +1,186 @@ +isStaticRoute($routeData)) { + $this->addStaticRoute($httpMethod, $routeData, $handler); + } else { + $this->addVariableRoute($httpMethod, $routeData, $handler); + } + } + + /** + * @return mixed[] + */ + public function getData() + { + if (empty($this->methodToRegexToRoutesMap)) { + return [$this->staticRoutes, []]; + } + + return [$this->staticRoutes, $this->generateVariableRouteData()]; + } + + /** + * @return mixed[] + */ + private function generateVariableRouteData() + { + $data = []; + foreach ($this->methodToRegexToRoutesMap as $method => $regexToRoutesMap) { + $chunkSize = $this->computeChunkSize(count($regexToRoutesMap)); + $chunks = array_chunk($regexToRoutesMap, $chunkSize, true); + $data[$method] = array_map([$this, 'processChunk'], $chunks); + } + return $data; + } + + /** + * @param int + * @return int + */ + private function computeChunkSize($count) + { + $numParts = max(1, round($count / $this->getApproxChunkSize())); + return (int) ceil($count / $numParts); + } + + /** + * @param mixed[] + * @return bool + */ + private function isStaticRoute($routeData) + { + return count($routeData) === 1 && is_string($routeData[0]); + } + + private function addStaticRoute($httpMethod, $routeData, $handler) + { + $routeStr = $routeData[0]; + + if (isset($this->staticRoutes[$httpMethod][$routeStr])) { + throw new BadRouteException(sprintf( + 'Cannot register two routes matching "%s" for method "%s"', + $routeStr, $httpMethod + )); + } + + if (isset($this->methodToRegexToRoutesMap[$httpMethod])) { + foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) { + if ($route->matches($routeStr)) { + throw new BadRouteException(sprintf( + 'Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"', + $routeStr, $route->regex, $httpMethod + )); + } + } + } + + $this->staticRoutes[$httpMethod][$routeStr] = $handler; + } + + private function addVariableRoute($httpMethod, $routeData, $handler) + { + list($regex, $variables) = $this->buildRegexForRoute($routeData); + + if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) { + throw new BadRouteException(sprintf( + 'Cannot register two routes matching "%s" for method "%s"', + $regex, $httpMethod + )); + } + + $this->methodToRegexToRoutesMap[$httpMethod][$regex] = new Route( + $httpMethod, $handler, $regex, $variables + ); + } + + /** + * @param mixed[] + * @return mixed[] + */ + private function buildRegexForRoute($routeData) + { + $regex = ''; + $variables = []; + foreach ($routeData as $part) { + if (is_string($part)) { + $regex .= preg_quote($part, '~'); + continue; + } + + list($varName, $regexPart) = $part; + + if (isset($variables[$varName])) { + throw new BadRouteException(sprintf( + 'Cannot use the same placeholder "%s" twice', $varName + )); + } + + if ($this->regexHasCapturingGroups($regexPart)) { + throw new BadRouteException(sprintf( + 'Regex "%s" for parameter "%s" contains a capturing group', + $regexPart, $varName + )); + } + + $variables[$varName] = $varName; + $regex .= '(' . $regexPart . ')'; + } + + return [$regex, $variables]; + } + + /** + * @param string + * @return bool + */ + private function regexHasCapturingGroups($regex) + { + if (false === strpos($regex, '(')) { + // Needs to have at least a ( to contain a capturing group + return false; + } + + // Semi-accurate detection for capturing groups + return (bool) preg_match( + '~ + (?: + \(\?\( + | \[ [^\]\\\\]* (?: \\\\ . [^\]\\\\]* )* \] + | \\\\ . + ) (*SKIP)(*FAIL) | + \( + (?! + \? (?! <(?![!=]) | P< | \' ) + | \* + ) + ~x', + $regex + ); + } +} diff --git a/vendor/nikic/fast-route/src/Dispatcher.php b/vendor/nikic/fast-route/src/Dispatcher.php new file mode 100644 index 0000000..4ae72a3 --- /dev/null +++ b/vendor/nikic/fast-route/src/Dispatcher.php @@ -0,0 +1,26 @@ + 'value', ...]] + * + * @param string $httpMethod + * @param string $uri + * + * @return array + */ + public function dispatch($httpMethod, $uri); +} diff --git a/vendor/nikic/fast-route/src/Dispatcher/CharCountBased.php b/vendor/nikic/fast-route/src/Dispatcher/CharCountBased.php new file mode 100644 index 0000000..ef1eec1 --- /dev/null +++ b/vendor/nikic/fast-route/src/Dispatcher/CharCountBased.php @@ -0,0 +1,31 @@ +staticRouteMap, $this->variableRouteData) = $data; + } + + protected function dispatchVariableRoute($routeData, $uri) + { + foreach ($routeData as $data) { + if (!preg_match($data['regex'], $uri . $data['suffix'], $matches)) { + continue; + } + + list($handler, $varNames) = $data['routeMap'][end($matches)]; + + $vars = []; + $i = 0; + foreach ($varNames as $varName) { + $vars[$varName] = $matches[++$i]; + } + return [self::FOUND, $handler, $vars]; + } + + return [self::NOT_FOUND]; + } +} diff --git a/vendor/nikic/fast-route/src/Dispatcher/GroupCountBased.php b/vendor/nikic/fast-route/src/Dispatcher/GroupCountBased.php new file mode 100644 index 0000000..493e7a9 --- /dev/null +++ b/vendor/nikic/fast-route/src/Dispatcher/GroupCountBased.php @@ -0,0 +1,31 @@ +staticRouteMap, $this->variableRouteData) = $data; + } + + protected function dispatchVariableRoute($routeData, $uri) + { + foreach ($routeData as $data) { + if (!preg_match($data['regex'], $uri, $matches)) { + continue; + } + + list($handler, $varNames) = $data['routeMap'][count($matches)]; + + $vars = []; + $i = 0; + foreach ($varNames as $varName) { + $vars[$varName] = $matches[++$i]; + } + return [self::FOUND, $handler, $vars]; + } + + return [self::NOT_FOUND]; + } +} diff --git a/vendor/nikic/fast-route/src/Dispatcher/GroupPosBased.php b/vendor/nikic/fast-route/src/Dispatcher/GroupPosBased.php new file mode 100644 index 0000000..498220e --- /dev/null +++ b/vendor/nikic/fast-route/src/Dispatcher/GroupPosBased.php @@ -0,0 +1,33 @@ +staticRouteMap, $this->variableRouteData) = $data; + } + + protected function dispatchVariableRoute($routeData, $uri) + { + foreach ($routeData as $data) { + if (!preg_match($data['regex'], $uri, $matches)) { + continue; + } + + // find first non-empty match + for ($i = 1; '' === $matches[$i]; ++$i); + + list($handler, $varNames) = $data['routeMap'][$i]; + + $vars = []; + foreach ($varNames as $varName) { + $vars[$varName] = $matches[$i++]; + } + return [self::FOUND, $handler, $vars]; + } + + return [self::NOT_FOUND]; + } +} diff --git a/vendor/nikic/fast-route/src/Dispatcher/MarkBased.php b/vendor/nikic/fast-route/src/Dispatcher/MarkBased.php new file mode 100644 index 0000000..22eb09b --- /dev/null +++ b/vendor/nikic/fast-route/src/Dispatcher/MarkBased.php @@ -0,0 +1,31 @@ +staticRouteMap, $this->variableRouteData) = $data; + } + + protected function dispatchVariableRoute($routeData, $uri) + { + foreach ($routeData as $data) { + if (!preg_match($data['regex'], $uri, $matches)) { + continue; + } + + list($handler, $varNames) = $data['routeMap'][$matches['MARK']]; + + $vars = []; + $i = 0; + foreach ($varNames as $varName) { + $vars[$varName] = $matches[++$i]; + } + return [self::FOUND, $handler, $vars]; + } + + return [self::NOT_FOUND]; + } +} diff --git a/vendor/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php b/vendor/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php new file mode 100644 index 0000000..206e879 --- /dev/null +++ b/vendor/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php @@ -0,0 +1,88 @@ +staticRouteMap[$httpMethod][$uri])) { + $handler = $this->staticRouteMap[$httpMethod][$uri]; + return [self::FOUND, $handler, []]; + } + + $varRouteData = $this->variableRouteData; + if (isset($varRouteData[$httpMethod])) { + $result = $this->dispatchVariableRoute($varRouteData[$httpMethod], $uri); + if ($result[0] === self::FOUND) { + return $result; + } + } + + // For HEAD requests, attempt fallback to GET + if ($httpMethod === 'HEAD') { + if (isset($this->staticRouteMap['GET'][$uri])) { + $handler = $this->staticRouteMap['GET'][$uri]; + return [self::FOUND, $handler, []]; + } + if (isset($varRouteData['GET'])) { + $result = $this->dispatchVariableRoute($varRouteData['GET'], $uri); + if ($result[0] === self::FOUND) { + return $result; + } + } + } + + // If nothing else matches, try fallback routes + if (isset($this->staticRouteMap['*'][$uri])) { + $handler = $this->staticRouteMap['*'][$uri]; + return [self::FOUND, $handler, []]; + } + if (isset($varRouteData['*'])) { + $result = $this->dispatchVariableRoute($varRouteData['*'], $uri); + if ($result[0] === self::FOUND) { + return $result; + } + } + + // Find allowed methods for this URI by matching against all other HTTP methods as well + $allowedMethods = []; + + foreach ($this->staticRouteMap as $method => $uriMap) { + if ($method !== $httpMethod && isset($uriMap[$uri])) { + $allowedMethods[] = $method; + } + } + + foreach ($varRouteData as $method => $routeData) { + if ($method === $httpMethod) { + continue; + } + + $result = $this->dispatchVariableRoute($routeData, $uri); + if ($result[0] === self::FOUND) { + $allowedMethods[] = $method; + } + } + + // If there are no allowed methods the route simply does not exist + if ($allowedMethods) { + return [self::METHOD_NOT_ALLOWED, $allowedMethods]; + } + + return [self::NOT_FOUND]; + } +} diff --git a/vendor/nikic/fast-route/src/Route.php b/vendor/nikic/fast-route/src/Route.php new file mode 100644 index 0000000..e1bf7dd --- /dev/null +++ b/vendor/nikic/fast-route/src/Route.php @@ -0,0 +1,47 @@ +httpMethod = $httpMethod; + $this->handler = $handler; + $this->regex = $regex; + $this->variables = $variables; + } + + /** + * Tests whether this route matches the given string. + * + * @param string $str + * + * @return bool + */ + public function matches($str) + { + $regex = '~^' . $this->regex . '$~'; + return (bool) preg_match($regex, $str); + } +} diff --git a/vendor/nikic/fast-route/src/RouteCollector.php b/vendor/nikic/fast-route/src/RouteCollector.php new file mode 100644 index 0000000..c1c1762 --- /dev/null +++ b/vendor/nikic/fast-route/src/RouteCollector.php @@ -0,0 +1,152 @@ +routeParser = $routeParser; + $this->dataGenerator = $dataGenerator; + $this->currentGroupPrefix = ''; + } + + /** + * Adds a route to the collection. + * + * The syntax used in the $route string depends on the used route parser. + * + * @param string|string[] $httpMethod + * @param string $route + * @param mixed $handler + */ + public function addRoute($httpMethod, $route, $handler) + { + $route = $this->currentGroupPrefix . $route; + $routeDatas = $this->routeParser->parse($route); + foreach ((array) $httpMethod as $method) { + foreach ($routeDatas as $routeData) { + $this->dataGenerator->addRoute($method, $routeData, $handler); + } + } + } + + /** + * Create a route group with a common prefix. + * + * All routes created in the passed callback will have the given group prefix prepended. + * + * @param string $prefix + * @param callable $callback + */ + public function addGroup($prefix, callable $callback) + { + $previousGroupPrefix = $this->currentGroupPrefix; + $this->currentGroupPrefix = $previousGroupPrefix . $prefix; + $callback($this); + $this->currentGroupPrefix = $previousGroupPrefix; + } + + /** + * Adds a GET route to the collection + * + * This is simply an alias of $this->addRoute('GET', $route, $handler) + * + * @param string $route + * @param mixed $handler + */ + public function get($route, $handler) + { + $this->addRoute('GET', $route, $handler); + } + + /** + * Adds a POST route to the collection + * + * This is simply an alias of $this->addRoute('POST', $route, $handler) + * + * @param string $route + * @param mixed $handler + */ + public function post($route, $handler) + { + $this->addRoute('POST', $route, $handler); + } + + /** + * Adds a PUT route to the collection + * + * This is simply an alias of $this->addRoute('PUT', $route, $handler) + * + * @param string $route + * @param mixed $handler + */ + public function put($route, $handler) + { + $this->addRoute('PUT', $route, $handler); + } + + /** + * Adds a DELETE route to the collection + * + * This is simply an alias of $this->addRoute('DELETE', $route, $handler) + * + * @param string $route + * @param mixed $handler + */ + public function delete($route, $handler) + { + $this->addRoute('DELETE', $route, $handler); + } + + /** + * Adds a PATCH route to the collection + * + * This is simply an alias of $this->addRoute('PATCH', $route, $handler) + * + * @param string $route + * @param mixed $handler + */ + public function patch($route, $handler) + { + $this->addRoute('PATCH', $route, $handler); + } + + /** + * Adds a HEAD route to the collection + * + * This is simply an alias of $this->addRoute('HEAD', $route, $handler) + * + * @param string $route + * @param mixed $handler + */ + public function head($route, $handler) + { + $this->addRoute('HEAD', $route, $handler); + } + + /** + * Returns the collected route data, as provided by the data generator. + * + * @return array + */ + public function getData() + { + return $this->dataGenerator->getData(); + } +} diff --git a/vendor/nikic/fast-route/src/RouteParser.php b/vendor/nikic/fast-route/src/RouteParser.php new file mode 100644 index 0000000..6a7685c --- /dev/null +++ b/vendor/nikic/fast-route/src/RouteParser.php @@ -0,0 +1,37 @@ + $segment) { + if ($segment === '' && $n !== 0) { + throw new BadRouteException('Empty optional part'); + } + + $currentRoute .= $segment; + $routeDatas[] = $this->parsePlaceholders($currentRoute); + } + return $routeDatas; + } + + /** + * Parses a route string that does not contain optional segments. + * + * @param string + * @return mixed[] + */ + private function parsePlaceholders($route) + { + if (!preg_match_all( + '~' . self::VARIABLE_REGEX . '~x', $route, $matches, + PREG_OFFSET_CAPTURE | PREG_SET_ORDER + )) { + return [$route]; + } + + $offset = 0; + $routeData = []; + foreach ($matches as $set) { + if ($set[0][1] > $offset) { + $routeData[] = substr($route, $offset, $set[0][1] - $offset); + } + $routeData[] = [ + $set[1][0], + isset($set[2]) ? trim($set[2][0]) : self::DEFAULT_DISPATCH_REGEX + ]; + $offset = $set[0][1] + strlen($set[0][0]); + } + + if ($offset !== strlen($route)) { + $routeData[] = substr($route, $offset); + } + + return $routeData; + } +} diff --git a/vendor/nikic/fast-route/src/bootstrap.php b/vendor/nikic/fast-route/src/bootstrap.php new file mode 100644 index 0000000..0bce3a4 --- /dev/null +++ b/vendor/nikic/fast-route/src/bootstrap.php @@ -0,0 +1,12 @@ + 'FastRoute\\RouteParser\\Std', + 'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased', + 'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased', + 'routeCollector' => 'FastRoute\\RouteCollector', + ]; + + /** @var RouteCollector $routeCollector */ + $routeCollector = new $options['routeCollector']( + new $options['routeParser'], new $options['dataGenerator'] + ); + $routeDefinitionCallback($routeCollector); + + return new $options['dispatcher']($routeCollector->getData()); + } + + /** + * @param callable $routeDefinitionCallback + * @param array $options + * + * @return Dispatcher + */ + function cachedDispatcher(callable $routeDefinitionCallback, array $options = []) + { + $options += [ + 'routeParser' => 'FastRoute\\RouteParser\\Std', + 'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased', + 'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased', + 'routeCollector' => 'FastRoute\\RouteCollector', + 'cacheDisabled' => false, + ]; + + if (!isset($options['cacheFile'])) { + throw new \LogicException('Must specify "cacheFile" option'); + } + + if (!$options['cacheDisabled'] && file_exists($options['cacheFile'])) { + $dispatchData = require $options['cacheFile']; + if (!is_array($dispatchData)) { + throw new \RuntimeException('Invalid cache file "' . $options['cacheFile'] . '"'); + } + return new $options['dispatcher']($dispatchData); + } + + $routeCollector = new $options['routeCollector']( + new $options['routeParser'], new $options['dataGenerator'] + ); + $routeDefinitionCallback($routeCollector); + + /** @var RouteCollector $routeCollector */ + $dispatchData = $routeCollector->getData(); + if (!$options['cacheDisabled']) { + file_put_contents( + $options['cacheFile'], + ' $this->getDataGeneratorClass(), + 'dispatcher' => $this->getDispatcherClass() + ]; + } + + /** + * @dataProvider provideFoundDispatchCases + */ + public function testFoundDispatches($method, $uri, $callback, $handler, $argDict) + { + $dispatcher = \FastRoute\simpleDispatcher($callback, $this->generateDispatcherOptions()); + $info = $dispatcher->dispatch($method, $uri); + $this->assertSame($dispatcher::FOUND, $info[0]); + $this->assertSame($handler, $info[1]); + $this->assertSame($argDict, $info[2]); + } + + /** + * @dataProvider provideNotFoundDispatchCases + */ + public function testNotFoundDispatches($method, $uri, $callback) + { + $dispatcher = \FastRoute\simpleDispatcher($callback, $this->generateDispatcherOptions()); + $routeInfo = $dispatcher->dispatch($method, $uri); + $this->assertArrayNotHasKey(1, $routeInfo, + 'NOT_FOUND result must only contain a single element in the returned info array' + ); + $this->assertSame($dispatcher::NOT_FOUND, $routeInfo[0]); + } + + /** + * @dataProvider provideMethodNotAllowedDispatchCases + */ + public function testMethodNotAllowedDispatches($method, $uri, $callback, $availableMethods) + { + $dispatcher = \FastRoute\simpleDispatcher($callback, $this->generateDispatcherOptions()); + $routeInfo = $dispatcher->dispatch($method, $uri); + $this->assertArrayHasKey(1, $routeInfo, + 'METHOD_NOT_ALLOWED result must return an array of allowed methods at index 1' + ); + + list($routedStatus, $methodArray) = $dispatcher->dispatch($method, $uri); + $this->assertSame($dispatcher::METHOD_NOT_ALLOWED, $routedStatus); + $this->assertSame($availableMethods, $methodArray); + } + + /** + * @expectedException \FastRoute\BadRouteException + * @expectedExceptionMessage Cannot use the same placeholder "test" twice + */ + public function testDuplicateVariableNameError() + { + \FastRoute\simpleDispatcher(function (RouteCollector $r) { + $r->addRoute('GET', '/foo/{test}/{test:\d+}', 'handler0'); + }, $this->generateDispatcherOptions()); + } + + /** + * @expectedException \FastRoute\BadRouteException + * @expectedExceptionMessage Cannot register two routes matching "/user/([^/]+)" for method "GET" + */ + public function testDuplicateVariableRoute() + { + \FastRoute\simpleDispatcher(function (RouteCollector $r) { + $r->addRoute('GET', '/user/{id}', 'handler0'); // oops, forgot \d+ restriction ;) + $r->addRoute('GET', '/user/{name}', 'handler1'); + }, $this->generateDispatcherOptions()); + } + + /** + * @expectedException \FastRoute\BadRouteException + * @expectedExceptionMessage Cannot register two routes matching "/user" for method "GET" + */ + public function testDuplicateStaticRoute() + { + \FastRoute\simpleDispatcher(function (RouteCollector $r) { + $r->addRoute('GET', '/user', 'handler0'); + $r->addRoute('GET', '/user', 'handler1'); + }, $this->generateDispatcherOptions()); + } + + /** + * @expectedException \FastRoute\BadRouteException + * @expectedExceptionMessage Static route "/user/nikic" is shadowed by previously defined variable route "/user/([^/]+)" for method "GET" + */ + public function testShadowedStaticRoute() + { + \FastRoute\simpleDispatcher(function (RouteCollector $r) { + $r->addRoute('GET', '/user/{name}', 'handler0'); + $r->addRoute('GET', '/user/nikic', 'handler1'); + }, $this->generateDispatcherOptions()); + } + + /** + * @expectedException \FastRoute\BadRouteException + * @expectedExceptionMessage Regex "(en|de)" for parameter "lang" contains a capturing group + */ + public function testCapturing() + { + \FastRoute\simpleDispatcher(function (RouteCollector $r) { + $r->addRoute('GET', '/{lang:(en|de)}', 'handler0'); + }, $this->generateDispatcherOptions()); + } + + public function provideFoundDispatchCases() + { + $cases = []; + + // 0 --------------------------------------------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/resource/123/456', 'handler0'); + }; + + $method = 'GET'; + $uri = '/resource/123/456'; + $handler = 'handler0'; + $argDict = []; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 1 --------------------------------------------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/handler0', 'handler0'); + $r->addRoute('GET', '/handler1', 'handler1'); + $r->addRoute('GET', '/handler2', 'handler2'); + }; + + $method = 'GET'; + $uri = '/handler2'; + $handler = 'handler2'; + $argDict = []; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 2 --------------------------------------------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); + $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1'); + $r->addRoute('GET', '/user/{name}', 'handler2'); + }; + + $method = 'GET'; + $uri = '/user/rdlowrey'; + $handler = 'handler2'; + $argDict = ['name' => 'rdlowrey']; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 3 --------------------------------------------------------------------------------------> + + // reuse $callback from #2 + + $method = 'GET'; + $uri = '/user/12345'; + $handler = 'handler1'; + $argDict = ['id' => '12345']; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 4 --------------------------------------------------------------------------------------> + + // reuse $callback from #3 + + $method = 'GET'; + $uri = '/user/NaN'; + $handler = 'handler2'; + $argDict = ['name' => 'NaN']; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 5 --------------------------------------------------------------------------------------> + + // reuse $callback from #4 + + $method = 'GET'; + $uri = '/user/rdlowrey/12345'; + $handler = 'handler0'; + $argDict = ['name' => 'rdlowrey', 'id' => '12345']; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 6 --------------------------------------------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler0'); + $r->addRoute('GET', '/user/12345/extension', 'handler1'); + $r->addRoute('GET', '/user/{id:[0-9]+}.{extension}', 'handler2'); + }; + + $method = 'GET'; + $uri = '/user/12345.svg'; + $handler = 'handler2'; + $argDict = ['id' => '12345', 'extension' => 'svg']; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 7 ----- Test GET method fallback on HEAD route miss ------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/user/{name}', 'handler0'); + $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler1'); + $r->addRoute('GET', '/static0', 'handler2'); + $r->addRoute('GET', '/static1', 'handler3'); + $r->addRoute('HEAD', '/static1', 'handler4'); + }; + + $method = 'HEAD'; + $uri = '/user/rdlowrey'; + $handler = 'handler0'; + $argDict = ['name' => 'rdlowrey']; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 8 ----- Test GET method fallback on HEAD route miss ------------------------------------> + + // reuse $callback from #7 + + $method = 'HEAD'; + $uri = '/user/rdlowrey/1234'; + $handler = 'handler1'; + $argDict = ['name' => 'rdlowrey', 'id' => '1234']; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 9 ----- Test GET method fallback on HEAD route miss ------------------------------------> + + // reuse $callback from #8 + + $method = 'HEAD'; + $uri = '/static0'; + $handler = 'handler2'; + $argDict = []; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 10 ---- Test existing HEAD route used if available (no fallback) -----------------------> + + // reuse $callback from #9 + + $method = 'HEAD'; + $uri = '/static1'; + $handler = 'handler4'; + $argDict = []; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 11 ---- More specified routes are not shadowed by less specific of another method ------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/user/{name}', 'handler0'); + $r->addRoute('POST', '/user/{name:[a-z]+}', 'handler1'); + }; + + $method = 'POST'; + $uri = '/user/rdlowrey'; + $handler = 'handler1'; + $argDict = ['name' => 'rdlowrey']; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 12 ---- Handler of more specific routes is used, if it occurs first --------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/user/{name}', 'handler0'); + $r->addRoute('POST', '/user/{name:[a-z]+}', 'handler1'); + $r->addRoute('POST', '/user/{name}', 'handler2'); + }; + + $method = 'POST'; + $uri = '/user/rdlowrey'; + $handler = 'handler1'; + $argDict = ['name' => 'rdlowrey']; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 13 ---- Route with constant suffix -----------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/user/{name}', 'handler0'); + $r->addRoute('GET', '/user/{name}/edit', 'handler1'); + }; + + $method = 'GET'; + $uri = '/user/rdlowrey/edit'; + $handler = 'handler1'; + $argDict = ['name' => 'rdlowrey']; + + $cases[] = [$method, $uri, $callback, $handler, $argDict]; + + // 14 ---- Handle multiple methods with the same handler ----------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute(['GET', 'POST'], '/user', 'handlerGetPost'); + $r->addRoute(['DELETE'], '/user', 'handlerDelete'); + $r->addRoute([], '/user', 'handlerNone'); + }; + + $argDict = []; + $cases[] = ['GET', '/user', $callback, 'handlerGetPost', $argDict]; + $cases[] = ['POST', '/user', $callback, 'handlerGetPost', $argDict]; + $cases[] = ['DELETE', '/user', $callback, 'handlerDelete', $argDict]; + + // 17 ---- + + $callback = function (RouteCollector $r) { + $r->addRoute('POST', '/user.json', 'handler0'); + $r->addRoute('GET', '/{entity}.json', 'handler1'); + }; + + $cases[] = ['GET', '/user.json', $callback, 'handler1', ['entity' => 'user']]; + + // 18 ---- + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '', 'handler0'); + }; + + $cases[] = ['GET', '', $callback, 'handler0', []]; + + // 19 ---- + + $callback = function (RouteCollector $r) { + $r->addRoute('HEAD', '/a/{foo}', 'handler0'); + $r->addRoute('GET', '/b/{foo}', 'handler1'); + }; + + $cases[] = ['HEAD', '/b/bar', $callback, 'handler1', ['foo' => 'bar']]; + + // 20 ---- + + $callback = function (RouteCollector $r) { + $r->addRoute('HEAD', '/a', 'handler0'); + $r->addRoute('GET', '/b', 'handler1'); + }; + + $cases[] = ['HEAD', '/b', $callback, 'handler1', []]; + + // 21 ---- + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/foo', 'handler0'); + $r->addRoute('HEAD', '/{bar}', 'handler1'); + }; + + $cases[] = ['HEAD', '/foo', $callback, 'handler1', ['bar' => 'foo']]; + + // 22 ---- + + $callback = function (RouteCollector $r) { + $r->addRoute('*', '/user', 'handler0'); + $r->addRoute('*', '/{user}', 'handler1'); + $r->addRoute('GET', '/user', 'handler2'); + }; + + $cases[] = ['GET', '/user', $callback, 'handler2', []]; + + // 23 ---- + + $callback = function (RouteCollector $r) { + $r->addRoute('*', '/user', 'handler0'); + $r->addRoute('GET', '/user', 'handler1'); + }; + + $cases[] = ['POST', '/user', $callback, 'handler0', []]; + + // 24 ---- + + $cases[] = ['HEAD', '/user', $callback, 'handler1', []]; + + // 25 ---- + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/{bar}', 'handler0'); + $r->addRoute('*', '/foo', 'handler1'); + }; + + $cases[] = ['GET', '/foo', $callback, 'handler0', ['bar' => 'foo']]; + + // 26 ---- + + $callback = function(RouteCollector $r) { + $r->addRoute('GET', '/user', 'handler0'); + $r->addRoute('*', '/{foo:.*}', 'handler1'); + }; + + $cases[] = ['POST', '/bar', $callback, 'handler1', ['foo' => 'bar']]; + + // x --------------------------------------------------------------------------------------> + + return $cases; + } + + public function provideNotFoundDispatchCases() + { + $cases = []; + + // 0 --------------------------------------------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/resource/123/456', 'handler0'); + }; + + $method = 'GET'; + $uri = '/not-found'; + + $cases[] = [$method, $uri, $callback]; + + // 1 --------------------------------------------------------------------------------------> + + // reuse callback from #0 + $method = 'POST'; + $uri = '/not-found'; + + $cases[] = [$method, $uri, $callback]; + + // 2 --------------------------------------------------------------------------------------> + + // reuse callback from #1 + $method = 'PUT'; + $uri = '/not-found'; + + $cases[] = [$method, $uri, $callback]; + + // 3 --------------------------------------------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/handler0', 'handler0'); + $r->addRoute('GET', '/handler1', 'handler1'); + $r->addRoute('GET', '/handler2', 'handler2'); + }; + + $method = 'GET'; + $uri = '/not-found'; + + $cases[] = [$method, $uri, $callback]; + + // 4 --------------------------------------------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); + $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1'); + $r->addRoute('GET', '/user/{name}', 'handler2'); + }; + + $method = 'GET'; + $uri = '/not-found'; + + $cases[] = [$method, $uri, $callback]; + + // 5 --------------------------------------------------------------------------------------> + + // reuse callback from #4 + $method = 'GET'; + $uri = '/user/rdlowrey/12345/not-found'; + + $cases[] = [$method, $uri, $callback]; + + // 6 --------------------------------------------------------------------------------------> + + // reuse callback from #5 + $method = 'HEAD'; + + $cases[] = [$method, $uri, $callback]; + + // x --------------------------------------------------------------------------------------> + + return $cases; + } + + public function provideMethodNotAllowedDispatchCases() + { + $cases = []; + + // 0 --------------------------------------------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/resource/123/456', 'handler0'); + }; + + $method = 'POST'; + $uri = '/resource/123/456'; + $allowedMethods = ['GET']; + + $cases[] = [$method, $uri, $callback, $allowedMethods]; + + // 1 --------------------------------------------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/resource/123/456', 'handler0'); + $r->addRoute('POST', '/resource/123/456', 'handler1'); + $r->addRoute('PUT', '/resource/123/456', 'handler2'); + $r->addRoute('*', '/', 'handler3'); + }; + + $method = 'DELETE'; + $uri = '/resource/123/456'; + $allowedMethods = ['GET', 'POST', 'PUT']; + + $cases[] = [$method, $uri, $callback, $allowedMethods]; + + // 2 --------------------------------------------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); + $r->addRoute('POST', '/user/{name}/{id:[0-9]+}', 'handler1'); + $r->addRoute('PUT', '/user/{name}/{id:[0-9]+}', 'handler2'); + $r->addRoute('PATCH', '/user/{name}/{id:[0-9]+}', 'handler3'); + }; + + $method = 'DELETE'; + $uri = '/user/rdlowrey/42'; + $allowedMethods = ['GET', 'POST', 'PUT', 'PATCH']; + + $cases[] = [$method, $uri, $callback, $allowedMethods]; + + // 3 --------------------------------------------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute('POST', '/user/{name}', 'handler1'); + $r->addRoute('PUT', '/user/{name:[a-z]+}', 'handler2'); + $r->addRoute('PATCH', '/user/{name:[a-z]+}', 'handler3'); + }; + + $method = 'GET'; + $uri = '/user/rdlowrey'; + $allowedMethods = ['POST', 'PUT', 'PATCH']; + + $cases[] = [$method, $uri, $callback, $allowedMethods]; + + // 4 --------------------------------------------------------------------------------------> + + $callback = function (RouteCollector $r) { + $r->addRoute(['GET', 'POST'], '/user', 'handlerGetPost'); + $r->addRoute(['DELETE'], '/user', 'handlerDelete'); + $r->addRoute([], '/user', 'handlerNone'); + }; + + $cases[] = ['PUT', '/user', $callback, ['GET', 'POST', 'DELETE']]; + + // 5 + + $callback = function (RouteCollector $r) { + $r->addRoute('POST', '/user.json', 'handler0'); + $r->addRoute('GET', '/{entity}.json', 'handler1'); + }; + + $cases[] = ['PUT', '/user.json', $callback, ['POST', 'GET']]; + + // x --------------------------------------------------------------------------------------> + + return $cases; + } +} diff --git a/vendor/nikic/fast-route/test/Dispatcher/GroupCountBasedTest.php b/vendor/nikic/fast-route/test/Dispatcher/GroupCountBasedTest.php new file mode 100644 index 0000000..f821ef5 --- /dev/null +++ b/vendor/nikic/fast-route/test/Dispatcher/GroupCountBasedTest.php @@ -0,0 +1,16 @@ +markTestSkipped('PHP 5.6 required for MARK support'); + } + } + + protected function getDispatcherClass() + { + return 'FastRoute\\Dispatcher\\MarkBased'; + } + + protected function getDataGeneratorClass() + { + return 'FastRoute\\DataGenerator\\MarkBased'; + } +} diff --git a/vendor/nikic/fast-route/test/HackTypechecker/HackTypecheckerTest.php b/vendor/nikic/fast-route/test/HackTypechecker/HackTypecheckerTest.php new file mode 100644 index 0000000..b6fc53f --- /dev/null +++ b/vendor/nikic/fast-route/test/HackTypechecker/HackTypecheckerTest.php @@ -0,0 +1,44 @@ +markTestSkipped('HHVM only'); + } + if (!version_compare(HHVM_VERSION, '3.9.0', '>=')) { + $this->markTestSkipped('classname requires HHVM 3.9+'); + } + + // The typechecker recurses the whole tree, so it makes sure + // that everything in fixtures/ is valid when this runs. + + $output = []; + $exit_code = null; + exec( + 'hh_server --check ' . escapeshellarg(__DIR__ . '/../../') . ' 2>&1', + $output, + $exit_code + ); + if ($exit_code === self::SERVER_ALREADY_RUNNING_CODE) { + $this->assertTrue( + $recurse, + 'Typechecker still running after running hh_client stop' + ); + // Server already running - 3.10 => 3.11 regression: + // https://github.com/facebook/hhvm/issues/6646 + exec('hh_client stop 2>/dev/null'); + $this->testTypechecks(/* recurse = */ false); + return; + + } + $this->assertSame(0, $exit_code, implode("\n", $output)); + } +} diff --git a/vendor/nikic/fast-route/test/HackTypechecker/fixtures/all_options.php b/vendor/nikic/fast-route/test/HackTypechecker/fixtures/all_options.php new file mode 100644 index 0000000..05a9af2 --- /dev/null +++ b/vendor/nikic/fast-route/test/HackTypechecker/fixtures/all_options.php @@ -0,0 +1,29 @@ + {}, + shape( + 'routeParser' => \FastRoute\RouteParser\Std::class, + 'dataGenerator' => \FastRoute\DataGenerator\GroupCountBased::class, + 'dispatcher' => \FastRoute\Dispatcher\GroupCountBased::class, + 'routeCollector' => \FastRoute\RouteCollector::class, + ), + ); +} + +function all_options_cached(): \FastRoute\Dispatcher { + return \FastRoute\cachedDispatcher( + $collector ==> {}, + shape( + 'routeParser' => \FastRoute\RouteParser\Std::class, + 'dataGenerator' => \FastRoute\DataGenerator\GroupCountBased::class, + 'dispatcher' => \FastRoute\Dispatcher\GroupCountBased::class, + 'routeCollector' => \FastRoute\RouteCollector::class, + 'cacheFile' => '/dev/null', + 'cacheDisabled' => false, + ), + ); +} diff --git a/vendor/nikic/fast-route/test/HackTypechecker/fixtures/empty_options.php b/vendor/nikic/fast-route/test/HackTypechecker/fixtures/empty_options.php new file mode 100644 index 0000000..61eb541 --- /dev/null +++ b/vendor/nikic/fast-route/test/HackTypechecker/fixtures/empty_options.php @@ -0,0 +1,11 @@ + {}, shape()); +} + +function empty_options_cached(): \FastRoute\Dispatcher { + return \FastRoute\cachedDispatcher($collector ==> {}, shape()); +} diff --git a/vendor/nikic/fast-route/test/HackTypechecker/fixtures/no_options.php b/vendor/nikic/fast-route/test/HackTypechecker/fixtures/no_options.php new file mode 100644 index 0000000..44b5422 --- /dev/null +++ b/vendor/nikic/fast-route/test/HackTypechecker/fixtures/no_options.php @@ -0,0 +1,11 @@ + {}); +} + +function no_options_cached(): \FastRoute\Dispatcher { + return \FastRoute\cachedDispatcher($collector ==> {}); +} diff --git a/vendor/nikic/fast-route/test/RouteCollectorTest.php b/vendor/nikic/fast-route/test/RouteCollectorTest.php new file mode 100644 index 0000000..cc54407 --- /dev/null +++ b/vendor/nikic/fast-route/test/RouteCollectorTest.php @@ -0,0 +1,108 @@ +delete('/delete', 'delete'); + $r->get('/get', 'get'); + $r->head('/head', 'head'); + $r->patch('/patch', 'patch'); + $r->post('/post', 'post'); + $r->put('/put', 'put'); + + $expected = [ + ['DELETE', '/delete', 'delete'], + ['GET', '/get', 'get'], + ['HEAD', '/head', 'head'], + ['PATCH', '/patch', 'patch'], + ['POST', '/post', 'post'], + ['PUT', '/put', 'put'], + ]; + + $this->assertSame($expected, $r->routes); + } + + public function testGroups() + { + $r = new DummyRouteCollector(); + + $r->delete('/delete', 'delete'); + $r->get('/get', 'get'); + $r->head('/head', 'head'); + $r->patch('/patch', 'patch'); + $r->post('/post', 'post'); + $r->put('/put', 'put'); + + $r->addGroup('/group-one', function (DummyRouteCollector $r) { + $r->delete('/delete', 'delete'); + $r->get('/get', 'get'); + $r->head('/head', 'head'); + $r->patch('/patch', 'patch'); + $r->post('/post', 'post'); + $r->put('/put', 'put'); + + $r->addGroup('/group-two', function (DummyRouteCollector $r) { + $r->delete('/delete', 'delete'); + $r->get('/get', 'get'); + $r->head('/head', 'head'); + $r->patch('/patch', 'patch'); + $r->post('/post', 'post'); + $r->put('/put', 'put'); + }); + }); + + $r->addGroup('/admin', function (DummyRouteCollector $r) { + $r->get('-some-info', 'admin-some-info'); + }); + $r->addGroup('/admin-', function (DummyRouteCollector $r) { + $r->get('more-info', 'admin-more-info'); + }); + + $expected = [ + ['DELETE', '/delete', 'delete'], + ['GET', '/get', 'get'], + ['HEAD', '/head', 'head'], + ['PATCH', '/patch', 'patch'], + ['POST', '/post', 'post'], + ['PUT', '/put', 'put'], + ['DELETE', '/group-one/delete', 'delete'], + ['GET', '/group-one/get', 'get'], + ['HEAD', '/group-one/head', 'head'], + ['PATCH', '/group-one/patch', 'patch'], + ['POST', '/group-one/post', 'post'], + ['PUT', '/group-one/put', 'put'], + ['DELETE', '/group-one/group-two/delete', 'delete'], + ['GET', '/group-one/group-two/get', 'get'], + ['HEAD', '/group-one/group-two/head', 'head'], + ['PATCH', '/group-one/group-two/patch', 'patch'], + ['POST', '/group-one/group-two/post', 'post'], + ['PUT', '/group-one/group-two/put', 'put'], + ['GET', '/admin-some-info', 'admin-some-info'], + ['GET', '/admin-more-info', 'admin-more-info'], + ]; + + $this->assertSame($expected, $r->routes); + } +} + +class DummyRouteCollector extends RouteCollector +{ + public $routes = []; + + public function __construct() + { + } + + public function addRoute($method, $route, $handler) + { + $route = $this->currentGroupPrefix . $route; + $this->routes[] = [$method, $route, $handler]; + } +} diff --git a/vendor/nikic/fast-route/test/RouteParser/StdTest.php b/vendor/nikic/fast-route/test/RouteParser/StdTest.php new file mode 100644 index 0000000..e13e4de --- /dev/null +++ b/vendor/nikic/fast-route/test/RouteParser/StdTest.php @@ -0,0 +1,154 @@ +parse($routeString); + $this->assertSame($expectedRouteDatas, $routeDatas); + } + + /** @dataProvider provideTestParseError */ + public function testParseError($routeString, $expectedExceptionMessage) + { + $parser = new Std(); + $this->setExpectedException('FastRoute\\BadRouteException', $expectedExceptionMessage); + $parser->parse($routeString); + } + + public function provideTestParse() + { + return [ + [ + '/test', + [ + ['/test'], + ] + ], + [ + '/test/{param}', + [ + ['/test/', ['param', '[^/]+']], + ] + ], + [ + '/te{ param }st', + [ + ['/te', ['param', '[^/]+'], 'st'] + ] + ], + [ + '/test/{param1}/test2/{param2}', + [ + ['/test/', ['param1', '[^/]+'], '/test2/', ['param2', '[^/]+']] + ] + ], + [ + '/test/{param:\d+}', + [ + ['/test/', ['param', '\d+']] + ] + ], + [ + '/test/{ param : \d{1,9} }', + [ + ['/test/', ['param', '\d{1,9}']] + ] + ], + [ + '/test[opt]', + [ + ['/test'], + ['/testopt'], + ] + ], + [ + '/test[/{param}]', + [ + ['/test'], + ['/test/', ['param', '[^/]+']], + ] + ], + [ + '/{param}[opt]', + [ + ['/', ['param', '[^/]+']], + ['/', ['param', '[^/]+'], 'opt'] + ] + ], + [ + '/test[/{name}[/{id:[0-9]+}]]', + [ + ['/test'], + ['/test/', ['name', '[^/]+']], + ['/test/', ['name', '[^/]+'], '/', ['id', '[0-9]+']], + ] + ], + [ + '', + [ + [''], + ] + ], + [ + '[test]', + [ + [''], + ['test'], + ] + ], + [ + '/{foo-bar}', + [ + ['/', ['foo-bar', '[^/]+']] + ] + ], + [ + '/{_foo:.*}', + [ + ['/', ['_foo', '.*']] + ] + ], + ]; + } + + public function provideTestParseError() + { + return [ + [ + '/test[opt', + "Number of opening '[' and closing ']' does not match" + ], + [ + '/test[opt[opt2]', + "Number of opening '[' and closing ']' does not match" + ], + [ + '/testopt]', + "Number of opening '[' and closing ']' does not match" + ], + [ + '/test[]', + 'Empty optional part' + ], + [ + '/test[[opt]]', + 'Empty optional part' + ], + [ + '[[test]]', + 'Empty optional part' + ], + [ + '/test[/opt]/required', + 'Optional segments can only occur at the end of a route' + ], + ]; + } +} diff --git a/vendor/nikic/fast-route/test/bootstrap.php b/vendor/nikic/fast-route/test/bootstrap.php new file mode 100644 index 0000000..3023f41 --- /dev/null +++ b/vendor/nikic/fast-route/test/bootstrap.php @@ -0,0 +1,11 @@ + + * + * 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. + */ + +namespace PhpOption; + +use Traversable; + +/** + * @template T + * + * @extends Option + */ +final class LazyOption extends Option +{ + /** @var callable(mixed...):(Option) */ + private $callback; + + /** @var array */ + private $arguments; + + /** @var Option|null */ + private $option; + + /** + * @template S + * @param callable(mixed...):(Option) $callback + * @param array $arguments + * + * @return LazyOption + */ + public static function create($callback, array $arguments = []): self + { + return new self($callback, $arguments); + } + + /** + * @param callable(mixed...):(Option) $callback + * @param array $arguments + */ + public function __construct($callback, array $arguments = []) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Invalid callback given'); + } + + $this->callback = $callback; + $this->arguments = $arguments; + } + + public function isDefined(): bool + { + return $this->option()->isDefined(); + } + + public function isEmpty(): bool + { + return $this->option()->isEmpty(); + } + + public function get() + { + return $this->option()->get(); + } + + public function getOrElse($default) + { + return $this->option()->getOrElse($default); + } + + public function getOrCall($callable) + { + return $this->option()->getOrCall($callable); + } + + public function getOrThrow(\Exception $ex) + { + return $this->option()->getOrThrow($ex); + } + + public function orElse(Option $else) + { + return $this->option()->orElse($else); + } + + public function ifDefined($callable) + { + $this->option()->forAll($callable); + } + + public function forAll($callable) + { + return $this->option()->forAll($callable); + } + + public function map($callable) + { + return $this->option()->map($callable); + } + + public function flatMap($callable) + { + return $this->option()->flatMap($callable); + } + + public function filter($callable) + { + return $this->option()->filter($callable); + } + + public function filterNot($callable) + { + return $this->option()->filterNot($callable); + } + + public function select($value) + { + return $this->option()->select($value); + } + + public function reject($value) + { + return $this->option()->reject($value); + } + + /** + * @return Traversable + */ + public function getIterator(): Traversable + { + return $this->option()->getIterator(); + } + + public function foldLeft($initialValue, $callable) + { + return $this->option()->foldLeft($initialValue, $callable); + } + + public function foldRight($initialValue, $callable) + { + return $this->option()->foldRight($initialValue, $callable); + } + + /** + * @return Option + */ + private function option(): Option + { + if (null === $this->option) { + /** @var mixed */ + $option = call_user_func_array($this->callback, $this->arguments); + if ($option instanceof Option) { + $this->option = $option; + } else { + throw new \RuntimeException(sprintf('Expected instance of %s', Option::class)); + } + } + + return $this->option; + } +} diff --git a/vendor/phpoption/phpoption/src/PhpOption/None.php b/vendor/phpoption/phpoption/src/PhpOption/None.php new file mode 100644 index 0000000..4b85d22 --- /dev/null +++ b/vendor/phpoption/phpoption/src/PhpOption/None.php @@ -0,0 +1,136 @@ + + * + * 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. + */ + +namespace PhpOption; + +use EmptyIterator; + +/** + * @extends Option + */ +final class None extends Option +{ + /** @var None|null */ + private static $instance; + + /** + * @return None + */ + public static function create(): self + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + public function get() + { + throw new \RuntimeException('None has no value.'); + } + + public function getOrCall($callable) + { + return $callable(); + } + + public function getOrElse($default) + { + return $default; + } + + public function getOrThrow(\Exception $ex) + { + throw $ex; + } + + public function isEmpty(): bool + { + return true; + } + + public function isDefined(): bool + { + return false; + } + + public function orElse(Option $else) + { + return $else; + } + + public function ifDefined($callable) + { + // Just do nothing in that case. + } + + public function forAll($callable) + { + return $this; + } + + public function map($callable) + { + return $this; + } + + public function flatMap($callable) + { + return $this; + } + + public function filter($callable) + { + return $this; + } + + public function filterNot($callable) + { + return $this; + } + + public function select($value) + { + return $this; + } + + public function reject($value) + { + return $this; + } + + public function getIterator(): EmptyIterator + { + return new EmptyIterator(); + } + + public function foldLeft($initialValue, $callable) + { + return $initialValue; + } + + public function foldRight($initialValue, $callable) + { + return $initialValue; + } + + private function __construct() + { + } +} diff --git a/vendor/phpoption/phpoption/src/PhpOption/Option.php b/vendor/phpoption/phpoption/src/PhpOption/Option.php new file mode 100644 index 0000000..91fab9c --- /dev/null +++ b/vendor/phpoption/phpoption/src/PhpOption/Option.php @@ -0,0 +1,434 @@ + + * + * 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. + */ + +namespace PhpOption; + +use ArrayAccess; +use IteratorAggregate; + +/** + * @template T + * + * @implements IteratorAggregate + */ +abstract class Option implements IteratorAggregate +{ + /** + * Creates an option given a return value. + * + * This is intended for consuming existing APIs and allows you to easily + * convert them to an option. By default, we treat ``null`` as the None + * case, and everything else as Some. + * + * @template S + * + * @param S $value The actual return value. + * @param S $noneValue The value which should be considered "None"; null by + * default. + * + * @return Option + */ + public static function fromValue($value, $noneValue = null) + { + if ($value === $noneValue) { + return None::create(); + } + + return new Some($value); + } + + /** + * Creates an option from an array's value. + * + * If the key does not exist in the array, the array is not actually an + * array, or the array's value at the given key is null, None is returned. + * Otherwise, Some is returned wrapping the value at the given key. + * + * @template S + * + * @param array|ArrayAccess|null $array A potential array or \ArrayAccess value. + * @param string|int|null $key The key to check. + * + * @return Option + */ + public static function fromArraysValue($array, $key) + { + if ($key === null || !(is_array($array) || $array instanceof ArrayAccess) || !isset($array[$key])) { + return None::create(); + } + + return new Some($array[$key]); + } + + /** + * Creates a lazy-option with the given callback. + * + * This is also a helper constructor for lazy-consuming existing APIs where + * the return value is not yet an option. By default, we treat ``null`` as + * None case, and everything else as Some. + * + * @template S + * + * @param callable $callback The callback to evaluate. + * @param array $arguments The arguments for the callback. + * @param S $noneValue The value which should be considered "None"; + * null by default. + * + * @return LazyOption + */ + public static function fromReturn($callback, array $arguments = [], $noneValue = null) + { + return new LazyOption(static function () use ($callback, $arguments, $noneValue) { + /** @var mixed */ + $return = call_user_func_array($callback, $arguments); + + if ($return === $noneValue) { + return None::create(); + } + + return new Some($return); + }); + } + + /** + * Option factory, which creates new option based on passed value. + * + * If value is already an option, it simply returns. If value is callable, + * LazyOption with passed callback created and returned. If Option + * returned from callback, it returns directly. On other case value passed + * to Option::fromValue() method. + * + * @template S + * + * @param Option|callable|S $value + * @param S $noneValue Used when $value is mixed or + * callable, for None-check. + * + * @return Option|LazyOption + */ + public static function ensure($value, $noneValue = null) + { + if ($value instanceof self) { + return $value; + } elseif (is_callable($value)) { + return new LazyOption(static function () use ($value, $noneValue) { + /** @var mixed */ + $return = $value(); + + if ($return instanceof self) { + return $return; + } else { + return self::fromValue($return, $noneValue); + } + }); + } else { + return self::fromValue($value, $noneValue); + } + } + + /** + * Lift a function so that it accepts Option as parameters. + * + * We return a new closure that wraps the original callback. If any of the + * parameters passed to the lifted function is empty, the function will + * return a value of None. Otherwise, we will pass all parameters to the + * original callback and return the value inside a new Option, unless an + * Option is returned from the function, in which case, we use that. + * + * @template S + * + * @param callable $callback + * @param mixed $noneValue + * + * @return callable + */ + public static function lift($callback, $noneValue = null) + { + return static function () use ($callback, $noneValue) { + /** @var array */ + $args = func_get_args(); + + $reduced_args = array_reduce( + $args, + /** @param bool $status */ + static function ($status, self $o) { + return $o->isEmpty() ? true : $status; + }, + false + ); + // if at least one parameter is empty, return None + if ($reduced_args) { + return None::create(); + } + + $args = array_map( + /** @return T */ + static function (self $o) { + // it is safe to do so because the fold above checked + // that all arguments are of type Some + /** @var T */ + return $o->get(); + }, + $args + ); + + return self::ensure(call_user_func_array($callback, $args), $noneValue); + }; + } + + /** + * Returns the value if available, or throws an exception otherwise. + * + * @throws \RuntimeException If value is not available. + * + * @return T + */ + abstract public function get(); + + /** + * Returns the value if available, or the default value if not. + * + * @template S + * + * @param S $default + * + * @return T|S + */ + abstract public function getOrElse($default); + + /** + * Returns the value if available, or the results of the callable. + * + * This is preferable over ``getOrElse`` if the computation of the default + * value is expensive. + * + * @template S + * + * @param callable():S $callable + * + * @return T|S + */ + abstract public function getOrCall($callable); + + /** + * Returns the value if available, or throws the passed exception. + * + * @param \Exception $ex + * + * @return T + */ + abstract public function getOrThrow(\Exception $ex); + + /** + * Returns true if no value is available, false otherwise. + * + * @return bool + */ + abstract public function isEmpty(); + + /** + * Returns true if a value is available, false otherwise. + * + * @return bool + */ + abstract public function isDefined(); + + /** + * Returns this option if non-empty, or the passed option otherwise. + * + * This can be used to try multiple alternatives, and is especially useful + * with lazy evaluating options: + * + * ```php + * $repo->findSomething() + * ->orElse(new LazyOption(array($repo, 'findSomethingElse'))) + * ->orElse(new LazyOption(array($repo, 'createSomething'))); + * ``` + * + * @param Option $else + * + * @return Option + */ + abstract public function orElse(self $else); + + /** + * This is similar to map() below except that the return value has no meaning; + * the passed callable is simply executed if the option is non-empty, and + * ignored if the option is empty. + * + * In all cases, the return value of the callable is discarded. + * + * ```php + * $comment->getMaybeFile()->ifDefined(function($file) { + * // Do something with $file here. + * }); + * ``` + * + * If you're looking for something like ``ifEmpty``, you can use ``getOrCall`` + * and ``getOrElse`` in these cases. + * + * @deprecated Use forAll() instead. + * + * @param callable(T):mixed $callable + * + * @return void + */ + abstract public function ifDefined($callable); + + /** + * This is similar to map() except that the return value of the callable has no meaning. + * + * The passed callable is simply executed if the option is non-empty, and ignored if the + * option is empty. This method is preferred for callables with side-effects, while map() + * is intended for callables without side-effects. + * + * @param callable(T):mixed $callable + * + * @return Option + */ + abstract public function forAll($callable); + + /** + * Applies the callable to the value of the option if it is non-empty, + * and returns the return value of the callable wrapped in Some(). + * + * If the option is empty, then the callable is not applied. + * + * ```php + * (new Some("foo"))->map('strtoupper')->get(); // "FOO" + * ``` + * + * @template S + * + * @param callable(T):S $callable + * + * @return Option + */ + abstract public function map($callable); + + /** + * Applies the callable to the value of the option if it is non-empty, and + * returns the return value of the callable directly. + * + * In contrast to ``map``, the return value of the callable is expected to + * be an Option itself; it is not automatically wrapped in Some(). + * + * @template S + * + * @param callable(T):Option $callable must return an Option + * + * @return Option + */ + abstract public function flatMap($callable); + + /** + * If the option is empty, it is returned immediately without applying the callable. + * + * If the option is non-empty, the callable is applied, and if it returns true, + * the option itself is returned; otherwise, None is returned. + * + * @param callable(T):bool $callable + * + * @return Option + */ + abstract public function filter($callable); + + /** + * If the option is empty, it is returned immediately without applying the callable. + * + * If the option is non-empty, the callable is applied, and if it returns false, + * the option itself is returned; otherwise, None is returned. + * + * @param callable(T):bool $callable + * + * @return Option + */ + abstract public function filterNot($callable); + + /** + * If the option is empty, it is returned immediately. + * + * If the option is non-empty, and its value does not equal the passed value + * (via a shallow comparison ===), then None is returned. Otherwise, the + * Option is returned. + * + * In other words, this will filter all but the passed value. + * + * @param T $value + * + * @return Option + */ + abstract public function select($value); + + /** + * If the option is empty, it is returned immediately. + * + * If the option is non-empty, and its value does equal the passed value (via + * a shallow comparison ===), then None is returned; otherwise, the Option is + * returned. + * + * In other words, this will let all values through except the passed value. + * + * @param T $value + * + * @return Option + */ + abstract public function reject($value); + + /** + * Binary operator for the initial value and the option's value. + * + * If empty, the initial value is returned. If non-empty, the callable + * receives the initial value and the option's value as arguments. + * + * ```php + * + * $some = new Some(5); + * $none = None::create(); + * $result = $some->foldLeft(1, function($a, $b) { return $a + $b; }); // int(6) + * $result = $none->foldLeft(1, function($a, $b) { return $a + $b; }); // int(1) + * + * // This can be used instead of something like the following: + * $option = Option::fromValue($integerOrNull); + * $result = 1; + * if ( ! $option->isEmpty()) { + * $result += $option->get(); + * } + * ``` + * + * @template S + * + * @param S $initialValue + * @param callable(S, T):S $callable + * + * @return S + */ + abstract public function foldLeft($initialValue, $callable); + + /** + * foldLeft() but with reversed arguments for the callable. + * + * @template S + * + * @param S $initialValue + * @param callable(T, S):S $callable + * + * @return S + */ + abstract public function foldRight($initialValue, $callable); +} diff --git a/vendor/phpoption/phpoption/src/PhpOption/Some.php b/vendor/phpoption/phpoption/src/PhpOption/Some.php new file mode 100644 index 0000000..032632e --- /dev/null +++ b/vendor/phpoption/phpoption/src/PhpOption/Some.php @@ -0,0 +1,169 @@ + + * + * 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. + */ + +namespace PhpOption; + +use ArrayIterator; + +/** + * @template T + * + * @extends Option + */ +final class Some extends Option +{ + /** @var T */ + private $value; + + /** + * @param T $value + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * @template U + * + * @param U $value + * + * @return Some + */ + public static function create($value): self + { + return new self($value); + } + + public function isDefined(): bool + { + return true; + } + + public function isEmpty(): bool + { + return false; + } + + public function get() + { + return $this->value; + } + + public function getOrElse($default) + { + return $this->value; + } + + public function getOrCall($callable) + { + return $this->value; + } + + public function getOrThrow(\Exception $ex) + { + return $this->value; + } + + public function orElse(Option $else) + { + return $this; + } + + public function ifDefined($callable) + { + $this->forAll($callable); + } + + public function forAll($callable) + { + $callable($this->value); + + return $this; + } + + public function map($callable) + { + return new self($callable($this->value)); + } + + public function flatMap($callable) + { + /** @var mixed */ + $rs = $callable($this->value); + if (!$rs instanceof Option) { + throw new \RuntimeException('Callables passed to flatMap() must return an Option. Maybe you should use map() instead?'); + } + + return $rs; + } + + public function filter($callable) + { + if (true === $callable($this->value)) { + return $this; + } + + return None::create(); + } + + public function filterNot($callable) + { + if (false === $callable($this->value)) { + return $this; + } + + return None::create(); + } + + public function select($value) + { + if ($this->value === $value) { + return $this; + } + + return None::create(); + } + + public function reject($value) + { + if ($this->value === $value) { + return None::create(); + } + + return $this; + } + + /** + * @return ArrayIterator + */ + public function getIterator(): ArrayIterator + { + return new ArrayIterator([$this->value]); + } + + public function foldLeft($initialValue, $callable) + { + return $callable($initialValue, $this->value); + } + + public function foldRight($initialValue, $callable) + { + return $callable($this->value, $initialValue); + } +} diff --git a/vendor/psr/cache/CHANGELOG.md b/vendor/psr/cache/CHANGELOG.md new file mode 100644 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 100644 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 100644 index 0000000..9855a31 --- /dev/null +++ b/vendor/psr/cache/README.md @@ -0,0 +1,12 @@ +Caching Interface +============== + +This repository holds all interfaces related to [PSR-6 (Caching Interface)][psr-url]. + +Note that this is not a Caching implementation of its own. It is merely interfaces that describe the components of a Caching mechanism. + +The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist. + +[psr-url]: https://www.php-fig.org/psr/psr-6/ +[package-url]: https://packagist.org/packages/psr/cache +[implementation-url]: https://packagist.org/providers/psr/cache-implementation diff --git a/vendor/psr/cache/composer.json b/vendor/psr/cache/composer.json new file mode 100644 index 0000000..4b68797 --- /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": "https://www.php-fig.org/" + } + ], + "require": { + "php": ">=8.0.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 100644 index 0000000..bb785f4 --- /dev/null +++ b/vendor/psr/cache/src/CacheException.php @@ -0,0 +1,10 @@ +clock = $clock; + } + + public function doSomething() + { + /** @var DateTimeImmutable $currentDateAndTime */ + $currentDateAndTime = $this->clock->now(); + // do something useful with that information + } +} +``` + +You can then pick one of the [implementations][implementation-url] of the interface to get a clock. + +If you want to implement the interface, you can require this package and +implement `Psr\Clock\ClockInterface` in your code. + +Don't forget to add `psr/clock-implementation` to your `composer.json`s `provides`-section like this: + +```json +{ + "provides": { + "psr/clock-implementation": "1.0" + } +} +``` + +And please read the [specification text][specification-url] for details on the interface. + +[psr-url]: https://www.php-fig.org/psr/psr-20 +[package-url]: https://packagist.org/packages/psr/clock +[implementation-url]: https://packagist.org/providers/psr/clock-implementation +[specification-url]: https://github.com/php-fig/fig-standards/blob/master/proposed/clock.md diff --git a/vendor/psr/clock/composer.json b/vendor/psr/clock/composer.json new file mode 100644 index 0000000..77992ed --- /dev/null +++ b/vendor/psr/clock/composer.json @@ -0,0 +1,21 @@ +{ + "name": "psr/clock", + "description": "Common interface for reading the clock.", + "keywords": ["psr", "psr-20", "time", "clock", "now"], + "homepage": "https://github.com/php-fig/clock", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "require": { + "php": "^7.0 || ^8.0" + }, + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + } +} diff --git a/vendor/psr/clock/src/ClockInterface.php b/vendor/psr/clock/src/ClockInterface.php new file mode 100644 index 0000000..7b6d8d8 --- /dev/null +++ b/vendor/psr/clock/src/ClockInterface.php @@ -0,0 +1,13 @@ +=7.4.0" + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + } +} diff --git a/vendor/psr/container/src/ContainerExceptionInterface.php b/vendor/psr/container/src/ContainerExceptionInterface.php new file mode 100644 index 0000000..0f213f2 --- /dev/null +++ b/vendor/psr/container/src/ContainerExceptionInterface.php @@ -0,0 +1,12 @@ +=7.0.0", + "psr/http-message": "^1.0 || ^2.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 100644 index 0000000..cb39a08 --- /dev/null +++ b/vendor/psr/http-factory/src/RequestFactoryInterface.php @@ -0,0 +1,18 @@ + `RequestInterface`, `ServerRequestInterface`, `ResponseInterface` extend `MessageInterface` because the `Request` and the `Response` are `HTTP Messages`. +> When using `ServerRequestInterface`, both `RequestInterface` and `Psr\Http\Message\MessageInterface` methods are considered. + diff --git a/vendor/psr/http-message/docs/PSR7-Usage.md b/vendor/psr/http-message/docs/PSR7-Usage.md new file mode 100644 index 0000000..b6d048a --- /dev/null +++ b/vendor/psr/http-message/docs/PSR7-Usage.md @@ -0,0 +1,159 @@ +### PSR-7 Usage + +All PSR-7 applications comply with these interfaces +They were created to establish a standard between middleware implementations. + +> `RequestInterface`, `ServerRequestInterface`, `ResponseInterface` extend `MessageInterface` because the `Request` and the `Response` are `HTTP Messages`. +> When using `ServerRequestInterface`, both `RequestInterface` and `Psr\Http\Message\MessageInterface` methods are considered. + + +The following examples will illustrate how basic operations are done in PSR-7. + +##### Examples + + +For this examples to work (at least) a PSR-7 implementation package is required. (eg: zendframework/zend-diactoros, guzzlehttp/psr7, slim/slim, etc) +All PSR-7 implementations should have the same behaviour. + +The following will be assumed: +`$request` is an object of `Psr\Http\Message\RequestInterface` and + +`$response` is an object implementing `Psr\Http\Message\RequestInterface` + + +### Working with HTTP Headers + +#### Adding headers to response: + +```php +$response->withHeader('My-Custom-Header', 'My Custom Message'); +``` + +#### Appending values to headers + +```php +$response->withAddedHeader('My-Custom-Header', 'The second message'); +``` + +#### Checking if header exists: + +```php +$request->hasHeader('My-Custom-Header'); // will return false +$response->hasHeader('My-Custom-Header'); // will return true +``` + +> Note: My-Custom-Header was only added in the Response + +#### Getting comma-separated values from a header (also applies to request) + +```php +// getting value from request headers +$request->getHeaderLine('Content-Type'); // will return: "text/html; charset=UTF-8" +// getting value from response headers +$response->getHeaderLine('My-Custom-Header'); // will return: "My Custom Message; The second message" +``` + +#### Getting array of value from a header (also applies to request) +```php +// getting value from request headers +$request->getHeader('Content-Type'); // will return: ["text/html", "charset=UTF-8"] +// getting value from response headers +$response->getHeader('My-Custom-Header'); // will return: ["My Custom Message", "The second message"] +``` + +#### Removing headers from HTTP Messages +```php +// removing a header from Request, removing deprecated "Content-MD5" header +$request->withoutHeader('Content-MD5'); + +// removing a header from Response +// effect: the browser won't know the size of the stream +// the browser will download the stream till it ends +$response->withoutHeader('Content-Length'); +``` + +### Working with HTTP Message Body + +When working with the PSR-7 there are two methods of implementation: +#### 1. Getting the body separately + +> This method makes the body handling easier to understand and is useful when repeatedly calling body methods. (You only call `getBody()` once). Using this method mistakes like `$response->write()` are also prevented. + +```php +$body = $response->getBody(); +// operations on body, eg. read, write, seek +// ... +// replacing the old body +$response->withBody($body); +// this last statement is optional as we working with objects +// in this case the "new" body is same with the "old" one +// the $body variable has the same value as the one in $request, only the reference is passed +``` + +#### 2. Working directly on response + +> This method is useful when only performing few operations as the `$request->getBody()` statement fragment is required + +```php +$response->getBody()->write('hello'); +``` + +### Getting the body contents + +The following snippet gets the contents of a stream contents. +> Note: Streams must be rewinded, if content was written into streams, it will be ignored when calling `getContents()` because the stream pointer is set to the last character, which is `\0` - meaning end of stream. +```php +$body = $response->getBody(); +$body->rewind(); // or $body->seek(0); +$bodyText = $body->getContents(); +``` +> Note: If `$body->seek(1)` is called before `$body->getContents()`, the first character will be ommited as the starting pointer is set to `1`, not `0`. This is why using `$body->rewind()` is recommended. + +### Append to body + +```php +$response->getBody()->write('Hello'); // writing directly +$body = $request->getBody(); // which is a `StreamInterface` +$body->write('xxxxx'); +``` + +### Prepend to body +Prepending is different when it comes to streams. The content must be copied before writing the content to be prepended. +The following example will explain the behaviour of streams. + +```php +// assuming our response is initially empty +$body = $repsonse->getBody(); +// writing the string "abcd" +$body->write('abcd'); + +// seeking to start of stream +$body->seek(0); +// writing 'ef' +$body->write('ef'); // at this point the stream contains "efcd" +``` + +#### Prepending by rewriting separately + +```php +// assuming our response body stream only contains: "abcd" +$body = $response->getBody(); +$body->rewind(); +$contents = $body->getContents(); // abcd +// seeking the stream to beginning +$body->rewind(); +$body->write('ef'); // stream contains "efcd" +$body->write($contents); // stream contains "efabcd" +``` + +> Note: `getContents()` seeks the stream while reading it, therefore if the second `rewind()` method call was not present the stream would have resulted in `abcdefabcd` because the `write()` method appends to stream if not preceeded by `rewind()` or `seek(0)`. + +#### Prepending by using contents as a string +```php +$body = $response->getBody(); +$body->rewind(); +$contents = $body->getContents(); // efabcd +$contents = 'ef'.$contents; +$body->rewind(); +$body->write($contents); +``` diff --git a/vendor/psr/http-message/src/MessageInterface.php b/vendor/psr/http-message/src/MessageInterface.php new file mode 100644 index 0000000..a83c985 --- /dev/null +++ b/vendor/psr/http-message/src/MessageInterface.php @@ -0,0 +1,187 @@ +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(): array; + + /** + * 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(string $name): bool; + + /** + * 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(string $name): array; + + /** + * 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(string $name): string; + + /** + * 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(string $name, $value): MessageInterface; + + /** + * 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(string $name, $value): MessageInterface; + + /** + * 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(string $name): MessageInterface; + + /** + * Gets the body of the message. + * + * @return StreamInterface Returns the body as a stream. + */ + public function getBody(): StreamInterface; + + /** + * 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): MessageInterface; +} diff --git a/vendor/psr/http-message/src/RequestInterface.php b/vendor/psr/http-message/src/RequestInterface.php new file mode 100644 index 0000000..33f85e5 --- /dev/null +++ b/vendor/psr/http-message/src/RequestInterface.php @@ -0,0 +1,130 @@ +getQuery()` + * or from the `QUERY_STRING` server param. + * + * @return array + */ + public function getQueryParams(): array; + + /** + * 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): ServerRequestInterface; + + /** + * 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(): array; + + /** + * 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): ServerRequestInterface; + + /** + * 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): ServerRequestInterface; + + /** + * 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(): array; + + /** + * 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(string $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(string $name, $value): ServerRequestInterface; + + /** + * 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(string $name): ServerRequestInterface; +} diff --git a/vendor/psr/http-message/src/StreamInterface.php b/vendor/psr/http-message/src/StreamInterface.php new file mode 100644 index 0000000..a62aabb --- /dev/null +++ b/vendor/psr/http-message/src/StreamInterface.php @@ -0,0 +1,158 @@ + + * [user-info@]host[:port] + *
+ * + * 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(): string; + + /** + * 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(): string; + + /** + * 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(): string; + + /** + * 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(): ?int; + + /** + * 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(): string; + + /** + * 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(): string; + + /** + * 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(): string; + + /** + * 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(string $scheme): UriInterface; + + /** + * 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(string $user, ?string $password = null): UriInterface; + + /** + * 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(string $host): UriInterface; + + /** + * 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(?int $port): UriInterface; + + /** + * 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(string $path): UriInterface; + + /** + * 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(string $query): UriInterface; + + /** + * 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(string $fragment): UriInterface; + + /** + * 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(): string; +} diff --git a/vendor/psr/log/LICENSE b/vendor/psr/log/LICENSE new file mode 100644 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/README.md b/vendor/psr/log/README.md new file mode 100644 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 +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 100644 index 0000000..f3f0667 --- /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": "https://www.php-fig.org/" + } + ], + "require": { + "php": ">=8.0.0" + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + } +} diff --git a/vendor/psr/log/src/AbstractLogger.php b/vendor/psr/log/src/AbstractLogger.php new file mode 100644 index 0000000..d60a091 --- /dev/null +++ b/vendor/psr/log/src/AbstractLogger.php @@ -0,0 +1,15 @@ +logger = $logger; + } +} diff --git a/vendor/psr/log/src/LoggerInterface.php b/vendor/psr/log/src/LoggerInterface.php new file mode 100644 index 0000000..b4d062b --- /dev/null +++ b/vendor/psr/log/src/LoggerInterface.php @@ -0,0 +1,125 @@ +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|\Stringable $message + * @param array $context + * + * @return void + */ + public function alert(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function critical(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function error(string|\Stringable $message, array $context = []) + { + $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|\Stringable $message + * @param array $context + * + * @return void + */ + public function warning(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function notice(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function info(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function debug(string|\Stringable $message, array $context = []) + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string|\Stringable $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + abstract public function log($level, string|\Stringable $message, array $context = []); +} diff --git a/vendor/psr/log/src/NullLogger.php b/vendor/psr/log/src/NullLogger.php new file mode 100644 index 0000000..5607705 --- /dev/null +++ b/vendor/psr/log/src/NullLogger.php @@ -0,0 +1,30 @@ +logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string|\Stringable $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + public function log($level, string|\Stringable $message, array $context = []) + { + // noop + } +} diff --git a/vendor/psr/simple-cache/.editorconfig b/vendor/psr/simple-cache/.editorconfig new file mode 100644 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 100644 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 100644 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 100644 index 0000000..a520e7d --- /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": "https://www.php-fig.org/" + } + ], + "require": { + "php": ">=8.0.0" + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + } +} diff --git a/vendor/psr/simple-cache/src/CacheException.php b/vendor/psr/simple-cache/src/CacheException.php new file mode 100644 index 0000000..f61b24c --- /dev/null +++ b/vendor/psr/simple-cache/src/CacheException.php @@ -0,0 +1,10 @@ + $keys A list of keys that can be 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(iterable $keys, mixed $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(iterable $values, null|int|\DateInterval $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(iterable $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(string $key); +} diff --git a/vendor/psr/simple-cache/src/InvalidArgumentException.php b/vendor/psr/simple-cache/src/InvalidArgumentException.php new file mode 100644 index 0000000..6a9524a --- /dev/null +++ b/vendor/psr/simple-cache/src/InvalidArgumentException.php @@ -0,0 +1,13 @@ += 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 + +For PHP version **`>= 5.6`**: + +``` +composer require ralouphie/getallheaders +``` + +For PHP version **`< 5.6`**: + +``` +composer require ralouphie/getallheaders "^2" +``` diff --git a/vendor/ralouphie/getallheaders/composer.json b/vendor/ralouphie/getallheaders/composer.json new file mode 100644 index 0000000..de8ce62 --- /dev/null +++ b/vendor/ralouphie/getallheaders/composer.json @@ -0,0 +1,26 @@ +{ + "name": "ralouphie/getallheaders", + "description": "A polyfill for getallheaders.", + "license": "MIT", + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "require": { + "php": ">=5.6" + }, + "require-dev": { + "phpunit/phpunit": "^5 || ^6.5", + "php-coveralls/php-coveralls": "^2.1" + }, + "autoload": { + "files": ["src/getallheaders.php"] + }, + "autoload-dev": { + "psr-4": { + "getallheaders\\Tests\\": "tests/" + } + } +} diff --git a/vendor/ralouphie/getallheaders/src/getallheaders.php b/vendor/ralouphie/getallheaders/src/getallheaders.php new file mode 100644 index 0000000..c7285a5 --- /dev/null +++ b/vendor/ralouphie/getallheaders/src/getallheaders.php @@ -0,0 +1,46 @@ + '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/respect/stringifier/LICENSE.md b/vendor/respect/stringifier/LICENSE.md new file mode 100644 index 0000000..b7df61c --- /dev/null +++ b/vendor/respect/stringifier/LICENSE.md @@ -0,0 +1,21 @@ +# License + +Copyright (c) [Henrique Moody](http://github.com/henriquemoody). + +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/respect/stringifier/README.md b/vendor/respect/stringifier/README.md new file mode 100644 index 0000000..c880904 --- /dev/null +++ b/vendor/respect/stringifier/README.md @@ -0,0 +1,46 @@ +# Respect\Stringifier + +[![Build Status](https://img.shields.io/travis/Respect/Stringifier/master.svg?style=flat-square)](http://travis-ci.org/Respect/Stringifier) +[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/Respect/Stringifier/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/Respect/Stringifier/?branch=master) +[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/Respect/Stringifier/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/Respect/Stringifier/?branch=master) +[![Latest Stable Version](https://img.shields.io/packagist/v/respect/stringifier.svg?style=flat-square)](https://packagist.org/packages/respect/stringifier) +[![Total Downloads](https://img.shields.io/packagist/dt/respect/stringifier.svg?style=flat-square)](https://packagist.org/packages/respect/stringifier) +[![License](https://img.shields.io/packagist/l/respect/stringifier.svg?style=flat-square)](https://packagist.org/packages/respect/stringifier) + +Converts any PHP value into a string. + +## Installation + +Package is available on [Packagist](https://packagist.org/packages/respect/stringifier), you can install it +using [Composer](http://getcomposer.org). + +```bash +composer require respect/stringifier +``` + +This library requires PHP >= 7.1. + +## Feature Guide + +Below a quick guide of how to use the library. + +### Namespace import + +Respect\Stringifier is namespaced, and you can make your life easier by importing +a single function into your context: + +```php +use function Respect\Stringifier\stringify; +``` + +Stringifier was built using objects, the `stringify()` is a easy way to use it. + +### Usage + +Simply use the function to convert any value you want to: + +```php +echo stringify($value); +``` + +To see more examples of how to use the library check the [integration tests](tests/integration). diff --git a/vendor/respect/stringifier/composer.json b/vendor/respect/stringifier/composer.json new file mode 100644 index 0000000..f63ace2 --- /dev/null +++ b/vendor/respect/stringifier/composer.json @@ -0,0 +1,36 @@ +{ + "name": "respect/stringifier", + "description": "Converts any value to a string", + "keywords": ["respect", "stringifier", "stringify"], + "type": "library", + "homepage": "http://respect.github.io/Stringifier/", + "license": "MIT", + "authors": [ + { + "name": "Respect/Stringifier Contributors", + "homepage": "https://github.com/Respect/Stringifier/graphs/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.8", + "malukenho/docheader": "^0.1.7", + "phpunit/phpunit": "^6.4" + }, + "autoload": { + "psr-4": { + "Respect\\Stringifier\\": "src/" + }, + "files": [ + "src/stringify.php" + ] + }, + "scripts": { + "docheader": "vendor/bin/docheader check src/ tests/", + "test": "vendor/bin/phpunit", + "test-unit": "vendor/bin/phpunit --testsuite=unit", + "test-integration": "vendor/bin/phpunit --testsuite=integration" + } +} diff --git a/vendor/respect/stringifier/src/Quoter.php b/vendor/respect/stringifier/src/Quoter.php new file mode 100644 index 0000000..3d5e9cf --- /dev/null +++ b/vendor/respect/stringifier/src/Quoter.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier; + +interface Quoter +{ + /** + * Should add quotes to the given string. + * + * @param string $string The string to add quotes to + * @param int $depth The current depth + * + * @return string + */ + public function quote(string $string, int $depth): string; +} diff --git a/vendor/respect/stringifier/src/Quoters/CodeQuoter.php b/vendor/respect/stringifier/src/Quoters/CodeQuoter.php new file mode 100644 index 0000000..d663ce0 --- /dev/null +++ b/vendor/respect/stringifier/src/Quoters/CodeQuoter.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Quoters; + +use Respect\Stringifier\Quoter; + +/** + * Add "`" quotes around a string depending on its level. + * + * @author Henrique Moody + */ +final class CodeQuoter implements Quoter +{ + /** + * {@inheritdoc} + */ + public function quote(string $string, int $depth): string + { + if (0 === $depth) { + return sprintf('`%s`', $string); + } + + return $string; + } +} diff --git a/vendor/respect/stringifier/src/Stringifier.php b/vendor/respect/stringifier/src/Stringifier.php new file mode 100644 index 0000000..b89e56d --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifier.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier; + +interface Stringifier +{ + /** + * Converts the value into string if possible. + * + * @param mixed $raw The raw value to be converted. + * @param int $depth The current depth of the conversion. + * + * @return null|string Returns NULL when the conversion is not possible. + */ + public function stringify($raw, int $depth): ?string; +} diff --git a/vendor/respect/stringifier/src/Stringifiers/ArrayStringifier.php b/vendor/respect/stringifier/src/Stringifiers/ArrayStringifier.php new file mode 100644 index 0000000..d7912ad --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/ArrayStringifier.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use function array_keys; +use function implode; +use function is_array; +use function is_int; +use function sprintf; +use Respect\Stringifier\Quoter; +use Respect\Stringifier\Stringifier; + +/** + * Converts an array value into a string. + * + * @author Henrique Moody + */ +final class ArrayStringifier implements Stringifier +{ + /** + * @var Stringifier + */ + private $stringifier; + + /** + * @var Quoter + */ + private $quoter; + + /** + * @var int + */ + private $maximumDepth; + + /** + * @var int + */ + private $itemsLimit; + + /** + * Initializes the stringifier. + * + * @param Stringifier $stringifier + * @param Quoter $quoter + * @param int $maximumDepth + * @param int $itemsLimit + */ + public function __construct(Stringifier $stringifier, Quoter $quoter, int $maximumDepth, int $itemsLimit) + { + $this->stringifier = $stringifier; + $this->quoter = $quoter; + $this->maximumDepth = $maximumDepth; + $this->itemsLimit = $itemsLimit; + } + + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + if (!is_array($raw)) { + return null; + } + + if (empty($raw)) { + return $this->quoter->quote('{ }', $depth); + } + + if ($depth >= $this->maximumDepth) { + return '...'; + } + + $items = []; + $itemsCount = 0; + $isSequential = $this->isSequential($raw); + foreach ($raw as $key => $value) { + if (++$itemsCount > $this->itemsLimit) { + $items[$itemsCount] = '...'; + break; + } + + $items[$itemsCount] = ''; + if (false === $isSequential) { + $items[$itemsCount] .= sprintf('%s: ', $this->stringifier->stringify($key, $depth + 1)); + } + $items[$itemsCount] .= $this->stringifier->stringify($value, $depth + 1); + } + + return $this->quoter->quote(sprintf('{ %s }', implode(', ', $items)), $depth); + } + + /** + * Returns whether the array is sequential or not. + * + * @param array $array + * + * @return bool + */ + private function isSequential(array $array): bool + { + return array_keys($array) === range(0, count($array) - 1); + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/BoolStringifier.php b/vendor/respect/stringifier/src/Stringifiers/BoolStringifier.php new file mode 100644 index 0000000..74debc6 --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/BoolStringifier.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use function is_bool; +use Respect\Stringifier\Quoter; +use Respect\Stringifier\Stringifier; + +/** + * Converts a boolean value into a string. + * + * @author Henrique Moody + */ +final class BoolStringifier implements Stringifier +{ + /** + * @var Quoter + */ + private $quoter; + + /** + * Initializes the stringifier. + * + * @param Quoter $quoter + */ + public function __construct(Quoter $quoter) + { + $this->quoter = $quoter; + } + + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + if (!is_bool($raw)) { + return null; + } + + return $this->quoter->quote($raw ? 'TRUE' : 'FALSE', $depth); + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/ClusterStringifier.php b/vendor/respect/stringifier/src/Stringifiers/ClusterStringifier.php new file mode 100644 index 0000000..30de710 --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/ClusterStringifier.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use Respect\Stringifier\Quoters\CodeQuoter; +use Respect\Stringifier\Quoters\StringQuoter; +use Respect\Stringifier\Stringifier; + +/** + * Converts a value into a string using the defined Stringifiers. + * + * @author Henrique Moody + */ +final class ClusterStringifier implements Stringifier +{ + /** + * @var Stringifier[] + */ + private $stringifiers; + + /** + * Initializes the stringifier. + * + * @param Stringifier[] ...$stringifiers + */ + public function __construct(Stringifier ...$stringifiers) + { + $this->setStringifiers($stringifiers); + } + + /** + * Create a default instance of the class. + * + * This instance includes all possible stringifiers. + * + * @return ClusterStringifier + */ + public static function createDefault(): self + { + $quoter = new CodeQuoter(); + + $stringifier = new self(); + $stringifier->setStringifiers([ + new TraversableStringifier($stringifier, $quoter), + new DateTimeStringifier($stringifier, $quoter, 'c'), + new ThrowableStringifier($stringifier, $quoter), + new StringableObjectStringifier($stringifier), + new JsonSerializableStringifier($stringifier, $quoter), + new ObjectStringifier($stringifier, $quoter), + new ArrayStringifier($stringifier, $quoter, 3, 5), + new InfiniteStringifier($quoter), + new NanStringifier($quoter), + new ResourceStringifier($quoter), + new BoolStringifier($quoter), + new NullStringifier($quoter), + new JsonParsableStringifier(), + ]); + + return $stringifier; + } + + /** + * Set stringifiers. + * + * @param array $stringifiers + * + * @return void + */ + public function setStringifiers(array $stringifiers): void + { + $this->stringifiers = []; + + foreach ($stringifiers as $stringifier) { + $this->addStringifier($stringifier); + } + } + + /** + * Add a stringifier to the chain + * + * @param Stringifier $stringifier + * + * @return void + */ + public function addStringifier(Stringifier $stringifier): void + { + $this->stringifiers[] = $stringifier; + } + + /** + * {@inheritdoc} + */ + public function stringify($value, int $depth): ?string + { + foreach ($this->stringifiers as $stringifier) { + $string = $stringifier->stringify($value, $depth); + if (null === $string) { + continue; + } + + return $string; + } + + return null; + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/DateTimeStringifier.php b/vendor/respect/stringifier/src/Stringifiers/DateTimeStringifier.php new file mode 100644 index 0000000..be9335f --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/DateTimeStringifier.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use DateTimeInterface; +use function get_class; +use function sprintf; +use Respect\Stringifier\Quoter; +use Respect\Stringifier\Stringifier; + +/** + * Converts an instance of DateTimeInterface into a string. + * + * @author Henrique Moody + */ +final class DateTimeStringifier implements Stringifier +{ + /** + * @var Stringifier + */ + private $stringifier; + + /** + * @var Quoter + */ + private $quoter; + + /** + * @var string + */ + private $format; + + /** + * Initializes the stringifier. + * + * @param Stringifier $stringifier + * @param Quoter $quoter + * @param string $format + */ + public function __construct(Stringifier $stringifier, Quoter $quoter, string $format) + { + $this->stringifier = $stringifier; + $this->quoter = $quoter; + $this->format = $format; + } + + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + if (!$raw instanceof DateTimeInterface) { + return null; + } + + return $this->quoter->quote( + sprintf( + '[date-time] (%s: %s)', + get_class($raw), + $this->stringifier->stringify($raw->format($this->format), $depth + 1) + ), + $depth + ); + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/InfiniteStringifier.php b/vendor/respect/stringifier/src/Stringifiers/InfiniteStringifier.php new file mode 100644 index 0000000..b09b3d3 --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/InfiniteStringifier.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use function is_float; +use function is_infinite; +use Respect\Stringifier\Quoter; +use Respect\Stringifier\Stringifier; + +/** + * Converts an infinite float value into a string. + * + * @author Henrique Moody + */ +final class InfiniteStringifier implements Stringifier +{ + /** + * @var Quoter + */ + private $quoter; + + /** + * Initializes the stringifier. + * + * @param Quoter $quoter + */ + public function __construct(Quoter $quoter) + { + $this->quoter = $quoter; + } + + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + if (!is_float($raw)) { + return null; + } + + if (!is_infinite($raw)) { + return null; + } + + return $this->quoter->quote(($raw > 0 ? '' : '-').'INF', $depth); + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/JsonParsableStringifier.php b/vendor/respect/stringifier/src/Stringifiers/JsonParsableStringifier.php new file mode 100644 index 0000000..3db387b --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/JsonParsableStringifier.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use const JSON_UNESCAPED_UNICODE; +use const JSON_UNESCAPED_SLASHES; +use function json_encode; +use Respect\Stringifier\Stringifier; + +/** + * Converts any value into JSON parsable string representation. + * + * @author Henrique Moody + */ +final class JsonParsableStringifier implements Stringifier +{ + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + $string = json_encode($raw, (JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION)); + if (false === $string) { + return null; + } + + return $string; + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/JsonSerializableStringifier.php b/vendor/respect/stringifier/src/Stringifiers/JsonSerializableStringifier.php new file mode 100644 index 0000000..67986e5 --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/JsonSerializableStringifier.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use Respect\Stringifier\Quoter; +use Respect\Stringifier\Stringifier; +use JsonSerializable; + +/** + * Converts an instance of JsonSerializable into a string. + * + * @author Henrique Moody + */ +final class JsonSerializableStringifier implements Stringifier +{ + /** + * @var Stringifier + */ + private $stringifier; + + /** + * @var Quoter + */ + private $quoter; + + /** + * Initializes the stringifier. + * + * @param Stringifier $stringifier + * @param Quoter $quoter + */ + public function __construct(Stringifier $stringifier, Quoter $quoter) + { + $this->stringifier = $stringifier; + $this->quoter = $quoter; + } + + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + if (!$raw instanceof JsonSerializable) { + return null; + } + + return $this->quoter->quote( + sprintf( + '[json-serializable] (%s: %s)', + get_class($raw), + $this->stringifier->stringify($raw->jsonSerialize(), $depth + 1) + ), + $depth + ); + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/NanStringifier.php b/vendor/respect/stringifier/src/Stringifiers/NanStringifier.php new file mode 100644 index 0000000..5afeefc --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/NanStringifier.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use function is_float; +use function is_nan; +use Respect\Stringifier\Quoter; +use Respect\Stringifier\Stringifier; + +/** + * Converts a NaN value into a string. + * + * @author Henrique Moody + */ +final class NanStringifier implements Stringifier +{ + /** + * @var Quoter + */ + private $quoter; + + /** + * Initializes the stringifier. + * + * @param Quoter $quoter + */ + public function __construct(Quoter $quoter) + { + $this->quoter = $quoter; + } + + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + if (!is_float($raw)) { + return null; + } + + if (!is_nan($raw)) { + return null; + } + + return $this->quoter->quote('NaN', $depth); + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/NullStringifier.php b/vendor/respect/stringifier/src/Stringifiers/NullStringifier.php new file mode 100644 index 0000000..a430e36 --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/NullStringifier.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use Respect\Stringifier\Quoter; +use Respect\Stringifier\Stringifier; + +/** + * Converts a NULL value into a string. + * + * @author Henrique Moody + */ +final class NullStringifier implements Stringifier +{ + /** + * @var Quoter + */ + private $quoter; + + /** + * Initializes the stringifier. + * + * @param Quoter $quoter + */ + public function __construct(Quoter $quoter) + { + $this->quoter = $quoter; + } + + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + if (null !== $raw) { + return null; + } + + return $this->quoter->quote('NULL', $depth); + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/ObjectStringifier.php b/vendor/respect/stringifier/src/Stringifiers/ObjectStringifier.php new file mode 100644 index 0000000..e19cbb3 --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/ObjectStringifier.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use function get_class; +use function get_object_vars; +use function is_object; +use function sprintf; +use Respect\Stringifier\Quoter; +use Respect\Stringifier\Stringifier; + +/** + * Converts an object into a string. + * + * @author Henrique Moody + */ +final class ObjectStringifier implements Stringifier +{ + /** + * @var Stringifier + */ + private $stringifier; + + /** + * @var Quoter + */ + private $quoter; + + /** + * Initializes the stringifier. + * + * @param Stringifier $stringifier + * @param Quoter $quoter + */ + public function __construct(Stringifier $stringifier, Quoter $quoter) + { + $this->stringifier = $stringifier; + $this->quoter = $quoter; + } + + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + if (!is_object($raw)) { + return null; + } + + return $this->quoter->quote( + sprintf( + '[object] (%s: %s)', + get_class($raw), + $this->stringifier->stringify(get_object_vars($raw), $depth + 1) + ), + $depth + ); + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/ResourceStringifier.php b/vendor/respect/stringifier/src/Stringifiers/ResourceStringifier.php new file mode 100644 index 0000000..2efb4ea --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/ResourceStringifier.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use function get_resource_type; +use function is_resource; +use function sprintf; +use Respect\Stringifier\Quoter; +use Respect\Stringifier\Stringifier; + +/** + * Converts a resource value into a string. + * + * @author Henrique Moody + */ +final class ResourceStringifier implements Stringifier +{ + /** + * @var Quoter + */ + private $quoter; + + /** + * Initializes the stringifier. + * + * @param Quoter $quoter + */ + public function __construct(Quoter $quoter) + { + $this->quoter = $quoter; + } + + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + if (!is_resource($raw)) { + return null; + } + + return $this->quoter->quote( + sprintf( + '[resource] (%s)', + get_resource_type($raw) + ), + $depth + ); + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/StringableObjectStringifier.php b/vendor/respect/stringifier/src/Stringifiers/StringableObjectStringifier.php new file mode 100644 index 0000000..18e5ef8 --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/StringableObjectStringifier.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use function is_object; +use function method_exists; +use Respect\Stringifier\Stringifier; + +/** + * Converts a object that implements the __toString() magic method into a string. + * + * @author Henrique Moody + */ +final class StringableObjectStringifier implements Stringifier +{ + /** + * @var Stringifier + */ + private $stringifier; + + /** + * Initializes the stringifier. + * + * @param Stringifier $stringifier + */ + public function __construct(Stringifier $stringifier) + { + $this->stringifier = $stringifier; + } + + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + if (!is_object($raw)) { + return null; + } + + if (!method_exists($raw, '__toString')) { + return null; + } + + return $this->stringifier->stringify($raw->__toString(), $depth); + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/ThrowableStringifier.php b/vendor/respect/stringifier/src/Stringifiers/ThrowableStringifier.php new file mode 100644 index 0000000..f9783a3 --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/ThrowableStringifier.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use function get_class; +use function getcwd; +use function sprintf; +use function str_replace; +use Respect\Stringifier\Quoter; +use Respect\Stringifier\Stringifier; +use Throwable; + +/** + * Converts an instance of Throwable into a string. + * + * @author Henrique Moody + */ +final class ThrowableStringifier implements Stringifier +{ + /** + * @var Stringifier + */ + private $stringifier; + + /** + * @var Quoter + */ + private $quoter; + + /** + * Initializes the stringifier. + * + * @param Stringifier $stringifier + * @param Quoter $quoter + */ + public function __construct(Stringifier $stringifier, Quoter $quoter) + { + $this->stringifier = $stringifier; + $this->quoter = $quoter; + } + + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + if (!$raw instanceof Throwable) { + return null; + } + + return $this->quoter->quote( + sprintf( + '[throwable] (%s: %s)', + get_class($raw), + $this->stringifier->stringify($this->getData($raw), $depth + 1) + ), + $depth + ); + } + + private function getData(Throwable $throwable): array + { + return [ + 'message' => $throwable->getMessage(), + 'code' => $throwable->getCode(), + 'file' => sprintf( + '%s:%d', + str_replace(getcwd().'/', '', $throwable->getFile()), + $throwable->getLine() + ), + ]; + } +} diff --git a/vendor/respect/stringifier/src/Stringifiers/TraversableStringifier.php b/vendor/respect/stringifier/src/Stringifiers/TraversableStringifier.php new file mode 100644 index 0000000..6c33f59 --- /dev/null +++ b/vendor/respect/stringifier/src/Stringifiers/TraversableStringifier.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier\Stringifiers; + +use function get_class; +use function iterator_to_array; +use Respect\Stringifier\Quoter; +use Respect\Stringifier\Stringifier; +use Traversable; + +/** + * Converts an instance of Traversable into a string. + * + * @author Henrique Moody + */ +final class TraversableStringifier implements Stringifier +{ + /** + * @var Stringifier + */ + private $stringifier; + + /** + * @var Quoter + */ + private $quoter; + + /** + * Initializes the stringifier. + * + * @param Stringifier $stringifier + * @param Quoter $quoter + */ + public function __construct(Stringifier $stringifier, Quoter $quoter) + { + $this->stringifier = $stringifier; + $this->quoter = $quoter; + } + + /** + * {@inheritdoc} + */ + public function stringify($raw, int $depth): ?string + { + if (!$raw instanceof Traversable) { + return null; + } + + return $this->quoter->quote( + sprintf( + '[traversable] (%s: %s)', + get_class($raw), + $this->stringifier->stringify(iterator_to_array($raw), $depth + 1) + ), + $depth + ); + } +} diff --git a/vendor/respect/stringifier/src/stringify.php b/vendor/respect/stringifier/src/stringify.php new file mode 100644 index 0000000..d12fddf --- /dev/null +++ b/vendor/respect/stringifier/src/stringify.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the "LICENSE.md" + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Stringifier; + +use Respect\Stringifier\Stringifiers\ClusterStringifier; + +function stringify($value): string +{ + static $stringifier; + + if (null === $stringifier) { + $stringifier = ClusterStringifier::createDefault(); + } + + return $stringifier->stringify($value, 0) ?? '#ERROR#'; +} diff --git a/vendor/robmorgan/phinx/.stickler.yml b/vendor/robmorgan/phinx/.stickler.yml new file mode 100644 index 0000000..cf599f1 --- /dev/null +++ b/vendor/robmorgan/phinx/.stickler.yml @@ -0,0 +1,12 @@ +linters: + phpcs: + standard: CakePHP3 + fixer: true + +files: + ignore: + - 'vendor/*' + +fixers: + enable: true + workflow: commit diff --git a/vendor/robmorgan/phinx/LICENSE b/vendor/robmorgan/phinx/LICENSE new file mode 100644 index 0000000..54619b9 --- /dev/null +++ b/vendor/robmorgan/phinx/LICENSE @@ -0,0 +1,23 @@ +(The MIT license) + +Copyright (c) 2012-2017 Rob Morgan +Copyright (c) 2017-present, Cake Software Foundation, Inc. (https://cakefoundation.org) + +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/robmorgan/phinx/README.md b/vendor/robmorgan/phinx/README.md new file mode 100644 index 0000000..ddecc7a --- /dev/null +++ b/vendor/robmorgan/phinx/README.md @@ -0,0 +1,143 @@ +# [Phinx](https://phinx.org): Simple PHP Database Migrations + +[![Build Status](https://github.com/cakephp/phinx/workflows/CI/badge.svg?branch=master&event=push)](https://github.com/cakephp/phinx/actions?query=workflow%3A%22CI%22+branch%3Amaster+event%3Apush) +[![Code Coverage](https://codecov.io/gh/cakephp/phinx/branch/master/graph/badge.svg)](https://codecov.io/gh/cakephp/phinx) +[![Latest Stable Version](https://poser.pugx.org/robmorgan/phinx/version.png)](https://packagist.org/packages/robmorgan/phinx) +[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.3-8892BF.svg)](https://php.net/) +[![Total Downloads](https://poser.pugx.org/robmorgan/phinx/d/total.png)](https://packagist.org/packages/robmorgan/phinx) + +## Intro + +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 [book.cakephp.org/phinx](https://book.cakephp.org/phinx) ([EN](https://book.cakephp.org/phinx), [ZH](https://tsy12321.gitbooks.io/phinx-doc/)) 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 + +See [version and branch overview](https://github.com/cakephp/phinx/wiki#version-and-branch-overview) for branch and PHP compatibility. + +### Composer + +The fastest way to install Phinx is to add it to your project using Composer (https://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 https://github.com/cakephp/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 https://book.cakephp.org/phinx for the comprehensive documentation. + +Other translations include: + + * [Chinese](https://tsy12321.gitbooks.io/phinx-doc/) (Maintained by [@tsy12321](https://github.com/tsy12321/phinx-doc)) + +## Contributing + +Please read the [CONTRIBUTING](CONTRIBUTING.md) document. + +## News & Updates + +Follow [@CakePHP](https://twitter.com/cakephp) on Twitter to stay up to date. + +## Limitations + +### PostgreSQL + +- Not able to set a unique constraint on a table (). + + +## Misc + +### Version History + +Please read the [release notes](https://github.com/cakephp/phinx/releases). + +### License + +(The MIT license) + +Copyright (c) 2017 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/robmorgan/phinx/app/phinx.php b/vendor/robmorgan/phinx/app/phinx.php new file mode 100644 index 0000000..b03ef7e --- /dev/null +++ b/vendor/robmorgan/phinx/app/phinx.php @@ -0,0 +1,36 @@ + 'getStatus', + 'migrate' => 'getMigrate', + 'rollback' => 'getRollback', +]; + +// Extract the requested command from the URL, default to "status". +$command = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/'); +if (!$command) { + $command = 'status'; +} + +// Verify that the command exists, or list available commands. +if (!isset($routes[$command])) { + $commands = implode(', ', array_keys($routes)); + header('Content-Type: text/plain', true, 404); + die("Command not found! Valid commands are: {$commands}."); +} + +// Get the environment and target version parameters. +$env = $_GET['e'] ?? null; +$target = $_GET['t'] ?? null; + +// Check if debugging is enabled. +$debug = !empty($_GET['debug']) && filter_var($_GET['debug'], FILTER_VALIDATE_BOOLEAN); + +// Execute the command and determine if it was successful. +$output = call_user_func([$wrap, $routes[$command]], $env, $target); +$error = $wrap->getExitCode() > 0; + +// Finally, display the output of the command. +header('Content-Type: text/plain', true, $error ? 500 : 200); +if ($debug) { + // Show what command was executed based on request parameters. + $args = implode(', ', [var_export($env, true), var_export($target, true)]); + echo "DEBUG: $command($args)" . PHP_EOL . PHP_EOL; +} +echo $output; diff --git a/vendor/robmorgan/phinx/bin/phinx b/vendor/robmorgan/phinx/bin/phinx new file mode 100755 index 0000000..ac37175 --- /dev/null +++ b/vendor/robmorgan/phinx/bin/phinx @@ -0,0 +1,28 @@ +#!/usr/bin/env php +run(); diff --git a/vendor/robmorgan/phinx/bin/phinx.bat b/vendor/robmorgan/phinx/bin/phinx.bat new file mode 100644 index 0000000..e7d7e7d --- /dev/null +++ b/vendor/robmorgan/phinx/bin/phinx.bat @@ -0,0 +1,41 @@ +@echo off + +rem This script will do the following: +rem - check for PHP_COMMAND env, if found, use it. +rem - if not found detect php, if found use it, otherwise err and terminate + +if "%OS%"=="Windows_NT" @setlocal + +rem %~dp0 is expanded pathname of the current script under NT +set DEFAULT_PHINX_HOME=%~dp0.. + +goto init +goto cleanup + +:init + +if "%PHINX_HOME%" == "" set PHINX_HOME=%DEFAULT_PHINX_HOME% +set DEFAULT_PHINX_HOME= + +if "%PHP_COMMAND%" == "" goto no_phpcommand + +goto run +goto cleanup + +:run +"%PHP_COMMAND%" -d html_errors=off -qC "%PHINX_HOME%\bin\phinx" %* +goto cleanup + +:no_phpcommand +rem PHP_COMMAND environment variable not found, assuming php.exe is on path. +set PHP_COMMAND=php.exe +goto init + +:err_home +echo ERROR: Environment var PHINX_HOME not set. Please point this +echo variable to your local phinx installation! +goto cleanup + +:cleanup +if "%OS%"=="Windows_NT" @endlocal +rem pause \ No newline at end of file diff --git a/vendor/robmorgan/phinx/composer.json b/vendor/robmorgan/phinx/composer.json new file mode 100644 index 0000000..e85909b --- /dev/null +++ b/vendor/robmorgan/phinx/composer.json @@ -0,0 +1,87 @@ +{ + "name": "robmorgan/phinx", + "type": "library", + "description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.", + "keywords": [ + "phinx", + "migrations", + "database", + "db", + "database migrations" + ], + "homepage": "https://phinx.org", + "license": "MIT", + "authors": [ + { + "name": "Rob Morgan", + "email": "robbym@gmail.com", + "homepage": "https://robmorgan.id.au", + "role": "Lead Developer" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com", + "homepage": "https://shadowhand.me", + "role": "Developer" + }, + { + "name": "Richard Quadling", + "email": "rquadling@gmail.com", + "role": "Developer" + }, + { + "name": "CakePHP Community", + "role": "Developer", + "homepage": "https://github.com/cakephp/phinx/graphs/contributors" + } + ], + "require": { + "php-64bit": ">=7.3", + "cakephp/database": "^4.0", + "psr/container": "^1.0 || ^2.0", + "symfony/console": "^3.4|^4.0|^5.0|^6.0", + "symfony/config": "^3.4|^4.0|^5.0|^6.0" + }, + "require-dev": { + "ext-json": "*", + "ext-pdo": "*", + "phpunit/phpunit": "^9.5", + "sebastian/comparator": ">=1.2.3", + "cakephp/cakephp-codesniffer": "^4.0", + "symfony/yaml": "^3.4|^4.0|^5.0" + }, + "autoload": { + "psr-4": { + "Phinx\\": "src/Phinx/" + } + }, + "autoload-dev": { + "psr-4": { + "Test\\Phinx\\": "tests/Phinx/" + } + }, + "suggest": { + "ext-json": "Install if using JSON configuration format", + "ext-pdo": "PDO extension is needed", + "symfony/yaml": "Install if using YAML configuration format" + }, + "scripts": { + "check": [ + "@test", + "@cs-check" + ], + "cs-check": "phpcs", + "cs-fix": "phpcbf", + "stan": "phpstan analyse", + "stan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:~1.9.0 && mv composer.backup composer.json", + "test": "phpunit --colors=always" + }, + "bin": [ + "bin/phinx" + ], + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + } +} diff --git a/vendor/robmorgan/phinx/data/phinx.json.dist b/vendor/robmorgan/phinx/data/phinx.json.dist new file mode 100644 index 0000000..1deeaf1 --- /dev/null +++ b/vendor/robmorgan/phinx/data/phinx.json.dist @@ -0,0 +1,38 @@ +{ + "paths": { + "migrations": "%%PHINX_CONFIG_DIR%%/db/migrations", + "seeds": "%%PHINX_CONFIG_DIR%%/db/seeds" + }, + "environments": { + "default_migration_table": "phinxlog", + "default_environment": "development", + "production": { + "adapter": "mysql", + "host": "localhost", + "name": "production_db", + "user": "root", + "pass": "", + "port": 3306, + "charset": "utf8" + }, + "development": { + "adapter": "mysql", + "host": "localhost", + "name": "development_db", + "user": "root", + "pass": "", + "port": 3306, + "charset": "utf8" + }, + "testing": { + "adapter": "mysql", + "host": "localhost", + "name": "testing_db", + "user": "root", + "pass": "", + "port": 3306, + "charset": "utf8" + } + }, + "version_order": "creation" +} diff --git a/vendor/robmorgan/phinx/data/phinx.php.dist b/vendor/robmorgan/phinx/data/phinx.php.dist new file mode 100644 index 0000000..7ff2b08 --- /dev/null +++ b/vendor/robmorgan/phinx/data/phinx.php.dist @@ -0,0 +1,41 @@ + [ + 'migrations' => '%%PHINX_CONFIG_DIR%%/db/migrations', + 'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds' + ], + 'environments' => [ + 'default_migration_table' => 'phinxlog', + 'default_environment' => 'development', + 'production' => [ + 'adapter' => 'mysql', + 'host' => 'localhost', + 'name' => 'production_db', + 'user' => 'root', + 'pass' => '', + 'port' => '3306', + 'charset' => 'utf8', + ], + 'development' => [ + 'adapter' => 'mysql', + 'host' => 'localhost', + 'name' => 'development_db', + 'user' => 'root', + 'pass' => '', + 'port' => '3306', + 'charset' => 'utf8', + ], + 'testing' => [ + 'adapter' => 'mysql', + 'host' => 'localhost', + 'name' => 'testing_db', + 'user' => 'root', + 'pass' => '', + 'port' => '3306', + 'charset' => 'utf8', + ] + ], + 'version_order' => 'creation' +]; diff --git a/vendor/robmorgan/phinx/data/phinx.yml.dist b/vendor/robmorgan/phinx/data/phinx.yml.dist new file mode 100644 index 0000000..857e535 --- /dev/null +++ b/vendor/robmorgan/phinx/data/phinx.yml.dist @@ -0,0 +1,35 @@ +paths: + migrations: '%%PHINX_CONFIG_DIR%%/db/migrations' + seeds: '%%PHINX_CONFIG_DIR%%/db/seeds' + +environments: + default_migration_table: phinxlog + default_environment: development + production: + adapter: mysql + host: localhost + name: production_db + user: root + pass: '' + port: 3306 + charset: utf8 + + development: + adapter: mysql + host: localhost + name: development_db + user: root + pass: '' + port: 3306 + charset: utf8 + + testing: + adapter: mysql + host: localhost + name: testing_db + user: root + pass: '' + port: 3306 + charset: utf8 + +version_order: creation diff --git a/vendor/robmorgan/phinx/docs/config/__init__.py b/vendor/robmorgan/phinx/docs/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/robmorgan/phinx/docs/config/all.py b/vendor/robmorgan/phinx/docs/config/all.py new file mode 100644 index 0000000..caebcd1 --- /dev/null +++ b/vendor/robmorgan/phinx/docs/config/all.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# Phinx documentation build configuration file, created by +# sphinx-quickstart on Thu Jun 14 17:39:42 2012. +# + +# Import the base theme configuration +from cakephpsphinx.config.all import * + +# The full version, including alpha/beta/rc tags. +release = '0.13.x' + +# The search index version. +search_version = 'phinx-0' + +# The marketing display name for the book. +version_name = '' + +# Project name shown in the black header bar +project = 'Phinx' + +# Other versions that display in the version picker menu. +version_list = [ + {'name': '0.13', 'number': '/phinx/0', 'title': '0.13', 'current': True}, +] + +# Languages available. +languages = ['en'] + +# The GitHub branch name for this version of the docs +# for edit links to point at. +branch = '0.x' + +# Current version being built +version = '0.13' + +show_root_link = True + +repository = 'cakephp/phinx' + +source_path = 'docs/' + +hide_page_contents = ('search', '404', 'contents') diff --git a/vendor/robmorgan/phinx/docs/en/commands.rst b/vendor/robmorgan/phinx/docs/en/commands.rst new file mode 100644 index 0000000..e93d7a7 --- /dev/null +++ b/vendor/robmorgan/phinx/docs/en/commands.rst @@ -0,0 +1,411 @@ +.. index:: + single: Commands + +Commands +======== + +Phinx is run using a number of commands. + +The Breakpoint Command +---------------------- + +The Breakpoint command is used to set breakpoints, allowing you to limit +rollbacks. You can toggle the breakpoint of the most recent migration by +not supplying any parameters. + +.. code-block:: bash + + $ phinx breakpoint -e development + +To toggle a breakpoint on a specific version then use the ``--target`` +parameter or ``-t`` for short. + +.. code-block:: bash + + $ phinx breakpoint -e development -t 20120103083322 + +You can remove all the breakpoints by using the ``--remove-all`` parameter +or ``-r`` for short. + +.. code-block:: bash + + $ phinx breakpoint -e development -r + +You can set or unset (rather than just toggle) the breakpoint on the most +recent migration (or on a specific migration when combined with the +``--target`` or ``-t`` parameter) by using ``-set`` or ``--unset``. + +Breakpoints are visible when you run the ``status`` command. + +The Create Command +------------------ + +The Create command is used to create a new migration file. It requires one +argument: the name of the migration. The migration name should be specified in +CamelCase format. + +.. code-block:: bash + + $ phinx create MyNewMigration + +Open the new migration file in your text editor to add your database +transformations. Phinx creates migration files using the path specified in your +phinx configuration file. Please see the :doc:`Configuration ` chapter +for more information. + +You are able to override the template file used by Phinx by supplying an +alternative template filename. + +.. code-block:: bash + + $ phinx create MyNewMigration --template="" + +You can also supply a template generating class. This class must implement the +interface ``Phinx\Migration\CreationInterface``. + +.. code-block:: bash + + $ phinx create MyNewMigration --class="" + +In addition to providing the template for the migration, the class can also define +a callback that will be called once the migration file has been generated from the +template. + +You cannot use ``--template`` and ``--class`` together. + +The Init Command +---------------- + +The Init command (short for initialize) is used to prepare your project for +Phinx. This command generates the phinx configuration file in the root of your +project directory. By default, this file will be named ``phinx.php``. + +.. code-block:: bash + + $ phinx init + +Optionally you can specify a custom location for Phinx's config file: + +.. code-block:: bash + + $ phinx init ./custom/location/ + +You can also specify a custom file name: + +.. code-block:: bash + + $ phinx init custom-config.yml + +As well as a different format from php, yml, and json. For example, to create yml file: + +.. code-block:: bash + + $ phinx init --format yml + +Open this file in your text editor to setup your project configuration. Please +see the :doc:`Configuration ` chapter for more information. + +The Migrate Command +------------------- + +The Migrate command runs all of the available migrations, optionally up to a +specific version. + +.. code-block:: bash + + $ phinx migrate -e development + +To migrate to a specific version then use the ``--target`` parameter or ``-t`` +for short. + +.. code-block:: bash + + $ phinx migrate -e development -t 20110103081132 + +Use ``--dry-run`` to print the queries to standard output without executing them + +.. code-block:: bash + + $ phinx migrate --dry-run + +The Rollback Command +-------------------- + +The Rollback command is used to undo previous migrations executed by Phinx. It +is the opposite of the Migrate command. + +You can rollback to the previous migration by using the ``rollback`` command +with no arguments. + +.. code-block:: bash + + $ phinx rollback -e development + +To rollback all migrations to a specific version then use the ``--target`` +parameter or ``-t`` for short. + +.. code-block:: bash + + $ phinx rollback -e development -t 20120103083322 + +Specifying 0 as the target version will revert all migrations. + +.. code-block:: bash + + $ phinx rollback -e development -t 0 + +To rollback all migrations to a specific date then use the ``--date`` +parameter or ``-d`` for short. + +.. code-block:: bash + + $ phinx rollback -e development -d 2012 + $ phinx rollback -e development -d 201201 + $ phinx rollback -e development -d 20120103 + $ phinx rollback -e development -d 2012010312 + $ phinx rollback -e development -d 201201031205 + $ phinx rollback -e development -d 20120103120530 + +If a breakpoint is set, blocking further rollbacks, you can override the +breakpoint using the ``--force`` parameter or ``-f`` for short. + +.. code-block:: bash + + $ phinx rollback -e development -t 0 -f + +Use ``--dry-run`` to print the queries to standard output without executing them + +.. code-block:: bash + + $ phinx rollback --dry-run + +.. note:: + + When rolling back, Phinx orders the executed migrations using + the order specified in the ``version_order`` option of your + phinx configuration file. + Please see the :doc:`Configuration ` chapter for more information. + +The Status Command +------------------ + +The Status command prints a list of all migrations, along with their current +status. You can use this command to determine which migrations have been run. + +.. code-block:: bash + + $ phinx status -e development + +This command exits with code 0 if the database is up-to-date (ie. all migrations are up) or one of the following codes otherwise: + +* 2: There is at least one missing migration. +* 3: There is at least one down migration. + +An exit code of 1 means an application error has occurred. + +The Seed Create Command +----------------------- + +The Seed Create command can be used to create new database seed classes. It +requires one argument, the name of the class. The class name should be specified +in CamelCase format. + +.. code-block:: bash + + $ phinx seed:create MyNewSeeder + +Open the new seed file in your text editor to add your database seed commands. +Phinx creates seed files using the path specified in your configuration file. +Please see the :doc:`Configuration ` chapter for more information. + +You are able to override the template file used by Phinx by supplying an +alternative template filename. + +.. code-block:: bash + + $ phinx seed:create MyNewSeeder --template="" + +The Seed Run Command +-------------------- + +The Seed Run command runs all of the available seed classes or optionally just +one. + +.. code-block:: bash + + $ phinx seed:run -e development + +To run only one seed class use the ``--seed`` parameter or ``-s`` for short. + +.. code-block:: bash + + $ phinx seed:run -e development -s MyNewSeeder + +Configuration File Parameter +---------------------------- + +When running Phinx from the command line, you may specify a configuration file +using the ``--configuration`` or ``-c`` parameter. In addition to YAML, the +configuration file may be the computed output of a PHP file as a PHP array: + +.. code-block:: php + + [ + "migrations" => "application/migrations" + ], + "environments" => [ + "default_migration_table" => "phinxlog", + "default_environment" => "dev", + "dev" => [ + "adapter" => "mysql", + "host" => $_ENV['DB_HOST'], + "name" => $_ENV['DB_NAME'], + "user" => $_ENV['DB_USER'], + "pass" => $_ENV['DB_PASS'], + "port" => $_ENV['DB_PORT'] + ] + ] + ]; + +Phinx auto-detects which language parser to use for files with ``*.yaml``, ``*.yml``, ``*.json``, and ``*.php`` extensions. +The appropriate parser may also be specified via the ``--parser`` and ``-p`` parameters. Anything other than ``"json"`` or +``"php"`` is treated as YAML. + +When using a PHP array, you can provide a ``connection`` key with an existing PDO instance. It is also important to pass +the database name too, as Phinx requires this for certain methods such as ``hasTable()``: + +.. code-block:: php + + [ + "migrations" => "application/migrations" + ), + "environments" => [ + "default_migration_table" => "phinxlog", + "default_environment" => "dev", + "dev" => [ + "name" => "dev_db", + "connection" => $pdo_instance + ] + ] + ]; + +Running Phinx in a Web App +-------------------------- + +Phinx can also be run inside of a web application by using the ``Phinx\Wrapper\TextWrapper`` +class. An example of this is provided in ``app/web.php``, which can be run as a +standalone server: + +.. code-block:: bash + + $ php -S localhost:8000 vendor/robmorgan/phinx/app/web.php + +This will create local web server at ``__ which will show current +migration status by default. To run migrations up, use ``__ +and to rollback use ``__. + +**The included web app is only an example and should not be used in production!** + +.. note:: + + To modify configuration variables at runtime and override ``%%PHINX_DBNAME%%`` + or other another dynamic option, set ``$_SERVER['PHINX_DBNAME']`` before + running commands. Available options are documented in the Configuration page. + +Wrapping Phinx in another Symfony Console Application +----------------------------------------------------- + +Phinx can be wrapped and run as part of a separate Symfony console application. This +may be desirable to present a unified interface to the user for all aspects of your +application, or because you wish to run multiple Phinx commands. While you could +run the commands through ``exec`` or use the above ``Phinx\Wrapper\TextWrapper``, +though this makes it hard to deal with the return code and output in a similar fashion +as your application. + +Luckily, Symfony makes doing this sort of "meta" command straight-forward: + +.. code-block:: php + + use Symfony\Component\Console\Input\ArrayInput; + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Output\OutputInterface; + use Phinx\Console\PhinxApplication; + + // ... + + protected function execute(InputInterface $input, OutputInterface $output) + { + + $phinx = new PhinxApplication(); + $command = $phinx->find('migrate'); + + $arguments = [ + 'command' => 'migrate', + '--environment' => 'production', + '--configuration' => '/path/to/phinx/config/file' + ]; + + $input = new ArrayInput($arguments); + $returnCode = $command->run(new ArrayInput($arguments), $output); + // ... + } + +Here, you are instantianting the ``PhinxApplication``, telling it to find the ``migrate`` +command, defining the arguments to pass to it (which match the commandline arguments and flags), +and then finally running the command, passing it the same ``OutputInterface`` that your +application uses. + +See this `Symfony page `_ for more information. + +Using Phinx with PHPUnit +-------------------------- + +Phinx can be used within your unit tests to prepare or seed the database. You can use it programatically : + +.. code-block:: php + + public function setUp () + { + $app = new PhinxApplication(); + $app->setAutoExit(false); + $app->run(new StringInput('migrate'), new NullOutput()); + } + +If you use a memory database, you'll need to give Phinx a specific PDO instance. You can interact with Phinx directly +using the Manager class : + +.. code-block:: php + + use PDO; + use Phinx\Config\Config; + use Phinx\Migration\Manager; + use PHPUnit\Framework\TestCase; + use Symfony\Component\Console\Input\StringInput; + use Symfony\Component\Console\Output\NullOutput; + + class DatabaseTestCase extends TestCase { + + public function setUp () + { + $pdo = new PDO('sqlite::memory:', null, null, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + ]); + $configArray = require('phinx.php'); + $configArray['environments']['test'] = [ + 'adapter' => 'sqlite', + 'connection' => $pdo + ]; + $config = new Config($configArray); + $manager = new Manager($config, new StringInput(' '), new NullOutput()); + $manager->migrate('test'); + $manager->seed('test'); + // You can change default fetch mode after the seeding + $this->pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); + $this->pdo = $pdo; + } + + } diff --git a/vendor/robmorgan/phinx/docs/en/conf.py b/vendor/robmorgan/phinx/docs/en/conf.py new file mode 100644 index 0000000..f638bda --- /dev/null +++ b/vendor/robmorgan/phinx/docs/en/conf.py @@ -0,0 +1,9 @@ +import sys, os + +# Append the top level directory of the docs, so we can import from the config dir. +sys.path.insert(0, os.path.abspath('..')) + +# Pull in all the configuration options defined in the global config file.. +from config.all import * + +language = 'en' diff --git a/vendor/robmorgan/phinx/docs/en/configuration.rst b/vendor/robmorgan/phinx/docs/en/configuration.rst new file mode 100644 index 0000000..05ff795 --- /dev/null +++ b/vendor/robmorgan/phinx/docs/en/configuration.rst @@ -0,0 +1,534 @@ +.. index:: + single: Configuration + +Configuration +============= + +When you initialize your project using the :doc:`Init Command`, Phinx +creates a default file in the root of your project directory. By default, this +file uses the YAML data serialization format, but you can use the ``--format`` +command line option to specify either ``yaml``, ``yml``, ``json``, or ``php``. + +If a ``--configuration`` command line option is given, Phinx will load the +specified file. Otherwise, it will attempt to find ``phinx.php``, ``phinx.json``, +``phinx.yml``, or ``phinx.yaml`` and load the first file found. See the +:doc:`Commands ` chapter for more information. + +.. warning:: + + Remember to store the configuration file outside of a publicly accessible + directory on your webserver. This file contains your database credentials + and may be accidentally served as plain text. + +Note that while JSON and YAML files are *parsed*, the PHP file is *included*. +This means that: + +* It must `return` an array of configuration items. +* The variable scope is local, i.e. you would need to explicitly declare + any global variables your initialization file reads or modifies. +* Its standard output is suppressed. +* Unlike with JSON and YAML, it is possible to omit environment connection details + and instead specify ``connection`` which must contain an initialized PDO instance. + This is useful when you want your migrations to interact with your application + and/or share the same connection. However remember to also pass the database name + as Phinx cannot infer this from the PDO connection. + +.. code-block:: php + + $app = require 'app/phinx.php'; + $pdo = $app->getDatabase()->getPdo(); + + return [ + 'environments' => [ + 'default_environment' => 'development', + 'development' => [ + 'name' => 'devdb', + 'connection' => $pdo + ] + ] + ]; + +Migration Paths +--------------- + +The first option specifies the path to your migration directory. Phinx uses +``%%PHINX_CONFIG_DIR%%/db/migrations`` by default. + +.. note:: + + ``%%PHINX_CONFIG_DIR%%`` is a special token and is automatically replaced + with the root directory where your phinx configuration file is stored. + +In order to overwrite the default ``%%PHINX_CONFIG_DIR%%/db/migrations``, you +need to add the following to the configuration. + +.. code-block:: yaml + + paths: + migrations: /your/full/path + +You can also provide multiple migration paths by using an array in your configuration: + +.. code-block:: yaml + + paths: + migrations: + - application/module1/migrations + - application/module2/migrations + + +You can also use the ``%%PHINX_CONFIG_DIR%%`` token in your path. + +.. code-block:: yaml + + paths: + migrations: '%%PHINX_CONFIG_DIR%%/your/relative/path' + +Migrations are captured with ``glob``, so you can define a pattern for multiple +directories. + +.. code-block:: yaml + + paths: + migrations: '%%PHINX_CONFIG_DIR%%/module/*/{data,scripts}/migrations' + +Custom Migration Base +--------------------- + +By default all migrations will extend from Phinx's `AbstractMigration` class. +This can be set to a custom class that extends from `AbstractMigration` by +setting ``migration_base_class`` in your config: + +.. code-block:: yaml + + migration_base_class: MyMagicalMigration + +Seed Paths +---------- + +The second option specifies the path to your seed directory. Phinx uses +``%%PHINX_CONFIG_DIR%%/db/seeds`` by default. + +.. note:: + + ``%%PHINX_CONFIG_DIR%%`` is a special token and is automatically replaced + with the root directory where your configuration file is stored. + +In order to overwrite the default ``%%PHINX_CONFIG_DIR%%/db/seeds``, you +need to add the following to the yaml configuration. + +.. code-block:: yaml + + paths: + seeds: /your/full/path + +You can also provide multiple seed paths by using an array in your configuration: + +.. code-block:: yaml + + paths: + seeds: + - /your/full/path1 + - /your/full/path2 + + +You can also use the ``%%PHINX_CONFIG_DIR%%`` token in your path. + +.. code-block:: yaml + + paths: + seeds: '%%PHINX_CONFIG_DIR%%/your/relative/path' + +Custom Seeder Base +--------------------- + +By default all seeders will extend from Phinx's `AbstractSeed` class. +This can be set to a custom class that extends from `AbstractSeed` by +setting ``seed_base_class`` in your config: + +.. code-block:: yaml + + seed_base_class: MyMagicalSeeder + +Custom Migration Template +------------------------- + +Custom template for Migrations could be used either by defining template file path +in configuration file: + +.. code-block:: yaml + + templates: + file: src/templates/customMigrationTemplate.php + + +Custom Seeder Template +---------------------- + +Custom Seeder template could be used either by defining template file path +in configuration file: + +.. code-block:: yaml + + templates: + seedFile: src/templates/customSeederTemplate.php + + +Environments +------------ + +One of the key features of Phinx is support for multiple database environments. +You can use Phinx to create migrations on your development environment, then +run the same migrations on your production environment. Environments are +specified under the ``environments`` nested collection. For example: + +.. code-block:: yaml + + environments: + default_migration_table: phinxlog + default_environment: development + production: + adapter: mysql + host: localhost + name: production_db + user: root + pass: '' + port: 3306 + charset: utf8mb4 + collation: utf8mb4_unicode_ci + +would define a new environment called ``production``. + +In a situation when multiple developers work on the same project and each has +a different environment (e.g. a convention such as ``--``), or when you need to have separate +environments for separate purposes (branches, testing, etc) use environment +variable `PHINX_ENVIRONMENT` to override the default environment in the yaml +file: + +.. code-block:: bash + + export PHINX_ENVIRONMENT=dev-`whoami`-`hostname` + +Migration Table +--------------- + +To keep track of the migration statuses for an environment, phinx creates +a table to store this information. You can customize where this table +is created by configuring ``default_migration_table`` to be used as default +for all environments: + +.. code-block:: yaml + + environment: + default_migration_table: phinxlog + +If this field is omitted, then it will default to ``phinxlog``. For +databases that support it, e.g. Postgres, the schema name can be prefixed +with a period separator (``.``). For example, ``phinx.log`` will create +the table ``log`` in the ``phinx`` schema instead of ``phinxlog`` in the +``public`` (default) schema. + +You may also specify the ``migration_table`` on a per environment basis. +Any environment that does not have a ``migration_table`` specified will +fallback to using the ``default_migration_table`` that is defined at the +top level. An example of how you might use this is as follows: + +.. code-block:: yaml + + environment: + default_migration_table: phinxlog + development: + migration_table: phinxlog_dev + # rest of the development settings + production: + # rest of the production settings + +In the above example, ``development`` will look to the ``phinxlog_dev`` +table for migration statues while ``production`` will use ``phinxlog``. + +Table Prefix and Suffix +----------------------- + +You can define a table prefix and table suffix: + +.. code-block:: yaml + + environments: + development: + .... + table_prefix: dev_ + table_suffix: _v1 + testing: + .... + table_prefix: test_ + table_suffix: _v2 + + +Socket Connections +------------------ + +When using the MySQL adapter, it is also possible to use sockets instead of +network connections. The socket path is configured with ``unix_socket``: + +.. code-block:: yaml + + environments: + default_migration_table: phinxlog + default_environment: development + production: + adapter: mysql + name: production_db + user: root + pass: '' + unix_socket: /var/run/mysql/mysql.sock + charset: utf8 + +External Variables +------------------ + +Phinx will automatically grab any environment variable prefixed with ``PHINX_`` +and make it available as a token in the config file. The token will have +exactly the same name as the variable but you must access it by wrapping two +``%%`` symbols on either side. e.g: ``'%%PHINX_DBUSER%%'``. This is especially +useful if you wish to store your secret database credentials directly on the +server and not in a version control system. This feature can be easily +demonstrated by the following example: + +.. code-block:: yaml + + environments: + default_migration_table: phinxlog + default_environment: development + production: + adapter: mysql + host: '%%PHINX_DBHOST%%' + name: '%%PHINX_DBNAME%%' + user: '%%PHINX_DBUSER%%' + pass: '%%PHINX_DBPASS%%' + port: 3306 + charset: utf8 + +Data Source Names +----------------- + +Phinx supports the use of data source names (DSN) to specify the connection +options, which can be useful if you use a single environment variable to hold +the database credentials. PDO has a different DSN formats depending on the +underlying driver, so Phinx uses a database-agnostic DSN format used by other +projects (Doctrine, Rails, AMQP, PaaS, etc). + +.. code-block:: text + + ://[[:]@][:]/[?] + +* A DSN requires at least ``adapter``, ``host`` and ``name``. +* You cannot specify a password without a username. +* ``port`` must be a positive integer. +* ``additionalOptions`` takes the form of a query string, and will be passed to + the adapter in the options array. + +.. code-block:: yaml + + environments: + default_migration_table: phinxlog + default_environment: development + production: + # Example data source name + dsn: mysql://root@localhost:3306/mydb?charset=utf8 + +Once a DSN is parsed, it's values are merged with the already existing +connection options. Values in specified in a DSN will never override any value +specified directly as connection options. + +.. code-block:: yaml + + environments: + default_migration_table: phinxlog + default_environment: development + development: + dsn: '%%DATABASE_URL%%' + production: + dsn: '%%DATABASE_URL%%' + name: production_database + +If the supplied DSN is invalid, then it is completely ignored. + +Supported Adapters +------------------ + +Phinx currently supports the following database adapters natively: + +* `MySQL `_: specify the ``mysql`` adapter. +* `PostgreSQL `_: specify the ``pgsql`` adapter. +* `SQLite `_: specify the ``sqlite`` adapter. +* `SQL Server `_: specify the ``sqlsrv`` adapter. + +For each adapter, you may configure the behavior of the underlying PDO object by setting in your +config object the lowercase version of the constant name. This works for both PDO options +(e.g. ``\PDO::ATTR_CASE`` would be ``attr_case``) and adapter specific options (e.g. for MySQL +you may set ``\PDO::MYSQL_ATTR_IGNORE_SPACE`` as ``mysql_attr_ignore_space``). Please consult +the `PDO documentation `_ for the allowed attributes +and their values. + +For example, to set the above example options: + +.. code-block:: php + + $config = [ + "environments" => [ + "development" => [ + "adapter" => "mysql", + # other adapter settings + "attr_case" => \PDO::ATTR_CASE, + "mysql_attr_ignore_space" => 1, + ], + ], + ]; + +By default, the only attribute that Phinx sets is ``\PDO::ATTR_ERRMODE`` to ``PDO::ERRMODE_EXCEPTION``. It is +not recommended to override this. + +MySQL +````````````````` + +The MySQL adapter has an unfortunate limitation in that it certain actions causes an +`implicit commit `_ regardless of transaction +state. Notably this list includes ``CREATE TABLE``, ``ALTER TABLE``, and ``DROP TABLE``, which are the most +common operations that Phinx will run. This means that unlike other adapters which will attempt to gracefully +rollback a transaction on a failed migration, if a migration fails for MySQL, it may leave your DB in a partially +migrated state. + +SQLite +````````````````` + +Declaring an SQLite database uses a simplified structure: + +.. code-block:: yaml + + environments: + development: + adapter: sqlite + name: ./data/derby + suffix: ".db" # Defaults to ".sqlite3" + testing: + adapter: sqlite + memory: true # Setting memory to *any* value overrides name + +Starting with PHP 8.1 the SQlite adapter supports ``cache`` and ``mode`` +query parameters by using the `URI scheme `_ as long as ``open_basedir`` is unset. + +.. code-block:: yaml + + environments: + testing: + adapter: sqlite + name: my_app + mode: memory # Determines if the new database is opened read-only, read-write, read-write and created if it does not exist, or that the database is a pure in-memory database that never interacts with disk, respectively. + cache: shared # Determines if the new database is opened using shared cache mode or with a private cache. + +SQL Server +````````````````` + +When using the ``sqlsrv`` adapter and connecting to a named instance you should +omit the ``port`` setting as SQL Server will negotiate the port automatically. +Additionally, omit the ``charset: utf8`` or change to ``charset: 65001`` which +corresponds to UTF8 for SQL Server. + +Custom Adapters +````````````````` + +You can provide a custom adapter by registering an implementation of the `Phinx\\Db\\Adapter\\AdapterInterface` +with `AdapterFactory`: + +.. code-block:: php + + $name = 'fizz'; + $class = 'Acme\Adapter\FizzAdapter'; + + AdapterFactory::instance()->registerAdapter($name, $class); + +Adapters can be registered any time before `$app->run()` is called, which normally +called by `bin/phinx`. + +Templates +--------- + +You may override how phinx generates the template used with in a handful of ways: + +* file - path to an alternative file to use. +* class - class to use for the template, must implement the ``Phinx\Migration\CreationInterface`` interface. +* style - style to use for template, either ``change`` or ``up_down``, defaults to ``change`` if not set. + +You should only use one of these options. These can be overridden by passing command line options to the +:doc:`Create Command `. + +The aliased classes will still be required to implement the ``Phinx\Migration\CreationInterface`` interface. + +.. code-block:: yaml + + aliases: + permission: \Namespace\Migrations\PermissionMigrationTemplateGenerator + view: \Namespace\Migrations\ViewMigrationTemplateGenerator + +Version Order +------------- + +When rolling back or printing the status of migrations, Phinx orders the executed migrations according to the +``version_order`` option, which can have the following values: + +* ``creation`` (the default): migrations are ordered by their creation time, which is also part of their filename. +* ``execution``: migrations are ordered by their execution time, also known as start time. + +Bootstrap Path +--------------- + +You can provide a path to a `bootstrap` php file that will included before any commands phinx commands are run. Note that +setting External Variables to modify the config will not work because the config has already been parsed by this point. + +.. code-block:: yaml + + paths: + bootstrap: 'phinx-bootstrap.php' + +Within the bootstrap script, the following variables will be available: + +.. code-block:: php + + /** + * @var string $filename The file name as provided by the configuration + * @var string $filePath The absolute, real path to the file + * @var \Symfony\Component\Console\Input\InputInterface $input The executing command's input object + * @var \Symfony\Component\Console\Output\OutputInterface $output The executing command's output object + * @var \Phinx\Console\Command\AbstractCommand $context the executing command object + */ + +Feature Flags +------------- + +For some breaking changes, Phinx offers a way to opt-out of new behavior. The following flags are available: + +* ``unsigned_primary_keys``: Should Phinx create primary keys as unsigned integers? (default: ``true``) +* ``column_null_default``: Should Phinx create columns as null by default? (default: ``true``) + +.. code-block:: yaml + + feature_flags: + unsigned_primary_keys: false + +These values can also be set by modifying class fields on the ```Phinx\Config\FeatureFlags``` class, converting +the flag name to ``camelCase``, for example: + +.. code-block:: php + + Phinx\Config\FeatureFlags::$unsignedPrimaryKeys = false; diff --git a/vendor/robmorgan/phinx/docs/en/contents.rst b/vendor/robmorgan/phinx/docs/en/contents.rst new file mode 100644 index 0000000..900f6ed --- /dev/null +++ b/vendor/robmorgan/phinx/docs/en/contents.rst @@ -0,0 +1,14 @@ +Contents +######## + +.. toctree:: + :caption: Phinx + + intro + goals + install + migrations + seeding + commands + configuration + copyright diff --git a/vendor/robmorgan/phinx/docs/en/copyright.rst b/vendor/robmorgan/phinx/docs/en/copyright.rst new file mode 100644 index 0000000..71a6dfc --- /dev/null +++ b/vendor/robmorgan/phinx/docs/en/copyright.rst @@ -0,0 +1,29 @@ +.. index:: + single: Copyright + +Copyright +========= + +License + +(The MIT license) + +Copyright (c) 2012 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/robmorgan/phinx/docs/en/goals.rst b/vendor/robmorgan/phinx/docs/en/goals.rst new file mode 100644 index 0000000..aaea245 --- /dev/null +++ b/vendor/robmorgan/phinx/docs/en/goals.rst @@ -0,0 +1,13 @@ +.. index:: + single: Goals + +Goals +===== + +Phinx was developed with the following goals in mind: + +* Be portable amongst the most popular database vendors. +* Be PHP framework independent. +* Have a simple install process. +* Have an easy to use command-line operation. +* Integrate with various other PHP tools (Phing, PHPUnit) and web frameworks. diff --git a/vendor/robmorgan/phinx/docs/en/index.rst b/vendor/robmorgan/phinx/docs/en/index.rst new file mode 100644 index 0000000..c173230 --- /dev/null +++ b/vendor/robmorgan/phinx/docs/en/index.rst @@ -0,0 +1,33 @@ +:orphan: + +Phinx Documentation +=================== + +Phinx makes it ridiculously easy to manage the database migrations for your PHP +app. In less than 5 minutes, you can install Phinx using Composer and create +your first database migration. Phinx is just about migrations without all the +bloat of a database ORM system or application framework. + +Phinx requires a 64-bit version of PHP to function. + +Contents +======== + +.. toctree:: + :maxdepth: 2 + + intro + goals + install + migrations + seeding + commands + configuration + copyright + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/vendor/robmorgan/phinx/docs/en/install.rst b/vendor/robmorgan/phinx/docs/en/install.rst new file mode 100644 index 0000000..66e1f8f --- /dev/null +++ b/vendor/robmorgan/phinx/docs/en/install.rst @@ -0,0 +1,28 @@ +.. index:: + single: Installation + +Installation +============ + +Phinx should be installed using Composer, which is a tool for dependency +management in PHP. Please visit the `Composer `_ +website for more information. + +.. note:: + + Phinx requires at least PHP 7.2 (or later). + +To install Phinx, simply require it using Composer: + +.. code-block:: bash + + php composer.phar require robmorgan/phinx + +Create folders in your project following the structure ``db/migrations`` with adequate permissions. +It is where your migration files will live and should be writable. + +Phinx can now be executed from within your project: + +.. code-block:: bash + + vendor/bin/phinx init diff --git a/vendor/robmorgan/phinx/docs/en/intro.rst b/vendor/robmorgan/phinx/docs/en/intro.rst new file mode 100644 index 0000000..50c4444 --- /dev/null +++ b/vendor/robmorgan/phinx/docs/en/intro.rst @@ -0,0 +1,16 @@ +.. index:: + single: Introduction + +Introduction +============ + +Good developers always version their code using a SCM system, so why don't they +do the same for their database schema? + +Phinx allows developers to alter and manipulate databases in a clear and +concise way. It avoids the use of writing SQL by hand and instead offers a +powerful API for creating migrations using PHP code. Developers can then +version these migrations using their preferred SCM system. This makes Phinx +migrations portable between different database systems. Phinx keeps track of +which migrations have been run, so you can worry less about the state of your +database and instead focus on building better software. \ No newline at end of file diff --git a/vendor/robmorgan/phinx/docs/en/migrations.rst b/vendor/robmorgan/phinx/docs/en/migrations.rst new file mode 100644 index 0000000..3710d3f --- /dev/null +++ b/vendor/robmorgan/phinx/docs/en/migrations.rst @@ -0,0 +1,1934 @@ +.. index:: + single: Writing Migrations + +Writing Migrations +================== + +Phinx relies on migrations in order to transform your database. Each migration +is represented by a PHP class in a unique file. It is preferred that you write +your migrations using the Phinx PHP API, but raw SQL is also supported. + +Creating a New Migration +------------------------ +Generating a skeleton migration file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Let's start by creating a new Phinx migration. Run Phinx using the ``create`` +command: + +.. code-block:: bash + + $ vendor/bin/phinx create MyNewMigration + +This will create a new migration in the format +``YYYYMMDDHHMMSS_my_new_migration.php``, where the first 14 characters are +replaced with the current timestamp down to the second. + +If you have specified multiple migration paths, you will be asked to select +which path to create the new migration in. + +Phinx automatically creates a skeleton migration file with a single method: + +.. code-block:: php + + table('user_logins'); + $table->addColumn('user_id', 'integer') + ->addColumn('created', 'datetime') + ->create(); + } + } + +When executing this migration, Phinx will create the ``user_logins`` table on +the way up and automatically figure out how to drop the table on the way down. +Please be aware that when a ``change`` method exists, Phinx will automatically +ignore the ``up`` and ``down`` methods. If you need to use these methods it is +recommended to create a separate migration file. + +.. note:: + + When creating or updating tables inside a ``change()`` method you must use + the Table ``create()`` and ``update()`` methods. Phinx cannot automatically + determine whether a ``save()`` call is creating a new table or modifying an + existing one. + +The following actions are reversible when done through the Table API in Phinx, +and will be automatically reversed: + +- Creating a table +- Renaming a table +- Adding a column +- Renaming a column +- Adding an index +- Adding a foreign key + +If a command cannot be reversed then Phinx will throw an +``IrreversibleMigrationException`` when it's migrating down. If you wish to +use a command that cannot be reversed in the change function, you can use an +if statement with ``$this->isMigratingUp()`` to only run things in the +up or down direction. For example: + +.. code-block:: php + + table('user_logins'); + $table->addColumn('user_id', 'integer') + ->addColumn('created', 'datetime') + ->create(); + if ($this->isMigratingUp()) { + $table->insert([['user_id' => 1, 'created' => '2020-01-19 03:14:07']]) + ->save(); + } + } + } + +The Up Method +~~~~~~~~~~~~~ + +The up method is automatically run by Phinx when you are migrating up and it +detects the given migration hasn't been executed previously. You should use the +up method to transform the database with your intended changes. + +The Down Method +~~~~~~~~~~~~~~~ + +The down method is automatically run by Phinx when you are migrating down and +it detects the given migration has been executed in the past. You should use +the down method to reverse/undo the transformations described in the up method. + +The Init Method +~~~~~~~~~~~~~~~ + +The ``init()`` method is run by Phinx before the migration methods if it exists. +This can be used for setting common class properties that are then used within +the migration methods. + +The Should Execute Method +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``shouldExecute()`` method is run by Phinx before executing the migration. +This can be used to prevent the migration from being executed at this time. It always +returns true by default. You can override it in your custom ``AbstractMigration`` +implementation. + +Executing Queries +----------------- + +Queries can be executed with the ``execute()`` and ``query()`` methods. The +``execute()`` method returns the number of affected rows whereas the +``query()`` method returns the result as a +`PDOStatement `_. Both methods +accept an optional second parameter ``$params`` which is an array of elements, +and if used will cause the underlying connection to use a prepared statement. + +.. code-block:: php + + execute('DELETE FROM users'); // returns the number of affected rows + + // query() + $stmt = $this->query('SELECT * FROM users'); // returns PDOStatement + $rows = $stmt->fetchAll(); // returns the result as an array + + // using prepared queries + $count = $this->execute('DELETE FROM users WHERE id = ?', [5]); + $stmt = $this->query('SELECT * FROM users WHERE id > ?', [5]); // returns PDOStatement + $rows = $stmt->fetchAll(); + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +.. note:: + + These commands run using the PHP Data Objects (PDO) extension which + defines a lightweight, consistent interface for accessing databases + in PHP. Always make sure your queries abide with PDOs before using + the ``execute()`` command. This is especially important when using + DELIMITERs during insertion of stored procedures or triggers which + don't support DELIMITERs. + +.. note:: + + If you wish to execute multiple queries at once, you may not also use the prepared + variant of these functions. When using prepared queries, PDO can only execute + them one at a time. + +.. warning:: + + When using ``execute()`` or ``query()`` with a batch of queries, PDO doesn't + throw an exception if there is an issue with one or more of the queries + in the batch. + + As such, the entire batch is assumed to have passed without issue. + + If Phinx was to iterate any potential result sets, looking to see if one + had an error, then Phinx would be denying access to all the results as there + is no facility in PDO to get a previous result set + `nextRowset() `_ - + but no ``previousSet()``). + + So, as a consequence, due to the design decision in PDO to not throw + an exception for batched queries, Phinx is unable to provide the fullest + support for error handling when batches of queries are supplied. + + Fortunately though, all the features of PDO are available, so multiple batches + can be controlled within the migration by calling upon + `nextRowset() `_ + and examining `errorInfo `_. + +Fetching Rows +------------- + +There are two methods available to fetch rows. The ``fetchRow()`` method will +fetch a single row, whilst the ``fetchAll()`` method will return multiple rows. +Both methods accept raw SQL as their only parameter. + +.. code-block:: php + + fetchRow('SELECT * FROM users'); + + // fetch an array of messages + $rows = $this->fetchAll('SELECT * FROM messages'); + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +Inserting Data +-------------- + +Phinx makes it easy to insert data into your tables. Whilst this feature is +intended for the :doc:`seed feature `, you are also free to use the +insert methods in your migrations. + +.. code-block:: php + + table('status'); + + // inserting only one row + $singleRow = [ + 'id' => 1, + 'name' => 'In Progress' + ]; + + $table->insert($singleRow)->saveData(); + + // inserting multiple rows + $rows = [ + [ + 'id' => 2, + 'name' => 'Stopped' + ], + [ + 'id' => 3, + 'name' => 'Queued' + ] + ]; + + $table->insert($rows)->saveData(); + } + + /** + * Migrate Down. + */ + public function down() + { + $this->execute('DELETE FROM status'); + } + } + +.. note:: + + You cannot use the insert methods inside a `change()` method. Please use the + `up()` and `down()` methods. + +Working With Tables +------------------- + +The Table Object +~~~~~~~~~~~~~~~~ + +The Table object is one of the most useful APIs provided by Phinx. It allows +you to easily manipulate database tables using PHP code. You can retrieve an +instance of the Table object by calling the ``table()`` method from within +your database migration. + +.. code-block:: php + + table('tableName'); + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +You can then manipulate this table using the methods provided by the Table +object. + +Saving Changes +~~~~~~~~~~~~~~ + +When working with the Table object, Phinx stores certain operations in a +pending changes cache. Once you have made the changes you want to the table, +you must save them. To perform this operation, Phinx provides three methods, +``create()``, ``update()``, and ``save()``. ``create()`` will first create +the table and then run the pending changes. ``update()`` will just run the +pending changes, and should be used when the table already exists. ``save()`` +is a helper function that checks first if the table exists and if it does not +will run ``create()``, else it will run ``update()``. + +As stated above, when using the ``change()`` migration method, you should always +use ``create()`` or ``update()``, and never ``save()`` as otherwise migrating +and rolling back may result in different states, due to ``save()`` calling +``create()`` when running migrate and then ``update()`` on rollback. When +using the ``up()``/``down()`` methods, it is safe to use either ``save()`` or +the more explicit methods. + +When in doubt with working with tables, it is always recommended to call +the appropriate function and commit any pending changes to the database. + +Creating a Table +~~~~~~~~~~~~~~~~ + +Creating a table is really easy using the Table object. Let's create a table to +store a collection of users. + +.. code-block:: php + + table('users'); + $users->addColumn('username', 'string', ['limit' => 20]) + ->addColumn('password', 'string', ['limit' => 40]) + ->addColumn('password_salt', 'string', ['limit' => 40]) + ->addColumn('email', 'string', ['limit' => 100]) + ->addColumn('first_name', 'string', ['limit' => 30]) + ->addColumn('last_name', 'string', ['limit' => 30]) + ->addColumn('created', 'datetime') + ->addColumn('updated', 'datetime', ['null' => true]) + ->addIndex(['username', 'email'], ['unique' => true]) + ->create(); + } + } + +Columns are added using the ``addColumn()`` method. We create a unique index +for both the username and email columns using the ``addIndex()`` method. +Finally calling ``create()`` commits the changes to the database. + +.. note:: + + Phinx automatically creates an auto-incrementing primary key column called ``id`` for every + table. + +The ``id`` option sets the name of the automatically created identity field, while the ``primary_key`` +option selects the field or fields used for primary key. ``id`` will always override the ``primary_key`` +option unless it's set to false. If you don't need a primary key set ``id`` to false without +specifying a ``primary_key``, and no primary key will be created. + +To specify an alternate primary key, you can specify the ``primary_key`` option +when accessing the Table object. Let's disable the automatic ``id`` column and +create a primary key using two columns instead: + +.. code-block:: php + + table('followers', ['id' => false, 'primary_key' => ['user_id', 'follower_id']]); + $table->addColumn('user_id', 'integer') + ->addColumn('follower_id', 'integer') + ->addColumn('created', 'datetime') + ->create(); + } + } + +Setting a single ``primary_key`` doesn't enable the ``AUTO_INCREMENT`` option. +To simply change the name of the primary key, we need to override the default ``id`` field name: + +.. code-block:: php + + table('followers', ['id' => 'user_id']); + $table->addColumn('follower_id', 'integer') + ->addColumn('created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP']) + ->create(); + } + } + +In addition, the MySQL adapter supports following options: + +========== =========== +Option Description +========== =========== +comment set a text comment on the table +row_format set the table row format +engine define table engine *(defaults to ``InnoDB``)* +collation define table collation *(defaults to ``utf8mb4_unicode_ci``)* +signed whether the primary key is ``signed`` *(defaults to ``false``)* +limit set the maximum length for the primary key +========== =========== + +By default, the primary key is ``unsigned``. +To simply set it to be signed just pass ``signed`` option with a ``true`` value: + +.. code-block:: php + + table('followers', ['signed' => false]); + $table->addColumn('follower_id', 'integer') + ->addColumn('created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP']) + ->create(); + } + } + + +The PostgreSQL adapter supports the following options: + +========= =========== +Option Description +========= =========== +comment set a text comment on the table +========= =========== + +To view available column types and options, see `Valid Column Types`_ for details. + +Determining Whether a Table Exists +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can determine whether or not a table exists by using the ``hasTable()`` +method. + +.. code-block:: php + + hasTable('users'); + if ($exists) { + // do something + } + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +Dropping a Table +~~~~~~~~~~~~~~~~ + +Tables can be dropped quite easily using the ``drop()`` method. It is a +good idea to recreate the table again in the ``down()`` method. + +Note that like other methods in the ``Table`` class, ``drop`` also needs ``save()`` +to be called at the end in order to be executed. This allows phinx to intelligently +plan migrations when more than one table is involved. + +.. code-block:: php + + table('users')->drop()->save(); + } + + /** + * Migrate Down. + */ + public function down() + { + $users = $this->table('users'); + $users->addColumn('username', 'string', ['limit' => 20]) + ->addColumn('password', 'string', ['limit' => 40]) + ->addColumn('password_salt', 'string', ['limit' => 40]) + ->addColumn('email', 'string', ['limit' => 100]) + ->addColumn('first_name', 'string', ['limit' => 30]) + ->addColumn('last_name', 'string', ['limit' => 30]) + ->addColumn('created', 'datetime') + ->addColumn('updated', 'datetime', ['null' => true]) + ->addIndex(['username', 'email'], ['unique' => true]) + ->save(); + } + } + +Renaming a Table +~~~~~~~~~~~~~~~~ + +To rename a table access an instance of the Table object then call the +``rename()`` method. + +.. code-block:: php + + table('users'); + $table + ->rename('legacy_users') + ->update(); + } + + /** + * Migrate Down. + */ + public function down() + { + $table = $this->table('legacy_users'); + $table + ->rename('users') + ->update(); + } + } + +Changing the Primary Key +~~~~~~~~~~~~~~~~~~~~~~~~ + +To change the primary key on an existing table, use the ``changePrimaryKey()`` method. +Pass in a column name or array of columns names to include in the primary key, or ``null`` to drop the primary key. +Note that the mentioned columns must be added to the table, they will not be added implicitly. + +.. code-block:: php + + table('users'); + $users + ->addColumn('username', 'string', ['limit' => 20, 'null' => false]) + ->addColumn('password', 'string', ['limit' => 40]) + ->save(); + + $users + ->addColumn('new_id', 'integer', ['null' => false]) + ->changePrimaryKey(['new_id', 'username']) + ->save(); + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +Changing the Table Comment +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To change the comment on an existing table, use the ``changeComment()`` method. +Pass in a string to set as the new table comment, or ``null`` to drop the existing comment. + +.. code-block:: php + + table('users'); + $users + ->addColumn('username', 'string', ['limit' => 20]) + ->addColumn('password', 'string', ['limit' => 40]) + ->save(); + + $users + ->changeComment('This is the table with users auth information, password should be encrypted') + ->save(); + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +Working With Columns +-------------------- + +.. _valid-column-types: + +Valid Column Types +~~~~~~~~~~~~~~~~~~ + +Column types are specified as strings and can be one of: + +- binary +- boolean +- char +- date +- datetime +- decimal +- float +- double +- smallinteger +- integer +- biginteger +- string +- text +- time +- timestamp +- uuid + +In addition, the MySQL adapter supports ``enum``, ``set``, ``blob``, ``tinyblob``, ``mediumblob``, ``longblob``, ``bit`` and ``json`` column types +(``json`` in MySQL 5.7 and above). When providing a limit value and using ``binary``, ``varbinary`` or ``blob`` and its subtypes, the retained column +type will be based on required length (see `Limit Option and MySQL`_ for details); + +In addition, the Postgres adapter supports ``interval``, ``json``, ``jsonb``, ``uuid``, ``cidr``, ``inet`` and ``macaddr`` column types +(PostgreSQL 9.3 and above). + +Valid Column Options +~~~~~~~~~~~~~~~~~~~~ + +The following are valid column options: + +For any column type: + +======= =========== +Option Description +======= =========== +limit set maximum length for strings, also hints column types in adapters (see note below) +length alias for ``limit`` +default set default value or action +null allow ``NULL`` values, defaults to false if `identity` option is set to true, else defaults to true +after specify the column that a new column should be placed after, or use ``\Phinx\Db\Adapter\MysqlAdapter::FIRST`` to place the column at the start of the table *(only applies to MySQL)* +comment set a text comment on the column +======= =========== + +For ``decimal`` columns: + +========= =========== +Option Description +========= =========== +precision combine with ``scale`` set to set decimal accuracy +scale combine with ``precision`` to set decimal accuracy +signed enable or disable the ``unsigned`` option *(only applies to MySQL)* +========= =========== + +For ``enum`` and ``set`` columns: + +========= =========== +Option Description +========= =========== +values Can be a comma separated list or an array of values +========= =========== + +For ``smallinteger``, ``integer`` and ``biginteger`` columns: + +======== =========== +Option Description +======== =========== +identity enable or disable automatic incrementing +signed enable or disable the ``unsigned`` option *(only applies to MySQL)* +======== =========== + +For Postgres, when using ``identity``, it will utilize the ``serial`` type appropriate for the integer size, so that +``smallinteger`` will give you ``smallserial``, ``integer`` gives ``serial``, and ``biginteger`` gives ``bigserial``. + +For ``timestamp`` columns: + +======== =========== +Option Description +======== =========== +default set default value (use with ``CURRENT_TIMESTAMP``) +update set an action to be triggered when the row is updated (use with ``CURRENT_TIMESTAMP``) *(only applies to MySQL)* +timezone enable or disable the ``with time zone`` option for ``time`` and ``timestamp`` columns *(only applies to Postgres)* +======== =========== + +You can add ``created_at`` and ``updated_at`` timestamps to a table using the ``addTimestamps()`` method. This method accepts +three arguments, where the first two allow setting alternative names for the columns while the third argument allows you to +enable the ``timezone`` option for the columns. The defaults for these arguments are ``created_at``, ``updated_at``, and ``false`` +respectively. For the first and second argument, if you provide ``null``, then the default name will be used, and if you provide +``false``, then that column will not be created. Please note that attempting to set both to ``false`` will throw a +``\RuntimeException``. Additionally, you can use the ``addTimestampsWithTimezone()`` method, which is an alias to +``addTimestamps()`` that will always set the third argument to ``true`` (see examples below). The ``created_at`` column will +have a default set to ``CURRENT_TIMESTAMP``. For MySQL only, ``update_at`` column will have update set to +``CURRENT_TIMESTAMP``. + +.. code-block:: php + + table('users')->addTimestamps()->create(); + // Use defaults (with timezones) + $table = $this->table('users')->addTimestampsWithTimezone()->create(); + + // Override the 'created_at' column name with 'recorded_at'. + $table = $this->table('books')->addTimestamps('recorded_at')->create(); + + // Override the 'updated_at' column name with 'amended_at', preserving timezones. + // The two lines below do the same, the second one is simply cleaner. + $table = $this->table('books')->addTimestamps(null, 'amended_at', true)->create(); + $table = $this->table('users')->addTimestampsWithTimezone(null, 'amended_at')->create(); + + // Only add the created_at column to the table + $table = $this->table('books')->addTimestamps(null, false); + // Only add the updated_at column to the table + $table = $this->table('users')->addTimestamps(false); + // Note, setting both false will throw a \RuntimeError + } + } + +For ``boolean`` columns: + +======== =========== +Option Description +======== =========== +signed enable or disable the ``unsigned`` option *(only applies to MySQL)* +======== =========== + +For ``string`` and ``text`` columns: + +========= =========== +Option Description +========= =========== +collation set collation that differs from table defaults *(only applies to MySQL)* +encoding set character set that differs from table defaults *(only applies to MySQL)* +========= =========== + +For foreign key definitions: + +========== =========== +Option Description +========== =========== +update set an action to be triggered when the row is updated +delete set an action to be triggered when the row is deleted +constraint set a name to be used by foreign key constraint +========== =========== + +You can pass one or more of these options to any column with the optional +third argument array. + +Limit Option and MySQL +~~~~~~~~~~~~~~~~~~~~~~ + +When using the MySQL adapter, there are a couple things to consider when working with limits: + +- When using a ``string`` primary key or index on MySQL 5.7 or below, or the MyISAM storage engine, and the default charset of ``utf8mb4_unicode_ci``, you must specify a limit less than or equal to 191, or use a different charset. +- Additional hinting of database column type can be made for ``integer``, ``text``, ``blob``, ``tinyblob``, ``mediumblob``, ``longblob`` columns. Using ``limit`` with one the following options will modify the column type accordingly: + +============ ============== +Limit Column Type +============ ============== +BLOB_TINY TINYBLOB +BLOB_REGULAR BLOB +BLOB_MEDIUM MEDIUMBLOB +BLOB_LONG LONGBLOB +TEXT_TINY TINYTEXT +TEXT_REGULAR TEXT +TEXT_MEDIUM MEDIUMTEXT +TEXT_LONG LONGTEXT +INT_TINY TINYINT +INT_SMALL SMALLINT +INT_MEDIUM MEDIUMINT +INT_REGULAR INT +INT_BIG BIGINT +============ ============== + +For ``binary`` or ``varbinary`` types, if limit is set greater than allowed 255 bytes, the type will be changed to the best matching blob type given the length. + +.. code-block:: php + + table('cart_items'); + $table->addColumn('user_id', 'integer') + ->addColumn('product_id', 'integer', ['limit' => MysqlAdapter::INT_BIG]) + ->addColumn('subtype_id', 'integer', ['limit' => MysqlAdapter::INT_SMALL]) + ->addColumn('quantity', 'integer', ['limit' => MysqlAdapter::INT_TINY]) + ->create(); + +Custom Column Types & Default Values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some DBMS systems provide additional column types and default values that are specific to them. +If you don't want to keep your migrations DBMS-agnostic you can use those custom types in your migrations +through the ``\Phinx\Util\Literal::from`` method, which takes a string as its only argument, and returns an +instance of ``\Phinx\Util\Literal``. When Phinx encounters this value as a column's type it knows not to +run any validation on it and to use it exactly as supplied without escaping. This also works for ``default`` +values. + +You can see an example below showing how to add a ``citext`` column as well as a column whose default value +is a function, in PostgreSQL. This method of preventing the built-in escaping is supported in all adapters. + +.. code-block:: php + + table('users') + ->addColumn('username', Literal::from('citext')) + ->addColumn('uniqid', 'uuid', [ + 'default' => Literal::from('uuid_generate_v4()') + ]) + ->addColumn('creation', 'timestamp', [ + 'timezone' => true, + 'default' => Literal::from('now()') + ]) + ->create(); + } + } + +User Defined Types (Custom Data Domain) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Building upon the base types and column options you can define your custom +user defined types. Custom user defined types are configured in the +``data_domain`` root config option. + +.. code-block:: yaml + + data_domain: + phone_number: + type: string + length: 20 + address_line: + type: string + length: 150 + +Each user defined type can hold any valid type and column option, they are just +used as "macros" and replaced at the time of migration. + +.. code-block:: php + + table('user_data'); + $table->addColumn('user_phone_number', 'phone_number') + ->addColumn('user_address_line_1', 'address_line') + ->addColumn('user_address_line_2', 'address_line', ['null' => true]) + ->create(); + +Specifying a data domain at the beginning of your project is crucial to have a +homogeneous data model. It avoids mistakes like having many ``contact_name`` +columns with different lengths, mismatched integer types (long vs. bigint, etc). + +.. note:: + + For ``integer``, ``text`` and ``blob`` columns you can use the special + constants from MySQL and Postgress adapter classes. + + You can even customize some internal types to add your own default options, + but some column options can't be overriden in the data model (some options + are fixed like ``limit`` for the ``uuid`` special data type). + +.. code-block:: yaml + + # Some examples of custom data types + data_domain: + file: + type: blob + limit: BLOB_LONG # For MySQL DB. Uses MysqlAdapter::BLOB_LONG + boolean: + type: boolean # Customization of the boolean to be unsigned + signed: false + image_type: + type: enum # Enums can use YAML lists or a comma separated string + values: + - gif + - jpg + - png + +Get a column list +~~~~~~~~~~~~~~~~~ + +To retrieve all table columns, simply create a `table` object and call `getColumns()` +method. This method will return an array of Column classes with basic info. Example below: + +.. code-block:: php + + table('users')->getColumns(); + ... + } + + /** + * Migrate Down. + */ + public function down() + { + ... + } + } + +Get a column by name +~~~~~~~~~~~~~~~~~~~~ + +To retrieve one table column, simply create a `table` object and call the `getColumn()` +method. This method will return a Column class with basic info or NULL when the column doesn't exist. Example below: + +.. code-block:: php + + table('users')->getColumn('email'); + ... + } + + /** + * Migrate Down. + */ + public function down() + { + ... + } + } + +Checking whether a column exists +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can check if a table already has a certain column by using the +``hasColumn()`` method. + +.. code-block:: php + + table('user'); + $column = $table->hasColumn('username'); + + if ($column) { + // do something + } + + } + } + +Renaming a Column +~~~~~~~~~~~~~~~~~ + +To rename a column, access an instance of the Table object then call the +``renameColumn()`` method. + +.. code-block:: php + + table('users'); + $table->renameColumn('bio', 'biography') + ->save(); + } + + /** + * Migrate Down. + */ + public function down() + { + $table = $this->table('users'); + $table->renameColumn('biography', 'bio') + ->save(); + } + } + +Adding a Column After Another Column +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When adding a column with the MySQL adapter, you can dictate its position using the ``after`` option, +where its value is the name of the column to position it after. + +.. code-block:: php + + table('users'); + $table->addColumn('city', 'string', ['after' => 'email']) + ->update(); + } + } + +This would create the new column ``city`` and position it after the ``email`` column. The +``\Phinx\Db\Adapter\MysqlAdapter::FIRST`` constant can be used to specify that the new column should be +created as the first column in that table. + +Dropping a Column +~~~~~~~~~~~~~~~~~ + +To drop a column, use the ``removeColumn()`` method. + +.. code-block:: php + + table('users'); + $table->removeColumn('short_name') + ->save(); + } + } + + +Specifying a Column Limit +~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can limit the maximum length of a column by using the ``limit`` option. + +.. code-block:: php + + table('tags'); + $table->addColumn('short_name', 'string', ['limit' => 30]) + ->update(); + } + } + +Changing Column Attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To change column type or options on an existing column, use the ``changeColumn()`` method. +See :ref:`valid-column-types` and `Valid Column Options`_ for allowed values. + +.. code-block:: php + + table('users'); + $users->changeColumn('email', 'string', ['limit' => 255]) + ->save(); + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +Working With Indexes +-------------------- + +To add an index to a table you can simply call the ``addIndex()`` method on the +table object. + +.. code-block:: php + + table('users'); + $table->addColumn('city', 'string') + ->addIndex(['city']) + ->save(); + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +By default Phinx instructs the database adapter to create a normal index. We +can pass an additional parameter ``unique`` to the ``addIndex()`` method to +specify a unique index. We can also explicitly specify a name for the index +using the ``name`` parameter, the index columns sort order can also be specified using +the ``order`` parameter. The order parameter takes an array of column names and sort order key/value pairs. + +.. code-block:: php + + table('users'); + $table->addColumn('email', 'string') + ->addColumn('username','string') + ->addIndex(['email', 'username'], [ + 'unique' => true, + 'name' => 'idx_users_email', + 'order' => ['email' => 'DESC', 'username' => 'ASC']] + ) + ->save(); + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +The MySQL adapter also supports ``fulltext`` indexes. If you are using a version before 5.6 you must +ensure the table uses the ``MyISAM`` engine. + +.. code-block:: php + + table('users', ['engine' => 'MyISAM']); + $table->addColumn('email', 'string') + ->addIndex('email', ['type' => 'fulltext']) + ->create(); + } + } + +In addition, MySQL adapter also supports setting the index length defined by limit option. +When you are using a multi-column index, you are able to define each column index length. +The single column index can define its index length with or without defining column name in limit option. + +.. code-block:: php + + table('users'); + $table->addColumn('email', 'string') + ->addColumn('username','string') + ->addColumn('user_guid', 'string', ['limit' => 36]) + ->addIndex(['email','username'], ['limit' => ['email' => 5, 'username' => 2]]) + ->addIndex('user_guid', ['limit' => 6]) + ->create(); + } + } + +The SQL Server and PostgreSQL adapters also supports ``include`` (non-key) columns on indexes. + +.. code-block:: php + + table('users'); + $table->addColumn('email', 'string') + ->addColumn('firstname','string') + ->addColumn('lastname','string') + ->addIndex(['email'], ['include' => ['firstname', 'lastname']]) + ->create(); + } + } + +In addition PostgreSQL adapters also supports Generalized Inverted Index ``gin`` indexes. + +.. code-block:: php + + table('users'); + $table->addColumn('address', 'string') + ->addIndex('address', ['type' => 'gin']) + ->create(); + } + } + +Removing indexes is as easy as calling the ``removeIndex()`` method. You must +call this method for each index. + +.. code-block:: php + + table('users'); + $table->removeIndex(['email']) + ->save(); + + // alternatively, you can delete an index by its name, ie: + $table->removeIndexByName('idx_users_email') + ->save(); + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + + +Working With Foreign Keys +------------------------- + +Phinx has support for creating foreign key constraints on your database tables. +Let's add a foreign key to an example table: + +.. code-block:: php + + table('tags'); + $table->addColumn('tag_name', 'string') + ->save(); + + $refTable = $this->table('tag_relationships'); + $refTable->addColumn('tag_id', 'integer', ['null' => true]) + ->addForeignKey('tag_id', 'tags', 'id', ['delete'=> 'SET_NULL', 'update'=> 'NO_ACTION']) + ->save(); + + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +"On delete" and "On update" actions are defined with a 'delete' and 'update' options array. Possibles values are 'SET_NULL', 'NO_ACTION', 'CASCADE' and 'RESTRICT'. If 'SET_NULL' is used then the column must be created as nullable with the option ``['null' => true]``. +Constraint name can be changed with the 'constraint' option. + +It is also possible to pass ``addForeignKey()`` an array of columns. +This allows us to establish a foreign key relationship to a table which uses a combined key. + +.. code-block:: php + + table('follower_events'); + $table->addColumn('user_id', 'integer') + ->addColumn('follower_id', 'integer') + ->addColumn('event_id', 'integer') + ->addForeignKey(['user_id', 'follower_id'], + 'followers', + ['user_id', 'follower_id'], + ['delete'=> 'NO_ACTION', 'update'=> 'NO_ACTION', 'constraint' => 'user_follower_id']) + ->save(); + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +We can add named foreign keys using the ``constraint`` parameter. This feature is supported as of Phinx version 0.6.5 + +.. code-block:: php + + table('your_table'); + $table->addForeignKey('foreign_id', 'reference_table', ['id'], + ['constraint' => 'your_foreign_key_name']); + ->save(); + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +We can also easily check if a foreign key exists: + +.. code-block:: php + + table('tag_relationships'); + $exists = $table->hasForeignKey('tag_id'); + if ($exists) { + // do something + } + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + +Finally, to delete a foreign key, use the ``dropForeignKey`` method. + +Note that like other methods in the ``Table`` class, ``dropForeignKey`` also needs ``save()`` +to be called at the end in order to be executed. This allows phinx to intelligently +plan migrations when more than one table is involved. + +.. code-block:: php + + table('tag_relationships'); + $table->dropForeignKey('tag_id')->save(); + } + + /** + * Migrate Down. + */ + public function down() + { + + } + } + + + +Using the Query Builder +----------------------- + +It is not uncommon to pair database structure changes with data changes. For example, you may want to +migrate the data in a couple columns from the users to a newly created table. For this type of scenarios, +Phinx provides access to a Query builder object, that you may use to execute complex ``SELECT``, ``UPDATE``, +``INSERT`` or ``DELETE`` statements. + +The Query builder is provided by the `cakephp/database `_ project, and should +be easy to work with as it resembles very closely plain SQL. Accesing the query builder is done by calling the +``getQueryBuilder()`` function: + + +.. code-block:: php + + getQueryBuilder(); + $statement = $builder->select('*')->from('users')->execute(); + var_dump($statement->fetchAll()); + } + } + +Selecting Fields +~~~~~~~~~~~~~~~~ + +Adding fields to the SELECT clause: + + +.. code-block:: php + + select(['id', 'title', 'body']); + + // Results in SELECT id AS pk, title AS aliased_title, body ... + $builder->select(['pk' => 'id', 'aliased_title' => 'title', 'body']); + + // Use a closure + $builder->select(function ($builder) { + return ['id', 'title', 'body']; + }); + + +Where Conditions +~~~~~~~~~~~~~~~~ + +Generating conditions: + +.. code-block:: php + + // WHERE id = 1 + $builder->where(['id' => 1]); + + // WHERE id > 1 + $builder->where(['id >' => 1]); + + +As you can see you can use any operator by placing it with a space after the field name. Adding multiple conditions is easy as well: + + +.. code-block:: php + + where(['id >' => 1])->andWhere(['title' => 'My Title']); + + // Equivalent to + $builder->where(['id >' => 1, 'title' => 'My title']); + + // WHERE id > 1 OR title = 'My title' + $builder->where(['OR' => ['id >' => 1, 'title' => 'My title']]); + + +For even more complex conditions you can use closures and expression objects: + +.. code-block:: php + + select('*') + ->from('articles') + ->where(function ($exp) { + return $exp + ->eq('author_id', 2) + ->eq('published', true) + ->notEq('spam', true) + ->gt('view_count', 10); + }); + + +Which results in: + +.. code-block:: sql + + SELECT * FROM articles + WHERE + author_id = 2 + AND published = 1 + AND spam != 1 + AND view_count > 10 + + +Combining expressions is also possible: + + +.. code-block:: php + + select('*') + ->from('articles') + ->where(function ($exp) { + $orConditions = $exp->or_(['author_id' => 2]) + ->eq('author_id', 5); + return $exp + ->not($orConditions) + ->lte('view_count', 10); + }); + +It generates: + +.. code-block:: sql + + SELECT * + FROM articles + WHERE + NOT (author_id = 2 OR author_id = 5) + AND view_count <= 10 + + +When using the expression objects you can use the following methods to create conditions: + +* ``eq()`` Creates an equality condition. +* ``notEq()`` Create an inequality condition +* ``like()`` Create a condition using the ``LIKE`` operator. +* ``notLike()`` Create a negated ``LIKE`` condition. +* ``in()`` Create a condition using ``IN``. +* ``notIn()`` Create a negated condition using ``IN``. +* ``gt()`` Create a ``>`` condition. +* ``gte()`` Create a ``>=`` condition. +* ``lt()`` Create a ``<`` condition. +* ``lte()`` Create a ``<=`` condition. +* ``isNull()`` Create an ``IS NULL`` condition. +* ``isNotNull()`` Create a negated ``IS NULL`` condition. + + +Aggregates and SQL Functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +.. code-block:: php + + select(['count' => $builder->func()->count('*')]); + +A number of commonly used functions can be created with the func() method: + +* ``sum()`` Calculate a sum. The arguments will be treated as literal values. +* ``avg()`` Calculate an average. The arguments will be treated as literal values. +* ``min()`` Calculate the min of a column. The arguments will be treated as literal values. +* ``max()`` Calculate the max of a column. The arguments will be treated as literal values. +* ``count()`` Calculate the count. The arguments will be treated as literal values. +* ``concat()`` Concatenate two values together. The arguments are treated as bound parameters unless marked as literal. +* ``coalesce()`` Coalesce values. The arguments are treated as bound parameters unless marked as literal. +* ``dateDiff()`` Get the difference between two dates/times. The arguments are treated as bound parameters unless marked as literal. +* ``now()`` Take either 'time' or 'date' as an argument allowing you to get either the current time, or current date. + +When providing arguments for SQL functions, there are two kinds of parameters you can use, +literal arguments and bound parameters. Literal parameters allow you to reference columns or +other SQL literals. Bound parameters can be used to safely add user data to SQL functions. For example: + + +.. code-block:: php + + func()->concat([ + 'title' => 'literal', + ' NEW' + ]); + $query->select(['title' => $concat]); + + +Getting Results out of a Query +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once you’ve made your query, you’ll want to retrieve rows from it. There are a few ways of doing this: + + +.. code-block:: php + + execute()->fetchAll('assoc'); + + +Creating an Insert Query +~~~~~~~~~~~~~~~~~~~~~~~~ + +Creating insert queries is also possible: + + +.. code-block:: php + + getQueryBuilder(); + $builder + ->insert(['first_name', 'last_name']) + ->into('users') + ->values(['first_name' => 'Steve', 'last_name' => 'Jobs']) + ->values(['first_name' => 'Jon', 'last_name' => 'Snow']) + ->execute(); + + +For increased performance, you can use another builder object as the values for an insert query: + +.. code-block:: php + + getQueryBuilder(); + $namesQuery + ->select(['fname', 'lname']) + ->from('users') + ->where(['is_active' => true]); + + $builder = $this->getQueryBuilder(); + $st = $builder + ->insert(['first_name', 'last_name']) + ->into('names') + ->values($namesQuery) + ->execute(); + + var_dump($st->lastInsertId('names', 'id')); + + +The above code will generate: + +.. code-block:: sql + + INSERT INTO names (first_name, last_name) + (SELECT fname, lname FROM USERS where is_active = 1) + + +Creating an update Query +~~~~~~~~~~~~~~~~~~~~~~~~ + +Creating update queries is similar to both inserting and selecting: + +.. code-block:: php + + getQueryBuilder(); + $builder + ->update('users') + ->set('fname', 'Snow') + ->where(['fname' => 'Jon']) + ->execute(); + + +Creating a Delete Query +~~~~~~~~~~~~~~~~~~~~~~~ + +Finally, delete queries: + +.. code-block:: php + + getQueryBuilder(); + $builder + ->delete('users') + ->where(['accepted_gdpr' => false]) + ->execute(); diff --git a/vendor/robmorgan/phinx/docs/en/namespaces.rst b/vendor/robmorgan/phinx/docs/en/namespaces.rst new file mode 100644 index 0000000..df00b09 --- /dev/null +++ b/vendor/robmorgan/phinx/docs/en/namespaces.rst @@ -0,0 +1,154 @@ +:orphan: + +.. index:: + single: Supporting namespaces + +PSR-4 compliance +================== + +Phinx allows the use of namespaces in Migrations and Seeders. +Migrations require a timestamp in the filename, and therefore won't be fully PSR-4 compliant. Seeders do not need a timestamp and will be fully PSR-4 compliant. + +Using namespaces +------------------------ +1) locate your Phinx config file, the config file may be in one of following three formats: PHP, YAML or JSON. +2) Locate the "paths" key inside the config file, it should look something like one of the below examples. + - (NB. the "migrations" and "seeds" keys may be both an array or a string, so don't be alarmed if yours looks different) + +PHP: + +.. code-block:: php + + 'paths' => [ + 'migrations' => 'database/migrations', + 'seeds' => 'database/seeds', + ], + + +YAML: + +.. code-block:: yaml + + paths: + migrations: ./database/migrations + seeds: ./database/seeds + +JSON: + +.. code-block:: json + + { + "paths": { + "migrations": "database/migrations", + "seeds": "database/seeds" + } + } + +3) Enabling namespaces is a fairly simple task, we're going to turn the "migrations" and "seeds" keys into arrays. + - Any value without a key is a global-non-namespaced path + - Any keyed value will use the key as namespace + +.. code-block:: php + + 'paths' => [ + 'migrations' => [ + '/path/to/migration/without/namespace', // Non-namespaced migrations + 'Foo' => '/path/to/migration/Foo', // Migrations in the Foo namespace + ], + 'seeds' => [ + '/path/to/seeds/without/namespace', // Non-namespaced seeders + 'Baz' => '/path/to/seeds/Baz', // Seeders in the Baz namespace + ] + ], + +PHP is a bit special in this case, as it allows keyless and keyed values in the same array. To make this configuration work in YAML and JSON, we have to key the non-namespaced path with "0". + +.. code-block:: json + + { + "paths": { + "migrations": { + "0": "./db/migrations", + "Foo\\Bar": "./src/FooBar/db/migrations" + } + } + } + +.. code-block:: yaml + + paths: + migrations: + 0: ./db/migrations + Foo\\Bar: ./src/FooBar/db/migrations + +Path resolving +^^^^^^^^^^^^^^ + +Let's take a closer look on how the paths are resolved, let's start with the non-namespaced path. + +"./" refers to the project-root, therefore "./db/migrations" would resolve to /db/migrations. +This is the directory where Phinx will look for migrations when migrating. +NB. these migrations must not have a namespace. + +.. image:: https://i.imgur.com/l84308Q.jpg + +This image shows the path for "./db/migrations" where "Phinx" is the project root. + +And the namespaced path would be resolved as shown below. + +"./src/FooBar/db/migrations" would resolve to /src/FooBar/db/migrations, which is where Phinx will look for migrations in the Foo\\Bar namespace. + +.. image:: https://i.imgur.com/2mg0V8V.jpg + +The file path would look like this, if the project-root was "Phinx" + +File examples +^^^^^^^^^^^^^ + +The non-namespaced file in /db/migrations may look like the following example. + +.. code-block:: php + + table('users'); + $table->addColumn('name', 'string')->create(); + } + } + +Whereas the namespaced file will be found in /src/FoorBar/db/migrations and can look like this: +(Notice the namespace is the same as defined in the paths config). + +.. code-block:: php + + table('users'); + $table->addColumn('name', 'string')->create(); + } + } + + +4) That's it, you're ready to go, to create a migration simply run: *$ phinx create CreateUsersTable [--path ./src/FoorBar/db/migrations]* + + - If multiple paths are configured, but none provided with the --path flag, you will be prompted for which path to use. + + +Did you run into an issue? +-------------------------- + +- Due to the way the migrations are created, it is impossible to generate a migration in the *global* namespace with a class-name that is the same as a migration in a user-defined namespace. diff --git a/vendor/robmorgan/phinx/docs/en/seeding.rst b/vendor/robmorgan/phinx/docs/en/seeding.rst new file mode 100644 index 0000000..47e465b --- /dev/null +++ b/vendor/robmorgan/phinx/docs/en/seeding.rst @@ -0,0 +1,234 @@ +.. index:: + single: Database Seeding + +Database Seeding +================ + +In version 0.5.0 Phinx introduced support for seeding your database with test +data. Seed classes are a great way to easily fill your database with data after +it's created. By default they are stored in the `seeds` directory; however, this +path can be changed in your configuration file. + +.. note:: + + Database seeding is entirely optional, and Phinx does not create a `seeds` + directory by default. + +Creating a New Seed Class +------------------------- + +Phinx includes a command to easily generate a new seed class: + +.. code-block:: bash + + $ php vendor/bin/phinx seed:create UserSeeder + +If you have specified multiple seed paths, you will be asked to select which +path to create the new seed class in. + +It is based on a skeleton template: + +.. code-block:: php + + 'foo', + 'created' => date('Y-m-d H:i:s'), + ],[ + 'body' => 'bar', + 'created' => date('Y-m-d H:i:s'), + ] + ]; + + $posts = $this->table('posts'); + $posts->insert($data) + ->saveData(); + } + } + +.. note:: + + You must call the `saveData()` method to commit your data to the table. Phinx + will buffer data until you do so. + +Truncating Tables +----------------- + +In addition to inserting data Phinx makes it trivial to empty your tables using the +SQL `TRUNCATE` command: + +.. code-block:: php + + 'foo', + 'created' => date('Y-m-d H:i:s'), + ], + [ + 'body' => 'bar', + 'created' => date('Y-m-d H:i:s'), + ] + ]; + + $posts = $this->table('posts'); + $posts->insert($data) + ->saveData(); + + // empty the table + $posts->truncate(); + } + } + +.. note:: + + SQLite doesn't natively support the `TRUNCATE` command so behind the scenes + `DELETE FROM` is used. It is recommended to call the `VACUUM` command + after truncating a table. Phinx does not do this automatically. + +Executing Seed Classes +---------------------- + +This is the easy part. To seed your database, simply use the `seed:run` command: + +.. code-block:: bash + + $ php vendor/bin/phinx seed:run + +By default, Phinx will execute all available seed classes. If you would like to +run a specific class, simply pass in the name of it using the `-s` parameter: + +.. code-block:: bash + + $ php vendor/bin/phinx seed:run -s UserSeeder + +You can also run multiple seeders: + +.. code-block:: bash + + $ php vendor/bin/phinx seed:run -s UserSeeder -s PermissionSeeder -s LogSeeder + +You can also use the `-v` parameter for more output verbosity: + +.. code-block:: bash + + $ php vendor/bin/phinx seed:run -v + +The Phinx seed functionality provides a simple mechanism to easily and repeatably +insert test data into your database. diff --git a/vendor/robmorgan/phinx/phpstan-baseline.neon b/vendor/robmorgan/phinx/phpstan-baseline.neon new file mode 100644 index 0000000..03587fb --- /dev/null +++ b/vendor/robmorgan/phinx/phpstan-baseline.neon @@ -0,0 +1,27 @@ +parameters: + ignoreErrors: + - + message: "#^Variable \\$tval on left side of \\?\\? always exists and is not nullable\\.$#" + count: 1 + path: src/Phinx/Config/Config.php + + - + message: "#^Expression on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/Phinx/Db/Adapter/MysqlAdapter.php + + - + message: "#^Ternary operator condition is always true\\.$#" + count: 2 + path: src/Phinx/Db/Adapter/SqlServerAdapter.php + + - + message: "#^Property Phinx\\\\Migration\\\\Manager\\\\Environment\\:\\:\\$adapter \\(Phinx\\\\Db\\\\Adapter\\\\AdapterInterface\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/Phinx/Migration/Manager/Environment.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: src/Phinx/Migration/Manager/Environment.php + diff --git a/vendor/robmorgan/phinx/src/Phinx/Config/Config.php b/vendor/robmorgan/phinx/src/Phinx/Config/Config.php new file mode 100644 index 0000000..91cfbd4 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Config/Config.php @@ -0,0 +1,553 @@ +configFilePath = $configFilePath; + $this->values = $this->replaceTokens($configArray); + + if (isset($this->values['feature_flags'])) { + FeatureFlags::setFlagsFromConfig($this->values['feature_flags']); + } + } + + /** + * Create a new instance of the config class using a Yaml file path. + * + * @param string $configFilePath Path to the Yaml File + * @throws \RuntimeException + * @return \Phinx\Config\ConfigInterface + */ + public static function fromYaml(string $configFilePath): ConfigInterface + { + if (!class_exists('Symfony\\Component\\Yaml\\Yaml', true)) { + // @codeCoverageIgnoreStart + throw new RuntimeException('Missing yaml parser, symfony/yaml package is not installed.'); + // @codeCoverageIgnoreEnd + } + + $configFile = file_get_contents($configFilePath); + $configArray = Yaml::parse($configFile); + + if (!is_array($configArray)) { + throw new RuntimeException(sprintf( + 'File \'%s\' must be valid YAML', + $configFilePath + )); + } + + return new static($configArray, $configFilePath); + } + + /** + * Create a new instance of the config class using a JSON file path. + * + * @param string $configFilePath Path to the JSON File + * @throws \RuntimeException + * @return \Phinx\Config\ConfigInterface + */ + public static function fromJson(string $configFilePath): ConfigInterface + { + if (!function_exists('json_decode')) { + // @codeCoverageIgnoreStart + throw new RuntimeException('Need to install JSON PHP extension to use JSON config'); + // @codeCoverageIgnoreEnd + } + + $configArray = json_decode(file_get_contents($configFilePath), true); + if (!is_array($configArray)) { + throw new RuntimeException(sprintf( + 'File \'%s\' must be valid JSON', + $configFilePath + )); + } + + return new static($configArray, $configFilePath); + } + + /** + * Create a new instance of the config class using a PHP file path. + * + * @param string $configFilePath Path to the PHP File + * @throws \RuntimeException + * @return \Phinx\Config\ConfigInterface + */ + public static function fromPhp(string $configFilePath): ConfigInterface + { + ob_start(); + /** @noinspection PhpIncludeInspection */ + $configArray = include $configFilePath; + + // Hide console output + ob_end_clean(); + + if (!is_array($configArray)) { + throw new RuntimeException(sprintf( + 'PHP file \'%s\' must return an array', + $configFilePath + )); + } + + return new static($configArray, $configFilePath); + } + + /** + * @inheritDoc + */ + public function getEnvironments(): ?array + { + if (isset($this->values['environments'])) { + $environments = []; + foreach ($this->values['environments'] as $key => $value) { + if (is_array($value)) { + $environments[$key] = $value; + } + } + + return $environments; + } + + return null; + } + + /** + * @inheritDoc + */ + public function getEnvironment(string $name): ?array + { + $environments = $this->getEnvironments(); + + if (isset($environments[$name])) { + if ( + isset($this->values['environments']['default_migration_table']) + && !isset($environments[$name]['migration_table']) + ) { + $environments[$name]['migration_table'] = + $this->values['environments']['default_migration_table']; + } + + if ( + isset($environments[$name]['adapter']) + && $environments[$name]['adapter'] === 'sqlite' + && !empty($environments[$name]['memory']) + ) { + $environments[$name]['name'] = SQLiteAdapter::MEMORY; + } + + return $this->parseAgnosticDsn($environments[$name]); + } + + return null; + } + + /** + * @inheritDoc + */ + public function hasEnvironment(string $name): bool + { + return $this->getEnvironment($name) !== null; + } + + /** + * @inheritDoc + */ + public function getDefaultEnvironment(): string + { + // The $PHINX_ENVIRONMENT variable overrides all other default settings + $env = getenv('PHINX_ENVIRONMENT'); + if (!empty($env)) { + if ($this->hasEnvironment($env)) { + return $env; + } + + throw new RuntimeException(sprintf( + 'The environment configuration (read from $PHINX_ENVIRONMENT) for \'%s\' is missing', + $env + )); + } + + // deprecated: to be removed 0.13 + if (isset($this->values['environments']['default_database'])) { + trigger_error('default_database in the config has been deprecated since 0.12, use default_environment instead.', E_USER_DEPRECATED); + $this->values['environments']['default_environment'] = $this->values['environments']['default_database']; + } + + // if the user has configured a default environment then use it, + // providing it actually exists! + if (isset($this->values['environments']['default_environment'])) { + if ($this->hasEnvironment($this->values['environments']['default_environment'])) { + return $this->values['environments']['default_environment']; + } + + throw new RuntimeException(sprintf( + 'The environment configuration for \'%s\' is missing', + $this->values['environments']['default_environment'] + )); + } + + // else default to the first available one + if (is_array($this->getEnvironments()) && count($this->getEnvironments()) > 0) { + $names = array_keys($this->getEnvironments()); + + return $names[0]; + } + + throw new RuntimeException('Could not find a default environment'); + } + + /** + * @inheritDoc + */ + public function getAlias($alias): ?string + { + return !empty($this->values['aliases'][$alias]) ? $this->values['aliases'][$alias] : null; + } + + /** + * @inheritDoc + */ + public function getAliases(): array + { + return !empty($this->values['aliases']) ? $this->values['aliases'] : []; + } + + /** + * @inheritDoc + */ + public function getConfigFilePath(): ?string + { + return $this->configFilePath; + } + + /** + * @inheritDoc + * @throws \UnexpectedValueException + */ + public function getMigrationPaths(): array + { + if (!isset($this->values['paths']['migrations'])) { + throw new UnexpectedValueException('Migrations path missing from config file'); + } + + if (is_string($this->values['paths']['migrations'])) { + $this->values['paths']['migrations'] = [$this->values['paths']['migrations']]; + } + + return $this->values['paths']['migrations']; + } + + /** + * @inheritDoc + * @throws \UnexpectedValueException + */ + public function getSeedPaths(): array + { + if (!isset($this->values['paths']['seeds'])) { + throw new UnexpectedValueException('Seeds path missing from config file'); + } + + if (is_string($this->values['paths']['seeds'])) { + $this->values['paths']['seeds'] = [$this->values['paths']['seeds']]; + } + + return $this->values['paths']['seeds']; + } + + /** + * @inheritdoc + */ + public function getMigrationBaseClassName(bool $dropNamespace = true): string + { + $className = !isset($this->values['migration_base_class']) ? 'Phinx\Migration\AbstractMigration' : $this->values['migration_base_class']; + + return $dropNamespace ? (substr(strrchr($className, '\\'), 1) ?: $className) : $className; + } + + /** + * @inheritdoc + */ + public function getSeedBaseClassName(bool $dropNamespace = true): string + { + $className = !isset($this->values['seed_base_class']) ? 'Phinx\Seed\AbstractSeed' : $this->values['seed_base_class']; + + return $dropNamespace ? substr(strrchr($className, '\\'), 1) : $className; + } + + /** + * @inheritdoc + */ + public function getTemplateFile() + { + if (!isset($this->values['templates']['file'])) { + return false; + } + + return $this->values['templates']['file']; + } + + /** + * @inheritdoc + */ + public function getTemplateClass() + { + if (!isset($this->values['templates']['class'])) { + return false; + } + + return $this->values['templates']['class']; + } + + /** + * @inheritdoc + */ + public function getTemplateStyle(): string + { + if (!isset($this->values['templates']['style'])) { + return self::TEMPLATE_STYLE_CHANGE; + } + + return $this->values['templates']['style'] === self::TEMPLATE_STYLE_UP_DOWN ? self::TEMPLATE_STYLE_UP_DOWN : self::TEMPLATE_STYLE_CHANGE; + } + + /** + * @inheritdoc + */ + public function getDataDomain(): array + { + if (!isset($this->values['data_domain'])) { + return []; + } + + return $this->values['data_domain']; + } + + /** + * @inheritDoc + */ + public function getContainer(): ?ContainerInterface + { + if (!isset($this->values['container'])) { + return null; + } + + return $this->values['container']; + } + + /** + * @inheritdoc + */ + public function getVersionOrder(): string + { + if (!isset($this->values['version_order'])) { + return self::VERSION_ORDER_CREATION_TIME; + } + + return $this->values['version_order']; + } + + /** + * @inheritdoc + */ + public function isVersionOrderCreationTime(): bool + { + $versionOrder = $this->getVersionOrder(); + + return $versionOrder == self::VERSION_ORDER_CREATION_TIME; + } + + /** + * @inheritdoc + */ + public function getBootstrapFile() + { + if (!isset($this->values['paths']['bootstrap'])) { + return false; + } + + return $this->values['paths']['bootstrap']; + } + + /** + * Replace tokens in the specified array. + * + * @param array $arr Array to replace + * @return array + */ + protected function replaceTokens(array $arr): array + { + // Get environment variables + // Depending on configuration of server / OS and variables_order directive, + // environment variables either end up in $_SERVER (most likely) or $_ENV, + // so we search through both + $tokens = []; + foreach (array_merge($_ENV, $_SERVER) as $varname => $varvalue) { + if (strpos($varname, 'PHINX_') === 0) { + $tokens['%%' . $varname . '%%'] = $varvalue; + } + } + + // Phinx defined tokens (override env tokens) + $tokens['%%PHINX_CONFIG_PATH%%'] = $this->getConfigFilePath(); + $tokens['%%PHINX_CONFIG_DIR%%'] = $this->getConfigFilePath() !== null ? dirname($this->getConfigFilePath()) : ''; + + // Recurse the array and replace tokens + return $this->recurseArrayForTokens($arr, $tokens); + } + + /** + * Recurse an array for the specified tokens and replace them. + * + * @param array $arr Array to recurse + * @param string[] $tokens Array of tokens to search for + * @return array + */ + protected function recurseArrayForTokens(array $arr, array $tokens): array + { + $out = []; + foreach ($arr as $name => $value) { + if (is_array($value)) { + $out[$name] = $this->recurseArrayForTokens($value, $tokens); + continue; + } + if (is_string($value)) { + foreach ($tokens as $token => $tval) { + $value = str_replace($token, $tval ?? '', $value); + } + $out[$name] = $value; + continue; + } + $out[$name] = $value; + } + + return $out; + } + + /** + * Parse a database-agnostic DSN into individual options. + * + * @param array $options Options + * @return array + */ + protected function parseAgnosticDsn(array $options): array + { + $parsed = Util::parseDsn($options['dsn'] ?? ''); + if ($parsed) { + unset($options['dsn']); + } + + $options += $parsed; + + return $options; + } + + /** + * {@inheritDoc} + * + * @param mixed $id ID + * @param mixed $value Value + * @return void + */ + public function offsetSet($id, $value): void + { + $this->values[$id] = $value; + } + + /** + * {@inheritDoc} + * + * @param mixed $id ID + * @throws \InvalidArgumentException + * @return mixed + */ + #[\ReturnTypeWillChange] + public function offsetGet($id) + { + if (!array_key_exists($id, $this->values)) { + throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + return $this->values[$id] instanceof Closure ? $this->values[$id]($this) : $this->values[$id]; + } + + /** + * {@inheritDoc} + * + * @param mixed $id ID + * @return bool + */ + public function offsetExists($id): bool + { + return isset($this->values[$id]); + } + + /** + * {@inheritDoc} + * + * @param mixed $id ID + * @return void + */ + public function offsetUnset($id): void + { + unset($this->values[$id]); + } + + /** + * @inheritdoc + */ + public function getSeedTemplateFile(): ?string + { + return $this->values['templates']['seedFile'] ?? null; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Config/ConfigInterface.php b/vendor/robmorgan/phinx/src/Phinx/Config/ConfigInterface.php new file mode 100644 index 0000000..3bd57f2 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Config/ConfigInterface.php @@ -0,0 +1,171 @@ +null if no environments exist. + * + * @return array|null + */ + public function getEnvironments(): ?array; + + /** + * Returns the configuration for a given environment. + * + * This method returns null if the specified environment + * doesn't exist. + * + * @param string $name Environment Name + * @return array|null + */ + public function getEnvironment(string $name): ?array; + + /** + * Does the specified environment exist in the configuration file? + * + * @param string $name Environment Name + * @return bool + */ + public function hasEnvironment(string $name): bool; + + /** + * Gets the default environment name. + * + * @throws \RuntimeException + * @return string + */ + public function getDefaultEnvironment(): string; + + /** + * Get the aliased value from a supplied alias. + * + * @param string $alias Alias + * @return string|null + */ + public function getAlias(string $alias): ?string; + + /** + * Get all the aliased values. + * + * @return string[] + */ + public function getAliases(): array; + + /** + * Gets the config file path. + * + * @return string|null + */ + public function getConfigFilePath(): ?string; + + /** + * Gets the paths to search for migration files. + * + * @return string[] + */ + public function getMigrationPaths(): array; + + /** + * Gets the paths to search for seed files. + * + * @return string[] + */ + public function getSeedPaths(): array; + + /** + * Get the template file name. + * + * @return string|false + */ + public function getTemplateFile(); + + /** + * Get the template class name. + * + * @return string|false + */ + public function getTemplateClass(); + + /** + * Get the template style to use, either change or up_down. + * + * @return string + */ + public function getTemplateStyle(): string; + + /** + * Get the user-provided container for instantiating seeds + * + * @return \Psr\Container\ContainerInterface|null + */ + public function getContainer(): ?ContainerInterface; + + /** + * Get the data domain array. + * + * @return array + */ + public function getDataDomain(): array; + + /** + * Get the version order. + * + * @return string + */ + public function getVersionOrder(): string; + + /** + * Is version order creation time? + * + * @return bool + */ + public function isVersionOrderCreationTime(): bool; + + /** + * Get the bootstrap file path + * + * @return string|false + */ + public function getBootstrapFile(); + + /** + * Gets the base class name for migrations. + * + * @param bool $dropNamespace Return the base migration class name without the namespace. + * @return string + */ + public function getMigrationBaseClassName(bool $dropNamespace = true): string; + + /** + * Gets the base class name for seeders. + * + * @param bool $dropNamespace Return the base seeder class name without the namespace. + * @return string + */ + public function getSeedBaseClassName(bool $dropNamespace = true): string; + + /** + * Get the seeder template file name or null if not set. + * + * @return string|null + */ + public function getSeedTemplateFile(): ?string; +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Config/FeatureFlags.php b/vendor/robmorgan/phinx/src/Phinx/Config/FeatureFlags.php new file mode 100644 index 0000000..dded535 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Config/FeatureFlags.php @@ -0,0 +1,41 @@ +getMigrationPaths(); + + return $this->searchNamespace($path, $paths); + } + + /** + * Get Seed Namespace associated with path. + * + * @param string $path Path + * @return string|null + */ + public function getSeedNamespaceByPath(string $path): ?string + { + $paths = $this->getSeedPaths(); + + return $this->searchNamespace($path, $paths); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Console/Command/AbstractCommand.php b/vendor/robmorgan/phinx/src/Phinx/Console/Command/AbstractCommand.php new file mode 100644 index 0000000..c9490d6 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Console/Command/AbstractCommand.php @@ -0,0 +1,427 @@ + + */ +abstract class AbstractCommand extends Command +{ + public const FORMAT_JSON = 'json'; + public const FORMAT_YML_ALIAS = 'yaml'; + public const FORMAT_YML = 'yml'; + public const FORMAT_PHP = 'php'; + public const FORMAT_DEFAULT = 'php'; + + /** + * The location of the default change migration template. + */ + protected const DEFAULT_CHANGE_MIGRATION_TEMPLATE = '/../../Migration/Migration.change.template.php.dist'; + + /** + * The location of the default up/down migration template. + */ + protected const DEFAULT_UP_DOWN_MIGRATION_TEMPLATE = '/../../Migration/Migration.up_down.template.php.dist'; + + /** + * The location of the default seed template. + */ + protected const DEFAULT_SEED_TEMPLATE = '/../../Seed/Seed.template.php.dist'; + + /** + * @var \Phinx\Config\ConfigInterface|null + */ + protected $config; + + /** + * @var \Phinx\Db\Adapter\AdapterInterface + */ + protected $adapter; + + /** + * @var \Phinx\Migration\Manager + */ + protected $manager; + + /** + * @var int + */ + protected $verbosityLevel = OutputInterface::OUTPUT_NORMAL | OutputInterface::VERBOSITY_NORMAL; + + /** + * Exit code for when command executes successfully + * + * @var int + */ + public const CODE_SUCCESS = 0; + + /** + * Exit code for when command hits a non-recoverable error during execution + * + * @var int + */ + public const CODE_ERROR = 1; + + /** + * Exit code for when status command is run and there are missing migrations + * + * @var int + */ + public const CODE_STATUS_MISSING = 2; + + /** + * Exit code for when status command is run and there are no missing migations, + * but does have down migrations + * + * @var int + */ + public const CODE_STATUS_DOWN = 3; + + /** + * {@inheritDoc} + * + * @return void + */ + protected function configure(): void + { + $this->addOption('--configuration', '-c', InputOption::VALUE_REQUIRED, 'The configuration file to load'); + $this->addOption('--parser', '-p', InputOption::VALUE_REQUIRED, 'Parser used to read the config file. Defaults to YAML'); + $this->addOption('--no-info', null, InputOption::VALUE_NONE, 'Hides all debug information'); + } + + /** + * Bootstrap Phinx. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return void + */ + public function bootstrap(InputInterface $input, OutputInterface $output): void + { + if ($input->hasParameterOption('--no-info')) { + $this->verbosityLevel = OutputInterface::VERBOSITY_VERBOSE; + } + + if (!$this->hasConfig()) { + $this->loadConfig($input, $output); + } + + $this->loadManager($input, $output); + + $bootstrap = $this->getConfig()->getBootstrapFile(); + if ($bootstrap) { + $output->writeln('using bootstrap ' . Util::relativePath($bootstrap) . ' ', $this->verbosityLevel); + Util::loadPhpFile($bootstrap, $input, $output, $this); + } + + // report the paths + $paths = $this->getConfig()->getMigrationPaths(); + + $output->writeln('using migration paths ', $this->verbosityLevel); + + foreach (Util::globAll($paths) as $path) { + $output->writeln(' - ' . realpath($path) . '', $this->verbosityLevel); + } + + try { + $paths = $this->getConfig()->getSeedPaths(); + + $output->writeln('using seed paths ', $this->verbosityLevel); + + foreach (Util::globAll($paths) as $path) { + $output->writeln(' - ' . realpath($path) . '', $this->verbosityLevel); + } + } catch (UnexpectedValueException $e) { + // do nothing as seeds are optional + } + } + + /** + * Sets the config. + * + * @param \Phinx\Config\ConfigInterface $config Config + * @return $this + */ + public function setConfig(ConfigInterface $config) + { + $this->config = $config; + + return $this; + } + + /** + * @return bool + */ + public function hasConfig(): bool + { + return $this->config !== null; + } + + /** + * Gets the config. + * + * @return \Phinx\Config\ConfigInterface + */ + public function getConfig(): ConfigInterface + { + if ($this->config === null) { + throw new RuntimeException('No config set yet'); + } + + return $this->config; + } + + /** + * Sets the database adapter. + * + * @param \Phinx\Db\Adapter\AdapterInterface $adapter Adapter + * @return $this + */ + public function setAdapter(AdapterInterface $adapter) + { + $this->adapter = $adapter; + + return $this; + } + + /** + * Gets the database adapter. + * + * @return \Phinx\Db\Adapter\AdapterInterface + */ + public function getAdapter(): AdapterInterface + { + return $this->adapter; + } + + /** + * Sets the migration manager. + * + * @param \Phinx\Migration\Manager $manager Manager + * @return $this + */ + public function setManager(Manager $manager) + { + $this->manager = $manager; + + return $this; + } + + /** + * Gets the migration manager. + * + * @return \Phinx\Migration\Manager|null + */ + public function getManager(): ?Manager + { + return $this->manager; + } + + /** + * Returns config file path + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @return string + */ + protected function locateConfigFile(InputInterface $input): string + { + $configFile = $input->hasOption('configuration') ? $input->getOption('configuration') : null; + + $useDefault = false; + + if ($configFile === null || $configFile === false) { + $useDefault = true; + } + + $cwd = getcwd(); + + // locate the phinx config file + // In future walk the tree in reverse (max 10 levels) + $locator = new FileLocator([ + $cwd . DIRECTORY_SEPARATOR, + ]); + + if (!$useDefault) { + // Locate() throws an exception if the file does not exist + return $locator->locate($configFile, $cwd, true); + } + + $possibleConfigFiles = ['phinx.php', 'phinx.json', 'phinx.yaml', 'phinx.yml']; + foreach ($possibleConfigFiles as $configFile) { + try { + return $locator->locate($configFile, $cwd, true); + } catch (InvalidArgumentException $exception) { + $lastException = $exception; + } + } + throw $lastException; + } + + /** + * Parse the config file and load it into the config object + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @throws \InvalidArgumentException + * @return void + */ + protected function loadConfig(InputInterface $input, OutputInterface $output): void + { + $configFilePath = $this->locateConfigFile($input); + $output->writeln('using config file ' . Util::relativePath($configFilePath), $this->verbosityLevel); + + /** @var string|null $parser */ + $parser = $input->getOption('parser'); + + // If no parser is specified try to determine the correct one from the file extension. Defaults to YAML + if ($parser === null) { + $extension = pathinfo($configFilePath, PATHINFO_EXTENSION); + + switch (strtolower($extension)) { + case self::FORMAT_JSON: + $parser = self::FORMAT_JSON; + break; + case self::FORMAT_YML_ALIAS: + case self::FORMAT_YML: + $parser = self::FORMAT_YML; + break; + case self::FORMAT_PHP: + default: + $parser = self::FORMAT_DEFAULT; + break; + } + } + + switch (strtolower($parser)) { + case self::FORMAT_JSON: + $config = Config::fromJson($configFilePath); + break; + case self::FORMAT_PHP: + $config = Config::fromPhp($configFilePath); + break; + case self::FORMAT_YML_ALIAS: + case self::FORMAT_YML: + $config = Config::fromYaml($configFilePath); + break; + default: + throw new InvalidArgumentException(sprintf('\'%s\' is not a valid parser.', $parser)); + } + + $output->writeln('using config parser ' . $parser, $this->verbosityLevel); + + $this->setConfig($config); + } + + /** + * Load the migrations manager and inject the config + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return void + */ + protected function loadManager(InputInterface $input, OutputInterface $output): void + { + if ($this->getManager() === null) { + $manager = new Manager($this->getConfig(), $input, $output); + $manager->setVerbosityLevel($this->verbosityLevel); + $container = $this->getConfig()->getContainer(); + if ($container !== null) { + $manager->setContainer($container); + } + $this->setManager($manager); + } else { + $manager = $this->getManager(); + $manager->setInput($input); + $manager->setOutput($output); + } + } + + /** + * Verify that the migration directory exists and is writable. + * + * @param string $path Path + * @throws \InvalidArgumentException + * @return void + */ + protected function verifyMigrationDirectory(string $path): void + { + 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 + )); + } + } + + /** + * Verify that the seed directory exists and is writable. + * + * @param string $path Path + * @throws \InvalidArgumentException + * @return void + */ + protected function verifySeedDirectory(string $path): void + { + if (!is_dir($path)) { + throw new InvalidArgumentException(sprintf( + 'Seed directory "%s" does not exist', + $path + )); + } + + if (!is_writable($path)) { + throw new InvalidArgumentException(sprintf( + 'Seed directory "%s" is not writable', + $path + )); + } + } + + /** + * Returns the migration template filename. + * + * @return string + */ + protected function getMigrationTemplateFilename(string $style): string + { + return $style === Config::TEMPLATE_STYLE_CHANGE ? __DIR__ . self::DEFAULT_CHANGE_MIGRATION_TEMPLATE : __DIR__ . self::DEFAULT_UP_DOWN_MIGRATION_TEMPLATE; + } + + /** + * Returns the seed template filename. + * + * @return string + */ + protected function getSeedTemplateFilename(): string + { + return __DIR__ . self::DEFAULT_SEED_TEMPLATE; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Console/Command/Breakpoint.php b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Breakpoint.php new file mode 100644 index 0000000..f94d597 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Breakpoint.php @@ -0,0 +1,109 @@ +addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment.'); + + $this->setDescription('Manage breakpoints') + ->addOption('--target', '-t', InputOption::VALUE_REQUIRED, 'The version number to target for the breakpoint') + ->addOption('--set', '-s', InputOption::VALUE_NONE, 'Set the breakpoint') + ->addOption('--unset', '-u', InputOption::VALUE_NONE, 'Unset the breakpoint') + ->addOption('--remove-all', '-r', InputOption::VALUE_NONE, 'Remove all breakpoints') + ->setHelp( + <<breakpoint command allows you to toggle, set, or unset 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 + +phinx breakpoint -e development +phinx breakpoint -e development -t 20110103081132 +phinx breakpoint -e development -r +EOT + ); + } + + /** + * Toggle the breakpoint. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @throws \InvalidArgumentException + * @return int integer 0 on success, or an error code. + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->bootstrap($input, $output); + + /** @var string|null $environment */ + $environment = $input->getOption('environment'); + $version = (int)$input->getOption('target') ?: null; + $removeAll = $input->getOption('remove-all'); + $set = $input->getOption('set'); + $unset = $input->getOption('unset'); + + if ($environment === null) { + $environment = $this->getConfig()->getDefaultEnvironment(); + $output->writeln('warning no environment specified, defaulting to: ' . $environment, $this->verbosityLevel); + } else { + $output->writeln('using environment ' . $environment, $this->verbosityLevel); + } + + if (!$this->getConfig()->hasEnvironment($environment)) { + $output->writeln(sprintf('The environment "%s" does not exist', $environment)); + + return self::CODE_ERROR; + } + + if ($version && $removeAll) { + throw new InvalidArgumentException('Cannot toggle a breakpoint and remove all breakpoints at the same time.'); + } + + if (($set && $unset) || ($set && $removeAll) || ($unset && $removeAll)) { + throw new InvalidArgumentException('Cannot use more than one of --set, --unset, or --remove-all at the same time.'); + } + + if ($removeAll) { + // Remove all breakpoints. + $this->getManager()->removeBreakpoints($environment); + } elseif ($set) { + // Set the breakpoint. + $this->getManager()->setBreakpoint($environment, $version); + } elseif ($unset) { + // Unset the breakpoint. + $this->getManager()->unsetBreakpoint($environment, $version); + } else { + // Toggle the breakpoint. + $this->getManager()->toggleBreakpoint($environment, $version); + } + + return self::CODE_SUCCESS; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Console/Command/Create.php b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Create.php new file mode 100644 index 0000000..1e1824b --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Create.php @@ -0,0 +1,326 @@ +setDescription('Create a new migration') + ->addArgument('name', InputArgument::OPTIONAL, 'Class name of the migration (in CamelCase)') + ->setHelp(sprintf( + '%sCreates a new database migration%s', + PHP_EOL, + PHP_EOL + )); + + // An alternative template. + $this->addOption('template', 't', InputOption::VALUE_REQUIRED, 'Use an alternative template'); + + // A classname to be used to gain access to the template content as well as the ability to + // have a callback once the migration file has been created. + $this->addOption('class', 'l', InputOption::VALUE_REQUIRED, 'Use a class implementing "' . self::CREATION_INTERFACE . '" to generate the template'); + + // Allow the migration path to be chosen non-interactively. + $this->addOption('path', null, InputOption::VALUE_REQUIRED, 'Specify the path in which to create this migration'); + + $this->addOption('style', null, InputOption::VALUE_REQUIRED, 'Specify the style of migration to create'); + } + + /** + * Get the confirmation question asking if the user wants to create the + * migrations directory. + * + * @return \Symfony\Component\Console\Question\ConfirmationQuestion + */ + protected function getCreateMigrationDirectoryQuestion(): ConfirmationQuestion + { + return new ConfirmationQuestion('Create migrations directory? [y]/n ', true); + } + + /** + * Get the question that allows the user to select which migration path to use. + * + * @param string[] $paths Paths + * @return \Symfony\Component\Console\Question\ChoiceQuestion + */ + protected function getSelectMigrationPathQuestion(array $paths): ChoiceQuestion + { + return new ChoiceQuestion('Which migrations path would you like to use?', $paths, 0); + } + + /** + * Returns the migration path to create the migration in. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @throws \Exception + * @return string + */ + protected function getMigrationPath(InputInterface $input, OutputInterface $output): string + { + // First, try the non-interactive option: + $path = $input->getOption('path'); + + if (!empty($path)) { + return $path; + } + + $paths = $this->getConfig()->getMigrationPaths(); + + // No paths? That's a problem. + if (empty($paths)) { + throw new Exception('No migration paths set in your Phinx configuration file.'); + } + + $paths = Util::globAll($paths); + + if (empty($paths)) { + throw new Exception( + 'You probably used curly braces to define migration path in your Phinx configuration file, ' . + 'but no directories have been matched using this pattern. ' . + 'You need to create a migration directory manually.' + ); + } + + // Only one path set, so select that: + if (count($paths) === 1) { + return array_shift($paths); + } + + /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ + $helper = $this->getHelper('question'); + $question = $this->getSelectMigrationPathQuestion($paths); + + return $helper->ask($input, $output, $question); + } + + /** + * Create the new migration. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @return int 0 on success + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->bootstrap($input, $output); + + // get the migration path from the config + $path = $this->getMigrationPath($input, $output); + + if (!file_exists($path)) { + /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ + $helper = $this->getHelper('question'); + $question = $this->getCreateMigrationDirectoryQuestion(); + + if ($helper->ask($input, $output, $question)) { + mkdir($path, 0755, true); + } + } + + $this->verifyMigrationDirectory($path); + + $config = $this->getConfig(); + $namespace = $config instanceof NamespaceAwareInterface ? $config->getMigrationNamespaceByPath($path) : null; + + $path = realpath($path); + $className = $input->getArgument('name'); + if ($className === null) { + $currentTimestamp = Util::getCurrentTimestamp(); + $className = 'V' . $currentTimestamp; + $fileName = $currentTimestamp . '.php'; + } else { + if (!Util::isValidPhinxClassName($className)) { + throw new InvalidArgumentException(sprintf( + 'The migration class name "%s" is invalid. Please use CamelCase format.', + $className + )); + } + + // Compute the file path + $fileName = Util::mapClassNameToFileName($className); + } + + if (!Util::isUniqueMigrationClassName($className, $path)) { + throw new InvalidArgumentException(sprintf( + 'The migration class name "%s%s" already exists', + $namespace ? $namespace . '\\' : '', + $className + )); + } + + $filePath = $path . DIRECTORY_SEPARATOR . $fileName; + + if (is_file($filePath)) { + throw new InvalidArgumentException(sprintf( + 'The file "%s" already exists', + $filePath + )); + } + + // Get the alternative template and static class options from the config, but only allow one of them. + $defaultAltTemplate = $this->getConfig()->getTemplateFile(); + $defaultCreationClassName = $this->getConfig()->getTemplateClass(); + $defaultStyle = $this->getConfig()->getTemplateStyle(); + if ($defaultAltTemplate && $defaultCreationClassName) { + throw new InvalidArgumentException('Cannot define template:class and template:file at the same time'); + } + + // Get the alternative template and static class options from the command line, but only allow one of them. + /** @var string|null $altTemplate */ + $altTemplate = $input->getOption('template'); + /** @var string|null $creationClassName */ + $creationClassName = $input->getOption('class'); + $style = $input->getOption('style'); + + if ($altTemplate && $creationClassName) { + throw new InvalidArgumentException('Cannot use --template and --class at the same time'); + } + + if ($style && !in_array($style, [Config::TEMPLATE_STYLE_CHANGE, Config::TEMPLATE_STYLE_UP_DOWN])) { + throw new InvalidArgumentException('--style should be one of ' . Config::TEMPLATE_STYLE_CHANGE . ' or ' . Config::TEMPLATE_STYLE_UP_DOWN); + } + + // If no commandline options then use the defaults. + if (!$altTemplate && !$creationClassName) { + $altTemplate = $defaultAltTemplate; + $creationClassName = $defaultCreationClassName; + } + + // Verify the alternative template file's existence. + if ($altTemplate && !is_file($altTemplate)) { + throw new InvalidArgumentException(sprintf( + 'The alternative template file "%s" does not exist', + $altTemplate + )); + } + + // Verify that the template creation class (or the aliased class) exists and that it implements the required interface. + $aliasedClassName = null; + if ($creationClassName) { + // Supplied class does not exist, is it aliased? + if (!class_exists($creationClassName)) { + $aliasedClassName = $this->getConfig()->getAlias($creationClassName); + if ($aliasedClassName && !class_exists($aliasedClassName)) { + throw new InvalidArgumentException(sprintf( + 'The class "%s" via the alias "%s" does not exist', + $aliasedClassName, + $creationClassName + )); + } elseif (!$aliasedClassName) { + throw new InvalidArgumentException(sprintf( + 'The class "%s" does not exist', + $creationClassName + )); + } + } + + // Does the class implement the required interface? + if (!$aliasedClassName && !is_subclass_of($creationClassName, self::CREATION_INTERFACE)) { + throw new InvalidArgumentException(sprintf( + 'The class "%s" does not implement the required interface "%s"', + $creationClassName, + self::CREATION_INTERFACE + )); + } elseif ($aliasedClassName && !is_subclass_of($aliasedClassName, self::CREATION_INTERFACE)) { + throw new InvalidArgumentException(sprintf( + 'The class "%s" via the alias "%s" does not implement the required interface "%s"', + $aliasedClassName, + $creationClassName, + self::CREATION_INTERFACE + )); + } + } + + // Use the aliased class. + $creationClassName = $aliasedClassName ?: $creationClassName; + + // Determine the appropriate mechanism to get the template + if ($creationClassName) { + // Get the template from the creation class + $creationClass = new $creationClassName($input, $output); + $contents = $creationClass->getMigrationTemplate(); + } else { + // Load the alternative template if it is defined. + $contents = file_get_contents($altTemplate ?: $this->getMigrationTemplateFilename($style ?: $defaultStyle)); + } + + // inject the class names appropriate to this migration + $classes = [ + '$namespaceDefinition' => $namespace !== null ? (PHP_EOL . 'namespace ' . $namespace . ';' . PHP_EOL) : '', + '$namespace' => $namespace, + '$useClassName' => $this->getConfig()->getMigrationBaseClassName(false), + '$className' => $className, + '$version' => Util::getVersionFromFileName($fileName), + '$baseClassName' => $this->getConfig()->getMigrationBaseClassName(true), + ]; + $contents = strtr($contents, $classes); + + if (file_put_contents($filePath, $contents) === false) { + throw new RuntimeException(sprintf( + 'The file "%s" could not be written to', + $path + )); + } + + // Do we need to do the post creation call to the creation class? + if (isset($creationClass)) { + /** @var \Phinx\Migration\CreationInterface $creationClass */ + $creationClass->postMigrationCreation($filePath, $className, $this->getConfig()->getMigrationBaseClassName()); + } + + $output->writeln('using migration base class ' . $classes['$useClassName'], $this->verbosityLevel); + + if (!empty($altTemplate)) { + $output->writeln('using alternative template ' . $altTemplate, $this->verbosityLevel); + } elseif (!empty($creationClassName)) { + $output->writeln('using template creation class ' . $creationClassName, $this->verbosityLevel); + } else { + $output->writeln('using default template', $this->verbosityLevel); + } + + $output->writeln('created ' . Util::relativePath($filePath), $this->verbosityLevel); + + return self::CODE_SUCCESS; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Console/Command/Init.php b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Init.php new file mode 100644 index 0000000..34568b6 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Init.php @@ -0,0 +1,171 @@ +setDescription('Initialize the application for Phinx') + ->addOption( + '--format', + '-f', + InputArgument::OPTIONAL, + 'What format should we use to initialize?', + AbstractCommand::FORMAT_DEFAULT + ) + ->addArgument('path', InputArgument::OPTIONAL, 'Which path should we initialize for Phinx?') + ->setHelp(sprintf( + '%sInitializes the application for Phinx%s', + PHP_EOL, + PHP_EOL + )); + } + + /** + * Initializes the application. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Interface implemented by all input classes. + * @param \Symfony\Component\Console\Output\OutputInterface $output Interface implemented by all output classes. + * @return int 0 on success + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $format = strtolower($input->getOption('format')); + $path = $this->resolvePath($input, $format); + $this->writeConfig($path, $format); + + $output->writeln("created {$path}"); + + return AbstractCommand::CODE_SUCCESS; + } + + /** + * Return valid $path for Phinx's config file. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Interface implemented by all input classes. + * @param string $format Format to resolve for + * @throws \InvalidArgumentException + * @return string + */ + protected function resolvePath(InputInterface $input, string $format): string + { + // get the migration path from the config + $path = (string)$input->getArgument('path'); + + if (!in_array($format, static::$supportedFormats, true)) { + throw new InvalidArgumentException(sprintf( + 'Invalid format "%s". Format must be either ' . implode(', ', static::$supportedFormats) . '.', + $format + )); + } + + // Fallback + if (!$path) { + $path = getcwd() . DIRECTORY_SEPARATOR . self::FILE_NAME . '.' . $format; + } + + // Adding file name if necessary + if (is_dir($path)) { + $path .= DIRECTORY_SEPARATOR . self::FILE_NAME . '.' . $format; + } + + // Check if path is available + $dirname = dirname($path); + if (is_dir($dirname) && !is_file($path)) { + return $path; + } + + // Path is valid, but file already exists + if (is_file($path)) { + throw new InvalidArgumentException(sprintf( + 'Config file "%s" already exists.', + $path + )); + } + + // Dir is invalid + throw new InvalidArgumentException(sprintf( + 'Invalid path "%s" for config file.', + $path + )); + } + + /** + * Writes Phinx's config in provided $path + * + * @param string $path Config file's path. + * @param string $format Format to use for config file + * @throws \InvalidArgumentException + * @throws \RuntimeException + * @return void + */ + protected function writeConfig(string $path, string $format = AbstractCommand::FORMAT_DEFAULT): void + { + // Check if dir is writable + $dirname = dirname($path); + if (!is_writable($dirname)) { + throw new InvalidArgumentException(sprintf( + 'The directory "%s" is not writable', + $dirname + )); + } + + if ($format === AbstractCommand::FORMAT_YML_ALIAS) { + $format = AbstractCommand::FORMAT_YML; + } + + // load the config template + if (is_dir(__DIR__ . '/../../../../data')) { + $contents = file_get_contents(__DIR__ . '/../../../../data/' . self::FILE_NAME . '.' . $format . '.dist'); + } else { + throw new RuntimeException(sprintf( + 'Could not find template for format "%s".', + $format + )); + } + + if (file_put_contents($path, $contents) === false) { + throw new RuntimeException(sprintf( + 'The file "%s" could not be written to', + $path + )); + } + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Console/Command/ListAliases.php b/vendor/robmorgan/phinx/src/Phinx/Console/Command/ListAliases.php new file mode 100644 index 0000000..1eeac2c --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Console/Command/ListAliases.php @@ -0,0 +1,80 @@ +setDescription('List template class aliases') + ->setHelp('The list:aliases command lists the migration template generation class aliases'); + } + + /** + * List migration template creation aliases. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return int 0 on success + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->bootstrap($input, $output); + + $aliases = $this->config->getAliases(); + + if ($aliases) { + $maxAliasLength = max(array_map('strlen', array_keys($aliases))); + $maxClassLength = max(array_map('strlen', $aliases)); + $output->writeln( + array_merge( + [ + '', + sprintf('%s %s', str_pad('Alias', $maxAliasLength), str_pad('Class', $maxClassLength)), + sprintf('%s %s', str_repeat('=', $maxAliasLength), str_repeat('=', $maxClassLength)), + ], + array_map( + function ($alias, $class) use ($maxAliasLength, $maxClassLength) { + return sprintf('%s %s', str_pad($alias, $maxAliasLength), str_pad($class, $maxClassLength)); + }, + array_keys($aliases), + $aliases + ) + ), + $this->verbosityLevel + ); + } else { + $output->writeln( + sprintf( + 'No aliases defined in %s', + Util::relativePath($this->config->getConfigFilePath()) + ) + ); + } + + return self::CODE_SUCCESS; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Console/Command/Migrate.php b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Migrate.php new file mode 100644 index 0000000..571e242 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Migrate.php @@ -0,0 +1,140 @@ +addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment'); + + $this->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') + ->addOption('--dry-run', '-x', InputOption::VALUE_NONE, 'Dump query to standard output instead of executing it') + ->addOption('--fake', null, InputOption::VALUE_NONE, "Mark any migrations selected as run, but don't actually execute them") + ->setHelp( + <<migrate command runs all available migrations, optionally up to a specific version + +phinx migrate -e development +phinx migrate -e development -t 20110103081132 +phinx migrate -e development -d 20110103 +phinx migrate -e development -v + +EOT + ); + } + + /** + * Migrate the database. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return int integer 0 on success, or an error code. + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->bootstrap($input, $output); + + $version = $input->getOption('target'); + /** @var string|null $environment */ + $environment = $input->getOption('environment'); + $date = $input->getOption('date'); + $fake = (bool)$input->getOption('fake'); + + if ($environment === null) { + $environment = $this->getConfig()->getDefaultEnvironment(); + $output->writeln('warning no environment specified, defaulting to: ' . $environment, $this->verbosityLevel); + } else { + $output->writeln('using environment ' . $environment, $this->verbosityLevel); + } + + if (!$this->getConfig()->hasEnvironment($environment)) { + $output->writeln(sprintf('The environment "%s" does not exist', $environment)); + + return self::CODE_ERROR; + } + + $envOptions = $this->getConfig()->getEnvironment($environment); + if (isset($envOptions['adapter'])) { + $output->writeln('using adapter ' . $envOptions['adapter'], $this->verbosityLevel); + } + + if (isset($envOptions['wrapper'])) { + $output->writeln('using wrapper ' . $envOptions['wrapper'], $this->verbosityLevel); + } + + if (isset($envOptions['name'])) { + $output->writeln('using database ' . $envOptions['name'], $this->verbosityLevel); + } else { + $output->writeln('Could not determine database name! Please specify a database name in your config file.'); + + return self::CODE_ERROR; + } + + if (isset($envOptions['table_prefix'])) { + $output->writeln('using table prefix ' . $envOptions['table_prefix'], $this->verbosityLevel); + } + if (isset($envOptions['table_suffix'])) { + $output->writeln('using table suffix ' . $envOptions['table_suffix'], $this->verbosityLevel); + } + + $versionOrder = $this->getConfig()->getVersionOrder(); + $output->writeln('ordering by ' . $versionOrder . ' time', $this->verbosityLevel); + + if ($fake) { + $output->writeln('warning performing fake migrations', $this->verbosityLevel); + } + + try { + // run the migrations + $start = microtime(true); + if ($date !== null) { + $this->getManager()->migrateToDateTime($environment, new DateTime($date), $fake); + } else { + $this->getManager()->migrate($environment, $version, $fake); + } + $end = microtime(true); + } catch (Exception $e) { + $output->writeln('' . $e->__toString() . ''); + + return self::CODE_ERROR; + } catch (Throwable $e) { + $output->writeln('' . $e->__toString() . ''); + + return self::CODE_ERROR; + } + + $output->writeln('', $this->verbosityLevel); + $output->writeln('All Done. Took ' . sprintf('%.4fs', $end - $start) . '', $this->verbosityLevel); + + return self::CODE_SUCCESS; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Console/Command/Rollback.php b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Rollback.php new file mode 100644 index 0000000..7e5bdc7 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Rollback.php @@ -0,0 +1,171 @@ +addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment'); + + $this->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') + ->addOption('--dry-run', '-x', InputOption::VALUE_NONE, 'Dump query to standard output instead of executing it') + ->addOption('--fake', null, InputOption::VALUE_NONE, "Mark any rollbacks selected as run, but don't actually execute them") + ->setHelp( + <<rollback command reverts the last migration, or optionally up to a specific version + +phinx rollback -e development +phinx rollback -e development -t 20111018185412 +phinx rollback -e development -d 20111018 +phinx rollback -e development -v +phinx rollback -e development -t 20111018185412 -f + +If you have a breakpoint set, then you can rollback to target 0 and the rollbacks will stop at the breakpoint. +phinx rollback -e development -t 0 + +The version_order configuration option is used to determine the order of the migrations when rolling back. +This can be used to allow the rolling back of the last executed migration instead of the last created one, or combined +with the -d|--date option to rollback to a certain date using the migration start times to order them. + +EOT + ); + } + + /** + * Rollback the migration. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return int integer 0 on success, or an error code. + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->bootstrap($input, $output); + + $environment = $input->getOption('environment'); + $version = $input->getOption('target'); + $date = $input->getOption('date'); + $force = (bool)$input->getOption('force'); + $fake = (bool)$input->getOption('fake'); + + $config = $this->getConfig(); + + if ($environment === null) { + $environment = $config->getDefaultEnvironment(); + $output->writeln('warning no environment specified, defaulting to: ' . $environment, $this->verbosityLevel); + } else { + $output->writeln('using environment ' . $environment, $this->verbosityLevel); + } + + if (!$this->getConfig()->hasEnvironment($environment)) { + $output->writeln(sprintf('The environment "%s" does not exist', $environment)); + + return self::CODE_ERROR; + } + + $envOptions = $config->getEnvironment($environment); + if (isset($envOptions['adapter'])) { + $output->writeln('using adapter ' . $envOptions['adapter'], $this->verbosityLevel); + } + + if (isset($envOptions['wrapper'])) { + $output->writeln('using wrapper ' . $envOptions['wrapper'], $this->verbosityLevel); + } + + if (isset($envOptions['name'])) { + $output->writeln('using database ' . $envOptions['name'], $this->verbosityLevel); + } + + $versionOrder = $this->getConfig()->getVersionOrder(); + $output->writeln('ordering by ' . $versionOrder . ' time', $this->verbosityLevel); + + if ($fake) { + $output->writeln('warning performing fake rollbacks', $this->verbosityLevel); + } + + // rollback the specified environment + if ($date === null) { + $targetMustMatchVersion = true; + $target = $version; + } else { + $targetMustMatchVersion = false; + $target = $this->getTargetFromDate($date); + } + + $start = microtime(true); + $this->getManager()->rollback($environment, $target, $force, $targetMustMatchVersion, $fake); + $end = microtime(true); + + $output->writeln('', $this->verbosityLevel); + $output->writeln('All Done. Took ' . sprintf('%.4fs', $end - $start) . '', $this->verbosityLevel); + + return self::CODE_SUCCESS; + } + + /** + * Get Target from Date + * + * @param string $date The date to convert to a target. + * @throws \InvalidArgumentException + * @return string The target + */ + public function getTargetFromDate(string $date): string + { + if (!preg_match('/^\d{4,14}$/', $date)) { + throw new InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].'); + } + + // what we need to append to the date according to the possible date string lengths + $dateStrlenToAppend = [ + 14 => '', + 12 => '00', + 10 => '0000', + 8 => '000000', + 6 => '01000000', + 4 => '0101000000', + ]; + + if (!isset($dateStrlenToAppend[strlen($date)])) { + throw new InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].'); + } + + $target = $date . $dateStrlenToAppend[strlen($date)]; + + $dateTime = DateTime::createFromFormat('YmdHis', $target); + + if ($dateTime === false) { + throw new InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].'); + } + + return $dateTime->format('YmdHis'); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Console/Command/SeedCreate.php b/vendor/robmorgan/phinx/src/Phinx/Console/Command/SeedCreate.php new file mode 100644 index 0000000..ae56f0b --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Console/Command/SeedCreate.php @@ -0,0 +1,219 @@ +setDescription('Create a new database seeder') + ->addArgument('name', InputArgument::REQUIRED, 'What is the name of the seeder?') + ->addOption('path', null, InputOption::VALUE_REQUIRED, 'Specify the path in which to create this seeder') + ->setHelp(sprintf( + '%sCreates a new database seeder%s', + PHP_EOL, + PHP_EOL + )); + + // An alternative template. + $this->addOption('template', 't', InputOption::VALUE_REQUIRED, 'Use an alternative template'); + } + + /** + * Get the confirmation question asking if the user wants to create the + * seeds directory. + * + * @return \Symfony\Component\Console\Question\ConfirmationQuestion + */ + protected function getCreateSeedDirectoryQuestion(): ConfirmationQuestion + { + return new ConfirmationQuestion('Create seeds directory? [y]/n ', true); + } + + /** + * Get the question that allows the user to select which seed path to use. + * + * @param string[] $paths Paths + * @return \Symfony\Component\Console\Question\ChoiceQuestion + */ + protected function getSelectSeedPathQuestion(array $paths): ChoiceQuestion + { + return new ChoiceQuestion('Which seeds path would you like to use?', $paths, 0); + } + + /** + * Returns the seed path to create the seeder in. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @throws \Exception + * @return string + */ + protected function getSeedPath(InputInterface $input, OutputInterface $output): string + { + // First, try the non-interactive option: + $path = $input->getOption('path'); + + if (!empty($path)) { + return $path; + } + + $paths = $this->getConfig()->getSeedPaths(); + + // No paths? That's a problem. + if (empty($paths)) { + throw new Exception('No seed paths set in your Phinx configuration file.'); + } + + $paths = Util::globAll($paths); + + if (empty($paths)) { + throw new Exception( + 'You probably used curly braces to define seed path in your Phinx configuration file, ' . + 'but no directories have been matched using this pattern. ' . + 'You need to create a seed directory manually.' + ); + } + + // Only one path set, so select that: + if (count($paths) === 1) { + return array_shift($paths); + } + + /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ + $helper = $this->getHelper('question'); + $question = $this->getSelectSeedPathQuestion($paths); + + return $helper->ask($input, $output, $question); + } + + /** + * Create the new seeder. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @return int 0 on success + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->bootstrap($input, $output); + + // get the seed path from the config + $path = $this->getSeedPath($input, $output); + + if (!file_exists($path)) { + /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ + $helper = $this->getHelper('question'); + $question = $this->getCreateSeedDirectoryQuestion(); + + if ($helper->ask($input, $output, $question)) { + mkdir($path, 0755, true); + } + } + + $this->verifySeedDirectory($path); + + $path = realpath($path); + /** @var string|null $className */ + $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) + )); + } + + // Get the alternative template option from the command line. + $altTemplate = $input->getOption('template'); + + // Verify the alternative template file's existence. + if ($altTemplate && !is_file($altTemplate)) { + throw new InvalidArgumentException(sprintf( + 'The template file "%s" does not exist', + $altTemplate + )); + } + + // Command-line option must have higher priority than value from Config + $config = $this->getConfig(); + if (is_null($altTemplate)) { + $altTemplate = $config->getSeedTemplateFile(); + if (!is_null($altTemplate) && !is_file($altTemplate)) { + throw new InvalidArgumentException(sprintf( + 'The template file `%s` from config does not exist', + $altTemplate + )); + } + } + + // Determine the appropriate mechanism to get the template + // Load the alternative template if it is defined. + $contents = file_get_contents($altTemplate ?: $this->getSeedTemplateFilename()); + + $namespace = $config instanceof NamespaceAwareInterface ? $config->getSeedNamespaceByPath($path) : null; + $classes = [ + '$namespaceDefinition' => $namespace !== null ? ('namespace ' . $namespace . ';') : '', + '$namespace' => $namespace, + '$useClassName' => $config->getSeedBaseClassName(false), + '$className' => $className, + '$baseClassName' => $config->getSeedBaseClassName(true), + ]; + $contents = strtr($contents, $classes); + + if (file_put_contents($filePath, $contents) === false) { + throw new RuntimeException(sprintf( + 'The file "%s" could not be written to', + $path + )); + } + + $output->writeln('using seed base class ' . $classes['$useClassName'], $this->verbosityLevel); + $output->writeln('created ' . Util::relativePath($filePath), $this->verbosityLevel); + + return self::CODE_SUCCESS; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Console/Command/SeedRun.php b/vendor/robmorgan/phinx/src/Phinx/Console/Command/SeedRun.php new file mode 100644 index 0000000..ba2b32f --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Console/Command/SeedRun.php @@ -0,0 +1,121 @@ +addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment'); + + $this->setDescription('Run database seeders') + ->addOption('--seed', '-s', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'What is the name of the seeder?') + ->setHelp( + <<seed:run command runs all available or individual seeders + +phinx seed:run -e development +phinx seed:run -e development -s UserSeeder +phinx seed:run -e development -s UserSeeder -s PermissionSeeder -s LogSeeder +phinx seed:run -e development -v + +EOT + ); + } + + /** + * Run database seeders. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return int integer 0 on success, or an error code. + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->bootstrap($input, $output); + + /** @var array|null $seedSet */ + $seedSet = $input->getOption('seed'); + /** @var string|null $environment */ + $environment = $input->getOption('environment'); + + if ($environment === null) { + $environment = $this->getConfig()->getDefaultEnvironment(); + $output->writeln('warning no environment specified, defaulting to: ' . $environment, $this->verbosityLevel); + } else { + $output->writeln('using environment ' . $environment, $this->verbosityLevel); + } + + if (!$this->getConfig()->hasEnvironment($environment)) { + $output->writeln(sprintf('The environment "%s" does not exist', $environment)); + + return self::CODE_ERROR; + } + + $envOptions = $this->getConfig()->getEnvironment($environment); + if (isset($envOptions['adapter'])) { + $output->writeln('using adapter ' . $envOptions['adapter'], $this->verbosityLevel); + } + + if (isset($envOptions['wrapper'])) { + $output->writeln('using wrapper ' . $envOptions['wrapper'], $this->verbosityLevel); + } + + if (isset($envOptions['name'])) { + $output->writeln('using database ' . $envOptions['name'], $this->verbosityLevel); + } else { + $output->writeln('Could not determine database name! Please specify a database name in your config file.'); + + return self::CODE_ERROR; + } + + if (isset($envOptions['table_prefix'])) { + $output->writeln('using table prefix ' . $envOptions['table_prefix'], $this->verbosityLevel); + } + if (isset($envOptions['table_suffix'])) { + $output->writeln('using table suffix ' . $envOptions['table_suffix'], $this->verbosityLevel); + } + + $start = microtime(true); + + if (empty($seedSet)) { + // run all the seed(ers) + $this->getManager()->seed($environment); + } else { + // run seed(ers) specified in a comma-separated list of classes + foreach ($seedSet as $seed) { + $this->getManager()->seed($environment, trim($seed)); + } + } + + $end = microtime(true); + + $output->writeln('', $this->verbosityLevel); + $output->writeln('All Done. Took ' . sprintf('%.4fs', $end - $start) . '', $this->verbosityLevel); + + return self::CODE_SUCCESS; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Console/Command/Status.php b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Status.php new file mode 100644 index 0000000..c00846f --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Status.php @@ -0,0 +1,94 @@ +addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment.'); + + $this->setDescription('Show migration status') + ->addOption('--format', '-f', InputOption::VALUE_REQUIRED, 'The output format: text or json. Defaults to text.') + ->setHelp( + <<status command prints a list of all migrations, along with their current status + +phinx status -e development +phinx status -e development -f json + +The version_order configuration option is used to determine the order of the status migrations. +EOT + ); + } + + /** + * Show the migration status. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return int 0 if all migrations are up, or an error code + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->bootstrap($input, $output); + + /** @var string|null $environment */ + $environment = $input->getOption('environment'); + /** @var string|null $environment */ + $format = $input->getOption('format'); + + if ($environment === null) { + $environment = $this->getConfig()->getDefaultEnvironment(); + $output->writeln('warning no environment specified, defaulting to: ' . $environment, $this->verbosityLevel); + } else { + $output->writeln('using environment ' . $environment, $this->verbosityLevel); + } + + if (!$this->getConfig()->hasEnvironment($environment)) { + $output->writeln(sprintf('The environment "%s" does not exist', $environment)); + + return self::CODE_ERROR; + } + + if ($format !== null) { + $output->writeln('using format ' . $format, $this->verbosityLevel); + } + + $output->writeln('ordering by ' . $this->getConfig()->getVersionOrder() . ' time', $this->verbosityLevel); + + // print the status + $result = $this->getManager()->printStatus($environment, $format); + + if ($result['hasMissingMigration']) { + return self::CODE_STATUS_MISSING; + } elseif ($result['hasDownMigration']) { + return self::CODE_STATUS_DOWN; + } + + return self::CODE_SUCCESS; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Console/Command/Test.php b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Test.php new file mode 100644 index 0000000..abe8036 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Console/Command/Test.php @@ -0,0 +1,97 @@ +addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment'); + + $this->setDescription('Verify the configuration file') + ->setHelp( + <<test command is used to verify the phinx configuration file and optionally an environment + +phinx test +phinx test -e development + +If the environment option is set, it will test that phinx can connect to the DB associated with that environment +EOT + ); + } + + /** + * Verify configuration file + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @throws \InvalidArgumentException + * @return int 0 on success + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->loadConfig($input, $output); + $this->loadManager($input, $output); + + // Verify the migrations path(s) + array_map( + [$this, 'verifyMigrationDirectory'], + Util::globAll($this->getConfig()->getMigrationPaths()) + ); + + // Verify the seed path(s) + array_map( + [$this, 'verifySeedDirectory'], + Util::globAll($this->getConfig()->getSeedPaths()) + ); + + $envName = $input->getOption('environment'); + if ($envName) { + if (!$this->getConfig()->hasEnvironment($envName)) { + throw new InvalidArgumentException(sprintf( + 'The environment "%s" does not exist', + $envName + )); + } + + $output->writeln(sprintf('validating environment %s', $envName), $this->verbosityLevel); + $environment = new Environment( + $envName, + $this->getConfig()->getEnvironment($envName) + ); + // validate environment connection + $environment->getAdapter()->connect(); + } + + $output->writeln('success!', $this->verbosityLevel); + + return self::CODE_SUCCESS; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Console/PhinxApplication.php b/vendor/robmorgan/phinx/src/Phinx/Console/PhinxApplication.php new file mode 100644 index 0000000..5ff32db --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Console/PhinxApplication.php @@ -0,0 +1,72 @@ + + */ +class PhinxApplication extends Application +{ + /** + * Initialize the Phinx console application. + */ + public function __construct() + { + parent::__construct('Phinx by CakePHP - https://phinx.org.'); + + $this->addCommands([ + new Init(), + new Create(), + new Migrate(), + new Rollback(), + new Status(), + new Breakpoint(), + new Test(), + new SeedCreate(), + new SeedRun(), + new ListAliases(), + ]); + } + + /** + * Runs the current application. + * + * @param \Symfony\Component\Console\Input\InputInterface $input An Input instance + * @param \Symfony\Component\Console\Output\OutputInterface $output An Output instance + * @return int 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output): int + { + // always show the version information except when the user invokes the help + // command as that already does it + if ($input->hasParameterOption('--no-info') === false) { + if (($input->hasParameterOption(['--help', '-h']) !== false) || ($input->getFirstArgument() !== null && $input->getFirstArgument() !== 'list')) { + $output->writeln($this->getLongVersion()); + $output->writeln(''); + } + } + + return parent::doRun($input, $output); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Action/Action.php b/vendor/robmorgan/phinx/src/Phinx/Db/Action/Action.php new file mode 100644 index 0000000..90c3ae4 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Action/Action.php @@ -0,0 +1,38 @@ +table = $table; + } + + /** + * The table this action will be applied to + * + * @return \Phinx\Db\Table\Table + */ + public function getTable(): Table + { + return $this->table; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Action/AddColumn.php b/vendor/robmorgan/phinx/src/Phinx/Db/Action/AddColumn.php new file mode 100644 index 0000000..299c927 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Action/AddColumn.php @@ -0,0 +1,62 @@ +column = $column; + } + + /** + * Returns a new AddColumn object after assembling the given commands + * + * @param \Phinx\Db\Table\Table $table The table to add the column to + * @param string $columnName The column name + * @param string|\Phinx\Util\Literal $type The column type + * @param array $options The column options + * @return static + */ + public static function build(Table $table, string $columnName, $type = null, array $options = []) + { + $column = new Column(); + $column->setName($columnName); + $column->setType($type); + $column->setOptions($options); // map options to column methods + + return new static($table, $column); + } + + /** + * Returns the column to be added + * + * @return \Phinx\Db\Table\Column + */ + public function getColumn(): Column + { + return $this->column; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Action/AddForeignKey.php b/vendor/robmorgan/phinx/src/Phinx/Db/Action/AddForeignKey.php new file mode 100644 index 0000000..9121cfe --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Action/AddForeignKey.php @@ -0,0 +1,78 @@ +foreignKey = $fk; + } + + /** + * Creates a new AddForeignKey object after building the foreign key with + * the passed attributes + * + * @param \Phinx\Db\Table\Table $table The table object to add the foreign key to + * @param string|string[] $columns The columns for the foreign key + * @param \Phinx\Db\Table\Table|string $referencedTable The table the foreign key references + * @param string|string[] $referencedColumns The columns in the referenced table + * @param array $options Extra options for the foreign key + * @param string|null $name The name of the foreign key + * @return static + */ + public static function build(Table $table, $columns, $referencedTable, $referencedColumns = ['id'], array $options = [], ?string $name = null) + { + if (is_string($referencedColumns)) { + $referencedColumns = [$referencedColumns]; // str to array + } + + if (is_string($referencedTable)) { + $referencedTable = new Table($referencedTable); + } + + $fk = new ForeignKey(); + $fk->setReferencedTable($referencedTable) + ->setColumns($columns) + ->setReferencedColumns($referencedColumns) + ->setOptions($options); + + if ($name !== null) { + $fk->setConstraint($name); + } + + return new static($table, $fk); + } + + /** + * Returns the foreign key to be added + * + * @return \Phinx\Db\Table\ForeignKey + */ + public function getForeignKey(): ForeignKey + { + return $this->foreignKey; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Action/AddIndex.php b/vendor/robmorgan/phinx/src/Phinx/Db/Action/AddIndex.php new file mode 100644 index 0000000..aac22cb --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Action/AddIndex.php @@ -0,0 +1,67 @@ +index = $index; + } + + /** + * Creates a new AddIndex object after building the index object with the + * provided arguments + * + * @param \Phinx\Db\Table\Table $table The table to add the index to + * @param string|string[]|\Phinx\Db\Table\Index $columns The columns to index + * @param array $options Additional options for the index creation + * @return static + */ + public static function build(Table $table, $columns, array $options = []) + { + // create a new index object if strings or an array of strings were supplied + $index = $columns; + + if (!$columns instanceof Index) { + $index = new Index(); + + $index->setColumns($columns); + $index->setOptions($options); + } + + return new static($table, $index); + } + + /** + * Returns the index to be added + * + * @return \Phinx\Db\Table\Index + */ + public function getIndex(): Index + { + return $this->index; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Action/ChangeColumn.php b/vendor/robmorgan/phinx/src/Phinx/Db/Action/ChangeColumn.php new file mode 100644 index 0000000..143baf9 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Action/ChangeColumn.php @@ -0,0 +1,87 @@ +columnName = $columnName; + $this->column = $column; + + // if the name was omitted use the existing column name + if ($column->getName() === null || strlen($column->getName()) === 0) { + $column->setName($columnName); + } + } + + /** + * Creates a new ChangeColumn object after building the column definition + * out of the provided arguments + * + * @param \Phinx\Db\Table\Table $table The table to alter + * @param string $columnName The name of the column to change + * @param string|\Phinx\Db\Table\Column|\Phinx\Util\Literal $type The type of the column + * @param array $options Additional options for the column + * @return static + */ + public static function build(Table $table, string $columnName, $type = null, array $options = []) + { + $column = new Column(); + $column->setName($columnName); + $column->setType($type); + $column->setOptions($options); // map options to column methods + + return new static($table, $columnName, $column); + } + + /** + * Returns the name of the column to change + * + * @return string + */ + public function getColumnName(): string + { + return $this->columnName; + } + + /** + * Returns the column definition + * + * @return \Phinx\Db\Table\Column + */ + public function getColumn(): Column + { + return $this->column; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Action/ChangeComment.php b/vendor/robmorgan/phinx/src/Phinx/Db/Action/ChangeComment.php new file mode 100644 index 0000000..5604530 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Action/ChangeComment.php @@ -0,0 +1,42 @@ +newComment = $newComment; + } + + /** + * Return the new comment for the table + * + * @return string|null + */ + public function getNewComment(): ?string + { + return $this->newComment; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Action/ChangePrimaryKey.php b/vendor/robmorgan/phinx/src/Phinx/Db/Action/ChangePrimaryKey.php new file mode 100644 index 0000000..bb220fd --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Action/ChangePrimaryKey.php @@ -0,0 +1,42 @@ +newColumns = $newColumns; + } + + /** + * Return the new columns for the primary key + * + * @return string|string[]|null + */ + public function getNewColumns() + { + return $this->newColumns; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Action/CreateTable.php b/vendor/robmorgan/phinx/src/Phinx/Db/Action/CreateTable.php new file mode 100644 index 0000000..8f50afa --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Action/CreateTable.php @@ -0,0 +1,12 @@ +foreignKey = $foreignKey; + } + + /** + * Creates a new DropForeignKey object after building the ForeignKey + * definition out of the passed arguments. + * + * @param \Phinx\Db\Table\Table $table The table to delete the foreign key from + * @param string|string[] $columns The columns participating in the foreign key + * @param string|null $constraint The constraint name + * @return static + */ + public static function build(Table $table, $columns, ?string $constraint = null) + { + if (is_string($columns)) { + $columns = [$columns]; + } + + $foreignKey = new ForeignKey(); + $foreignKey->setColumns($columns); + + if ($constraint) { + $foreignKey->setConstraint($constraint); + } + + return new static($table, $foreignKey); + } + + /** + * Returns the foreign key to remove + * + * @return \Phinx\Db\Table\ForeignKey + */ + public function getForeignKey(): ForeignKey + { + return $this->foreignKey; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Action/DropIndex.php b/vendor/robmorgan/phinx/src/Phinx/Db/Action/DropIndex.php new file mode 100644 index 0000000..f2b6e21 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Action/DropIndex.php @@ -0,0 +1,75 @@ +index = $index; + } + + /** + * Creates a new DropIndex object after assembling the passed + * arguments. + * + * @param \Phinx\Db\Table\Table $table The table where the index is + * @param string[] $columns the indexed columns + * @return static + */ + public static function build(Table $table, array $columns = []) + { + $index = new Index(); + $index->setColumns($columns); + + return new static($table, $index); + } + + /** + * Creates a new DropIndex when the name of the index to drop + * is known. + * + * @param \Phinx\Db\Table\Table $table The table where the index is + * @param string $name The name of the index + * @return static + */ + public static function buildFromName(Table $table, string $name) + { + $index = new Index(); + $index->setName($name); + + return new static($table, $index); + } + + /** + * Returns the index to be dropped + * + * @return \Phinx\Db\Table\Index + */ + public function getIndex(): Index + { + return $this->index; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Action/DropTable.php b/vendor/robmorgan/phinx/src/Phinx/Db/Action/DropTable.php new file mode 100644 index 0000000..6343d0b --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Action/DropTable.php @@ -0,0 +1,12 @@ +column = $column; + } + + /** + * Creates a new RemoveColumn object after assembling the + * passed arguments. + * + * @param \Phinx\Db\Table\Table $table The table where the column is + * @param string $columnName The name of the column to drop + * @return static + */ + public static function build(Table $table, string $columnName) + { + $column = new Column(); + $column->setName($columnName); + + return new static($table, $column); + } + + /** + * Returns the column to be dropped + * + * @return \Phinx\Db\Table\Column + */ + public function getColumn(): Column + { + return $this->column; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Action/RenameColumn.php b/vendor/robmorgan/phinx/src/Phinx/Db/Action/RenameColumn.php new file mode 100644 index 0000000..0f18395 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Action/RenameColumn.php @@ -0,0 +1,79 @@ +newName = $newName; + $this->column = $column; + } + + /** + * Creates a new RenameColumn object after building the passed + * arguments + * + * @param \Phinx\Db\Table\Table $table The table where the column is + * @param string $columnName The name of the column to be changed + * @param string $newName The new name for the column + * @return static + */ + public static function build(Table $table, string $columnName, string $newName) + { + $column = new Column(); + $column->setName($columnName); + + return new static($table, $column, $newName); + } + + /** + * Returns the column to be changed + * + * @return \Phinx\Db\Table\Column + */ + public function getColumn(): Column + { + return $this->column; + } + + /** + * Returns the new name for the column + * + * @return string + */ + public function getNewName(): string + { + return $this->newName; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Action/RenameTable.php b/vendor/robmorgan/phinx/src/Phinx/Db/Action/RenameTable.php new file mode 100644 index 0000000..dd9652d --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Action/RenameTable.php @@ -0,0 +1,42 @@ +newName = $newName; + } + + /** + * Return the new name for the table + * + * @return string + */ + public function getNewName(): string + { + return $this->newName; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AbstractAdapter.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AbstractAdapter.php new file mode 100644 index 0000000..3e369fd --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AbstractAdapter.php @@ -0,0 +1,412 @@ + + */ + protected $options = []; + + /** + * @var \Symfony\Component\Console\Input\InputInterface|null + */ + protected $input; + + /** + * @var \Symfony\Component\Console\Output\OutputInterface + */ + protected $output; + + /** + * @var string[] + */ + protected $createdTables = []; + + /** + * @var string + */ + protected $schemaTableName = 'phinxlog'; + + /** + * @var array + */ + protected $dataDomain = []; + + /** + * Class Constructor. + * + * @param array $options Options + * @param \Symfony\Component\Console\Input\InputInterface|null $input Input Interface + * @param \Symfony\Component\Console\Output\OutputInterface|null $output Output Interface + */ + public function __construct(array $options, ?InputInterface $input = null, ?OutputInterface $output = null) + { + $this->setOptions($options); + if ($input !== null) { + $this->setInput($input); + } + if ($output !== null) { + $this->setOutput($output); + } + } + + /** + * @inheritDoc + */ + public function setOptions(array $options): AdapterInterface + { + $this->options = $options; + + if (isset($options['default_migration_table'])) { + trigger_error('The default_migration_table setting for adapter has been deprecated since 0.13.0. Use `migration_table` instead.', E_USER_DEPRECATED); + if (!isset($options['migration_table'])) { + $options['migration_table'] = $options['default_migration_table']; + } + } + + if (isset($options['migration_table'])) { + $this->setSchemaTableName($options['migration_table']); + } + + if (isset($options['data_domain'])) { + $this->setDataDomain($options['data_domain']); + } + + return $this; + } + + /** + * @inheritDoc + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * @inheritDoc + */ + public function hasOption(string $name): bool + { + return isset($this->options[$name]); + } + + /** + * @inheritDoc + */ + public function getOption(string $name) + { + if (!$this->hasOption($name)) { + return null; + } + + return $this->options[$name]; + } + + /** + * @inheritDoc + */ + public function setInput(InputInterface $input): AdapterInterface + { + $this->input = $input; + + return $this; + } + + /** + * @inheritDoc + */ + public function getInput(): ?InputInterface + { + return $this->input; + } + + /** + * @inheritDoc + */ + public function setOutput(OutputInterface $output): AdapterInterface + { + $this->output = $output; + + return $this; + } + + /** + * @inheritDoc + */ + public function getOutput(): OutputInterface + { + if ($this->output === null) { + $output = new NullOutput(); + $this->setOutput($output); + } + + return $this->output; + } + + /** + * @inheritDoc + * @return array + */ + public function getVersions(): array + { + $rows = $this->getVersionLog(); + + return array_keys($rows); + } + + /** + * Gets the schema table name. + * + * @return string + */ + public function getSchemaTableName(): string + { + return $this->schemaTableName; + } + + /** + * Sets the schema table name. + * + * @param string $schemaTableName Schema Table Name + * @return $this + */ + public function setSchemaTableName(string $schemaTableName) + { + $this->schemaTableName = $schemaTableName; + + return $this; + } + + /** + * Gets the data domain. + * + * @return array + */ + public function getDataDomain(): array + { + return $this->dataDomain; + } + + /** + * Sets the data domain. + * + * @param array $dataDomain Array for the data domain + * @return $this + */ + public function setDataDomain(array $dataDomain) + { + $this->dataDomain = []; + + // Iterate over data domain field definitions and perform initial and + // simple normalization. We make sure the definition as a base 'type' + // and it is compatible with the base Phinx types. + foreach ($dataDomain as $type => $options) { + if (!isset($options['type'])) { + throw new \InvalidArgumentException(sprintf( + 'You must specify a type for data domain type "%s".', + $type + )); + } + + // Replace type if it's the name of a Phinx constant + if (defined('static::' . $options['type'])) { + $options['type'] = constant('static::' . $options['type']); + } + + if (!in_array($options['type'], $this->getColumnTypes(), true)) { + throw new \InvalidArgumentException(sprintf( + 'An invalid column type "%s" was specified for data domain type "%s".', + $options['type'], + $type + )); + } + + $internal_type = $options['type']; + unset($options['type']); + + // Do a simple replacement for the 'length' / 'limit' option and + // detect hinting values for 'limit'. + if (isset($options['length'])) { + $options['limit'] = $options['length']; + unset($options['length']); + } + + if (isset($options['limit']) && !is_numeric($options['limit'])) { + if (!defined('static::' . $options['limit'])) { + throw new \InvalidArgumentException(sprintf( + 'An invalid limit value "%s" was specified for data domain type "%s".', + $options['limit'], + $type + )); + } + + $options['limit'] = constant('static::' . $options['limit']); + } + + // Save the data domain types in a more suitable format + $this->dataDomain[$type] = [ + 'type' => $internal_type, + 'options' => $options, + ]; + } + + return $this; + } + + /** + * @inheritdoc + */ + public function getColumnForType(string $columnName, string $type, array $options): Column + { + $column = new Column(); + $column->setName($columnName); + + if (array_key_exists($type, $this->getDataDomain())) { + $column->setType($this->dataDomain[$type]['type']); + $column->setOptions($this->dataDomain[$type]['options']); + } else { + $column->setType($type); + } + + $column->setOptions($options); + + return $column; + } + + /** + * @inheritDoc + * @throws \InvalidArgumentException + * @return void + */ + public function createSchemaTable(): void + { + try { + $options = [ + 'id' => false, + 'primary_key' => 'version', + ]; + + $table = new Table($this->getSchemaTableName(), $options, $this); + $table->addColumn('version', 'biginteger', ['null' => false]) + ->addColumn('migration_name', 'string', ['limit' => 100, 'default' => null, 'null' => true]) + ->addColumn('start_time', 'timestamp', ['default' => null, 'null' => true]) + ->addColumn('end_time', 'timestamp', ['default' => null, 'null' => true]) + ->addColumn('breakpoint', 'boolean', ['default' => false, 'null' => false]) + ->save(); + } catch (Exception $exception) { + throw new InvalidArgumentException( + 'There was a problem creating the schema table: ' . $exception->getMessage(), + (int)$exception->getCode(), + $exception + ); + } + } + + /** + * @inheritDoc + */ + public function getAdapterType(): string + { + return $this->getOption('adapter'); + } + + /** + * @inheritDoc + */ + public function isValidColumnType(Column $column): bool + { + return $column->getType() instanceof Literal || in_array($column->getType(), $this->getColumnTypes(), true); + } + + /** + * Determines if instead of executing queries a dump to standard output is needed + * + * @return bool + */ + public function isDryRunEnabled(): bool + { + /** @var \Symfony\Component\Console\Input\InputInterface|null $input */ + $input = $this->getInput(); + + return $input && $input->hasOption('dry-run') ? (bool)$input->getOption('dry-run') : false; + } + + /** + * Adds user-created tables (e.g. not phinxlog) to a cached list + * + * @param string $tableName The name of the table + * @return void + */ + protected function addCreatedTable(string $tableName): void + { + $tableName = $this->quoteTableName($tableName); + if (substr_compare($tableName, 'phinxlog', -strlen('phinxlog')) !== 0) { + $this->createdTables[] = $tableName; + } + } + + /** + * Updates the name of the cached table + * + * @param string $tableName Original name of the table + * @param string $newTableName New name of the table + * @return void + */ + protected function updateCreatedTableName(string $tableName, string $newTableName): void + { + $tableName = $this->quoteTableName($tableName); + $newTableName = $this->quoteTableName($newTableName); + $key = array_search($tableName, $this->createdTables, true); + if ($key !== false) { + $this->createdTables[$key] = $newTableName; + } + } + + /** + * Removes table from the cached created list + * + * @param string $tableName The name of the table + * @return void + */ + protected function removeCreatedTable(string $tableName): void + { + $tableName = $this->quoteTableName($tableName); + $key = array_search($tableName, $this->createdTables, true); + if ($key !== false) { + unset($this->createdTables[$key]); + } + } + + /** + * Check if the table is in the cached list of created tables + * + * @param string $tableName The name of the table + * @return bool + */ + protected function hasCreatedTable(string $tableName): bool + { + $tableName = $this->quoteTableName($tableName); + + return in_array($tableName, $this->createdTables, true); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterFactory.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterFactory.php new file mode 100644 index 0000000..3843691 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterFactory.php @@ -0,0 +1,172 @@ + + */ +class AdapterFactory +{ + /** + * @var \Phinx\Db\Adapter\AdapterFactory|null + */ + protected static $instance; + + /** + * Get the factory singleton instance. + * + * @return \Phinx\Db\Adapter\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 + * @phpstan-var array> + */ + protected $adapters = [ + '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 = [ + 'prefix' => 'Phinx\Db\Adapter\TablePrefixAdapter', + 'proxy' => 'Phinx\Db\Adapter\ProxyAdapter', + 'timed' => 'Phinx\Db\Adapter\TimedOutputAdapter', + ]; + + /** + * Register an adapter class with a given name. + * + * @param string $name Name + * @param object|string $class Class + * @throws \RuntimeException + * @return $this + */ + public function registerAdapter(string $name, $class) + { + if (!is_subclass_of($class, 'Phinx\Db\Adapter\AdapterInterface')) { + throw new RuntimeException(sprintf( + 'Adapter class "%s" must implement Phinx\\Db\\Adapter\\AdapterInterface', + is_string($class) ? $class : get_class($class) + )); + } + $this->adapters[$name] = $class; + + return $this; + } + + /** + * Get an adapter class by name. + * + * @param string $name Name + * @throws \RuntimeException + * @return object|string + * @phpstan-return object|class-string<\Phinx\Db\Adapter\AdapterInterface> + */ + protected function getClass(string $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 Name + * @param array $options Options + * @return \Phinx\Db\Adapter\AdapterInterface + */ + public function getAdapter(string $name, array $options): AdapterInterface + { + $class = $this->getClass($name); + + return new $class($options); + } + + /** + * Add or replace a wrapper with a fully qualified class name. + * + * @param string $name Name + * @param object|string $class Class + * @throws \RuntimeException + * @return $this + */ + public function registerWrapper(string $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', + is_string($class) ? $class : get_class($class) + )); + } + $this->wrappers[$name] = $class; + + return $this; + } + + /** + * Get a wrapper class by name. + * + * @param string $name Name + * @throws \RuntimeException + * @return \Phinx\Db\Adapter\WrapperInterface|string + */ + protected function getWrapperClass(string $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 Name + * @param \Phinx\Db\Adapter\AdapterInterface $adapter Adapter + * @return \Phinx\Db\Adapter\AdapterWrapper + */ + public function getWrapper(string $name, AdapterInterface $adapter): AdapterWrapper + { + $class = $this->getWrapperClass($name); + + return new $class($adapter); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterInterface.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterInterface.php new file mode 100644 index 0000000..65d7cc3 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterInterface.php @@ -0,0 +1,505 @@ + + * @method \PDO getConnection() + */ +interface AdapterInterface +{ + public const PHINX_TYPE_STRING = 'string'; + public const PHINX_TYPE_CHAR = 'char'; + public const PHINX_TYPE_TEXT = 'text'; + public const PHINX_TYPE_INTEGER = 'integer'; + public const PHINX_TYPE_TINY_INTEGER = 'tinyinteger'; + public const PHINX_TYPE_SMALL_INTEGER = 'smallinteger'; + public const PHINX_TYPE_BIG_INTEGER = 'biginteger'; + public const PHINX_TYPE_BIT = 'bit'; + public const PHINX_TYPE_FLOAT = 'float'; + public const PHINX_TYPE_DECIMAL = 'decimal'; + public const PHINX_TYPE_DOUBLE = 'double'; + public const PHINX_TYPE_DATETIME = 'datetime'; + public const PHINX_TYPE_TIMESTAMP = 'timestamp'; + public const PHINX_TYPE_TIME = 'time'; + public const PHINX_TYPE_DATE = 'date'; + public const PHINX_TYPE_BINARY = 'binary'; + public const PHINX_TYPE_VARBINARY = 'varbinary'; + public const PHINX_TYPE_BINARYUUID = 'binaryuuid'; + public const PHINX_TYPE_BLOB = 'blob'; + public const PHINX_TYPE_TINYBLOB = 'tinyblob'; // Specific to Mysql. + public const PHINX_TYPE_MEDIUMBLOB = 'mediumblob'; // Specific to Mysql + public const PHINX_TYPE_LONGBLOB = 'longblob'; // Specific to Mysql + public const PHINX_TYPE_BOOLEAN = 'boolean'; + public const PHINX_TYPE_JSON = 'json'; + public const PHINX_TYPE_JSONB = 'jsonb'; + public const PHINX_TYPE_UUID = 'uuid'; + public const PHINX_TYPE_FILESTREAM = 'filestream'; + + // Geospatial database types + public const PHINX_TYPE_GEOMETRY = 'geometry'; + public const PHINX_TYPE_GEOGRAPHY = 'geography'; + public const PHINX_TYPE_POINT = 'point'; + public const PHINX_TYPE_LINESTRING = 'linestring'; + public const PHINX_TYPE_POLYGON = 'polygon'; + + public const PHINX_TYPES_GEOSPATIAL = [ + self::PHINX_TYPE_GEOMETRY, + self::PHINX_TYPE_POINT, + self::PHINX_TYPE_LINESTRING, + self::PHINX_TYPE_POLYGON, + ]; + + // only for mysql so far + public const PHINX_TYPE_MEDIUM_INTEGER = 'mediuminteger'; + public const PHINX_TYPE_ENUM = 'enum'; + public const PHINX_TYPE_SET = 'set'; + public const PHINX_TYPE_YEAR = 'year'; + + // only for postgresql so far + public const PHINX_TYPE_CIDR = 'cidr'; + public const PHINX_TYPE_INET = 'inet'; + public const PHINX_TYPE_MACADDR = 'macaddr'; + public const PHINX_TYPE_INTERVAL = 'interval'; + + /** + * Get all migrated version numbers. + * + * @return array + */ + public function getVersions(): array; + + /** + * Get all migration log entries, indexed by version creation time and sorted ascendingly by the configuration's + * version order option + * + * @return array + */ + public function getVersionLog(): array; + + /** + * Set adapter configuration options. + * + * @param array $options Options + * @return $this + */ + public function setOptions(array $options); + + /** + * Get all adapter options. + * + * @return array + */ + public function getOptions(): array; + + /** + * Check if an option has been set. + * + * @param string $name Name + * @return bool + */ + public function hasOption(string $name): bool; + + /** + * Get a single adapter option, or null if the option does not exist. + * + * @param string $name Name + * @return mixed + */ + public function getOption(string $name); + + /** + * Sets the console input. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @return $this + */ + public function setInput(InputInterface $input); + + /** + * Gets the console input. + * + * @return \Symfony\Component\Console\Input\InputInterface|null + */ + public function getInput(): ?InputInterface; + + /** + * Sets the console output. + * + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return $this + */ + public function setOutput(OutputInterface $output); + + /** + * Gets the console output. + * + * @return \Symfony\Component\Console\Output\OutputInterface + */ + public function getOutput(): OutputInterface; + + /** + * Returns a new Phinx\Db\Table\Column using the existent data domain. + * + * @param string $columnName The desired column name + * @param string $type The type for the column. Can be a data domain type. + * @param array $options Options array + * @return \Phinx\Db\Table\Column + */ + public function getColumnForType(string $columnName, string $type, array $options): Column; + + /** + * Records a migration being run. + * + * @param \Phinx\Migration\MigrationInterface $migration Migration + * @param string $direction Direction + * @param string $startTime Start Time + * @param string $endTime End Time + * @return $this + */ + public function migrated(MigrationInterface $migration, string $direction, string $startTime, string $endTime); + + /** + * Toggle a migration breakpoint. + * + * @param \Phinx\Migration\MigrationInterface $migration Migration + * @return $this + */ + public function toggleBreakpoint(MigrationInterface $migration); + + /** + * Reset all migration breakpoints. + * + * @return int The number of breakpoints reset + */ + public function resetAllBreakpoints(): int; + + /** + * Set a migration breakpoint. + * + * @param \Phinx\Migration\MigrationInterface $migration The migration target for the breakpoint set + * @return $this + */ + public function setBreakpoint(MigrationInterface $migration); + + /** + * Unset a migration breakpoint. + * + * @param \Phinx\Migration\MigrationInterface $migration The migration target for the breakpoint unset + * @return $this + */ + public function unsetBreakpoint(MigrationInterface $migration); + + /** + * Creates the schema table. + * + * @return void + */ + public function createSchemaTable(): void; + + /** + * Returns the adapter type. + * + * @return string + */ + public function getAdapterType(): string; + + /** + * Initializes the database connection. + * + * @throws \RuntimeException When the requested database driver is not installed. + * @return void + */ + public function connect(): void; + + /** + * Closes the database connection. + * + * @return void + */ + public function disconnect(): void; + + /** + * Does the adapter support transactions? + * + * @return bool + */ + public function hasTransactions(): bool; + + /** + * Begin a transaction. + * + * @return void + */ + public function beginTransaction(): void; + + /** + * Commit a transaction. + * + * @return void + */ + public function commitTransaction(): void; + + /** + * Rollback a transaction. + * + * @return void + */ + public function rollbackTransaction(): void; + + /** + * Executes a SQL statement and returns the number of affected rows. + * + * @param string $sql SQL + * @param array $params parameters to use for prepared query + * @return int + */ + public function execute(string $sql, array $params = []): int; + + /** + * Executes a list of migration actions for the given table + * + * @param \Phinx\Db\Table\Table $table The table to execute the actions for + * @param \Phinx\Db\Action\Action[] $actions The table to execute the actions for + * @return void + */ + public function executeActions(Table $table, array $actions): void; + + /** + * Returns a new Query object + * + * @return \Cake\Database\Query + */ + public function getQueryBuilder(): Query; + + /** + * Executes a SQL statement. + * + * The return type depends on the underlying adapter being used. + * + * @param string $sql SQL + * @param array $params parameters to use for prepared query + * @return mixed + */ + public function query(string $sql, array $params = []); + + /** + * Executes a query and returns only one row as an array. + * + * @param string $sql SQL + * @return array|false + */ + public function fetchRow(string $sql); + + /** + * Executes a query and returns an array of rows. + * + * @param string $sql SQL + * @return array + */ + public function fetchAll(string $sql): array; + + /** + * Inserts data into a table. + * + * @param \Phinx\Db\Table\Table $table Table where to insert data + * @param array $row Row + * @return void + */ + public function insert(Table $table, array $row): void; + + /** + * Inserts data into a table in a bulk. + * + * @param \Phinx\Db\Table\Table $table Table where to insert data + * @param array $rows Rows + * @return void + */ + public function bulkinsert(Table $table, array $rows): void; + + /** + * Quotes a table name for use in a query. + * + * @param string $tableName Table name + * @return string + */ + public function quoteTableName(string $tableName): string; + + /** + * Quotes a column name for use in a query. + * + * @param string $columnName Table name + * @return string + */ + public function quoteColumnName(string $columnName): string; + + /** + * Checks to see if a table exists. + * + * @param string $tableName Table name + * @return bool + */ + public function hasTable(string $tableName): bool; + + /** + * Creates the specified database table. + * + * @param \Phinx\Db\Table\Table $table Table + * @param \Phinx\Db\Table\Column[] $columns List of columns in the table + * @param \Phinx\Db\Table\Index[] $indexes List of indexes for the table + * @return void + */ + public function createTable(Table $table, array $columns = [], array $indexes = []): void; + + /** + * Truncates the specified table + * + * @param string $tableName Table name + * @return void + */ + public function truncateTable(string $tableName): void; + + /** + * Returns table columns + * + * @param string $tableName Table name + * @return \Phinx\Db\Table\Column[] + */ + public function getColumns(string $tableName): array; + + /** + * Checks to see if a column exists. + * + * @param string $tableName Table name + * @param string $columnName Column name + * @return bool + */ + public function hasColumn(string $tableName, string $columnName): bool; + + /** + * Checks to see if an index exists. + * + * @param string $tableName Table name + * @param string|string[] $columns Column(s) + * @return bool + */ + public function hasIndex(string $tableName, $columns): bool; + + /** + * Checks to see if an index specified by name exists. + * + * @param string $tableName Table name + * @param string $indexName Index name + * @return bool + */ + public function hasIndexByName(string $tableName, string $indexName): bool; + + /** + * Checks to see if the specified primary key exists. + * + * @param string $tableName Table name + * @param string|string[] $columns Column(s) + * @param string|null $constraint Constraint name + * @return bool + */ + public function hasPrimaryKey(string $tableName, $columns, ?string $constraint = null): bool; + + /** + * Checks to see if a foreign key exists. + * + * @param string $tableName Table name + * @param string|string[] $columns Column(s) + * @param string|null $constraint Constraint name + * @return bool + */ + public function hasForeignKey(string $tableName, $columns, ?string $constraint = null): bool; + + /** + * Returns an array of the supported Phinx column types. + * + * @return string[] + */ + public function getColumnTypes(): array; + + /** + * Checks that the given column is of a supported type. + * + * @param \Phinx\Db\Table\Column $column Column + * @return bool + */ + public function isValidColumnType(Column $column): bool; + + /** + * Converts the Phinx logical type to the adapter's SQL type. + * + * @param \Phinx\Util\Literal|string $type Type + * @param int|null $limit Limit + * @return array + */ + public function getSqlType($type, ?int $limit = null): array; + + /** + * Creates a new database. + * + * @param string $name Database Name + * @param array $options Options + * @return void + */ + public function createDatabase(string $name, array $options = []): void; + + /** + * Checks to see if a database exists. + * + * @param string $name Database Name + * @return bool + */ + public function hasDatabase(string $name): bool; + + /** + * Drops the specified database. + * + * @param string $name Database Name + * @return void + */ + public function dropDatabase(string $name): void; + + /** + * Creates the specified schema or throws an exception + * if there is no support for it. + * + * @param string $schemaName Schema Name + * @return void + */ + public function createSchema(string $schemaName = 'public'): void; + + /** + * Drops the specified schema table or throws an exception + * if there is no support for it. + * + * @param string $schemaName Schema name + * @return void + */ + public function dropSchema(string $schemaName): void; + + /** + * Cast a value to a boolean appropriate for the adapter. + * + * @param mixed $value The value to be cast + * @return mixed + */ + public function castToBool($value); +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php new file mode 100644 index 0000000..7df50a8 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php @@ -0,0 +1,487 @@ + + */ +abstract class AdapterWrapper implements AdapterInterface, WrapperInterface +{ + /** + * @var \Phinx\Db\Adapter\AdapterInterface + */ + protected $adapter; + + /** + * @inheritDoc + */ + public function __construct(AdapterInterface $adapter) + { + $this->setAdapter($adapter); + } + + /** + * @inheritDoc + */ + public function setAdapter(AdapterInterface $adapter): AdapterInterface + { + $this->adapter = $adapter; + + return $this; + } + + /** + * @inheritDoc + */ + public function getAdapter(): AdapterInterface + { + return $this->adapter; + } + + /** + * @inheritDoc + */ + public function setOptions(array $options): AdapterInterface + { + $this->adapter->setOptions($options); + + return $this; + } + + /** + * @inheritDoc + */ + public function getOptions(): array + { + return $this->adapter->getOptions(); + } + + /** + * @inheritDoc + */ + public function hasOption(string $name): bool + { + return $this->adapter->hasOption($name); + } + + /** + * @inheritDoc + */ + public function getOption(string $name) + { + return $this->adapter->getOption($name); + } + + /** + * @inheritDoc + */ + public function setInput(InputInterface $input): AdapterInterface + { + $this->adapter->setInput($input); + + return $this; + } + + /** + * @inheritDoc + */ + public function getInput(): InputInterface + { + return $this->adapter->getInput(); + } + + /** + * @inheritDoc + */ + public function setOutput(OutputInterface $output): AdapterInterface + { + $this->adapter->setOutput($output); + + return $this; + } + + /** + * @inheritDoc + */ + public function getOutput(): OutputInterface + { + return $this->adapter->getOutput(); + } + + /** + * @inheritDoc + */ + public function getColumnForType(string $columnName, string $type, array $options): Column + { + return $this->adapter->getColumnForType($columnName, $type, $options); + } + + /** + * @inheritDoc + */ + public function connect(): void + { + $this->getAdapter()->connect(); + } + + /** + * @inheritDoc + */ + public function disconnect(): void + { + $this->getAdapter()->disconnect(); + } + + /** + * @inheritDoc + */ + public function execute(string $sql, array $params = []): int + { + return $this->getAdapter()->execute($sql, $params); + } + + /** + * @inheritDoc + */ + public function query(string $sql, array $params = []) + { + return $this->getAdapter()->query($sql, $params); + } + + /** + * @inheritDoc + */ + public function insert(Table $table, array $row): void + { + $this->getAdapter()->insert($table, $row); + } + + /** + * @inheritDoc + */ + public function bulkinsert(Table $table, array $rows): void + { + $this->getAdapter()->bulkinsert($table, $rows); + } + + /** + * @inheritDoc + */ + public function fetchRow(string $sql) + { + return $this->getAdapter()->fetchRow($sql); + } + + /** + * @inheritDoc + */ + public function fetchAll(string $sql): array + { + return $this->getAdapter()->fetchAll($sql); + } + + /** + * @inheritDoc + */ + public function getVersions(): array + { + return $this->getAdapter()->getVersions(); + } + + /** + * @inheritDoc + */ + public function getVersionLog(): array + { + return $this->getAdapter()->getVersionLog(); + } + + /** + * @inheritDoc + */ + public function migrated(MigrationInterface $migration, string $direction, string $startTime, string $endTime): AdapterInterface + { + $this->getAdapter()->migrated($migration, $direction, $startTime, $endTime); + + return $this; + } + + /** + * @inheritDoc + */ + public function toggleBreakpoint(MigrationInterface $migration): AdapterInterface + { + $this->getAdapter()->toggleBreakpoint($migration); + + return $this; + } + + /** + * @inheritDoc + */ + public function resetAllBreakpoints(): int + { + return $this->getAdapter()->resetAllBreakpoints(); + } + + /** + * @inheritDoc + */ + public function setBreakpoint(MigrationInterface $migration): AdapterInterface + { + $this->getAdapter()->setBreakpoint($migration); + + return $this; + } + + /** + * @inheritDoc + */ + public function unsetBreakpoint(MigrationInterface $migration): AdapterInterface + { + $this->getAdapter()->unsetBreakpoint($migration); + + return $this; + } + + /** + * @inheritDoc + */ + public function createSchemaTable(): void + { + $this->getAdapter()->createSchemaTable(); + } + + /** + * @inheritDoc + */ + public function getColumnTypes(): array + { + return $this->getAdapter()->getColumnTypes(); + } + + /** + * @inheritDoc + */ + public function isValidColumnType(Column $column): bool + { + return $this->getAdapter()->isValidColumnType($column); + } + + /** + * @inheritDoc + */ + public function hasTransactions(): bool + { + return $this->getAdapter()->hasTransactions(); + } + + /** + * @inheritDoc + */ + public function beginTransaction(): void + { + $this->getAdapter()->beginTransaction(); + } + + /** + * @inheritDoc + */ + public function commitTransaction(): void + { + $this->getAdapter()->commitTransaction(); + } + + /** + * @inheritDoc + */ + public function rollbackTransaction(): void + { + $this->getAdapter()->rollbackTransaction(); + } + + /** + * @inheritDoc + */ + public function quoteTableName(string $tableName): string + { + return $this->getAdapter()->quoteTableName($tableName); + } + + /** + * @inheritDoc + */ + public function quoteColumnName(string $columnName): string + { + return $this->getAdapter()->quoteColumnName($columnName); + } + + /** + * @inheritDoc + */ + public function hasTable(string $tableName): bool + { + return $this->getAdapter()->hasTable($tableName); + } + + /** + * @inheritDoc + */ + public function createTable(Table $table, array $columns = [], array $indexes = []): void + { + $this->getAdapter()->createTable($table, $columns, $indexes); + } + + /** + * @inheritDoc + */ + public function getColumns(string $tableName): array + { + return $this->getAdapter()->getColumns($tableName); + } + + /** + * @inheritDoc + */ + public function hasColumn(string $tableName, string $columnName): bool + { + return $this->getAdapter()->hasColumn($tableName, $columnName); + } + + /** + * @inheritDoc + */ + public function hasIndex(string $tableName, $columns): bool + { + return $this->getAdapter()->hasIndex($tableName, $columns); + } + + /** + * @inheritDoc + */ + public function hasIndexByName(string $tableName, string $indexName): bool + { + return $this->getAdapter()->hasIndexByName($tableName, $indexName); + } + + /** + * @inheritDoc + */ + public function hasPrimaryKey(string $tableName, $columns, ?string $constraint = null): bool + { + return $this->getAdapter()->hasPrimaryKey($tableName, $columns, $constraint); + } + + /** + * @inheritDoc + */ + public function hasForeignKey(string $tableName, $columns, ?string $constraint = null): bool + { + return $this->getAdapter()->hasForeignKey($tableName, $columns, $constraint); + } + + /** + * @inheritDoc + */ + public function getSqlType($type, ?int $limit = null): array + { + return $this->getAdapter()->getSqlType($type, $limit); + } + + /** + * @inheritDoc + */ + public function createDatabase(string $name, array $options = []): void + { + $this->getAdapter()->createDatabase($name, $options); + } + + /** + * @inheritDoc + */ + public function hasDatabase(string $name): bool + { + return $this->getAdapter()->hasDatabase($name); + } + + /** + * @inheritDoc + */ + public function dropDatabase(string $name): void + { + $this->getAdapter()->dropDatabase($name); + } + + /** + * @inheritDoc + */ + public function createSchema(string $schemaName = 'public'): void + { + $this->getAdapter()->createSchema($schemaName); + } + + /** + * @inheritDoc + */ + public function dropSchema(string $schemaName): void + { + $this->getAdapter()->dropSchema($schemaName); + } + + /** + * @inheritDoc + */ + public function truncateTable(string $tableName): void + { + $this->getAdapter()->truncateTable($tableName); + } + + /** + * @inheritDoc + */ + public function castToBool($value) + { + return $this->getAdapter()->castToBool($value); + } + + /** + * @return \PDO + */ + public function getConnection() + { + return $this->getAdapter()->getConnection(); + } + + /** + * @inheritDoc + */ + public function executeActions(Table $table, array $actions): void + { + $this->getAdapter()->executeActions($table, $actions); + } + + /** + * @inheritDoc + */ + public function getQueryBuilder(): Query + { + return $this->getAdapter()->getQueryBuilder(); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/DirectActionInterface.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/DirectActionInterface.php new file mode 100644 index 0000000..64b30f6 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/DirectActionInterface.php @@ -0,0 +1,139 @@ + + */ +class MysqlAdapter extends PdoAdapter +{ + /** + * @var string[] + */ + protected static $specificColumnTypes = [ + self::PHINX_TYPE_ENUM, + self::PHINX_TYPE_SET, + self::PHINX_TYPE_YEAR, + self::PHINX_TYPE_JSON, + self::PHINX_TYPE_BINARYUUID, + self::PHINX_TYPE_TINYBLOB, + self::PHINX_TYPE_MEDIUMBLOB, + self::PHINX_TYPE_LONGBLOB, + self::PHINX_TYPE_MEDIUM_INTEGER, + ]; + + /** + * @var bool[] + */ + protected $signedColumnTypes = [ + self::PHINX_TYPE_INTEGER => true, + self::PHINX_TYPE_TINY_INTEGER => true, + self::PHINX_TYPE_SMALL_INTEGER => true, + self::PHINX_TYPE_MEDIUM_INTEGER => true, + self::PHINX_TYPE_BIG_INTEGER => true, + self::PHINX_TYPE_FLOAT => true, + self::PHINX_TYPE_DECIMAL => true, + self::PHINX_TYPE_DOUBLE => true, + self::PHINX_TYPE_BOOLEAN => true, + ]; + + // These constants roughly correspond to the maximum allowed value for each field, + // except for the `_LONG` and `_BIG` variants, which are maxed at 32-bit + // PHP_INT_MAX value. The `INT_REGULAR` field is just arbitrarily half of INT_BIG + // as its actual value is its regular value is larger than PHP_INT_MAX. We do this + // to keep consistent the type hints for getSqlType and Column::$limit being integers. + public const TEXT_TINY = 255; + public const TEXT_SMALL = 255; /* deprecated, alias of TEXT_TINY */ + public const TEXT_REGULAR = 65535; + public const TEXT_MEDIUM = 16777215; + public const TEXT_LONG = 2147483647; + + // According to https://dev.mysql.com/doc/refman/5.0/en/blob.html BLOB sizes are the same as TEXT + public const BLOB_TINY = 255; + public const BLOB_SMALL = 255; /* deprecated, alias of BLOB_TINY */ + public const BLOB_REGULAR = 65535; + public const BLOB_MEDIUM = 16777215; + public const BLOB_LONG = 2147483647; + + public const INT_TINY = 255; + public const INT_SMALL = 65535; + public const INT_MEDIUM = 16777215; + public const INT_REGULAR = 1073741823; + public const INT_BIG = 2147483647; + + public const INT_DISPLAY_TINY = 4; + public const INT_DISPLAY_SMALL = 6; + public const INT_DISPLAY_MEDIUM = 8; + public const INT_DISPLAY_REGULAR = 11; + public const INT_DISPLAY_BIG = 20; + + public const BIT = 64; + + public const TYPE_YEAR = 'year'; + + public const FIRST = 'FIRST'; + + /** + * {@inheritDoc} + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @return void + */ + public function connect(): void + { + if ($this->connection === null) { + 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 + } + + $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 = []; + + // use custom data fetch mode + if (!empty($options['fetch_mode'])) { + $driverOptions[PDO::ATTR_DEFAULT_FETCH_MODE] = constant('\PDO::FETCH_' . strtoupper($options['fetch_mode'])); + } + + // pass \PDO::ATTR_PERSISTENT to driver options instead of useless setting it after instantiation + if (isset($options['attr_persistent'])) { + $driverOptions[PDO::ATTR_PERSISTENT] = $options['attr_persistent']; + } + + // support arbitrary \PDO::MYSQL_ATTR_* driver options and pass them to PDO + // https://php.net/manual/en/ref.pdo-mysql.php#pdo-mysql.constants + foreach ($options as $key => $option) { + if (strpos($key, 'mysql_attr_') === 0) { + $pdoConstant = '\PDO::' . strtoupper($key); + if (!defined($pdoConstant)) { + throw new \UnexpectedValueException('Invalid PDO attribute: ' . $key . ' (' . $pdoConstant . ')'); + } + $driverOptions[constant($pdoConstant)] = $option; + } + } + + $db = $this->createPdoConnection($dsn, $options['user'] ?? null, $options['pass'] ?? null, $driverOptions); + + $this->setConnection($db); + } + } + + /** + * @inheritDoc + */ + public function disconnect(): void + { + $this->connection = null; + } + + /** + * @inheritDoc + */ + public function hasTransactions(): bool + { + return true; + } + + /** + * @inheritDoc + */ + public function beginTransaction(): void + { + $this->execute('START TRANSACTION'); + } + + /** + * @inheritDoc + */ + public function commitTransaction(): void + { + $this->execute('COMMIT'); + } + + /** + * @inheritDoc + */ + public function rollbackTransaction(): void + { + $this->execute('ROLLBACK'); + } + + /** + * @inheritDoc + */ + public function quoteTableName(string $tableName): string + { + return str_replace('.', '`.`', $this->quoteColumnName($tableName)); + } + + /** + * @inheritDoc + */ + public function quoteColumnName(string $columnName): string + { + return '`' . str_replace('`', '``', $columnName) . '`'; + } + + /** + * @inheritDoc + */ + public function hasTable(string $tableName): bool + { + if ($this->hasCreatedTable($tableName)) { + return true; + } + + if (strpos($tableName, '.') !== false) { + [$schema, $table] = explode('.', $tableName); + $exists = $this->hasTableWithSchema($schema, $table); + // Only break here on success, because it is possible for table names to contain a dot. + if ($exists) { + return true; + } + } + + $options = $this->getOptions(); + + return $this->hasTableWithSchema($options['name'], $tableName); + } + + /** + * @param string $schema The table schema + * @param string $tableName The table name + * @return bool + */ + protected function hasTableWithSchema(string $schema, string $tableName): bool + { + $result = $this->fetchRow(sprintf( + "SELECT TABLE_NAME + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'", + $schema, + $tableName + )); + + return !empty($result); + } + + /** + * @inheritDoc + */ + public function createTable(Table $table, array $columns = [], array $indexes = []): void + { + // This method is based on the MySQL docs here: https://dev.mysql.com/doc/refman/5.1/en/create-index.html + $defaultOptions = [ + 'engine' => 'InnoDB', + 'collation' => 'utf8mb4_unicode_ci', + ]; + + $options = array_merge( + $defaultOptions, + array_intersect_key($this->getOptions(), $defaultOptions), + $table->getOptions() + ); + + // Add the default primary key + 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') + ->setOptions([ + 'signed' => $options['signed'] ?? !FeatureFlags::$unsignedPrimaryKeys, + 'identity' => true, + ]); + + if (isset($options['limit'])) { + $column->setLimit($options['limit']); + } + + array_unshift($columns, $column); + if (isset($options['primary_key']) && (array)$options['id'] !== (array)$options['primary_key']) { + throw new InvalidArgumentException('You cannot enable an auto incrementing ID field and a primary key'); + } + $options['primary_key'] = $options['id']; + } + + // open: 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'])); + } + + // set the table row format + if (isset($options['row_format'])) { + $optionsStr .= sprintf(' ROW_FORMAT=%s ', $options['row_format']); + } + + $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') + $sql .= implode(',', array_map([$this, 'quoteColumnName'], (array)$options['primary_key'])); + } + $sql .= ')'; + } else { + $sql = substr(rtrim($sql), 0, -1); // no primary keys + } + + // set the indexes + foreach ($indexes as $index) { + $sql .= ', ' . $this->getIndexSqlDefinition($index); + } + + $sql .= ') ' . $optionsStr; + $sql = rtrim($sql); + + // execute the sql + $this->execute($sql); + + $this->addCreatedTable($table->getName()); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getChangePrimaryKeyInstructions(Table $table, $newColumns): AlterInstructions + { + $instructions = new AlterInstructions(); + + // Drop the existing primary key + $primaryKey = $this->getPrimaryKey($table->getName()); + if (!empty($primaryKey['columns'])) { + $instructions->addAlter('DROP PRIMARY KEY'); + } + + // Add the primary key(s) + if (!empty($newColumns)) { + $sql = 'ADD PRIMARY KEY ('; + if (is_string($newColumns)) { // handle primary_key => 'id' + $sql .= $this->quoteColumnName($newColumns); + } elseif (is_array($newColumns)) { // handle primary_key => array('tag_id', 'resource_id') + $sql .= implode(',', array_map([$this, 'quoteColumnName'], $newColumns)); + } else { + throw new InvalidArgumentException(sprintf( + 'Invalid value for primary key: %s', + json_encode($newColumns) + )); + } + $sql .= ')'; + $instructions->addAlter($sql); + } + + return $instructions; + } + + /** + * @inheritDoc + */ + protected function getChangeCommentInstructions(Table $table, ?string $newComment): AlterInstructions + { + $instructions = new AlterInstructions(); + + // passing 'null' is to remove table comment + $newComment = $newComment ?? ''; + $sql = sprintf(' COMMENT=%s ', $this->getConnection()->quote($newComment)); + $instructions->addAlter($sql); + + return $instructions; + } + + /** + * @inheritDoc + */ + protected function getRenameTableInstructions(string $tableName, string $newTableName): AlterInstructions + { + $this->updateCreatedTableName($tableName, $newTableName); + $sql = sprintf( + 'RENAME TABLE %s TO %s', + $this->quoteTableName($tableName), + $this->quoteTableName($newTableName) + ); + + return new AlterInstructions([], [$sql]); + } + + /** + * @inheritDoc + */ + protected function getDropTableInstructions(string $tableName): AlterInstructions + { + $this->removeCreatedTable($tableName); + $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName)); + + return new AlterInstructions([], [$sql]); + } + + /** + * @inheritDoc + */ + public function truncateTable(string $tableName): void + { + $sql = sprintf( + 'TRUNCATE TABLE %s', + $this->quoteTableName($tableName) + ); + + $this->execute($sql); + } + + /** + * @inheritDoc + */ + public function getColumns(string $tableName): array + { + $columns = []; + $rows = $this->fetchAll(sprintf('SHOW FULL 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') + ->setType($phinxType['name']) + ->setSigned(strpos($columnInfo['Type'], 'unsigned') === false) + ->setLimit($phinxType['limit']) + ->setScale($phinxType['scale']) + ->setComment($columnInfo['Comment']); + + if ($columnInfo['Extra'] === 'auto_increment') { + $column->setIdentity(true); + } + + if (isset($phinxType['values'])) { + $column->setValues($phinxType['values']); + } + + $default = $columnInfo['Default']; + if ( + is_string($default) && + in_array( + $column->getType(), + array_merge( + static::PHINX_TYPES_GEOSPATIAL, + [static::PHINX_TYPE_BLOB, static::PHINX_TYPE_JSON, static::PHINX_TYPE_TEXT] + ) + ) + ) { + // The default that comes back from MySQL for these types prefixes the collation type and + // surrounds the value with escaped single quotes, for example "_utf8mbf4\'abc\'", and so + // this converts that then down to the default value of "abc" to correspond to what the user + // would have specified in a migration. + $default = preg_replace("/^_(?:[a-zA-Z0-9]+?)\\\'(.*)\\\'$/", '\1', $default); + } + $column->setDefault($default); + + $columns[] = $column; + } + + return $columns; + } + + /** + * @inheritDoc + */ + public function hasColumn(string $tableName, string $columnName): bool + { + $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; + } + + /** + * @inheritDoc + */ + protected function getAddColumnInstructions(Table $table, Column $column): AlterInstructions + { + $alter = sprintf( + 'ADD %s %s', + $this->quoteColumnName($column->getName()), + $this->getColumnSqlDefinition($column) + ); + + $alter .= $this->afterClause($column); + + return new AlterInstructions([$alter]); + } + + /** + * Exposes the MySQL syntax to arrange a column `FIRST`. + * + * @param \Phinx\Db\Table\Column $column The column being altered. + * @return string The appropriate SQL fragment. + */ + protected function afterClause(Column $column): string + { + $after = $column->getAfter(); + if (empty($after)) { + return ''; + } + + if ($after === self::FIRST) { + return ' FIRST'; + } + + return ' AFTER ' . $this->quoteColumnName($after); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getRenameColumnInstructions(string $tableName, string $columnName, string $newColumnName): AlterInstructions + { + $rows = $this->fetchAll(sprintf('SHOW FULL COLUMNS FROM %s', $this->quoteTableName($tableName))); + + foreach ($rows as $row) { + if (strcasecmp($row['Field'], $columnName) === 0) { + $null = $row['Null'] === 'NO' ? 'NOT NULL' : 'NULL'; + $comment = isset($row['Comment']) ? ' COMMENT ' . '\'' . addslashes($row['Comment']) . '\'' : ''; + $extra = ' ' . strtoupper($row['Extra']); + if (($row['Default'] !== null)) { + $extra .= $this->getDefaultValueDefinition($row['Default']); + } + $definition = $row['Type'] . ' ' . $null . $extra . $comment; + + $alter = sprintf( + 'CHANGE COLUMN %s %s %s', + $this->quoteColumnName($columnName), + $this->quoteColumnName($newColumnName), + $definition + ); + + return new AlterInstructions([$alter]); + } + } + + throw new InvalidArgumentException(sprintf( + "The specified column doesn't exist: " . + $columnName + )); + } + + /** + * @inheritDoc + */ + protected function getChangeColumnInstructions(string $tableName, string $columnName, Column $newColumn): AlterInstructions + { + $alter = sprintf( + 'CHANGE %s %s %s%s', + $this->quoteColumnName($columnName), + $this->quoteColumnName($newColumn->getName()), + $this->getColumnSqlDefinition($newColumn), + $this->afterClause($newColumn) + ); + + return new AlterInstructions([$alter]); + } + + /** + * @inheritDoc + */ + protected function getDropColumnInstructions(string $tableName, string $columnName): AlterInstructions + { + $alter = sprintf('DROP COLUMN %s', $this->quoteColumnName($columnName)); + + return new AlterInstructions([$alter]); + } + + /** + * Get an array of indexes from a particular table. + * + * @param string $tableName Table name + * @return array + */ + protected function getIndexes(string $tableName): array + { + $indexes = []; + $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']] = ['columns' => []]; + } + $indexes[$row['Key_name']]['columns'][] = strtolower($row['Column_name']); + } + + return $indexes; + } + + /** + * @inheritDoc + */ + public function hasIndex(string $tableName, $columns): bool + { + if (is_string($columns)) { + $columns = [$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(string $tableName, string $indexName): bool + { + $indexes = $this->getIndexes($tableName); + + foreach ($indexes as $name => $index) { + if ($name === $indexName) { + return true; + } + } + + return false; + } + + /** + * @inheritDoc + */ + protected function getAddIndexInstructions(Table $table, Index $index): AlterInstructions + { + $instructions = new AlterInstructions(); + + if ($index->getType() === Index::FULLTEXT) { + // Must be executed separately + // SQLSTATE[HY000]: General error: 1795 InnoDB presently supports one FULLTEXT index creation at a time + $alter = sprintf( + 'ALTER TABLE %s ADD %s', + $this->quoteTableName($table->getName()), + $this->getIndexSqlDefinition($index) + ); + + $instructions->addPostStep($alter); + } else { + $alter = sprintf( + 'ADD %s', + $this->getIndexSqlDefinition($index) + ); + + $instructions->addAlter($alter); + } + + return $instructions; + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getDropIndexByColumnsInstructions(string $tableName, $columns): AlterInstructions + { + if (is_string($columns)) { + $columns = [$columns]; // str to array + } + + $indexes = $this->getIndexes($tableName); + $columns = array_map('strtolower', $columns); + + foreach ($indexes as $indexName => $index) { + if ($columns == $index['columns']) { + return new AlterInstructions([sprintf( + 'DROP INDEX %s', + $this->quoteColumnName($indexName) + )]); + } + } + + throw new InvalidArgumentException(sprintf( + "The specified index on columns '%s' does not exist", + implode(',', $columns) + )); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getDropIndexByNameInstructions(string $tableName, $indexName): AlterInstructions + { + $indexes = $this->getIndexes($tableName); + + foreach ($indexes as $name => $index) { + if ($name === $indexName) { + return new AlterInstructions([sprintf( + 'DROP INDEX %s', + $this->quoteColumnName($indexName) + )]); + } + } + + throw new InvalidArgumentException(sprintf( + "The specified index name '%s' does not exist", + $indexName + )); + } + + /** + * @inheritDoc + */ + public function hasPrimaryKey(string $tableName, $columns, ?string $constraint = null): bool + { + $primaryKey = $this->getPrimaryKey($tableName); + + if (empty($primaryKey['constraint'])) { + return false; + } + + if ($constraint) { + return $primaryKey['constraint'] === $constraint; + } else { + if (is_string($columns)) { + $columns = [$columns]; // str to array + } + $missingColumns = array_diff($columns, $primaryKey['columns']); + + return empty($missingColumns); + } + } + + /** + * Get the primary key from a particular table. + * + * @param string $tableName Table name + * @return array + */ + public function getPrimaryKey(string $tableName): array + { + $options = $this->getOptions(); + $rows = $this->fetchAll(sprintf( + "SELECT + k.CONSTRAINT_NAME, + k.COLUMN_NAME + FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS t + JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE k + USING(CONSTRAINT_NAME,TABLE_SCHEMA,TABLE_NAME) + WHERE t.CONSTRAINT_TYPE='PRIMARY KEY' + AND t.TABLE_SCHEMA='%s' + AND t.TABLE_NAME='%s'", + $options['name'], + $tableName + )); + + $primaryKey = [ + 'columns' => [], + ]; + foreach ($rows as $row) { + $primaryKey['constraint'] = $row['CONSTRAINT_NAME']; + $primaryKey['columns'][] = $row['COLUMN_NAME']; + } + + return $primaryKey; + } + + /** + * @inheritDoc + */ + public function hasForeignKey(string $tableName, $columns, ?string $constraint = null): bool + { + if (is_string($columns)) { + $columns = [$columns]; // str to array + } + $foreignKeys = $this->getForeignKeys($tableName); + if ($constraint) { + if (isset($foreignKeys[$constraint])) { + return !empty($foreignKeys[$constraint]); + } + + return false; + } + + foreach ($foreignKeys as $key) { + 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(string $tableName): array + { + if (strpos($tableName, '.') !== false) { + [$schema, $tableName] = explode('.', $tableName); + } + + $foreignKeys = []; + $rows = $this->fetchAll(sprintf( + "SELECT + CONSTRAINT_NAME, + CONCAT(TABLE_SCHEMA, '.', TABLE_NAME) AS TABLE_NAME, + COLUMN_NAME, + CONCAT(REFERENCED_TABLE_SCHEMA, '.', REFERENCED_TABLE_NAME) AS REFERENCED_TABLE_NAME, + REFERENCED_COLUMN_NAME + FROM information_schema.KEY_COLUMN_USAGE + WHERE REFERENCED_TABLE_NAME IS NOT NULL + AND TABLE_SCHEMA = %s + AND TABLE_NAME = '%s' + ORDER BY POSITION_IN_UNIQUE_CONSTRAINT", + empty($schema) ? 'DATABASE()' : "'$schema'", + $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 + */ + protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey): AlterInstructions + { + $alter = sprintf( + 'ADD %s', + $this->getForeignKeySqlDefinition($foreignKey) + ); + + return new AlterInstructions([$alter]); + } + + /** + * @inheritDoc + */ + protected function getDropForeignKeyInstructions(string $tableName, string $constraint): AlterInstructions + { + $alter = sprintf( + 'DROP FOREIGN KEY %s', + $constraint + ); + + return new AlterInstructions([$alter]); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getDropForeignKeyByColumnsInstructions(string $tableName, array $columns): AlterInstructions + { + $instructions = new AlterInstructions(); + + 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) { + $instructions->merge($this->getDropForeignKeyInstructions($tableName, $row['CONSTRAINT_NAME'])); + } + } + + if (empty($instructions->getAlterParts())) { + throw new InvalidArgumentException(sprintf( + "No foreign key on column(s) '%s' exists", + implode(',', $columns) + )); + } + + return $instructions; + } + + /** + * {@inheritDoc} + * + * @throws \Phinx\Db\Adapter\UnsupportedColumnTypeException + */ + public function getSqlType($type, ?int $limit = null): array + { + switch ($type) { + case static::PHINX_TYPE_FLOAT: + case static::PHINX_TYPE_DOUBLE: + case static::PHINX_TYPE_DECIMAL: + case static::PHINX_TYPE_DATE: + case static::PHINX_TYPE_ENUM: + case static::PHINX_TYPE_SET: + case static::PHINX_TYPE_JSON: + // Geospatial database types + case static::PHINX_TYPE_GEOMETRY: + case static::PHINX_TYPE_POINT: + case static::PHINX_TYPE_LINESTRING: + case static::PHINX_TYPE_POLYGON: + return ['name' => $type]; + case static::PHINX_TYPE_DATETIME: + case static::PHINX_TYPE_TIMESTAMP: + case static::PHINX_TYPE_TIME: + return ['name' => $type, 'limit' => $limit]; + case static::PHINX_TYPE_STRING: + return ['name' => 'varchar', 'limit' => $limit ?: 255]; + case static::PHINX_TYPE_CHAR: + return ['name' => 'char', 'limit' => $limit ?: 255]; + case static::PHINX_TYPE_TEXT: + if ($limit) { + $sizes = [ + // 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 ['name' => $name]; + } + } + } + + return ['name' => 'text']; + case static::PHINX_TYPE_BINARY: + if ($limit === null) { + $limit = 255; + } + + if ($limit > 255) { + return $this->getSqlType(static::PHINX_TYPE_BLOB, $limit); + } + + return ['name' => 'binary', 'limit' => $limit]; + case static::PHINX_TYPE_BINARYUUID: + return ['name' => 'binary', 'limit' => 16]; + case static::PHINX_TYPE_VARBINARY: + if ($limit === null) { + $limit = 255; + } + + if ($limit > 255) { + return $this->getSqlType(static::PHINX_TYPE_BLOB, $limit); + } + + return ['name' => 'varbinary', 'limit' => $limit]; + case static::PHINX_TYPE_BLOB: + if ($limit !== null) { + // Rework this part as the choosen types were always UNDER the required length + $sizes = [ + 'tinyblob' => static::BLOB_SMALL, + 'blob' => static::BLOB_REGULAR, + 'mediumblob' => static::BLOB_MEDIUM, + ]; + + foreach ($sizes as $name => $length) { + if ($limit <= $length) { + return ['name' => $name]; + } + } + + // For more length requirement, the longblob is used + return ['name' => 'longblob']; + } + + // If not limit is provided, fallback on blob + return ['name' => 'blob']; + case static::PHINX_TYPE_TINYBLOB: + // Automatically reprocess blob type to ensure that correct blob subtype is selected given provided limit + return $this->getSqlType(static::PHINX_TYPE_BLOB, $limit ?: static::BLOB_TINY); + case static::PHINX_TYPE_MEDIUMBLOB: + // Automatically reprocess blob type to ensure that correct blob subtype is selected given provided limit + return $this->getSqlType(static::PHINX_TYPE_BLOB, $limit ?: static::BLOB_MEDIUM); + case static::PHINX_TYPE_LONGBLOB: + // Automatically reprocess blob type to ensure that correct blob subtype is selected given provided limit + return $this->getSqlType(static::PHINX_TYPE_BLOB, $limit ?: static::BLOB_LONG); + case static::PHINX_TYPE_BIT: + return ['name' => 'bit', 'limit' => $limit ?: 64]; + case static::PHINX_TYPE_BIG_INTEGER: + if ($limit === static::INT_BIG) { + $limit = static::INT_DISPLAY_BIG; + } + + return ['name' => 'bigint', 'limit' => $limit ?: 20]; + case static::PHINX_TYPE_MEDIUM_INTEGER: + if ($limit === static::INT_MEDIUM) { + $limit = static::INT_DISPLAY_MEDIUM; + } + + return ['name' => 'mediumint', 'limit' => $limit ?: 8]; + case static::PHINX_TYPE_SMALL_INTEGER: + if ($limit === static::INT_SMALL) { + $limit = static::INT_DISPLAY_SMALL; + } + + return ['name' => 'smallint', 'limit' => $limit ?: 6]; + case static::PHINX_TYPE_TINY_INTEGER: + if ($limit === static::INT_TINY) { + $limit = static::INT_DISPLAY_TINY; + } + + return ['name' => 'tinyint', 'limit' => $limit ?: 4]; + case static::PHINX_TYPE_INTEGER: + if ($limit && $limit >= static::INT_TINY) { + $sizes = [ + // 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 = [ + 'tinyint' => static::INT_DISPLAY_TINY, + 'smallint' => static::INT_DISPLAY_SMALL, + 'mediumint' => static::INT_DISPLAY_MEDIUM, + 'int' => static::INT_DISPLAY_REGULAR, + 'bigint' => static::INT_DISPLAY_BIG, + ]; + foreach ($sizes as $name => $length) { + if ($limit >= $length) { + $def = ['name' => $name]; + if (isset($limits[$name])) { + $def['limit'] = $limits[$name]; + } + + return $def; + } + } + } elseif (!$limit) { + $limit = static::INT_DISPLAY_REGULAR; + } + + return ['name' => 'int', 'limit' => $limit]; + case static::PHINX_TYPE_BOOLEAN: + return ['name' => 'tinyint', 'limit' => 1]; + case static::PHINX_TYPE_UUID: + return ['name' => 'char', 'limit' => 36]; + case static::PHINX_TYPE_YEAR: + if (!$limit || in_array($limit, [2, 4])) { + $limit = 4; + } + + return ['name' => 'year', 'limit' => $limit]; + default: + throw new UnsupportedColumnTypeException('Column type "' . $type . '" is not supported by MySQL.'); + } + } + + /** + * Returns Phinx type by SQL type + * + * @internal param string $sqlType SQL type + * @param string $sqlTypeDef SQL Type definition + * @throws \Phinx\Db\Adapter\UnsupportedColumnTypeException + * @return array Phinx type + */ + public function getPhinxType($sqlTypeDef) + { + $matches = []; + if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*(.+)*$/', $sqlTypeDef, $matches)) { + throw new UnsupportedColumnTypeException('Column type "' . $sqlTypeDef . '" is not supported by MySQL.'); + } + + $limit = null; + $scale = null; + $type = $matches[1]; + if (count($matches) > 2) { + $limit = $matches[3] ? (int)$matches[3] : null; + } + if (count($matches) > 4) { + $scale = (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_TINY_INTEGER; + break; + case 'smallint': + $type = static::PHINX_TYPE_SMALL_INTEGER; + break; + case 'mediumint': + $type = static::PHINX_TYPE_MEDIUM_INTEGER; + break; + case 'int': + $type = static::PHINX_TYPE_INTEGER; + break; + case 'bigint': + $type = static::PHINX_TYPE_BIG_INTEGER; + break; + case 'bit': + $type = static::PHINX_TYPE_BIT; + if ($limit === 64) { + $limit = null; + } + break; + case 'blob': + $type = static::PHINX_TYPE_BLOB; + $limit = static::BLOB_REGULAR; + break; + case 'tinyblob': + $type = static::PHINX_TYPE_TINYBLOB; + $limit = static::BLOB_TINY; + break; + case 'mediumblob': + $type = static::PHINX_TYPE_MEDIUMBLOB; + $limit = static::BLOB_MEDIUM; + break; + case 'longblob': + $type = static::PHINX_TYPE_LONGBLOB; + $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; + case 'binary': + if ($limit === null) { + $limit = 255; + } + + if ($limit > 255) { + $type = static::PHINX_TYPE_BLOB; + break; + } + + if ($limit === 16) { + $type = static::PHINX_TYPE_BINARYUUID; + } + break; + } + + try { + // Call this to check if parsed type is supported. + $this->getSqlType($type, $limit); + } catch (UnsupportedColumnTypeException $e) { + $type = Literal::from($type); + } + + $phinxType = [ + 'name' => $type, + 'limit' => $limit, + 'scale' => $scale, + ]; + + if ($type === static::PHINX_TYPE_ENUM || $type === static::PHINX_TYPE_SET) { + $values = trim($matches[6], '()'); + $phinxType['values'] = []; + $opened = false; + $escaped = false; + $wasEscaped = false; + $value = ''; + $valuesLength = strlen($values); + for ($i = 0; $i < $valuesLength; $i++) { + $char = $values[$i]; + if ($char === "'" && !$opened) { + $opened = true; + } elseif ( + !$escaped + && ($i + 1) < $valuesLength + && ( + $char === "'" && $values[$i + 1] === "'" + || $char === '\\' && $values[$i + 1] === '\\' + ) + ) { + $escaped = true; + } elseif ($char === "'" && $opened && !$escaped) { + $phinxType['values'][] = $value; + $value = ''; + $opened = false; + } elseif (($char === "'" || $char === '\\') && $opened && $escaped) { + $value .= $char; + $escaped = false; + $wasEscaped = true; + } elseif ($opened) { + if ($values[$i - 1] === '\\' && !$wasEscaped) { + if ($char === 'n') { + $char = "\n"; + } elseif ($char === 'r') { + $char = "\r"; + } elseif ($char === 't') { + $char = "\t"; + } + if ($values[$i] !== $char) { + $value = substr($value, 0, strlen($value) - 1); + } + } + $value .= $char; + $wasEscaped = false; + } + } + } + + return $phinxType; + } + + /** + * @inheritDoc + */ + public function createDatabase(string $name, array $options = []): void + { + $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)); + } + } + + /** + * @inheritDoc + */ + public function hasDatabase(string $name): bool + { + $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(string $name): void + { + $this->execute(sprintf('DROP DATABASE IF EXISTS `%s`', $name)); + $this->createdTables = []; + } + + /** + * Gets the MySQL Column Definition for a Column object. + * + * @param \Phinx\Db\Table\Column $column Column + * @return string + */ + protected function getColumnSqlDefinition(Column $column): string + { + if ($column->getType() instanceof Literal) { + $def = (string)$column->getType(); + } else { + $sqlType = $this->getSqlType($column->getType(), $column->getLimit()); + $def = strtoupper($sqlType['name']); + } + if ($column->getPrecision() && $column->getScale()) { + $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')'; + } elseif (isset($sqlType['limit'])) { + $def .= '(' . $sqlType['limit'] . ')'; + } + + $values = $column->getValues(); + if ($values && is_array($values)) { + $def .= '(' . implode(', ', array_map(function ($value) { + // we special case NULL as it's not actually allowed an enum value, + // and we want MySQL to issue an error on the create statement, but + // quote coerces it to an empty string, which will not error + return $value === null ? 'NULL' : $this->getConnection()->quote($value); + }, $values)) . ')'; + } + + $def .= $column->getEncoding() ? ' CHARACTER SET ' . $column->getEncoding() : ''; + $def .= $column->getCollation() ? ' COLLATE ' . $column->getCollation() : ''; + $def .= !$column->isSigned() && isset($this->signedColumnTypes[$column->getType()]) ? ' unsigned' : ''; + $def .= $column->isNull() ? ' NULL' : ' NOT NULL'; + + if ( + version_compare($this->getAttribute(\PDO::ATTR_SERVER_VERSION), '8', '>=') + && in_array($column->getType(), static::PHINX_TYPES_GEOSPATIAL) + && !is_null($column->getSrid()) + ) { + $def .= " SRID {$column->getSrid()}"; + } + + $def .= $column->isIdentity() ? ' AUTO_INCREMENT' : ''; + + $default = $column->getDefault(); + // MySQL 8 supports setting default for the following tested types, but only if they are "cast as expressions" + if ( + version_compare($this->getAttribute(\PDO::ATTR_SERVER_VERSION), '8', '>=') && + is_string($default) && + in_array( + $column->getType(), + array_merge( + static::PHINX_TYPES_GEOSPATIAL, + [static::PHINX_TYPE_BLOB, static::PHINX_TYPE_JSON, static::PHINX_TYPE_TEXT] + ) + ) + ) { + $default = Literal::from('(' . $this->getConnection()->quote($column->getDefault()) . ')'); + } + $def .= $this->getDefaultValueDefinition($default, $column->getType()); + + 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 \Phinx\Db\Table\Index $index Index + * @return string + */ + protected function getIndexSqlDefinition(Index $index): string + { + $def = ''; + $limit = ''; + + if ($index->getType() === Index::UNIQUE) { + $def .= ' UNIQUE'; + } + + if ($index->getType() === Index::FULLTEXT) { + $def .= ' FULLTEXT'; + } + + $def .= ' KEY'; + + if (is_string($index->getName())) { + $def .= ' `' . $index->getName() . '`'; + } + + $columnNames = $index->getColumns(); + $order = $index->getOrder() ?? []; + $columnNames = array_map(function ($columnName) use ($order) { + $ret = '`' . $columnName . '`'; + if (isset($order[$columnName])) { + $ret .= ' ' . $order[$columnName]; + } + + return $ret; + }, $columnNames); + + if (!is_array($index->getLimit())) { + if ($index->getLimit()) { + $limit = '(' . $index->getLimit() . ')'; + } + $def .= ' (' . implode(',', $columnNames) . $limit . ')'; + } else { + $columns = $index->getColumns(); + $limits = $index->getLimit(); + $def .= ' ('; + foreach ($columns as $column) { + $limit = !isset($limits[$column]) || $limits[$column] <= 0 ? '' : '(' . $limits[$column] . ')'; + $columnSort = isset($order[$column]) ?? ''; + $def .= '`' . $column . '`' . $limit . ' ' . $columnSort . ', '; + } + $def = rtrim($def, ', '); + $def .= ' )'; + } + + return $def; + } + + /** + * Gets the MySQL Foreign Key Definition for an ForeignKey object. + * + * @param \Phinx\Db\Table\ForeignKey $foreignKey Foreign key + * @return string + */ + protected function getForeignKeySqlDefinition(ForeignKey $foreignKey): string + { + $def = ''; + if ($foreignKey->getConstraint()) { + $def .= ' CONSTRAINT ' . $this->quoteColumnName($foreignKey->getConstraint()); + } + $columnNames = []; + foreach ($foreignKey->getColumns() as $column) { + $columnNames[] = $this->quoteColumnName($column); + } + $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')'; + $refColumnNames = []; + 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(string $tableName): array + { + $options = $this->getOptions(); + + // mysql specific + $sql = sprintf( + "SELECT * + FROM information_schema.tables + WHERE table_schema = '%s' + AND table_name = '%s'", + $options['name'], + $tableName + ); + + $table = $this->fetchRow($sql); + + return $table !== false ? $table : []; + } + + /** + * Returns MySQL column types (inherited and MySQL specified). + * + * @return string[] + */ + public function getColumnTypes(): array + { + return array_merge(parent::getColumnTypes(), static::$specificColumnTypes); + } + + /** + * @inheritDoc + */ + protected function getDecoratedConnectionConfig(): array + { + $options = $this->getOptions(); + $options = [ + 'username' => $options['user'] ?? null, + 'password' => $options['pass'] ?? null, + 'database' => $options['name'], + 'quoteIdentifiers' => true, + ] + $options; + + return ['driver' => new MysqlDriver($options)] + $options; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/PdoAdapter.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/PdoAdapter.php new file mode 100644 index 0000000..14f750c --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/PdoAdapter.php @@ -0,0 +1,1029 @@ + + */ +abstract class PdoAdapter extends AbstractAdapter implements DirectActionInterface +{ + /** + * @var \PDO|null + */ + protected $connection; + + /** + * @var \Cake\Database\Connection|null + */ + protected $decoratedConnection; + + /** + * Writes a message to stdout if verbose output is on + * + * @param string $message The message to show + * @return void + */ + protected function verboseLog($message): void + { + if ( + !$this->isDryRunEnabled() && + $this->getOutput()->getVerbosity() < OutputInterface::VERBOSITY_VERY_VERBOSE + ) { + return; + } + + $this->getOutput()->writeln($message); + } + + /** + * Create PDO connection + * + * @param string $dsn Connection string + * @param string|null $username Database username + * @param string|null $password Database password + * @param array $options Connection options + * @return \PDO + */ + protected function createPdoConnection(string $dsn, ?string $username = null, ?string $password = null, array $options = []): PDO + { + $adapterOptions = $this->getOptions() + [ + 'attr_errmode' => PDO::ERRMODE_EXCEPTION, + ]; + + try { + $db = new PDO($dsn, $username, $password, $options); + + foreach ($adapterOptions as $key => $option) { + if (strpos($key, 'attr_') === 0) { + $pdoConstant = '\PDO::' . strtoupper($key); + if (!defined($pdoConstant)) { + throw new \UnexpectedValueException('Invalid PDO attribute: ' . $key . ' (' . $pdoConstant . ')'); + } + $db->setAttribute(constant($pdoConstant), $option); + } + } + } catch (PDOException $e) { + throw new InvalidArgumentException(sprintf( + 'There was a problem connecting to the database: %s', + $e->getMessage() + ), 0, $e); + } + + return $db; + } + + /** + * @inheritDoc + */ + public function setOptions(array $options): AdapterInterface + { + parent::setOptions($options); + + if (isset($options['connection'])) { + $this->setConnection($options['connection']); + } + + return $this; + } + + /** + * Sets the database connection. + * + * @param \PDO $connection Connection + * @return \Phinx\Db\Adapter\AdapterInterface + */ + public function setConnection(PDO $connection): AdapterInterface + { + $this->connection = $connection; + + // Create the schema table if it doesn't already exist + if (!$this->hasTable($this->getSchemaTableName())) { + $this->createSchemaTable(); + } else { + $table = new DbTable($this->getSchemaTableName(), [], $this); + if (!$table->hasColumn('migration_name')) { + $table + ->addColumn( + 'migration_name', + 'string', + ['limit' => 100, 'after' => 'version', 'default' => null, 'null' => true] + ) + ->save(); + } + if (!$table->hasColumn('breakpoint')) { + $table + ->addColumn('breakpoint', 'boolean', ['default' => false, 'null' => false]) + ->save(); + } + } + + return $this; + } + + /** + * Gets the database connection + * + * @return \PDO + */ + public function getConnection(): PDO + { + if ($this->connection === null) { + $this->connect(); + } + + return $this->connection; + } + + /** + * @inheritDoc + */ + abstract public function connect(): void; + + /** + * @inheritDoc + */ + abstract public function disconnect(): void; + + /** + * @inheritDoc + */ + public function execute(string $sql, array $params = []): int + { + $sql = rtrim($sql, "; \t\n\r\0\x0B") . ';'; + $this->verboseLog($sql); + + if ($this->isDryRunEnabled()) { + return 0; + } + + if (empty($params)) { + return $this->getConnection()->exec($sql); + } + + $stmt = $this->getConnection()->prepare($sql); + $result = $stmt->execute($params); + + return $result ? $stmt->rowCount() : $result; + } + + /** + * Returns the config for a new decorated connection. + * + * @return array + */ + abstract protected function getDecoratedConnectionConfig(): array; + + /** + * Returns the Cake\Database connection object using the same underlying + * PDO object as this connection. + * + * @return \Cake\Database\Connection + */ + public function getDecoratedConnection(): Connection + { + if (isset($this->decoratedConnection)) { + return $this->decoratedConnection; + } + + $config = $this->getDecoratedConnectionConfig(); + if (!isset($config['driver'])) { + throw new RuntimeException('Decorated connection config is missing the driver.'); + } + + $config['driver']->setConnection($this->connection); + + return $this->decoratedConnection = new Connection($config); + } + + /** + * @inheritDoc + */ + public function getQueryBuilder(): Query + { + return $this->getDecoratedConnection()->newQuery(); + } + + /** + * Executes a query and returns PDOStatement. + * + * @param string $sql SQL + * @return \PDOStatement|false + */ + public function query(string $sql, array $params = []) + { + if (empty($params)) { + return $this->getConnection()->query($sql); + } + $stmt = $this->getConnection()->prepare($sql); + $result = $stmt->execute($params); + + return $result ? $stmt : false; + } + + /** + * @inheritDoc + */ + public function fetchRow(string $sql) + { + return $this->query($sql)->fetch(); + } + + /** + * @inheritDoc + */ + public function fetchAll(string $sql): array + { + return $this->query($sql)->fetchAll(); + } + + /** + * @inheritDoc + */ + public function insert(Table $table, array $row): void + { + $sql = sprintf( + 'INSERT INTO %s ', + $this->quoteTableName($table->getName()) + ); + $columns = array_keys($row); + $sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $columns)) . ')'; + + foreach ($row as $column => $value) { + if (is_bool($value)) { + $row[$column] = $this->castToBool($value); + } + } + + if ($this->isDryRunEnabled()) { + $sql .= ' VALUES (' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ');'; + $this->output->writeln($sql); + } else { + $sql .= ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')'; + $stmt = $this->getConnection()->prepare($sql); + $stmt->execute(array_values($row)); + } + } + + /** + * Quotes a database value. + * + * @param mixed $value The value to quote + * @return mixed + */ + protected function quoteValue($value) + { + if (is_numeric($value)) { + return $value; + } + + if ($value === null) { + return 'null'; + } + + return $this->getConnection()->quote($value); + } + + /** + * Quotes a database string. + * + * @param string $value The string to quote + * @return string + */ + protected function quoteString(string $value): string + { + return $this->getConnection()->quote($value); + } + + /** + * @inheritDoc + */ + public function bulkinsert(Table $table, array $rows): void + { + $sql = sprintf( + 'INSERT INTO %s ', + $this->quoteTableName($table->getName()) + ); + $current = current($rows); + $keys = array_keys($current); + $sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $keys)) . ') VALUES '; + + if ($this->isDryRunEnabled()) { + $values = array_map(function ($row) { + return '(' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ')'; + }, $rows); + $sql .= implode(', ', $values) . ';'; + $this->output->writeln($sql); + } else { + $count_keys = count($keys); + $query = '(' . implode(', ', array_fill(0, $count_keys, '?')) . ')'; + $count_vars = count($rows); + $queries = array_fill(0, $count_vars, $query); + $sql .= implode(',', $queries); + $stmt = $this->getConnection()->prepare($sql); + $vals = []; + + foreach ($rows as $row) { + foreach ($row as $v) { + if (is_bool($v)) { + $vals[] = $this->castToBool($v); + } else { + $vals[] = $v; + } + } + } + + $stmt->execute($vals); + } + } + + /** + * @inheritDoc + */ + public function getVersions(): array + { + $rows = $this->getVersionLog(); + + return array_keys($rows); + } + + /** + * {@inheritDoc} + * + * @throws \RuntimeException + */ + public function getVersionLog(): array + { + $result = []; + + switch ($this->options['version_order']) { + case Config::VERSION_ORDER_CREATION_TIME: + $orderBy = 'version ASC'; + break; + case Config::VERSION_ORDER_EXECUTION_TIME: + $orderBy = 'start_time ASC, version ASC'; + break; + default: + throw new RuntimeException('Invalid version_order configuration option'); + } + + // This will throw an exception if doing a --dry-run without any migrations as phinxlog + // does not exist, so in that case, we can just expect to trivially return empty set + try { + $rows = $this->fetchAll(sprintf('SELECT * FROM %s ORDER BY %s', $this->quoteTableName($this->getSchemaTableName()), $orderBy)); + } catch (PDOException $e) { + if (!$this->isDryRunEnabled()) { + throw $e; + } + $rows = []; + } + + foreach ($rows as $version) { + $result[(int)$version['version']] = $version; + } + + return $result; + } + + /** + * @inheritDoc + */ + public function migrated(MigrationInterface $migration, string $direction, string $startTime, string $endTime): AdapterInterface + { + if (strcasecmp($direction, MigrationInterface::UP) === 0) { + // up + $sql = sprintf( + "INSERT INTO %s (%s, %s, %s, %s, %s) VALUES ('%s', '%s', '%s', '%s', %s);", + $this->quoteTableName($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->execute($sql); + } else { + // down + $sql = sprintf( + "DELETE FROM %s WHERE %s = '%s'", + $this->quoteTableName($this->getSchemaTableName()), + $this->quoteColumnName('version'), + $migration->getVersion() + ); + + $this->execute($sql); + } + + return $this; + } + + /** + * @inheritDoc + */ + public function toggleBreakpoint(MigrationInterface $migration): AdapterInterface + { + $this->query( + sprintf( + 'UPDATE %1$s SET %2$s = CASE %2$s WHEN %3$s THEN %4$s ELSE %3$s END, %7$s = %7$s WHERE %5$s = \'%6$s\';', + $this->quoteTableName($this->getSchemaTableName()), + $this->quoteColumnName('breakpoint'), + $this->castToBool(true), + $this->castToBool(false), + $this->quoteColumnName('version'), + $migration->getVersion(), + $this->quoteColumnName('start_time') + ) + ); + + return $this; + } + + /** + * @inheritDoc + */ + public function resetAllBreakpoints(): int + { + return $this->execute( + sprintf( + 'UPDATE %1$s SET %2$s = %3$s, %4$s = %4$s WHERE %2$s <> %3$s;', + $this->quoteTableName($this->getSchemaTableName()), + $this->quoteColumnName('breakpoint'), + $this->castToBool(false), + $this->quoteColumnName('start_time') + ) + ); + } + + /** + * @inheritDoc + */ + public function setBreakpoint(MigrationInterface $migration): AdapterInterface + { + $this->markBreakpoint($migration, true); + + return $this; + } + + /** + * @inheritDoc + */ + public function unsetBreakpoint(MigrationInterface $migration): AdapterInterface + { + $this->markBreakpoint($migration, false); + + return $this; + } + + /** + * Mark a migration breakpoint. + * + * @param \Phinx\Migration\MigrationInterface $migration The migration target for the breakpoint + * @param bool $state The required state of the breakpoint + * @return \Phinx\Db\Adapter\AdapterInterface + */ + protected function markBreakpoint(MigrationInterface $migration, bool $state): AdapterInterface + { + $this->query( + sprintf( + 'UPDATE %1$s SET %2$s = %3$s, %4$s = %4$s WHERE %5$s = \'%6$s\';', + $this->quoteTableName($this->getSchemaTableName()), + $this->quoteColumnName('breakpoint'), + $this->castToBool($state), + $this->quoteColumnName('start_time'), + $this->quoteColumnName('version'), + $migration->getVersion() + ) + ); + + return $this; + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function createSchema(string $schemaName = 'public'): void + { + throw new BadMethodCallException('Creating a schema is not supported'); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function dropSchema(string $name): void + { + throw new BadMethodCallException('Dropping a schema is not supported'); + } + + /** + * @inheritDoc + */ + public function getColumnTypes(): array + { + return [ + 'string', + 'char', + 'text', + 'tinyinteger', + 'smallinteger', + 'integer', + 'biginteger', + 'bit', + 'float', + 'decimal', + 'double', + 'datetime', + 'timestamp', + 'time', + 'date', + 'blob', + 'binary', + 'varbinary', + 'boolean', + 'uuid', + // Geospatial data types + 'geometry', + 'point', + 'linestring', + 'polygon', + ]; + } + + /** + * @inheritDoc + */ + public function castToBool($value) + { + return (bool)$value ? 1 : 0; + } + + /** + * Retrieve a database connection attribute + * + * @see https://php.net/manual/en/pdo.getattribute.php + * @param int $attribute One of the PDO::ATTR_* constants + * @return mixed + */ + public function getAttribute(int $attribute) + { + return $this->connection->getAttribute($attribute); + } + + /** + * Get the definition for a `DEFAULT` statement. + * + * @param mixed $default Default value + * @param string|null $columnType column type added + * @return string + */ + protected function getDefaultValueDefinition($default, ?string $columnType = null): string + { + if ($default instanceof Literal) { + $default = (string)$default; + } elseif (is_string($default) && stripos($default, 'CURRENT_TIMESTAMP') !== 0) { + // Ensure a defaults of CURRENT_TIMESTAMP(3) is not quoted. + $default = $this->getConnection()->quote($default); + } elseif (is_bool($default)) { + $default = $this->castToBool($default); + } elseif ($default !== null && $columnType === static::PHINX_TYPE_BOOLEAN) { + $default = $this->castToBool((bool)$default); + } + + return isset($default) ? " DEFAULT $default" : ''; + } + + /** + * Executes all the ALTER TABLE instructions passed for the given table + * + * @param string $tableName The table name to use in the ALTER statement + * @param \Phinx\Db\Util\AlterInstructions $instructions The object containing the alter sequence + * @return void + */ + protected function executeAlterSteps(string $tableName, AlterInstructions $instructions): void + { + $alter = sprintf('ALTER TABLE %s %%s', $this->quoteTableName($tableName)); + $instructions->execute($alter, [$this, 'execute']); + } + + /** + * @inheritDoc + */ + public function addColumn(Table $table, Column $column): void + { + $instructions = $this->getAddColumnInstructions($table, $column); + $this->executeAlterSteps($table->getName(), $instructions); + } + + /** + * Returns the instructions to add the specified column to a database table. + * + * @param \Phinx\Db\Table\Table $table Table + * @param \Phinx\Db\Table\Column $column Column + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getAddColumnInstructions(Table $table, Column $column): AlterInstructions; + + /** + * @inheritdoc + */ + public function renameColumn(string $tableName, string $columnName, string $newColumnName): void + { + $instructions = $this->getRenameColumnInstructions($tableName, $columnName, $newColumnName); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to rename the specified column. + * + * @param string $tableName Table name + * @param string $columnName Column Name + * @param string $newColumnName New Column Name + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getRenameColumnInstructions(string $tableName, string $columnName, string $newColumnName): AlterInstructions; + + /** + * @inheritdoc + */ + public function changeColumn(string $tableName, string $columnName, Column $newColumn): void + { + $instructions = $this->getChangeColumnInstructions($tableName, $columnName, $newColumn); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to change a table column type. + * + * @param string $tableName Table name + * @param string $columnName Column Name + * @param \Phinx\Db\Table\Column $newColumn New Column + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getChangeColumnInstructions(string $tableName, string $columnName, Column $newColumn): AlterInstructions; + + /** + * @inheritdoc + */ + public function dropColumn(string $tableName, string $columnName): void + { + $instructions = $this->getDropColumnInstructions($tableName, $columnName); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to drop the specified column. + * + * @param string $tableName Table name + * @param string $columnName Column Name + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getDropColumnInstructions(string $tableName, string $columnName): AlterInstructions; + + /** + * @inheritdoc + */ + public function addIndex(Table $table, Index $index): void + { + $instructions = $this->getAddIndexInstructions($table, $index); + $this->executeAlterSteps($table->getName(), $instructions); + } + + /** + * Returns the instructions to add the specified index to a database table. + * + * @param \Phinx\Db\Table\Table $table Table + * @param \Phinx\Db\Table\Index $index Index + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getAddIndexInstructions(Table $table, Index $index): AlterInstructions; + + /** + * @inheritdoc + */ + public function dropIndex(string $tableName, $columns): void + { + $instructions = $this->getDropIndexByColumnsInstructions($tableName, $columns); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to drop the specified index from a database table. + * + * @param string $tableName The name of of the table where the index is + * @param string|string[] $columns Column(s) + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getDropIndexByColumnsInstructions(string $tableName, $columns): AlterInstructions; + + /** + * @inheritdoc + */ + public function dropIndexByName(string $tableName, string $indexName): void + { + $instructions = $this->getDropIndexByNameInstructions($tableName, $indexName); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to drop the index specified by name from a database table. + * + * @param string $tableName The table name whe the index is + * @param string $indexName The name of the index + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getDropIndexByNameInstructions(string $tableName, string $indexName): AlterInstructions; + + /** + * @inheritdoc + */ + public function addForeignKey(Table $table, ForeignKey $foreignKey): void + { + $instructions = $this->getAddForeignKeyInstructions($table, $foreignKey); + $this->executeAlterSteps($table->getName(), $instructions); + } + + /** + * Returns the instructions to adds the specified foreign key to a database table. + * + * @param \Phinx\Db\Table\Table $table The table to add the constraint to + * @param \Phinx\Db\Table\ForeignKey $foreignKey The foreign key to add + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey): AlterInstructions; + + /** + * @inheritDoc + */ + public function dropForeignKey(string $tableName, array $columns, ?string $constraint = null): void + { + if ($constraint) { + $instructions = $this->getDropForeignKeyInstructions($tableName, $constraint); + } else { + $instructions = $this->getDropForeignKeyByColumnsInstructions($tableName, $columns); + } + + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to drop the specified foreign key from a database table. + * + * @param string $tableName The table where the foreign key constraint is + * @param string $constraint Constraint name + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getDropForeignKeyInstructions(string $tableName, string $constraint): AlterInstructions; + + /** + * Returns the instructions to drop the specified foreign key from a database table. + * + * @param string $tableName The table where the foreign key constraint is + * @param string[] $columns The list of column names + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getDropForeignKeyByColumnsInstructions(string $tableName, array $columns): AlterInstructions; + + /** + * @inheritdoc + */ + public function dropTable(string $tableName): void + { + $instructions = $this->getDropTableInstructions($tableName); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to drop the specified database table. + * + * @param string $tableName Table name + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getDropTableInstructions(string $tableName): AlterInstructions; + + /** + * @inheritdoc + */ + public function renameTable(string $tableName, string $newTableName): void + { + $instructions = $this->getRenameTableInstructions($tableName, $newTableName); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to rename the specified database table. + * + * @param string $tableName Table name + * @param string $newTableName New Name + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getRenameTableInstructions(string $tableName, string $newTableName): AlterInstructions; + + /** + * @inheritdoc + */ + public function changePrimaryKey(Table $table, $newColumns): void + { + $instructions = $this->getChangePrimaryKeyInstructions($table, $newColumns); + $this->executeAlterSteps($table->getName(), $instructions); + } + + /** + * Returns the instructions to change the primary key for the specified database table. + * + * @param \Phinx\Db\Table\Table $table Table + * @param string|string[]|null $newColumns Column name(s) to belong to the primary key, or null to drop the key + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getChangePrimaryKeyInstructions(Table $table, $newColumns): AlterInstructions; + + /** + * @inheritdoc + */ + public function changeComment(Table $table, $newComment): void + { + $instructions = $this->getChangeCommentInstructions($table, $newComment); + $this->executeAlterSteps($table->getName(), $instructions); + } + + /** + * Returns the instruction to change the comment for the specified database table. + * + * @param \Phinx\Db\Table\Table $table Table + * @param string|null $newComment New comment string, or null to drop the comment + * @return \Phinx\Db\Util\AlterInstructions + */ + abstract protected function getChangeCommentInstructions(Table $table, ?string $newComment): AlterInstructions; + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + * @return void + */ + public function executeActions(Table $table, array $actions): void + { + $instructions = new AlterInstructions(); + + foreach ($actions as $action) { + switch (true) { + case $action instanceof AddColumn: + /** @var \Phinx\Db\Action\AddColumn $action */ + $instructions->merge($this->getAddColumnInstructions($table, $action->getColumn())); + break; + + case $action instanceof AddIndex: + /** @var \Phinx\Db\Action\AddIndex $action */ + $instructions->merge($this->getAddIndexInstructions($table, $action->getIndex())); + break; + + case $action instanceof AddForeignKey: + /** @var \Phinx\Db\Action\AddForeignKey $action */ + $instructions->merge($this->getAddForeignKeyInstructions($table, $action->getForeignKey())); + break; + + case $action instanceof ChangeColumn: + /** @var \Phinx\Db\Action\ChangeColumn $action */ + $instructions->merge($this->getChangeColumnInstructions( + $table->getName(), + $action->getColumnName(), + $action->getColumn() + )); + break; + + case $action instanceof DropForeignKey && !$action->getForeignKey()->getConstraint(): + /** @var \Phinx\Db\Action\DropForeignKey $action */ + $instructions->merge($this->getDropForeignKeyByColumnsInstructions( + $table->getName(), + $action->getForeignKey()->getColumns() + )); + break; + + case $action instanceof DropForeignKey && $action->getForeignKey()->getConstraint(): + /** @var \Phinx\Db\Action\DropForeignKey $action */ + $instructions->merge($this->getDropForeignKeyInstructions( + $table->getName(), + $action->getForeignKey()->getConstraint() + )); + break; + + case $action instanceof DropIndex && $action->getIndex()->getName() !== null: + /** @var \Phinx\Db\Action\DropIndex $action */ + $instructions->merge($this->getDropIndexByNameInstructions( + $table->getName(), + $action->getIndex()->getName() + )); + break; + + case $action instanceof DropIndex && $action->getIndex()->getName() == null: + /** @var \Phinx\Db\Action\DropIndex $action */ + $instructions->merge($this->getDropIndexByColumnsInstructions( + $table->getName(), + $action->getIndex()->getColumns() + )); + break; + + case $action instanceof DropTable: + /** @var \Phinx\Db\Action\DropTable $action */ + $instructions->merge($this->getDropTableInstructions( + $table->getName() + )); + break; + + case $action instanceof RemoveColumn: + /** @var \Phinx\Db\Action\RemoveColumn $action */ + $instructions->merge($this->getDropColumnInstructions( + $table->getName(), + $action->getColumn()->getName() + )); + break; + + case $action instanceof RenameColumn: + /** @var \Phinx\Db\Action\RenameColumn $action */ + $instructions->merge($this->getRenameColumnInstructions( + $table->getName(), + $action->getColumn()->getName(), + $action->getNewName() + )); + break; + + case $action instanceof RenameTable: + /** @var \Phinx\Db\Action\RenameTable $action */ + $instructions->merge($this->getRenameTableInstructions( + $table->getName(), + $action->getNewName() + )); + break; + + case $action instanceof ChangePrimaryKey: + /** @var \Phinx\Db\Action\ChangePrimaryKey $action */ + $instructions->merge($this->getChangePrimaryKeyInstructions( + $table, + $action->getNewColumns() + )); + break; + + case $action instanceof ChangeComment: + /** @var \Phinx\Db\Action\ChangeComment $action */ + $instructions->merge($this->getChangeCommentInstructions( + $table, + $action->getNewComment() + )); + break; + + default: + throw new InvalidArgumentException( + sprintf("Don't know how to execute action: '%s'", get_class($action)) + ); + } + } + + $this->executeAlterSteps($table->getName(), $instructions); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/PostgresAdapter.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/PostgresAdapter.php new file mode 100644 index 0000000..6d8aac4 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/PostgresAdapter.php @@ -0,0 +1,1675 @@ += 10.0) + * + * @var bool + */ + protected $useIdentity; + + /** + * {@inheritDoc} + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @return void + */ + public function connect(): void + { + if ($this->connection === null) { + 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 + } + + $options = $this->getOptions(); + $dsn = 'pgsql:dbname=' . $options['name']; + + if (isset($options['host'])) { + $dsn .= ';host=' . $options['host']; + } + + // if custom port is specified use it + if (isset($options['port'])) { + $dsn .= ';port=' . $options['port']; + } + + $driverOptions = []; + + // use custom data fetch mode + if (!empty($options['fetch_mode'])) { + $driverOptions[PDO::ATTR_DEFAULT_FETCH_MODE] = + constant('\PDO::FETCH_' . strtoupper($options['fetch_mode'])); + } + + // pass \PDO::ATTR_PERSISTENT to driver options instead of useless setting it after instantiation + if (isset($options['attr_persistent'])) { + $driverOptions[PDO::ATTR_PERSISTENT] = $options['attr_persistent']; + } + + $db = $this->createPdoConnection($dsn, $options['user'] ?? null, $options['pass'] ?? null, $driverOptions); + + try { + if (isset($options['schema'])) { + $db->exec('SET search_path TO ' . $this->quoteSchemaName($options['schema'])); + } + } catch (PDOException $exception) { + throw new InvalidArgumentException( + sprintf('Schema does not exists: %s', $options['schema']), + 0, + $exception + ); + } + + $this->useIdentity = (float)$db->getAttribute(PDO::ATTR_SERVER_VERSION) >= 10; + + $this->setConnection($db); + } + } + + /** + * @inheritDoc + */ + public function disconnect(): void + { + $this->connection = null; + } + + /** + * @inheritDoc + */ + public function hasTransactions(): bool + { + return true; + } + + /** + * @inheritDoc + */ + public function beginTransaction(): void + { + $this->execute('BEGIN'); + } + + /** + * @inheritDoc + */ + public function commitTransaction(): void + { + $this->execute('COMMIT'); + } + + /** + * @inheritDoc + */ + public function rollbackTransaction(): void + { + $this->execute('ROLLBACK'); + } + + /** + * Quotes a schema name for use in a query. + * + * @param string $schemaName Schema Name + * @return string + */ + public function quoteSchemaName(string $schemaName): string + { + return $this->quoteColumnName($schemaName); + } + + /** + * @inheritDoc + */ + public function quoteTableName(string $tableName): string + { + $parts = $this->getSchemaName($tableName); + + return $this->quoteSchemaName($parts['schema']) . '.' . $this->quoteColumnName($parts['table']); + } + + /** + * @inheritDoc + */ + public function quoteColumnName(string $columnName): string + { + return '"' . $columnName . '"'; + } + + /** + * @inheritDoc + */ + public function hasTable(string $tableName): bool + { + if ($this->hasCreatedTable($tableName)) { + return true; + } + + $parts = $this->getSchemaName($tableName); + $result = $this->getConnection()->query( + sprintf( + 'SELECT * + FROM information_schema.tables + WHERE table_schema = %s + AND table_name = %s', + $this->getConnection()->quote($parts['schema']), + $this->getConnection()->quote($parts['table']) + ) + ); + + return $result->rowCount() === 1; + } + + /** + * @inheritDoc + */ + public function createTable(Table $table, array $columns = [], array $indexes = []): void + { + $queries = []; + + $options = $table->getOptions(); + $parts = $this->getSchemaName($table->getName()); + + // Add the default primary key + 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') + ->setOptions(['identity' => true]); + + array_unshift($columns, $column); + if (isset($options['primary_key']) && (array)$options['id'] !== (array)$options['primary_key']) { + throw new InvalidArgumentException('You cannot enable an auto incrementing ID field and a primary key'); + } + $options['primary_key'] = $options['id']; + } + + // TODO - process table options like collation etc + $sql = 'CREATE TABLE '; + $sql .= $this->quoteTableName($table->getName()) . ' ('; + + $this->columnsWithComments = []; + foreach ($columns as $column) { + $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column); + if ($this->useIdentity && $column->getIdentity() && $column->getGenerated() !== null) { + $sql .= sprintf(' GENERATED %s AS IDENTITY', $column->getGenerated()); + } + $sql .= ', '; + + // 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 PRIMARY KEY (', $this->quoteColumnName($parts['table'] . '_pkey')); + 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') + $sql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key'])); + } + $sql .= ')'; + } else { + $sql = rtrim($sql, ', '); // no primary keys + } + + $sql .= ')'; + $queries[] = $sql; + + // process column comments + if (!empty($this->columnsWithComments)) { + foreach ($this->columnsWithComments as $column) { + $queries[] = $this->getColumnCommentSqlDefinition($column, $table->getName()); + } + } + + // set the indexes + if (!empty($indexes)) { + foreach ($indexes as $index) { + $queries[] = $this->getIndexSqlDefinition($index, $table->getName()); + } + } + + // process table comments + if (isset($options['comment'])) { + $queries[] = sprintf( + 'COMMENT ON TABLE %s IS %s', + $this->quoteTableName($table->getName()), + $this->getConnection()->quote($options['comment']) + ); + } + + foreach ($queries as $query) { + $this->execute($query); + } + + $this->addCreatedTable($table->getName()); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getChangePrimaryKeyInstructions(Table $table, $newColumns): AlterInstructions + { + $parts = $this->getSchemaName($table->getName()); + + $instructions = new AlterInstructions(); + + // Drop the existing primary key + $primaryKey = $this->getPrimaryKey($table->getName()); + if (!empty($primaryKey['constraint'])) { + $sql = sprintf( + 'DROP CONSTRAINT %s', + $this->quoteColumnName($primaryKey['constraint']) + ); + $instructions->addAlter($sql); + } + + // Add the new primary key + if (!empty($newColumns)) { + $sql = sprintf( + 'ADD CONSTRAINT %s PRIMARY KEY (', + $this->quoteColumnName($parts['table'] . '_pkey') + ); + if (is_string($newColumns)) { // handle primary_key => 'id' + $sql .= $this->quoteColumnName($newColumns); + } elseif (is_array($newColumns)) { // handle primary_key => array('tag_id', 'resource_id') + $sql .= implode(',', array_map([$this, 'quoteColumnName'], $newColumns)); + } else { + throw new InvalidArgumentException(sprintf( + 'Invalid value for primary key: %s', + json_encode($newColumns) + )); + } + $sql .= ')'; + $instructions->addAlter($sql); + } + + return $instructions; + } + + /** + * @inheritDoc + */ + protected function getChangeCommentInstructions(Table $table, ?string $newComment): AlterInstructions + { + $instructions = new AlterInstructions(); + + // passing 'null' is to remove table comment + $newComment = $newComment !== null + ? $this->getConnection()->quote($newComment) + : 'NULL'; + $sql = sprintf( + 'COMMENT ON TABLE %s IS %s', + $this->quoteTableName($table->getName()), + $newComment + ); + $instructions->addPostStep($sql); + + return $instructions; + } + + /** + * @inheritDoc + */ + protected function getRenameTableInstructions(string $tableName, string $newTableName): AlterInstructions + { + $this->updateCreatedTableName($tableName, $newTableName); + $sql = sprintf( + 'ALTER TABLE %s RENAME TO %s', + $this->quoteTableName($tableName), + $this->quoteColumnName($newTableName) + ); + + return new AlterInstructions([], [$sql]); + } + + /** + * @inheritDoc + */ + protected function getDropTableInstructions(string $tableName): AlterInstructions + { + $this->removeCreatedTable($tableName); + $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName)); + + return new AlterInstructions([], [$sql]); + } + + /** + * @inheritDoc + */ + public function truncateTable(string $tableName): void + { + $sql = sprintf( + 'TRUNCATE TABLE %s RESTART IDENTITY', + $this->quoteTableName($tableName) + ); + + $this->execute($sql); + } + + /** + * @inheritDoc + */ + public function getColumns(string $tableName): array + { + $parts = $this->getSchemaName($tableName); + $columns = []; + $sql = sprintf( + 'SELECT column_name, data_type, udt_name, is_identity, is_nullable, + column_default, character_maximum_length, numeric_precision, numeric_scale, + datetime_precision + %s + FROM information_schema.columns + WHERE table_schema = %s AND table_name = %s + ORDER BY ordinal_position', + $this->useIdentity ? ', identity_generation' : '', + $this->getConnection()->quote($parts['schema']), + $this->getConnection()->quote($parts['table']) + ); + $columnsInfo = $this->fetchAll($sql); + foreach ($columnsInfo as $columnInfo) { + $isUserDefined = strtoupper(trim($columnInfo['data_type'])) === 'USER-DEFINED'; + + if ($isUserDefined) { + $columnType = Literal::from($columnInfo['udt_name']); + } else { + $columnType = $this->getPhinxType($columnInfo['data_type']); + } + + // If the default value begins with a ' or looks like a function mark it as literal + if (isset($columnInfo['column_default'][0]) && $columnInfo['column_default'][0] === "'") { + if (preg_match('/^\'(.*)\'::[^:]+$/', $columnInfo['column_default'], $match)) { + // '' and \' are replaced with a single ' + $columnDefault = preg_replace('/[\'\\\\]\'/', "'", $match[1]); + } else { + $columnDefault = Literal::from($columnInfo['column_default']); + } + } elseif ( + $columnInfo['column_default'] !== null && + preg_match('/^\D[a-z_\d]*\(.*\)$/', $columnInfo['column_default']) + ) { + $columnDefault = Literal::from($columnInfo['column_default']); + } else { + $columnDefault = $columnInfo['column_default']; + } + + $column = new Column(); + + $column->setName($columnInfo['column_name']) + ->setType($columnType) + ->setNull($columnInfo['is_nullable'] === 'YES') + ->setDefault($columnDefault) + ->setIdentity($columnInfo['is_identity'] === 'YES') + ->setScale($columnInfo['numeric_scale']); + + if ($this->useIdentity) { + $column->setGenerated($columnInfo['identity_generation']); + } + + if (preg_match('/\bwith time zone$/', $columnInfo['data_type'])) { + $column->setTimezone(true); + } + + if (isset($columnInfo['character_maximum_length'])) { + $column->setLimit($columnInfo['character_maximum_length']); + } + + if (in_array($columnType, [static::PHINX_TYPE_TIME, static::PHINX_TYPE_DATETIME], true)) { + $column->setPrecision($columnInfo['datetime_precision']); + } elseif ($columnType === self::PHINX_TYPE_DECIMAL) { + $column->setPrecision($columnInfo['numeric_precision']); + } + $columns[] = $column; + } + + return $columns; + } + + /** + * @inheritDoc + */ + public function hasColumn(string $tableName, string $columnName): bool + { + $parts = $this->getSchemaName($tableName); + $sql = sprintf( + 'SELECT count(*) + FROM information_schema.columns + WHERE table_schema = %s AND table_name = %s AND column_name = %s', + $this->getConnection()->quote($parts['schema']), + $this->getConnection()->quote($parts['table']), + $this->getConnection()->quote($columnName) + ); + + $result = $this->fetchRow($sql); + + return $result['count'] > 0; + } + + /** + * @inheritDoc + */ + protected function getAddColumnInstructions(Table $table, Column $column): AlterInstructions + { + $instructions = new AlterInstructions(); + $instructions->addAlter(sprintf( + 'ADD %s %s %s', + $this->quoteColumnName($column->getName()), + $this->getColumnSqlDefinition($column), + $column->isIdentity() && $column->getGenerated() !== null && $this->useIdentity ? + sprintf('GENERATED %s AS IDENTITY', $column->getGenerated()) : '' + )); + + if ($column->getComment()) { + $instructions->addPostStep($this->getColumnCommentSqlDefinition($column, $table->getName())); + } + + return $instructions; + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getRenameColumnInstructions( + string $tableName, + string $columnName, + string $newColumnName + ): AlterInstructions { + $parts = $this->getSchemaName($tableName); + $sql = sprintf( + 'SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END AS column_exists + FROM information_schema.columns + WHERE table_schema = %s AND table_name = %s AND column_name = %s', + $this->getConnection()->quote($parts['schema']), + $this->getConnection()->quote($parts['table']), + $this->getConnection()->quote($columnName) + ); + + $result = $this->fetchRow($sql); + if (!(bool)$result['column_exists']) { + throw new InvalidArgumentException("The specified column does not exist: $columnName"); + } + + $instructions = new AlterInstructions(); + $instructions->addPostStep( + sprintf( + 'ALTER TABLE %s RENAME COLUMN %s TO %s', + $this->quoteTableName($tableName), + $this->quoteColumnName($columnName), + $this->quoteColumnName($newColumnName) + ) + ); + + return $instructions; + } + + /** + * @inheritDoc + */ + protected function getChangeColumnInstructions( + string $tableName, + string $columnName, + Column $newColumn + ): AlterInstructions { + $quotedColumnName = $this->quoteColumnName($columnName); + $instructions = new AlterInstructions(); + if ($newColumn->getType() === 'boolean') { + $sql = sprintf('ALTER COLUMN %s DROP DEFAULT', $quotedColumnName); + $instructions->addAlter($sql); + } + $sql = sprintf( + 'ALTER COLUMN %s TYPE %s', + $quotedColumnName, + $this->getColumnSqlDefinition($newColumn) + ); + if (in_array($newColumn->getType(), ['smallinteger', 'integer', 'biginteger'], true)) { + $sql .= sprintf( + ' USING (%s::bigint)', + $quotedColumnName + ); + } + if ($newColumn->getType() === 'uuid') { + $sql .= sprintf( + ' USING (%s::uuid)', + $quotedColumnName + ); + } + //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); + if ($newColumn->getType() === 'boolean') { + $sql .= sprintf( + ' USING (CASE WHEN %s IS NULL THEN NULL WHEN %s::int=0 THEN FALSE ELSE TRUE END)', + $quotedColumnName, + $quotedColumnName + ); + } + $instructions->addAlter($sql); + + $column = $this->getColumn($tableName, $columnName); + + if ($this->useIdentity) { + // process identity + $sql = sprintf( + 'ALTER COLUMN %s', + $quotedColumnName + ); + if ($newColumn->isIdentity() && $newColumn->getGenerated() !== null) { + if ($column->isIdentity()) { + $sql .= sprintf(' SET GENERATED %s', $newColumn->getGenerated()); + } else { + $sql .= sprintf(' ADD GENERATED %s AS IDENTITY', $newColumn->getGenerated()); + } + } else { + $sql .= ' DROP IDENTITY IF EXISTS'; + } + $instructions->addAlter($sql); + } + + // process null + $sql = sprintf( + 'ALTER COLUMN %s', + $quotedColumnName + ); + + if (!$newColumn->getIdentity() && !$column->getIdentity() && $newColumn->isNull()) { + $sql .= ' DROP NOT NULL'; + } else { + $sql .= ' SET NOT NULL'; + } + + $instructions->addAlter($sql); + + if ($newColumn->getDefault() !== null) { + $instructions->addAlter(sprintf( + 'ALTER COLUMN %s SET %s', + $quotedColumnName, + $this->getDefaultValueDefinition($newColumn->getDefault(), $newColumn->getType()) + )); + } elseif (!$newColumn->getIdentity()) { + //drop default + $instructions->addAlter(sprintf( + 'ALTER COLUMN %s DROP DEFAULT', + $quotedColumnName + )); + } + + // rename column + if ($columnName !== $newColumn->getName()) { + $instructions->addPostStep(sprintf( + 'ALTER TABLE %s RENAME COLUMN %s TO %s', + $this->quoteTableName($tableName), + $quotedColumnName, + $this->quoteColumnName($newColumn->getName()) + )); + } + + // change column comment if needed + if ($newColumn->getComment()) { + $instructions->addPostStep($this->getColumnCommentSqlDefinition($newColumn, $tableName)); + } + + return $instructions; + } + + /** + * @param string $tableName Table name + * @param string $columnName Column name + * @return ?\Phinx\Db\Table\Column + */ + protected function getColumn(string $tableName, string $columnName): ?Column + { + $columns = $this->getColumns($tableName); + foreach ($columns as $column) { + if ($column->getName() === $columnName) { + return $column; + } + } + + return null; + } + + /** + * @inheritDoc + */ + protected function getDropColumnInstructions(string $tableName, string $columnName): AlterInstructions + { + $alter = sprintf( + 'DROP COLUMN %s', + $this->quoteColumnName($columnName) + ); + + return new AlterInstructions([$alter]); + } + + /** + * Get an array of indexes from a particular table. + * + * @param string $tableName Table name + * @return array + */ + protected function getIndexes($tableName) + { + $parts = $this->getSchemaName($tableName); + + $indexes = []; + $sql = sprintf( + "SELECT + i.relname AS index_name, + a.attname AS column_name + FROM + pg_class t, + pg_class i, + pg_index ix, + pg_attribute a, + pg_namespace nsp + WHERE + t.oid = ix.indrelid + AND i.oid = ix.indexrelid + AND a.attrelid = t.oid + AND a.attnum = ANY(ix.indkey) + AND t.relnamespace = nsp.oid + AND nsp.nspname = %s + AND t.relkind = 'r' + AND t.relname = %s + ORDER BY + t.relname, + i.relname", + $this->getConnection()->quote($parts['schema']), + $this->getConnection()->quote($parts['table']) + ); + $rows = $this->fetchAll($sql); + foreach ($rows as $row) { + if (!isset($indexes[$row['index_name']])) { + $indexes[$row['index_name']] = ['columns' => []]; + } + $indexes[$row['index_name']]['columns'][] = $row['column_name']; + } + + return $indexes; + } + + /** + * @inheritDoc + */ + public function hasIndex(string $tableName, $columns): bool + { + if (is_string($columns)) { + $columns = [$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(string $tableName, string $indexName): bool + { + $indexes = $this->getIndexes($tableName); + foreach ($indexes as $name => $index) { + if ($name === $indexName) { + return true; + } + } + + return false; + } + + /** + * @inheritDoc + */ + protected function getAddIndexInstructions(Table $table, Index $index): AlterInstructions + { + $instructions = new AlterInstructions(); + $instructions->addPostStep($this->getIndexSqlDefinition($index, $table->getName())); + + return $instructions; + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getDropIndexByColumnsInstructions(string $tableName, $columns): AlterInstructions + { + $parts = $this->getSchemaName($tableName); + + if (is_string($columns)) { + $columns = [$columns]; // str to array + } + + $indexes = $this->getIndexes($tableName); + foreach ($indexes as $indexName => $index) { + $a = array_diff($columns, $index['columns']); + if (empty($a)) { + return new AlterInstructions([], [sprintf( + 'DROP INDEX IF EXISTS %s', + '"' . ($parts['schema'] . '".' . $this->quoteColumnName($indexName)) + )]); + } + } + + throw new InvalidArgumentException(sprintf( + "The specified index on columns '%s' does not exist", + implode(',', $columns) + )); + } + + /** + * @inheritDoc + */ + protected function getDropIndexByNameInstructions(string $tableName, string $indexName): AlterInstructions + { + $parts = $this->getSchemaName($tableName); + + $sql = sprintf( + 'DROP INDEX IF EXISTS %s', + '"' . ($parts['schema'] . '".' . $this->quoteColumnName($indexName)) + ); + + return new AlterInstructions([], [$sql]); + } + + /** + * @inheritDoc + */ + public function hasPrimaryKey(string $tableName, $columns, ?string $constraint = null): bool + { + $primaryKey = $this->getPrimaryKey($tableName); + + if (empty($primaryKey)) { + return false; + } + + if ($constraint) { + return $primaryKey['constraint'] === $constraint; + } else { + if (is_string($columns)) { + $columns = [$columns]; // str to array + } + $missingColumns = array_diff($columns, $primaryKey['columns']); + + return empty($missingColumns); + } + } + + /** + * Get the primary key from a particular table. + * + * @param string $tableName Table name + * @return array + */ + public function getPrimaryKey(string $tableName): array + { + $parts = $this->getSchemaName($tableName); + $rows = $this->fetchAll(sprintf( + "SELECT + tc.constraint_name, + kcu.column_name + FROM information_schema.table_constraints AS tc + JOIN information_schema.key_column_usage AS kcu + ON tc.constraint_name = kcu.constraint_name + WHERE constraint_type = 'PRIMARY KEY' + AND tc.table_schema = %s + AND tc.table_name = %s + ORDER BY kcu.position_in_unique_constraint", + $this->getConnection()->quote($parts['schema']), + $this->getConnection()->quote($parts['table']) + )); + + $primaryKey = [ + 'columns' => [], + ]; + foreach ($rows as $row) { + $primaryKey['constraint'] = $row['constraint_name']; + $primaryKey['columns'][] = $row['column_name']; + } + + return $primaryKey; + } + + /** + * @inheritDoc + */ + public function hasForeignKey(string $tableName, $columns, ?string $constraint = null): bool + { + if (is_string($columns)) { + $columns = [$columns]; // str to array + } + $foreignKeys = $this->getForeignKeys($tableName); + if ($constraint) { + if (isset($foreignKeys[$constraint])) { + return !empty($foreignKeys[$constraint]); + } + + return false; + } + + 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(string $tableName): array + { + $parts = $this->getSchemaName($tableName); + $foreignKeys = []; + $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_schema = %s AND tc.table_name = %s + ORDER BY kcu.position_in_unique_constraint", + $this->getConnection()->quote($parts['schema']), + $this->getConnection()->quote($parts['table']) + )); + 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 + */ + protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey): AlterInstructions + { + $alter = sprintf( + 'ADD %s', + $this->getForeignKeySqlDefinition($foreignKey, $table->getName()) + ); + + return new AlterInstructions([$alter]); + } + + /** + * @inheritDoc + */ + protected function getDropForeignKeyInstructions($tableName, $constraint): AlterInstructions + { + $alter = sprintf( + 'DROP CONSTRAINT %s', + $this->quoteColumnName($constraint) + ); + + return new AlterInstructions([$alter]); + } + + /** + * @inheritDoc + */ + protected function getDropForeignKeyByColumnsInstructions(string $tableName, array $columns): AlterInstructions + { + $instructions = new AlterInstructions(); + + $parts = $this->getSchemaName($tableName); + $sql = 'SELECT c.CONSTRAINT_NAME + FROM ( + SELECT CONSTRAINT_NAME, array_agg(COLUMN_NAME::varchar) as columns + FROM information_schema.KEY_COLUMN_USAGE + WHERE TABLE_SCHEMA = %s + AND TABLE_NAME IS NOT NULL + AND TABLE_NAME = %s + AND POSITION_IN_UNIQUE_CONSTRAINT IS NOT NULL + GROUP BY CONSTRAINT_NAME + ) c + WHERE + ARRAY[%s]::varchar[] <@ c.columns AND + ARRAY[%s]::varchar[] @> c.columns'; + + $array = []; + foreach ($columns as $col) { + $array[] = "'$col'"; + } + + $rows = $this->fetchAll(sprintf( + $sql, + $this->getConnection()->quote($parts['schema']), + $this->getConnection()->quote($parts['table']), + implode(',', $array), + implode(',', $array) + )); + + foreach ($rows as $row) { + $newInstr = $this->getDropForeignKeyInstructions($tableName, $row['constraint_name']); + $instructions->merge($newInstr); + } + + return $instructions; + } + + /** + * {@inheritDoc} + * + * @throws \Phinx\Db\Adapter\UnsupportedColumnTypeException + */ + public function getSqlType($type, ?int $limit = null): array + { + switch ($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: + case static::PHINX_TYPE_CIDR: + case static::PHINX_TYPE_INET: + case static::PHINX_TYPE_MACADDR: + case static::PHINX_TYPE_TIMESTAMP: + case static::PHINX_TYPE_INTEGER: + return ['name' => $type]; + case static::PHINX_TYPE_TINY_INTEGER: + return ['name' => 'smallint']; + case static::PHINX_TYPE_SMALL_INTEGER: + return ['name' => 'smallint']; + case static::PHINX_TYPE_DECIMAL: + return ['name' => $type, 'precision' => 18, 'scale' => 0]; + case static::PHINX_TYPE_DOUBLE: + return ['name' => 'double precision']; + case static::PHINX_TYPE_STRING: + return ['name' => 'character varying', 'limit' => 255]; + case static::PHINX_TYPE_CHAR: + return ['name' => 'character', 'limit' => 255]; + case static::PHINX_TYPE_BIG_INTEGER: + return ['name' => 'bigint']; + case static::PHINX_TYPE_FLOAT: + return ['name' => 'real']; + case static::PHINX_TYPE_DATETIME: + return ['name' => 'timestamp']; + case static::PHINX_TYPE_BINARYUUID: + return ['name' => 'uuid']; + case static::PHINX_TYPE_BLOB: + case static::PHINX_TYPE_BINARY: + return ['name' => 'bytea']; + case static::PHINX_TYPE_INTERVAL: + return ['name' => 'interval']; + // 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 ['name' => 'geography', 'type' => 'geometry', 'srid' => 4326]; + case static::PHINX_TYPE_POINT: + return ['name' => 'geography', 'type' => 'point', 'srid' => 4326]; + case static::PHINX_TYPE_LINESTRING: + return ['name' => 'geography', 'type' => 'linestring', 'srid' => 4326]; + case static::PHINX_TYPE_POLYGON: + return ['name' => 'geography', 'type' => 'polygon', 'srid' => 4326]; + default: + if ($this->isArrayType($type)) { + return ['name' => $type]; + } + // Return array type + throw new UnsupportedColumnTypeException('Column type `' . $type . '` is not supported by Postgresql.'); + } + } + + /** + * Returns Phinx type by SQL type + * + * @param string $sqlType SQL type + * @throws \Phinx\Db\Adapter\UnsupportedColumnTypeException + * @return string Phinx type + */ + public function getPhinxType(string $sqlType): string + { + 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 static::PHINX_TYPE_SMALL_INTEGER; + 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 'double precision': + return static::PHINX_TYPE_DOUBLE; + case 'bytea': + return static::PHINX_TYPE_BINARY; + case 'interval': + return static::PHINX_TYPE_INTERVAL; + 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; + case 'cidr': + return static::PHINX_TYPE_CIDR; + case 'inet': + return static::PHINX_TYPE_INET; + case 'macaddr': + return static::PHINX_TYPE_MACADDR; + default: + throw new UnsupportedColumnTypeException( + 'Column type `' . $sqlType . '` is not supported by Postgresql.' + ); + } + } + + /** + * @inheritDoc + */ + public function createDatabase(string $name, array $options = []): void + { + $charset = $options['charset'] ?? 'utf8'; + $this->execute(sprintf("CREATE DATABASE %s WITH ENCODING = '%s'", $name, $charset)); + } + + /** + * @inheritDoc + */ + public function hasDatabase(string $name): bool + { + $sql = sprintf("SELECT count(*) FROM pg_database WHERE datname = '%s'", $name); + $result = $this->fetchRow($sql); + + return $result['count'] > 0; + } + + /** + * @inheritDoc + */ + public function dropDatabase($name): void + { + $this->disconnect(); + $this->execute(sprintf('DROP DATABASE IF EXISTS %s', $name)); + $this->createdTables = []; + $this->connect(); + } + + /** + * Gets the PostgreSQL Column Definition for a Column object. + * + * @param \Phinx\Db\Table\Column $column Column + * @return string + */ + protected function getColumnSqlDefinition(Column $column): string + { + $buffer = []; + + if ($column->isIdentity() && (!$this->useIdentity || $column->getGenerated() === null)) { + if ($column->getType() === 'smallinteger') { + $buffer[] = 'SMALLSERIAL'; + } elseif ($column->getType() === 'biginteger') { + $buffer[] = 'BIGSERIAL'; + } else { + $buffer[] = 'SERIAL'; + } + } elseif ($column->getType() instanceof Literal) { + $buffer[] = (string)$column->getType(); + } else { + $sqlType = $this->getSqlType($column->getType(), $column->getLimit()); + $buffer[] = strtoupper($sqlType['name']); + + // integers cant have limits in postgres + if ($sqlType['name'] === static::PHINX_TYPE_DECIMAL && ($column->getPrecision() || $column->getScale())) { + $buffer[] = sprintf( + '(%s, %s)', + $column->getPrecision() ?: $sqlType['precision'], + $column->getScale() ?: $sqlType['scale'] + ); + } elseif ($sqlType['name'] === self::PHINX_TYPE_GEOMETRY) { + // geography type must be written with geometry type and srid, like this: geography(POLYGON,4326) + $buffer[] = sprintf( + '(%s,%s)', + strtoupper($sqlType['type']), + $column->getSrid() ?: $sqlType['srid'] + ); + } elseif (in_array($sqlType['name'], [self::PHINX_TYPE_TIME, self::PHINX_TYPE_TIMESTAMP], true)) { + if (is_numeric($column->getPrecision())) { + $buffer[] = sprintf('(%s)', $column->getPrecision()); + } + + if ($column->isTimezone()) { + $buffer[] = strtoupper('with time zone'); + } + } elseif ( + !in_array($column->getType(), [ + self::PHINX_TYPE_TINY_INTEGER, + self::PHINX_TYPE_SMALL_INTEGER, + self::PHINX_TYPE_INTEGER, + self::PHINX_TYPE_BIG_INTEGER, + self::PHINX_TYPE_BOOLEAN, + self::PHINX_TYPE_TEXT, + self::PHINX_TYPE_BINARY, + ], true) + ) { + if ($column->getLimit() || isset($sqlType['limit'])) { + $buffer[] = sprintf('(%s)', $column->getLimit() ?: $sqlType['limit']); + } + } + } + + $buffer[] = $column->isNull() ? 'NULL' : 'NOT NULL'; + + if ($column->getDefault() !== null) { + $buffer[] = $this->getDefaultValueDefinition($column->getDefault(), $column->getType()); + } + + return implode(' ', $buffer); + } + + /** + * Gets the PostgreSQL Column Comment Definition for a column object. + * + * @param \Phinx\Db\Table\Column $column Column + * @param string $tableName Table name + * @return string + */ + protected function getColumnCommentSqlDefinition(Column $column, string $tableName): string + { + // 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;', + $this->quoteTableName($tableName), + $this->quoteColumnName($column->getName()), + $comment + ); + } + + /** + * Gets the PostgreSQL Index Definition for an Index object. + * + * @param \Phinx\Db\Table\Index $index Index + * @param string $tableName Table name + * @return string + */ + protected function getIndexSqlDefinition(Index $index, string $tableName): string + { + $parts = $this->getSchemaName($tableName); + $columnNames = $index->getColumns(); + + if (is_string($index->getName())) { + $indexName = $index->getName(); + } else { + $indexName = sprintf('%s_%s', $parts['table'], implode('_', $columnNames)); + } + + $order = $index->getOrder() ?? []; + $columnNames = array_map(function ($columnName) use ($order) { + $ret = '"' . $columnName . '"'; + if (isset($order[$columnName])) { + $ret .= ' ' . $order[$columnName]; + } + + return $ret; + }, $columnNames); + + $includedColumns = $index->getInclude() ? sprintf('INCLUDE ("%s")', implode('","', $index->getInclude())) : ''; + + $createIndexSentence = 'CREATE %s INDEX %s ON %s '; + if ($index->getType() === self::GIN_INDEX_TYPE) { + $createIndexSentence .= ' USING ' . $index->getType() . '(%s) %s;'; + } else { + $createIndexSentence .= '(%s) %s;'; + } + + return sprintf( + $createIndexSentence, + ($index->getType() === Index::UNIQUE ? 'UNIQUE' : ''), + $this->quoteColumnName($indexName), + $this->quoteTableName($tableName), + implode(',', $columnNames), + $includedColumns + ); + } + + /** + * Gets the MySQL Foreign Key Definition for an ForeignKey object. + * + * @param \Phinx\Db\Table\ForeignKey $foreignKey Foreign key + * @param string $tableName Table name + * @return string + */ + protected function getForeignKeySqlDefinition(ForeignKey $foreignKey, string $tableName): string + { + $parts = $this->getSchemaName($tableName); + + $constraintName = $foreignKey->getConstraint() ?: ( + $parts['table'] . '_' . implode('_', $foreignKey->getColumns()) . '_fkey' + ); + $def = ' CONSTRAINT ' . $this->quoteColumnName($constraintName) . + ' FOREIGN KEY ("' . implode('", "', $foreignKey->getColumns()) . '")' . + " 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(): void + { + // Create the public/custom schema if it doesn't already exist + if ($this->hasSchema($this->getGlobalSchemaName()) === false) { + $this->createSchema($this->getGlobalSchemaName()); + } + + $this->setSearchPath(); + + parent::createSchemaTable(); + } + + /** + * @inheritDoc + */ + public function getVersions(): array + { + $this->setSearchPath(); + + return parent::getVersions(); + } + + /** + * @inheritDoc + */ + public function getVersionLog(): array + { + $this->setSearchPath(); + + return parent::getVersionLog(); + } + + /** + * Creates the specified schema. + * + * @param string $schemaName Schema Name + * @return void + */ + public function createSchema(string $schemaName = 'public'): void + { + // from postgres 9.3 we can use "CREATE SCHEMA IF NOT EXISTS schema_name" + $sql = sprintf('CREATE SCHEMA IF NOT EXISTS %s', $this->quoteSchemaName($schemaName)); + $this->execute($sql); + } + + /** + * Checks to see if a schema exists. + * + * @param string $schemaName Schema Name + * @return bool + */ + public function hasSchema(string $schemaName): bool + { + $sql = sprintf( + 'SELECT count(*) + FROM pg_namespace + WHERE nspname = %s', + $this->getConnection()->quote($schemaName) + ); + $result = $this->fetchRow($sql); + + return $result['count'] > 0; + } + + /** + * Drops the specified schema table. + * + * @param string $schemaName Schema name + * @return void + */ + public function dropSchema(string $schemaName): void + { + $sql = sprintf('DROP SCHEMA IF EXISTS %s CASCADE', $this->quoteSchemaName($schemaName)); + $this->execute($sql); + + foreach ($this->createdTables as $idx => $createdTable) { + if ($this->getSchemaName($createdTable)['schema'] === $this->quoteSchemaName($schemaName)) { + unset($this->createdTables[$idx]); + } + } + } + + /** + * Drops all schemas. + * + * @return void + */ + public function dropAllSchemas(): void + { + foreach ($this->getAllSchemas() as $schema) { + $this->dropSchema($schema); + } + } + + /** + * Returns schemas. + * + * @return array + */ + public function getAllSchemas(): array + { + $sql = "SELECT schema_name + FROM information_schema.schemata + WHERE schema_name <> 'information_schema' AND schema_name !~ '^pg_'"; + $items = $this->fetchAll($sql); + $schemaNames = []; + foreach ($items as $item) { + $schemaNames[] = $item['schema_name']; + } + + return $schemaNames; + } + + /** + * @inheritDoc + */ + public function getColumnTypes(): array + { + return array_merge(parent::getColumnTypes(), static::$specificColumnTypes); + } + + /** + * @inheritDoc + */ + public function isValidColumnType(Column $column): bool + { + // 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|\Phinx\Util\Literal $columnType Column type + * @return bool + */ + protected function isArrayType($columnType): bool + { + if (!preg_match('/^([a-z]+)(?:\[\]){1,}$/', $columnType, $matches)) { + return false; + } + + $baseType = $matches[1]; + + return in_array($baseType, $this->getColumnTypes(), true); + } + + /** + * @param string $tableName Table name + * @return array + */ + protected function getSchemaName(string $tableName): array + { + $schema = $this->getGlobalSchemaName(); + $table = $tableName; + if (strpos($tableName, '.') !== false) { + [$schema, $table] = explode('.', $tableName); + } + + return [ + 'schema' => $schema, + 'table' => $table, + ]; + } + + /** + * Gets the schema name. + * + * @return string + */ + protected function getGlobalSchemaName(): string + { + $options = $this->getOptions(); + + return empty($options['schema']) ? 'public' : $options['schema']; + } + + /** + * @inheritDoc + */ + public function castToBool($value) + { + return (bool)$value ? 'TRUE' : 'FALSE'; + } + + /** + * @inheritDoc + */ + protected function getDecoratedConnectionConfig(): array + { + $options = $this->getOptions(); + $options = [ + 'username' => $options['user'] ?? null, + 'password' => $options['pass'] ?? null, + 'database' => $options['name'], + 'quoteIdentifiers' => true, + ] + $options; + + return ['driver' => new PostgresDriver($options)] + $options; + } + + /** + * Sets search path of schemas to look through for a table + * + * @return void + */ + public function setSearchPath(): void + { + $this->execute( + sprintf( + 'SET search_path TO %s,"$user",public', + $this->quoteSchemaName($this->getGlobalSchemaName()) + ) + ); + } + + /** + * @inheritDoc + */ + public function insert(Table $table, array $row): void + { + $sql = sprintf( + 'INSERT INTO %s ', + $this->quoteTableName($table->getName()) + ); + $columns = array_keys($row); + $sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $columns)) . ')'; + + foreach ($row as $column => $value) { + if (is_bool($value)) { + $row[$column] = $this->castToBool($value); + } + } + + $override = ''; + if ($this->useIdentity) { + $override = self::OVERRIDE_SYSTEM_VALUE . ' '; + } + + if ($this->isDryRunEnabled()) { + $sql .= ' ' . $override . 'VALUES (' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ');'; + $this->output->writeln($sql); + } else { + $sql .= ' ' . $override . 'VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')'; + $stmt = $this->getConnection()->prepare($sql); + $stmt->execute(array_values($row)); + } + } + + /** + * @inheritDoc + */ + public function bulkinsert(Table $table, array $rows): void + { + $sql = sprintf( + 'INSERT INTO %s ', + $this->quoteTableName($table->getName()) + ); + $current = current($rows); + $keys = array_keys($current); + + $override = ''; + if ($this->useIdentity) { + $override = self::OVERRIDE_SYSTEM_VALUE . ' '; + } + + $sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $keys)) . ') ' . $override . 'VALUES '; + + if ($this->isDryRunEnabled()) { + $values = array_map(function ($row) { + return '(' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ')'; + }, $rows); + $sql .= implode(', ', $values) . ';'; + $this->output->writeln($sql); + } else { + $count_keys = count($keys); + $query = '(' . implode(', ', array_fill(0, $count_keys, '?')) . ')'; + $count_vars = count($rows); + $queries = array_fill(0, $count_vars, $query); + $sql .= implode(',', $queries); + $stmt = $this->getConnection()->prepare($sql); + $vals = []; + + foreach ($rows as $row) { + foreach ($row as $v) { + if (is_bool($v)) { + $vals[] = $this->castToBool($v); + } else { + $vals[] = $v; + } + } + } + + $stmt->execute($vals); + } + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/ProxyAdapter.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/ProxyAdapter.php new file mode 100644 index 0000000..979700c --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/ProxyAdapter.php @@ -0,0 +1,129 @@ + + */ +class ProxyAdapter extends AdapterWrapper +{ + /** + * @var \Phinx\Db\Action\Action[] + */ + protected $commands = []; + + /** + * @inheritDoc + */ + public function getAdapterType(): string + { + return 'ProxyAdapter'; + } + + /** + * @inheritDoc + */ + public function createTable(Table $table, array $columns = [], array $indexes = []): void + { + $this->commands[] = new CreateTable($table); + } + + /** + * @inheritDoc + */ + public function executeActions(Table $table, array $actions): void + { + $this->commands = array_merge($this->commands, $actions); + } + + /** + * Gets an array of the recorded commands in reverse. + * + * @throws \Phinx\Migration\IrreversibleMigrationException if a command cannot be reversed. + * @return \Phinx\Db\Plan\Intent + */ + public function getInvertedCommands(): Intent + { + $inverted = new Intent(); + + foreach (array_reverse($this->commands) as $command) { + switch (true) { + case $command instanceof CreateTable: + /** @var \Phinx\Db\Action\CreateTable $command */ + $inverted->addAction(new DropTable($command->getTable())); + break; + + case $command instanceof RenameTable: + /** @var \Phinx\Db\Action\RenameTable $command */ + $inverted->addAction(new RenameTable(new Table($command->getNewName()), $command->getTable()->getName())); + break; + + case $command instanceof AddColumn: + /** @var \Phinx\Db\Action\AddColumn $command */ + $inverted->addAction(new RemoveColumn($command->getTable(), $command->getColumn())); + break; + + case $command instanceof RenameColumn: + /** @var \Phinx\Db\Action\RenameColumn $command */ + $column = clone $command->getColumn(); + $name = $column->getName(); + $column->setName($command->getNewName()); + $inverted->addAction(new RenameColumn($command->getTable(), $column, $name)); + break; + + case $command instanceof AddIndex: + /** @var \Phinx\Db\Action\AddIndex $command */ + $inverted->addAction(new DropIndex($command->getTable(), $command->getIndex())); + break; + + case $command instanceof AddForeignKey: + /** @var \Phinx\Db\Action\AddForeignKey $command */ + $inverted->addAction(new DropForeignKey($command->getTable(), $command->getForeignKey())); + break; + + default: + throw new IrreversibleMigrationException(sprintf( + 'Cannot reverse a "%s" command', + get_class($command) + )); + } + } + + return $inverted; + } + + /** + * Execute the recorded commands in reverse. + * + * @return void + */ + public function executeInvertedCommands(): void + { + $plan = new Plan($this->getInvertedCommands()); + $plan->executeInverse($this->getAdapter()); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php new file mode 100644 index 0000000..c15ecff --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php @@ -0,0 +1,1953 @@ + + * @author Richard McIntyre + */ +class SQLiteAdapter extends PdoAdapter +{ + public const MEMORY = ':memory:'; + + /** + * List of supported Phinx column types with their SQL equivalents + * some types have an affinity appended to ensure they do not receive NUMERIC affinity + * + * @var string[] + */ + protected static $supportedColumnTypes = [ + self::PHINX_TYPE_BIG_INTEGER => 'biginteger', + self::PHINX_TYPE_BINARY => 'binary_blob', + self::PHINX_TYPE_BINARYUUID => 'binary_blob', + self::PHINX_TYPE_BLOB => 'blob', + self::PHINX_TYPE_BOOLEAN => 'boolean_integer', + self::PHINX_TYPE_CHAR => 'char', + self::PHINX_TYPE_DATE => 'date_text', + self::PHINX_TYPE_DATETIME => 'datetime_text', + self::PHINX_TYPE_DECIMAL => 'decimal', + self::PHINX_TYPE_DOUBLE => 'double', + self::PHINX_TYPE_FLOAT => 'float', + self::PHINX_TYPE_INTEGER => 'integer', + self::PHINX_TYPE_JSON => 'json_text', + self::PHINX_TYPE_JSONB => 'jsonb_text', + self::PHINX_TYPE_SMALL_INTEGER => 'smallinteger', + self::PHINX_TYPE_STRING => 'varchar', + self::PHINX_TYPE_TEXT => 'text', + self::PHINX_TYPE_TIME => 'time_text', + self::PHINX_TYPE_TIMESTAMP => 'timestamp_text', + self::PHINX_TYPE_TINY_INTEGER => 'tinyinteger', + self::PHINX_TYPE_UUID => 'uuid_text', + self::PHINX_TYPE_VARBINARY => 'varbinary_blob', + ]; + + /** + * List of aliases of supported column types + * + * @var string[] + */ + protected static $supportedColumnTypeAliases = [ + 'varchar' => self::PHINX_TYPE_STRING, + 'tinyint' => self::PHINX_TYPE_TINY_INTEGER, + 'tinyinteger' => self::PHINX_TYPE_TINY_INTEGER, + 'smallint' => self::PHINX_TYPE_SMALL_INTEGER, + 'int' => self::PHINX_TYPE_INTEGER, + 'mediumint' => self::PHINX_TYPE_INTEGER, + 'mediuminteger' => self::PHINX_TYPE_INTEGER, + 'bigint' => self::PHINX_TYPE_BIG_INTEGER, + 'tinytext' => self::PHINX_TYPE_TEXT, + 'mediumtext' => self::PHINX_TYPE_TEXT, + 'longtext' => self::PHINX_TYPE_TEXT, + 'tinyblob' => self::PHINX_TYPE_BLOB, + 'mediumblob' => self::PHINX_TYPE_BLOB, + 'longblob' => self::PHINX_TYPE_BLOB, + 'real' => self::PHINX_TYPE_FLOAT, + ]; + + /** + * List of known but unsupported Phinx column types + * + * @var string[] + */ + protected static $unsupportedColumnTypes = [ + self::PHINX_TYPE_BIT, + self::PHINX_TYPE_CIDR, + self::PHINX_TYPE_ENUM, + self::PHINX_TYPE_FILESTREAM, + self::PHINX_TYPE_GEOMETRY, + self::PHINX_TYPE_INET, + self::PHINX_TYPE_INTERVAL, + self::PHINX_TYPE_LINESTRING, + self::PHINX_TYPE_MACADDR, + self::PHINX_TYPE_POINT, + self::PHINX_TYPE_POLYGON, + self::PHINX_TYPE_SET, + ]; + + /** + * @var string[] + */ + protected $definitionsWithLimits = [ + 'CHAR', + 'CHARACTER', + 'VARCHAR', + 'VARYING CHARACTER', + 'NCHAR', + 'NATIVE CHARACTER', + 'NVARCHAR', + ]; + + /** + * @var string + */ + protected $suffix = '.sqlite3'; + + /** + * Indicates whether the database library version is at least the specified version + * + * @param string $ver The version to check against e.g. '3.28.0' + * @return bool + */ + public function databaseVersionAtLeast($ver): bool + { + $actual = $this->query('SELECT sqlite_version()')->fetchColumn(); + + return version_compare($actual, $ver, '>='); + } + + /** + * {@inheritDoc} + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @return void + */ + public function connect(): void + { + if ($this->connection === null) { + 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 + } + + $options = $this->getOptions(); + + if (PHP_VERSION_ID < 80100 && (!empty($options['mode']) || !empty($options['cache']))) { + throw new RuntimeException('SQLite URI support requires PHP 8.1.'); + } elseif ((!empty($options['mode']) || !empty($options['cache'])) && !empty($options['memory'])) { + throw new RuntimeException('Memory must not be set when cache or mode are.'); + } elseif (PHP_VERSION_ID >= 80100 && (!empty($options['mode']) || !empty($options['cache']))) { + $params = []; + if (!empty($options['cache'])) { + $params[] = 'cache=' . $options['cache']; + } + if (!empty($options['mode'])) { + $params[] = 'mode=' . $options['mode']; + } + $dsn = 'sqlite:file:' . ($options['name'] ?? '') . '?' . implode('&', $params); + } else { + // use a memory database if the option was specified + if (!empty($options['memory']) || $options['name'] === static::MEMORY) { + $dsn = 'sqlite:' . static::MEMORY; + } else { + $dsn = 'sqlite:' . $options['name'] . $this->suffix; + } + } + + $driverOptions = []; + + // use custom data fetch mode + if (!empty($options['fetch_mode'])) { + $driverOptions[PDO::ATTR_DEFAULT_FETCH_MODE] = constant('\PDO::FETCH_' . strtoupper($options['fetch_mode'])); + } + + // pass \PDO::ATTR_PERSISTENT to driver options instead of useless setting it after instantiation + if (isset($options['attr_persistent'])) { + $driverOptions[PDO::ATTR_PERSISTENT] = $options['attr_persistent']; + } + + $db = $this->createPdoConnection($dsn, null, null, $driverOptions); + + $this->setConnection($db); + } + } + + /** + * @inheritDoc + */ + public function setOptions(array $options): AdapterInterface + { + parent::setOptions($options); + + if (isset($options['suffix'])) { + $this->suffix = $options['suffix']; + } + //don't "fix" the file extension if it is blank, some people + //might want a SQLITE db file with absolutely no extension. + if ($this->suffix !== '' && strpos($this->suffix, '.') !== 0) { + $this->suffix = '.' . $this->suffix; + } + + return $this; + } + + /** + * @inheritDoc + */ + public function disconnect(): void + { + $this->connection = null; + } + + /** + * @inheritDoc + */ + public function hasTransactions(): bool + { + return true; + } + + /** + * @inheritDoc + */ + public function beginTransaction(): void + { + $this->getConnection()->beginTransaction(); + } + + /** + * @inheritDoc + */ + public function commitTransaction(): void + { + $this->getConnection()->commit(); + } + + /** + * @inheritDoc + */ + public function rollbackTransaction(): void + { + $this->getConnection()->rollBack(); + } + + /** + * @inheritDoc + */ + public function quoteTableName($tableName): string + { + return str_replace('.', '`.`', $this->quoteColumnName($tableName)); + } + + /** + * @inheritDoc + */ + public function quoteColumnName($columnName): string + { + return '`' . str_replace('`', '``', $columnName) . '`'; + } + + /** + * @param string $tableName Table name + * @param bool $quoted Whether to return the schema name and table name escaped and quoted. If quoted, the schema (if any) will also be appended with a dot + * @return array + */ + protected function getSchemaName(string $tableName, bool $quoted = false): array + { + if (preg_match("/.\.([^\.]+)$/", $tableName, $match)) { + $table = $match[1]; + $schema = substr($tableName, 0, strlen($tableName) - strlen($match[0]) + 1); + $result = ['schema' => $schema, 'table' => $table]; + } else { + $result = ['schema' => '', 'table' => $tableName]; + } + + if ($quoted) { + $result['schema'] = $result['schema'] !== '' ? $this->quoteColumnName($result['schema']) . '.' : ''; + $result['table'] = $this->quoteColumnName($result['table']); + } + + return $result; + } + + /** + * Retrieves information about a given table from one of the SQLite pragmas + * + * @param string $tableName The table to query + * @param string $pragma The pragma to query + * @return array + */ + protected function getTableInfo(string $tableName, string $pragma = 'table_info'): array + { + $info = $this->getSchemaName($tableName, true); + + return $this->fetchAll(sprintf('PRAGMA %s%s(%s)', $info['schema'], $pragma, $info['table'])); + } + + /** + * Searches through all available schemata to find a table and returns an array + * containing the bare schema name and whether the table exists at all. + * If no schema was specified and the table does not exist the "main" schema is returned + * + * @param string $tableName The name of the table to find + * @return array + */ + protected function resolveTable(string $tableName): array + { + $info = $this->getSchemaName($tableName); + if ($info['schema'] === '') { + // if no schema is specified we search all schemata + $rows = $this->fetchAll('PRAGMA database_list;'); + // the temp schema is always first to be searched + $schemata = ['temp']; + foreach ($rows as $row) { + if (strtolower($row['name']) !== 'temp') { + $schemata[] = $row['name']; + } + } + $defaultSchema = 'main'; + } else { + // otherwise we search just the specified schema + $schemata = (array)$info['schema']; + $defaultSchema = $info['schema']; + } + + $table = strtolower($info['table']); + foreach ($schemata as $schema) { + if (strtolower($schema) === 'temp') { + $master = 'sqlite_temp_master'; + } else { + $master = sprintf('%s.%s', $this->quoteColumnName($schema), 'sqlite_master'); + } + try { + $rows = $this->fetchAll(sprintf("SELECT name FROM %s WHERE type='table' AND lower(name) = %s", $master, $this->quoteString($table))); + } catch (PDOException $e) { + // an exception can occur if the schema part of the table refers to a database which is not attached + break; + } + + // this somewhat pedantic check with strtolower is performed because the SQL lower function may be redefined, + // and can act on all Unicode characters if the ICU extension is loaded, while SQL identifiers are only case-insensitive for ASCII + foreach ($rows as $row) { + if (strtolower($row['name']) === $table) { + return ['schema' => $schema, 'table' => $row['name'], 'exists' => true]; + } + } + } + + return ['schema' => $defaultSchema, 'table' => $info['table'], 'exists' => false]; + } + + /** + * @inheritDoc + */ + public function hasTable(string $tableName): bool + { + return $this->hasCreatedTable($tableName) || $this->resolveTable($tableName)['exists']; + } + + /** + * @inheritDoc + */ + public function createTable(Table $table, array $columns = [], array $indexes = []): void + { + // Add the default primary key + $options = $table->getOptions(); + 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') + ->setOptions(['identity' => 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) . ', '; + + if (isset($options['primary_key']) && $column->getIdentity()) { + //remove column from the primary key array as it is already defined as an autoincrement + //primary id + $identityColumnIndex = array_search($column->getName(), $options['primary_key'], true); + if ($identityColumnIndex !== false) { + unset($options['primary_key'][$identityColumnIndex]); + + if (empty($options['primary_key'])) { + //The last primary key has been removed + unset($options['primary_key']); + } + } + } + } + + // 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') + $sql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key'])); + } + $sql .= ')'; + } else { + $sql = substr(rtrim($sql), 0, -1); // no primary keys + } + + $sql = rtrim($sql) . ');'; + // execute the sql + $this->execute($sql); + + foreach ($indexes as $index) { + $this->addIndex($table, $index); + } + + $this->addCreatedTable($table->getName()); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getChangePrimaryKeyInstructions(Table $table, $newColumns): AlterInstructions + { + $instructions = new AlterInstructions(); + + // Drop the existing primary key + $primaryKey = $this->getPrimaryKey($table->getName()); + if (!empty($primaryKey)) { + $instructions->merge( + // FIXME: array access is a hack to make this incomplete implementation work with a correct getPrimaryKey implementation + $this->getDropPrimaryKeyInstructions($table, $primaryKey[0]) + ); + } + + // Add the primary key(s) + if (!empty($newColumns)) { + if (!is_string($newColumns)) { + throw new InvalidArgumentException(sprintf( + 'Invalid value for primary key: %s', + json_encode($newColumns) + )); + } + + $instructions->merge( + $this->getAddPrimaryKeyInstructions($table, $newColumns) + ); + } + + return $instructions; + } + + /** + * {@inheritDoc} + * + * SQLiteAdapter does not implement this functionality, and so will always throw an exception if used. + * + * @throws \BadMethodCallException + */ + protected function getChangeCommentInstructions(Table $table, $newComment): AlterInstructions + { + throw new BadMethodCallException('SQLite does not have table comments'); + } + + /** + * @inheritDoc + */ + protected function getRenameTableInstructions(string $tableName, string $newTableName): AlterInstructions + { + $this->updateCreatedTableName($tableName, $newTableName); + $sql = sprintf( + 'ALTER TABLE %s RENAME TO %s', + $this->quoteTableName($tableName), + $this->quoteTableName($newTableName) + ); + + return new AlterInstructions([], [$sql]); + } + + /** + * @inheritDoc + */ + protected function getDropTableInstructions(string $tableName): AlterInstructions + { + $this->removeCreatedTable($tableName); + $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName)); + + return new AlterInstructions([], [$sql]); + } + + /** + * @inheritDoc + */ + public function truncateTable(string $tableName): void + { + $info = $this->resolveTable($tableName); + // first try deleting the rows + $this->execute(sprintf( + 'DELETE FROM %s.%s', + $this->quoteColumnName($info['schema']), + $this->quoteColumnName($info['table']) + )); + + // assuming no error occurred, reset the autoincrement (if any) + if ($this->hasTable($info['schema'] . '.sqlite_sequence')) { + $this->execute(sprintf( + 'DELETE FROM %s.%s where name = %s', + $this->quoteColumnName($info['schema']), + 'sqlite_sequence', + $this->quoteString($info['table']) + )); + } + } + + /** + * Parses a default-value expression to yield either a Literal representing + * a string value, a string representing an expression, or some other scalar + * + * @param mixed $default The default-value expression to interpret + * @param string $columnType The Phinx type of the column + * @return mixed + */ + protected function parseDefaultValue($default, string $columnType) + { + if ($default === null) { + return null; + } + + // split the input into tokens + $trimChars = " \t\n\r\0\x0B"; + $pattern = <<getTableInfo($tableName) as $col) { + $type = strtolower($col['type']); + if ($col['pk'] > 1) { + // the table has a composite primary key + return null; + } elseif ($col['pk'] == 0) { + // the column is not a primary key column and is thus not relevant + continue; + } elseif ($type !== 'integer') { + // if the primary key's type is not exactly INTEGER, it cannot be a row ID alias + return null; + } else { + // the column is a candidate for a row ID alias + $result = $col['name']; + } + } + // if there is no suitable PK column, stop now + if ($result === null) { + return null; + } + // make sure the table does not have a PK-origin autoindex + // such an autoindex would indicate either that the primary key was specified as descending, or that this is a WITHOUT ROWID table + foreach ($this->getTableInfo($tableName, 'index_list') as $idx) { + if ($idx['origin'] === 'pk') { + return null; + } + } + + return $result; + } + + /** + * @inheritDoc + */ + public function getColumns(string $tableName): array + { + $columns = []; + + $rows = $this->getTableInfo($tableName); + $identity = $this->resolveIdentity($tableName); + + foreach ($rows as $columnInfo) { + $column = new Column(); + $type = $this->getPhinxType($columnInfo['type']); + $default = $this->parseDefaultValue($columnInfo['dflt_value'], $type['name']); + + $column->setName($columnInfo['name']) + // SQLite on PHP 8.1 returns int for notnull, older versions return a string + ->setNull((int)$columnInfo['notnull'] !== 1) + ->setDefault($default) + ->setType($type['name']) + ->setLimit($type['limit']) + ->setScale($type['scale']) + ->setIdentity($columnInfo['name'] === $identity); + + $columns[] = $column; + } + + return $columns; + } + + /** + * @inheritDoc + */ + public function hasColumn(string $tableName, string $columnName): bool + { + $rows = $this->getTableInfo($tableName); + foreach ($rows as $column) { + if (strcasecmp($column['name'], $columnName) === 0) { + return true; + } + } + + return false; + } + + /** + * @inheritDoc + */ + protected function getAddColumnInstructions(Table $table, Column $column): AlterInstructions + { + $tableName = $table->getName(); + + $instructions = $this->beginAlterByCopyTable($tableName); + + $instructions->addPostStep(function ($state) use ($tableName, $column) { + // we use the final column to anchor our regex to insert the new column, + // as the alternative is unwinding all possible table constraints which + // gets messy quickly with CHECK constraints. + $columns = $this->getColumns($tableName); + if (!$columns) { + return $state; + } + $finalColumnName = end($columns)->getName(); + $sql = preg_replace( + sprintf( + "/(%s(?:\/\*.*?\*\/|\([^)]+\)|'[^']*?'|[^,])+)([,)])/", + $this->quoteColumnName($finalColumnName) + ), + sprintf( + '$1, %s %s$2', + $this->quoteColumnName($column->getName()), + $this->getColumnSqlDefinition($column) + ), + $state['createSQL'], + 1 + ); + $this->execute($sql); + + return $state; + }); + + $instructions->addPostStep(function ($state) use ($tableName) { + $newState = $this->calculateNewTableColumns($tableName, false, false); + + return $newState + $state; + }); + + return $this->endAlterByCopyTable($instructions, $tableName); + } + + /** + * Returns the original CREATE statement for the give table + * + * @param string $tableName The table name to get the create statement for + * @return string + */ + protected function getDeclaringSql(string $tableName): string + { + $rows = $this->fetchAll("SELECT * FROM sqlite_master WHERE `type` = 'table'"); + + $sql = ''; + foreach ($rows as $table) { + if ($table['tbl_name'] === $tableName) { + $sql = $table['sql']; + } + } + + $columnsInfo = $this->getTableInfo($tableName); + + foreach ($columnsInfo as $column) { + $columnName = $column['name']; + $columnNamePattern = "\"$columnName\"|`$columnName`|\\[$columnName\\]|$columnName"; + $columnNamePattern = "#([\(,]+\\s*)($columnNamePattern)(\\s)#iU"; + + $sql = preg_replace($columnNamePattern, "$1`$columnName`$3", $sql); + } + + $tableNamePattern = "\"$tableName\"|`$tableName`|\\[$tableName\\]|$tableName"; + $tableNamePattern = "#^(CREATE TABLE)\s*($tableNamePattern)\s*(\()#Ui"; + + $sql = preg_replace($tableNamePattern, "$1 `$tableName` $3", $sql, 1); + + return $sql; + } + + /** + * Returns the original CREATE statement for the give index + * + * @param string $tableName The table name to get the create statement for + * @param string $indexName The table index + * @return string + */ + protected function getDeclaringIndexSql(string $tableName, string $indexName): string + { + $rows = $this->fetchAll("SELECT * FROM sqlite_master WHERE `type` = 'index'"); + + $sql = ''; + foreach ($rows as $table) { + if ($table['tbl_name'] === $tableName && $table['name'] === $indexName) { + $sql = $table['sql'] . '; '; + } + } + + return $sql; + } + + /** + * Obtains index and trigger information for a table. + * + * They will be stored in the state as arrays under the `indices` and `triggers` + * keys accordingly. + * + * Index columns defined as expressions, as for example in `ON (ABS(id), other)`, + * will appear as `null`, so for the given example the columns for the index would + * look like `[null, 'other']`. + * + * @param \Phinx\Db\Util\AlterInstructions $instructions The instructions to modify + * @param string $tableName The name of table being processed + * @return \Phinx\Db\Util\AlterInstructions + */ + protected function bufferIndicesAndTriggers(AlterInstructions $instructions, string $tableName): AlterInstructions + { + $instructions->addPostStep(function (array $state) use ($tableName): array { + $state['indices'] = []; + $state['triggers'] = []; + + $rows = $this->fetchAll( + sprintf( + " + SELECT * + FROM sqlite_master + WHERE + (`type` = 'index' OR `type` = 'trigger') + AND tbl_name = %s + AND sql IS NOT NULL + ", + $this->quoteValue($tableName) + ) + ); + + $schema = $this->getSchemaName($tableName, true)['schema']; + + foreach ($rows as $row) { + switch ($row['type']) { + case 'index': + $info = $this->fetchAll( + sprintf('PRAGMA %sindex_info(%s)', $schema, $this->quoteValue($row['name'])) + ); + + $columns = array_map( + function ($column) { + if ($column === null) { + return null; + } + + return strtolower($column); + }, + array_column($info, 'name') + ); + $hasExpressions = in_array(null, $columns, true); + + $index = [ + 'columns' => $columns, + 'hasExpressions' => $hasExpressions, + ]; + + $state['indices'][] = $index + $row; + break; + + case 'trigger': + $state['triggers'][] = $row; + break; + } + } + + return $state; + }); + + return $instructions; + } + + /** + * Filters out indices that reference a removed column. + * + * @param \Phinx\Db\Util\AlterInstructions $instructions The instructions to modify + * @param string $columnName The name of the removed column + * @return \Phinx\Db\Util\AlterInstructions + */ + protected function filterIndicesForRemovedColumn( + AlterInstructions $instructions, + string $columnName + ): AlterInstructions { + $instructions->addPostStep(function (array $state) use ($columnName): array { + foreach ($state['indices'] as $key => $index) { + if ( + !$index['hasExpressions'] && + in_array(strtolower($columnName), $index['columns'], true) + ) { + unset($state['indices'][$key]); + } + } + + return $state; + }); + + return $instructions; + } + + /** + * Updates indices that reference a renamed column. + * + * @param \Phinx\Db\Util\AlterInstructions $instructions The instructions to modify + * @param string $oldColumnName The old column name + * @param string $newColumnName The new column name + * @return \Phinx\Db\Util\AlterInstructions + */ + protected function updateIndicesForRenamedColumn( + AlterInstructions $instructions, + string $oldColumnName, + string $newColumnName + ): AlterInstructions { + $instructions->addPostStep(function (array $state) use ($oldColumnName, $newColumnName): array { + foreach ($state['indices'] as $key => $index) { + if ( + !$index['hasExpressions'] && + in_array(strtolower($oldColumnName), $index['columns'], true) + ) { + $pattern = ' + / + (INDEX.+?ON\s.+?) + (\(\s*|,\s*) # opening parenthesis or comma + (?:`|"|\[)? # optional opening quote + (%s) # column name + (?:`|"|\])? # optional closing quote + (\s+COLLATE\s+.+?)? # optional collation + (\s+(?:ASC|DESC))? # optional order + (\s*,|\s*\)) # comma or closing parenthesis + /isx'; + + $newColumnName = $this->quoteColumnName($newColumnName); + + $state['indices'][$key]['sql'] = preg_replace( + sprintf($pattern, preg_quote($oldColumnName, '/')), + "\\1\\2$newColumnName\\4\\5\\6", + $index['sql'] + ); + } + } + + return $state; + }); + + return $instructions; + } + + /** + * Recreates indices and triggers. + * + * @param \Phinx\Db\Util\AlterInstructions $instructions The instructions to process + * @return \Phinx\Db\Util\AlterInstructions + */ + protected function recreateIndicesAndTriggers(AlterInstructions $instructions): AlterInstructions + { + $instructions->addPostStep(function (array $state): array { + foreach ($state['indices'] as $index) { + $this->execute($index['sql']); + } + + foreach ($state['triggers'] as $trigger) { + $this->execute($trigger['sql']); + } + + return $state; + }); + + return $instructions; + } + + /** + * Returns instructions for validating the foreign key constraints of + * the given table, and of those tables whose constraints are + * targeting it. + * + * @param \Phinx\Db\Util\AlterInstructions $instructions The instructions to process + * @param string $tableName The name of the table for which to check constraints. + * @return \Phinx\Db\Util\AlterInstructions + */ + protected function validateForeignKeys(AlterInstructions $instructions, string $tableName): AlterInstructions + { + $instructions->addPostStep(function ($state) use ($tableName) { + $tablesToCheck = [ + $tableName, + ]; + + $otherTables = $this + ->query( + "SELECT name FROM sqlite_master WHERE type = 'table' AND name != ?", + [$tableName] + ) + ->fetchAll(); + + foreach ($otherTables as $otherTable) { + $foreignKeyList = $this->getTableInfo($otherTable['name'], 'foreign_key_list'); + foreach ($foreignKeyList as $foreignKey) { + if (strcasecmp($foreignKey['table'], $tableName) === 0) { + $tablesToCheck[] = $otherTable['name']; + break; + } + } + } + + $tablesToCheck = array_unique(array_map('strtolower', $tablesToCheck)); + + foreach ($tablesToCheck as $tableToCheck) { + $schema = $this->getSchemaName($tableToCheck, true)['schema']; + + $stmt = $this->query( + sprintf('PRAGMA %sforeign_key_check(%s)', $schema, $this->quoteTableName($tableToCheck)) + ); + $row = $stmt->fetch(); + $stmt->closeCursor(); + + if (is_array($row)) { + throw new RuntimeException(sprintf( + 'Integrity constraint violation: FOREIGN KEY constraint on `%s` failed.', + $tableToCheck + )); + } + } + + return $state; + }); + + return $instructions; + } + + /** + * Copies all the data from a tmp table to another table + * + * @param string $tableName The table name to copy the data to + * @param string $tmpTableName The tmp table name where the data is stored + * @param string[] $writeColumns The list of columns in the target table + * @param string[] $selectColumns The list of columns in the tmp table + * @return void + */ + protected function copyDataToNewTable(string $tableName, string $tmpTableName, array $writeColumns, array $selectColumns): void + { + $sql = sprintf( + 'INSERT INTO %s(%s) SELECT %s FROM %s', + $this->quoteTableName($tableName), + implode(', ', $writeColumns), + implode(', ', $selectColumns), + $this->quoteTableName($tmpTableName) + ); + $this->execute($sql); + } + + /** + * Modifies the passed instructions to copy all data from the table into + * the provided tmp table and then drops the table and rename tmp table. + * + * @param \Phinx\Db\Util\AlterInstructions $instructions The instructions to modify + * @param string $tableName The table name to copy the data to + * @return \Phinx\Db\Util\AlterInstructions + */ + protected function copyAndDropTmpTable(AlterInstructions $instructions, string $tableName): AlterInstructions + { + $instructions->addPostStep(function ($state) use ($tableName) { + $this->copyDataToNewTable( + $state['tmpTableName'], + $tableName, + $state['writeColumns'], + $state['selectColumns'] + ); + + $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName))); + $this->execute(sprintf( + 'ALTER TABLE %s RENAME TO %s', + $this->quoteTableName($state['tmpTableName']), + $this->quoteTableName($tableName) + )); + + return $state; + }); + + return $instructions; + } + + /** + * Returns the columns and type to use when copying a table to another in the process + * of altering a table + * + * @param string $tableName The table to modify + * @param string|false $columnName The column name that is about to change + * @param string|false $newColumnName Optionally the new name for the column + * @throws \InvalidArgumentException + * @return array + */ + protected function calculateNewTableColumns(string $tableName, $columnName, $newColumnName): array + { + $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName))); + $selectColumns = []; + $writeColumns = []; + $columnType = null; + $found = false; + + foreach ($columns as $column) { + $selectName = $column['name']; + $writeName = $selectName; + + if ($selectName === $columnName) { + $writeName = $newColumnName; + $found = true; + $columnType = $column['type']; + $selectName = $newColumnName === false ? $newColumnName : $selectName; + } + + $selectColumns[] = $selectName; + $writeColumns[] = $writeName; + } + + $selectColumns = array_filter($selectColumns, 'strlen'); + $writeColumns = array_filter($writeColumns, 'strlen'); + $selectColumns = array_map([$this, 'quoteColumnName'], $selectColumns); + $writeColumns = array_map([$this, 'quoteColumnName'], $writeColumns); + + if ($columnName && !$found) { + throw new InvalidArgumentException(sprintf( + 'The specified column doesn\'t exist: ' . $columnName + )); + } + + return compact('writeColumns', 'selectColumns', 'columnType'); + } + + /** + * Returns the initial instructions to alter a table using the + * create-copy-drop strategy + * + * @param string $tableName The table to modify + * @return \Phinx\Db\Util\AlterInstructions + */ + protected function beginAlterByCopyTable(string $tableName): AlterInstructions + { + $instructions = new AlterInstructions(); + $instructions->addPostStep(function ($state) use ($tableName) { + $tmpTableName = "tmp_{$tableName}"; + $createSQL = $this->getDeclaringSql($tableName); + + // Table name in SQLite can be hilarious inside declaring SQL: + // - tableName + // - `tableName` + // - "tableName" + // - [this is a valid table name too!] + // - etc. + // Just remove all characters before first "(" and build them again + $createSQL = preg_replace( + "/^CREATE TABLE .* \(/Ui", + '', + $createSQL + ); + + $createSQL = "CREATE TABLE {$this->quoteTableName($tmpTableName)} ({$createSQL}"; + + return compact('createSQL', 'tmpTableName') + $state; + }); + + return $instructions; + } + + /** + * Returns the final instructions to alter a table using the + * create-copy-drop strategy. + * + * @param \Phinx\Db\Util\AlterInstructions $instructions The instructions to modify + * @param string $tableName The name of table being processed + * @param ?string $renamedOrRemovedColumnName The name of the renamed or removed column when part of a column + * rename/drop operation. + * @param ?string $newColumnName The new column name when part of a column rename operation. + * @param bool $validateForeignKeys Whether to validate foreign keys after the copy and drop operations. Note that + * enabling this option only has an effect when the `foreign_keys` PRAGMA is set to `ON`! + * @return \Phinx\Db\Util\AlterInstructions + */ + protected function endAlterByCopyTable( + AlterInstructions $instructions, + string $tableName, + ?string $renamedOrRemovedColumnName = null, + ?string $newColumnName = null, + bool $validateForeignKeys = true + ): AlterInstructions { + $instructions = $this->bufferIndicesAndTriggers($instructions, $tableName); + + if ($renamedOrRemovedColumnName !== null) { + if ($newColumnName !== null) { + $this->updateIndicesForRenamedColumn($instructions, $renamedOrRemovedColumnName, $newColumnName); + } else { + $this->filterIndicesForRemovedColumn($instructions, $renamedOrRemovedColumnName); + } + } + + $foreignKeysEnabled = (bool)$this->fetchRow('PRAGMA foreign_keys')['foreign_keys']; + + if ($foreignKeysEnabled) { + $instructions->addPostStep('PRAGMA foreign_keys = OFF'); + } + + $instructions = $this->copyAndDropTmpTable($instructions, $tableName); + $instructions = $this->recreateIndicesAndTriggers($instructions); + + if ($foreignKeysEnabled) { + $instructions->addPostStep('PRAGMA foreign_keys = ON'); + } + + if ( + $foreignKeysEnabled && + $validateForeignKeys + ) { + $instructions = $this->validateForeignKeys($instructions, $tableName); + } + + return $instructions; + } + + /** + * @inheritDoc + */ + protected function getRenameColumnInstructions(string $tableName, string $columnName, string $newColumnName): AlterInstructions + { + $instructions = $this->beginAlterByCopyTable($tableName); + + $instructions->addPostStep(function ($state) use ($columnName, $newColumnName) { + $sql = str_replace( + $this->quoteColumnName($columnName), + $this->quoteColumnName($newColumnName), + $state['createSQL'] + ); + $this->execute($sql); + + return $state; + }); + + $instructions->addPostStep(function ($state) use ($columnName, $newColumnName, $tableName) { + $newState = $this->calculateNewTableColumns($tableName, $columnName, $newColumnName); + + return $newState + $state; + }); + + return $this->endAlterByCopyTable($instructions, $tableName, $columnName, $newColumnName); + } + + /** + * @inheritDoc + */ + protected function getChangeColumnInstructions(string $tableName, string $columnName, Column $newColumn): AlterInstructions + { + $instructions = $this->beginAlterByCopyTable($tableName); + + $newColumnName = $newColumn->getName(); + $instructions->addPostStep(function ($state) use ($columnName, $newColumn) { + $sql = preg_replace( + sprintf("/%s(?:\/\*.*?\*\/|\([^)]+\)|'[^']*?'|[^,])+([,)])/", $this->quoteColumnName($columnName)), + sprintf('%s %s$1', $this->quoteColumnName($newColumn->getName()), $this->getColumnSqlDefinition($newColumn)), + $state['createSQL'], + 1 + ); + $this->execute($sql); + + return $state; + }); + + $instructions->addPostStep(function ($state) use ($columnName, $newColumnName, $tableName) { + $newState = $this->calculateNewTableColumns($tableName, $columnName, $newColumnName); + + return $newState + $state; + }); + + return $this->endAlterByCopyTable($instructions, $tableName); + } + + /** + * @inheritDoc + */ + protected function getDropColumnInstructions(string $tableName, string $columnName): AlterInstructions + { + $instructions = $this->beginAlterByCopyTable($tableName); + + $instructions->addPostStep(function ($state) use ($tableName, $columnName) { + $newState = $this->calculateNewTableColumns($tableName, $columnName, false); + + return $newState + $state; + }); + + $instructions->addPostStep(function ($state) use ($columnName) { + $sql = preg_replace( + sprintf("/%s\s%s.*(,\s(?!')|\)$)/U", preg_quote($this->quoteColumnName($columnName)), preg_quote($state['columnType'])), + '', + $state['createSQL'] + ); + + if (substr($sql, -2) === ', ') { + $sql = substr($sql, 0, -2) . ')'; + } + + $this->execute($sql); + + return $state; + }); + + return $this->endAlterByCopyTable($instructions, $tableName, $columnName); + } + + /** + * Get an array of indexes from a particular table. + * + * @param string $tableName Table name + * @return array + */ + protected function getIndexes(string $tableName): array + { + $indexes = []; + $schema = $this->getSchemaName($tableName, true)['schema']; + $indexList = $this->getTableInfo($tableName, 'index_list'); + + foreach ($indexList as $index) { + $indexData = $this->fetchAll(sprintf('pragma %sindex_info(%s)', $schema, $this->quoteColumnName($index['name']))); + $cols = []; + foreach ($indexData as $indexItem) { + $cols[] = $indexItem['name']; + } + $indexes[$index['name']] = $cols; + } + + return $indexes; + } + + /** + * Finds the names of a table's indexes matching the supplied columns + * + * @param string $tableName The table to which the index belongs + * @param string|string[] $columns The columns of the index + * @return array + */ + protected function resolveIndex(string $tableName, $columns): array + { + $columns = array_map('strtolower', (array)$columns); + $indexes = $this->getIndexes($tableName); + $matches = []; + + foreach ($indexes as $name => $index) { + $indexCols = array_map('strtolower', $index); + if ($columns == $indexCols) { + $matches[] = $name; + } + } + + return $matches; + } + + /** + * @inheritDoc + */ + public function hasIndex(string $tableName, $columns): bool + { + return (bool)$this->resolveIndex($tableName, $columns); + } + + /** + * @inheritDoc + */ + public function hasIndexByName(string $tableName, string $indexName): bool + { + $indexName = strtolower($indexName); + $indexes = $this->getIndexes($tableName); + + foreach (array_keys($indexes) as $index) { + if ($indexName === strtolower($index)) { + return true; + } + } + + return false; + } + + /** + * @inheritDoc + */ + protected function getAddIndexInstructions(Table $table, Index $index): AlterInstructions + { + $indexColumnArray = []; + foreach ($index->getColumns() as $column) { + $indexColumnArray[] = sprintf('`%s` ASC', $column); + } + $indexColumns = implode(',', $indexColumnArray); + $sql = sprintf( + 'CREATE %s ON %s (%s)', + $this->getIndexSqlDefinition($table, $index), + $this->quoteTableName($table->getName()), + $indexColumns + ); + + return new AlterInstructions([], [$sql]); + } + + /** + * @inheritDoc + */ + protected function getDropIndexByColumnsInstructions(string $tableName, $columns): AlterInstructions + { + $instructions = new AlterInstructions(); + $indexNames = $this->resolveIndex($tableName, $columns); + $schema = $this->getSchemaName($tableName, true)['schema']; + foreach ($indexNames as $indexName) { + if (strpos($indexName, 'sqlite_autoindex_') !== 0) { + $instructions->addPostStep(sprintf( + 'DROP INDEX %s%s', + $schema, + $this->quoteColumnName($indexName) + )); + } + } + + return $instructions; + } + + /** + * @inheritDoc + */ + protected function getDropIndexByNameInstructions(string $tableName, string $indexName): AlterInstructions + { + $instructions = new AlterInstructions(); + $indexName = strtolower($indexName); + $indexes = $this->getIndexes($tableName); + + $found = false; + foreach (array_keys($indexes) as $index) { + if ($indexName === strtolower($index)) { + $found = true; + break; + } + } + + if ($found) { + $schema = $this->getSchemaName($tableName, true)['schema']; + $instructions->addPostStep(sprintf( + 'DROP INDEX %s%s', + $schema, + $this->quoteColumnName($indexName) + )); + } + + return $instructions; + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + public function hasPrimaryKey(string $tableName, $columns, ?string $constraint = null): bool + { + if ($constraint !== null) { + throw new InvalidArgumentException('SQLite does not support named constraints.'); + } + + $columns = array_map('strtolower', (array)$columns); + $primaryKey = array_map('strtolower', $this->getPrimaryKey($tableName)); + + if (array_diff($primaryKey, $columns) || array_diff($columns, $primaryKey)) { + return false; + } + + return true; + } + + /** + * Get the primary key from a particular table. + * + * @param string $tableName Table name + * @return string[] + */ + protected function getPrimaryKey(string $tableName): array + { + $primaryKey = []; + + $rows = $this->getTableInfo($tableName); + + foreach ($rows as $row) { + if ($row['pk'] > 0) { + $primaryKey[$row['pk'] - 1] = $row['name']; + } + } + + return $primaryKey; + } + + /** + * @inheritDoc + */ + public function hasForeignKey(string $tableName, $columns, ?string $constraint = null): bool + { + if ($constraint !== null) { + return preg_match( + "/,?\sCONSTRAINT\s" . preg_quote($this->quoteColumnName($constraint)) . ' FOREIGN KEY/', + $this->getDeclaringSql($tableName) + ) === 1; + } + + $columns = array_map('strtolower', (array)$columns); + $foreignKeys = $this->getForeignKeys($tableName); + + foreach ($foreignKeys as $key) { + $key = array_map('strtolower', $key); + if (array_diff($key, $columns) || array_diff($columns, $key)) { + continue; + } + + return true; + } + + return false; + } + + /** + * Get an array of foreign keys from a particular table. + * + * @param string $tableName Table name + * @return array + */ + protected function getForeignKeys(string $tableName): array + { + $foreignKeys = []; + + $rows = $this->getTableInfo($tableName, 'foreign_key_list'); + + foreach ($rows as $row) { + if (!isset($foreignKeys[$row['id']])) { + $foreignKeys[$row['id']] = []; + } + $foreignKeys[$row['id']][$row['seq']] = $row['from']; + } + + return $foreignKeys; + } + + /** + * @param \Phinx\Db\Table\Table $table The Table + * @param string $column Column Name + * @return \Phinx\Db\Util\AlterInstructions + */ + protected function getAddPrimaryKeyInstructions(Table $table, string $column): AlterInstructions + { + $instructions = $this->beginAlterByCopyTable($table->getName()); + + $tableName = $table->getName(); + $instructions->addPostStep(function ($state) use ($column) { + $matchPattern = "/(`$column`)\s+(\w+(\(\d+\))?)\s+((NOT )?NULL)/"; + + $sql = $state['createSQL']; + + if (preg_match($matchPattern, $state['createSQL'], $matches)) { + if (isset($matches[2])) { + if ($matches[2] === 'INTEGER') { + $replace = '$1 INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT'; + } else { + $replace = '$1 $2 NOT NULL PRIMARY KEY'; + } + + $sql = preg_replace($matchPattern, $replace, $state['createSQL'], 1); + } + } + + $this->execute($sql); + + return $state; + }); + + $instructions->addPostStep(function ($state) { + $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($state['tmpTableName']))); + $names = array_map([$this, 'quoteColumnName'], array_column($columns, 'name')); + $selectColumns = $writeColumns = $names; + + return compact('selectColumns', 'writeColumns') + $state; + }); + + return $this->endAlterByCopyTable($instructions, $tableName); + } + + /** + * @param \Phinx\Db\Table\Table $table Table + * @param string $column Column Name + * @return \Phinx\Db\Util\AlterInstructions + */ + protected function getDropPrimaryKeyInstructions(Table $table, string $column): AlterInstructions + { + $tableName = $table->getName(); + $instructions = $this->beginAlterByCopyTable($tableName); + + $instructions->addPostStep(function ($state) { + $search = "/(,?\s*PRIMARY KEY\s*\([^\)]*\)|\s+PRIMARY KEY(\s+AUTOINCREMENT)?)/"; + $sql = preg_replace($search, '', $state['createSQL'], 1); + + if ($sql) { + $this->execute($sql); + } + + return $state; + }); + + $instructions->addPostStep(function ($state) use ($column) { + $newState = $this->calculateNewTableColumns($state['tmpTableName'], $column, $column); + + return $newState + $state; + }); + + return $this->endAlterByCopyTable($instructions, $tableName, null, null, false); + } + + /** + * @inheritDoc + */ + protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey): AlterInstructions + { + $instructions = $this->beginAlterByCopyTable($table->getName()); + + $tableName = $table->getName(); + $instructions->addPostStep(function ($state) use ($foreignKey, $tableName) { + $this->execute('pragma foreign_keys = ON'); + $sql = substr($state['createSQL'], 0, -1) . ',' . $this->getForeignKeySqlDefinition($foreignKey) . '); '; + + //Delete indexes from original table and recreate them in temporary table + $schema = $this->getSchemaName($tableName, true)['schema']; + $tmpTableName = $state['tmpTableName']; + $indexes = $this->getIndexes($tableName); + foreach (array_keys($indexes) as $indexName) { + if (strpos($indexName, 'sqlite_autoindex_') !== 0) { + $sql .= sprintf( + 'DROP INDEX %s%s; ', + $schema, + $this->quoteColumnName($indexName) + ); + $createIndexSQL = $this->getDeclaringIndexSQL($tableName, $indexName); + $sql .= preg_replace( + "/\b{$tableName}\b/", + $tmpTableName, + $createIndexSQL + ); + } + } + + $this->execute($sql); + + return $state; + }); + + $instructions->addPostStep(function ($state) { + $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($state['tmpTableName']))); + $names = array_map([$this, 'quoteColumnName'], array_column($columns, 'name')); + $selectColumns = $writeColumns = $names; + + return compact('selectColumns', 'writeColumns') + $state; + }); + + return $this->endAlterByCopyTable($instructions, $tableName); + } + + /** + * {@inheritDoc} + * + * SQLiteAdapter does not implement this functionality, and so will always throw an exception if used. + * + * @throws \BadMethodCallException + */ + protected function getDropForeignKeyInstructions(string $tableName, string $constraint): AlterInstructions + { + throw new BadMethodCallException('SQLite does not have named foreign keys'); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getDropForeignKeyByColumnsInstructions(string $tableName, array $columns): AlterInstructions + { + $instructions = $this->beginAlterByCopyTable($tableName); + + $instructions->addPostStep(function ($state) use ($columns) { + $sql = ''; + + foreach ($columns as $columnName) { + $search = sprintf( + "/,[^,]*\(%s(?:,`?(.*)`?)?\) REFERENCES[^,]*\([^\)]*\)[^,)]*/", + $this->quoteColumnName($columnName) + ); + $sql = preg_replace($search, '', $state['createSQL'], 1); + } + + if ($sql) { + $this->execute($sql); + } + + return $state; + }); + + $instructions->addPostStep(function ($state) use ($columns) { + $newState = $this->calculateNewTableColumns($state['tmpTableName'], $columns[0], $columns[0]); + + $selectColumns = $newState['selectColumns']; + $columns = array_map([$this, 'quoteColumnName'], $columns); + $diff = array_diff($columns, $selectColumns); + + if (!empty($diff)) { + throw new InvalidArgumentException(sprintf( + 'The specified columns don\'t exist: ' . implode(', ', $diff) + )); + } + + return $newState + $state; + }); + + return $this->endAlterByCopyTable($instructions, $tableName); + } + + /** + * {@inheritDoc} + * + * @throws \Phinx\Db\Adapter\UnsupportedColumnTypeException + */ + public function getSqlType($type, ?int $limit = null): array + { + $typeLC = strtolower($type); + if ($type instanceof Literal) { + $name = $type; + } elseif (isset(static::$supportedColumnTypes[$typeLC])) { + $name = static::$supportedColumnTypes[$typeLC]; + } elseif (in_array($typeLC, static::$unsupportedColumnTypes, true)) { + throw new UnsupportedColumnTypeException('Column type "' . $type . '" is not supported by SQLite.'); + } else { + throw new UnsupportedColumnTypeException('Column type "' . $type . '" is not known by SQLite.'); + } + + return ['name' => $name, 'limit' => $limit]; + } + + /** + * Returns Phinx type by SQL type + * + * @param string|null $sqlTypeDef SQL Type definition + * @return array + */ + public function getPhinxType(?string $sqlTypeDef): array + { + $limit = null; + $scale = null; + if ($sqlTypeDef === null) { + // in SQLite columns can legitimately have null as a type, which is distinct from the empty string + $name = null; + } elseif (!preg_match('/^([a-z]+)(_(?:integer|float|text|blob))?(?:\((\d+)(?:,(\d+))?\))?$/i', $sqlTypeDef, $match)) { + // doesn't match the pattern of a type we'd know about + $name = Literal::from($sqlTypeDef); + } else { + // possibly a known type + $type = $match[1]; + $typeLC = strtolower($type); + $affinity = $match[2] ?? ''; + $limit = isset($match[3]) && strlen($match[3]) ? (int)$match[3] : null; + $scale = isset($match[4]) && strlen($match[4]) ? (int)$match[4] : null; + if (in_array($typeLC, ['tinyint', 'tinyinteger'], true) && $limit === 1) { + // the type is a MySQL-style boolean + $name = static::PHINX_TYPE_BOOLEAN; + $limit = null; + } elseif (isset(static::$supportedColumnTypes[$typeLC])) { + // the type is an explicitly supported type + $name = $typeLC; + } elseif (isset(static::$supportedColumnTypeAliases[$typeLC])) { + // the type is an alias for a supported type + $name = static::$supportedColumnTypeAliases[$typeLC]; + } elseif (in_array($typeLC, static::$unsupportedColumnTypes, true)) { + // unsupported but known types are passed through lowercased, and without appended affinity + $name = Literal::from($typeLC); + } else { + // unknown types are passed through as-is + $name = Literal::from($type . $affinity); + } + } + + return [ + 'name' => $name, + 'limit' => $limit, + 'scale' => $scale, + ]; + } + + /** + * @inheritDoc + */ + public function createDatabase(string $name, array $options = []): void + { + touch($name . $this->suffix); + } + + /** + * @inheritDoc + */ + public function hasDatabase(string $name): bool + { + return is_file($name . $this->suffix); + } + + /** + * @inheritDoc + */ + public function dropDatabase(string $name): void + { + $this->createdTables = []; + if ($this->getOption('memory')) { + $this->disconnect(); + $this->connect(); + } + if (file_exists($name . $this->suffix)) { + unlink($name . $this->suffix); + } + } + + /** + * Gets the SQLite Column Definition for a Column object. + * + * @param \Phinx\Db\Table\Column $column Column + * @return string + */ + protected function getColumnSqlDefinition(Column $column): string + { + $isLiteralType = $column->getType() instanceof Literal; + if ($isLiteralType) { + $def = (string)$column->getType(); + } else { + $sqlType = $this->getSqlType($column->getType()); + $def = strtoupper($sqlType['name']); + + $limitable = in_array(strtoupper($sqlType['name']), $this->definitionsWithLimits, true); + if (($column->getLimit() || isset($sqlType['limit'])) && $limitable) { + $def .= '(' . ($column->getLimit() ?: $sqlType['limit']) . ')'; + } + } + if ($column->getPrecision() && $column->getScale()) { + $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')'; + } + + $default = $column->getDefault(); + + $def .= $column->isNull() ? ' NULL' : ' NOT NULL'; + $def .= $this->getDefaultValueDefinition($default, $column->getType()); + $def .= $column->isIdentity() ? ' PRIMARY KEY AUTOINCREMENT' : ''; + + $def .= $this->getCommentDefinition($column); + + return $def; + } + + /** + * Gets the comment Definition for a Column object. + * + * @param \Phinx\Db\Table\Column $column Column + * @return string + */ + protected function getCommentDefinition(Column $column): string + { + if ($column->getComment()) { + return ' /* ' . $column->getComment() . ' */ '; + } + + return ''; + } + + /** + * Gets the SQLite Index Definition for an Index object. + * + * @param \Phinx\Db\Table\Table $table Table + * @param \Phinx\Db\Table\Index $index Index + * @return string + */ + protected function getIndexSqlDefinition(Table $table, Index $index): string + { + 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(): array + { + return array_keys(static::$supportedColumnTypes); + } + + /** + * Gets the SQLite Foreign Key Definition for an ForeignKey object. + * + * @param \Phinx\Db\Table\ForeignKey $foreignKey Foreign key + * @return string + */ + protected function getForeignKeySqlDefinition(ForeignKey $foreignKey): string + { + $def = ''; + if ($foreignKey->getConstraint()) { + $def .= ' CONSTRAINT ' . $this->quoteColumnName($foreignKey->getConstraint()); + } + $columnNames = []; + foreach ($foreignKey->getColumns() as $column) { + $columnNames[] = $this->quoteColumnName($column); + } + $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')'; + $refColumnNames = []; + 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; + } + + /** + * @inheritDoc + */ + protected function getDecoratedConnectionConfig(): array + { + $options = $this->getOptions(); + $options['quoteIdentifiers'] = true; + + if (!empty($options['name'])) { + $options['database'] = $options['name']; + + if (file_exists($options['name'] . $this->suffix)) { + $options['database'] = $options['name'] . $this->suffix; + } + } + + if ($this->connection === null) { + throw new RuntimeException('You need to connect first.'); + } + + return ['driver' => new SqliteDriver($options)] + $options; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php new file mode 100644 index 0000000..a7dcad3 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php @@ -0,0 +1,1373 @@ + + */ +class SqlServerAdapter extends PdoAdapter +{ + /** + * @var string[] + */ + protected static $specificColumnTypes = [ + self::PHINX_TYPE_FILESTREAM, + self::PHINX_TYPE_BINARYUUID, + ]; + + /** + * @var string + */ + protected $schema = 'dbo'; + + /** + * @var bool[] + */ + protected $signedColumnTypes = [ + self::PHINX_TYPE_INTEGER => true, + self::PHINX_TYPE_BIG_INTEGER => true, + self::PHINX_TYPE_FLOAT => true, + self::PHINX_TYPE_DECIMAL => true, + ]; + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + * @return void + */ + public function connect(): void + { + if ($this->connection === null) { + if (!class_exists('PDO') || !in_array('sqlsrv', PDO::getAvailableDrivers(), true)) { + // try our connection via freetds (Mac/Linux) + $this->connectDblib(); + + return; + } + + $options = $this->getOptions(); + + $dsn = 'sqlsrv:server=' . $options['host']; + // if port is specified use it, otherwise use the SqlServer default + if (!empty($options['port'])) { + $dsn .= ',' . $options['port']; + } + $dsn .= ';database=' . $options['name'] . ';MultipleActiveResultSets=false'; + + // option to add additional connection options + // https://docs.microsoft.com/en-us/sql/connect/php/connection-options?view=sql-server-ver15 + if (isset($options['dsn_options'])) { + foreach ($options['dsn_options'] as $key => $option) { + $dsn .= ';' . $key . '=' . $option; + } + } + + $driverOptions = []; + + // charset support + if (isset($options['charset'])) { + $driverOptions[PDO::SQLSRV_ATTR_ENCODING] = $options['charset']; + } + + // use custom data fetch mode + if (!empty($options['fetch_mode'])) { + $driverOptions[PDO::ATTR_DEFAULT_FETCH_MODE] = constant('\PDO::FETCH_' . strtoupper($options['fetch_mode'])); + } + + // Note, the PDO::ATTR_PERSISTENT attribute is not supported for sqlsrv and will throw an error when used + // See https://github.com/Microsoft/msphpsql/issues/65 + + // support arbitrary \PDO::SQLSRV_ATTR_* driver options and pass them to PDO + // https://php.net/manual/en/ref.pdo-sqlsrv.php#pdo-sqlsrv.constants + foreach ($options as $key => $option) { + if (strpos($key, 'sqlsrv_attr_') === 0) { + $pdoConstant = '\PDO::' . strtoupper($key); + if (!defined($pdoConstant)) { + throw new \UnexpectedValueException('Invalid PDO attribute: ' . $key . ' (' . $pdoConstant . ')'); + } + $driverOptions[constant($pdoConstant)] = $option; + } + } + + $db = $this->createPdoConnection($dsn, $options['user'] ?? null, $options['pass'] ?? null, $driverOptions); + + $this->setConnection($db); + } + } + + /** + * Connect to MSSQL using dblib/freetds. + * + * The "sqlsrv" driver is not available on Unix machines. + * + * @throws \InvalidArgumentException + * @throws \RuntimeException + * @return void + */ + protected function connectDblib(): void + { + 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 = [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() + ), 0, $exception); + } + + $this->setConnection($db); + } + + /** + * @inheritDoc + */ + public function disconnect(): void + { + $this->connection = null; + } + + /** + * @inheritDoc + */ + public function hasTransactions(): bool + { + return true; + } + + /** + * @inheritDoc + */ + public function beginTransaction(): void + { + $this->execute('BEGIN TRANSACTION'); + } + + /** + * @inheritDoc + */ + public function commitTransaction(): void + { + $this->execute('COMMIT TRANSACTION'); + } + + /** + * @inheritDoc + */ + public function rollbackTransaction(): void + { + $this->execute('ROLLBACK TRANSACTION'); + } + + /** + * @inheritDoc + */ + public function quoteTableName(string $tableName): string + { + return str_replace('.', '].[', $this->quoteColumnName($tableName)); + } + + /** + * @inheritDoc + */ + public function quoteColumnName(string $columnName): string + { + return '[' . str_replace(']', '\]', $columnName) . ']'; + } + + /** + * @inheritDoc + */ + public function hasTable(string $tableName): bool + { + if ($this->hasCreatedTable($tableName)) { + return true; + } + + /** @var array $result */ + $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, array $columns = [], array $indexes = []): void + { + $options = $table->getOptions(); + + // Add the default primary key + 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') + ->setOptions(['identity' => true]); + + array_unshift($columns, $column); + if (isset($options['primary_key']) && (array)$options['id'] !== (array)$options['primary_key']) { + throw new InvalidArgumentException('You cannot enable an auto incrementing ID field and a primary key'); + } + $options['primary_key'] = $options['id']; + } + + $sql = 'CREATE TABLE '; + $sql .= $this->quoteTableName($table->getName()) . ' ('; + $sqlBuffer = []; + $columnsWithComments = []; + 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') + $pkSql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key'])); + } + $pkSql .= ')'; + $sqlBuffer[] = $pkSql; + } + + $sql .= implode(', ', $sqlBuffer); + $sql .= ');'; + + // process column comments + foreach ($columnsWithComments as $column) { + $sql .= $this->getColumnCommentSqlDefinition($column, $table->getName()); + } + + // set the indexes + foreach ($indexes as $index) { + $sql .= $this->getIndexSqlDefinition($index, $table->getName()); + } + + // execute the sql + $this->execute($sql); + + $this->addCreatedTable($table->getName()); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getChangePrimaryKeyInstructions(Table $table, $newColumns): AlterInstructions + { + $instructions = new AlterInstructions(); + + // Drop the existing primary key + $primaryKey = $this->getPrimaryKey($table->getName()); + if (!empty($primaryKey['constraint'])) { + $sql = sprintf( + 'DROP CONSTRAINT %s', + $this->quoteColumnName($primaryKey['constraint']) + ); + $instructions->addAlter($sql); + } + + // Add the primary key(s) + if (!empty($newColumns)) { + $sql = sprintf( + 'ALTER TABLE %s ADD CONSTRAINT %s PRIMARY KEY (', + $this->quoteTableName($table->getName()), + $this->quoteColumnName('PK_' . $table->getName()) + ); + if (is_string($newColumns)) { // handle primary_key => 'id' + $sql .= $this->quoteColumnName($newColumns); + } elseif (is_array($newColumns)) { // handle primary_key => array('tag_id', 'resource_id') + $sql .= implode(',', array_map([$this, 'quoteColumnName'], $newColumns)); + } else { + throw new InvalidArgumentException(sprintf( + 'Invalid value for primary key: %s', + json_encode($newColumns) + )); + } + $sql .= ')'; + $instructions->addPostStep($sql); + } + + return $instructions; + } + + /** + * @inheritDoc + * + * SqlServer does not implement this functionality, and so will always throw an exception if used. + * @throws \BadMethodCallException + */ + protected function getChangeCommentInstructions(Table $table, ?string $newComment): AlterInstructions + { + throw new BadMethodCallException('SqlServer does not have table comments'); + } + + /** + * Gets the SqlServer Column Comment Defininition for a column object. + * + * @param \Phinx\Db\Table\Column $column Column + * @param string $tableName Table name + * @return string + */ + protected function getColumnCommentSqlDefinition(Column $column, $tableName): string + { + // 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 === null ? '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 + */ + protected function getRenameTableInstructions(string $tableName, string $newTableName): AlterInstructions + { + $this->updateCreatedTableName($tableName, $newTableName); + $sql = sprintf( + "EXEC sp_rename '%s', '%s'", + $tableName, + $newTableName + ); + + return new AlterInstructions([], [$sql]); + } + + /** + * @inheritDoc + */ + protected function getDropTableInstructions(string $tableName): AlterInstructions + { + $this->removeCreatedTable($tableName); + $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName)); + + return new AlterInstructions([], [$sql]); + } + + /** + * @inheritDoc + */ + public function truncateTable(string $tableName): void + { + $sql = sprintf( + 'TRUNCATE TABLE %s', + $this->quoteTableName($tableName) + ); + + $this->execute($sql); + } + + /** + * @param string $tableName Table name + * @param string $columnName Column name + * @return string|null + */ + public function getColumnComment(string $tableName, string $columnName): ?string + { + $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 trim($row['comment']); + } + + return null; + } + + /** + * @inheritDoc + */ + public function getColumns(string $tableName): array + { + $columns = []; + $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) { + try { + $type = $this->getPhinxType($columnInfo['type']); + } catch (UnsupportedColumnTypeException $e) { + $type = Literal::from($columnInfo['type']); + } + + $column = new Column(); + $column->setName($columnInfo['name']) + ->setType($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; + } + + /** + * @param string|null $default Default + * @return int|string|null + */ + protected function parseDefault(?string $default) + { + // if a column is non-nullable and has no default, the value of column_default is null, + // otherwise it should be a string value that we parse below, including "(NULL)" which + // also stands for a null default + if ($default === null) { + return null; + } + + $result = preg_replace(["/\('(.*)'\)/", "/\(\((.*)\)\)/", "/\((.*)\)/"], '$1', $default); + + if (strtoupper($result) === 'NULL') { + $result = null; + } elseif (is_numeric($result)) { + $result = (int)$result; + } + + return $result; + } + + /** + * @inheritDoc + */ + public function hasColumn(string $tableName, string $columnName): bool + { + $sql = sprintf( + "SELECT count(*) as [count] + FROM information_schema.columns + WHERE table_name = '%s' AND column_name = '%s'", + $tableName, + $columnName + ); + /** @var array $result */ + $result = $this->fetchRow($sql); + + return $result['count'] > 0; + } + + /** + * @inheritDoc + */ + protected function getAddColumnInstructions(Table $table, Column $column): AlterInstructions + { + $alter = sprintf( + 'ALTER TABLE %s ADD %s %s', + $table->getName(), + $this->quoteColumnName($column->getName()), + $this->getColumnSqlDefinition($column) + ); + + return new AlterInstructions([], [$alter]); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getRenameColumnInstructions(string $tableName, string $columnName, string $newColumnName): AlterInstructions + { + if (!$this->hasColumn($tableName, $columnName)) { + throw new InvalidArgumentException("The specified column does not exist: $columnName"); + } + + $instructions = new AlterInstructions(); + + $oldConstraintName = "DF_{$tableName}_{$columnName}"; + $newConstraintName = "DF_{$tableName}_{$newColumnName}"; + $sql = <<addPostStep(sprintf( + $sql, + $oldConstraintName, + $newConstraintName + )); + + $instructions->addPostStep(sprintf( + "EXECUTE sp_rename N'%s.%s', N'%s', 'COLUMN' ", + $tableName, + $columnName, + $newColumnName + )); + + return $instructions; + } + + /** + * Returns the instructions to change a column default value + * + * @param string $tableName The table where the column is + * @param \Phinx\Db\Table\Column $newColumn The column to alter + * @return \Phinx\Db\Util\AlterInstructions + */ + protected function getChangeDefault(string $tableName, Column $newColumn): AlterInstructions + { + $constraintName = "DF_{$tableName}_{$newColumn->getName()}"; + $default = $newColumn->getDefault(); + $instructions = new AlterInstructions(); + + if ($default === null) { + $default = 'DEFAULT NULL'; + } else { + $default = ltrim($this->getDefaultValueDefinition($default)); + } + + if (empty($default)) { + return $instructions; + } + + $instructions->addPostStep(sprintf( + 'ALTER TABLE %s ADD CONSTRAINT %s %s FOR %s', + $this->quoteTableName($tableName), + $constraintName, + $default, + $this->quoteColumnName($newColumn->getName()) + )); + + return $instructions; + } + + /** + * @inheritDoc + */ + protected function getChangeColumnInstructions(string $tableName, string $columnName, Column $newColumn): AlterInstructions + { + $columns = $this->getColumns($tableName); + $changeDefault = + $newColumn->getDefault() !== $columns[$columnName]->getDefault() || + $newColumn->getType() !== $columns[$columnName]->getType(); + + $instructions = new AlterInstructions(); + + if ($columnName !== $newColumn->getName()) { + $instructions->merge( + $this->getRenameColumnInstructions($tableName, $columnName, $newColumn->getName()) + ); + } + + if ($changeDefault) { + $instructions->merge($this->getDropDefaultConstraint($tableName, $newColumn->getName())); + } + + $instructions->addPostStep(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()) { + $instructions->addPostStep($this->getColumnCommentSqlDefinition($newColumn, $tableName)); + } + + if ($changeDefault) { + $instructions->merge($this->getChangeDefault($tableName, $newColumn)); + } + + return $instructions; + } + + /** + * @inheritDoc + */ + protected function getDropColumnInstructions(string $tableName, string $columnName): AlterInstructions + { + $instructions = $this->getDropDefaultConstraint($tableName, $columnName); + + $instructions->addPostStep(sprintf( + 'ALTER TABLE %s DROP COLUMN %s', + $this->quoteTableName($tableName), + $this->quoteColumnName($columnName) + )); + + return $instructions; + } + + /** + * @param string $tableName Table name + * @param string|null $columnName Column name + * @return \Phinx\Db\Util\AlterInstructions + */ + protected function getDropDefaultConstraint(string $tableName, ?string $columnName): AlterInstructions + { + $defaultConstraint = $this->getDefaultConstraint($tableName, $columnName); + + if (!$defaultConstraint) { + return new AlterInstructions(); + } + + return $this->getDropForeignKeyInstructions($tableName, $defaultConstraint); + } + + /** + * @param string $tableName Table name + * @param string $columnName Column name + * @return string|false + */ + protected function getDefaultConstraint(string $tableName, string $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']; + } + + /** + * @param int $tableId Table ID + * @param int $indexId Index ID + * @return array + */ + protected function getIndexColums(int $tableId, int $indexId): array + { + $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 = []; + 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(string $tableName): array + { + $indexes = []; + $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']] = ['columns' => $columns]; + } + + return $indexes; + } + + /** + * @inheritDoc + */ + public function hasIndex(string $tableName, $columns): bool + { + if (is_string($columns)) { + $columns = [$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(string $tableName, string $indexName): bool + { + $indexes = $this->getIndexes($tableName); + + foreach ($indexes as $name => $index) { + if ($name === $indexName) { + return true; + } + } + + return false; + } + + /** + * @inheritDoc + */ + protected function getAddIndexInstructions(Table $table, Index $index): AlterInstructions + { + $sql = $this->getIndexSqlDefinition($index, $table->getName()); + + return new AlterInstructions([], [$sql]); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getDropIndexByColumnsInstructions(string $tableName, $columns): AlterInstructions + { + if (is_string($columns)) { + $columns = [$columns]; // str to array + } + + $indexes = $this->getIndexes($tableName); + $columns = array_map('strtolower', $columns); + $instructions = new AlterInstructions(); + + foreach ($indexes as $indexName => $index) { + $a = array_diff($columns, $index['columns']); + if (empty($a)) { + $instructions->addPostStep(sprintf( + 'DROP INDEX %s ON %s', + $this->quoteColumnName($indexName), + $this->quoteTableName($tableName) + )); + + return $instructions; + } + } + + throw new InvalidArgumentException(sprintf( + "The specified index on columns '%s' does not exist", + implode(',', $columns) + )); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + */ + protected function getDropIndexByNameInstructions(string $tableName, string $indexName): AlterInstructions + { + $indexes = $this->getIndexes($tableName); + $instructions = new AlterInstructions(); + + foreach ($indexes as $name => $index) { + if ($name === $indexName) { + $instructions->addPostStep(sprintf( + 'DROP INDEX %s ON %s', + $this->quoteColumnName($indexName), + $this->quoteTableName($tableName) + )); + + return $instructions; + } + } + + throw new InvalidArgumentException(sprintf( + "The specified index name '%s' does not exist", + $indexName + )); + } + + /** + * @inheritDoc + */ + public function hasPrimaryKey(string $tableName, $columns, ?string $constraint = null): bool + { + $primaryKey = $this->getPrimaryKey($tableName); + + if (empty($primaryKey)) { + return false; + } + + if ($constraint) { + return $primaryKey['constraint'] === $constraint; + } + + if (is_string($columns)) { + $columns = [$columns]; // str to array + } + $missingColumns = array_diff($columns, $primaryKey['columns']); + + return empty($missingColumns); + } + + /** + * Get the primary key from a particular table. + * + * @param string $tableName Table name + * @return array + */ + public function getPrimaryKey(string $tableName): array + { + $rows = $this->fetchAll(sprintf( + "SELECT + tc.constraint_name, + kcu.column_name + FROM information_schema.table_constraints AS tc + JOIN information_schema.key_column_usage AS kcu + ON tc.constraint_name = kcu.constraint_name + WHERE constraint_type = 'PRIMARY KEY' + AND tc.table_name = '%s' + ORDER BY kcu.ordinal_position", + $tableName + )); + + $primaryKey = [ + 'columns' => [], + ]; + foreach ($rows as $row) { + $primaryKey['constraint'] = $row['constraint_name']; + $primaryKey['columns'][] = $row['column_name']; + } + + return $primaryKey; + } + + /** + * @inheritDoc + */ + public function hasForeignKey(string $tableName, $columns, ?string $constraint = null): bool + { + if (is_string($columns)) { + $columns = [$columns]; // str to array + } + $foreignKeys = $this->getForeignKeys($tableName); + if ($constraint) { + if (isset($foreignKeys[$constraint])) { + return !empty($foreignKeys[$constraint]); + } + + return false; + } + + 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(string $tableName): array + { + $foreignKeys = []; + $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 + */ + protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey): AlterInstructions + { + $instructions = new AlterInstructions(); + $instructions->addPostStep(sprintf( + 'ALTER TABLE %s ADD %s', + $this->quoteTableName($table->getName()), + $this->getForeignKeySqlDefinition($foreignKey, $table->getName()) + )); + + return $instructions; + } + + /** + * @inheritDoc + */ + protected function getDropForeignKeyInstructions(string $tableName, string $constraint): AlterInstructions + { + $instructions = new AlterInstructions(); + $instructions->addPostStep(sprintf( + 'ALTER TABLE %s DROP CONSTRAINT %s', + $this->quoteTableName($tableName), + $this->quoteColumnName($constraint) + )); + + return $instructions; + } + + /** + * @inheritDoc + */ + protected function getDropForeignKeyByColumnsInstructions(string $tableName, array $columns): AlterInstructions + { + $instructions = new AlterInstructions(); + + 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) { + $instructions->merge( + $this->getDropForeignKeyInstructions($tableName, $row['constraint_name']) + ); + } + } + + return $instructions; + } + + /** + * {@inheritDoc} + * + * @throws \Phinx\Db\Adapter\UnsupportedColumnTypeException + */ + public function getSqlType($type, ?int $limit = null): array + { + switch ($type) { + case static::PHINX_TYPE_FLOAT: + case static::PHINX_TYPE_DECIMAL: + case static::PHINX_TYPE_DATETIME: + case static::PHINX_TYPE_TIME: + case static::PHINX_TYPE_DATE: + return ['name' => $type]; + case static::PHINX_TYPE_STRING: + return ['name' => 'nvarchar', 'limit' => 255]; + case static::PHINX_TYPE_CHAR: + return ['name' => 'nchar', 'limit' => 255]; + case static::PHINX_TYPE_TEXT: + return ['name' => 'ntext']; + case static::PHINX_TYPE_INTEGER: + return ['name' => 'int']; + case static::PHINX_TYPE_TINY_INTEGER: + return ['name' => 'tinyint']; + case static::PHINX_TYPE_SMALL_INTEGER: + return ['name' => 'smallint']; + case static::PHINX_TYPE_BIG_INTEGER: + return ['name' => 'bigint']; + case static::PHINX_TYPE_TIMESTAMP: + return ['name' => 'datetime']; + case static::PHINX_TYPE_BLOB: + case static::PHINX_TYPE_BINARY: + return ['name' => 'varbinary']; + case static::PHINX_TYPE_BOOLEAN: + return ['name' => 'bit']; + case static::PHINX_TYPE_BINARYUUID: + case static::PHINX_TYPE_UUID: + return ['name' => 'uniqueidentifier']; + case static::PHINX_TYPE_FILESTREAM: + return ['name' => 'varbinary', 'limit' => 'max']; + // Geospatial database types + case static::PHINX_TYPE_GEOGRAPHY: + 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 ['name' => 'geography']; + // Geometry specific type + case static::PHINX_TYPE_GEOMETRY: + return ['name' => 'geometry']; + default: + throw new UnsupportedColumnTypeException('Column type "' . $type . '" is not supported by SqlServer.'); + } + } + + /** + * Returns Phinx type by SQL type + * + * @internal param string $sqlType SQL type + * @param string $sqlType SQL Type definition + * @throws \Phinx\Db\Adapter\UnsupportedColumnTypeException + * @return string Phinx type + */ + public function getPhinxType(string $sqlType): string + { + 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 'tinyint': + return static::PHINX_TYPE_TINY_INTEGER; + case 'smallint': + return static::PHINX_TYPE_SMALL_INTEGER; + 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; + 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 UnsupportedColumnTypeException('Column type "' . $sqlType . '" is not supported by SqlServer.'); + } + } + + /** + * @inheritDoc + */ + public function createDatabase(string $name, array $options = []): void + { + 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)); + } + + /** + * @inheritDoc + */ + public function hasDatabase(string $name): bool + { + /** @var array $result */ + $result = $this->fetchRow( + sprintf( + "SELECT count(*) as [count] FROM master.dbo.sysdatabases WHERE [name] = '%s'", + $name + ) + ); + + return $result['count'] > 0; + } + + /** + * @inheritDoc + */ + public function dropDatabase(string $name): void + { + $sql = <<execute($sql); + $this->createdTables = []; + } + + /** + * Gets the SqlServer Column Definition for a Column object. + * + * @param \Phinx\Db\Table\Column $column Column + * @param bool $create Create column flag + * @return string + */ + protected function getColumnSqlDefinition(Column $column, bool $create = true): string + { + $buffer = []; + if ($column->getType() instanceof Literal) { + $buffer[] = (string)$column->getType(); + } else { + $sqlType = $this->getSqlType($column->getType()); + $buffer[] = strtoupper($sqlType['name']); + // integers cant have limits in SQlServer + $noLimits = [ + 'bigint', + 'int', + 'tinyint', + 'smallint', + ]; + if ($sqlType['name'] === static::PHINX_TYPE_DECIMAL && $column->getPrecision() && $column->getScale()) { + $buffer[] = sprintf( + '(%s, %s)', + $column->getPrecision() ?: $sqlType['precision'], + $column->getScale() ?: $sqlType['scale'] + ); + } elseif (!in_array($sqlType['name'], $noLimits) && ($column->getLimit() || isset($sqlType['limit']))) { + $buffer[] = sprintf('(%s)', $column->getLimit() ?: $sqlType['limit']); + } + } + + $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()) { + $seed = $column->getSeed() ?: 1; + $increment = $column->getIncrement() ?: 1; + $buffer[] = sprintf('IDENTITY(%d,%d)', $seed, $increment); + } + + return implode(' ', $buffer); + } + + /** + * Gets the SqlServer Index Definition for an Index object. + * + * @param \Phinx\Db\Table\Index $index Index + * @param string $tableName Table name + * @return string + */ + protected function getIndexSqlDefinition(Index $index, string $tableName): string + { + $columnNames = $index->getColumns(); + if (is_string($index->getName())) { + $indexName = $index->getName(); + } else { + $indexName = sprintf('%s_%s', $tableName, implode('_', $columnNames)); + } + $order = $index->getOrder() ?? []; + $columnNames = array_map(function ($columnName) use ($order) { + $ret = '[' . $columnName . ']'; + if (isset($order[$columnName])) { + $ret .= ' ' . $order[$columnName]; + } + + return $ret; + }, $columnNames); + + $includedColumns = $index->getInclude() ? sprintf('INCLUDE ([%s])', implode('],[', $index->getInclude())) : ''; + + return sprintf( + 'CREATE %s INDEX %s ON %s (%s) %s;', + ($index->getType() === Index::UNIQUE ? 'UNIQUE' : ''), + $indexName, + $this->quoteTableName($tableName), + implode(',', $columnNames), + $includedColumns + ); + } + + /** + * Gets the SqlServer Foreign Key Definition for an ForeignKey object. + * + * @param \Phinx\Db\Table\ForeignKey $foreignKey Foreign key + * @param string $tableName Table name + * @return string + */ + protected function getForeignKeySqlDefinition(ForeignKey $foreignKey, string $tableName): string + { + $constraintName = $foreignKey->getConstraint() ?: $tableName . '_' . implode('_', $foreignKey->getColumns()); + $def = ' CONSTRAINT ' . $this->quoteColumnName($constraintName); + $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(): array + { + return array_merge(parent::getColumnTypes(), static::$specificColumnTypes); + } + + /** + * Records a migration being run. + * + * @param \Phinx\Migration\MigrationInterface $migration Migration + * @param string $direction Direction + * @param string $startTime Start Time + * @param string $endTime End Time + * @return \Phinx\Db\Adapter\AdapterInterface + */ + public function migrated(MigrationInterface $migration, string $direction, string $startTime, string $endTime): AdapterInterface + { + $startTime = str_replace(' ', 'T', $startTime); + $endTime = str_replace(' ', 'T', $endTime); + + return parent::migrated($migration, $direction, $startTime, $endTime); + } + + /** + * @inheritDoc + */ + protected function getDecoratedConnectionConfig(): array + { + $options = $this->getOptions(); + $options = [ + 'username' => $options['user'] ?? null, + 'password' => $options['pass'] ?? null, + 'database' => $options['name'], + 'quoteIdentifiers' => true, + ] + $options; + + return ['driver' => new SqlServerDriver($options)] + $options; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php new file mode 100644 index 0000000..dafc840 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php @@ -0,0 +1,494 @@ + + */ +class TablePrefixAdapter extends AdapterWrapper implements DirectActionInterface +{ + /** + * @inheritDoc + */ + public function getAdapterType(): string + { + return 'TablePrefixAdapter'; + } + + /** + * @inheritDoc + */ + public function hasTable(string $tableName): bool + { + $adapterTableName = $this->getAdapterTableName($tableName); + + return parent::hasTable($adapterTableName); + } + + /** + * @inheritDoc + */ + public function createTable(Table $table, array $columns = [], array $indexes = []): void + { + $adapterTable = new Table( + $this->getAdapterTableName($table->getName()), + $table->getOptions() + ); + parent::createTable($adapterTable, $columns, $indexes); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function changePrimaryKey(Table $table, $newColumns): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + + $adapterTable = new Table( + $this->getAdapterTableName($table->getName()), + $table->getOptions() + ); + $adapter->changePrimaryKey($adapterTable, $newColumns); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function changeComment(Table $table, ?string $newComment): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + + $adapterTable = new Table( + $this->getAdapterTableName($table->getName()), + $table->getOptions() + ); + $adapter->changeComment($adapterTable, $newComment); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function renameTable(string $tableName, string $newTableName): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + + $adapterTableName = $this->getAdapterTableName($tableName); + $adapterNewTableName = $this->getAdapterTableName($newTableName); + $adapter->renameTable($adapterTableName, $adapterNewTableName); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function dropTable(string $tableName): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + $adapterTableName = $this->getAdapterTableName($tableName); + $adapter->dropTable($adapterTableName); + } + + /** + * @inheritDoc + */ + public function truncateTable(string $tableName): void + { + $adapterTableName = $this->getAdapterTableName($tableName); + parent::truncateTable($adapterTableName); + } + + /** + * @inheritDoc + */ + public function getColumns(string $tableName): array + { + $adapterTableName = $this->getAdapterTableName($tableName); + + return parent::getColumns($adapterTableName); + } + + /** + * @inheritDoc + */ + public function hasColumn(string $tableName, string $columnName): bool + { + $adapterTableName = $this->getAdapterTableName($tableName); + + return parent::hasColumn($adapterTableName, $columnName); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function addColumn(Table $table, Column $column): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + $adapterTableName = $this->getAdapterTableName($table->getName()); + $adapterTable = new Table($adapterTableName, $table->getOptions()); + $adapter->addColumn($adapterTable, $column); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function renameColumn(string $tableName, string $columnName, string $newColumnName): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + $adapterTableName = $this->getAdapterTableName($tableName); + $adapter->renameColumn($adapterTableName, $columnName, $newColumnName); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function changeColumn(string $tableName, string $columnName, Column $newColumn): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + $adapterTableName = $this->getAdapterTableName($tableName); + $adapter->changeColumn($adapterTableName, $columnName, $newColumn); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function dropColumn(string $tableName, string $columnName): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + $adapterTableName = $this->getAdapterTableName($tableName); + $adapter->dropColumn($adapterTableName, $columnName); + } + + /** + * @inheritDoc + */ + public function hasIndex(string $tableName, $columns): bool + { + $adapterTableName = $this->getAdapterTableName($tableName); + + return parent::hasIndex($adapterTableName, $columns); + } + + /** + * @inheritDoc + */ + public function hasIndexByName(string $tableName, string $indexName): bool + { + $adapterTableName = $this->getAdapterTableName($tableName); + + return parent::hasIndexByName($adapterTableName, $indexName); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function addIndex(Table $table, Index $index): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + $adapterTable = new Table($table->getName(), $table->getOptions()); + $adapter->addIndex($adapterTable, $index); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function dropIndex(string $tableName, $columns): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + $adapterTableName = $this->getAdapterTableName($tableName); + $adapter->dropIndex($adapterTableName, $columns); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function dropIndexByName(string $tableName, string $indexName): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + $adapterTableName = $this->getAdapterTableName($tableName); + $adapter->dropIndexByName($adapterTableName, $indexName); + } + + /** + * @inheritDoc + */ + public function hasPrimaryKey(string $tableName, $columns, ?string $constraint = null): bool + { + $adapterTableName = $this->getAdapterTableName($tableName); + + return parent::hasPrimaryKey($adapterTableName, $columns, $constraint); + } + + /** + * @inheritDoc + */ + public function hasForeignKey(string $tableName, $columns, ?string $constraint = null): bool + { + $adapterTableName = $this->getAdapterTableName($tableName); + + return parent::hasForeignKey($adapterTableName, $columns, $constraint); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function addForeignKey(Table $table, ForeignKey $foreignKey): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + $adapterTableName = $this->getAdapterTableName($table->getName()); + $adapterTable = new Table($adapterTableName, $table->getOptions()); + $adapter->addForeignKey($adapterTable, $foreignKey); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function dropForeignKey(string $tableName, array $columns, ?string $constraint = null): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + $adapterTableName = $this->getAdapterTableName($tableName); + $adapter->dropForeignKey($adapterTableName, $columns, $constraint); + } + + /** + * @inheritDoc + */ + public function insert(Table $table, array $row): void + { + $adapterTableName = $this->getAdapterTableName($table->getName()); + $adapterTable = new Table($adapterTableName, $table->getOptions()); + parent::insert($adapterTable, $row); + } + + /** + * @inheritDoc + */ + public function bulkinsert(Table $table, array $rows): void + { + $adapterTableName = $this->getAdapterTableName($table->getName()); + $adapterTable = new Table($adapterTableName, $table->getOptions()); + parent::bulkinsert($adapterTable, $rows); + } + + /** + * Gets the table prefix. + * + * @return string + */ + public function getPrefix(): string + { + return (string)$this->getOption('table_prefix'); + } + + /** + * Gets the table suffix. + * + * @return string + */ + public function getSuffix(): string + { + return (string)$this->getOption('table_suffix'); + } + + /** + * Applies the prefix and suffix to the table name. + * + * @param string $tableName Table name + * @return string + */ + public function getAdapterTableName(string $tableName): string + { + return $this->getPrefix() . $tableName . $this->getSuffix(); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException + * @return void + */ + public function executeActions(Table $table, array $actions): void + { + $adapterTableName = $this->getAdapterTableName($table->getName()); + $adapterTable = new Table($adapterTableName, $table->getOptions()); + + foreach ($actions as $k => $action) { + switch (true) { + case $action instanceof AddColumn: + /** @var \Phinx\Db\Action\AddColumn $action */ + $actions[$k] = new AddColumn($adapterTable, $action->getColumn()); + break; + + case $action instanceof AddIndex: + /** @var \Phinx\Db\Action\AddIndex $action */ + $actions[$k] = new AddIndex($adapterTable, $action->getIndex()); + break; + + case $action instanceof AddForeignKey: + /** @var \Phinx\Db\Action\AddForeignKey $action */ + $foreignKey = clone $action->getForeignKey(); + $refTable = $foreignKey->getReferencedTable(); + $refTableName = $this->getAdapterTableName($refTable->getName()); + $foreignKey->setReferencedTable(new Table($refTableName, $refTable->getOptions())); + $actions[$k] = new AddForeignKey($adapterTable, $foreignKey); + break; + + case $action instanceof ChangeColumn: + /** @var \Phinx\Db\Action\ChangeColumn $action */ + $actions[$k] = new ChangeColumn($adapterTable, $action->getColumnName(), $action->getColumn()); + break; + + case $action instanceof DropForeignKey: + /** @var \Phinx\Db\Action\DropForeignKey $action */ + $actions[$k] = new DropForeignKey($adapterTable, $action->getForeignKey()); + break; + + case $action instanceof DropIndex: + /** @var \Phinx\Db\Action\DropIndex $action */ + $actions[$k] = new DropIndex($adapterTable, $action->getIndex()); + break; + + case $action instanceof DropTable: + /** @var \Phinx\Db\Action\DropTable $action */ + $actions[$k] = new DropTable($adapterTable); + break; + + case $action instanceof RemoveColumn: + /** @var \Phinx\Db\Action\RemoveColumn $action */ + $actions[$k] = new RemoveColumn($adapterTable, $action->getColumn()); + break; + + case $action instanceof RenameColumn: + /** @var \Phinx\Db\Action\RenameColumn $action */ + $actions[$k] = new RenameColumn($adapterTable, $action->getColumn(), $action->getNewName()); + break; + + case $action instanceof RenameTable: + /** @var \Phinx\Db\Action\RenameTable $action */ + $actions[$k] = new RenameTable($adapterTable, $this->getAdapterTableName($action->getNewName())); + break; + + case $action instanceof ChangePrimaryKey: + /** @var \Phinx\Db\Action\ChangePrimaryKey $action */ + $actions[$k] = new ChangePrimaryKey($adapterTable, $action->getNewColumns()); + break; + + case $action instanceof ChangeComment: + /** @var \Phinx\Db\Action\ChangeComment $action */ + $actions[$k] = new ChangeComment($adapterTable, $action->getNewComment()); + break; + + default: + throw new InvalidArgumentException( + sprintf("Forgot to implement table prefixing for action: '%s'", get_class($action)) + ); + } + } + + parent::executeActions($adapterTable, $actions); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/TimedOutputAdapter.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/TimedOutputAdapter.php new file mode 100644 index 0000000..1ef740a --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/TimedOutputAdapter.php @@ -0,0 +1,423 @@ +getAdapter()->getAdapterType(); + } + + /** + * Start timing a command. + * + * @return callable A function that is to be called when the command finishes + */ + public function startCommandTimer(): callable + { + $started = microtime(true); + + return function () use ($started) { + $end = microtime(true); + if (OutputInterface::VERBOSITY_VERBOSE <= $this->getOutput()->getVerbosity()) { + $this->getOutput()->writeln(' -> ' . sprintf('%.4fs', $end - $started)); + } + }; + } + + /** + * Write a Phinx command to the output. + * + * @param string $command Command Name + * @param array $args Command Args + * @return void + */ + public function writeCommand(string $command, array $args = []): void + { + if (OutputInterface::VERBOSITY_VERBOSE > $this->getOutput()->getVerbosity()) { + return; + } + + if (count($args)) { + $outArr = []; + 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 insert(Table $table, array $row): void + { + $end = $this->startCommandTimer(); + $this->writeCommand('insert', [$table->getName()]); + parent::insert($table, $row); + $end(); + } + + /** + * @inheritDoc + */ + public function bulkinsert(Table $table, array $rows): void + { + $end = $this->startCommandTimer(); + $this->writeCommand('bulkinsert', [$table->getName()]); + parent::bulkinsert($table, $rows); + $end(); + } + + /** + * @inheritDoc + */ + public function createTable(Table $table, array $columns = [], array $indexes = []): void + { + $end = $this->startCommandTimer(); + $this->writeCommand('createTable', [$table->getName()]); + parent::createTable($table, $columns, $indexes); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function changePrimaryKey(Table $table, $newColumns): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand('changePrimaryKey', [$table->getName()]); + $adapter->changePrimaryKey($table, $newColumns); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function changeComment(Table $table, ?string $newComment): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand('changeComment', [$table->getName()]); + $adapter->changeComment($table, $newComment); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function renameTable(string $tableName, string $newTableName): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand('renameTable', [$tableName, $newTableName]); + $adapter->renameTable($tableName, $newTableName); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function dropTable(string $tableName): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand('dropTable', [$tableName]); + $adapter->dropTable($tableName); + $end(); + } + + /** + * @inheritDoc + */ + public function truncateTable(string $tableName): void + { + $end = $this->startCommandTimer(); + $this->writeCommand('truncateTable', [$tableName]); + parent::truncateTable($tableName); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function addColumn(Table $table, Column $column): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand( + 'addColumn', + [ + $table->getName(), + $column->getName(), + $column->getType(), + ] + ); + $adapter->addColumn($table, $column); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function renameColumn(string $tableName, string $columnName, string $newColumnName): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand('renameColumn', [$tableName, $columnName, $newColumnName]); + $adapter->renameColumn($tableName, $columnName, $newColumnName); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function changeColumn(string $tableName, string $columnName, Column $newColumn): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand('changeColumn', [$tableName, $columnName, $newColumn->getType()]); + $adapter->changeColumn($tableName, $columnName, $newColumn); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function dropColumn(string $tableName, string $columnName): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand('dropColumn', [$tableName, $columnName]); + $adapter->dropColumn($tableName, $columnName); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function addIndex(Table $table, Index $index): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand('addIndex', [$table->getName(), $index->getColumns()]); + $adapter->addIndex($table, $index); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function dropIndex(string $tableName, $columns): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand('dropIndex', [$tableName, $columns]); + $adapter->dropIndex($tableName, $columns); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function dropIndexByName(string $tableName, string $indexName): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand('dropIndexByName', [$tableName, $indexName]); + $adapter->dropIndexByName($tableName, $indexName); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function addForeignKey(Table $table, ForeignKey $foreignKey): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand('addForeignKey', [$table->getName(), $foreignKey->getColumns()]); + $adapter->addForeignKey($table, $foreignKey); + $end(); + } + + /** + * {@inheritDoc} + * + * @throws \BadMethodCallException + * @return void + */ + public function dropForeignKey(string $tableName, array $columns, ?string $constraint = null): void + { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } + $end = $this->startCommandTimer(); + $this->writeCommand('dropForeignKey', [$tableName, $columns]); + $adapter->dropForeignKey($tableName, $columns, $constraint); + $end(); + } + + /** + * @inheritDoc + */ + public function createDatabase(string $name, array $options = []): void + { + $end = $this->startCommandTimer(); + $this->writeCommand('createDatabase', [$name]); + parent::createDatabase($name, $options); + $end(); + } + + /** + * @inheritDoc + */ + public function dropDatabase(string $name): void + { + $end = $this->startCommandTimer(); + $this->writeCommand('dropDatabase', [$name]); + parent::dropDatabase($name); + $end(); + } + + /** + * @inheritDoc + */ + public function createSchema(string $name = 'public'): void + { + $end = $this->startCommandTimer(); + $this->writeCommand('createSchema', [$name]); + parent::createSchema($name); + $end(); + } + + /** + * @inheritDoc + */ + public function dropSchema(string $name): void + { + $end = $this->startCommandTimer(); + $this->writeCommand('dropSchema', [$name]); + parent::dropSchema($name); + $end(); + } + + /** + * @inheritDoc + */ + public function executeActions(Table $table, array $actions): void + { + $end = $this->startCommandTimer(); + $this->writeCommand(sprintf('Altering table %s', $table->getName())); + parent::executeActions($table, $actions); + $end(); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/UnsupportedColumnTypeException.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/UnsupportedColumnTypeException.php new file mode 100644 index 0000000..5ef75e9 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/UnsupportedColumnTypeException.php @@ -0,0 +1,19 @@ + + */ +class UnsupportedColumnTypeException extends RuntimeException +{ +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/WrapperInterface.php b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/WrapperInterface.php new file mode 100644 index 0000000..669d0d2 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/WrapperInterface.php @@ -0,0 +1,39 @@ + + */ +interface WrapperInterface +{ + /** + * Class constructor, must always wrap another adapter. + * + * @param \Phinx\Db\Adapter\AdapterInterface $adapter Adapter + */ + public function __construct(AdapterInterface $adapter); + + /** + * Sets the database adapter to proxy commands to. + * + * @param \Phinx\Db\Adapter\AdapterInterface $adapter Adapter + * @return \Phinx\Db\Adapter\AdapterInterface + */ + public function setAdapter(AdapterInterface $adapter): AdapterInterface; + + /** + * Gets the database adapter. + * + * @throws \RuntimeException if the adapter has not been set + * @return \Phinx\Db\Adapter\AdapterInterface + */ + public function getAdapter(): AdapterInterface; +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Plan/AlterTable.php b/vendor/robmorgan/phinx/src/Phinx/Db/Plan/AlterTable.php new file mode 100644 index 0000000..1746e15 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Plan/AlterTable.php @@ -0,0 +1,72 @@ +table = $table; + } + + /** + * Adds another action to the collection + * + * @param \Phinx\Db\Action\Action $action The action to add + * @return void + */ + public function addAction(Action $action): void + { + $this->actions[] = $action; + } + + /** + * Returns the table associated to this collection + * + * @return \Phinx\Db\Table\Table + */ + public function getTable(): Table + { + return $this->table; + } + + /** + * Returns an array with all collected actions + * + * @return \Phinx\Db\Action\Action[] + */ + public function getActions(): array + { + return $this->actions; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Plan/Intent.php b/vendor/robmorgan/phinx/src/Phinx/Db/Plan/Intent.php new file mode 100644 index 0000000..eadd344 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Plan/Intent.php @@ -0,0 +1,55 @@ +actions[] = $action; + } + + /** + * Returns the full list of actions + * + * @return \Phinx\Db\Action\Action[] + */ + public function getActions(): array + { + return $this->actions; + } + + /** + * Merges another Intent object with this one + * + * @param \Phinx\Db\Plan\Intent $another The other intent to merge in + * @return void + */ + public function merge(Intent $another): void + { + $this->actions = array_merge($this->actions, $another->getActions()); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Plan/NewTable.php b/vendor/robmorgan/phinx/src/Phinx/Db/Plan/NewTable.php new file mode 100644 index 0000000..0ac62fc --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Plan/NewTable.php @@ -0,0 +1,101 @@ +table = $table; + } + + /** + * Adds a column to the collection + * + * @param \Phinx\Db\Table\Column $column The column description + * @return void + */ + public function addColumn(Column $column): void + { + $this->columns[] = $column; + } + + /** + * Adds an index to the collection + * + * @param \Phinx\Db\Table\Index $index The index description + * @return void + */ + public function addIndex(Index $index): void + { + $this->indexes[] = $index; + } + + /** + * Returns the table object associated to this collection + * + * @return \Phinx\Db\Table\Table + */ + public function getTable(): Table + { + return $this->table; + } + + /** + * Returns the columns collection + * + * @return \Phinx\Db\Table\Column[] + */ + public function getColumns(): array + { + return $this->columns; + } + + /** + * Returns the indexes collection + * + * @return \Phinx\Db\Table\Index[] + */ + public function getIndexes(): array + { + return $this->indexes; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Plan/Plan.php b/vendor/robmorgan/phinx/src/Phinx/Db/Plan/Plan.php new file mode 100644 index 0000000..4e0b6fd --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Plan/Plan.php @@ -0,0 +1,492 @@ +createPlan($intent->getActions()); + } + + /** + * Parses the given Intent and creates the separate steps to execute + * + * @param \Phinx\Db\Action\Action[] $actions The actions to use for the plan + * @return void + */ + protected function createPlan(array $actions): void + { + $this->gatherCreates($actions); + $this->gatherUpdates($actions); + $this->gatherTableMoves($actions); + $this->gatherIndexes($actions); + $this->gatherConstraints($actions); + $this->resolveConflicts(); + } + + /** + * Returns a nested list of all the steps to execute + * + * @return \Phinx\Db\Plan\AlterTable[][] + */ + protected function updatesSequence(): array + { + return [ + $this->tableUpdates, + $this->constraints, + $this->indexes, + $this->columnRemoves, + $this->tableMoves, + ]; + } + + /** + * Returns a nested list of all the steps to execute in inverse order + * + * @return \Phinx\Db\Plan\AlterTable[][] + */ + protected function inverseUpdatesSequence(): array + { + return [ + $this->constraints, + $this->tableMoves, + $this->indexes, + $this->columnRemoves, + $this->tableUpdates, + ]; + } + + /** + * Executes this plan using the given AdapterInterface + * + * @param \Phinx\Db\Adapter\AdapterInterface $executor The executor object for the plan + * @return void + */ + public function execute(AdapterInterface $executor): void + { + foreach ($this->tableCreates as $newTable) { + $executor->createTable($newTable->getTable(), $newTable->getColumns(), $newTable->getIndexes()); + } + + foreach ($this->updatesSequence() as $updates) { + foreach ($updates as $update) { + $executor->executeActions($update->getTable(), $update->getActions()); + } + } + } + + /** + * Executes the inverse plan (rollback the actions) with the given AdapterInterface:w + * + * @param \Phinx\Db\Adapter\AdapterInterface $executor The executor object for the plan + * @return void + */ + public function executeInverse(AdapterInterface $executor): void + { + foreach ($this->inverseUpdatesSequence() as $updates) { + foreach ($updates as $update) { + $executor->executeActions($update->getTable(), $update->getActions()); + } + } + + foreach ($this->tableCreates as $newTable) { + $executor->createTable($newTable->getTable(), $newTable->getColumns(), $newTable->getIndexes()); + } + } + + /** + * Deletes certain actions from the plan if they are found to be conflicting or redundant. + * + * @return void + */ + protected function resolveConflicts(): void + { + foreach ($this->tableMoves as $alterTable) { + foreach ($alterTable->getActions() as $action) { + if ($action instanceof DropTable) { + $this->tableUpdates = $this->forgetTable($action->getTable(), $this->tableUpdates); + $this->constraints = $this->forgetTable($action->getTable(), $this->constraints); + $this->indexes = $this->forgetTable($action->getTable(), $this->indexes); + $this->columnRemoves = $this->forgetTable($action->getTable(), $this->columnRemoves); + } + } + } + + // Renaming a column and then changing the renamed column is something people do, + // but it is a conflicting action. Luckily solving the conflict can be done by moving + // the ChangeColumn action to another AlterTable. + $splitter = new ActionSplitter( + RenameColumn::class, + ChangeColumn::class, + function (RenameColumn $a, ChangeColumn $b) { + return $a->getNewName() === $b->getColumnName(); + } + ); + $tableUpdates = []; + foreach ($this->tableUpdates as $update) { + $tableUpdates = array_merge($tableUpdates, $splitter($update)); + } + $this->tableUpdates = $tableUpdates; + + // Dropping indexes used by foreign keys is a conflict, but one we can resolve + // if the foreign key is also scheduled to be dropped. If we can find such a a case, + // we force the execution of the index drop after the foreign key is dropped. + // Changing constraint properties sometimes require dropping it and then + // creating it again with the new stuff. Unfortunately, we have already bundled + // everything together in as few AlterTable statements as we could, so we need to + // resolve this conflict manually. + $splitter = new ActionSplitter( + DropForeignKey::class, + AddForeignKey::class, + function (DropForeignKey $a, AddForeignKey $b) { + return $a->getForeignKey()->getColumns() === $b->getForeignKey()->getColumns(); + } + ); + $constraints = []; + foreach ($this->constraints as $constraint) { + $constraints = array_merge( + $constraints, + $splitter($this->remapContraintAndIndexConflicts($constraint)) + ); + } + $this->constraints = $constraints; + } + + /** + * Deletes all actions related to the given table and keeps the + * rest + * + * @param \Phinx\Db\Table\Table $table The table to find in the list of actions + * @param \Phinx\Db\Plan\AlterTable[] $actions The actions to transform + * @return \Phinx\Db\Plan\AlterTable[] The list of actions without actions for the given table + */ + protected function forgetTable(Table $table, array $actions): array + { + $result = []; + foreach ($actions as $action) { + if ($action->getTable()->getName() === $table->getName()) { + continue; + } + $result[] = $action; + } + + return $result; + } + + /** + * Finds all DropForeignKey actions in an AlterTable and moves + * all conflicting DropIndex action in `$this->indexes` into the + * given AlterTable. + * + * @param \Phinx\Db\Plan\AlterTable $alter The collection of actions to inspect + * @return \Phinx\Db\Plan\AlterTable The updated AlterTable object. This function + * has the side effect of changing the `$this->indexes` property. + */ + protected function remapContraintAndIndexConflicts(AlterTable $alter): AlterTable + { + $newAlter = new AlterTable($alter->getTable()); + + foreach ($alter->getActions() as $action) { + $newAlter->addAction($action); + if ($action instanceof DropForeignKey) { + [$this->indexes, $dropIndexActions] = $this->forgetDropIndex( + $action->getTable(), + $action->getForeignKey()->getColumns(), + $this->indexes + ); + foreach ($dropIndexActions as $dropIndexAction) { + $newAlter->addAction($dropIndexAction); + } + } + } + + return $newAlter; + } + + /** + * Deletes any DropIndex actions for the given table and exact columns + * + * @param \Phinx\Db\Table\Table $table The table to find in the list of actions + * @param string[] $columns The column names to match + * @param \Phinx\Db\Plan\AlterTable[] $actions The actions to transform + * @return array A tuple containing the list of actions without actions for dropping the index + * and a list of drop index actions that were removed. + */ + protected function forgetDropIndex(Table $table, array $columns, array $actions): array + { + $dropIndexActions = new ArrayObject(); + $indexes = array_map(function ($alter) use ($table, $columns, $dropIndexActions) { + if ($alter->getTable()->getName() !== $table->getName()) { + return $alter; + } + + $newAlter = new AlterTable($table); + foreach ($alter->getActions() as $action) { + if ($action instanceof DropIndex && $action->getIndex()->getColumns() === $columns) { + $dropIndexActions->append($action); + } else { + $newAlter->addAction($action); + } + } + + return $newAlter; + }, $actions); + + return [$indexes, $dropIndexActions->getArrayCopy()]; + } + + /** + * Deletes any RemoveColumn actions for the given table and exact columns + * + * @param \Phinx\Db\Table\Table $table The table to find in the list of actions + * @param string[] $columns The column names to match + * @param \Phinx\Db\Plan\AlterTable[] $actions The actions to transform + * @return array A tuple containing the list of actions without actions for removing the column + * and a list of remove column actions that were removed. + */ + protected function forgetRemoveColumn(Table $table, array $columns, array $actions): array + { + $removeColumnActions = new ArrayObject(); + $indexes = array_map(function ($alter) use ($table, $columns, $removeColumnActions) { + if ($alter->getTable()->getName() !== $table->getName()) { + return $alter; + } + + $newAlter = new AlterTable($table); + foreach ($alter->getActions() as $action) { + if ($action instanceof RemoveColumn && in_array($action->getColumn()->getName(), $columns, true)) { + $removeColumnActions->append($action); + } else { + $newAlter->addAction($action); + } + } + + return $newAlter; + }, $actions); + + return [$indexes, $removeColumnActions->getArrayCopy()]; + } + + /** + * Collects all table creation actions from the given intent + * + * @param \Phinx\Db\Action\Action[] $actions The actions to parse + * @return void + */ + protected function gatherCreates(array $actions): void + { + foreach ($actions as $action) { + if ($action instanceof CreateTable) { + $this->tableCreates[$action->getTable()->getName()] = new NewTable($action->getTable()); + } + } + + foreach ($actions as $action) { + if ( + ($action instanceof AddColumn || $action instanceof AddIndex) + && isset($this->tableCreates[$action->getTable()->getName()]) + ) { + $table = $action->getTable(); + + if ($action instanceof AddColumn) { + $this->tableCreates[$table->getName()]->addColumn($action->getColumn()); + } + + if ($action instanceof AddIndex) { + $this->tableCreates[$table->getName()]->addIndex($action->getIndex()); + } + } + } + } + + /** + * Collects all alter table actions from the given intent + * + * @param \Phinx\Db\Action\Action[] $actions The actions to parse + * @return void + */ + protected function gatherUpdates(array $actions): void + { + foreach ($actions as $action) { + if ( + !($action instanceof AddColumn) + && !($action instanceof ChangeColumn) + && !($action instanceof RemoveColumn) + && !($action instanceof RenameColumn) + ) { + continue; + } elseif (isset($this->tableCreates[$action->getTable()->getName()])) { + continue; + } + $table = $action->getTable(); + $name = $table->getName(); + + if ($action instanceof RemoveColumn) { + if (!isset($this->columnRemoves[$name])) { + $this->columnRemoves[$name] = new AlterTable($table); + } + $this->columnRemoves[$name]->addAction($action); + } else { + if (!isset($this->tableUpdates[$name])) { + $this->tableUpdates[$name] = new AlterTable($table); + } + $this->tableUpdates[$name]->addAction($action); + } + } + } + + /** + * Collects all alter table drop and renames from the given intent + * + * @param \Phinx\Db\Action\Action[] $actions The actions to parse + * @return void + */ + protected function gatherTableMoves(array $actions): void + { + foreach ($actions as $action) { + if ( + !($action instanceof DropTable) + && !($action instanceof RenameTable) + && !($action instanceof ChangePrimaryKey) + && !($action instanceof ChangeComment) + ) { + continue; + } + $table = $action->getTable(); + $name = $table->getName(); + + if (!isset($this->tableMoves[$name])) { + $this->tableMoves[$name] = new AlterTable($table); + } + + $this->tableMoves[$name]->addAction($action); + } + } + + /** + * Collects all index creation and drops from the given intent + * + * @param \Phinx\Db\Action\Action[] $actions The actions to parse + * @return void + */ + protected function gatherIndexes(array $actions): void + { + foreach ($actions as $action) { + if (!($action instanceof AddIndex) && !($action instanceof DropIndex)) { + continue; + } elseif (isset($this->tableCreates[$action->getTable()->getName()])) { + continue; + } + + $table = $action->getTable(); + $name = $table->getName(); + + if (!isset($this->indexes[$name])) { + $this->indexes[$name] = new AlterTable($table); + } + + $this->indexes[$name]->addAction($action); + } + } + + /** + * Collects all foreign key creation and drops from the given intent + * + * @param \Phinx\Db\Action\Action[] $actions The actions to parse + * @return void + */ + protected function gatherConstraints(array $actions): void + { + foreach ($actions as $action) { + if (!($action instanceof AddForeignKey || $action instanceof DropForeignKey)) { + continue; + } + $table = $action->getTable(); + $name = $table->getName(); + + if (!isset($this->constraints[$name])) { + $this->constraints[$name] = new AlterTable($table); + } + + $this->constraints[$name]->addAction($action); + } + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Plan/Solver/ActionSplitter.php b/vendor/robmorgan/phinx/src/Phinx/Db/Plan/Solver/ActionSplitter.php new file mode 100644 index 0000000..a9a157a --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Plan/Solver/ActionSplitter.php @@ -0,0 +1,103 @@ +conflictClass = $conflictClass; + $this->conflictClassDual = $conflictClassDual; + $this->conflictFilter = $conflictFilter; + } + + /** + * Returs a sequence of AlterTable instructions that are non conflicting + * based on the constructor parameters. + * + * @param \Phinx\Db\Plan\AlterTable $alter The collection of actions to inspect + * @return \Phinx\Db\Plan\AlterTable[] A list of AlterTable that can be executed without + * this type of conflict + */ + public function __invoke(AlterTable $alter): array + { + $conflictActions = array_filter($alter->getActions(), function ($action) { + return $action instanceof $this->conflictClass; + }); + + $originalAlter = new AlterTable($alter->getTable()); + $newAlter = new AlterTable($alter->getTable()); + + foreach ($alter->getActions() as $action) { + if (!$action instanceof $this->conflictClassDual) { + $originalAlter->addAction($action); + continue; + } + + $found = false; + $matches = $this->conflictFilter; + foreach ($conflictActions as $ca) { + if ($matches($ca, $action)) { + $found = true; + break; + } + } + + if ($found) { + $newAlter->addAction($action); + } else { + $originalAlter->addAction($action); + } + } + + return [$originalAlter, $newAlter]; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Table.php b/vendor/robmorgan/phinx/src/Phinx/Db/Table.php new file mode 100644 index 0000000..e532517 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Table.php @@ -0,0 +1,721 @@ + $options Options + * @param \Phinx\Db\Adapter\AdapterInterface|null $adapter Database Adapter + */ + public function __construct(string $name, array $options = [], ?AdapterInterface $adapter = null) + { + $this->table = new TableValue($name, $options); + $this->actions = new Intent(); + + if ($adapter !== null) { + $this->setAdapter($adapter); + } + } + + /** + * Gets the table name. + * + * @return string + */ + public function getName(): string + { + return $this->table->getName(); + } + + /** + * Gets the table options. + * + * @return array + */ + public function getOptions(): array + { + return $this->table->getOptions(); + } + + /** + * Gets the table name and options as an object + * + * @return \Phinx\Db\Table\Table + */ + public function getTable(): TableValue + { + return $this->table; + } + + /** + * Sets the database adapter. + * + * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter + * @return $this + */ + public function setAdapter(AdapterInterface $adapter) + { + $this->adapter = $adapter; + + return $this; + } + + /** + * Gets the database adapter. + * + * @throws \RuntimeException + * @return \Phinx\Db\Adapter\AdapterInterface + */ + public function getAdapter(): AdapterInterface + { + if (!$this->adapter) { + throw new RuntimeException('There is no database adapter set yet, cannot proceed'); + } + + return $this->adapter; + } + + /** + * Does the table have pending actions? + * + * @return bool + */ + public function hasPendingActions(): bool + { + return count($this->actions->getActions()) > 0 || count($this->data) > 0; + } + + /** + * Does the table exist? + * + * @return bool + */ + public function exists(): bool + { + return $this->getAdapter()->hasTable($this->getName()); + } + + /** + * Drops the database table. + * + * @return $this + */ + public function drop() + { + $this->actions->addAction(new DropTable($this->table)); + + return $this; + } + + /** + * Renames the database table. + * + * @param string $newTableName New Table Name + * @return $this + */ + public function rename(string $newTableName) + { + $this->actions->addAction(new RenameTable($this->table, $newTableName)); + + return $this; + } + + /** + * Changes the primary key of the database table. + * + * @param string|string[]|null $columns Column name(s) to belong to the primary key, or null to drop the key + * @return $this + */ + public function changePrimaryKey($columns) + { + $this->actions->addAction(new ChangePrimaryKey($this->table, $columns)); + + return $this; + } + + /** + * Checks to see if a primary key exists. + * + * @param string|string[] $columns Column(s) + * @param string|null $constraint Constraint names + * @return bool + */ + public function hasPrimaryKey($columns, ?string $constraint = null): bool + { + return $this->getAdapter()->hasPrimaryKey($this->getName(), $columns, $constraint); + } + + /** + * Changes the comment of the database table. + * + * @param string|null $comment New comment string, or null to drop the comment + * @return $this + */ + public function changeComment(?string $comment) + { + $this->actions->addAction(new ChangeComment($this->table, $comment)); + + return $this; + } + + /** + * Gets an array of the table columns. + * + * @return \Phinx\Db\Table\Column[] + */ + public function getColumns(): array + { + return $this->getAdapter()->getColumns($this->getName()); + } + + /** + * Gets a table column if it exists. + * + * @param string $name Column name + * @return \Phinx\Db\Table\Column|null + */ + public function getColumn(string $name): ?Column + { + $columns = array_filter( + $this->getColumns(), + function ($column) use ($name) { + return $column->getName() === $name; + } + ); + + return array_pop($columns); + } + + /** + * Sets an array of data to be inserted. + * + * @param array $data Data + * @return $this + */ + public function setData(array $data) + { + $this->data = $data; + + return $this; + } + + /** + * Gets the data waiting to be inserted. + * + * @return array + */ + public function getData(): array + { + return $this->data; + } + + /** + * Resets all of the pending data to be inserted + * + * @return void + */ + public function resetData(): void + { + $this->setData([]); + } + + /** + * Resets all of the pending table changes. + * + * @return void + */ + public function reset(): void + { + $this->actions = new Intent(); + $this->resetData(); + } + + /** + * 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|\Phinx\Db\Table\Column $columnName Column Name + * @param string|\Phinx\Util\Literal|null $type Column Type + * @param array $options Column Options + * @throws \InvalidArgumentException + * @return $this + */ + public function addColumn($columnName, $type = null, array $options = []) + { + if ($columnName instanceof Column) { + $action = new AddColumn($this->table, $columnName); + } elseif ($type instanceof Literal) { + $action = AddColumn::build($this->table, $columnName, $type, $options); + } else { + $action = new AddColumn($this->table, $this->getAdapter()->getColumnForType($columnName, $type, $options)); + } + + // Delegate to Adapters to check column type + if (!$this->getAdapter()->isValidColumnType($action->getColumn())) { + throw new InvalidArgumentException(sprintf( + 'An invalid column type "%s" was specified for column "%s".', + $type, + $action->getColumn()->getName() + )); + } + + $this->actions->addAction($action); + + return $this; + } + + /** + * Remove a table column. + * + * @param string $columnName Column Name + * @return $this + */ + public function removeColumn(string $columnName) + { + $action = RemoveColumn::build($this->table, $columnName); + $this->actions->addAction($action); + + return $this; + } + + /** + * Rename a table column. + * + * @param string $oldName Old Column Name + * @param string $newName New Column Name + * @return $this + */ + public function renameColumn(string $oldName, string $newName) + { + $action = RenameColumn::build($this->table, $oldName, $newName); + $this->actions->addAction($action); + + return $this; + } + + /** + * Change a table column type. + * + * @param string $columnName Column Name + * @param string|\Phinx\Db\Table\Column|\Phinx\Util\Literal $newColumnType New Column Type + * @param array $options Options + * @return $this + */ + public function changeColumn(string $columnName, $newColumnType, array $options = []) + { + if ($newColumnType instanceof Column) { + $action = new ChangeColumn($this->table, $columnName, $newColumnType); + } else { + $action = ChangeColumn::build($this->table, $columnName, $newColumnType, $options); + } + $this->actions->addAction($action); + + return $this; + } + + /** + * Checks to see if a column exists. + * + * @param string $columnName Column Name + * @return bool + */ + public function hasColumn(string $columnName): bool + { + return $this->getAdapter()->hasColumn($this->getName(), $columnName); + } + + /** + * Add an index to a database table. + * + * In $options you can specify unique = true/false, and name (index name). + * + * @param string|array|\Phinx\Db\Table\Index $columns Table Column(s) + * @param array $options Index Options + * @return $this + */ + public function addIndex($columns, array $options = []) + { + $action = AddIndex::build($this->table, $columns, $options); + $this->actions->addAction($action); + + return $this; + } + + /** + * Removes the given index from a table. + * + * @param string|string[] $columns Columns + * @return $this + */ + public function removeIndex($columns) + { + $action = DropIndex::build($this->table, is_string($columns) ? [$columns] : $columns); + $this->actions->addAction($action); + + return $this; + } + + /** + * Removes the given index identified by its name from a table. + * + * @param string $name Index name + * @return $this + */ + public function removeIndexByName(string $name) + { + $action = DropIndex::buildFromName($this->table, $name); + $this->actions->addAction($action); + + return $this; + } + + /** + * Checks to see if an index exists. + * + * @param string|string[] $columns Columns + * @return bool + */ + public function hasIndex($columns): bool + { + return $this->getAdapter()->hasIndex($this->getName(), $columns); + } + + /** + * Checks to see if an index specified by name exists. + * + * @param string $indexName Index name + * @return bool + */ + public function hasIndexByName($indexName): bool + { + return $this->getAdapter()->hasIndexByName($this->getName(), $indexName); + } + + /** + * 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|string[] $columns Columns + * @param string|\Phinx\Db\Table\Table $referencedTable Referenced Table + * @param string|string[] $referencedColumns Referenced Columns + * @param array $options Options + * @return $this + */ + public function addForeignKey($columns, $referencedTable, $referencedColumns = ['id'], array $options = []) + { + $action = AddForeignKey::build($this->table, $columns, $referencedTable, $referencedColumns, $options); + $this->actions->addAction($action); + + return $this; + } + + /** + * Add a foreign key to a database table with a given name. + * + * In $options you can specify on_delete|on_delete = cascade|no_action .., + * on_update, constraint = constraint name. + * + * @param string $name The constraint name + * @param string|string[] $columns Columns + * @param string|\Phinx\Db\Table\Table $referencedTable Referenced Table + * @param string|string[] $referencedColumns Referenced Columns + * @param array $options Options + * @return $this + */ + public function addForeignKeyWithName(string $name, $columns, $referencedTable, $referencedColumns = ['id'], array $options = []) + { + $action = AddForeignKey::build( + $this->table, + $columns, + $referencedTable, + $referencedColumns, + $options, + $name + ); + $this->actions->addAction($action); + + return $this; + } + + /** + * Removes the given foreign key from the table. + * + * @param string|string[] $columns Column(s) + * @param string|null $constraint Constraint names + * @return $this + */ + public function dropForeignKey($columns, ?string $constraint = null) + { + $action = DropForeignKey::build($this->table, $columns, $constraint); + $this->actions->addAction($action); + + return $this; + } + + /** + * Checks to see if a foreign key exists. + * + * @param string|string[] $columns Column(s) + * @param string|null $constraint Constraint names + * @return bool + */ + public function hasForeignKey($columns, ?string $constraint = null): bool + { + return $this->getAdapter()->hasForeignKey($this->getName(), $columns, $constraint); + } + + /** + * Add timestamp columns created_at and updated_at to the table. + * + * @param string|false|null $createdAt Alternate name for the created_at column + * @param string|false|null $updatedAt Alternate name for the updated_at column + * @param bool $withTimezone Whether to set the timezone option on the added columns + * @return $this + */ + public function addTimestamps($createdAt = 'created_at', $updatedAt = 'updated_at', bool $withTimezone = false) + { + $createdAt = $createdAt ?? 'created_at'; + $updatedAt = $updatedAt ?? 'updated_at'; + + if (!$createdAt && !$updatedAt) { + throw new \RuntimeException('Cannot set both created_at and updated_at columns to false'); + } + + if ($createdAt) { + $this->addColumn($createdAt, 'timestamp', [ + 'null' => false, + 'default' => 'CURRENT_TIMESTAMP', + 'update' => '', + 'timezone' => $withTimezone, + ]); + } + if ($updatedAt) { + $this->addColumn($updatedAt, 'timestamp', [ + 'null' => true, + 'default' => null, + 'update' => 'CURRENT_TIMESTAMP', + 'timezone' => $withTimezone, + ]); + } + + return $this; + } + + /** + * Alias that always sets $withTimezone to true + * + * @see addTimestamps + * @param string|false|null $createdAt Alternate name for the created_at column + * @param string|false|null $updatedAt Alternate name for the updated_at column + * @return $this + */ + public function addTimestampsWithTimezone($createdAt = null, $updatedAt = null) + { + $this->addTimestamps($createdAt, $updatedAt, true); + + return $this; + } + + /** + * Insert data into the table. + * + * @param array $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 $this + */ + public function insert(array $data) + { + // handle array of array situations + $keys = array_keys($data); + $firstKey = array_shift($keys); + if ($firstKey !== null && is_array($data[$firstKey])) { + foreach ($data as $row) { + $this->data[] = $row; + } + + return $this; + } + + if (count($data) > 0) { + $this->data[] = $data; + } + + return $this; + } + + /** + * Creates a table from the object instance. + * + * @return void + */ + public function create(): void + { + $this->executeActions(false); + $this->saveData(); + $this->reset(); // reset pending changes + } + + /** + * Updates a table from the object instance. + * + * @return void + */ + public function update(): void + { + $this->executeActions(true); + $this->saveData(); + $this->reset(); // reset pending changes + } + + /** + * Commit the pending data waiting for insertion. + * + * @return void + */ + public function saveData(): void + { + $rows = $this->getData(); + if (empty($rows)) { + return; + } + + $bulk = true; + $row = current($rows); + $c = array_keys($row); + foreach ($this->getData() as $row) { + $k = array_keys($row); + if ($k != $c) { + $bulk = false; + break; + } + } + + if ($bulk) { + $this->getAdapter()->bulkinsert($this->table, $this->getData()); + } else { + foreach ($this->getData() as $row) { + $this->getAdapter()->insert($this->table, $row); + } + } + + $this->resetData(); + } + + /** + * Immediately truncates the table. This operation cannot be undone + * + * @return void + */ + public function truncate(): void + { + $this->getAdapter()->truncateTable($this->getName()); + } + + /** + * Commits the table changes. + * + * If the table doesn't exist it is created otherwise it is updated. + * + * @return void + */ + public function save(): void + { + if ($this->exists()) { + $this->update(); // update the table + } else { + $this->create(); // create the table + } + } + + /** + * Executes all the pending actions for this table + * + * @param bool $exists Whether or not the table existed prior to executing this method + * @return void + */ + protected function executeActions(bool $exists): void + { + // Renaming a table is tricky, specially when running a reversible migration + // down. We will just assume the table already exists if the user commands a + // table rename. + if (!$exists) { + foreach ($this->actions->getActions() as $action) { + if ($action instanceof RenameTable) { + $exists = true; + break; + } + } + } + + // If the table does not exist, the last command in the chain needs to be + // a CreateTable action. + if (!$exists) { + $this->actions->addAction(new CreateTable($this->table)); + } + + $plan = new Plan($this->actions); + $plan->execute($this->getAdapter()); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Table/Column.php b/vendor/robmorgan/phinx/src/Phinx/Db/Table/Column.php new file mode 100644 index 0000000..f825b15 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Table/Column.php @@ -0,0 +1,801 @@ +null = FeatureFlags::$columnNullDefault; + } + + /** + * Sets the column name. + * + * @param string $name Name + * @return $this + */ + public function setName(string $name) + { + $this->name = $name; + + return $this; + } + + /** + * Gets the column name. + * + * @return string|null + */ + public function getName(): ?string + { + return $this->name; + } + + /** + * Sets the column type. + * + * @param string|\Phinx\Util\Literal $type Column type + * @return $this + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * Gets the column type. + * + * @return string|\Phinx\Util\Literal + */ + public function getType() + { + return $this->type; + } + + /** + * Sets the column limit. + * + * @param int|null $limit Limit + * @return $this + */ + public function setLimit(?int $limit) + { + $this->limit = $limit; + + return $this; + } + + /** + * Gets the column limit. + * + * @return int|null + */ + public function getLimit(): ?int + { + return $this->limit; + } + + /** + * Sets whether the column allows nulls. + * + * @param bool $null Null + * @return $this + */ + public function setNull(bool $null) + { + $this->null = (bool)$null; + + return $this; + } + + /** + * Gets whether the column allows nulls. + * + * @return bool + */ + public function getNull(): bool + { + return $this->null; + } + + /** + * Does the column allow nulls? + * + * @return bool + */ + public function isNull(): bool + { + return $this->getNull(); + } + + /** + * Sets the default column value. + * + * @param mixed $default 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 generated option for identity columns. Ignored otherwise. + * + * @param string|null $generated Generated option + * @return $this + */ + public function setGenerated(?string $generated) + { + $this->generated = $generated; + + return $this; + } + + /** + * Gets generated option for identity columns. Null otherwise + * + * @return string|null + */ + public function getGenerated(): ?string + { + return $this->generated; + } + + /** + * Sets whether or not the column is an identity column. + * + * @param bool $identity Identity + * @return $this + */ + public function setIdentity(bool $identity) + { + $this->identity = $identity; + + return $this; + } + + /** + * Gets whether or not the column is an identity column. + * + * @return bool + */ + public function getIdentity(): bool + { + return $this->identity; + } + + /** + * Is the column an identity column? + * + * @return bool + */ + public function isIdentity(): bool + { + return $this->getIdentity(); + } + + /** + * Sets the name of the column to add this column after. + * + * @param string $after After + * @return $this + */ + public function setAfter(string $after) + { + $this->after = $after; + + return $this; + } + + /** + * Returns the name of the column to add this column after. + * + * @return string|null + */ + public function getAfter(): ?string + { + return $this->after; + } + + /** + * Sets the 'ON UPDATE' mysql column function. + * + * @param string $update On Update function + * @return $this + */ + public function setUpdate(string $update) + { + $this->update = $update; + + return $this; + } + + /** + * Returns the value of the ON UPDATE column function. + * + * @return string|null + */ + public function getUpdate(): ?string + { + return $this->update; + } + + /** + * Sets the number precision for decimal or float column. + * + * For example `DECIMAL(5,2)`, 5 is the precision and 2 is the scale, + * and the column could store value from -999.99 to 999.99. + * + * @param int|null $precision Number precision + * @return $this + */ + public function setPrecision(?int $precision) + { + $this->setLimit($precision); + + return $this; + } + + /** + * Gets the number precision for decimal or float column. + * + * For example `DECIMAL(5,2)`, 5 is the precision and 2 is the scale, + * and the column could store value from -999.99 to 999.99. + * + * @return int|null + */ + public function getPrecision(): ?int + { + return $this->limit; + } + + /** + * Sets the column identity increment. + * + * @param int $increment Number increment + * @return $this + */ + public function setIncrement(int $increment) + { + $this->increment = $increment; + + return $this; + } + + /** + * Gets the column identity increment. + * + * @return int|null + */ + public function getIncrement(): ?int + { + return $this->increment; + } + + /** + * Sets the column identity seed. + * + * @param int $seed Number seed + * @return $this + */ + public function setSeed(int $seed) + { + $this->seed = $seed; + + return $this; + } + + /** + * Gets the column identity seed. + * + * @return int + */ + public function getSeed(): ?int + { + return $this->seed; + } + + /** + * Sets the number scale for decimal or float column. + * + * For example `DECIMAL(5,2)`, 5 is the precision and 2 is the scale, + * and the column could store value from -999.99 to 999.99. + * + * @param int|null $scale Number scale + * @return $this + */ + public function setScale(?int $scale) + { + $this->scale = $scale; + + return $this; + } + + /** + * Gets the number scale for decimal or float column. + * + * For example `DECIMAL(5,2)`, 5 is the precision and 2 is the scale, + * and the column could store value from -999.99 to 999.99. + * + * @return int + */ + public function getScale(): ?int + { + return $this->scale; + } + + /** + * Sets the number precision and scale for decimal or float column. + * + * For example `DECIMAL(5,2)`, 5 is the precision and 2 is the scale, + * and the column could store value from -999.99 to 999.99. + * + * @param int $precision Number precision + * @param int $scale Number scale + * @return $this + */ + public function setPrecisionAndScale(int $precision, int $scale) + { + $this->setLimit($precision); + $this->scale = $scale; + + return $this; + } + + /** + * Sets the column comment. + * + * @param string|null $comment Comment + * @return $this + */ + public function setComment(?string $comment) + { + $this->comment = $comment; + + return $this; + } + + /** + * Gets the column comment. + * + * @return string + */ + public function getComment(): ?string + { + return $this->comment; + } + + /** + * Sets whether field should be signed. + * + * @param bool $signed Signed + * @return $this + */ + public function setSigned(bool $signed) + { + $this->signed = (bool)$signed; + + return $this; + } + + /** + * Gets whether field should be signed. + * + * @return bool + */ + public function getSigned(): bool + { + return $this->signed; + } + + /** + * Should the column be signed? + * + * @return bool + */ + public function isSigned(): bool + { + return $this->getSigned(); + } + + /** + * Sets whether the field should have a timezone identifier. + * Used for date/time columns only! + * + * @param bool $timezone Timezone + * @return $this + */ + public function setTimezone(bool $timezone) + { + $this->timezone = (bool)$timezone; + + return $this; + } + + /** + * Gets whether field has a timezone identifier. + * + * @return bool + */ + public function getTimezone(): bool + { + return $this->timezone; + } + + /** + * Should the column have a timezone? + * + * @return bool + */ + public function isTimezone(): bool + { + return $this->getTimezone(); + } + + /** + * Sets field properties. + * + * @param array $properties Properties + * @return $this + */ + public function setProperties(array $properties) + { + $this->properties = $properties; + + return $this; + } + + /** + * Gets field properties + * + * @return array + */ + public function getProperties(): array + { + return $this->properties; + } + + /** + * Sets field values. + * + * @param string[]|string $values Value(s) + * @return $this + */ + public function setValues($values) + { + if (!is_array($values)) { + $values = preg_split('/,\s*/', $values) ?: []; + } + $this->values = $values; + + return $this; + } + + /** + * Gets field values + * + * @return array|null + */ + public function getValues(): ?array + { + return $this->values; + } + + /** + * Sets the column collation. + * + * @param string $collation Collation + * @return $this + */ + public function setCollation(string $collation) + { + $this->collation = $collation; + + return $this; + } + + /** + * Gets the column collation. + * + * @return string|null + */ + public function getCollation(): ?string + { + return $this->collation; + } + + /** + * Sets the column character set. + * + * @param string $encoding Encoding + * @return $this + */ + public function setEncoding(string $encoding) + { + $this->encoding = $encoding; + + return $this; + } + + /** + * Gets the column character set. + * + * @return string|null + */ + public function getEncoding(): ?string + { + return $this->encoding; + } + + /** + * Sets the column SRID. + * + * @param int $srid SRID + * @return $this + */ + public function setSrid(int $srid) + { + $this->srid = $srid; + + return $this; + } + + /** + * Gets the column SRID. + * + * @return int|null + */ + public function getSrid(): ?int + { + return $this->srid; + } + + /** + * Gets all allowed options. Each option must have a corresponding `setFoo` method. + * + * @return array + */ + protected function getValidOptions(): array + { + return [ + 'limit', + 'default', + 'null', + 'identity', + 'scale', + 'after', + 'update', + 'comment', + 'signed', + 'timezone', + 'properties', + 'values', + 'collation', + 'encoding', + 'srid', + 'seed', + 'increment', + 'generated', + ]; + } + + /** + * Gets all aliased options. Each alias must reference a valid option. + * + * @return array + */ + protected function getAliasedOptions(): array + { + return [ + 'length' => 'limit', + 'precision' => 'limit', + ]; + } + + /** + * Utility method that maps an array of column options to this objects methods. + * + * @param array $options Options + * @throws \RuntimeException + * @return $this + */ + public function setOptions(array $options) + { + $validOptions = $this->getValidOptions(); + $aliasOptions = $this->getAliasedOptions(); + + if (isset($options['identity']) && $options['identity'] && !isset($options['null'])) { + $options['null'] = false; + } + + 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/robmorgan/phinx/src/Phinx/Db/Table/ForeignKey.php b/vendor/robmorgan/phinx/src/Phinx/Db/Table/ForeignKey.php new file mode 100644 index 0000000..cd14893 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Table/ForeignKey.php @@ -0,0 +1,237 @@ + + */ + protected static $validOptions = ['delete', 'update', 'constraint']; + + /** + * @var string[] + */ + protected $columns = []; + + /** + * @var \Phinx\Db\Table\Table + */ + protected $referencedTable; + + /** + * @var string[] + */ + protected $referencedColumns = []; + + /** + * @var string|null + */ + protected $onDelete; + + /** + * @var string|null + */ + protected $onUpdate; + + /** + * @var string|null + */ + protected $constraint; + + /** + * Sets the foreign key columns. + * + * @param string[]|string $columns Columns + * @return $this + */ + public function setColumns($columns) + { + $this->columns = is_string($columns) ? [$columns] : $columns; + + return $this; + } + + /** + * Gets the foreign key columns. + * + * @return string[] + */ + public function getColumns(): array + { + return $this->columns; + } + + /** + * Sets the foreign key referenced table. + * + * @param \Phinx\Db\Table\Table $table The table this KEY is pointing to + * @return $this + */ + public function setReferencedTable(Table $table) + { + $this->referencedTable = $table; + + return $this; + } + + /** + * Gets the foreign key referenced table. + * + * @return \Phinx\Db\Table\Table + */ + public function getReferencedTable(): Table + { + return $this->referencedTable; + } + + /** + * Sets the foreign key referenced columns. + * + * @param string[] $referencedColumns Referenced columns + * @return $this + */ + public function setReferencedColumns(array $referencedColumns) + { + $this->referencedColumns = $referencedColumns; + + return $this; + } + + /** + * Gets the foreign key referenced columns. + * + * @return string[] + */ + public function getReferencedColumns(): array + { + return $this->referencedColumns; + } + + /** + * Sets ON DELETE action for the foreign key. + * + * @param string $onDelete On Delete + * @return $this + */ + public function setOnDelete(string $onDelete) + { + $this->onDelete = $this->normalizeAction($onDelete); + + return $this; + } + + /** + * Gets ON DELETE action for the foreign key. + * + * @return string|null + */ + public function getOnDelete(): ?string + { + return $this->onDelete; + } + + /** + * Gets ON UPDATE action for the foreign key. + * + * @return string|null + */ + public function getOnUpdate(): ?string + { + return $this->onUpdate; + } + + /** + * Sets ON UPDATE action for the foreign key. + * + * @param string $onUpdate On Update + * @return $this + */ + public function setOnUpdate(string $onUpdate) + { + $this->onUpdate = $this->normalizeAction($onUpdate); + + return $this; + } + + /** + * Sets constraint for the foreign key. + * + * @param string $constraint Constraint + * @return $this + */ + public function setConstraint(string $constraint) + { + $this->constraint = $constraint; + + return $this; + } + + /** + * Gets constraint name for the foreign key. + * + * @return string|null + */ + public function getConstraint(): ?string + { + return $this->constraint; + } + + /** + * Utility method that maps an array of index options to this objects methods. + * + * @param array $options Options + * @throws \RuntimeException + * @return $this + */ + public function setOptions(array $options) + { + foreach ($options as $option => $value) { + if (!in_array($option, static::$validOptions, true)) { + throw new RuntimeException(sprintf('"%s" is not a valid foreign key option.', $option)); + } + + // handle $options['delete'] as $options['update'] + if ($option === 'delete') { + $this->setOnDelete($value); + } elseif ($option === 'update') { + $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 Action + * @throws \InvalidArgumentException + * @return string + */ + protected function normalizeAction(string $action): string + { + $constantName = 'static::' . str_replace(' ', '_', strtoupper(trim($action))); + if (!defined($constantName)) { + throw new InvalidArgumentException('Unknown action passed: ' . $action); + } + + return constant($constantName); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Table/Index.php b/vendor/robmorgan/phinx/src/Phinx/Db/Table/Index.php new file mode 100644 index 0000000..f405f02 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Table/Index.php @@ -0,0 +1,227 @@ +columns = is_string($columns) ? [$columns] : $columns; + + return $this; + } + + /** + * Gets the index columns. + * + * @return string[]|null + */ + public function getColumns(): ?array + { + return $this->columns; + } + + /** + * Sets the index type. + * + * @param string $type Type + * @return $this + */ + public function setType(string $type) + { + $this->type = $type; + + return $this; + } + + /** + * Gets the index type. + * + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * Sets the index name. + * + * @param string $name Name + * @return $this + */ + public function setName(string $name) + { + $this->name = $name; + + return $this; + } + + /** + * Gets the index name. + * + * @return string|null + */ + public function getName(): ?string + { + return $this->name; + } + + /** + * Sets the index limit. + * + * @param int|array $limit limit value or array of limit value + * @return $this + */ + public function setLimit($limit) + { + $this->limit = $limit; + + return $this; + } + + /** + * Gets the index limit. + * + * @return int|array|null + */ + public function getLimit() + { + return $this->limit; + } + + /** + * Sets the index columns sort order. + * + * @param string[] $order column name sort order key value pair + * @return $this + */ + public function setOrder(array $order) + { + $this->order = $order; + + return $this; + } + + /** + * Gets the index columns sort order. + * + * @return string[]|null + */ + public function getOrder(): ?array + { + return $this->order; + } + + /** + * Sets the index included columns. + * + * @param string[] $includedColumns Columns + * @return $this + */ + public function setInclude(array $includedColumns) + { + $this->includedColumns = $includedColumns; + + return $this; + } + + /** + * Gets the index included columns. + * + * @return string[]|null + */ + public function getInclude(): ?array + { + return $this->includedColumns; + } + + /** + * Utility method that maps an array of index options to this objects methods. + * + * @param array $options Options + * @throws \RuntimeException + * @return $this + */ + public function setOptions(array $options) + { + // Valid Options + $validOptions = ['type', 'unique', 'name', 'limit', 'order', 'include']; + 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/robmorgan/phinx/src/Phinx/Db/Table/Table.php b/vendor/robmorgan/phinx/src/Phinx/Db/Table/Table.php new file mode 100644 index 0000000..0352157 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Table/Table.php @@ -0,0 +1,84 @@ + + */ + protected $options; + + /** + * @param string $name The table name + * @param array $options The creation options for this table + * @throws \InvalidArgumentException + */ + public function __construct($name, array $options = []) + { + if (empty($name)) { + throw new InvalidArgumentException('Cannot use an empty table name'); + } + + $this->name = $name; + $this->options = $options; + } + + /** + * Sets the table name. + * + * @param string $name The name of the table + * @return $this + */ + public function setName(string $name) + { + $this->name = $name; + + return $this; + } + + /** + * Gets the table name. + * + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * Gets the table options + * + * @return array + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * Sets the table options + * + * @param array $options The options for the table creation + * @return $this + */ + public function setOptions(array $options) + { + $this->options = $options; + + return $this; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Db/Util/AlterInstructions.php b/vendor/robmorgan/phinx/src/Phinx/Db/Util/AlterInstructions.php new file mode 100644 index 0000000..ddc49fc --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Db/Util/AlterInstructions.php @@ -0,0 +1,122 @@ +alterParts = $alterParts; + $this->postSteps = $postSteps; + } + + /** + * Adds another part to the single ALTER instruction + * + * @param string $part The SQL snipped to add as part of the ALTER instruction + * @return void + */ + public function addAlter(string $part): void + { + $this->alterParts[] = $part; + } + + /** + * Adds a SQL command to be executed after the ALTER instruction. + * This method allows a callable, with will get an empty array as state + * for the first time and will pass the return value of the callable to + * the next callable, if present. + * + * This allows to keep a single state across callbacks. + * + * @param string|callable $sql The SQL to run after, or a callable to execute + * @return void + */ + public function addPostStep($sql): void + { + $this->postSteps[] = $sql; + } + + /** + * Returns the alter SQL snippets + * + * @return string[] + */ + public function getAlterParts(): array + { + return $this->alterParts; + } + + /** + * Returns the SQL commands to run after the ALTER instruction + * + * @return (string|callable)[] + */ + public function getPostSteps(): array + { + return $this->postSteps; + } + + /** + * Merges another AlterInstructions object to this one + * + * @param \Phinx\Db\Util\AlterInstructions $other The other collection of instructions to merge in + * @return void + */ + public function merge(AlterInstructions $other): void + { + $this->alterParts = array_merge($this->alterParts, $other->getAlterParts()); + $this->postSteps = array_merge($this->postSteps, $other->getPostSteps()); + } + + /** + * Executes the ALTER instruction and all of the post steps. + * + * @param string $alterTemplate The template for the alter instruction + * @param callable $executor The function to be used to execute all instructions + * @return void + */ + public function execute(string $alterTemplate, callable $executor): void + { + if ($this->alterParts) { + $alter = sprintf($alterTemplate, implode(', ', $this->alterParts)); + $executor($alter); + } + + $state = []; + + foreach ($this->postSteps as $instruction) { + if (is_callable($instruction)) { + $state = $instruction($state); + continue; + } + + $executor($instruction); + } + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Migration/AbstractMigration.php b/vendor/robmorgan/phinx/src/Phinx/Migration/AbstractMigration.php new file mode 100644 index 0000000..9602efe --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Migration/AbstractMigration.php @@ -0,0 +1,338 @@ + + */ +abstract class AbstractMigration implements MigrationInterface +{ + /** + * @var string + */ + protected $environment; + + /** + * @var int + */ + protected $version; + + /** + * @var \Phinx\Db\Adapter\AdapterInterface|null + */ + protected $adapter; + + /** + * @var \Symfony\Component\Console\Output\OutputInterface|null + */ + protected $output; + + /** + * @var \Symfony\Component\Console\Input\InputInterface|null + */ + protected $input; + + /** + * Whether this migration is being applied or reverted + * + * @var bool + */ + protected $isMigratingUp = true; + + /** + * List of all the table objects created by this migration + * + * @var array<\Phinx\Db\Table> + */ + protected $tables = []; + + /** + * @param string $environment Environment Detected + * @param int $version Migration Version + * @param \Symfony\Component\Console\Input\InputInterface|null $input Input + * @param \Symfony\Component\Console\Output\OutputInterface|null $output Output + */ + final public function __construct(string $environment, int $version, ?InputInterface $input = null, ?OutputInterface $output = null) + { + $this->environment = $environment; + $this->version = $version; + + if ($input !== null) { + $this->setInput($input); + } + + if ($output !== null) { + $this->setOutput($output); + } + } + + /** + * @inheritDoc + */ + public function setAdapter(AdapterInterface $adapter): MigrationInterface + { + $this->adapter = $adapter; + + return $this; + } + + /** + * @inheritDoc + */ + public function getAdapter(): ?AdapterInterface + { + return $this->adapter; + } + + /** + * @inheritDoc + */ + public function setInput(InputInterface $input): MigrationInterface + { + $this->input = $input; + + return $this; + } + + /** + * @inheritDoc + */ + public function getInput(): ?InputInterface + { + return $this->input; + } + + /** + * @inheritDoc + */ + public function setOutput(OutputInterface $output): MigrationInterface + { + $this->output = $output; + + return $this; + } + + /** + * @inheritDoc + */ + public function getOutput(): ?OutputInterface + { + return $this->output; + } + + /** + * @inheritDoc + */ + public function getName(): string + { + return static::class; + } + + /** + * @inheritDoc + */ + public function getEnvironment(): string + { + return $this->environment; + } + + /** + * @inheritDoc + */ + public function setVersion($version): MigrationInterface + { + $this->version = $version; + + return $this; + } + + /** + * @inheritDoc + */ + public function getVersion(): int + { + return $this->version; + } + + /** + * @inheritDoc + */ + public function setMigratingUp(bool $isMigratingUp): MigrationInterface + { + $this->isMigratingUp = $isMigratingUp; + + return $this; + } + + /** + * @inheritDoc + */ + public function isMigratingUp(): bool + { + return $this->isMigratingUp; + } + + /** + * @inheritDoc + */ + public function execute(string $sql, array $params = []): int + { + return $this->getAdapter()->execute($sql, $params); + } + + /** + * @inheritDoc + */ + public function query(string $sql, array $params = []) + { + return $this->getAdapter()->query($sql, $params); + } + + /** + * @inheritDoc + */ + public function getQueryBuilder(): Query + { + return $this->getAdapter()->getQueryBuilder(); + } + + /** + * @inheritDoc + */ + public function fetchRow(string $sql) + { + return $this->getAdapter()->fetchRow($sql); + } + + /** + * @inheritDoc + */ + public function fetchAll(string $sql): array + { + return $this->getAdapter()->fetchAll($sql); + } + + /** + * @inheritDoc + */ + public function createDatabase(string $name, array $options): void + { + $this->getAdapter()->createDatabase($name, $options); + } + + /** + * @inheritDoc + */ + public function dropDatabase(string $name): void + { + $this->getAdapter()->dropDatabase($name); + } + + /** + * @inheritDoc + */ + public function createSchema(string $name): void + { + $this->getAdapter()->createSchema($name); + } + + /** + * @inheritDoc + */ + public function dropSchema(string $name): void + { + $this->getAdapter()->dropSchema($name); + } + + /** + * @inheritDoc + */ + public function hasTable(string $tableName): bool + { + return $this->getAdapter()->hasTable($tableName); + } + + /** + * @inheritDoc + */ + public function table(string $tableName, array $options = []): Table + { + $table = new Table($tableName, $options, $this->getAdapter()); + $this->tables[] = $table; + + return $table; + } + + /** + * Perform checks on the migration, print a warning + * if there are potential problems. + * + * Right now, the only check is if there is both a `change()` and + * an `up()` or a `down()` method. + * + * @return void + */ + public function preFlightCheck(): void + { + if (method_exists($this, MigrationInterface::CHANGE)) { + if ( + method_exists($this, MigrationInterface::UP) || + method_exists($this, MigrationInterface::DOWN) + ) { + $this->output->writeln(sprintf( + 'warning Migration contains both change() and up()/down() methods. Ignoring up() and down().' + )); + } + } + } + + /** + * Perform checks on the migration after completion + * + * Right now, the only check is whether all changes were committed + * + * @throws \RuntimeException + * @return void + */ + public function postFlightCheck(): void + { + foreach ($this->tables as $table) { + if ($table->hasPendingActions()) { + throw new RuntimeException('Migration has pending actions after execution!'); + } + } + } + + /** + * Checks to see if the migration should be executed. + * + * Returns true by default. + * + * You can use this to prevent a migration from executing. + * + * @return bool + */ + public function shouldExecute(): bool + { + return true; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Migration/AbstractTemplateCreation.php b/vendor/robmorgan/phinx/src/Phinx/Migration/AbstractTemplateCreation.php new file mode 100644 index 0000000..286bc18 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Migration/AbstractTemplateCreation.php @@ -0,0 +1,74 @@ +setInput($input); + } + if ($output !== null) { + $this->setOutput($output); + } + } + + /** + * @inheritDoc + */ + public function getInput(): InputInterface + { + return $this->input; + } + + /** + * @inheritDoc + */ + public function setInput(InputInterface $input): CreationInterface + { + $this->input = $input; + + return $this; + } + + /** + * @inheritDoc + */ + public function getOutput(): OutputInterface + { + return $this->output; + } + + /** + * @inheritDoc + */ + public function setOutput(OutputInterface $output): CreationInterface + { + $this->output = $output; + + return $this; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Migration/CreationInterface.php b/vendor/robmorgan/phinx/src/Phinx/Migration/CreationInterface.php new file mode 100644 index 0000000..b7c9fe3 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Migration/CreationInterface.php @@ -0,0 +1,69 @@ + + */ +interface CreationInterface +{ + /** + * @param \Symfony\Component\Console\Input\InputInterface|null $input Input + * @param \Symfony\Component\Console\Output\OutputInterface|null $output Output + */ + public function __construct(?InputInterface $input = null, ?OutputInterface $output = null); + + /** + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @return $this + */ + public function setInput(InputInterface $input); + + /** + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return $this + */ + public function setOutput(OutputInterface $output); + + /** + * @return \Symfony\Component\Console\Input\InputInterface + */ + public function getInput(): InputInterface; + + /** + * @return \Symfony\Component\Console\Output\OutputInterface + */ + public function getOutput(): OutputInterface; + + /** + * 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(): string; + + /** + * 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(string $migrationFilename, string $className, string $baseClassName): void; +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Migration/IrreversibleMigrationException.php b/vendor/robmorgan/phinx/src/Phinx/Migration/IrreversibleMigrationException.php new file mode 100644 index 0000000..376bcdc --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Migration/IrreversibleMigrationException.php @@ -0,0 +1,20 @@ + + */ +class IrreversibleMigrationException extends Exception +{ +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Migration/Manager.php b/vendor/robmorgan/phinx/src/Phinx/Migration/Manager.php new file mode 100644 index 0000000..76b68a0 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Migration/Manager.php @@ -0,0 +1,1141 @@ +setConfig($config); + $this->setInput($input); + $this->setOutput($output); + } + + /** + * Prints the specified environment's migration status. + * + * @param string $environment environment to print status of + * @param string|null $format format to print status in (either text, json, or null) + * @throws \RuntimeException + * @return array array indicating if there are any missing or down migrations + */ + public function printStatus(string $environment, ?string $format = null): array + { + $output = $this->getOutput(); + $hasDownMigration = false; + $hasMissingMigration = false; + $migrations = $this->getMigrations($environment); + $migrationCount = 0; + $missingCount = 0; + $pendingMigrationCount = 0; + $finalMigrations = []; + $verbosity = $output->getVerbosity(); + if ($format === 'json') { + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + } + if (count($migrations)) { + // 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('', $this->verbosityLevel); + + switch ($this->getConfig()->getVersionOrder()) { + case Config::VERSION_ORDER_CREATION_TIME: + $migrationIdAndStartedHeader = '[Migration ID] Started '; + break; + case Config::VERSION_ORDER_EXECUTION_TIME: + $migrationIdAndStartedHeader = 'Migration ID [Started ]'; + break; + default: + throw new RuntimeException('Invalid version_order configuration option'); + } + + $output->writeln(" Status $migrationIdAndStartedHeader Finished Migration Name ", $this->verbosityLevel); + $output->writeln('----------------------------------------------------------------------------------', $this->verbosityLevel); + + $env = $this->getEnvironment($environment); + $versions = $env->getVersionLog(); + + $maxNameLength = $versions ? max(array_map(function ($version) { + return strlen($version['migration_name']); + }, $versions)) : 0; + + $missingVersions = array_diff_key($versions, $migrations); + $missingCount = count($missingVersions); + + $hasMissingMigration = !empty($missingVersions); + + // get the migrations sorted in the same way as the versions + /** @var \Phinx\Migration\AbstractMigration[] $sortedMigrations */ + $sortedMigrations = []; + + foreach ($versions as $versionCreationTime => $version) { + if (isset($migrations[$versionCreationTime])) { + array_push($sortedMigrations, $migrations[$versionCreationTime]); + unset($migrations[$versionCreationTime]); + } + } + + if (empty($sortedMigrations) && !empty($missingVersions)) { + // this means we have no up migrations, so we write all the missing versions already so they show up + // before any possible down migration + foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) { + $this->printMissingVersion($missingVersion, $maxNameLength); + + unset($missingVersions[$missingVersionCreationTime]); + } + } + + // any migration left in the migrations (ie. not unset when sorting the migrations by the version order) is + // a migration that is down, so we add them to the end of the sorted migrations list + if (!empty($migrations)) { + $sortedMigrations = array_merge($sortedMigrations, $migrations); + } + + $migrationCount = count($sortedMigrations); + foreach ($sortedMigrations as $migration) { + $version = array_key_exists($migration->getVersion(), $versions) ? $versions[$migration->getVersion()] : false; + if ($version) { + // check if there are missing versions before this version + foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) { + if ($this->getConfig()->isVersionOrderCreationTime()) { + if ($missingVersion['version'] > $version['version']) { + break; + } + } else { + if ($missingVersion['start_time'] > $version['start_time']) { + break; + } elseif ( + $missingVersion['start_time'] == $version['start_time'] && + $missingVersion['version'] > $version['version'] + ) { + break; + } + } + + $this->printMissingVersion($missingVersion, $maxNameLength); + + unset($missingVersions[$missingVersionCreationTime]); + } + + $status = ' up '; + } else { + $pendingMigrationCount++; + $hasDownMigration = true; + $status = ' down '; + } + $maxNameLength = max($maxNameLength, strlen($migration->getName())); + + $output->writeln( + sprintf( + '%s %14.0f %19s %19s %s', + $status, + $migration->getVersion(), + ($version ? $version['start_time'] : ''), + ($version ? $version['end_time'] : ''), + $migration->getName() + ), + $this->verbosityLevel + ); + + if ($version && $version['breakpoint']) { + $output->writeln(' BREAKPOINT SET', $this->verbosityLevel); + } + + $finalMigrations[] = ['migration_status' => trim(strip_tags($status)), 'migration_id' => sprintf('%14.0f', $migration->getVersion()), 'migration_name' => $migration->getName()]; + unset($versions[$migration->getVersion()]); + } + + // and finally add any possibly-remaining missing migrations + foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) { + $this->printMissingVersion($missingVersion, $maxNameLength); + + unset($missingVersions[$missingVersionCreationTime]); + } + } else { + // there are no migrations + $output->writeln('', $this->verbosityLevel); + $output->writeln('There are no available migrations. Try creating one using the create command.', $this->verbosityLevel); + } + + // write an empty line + $output->writeln('', $this->verbosityLevel); + + if ($format !== null) { + switch ($format) { + case AbstractCommand::FORMAT_JSON: + $output->setVerbosity($verbosity); + $output->writeln(json_encode( + [ + 'pending_count' => $pendingMigrationCount, + 'missing_count' => $missingCount, + 'total_count' => $migrationCount + $missingCount, + 'migrations' => $finalMigrations, + ] + )); + break; + default: + $output->writeln('Unsupported format: ' . $format . ''); + } + } + + return [ + 'hasMissingMigration' => $hasMissingMigration, + 'hasDownMigration' => $hasDownMigration, + ]; + } + + /** + * Print Missing Version + * + * @param array $version The missing version to print (in the format returned by Environment.getVersionLog). + * @param int $maxNameLength The maximum migration name length. + * @return void + */ + protected function printMissingVersion(array $version, int $maxNameLength): void + { + $this->getOutput()->writeln(sprintf( + ' up %14.0f %19s %19s %s ** MISSING MIGRATION FILE **', + $version['version'], + $version['start_time'], + $version['end_time'], + str_pad($version['migration_name'], $maxNameLength, ' ') + )); + + if ($version && $version['breakpoint']) { + $this->getOutput()->writeln(' BREAKPOINT SET'); + } + } + + /** + * Migrate to the version of the database on a given date. + * + * @param string $environment Environment + * @param \DateTime $dateTime Date to migrate to + * @param bool $fake flag that if true, we just record running the migration, but not actually do the + * migration + * @return void + */ + public function migrateToDateTime(string $environment, DateTime $dateTime, bool $fake = false): void + { + $versions = array_keys($this->getMigrations($environment)); + $dateString = $dateTime->format('YmdHis'); + + $outstandingMigrations = array_filter($versions, function ($version) use ($dateString) { + return $version <= $dateString; + }); + + if (count($outstandingMigrations) > 0) { + $migration = max($outstandingMigrations); + $this->getOutput()->writeln('Migrating to version ' . $migration, $this->verbosityLevel); + $this->migrate($environment, $migration, $fake); + } + } + + /** + * Migrate an environment to the specified version. + * + * @param string $environment Environment + * @param int|null $version version to migrate to + * @param bool $fake flag that if true, we just record running the migration, but not actually do the migration + * @return void + */ + public function migrate(string $environment, ?int $version = null, bool $fake = false): void + { + $migrations = $this->getMigrations($environment); + $env = $this->getEnvironment($environment); + $versions = $env->getVersions(); + $current = $env->getCurrentVersion(); + + if (empty($versions) && empty($migrations)) { + return; + } + + if ($version === null) { + $version = max(array_merge($versions, array_keys($migrations))); + } else { + if ($version != 0 && !isset($migrations[$version])) { + $this->output->writeln(sprintf( + 'warning %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($environment, $migration, MigrationInterface::DOWN, $fake); + } + } + } + + ksort($migrations); + foreach ($migrations as $migration) { + if ($migration->getVersion() > $version) { + break; + } + + if (!in_array($migration->getVersion(), $versions)) { + $this->executeMigration($environment, $migration, MigrationInterface::UP, $fake); + } + } + } + + /** + * Execute a migration against the specified environment. + * + * @param string $name Environment Name + * @param \Phinx\Migration\MigrationInterface $migration Migration + * @param string $direction Direction + * @param bool $fake flag that if true, we just record running the migration, but not actually do the migration + * @return void + */ + public function executeMigration(string $name, MigrationInterface $migration, string $direction = MigrationInterface::UP, bool $fake = false): void + { + $this->getOutput()->writeln('', $this->verbosityLevel); + + // Skip the migration if it should not be executed + if (!$migration->shouldExecute()) { + $this->printMigrationStatus($migration, 'skipped'); + + return; + } + + $this->printMigrationStatus($migration, ($direction === MigrationInterface::UP ? 'migrating' : 'reverting')); + + // Execute the migration and log the time elapsed. + $start = microtime(true); + $this->getEnvironment($name)->executeMigration($migration, $direction, $fake); + $end = microtime(true); + + $this->printMigrationStatus( + $migration, + ($direction === MigrationInterface::UP ? 'migrated' : 'reverted'), + sprintf('%.4fs', $end - $start) + ); + } + + /** + * Execute a seeder against the specified environment. + * + * @param string $name Environment Name + * @param \Phinx\Seed\SeedInterface $seed Seed + * @return void + */ + public function executeSeed(string $name, SeedInterface $seed): void + { + $this->getOutput()->writeln('', $this->verbosityLevel); + + // Skip the seed if it should not be executed + if (!$seed->shouldExecute()) { + $this->printSeedStatus($seed, 'skipped'); + + return; + } + + $this->printSeedStatus($seed, 'seeding'); + + // Execute the seeder and log the time elapsed. + $start = microtime(true); + $this->getEnvironment($name)->executeSeed($seed); + $end = microtime(true); + + $this->printSeedStatus( + $seed, + 'seeded', + sprintf('%.4fs', $end - $start) + ); + } + + /** + * Print Migration Status + * + * @param \Phinx\Migration\MigrationInterface $migration Migration + * @param string $status Status of the migration + * @param string|null $duration Duration the migration took the be executed + * @return void + */ + protected function printMigrationStatus(MigrationInterface $migration, string $status, ?string $duration = null): void + { + $this->printStatusOutput( + $migration->getVersion() . ' ' . $migration->getName(), + $status, + $duration + ); + } + + /** + * Print Seed Status + * + * @param \Phinx\Seed\SeedInterface $seed Seed + * @param string $status Status of the seed + * @param string|null $duration Duration the seed took the be executed + * @return void + */ + protected function printSeedStatus(SeedInterface $seed, string $status, ?string $duration = null): void + { + $this->printStatusOutput( + $seed->getName(), + $status, + $duration + ); + } + + /** + * Print Status in Output + * + * @param string $name Name of the migration or seed + * @param string $status Status of the migration or seed + * @param string|null $duration Duration the migration or seed took the be executed + * @return void + */ + protected function printStatusOutput(string $name, string $status, ?string $duration = null): void + { + $this->getOutput()->writeln( + ' ==' . + ' ' . $name . ':' . + ' ' . $status . ' ' . $duration . '', + $this->verbosityLevel + ); + } + + /** + * Rollback an environment to the specified version. + * + * @param string $environment Environment + * @param int|string|null $target Target + * @param bool $force Force + * @param bool $targetMustMatchVersion Target must match version + * @param bool $fake Flag that if true, we just record running the migration, but not actually do the migration + * @return void + */ + public function rollback(string $environment, $target = null, bool $force = false, bool $targetMustMatchVersion = true, bool $fake = false): void + { + // note that the migrations are indexed by name (aka creation time) in ascending order + $migrations = $this->getMigrations($environment); + + // note that the version log are also indexed by name with the proper ascending order according to the version order + $executedVersions = $this->getEnvironment($environment)->getVersionLog(); + + // get a list of migrations sorted in the opposite way of the executed versions + $sortedMigrations = []; + + foreach ($executedVersions as $versionCreationTime => &$executedVersion) { + // if we have a date (ie. the target must not match a version) and we are sorting by execution time, we + // convert the version start time so we can compare directly with the target date + if (!$this->getConfig()->isVersionOrderCreationTime() && !$targetMustMatchVersion) { + /** @var \DateTime $dateTime */ + $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', $executedVersion['start_time']); + $executedVersion['start_time'] = $dateTime->format('YmdHis'); + } + + if (isset($migrations[$versionCreationTime])) { + array_unshift($sortedMigrations, $migrations[$versionCreationTime]); + } else { + // this means the version is missing so we unset it so that we don't consider it when rolling back + // migrations (or choosing the last up version as target) + unset($executedVersions[$versionCreationTime]); + } + } + + if ($target === 'all' || $target === '0') { + $target = 0; + } elseif (!is_numeric($target) && $target !== null) { // try to find a target version based on name + // search through the migrations using the name + $migrationNames = array_map(function ($item) { + return $item['migration_name']; + }, $executedVersions); + $found = array_search($target, $migrationNames, true); + + // check on was found + if ($found !== false) { + $target = (string)$found; + } else { + $this->getOutput()->writeln("No migration found with name ($target)"); + + return; + } + } + + // Check we have at least 1 migration to revert + $executedVersionCreationTimes = array_keys($executedVersions); + if (empty($executedVersionCreationTimes) || $target == end($executedVersionCreationTimes)) { + $this->getOutput()->writeln('No migrations to rollback'); + + return; + } + + // If no target was supplied, revert the last migration + if ($target === null) { + // Get the migration before the last run migration + $prev = count($executedVersionCreationTimes) - 2; + $target = $prev >= 0 ? $executedVersionCreationTimes[$prev] : 0; + } + + // If the target must match a version, check the target version exists + if ($targetMustMatchVersion && $target !== 0 && !isset($migrations[$target])) { + $this->getOutput()->writeln("Target version ($target) not found"); + + return; + } + + // Rollback all versions until we find the wanted rollback target + $rollbacked = false; + + foreach ($sortedMigrations as $migration) { + if ($targetMustMatchVersion && $migration->getVersion() == $target) { + break; + } + + if (in_array($migration->getVersion(), $executedVersionCreationTimes)) { + $executedVersion = $executedVersions[$migration->getVersion()]; + + if (!$targetMustMatchVersion) { + if ( + ($this->getConfig()->isVersionOrderCreationTime() && $executedVersion['version'] <= $target) || + (!$this->getConfig()->isVersionOrderCreationTime() && $executedVersion['start_time'] <= $target) + ) { + break; + } + } + + if ($executedVersion['breakpoint'] != 0 && !$force) { + $this->getOutput()->writeln('Breakpoint reached. Further rollbacks inhibited.'); + break; + } + $this->executeMigration($environment, $migration, MigrationInterface::DOWN, $fake); + $rollbacked = true; + } + } + + if (!$rollbacked) { + $this->getOutput()->writeln('No migrations to rollback'); + } + } + + /** + * Run database seeders against an environment. + * + * @param string $environment Environment + * @param string|null $seed Seeder + * @throws \InvalidArgumentException + * @return void + */ + public function seed(string $environment, ?string $seed = null): void + { + $seeds = $this->getSeeds($environment); + + if ($seed === null) { + // run all seeders + foreach ($seeds as $seeder) { + if (array_key_exists($seeder->getName(), $seeds)) { + $this->executeSeed($environment, $seeder); + } + } + } else { + // run only one seeder + if (array_key_exists($seed, $seeds)) { + $this->executeSeed($environment, $seeds[$seed]); + } else { + throw new InvalidArgumentException(sprintf('The seed class "%s" does not exist', $seed)); + } + } + } + + /** + * Sets the environments. + * + * @param \Phinx\Migration\Manager\Environment[] $environments Environments + * @return $this + */ + public function setEnvironments(array $environments = []) + { + $this->environments = $environments; + + return $this; + } + + /** + * Gets the manager class for the given environment. + * + * @param string $name Environment Name + * @throws \InvalidArgumentException + * @return \Phinx\Migration\Manager\Environment + */ + public function getEnvironment(string $name): Environment + { + if (isset($this->environments[$name])) { + return $this->environments[$name]; + } + + // check the environment exists + if (!$this->getConfig()->hasEnvironment($name)) { + throw new InvalidArgumentException(sprintf( + 'The environment "%s" does not exist', + $name + )); + } + + // create an environment instance and cache it + $envOptions = $this->getConfig()->getEnvironment($name); + $envOptions['version_order'] = $this->getConfig()->getVersionOrder(); + $envOptions['data_domain'] = $this->getConfig()->getDataDomain(); + + $environment = new Environment($name, $envOptions); + $this->environments[$name] = $environment; + $environment->setInput($this->getInput()); + $environment->setOutput($this->getOutput()); + + return $environment; + } + + /** + * Sets the user defined PSR-11 container + * + * @param \Psr\Container\ContainerInterface $container Container + * @return $this + */ + public function setContainer(ContainerInterface $container) + { + $this->container = $container; + + return $this; + } + + /** + * Sets the console input. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @return $this + */ + public function setInput(InputInterface $input) + { + $this->input = $input; + + return $this; + } + + /** + * Gets the console input. + * + * @return \Symfony\Component\Console\Input\InputInterface + */ + public function getInput(): InputInterface + { + return $this->input; + } + + /** + * Sets the console output. + * + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return $this + */ + public function setOutput(OutputInterface $output) + { + $this->output = $output; + + return $this; + } + + /** + * Gets the console output. + * + * @return \Symfony\Component\Console\Output\OutputInterface + */ + public function getOutput(): OutputInterface + { + return $this->output; + } + + /** + * Sets the database migrations. + * + * @param \Phinx\Migration\AbstractMigration[] $migrations Migrations + * @return $this + */ + public function setMigrations(array $migrations) + { + $this->migrations = $migrations; + + return $this; + } + + /** + * Gets an array of the database migrations, indexed by migration name (aka creation time) and sorted in ascending + * order + * + * @param string $environment Environment + * @throws \InvalidArgumentException + * @return \Phinx\Migration\MigrationInterface[] + */ + public function getMigrations(string $environment): array + { + if ($this->migrations === null) { + $phpFiles = $this->getMigrationFiles(); + + if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { + $this->getOutput()->writeln('Migration file'); + $this->getOutput()->writeln( + array_map( + function ($phpFile) { + return " {$phpFile}"; + }, + $phpFiles + ) + ); + } + + // filter the files to only get the ones that match our naming scheme + $fileNames = []; + /** @var \Phinx\Migration\AbstractMigration[] $versions */ + $versions = []; + + foreach ($phpFiles as $filePath) { + if (Util::isValidMigrationFileName(basename($filePath))) { + if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { + $this->getOutput()->writeln("Valid migration file {$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())); + } + + $config = $this->getConfig(); + $namespace = $config instanceof NamespaceAwareInterface ? $config->getMigrationNamespaceByPath(dirname($filePath)) : null; + + // convert the filename to a class name + $class = ($namespace === null ? '' : $namespace . '\\') . 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); + + if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { + $this->getOutput()->writeln("Loading class $class from $filePath."); + } + + // load the migration file + $orig_display_errors_setting = ini_get('display_errors'); + ini_set('display_errors', 'On'); + /** @noinspection PhpIncludeInspection */ + require_once $filePath; + ini_set('display_errors', $orig_display_errors_setting); + if (!class_exists($class)) { + throw new InvalidArgumentException(sprintf( + 'Could not find class "%s" in file "%s"', + $class, + $filePath + )); + } + + if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { + $this->getOutput()->writeln("Running $class."); + } + + // instantiate it + $migration = new $class($environment, $version, $this->getInput(), $this->getOutput()); + + if (!($migration instanceof AbstractMigration)) { + throw new InvalidArgumentException(sprintf( + 'The class "%s" in file "%s" must extend \Phinx\Migration\AbstractMigration', + $class, + $filePath + )); + } + + $versions[$version] = $migration; + } else { + if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { + $this->getOutput()->writeln("Invalid migration file {$filePath}."); + } + } + } + + ksort($versions); + $this->setMigrations($versions); + } + + return $this->migrations; + } + + /** + * Returns a list of migration files found in the provided migration paths. + * + * @return string[] + */ + protected function getMigrationFiles(): array + { + return Util::getFiles($this->getConfig()->getMigrationPaths()); + } + + /** + * Sets the database seeders. + * + * @param \Phinx\Seed\SeedInterface[] $seeds Seeders + * @return $this + */ + public function setSeeds(array $seeds) + { + $this->seeds = $seeds; + + return $this; + } + + /** + * Get seed dependencies instances from seed dependency array + * + * @param \Phinx\Seed\SeedInterface $seed Seed + * @return \Phinx\Seed\SeedInterface[] + */ + protected function getSeedDependenciesInstances(SeedInterface $seed): array + { + $dependenciesInstances = []; + $dependencies = $seed->getDependencies(); + if (!empty($dependencies)) { + foreach ($dependencies as $dependency) { + foreach ($this->seeds as $seed) { + if (get_class($seed) === $dependency) { + $dependenciesInstances[get_class($seed)] = $seed; + } + } + } + } + + return $dependenciesInstances; + } + + /** + * Order seeds by dependencies + * + * @param \Phinx\Seed\SeedInterface[] $seeds Seeds + * @return \Phinx\Seed\SeedInterface[] + */ + protected function orderSeedsByDependencies(array $seeds): array + { + $orderedSeeds = []; + foreach ($seeds as $seed) { + $orderedSeeds[get_class($seed)] = $seed; + $dependencies = $this->getSeedDependenciesInstances($seed); + if (!empty($dependencies)) { + $orderedSeeds = array_merge($this->orderSeedsByDependencies($dependencies), $orderedSeeds); + } + } + + return $orderedSeeds; + } + + /** + * Gets an array of database seeders. + * + * @param string $environment Environment + * @throws \InvalidArgumentException + * @return \Phinx\Seed\SeedInterface[] + */ + public function getSeeds(string $environment): array + { + if ($this->seeds === null) { + $phpFiles = $this->getSeedFiles(); + + // filter the files to only get the ones that match our naming scheme + $fileNames = []; + /** @var \Phinx\Seed\SeedInterface[] $seeds */ + $seeds = []; + + foreach ($phpFiles as $filePath) { + if (Util::isValidSeedFileName(basename($filePath))) { + $config = $this->getConfig(); + $namespace = $config instanceof NamespaceAwareInterface ? $config->getSeedNamespaceByPath(dirname($filePath)) : null; + + // convert the filename to a class name + $class = ($namespace === null ? '' : $namespace . '\\') . 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 + /** @var \Phinx\Seed\AbstractSeed $seed */ + if ($this->container !== null) { + $seed = $this->container->get($class); + } else { + $seed = new $class(); + } + $seed->setEnvironment($environment); + $input = $this->getInput(); + if ($input !== null) { + $seed->setInput($input); + } + $output = $this->getOutput(); + if ($output !== null) { + $seed->setOutput($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->setSeeds($seeds); + } + + $this->seeds = $this->orderSeedsByDependencies($this->seeds); + + return $this->seeds; + } + + /** + * Returns a list of seed files found in the provided seed paths. + * + * @return string[] + */ + protected function getSeedFiles(): array + { + return Util::getFiles($this->getConfig()->getSeedPaths()); + } + + /** + * Sets the config. + * + * @param \Phinx\Config\ConfigInterface $config Configuration Object + * @return $this + */ + public function setConfig(ConfigInterface $config) + { + $this->config = $config; + + return $this; + } + + /** + * Gets the config. + * + * @return \Phinx\Config\ConfigInterface + */ + public function getConfig(): ConfigInterface + { + return $this->config; + } + + /** + * Toggles the breakpoint for a specific version. + * + * @param string $environment Environment name + * @param int|null $version Version + * @return void + */ + public function toggleBreakpoint(string $environment, ?int $version): void + { + $this->markBreakpoint($environment, $version, self::BREAKPOINT_TOGGLE); + } + + /** + * Updates the breakpoint for a specific version. + * + * @param string $environment The required environment + * @param int|null $version The version of the target migration + * @param int $mark The state of the breakpoint as defined by self::BREAKPOINT_xxxx constants. + * @return void + */ + protected function markBreakpoint(string $environment, ?int $version, int $mark): void + { + $migrations = $this->getMigrations($environment); + $env = $this->getEnvironment($environment); + $versions = $env->getVersionLog(); + + if (empty($versions) || empty($migrations)) { + return; + } + + if ($version === null) { + $lastVersion = end($versions); + $version = $lastVersion['version']; + } + + if ($version != 0 && (!isset($versions[$version]) || !isset($migrations[$version]))) { + $this->output->writeln(sprintf( + 'warning %s is not a valid version', + $version + )); + + return; + } + + switch ($mark) { + case self::BREAKPOINT_TOGGLE: + $env->getAdapter()->toggleBreakpoint($migrations[$version]); + break; + case self::BREAKPOINT_SET: + if ($versions[$version]['breakpoint'] == 0) { + $env->getAdapter()->setBreakpoint($migrations[$version]); + } + break; + case self::BREAKPOINT_UNSET: + if ($versions[$version]['breakpoint'] == 1) { + $env->getAdapter()->unsetBreakpoint($migrations[$version]); + } + break; + } + + $versions = $env->getVersionLog(); + + $this->getOutput()->writeln( + ' Breakpoint ' . ($versions[$version]['breakpoint'] ? 'set' : 'cleared') . + ' for ' . $version . '' . + ' ' . $migrations[$version]->getName() . '' + ); + } + + /** + * Remove all breakpoints + * + * @param string $environment The required environment + * @return void + */ + public function removeBreakpoints(string $environment): void + { + $this->getOutput()->writeln(sprintf( + ' %d breakpoints cleared.', + $this->getEnvironment($environment)->getAdapter()->resetAllBreakpoints() + )); + } + + /** + * Set the breakpoint for a specific version. + * + * @param string $environment The required environment + * @param int|null $version The version of the target migration + * @return void + */ + public function setBreakpoint(string $environment, ?int $version): void + { + $this->markBreakpoint($environment, $version, self::BREAKPOINT_SET); + } + + /** + * Unset the breakpoint for a specific version. + * + * @param string $environment The required environment + * @param int|null $version The version of the target migration + * @return void + */ + public function unsetBreakpoint(string $environment, ?int $version): void + { + $this->markBreakpoint($environment, $version, self::BREAKPOINT_UNSET); + } + + /** + * @param int $verbosityLevel Verbosity level for info messages + * @return $this + */ + public function setVerbosityLevel(int $verbosityLevel) + { + $this->verbosityLevel = $verbosityLevel; + + return $this; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Migration/Manager/Environment.php b/vendor/robmorgan/phinx/src/Phinx/Migration/Manager/Environment.php new file mode 100644 index 0000000..9a65b3f --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Migration/Manager/Environment.php @@ -0,0 +1,397 @@ + + */ + protected $options; + + /** + * @var \Symfony\Component\Console\Input\InputInterface|null + */ + protected $input; + + /** + * @var \Symfony\Component\Console\Output\OutputInterface|null + */ + protected $output; + + /** + * @var int + */ + protected $currentVersion; + + /** + * @var string + */ + protected $schemaTableName = 'phinxlog'; + + /** + * @var \Phinx\Db\Adapter\AdapterInterface + */ + protected $adapter; + + /** + * @param string $name Environment Name + * @param array $options Options + */ + public function __construct(string $name, array $options) + { + $this->name = $name; + $this->options = $options; + } + + /** + * Executes the specified migration on this environment. + * + * @param \Phinx\Migration\MigrationInterface $migration Migration + * @param string $direction Direction + * @param bool $fake flag that if true, we just record running the migration, but not actually do the migration + * @return void + */ + public function executeMigration(MigrationInterface $migration, string $direction = MigrationInterface::UP, bool $fake = false): void + { + $direction = $direction === MigrationInterface::UP ? MigrationInterface::UP : MigrationInterface::DOWN; + $migration->setMigratingUp($direction === MigrationInterface::UP); + + $startTime = time(); + $migration->setAdapter($this->getAdapter()); + + $migration->preFlightCheck(); + + if (method_exists($migration, MigrationInterface::INIT)) { + $migration->{MigrationInterface::INIT}(); + } + + if (!$fake) { + // 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 ($direction === MigrationInterface::DOWN) { + // Create an instance of the ProxyAdapter so we can record all + // of the migration commands for reverse playback + + /** @var \Phinx\Db\Adapter\ProxyAdapter $proxyAdapter */ + $proxyAdapter = AdapterFactory::instance() + ->getWrapper('proxy', $this->getAdapter()); + $migration->setAdapter($proxyAdapter); + $migration->{MigrationInterface::CHANGE}(); + $proxyAdapter->executeInvertedCommands(); + $migration->setAdapter($this->getAdapter()); + } else { + $migration->{MigrationInterface::CHANGE}(); + } + } else { + $migration->{$direction}(); + } + + // 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())); + + // commit the transaction if the adapter supports it + if ($this->getAdapter()->hasTransactions()) { + $this->getAdapter()->commitTransaction(); + } + } + $migration->postFlightCheck(); + } + + /** + * Executes the specified seeder on this environment. + * + * @param \Phinx\Seed\SeedInterface $seed Seed + * @return void + */ + public function executeSeed(SeedInterface $seed): void + { + $seed->setAdapter($this->getAdapter()); + if (method_exists($seed, SeedInterface::INIT)) { + $seed->{SeedInterface::INIT}(); + } + + // 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->{SeedInterface::RUN}(); + } + + // commit the transaction if the adapter supports it + if ($this->getAdapter()->hasTransactions()) { + $this->getAdapter()->commitTransaction(); + } + } + + /** + * Sets the environment's name. + * + * @param string $name Environment Name + * @return $this + */ + public function setName(string $name) + { + $this->name = $name; + + return $this; + } + + /** + * Gets the environment name. + * + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * Sets the environment's options. + * + * @param array $options Environment Options + * @return $this + */ + public function setOptions(array $options) + { + $this->options = $options; + + return $this; + } + + /** + * Gets the environment's options. + * + * @return array + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * Sets the console input. + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @return $this + */ + public function setInput(InputInterface $input) + { + $this->input = $input; + + return $this; + } + + /** + * Gets the console input. + * + * @return \Symfony\Component\Console\Input\InputInterface|null + */ + public function getInput(): ?InputInterface + { + return $this->input; + } + + /** + * Sets the console output. + * + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return $this + */ + public function setOutput(OutputInterface $output) + { + $this->output = $output; + + return $this; + } + + /** + * Gets the console output. + * + * @return \Symfony\Component\Console\Output\OutputInterface|null + */ + public function getOutput(): ?OutputInterface + { + return $this->output; + } + + /** + * Gets all migrated version numbers. + * + * @return array + */ + public function getVersions(): array + { + return $this->getAdapter()->getVersions(); + } + + /** + * Get all migration log entries, indexed by version creation time and sorted in ascending order by the configuration's + * version_order option + * + * @return array + */ + public function getVersionLog(): array + { + return $this->getAdapter()->getVersionLog(); + } + + /** + * Sets the current version of the environment. + * + * @param int $version Environment Version + * @return $this + */ + public function setCurrentVersion(int $version) + { + $this->currentVersion = $version; + + return $this; + } + + /** + * Gets the current version of the environment. + * + * @return int + */ + public function getCurrentVersion(): int + { + // We don't cache this code as the current version is pretty volatile. + // that means they're no point in a setter then? + // maybe we should cache and call a reset() method every time a migration is run + $versions = $this->getVersions(); + $version = 0; + + if (!empty($versions)) { + $version = end($versions); + } + + $this->setCurrentVersion($version); + + return $this->currentVersion; + } + + /** + * Sets the database adapter. + * + * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter + * @return $this + */ + public function setAdapter(AdapterInterface $adapter) + { + $this->adapter = $adapter; + + return $this; + } + + /** + * Gets the database adapter. + * + * @throws \RuntimeException + * @return \Phinx\Db\Adapter\AdapterInterface + */ + public function getAdapter(): AdapterInterface + { + if (isset($this->adapter)) { + return $this->adapter; + } + + $options = $this->getOptions(); + if (isset($options['connection'])) { + if (!($options['connection'] instanceof PDO)) { + throw new RuntimeException('The specified connection is not a PDO instance'); + } + + $options['connection']->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $options['adapter'] = $options['connection']->getAttribute(PDO::ATTR_DRIVER_NAME); + } + if (!isset($options['adapter'])) { + throw new RuntimeException('No adapter was specified for environment: ' . $this->getName()); + } + + $factory = AdapterFactory::instance(); + $adapter = $factory + ->getAdapter($options['adapter'], $options); + + // Automatically time the executed commands + $adapter = $factory->getWrapper('timed', $adapter); + + if (isset($options['wrapper'])) { + $adapter = $factory + ->getWrapper($options['wrapper'], $adapter); + } + + /** @var \Symfony\Component\Console\Input\InputInterface|null $input */ + $input = $this->getInput(); + if ($input) { + $adapter->setInput($this->getInput()); + } + + /** @var \Symfony\Component\Console\Output\OutputInterface|null $output */ + $output = $this->getOutput(); + if ($output) { + $adapter->setOutput($this->getOutput()); + } + + // Use the TablePrefixAdapter if table prefix/suffixes are in use + if ($adapter->hasOption('table_prefix') || $adapter->hasOption('table_suffix')) { + $adapter = AdapterFactory::instance() + ->getWrapper('prefix', $adapter); + } + + $this->setAdapter($adapter); + + return $adapter; + } + + /** + * Sets the schema table name. + * + * @param string $schemaTableName Schema Table Name + * @return $this + */ + public function setSchemaTableName($schemaTableName) + { + $this->schemaTableName = $schemaTableName; + + return $this; + } + + /** + * Gets the schema table name. + * + * @return string + */ + public function getSchemaTableName(): string + { + return $this->schemaTableName; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Migration/Migration.change.template.php.dist b/vendor/robmorgan/phinx/src/Phinx/Migration/Migration.change.template.php.dist new file mode 100644 index 0000000..40b609b --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Migration/Migration.change.template.php.dist @@ -0,0 +1,24 @@ + + */ +interface MigrationInterface +{ + /** + * @var string + */ + public const CHANGE = 'change'; + + /** + * @var string + */ + public const UP = 'up'; + + /** + * @var string + */ + public const DOWN = 'down'; + + /** + * @var string + */ + public const INIT = 'init'; + + /** + * Sets the database adapter. + * + * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter + * @return $this + */ + public function setAdapter(AdapterInterface $adapter); + + /** + * Gets the database adapter. + * + * @return \Phinx\Db\Adapter\AdapterInterface|null + */ + public function getAdapter(): ?AdapterInterface; + + /** + * Sets the input object to be used in migration object + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @return $this + */ + public function setInput(InputInterface $input); + + /** + * Gets the input object to be used in migration object + * + * @return \Symfony\Component\Console\Input\InputInterface|null + */ + public function getInput(): ?InputInterface; + + /** + * Sets the output object to be used in migration object + * + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return $this + */ + public function setOutput(OutputInterface $output); + + /** + * Gets the output object to be used in migration object + * + * @return \Symfony\Component\Console\Output\OutputInterface|null + */ + public function getOutput(): ?OutputInterface; + + /** + * Gets the name. + * + * @return string + */ + public function getName(): string; + + /** + * Gets the detected environment + * + * @return string + */ + public function getEnvironment(): string; + + /** + * Sets the migration version number. + * + * @param int $version Version + * @return $this + */ + public function setVersion(int $version); + + /** + * Gets the migration version number. + * + * @return int + */ + public function getVersion(): int; + + /** + * Sets whether this migration is being applied or reverted + * + * @param bool $isMigratingUp True if the migration is being applied + * @return $this + */ + public function setMigratingUp(bool $isMigratingUp); + + /** + * Gets whether this migration is being applied or reverted. + * True means that the migration is being applied. + * + * @return bool + */ + public function isMigratingUp(): bool; + + /** + * Executes a SQL statement and returns the number of affected rows. + * + * @param string $sql SQL + * @param array $params parameters to use for prepared query + * @return int + */ + public function execute(string $sql, array $params = []): int; + + /** + * Executes a SQL statement. + * + * The return type depends on the underlying adapter being used. To improve + * IDE auto-completion possibility, you can overwrite the query method + * phpDoc in your (typically custom abstract parent) migration class, where + * you can set the return type by the adapter in your current use. + * + * @param string $sql SQL + * @param array $params parameters to use for prepared query + * @return mixed + */ + public function query(string $sql, array $params = []); + + /** + * Returns a new Query object that can be used to build complex SELECT, UPDATE, INSERT or DELETE + * queries and execute them against the current database. + * + * Queries executed through the query builder are always sent to the database, regardless of the + * the dry-run settings. + * + * @see https://api.cakephp.org/3.6/class-Cake.Database.Query.html + * @return \Cake\Database\Query + */ + public function getQueryBuilder(): Query; + + /** + * Executes a query and returns only one row as an array. + * + * @param string $sql SQL + * @return array|false + */ + public function fetchRow(string $sql); + + /** + * Executes a query and returns an array of rows. + * + * @param string $sql SQL + * @return array + */ + public function fetchAll(string $sql): array; + + /** + * Create a new database. + * + * @param string $name Database Name + * @param array $options Options + * @return void + */ + public function createDatabase(string $name, array $options): void; + + /** + * Drop a database. + * + * @param string $name Database Name + * @return void + */ + public function dropDatabase(string $name): void; + + /** + * Creates schema. + * + * This will thrown an error for adapters that do not support schemas. + * + * @param string $name Schema name + * @return void + * @throws \BadMethodCallException + */ + public function createSchema(string $name): void; + + /** + * Drops schema. + * + * This will thrown an error for adapters that do not support schemas. + * + * @param string $name Schema name + * @return void + * @throws \BadMethodCallException + */ + public function dropSchema(string $name): void; + + /** + * Checks to see if a table exists. + * + * @param string $tableName Table name + * @return bool + */ + public function hasTable(string $tableName): bool; + + /** + * Returns an instance of the \Table class. + * + * You can use this class to create and manipulate tables. + * + * @param string $tableName Table name + * @param array $options Options + * @return \Phinx\Db\Table + */ + public function table(string $tableName, array $options): Table; + + /** + * Perform checks on the migration, printing a warning + * if there are potential problems. + * + * @return void + */ + public function preFlightCheck(): void; + + /** + * Perform checks on the migration after completion + * + * Right now, the only check is whether all changes were committed + * + * @return void + */ + public function postFlightCheck(): void; + + /** + * Checks to see if the migration should be executed. + * + * Returns true by default. + * + * You can use this to prevent a migration from executing. + * + * @return bool + */ + public function shouldExecute(): bool; +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Seed/AbstractSeed.php b/vendor/robmorgan/phinx/src/Phinx/Seed/AbstractSeed.php new file mode 100644 index 0000000..7706d30 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Seed/AbstractSeed.php @@ -0,0 +1,222 @@ + + */ +abstract class AbstractSeed implements SeedInterface +{ + /** + * @var string + */ + protected $environment; + + /** + * @var \Phinx\Db\Adapter\AdapterInterface + */ + protected $adapter; + + /** + * @var \Symfony\Component\Console\Input\InputInterface + */ + protected $input; + + /** + * @var \Symfony\Component\Console\Output\OutputInterface + */ + protected $output; + + /** + * Override to specify dependencies for dependency injection from the configured PSR-11 container + */ + public function __construct() + { + } + + /** + * @inheritDoc + */ + public function run(): void + { + } + + /** + * @inheritDoc + */ + public function getDependencies(): array + { + return []; + } + + /** + * @inheritDoc + */ + public function setEnvironment(string $environment) + { + $this->environment = $environment; + + return $this; + } + + /** + * @inheritDoc + */ + public function getEnvironment(): string + { + return $this->environment; + } + + /** + * @inheritDoc + */ + public function setAdapter(AdapterInterface $adapter): SeedInterface + { + $this->adapter = $adapter; + + return $this; + } + + /** + * @inheritDoc + */ + public function getAdapter(): AdapterInterface + { + return $this->adapter; + } + + /** + * @inheritDoc + */ + public function setInput(InputInterface $input) + { + $this->input = $input; + + return $this; + } + + /** + * @inheritDoc + */ + public function getInput(): InputInterface + { + return $this->input; + } + + /** + * @inheritDoc + */ + public function setOutput(OutputInterface $output): SeedInterface + { + $this->output = $output; + + return $this; + } + + /** + * @inheritDoc + */ + public function getOutput(): OutputInterface + { + return $this->output; + } + + /** + * @inheritDoc + */ + public function getName(): string + { + return static::class; + } + + /** + * @inheritDoc + */ + public function execute(string $sql, array $params = []) + { + return $this->getAdapter()->execute($sql, $params); + } + + /** + * @inheritDoc + */ + public function query(string $sql, array $params = []) + { + return $this->getAdapter()->query($sql, $params); + } + + /** + * @inheritDoc + */ + public function fetchRow(string $sql) + { + return $this->getAdapter()->fetchRow($sql); + } + + /** + * @inheritDoc + */ + public function fetchAll(string $sql): array + { + return $this->getAdapter()->fetchAll($sql); + } + + /** + * @inheritDoc + */ + public function insert(string $table, array $data): void + { + // convert to table object + if (is_string($table)) { + $table = new Table($table, [], $this->getAdapter()); + } + $table->insert($data)->save(); + } + + /** + * @inheritDoc + */ + public function hasTable(string $tableName): bool + { + return $this->getAdapter()->hasTable($tableName); + } + + /** + * @inheritDoc + */ + public function table(string $tableName, array $options = []): Table + { + return new Table($tableName, $options, $this->getAdapter()); + } + + /** + * Checks to see if the seed should be executed. + * + * Returns true by default. + * + * You can use this to prevent a seed from executing. + * + * @return bool + */ + public function shouldExecute(): bool + { + return true; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Seed/Seed.template.php.dist b/vendor/robmorgan/phinx/src/Phinx/Seed/Seed.template.php.dist new file mode 100644 index 0000000..74ba622 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Seed/Seed.template.php.dist @@ -0,0 +1,20 @@ + + */ +interface SeedInterface +{ + /** + * @var string + */ + public const RUN = 'run'; + + /** + * @var string + */ + public const INIT = 'init'; + + /** + * Run the seeder. + * + * @return void + */ + public function run(): void; + + /** + * Return seeds dependencies. + * + * @return array + */ + public function getDependencies(): array; + + /** + * Sets the environment. + * + * @return $this + */ + public function setEnvironment(string $environment); + + /** + * Gets the environment. + * + * @return string + */ + public function getEnvironment(): string; + + /** + * Sets the database adapter. + * + * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter + * @return $this + */ + public function setAdapter(AdapterInterface $adapter); + + /** + * Gets the database adapter. + * + * @return \Phinx\Db\Adapter\AdapterInterface + */ + public function getAdapter(): AdapterInterface; + + /** + * Sets the input object to be used in migration object + * + * @param \Symfony\Component\Console\Input\InputInterface $input Input + * @return $this + */ + public function setInput(InputInterface $input); + + /** + * Gets the input object to be used in migration object + * + * @return \Symfony\Component\Console\Input\InputInterface + */ + public function getInput(): InputInterface; + + /** + * Sets the output object to be used in migration object + * + * @param \Symfony\Component\Console\Output\OutputInterface $output Output + * @return $this + */ + public function setOutput(OutputInterface $output); + + /** + * Gets the output object to be used in migration object + * + * @return \Symfony\Component\Console\Output\OutputInterface + */ + public function getOutput(): OutputInterface; + + /** + * Gets the name. + * + * @return string + */ + public function getName(): string; + + /** + * Executes a SQL statement and returns the number of affected rows. + * + * @param string $sql SQL + * @param array $params parameters to use for prepared query + * @return int + */ + public function execute(string $sql, array $params = []); + + /** + * Executes a SQL statement. + * + * The return type depends on the underlying adapter being used. To improve + * IDE auto-completion possibility, you can overwrite the query method + * phpDoc in your (typically custom abstract parent) seed class, where + * you can set the return type by the adapter in your current use. + * + * @param string $sql SQL + * @param array $params parameters to use for prepared query + * @return mixed + */ + public function query(string $sql, array $params = []); + + /** + * Executes a query and returns only one row as an array. + * + * @param string $sql SQL + * @return array|false + */ + public function fetchRow(string $sql); + + /** + * Executes a query and returns an array of rows. + * + * @param string $sql SQL + * @return array + */ + public function fetchAll(string $sql): array; + + /** + * Insert data into a table. + * + * @param string $tableName Table name + * @param array $data Data + * @return void + */ + public function insert(string $tableName, array $data): void; + + /** + * Checks to see if a table exists. + * + * @param string $tableName Table name + * @return bool + */ + public function hasTable(string $tableName): bool; + + /** + * Returns an instance of the \Table class. + * + * You can use this class to create and manipulate tables. + * + * @param string $tableName Table name + * @param array $options Options + * @return \Phinx\Db\Table + */ + public function table(string $tableName, array $options): \Phinx\Db\Table; + + /** + * Checks to see if the seed should be executed. + * + * Returns true by default. + * + * You can use this to prevent a seed from executing. + * + * @return bool + */ + public function shouldExecute(): bool; +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Util/Expression.php b/vendor/robmorgan/phinx/src/Phinx/Util/Expression.php new file mode 100644 index 0000000..23a6b9e --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Util/Expression.php @@ -0,0 +1,41 @@ +value = $value; + } + + /** + * @return string Returns the expression + */ + public function __toString(): string + { + return $this->value; + } + + /** + * @param string $value The expression + * @return self + */ + public static function from(string $value): Expression + { + return new self($value); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Util/Literal.php b/vendor/robmorgan/phinx/src/Phinx/Util/Literal.php new file mode 100644 index 0000000..5889832 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Util/Literal.php @@ -0,0 +1,41 @@ +value = $value; + } + + /** + * @return string Returns the literal's value + */ + public function __toString(): string + { + return $this->value; + } + + /** + * @param string $value The literal's value + * @return self + */ + public static function from(string $value): Literal + { + return new self($value); + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Util/Util.php b/vendor/robmorgan/phinx/src/Phinx/Util/Util.php new file mode 100644 index 0000000..bea3436 --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Util/Util.php @@ -0,0 +1,361 @@ +format(static::DATE_FORMAT); + } + + /** + * Gets an array of all the existing migration class names. + * + * @param string $path Path + * @return string[] + */ + public static function getExistingMigrationClassNames(string $path): array + { + $classNames = []; + + if (!is_dir($path)) { + return $classNames; + } + + // filter the files to only get the ones that match our naming scheme + $phpFiles = static::getFiles($path); + + foreach ($phpFiles as $filePath) { + $fileName = basename($filePath); + if (preg_match(static::MIGRATION_FILE_NAME_PATTERN, $fileName)) { + $classNames[] = static::mapFileNameToClassName($fileName); + } + } + + return $classNames; + } + + /** + * Get the version from the beginning of a file name. + * + * @param string $fileName File Name + * @return int + */ + public static function getVersionFromFileName(string $fileName): int + { + $matches = []; + preg_match('/^[0-9]+/', basename($fileName), $matches); + $value = (int)($matches[0] ?? null); + if (!$value) { + throw new RuntimeException(sprintf('Cannot get a valid version from filename `%s`', $fileName)); + } + + return $value; + } + + /** + * 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(string $className): string + { + $snake = function ($matches) { + return '_' . strtolower($matches[0]); + }; + $fileName = preg_replace_callback('/\d+|[A-Z]/', $snake, $className); + $fileName = static::getCurrentTimestamp() . "$fileName.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(string $fileName): string + { + $matches = []; + if (preg_match(static::MIGRATION_FILE_NAME_PATTERN, $fileName, $matches)) { + $fileName = $matches[1]; + } elseif (preg_match(static::MIGRATION_FILE_NAME_NO_NAME_PATTERN, $fileName)) { + return 'V' . substr($fileName, 0, strlen($fileName) - 4); + } + + $className = str_replace('_', '', ucwords($fileName, '_')); + + return $className; + } + + /** + * 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 PascalCase format but consecutive + * capitals are allowed. + * e.g: AddIndexToPostsTable or CustomHTMLTitle. + * + * @param string $className Class Name + * @param string $path Path + * @return bool + */ + public static function isUniqueMigrationClassName(string $className, string $path): bool + { + $existingClassNames = static::getExistingMigrationClassNames($path); + + return !in_array($className, $existingClassNames, true); + } + + /** + * 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 bool + */ + public static function isValidPhinxClassName(string $className): bool + { + return (bool)preg_match(static::CLASS_NAME_PATTERN, $className); + } + + /** + * Check if a migration file name is valid. + * + * @param string $fileName File Name + * @return bool + */ + public static function isValidMigrationFileName(string $fileName): bool + { + return (bool)preg_match(static::MIGRATION_FILE_NAME_PATTERN, $fileName) + || (bool)preg_match(static::MIGRATION_FILE_NAME_NO_NAME_PATTERN, $fileName); + } + + /** + * Check if a seed file name is valid. + * + * @param string $fileName File Name + * @return bool + */ + public static function isValidSeedFileName(string $fileName): bool + { + return (bool)preg_match(static::SEED_FILE_NAME_PATTERN, $fileName); + } + + /** + * Expands a set of paths with curly braces (if supported by the OS). + * + * @param string[] $paths Paths + * @return string[] + */ + public static function globAll(array $paths): array + { + $result = []; + + foreach ($paths as $path) { + $result = array_merge($result, static::glob($path)); + } + + return $result; + } + + /** + * Expands a path with curly braces (if supported by the OS). + * + * @param string $path Path + * @return string[] + */ + public static function glob(string $path): array + { + return glob($path, defined('GLOB_BRACE') ? GLOB_BRACE : 0); + } + + /** + * Takes the path to a php file and attempts to include it if readable + * + * @param string $filename Filename + * @param \Symfony\Component\Console\Input\InputInterface|null $input Input + * @param \Symfony\Component\Console\Output\OutputInterface|null $output Output + * @param \Phinx\Console\Command\AbstractCommand|mixed|null $context Context + * @throws \Exception + * @return string + */ + public static function loadPhpFile(string $filename, ?InputInterface $input = null, ?OutputInterface $output = null, $context = null): string + { + $filePath = realpath($filename); + if (!file_exists($filePath)) { + throw new Exception(sprintf("File does not exist: %s \n", $filename)); + } + + /** + * I lifed this from phpunits FileLoader class + * + * @see https://github.com/sebastianbergmann/phpunit/pull/2751 + */ + $isReadable = @fopen($filePath, 'r') !== false; + + if (!$isReadable) { + throw new Exception(sprintf("Cannot open file %s \n", $filename)); + } + + // prevent this to be propagated to the included file + unset($isReadable); + + include_once $filePath; + + return $filePath; + } + + /** + * Given an array of paths, return all unique PHP files that are in them + * + * @param string|string[] $paths Path or array of paths to get .php files. + * @return string[] + */ + public static function getFiles($paths): array + { + $files = static::globAll(array_map(function ($path) { + return $path . DIRECTORY_SEPARATOR . '*.php'; + }, (array)$paths)); + // glob() can return the same file multiple times + // This will cause the migration to fail with a + // false assumption of duplicate migrations + // https://php.net/manual/en/function.glob.php#110340 + $files = array_unique($files); + + return $files; + } + + /** + * Attempt to remove the current working directory from a path for output. + * + * @param string $path Path to remove cwd prefix from + * @return string + */ + public static function relativePath(string $path): string + { + $realpath = realpath($path); + if ($realpath !== false) { + $path = $realpath; + } + + $cwd = getcwd(); + if ($cwd !== false) { + $cwd .= DIRECTORY_SEPARATOR; + $cwdLen = strlen($cwd); + + if (substr($path, 0, $cwdLen) === $cwd) { + $path = substr($path, $cwdLen); + } + } + + return $path; + } + + /** + * Parses DSN string into db config array. + * + * @param string $dsn DSN string + * @return array + */ + public static function parseDsn(string $dsn): array + { + $pattern = <<<'REGEXP' +{ + ^ + (?: + (?P[\w\\\\]+):// + ) + (?: + (?P.*?) + (?: + :(?P.*?) + )? + @ + )? + (?: + (?P[^?#/:@]+) + (?: + :(?P\d+) + )? + )? + (?: + /(?P[^?#]*) + )? + (?: + \?(?P[^#]*) + )? + $ +}x +REGEXP; + + if (!preg_match($pattern, $dsn, $parsed)) { + return []; + } + + // filter out everything except the matched groups + $config = array_intersect_key($parsed, array_flip(['adapter', 'user', 'pass', 'host', 'port', 'name'])); + $config = array_filter($config); + + parse_str($parsed['query'] ?? '', $query); + $config = array_merge($query, $config); + + return $config; + } +} diff --git a/vendor/robmorgan/phinx/src/Phinx/Wrapper/TextWrapper.php b/vendor/robmorgan/phinx/src/Phinx/Wrapper/TextWrapper.php new file mode 100644 index 0000000..62b7f2d --- /dev/null +++ b/vendor/robmorgan/phinx/src/Phinx/Wrapper/TextWrapper.php @@ -0,0 +1,249 @@ + + */ +class TextWrapper +{ + /** + * @var \Phinx\Console\PhinxApplication + */ + protected $app; + + /** + * @var array + */ + protected $options; + + /** + * @var int + */ + protected $exitCode; + + /** + * @param \Phinx\Console\PhinxApplication $app Application + * @param array $options Options + */ + public function __construct(PhinxApplication $app, array $options = []) + { + $this->app = $app; + $this->options = $options; + } + + /** + * Get the application instance. + * + * @return \Phinx\Console\PhinxApplication + */ + public function getApp(): PhinxApplication + { + return $this->app; + } + + /** + * Returns the exit code from the last run command. + * + * @return int + */ + public function getExitCode(): int + { + return $this->exitCode; + } + + /** + * Returns the output from running the "status" command. + * + * @param string|null $env environment name (optional) + * @return string + */ + public function getStatus(?string $env = null): string + { + $command = ['status']; + if ($this->hasEnvValue($env)) { + $command['-e'] = $env ?: $this->getOption('environment'); + } + if ($this->hasOption('configuration')) { + $command['-c'] = $this->getOption('configuration'); + } + if ($this->hasOption('parser')) { + $command['-p'] = $this->getOption('parser'); + } + if ($this->hasOption('format')) { + $command['-f'] = $this->getOption('format'); + } + + return $this->executeRun($command); + } + + /** + * @param string|null $env environment name + * @return bool + */ + private function hasEnvValue($env): bool + { + return $env || $this->hasOption('environment'); + } + + /** + * Returns the output from running the "migrate" command. + * + * @param string|null $env environment name (optional) + * @param string|null $target target version (optional) + * @return string + */ + public function getMigrate(?string $env = null, ?string $target = null): string + { + $command = ['migrate']; + if ($this->hasEnvValue($env)) { + $command += ['-e' => $env ?: $this->getOption('environment')]; + } + if ($this->hasOption('configuration')) { + $command += ['-c' => $this->getOption('configuration')]; + } + if ($this->hasOption('parser')) { + $command += ['-p' => $this->getOption('parser')]; + } + if ($target) { + $command += ['-t' => $target]; + } + + return $this->executeRun($command); + } + + /** + * Returns the output from running the "seed:run" command. + * + * @param string|null $env Environment name + * @param string|null $target Target version + * @param string[]|string|null $seed Array of seed names or seed name + * @return string + */ + public function getSeed(?string $env = null, ?string $target = null, $seed = null): string + { + $command = ['seed:run']; + if ($this->hasEnvValue($env)) { + $command += ['-e' => $env ?: $this->getOption('environment')]; + } + if ($this->hasOption('configuration')) { + $command += ['-c' => $this->getOption('configuration')]; + } + if ($this->hasOption('parser')) { + $command += ['-p' => $this->getOption('parser')]; + } + if ($target) { + $command += ['-t' => $target]; + } + if ($seed) { + $seed = (array)$seed; + $command += ['-s' => $seed]; + } + + return $this->executeRun($command); + } + + /** + * Returns the output from running the "rollback" command. + * + * @param string|null $env Environment name (optional) + * @param mixed $target Target version, or 0 (zero) fully revert (optional) + * @return string + */ + public function getRollback(?string $env = null, $target = null): string + { + $command = ['rollback']; + if ($this->hasEnvValue($env)) { + $command += ['-e' => $env ?: $this->getOption('environment')]; + } + if ($this->hasOption('configuration')) { + $command += ['-c' => $this->getOption('configuration')]; + } + if ($this->hasOption('parser')) { + $command += ['-p' => $this->getOption('parser')]; + } + if (isset($target)) { + // Need to use isset() with rollback, because -t0 is a valid option! + // See https://book.cakephp.org/phinx/0/en/commands.html#the-rollback-command + $command += ['-t' => $target]; + } + + return $this->executeRun($command); + } + + /** + * Check option from options array + * + * @param string $key Key + * @return bool + */ + protected function hasOption(string $key): bool + { + return isset($this->options[$key]); + } + + /** + * Get option from options array + * + * @param string $key Key + * @return string|null + */ + protected function getOption(string $key): ?string + { + if (!isset($this->options[$key])) { + return null; + } + + return $this->options[$key]; + } + + /** + * Set option in options array + * + * @param string $key Key + * @param string $value Value + * @return $this + */ + public function setOption(string $key, string $value) + { + $this->options[$key] = $value; + + return $this; + } + + /** + * Execute a command, capturing output and storing the exit code. + * + * @param array $command Command + * @return string + */ + protected function executeRun(array $command): string + { + // Output will be written to a temporary stream, so that it can be + // collected after running the command. + $stream = fopen('php://temp', 'w+'); + + // Execute the command, capturing the output in the temporary stream + // and storing the exit code for debugging purposes. + $this->exitCode = $this->app->doRun(new ArrayInput($command), new StreamOutput($stream)); + + // Get the output of the command and close the stream, which will + // destroy the temporary file. + $result = stream_get_contents($stream, -1, 0); + fclose($stream); + + return $result; + } +} diff --git a/vendor/robmorgan/phinx/src/composer_autoloader.php b/vendor/robmorgan/phinx/src/composer_autoloader.php new file mode 100644 index 0000000..2cde666 --- /dev/null +++ b/vendor/robmorgan/phinx/src/composer_autoloader.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Cache; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\InvalidArgumentException; + +/** + * Covers most simple to advanced caching needs. + * + * @author Nicolas Grekas + */ +interface CacheInterface +{ + /** + * Fetches a value from the pool or computes it if not found. + * + * On cache misses, a callback is called that should return the missing value. + * This callback is given a PSR-6 CacheItemInterface instance corresponding to the + * requested key, that could be used e.g. for expiration control. It could also + * be an ItemInterface instance when its additional features are needed. + * + * @param string $key The key of the item to retrieve from the cache + * @param callable|CallbackInterface $callback Should return the computed value for the given key/item + * @param float|null $beta A float that, as it grows, controls the likeliness of triggering + * early expiration. 0 disables it, INF forces immediate expiration. + * The default (or providing null) is implementation dependent but should + * typically be 1.0, which should provide optimal stampede protection. + * See https://en.wikipedia.org/wiki/Cache_stampede#Probabilistic_early_expiration + * @param array &$metadata The metadata of the cached item {@see ItemInterface::getMetadata()} + * + * @throws InvalidArgumentException When $key is not valid or when $beta is negative + */ + public function get(string $key, callable $callback, float $beta = null, array &$metadata = null): mixed; + + /** + * Removes an item from the pool. + * + * @param string $key The key to delete + * + * @throws InvalidArgumentException When $key is not valid + * + * @return bool True if the item was successfully removed, false if there was any error + */ + public function delete(string $key): bool; +} diff --git a/vendor/symfony/cache-contracts/CacheTrait.php b/vendor/symfony/cache-contracts/CacheTrait.php new file mode 100644 index 0000000..d245df6 --- /dev/null +++ b/vendor/symfony/cache-contracts/CacheTrait.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Cache; + +use Psr\Cache\CacheItemPoolInterface; +use Psr\Cache\InvalidArgumentException; +use Psr\Log\LoggerInterface; + +// Help opcache.preload discover always-needed symbols +class_exists(InvalidArgumentException::class); + +/** + * An implementation of CacheInterface for PSR-6 CacheItemPoolInterface classes. + * + * @author Nicolas Grekas + */ +trait CacheTrait +{ + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, float $beta = null, array &$metadata = null): mixed + { + return $this->doGet($this, $key, $callback, $beta, $metadata); + } + + /** + * {@inheritdoc} + */ + public function delete(string $key): bool + { + return $this->deleteItem($key); + } + + private function doGet(CacheItemPoolInterface $pool, string $key, callable $callback, ?float $beta, array &$metadata = null, LoggerInterface $logger = null): mixed + { + if (0 > $beta = $beta ?? 1.0) { + throw new class(sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', static::class, $beta)) extends \InvalidArgumentException implements InvalidArgumentException { }; + } + + $item = $pool->getItem($key); + $recompute = !$item->isHit() || \INF === $beta; + $metadata = $item instanceof ItemInterface ? $item->getMetadata() : []; + + if (!$recompute && $metadata) { + $expiry = $metadata[ItemInterface::METADATA_EXPIRY] ?? false; + $ctime = $metadata[ItemInterface::METADATA_CTIME] ?? false; + + if ($recompute = $ctime && $expiry && $expiry <= ($now = microtime(true)) - $ctime / 1000 * $beta * log(random_int(1, \PHP_INT_MAX) / \PHP_INT_MAX)) { + // force applying defaultLifetime to expiry + $item->expiresAt(null); + $logger && $logger->info('Item "{key}" elected for early recomputation {delta}s before its expiration', [ + 'key' => $key, + 'delta' => sprintf('%.1f', $expiry - $now), + ]); + } + } + + if ($recompute) { + $save = true; + $item->set($callback($item, $save)); + if ($save) { + $pool->save($item); + } + } + + return $item->get(); + } +} diff --git a/vendor/symfony/cache-contracts/CallbackInterface.php b/vendor/symfony/cache-contracts/CallbackInterface.php new file mode 100644 index 0000000..437a3c9 --- /dev/null +++ b/vendor/symfony/cache-contracts/CallbackInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Cache; + +use Psr\Cache\CacheItemInterface; + +/** + * Computes and returns the cached value of an item. + * + * @author Nicolas Grekas + */ +interface CallbackInterface +{ + /** + * @param CacheItemInterface|ItemInterface $item The item to compute the value for + * @param bool &$save Should be set to false when the value should not be saved in the pool + * + * @return mixed The computed value for the passed item + */ + public function __invoke(CacheItemInterface $item, bool &$save): mixed; +} diff --git a/vendor/symfony/cache-contracts/ItemInterface.php b/vendor/symfony/cache-contracts/ItemInterface.php new file mode 100644 index 0000000..8c4c512 --- /dev/null +++ b/vendor/symfony/cache-contracts/ItemInterface.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Cache; + +use Psr\Cache\CacheException; +use Psr\Cache\CacheItemInterface; +use Psr\Cache\InvalidArgumentException; + +/** + * Augments PSR-6's CacheItemInterface with support for tags and metadata. + * + * @author Nicolas Grekas + */ +interface ItemInterface extends CacheItemInterface +{ + /** + * References the Unix timestamp stating when the item will expire. + */ + public const METADATA_EXPIRY = 'expiry'; + + /** + * References the time the item took to be created, in milliseconds. + */ + public const METADATA_CTIME = 'ctime'; + + /** + * References the list of tags that were assigned to the item, as string[]. + */ + public const METADATA_TAGS = 'tags'; + + /** + * Reserved characters that cannot be used in a key or tag. + */ + public const RESERVED_CHARACTERS = '{}()/\@:'; + + /** + * Adds a tag to a cache item. + * + * Tags are strings that follow the same validation rules as keys. + * + * @param string|string[] $tags A tag or array of tags + * + * @return $this + * + * @throws InvalidArgumentException When $tag is not valid + * @throws CacheException When the item comes from a pool that is not tag-aware + */ + public function tag(string|iterable $tags): static; + + /** + * Returns a list of metadata info that were saved alongside with the cached value. + * + * See ItemInterface::METADATA_* consts for keys potentially found in the returned array. + */ + public function getMetadata(): array; +} diff --git a/vendor/symfony/cache-contracts/LICENSE b/vendor/symfony/cache-contracts/LICENSE new file mode 100644 index 0000000..74cdc2d --- /dev/null +++ b/vendor/symfony/cache-contracts/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-2022 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/cache-contracts/README.md b/vendor/symfony/cache-contracts/README.md new file mode 100644 index 0000000..7085a69 --- /dev/null +++ b/vendor/symfony/cache-contracts/README.md @@ -0,0 +1,9 @@ +Symfony Cache 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/main/README.md for more information. diff --git a/vendor/symfony/cache-contracts/TagAwareCacheInterface.php b/vendor/symfony/cache-contracts/TagAwareCacheInterface.php new file mode 100644 index 0000000..8e0b6be --- /dev/null +++ b/vendor/symfony/cache-contracts/TagAwareCacheInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Cache; + +use Psr\Cache\InvalidArgumentException; + +/** + * Allows invalidating cached items using tags. + * + * @author Nicolas Grekas + */ +interface TagAwareCacheInterface extends CacheInterface +{ + /** + * Invalidates cached items using tags. + * + * When implemented on a PSR-6 pool, invalidation should not apply + * to deferred items. Instead, they should be committed as usual. + * This allows replacing old tagged values by new ones without + * race conditions. + * + * @param string[] $tags An array of tags to invalidate + * + * @return bool True on success + * + * @throws InvalidArgumentException When $tags is not valid + */ + public function invalidateTags(array $tags): bool; +} diff --git a/vendor/symfony/cache-contracts/composer.json b/vendor/symfony/cache-contracts/composer.json new file mode 100644 index 0000000..1523e06 --- /dev/null +++ b/vendor/symfony/cache-contracts/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/cache-contracts", + "type": "library", + "description": "Generic abstractions related to caching", + "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": ">=8.0.2", + "psr/cache": "^3.0" + }, + "suggest": { + "symfony/cache-implementation": "" + }, + "autoload": { + "psr-4": { "Symfony\\Contracts\\Cache\\": "" } + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + } +} diff --git a/vendor/symfony/cache/Adapter/AbstractAdapter.php b/vendor/symfony/cache/Adapter/AbstractAdapter.php new file mode 100644 index 0000000..30ea8f8 --- /dev/null +++ b/vendor/symfony/cache/Adapter/AbstractAdapter.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\AbstractAdapterTrait; +use Symfony\Component\Cache\Traits\ContractsTrait; +use Symfony\Contracts\Cache\CacheInterface; + +/** + * @author Nicolas Grekas + */ +abstract class AbstractAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface +{ + use AbstractAdapterTrait; + use ContractsTrait; + + /** + * @internal + */ + protected const NS_SEPARATOR = ':'; + + private static $apcuSupported; + private static $phpFilesSupported; + + protected function __construct(string $namespace = '', int $defaultLifetime = 0) + { + $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).static::NS_SEPARATOR; + $this->defaultLifetime = $defaultLifetime; + if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) { + throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s").', $this->maxIdLength - 24, \strlen($namespace), $namespace)); + } + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind( + static function ($key, $value, $isHit) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $v = $value; + $item->isHit = $isHit; + // Detect wrapped values that encode for their expiry and creation duration + // For compactness, these values are packed in the key of an array using + // magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F + if (\is_array($v) && 1 === \count($v) && 10 === \strlen($k = (string) array_key_first($v)) && "\x9D" === $k[0] && "\0" === $k[5] && "\x5F" === $k[9]) { + $item->value = $v[$k]; + $v = unpack('Ve/Nc', substr($k, 1, -1)); + $item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET; + $item->metadata[CacheItem::METADATA_CTIME] = $v['c']; + } + + return $item; + }, + null, + CacheItem::class + ); + self::$mergeByLifetime ?? self::$mergeByLifetime = \Closure::bind( + static function ($deferred, $namespace, &$expiredIds, $getId, $defaultLifetime) { + $byLifetime = []; + $now = microtime(true); + $expiredIds = []; + + foreach ($deferred as $key => $item) { + $key = (string) $key; + if (null === $item->expiry) { + $ttl = 0 < $defaultLifetime ? $defaultLifetime : 0; + } elseif (!$item->expiry) { + $ttl = 0; + } elseif (0 >= $ttl = (int) (0.1 + $item->expiry - $now)) { + $expiredIds[] = $getId($key); + continue; + } + if (isset(($metadata = $item->newMetadata)[CacheItem::METADATA_TAGS])) { + unset($metadata[CacheItem::METADATA_TAGS]); + } + // For compactness, expiry and creation duration are packed in the key of an array, using magic numbers as separators + $byLifetime[$ttl][$getId($key)] = $metadata ? ["\x9D".pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME])."\x5F" => $item->value] : $item->value; + } + + return $byLifetime; + }, + null, + CacheItem::class + ); + } + + /** + * Returns the best possible adapter that your runtime supports. + * + * Using ApcuAdapter makes system caches compatible with read-only filesystems. + */ + public static function createSystemCache(string $namespace, int $defaultLifetime, string $version, string $directory, LoggerInterface $logger = null): AdapterInterface + { + $opcache = new PhpFilesAdapter($namespace, $defaultLifetime, $directory, true); + if (null !== $logger) { + $opcache->setLogger($logger); + } + + if (!self::$apcuSupported = self::$apcuSupported ?? ApcuAdapter::isSupported()) { + return $opcache; + } + + if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && !filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) { + return $opcache; + } + + $apcu = new ApcuAdapter($namespace, intdiv($defaultLifetime, 5), $version); + if (null !== $logger) { + $apcu->setLogger($logger); + } + + return new ChainAdapter([$apcu, $opcache]); + } + + public static function createConnection(string $dsn, array $options = []) + { + if (str_starts_with($dsn, 'redis:') || str_starts_with($dsn, 'rediss:')) { + return RedisAdapter::createConnection($dsn, $options); + } + if (str_starts_with($dsn, 'memcached:')) { + return MemcachedAdapter::createConnection($dsn, $options); + } + if (0 === strpos($dsn, 'couchbase:')) { + if (CouchbaseBucketAdapter::isSupported()) { + return CouchbaseBucketAdapter::createConnection($dsn, $options); + } + + return CouchbaseCollectionAdapter::createConnection($dsn, $options); + } + + throw new InvalidArgumentException(sprintf('Unsupported DSN: "%s".', $dsn)); + } + + /** + * {@inheritdoc} + */ + public function commit(): bool + { + $ok = true; + $byLifetime = (self::$mergeByLifetime)($this->deferred, $this->namespace, $expiredIds, \Closure::fromCallable([$this, 'getId']), $this->defaultLifetime); + $retry = $this->deferred = []; + + if ($expiredIds) { + try { + $this->doDelete($expiredIds); + } catch (\Exception $e) { + $ok = false; + CacheItem::log($this->logger, 'Failed to delete expired items: '.$e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]); + } + } + foreach ($byLifetime as $lifetime => $values) { + try { + $e = $this->doSave($values, $lifetime); + } catch (\Exception $e) { + } + if (true === $e || [] === $e) { + continue; + } + if (\is_array($e) || 1 === \count($values)) { + foreach (\is_array($e) ? $e : array_keys($values) as $id) { + $ok = false; + $v = $values[$id]; + $type = get_debug_type($v); + $message = sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => get_debug_type($this)]); + } + } else { + foreach ($values as $id => $v) { + $retry[$lifetime][] = $id; + } + } + } + + // When bulk-save failed, retry each item individually + foreach ($retry as $lifetime => $ids) { + foreach ($ids as $id) { + try { + $v = $byLifetime[$lifetime][$id]; + $e = $this->doSave([$id => $v], $lifetime); + } catch (\Exception $e) { + } + if (true === $e || [] === $e) { + continue; + } + $ok = false; + $type = get_debug_type($v); + $message = sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => get_debug_type($this)]); + } + } + + return $ok; + } +} diff --git a/vendor/symfony/cache/Adapter/AbstractTagAwareAdapter.php b/vendor/symfony/cache/Adapter/AbstractTagAwareAdapter.php new file mode 100644 index 0000000..565bc9b --- /dev/null +++ b/vendor/symfony/cache/Adapter/AbstractTagAwareAdapter.php @@ -0,0 +1,328 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Log\LoggerAwareInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\AbstractAdapterTrait; +use Symfony\Component\Cache\Traits\ContractsTrait; +use Symfony\Contracts\Cache\TagAwareCacheInterface; + +/** + * Abstract for native TagAware adapters. + * + * To keep info on tags, the tags are both serialized as part of cache value and provided as tag ids + * to Adapters on operations when needed for storage to doSave(), doDelete() & doInvalidate(). + * + * @author Nicolas Grekas + * @author André Rømcke + * + * @internal + */ +abstract class AbstractTagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, LoggerAwareInterface, ResettableInterface +{ + use AbstractAdapterTrait; + use ContractsTrait; + + private const TAGS_PREFIX = "\0tags\0"; + + protected function __construct(string $namespace = '', int $defaultLifetime = 0) + { + $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':'; + $this->defaultLifetime = $defaultLifetime; + if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) { + throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s").', $this->maxIdLength - 24, \strlen($namespace), $namespace)); + } + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind( + static function ($key, $value, $isHit) { + $item = new CacheItem(); + $item->key = $key; + $item->isTaggable = true; + // If structure does not match what we expect return item as is (no value and not a hit) + if (!\is_array($value) || !\array_key_exists('value', $value)) { + return $item; + } + $item->isHit = $isHit; + // Extract value, tags and meta data from the cache value + $item->value = $value['value']; + $item->metadata[CacheItem::METADATA_TAGS] = $value['tags'] ?? []; + if (isset($value['meta'])) { + // For compactness these values are packed, & expiry is offset to reduce size + $v = unpack('Ve/Nc', $value['meta']); + $item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET; + $item->metadata[CacheItem::METADATA_CTIME] = $v['c']; + } + + return $item; + }, + null, + CacheItem::class + ); + self::$mergeByLifetime ?? self::$mergeByLifetime = \Closure::bind( + static function ($deferred, &$expiredIds, $getId, $tagPrefix, $defaultLifetime) { + $byLifetime = []; + $now = microtime(true); + $expiredIds = []; + + foreach ($deferred as $key => $item) { + $key = (string) $key; + if (null === $item->expiry) { + $ttl = 0 < $defaultLifetime ? $defaultLifetime : 0; + } elseif (!$item->expiry) { + $ttl = 0; + } elseif (0 >= $ttl = (int) (0.1 + $item->expiry - $now)) { + $expiredIds[] = $getId($key); + continue; + } + // Store Value and Tags on the cache value + if (isset(($metadata = $item->newMetadata)[CacheItem::METADATA_TAGS])) { + $value = ['value' => $item->value, 'tags' => $metadata[CacheItem::METADATA_TAGS]]; + unset($metadata[CacheItem::METADATA_TAGS]); + } else { + $value = ['value' => $item->value, 'tags' => []]; + } + + if ($metadata) { + // For compactness, expiry and creation duration are packed, using magic numbers as separators + $value['meta'] = pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME]); + } + + // Extract tag changes, these should be removed from values in doSave() + $value['tag-operations'] = ['add' => [], 'remove' => []]; + $oldTags = $item->metadata[CacheItem::METADATA_TAGS] ?? []; + foreach (array_diff($value['tags'], $oldTags) as $addedTag) { + $value['tag-operations']['add'][] = $getId($tagPrefix.$addedTag); + } + foreach (array_diff($oldTags, $value['tags']) as $removedTag) { + $value['tag-operations']['remove'][] = $getId($tagPrefix.$removedTag); + } + + $byLifetime[$ttl][$getId($key)] = $value; + $item->metadata = $item->newMetadata; + } + + return $byLifetime; + }, + null, + CacheItem::class + ); + } + + /** + * Persists several cache items immediately. + * + * @param array $values The values to cache, indexed by their cache identifier + * @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning + * @param array[] $addTagData Hash where key is tag id, and array value is list of cache id's to add to tag + * @param array[] $removeTagData Hash where key is tag id, and array value is list of cache id's to remove to tag + * + * @return array The identifiers that failed to be cached or a boolean stating if caching succeeded or not + */ + abstract protected function doSave(array $values, int $lifetime, array $addTagData = [], array $removeTagData = []): array; + + /** + * Removes multiple items from the pool and their corresponding tags. + * + * @param array $ids An array of identifiers that should be removed from the pool + */ + abstract protected function doDelete(array $ids): bool; + + /** + * Removes relations between tags and deleted items. + * + * @param array $tagData Array of tag => key identifiers that should be removed from the pool + */ + abstract protected function doDeleteTagRelations(array $tagData): bool; + + /** + * Invalidates cached items using tags. + * + * @param string[] $tagIds An array of tags to invalidate, key is tag and value is tag id + */ + abstract protected function doInvalidate(array $tagIds): bool; + + /** + * Delete items and yields the tags they were bound to. + */ + protected function doDeleteYieldTags(array $ids): iterable + { + foreach ($this->doFetch($ids) as $id => $value) { + yield $id => \is_array($value) && \is_array($value['tags'] ?? null) ? $value['tags'] : []; + } + + $this->doDelete($ids); + } + + /** + * {@inheritdoc} + */ + public function commit(): bool + { + $ok = true; + $byLifetime = (self::$mergeByLifetime)($this->deferred, $expiredIds, \Closure::fromCallable([$this, 'getId']), self::TAGS_PREFIX, $this->defaultLifetime); + $retry = $this->deferred = []; + + if ($expiredIds) { + // Tags are not cleaned up in this case, however that is done on invalidateTags(). + try { + $this->doDelete($expiredIds); + } catch (\Exception $e) { + $ok = false; + CacheItem::log($this->logger, 'Failed to delete expired items: '.$e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]); + } + } + foreach ($byLifetime as $lifetime => $values) { + try { + $values = $this->extractTagData($values, $addTagData, $removeTagData); + $e = $this->doSave($values, $lifetime, $addTagData, $removeTagData); + } catch (\Exception $e) { + } + if (true === $e || [] === $e) { + continue; + } + if (\is_array($e) || 1 === \count($values)) { + foreach (\is_array($e) ? $e : array_keys($values) as $id) { + $ok = false; + $v = $values[$id]; + $type = get_debug_type($v); + $message = sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => get_debug_type($this)]); + } + } else { + foreach ($values as $id => $v) { + $retry[$lifetime][] = $id; + } + } + } + + // When bulk-save failed, retry each item individually + foreach ($retry as $lifetime => $ids) { + foreach ($ids as $id) { + try { + $v = $byLifetime[$lifetime][$id]; + $values = $this->extractTagData([$id => $v], $addTagData, $removeTagData); + $e = $this->doSave($values, $lifetime, $addTagData, $removeTagData); + } catch (\Exception $e) { + } + if (true === $e || [] === $e) { + continue; + } + $ok = false; + $type = get_debug_type($v); + $message = sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => get_debug_type($this)]); + } + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys): bool + { + if (!$keys) { + return true; + } + + $ok = true; + $ids = []; + $tagData = []; + + foreach ($keys as $key) { + $ids[$key] = $this->getId($key); + unset($this->deferred[$key]); + } + + try { + foreach ($this->doDeleteYieldTags(array_values($ids)) as $id => $tags) { + foreach ($tags as $tag) { + $tagData[$this->getId(self::TAGS_PREFIX.$tag)][] = $id; + } + } + } catch (\Exception $e) { + $ok = false; + } + + try { + if ((!$tagData || $this->doDeleteTagRelations($tagData)) && $ok) { + return true; + } + } catch (\Exception $e) { + } + + // When bulk-delete failed, retry each item individually + foreach ($ids as $key => $id) { + try { + $e = null; + if ($this->doDelete([$id])) { + continue; + } + } catch (\Exception $e) { + } + $message = 'Failed to delete key "{key}"'.($e instanceof \Exception ? ': '.$e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + $ok = false; + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags): bool + { + if (empty($tags)) { + return false; + } + + $tagIds = []; + foreach (array_unique($tags) as $tag) { + $tagIds[] = $this->getId(self::TAGS_PREFIX.$tag); + } + + try { + if ($this->doInvalidate($tagIds)) { + return true; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to invalidate tags: '.$e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]); + } + + return false; + } + + /** + * Extracts tags operation data from $values set in mergeByLifetime, and returns values without it. + */ + private function extractTagData(array $values, ?array &$addTagData, ?array &$removeTagData): array + { + $addTagData = $removeTagData = []; + foreach ($values as $id => $value) { + foreach ($value['tag-operations']['add'] as $tag => $tagId) { + $addTagData[$tagId][] = $id; + } + + foreach ($value['tag-operations']['remove'] as $tag => $tagId) { + $removeTagData[$tagId][] = $id; + } + + unset($values[$id]['tag-operations']); + } + + return $values; + } +} diff --git a/vendor/symfony/cache/Adapter/AdapterInterface.php b/vendor/symfony/cache/Adapter/AdapterInterface.php new file mode 100644 index 0000000..7b0771b --- /dev/null +++ b/vendor/symfony/cache/Adapter/AdapterInterface.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\CacheItem; + +// Help opcache.preload discover always-needed symbols +class_exists(CacheItem::class); + +/** + * Interface for adapters managing instances of Symfony's CacheItem. + * + * @author Kévin Dunglas + */ +interface AdapterInterface extends CacheItemPoolInterface +{ + /** + * {@inheritdoc} + */ + public function getItem(mixed $key): CacheItem; + + /** + * {@inheritdoc} + * + * @return iterable + */ + public function getItems(array $keys = []): iterable; + + /** + * {@inheritdoc} + */ + public function clear(string $prefix = ''): bool; +} diff --git a/vendor/symfony/cache/Adapter/ApcuAdapter.php b/vendor/symfony/cache/Adapter/ApcuAdapter.php new file mode 100644 index 0000000..ba222d1 --- /dev/null +++ b/vendor/symfony/cache/Adapter/ApcuAdapter.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Marshaller\MarshallerInterface; + +/** + * @author Nicolas Grekas + */ +class ApcuAdapter extends AbstractAdapter +{ + private $marshaller; + + /** + * @throws CacheException if APCu is not enabled + */ + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $version = null, MarshallerInterface $marshaller = null) + { + if (!static::isSupported()) { + throw new CacheException('APCu is not enabled.'); + } + if ('cli' === \PHP_SAPI) { + ini_set('apc.use_request_time', 0); + } + parent::__construct($namespace, $defaultLifetime); + + if (null !== $version) { + CacheItem::validateKey($version); + + if (!apcu_exists($version.'@'.$namespace)) { + $this->doClear($namespace); + apcu_add($version.'@'.$namespace, null); + } + } + + $this->marshaller = $marshaller; + } + + public static function isSupported() + { + return \function_exists('apcu_fetch') && filter_var(\ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN); + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids): iterable + { + $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback'); + try { + $values = []; + foreach (apcu_fetch($ids, $ok) ?: [] as $k => $v) { + if (null !== $v || $ok) { + $values[$k] = null !== $this->marshaller ? $this->marshaller->unmarshall($v) : $v; + } + } + + return $values; + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine()); + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } + + /** + * {@inheritdoc} + */ + protected function doHave(string $id): bool + { + return apcu_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace): bool + { + return isset($namespace[0]) && class_exists(\APCUIterator::class, false) && ('cli' !== \PHP_SAPI || filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) + ? apcu_delete(new \APCUIterator(sprintf('/^%s/', preg_quote($namespace, '/')), \APC_ITER_KEY)) + : apcu_clear_cache(); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + foreach ($ids as $id) { + apcu_delete($id); + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime): array|bool + { + if (null !== $this->marshaller && (!$values = $this->marshaller->marshall($values, $failed))) { + return $failed; + } + + try { + if (false === $failures = apcu_store($values, null, $lifetime)) { + $failures = $values; + } + + return array_keys($failures); + } catch (\Throwable $e) { + if (1 === \count($values)) { + // Workaround https://github.com/krakjoe/apcu/issues/170 + apcu_delete(array_key_first($values)); + } + + throw $e; + } + } +} diff --git a/vendor/symfony/cache/Adapter/ArrayAdapter.php b/vendor/symfony/cache/Adapter/ArrayAdapter.php new file mode 100644 index 0000000..8d503d0 --- /dev/null +++ b/vendor/symfony/cache/Adapter/ArrayAdapter.php @@ -0,0 +1,391 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Contracts\Cache\CacheInterface; + +/** + * An in-memory cache storage. + * + * Acts as a least-recently-used (LRU) storage when configured with a maximum number of items. + * + * @author Nicolas Grekas + */ +class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface +{ + use LoggerAwareTrait; + + private bool $storeSerialized; + private array $values = []; + private array $expiries = []; + private int $defaultLifetime; + private float $maxLifetime; + private int $maxItems; + + private static \Closure $createCacheItem; + + /** + * @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise + */ + public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true, float $maxLifetime = 0, int $maxItems = 0) + { + if (0 > $maxLifetime) { + throw new InvalidArgumentException(sprintf('Argument $maxLifetime must be positive, %F passed.', $maxLifetime)); + } + + if (0 > $maxItems) { + throw new InvalidArgumentException(sprintf('Argument $maxItems must be a positive integer, %d passed.', $maxItems)); + } + + $this->defaultLifetime = $defaultLifetime; + $this->storeSerialized = $storeSerialized; + $this->maxLifetime = $maxLifetime; + $this->maxItems = $maxItems; + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind( + static function ($key, $value, $isHit) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->isHit = $isHit; + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, float $beta = null, array &$metadata = null): mixed + { + $item = $this->getItem($key); + $metadata = $item->getMetadata(); + + // ArrayAdapter works in memory, we don't care about stampede protection + if (\INF === $beta || !$item->isHit()) { + $save = true; + $item->set($callback($item, $save)); + if ($save) { + $this->save($item); + } + } + + return $item->get(); + } + + /** + * {@inheritdoc} + */ + public function delete(string $key): bool + { + return $this->deleteItem($key); + } + + /** + * {@inheritdoc} + */ + public function hasItem(mixed $key): bool + { + if (\is_string($key) && isset($this->expiries[$key]) && $this->expiries[$key] > microtime(true)) { + if ($this->maxItems) { + // Move the item last in the storage + $value = $this->values[$key]; + unset($this->values[$key]); + $this->values[$key] = $value; + } + + return true; + } + \assert('' !== CacheItem::validateKey($key)); + + return isset($this->expiries[$key]) && !$this->deleteItem($key); + } + + /** + * {@inheritdoc} + */ + public function getItem(mixed $key): CacheItem + { + if (!$isHit = $this->hasItem($key)) { + $value = null; + + if (!$this->maxItems) { + // Track misses in non-LRU mode only + $this->values[$key] = null; + } + } else { + $value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key]; + } + + return (self::$createCacheItem)($key, $value, $isHit); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []): iterable + { + \assert(self::validateKeys($keys)); + + return $this->generateItems($keys, microtime(true), self::$createCacheItem); + } + + /** + * {@inheritdoc} + */ + public function deleteItem(mixed $key): bool + { + \assert('' !== CacheItem::validateKey($key)); + unset($this->values[$key], $this->expiries[$key]); + + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys): bool + { + foreach ($keys as $key) { + $this->deleteItem($key); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item): bool + { + if (!$item instanceof CacheItem) { + return false; + } + $item = (array) $item; + $key = $item["\0*\0key"]; + $value = $item["\0*\0value"]; + $expiry = $item["\0*\0expiry"]; + + $now = microtime(true); + + if (null !== $expiry) { + if (!$expiry) { + $expiry = \PHP_INT_MAX; + } elseif ($expiry <= $now) { + $this->deleteItem($key); + + return true; + } + } + if ($this->storeSerialized && null === $value = $this->freeze($value, $key)) { + return false; + } + if (null === $expiry && 0 < $this->defaultLifetime) { + $expiry = $this->defaultLifetime; + $expiry = $now + ($expiry > ($this->maxLifetime ?: $expiry) ? $this->maxLifetime : $expiry); + } elseif ($this->maxLifetime && (null === $expiry || $expiry > $now + $this->maxLifetime)) { + $expiry = $now + $this->maxLifetime; + } + + if ($this->maxItems) { + unset($this->values[$key]); + + // Iterate items and vacuum expired ones while we are at it + foreach ($this->values as $k => $v) { + if ($this->expiries[$k] > $now && \count($this->values) < $this->maxItems) { + break; + } + + unset($this->values[$k], $this->expiries[$k]); + } + } + + $this->values[$key] = $value; + $this->expiries[$key] = $expiry ?? \PHP_INT_MAX; + + return true; + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item): bool + { + return $this->save($item); + } + + /** + * {@inheritdoc} + */ + public function commit(): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function clear(string $prefix = ''): bool + { + if ('' !== $prefix) { + $now = microtime(true); + + foreach ($this->values as $key => $value) { + if (!isset($this->expiries[$key]) || $this->expiries[$key] <= $now || 0 === strpos($key, $prefix)) { + unset($this->values[$key], $this->expiries[$key]); + } + } + + if ($this->values) { + return true; + } + } + + $this->values = $this->expiries = []; + + return true; + } + + /** + * Returns all cached values, with cache miss as null. + */ + public function getValues(): array + { + if (!$this->storeSerialized) { + return $this->values; + } + + $values = $this->values; + foreach ($values as $k => $v) { + if (null === $v || 'N;' === $v) { + continue; + } + if (!\is_string($v) || !isset($v[2]) || ':' !== $v[1]) { + $values[$k] = serialize($v); + } + } + + return $values; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->clear(); + } + + private function generateItems(array $keys, float $now, \Closure $f): \Generator + { + foreach ($keys as $i => $key) { + if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key))) { + $value = null; + + if (!$this->maxItems) { + // Track misses in non-LRU mode only + $this->values[$key] = null; + } + } else { + if ($this->maxItems) { + // Move the item last in the storage + $value = $this->values[$key]; + unset($this->values[$key]); + $this->values[$key] = $value; + } + + $value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key]; + } + unset($keys[$i]); + + yield $key => $f($key, $value, $isHit); + } + + foreach ($keys as $key) { + yield $key => $f($key, null, false); + } + } + + private function freeze($value, string $key) + { + if (null === $value) { + return 'N;'; + } + if (\is_string($value)) { + // Serialize strings if they could be confused with serialized objects or arrays + if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) { + return serialize($value); + } + } elseif (!\is_scalar($value)) { + try { + $serialized = serialize($value); + } catch (\Exception $e) { + unset($this->values[$key]); + $type = get_debug_type($value); + $message = sprintf('Failed to save key "{key}" of type %s: %s', $type, $e->getMessage()); + CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + + return; + } + // Keep value serialized if it contains any objects or any internal references + if ('C' === $serialized[0] || 'O' === $serialized[0] || preg_match('/;[OCRr]:[1-9]/', $serialized)) { + return $serialized; + } + } + + return $value; + } + + private function unfreeze(string $key, bool &$isHit) + { + if ('N;' === $value = $this->values[$key]) { + return null; + } + if (\is_string($value) && isset($value[2]) && ':' === $value[1]) { + try { + $value = unserialize($value); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to unserialize key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + $value = false; + } + if (false === $value) { + $value = null; + $isHit = false; + + if (!$this->maxItems) { + $this->values[$key] = null; + } + } + } + + return $value; + } + + private function validateKeys(array $keys): bool + { + foreach ($keys as $key) { + if (!\is_string($key) || !isset($this->expiries[$key])) { + CacheItem::validateKey($key); + } + } + + return true; + } +} diff --git a/vendor/symfony/cache/Adapter/ChainAdapter.php b/vendor/symfony/cache/Adapter/ChainAdapter.php new file mode 100644 index 0000000..b003ce0 --- /dev/null +++ b/vendor/symfony/cache/Adapter/ChainAdapter.php @@ -0,0 +1,328 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ContractsTrait; +use Symfony\Contracts\Cache\CacheInterface; +use Symfony\Contracts\Service\ResetInterface; + +/** + * Chains several adapters together. + * + * Cached items are fetched from the first adapter having them in its data store. + * They are saved and deleted in all adapters at once. + * + * @author Kévin Dunglas + */ +class ChainAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface +{ + use ContractsTrait; + + private array $adapters = []; + private int $adapterCount; + private int $defaultLifetime; + + private static \Closure $syncItem; + + /** + * @param CacheItemPoolInterface[] $adapters The ordered list of adapters used to fetch cached items + * @param int $defaultLifetime The default lifetime of items propagated from lower adapters to upper ones + */ + public function __construct(array $adapters, int $defaultLifetime = 0) + { + if (!$adapters) { + throw new InvalidArgumentException('At least one adapter must be specified.'); + } + + foreach ($adapters as $adapter) { + if (!$adapter instanceof CacheItemPoolInterface) { + throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', get_debug_type($adapter), CacheItemPoolInterface::class)); + } + if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && $adapter instanceof ApcuAdapter && !filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) { + continue; // skip putting APCu in the chain when the backend is disabled + } + + if ($adapter instanceof AdapterInterface) { + $this->adapters[] = $adapter; + } else { + $this->adapters[] = new ProxyAdapter($adapter); + } + } + $this->adapterCount = \count($this->adapters); + $this->defaultLifetime = $defaultLifetime; + + self::$syncItem ?? self::$syncItem = \Closure::bind( + static function ($sourceItem, $item, $defaultLifetime, $sourceMetadata = null) { + $sourceItem->isTaggable = false; + $sourceMetadata = $sourceMetadata ?? $sourceItem->metadata; + unset($sourceMetadata[CacheItem::METADATA_TAGS]); + + $item->value = $sourceItem->value; + $item->isHit = $sourceItem->isHit; + $item->metadata = $item->newMetadata = $sourceItem->metadata = $sourceMetadata; + + if (isset($item->metadata[CacheItem::METADATA_EXPIRY])) { + $item->expiresAt(\DateTime::createFromFormat('U.u', sprintf('%.6F', $item->metadata[CacheItem::METADATA_EXPIRY]))); + } elseif (0 < $defaultLifetime) { + $item->expiresAfter($defaultLifetime); + } + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, float $beta = null, array &$metadata = null): mixed + { + $doSave = true; + $callback = static function (CacheItem $item, bool &$save) use ($callback, &$doSave) { + $value = $callback($item, $save); + $doSave = $save; + + return $value; + }; + + $lastItem = null; + $i = 0; + $wrap = function (CacheItem $item = null, bool &$save = true) use ($key, $callback, $beta, &$wrap, &$i, &$doSave, &$lastItem, &$metadata) { + $adapter = $this->adapters[$i]; + if (isset($this->adapters[++$i])) { + $callback = $wrap; + $beta = \INF === $beta ? \INF : 0; + } + if ($adapter instanceof CacheInterface) { + $value = $adapter->get($key, $callback, $beta, $metadata); + } else { + $value = $this->doGet($adapter, $key, $callback, $beta, $metadata); + } + if (null !== $item) { + (self::$syncItem)($lastItem = $lastItem ?? $item, $item, $this->defaultLifetime, $metadata); + } + $save = $doSave; + + return $value; + }; + + return $wrap(); + } + + /** + * {@inheritdoc} + */ + public function getItem(mixed $key): CacheItem + { + $syncItem = self::$syncItem; + $misses = []; + + foreach ($this->adapters as $i => $adapter) { + $item = $adapter->getItem($key); + + if ($item->isHit()) { + while (0 <= --$i) { + $this->adapters[$i]->save($syncItem($item, $misses[$i], $this->defaultLifetime)); + } + + return $item; + } + + $misses[$i] = $item; + } + + return $item; + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []): iterable + { + return $this->generateItems($this->adapters[0]->getItems($keys), 0); + } + + private function generateItems(iterable $items, int $adapterIndex): \Generator + { + $missing = []; + $misses = []; + $nextAdapterIndex = $adapterIndex + 1; + $nextAdapter = $this->adapters[$nextAdapterIndex] ?? null; + + foreach ($items as $k => $item) { + if (!$nextAdapter || $item->isHit()) { + yield $k => $item; + } else { + $missing[] = $k; + $misses[$k] = $item; + } + } + + if ($missing) { + $syncItem = self::$syncItem; + $adapter = $this->adapters[$adapterIndex]; + $items = $this->generateItems($nextAdapter->getItems($missing), $nextAdapterIndex); + + foreach ($items as $k => $item) { + if ($item->isHit()) { + $adapter->save($syncItem($item, $misses[$k], $this->defaultLifetime)); + } + + yield $k => $item; + } + } + } + + /** + * {@inheritdoc} + */ + public function hasItem(mixed $key): bool + { + foreach ($this->adapters as $adapter) { + if ($adapter->hasItem($key)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function clear(string $prefix = ''): bool + { + $cleared = true; + $i = $this->adapterCount; + + while ($i--) { + if ($this->adapters[$i] instanceof AdapterInterface) { + $cleared = $this->adapters[$i]->clear($prefix) && $cleared; + } else { + $cleared = $this->adapters[$i]->clear() && $cleared; + } + } + + return $cleared; + } + + /** + * {@inheritdoc} + */ + public function deleteItem(mixed $key): bool + { + $deleted = true; + $i = $this->adapterCount; + + while ($i--) { + $deleted = $this->adapters[$i]->deleteItem($key) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys): bool + { + $deleted = true; + $i = $this->adapterCount; + + while ($i--) { + $deleted = $this->adapters[$i]->deleteItems($keys) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item): bool + { + $saved = true; + $i = $this->adapterCount; + + while ($i--) { + $saved = $this->adapters[$i]->save($item) && $saved; + } + + return $saved; + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item): bool + { + $saved = true; + $i = $this->adapterCount; + + while ($i--) { + $saved = $this->adapters[$i]->saveDeferred($item) && $saved; + } + + return $saved; + } + + /** + * {@inheritdoc} + */ + public function commit(): bool + { + $committed = true; + $i = $this->adapterCount; + + while ($i--) { + $committed = $this->adapters[$i]->commit() && $committed; + } + + return $committed; + } + + /** + * {@inheritdoc} + */ + public function prune(): bool + { + $pruned = true; + + foreach ($this->adapters as $adapter) { + if ($adapter instanceof PruneableInterface) { + $pruned = $adapter->prune() && $pruned; + } + } + + return $pruned; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + foreach ($this->adapters as $adapter) { + if ($adapter instanceof ResetInterface) { + $adapter->reset(); + } + } + } +} diff --git a/vendor/symfony/cache/Adapter/CouchbaseBucketAdapter.php b/vendor/symfony/cache/Adapter/CouchbaseBucketAdapter.php new file mode 100644 index 0000000..1107c1d --- /dev/null +++ b/vendor/symfony/cache/Adapter/CouchbaseBucketAdapter.php @@ -0,0 +1,247 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Symfony\Component\Cache\Marshaller\MarshallerInterface; + +/** + * @author Antonio Jose Cerezo Aranda + */ +class CouchbaseBucketAdapter extends AbstractAdapter +{ + private const THIRTY_DAYS_IN_SECONDS = 2592000; + private const MAX_KEY_LENGTH = 250; + private const KEY_NOT_FOUND = 13; + private const VALID_DSN_OPTIONS = [ + 'operationTimeout', + 'configTimeout', + 'configNodeTimeout', + 'n1qlTimeout', + 'httpTimeout', + 'configDelay', + 'htconfigIdleTimeout', + 'durabilityInterval', + 'durabilityTimeout', + ]; + + private $bucket; + private $marshaller; + + public function __construct(\CouchbaseBucket $bucket, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null) + { + if (!static::isSupported()) { + throw new CacheException('Couchbase >= 2.6.0 < 3.0.0 is required.'); + } + + $this->maxIdLength = static::MAX_KEY_LENGTH; + + $this->bucket = $bucket; + + parent::__construct($namespace, $defaultLifetime); + $this->enableVersioning(); + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + } + + public static function createConnection(array|string $servers, array $options = []): \CouchbaseBucket + { + if (\is_string($servers)) { + $servers = [$servers]; + } + + if (!static::isSupported()) { + throw new CacheException('Couchbase >= 2.6.0 < 3.0.0 is required.'); + } + + set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); }); + + $dsnPattern = '/^(?couchbase(?:s)?)\:\/\/(?:(?[^\:]+)\:(?[^\@]{6,})@)?' + .'(?[^\:]+(?:\:\d+)?)(?:\/(?[^\?]+))(?:\?(?.*))?$/i'; + + $newServers = []; + $protocol = 'couchbase'; + try { + $options = self::initOptions($options); + $username = $options['username']; + $password = $options['password']; + + foreach ($servers as $dsn) { + if (0 !== strpos($dsn, 'couchbase:')) { + throw new InvalidArgumentException(sprintf('Invalid Couchbase DSN: "%s" does not start with "couchbase:".', $dsn)); + } + + preg_match($dsnPattern, $dsn, $matches); + + $username = $matches['username'] ?: $username; + $password = $matches['password'] ?: $password; + $protocol = $matches['protocol'] ?: $protocol; + + if (isset($matches['options'])) { + $optionsInDsn = self::getOptions($matches['options']); + + foreach ($optionsInDsn as $parameter => $value) { + $options[$parameter] = $value; + } + } + + $newServers[] = $matches['host']; + } + + $connectionString = $protocol.'://'.implode(',', $newServers); + + $client = new \CouchbaseCluster($connectionString); + $client->authenticateAs($username, $password); + + $bucket = $client->openBucket($matches['bucketName']); + + unset($options['username'], $options['password']); + foreach ($options as $option => $value) { + if (!empty($value)) { + $bucket->$option = $value; + } + } + + return $bucket; + } finally { + restore_error_handler(); + } + } + + public static function isSupported(): bool + { + return \extension_loaded('couchbase') && version_compare(phpversion('couchbase'), '2.6.0', '>=') && version_compare(phpversion('couchbase'), '3.0', '<'); + } + + private static function getOptions(string $options): array + { + $results = []; + $optionsInArray = explode('&', $options); + + foreach ($optionsInArray as $option) { + [$key, $value] = explode('=', $option); + + if (\in_array($key, static::VALID_DSN_OPTIONS, true)) { + $results[$key] = $value; + } + } + + return $results; + } + + private static function initOptions(array $options): array + { + $options['username'] = $options['username'] ?? ''; + $options['password'] = $options['password'] ?? ''; + $options['operationTimeout'] = $options['operationTimeout'] ?? 0; + $options['configTimeout'] = $options['configTimeout'] ?? 0; + $options['configNodeTimeout'] = $options['configNodeTimeout'] ?? 0; + $options['n1qlTimeout'] = $options['n1qlTimeout'] ?? 0; + $options['httpTimeout'] = $options['httpTimeout'] ?? 0; + $options['configDelay'] = $options['configDelay'] ?? 0; + $options['htconfigIdleTimeout'] = $options['htconfigIdleTimeout'] ?? 0; + $options['durabilityInterval'] = $options['durabilityInterval'] ?? 0; + $options['durabilityTimeout'] = $options['durabilityTimeout'] ?? 0; + + return $options; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids): iterable + { + $resultsCouchbase = $this->bucket->get($ids); + + $results = []; + foreach ($resultsCouchbase as $key => $value) { + if (null !== $value->error) { + continue; + } + $results[$key] = $this->marshaller->unmarshall($value->value); + } + + return $results; + } + + /** + * {@inheritdoc} + */ + protected function doHave(string $id): bool + { + return false !== $this->bucket->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace): bool + { + if ('' === $namespace) { + $this->bucket->manager()->flush(); + + return true; + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + $results = $this->bucket->remove(array_values($ids)); + + foreach ($results as $key => $result) { + if (null !== $result->error && static::KEY_NOT_FOUND !== $result->error->getCode()) { + continue; + } + unset($results[$key]); + } + + return 0 === \count($results); + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime): array|bool + { + if (!$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + + $lifetime = $this->normalizeExpiry($lifetime); + + $ko = []; + foreach ($values as $key => $value) { + $result = $this->bucket->upsert($key, $value, ['expiry' => $lifetime]); + + if (null !== $result->error) { + $ko[$key] = $result; + } + } + + return [] === $ko ? true : $ko; + } + + private function normalizeExpiry(int $expiry): int + { + if ($expiry && $expiry > static::THIRTY_DAYS_IN_SECONDS) { + $expiry += time(); + } + + return $expiry; + } +} diff --git a/vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php b/vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php new file mode 100644 index 0000000..6c91ef4 --- /dev/null +++ b/vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php @@ -0,0 +1,214 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Couchbase\Bucket; +use Couchbase\Cluster; +use Couchbase\ClusterOptions; +use Couchbase\Collection; +use Couchbase\DocumentNotFoundException; +use Couchbase\UpsertOptions; +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Symfony\Component\Cache\Marshaller\MarshallerInterface; + +/** + * @author Antonio Jose Cerezo Aranda + */ +class CouchbaseCollectionAdapter extends AbstractAdapter +{ + private const MAX_KEY_LENGTH = 250; + + private $connection; + private $marshaller; + + public function __construct(Collection $connection, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null) + { + if (!static::isSupported()) { + throw new CacheException('Couchbase >= 3.0.0 < 4.0.0 is required.'); + } + + $this->maxIdLength = static::MAX_KEY_LENGTH; + + $this->connection = $connection; + + parent::__construct($namespace, $defaultLifetime); + $this->enableVersioning(); + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + } + + public static function createConnection(array|string $dsn, array $options = []): Bucket|Collection + { + if (\is_string($dsn)) { + $dsn = [$dsn]; + } + + if (!static::isSupported()) { + throw new CacheException('Couchbase >= 3.0.0 < 4.0.0 is required.'); + } + + set_error_handler(function ($type, $msg, $file, $line): bool { throw new \ErrorException($msg, 0, $type, $file, $line); }); + + $dsnPattern = '/^(?couchbase(?:s)?)\:\/\/(?:(?[^\:]+)\:(?[^\@]{6,})@)?' + .'(?[^\:]+(?:\:\d+)?)(?:\/(?[^\/\?]+))(?:(?:\/(?[^\/]+))' + .'(?:\/(?[^\/\?]+)))?(?:\/)?(?:\?(?.*))?$/i'; + + $newServers = []; + $protocol = 'couchbase'; + try { + $username = $options['username'] ?? ''; + $password = $options['password'] ?? ''; + + foreach ($dsn as $server) { + if (0 !== strpos($server, 'couchbase:')) { + throw new InvalidArgumentException(sprintf('Invalid Couchbase DSN: "%s" does not start with "couchbase:".', $server)); + } + + preg_match($dsnPattern, $server, $matches); + + $username = $matches['username'] ?: $username; + $password = $matches['password'] ?: $password; + $protocol = $matches['protocol'] ?: $protocol; + + if (isset($matches['options'])) { + $optionsInDsn = self::getOptions($matches['options']); + + foreach ($optionsInDsn as $parameter => $value) { + $options[$parameter] = $value; + } + } + + $newServers[] = $matches['host']; + } + + $option = isset($matches['options']) ? '?'.$matches['options'] : ''; + $connectionString = $protocol.'://'.implode(',', $newServers).$option; + + $clusterOptions = new ClusterOptions(); + $clusterOptions->credentials($username, $password); + + $client = new Cluster($connectionString, $clusterOptions); + + $bucket = $client->bucket($matches['bucketName']); + $collection = $bucket->defaultCollection(); + if (!empty($matches['scopeName'])) { + $scope = $bucket->scope($matches['scopeName']); + $collection = $scope->collection($matches['collectionName']); + } + + return $collection; + } finally { + restore_error_handler(); + } + } + + public static function isSupported(): bool + { + return \extension_loaded('couchbase') && version_compare(phpversion('couchbase'), '3.0.5', '>=') && version_compare(phpversion('couchbase'), '4.0', '<'); + } + + private static function getOptions(string $options): array + { + $results = []; + $optionsInArray = explode('&', $options); + + foreach ($optionsInArray as $option) { + [$key, $value] = explode('=', $option); + + $results[$key] = $value; + } + + return $results; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids): array + { + $results = []; + foreach ($ids as $id) { + try { + $resultCouchbase = $this->connection->get($id); + } catch (DocumentNotFoundException $exception) { + continue; + } + + $content = $resultCouchbase->value ?? $resultCouchbase->content(); + + $results[$id] = $this->marshaller->unmarshall($content); + } + + return $results; + } + + /** + * {@inheritdoc} + */ + protected function doHave($id): bool + { + return $this->connection->exists($id)->exists(); + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + $idsErrors = []; + foreach ($ids as $id) { + try { + $result = $this->connection->remove($id); + + if (null === $result->mutationToken()) { + $idsErrors[] = $id; + } + } catch (DocumentNotFoundException $exception) { + } + } + + return 0 === \count($idsErrors); + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime): array|bool + { + if (!$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + + $upsertOptions = new UpsertOptions(); + $upsertOptions->expiry($lifetime); + + $ko = []; + foreach ($values as $key => $value) { + try { + $this->connection->upsert($key, $value, $upsertOptions); + } catch (\Exception $exception) { + $ko[$key] = ''; + } + } + + return [] === $ko ? true : $ko; + } +} diff --git a/vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php b/vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php new file mode 100644 index 0000000..606f341 --- /dev/null +++ b/vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php @@ -0,0 +1,393 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Exception as DBALException; +use Doctrine\DBAL\Exception\TableNotFoundException; +use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Schema\Schema; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Symfony\Component\Cache\Marshaller\MarshallerInterface; +use Symfony\Component\Cache\PruneableInterface; + +class DoctrineDbalAdapter extends AbstractAdapter implements PruneableInterface +{ + protected $maxIdLength = 255; + + private $marshaller; + private $conn; + private string $platformName; + private string $serverVersion; + private string $table = 'cache_items'; + private string $idCol = 'item_id'; + private string $dataCol = 'item_data'; + private string $lifetimeCol = 'item_lifetime'; + private string $timeCol = 'item_time'; + private string $namespace; + + /** + * You can either pass an existing database Doctrine DBAL Connection or + * a DSN string that will be used to connect to the database. + * + * The cache table is created automatically when possible. + * Otherwise, use the createTable() method. + * + * List of available options: + * * db_table: The name of the table [default: cache_items] + * * db_id_col: The column where to store the cache id [default: item_id] + * * db_data_col: The column where to store the cache data [default: item_data] + * * db_lifetime_col: The column where to store the lifetime [default: item_lifetime] + * * db_time_col: The column where to store the timestamp [default: item_time] + * + * @throws InvalidArgumentException When namespace contains invalid characters + */ + public function __construct(Connection|string $connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = [], MarshallerInterface $marshaller = null) + { + if (isset($namespace[0]) && preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0])); + } + + if ($connOrDsn instanceof Connection) { + $this->conn = $connOrDsn; + } else { + if (!class_exists(DriverManager::class)) { + throw new InvalidArgumentException(sprintf('Failed to parse the DSN "%s". Try running "composer require doctrine/dbal".', $connOrDsn)); + } + $this->conn = DriverManager::getConnection(['url' => $connOrDsn]); + } + + $this->table = $options['db_table'] ?? $this->table; + $this->idCol = $options['db_id_col'] ?? $this->idCol; + $this->dataCol = $options['db_data_col'] ?? $this->dataCol; + $this->lifetimeCol = $options['db_lifetime_col'] ?? $this->lifetimeCol; + $this->timeCol = $options['db_time_col'] ?? $this->timeCol; + $this->namespace = $namespace; + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + + parent::__construct($namespace, $defaultLifetime); + } + + /** + * Creates the table to store cache items which can be called once for setup. + * + * Cache ID are saved in a column of maximum length 255. Cache data is + * saved in a BLOB. + * + * @throws DBALException When the table already exists + */ + public function createTable(): void + { + $schema = new Schema(); + $this->addTableToSchema($schema); + + foreach ($schema->toSql($this->conn->getDatabasePlatform()) as $sql) { + $this->conn->executeStatement($sql); + } + } + + /** + * {@inheritdoc} + */ + public function configureSchema(Schema $schema, Connection $forConnection): void + { + // only update the schema for this connection + if ($forConnection !== $this->conn) { + return; + } + + if ($schema->hasTable($this->table)) { + return; + } + + $this->addTableToSchema($schema); + } + + /** + * {@inheritdoc} + */ + public function prune(): bool + { + $deleteSql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= ?"; + $params = [time()]; + $paramTypes = [ParameterType::INTEGER]; + + if ('' !== $this->namespace) { + $deleteSql .= " AND $this->idCol LIKE ?"; + $params[] = sprintf('%s%%', $this->namespace); + $paramTypes[] = ParameterType::STRING; + } + + try { + $this->conn->executeStatement($deleteSql, $params, $paramTypes); + } catch (TableNotFoundException $e) { + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids): iterable + { + $now = time(); + $expired = []; + + $sql = "SELECT $this->idCol, CASE WHEN $this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > ? THEN $this->dataCol ELSE NULL END FROM $this->table WHERE $this->idCol IN (?)"; + $result = $this->conn->executeQuery($sql, [ + $now, + $ids, + ], [ + ParameterType::INTEGER, + Connection::PARAM_STR_ARRAY, + ])->iterateNumeric(); + + foreach ($result as $row) { + if (null === $row[1]) { + $expired[] = $row[0]; + } else { + yield $row[0] => $this->marshaller->unmarshall(\is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]); + } + } + + if ($expired) { + $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= ? AND $this->idCol IN (?)"; + $this->conn->executeStatement($sql, [ + $now, + $expired, + ], [ + ParameterType::INTEGER, + Connection::PARAM_STR_ARRAY, + ]); + } + } + + /** + * {@inheritdoc} + */ + protected function doHave(string $id): bool + { + $sql = "SELECT 1 FROM $this->table WHERE $this->idCol = ? AND ($this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > ?)"; + $result = $this->conn->executeQuery($sql, [ + $id, + time(), + ], [ + ParameterType::STRING, + ParameterType::INTEGER, + ]); + + return (bool) $result->fetchOne(); + } + + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace): bool + { + if ('' === $namespace) { + if ('sqlite' === $this->getPlatformName()) { + $sql = "DELETE FROM $this->table"; + } else { + $sql = "TRUNCATE TABLE $this->table"; + } + } else { + $sql = "DELETE FROM $this->table WHERE $this->idCol LIKE '$namespace%'"; + } + + try { + $this->conn->executeStatement($sql); + } catch (TableNotFoundException $e) { + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + $sql = "DELETE FROM $this->table WHERE $this->idCol IN (?)"; + try { + $this->conn->executeStatement($sql, [array_values($ids)], [Connection::PARAM_STR_ARRAY]); + } catch (TableNotFoundException $e) { + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime): array|bool + { + if (!$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + + $platformName = $this->getPlatformName(); + $insertSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?)"; + + switch (true) { + case 'mysql' === $platformName: + $sql = $insertSql." ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)"; + break; + case 'oci' === $platformName: + // DUAL is Oracle specific dummy table + $sql = "MERGE INTO $this->table USING DUAL 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 'sqlsrv' === $platformName && version_compare($this->getServerVersion(), '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 + $sql = "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' === $platformName: + $sql = 'INSERT OR REPLACE'.substr($insertSql, 6); + break; + case 'pgsql' === $platformName && version_compare($this->getServerVersion(), '9.5', '>='): + $sql = $insertSql." ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)"; + break; + default: + $platformName = null; + $sql = "UPDATE $this->table SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ? WHERE $this->idCol = ?"; + break; + } + + $now = time(); + $lifetime = $lifetime ?: null; + try { + $stmt = $this->conn->prepare($sql); + } catch (TableNotFoundException $e) { + if (!$this->conn->isTransactionActive() || \in_array($platformName, ['pgsql', 'sqlite', 'sqlsrv'], true)) { + $this->createTable(); + } + $stmt = $this->conn->prepare($sql); + } + + // $id and $data are defined later in the loop. Binding is done by reference, values are read on execution. + if ('sqlsrv' === $platformName || 'oci' === $platformName) { + $stmt->bindParam(1, $id); + $stmt->bindParam(2, $id); + $stmt->bindParam(3, $data, ParameterType::LARGE_OBJECT); + $stmt->bindValue(4, $lifetime, ParameterType::INTEGER); + $stmt->bindValue(5, $now, ParameterType::INTEGER); + $stmt->bindParam(6, $data, ParameterType::LARGE_OBJECT); + $stmt->bindValue(7, $lifetime, ParameterType::INTEGER); + $stmt->bindValue(8, $now, ParameterType::INTEGER); + } elseif (null !== $platformName) { + $stmt->bindParam(1, $id); + $stmt->bindParam(2, $data, ParameterType::LARGE_OBJECT); + $stmt->bindValue(3, $lifetime, ParameterType::INTEGER); + $stmt->bindValue(4, $now, ParameterType::INTEGER); + } else { + $stmt->bindParam(1, $data, ParameterType::LARGE_OBJECT); + $stmt->bindValue(2, $lifetime, ParameterType::INTEGER); + $stmt->bindValue(3, $now, ParameterType::INTEGER); + $stmt->bindParam(4, $id); + + $insertStmt = $this->conn->prepare($insertSql); + $insertStmt->bindParam(1, $id); + $insertStmt->bindParam(2, $data, ParameterType::LARGE_OBJECT); + $insertStmt->bindValue(3, $lifetime, ParameterType::INTEGER); + $insertStmt->bindValue(4, $now, ParameterType::INTEGER); + } + + foreach ($values as $id => $data) { + try { + $rowCount = $stmt->executeStatement(); + } catch (TableNotFoundException $e) { + if (!$this->conn->isTransactionActive() || \in_array($platformName, ['pgsql', 'sqlite', 'sqlsrv'], true)) { + $this->createTable(); + } + $rowCount = $stmt->executeStatement(); + } + if (null === $platformName && 0 === $rowCount) { + try { + $insertStmt->executeStatement(); + } catch (DBALException $e) { + // A concurrent write won, let it be + } + } + } + + return $failed; + } + + private function getPlatformName(): string + { + if (isset($this->platformName)) { + return $this->platformName; + } + + $platform = $this->conn->getDatabasePlatform(); + + switch (true) { + case $platform instanceof \Doctrine\DBAL\Platforms\MySQLPlatform: + case $platform instanceof \Doctrine\DBAL\Platforms\MySQL57Platform: + return $this->platformName = 'mysql'; + + case $platform instanceof \Doctrine\DBAL\Platforms\SqlitePlatform: + return $this->platformName = 'sqlite'; + + case $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQLPlatform: + case $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQL94Platform: + return $this->platformName = 'pgsql'; + + case $platform instanceof \Doctrine\DBAL\Platforms\OraclePlatform: + return $this->platformName = 'oci'; + + case $platform instanceof \Doctrine\DBAL\Platforms\SQLServerPlatform: + case $platform instanceof \Doctrine\DBAL\Platforms\SQLServer2012Platform: + return $this->platformName = 'sqlsrv'; + + default: + return $this->platformName = \get_class($platform); + } + } + + private function getServerVersion(): string + { + if (isset($this->serverVersion)) { + return $this->serverVersion; + } + + $conn = $this->conn->getWrappedConnection(); + if ($conn instanceof ServerInfoAwareConnection) { + return $this->serverVersion = $conn->getServerVersion(); + } + + return $this->serverVersion = '0'; + } + + private function addTableToSchema(Schema $schema): void + { + $types = [ + 'mysql' => 'binary', + 'sqlite' => 'text', + ]; + + $table = $schema->createTable($this->table); + $table->addColumn($this->idCol, $types[$this->getPlatformName()] ?? 'string', ['length' => 255]); + $table->addColumn($this->dataCol, 'blob', ['length' => 16777215]); + $table->addColumn($this->lifetimeCol, 'integer', ['unsigned' => true, 'notnull' => false]); + $table->addColumn($this->timeCol, 'integer', ['unsigned' => true]); + $table->setPrimaryKey([$this->idCol]); + } +} diff --git a/vendor/symfony/cache/Adapter/FilesystemAdapter.php b/vendor/symfony/cache/Adapter/FilesystemAdapter.php new file mode 100644 index 0000000..7185dd4 --- /dev/null +++ b/vendor/symfony/cache/Adapter/FilesystemAdapter.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Symfony\Component\Cache\Marshaller\MarshallerInterface; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\FilesystemTrait; + +class FilesystemAdapter extends AbstractAdapter implements PruneableInterface +{ + use FilesystemTrait; + + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, MarshallerInterface $marshaller = null) + { + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + } +} diff --git a/vendor/symfony/cache/Adapter/FilesystemTagAwareAdapter.php b/vendor/symfony/cache/Adapter/FilesystemTagAwareAdapter.php new file mode 100644 index 0000000..140c91f --- /dev/null +++ b/vendor/symfony/cache/Adapter/FilesystemTagAwareAdapter.php @@ -0,0 +1,235 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\Marshaller\MarshallerInterface; +use Symfony\Component\Cache\Marshaller\TagAwareMarshaller; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\FilesystemTrait; + +/** + * Stores tag id <> cache id relationship as a symlink, and lookup on invalidation calls. + * + * @author Nicolas Grekas + * @author André Rømcke + */ +class FilesystemTagAwareAdapter extends AbstractTagAwareAdapter implements PruneableInterface +{ + use FilesystemTrait { + doClear as private doClearCache; + doSave as private doSaveCache; + } + + /** + * Folder used for tag symlinks. + */ + private const TAG_FOLDER = 'tags'; + + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, MarshallerInterface $marshaller = null) + { + $this->marshaller = new TagAwareMarshaller($marshaller); + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + } + + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace): bool + { + $ok = $this->doClearCache($namespace); + + if ('' !== $namespace) { + return $ok; + } + + set_error_handler(static function () {}); + $chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + + try { + foreach ($this->scanHashDir($this->directory.self::TAG_FOLDER.\DIRECTORY_SEPARATOR) as $dir) { + if (rename($dir, $renamed = substr_replace($dir, bin2hex(random_bytes(4)), -8))) { + $dir = $renamed.\DIRECTORY_SEPARATOR; + } else { + $dir .= \DIRECTORY_SEPARATOR; + $renamed = null; + } + + for ($i = 0; $i < 38; ++$i) { + if (!is_dir($dir.$chars[$i])) { + continue; + } + for ($j = 0; $j < 38; ++$j) { + if (!is_dir($d = $dir.$chars[$i].\DIRECTORY_SEPARATOR.$chars[$j])) { + continue; + } + foreach (scandir($d, \SCANDIR_SORT_NONE) ?: [] as $link) { + if ('.' !== $link && '..' !== $link && (null !== $renamed || !realpath($d.\DIRECTORY_SEPARATOR.$link))) { + unlink($d.\DIRECTORY_SEPARATOR.$link); + } + } + null === $renamed ?: rmdir($d); + } + null === $renamed ?: rmdir($dir.$chars[$i]); + } + null === $renamed ?: rmdir($renamed); + } + } finally { + restore_error_handler(); + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime, array $addTagData = [], array $removeTagData = []): array + { + $failed = $this->doSaveCache($values, $lifetime); + + // Add Tags as symlinks + foreach ($addTagData as $tagId => $ids) { + $tagFolder = $this->getTagFolder($tagId); + foreach ($ids as $id) { + if ($failed && \in_array($id, $failed, true)) { + continue; + } + + $file = $this->getFile($id); + + if (!@symlink($file, $tagLink = $this->getFile($id, true, $tagFolder)) && !is_link($tagLink)) { + @unlink($file); + $failed[] = $id; + } + } + } + + // Unlink removed Tags + foreach ($removeTagData as $tagId => $ids) { + $tagFolder = $this->getTagFolder($tagId); + foreach ($ids as $id) { + if ($failed && \in_array($id, $failed, true)) { + continue; + } + + @unlink($this->getFile($id, false, $tagFolder)); + } + } + + return $failed; + } + + /** + * {@inheritdoc} + */ + protected function doDeleteYieldTags(array $ids): iterable + { + foreach ($ids as $id) { + $file = $this->getFile($id); + if (!is_file($file) || !$h = @fopen($file, 'r')) { + continue; + } + + if (!@unlink($file)) { + fclose($h); + continue; + } + + $meta = explode("\n", fread($h, 4096), 3)[2] ?? ''; + + // detect the compact format used in marshall() using magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F + if (13 < \strlen($meta) && "\x9D" === $meta[0] && "\0" === $meta[5] && "\x5F" === $meta[9]) { + $meta[9] = "\0"; + $tagLen = unpack('Nlen', $meta, 9)['len']; + $meta = substr($meta, 13, $tagLen); + + if (0 < $tagLen -= \strlen($meta)) { + $meta .= fread($h, $tagLen); + } + + try { + yield $id => '' === $meta ? [] : $this->marshaller->unmarshall($meta); + } catch (\Exception $e) { + yield $id => []; + } + } + + fclose($h); + } + } + + /** + * {@inheritdoc} + */ + protected function doDeleteTagRelations(array $tagData): bool + { + foreach ($tagData as $tagId => $idList) { + $tagFolder = $this->getTagFolder($tagId); + foreach ($idList as $id) { + @unlink($this->getFile($id, false, $tagFolder)); + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doInvalidate(array $tagIds): bool + { + foreach ($tagIds as $tagId) { + if (!is_dir($tagFolder = $this->getTagFolder($tagId))) { + continue; + } + + set_error_handler(static function () {}); + + try { + if (rename($tagFolder, $renamed = substr_replace($tagFolder, bin2hex(random_bytes(4)), -9))) { + $tagFolder = $renamed.\DIRECTORY_SEPARATOR; + } else { + $renamed = null; + } + + foreach ($this->scanHashDir($tagFolder) as $itemLink) { + unlink(realpath($itemLink) ?: $itemLink); + unlink($itemLink); + } + + if (null === $renamed) { + continue; + } + + $chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + + for ($i = 0; $i < 38; ++$i) { + for ($j = 0; $j < 38; ++$j) { + rmdir($tagFolder.$chars[$i].\DIRECTORY_SEPARATOR.$chars[$j]); + } + rmdir($tagFolder.$chars[$i]); + } + rmdir($renamed); + } finally { + restore_error_handler(); + } + } + + return true; + } + + private function getTagFolder(string $tagId): string + { + return $this->getFile($tagId, false, $this->directory.self::TAG_FOLDER.\DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR; + } +} diff --git a/vendor/symfony/cache/Adapter/MemcachedAdapter.php b/vendor/symfony/cache/Adapter/MemcachedAdapter.php new file mode 100644 index 0000000..f00b42d --- /dev/null +++ b/vendor/symfony/cache/Adapter/MemcachedAdapter.php @@ -0,0 +1,349 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Symfony\Component\Cache\Marshaller\MarshallerInterface; + +/** + * @author Rob Frawley 2nd + * @author Nicolas Grekas + */ +class MemcachedAdapter extends AbstractAdapter +{ + /** + * We are replacing characters that are illegal in Memcached keys with reserved characters from + * {@see \Symfony\Contracts\Cache\ItemInterface::RESERVED_CHARACTERS} that are legal in Memcached. + * Note: don’t use {@see \Symfony\Component\Cache\Adapter\AbstractAdapter::NS_SEPARATOR}. + */ + private const RESERVED_MEMCACHED = " \n\r\t\v\f\0"; + private const RESERVED_PSR6 = '@()\{}/'; + + protected $maxIdLength = 250; + + private const DEFAULT_CLIENT_OPTIONS = [ + 'persistent_id' => null, + 'username' => null, + 'password' => null, + \Memcached::OPT_SERIALIZER => \Memcached::SERIALIZER_PHP, + ]; + + private $marshaller; + private $client; + private $lazyClient; + + /** + * Using a MemcachedAdapter with a TagAwareAdapter for storing tags is discouraged. + * Using a RedisAdapter is recommended instead. If you cannot do otherwise, be aware that: + * - the Memcached::OPT_BINARY_PROTOCOL must be enabled + * (that's the default when using MemcachedAdapter::createConnection()); + * - tags eviction by Memcached's LRU algorithm will break by-tags invalidation; + * your Memcached memory should be large enough to never trigger LRU. + * + * Using a MemcachedAdapter as a pure items store is fine. + */ + public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null) + { + if (!static::isSupported()) { + throw new CacheException('Memcached '.(\PHP_VERSION_ID >= 80100 ? '> 3.1.5' : '>= 2.2.0').' is required.'); + } + if ('Memcached' === \get_class($client)) { + $opt = $client->getOption(\Memcached::OPT_SERIALIZER); + if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) { + throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".'); + } + $this->maxIdLength -= \strlen($client->getOption(\Memcached::OPT_PREFIX_KEY)); + $this->client = $client; + } else { + $this->lazyClient = $client; + } + + parent::__construct($namespace, $defaultLifetime); + $this->enableVersioning(); + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + } + + public static function isSupported() + { + return \extension_loaded('memcached') && version_compare(phpversion('memcached'), \PHP_VERSION_ID >= 80100 ? '3.1.6' : '2.2.0', '>='); + } + + /** + * Creates a Memcached instance. + * + * By default, the binary protocol, no block, and libketama compatible options are enabled. + * + * Examples for servers: + * - 'memcached://user:pass@localhost?weight=33' + * - [['localhost', 11211, 33]] + * + * @param array[]|string|string[] $servers An array of servers, a DSN, or an array of DSNs + * + * @throws \ErrorException When invalid options or servers are provided + */ + public static function createConnection(array|string $servers, array $options = []): \Memcached + { + if (\is_string($servers)) { + $servers = [$servers]; + } + if (!static::isSupported()) { + throw new CacheException('Memcached '.(\PHP_VERSION_ID >= 80100 ? '> 3.1.5' : '>= 2.2.0').' is required.'); + } + set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); }); + try { + $options += static::DEFAULT_CLIENT_OPTIONS; + $client = new \Memcached($options['persistent_id']); + $username = $options['username']; + $password = $options['password']; + + // parse any DSN in $servers + foreach ($servers as $i => $dsn) { + if (\is_array($dsn)) { + continue; + } + if (!str_starts_with($dsn, 'memcached:')) { + throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: "%s" does not start with "memcached:".', $dsn)); + } + $params = preg_replace_callback('#^memcached:(//)?(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) { + if (!empty($m[2])) { + [$username, $password] = explode(':', $m[2], 2) + [1 => null]; + } + + return 'file:'.($m[1] ?? ''); + }, $dsn); + if (false === $params = parse_url($params)) { + throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: "%s".', $dsn)); + } + $query = $hosts = []; + if (isset($params['query'])) { + parse_str($params['query'], $query); + + if (isset($query['host'])) { + if (!\is_array($hosts = $query['host'])) { + throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: "%s".', $dsn)); + } + foreach ($hosts as $host => $weight) { + if (false === $port = strrpos($host, ':')) { + $hosts[$host] = [$host, 11211, (int) $weight]; + } else { + $hosts[$host] = [substr($host, 0, $port), (int) substr($host, 1 + $port), (int) $weight]; + } + } + $hosts = array_values($hosts); + unset($query['host']); + } + if ($hosts && !isset($params['host']) && !isset($params['path'])) { + unset($servers[$i]); + $servers = array_merge($servers, $hosts); + continue; + } + } + if (!isset($params['host']) && !isset($params['path'])) { + throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: "%s".', $dsn)); + } + if (isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) { + $params['weight'] = $m[1]; + $params['path'] = substr($params['path'], 0, -\strlen($m[0])); + } + $params += [ + 'host' => $params['host'] ?? $params['path'], + 'port' => isset($params['host']) ? 11211 : null, + 'weight' => 0, + ]; + if ($query) { + $params += $query; + $options = $query + $options; + } + + $servers[$i] = [$params['host'], $params['port'], $params['weight']]; + + if ($hosts) { + $servers = array_merge($servers, $hosts); + } + } + + // set client's options + unset($options['persistent_id'], $options['username'], $options['password'], $options['weight'], $options['lazy']); + $options = array_change_key_case($options, \CASE_UPPER); + $client->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); + $client->setOption(\Memcached::OPT_NO_BLOCK, true); + $client->setOption(\Memcached::OPT_TCP_NODELAY, true); + if (!\array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !\array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) { + $client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, true); + } + foreach ($options as $name => $value) { + if (\is_int($name)) { + continue; + } + if ('HASH' === $name || 'SERIALIZER' === $name || 'DISTRIBUTION' === $name) { + $value = \constant('Memcached::'.$name.'_'.strtoupper($value)); + } + unset($options[$name]); + + if (\defined('Memcached::OPT_'.$name)) { + $options[\constant('Memcached::OPT_'.$name)] = $value; + } + } + $client->setOptions($options); + + // set client's servers, taking care of persistent connections + if (!$client->isPristine()) { + $oldServers = []; + foreach ($client->getServerList() as $server) { + $oldServers[] = [$server['host'], $server['port']]; + } + + $newServers = []; + foreach ($servers as $server) { + if (1 < \count($server)) { + $server = array_values($server); + unset($server[2]); + $server[1] = (int) $server[1]; + } + $newServers[] = $server; + } + + if ($oldServers !== $newServers) { + $client->resetServerList(); + $client->addServers($servers); + } + } else { + $client->addServers($servers); + } + + if (null !== $username || null !== $password) { + if (!method_exists($client, 'setSaslAuthData')) { + trigger_error('Missing SASL support: the memcached extension must be compiled with --enable-memcached-sasl.'); + } + $client->setSaslAuthData($username, $password); + } + + return $client; + } finally { + restore_error_handler(); + } + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime): array|bool + { + if (!$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + + if ($lifetime && $lifetime > 30 * 86400) { + $lifetime += time(); + } + + $encodedValues = []; + foreach ($values as $key => $value) { + $encodedValues[self::encodeKey($key)] = $value; + } + + return $this->checkResultCode($this->getClient()->setMulti($encodedValues, $lifetime)) ? $failed : false; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids): iterable + { + try { + $encodedIds = array_map([__CLASS__, 'encodeKey'], $ids); + + $encodedResult = $this->checkResultCode($this->getClient()->getMulti($encodedIds)); + + $result = []; + foreach ($encodedResult as $key => $value) { + $result[self::decodeKey($key)] = $this->marshaller->unmarshall($value); + } + + return $result; + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine()); + } + } + + /** + * {@inheritdoc} + */ + protected function doHave(string $id): bool + { + return false !== $this->getClient()->get(self::encodeKey($id)) || $this->checkResultCode(\Memcached::RES_SUCCESS === $this->client->getResultCode()); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + $ok = true; + $encodedIds = array_map([__CLASS__, 'encodeKey'], $ids); + foreach ($this->checkResultCode($this->getClient()->deleteMulti($encodedIds)) as $result) { + if (\Memcached::RES_SUCCESS !== $result && \Memcached::RES_NOTFOUND !== $result) { + $ok = false; + } + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace): bool + { + return '' === $namespace && $this->getClient()->flush(); + } + + private function checkResultCode(mixed $result) + { + $code = $this->client->getResultCode(); + + if (\Memcached::RES_SUCCESS === $code || \Memcached::RES_NOTFOUND === $code) { + return $result; + } + + throw new CacheException('MemcachedAdapter client error: '.strtolower($this->client->getResultMessage())); + } + + private function getClient(): \Memcached + { + if (isset($this->client)) { + return $this->client; + } + + $opt = $this->lazyClient->getOption(\Memcached::OPT_SERIALIZER); + if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) { + throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".'); + } + if ('' !== $prefix = (string) $this->lazyClient->getOption(\Memcached::OPT_PREFIX_KEY)) { + throw new CacheException(sprintf('MemcachedAdapter: "prefix_key" option must be empty when using proxified connections, "%s" given.', $prefix)); + } + + return $this->client = $this->lazyClient; + } + + private static function encodeKey(string $key): string + { + return strtr($key, self::RESERVED_MEMCACHED, self::RESERVED_PSR6); + } + + private static function decodeKey(string $key): string + { + return strtr($key, self::RESERVED_PSR6, self::RESERVED_MEMCACHED); + } +} diff --git a/vendor/symfony/cache/Adapter/NullAdapter.php b/vendor/symfony/cache/Adapter/NullAdapter.php new file mode 100644 index 0000000..7827000 --- /dev/null +++ b/vendor/symfony/cache/Adapter/NullAdapter.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Contracts\Cache\CacheInterface; + +/** + * @author Titouan Galopin + */ +class NullAdapter implements AdapterInterface, CacheInterface +{ + private static $createCacheItem; + + public function __construct() + { + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind( + static function ($key) { + $item = new CacheItem(); + $item->key = $key; + $item->isHit = false; + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, float $beta = null, array &$metadata = null): mixed + { + $save = true; + + return $callback((self::$createCacheItem)($key), $save); + } + + /** + * {@inheritdoc} + */ + public function getItem(mixed $key): CacheItem + { + return (self::$createCacheItem)($key); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []): iterable + { + return $this->generateItems($keys); + } + + /** + * {@inheritdoc} + */ + public function hasItem(mixed $key): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function clear(string $prefix = ''): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteItem(mixed $key): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function commit(): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function delete(string $key): bool + { + return $this->deleteItem($key); + } + + private function generateItems(array $keys): \Generator + { + $f = self::$createCacheItem; + + foreach ($keys as $key) { + yield $key => $f($key); + } + } +} diff --git a/vendor/symfony/cache/Adapter/ParameterNormalizer.php b/vendor/symfony/cache/Adapter/ParameterNormalizer.php new file mode 100644 index 0000000..e33ae9f --- /dev/null +++ b/vendor/symfony/cache/Adapter/ParameterNormalizer.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +/** + * @author Lars Strojny + */ +final class ParameterNormalizer +{ + public static function normalizeDuration(string $duration): int + { + if (is_numeric($duration)) { + return $duration; + } + + if (false !== $time = strtotime($duration, 0)) { + return $time; + } + + try { + return \DateTime::createFromFormat('U', 0)->add(new \DateInterval($duration))->getTimestamp(); + } catch (\Exception $e) { + throw new \InvalidArgumentException(sprintf('Cannot parse date interval "%s".', $duration), 0, $e); + } + } +} diff --git a/vendor/symfony/cache/Adapter/PdoAdapter.php b/vendor/symfony/cache/Adapter/PdoAdapter.php new file mode 100644 index 0000000..7c3c705 --- /dev/null +++ b/vendor/symfony/cache/Adapter/PdoAdapter.php @@ -0,0 +1,383 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Doctrine\DBAL\Connection; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Symfony\Component\Cache\Marshaller\MarshallerInterface; +use Symfony\Component\Cache\PruneableInterface; + +class PdoAdapter extends AbstractAdapter implements PruneableInterface +{ + protected $maxIdLength = 255; + + private $marshaller; + private $conn; + private string $dsn; + private string $driver; + private string $serverVersion; + private mixed $table = 'cache_items'; + private mixed $idCol = 'item_id'; + private mixed $dataCol = 'item_data'; + private mixed $lifetimeCol = 'item_lifetime'; + private mixed $timeCol = 'item_time'; + private mixed $username = ''; + private mixed $password = ''; + private mixed $connectionOptions = []; + private string $namespace; + + /** + * You can either pass an existing database connection as PDO instance or + * a DSN string that will be used to lazy-connect to the database when the + * cache is actually used. + * + * List of available options: + * * db_table: The name of the table [default: cache_items] + * * db_id_col: The column where to store the cache id [default: item_id] + * * db_data_col: The column where to store the cache data [default: item_data] + * * db_lifetime_col: The column where to store the lifetime [default: item_lifetime] + * * db_time_col: The column where to store the timestamp [default: item_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: []] + * + * @throws InvalidArgumentException When first argument is not PDO nor Connection nor string + * @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION + * @throws InvalidArgumentException When namespace contains invalid characters + */ + public function __construct(\PDO|string $connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = [], MarshallerInterface $marshaller = null) + { + if (\is_string($connOrDsn) && str_contains($connOrDsn, '://')) { + throw new InvalidArgumentException(sprintf('Usage of Doctrine DBAL URL with "%s" is not supported. Use a PDO DSN or "%s" instead. Got "%s".', __CLASS__, DoctrineDbalAdapter::class, $connOrDsn)); + } + + if (isset($namespace[0]) && preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0])); + } + + if ($connOrDsn instanceof \PDO) { + if (\PDO::ERRMODE_EXCEPTION !== $connOrDsn->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->conn = $connOrDsn; + } else { + $this->dsn = $connOrDsn; + } + + $this->table = $options['db_table'] ?? $this->table; + $this->idCol = $options['db_id_col'] ?? $this->idCol; + $this->dataCol = $options['db_data_col'] ?? $this->dataCol; + $this->lifetimeCol = $options['db_lifetime_col'] ?? $this->lifetimeCol; + $this->timeCol = $options['db_time_col'] ?? $this->timeCol; + $this->username = $options['db_username'] ?? $this->username; + $this->password = $options['db_password'] ?? $this->password; + $this->connectionOptions = $options['db_connection_options'] ?? $this->connectionOptions; + $this->namespace = $namespace; + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + + parent::__construct($namespace, $defaultLifetime); + } + + /** + * Creates the table to store cache items which can be called once for setup. + * + * Cache ID are saved in a column of maximum length 255. Cache data is + * saved in a BLOB. + * + * @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 + $conn = $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(255) NOT NULL PRIMARY KEY, $this->dataCol MEDIUMBLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8mb4_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, $this->timeCol INTEGER NOT NULL)"; + break; + case 'pgsql': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(255) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)"; + break; + case 'oci': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(255) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)"; + break; + case 'sqlsrv': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(255) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)"; + break; + default: + throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver)); + } + + $conn->exec($sql); + } + + /** + * {@inheritdoc} + */ + public function prune(): bool + { + $deleteSql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= :time"; + + if ('' !== $this->namespace) { + $deleteSql .= " AND $this->idCol LIKE :namespace"; + } + + $connection = $this->getConnection(); + + try { + $delete = $connection->prepare($deleteSql); + } catch (\PDOException $e) { + return true; + } + $delete->bindValue(':time', time(), \PDO::PARAM_INT); + + if ('' !== $this->namespace) { + $delete->bindValue(':namespace', sprintf('%s%%', $this->namespace), \PDO::PARAM_STR); + } + try { + return $delete->execute(); + } catch (\PDOException $e) { + return true; + } + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids): iterable + { + $connection = $this->getConnection(); + + $now = time(); + $expired = []; + + $sql = str_pad('', (\count($ids) << 1) - 1, '?,'); + $sql = "SELECT $this->idCol, CASE WHEN $this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > ? THEN $this->dataCol ELSE NULL END FROM $this->table WHERE $this->idCol IN ($sql)"; + $stmt = $connection->prepare($sql); + $stmt->bindValue($i = 1, $now, \PDO::PARAM_INT); + foreach ($ids as $id) { + $stmt->bindValue(++$i, $id); + } + $result = $stmt->execute(); + + if (\is_object($result)) { + $result = $result->iterateNumeric(); + } else { + $stmt->setFetchMode(\PDO::FETCH_NUM); + $result = $stmt; + } + + foreach ($result as $row) { + if (null === $row[1]) { + $expired[] = $row[0]; + } else { + yield $row[0] => $this->marshaller->unmarshall(\is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]); + } + } + + if ($expired) { + $sql = str_pad('', (\count($expired) << 1) - 1, '?,'); + $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= ? AND $this->idCol IN ($sql)"; + $stmt = $connection->prepare($sql); + $stmt->bindValue($i = 1, $now, \PDO::PARAM_INT); + foreach ($expired as $id) { + $stmt->bindValue(++$i, $id); + } + $stmt->execute(); + } + } + + /** + * {@inheritdoc} + */ + protected function doHave(string $id): bool + { + $connection = $this->getConnection(); + + $sql = "SELECT 1 FROM $this->table WHERE $this->idCol = :id AND ($this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > :time)"; + $stmt = $connection->prepare($sql); + + $stmt->bindValue(':id', $id); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + + return (bool) $stmt->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace): bool + { + $conn = $this->getConnection(); + + if ('' === $namespace) { + if ('sqlite' === $this->driver) { + $sql = "DELETE FROM $this->table"; + } else { + $sql = "TRUNCATE TABLE $this->table"; + } + } else { + $sql = "DELETE FROM $this->table WHERE $this->idCol LIKE '$namespace%'"; + } + + try { + $conn->exec($sql); + } catch (\PDOException $e) { + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + $sql = str_pad('', (\count($ids) << 1) - 1, '?,'); + $sql = "DELETE FROM $this->table WHERE $this->idCol IN ($sql)"; + try { + $stmt = $this->getConnection()->prepare($sql); + $stmt->execute(array_values($ids)); + } catch (\PDOException $e) { + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime): array|bool + { + if (!$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + + $conn = $this->getConnection(); + + $driver = $this->driver; + $insertSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"; + + switch (true) { + case 'mysql' === $driver: + $sql = $insertSql." ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)"; + break; + case 'oci' === $driver: + // DUAL is Oracle specific dummy table + $sql = "MERGE INTO $this->table USING DUAL 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 'sqlsrv' === $driver && version_compare($this->getServerVersion(), '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 + $sql = "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' === $driver: + $sql = 'INSERT OR REPLACE'.substr($insertSql, 6); + break; + case 'pgsql' === $driver && version_compare($this->getServerVersion(), '9.5', '>='): + $sql = $insertSql." ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)"; + break; + default: + $driver = null; + $sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id"; + break; + } + + $now = time(); + $lifetime = $lifetime ?: null; + try { + $stmt = $conn->prepare($sql); + } catch (\PDOException $e) { + if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) { + $this->createTable(); + } + $stmt = $conn->prepare($sql); + } + + // $id and $data are defined later in the loop. Binding is done by reference, values are read on execution. + if ('sqlsrv' === $driver || 'oci' === $driver) { + $stmt->bindParam(1, $id); + $stmt->bindParam(2, $id); + $stmt->bindParam(3, $data, \PDO::PARAM_LOB); + $stmt->bindValue(4, $lifetime, \PDO::PARAM_INT); + $stmt->bindValue(5, $now, \PDO::PARAM_INT); + $stmt->bindParam(6, $data, \PDO::PARAM_LOB); + $stmt->bindValue(7, $lifetime, \PDO::PARAM_INT); + $stmt->bindValue(8, $now, \PDO::PARAM_INT); + } else { + $stmt->bindParam(':id', $id); + $stmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $stmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT); + $stmt->bindValue(':time', $now, \PDO::PARAM_INT); + } + if (null === $driver) { + $insertStmt = $conn->prepare($insertSql); + + $insertStmt->bindParam(':id', $id); + $insertStmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $insertStmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT); + $insertStmt->bindValue(':time', $now, \PDO::PARAM_INT); + } + + foreach ($values as $id => $data) { + try { + $stmt->execute(); + } catch (\PDOException $e) { + if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) { + $this->createTable(); + } + $stmt->execute(); + } + if (null === $driver && !$stmt->rowCount()) { + try { + $insertStmt->execute(); + } catch (\PDOException $e) { + // A concurrent write won, let it be + } + } + } + + return $failed; + } + + private function getConnection(): \PDO + { + if (!isset($this->conn)) { + $this->conn = new \PDO($this->dsn, $this->username, $this->password, $this->connectionOptions); + $this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + } + $this->driver ??= $this->conn->getAttribute(\PDO::ATTR_DRIVER_NAME); + + return $this->conn; + } + + private function getServerVersion(): string + { + return $this->serverVersion ??= $this->conn->getAttribute(\PDO::ATTR_SERVER_VERSION); + } +} diff --git a/vendor/symfony/cache/Adapter/PhpArrayAdapter.php b/vendor/symfony/cache/Adapter/PhpArrayAdapter.php new file mode 100644 index 0000000..ee6ce63 --- /dev/null +++ b/vendor/symfony/cache/Adapter/PhpArrayAdapter.php @@ -0,0 +1,419 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ContractsTrait; +use Symfony\Component\Cache\Traits\ProxyTrait; +use Symfony\Component\VarExporter\VarExporter; +use Symfony\Contracts\Cache\CacheInterface; + +/** + * Caches items at warm up time using a PHP array that is stored in shared memory by OPCache since PHP 7.0. + * Warmed up items are read-only and run-time discovered items are cached using a fallback adapter. + * + * @author Titouan Galopin + * @author Nicolas Grekas + */ +class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface +{ + use ContractsTrait; + use ProxyTrait; + + private string $file; + private array $keys; + private array $values; + + private static \Closure $createCacheItem; + private static array $valuesCache = []; + + /** + * @param string $file The PHP file were values are cached + * @param AdapterInterface $fallbackPool A pool to fallback on when an item is not hit + */ + public function __construct(string $file, AdapterInterface $fallbackPool) + { + $this->file = $file; + $this->pool = $fallbackPool; + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind( + static function ($key, $value, $isHit) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->isHit = $isHit; + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * This adapter takes advantage of how PHP stores arrays in its latest versions. + * + * @param string $file The PHP file were values are cached + * @param CacheItemPoolInterface $fallbackPool A pool to fallback on when an item is not hit + */ + public static function create(string $file, CacheItemPoolInterface $fallbackPool): CacheItemPoolInterface + { + if (!$fallbackPool instanceof AdapterInterface) { + $fallbackPool = new ProxyAdapter($fallbackPool); + } + + return new static($file, $fallbackPool); + } + + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, float $beta = null, array &$metadata = null): mixed + { + if (!isset($this->values)) { + $this->initialize(); + } + if (!isset($this->keys[$key])) { + get_from_pool: + if ($this->pool instanceof CacheInterface) { + return $this->pool->get($key, $callback, $beta, $metadata); + } + + return $this->doGet($this->pool, $key, $callback, $beta, $metadata); + } + $value = $this->values[$this->keys[$key]]; + + if ('N;' === $value) { + return null; + } + try { + if ($value instanceof \Closure) { + return $value(); + } + } catch (\Throwable $e) { + unset($this->keys[$key]); + goto get_from_pool; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getItem(mixed $key): CacheItem + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key))); + } + if (!isset($this->values)) { + $this->initialize(); + } + if (!isset($this->keys[$key])) { + return $this->pool->getItem($key); + } + + $value = $this->values[$this->keys[$key]]; + $isHit = true; + + if ('N;' === $value) { + $value = null; + } elseif ($value instanceof \Closure) { + try { + $value = $value(); + } catch (\Throwable $e) { + $value = null; + $isHit = false; + } + } + + return (self::$createCacheItem)($key, $value, $isHit); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []): iterable + { + foreach ($keys as $key) { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key))); + } + } + if (!isset($this->values)) { + $this->initialize(); + } + + return $this->generateItems($keys); + } + + /** + * {@inheritdoc} + */ + public function hasItem(mixed $key): bool + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key))); + } + if (!isset($this->values)) { + $this->initialize(); + } + + return isset($this->keys[$key]) || $this->pool->hasItem($key); + } + + /** + * {@inheritdoc} + */ + public function deleteItem(mixed $key): bool + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key))); + } + if (!isset($this->values)) { + $this->initialize(); + } + + return !isset($this->keys[$key]) && $this->pool->deleteItem($key); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys): bool + { + $deleted = true; + $fallbackKeys = []; + + foreach ($keys as $key) { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key))); + } + + if (isset($this->keys[$key])) { + $deleted = false; + } else { + $fallbackKeys[] = $key; + } + } + if (!isset($this->values)) { + $this->initialize(); + } + + if ($fallbackKeys) { + $deleted = $this->pool->deleteItems($fallbackKeys) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item): bool + { + if (!isset($this->values)) { + $this->initialize(); + } + + return !isset($this->keys[$item->getKey()]) && $this->pool->save($item); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item): bool + { + if (!isset($this->values)) { + $this->initialize(); + } + + return !isset($this->keys[$item->getKey()]) && $this->pool->saveDeferred($item); + } + + /** + * {@inheritdoc} + */ + public function commit(): bool + { + return $this->pool->commit(); + } + + /** + * {@inheritdoc} + */ + public function clear(string $prefix = ''): bool + { + $this->keys = $this->values = []; + + $cleared = @unlink($this->file) || !file_exists($this->file); + unset(self::$valuesCache[$this->file]); + + if ($this->pool instanceof AdapterInterface) { + return $this->pool->clear($prefix) && $cleared; + } + + return $this->pool->clear() && $cleared; + } + + /** + * Store an array of cached values. + * + * @param array $values The cached values + * + * @return string[] A list of classes to preload on PHP 7.4+ + */ + public function warmUp(array $values): array + { + if (file_exists($this->file)) { + if (!is_file($this->file)) { + throw new InvalidArgumentException(sprintf('Cache path exists and is not a file: "%s".', $this->file)); + } + + if (!is_writable($this->file)) { + throw new InvalidArgumentException(sprintf('Cache file is not writable: "%s".', $this->file)); + } + } else { + $directory = \dirname($this->file); + + if (!is_dir($directory) && !@mkdir($directory, 0777, true)) { + throw new InvalidArgumentException(sprintf('Cache directory does not exist and cannot be created: "%s".', $directory)); + } + + if (!is_writable($directory)) { + throw new InvalidArgumentException(sprintf('Cache directory is not writable: "%s".', $directory)); + } + } + + $preload = []; + $dumpedValues = ''; + $dumpedMap = []; + $dump = <<<'EOF' + $value) { + CacheItem::validateKey(\is_int($key) ? (string) $key : $key); + $isStaticValue = true; + + if (null === $value) { + $value = "'N;'"; + } elseif (\is_object($value) || \is_array($value)) { + try { + $value = VarExporter::export($value, $isStaticValue, $preload); + } catch (\Exception $e) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value)), 0, $e); + } + } elseif (\is_string($value)) { + // Wrap "N;" in a closure to not confuse it with an encoded `null` + if ('N;' === $value) { + $isStaticValue = false; + } + $value = var_export($value, true); + } elseif (!\is_scalar($value)) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value))); + } else { + $value = var_export($value, true); + } + + if (!$isStaticValue) { + $value = str_replace("\n", "\n ", $value); + $value = "static function () {\n return {$value};\n}"; + } + $hash = hash('md5', $value); + + if (null === $id = $dumpedMap[$hash] ?? null) { + $id = $dumpedMap[$hash] = \count($dumpedMap); + $dumpedValues .= "{$id} => {$value},\n"; + } + + $dump .= var_export($key, true)." => {$id},\n"; + } + + $dump .= "\n], [\n\n{$dumpedValues}\n]];\n"; + + $tmpFile = uniqid($this->file, true); + + file_put_contents($tmpFile, $dump); + @chmod($tmpFile, 0666 & ~umask()); + unset($serialized, $value, $dump); + + @rename($tmpFile, $this->file); + unset(self::$valuesCache[$this->file]); + + $this->initialize(); + + return $preload; + } + + /** + * Load the cache file. + */ + private function initialize() + { + if (isset(self::$valuesCache[$this->file])) { + $values = self::$valuesCache[$this->file]; + } elseif (!is_file($this->file)) { + $this->keys = $this->values = []; + + return; + } else { + $values = self::$valuesCache[$this->file] = (include $this->file) ?: [[], []]; + } + + if (2 !== \count($values) || !isset($values[0], $values[1])) { + $this->keys = $this->values = []; + } else { + [$this->keys, $this->values] = $values; + } + } + + private function generateItems(array $keys): \Generator + { + $f = self::$createCacheItem; + $fallbackKeys = []; + + foreach ($keys as $key) { + if (isset($this->keys[$key])) { + $value = $this->values[$this->keys[$key]]; + + if ('N;' === $value) { + yield $key => $f($key, null, true); + } elseif ($value instanceof \Closure) { + try { + yield $key => $f($key, $value(), true); + } catch (\Throwable $e) { + yield $key => $f($key, null, false); + } + } else { + yield $key => $f($key, $value, true); + } + } else { + $fallbackKeys[] = $key; + } + } + + if ($fallbackKeys) { + yield from $this->pool->getItems($fallbackKeys); + } + } +} diff --git a/vendor/symfony/cache/Adapter/PhpFilesAdapter.php b/vendor/symfony/cache/Adapter/PhpFilesAdapter.php new file mode 100644 index 0000000..bd9792d --- /dev/null +++ b/vendor/symfony/cache/Adapter/PhpFilesAdapter.php @@ -0,0 +1,327 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\FilesystemCommonTrait; +use Symfony\Component\VarExporter\VarExporter; + +/** + * @author Piotr Stankowski + * @author Nicolas Grekas + * @author Rob Frawley 2nd + */ +class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface +{ + use FilesystemCommonTrait { + doClear as private doCommonClear; + doDelete as private doCommonDelete; + } + + private \Closure $includeHandler; + private bool $appendOnly; + private array $values = []; + private array $files = []; + + private static int $startTime; + private static array $valuesCache = []; + + /** + * @param $appendOnly Set to `true` to gain extra performance when the items stored in this pool never expire. + * Doing so is encouraged because it fits perfectly OPcache's memory model. + * + * @throws CacheException if OPcache is not enabled + */ + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, bool $appendOnly = false) + { + $this->appendOnly = $appendOnly; + self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time(); + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + $this->includeHandler = static function ($type, $msg, $file, $line) { + throw new \ErrorException($msg, 0, $type, $file, $line); + }; + } + + public static function isSupported() + { + self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time(); + + return \function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN)); + } + + public function prune(): bool + { + $time = time(); + $pruned = true; + $getExpiry = true; + + set_error_handler($this->includeHandler); + try { + foreach ($this->scanHashDir($this->directory) as $file) { + try { + if (\is_array($expiresAt = include $file)) { + $expiresAt = $expiresAt[0]; + } + } catch (\ErrorException $e) { + $expiresAt = $time; + } + + if ($time >= $expiresAt) { + $pruned = $this->doUnlink($file) && !file_exists($file) && $pruned; + } + } + } finally { + restore_error_handler(); + } + + return $pruned; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids): iterable + { + if ($this->appendOnly) { + $now = 0; + $missingIds = []; + } else { + $now = time(); + $missingIds = $ids; + $ids = []; + } + $values = []; + + begin: + $getExpiry = false; + + foreach ($ids as $id) { + if (null === $value = $this->values[$id] ?? null) { + $missingIds[] = $id; + } elseif ('N;' === $value) { + $values[$id] = null; + } elseif (!\is_object($value)) { + $values[$id] = $value; + } elseif (!$value instanceof LazyValue) { + $values[$id] = $value(); + } elseif (false === $values[$id] = include $value->file) { + unset($values[$id], $this->values[$id]); + $missingIds[] = $id; + } + if (!$this->appendOnly) { + unset($this->values[$id]); + } + } + + if (!$missingIds) { + return $values; + } + + set_error_handler($this->includeHandler); + try { + $getExpiry = true; + + foreach ($missingIds as $k => $id) { + try { + $file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id); + + if (isset(self::$valuesCache[$file])) { + [$expiresAt, $this->values[$id]] = self::$valuesCache[$file]; + } elseif (\is_array($expiresAt = include $file)) { + if ($this->appendOnly) { + self::$valuesCache[$file] = $expiresAt; + } + + [$expiresAt, $this->values[$id]] = $expiresAt; + } elseif ($now < $expiresAt) { + $this->values[$id] = new LazyValue($file); + } + + if ($now >= $expiresAt) { + unset($this->values[$id], $missingIds[$k], self::$valuesCache[$file]); + } + } catch (\ErrorException $e) { + unset($missingIds[$k]); + } + } + } finally { + restore_error_handler(); + } + + $ids = $missingIds; + $missingIds = []; + goto begin; + } + + /** + * {@inheritdoc} + */ + protected function doHave(string $id): bool + { + if ($this->appendOnly && isset($this->values[$id])) { + return true; + } + + set_error_handler($this->includeHandler); + try { + $file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id); + $getExpiry = true; + + if (isset(self::$valuesCache[$file])) { + [$expiresAt, $value] = self::$valuesCache[$file]; + } elseif (\is_array($expiresAt = include $file)) { + if ($this->appendOnly) { + self::$valuesCache[$file] = $expiresAt; + } + + [$expiresAt, $value] = $expiresAt; + } elseif ($this->appendOnly) { + $value = new LazyValue($file); + } + } catch (\ErrorException $e) { + return false; + } finally { + restore_error_handler(); + } + if ($this->appendOnly) { + $now = 0; + $this->values[$id] = $value; + } else { + $now = time(); + } + + return $now < $expiresAt; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime): array|bool + { + $ok = true; + $expiry = $lifetime ? time() + $lifetime : 'PHP_INT_MAX'; + $allowCompile = self::isSupported(); + + foreach ($values as $key => $value) { + unset($this->values[$key]); + $isStaticValue = true; + if (null === $value) { + $value = "'N;'"; + } elseif (\is_object($value) || \is_array($value)) { + try { + $value = VarExporter::export($value, $isStaticValue); + } catch (\Exception $e) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value)), 0, $e); + } + } elseif (\is_string($value)) { + // Wrap "N;" in a closure to not confuse it with an encoded `null` + if ('N;' === $value) { + $isStaticValue = false; + } + $value = var_export($value, true); + } elseif (!\is_scalar($value)) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value))); + } else { + $value = var_export($value, true); + } + + $encodedKey = rawurlencode($key); + + if ($isStaticValue) { + $value = "return [{$expiry}, {$value}];"; + } elseif ($this->appendOnly) { + $value = "return [{$expiry}, static function () { return {$value}; }];"; + } else { + // We cannot use a closure here because of https://bugs.php.net/76982 + $value = str_replace('\Symfony\Component\VarExporter\Internal\\', '', $value); + $value = "namespace Symfony\Component\VarExporter\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};"; + } + + $file = $this->files[$key] = $this->getFile($key, true); + // Since OPcache only compiles files older than the script execution start, set the file's mtime in the past + $ok = $this->write($file, "directory)) { + throw new CacheException(sprintf('Cache directory is not writable (%s).', $this->directory)); + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace): bool + { + $this->values = []; + + return $this->doCommonClear($namespace); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + foreach ($ids as $id) { + unset($this->values[$id]); + } + + return $this->doCommonDelete($ids); + } + + protected function doUnlink(string $file) + { + unset(self::$valuesCache[$file]); + + if (self::isSupported()) { + @opcache_invalidate($file, true); + } + + return @unlink($file); + } + + private function getFileKey(string $file): string + { + if (!$h = @fopen($file, 'r')) { + return ''; + } + + $encodedKey = substr(fgets($h), 8); + fclose($h); + + return rawurldecode(rtrim($encodedKey)); + } +} + +/** + * @internal + */ +class LazyValue +{ + public string $file; + + public function __construct(string $file) + { + $this->file = $file; + } +} diff --git a/vendor/symfony/cache/Adapter/ProxyAdapter.php b/vendor/symfony/cache/Adapter/ProxyAdapter.php new file mode 100644 index 0000000..23434cd --- /dev/null +++ b/vendor/symfony/cache/Adapter/ProxyAdapter.php @@ -0,0 +1,254 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ContractsTrait; +use Symfony\Component\Cache\Traits\ProxyTrait; +use Symfony\Contracts\Cache\CacheInterface; + +/** + * @author Nicolas Grekas + */ +class ProxyAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface +{ + use ContractsTrait; + use ProxyTrait; + + private string $namespace = ''; + private int $namespaceLen; + private string $poolHash; + private int $defaultLifetime; + + private static \Closure $createCacheItem; + private static \Closure $setInnerItem; + + public function __construct(CacheItemPoolInterface $pool, string $namespace = '', int $defaultLifetime = 0) + { + $this->pool = $pool; + $this->poolHash = spl_object_hash($pool); + if ('' !== $namespace) { + \assert('' !== CacheItem::validateKey($namespace)); + $this->namespace = $namespace; + } + $this->namespaceLen = \strlen($namespace); + $this->defaultLifetime = $defaultLifetime; + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind( + static function ($key, $innerItem, $poolHash) { + $item = new CacheItem(); + $item->key = $key; + + if (null === $innerItem) { + return $item; + } + + $item->value = $v = $innerItem->get(); + $item->isHit = $innerItem->isHit(); + $item->innerItem = $innerItem; + $item->poolHash = $poolHash; + + // Detect wrapped values that encode for their expiry and creation duration + // For compactness, these values are packed in the key of an array using + // magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F + if (\is_array($v) && 1 === \count($v) && 10 === \strlen($k = (string) array_key_first($v)) && "\x9D" === $k[0] && "\0" === $k[5] && "\x5F" === $k[9]) { + $item->value = $v[$k]; + $v = unpack('Ve/Nc', substr($k, 1, -1)); + $item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET; + $item->metadata[CacheItem::METADATA_CTIME] = $v['c']; + } elseif ($innerItem instanceof CacheItem) { + $item->metadata = $innerItem->metadata; + } + $innerItem->set(null); + + return $item; + }, + null, + CacheItem::class + ); + self::$setInnerItem ?? self::$setInnerItem = \Closure::bind( + /** + * @param array $item A CacheItem cast to (array); accessing protected properties requires adding the "\0*\0" PHP prefix + */ + static function (CacheItemInterface $innerItem, array $item) { + // Tags are stored separately, no need to account for them when considering this item's newly set metadata + if (isset(($metadata = $item["\0*\0newMetadata"])[CacheItem::METADATA_TAGS])) { + unset($metadata[CacheItem::METADATA_TAGS]); + } + if ($metadata) { + // For compactness, expiry and creation duration are packed in the key of an array, using magic numbers as separators + $item["\0*\0value"] = ["\x9D".pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME])."\x5F" => $item["\0*\0value"]]; + } + $innerItem->set($item["\0*\0value"]); + $innerItem->expiresAt(null !== $item["\0*\0expiry"] ? \DateTime::createFromFormat('U.u', sprintf('%.6F', $item["\0*\0expiry"])) : null); + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, float $beta = null, array &$metadata = null): mixed + { + if (!$this->pool instanceof CacheInterface) { + return $this->doGet($this, $key, $callback, $beta, $metadata); + } + + return $this->pool->get($this->getId($key), function ($innerItem, bool &$save) use ($key, $callback) { + $item = (self::$createCacheItem)($key, $innerItem, $this->poolHash); + $item->set($value = $callback($item, $save)); + (self::$setInnerItem)($innerItem, (array) $item); + + return $value; + }, $beta, $metadata); + } + + /** + * {@inheritdoc} + */ + public function getItem(mixed $key): CacheItem + { + $item = $this->pool->getItem($this->getId($key)); + + return (self::$createCacheItem)($key, $item, $this->poolHash); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []): iterable + { + if ($this->namespaceLen) { + foreach ($keys as $i => $key) { + $keys[$i] = $this->getId($key); + } + } + + return $this->generateItems($this->pool->getItems($keys)); + } + + /** + * {@inheritdoc} + */ + public function hasItem(mixed $key): bool + { + return $this->pool->hasItem($this->getId($key)); + } + + /** + * {@inheritdoc} + */ + public function clear(string $prefix = ''): bool + { + if ($this->pool instanceof AdapterInterface) { + return $this->pool->clear($this->namespace.$prefix); + } + + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + public function deleteItem(mixed $key): bool + { + return $this->pool->deleteItem($this->getId($key)); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys): bool + { + if ($this->namespaceLen) { + foreach ($keys as $i => $key) { + $keys[$i] = $this->getId($key); + } + } + + return $this->pool->deleteItems($keys); + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item): bool + { + return $this->doSave($item, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item): bool + { + return $this->doSave($item, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function commit(): bool + { + return $this->pool->commit(); + } + + private function doSave(CacheItemInterface $item, string $method): bool + { + if (!$item instanceof CacheItem) { + return false; + } + $item = (array) $item; + if (null === $item["\0*\0expiry"] && 0 < $this->defaultLifetime) { + $item["\0*\0expiry"] = microtime(true) + $this->defaultLifetime; + } + + if ($item["\0*\0poolHash"] === $this->poolHash && $item["\0*\0innerItem"]) { + $innerItem = $item["\0*\0innerItem"]; + } elseif ($this->pool instanceof AdapterInterface) { + // this is an optimization specific for AdapterInterface implementations + // so we can save a round-trip to the backend by just creating a new item + $innerItem = (self::$createCacheItem)($this->namespace.$item["\0*\0key"], null, $this->poolHash); + } else { + $innerItem = $this->pool->getItem($this->namespace.$item["\0*\0key"]); + } + + (self::$setInnerItem)($innerItem, $item); + + return $this->pool->$method($innerItem); + } + + private function generateItems(iterable $items): \Generator + { + $f = self::$createCacheItem; + + foreach ($items as $key => $item) { + if ($this->namespaceLen) { + $key = substr($key, $this->namespaceLen); + } + + yield $key => $f($key, $item, $this->poolHash); + } + } + + private function getId(mixed $key): string + { + \assert('' !== CacheItem::validateKey($key)); + + return $this->namespace.$key; + } +} diff --git a/vendor/symfony/cache/Adapter/Psr16Adapter.php b/vendor/symfony/cache/Adapter/Psr16Adapter.php new file mode 100644 index 0000000..664c18b --- /dev/null +++ b/vendor/symfony/cache/Adapter/Psr16Adapter.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ProxyTrait; + +/** + * Turns a PSR-16 cache into a PSR-6 one. + * + * @author Nicolas Grekas + */ +class Psr16Adapter extends AbstractAdapter implements PruneableInterface, ResettableInterface +{ + use ProxyTrait; + + /** + * @internal + */ + protected const NS_SEPARATOR = '_'; + + private object $miss; + + public function __construct(CacheInterface $pool, string $namespace = '', int $defaultLifetime = 0) + { + parent::__construct($namespace, $defaultLifetime); + + $this->pool = $pool; + $this->miss = new \stdClass(); + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids): iterable + { + foreach ($this->pool->getMultiple($ids, $this->miss) as $key => $value) { + if ($this->miss !== $value) { + yield $key => $value; + } + } + } + + /** + * {@inheritdoc} + */ + protected function doHave(string $id): bool + { + return $this->pool->has($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace): bool + { + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + return $this->pool->deleteMultiple($ids); + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime): array|bool + { + return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime); + } +} diff --git a/vendor/symfony/cache/Adapter/RedisAdapter.php b/vendor/symfony/cache/Adapter/RedisAdapter.php new file mode 100644 index 0000000..45bc340 --- /dev/null +++ b/vendor/symfony/cache/Adapter/RedisAdapter.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\Marshaller\MarshallerInterface; +use Symfony\Component\Cache\Traits\RedisClusterProxy; +use Symfony\Component\Cache\Traits\RedisProxy; +use Symfony\Component\Cache\Traits\RedisTrait; + +class RedisAdapter extends AbstractAdapter +{ + use RedisTrait; + + public function __construct(\Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null) + { + $this->init($redis, $namespace, $defaultLifetime, $marshaller); + } +} diff --git a/vendor/symfony/cache/Adapter/RedisTagAwareAdapter.php b/vendor/symfony/cache/Adapter/RedisTagAwareAdapter.php new file mode 100644 index 0000000..7451592 --- /dev/null +++ b/vendor/symfony/cache/Adapter/RedisTagAwareAdapter.php @@ -0,0 +1,320 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Predis\Connection\Aggregate\ClusterInterface; +use Predis\Connection\Aggregate\PredisCluster; +use Predis\Connection\Aggregate\ReplicationInterface; +use Predis\Response\ErrorInterface; +use Predis\Response\Status; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Exception\LogicException; +use Symfony\Component\Cache\Marshaller\DeflateMarshaller; +use Symfony\Component\Cache\Marshaller\MarshallerInterface; +use Symfony\Component\Cache\Marshaller\TagAwareMarshaller; +use Symfony\Component\Cache\Traits\RedisClusterProxy; +use Symfony\Component\Cache\Traits\RedisProxy; +use Symfony\Component\Cache\Traits\RedisTrait; + +/** + * Stores tag id <> cache id relationship as a Redis Set. + * + * Set (tag relation info) is stored without expiry (non-volatile), while cache always gets an expiry (volatile) even + * if not set by caller. Thus if you configure redis with the right eviction policy you can be safe this tag <> cache + * relationship survives eviction (cache cleanup when Redis runs out of memory). + * + * Redis server 2.8+ with any `volatile-*` eviction policy, OR `noeviction` if you're sure memory will NEVER fill up + * + * Design limitations: + * - Max 4 billion cache keys per cache tag as limited by Redis Set datatype. + * E.g. If you use a "all" items tag for expiry instead of clear(), that limits you to 4 billion cache items also. + * + * @see https://redis.io/topics/lru-cache#eviction-policies Documentation for Redis eviction policies. + * @see https://redis.io/topics/data-types#sets Documentation for Redis Set datatype. + * + * @author Nicolas Grekas + * @author André Rømcke + */ +class RedisTagAwareAdapter extends AbstractTagAwareAdapter +{ + use RedisTrait; + + /** + * On cache items without a lifetime set, we set it to 100 days. This is to make sure cache items are + * preferred to be evicted over tag Sets, if eviction policy is configured according to requirements. + */ + private const DEFAULT_CACHE_TTL = 8640000; + + /** + * detected eviction policy used on Redis server. + */ + private string $redisEvictionPolicy; + private string $namespace; + + public function __construct(\Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null) + { + if ($redis instanceof \Predis\ClientInterface && $redis->getConnection() instanceof ClusterInterface && !$redis->getConnection() instanceof PredisCluster) { + throw new InvalidArgumentException(sprintf('Unsupported Predis cluster connection: only "%s" is, "%s" given.', PredisCluster::class, get_debug_type($redis->getConnection()))); + } + + if (\defined('Redis::OPT_COMPRESSION') && ($redis instanceof \Redis || $redis instanceof \RedisArray || $redis instanceof \RedisCluster)) { + $compression = $redis->getOption(\Redis::OPT_COMPRESSION); + + foreach (\is_array($compression) ? $compression : [$compression] as $c) { + if (\Redis::COMPRESSION_NONE !== $c) { + throw new InvalidArgumentException(sprintf('phpredis compression must be disabled when using "%s", use "%s" instead.', static::class, DeflateMarshaller::class)); + } + } + } + + $this->init($redis, $namespace, $defaultLifetime, new TagAwareMarshaller($marshaller)); + $this->namespace = $namespace; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime, array $addTagData = [], array $delTagData = []): array + { + $eviction = $this->getRedisEvictionPolicy(); + if ('noeviction' !== $eviction && !str_starts_with($eviction, 'volatile-')) { + throw new LogicException(sprintf('Redis maxmemory-policy setting "%s" is *not* supported by RedisTagAwareAdapter, use "noeviction" or "volatile-*" eviction policies.', $eviction)); + } + + // serialize values + if (!$serialized = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + + // While pipeline isn't supported on RedisCluster, other setups will at least benefit from doing this in one op + $results = $this->pipeline(static function () use ($serialized, $lifetime, $addTagData, $delTagData, $failed) { + // Store cache items, force a ttl if none is set, as there is no MSETEX we need to set each one + foreach ($serialized as $id => $value) { + yield 'setEx' => [ + $id, + 0 >= $lifetime ? self::DEFAULT_CACHE_TTL : $lifetime, + $value, + ]; + } + + // Add and Remove Tags + foreach ($addTagData as $tagId => $ids) { + if (!$failed || $ids = array_diff($ids, $failed)) { + yield 'sAdd' => array_merge([$tagId], $ids); + } + } + + foreach ($delTagData as $tagId => $ids) { + if (!$failed || $ids = array_diff($ids, $failed)) { + yield 'sRem' => array_merge([$tagId], $ids); + } + } + }); + + foreach ($results as $id => $result) { + // Skip results of SADD/SREM operations, they'll be 1 or 0 depending on if set value already existed or not + if (is_numeric($result)) { + continue; + } + // setEx results + if (true !== $result && (!$result instanceof Status || Status::get('OK') !== $result)) { + $failed[] = $id; + } + } + + return $failed; + } + + /** + * {@inheritdoc} + */ + protected function doDeleteYieldTags(array $ids): iterable + { + $lua = <<<'EOLUA' + local v = redis.call('GET', KEYS[1]) + local e = redis.pcall('UNLINK', KEYS[1]) + + if type(e) ~= 'number' then + redis.call('DEL', KEYS[1]) + end + + if not v or v:len() <= 13 or v:byte(1) ~= 0x9D or v:byte(6) ~= 0 or v:byte(10) ~= 0x5F then + return '' + end + + return v:sub(14, 13 + v:byte(13) + v:byte(12) * 256 + v:byte(11) * 65536) +EOLUA; + + $results = $this->pipeline(function () use ($ids, $lua) { + foreach ($ids as $id) { + yield 'eval' => $this->redis instanceof \Predis\ClientInterface ? [$lua, 1, $id] : [$lua, [$id], 1]; + } + }); + + foreach ($results as $id => $result) { + if ($result instanceof \RedisException || $result instanceof ErrorInterface) { + CacheItem::log($this->logger, 'Failed to delete key "{key}": '.$result->getMessage(), ['key' => substr($id, \strlen($this->namespace)), 'exception' => $result]); + + continue; + } + + try { + yield $id => !\is_string($result) || '' === $result ? [] : $this->marshaller->unmarshall($result); + } catch (\Exception $e) { + yield $id => []; + } + } + } + + /** + * {@inheritdoc} + */ + protected function doDeleteTagRelations(array $tagData): bool + { + $results = $this->pipeline(static function () use ($tagData) { + foreach ($tagData as $tagId => $idList) { + array_unshift($idList, $tagId); + yield 'sRem' => $idList; + } + }); + foreach ($results as $result) { + // no-op + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doInvalidate(array $tagIds): bool + { + // This script scans the set of items linked to tag: it empties the set + // and removes the linked items. When the set is still not empty after + // the scan, it means we're in cluster mode and that the linked items + // are on other nodes: we move the links to a temporary set and we + // garbage collect that set from the client side. + + $lua = <<<'EOLUA' + redis.replicate_commands() + + local cursor = '0' + local id = KEYS[1] + repeat + local result = redis.call('SSCAN', id, cursor, 'COUNT', 5000); + cursor = result[1]; + local rems = {} + + for _, v in ipairs(result[2]) do + local ok, _ = pcall(redis.call, 'DEL', ARGV[1]..v) + if ok then + table.insert(rems, v) + end + end + if 0 < #rems then + redis.call('SREM', id, unpack(rems)) + end + until '0' == cursor; + + redis.call('SUNIONSTORE', '{'..id..'}'..id, id) + redis.call('DEL', id) + + return redis.call('SSCAN', '{'..id..'}'..id, '0', 'COUNT', 5000) +EOLUA; + + $results = $this->pipeline(function () use ($tagIds, $lua) { + if ($this->redis instanceof \Predis\ClientInterface) { + $prefix = $this->redis->getOptions()->prefix ? $this->redis->getOptions()->prefix->getPrefix() : ''; + } elseif (\is_array($prefix = $this->redis->getOption(\Redis::OPT_PREFIX) ?? '')) { + $prefix = current($prefix); + } + + foreach ($tagIds as $id) { + yield 'eval' => $this->redis instanceof \Predis\ClientInterface ? [$lua, 1, $id, $prefix] : [$lua, [$id, $prefix], 1]; + } + }); + + $lua = <<<'EOLUA' + redis.replicate_commands() + + local id = KEYS[1] + local cursor = table.remove(ARGV) + redis.call('SREM', '{'..id..'}'..id, unpack(ARGV)) + + return redis.call('SSCAN', '{'..id..'}'..id, cursor, 'COUNT', 5000) +EOLUA; + + $success = true; + foreach ($results as $id => $values) { + if ($values instanceof \RedisException || $values instanceof ErrorInterface) { + CacheItem::log($this->logger, 'Failed to invalidate key "{key}": '.$values->getMessage(), ['key' => substr($id, \strlen($this->namespace)), 'exception' => $values]); + $success = false; + + continue; + } + + [$cursor, $ids] = $values; + + while ($ids || '0' !== $cursor) { + $this->doDelete($ids); + + $evalArgs = [$id, $cursor]; + array_splice($evalArgs, 1, 0, $ids); + + if ($this->redis instanceof \Predis\ClientInterface) { + array_unshift($evalArgs, $lua, 1); + } else { + $evalArgs = [$lua, $evalArgs, 1]; + } + + $results = $this->pipeline(function () use ($evalArgs) { + yield 'eval' => $evalArgs; + }); + + foreach ($results as [$cursor, $ids]) { + // no-op + } + } + } + + return $success; + } + + private function getRedisEvictionPolicy(): string + { + if (isset($this->redisEvictionPolicy)) { + return $this->redisEvictionPolicy; + } + + $hosts = $this->getHosts(); + $host = reset($hosts); + if ($host instanceof \Predis\Client && $host->getConnection() instanceof ReplicationInterface) { + // Predis supports info command only on the master in replication environments + $hosts = [$host->getClientFor('master')]; + } + + foreach ($hosts as $host) { + $info = $host->info('Memory'); + + if ($info instanceof ErrorInterface) { + continue; + } + + $info = $info['Memory'] ?? $info; + + return $this->redisEvictionPolicy = $info['maxmemory_policy']; + } + + return $this->redisEvictionPolicy = ''; + } +} diff --git a/vendor/symfony/cache/Adapter/TagAwareAdapter.php b/vendor/symfony/cache/Adapter/TagAwareAdapter.php new file mode 100644 index 0000000..a6f66ee --- /dev/null +++ b/vendor/symfony/cache/Adapter/TagAwareAdapter.php @@ -0,0 +1,409 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\InvalidArgumentException; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ContractsTrait; +use Symfony\Component\Cache\Traits\ProxyTrait; +use Symfony\Contracts\Cache\TagAwareCacheInterface; + +/** + * @author Nicolas Grekas + */ +class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, PruneableInterface, ResettableInterface, LoggerAwareInterface +{ + use ContractsTrait; + use LoggerAwareTrait; + use ProxyTrait; + + public const TAGS_PREFIX = "\0tags\0"; + + private array $deferred = []; + private $tags; + private array $knownTagVersions = []; + private float $knownTagVersionsTtl; + + private static \Closure $createCacheItem; + private static \Closure $setCacheItemTags; + private static \Closure $getTagsByKey; + private static \Closure $saveTags; + + public function __construct(AdapterInterface $itemsPool, AdapterInterface $tagsPool = null, float $knownTagVersionsTtl = 0.15) + { + $this->pool = $itemsPool; + $this->tags = $tagsPool ?? $itemsPool; + $this->knownTagVersionsTtl = $knownTagVersionsTtl; + self::$createCacheItem ?? self::$createCacheItem = \Closure::bind( + static function ($key, $value, CacheItem $protoItem) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->expiry = $protoItem->expiry; + $item->poolHash = $protoItem->poolHash; + + return $item; + }, + null, + CacheItem::class + ); + self::$setCacheItemTags ?? self::$setCacheItemTags = \Closure::bind( + static function (CacheItem $item, $key, array &$itemTags) { + $item->isTaggable = true; + if (!$item->isHit) { + return $item; + } + if (isset($itemTags[$key])) { + foreach ($itemTags[$key] as $tag => $version) { + $item->metadata[CacheItem::METADATA_TAGS][$tag] = $tag; + } + unset($itemTags[$key]); + } else { + $item->value = null; + $item->isHit = false; + } + + return $item; + }, + null, + CacheItem::class + ); + self::$getTagsByKey ?? self::$getTagsByKey = \Closure::bind( + static function ($deferred) { + $tagsByKey = []; + foreach ($deferred as $key => $item) { + $tagsByKey[$key] = $item->newMetadata[CacheItem::METADATA_TAGS] ?? []; + $item->metadata = $item->newMetadata; + } + + return $tagsByKey; + }, + null, + CacheItem::class + ); + self::$saveTags ?? self::$saveTags = \Closure::bind( + static function (AdapterInterface $tagsAdapter, array $tags) { + ksort($tags); + + foreach ($tags as $v) { + $v->expiry = 0; + $tagsAdapter->saveDeferred($v); + } + + return $tagsAdapter->commit(); + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags): bool + { + $ids = []; + foreach ($tags as $tag) { + \assert('' !== CacheItem::validateKey($tag)); + unset($this->knownTagVersions[$tag]); + $ids[] = $tag.static::TAGS_PREFIX; + } + + return !$tags || $this->tags->deleteItems($ids); + } + + /** + * {@inheritdoc} + */ + public function hasItem(mixed $key): bool + { + if (\is_string($key) && isset($this->deferred[$key])) { + $this->commit(); + } + + if (!$this->pool->hasItem($key)) { + return false; + } + + $itemTags = $this->pool->getItem(static::TAGS_PREFIX.$key); + + if (!$itemTags->isHit()) { + return false; + } + + if (!$itemTags = $itemTags->get()) { + return true; + } + + foreach ($this->getTagVersions([$itemTags]) as $tag => $version) { + if ($itemTags[$tag] !== $version) { + return false; + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getItem(mixed $key): CacheItem + { + foreach ($this->getItems([$key]) as $item) { + return $item; + } + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []): iterable + { + $tagKeys = []; + $commit = false; + + foreach ($keys as $key) { + if ('' !== $key && \is_string($key)) { + $commit = $commit || isset($this->deferred[$key]); + $key = static::TAGS_PREFIX.$key; + $tagKeys[$key] = $key; + } + } + + if ($commit) { + $this->commit(); + } + + try { + $items = $this->pool->getItems($tagKeys + $keys); + } catch (InvalidArgumentException $e) { + $this->pool->getItems($keys); // Should throw an exception + + throw $e; + } + + return $this->generateItems($items, $tagKeys); + } + + /** + * {@inheritdoc} + */ + public function clear(string $prefix = ''): bool + { + if ('' !== $prefix) { + foreach ($this->deferred as $key => $item) { + if (str_starts_with($key, $prefix)) { + unset($this->deferred[$key]); + } + } + } else { + $this->deferred = []; + } + + if ($this->pool instanceof AdapterInterface) { + return $this->pool->clear($prefix); + } + + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + public function deleteItem(mixed $key): bool + { + return $this->deleteItems([$key]); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys): bool + { + foreach ($keys as $key) { + if ('' !== $key && \is_string($key)) { + $keys[] = static::TAGS_PREFIX.$key; + } + } + + return $this->pool->deleteItems($keys); + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item): bool + { + if (!$item instanceof CacheItem) { + return false; + } + $this->deferred[$item->getKey()] = $item; + + return $this->commit(); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item): bool + { + if (!$item instanceof CacheItem) { + return false; + } + $this->deferred[$item->getKey()] = $item; + + return true; + } + + /** + * {@inheritdoc} + */ + public function commit(): bool + { + if (!$this->deferred) { + return true; + } + + $ok = true; + foreach ($this->deferred as $key => $item) { + if (!$this->pool->saveDeferred($item)) { + unset($this->deferred[$key]); + $ok = false; + } + } + + $items = $this->deferred; + $tagsByKey = (self::$getTagsByKey)($items); + $this->deferred = []; + + $tagVersions = $this->getTagVersions($tagsByKey); + $f = self::$createCacheItem; + + foreach ($tagsByKey as $key => $tags) { + $this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key])); + } + + return $this->pool->commit() && $ok; + } + + public function __sleep(): array + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + + public function __destruct() + { + $this->commit(); + } + + private function generateItems(iterable $items, array $tagKeys): \Generator + { + $bufferedItems = $itemTags = []; + $f = self::$setCacheItemTags; + + foreach ($items as $key => $item) { + if (!$tagKeys) { + yield $key => $f($item, static::TAGS_PREFIX.$key, $itemTags); + continue; + } + if (!isset($tagKeys[$key])) { + $bufferedItems[$key] = $item; + continue; + } + + unset($tagKeys[$key]); + + if ($item->isHit()) { + $itemTags[$key] = $item->get() ?: []; + } + + if (!$tagKeys) { + $tagVersions = $this->getTagVersions($itemTags); + + foreach ($itemTags as $key => $tags) { + foreach ($tags as $tag => $version) { + if ($tagVersions[$tag] !== $version) { + unset($itemTags[$key]); + continue 2; + } + } + } + $tagVersions = $tagKeys = null; + + foreach ($bufferedItems as $key => $item) { + yield $key => $f($item, static::TAGS_PREFIX.$key, $itemTags); + } + $bufferedItems = null; + } + } + } + + private function getTagVersions(array $tagsByKey): array + { + $tagVersions = []; + $fetchTagVersions = false; + + foreach ($tagsByKey as $tags) { + $tagVersions += $tags; + + foreach ($tags as $tag => $version) { + if ($tagVersions[$tag] !== $version) { + unset($this->knownTagVersions[$tag]); + } + } + } + + if (!$tagVersions) { + return []; + } + + $now = microtime(true); + $tags = []; + foreach ($tagVersions as $tag => $version) { + $tags[$tag.static::TAGS_PREFIX] = $tag; + if ($fetchTagVersions || ($this->knownTagVersions[$tag][1] ?? null) !== $version || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) { + // reuse previously fetched tag versions up to the ttl + $fetchTagVersions = true; + } + } + + if (!$fetchTagVersions) { + return $tagVersions; + } + + $newTags = []; + $newVersion = null; + foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) { + if (!$version->isHit()) { + $newTags[$tag] = $version->set($newVersion ?? $newVersion = random_int(\PHP_INT_MIN, \PHP_INT_MAX)); + } + $tagVersions[$tag = $tags[$tag]] = $version->get(); + $this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]]; + } + + if ($newTags) { + (self::$saveTags)($this->tags, $newTags); + } + + return $tagVersions; + } +} diff --git a/vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php b/vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php new file mode 100644 index 0000000..9242779 --- /dev/null +++ b/vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\InvalidArgumentException; + +/** + * Interface for invalidating cached items using tags. + * + * @author Nicolas Grekas + */ +interface TagAwareAdapterInterface extends AdapterInterface +{ + /** + * Invalidates cached items using tags. + * + * @param string[] $tags An array of tags to invalidate + * + * @throws InvalidArgumentException When $tags is not valid + */ + public function invalidateTags(array $tags): bool; +} diff --git a/vendor/symfony/cache/Adapter/TraceableAdapter.php b/vendor/symfony/cache/Adapter/TraceableAdapter.php new file mode 100644 index 0000000..0b577aa --- /dev/null +++ b/vendor/symfony/cache/Adapter/TraceableAdapter.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Contracts\Cache\CacheInterface; +use Symfony\Contracts\Service\ResetInterface; + +/** + * An adapter that collects data about all cache calls. + * + * @author Aaron Scherer + * @author Tobias Nyholm + * @author Nicolas Grekas + */ +class TraceableAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface +{ + protected $pool; + private array $calls = []; + + public function __construct(AdapterInterface $pool) + { + $this->pool = $pool; + } + + /** + * {@inheritdoc} + */ + public function get(string $key, callable $callback, float $beta = null, array &$metadata = null): mixed + { + if (!$this->pool instanceof CacheInterface) { + throw new \BadMethodCallException(sprintf('Cannot call "%s::get()": this class doesn\'t implement "%s".', get_debug_type($this->pool), CacheInterface::class)); + } + + $isHit = true; + $callback = function (CacheItem $item, bool &$save) use ($callback, &$isHit) { + $isHit = $item->isHit(); + + return $callback($item, $save); + }; + + $event = $this->start(__FUNCTION__); + try { + $value = $this->pool->get($key, $callback, $beta, $metadata); + $event->result[$key] = get_debug_type($value); + } finally { + $event->end = microtime(true); + } + if ($isHit) { + ++$event->hits; + } else { + ++$event->misses; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getItem(mixed $key): CacheItem + { + $event = $this->start(__FUNCTION__); + try { + $item = $this->pool->getItem($key); + } finally { + $event->end = microtime(true); + } + if ($event->result[$key] = $item->isHit()) { + ++$event->hits; + } else { + ++$event->misses; + } + + return $item; + } + + /** + * {@inheritdoc} + */ + public function hasItem(mixed $key): bool + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->hasItem($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function deleteItem(mixed $key): bool + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->deleteItem($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item): bool + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$item->getKey()] = $this->pool->save($item); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item): bool + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$item->getKey()] = $this->pool->saveDeferred($item); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []): iterable + { + $event = $this->start(__FUNCTION__); + try { + $result = $this->pool->getItems($keys); + } finally { + $event->end = microtime(true); + } + $f = function () use ($result, $event) { + $event->result = []; + foreach ($result as $key => $item) { + if ($event->result[$key] = $item->isHit()) { + ++$event->hits; + } else { + ++$event->misses; + } + yield $key => $item; + } + }; + + return $f(); + } + + /** + * {@inheritdoc} + */ + public function clear(string $prefix = ''): bool + { + $event = $this->start(__FUNCTION__); + try { + if ($this->pool instanceof AdapterInterface) { + return $event->result = $this->pool->clear($prefix); + } + + return $event->result = $this->pool->clear(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys): bool + { + $event = $this->start(__FUNCTION__); + $event->result['keys'] = $keys; + try { + return $event->result['result'] = $this->pool->deleteItems($keys); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function commit(): bool + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->commit(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function prune(): bool + { + if (!$this->pool instanceof PruneableInterface) { + return false; + } + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->prune(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->pool instanceof ResetInterface) { + $this->pool->reset(); + } + + $this->clearCalls(); + } + + /** + * {@inheritdoc} + */ + public function delete(string $key): bool + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->deleteItem($key); + } finally { + $event->end = microtime(true); + } + } + + public function getCalls() + { + return $this->calls; + } + + public function clearCalls() + { + $this->calls = []; + } + + protected function start(string $name) + { + $this->calls[] = $event = new TraceableAdapterEvent(); + $event->name = $name; + $event->start = microtime(true); + + return $event; + } +} + +/** + * @internal + */ +class TraceableAdapterEvent +{ + public string $name; + public float $start; + public float $end; + public array|bool $result; + public int $hits = 0; + public int $misses = 0; +} diff --git a/vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php b/vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php new file mode 100644 index 0000000..3550633 --- /dev/null +++ b/vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Contracts\Cache\TagAwareCacheInterface; + +/** + * @author Robin Chalas + */ +class TraceableTagAwareAdapter extends TraceableAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface +{ + public function __construct(TagAwareAdapterInterface $pool) + { + parent::__construct($pool); + } + + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags): bool + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->invalidateTags($tags); + } finally { + $event->end = microtime(true); + } + } +} diff --git a/vendor/symfony/cache/CHANGELOG.md b/vendor/symfony/cache/CHANGELOG.md new file mode 100644 index 0000000..1f54db7 --- /dev/null +++ b/vendor/symfony/cache/CHANGELOG.md @@ -0,0 +1,114 @@ +CHANGELOG +========= + +6.0 +--- + + * Remove `DoctrineProvider` and `DoctrineAdapter` + * Remove support of Doctrine DBAL in `PdoAdapter` + +5.4 +--- + + * Deprecate `DoctrineProvider` and `DoctrineAdapter` because these classes have been added to the `doctrine/cache` package + * Add `DoctrineDbalAdapter` identical to `PdoAdapter` for `Doctrine\DBAL\Connection` or DBAL URL + * Deprecate usage of `PdoAdapter` with `Doctrine\DBAL\Connection` or DBAL URL + +5.3 +--- + + * added support for connecting to Redis Sentinel clusters when using the Redis PHP extension + * add support for a custom serializer to the `ApcuAdapter` class + +5.2.0 +----- + + * added integration with Messenger to allow computing cached values in a worker + * allow ISO 8601 time intervals to specify default lifetime + +5.1.0 +----- + + * added max-items + LRU + max-lifetime capabilities to `ArrayCache` + * added `CouchbaseBucketAdapter` + * added context `cache-adapter` to log messages + +5.0.0 +----- + + * removed all PSR-16 implementations in the `Simple` namespace + * removed `SimpleCacheAdapter` + * removed `AbstractAdapter::unserialize()` + * removed `CacheItem::getPreviousTags()` + +4.4.0 +----- + + * added support for connecting to Redis Sentinel clusters + * added argument `$prefix` to `AdapterInterface::clear()` + * improved `RedisTagAwareAdapter` to support Redis server >= 2.8 and up to 4B items per tag + * added `TagAwareMarshaller` for optimized data storage when using `AbstractTagAwareAdapter` + * added `DeflateMarshaller` to compress serialized values + * removed support for phpredis 4 `compression` + * [BC BREAK] `RedisTagAwareAdapter` is not compatible with `RedisCluster` from `Predis` anymore, use `phpredis` instead + * Marked the `CacheDataCollector` class as `@final`. + * added `SodiumMarshaller` to encrypt/decrypt values using libsodium + +4.3.0 +----- + + * removed `psr/simple-cache` dependency, run `composer require psr/simple-cache` if you need it + * deprecated all PSR-16 adapters, use `Psr16Cache` or `Symfony\Contracts\Cache\CacheInterface` implementations instead + * deprecated `SimpleCacheAdapter`, use `Psr16Adapter` instead + +4.2.0 +----- + + * added support for connecting to Redis clusters via DSN + * added support for configuring multiple Memcached servers via DSN + * added `MarshallerInterface` and `DefaultMarshaller` to allow changing the serializer and provide one that automatically uses igbinary when available + * implemented `CacheInterface`, which provides stampede protection via probabilistic early expiration and should become the preferred way to use a cache + * added sub-second expiry accuracy for backends that support it + * added support for phpredis 4 `compression` and `tcp_keepalive` options + * added automatic table creation when using Doctrine DBAL with PDO-based backends + * throw `LogicException` when `CacheItem::tag()` is called on an item coming from a non tag-aware pool + * deprecated `CacheItem::getPreviousTags()`, use `CacheItem::getMetadata()` instead + * deprecated the `AbstractAdapter::unserialize()` and `AbstractCache::unserialize()` methods + * added `CacheCollectorPass` (originally in `FrameworkBundle`) + * added `CachePoolClearerPass` (originally in `FrameworkBundle`) + * added `CachePoolPass` (originally in `FrameworkBundle`) + * added `CachePoolPrunerPass` (originally in `FrameworkBundle`) + +3.4.0 +----- + + * added using options from Memcached DSN + * added PruneableInterface so PSR-6 or PSR-16 cache implementations can declare support for manual stale cache pruning + * added prune logic to FilesystemTrait, PhpFilesTrait, PdoTrait, TagAwareAdapter and ChainTrait + * now FilesystemAdapter, PhpFilesAdapter, FilesystemCache, PhpFilesCache, PdoAdapter, PdoCache, ChainAdapter, and + ChainCache implement PruneableInterface and support manual stale cache pruning + +3.3.0 +----- + + * added CacheItem::getPreviousTags() to get bound tags coming from the pool storage if any + * added PSR-16 "Simple Cache" implementations for all existing PSR-6 adapters + * added Psr6Cache and SimpleCacheAdapter for bidirectional interoperability between PSR-6 and PSR-16 + * added MemcachedAdapter (PSR-6) and MemcachedCache (PSR-16) + * added TraceableAdapter (PSR-6) and TraceableCache (PSR-16) + +3.2.0 +----- + + * added TagAwareAdapter for tags-based invalidation + * added PdoAdapter with PDO and Doctrine DBAL support + * added PhpArrayAdapter and PhpFilesAdapter for OPcache-backed shared memory storage (PHP 7+ only) + * added NullAdapter + +3.1.0 +----- + + * added the component with strict PSR-6 implementations + * added ApcuAdapter, ArrayAdapter, FilesystemAdapter and RedisAdapter + * added AbstractAdapter, ChainAdapter and ProxyAdapter + * added DoctrineAdapter and DoctrineProvider for bidirectional interoperability with Doctrine Cache diff --git a/vendor/symfony/cache/CacheItem.php b/vendor/symfony/cache/CacheItem.php new file mode 100644 index 0000000..d48a46d --- /dev/null +++ b/vendor/symfony/cache/CacheItem.php @@ -0,0 +1,184 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Exception\LogicException; +use Symfony\Contracts\Cache\ItemInterface; + +/** + * @author Nicolas Grekas + */ +final class CacheItem implements ItemInterface +{ + private const METADATA_EXPIRY_OFFSET = 1527506807; + + protected string $key; + protected mixed $value = null; + protected bool $isHit = false; + protected float|int|null $expiry = null; + protected array $metadata = []; + protected array $newMetadata = []; + protected $innerItem = null; + protected ?string $poolHash = null; + protected bool $isTaggable = false; + + /** + * {@inheritdoc} + */ + public function getKey(): string + { + return $this->key; + } + + /** + * {@inheritdoc} + */ + public function get(): mixed + { + return $this->value; + } + + /** + * {@inheritdoc} + */ + public function isHit(): bool + { + return $this->isHit; + } + + /** + * {@inheritdoc} + * + * @return $this + */ + public function set($value): static + { + $this->value = $value; + + return $this; + } + + /** + * {@inheritdoc} + * + * @return $this + */ + public function expiresAt(?\DateTimeInterface $expiration): static + { + $this->expiry = null !== $expiration ? (float) $expiration->format('U.u') : null; + + return $this; + } + + /** + * {@inheritdoc} + * + * @return $this + */ + public function expiresAfter(mixed $time): static + { + if (null === $time) { + $this->expiry = null; + } elseif ($time instanceof \DateInterval) { + $this->expiry = microtime(true) + \DateTime::createFromFormat('U', 0)->add($time)->format('U.u'); + } elseif (\is_int($time)) { + $this->expiry = $time + microtime(true); + } else { + throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given.', get_debug_type($time))); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function tag(mixed $tags): static + { + if (!$this->isTaggable) { + throw new LogicException(sprintf('Cache item "%s" comes from a non tag-aware pool: you cannot tag it.', $this->key)); + } + if (!is_iterable($tags)) { + $tags = [$tags]; + } + foreach ($tags as $tag) { + if (!\is_string($tag) && !$tag instanceof \Stringable) { + throw new InvalidArgumentException(sprintf('Cache tag must be string or object that implements __toString(), "%s" given.', get_debug_type($tag))); + } + $tag = (string) $tag; + if (isset($this->newMetadata[self::METADATA_TAGS][$tag])) { + continue; + } + if ('' === $tag) { + throw new InvalidArgumentException('Cache tag length must be greater than zero.'); + } + if (false !== strpbrk($tag, self::RESERVED_CHARACTERS)) { + throw new InvalidArgumentException(sprintf('Cache tag "%s" contains reserved characters "%s".', $tag, self::RESERVED_CHARACTERS)); + } + $this->newMetadata[self::METADATA_TAGS][$tag] = $tag; + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getMetadata(): array + { + return $this->metadata; + } + + /** + * Validates a cache key according to PSR-6. + * + * @param mixed $key The key to validate + * + * @throws InvalidArgumentException When $key is not valid + */ + public static function validateKey($key): string + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key))); + } + if ('' === $key) { + throw new InvalidArgumentException('Cache key length must be greater than zero.'); + } + if (false !== strpbrk($key, self::RESERVED_CHARACTERS)) { + throw new InvalidArgumentException(sprintf('Cache key "%s" contains reserved characters "%s".', $key, self::RESERVED_CHARACTERS)); + } + + return $key; + } + + /** + * Internal logging helper. + * + * @internal + */ + public static function log(?LoggerInterface $logger, string $message, array $context = []) + { + if ($logger) { + $logger->warning($message, $context); + } else { + $replace = []; + foreach ($context as $k => $v) { + if (\is_scalar($v)) { + $replace['{'.$k.'}'] = $v; + } + } + @trigger_error(strtr($message, $replace), \E_USER_WARNING); + } + } +} diff --git a/vendor/symfony/cache/DataCollector/CacheDataCollector.php b/vendor/symfony/cache/DataCollector/CacheDataCollector.php new file mode 100644 index 0000000..dc3c687 --- /dev/null +++ b/vendor/symfony/cache/DataCollector/CacheDataCollector.php @@ -0,0 +1,181 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\DataCollector; + +use Symfony\Component\Cache\Adapter\TraceableAdapter; +use Symfony\Component\Cache\Adapter\TraceableAdapterEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; + +/** + * @author Aaron Scherer + * @author Tobias Nyholm + * + * @final + */ +class CacheDataCollector extends DataCollector implements LateDataCollectorInterface +{ + /** + * @var TraceableAdapter[] + */ + private array $instances = []; + + public function addInstance(string $name, TraceableAdapter $instance) + { + $this->instances[$name] = $instance; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Throwable $exception = null) + { + $empty = ['calls' => [], 'config' => [], 'options' => [], 'statistics' => []]; + $this->data = ['instances' => $empty, 'total' => $empty]; + foreach ($this->instances as $name => $instance) { + $this->data['instances']['calls'][$name] = $instance->getCalls(); + } + + $this->data['instances']['statistics'] = $this->calculateStatistics(); + $this->data['total']['statistics'] = $this->calculateTotalStatistics(); + } + + public function reset() + { + $this->data = []; + foreach ($this->instances as $instance) { + $instance->clearCalls(); + } + } + + public function lateCollect() + { + $this->data['instances']['calls'] = $this->cloneVar($this->data['instances']['calls']); + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return 'cache'; + } + + /** + * Method returns amount of logged Cache reads: "get" calls. + */ + public function getStatistics(): array + { + return $this->data['instances']['statistics']; + } + + /** + * Method returns the statistic totals. + */ + public function getTotals(): array + { + return $this->data['total']['statistics']; + } + + /** + * Method returns all logged Cache call objects. + */ + public function getCalls(): mixed + { + return $this->data['instances']['calls']; + } + + private function calculateStatistics(): array + { + $statistics = []; + foreach ($this->data['instances']['calls'] as $name => $calls) { + $statistics[$name] = [ + 'calls' => 0, + 'time' => 0, + 'reads' => 0, + 'writes' => 0, + 'deletes' => 0, + 'hits' => 0, + 'misses' => 0, + ]; + /** @var TraceableAdapterEvent $call */ + foreach ($calls as $call) { + ++$statistics[$name]['calls']; + $statistics[$name]['time'] += ($call->end ?? microtime(true)) - $call->start; + if ('get' === $call->name) { + ++$statistics[$name]['reads']; + if ($call->hits) { + ++$statistics[$name]['hits']; + } else { + ++$statistics[$name]['misses']; + ++$statistics[$name]['writes']; + } + } elseif ('getItem' === $call->name) { + ++$statistics[$name]['reads']; + if ($call->hits) { + ++$statistics[$name]['hits']; + } else { + ++$statistics[$name]['misses']; + } + } elseif ('getItems' === $call->name) { + $statistics[$name]['reads'] += $call->hits + $call->misses; + $statistics[$name]['hits'] += $call->hits; + $statistics[$name]['misses'] += $call->misses; + } elseif ('hasItem' === $call->name) { + ++$statistics[$name]['reads']; + foreach ($call->result ?? [] as $result) { + ++$statistics[$name][$result ? 'hits' : 'misses']; + } + } elseif ('save' === $call->name) { + ++$statistics[$name]['writes']; + } elseif ('deleteItem' === $call->name) { + ++$statistics[$name]['deletes']; + } + } + if ($statistics[$name]['reads']) { + $statistics[$name]['hit_read_ratio'] = round(100 * $statistics[$name]['hits'] / $statistics[$name]['reads'], 2); + } else { + $statistics[$name]['hit_read_ratio'] = null; + } + } + + return $statistics; + } + + private function calculateTotalStatistics(): array + { + $statistics = $this->getStatistics(); + $totals = [ + 'calls' => 0, + 'time' => 0, + 'reads' => 0, + 'writes' => 0, + 'deletes' => 0, + 'hits' => 0, + 'misses' => 0, + ]; + foreach ($statistics as $name => $values) { + foreach ($totals as $key => $value) { + $totals[$key] += $statistics[$name][$key]; + } + } + if ($totals['reads']) { + $totals['hit_read_ratio'] = round(100 * $totals['hits'] / $totals['reads'], 2); + } else { + $totals['hit_read_ratio'] = null; + } + + return $totals; + } +} diff --git a/vendor/symfony/cache/DependencyInjection/CacheCollectorPass.php b/vendor/symfony/cache/DependencyInjection/CacheCollectorPass.php new file mode 100644 index 0000000..33b3b6e --- /dev/null +++ b/vendor/symfony/cache/DependencyInjection/CacheCollectorPass.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\DependencyInjection; + +use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; +use Symfony\Component\Cache\Adapter\TraceableAdapter; +use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Inject a data collector to all the cache services to be able to get detailed statistics. + * + * @author Tobias Nyholm + */ +class CacheCollectorPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('data_collector.cache')) { + return; + } + + foreach ($container->findTaggedServiceIds('cache.pool') as $id => $attributes) { + $poolName = $attributes[0]['name'] ?? $id; + + $this->addToCollector($id, $poolName, $container); + } + } + + private function addToCollector(string $id, string $name, ContainerBuilder $container) + { + $definition = $container->getDefinition($id); + if ($definition->isAbstract()) { + return; + } + + $collectorDefinition = $container->getDefinition('data_collector.cache'); + $recorder = new Definition(is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? TraceableTagAwareAdapter::class : TraceableAdapter::class); + $recorder->setTags($definition->getTags()); + if (!$definition->isPublic() || !$definition->isPrivate()) { + $recorder->setPublic($definition->isPublic()); + } + $recorder->setArguments([new Reference($innerId = $id.'.recorder_inner')]); + + foreach ($definition->getMethodCalls() as [$method, $args]) { + if ('setCallbackWrapper' !== $method || !$args[0] instanceof Definition || !($args[0]->getArguments()[2] ?? null) instanceof Definition) { + continue; + } + if ([new Reference($id), 'setCallbackWrapper'] == $args[0]->getArguments()[2]->getFactory()) { + $args[0]->getArguments()[2]->setFactory([new Reference($innerId), 'setCallbackWrapper']); + } + } + + $definition->setTags([]); + $definition->setPublic(false); + + $container->setDefinition($innerId, $definition); + $container->setDefinition($id, $recorder); + + // Tell the collector to add the new instance + $collectorDefinition->addMethodCall('addInstance', [$name, new Reference($id)]); + $collectorDefinition->setPublic(false); + } +} diff --git a/vendor/symfony/cache/DependencyInjection/CachePoolClearerPass.php b/vendor/symfony/cache/DependencyInjection/CachePoolClearerPass.php new file mode 100644 index 0000000..76dd19d --- /dev/null +++ b/vendor/symfony/cache/DependencyInjection/CachePoolClearerPass.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Nicolas Grekas + */ +class CachePoolClearerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $container->getParameterBag()->remove('cache.prefix.seed'); + + foreach ($container->findTaggedServiceIds('cache.pool.clearer') as $id => $attr) { + $clearer = $container->getDefinition($id); + $pools = []; + foreach ($clearer->getArgument(0) as $name => $ref) { + if ($container->hasDefinition($ref)) { + $pools[$name] = new Reference($ref); + } + } + $clearer->replaceArgument(0, $pools); + } + } +} diff --git a/vendor/symfony/cache/DependencyInjection/CachePoolPass.php b/vendor/symfony/cache/DependencyInjection/CachePoolPass.php new file mode 100644 index 0000000..e292e31 --- /dev/null +++ b/vendor/symfony/cache/DependencyInjection/CachePoolPass.php @@ -0,0 +1,247 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\DependencyInjection; + +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\ChainAdapter; +use Symfony\Component\Cache\Adapter\NullAdapter; +use Symfony\Component\Cache\Adapter\ParameterNormalizer; +use Symfony\Component\Cache\Messenger\EarlyExpirationDispatcher; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Nicolas Grekas + */ +class CachePoolPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if ($container->hasParameter('cache.prefix.seed')) { + $seed = $container->getParameterBag()->resolveValue($container->getParameter('cache.prefix.seed')); + } else { + $seed = '_'.$container->getParameter('kernel.project_dir'); + $seed .= '.'.$container->getParameter('kernel.container_class'); + } + + $needsMessageHandler = false; + $allPools = []; + $clearers = []; + $attributes = [ + 'provider', + 'name', + 'namespace', + 'default_lifetime', + 'early_expiration_message_bus', + 'reset', + ]; + foreach ($container->findTaggedServiceIds('cache.pool') as $id => $tags) { + $adapter = $pool = $container->getDefinition($id); + if ($pool->isAbstract()) { + continue; + } + $class = $adapter->getClass(); + while ($adapter instanceof ChildDefinition) { + $adapter = $container->findDefinition($adapter->getParent()); + $class = $class ?: $adapter->getClass(); + if ($t = $adapter->getTag('cache.pool')) { + $tags[0] += $t[0]; + } + } + $name = $tags[0]['name'] ?? $id; + if (!isset($tags[0]['namespace'])) { + $namespaceSeed = $seed; + if (null !== $class) { + $namespaceSeed .= '.'.$class; + } + + $tags[0]['namespace'] = $this->getNamespace($namespaceSeed, $name); + } + if (isset($tags[0]['clearer'])) { + $clearer = $tags[0]['clearer']; + while ($container->hasAlias($clearer)) { + $clearer = (string) $container->getAlias($clearer); + } + } else { + $clearer = null; + } + unset($tags[0]['clearer'], $tags[0]['name']); + + if (isset($tags[0]['provider'])) { + $tags[0]['provider'] = new Reference(static::getServiceProvider($container, $tags[0]['provider'])); + } + + if (ChainAdapter::class === $class) { + $adapters = []; + foreach ($adapter->getArgument(0) as $provider => $adapter) { + if ($adapter instanceof ChildDefinition) { + $chainedPool = $adapter; + } else { + $chainedPool = $adapter = new ChildDefinition($adapter); + } + + $chainedTags = [\is_int($provider) ? [] : ['provider' => $provider]]; + $chainedClass = ''; + + while ($adapter instanceof ChildDefinition) { + $adapter = $container->findDefinition($adapter->getParent()); + $chainedClass = $chainedClass ?: $adapter->getClass(); + if ($t = $adapter->getTag('cache.pool')) { + $chainedTags[0] += $t[0]; + } + } + + if (ChainAdapter::class === $chainedClass) { + throw new InvalidArgumentException(sprintf('Invalid service "%s": chain of adapters cannot reference another chain, found "%s".', $id, $chainedPool->getParent())); + } + + $i = 0; + + if (isset($chainedTags[0]['provider'])) { + $chainedPool->replaceArgument($i++, new Reference(static::getServiceProvider($container, $chainedTags[0]['provider']))); + } + + if (isset($tags[0]['namespace']) && !\in_array($adapter->getClass(), [ArrayAdapter::class, NullAdapter::class], true)) { + $chainedPool->replaceArgument($i++, $tags[0]['namespace']); + } + + if (isset($tags[0]['default_lifetime'])) { + $chainedPool->replaceArgument($i++, $tags[0]['default_lifetime']); + } + + $adapters[] = $chainedPool; + } + + $pool->replaceArgument(0, $adapters); + unset($tags[0]['provider'], $tags[0]['namespace']); + $i = 1; + } else { + $i = 0; + } + + foreach ($attributes as $attr) { + if (!isset($tags[0][$attr])) { + // no-op + } elseif ('reset' === $attr) { + if ($tags[0][$attr]) { + $pool->addTag('kernel.reset', ['method' => $tags[0][$attr]]); + } + } elseif ('early_expiration_message_bus' === $attr) { + $needsMessageHandler = true; + $pool->addMethodCall('setCallbackWrapper', [(new Definition(EarlyExpirationDispatcher::class)) + ->addArgument(new Reference($tags[0]['early_expiration_message_bus'])) + ->addArgument(new Reference('reverse_container')) + ->addArgument((new Definition('callable')) + ->setFactory([new Reference($id), 'setCallbackWrapper']) + ->addArgument(null) + ), + ]); + $pool->addTag('container.reversible'); + } elseif ('namespace' !== $attr || !\in_array($class, [ArrayAdapter::class, NullAdapter::class], true)) { + $argument = $tags[0][$attr]; + + if ('default_lifetime' === $attr && !is_numeric($argument)) { + $argument = (new Definition('int', [$argument])) + ->setFactory([ParameterNormalizer::class, 'normalizeDuration']); + } + + $pool->replaceArgument($i++, $argument); + } + unset($tags[0][$attr]); + } + if (!empty($tags[0])) { + throw new InvalidArgumentException(sprintf('Invalid "cache.pool" tag for service "%s": accepted attributes are "clearer", "provider", "name", "namespace", "default_lifetime", "early_expiration_message_bus" and "reset", found "%s".', $id, implode('", "', array_keys($tags[0])))); + } + + if (null !== $clearer) { + $clearers[$clearer][$name] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE); + } + + $allPools[$name] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE); + } + + if (!$needsMessageHandler) { + $container->removeDefinition('cache.early_expiration_handler'); + } + + $notAliasedCacheClearerId = $aliasedCacheClearerId = 'cache.global_clearer'; + while ($container->hasAlias('cache.global_clearer')) { + $aliasedCacheClearerId = (string) $container->getAlias('cache.global_clearer'); + } + if ($container->hasDefinition($aliasedCacheClearerId)) { + $clearers[$notAliasedCacheClearerId] = $allPools; + } + + foreach ($clearers as $id => $pools) { + $clearer = $container->getDefinition($id); + if ($clearer instanceof ChildDefinition) { + $clearer->replaceArgument(0, $pools); + } else { + $clearer->setArgument(0, $pools); + } + $clearer->addTag('cache.pool.clearer'); + + if ('cache.system_clearer' === $id) { + $clearer->addTag('kernel.cache_clearer'); + } + } + + $allPoolsKeys = array_keys($allPools); + + if ($container->hasDefinition('console.command.cache_pool_list')) { + $container->getDefinition('console.command.cache_pool_list')->replaceArgument(0, $allPoolsKeys); + } + + if ($container->hasDefinition('console.command.cache_pool_clear')) { + $container->getDefinition('console.command.cache_pool_clear')->addArgument($allPoolsKeys); + } + + if ($container->hasDefinition('console.command.cache_pool_delete')) { + $container->getDefinition('console.command.cache_pool_delete')->addArgument($allPoolsKeys); + } + } + + private function getNamespace(string $seed, string $id) + { + return substr(str_replace('/', '-', base64_encode(hash('sha256', $id.$seed, true))), 0, 10); + } + + /** + * @internal + */ + public static function getServiceProvider(ContainerBuilder $container, string $name) + { + $container->resolveEnvPlaceholders($name, null, $usedEnvs); + + if ($usedEnvs || preg_match('#^[a-z]++:#', $name)) { + $dsn = $name; + + if (!$container->hasDefinition($name = '.cache_connection.'.ContainerBuilder::hash($dsn))) { + $definition = new Definition(AbstractAdapter::class); + $definition->setPublic(false); + $definition->setFactory([AbstractAdapter::class, 'createConnection']); + $definition->setArguments([$dsn, ['lazy' => true]]); + $container->setDefinition($name, $definition); + } + } + + return $name; + } +} diff --git a/vendor/symfony/cache/DependencyInjection/CachePoolPrunerPass.php b/vendor/symfony/cache/DependencyInjection/CachePoolPrunerPass.php new file mode 100644 index 0000000..3bbc4b8 --- /dev/null +++ b/vendor/symfony/cache/DependencyInjection/CachePoolPrunerPass.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\DependencyInjection; + +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Rob Frawley 2nd + */ +class CachePoolPrunerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('console.command.cache_pool_prune')) { + return; + } + + $services = []; + + foreach ($container->findTaggedServiceIds('cache.pool') as $id => $tags) { + $class = $container->getParameterBag()->resolveValue($container->getDefinition($id)->getClass()); + + if (!$reflection = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + + if ($reflection->implementsInterface(PruneableInterface::class)) { + $services[$id] = new Reference($id); + } + } + + $container->getDefinition('console.command.cache_pool_prune')->replaceArgument(0, new IteratorArgument($services)); + } +} diff --git a/vendor/symfony/cache/Exception/CacheException.php b/vendor/symfony/cache/Exception/CacheException.php new file mode 100644 index 0000000..d2e975b --- /dev/null +++ b/vendor/symfony/cache/Exception/CacheException.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Exception; + +use Psr\Cache\CacheException as Psr6CacheInterface; +use Psr\SimpleCache\CacheException as SimpleCacheInterface; + +if (interface_exists(SimpleCacheInterface::class)) { + class CacheException extends \Exception implements Psr6CacheInterface, SimpleCacheInterface + { + } +} else { + class CacheException extends \Exception implements Psr6CacheInterface + { + } +} diff --git a/vendor/symfony/cache/Exception/InvalidArgumentException.php b/vendor/symfony/cache/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..7f9584a --- /dev/null +++ b/vendor/symfony/cache/Exception/InvalidArgumentException.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Exception; + +use Psr\Cache\InvalidArgumentException as Psr6CacheInterface; +use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInterface; + +if (interface_exists(SimpleCacheInterface::class)) { + class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface, SimpleCacheInterface + { + } +} else { + class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface + { + } +} diff --git a/vendor/symfony/cache/Exception/LogicException.php b/vendor/symfony/cache/Exception/LogicException.php new file mode 100644 index 0000000..9ffa7ed --- /dev/null +++ b/vendor/symfony/cache/Exception/LogicException.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Exception; + +use Psr\Cache\CacheException as Psr6CacheInterface; +use Psr\SimpleCache\CacheException as SimpleCacheInterface; + +if (interface_exists(SimpleCacheInterface::class)) { + class LogicException extends \LogicException implements Psr6CacheInterface, SimpleCacheInterface + { + } +} else { + class LogicException extends \LogicException implements Psr6CacheInterface + { + } +} diff --git a/vendor/symfony/cache/LICENSE b/vendor/symfony/cache/LICENSE new file mode 100644 index 0000000..f234523 --- /dev/null +++ b/vendor/symfony/cache/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016-2023 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/cache/LockRegistry.php b/vendor/symfony/cache/LockRegistry.php new file mode 100644 index 0000000..d8b8296 --- /dev/null +++ b/vendor/symfony/cache/LockRegistry.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache; + +use Psr\Log\LoggerInterface; +use Symfony\Contracts\Cache\CacheInterface; +use Symfony\Contracts\Cache\ItemInterface; + +/** + * LockRegistry is used internally by existing adapters to protect against cache stampede. + * + * It does so by wrapping the computation of items in a pool of locks. + * Foreach each apps, there can be at most 20 concurrent processes that + * compute items at the same time and only one per cache-key. + * + * @author Nicolas Grekas + */ +final class LockRegistry +{ + private static $openedFiles = []; + private static $lockedFiles; + private static $signalingException; + private static $signalingCallback; + + /** + * The number of items in this list controls the max number of concurrent processes. + */ + private static $files = [ + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'AbstractAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'AbstractTagAwareAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'AdapterInterface.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'ApcuAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'ArrayAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'ChainAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'CouchbaseBucketAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'CouchbaseCollectionAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'DoctrineDbalAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'FilesystemAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'FilesystemTagAwareAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'MemcachedAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'NullAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'ParameterNormalizer.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'PdoAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'PhpArrayAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'PhpFilesAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'ProxyAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'Psr16Adapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'RedisAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'RedisTagAwareAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'TagAwareAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'TagAwareAdapterInterface.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'TraceableAdapter.php', + __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'TraceableTagAwareAdapter.php', + ]; + + /** + * Defines a set of existing files that will be used as keys to acquire locks. + * + * @return array The previously defined set of files + */ + public static function setFiles(array $files): array + { + $previousFiles = self::$files; + self::$files = $files; + + foreach (self::$openedFiles as $file) { + if ($file) { + flock($file, \LOCK_UN); + fclose($file); + } + } + self::$openedFiles = self::$lockedFiles = []; + + return $previousFiles; + } + + public static function compute(callable $callback, ItemInterface $item, bool &$save, CacheInterface $pool, \Closure $setMetadata = null, LoggerInterface $logger = null) + { + if ('\\' === \DIRECTORY_SEPARATOR && null === self::$lockedFiles) { + // disable locking on Windows by default + self::$files = self::$lockedFiles = []; + } + + $key = self::$files ? abs(crc32($item->getKey())) % \count(self::$files) : -1; + + if ($key < 0 || self::$lockedFiles || !$lock = self::open($key)) { + return $callback($item, $save); + } + + self::$signalingException ??= unserialize("O:9:\"Exception\":1:{s:16:\"\0Exception\0trace\";a:0:{}}"); + self::$signalingCallback ??= function () { throw self::$signalingException; }; + + while (true) { + try { + $locked = false; + // race to get the lock in non-blocking mode + $locked = flock($lock, \LOCK_EX | \LOCK_NB, $wouldBlock); + + if ($locked || !$wouldBlock) { + $logger && $logger->info(sprintf('Lock %s, now computing item "{key}"', $locked ? 'acquired' : 'not supported'), ['key' => $item->getKey()]); + self::$lockedFiles[$key] = true; + + $value = $callback($item, $save); + + if ($save) { + if ($setMetadata) { + $setMetadata($item); + } + + $pool->save($item->set($value)); + $save = false; + } + + return $value; + } + // if we failed the race, retry locking in blocking mode to wait for the winner + $logger && $logger->info('Item "{key}" is locked, waiting for it to be released', ['key' => $item->getKey()]); + flock($lock, \LOCK_SH); + } finally { + flock($lock, \LOCK_UN); + unset(self::$lockedFiles[$key]); + } + + try { + $value = $pool->get($item->getKey(), self::$signalingCallback, 0); + $logger && $logger->info('Item "{key}" retrieved after lock was released', ['key' => $item->getKey()]); + $save = false; + + return $value; + } catch (\Exception $e) { + if (self::$signalingException !== $e) { + throw $e; + } + $logger && $logger->info('Item "{key}" not found while lock was released, now retrying', ['key' => $item->getKey()]); + } + } + + return null; + } + + private static function open(int $key) + { + if (null !== $h = self::$openedFiles[$key] ?? null) { + return $h; + } + set_error_handler(function () {}); + try { + $h = fopen(self::$files[$key], 'r+'); + } finally { + restore_error_handler(); + } + + return self::$openedFiles[$key] = $h ?: @fopen(self::$files[$key], 'r'); + } +} diff --git a/vendor/symfony/cache/Marshaller/DefaultMarshaller.php b/vendor/symfony/cache/Marshaller/DefaultMarshaller.php new file mode 100644 index 0000000..7459a9d --- /dev/null +++ b/vendor/symfony/cache/Marshaller/DefaultMarshaller.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Marshaller; + +use Symfony\Component\Cache\Exception\CacheException; + +/** + * Serializes/unserializes values using igbinary_serialize() if available, serialize() otherwise. + * + * @author Nicolas Grekas + */ +class DefaultMarshaller implements MarshallerInterface +{ + private bool $useIgbinarySerialize = true; + private bool $throwOnSerializationFailure = false; + + public function __construct(bool $useIgbinarySerialize = null, bool $throwOnSerializationFailure = false) + { + if (null === $useIgbinarySerialize) { + $useIgbinarySerialize = \extension_loaded('igbinary') && version_compare('3.1.6', phpversion('igbinary'), '<='); + } elseif ($useIgbinarySerialize && (!\extension_loaded('igbinary') || version_compare('3.1.6', phpversion('igbinary'), '>'))) { + throw new CacheException(\extension_loaded('igbinary') ? 'Please upgrade the "igbinary" PHP extension to v3.1.6 or higher.' : 'The "igbinary" PHP extension is not loaded.'); + } + $this->useIgbinarySerialize = $useIgbinarySerialize; + $this->throwOnSerializationFailure = $throwOnSerializationFailure; + } + + /** + * {@inheritdoc} + */ + public function marshall(array $values, ?array &$failed): array + { + $serialized = $failed = []; + + foreach ($values as $id => $value) { + try { + if ($this->useIgbinarySerialize) { + $serialized[$id] = igbinary_serialize($value); + } else { + $serialized[$id] = serialize($value); + } + } catch (\Exception $e) { + if ($this->throwOnSerializationFailure) { + throw new \ValueError($e->getMessage(), 0, $e); + } + $failed[] = $id; + } + } + + return $serialized; + } + + /** + * {@inheritdoc} + */ + public function unmarshall(string $value): mixed + { + if ('b:0;' === $value) { + return false; + } + if ('N;' === $value) { + return null; + } + static $igbinaryNull; + if ($value === ($igbinaryNull ?? $igbinaryNull = \extension_loaded('igbinary') ? igbinary_serialize(null) : false)) { + return null; + } + $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback'); + try { + if (':' === ($value[1] ?? ':')) { + if (false !== $value = unserialize($value)) { + return $value; + } + } elseif (false === $igbinaryNull) { + throw new \RuntimeException('Failed to unserialize values, did you forget to install the "igbinary" extension?'); + } elseif (null !== $value = igbinary_unserialize($value)) { + return $value; + } + + throw new \DomainException(error_get_last() ? error_get_last()['message'] : 'Failed to unserialize values.'); + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine()); + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } + + /** + * @internal + */ + public static function handleUnserializeCallback(string $class) + { + throw new \DomainException('Class not found: '.$class); + } +} diff --git a/vendor/symfony/cache/Marshaller/DeflateMarshaller.php b/vendor/symfony/cache/Marshaller/DeflateMarshaller.php new file mode 100644 index 0000000..ef861fa --- /dev/null +++ b/vendor/symfony/cache/Marshaller/DeflateMarshaller.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Marshaller; + +use Symfony\Component\Cache\Exception\CacheException; + +/** + * Compresses values using gzdeflate(). + * + * @author Nicolas Grekas + */ +class DeflateMarshaller implements MarshallerInterface +{ + private $marshaller; + + public function __construct(MarshallerInterface $marshaller) + { + if (!\function_exists('gzdeflate')) { + throw new CacheException('The "zlib" PHP extension is not loaded.'); + } + + $this->marshaller = $marshaller; + } + + /** + * {@inheritdoc} + */ + public function marshall(array $values, ?array &$failed): array + { + return array_map('gzdeflate', $this->marshaller->marshall($values, $failed)); + } + + /** + * {@inheritdoc} + */ + public function unmarshall(string $value): mixed + { + if (false !== $inflatedValue = @gzinflate($value)) { + $value = $inflatedValue; + } + + return $this->marshaller->unmarshall($value); + } +} diff --git a/vendor/symfony/cache/Marshaller/MarshallerInterface.php b/vendor/symfony/cache/Marshaller/MarshallerInterface.php new file mode 100644 index 0000000..5b81aad --- /dev/null +++ b/vendor/symfony/cache/Marshaller/MarshallerInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Marshaller; + +/** + * Serializes/unserializes PHP values. + * + * Implementations of this interface MUST deal with errors carefully. They MUST + * also deal with forward and backward compatibility at the storage format level. + * + * @author Nicolas Grekas + */ +interface MarshallerInterface +{ + /** + * Serializes a list of values. + * + * When serialization fails for a specific value, no exception should be + * thrown. Instead, its key should be listed in $failed. + */ + public function marshall(array $values, ?array &$failed): array; + + /** + * Unserializes a single value and throws an exception if anything goes wrong. + * + * @throws \Exception Whenever unserialization fails + */ + public function unmarshall(string $value): mixed; +} diff --git a/vendor/symfony/cache/Marshaller/SodiumMarshaller.php b/vendor/symfony/cache/Marshaller/SodiumMarshaller.php new file mode 100644 index 0000000..a8119dc --- /dev/null +++ b/vendor/symfony/cache/Marshaller/SodiumMarshaller.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Marshaller; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * Encrypt/decrypt values using Libsodium. + * + * @author Ahmed TAILOULOUTE + */ +class SodiumMarshaller implements MarshallerInterface +{ + private $marshaller; + private array $decryptionKeys; + + /** + * @param string[] $decryptionKeys The key at index "0" is required and is used to decrypt and encrypt values; + * more rotating keys can be provided to decrypt values; + * each key must be generated using sodium_crypto_box_keypair() + */ + public function __construct(array $decryptionKeys, MarshallerInterface $marshaller = null) + { + if (!self::isSupported()) { + throw new CacheException('The "sodium" PHP extension is not loaded.'); + } + + if (!isset($decryptionKeys[0])) { + throw new InvalidArgumentException('At least one decryption key must be provided at index "0".'); + } + + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + $this->decryptionKeys = $decryptionKeys; + } + + public static function isSupported(): bool + { + return \function_exists('sodium_crypto_box_seal'); + } + + /** + * {@inheritdoc} + */ + public function marshall(array $values, ?array &$failed): array + { + $encryptionKey = sodium_crypto_box_publickey($this->decryptionKeys[0]); + + $encryptedValues = []; + foreach ($this->marshaller->marshall($values, $failed) as $k => $v) { + $encryptedValues[$k] = sodium_crypto_box_seal($v, $encryptionKey); + } + + return $encryptedValues; + } + + /** + * {@inheritdoc} + */ + public function unmarshall(string $value): mixed + { + foreach ($this->decryptionKeys as $k) { + if (false !== $decryptedValue = @sodium_crypto_box_seal_open($value, $k)) { + $value = $decryptedValue; + break; + } + } + + return $this->marshaller->unmarshall($value); + } +} diff --git a/vendor/symfony/cache/Marshaller/TagAwareMarshaller.php b/vendor/symfony/cache/Marshaller/TagAwareMarshaller.php new file mode 100644 index 0000000..d32c994 --- /dev/null +++ b/vendor/symfony/cache/Marshaller/TagAwareMarshaller.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Marshaller; + +/** + * A marshaller optimized for data structures generated by AbstractTagAwareAdapter. + * + * @author Nicolas Grekas + */ +class TagAwareMarshaller implements MarshallerInterface +{ + private $marshaller; + + public function __construct(MarshallerInterface $marshaller = null) + { + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + } + + /** + * {@inheritdoc} + */ + public function marshall(array $values, ?array &$failed): array + { + $failed = $notSerialized = $serialized = []; + + foreach ($values as $id => $value) { + if (\is_array($value) && \is_array($value['tags'] ?? null) && \array_key_exists('value', $value) && \count($value) === 2 + (\is_string($value['meta'] ?? null) && 8 === \strlen($value['meta']))) { + // if the value is an array with keys "tags", "value" and "meta", use a compact serialization format + // magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F allow detecting this format quickly in unmarshall() + + $v = $this->marshaller->marshall($value, $f); + + if ($f) { + $f = []; + $failed[] = $id; + } else { + if ([] === $value['tags']) { + $v['tags'] = ''; + } + + $serialized[$id] = "\x9D".($value['meta'] ?? "\0\0\0\0\0\0\0\0").pack('N', \strlen($v['tags'])).$v['tags'].$v['value']; + $serialized[$id][9] = "\x5F"; + } + } else { + // other arbitrary values are serialized using the decorated marshaller below + $notSerialized[$id] = $value; + } + } + + if ($notSerialized) { + $serialized += $this->marshaller->marshall($notSerialized, $f); + $failed = array_merge($failed, $f); + } + + return $serialized; + } + + /** + * {@inheritdoc} + */ + public function unmarshall(string $value): mixed + { + // detect the compact format used in marshall() using magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F + if (13 >= \strlen($value) || "\x9D" !== $value[0] || "\0" !== $value[5] || "\x5F" !== $value[9]) { + return $this->marshaller->unmarshall($value); + } + + // data consists of value, tags and metadata which we need to unpack + $meta = substr($value, 1, 12); + $meta[8] = "\0"; + $tagLen = unpack('Nlen', $meta, 8)['len']; + $meta = substr($meta, 0, 8); + + return [ + 'value' => $this->marshaller->unmarshall(substr($value, 13 + $tagLen)), + 'tags' => $tagLen ? $this->marshaller->unmarshall(substr($value, 13, $tagLen)) : [], + 'meta' => "\0\0\0\0\0\0\0\0" === $meta ? null : $meta, + ]; + } +} diff --git a/vendor/symfony/cache/Messenger/EarlyExpirationDispatcher.php b/vendor/symfony/cache/Messenger/EarlyExpirationDispatcher.php new file mode 100644 index 0000000..e4d9ea0 --- /dev/null +++ b/vendor/symfony/cache/Messenger/EarlyExpirationDispatcher.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Messenger; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\DependencyInjection\ReverseContainer; +use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\Messenger\Stamp\HandledStamp; + +/** + * Sends the computation of cached values to a message bus. + */ +class EarlyExpirationDispatcher +{ + private $bus; + private $reverseContainer; + private ?\Closure $callbackWrapper; + + public function __construct(MessageBusInterface $bus, ReverseContainer $reverseContainer, callable $callbackWrapper = null) + { + $this->bus = $bus; + $this->reverseContainer = $reverseContainer; + $this->callbackWrapper = null === $callbackWrapper || $callbackWrapper instanceof \Closure ? $callbackWrapper : \Closure::fromCallable($callbackWrapper); + } + + public function __invoke(callable $callback, CacheItem $item, bool &$save, AdapterInterface $pool, \Closure $setMetadata, LoggerInterface $logger = null) + { + if (!$item->isHit() || null === $message = EarlyExpirationMessage::create($this->reverseContainer, $callback, $item, $pool)) { + // The item is stale or the callback cannot be reversed: we must compute the value now + $logger && $logger->info('Computing item "{key}" online: '.($item->isHit() ? 'callback cannot be reversed' : 'item is stale'), ['key' => $item->getKey()]); + + return null !== $this->callbackWrapper ? ($this->callbackWrapper)($callback, $item, $save, $pool, $setMetadata, $logger) : $callback($item, $save); + } + + $envelope = $this->bus->dispatch($message); + + if ($logger) { + if ($envelope->last(HandledStamp::class)) { + $logger->info('Item "{key}" was computed online', ['key' => $item->getKey()]); + } else { + $logger->info('Item "{key}" sent for recomputation', ['key' => $item->getKey()]); + } + } + + // The item's value is not stale, no need to write it to the backend + $save = false; + + return $message->getItem()->get() ?? $item->get(); + } +} diff --git a/vendor/symfony/cache/Messenger/EarlyExpirationHandler.php b/vendor/symfony/cache/Messenger/EarlyExpirationHandler.php new file mode 100644 index 0000000..88e725a --- /dev/null +++ b/vendor/symfony/cache/Messenger/EarlyExpirationHandler.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Messenger; + +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\DependencyInjection\ReverseContainer; +use Symfony\Component\Messenger\Handler\MessageHandlerInterface; + +/** + * Computes cached values sent to a message bus. + */ +class EarlyExpirationHandler implements MessageHandlerInterface +{ + private $reverseContainer; + private array $processedNonces = []; + + public function __construct(ReverseContainer $reverseContainer) + { + $this->reverseContainer = $reverseContainer; + } + + public function __invoke(EarlyExpirationMessage $message) + { + $item = $message->getItem(); + $metadata = $item->getMetadata(); + $expiry = $metadata[CacheItem::METADATA_EXPIRY] ?? 0; + $ctime = $metadata[CacheItem::METADATA_CTIME] ?? 0; + + if ($expiry && $ctime) { + // skip duplicate or expired messages + + $processingNonce = [$expiry, $ctime]; + $pool = $message->getPool(); + $key = $item->getKey(); + + if (($this->processedNonces[$pool][$key] ?? null) === $processingNonce) { + return; + } + + if (microtime(true) >= $expiry) { + return; + } + + $this->processedNonces[$pool] = [$key => $processingNonce] + ($this->processedNonces[$pool] ?? []); + + if (\count($this->processedNonces[$pool]) > 100) { + array_pop($this->processedNonces[$pool]); + } + } + + static $setMetadata; + + $setMetadata ?? $setMetadata = \Closure::bind( + function (CacheItem $item, float $startTime) { + if ($item->expiry > $endTime = microtime(true)) { + $item->newMetadata[CacheItem::METADATA_EXPIRY] = $item->expiry; + $item->newMetadata[CacheItem::METADATA_CTIME] = (int) ceil(1000 * ($endTime - $startTime)); + } + }, + null, + CacheItem::class + ); + + $startTime = microtime(true); + $pool = $message->findPool($this->reverseContainer); + $callback = $message->findCallback($this->reverseContainer); + $value = $callback($item); + $setMetadata($item, $startTime); + $pool->save($item->set($value)); + } +} diff --git a/vendor/symfony/cache/Messenger/EarlyExpirationMessage.php b/vendor/symfony/cache/Messenger/EarlyExpirationMessage.php new file mode 100644 index 0000000..9633e08 --- /dev/null +++ b/vendor/symfony/cache/Messenger/EarlyExpirationMessage.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Messenger; + +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\DependencyInjection\ReverseContainer; + +/** + * Conveys a cached value that needs to be computed. + */ +final class EarlyExpirationMessage +{ + private $item; + private string $pool; + private string|array $callback; + + public static function create(ReverseContainer $reverseContainer, callable $callback, CacheItem $item, AdapterInterface $pool): ?self + { + try { + $item = clone $item; + $item->set(null); + } catch (\Exception $e) { + return null; + } + + $pool = $reverseContainer->getId($pool); + + if (\is_object($callback)) { + if (null === $id = $reverseContainer->getId($callback)) { + return null; + } + + $callback = '@'.$id; + } elseif (!\is_array($callback)) { + $callback = (string) $callback; + } elseif (!\is_object($callback[0])) { + $callback = [(string) $callback[0], (string) $callback[1]]; + } else { + if (null === $id = $reverseContainer->getId($callback[0])) { + return null; + } + + $callback = ['@'.$id, (string) $callback[1]]; + } + + return new self($item, $pool, $callback); + } + + public function getItem(): CacheItem + { + return $this->item; + } + + public function getPool(): string + { + return $this->pool; + } + + /** + * @return string|string[] + */ + public function getCallback(): string|array + { + return $this->callback; + } + + public function findPool(ReverseContainer $reverseContainer): AdapterInterface + { + return $reverseContainer->getService($this->pool); + } + + public function findCallback(ReverseContainer $reverseContainer): callable + { + if (\is_string($callback = $this->callback)) { + return '@' === $callback[0] ? $reverseContainer->getService(substr($callback, 1)) : $callback; + } + if ('@' === $callback[0][0]) { + $callback[0] = $reverseContainer->getService(substr($callback[0], 1)); + } + + return $callback; + } + + private function __construct(CacheItem $item, string $pool, string|array $callback) + { + $this->item = $item; + $this->pool = $pool; + $this->callback = $callback; + } +} diff --git a/vendor/symfony/cache/PruneableInterface.php b/vendor/symfony/cache/PruneableInterface.php new file mode 100644 index 0000000..3095c80 --- /dev/null +++ b/vendor/symfony/cache/PruneableInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache; + +/** + * Interface extends psr-6 and psr-16 caches to allow for pruning (deletion) of all expired cache items. + */ +interface PruneableInterface +{ + public function prune(): bool; +} diff --git a/vendor/symfony/cache/Psr16Cache.php b/vendor/symfony/cache/Psr16Cache.php new file mode 100644 index 0000000..5c1618d --- /dev/null +++ b/vendor/symfony/cache/Psr16Cache.php @@ -0,0 +1,269 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache; + +use Psr\Cache\CacheException as Psr6CacheException; +use Psr\Cache\CacheItemPoolInterface; +use Psr\SimpleCache\CacheException as SimpleCacheException; +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Traits\ProxyTrait; + +/** + * Turns a PSR-6 cache into a PSR-16 one. + * + * @author Nicolas Grekas + */ +class Psr16Cache implements CacheInterface, PruneableInterface, ResettableInterface +{ + use ProxyTrait; + + private const METADATA_EXPIRY_OFFSET = 1527506807; + + private ?\Closure $createCacheItem = null; + private $cacheItemPrototype = null; + + public function __construct(CacheItemPoolInterface $pool) + { + $this->pool = $pool; + + if (!$pool instanceof AdapterInterface) { + return; + } + $cacheItemPrototype = &$this->cacheItemPrototype; + $createCacheItem = \Closure::bind( + static function ($key, $value, $allowInt = false) use (&$cacheItemPrototype) { + $item = clone $cacheItemPrototype; + $item->poolHash = $item->innerItem = null; + if ($allowInt && \is_int($key)) { + $item->key = (string) $key; + } else { + \assert('' !== CacheItem::validateKey($key)); + $item->key = $key; + } + $item->value = $value; + $item->isHit = false; + + return $item; + }, + null, + CacheItem::class + ); + $this->createCacheItem = function ($key, $value, $allowInt = false) use ($createCacheItem) { + if (null === $this->cacheItemPrototype) { + $this->get($allowInt && \is_int($key) ? (string) $key : $key); + } + $this->createCacheItem = $createCacheItem; + + return $createCacheItem($key, null, $allowInt)->set($value); + }; + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null): mixed + { + try { + $item = $this->pool->getItem($key); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + if (null === $this->cacheItemPrototype) { + $this->cacheItemPrototype = clone $item; + $this->cacheItemPrototype->set(null); + } + + return $item->isHit() ? $item->get() : $default; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null): bool + { + try { + if (null !== $f = $this->createCacheItem) { + $item = $f($key, $value); + } else { + $item = $this->pool->getItem($key)->set($value); + } + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + if (null !== $ttl) { + $item->expiresAfter($ttl); + } + + return $this->pool->save($item); + } + + /** + * {@inheritdoc} + */ + public function delete($key): bool + { + try { + return $this->pool->deleteItem($key); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * {@inheritdoc} + */ + public function clear(): bool + { + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null): iterable + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given.', get_debug_type($keys))); + } + + try { + $items = $this->pool->getItems($keys); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + $values = []; + + if (!$this->pool instanceof AdapterInterface) { + foreach ($items as $key => $item) { + $values[$key] = $item->isHit() ? $item->get() : $default; + } + + return $values; + } + + foreach ($items as $key => $item) { + if (!$item->isHit()) { + $values[$key] = $default; + continue; + } + $values[$key] = $item->get(); + + if (!$metadata = $item->getMetadata()) { + continue; + } + unset($metadata[CacheItem::METADATA_TAGS]); + + if ($metadata) { + $values[$key] = ["\x9D".pack('VN', (int) (0.1 + $metadata[CacheItem::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[CacheItem::METADATA_CTIME])."\x5F" => $values[$key]]; + } + } + + return $values; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null): bool + { + $valuesIsArray = \is_array($values); + if (!$valuesIsArray && !$values instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given.', get_debug_type($values))); + } + $items = []; + + try { + if (null !== $f = $this->createCacheItem) { + $valuesIsArray = false; + foreach ($values as $key => $value) { + $items[$key] = $f($key, $value, true); + } + } elseif ($valuesIsArray) { + $items = []; + foreach ($values as $key => $value) { + $items[] = (string) $key; + } + $items = $this->pool->getItems($items); + } else { + foreach ($values as $key => $value) { + if (\is_int($key)) { + $key = (string) $key; + } + $items[$key] = $this->pool->getItem($key)->set($value); + } + } + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + $ok = true; + + foreach ($items as $key => $item) { + if ($valuesIsArray) { + $item->set($values[$key]); + } + if (null !== $ttl) { + $item->expiresAfter($ttl); + } + $ok = $this->pool->saveDeferred($item) && $ok; + } + + return $this->pool->commit() && $ok; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys): bool + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given.', get_debug_type($keys))); + } + + try { + return $this->pool->deleteItems($keys); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * {@inheritdoc} + */ + public function has($key): bool + { + try { + return $this->pool->hasItem($key); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/vendor/symfony/cache/README.md b/vendor/symfony/cache/README.md new file mode 100644 index 0000000..c466d57 --- /dev/null +++ b/vendor/symfony/cache/README.md @@ -0,0 +1,19 @@ +Symfony PSR-6 implementation for caching +======================================== + +The Cache component provides extended +[PSR-6](https://www.php-fig.org/psr/psr-6/) implementations for adding cache to +your applications. It is designed to have a low overhead so that caching is +fastest. It ships with adapters for the most widespread caching backends. +It also provides a [PSR-16](https://www.php-fig.org/psr/psr-16/) adapter, +and implementations for [symfony/cache-contracts](https://github.com/symfony/cache-contracts)' +`CacheInterface` and `TagAwareCacheInterface`. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/cache.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/cache/ResettableInterface.php b/vendor/symfony/cache/ResettableInterface.php new file mode 100644 index 0000000..7b0a853 --- /dev/null +++ b/vendor/symfony/cache/ResettableInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache; + +use Symfony\Contracts\Service\ResetInterface; + +/** + * Resets a pool's local state. + */ +interface ResettableInterface extends ResetInterface +{ +} diff --git a/vendor/symfony/cache/Traits/AbstractAdapterTrait.php b/vendor/symfony/cache/Traits/AbstractAdapterTrait.php new file mode 100644 index 0000000..749217c --- /dev/null +++ b/vendor/symfony/cache/Traits/AbstractAdapterTrait.php @@ -0,0 +1,403 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Psr\Cache\CacheItemInterface; +use Psr\Log\LoggerAwareTrait; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait AbstractAdapterTrait +{ + use LoggerAwareTrait; + + /** + * needs to be set by class, signature is function(string , mixed , bool ). + */ + private static \Closure $createCacheItem; + + /** + * needs to be set by class, signature is function(array , string , array <&expiredIds>). + */ + private static \Closure $mergeByLifetime; + + private string $namespace = ''; + private int $defaultLifetime; + private string $namespaceVersion = ''; + private bool $versioningIsEnabled = false; + private array $deferred = []; + private array $ids = []; + + /** + * @var int|null The maximum length to enforce for identifiers or null when no limit applies + */ + protected $maxIdLength; + + /** + * Fetches several cache items. + * + * @param array $ids The cache identifiers to fetch + * + * @return array|\Traversable + */ + abstract protected function doFetch(array $ids): iterable; + + /** + * Confirms if the cache contains specified cache item. + * + * @param string $id The identifier for which to check existence + */ + abstract protected function doHave(string $id): bool; + + /** + * Deletes all items in the pool. + * + * @param string $namespace The prefix used for all identifiers managed by this pool + */ + abstract protected function doClear(string $namespace): bool; + + /** + * Removes multiple items from the pool. + * + * @param array $ids An array of identifiers that should be removed from the pool + */ + abstract protected function doDelete(array $ids): bool; + + /** + * Persists several cache items immediately. + * + * @param array $values The values to cache, indexed by their cache identifier + * @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning + * + * @return array|bool The identifiers that failed to be cached or a boolean stating if caching succeeded or not + */ + abstract protected function doSave(array $values, int $lifetime): array|bool; + + /** + * {@inheritdoc} + */ + public function hasItem(mixed $key): bool + { + $id = $this->getId($key); + + if (isset($this->deferred[$key])) { + $this->commit(); + } + + try { + return $this->doHave($id); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to check if key "{key}" is cached: '.$e->getMessage(), ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + + return false; + } + } + + /** + * {@inheritdoc} + */ + public function clear(string $prefix = ''): bool + { + $this->deferred = []; + if ($cleared = $this->versioningIsEnabled) { + if ('' === $namespaceVersionToClear = $this->namespaceVersion) { + foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) { + $namespaceVersionToClear = $v; + } + } + $namespaceToClear = $this->namespace.$namespaceVersionToClear; + $namespaceVersion = self::formatNamespaceVersion(mt_rand()); + try { + $e = $this->doSave([static::NS_SEPARATOR.$this->namespace => $namespaceVersion], 0); + } catch (\Exception $e) { + } + if (true !== $e && [] !== $e) { + $cleared = false; + $message = 'Failed to save the new namespace'.($e instanceof \Exception ? ': '.$e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => get_debug_type($this)]); + } else { + $this->namespaceVersion = $namespaceVersion; + $this->ids = []; + } + } else { + $namespaceToClear = $this->namespace.$prefix; + } + + try { + return $this->doClear($namespaceToClear) || $cleared; + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to clear the cache: '.$e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]); + + return false; + } + } + + /** + * {@inheritdoc} + */ + public function deleteItem(mixed $key): bool + { + return $this->deleteItems([$key]); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys): bool + { + $ids = []; + + foreach ($keys as $key) { + $ids[$key] = $this->getId($key); + unset($this->deferred[$key]); + } + + try { + if ($this->doDelete($ids)) { + return true; + } + } catch (\Exception $e) { + } + + $ok = true; + + // When bulk-delete failed, retry each item individually + foreach ($ids as $key => $id) { + try { + $e = null; + if ($this->doDelete([$id])) { + continue; + } + } catch (\Exception $e) { + } + $message = 'Failed to delete key "{key}"'.($e instanceof \Exception ? ': '.$e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + $ok = false; + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + public function getItem(mixed $key): CacheItem + { + $id = $this->getId($key); + + if (isset($this->deferred[$key])) { + $this->commit(); + } + + $isHit = false; + $value = null; + + try { + foreach ($this->doFetch([$id]) as $value) { + $isHit = true; + } + + return (self::$createCacheItem)($key, $value, $isHit); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + } + + return (self::$createCacheItem)($key, null, false); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []): iterable + { + $ids = []; + $commit = false; + + foreach ($keys as $key) { + $ids[] = $this->getId($key); + $commit = $commit || isset($this->deferred[$key]); + } + + if ($commit) { + $this->commit(); + } + + try { + $items = $this->doFetch($ids); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch items: '.$e->getMessage(), ['keys' => $keys, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + $items = []; + } + $ids = array_combine($ids, $keys); + + return $this->generateItems($items, $ids); + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item): bool + { + if (!$item instanceof CacheItem) { + return false; + } + $this->deferred[$item->getKey()] = $item; + + return $this->commit(); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item): bool + { + if (!$item instanceof CacheItem) { + return false; + } + $this->deferred[$item->getKey()] = $item; + + return true; + } + + /** + * Enables/disables versioning of items. + * + * When versioning is enabled, clearing the cache is atomic and doesn't require listing existing keys to proceed, + * but old keys may need garbage collection and extra round-trips to the back-end are required. + * + * Calling this method also clears the memoized namespace version and thus forces a resynchonization of it. + * + * @return bool the previous state of versioning + */ + public function enableVersioning(bool $enable = true): bool + { + $wasEnabled = $this->versioningIsEnabled; + $this->versioningIsEnabled = $enable; + $this->namespaceVersion = ''; + $this->ids = []; + + return $wasEnabled; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->deferred) { + $this->commit(); + } + $this->namespaceVersion = ''; + $this->ids = []; + } + + public function __sleep(): array + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + + public function __destruct() + { + if ($this->deferred) { + $this->commit(); + } + } + + private function generateItems(iterable $items, array &$keys): \Generator + { + $f = self::$createCacheItem; + + try { + foreach ($items as $id => $value) { + if (!isset($keys[$id])) { + throw new InvalidArgumentException(sprintf('Could not match value id "%s" to keys "%s".', $id, implode('", "', $keys))); + } + $key = $keys[$id]; + unset($keys[$id]); + yield $key => $f($key, $value, true); + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch items: '.$e->getMessage(), ['keys' => array_values($keys), 'exception' => $e, 'cache-adapter' => get_debug_type($this)]); + } + + foreach ($keys as $key) { + yield $key => $f($key, null, false); + } + } + + private function getId(mixed $key) + { + if ($this->versioningIsEnabled && '' === $this->namespaceVersion) { + $this->ids = []; + $this->namespaceVersion = '1'.static::NS_SEPARATOR; + try { + foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) { + $this->namespaceVersion = $v; + } + $e = true; + if ('1'.static::NS_SEPARATOR === $this->namespaceVersion) { + $this->namespaceVersion = self::formatNamespaceVersion(time()); + $e = $this->doSave([static::NS_SEPARATOR.$this->namespace => $this->namespaceVersion], 0); + } + } catch (\Exception $e) { + } + if (true !== $e && [] !== $e) { + $message = 'Failed to save the new namespace'.($e instanceof \Exception ? ': '.$e->getMessage() : '.'); + CacheItem::log($this->logger, $message, ['exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => get_debug_type($this)]); + } + } + + if (\is_string($key) && isset($this->ids[$key])) { + return $this->namespace.$this->namespaceVersion.$this->ids[$key]; + } + \assert('' !== CacheItem::validateKey($key)); + $this->ids[$key] = $key; + + if (\count($this->ids) > 1000) { + $this->ids = \array_slice($this->ids, 500, null, true); // stop memory leak if there are many keys + } + + if (null === $this->maxIdLength) { + return $this->namespace.$this->namespaceVersion.$key; + } + if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) { + // Use MD5 to favor speed over security, which is not an issue here + $this->ids[$key] = $id = substr_replace(base64_encode(hash('md5', $key, true)), static::NS_SEPARATOR, -(\strlen($this->namespaceVersion) + 2)); + $id = $this->namespace.$this->namespaceVersion.$id; + } + + return $id; + } + + /** + * @internal + */ + public static function handleUnserializeCallback(string $class) + { + throw new \DomainException('Class not found: '.$class); + } + + private static function formatNamespaceVersion(int $value): string + { + return strtr(substr_replace(base64_encode(pack('V', $value)), static::NS_SEPARATOR, 5), '/', '_'); + } +} diff --git a/vendor/symfony/cache/Traits/ContractsTrait.php b/vendor/symfony/cache/Traits/ContractsTrait.php new file mode 100644 index 0000000..30b0ed1 --- /dev/null +++ b/vendor/symfony/cache/Traits/ContractsTrait.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\LockRegistry; +use Symfony\Contracts\Cache\CacheInterface; +use Symfony\Contracts\Cache\CacheTrait; +use Symfony\Contracts\Cache\ItemInterface; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait ContractsTrait +{ + use CacheTrait { + doGet as private contractsGet; + } + + /** + * @var callable + */ + private $callbackWrapper; + private array $computing = []; + + /** + * Wraps the callback passed to ->get() in a callable. + * + * @return callable the previous callback wrapper + */ + public function setCallbackWrapper(?callable $callbackWrapper): callable + { + if (!isset($this->callbackWrapper)) { + $this->callbackWrapper = \Closure::fromCallable([LockRegistry::class, 'compute']); + + if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { + $this->setCallbackWrapper(null); + } + } + + if (null !== $callbackWrapper && !$callbackWrapper instanceof \Closure) { + $callbackWrapper = \Closure::fromCallable($callbackWrapper); + } + + $previousWrapper = $this->callbackWrapper; + $this->callbackWrapper = $callbackWrapper ?? static function (callable $callback, ItemInterface $item, bool &$save, CacheInterface $pool, \Closure $setMetadata, ?LoggerInterface $logger) { + return $callback($item, $save); + }; + + return $previousWrapper; + } + + private function doGet(AdapterInterface $pool, string $key, callable $callback, ?float $beta, array &$metadata = null) + { + if (0 > $beta = $beta ?? 1.0) { + throw new InvalidArgumentException(sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', static::class, $beta)); + } + + static $setMetadata; + + $setMetadata ?? $setMetadata = \Closure::bind( + static function (CacheItem $item, float $startTime, ?array &$metadata) { + if ($item->expiry > $endTime = microtime(true)) { + $item->newMetadata[CacheItem::METADATA_EXPIRY] = $metadata[CacheItem::METADATA_EXPIRY] = $item->expiry; + $item->newMetadata[CacheItem::METADATA_CTIME] = $metadata[CacheItem::METADATA_CTIME] = (int) ceil(1000 * ($endTime - $startTime)); + } else { + unset($metadata[CacheItem::METADATA_EXPIRY], $metadata[CacheItem::METADATA_CTIME]); + } + }, + null, + CacheItem::class + ); + + $this->callbackWrapper ??= \Closure::fromCallable([LockRegistry::class, 'compute']); + + return $this->contractsGet($pool, $key, function (CacheItem $item, bool &$save) use ($pool, $callback, $setMetadata, &$metadata, $key) { + // don't wrap nor save recursive calls + if (isset($this->computing[$key])) { + $value = $callback($item, $save); + $save = false; + + return $value; + } + + $this->computing[$key] = $key; + $startTime = microtime(true); + + if (!isset($this->callbackWrapper)) { + $this->setCallbackWrapper($this->setCallbackWrapper(null)); + } + + try { + $value = ($this->callbackWrapper)($callback, $item, $save, $pool, function (CacheItem $item) use ($setMetadata, $startTime, &$metadata) { + $setMetadata($item, $startTime, $metadata); + }, $this->logger ?? null); + $setMetadata($item, $startTime, $metadata); + + return $value; + } finally { + unset($this->computing[$key]); + } + }, $beta, $metadata, $this->logger ?? null); + } +} diff --git a/vendor/symfony/cache/Traits/FilesystemCommonTrait.php b/vendor/symfony/cache/Traits/FilesystemCommonTrait.php new file mode 100644 index 0000000..8e30a55 --- /dev/null +++ b/vendor/symfony/cache/Traits/FilesystemCommonTrait.php @@ -0,0 +1,193 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait FilesystemCommonTrait +{ + private string $directory; + private string $tmp; + + private function init(string $namespace, ?string $directory) + { + if (!isset($directory[0])) { + $directory = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'symfony-cache'; + } else { + $directory = realpath($directory) ?: $directory; + } + if (isset($namespace[0])) { + if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0])); + } + $directory .= \DIRECTORY_SEPARATOR.$namespace; + } else { + $directory .= \DIRECTORY_SEPARATOR.'@'; + } + if (!is_dir($directory)) { + @mkdir($directory, 0777, true); + } + $directory .= \DIRECTORY_SEPARATOR; + // On Windows the whole path is limited to 258 chars + if ('\\' === \DIRECTORY_SEPARATOR && \strlen($directory) > 234) { + throw new InvalidArgumentException(sprintf('Cache directory too long (%s).', $directory)); + } + + $this->directory = $directory; + } + + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace): bool + { + $ok = true; + + foreach ($this->scanHashDir($this->directory) as $file) { + if ('' !== $namespace && !str_starts_with($this->getFileKey($file), $namespace)) { + continue; + } + + $ok = ($this->doUnlink($file) || !file_exists($file)) && $ok; + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + $ok = true; + + foreach ($ids as $id) { + $file = $this->getFile($id); + $ok = (!is_file($file) || $this->doUnlink($file) || !file_exists($file)) && $ok; + } + + return $ok; + } + + protected function doUnlink(string $file) + { + return @unlink($file); + } + + private function write(string $file, string $data, int $expiresAt = null) + { + set_error_handler(__CLASS__.'::throwError'); + try { + if (!isset($this->tmp)) { + $this->tmp = $this->directory.bin2hex(random_bytes(6)); + } + try { + $h = fopen($this->tmp, 'x'); + } catch (\ErrorException $e) { + if (!str_contains($e->getMessage(), 'File exists')) { + throw $e; + } + + $this->tmp = $this->directory.bin2hex(random_bytes(6)); + $h = fopen($this->tmp, 'x'); + } + fwrite($h, $data); + fclose($h); + + if (null !== $expiresAt) { + touch($this->tmp, $expiresAt ?: time() + 31556952); // 1 year in seconds + } + + return rename($this->tmp, $file); + } finally { + restore_error_handler(); + } + } + + private function getFile(string $id, bool $mkdir = false, string $directory = null) + { + // Use MD5 to favor speed over security, which is not an issue here + $hash = str_replace('/', '-', base64_encode(hash('md5', static::class.$id, true))); + $dir = ($directory ?? $this->directory).strtoupper($hash[0].\DIRECTORY_SEPARATOR.$hash[1].\DIRECTORY_SEPARATOR); + + if ($mkdir && !is_dir($dir)) { + @mkdir($dir, 0777, true); + } + + return $dir.substr($hash, 2, 20); + } + + private function getFileKey(string $file): string + { + return ''; + } + + private function scanHashDir(string $directory): \Generator + { + if (!is_dir($directory)) { + return; + } + + $chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + + for ($i = 0; $i < 38; ++$i) { + if (!is_dir($directory.$chars[$i])) { + continue; + } + + for ($j = 0; $j < 38; ++$j) { + if (!is_dir($dir = $directory.$chars[$i].\DIRECTORY_SEPARATOR.$chars[$j])) { + continue; + } + + foreach (@scandir($dir, \SCANDIR_SORT_NONE) ?: [] as $file) { + if ('.' !== $file && '..' !== $file) { + yield $dir.\DIRECTORY_SEPARATOR.$file; + } + } + } + } + } + + /** + * @internal + */ + public static function throwError(int $type, string $message, string $file, int $line) + { + throw new \ErrorException($message, 0, $type, $file, $line); + } + + public function __sleep(): array + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + + public function __destruct() + { + if (method_exists(parent::class, '__destruct')) { + parent::__destruct(); + } + if (isset($this->tmp) && is_file($this->tmp)) { + unlink($this->tmp); + } + } +} diff --git a/vendor/symfony/cache/Traits/FilesystemTrait.php b/vendor/symfony/cache/Traits/FilesystemTrait.php new file mode 100644 index 0000000..b25c8d6 --- /dev/null +++ b/vendor/symfony/cache/Traits/FilesystemTrait.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\Exception\CacheException; + +/** + * @author Nicolas Grekas + * @author Rob Frawley 2nd + * + * @internal + */ +trait FilesystemTrait +{ + use FilesystemCommonTrait; + + private $marshaller; + + public function prune(): bool + { + $time = time(); + $pruned = true; + + foreach ($this->scanHashDir($this->directory) as $file) { + if (!$h = @fopen($file, 'r')) { + continue; + } + + if (($expiresAt = (int) fgets($h)) && $time >= $expiresAt) { + fclose($h); + $pruned = @unlink($file) && !file_exists($file) && $pruned; + } else { + fclose($h); + } + } + + return $pruned; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids): iterable + { + $values = []; + $now = time(); + + foreach ($ids as $id) { + $file = $this->getFile($id); + if (!is_file($file) || !$h = @fopen($file, 'r')) { + continue; + } + if (($expiresAt = (int) fgets($h)) && $now >= $expiresAt) { + fclose($h); + @unlink($file); + } else { + $i = rawurldecode(rtrim(fgets($h))); + $value = stream_get_contents($h); + fclose($h); + if ($i === $id) { + $values[$id] = $this->marshaller->unmarshall($value); + } + } + } + + return $values; + } + + /** + * {@inheritdoc} + */ + protected function doHave(string $id): bool + { + $file = $this->getFile($id); + + return is_file($file) && (@filemtime($file) > time() || $this->doFetch([$id])); + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime): array|bool + { + $expiresAt = $lifetime ? (time() + $lifetime) : 0; + $values = $this->marshaller->marshall($values, $failed); + + foreach ($values as $id => $value) { + if (!$this->write($this->getFile($id, true), $expiresAt."\n".rawurlencode($id)."\n".$value, $expiresAt)) { + $failed[] = $id; + } + } + + if ($failed && !is_writable($this->directory)) { + throw new CacheException(sprintf('Cache directory is not writable (%s).', $this->directory)); + } + + return $failed; + } + + private function getFileKey(string $file): string + { + if (!$h = @fopen($file, 'r')) { + return ''; + } + + fgets($h); // expiry + $encodedKey = fgets($h); + fclose($h); + + return rawurldecode(rtrim($encodedKey)); + } +} diff --git a/vendor/symfony/cache/Traits/ProxyTrait.php b/vendor/symfony/cache/Traits/ProxyTrait.php new file mode 100644 index 0000000..ba6f8cb --- /dev/null +++ b/vendor/symfony/cache/Traits/ProxyTrait.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Contracts\Service\ResetInterface; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait ProxyTrait +{ + private object $pool; + + /** + * {@inheritdoc} + */ + public function prune(): bool + { + return $this->pool instanceof PruneableInterface && $this->pool->prune(); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->pool instanceof ResetInterface) { + $this->pool->reset(); + } + } +} diff --git a/vendor/symfony/cache/Traits/RedisClusterNodeProxy.php b/vendor/symfony/cache/Traits/RedisClusterNodeProxy.php new file mode 100644 index 0000000..8d738bb --- /dev/null +++ b/vendor/symfony/cache/Traits/RedisClusterNodeProxy.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +/** + * This file acts as a wrapper to the \RedisCluster implementation so it can accept the same type of calls as + * individual \Redis objects. + * + * Calls are made to individual nodes via: RedisCluster->{method}($host, ...args)' + * according to https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#directed-node-commands + * + * @author Jack Thomas + * + * @internal + */ +class RedisClusterNodeProxy +{ + private array $host; + private $redis; + + public function __construct(array $host, \RedisCluster|RedisClusterProxy $redis) + { + $this->host = $host; + $this->redis = $redis; + } + + public function __call(string $method, array $args) + { + return $this->redis->{$method}($this->host, ...$args); + } + + public function scan(&$iIterator, $strPattern = null, $iCount = null) + { + return $this->redis->scan($iIterator, $this->host, $strPattern, $iCount); + } + + public function getOption($name) + { + return $this->redis->getOption($name); + } +} diff --git a/vendor/symfony/cache/Traits/RedisClusterProxy.php b/vendor/symfony/cache/Traits/RedisClusterProxy.php new file mode 100644 index 0000000..2a8fcc8 --- /dev/null +++ b/vendor/symfony/cache/Traits/RedisClusterProxy.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +/** + * @author Alessandro Chitolina + * + * @internal + */ +class RedisClusterProxy +{ + private $redis; + private \Closure $initializer; + + public function __construct(\Closure $initializer) + { + $this->initializer = $initializer; + } + + public function __call(string $method, array $args) + { + $this->redis ??= ($this->initializer)(); + + return $this->redis->{$method}(...$args); + } + + public function hscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->redis ??= ($this->initializer)(); + + return $this->redis->hscan($strKey, $iIterator, $strPattern, $iCount); + } + + public function scan(&$iIterator, $strPattern = null, $iCount = null) + { + $this->redis ??= ($this->initializer)(); + + return $this->redis->scan($iIterator, $strPattern, $iCount); + } + + public function sscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->redis ??= ($this->initializer)(); + + return $this->redis->sscan($strKey, $iIterator, $strPattern, $iCount); + } + + public function zscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->redis ??= ($this->initializer)(); + + return $this->redis->zscan($strKey, $iIterator, $strPattern, $iCount); + } +} diff --git a/vendor/symfony/cache/Traits/RedisProxy.php b/vendor/symfony/cache/Traits/RedisProxy.php new file mode 100644 index 0000000..463e724 --- /dev/null +++ b/vendor/symfony/cache/Traits/RedisProxy.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class RedisProxy +{ + private $redis; + private \Closure $initializer; + private bool $ready = false; + + public function __construct(\Redis $redis, \Closure $initializer) + { + $this->redis = $redis; + $this->initializer = $initializer; + } + + public function __call(string $method, array $args) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->{$method}(...$args); + } + + public function hscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->hscan($strKey, $iIterator, $strPattern, $iCount); + } + + public function scan(&$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->scan($iIterator, $strPattern, $iCount); + } + + public function sscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->sscan($strKey, $iIterator, $strPattern, $iCount); + } + + public function zscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->zscan($strKey, $iIterator, $strPattern, $iCount); + } +} diff --git a/vendor/symfony/cache/Traits/RedisTrait.php b/vendor/symfony/cache/Traits/RedisTrait.php new file mode 100644 index 0000000..16bc889 --- /dev/null +++ b/vendor/symfony/cache/Traits/RedisTrait.php @@ -0,0 +1,607 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Predis\Command\Redis\UNLINK; +use Predis\Connection\Aggregate\ClusterInterface; +use Predis\Connection\Aggregate\RedisCluster; +use Predis\Connection\Aggregate\ReplicationInterface; +use Predis\Response\ErrorInterface; +use Predis\Response\Status; +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Marshaller\DefaultMarshaller; +use Symfony\Component\Cache\Marshaller\MarshallerInterface; + +/** + * @author Aurimas Niekis + * @author Nicolas Grekas + * + * @internal + */ +trait RedisTrait +{ + private static array $defaultConnectionOptions = [ + 'class' => null, + 'persistent' => 0, + 'persistent_id' => null, + 'timeout' => 30, + 'read_timeout' => 0, + 'retry_interval' => 0, + 'tcp_keepalive' => 0, + 'lazy' => null, + 'redis_cluster' => false, + 'redis_sentinel' => null, + 'dbindex' => 0, + 'failover' => 'none', + 'ssl' => null, // see https://php.net/context.ssl + ]; + private $redis; + private $marshaller; + + private function init(\Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis, string $namespace, int $defaultLifetime, ?MarshallerInterface $marshaller) + { + parent::__construct($namespace, $defaultLifetime); + + if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('RedisAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0])); + } + + if ($redis instanceof \Predis\ClientInterface && $redis->getOptions()->exceptions) { + $options = clone $redis->getOptions(); + \Closure::bind(function () { $this->options['exceptions'] = false; }, $options, $options)(); + $redis = new $redis($redis->getConnection(), $options); + } + + $this->redis = $redis; + $this->marshaller = $marshaller ?? new DefaultMarshaller(); + } + + /** + * Creates a Redis connection using a DSN configuration. + * + * Example DSN: + * - redis://localhost + * - redis://example.com:1234 + * - redis://secret@example.com/13 + * - redis:///var/run/redis.sock + * - redis://secret@/var/run/redis.sock/13 + * + * @param array $options See self::$defaultConnectionOptions + * + * @throws InvalidArgumentException when the DSN is invalid + */ + public static function createConnection(string $dsn, array $options = []): \Redis|\RedisArray|\RedisCluster|RedisClusterProxy|RedisProxy|\Predis\ClientInterface + { + if (str_starts_with($dsn, 'redis:')) { + $scheme = 'redis'; + } elseif (str_starts_with($dsn, 'rediss:')) { + $scheme = 'rediss'; + } else { + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: "%s" does not start with "redis:" or "rediss".', $dsn)); + } + + if (!\extension_loaded('redis') && !class_exists(\Predis\Client::class)) { + throw new CacheException(sprintf('Cannot find the "redis" extension nor the "predis/predis" package: "%s".', $dsn)); + } + + $params = preg_replace_callback('#^'.$scheme.':(//)?(?:(?:[^:@]*+:)?([^@]*+)@)?#', function ($m) use (&$auth) { + if (isset($m[2])) { + $auth = $m[2]; + + if ('' === $auth) { + $auth = null; + } + } + + return 'file:'.($m[1] ?? ''); + }, $dsn); + + if (false === $params = parse_url($params)) { + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: "%s".', $dsn)); + } + + $query = $hosts = []; + + $tls = 'rediss' === $scheme; + $tcpScheme = $tls ? 'tls' : 'tcp'; + + if (isset($params['query'])) { + parse_str($params['query'], $query); + + if (isset($query['host'])) { + if (!\is_array($hosts = $query['host'])) { + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: "%s".', $dsn)); + } + foreach ($hosts as $host => $parameters) { + if (\is_string($parameters)) { + parse_str($parameters, $parameters); + } + if (false === $i = strrpos($host, ':')) { + $hosts[$host] = ['scheme' => $tcpScheme, 'host' => $host, 'port' => 6379] + $parameters; + } elseif ($port = (int) substr($host, 1 + $i)) { + $hosts[$host] = ['scheme' => $tcpScheme, 'host' => substr($host, 0, $i), 'port' => $port] + $parameters; + } else { + $hosts[$host] = ['scheme' => 'unix', 'path' => substr($host, 0, $i)] + $parameters; + } + } + $hosts = array_values($hosts); + } + } + + if (isset($params['host']) || isset($params['path'])) { + if (!isset($params['dbindex']) && isset($params['path'])) { + if (preg_match('#/(\d+)$#', $params['path'], $m)) { + $params['dbindex'] = $m[1]; + $params['path'] = substr($params['path'], 0, -\strlen($m[0])); + } elseif (isset($params['host'])) { + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: "%s", the "dbindex" parameter must be a number.', $dsn)); + } + } + + if (isset($params['host'])) { + array_unshift($hosts, ['scheme' => $tcpScheme, 'host' => $params['host'], 'port' => $params['port'] ?? 6379]); + } else { + array_unshift($hosts, ['scheme' => 'unix', 'path' => $params['path']]); + } + } + + if (!$hosts) { + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: "%s".', $dsn)); + } + + $params += $query + $options + self::$defaultConnectionOptions; + + if (isset($params['redis_sentinel']) && !class_exists(\Predis\Client::class) && !class_exists(\RedisSentinel::class)) { + throw new CacheException(sprintf('Redis Sentinel support requires the "predis/predis" package or the "redis" extension v5.2 or higher: "%s".', $dsn)); + } + + if ($params['redis_cluster'] && isset($params['redis_sentinel'])) { + throw new InvalidArgumentException(sprintf('Cannot use both "redis_cluster" and "redis_sentinel" at the same time: "%s".', $dsn)); + } + + if (null === $params['class'] && \extension_loaded('redis')) { + $class = $params['redis_cluster'] ? \RedisCluster::class : (1 < \count($hosts) && !isset($params['redis_sentinel']) ? \RedisArray::class : \Redis::class); + } else { + $class = $params['class'] ?? \Predis\Client::class; + + if (isset($params['redis_sentinel']) && !is_a($class, \Predis\Client::class, true) && !class_exists(\RedisSentinel::class)) { + throw new CacheException(sprintf('Cannot use Redis Sentinel: class "%s" does not extend "Predis\Client" and ext-redis >= 5.2 not found: "%s".', $class, $dsn)); + } + } + + if (is_a($class, \Redis::class, true)) { + $connect = $params['persistent'] || $params['persistent_id'] ? 'pconnect' : 'connect'; + $redis = new $class(); + + $initializer = static function ($redis) use ($connect, $params, $dsn, $auth, $hosts, $tls) { + $hostIndex = 0; + do { + $host = $hosts[$hostIndex]['host'] ?? $hosts[$hostIndex]['path']; + $port = $hosts[$hostIndex]['port'] ?? 0; + $address = false; + + if (isset($hosts[$hostIndex]['host']) && $tls) { + $host = 'tls://'.$host; + } + + if (!isset($params['redis_sentinel'])) { + break; + } + $extra = []; + if (\defined('Redis::OPT_NULL_MULTIBULK_AS_NULL') && isset($params['auth'])) { + $extra = [$params['auth']]; + } + $sentinel = new \RedisSentinel($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout'], ...$extra); + + if ($address = $sentinel->getMasterAddrByName($params['redis_sentinel'])) { + [$host, $port] = $address; + } + } while (++$hostIndex < \count($hosts) && !$address); + + if (isset($params['redis_sentinel']) && !$address) { + throw new InvalidArgumentException(sprintf('Failed to retrieve master information from sentinel "%s" and dsn "%s".', $params['redis_sentinel'], $dsn)); + } + + try { + $extra = [ + 'stream' => $params['ssl'] ?? null, + ]; + if (isset($params['auth'])) { + $extra['auth'] = $params['auth']; + } + @$redis->{$connect}($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout'], ...\defined('Redis::SCAN_PREFIX') ? [$extra] : []); + + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + try { + $isConnected = $redis->isConnected(); + } finally { + restore_error_handler(); + } + if (!$isConnected) { + $error = preg_match('/^Redis::p?connect\(\): (.*)/', $error ?? '', $error) ? sprintf(' (%s)', $error[1]) : ''; + throw new InvalidArgumentException(sprintf('Redis connection "%s" failed: ', $dsn).$error.'.'); + } + + if ((null !== $auth && !$redis->auth($auth)) + || ($params['dbindex'] && !$redis->select($params['dbindex'])) + ) { + $e = preg_replace('/^ERR /', '', $redis->getLastError()); + throw new InvalidArgumentException(sprintf('Redis connection "%s" failed: ', $dsn).$e.'.'); + } + + if (0 < $params['tcp_keepalive'] && \defined('Redis::OPT_TCP_KEEPALIVE')) { + $redis->setOption(\Redis::OPT_TCP_KEEPALIVE, $params['tcp_keepalive']); + } + } catch (\RedisException $e) { + throw new InvalidArgumentException(sprintf('Redis connection "%s" failed: ', $dsn).$e->getMessage()); + } + + return true; + }; + + if ($params['lazy']) { + $redis = new RedisProxy($redis, $initializer); + } else { + $initializer($redis); + } + } elseif (is_a($class, \RedisArray::class, true)) { + foreach ($hosts as $i => $host) { + switch ($host['scheme']) { + case 'tcp': $hosts[$i] = $host['host'].':'.$host['port']; break; + case 'tls': $hosts[$i] = 'tls://'.$host['host'].':'.$host['port']; break; + default: $hosts[$i] = $host['path']; + } + } + $params['lazy_connect'] = $params['lazy'] ?? true; + $params['connect_timeout'] = $params['timeout']; + + try { + $redis = new $class($hosts, $params); + } catch (\RedisClusterException $e) { + throw new InvalidArgumentException(sprintf('Redis connection "%s" failed: ', $dsn).$e->getMessage()); + } + + if (0 < $params['tcp_keepalive'] && \defined('Redis::OPT_TCP_KEEPALIVE')) { + $redis->setOption(\Redis::OPT_TCP_KEEPALIVE, $params['tcp_keepalive']); + } + } elseif (is_a($class, \RedisCluster::class, true)) { + $initializer = static function () use ($class, $params, $dsn, $hosts) { + foreach ($hosts as $i => $host) { + switch ($host['scheme']) { + case 'tcp': $hosts[$i] = $host['host'].':'.$host['port']; break; + case 'tls': $hosts[$i] = 'tls://'.$host['host'].':'.$host['port']; break; + default: $hosts[$i] = $host['path']; + } + } + + try { + $redis = new $class(null, $hosts, $params['timeout'], $params['read_timeout'], (bool) $params['persistent'], $params['auth'] ?? '', ...\defined('Redis::SCAN_PREFIX') ? [$params['ssl'] ?? null] : []); + } catch (\RedisClusterException $e) { + throw new InvalidArgumentException(sprintf('Redis connection "%s" failed: ', $dsn).$e->getMessage()); + } + + if (0 < $params['tcp_keepalive'] && \defined('Redis::OPT_TCP_KEEPALIVE')) { + $redis->setOption(\Redis::OPT_TCP_KEEPALIVE, $params['tcp_keepalive']); + } + switch ($params['failover']) { + case 'error': $redis->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, \RedisCluster::FAILOVER_ERROR); break; + case 'distribute': $redis->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, \RedisCluster::FAILOVER_DISTRIBUTE); break; + case 'slaves': $redis->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, \RedisCluster::FAILOVER_DISTRIBUTE_SLAVES); break; + } + + return $redis; + }; + + $redis = $params['lazy'] ? new RedisClusterProxy($initializer) : $initializer(); + } elseif (is_a($class, \Predis\ClientInterface::class, true)) { + if ($params['redis_cluster']) { + $params['cluster'] = 'redis'; + } elseif (isset($params['redis_sentinel'])) { + $params['replication'] = 'sentinel'; + $params['service'] = $params['redis_sentinel']; + } + $params += ['parameters' => []]; + $params['parameters'] += [ + 'persistent' => $params['persistent'], + 'timeout' => $params['timeout'], + 'read_write_timeout' => $params['read_timeout'], + 'tcp_nodelay' => true, + ]; + if ($params['dbindex']) { + $params['parameters']['database'] = $params['dbindex']; + } + if (null !== $auth) { + $params['parameters']['password'] = $auth; + } + if (1 === \count($hosts) && !($params['redis_cluster'] || $params['redis_sentinel'])) { + $hosts = $hosts[0]; + } elseif (\in_array($params['failover'], ['slaves', 'distribute'], true) && !isset($params['replication'])) { + $params['replication'] = true; + $hosts[0] += ['alias' => 'master']; + } + $params['exceptions'] = false; + + $redis = new $class($hosts, array_diff_key($params, array_diff_key(self::$defaultConnectionOptions, ['ssl' => null]))); + if (isset($params['redis_sentinel'])) { + $redis->getConnection()->setSentinelTimeout($params['timeout']); + } + } elseif (class_exists($class, false)) { + throw new InvalidArgumentException(sprintf('"%s" is not a subclass of "Redis", "RedisArray", "RedisCluster" nor "Predis\ClientInterface".', $class)); + } else { + throw new InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); + } + + return $redis; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids): iterable + { + if (!$ids) { + return []; + } + + $result = []; + + if ($this->redis instanceof \Predis\ClientInterface && $this->redis->getConnection() instanceof ClusterInterface) { + $values = $this->pipeline(function () use ($ids) { + foreach ($ids as $id) { + yield 'get' => [$id]; + } + }); + } else { + $values = $this->redis->mget($ids); + + if (!\is_array($values) || \count($values) !== \count($ids)) { + return []; + } + + $values = array_combine($ids, $values); + } + + foreach ($values as $id => $v) { + if ($v) { + $result[$id] = $this->marshaller->unmarshall($v); + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function doHave(string $id): bool + { + return (bool) $this->redis->exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear(string $namespace): bool + { + if ($this->redis instanceof \Predis\ClientInterface) { + $prefix = $this->redis->getOptions()->prefix ? $this->redis->getOptions()->prefix->getPrefix() : ''; + $prefixLen = \strlen($prefix ?? ''); + } + + $cleared = true; + $hosts = $this->getHosts(); + $host = reset($hosts); + if ($host instanceof \Predis\Client && $host->getConnection() instanceof ReplicationInterface) { + // Predis supports info command only on the master in replication environments + $hosts = [$host->getClientFor('master')]; + } + + foreach ($hosts as $host) { + if (!isset($namespace[0])) { + $cleared = $host->flushDb() && $cleared; + continue; + } + + $info = $host->info('Server'); + $info = !$info instanceof ErrorInterface ? $info['Server'] ?? $info : ['redis_version' => '2.0']; + + if (!$host instanceof \Predis\ClientInterface) { + $prefix = \defined('Redis::SCAN_PREFIX') && (\Redis::SCAN_PREFIX & $host->getOption(\Redis::OPT_SCAN)) ? '' : $host->getOption(\Redis::OPT_PREFIX); + $prefixLen = \strlen($host->getOption(\Redis::OPT_PREFIX) ?? ''); + } + $pattern = $prefix.$namespace.'*'; + + if (!version_compare($info['redis_version'], '2.8', '>=')) { + // As documented in Redis documentation (http://redis.io/commands/keys) using KEYS + // can hang your server when it is executed against large databases (millions of items). + // Whenever you hit this scale, you should really consider upgrading to Redis 2.8 or above. + $unlink = version_compare($info['redis_version'], '4.0', '>=') ? 'UNLINK' : 'DEL'; + $args = $this->redis instanceof \Predis\ClientInterface ? [0, $pattern] : [[$pattern], 0]; + $cleared = $host->eval("local keys=redis.call('KEYS',ARGV[1]) for i=1,#keys,5000 do redis.call('$unlink',unpack(keys,i,math.min(i+4999,#keys))) end return 1", $args[0], $args[1]) && $cleared; + continue; + } + + $cursor = null; + do { + $keys = $host instanceof \Predis\ClientInterface ? $host->scan($cursor, 'MATCH', $pattern, 'COUNT', 1000) : $host->scan($cursor, $pattern, 1000); + if (isset($keys[1]) && \is_array($keys[1])) { + $cursor = $keys[0]; + $keys = $keys[1]; + } + if ($keys) { + if ($prefixLen) { + foreach ($keys as $i => $key) { + $keys[$i] = substr($key, $prefixLen); + } + } + $this->doDelete($keys); + } + } while ($cursor = (int) $cursor); + } + + return $cleared; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids): bool + { + if (!$ids) { + return true; + } + + if ($this->redis instanceof \Predis\ClientInterface && $this->redis->getConnection() instanceof ClusterInterface) { + static $del; + $del = $del ?? (class_exists(UNLINK::class) ? 'unlink' : 'del'); + + $this->pipeline(function () use ($ids, $del) { + foreach ($ids as $id) { + yield $del => [$id]; + } + })->rewind(); + } else { + static $unlink = true; + + if ($unlink) { + try { + $unlink = false !== $this->redis->unlink($ids); + } catch (\Throwable $e) { + $unlink = false; + } + } + + if (!$unlink) { + $this->redis->del($ids); + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, int $lifetime): array|bool + { + if (!$values = $this->marshaller->marshall($values, $failed)) { + return $failed; + } + + $results = $this->pipeline(function () use ($values, $lifetime) { + foreach ($values as $id => $value) { + if (0 >= $lifetime) { + yield 'set' => [$id, $value]; + } else { + yield 'setEx' => [$id, $lifetime, $value]; + } + } + }); + + foreach ($results as $id => $result) { + if (true !== $result && (!$result instanceof Status || Status::get('OK') !== $result)) { + $failed[] = $id; + } + } + + return $failed; + } + + private function pipeline(\Closure $generator, object $redis = null): \Generator + { + $ids = []; + $redis = $redis ?? $this->redis; + + if ($redis instanceof RedisClusterProxy || $redis instanceof \RedisCluster || ($redis instanceof \Predis\ClientInterface && $redis->getConnection() instanceof RedisCluster)) { + // phpredis & predis don't support pipelining with RedisCluster + // see https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#pipelining + // see https://github.com/nrk/predis/issues/267#issuecomment-123781423 + $results = []; + foreach ($generator() as $command => $args) { + $results[] = $redis->{$command}(...$args); + $ids[] = 'eval' === $command ? ($redis instanceof \Predis\ClientInterface ? $args[2] : $args[1][0]) : $args[0]; + } + } elseif ($redis instanceof \Predis\ClientInterface) { + $results = $redis->pipeline(static function ($redis) use ($generator, &$ids) { + foreach ($generator() as $command => $args) { + $redis->{$command}(...$args); + $ids[] = 'eval' === $command ? $args[2] : $args[0]; + } + }); + } elseif ($redis instanceof \RedisArray) { + $connections = $results = $ids = []; + foreach ($generator() as $command => $args) { + $id = 'eval' === $command ? $args[1][0] : $args[0]; + if (!isset($connections[$h = $redis->_target($id)])) { + $connections[$h] = [$redis->_instance($h), -1]; + $connections[$h][0]->multi(\Redis::PIPELINE); + } + $connections[$h][0]->{$command}(...$args); + $results[] = [$h, ++$connections[$h][1]]; + $ids[] = $id; + } + foreach ($connections as $h => $c) { + $connections[$h] = $c[0]->exec(); + } + foreach ($results as $k => [$h, $c]) { + $results[$k] = $connections[$h][$c]; + } + } else { + $redis->multi(\Redis::PIPELINE); + foreach ($generator() as $command => $args) { + $redis->{$command}(...$args); + $ids[] = 'eval' === $command ? $args[1][0] : $args[0]; + } + $results = $redis->exec(); + } + + if (!$redis instanceof \Predis\ClientInterface && 'eval' === $command && $redis->getLastError()) { + $e = new \RedisException($redis->getLastError()); + $results = array_map(function ($v) use ($e) { return false === $v ? $e : $v; }, (array) $results); + } + + if (\is_bool($results)) { + return; + } + + foreach ($ids as $k => $id) { + yield $id => $results[$k]; + } + } + + private function getHosts(): array + { + $hosts = [$this->redis]; + if ($this->redis instanceof \Predis\ClientInterface) { + $connection = $this->redis->getConnection(); + if ($connection instanceof ClusterInterface && $connection instanceof \Traversable) { + $hosts = []; + foreach ($connection as $c) { + $hosts[] = new \Predis\Client($c); + } + } + } elseif ($this->redis instanceof \RedisArray) { + $hosts = []; + foreach ($this->redis->_hosts() as $host) { + $hosts[] = $this->redis->_instance($host); + } + } elseif ($this->redis instanceof RedisClusterProxy || $this->redis instanceof \RedisCluster) { + $hosts = []; + foreach ($this->redis->_masters() as $host) { + $hosts[] = new RedisClusterNodeProxy($host, $this->redis); + } + } + + return $hosts; + } +} diff --git a/vendor/symfony/cache/composer.json b/vendor/symfony/cache/composer.json new file mode 100644 index 0000000..63d27f6 --- /dev/null +++ b/vendor/symfony/cache/composer.json @@ -0,0 +1,56 @@ +{ + "name": "symfony/cache", + "type": "library", + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", + "keywords": ["caching", "psr6"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "provide": { + "psr/cache-implementation": "2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0", + "symfony/cache-implementation": "1.1|2.0|3.0" + }, + "require": { + "php": ">=8.0.2", + "psr/cache": "^2.0|^3.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^1.1.7|^2|^3", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/var-exporter": "^5.4|^6.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/dbal": "^2.13.1|^3.0", + "predis/predis": "^1.1", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "conflict": { + "doctrine/dbal": "<2.13.1", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/var-dumper": "<5.4" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Cache\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/vendor/symfony/config/Builder/ClassBuilder.php b/vendor/symfony/config/Builder/ClassBuilder.php new file mode 100644 index 0000000..8194a15 --- /dev/null +++ b/vendor/symfony/config/Builder/ClassBuilder.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Builder; + +/** + * Build PHP classes to generate config. + * + * @internal + * + * @author Tobias Nyholm + */ +class ClassBuilder +{ + private string $namespace; + private string $name; + + /** @var Property[] */ + private array $properties = []; + + /** @var Method[] */ + private array $methods = []; + private array $require = []; + private array $use = []; + private array $implements = []; + private bool $allowExtraKeys = false; + + public function __construct(string $namespace, string $name) + { + $this->namespace = $namespace; + $this->name = ucfirst($this->camelCase($name)).'Config'; + } + + public function getDirectory(): string + { + return str_replace('\\', \DIRECTORY_SEPARATOR, $this->namespace); + } + + public function getFilename(): string + { + return $this->name.'.php'; + } + + public function build(): string + { + $rootPath = explode(\DIRECTORY_SEPARATOR, $this->getDirectory()); + $require = ''; + foreach ($this->require as $class) { + // figure out relative path. + $path = explode(\DIRECTORY_SEPARATOR, $class->getDirectory()); + $path[] = $class->getFilename(); + foreach ($rootPath as $key => $value) { + if ($path[$key] !== $value) { + break; + } + unset($path[$key]); + } + $require .= sprintf('require_once __DIR__.\DIRECTORY_SEPARATOR.\'%s\';', implode('\'.\DIRECTORY_SEPARATOR.\'', $path))."\n"; + } + $use = $require ? "\n" : ''; + foreach (array_keys($this->use) as $statement) { + $use .= sprintf('use %s;', $statement)."\n"; + } + + $implements = [] === $this->implements ? '' : 'implements '.implode(', ', $this->implements); + $body = ''; + foreach ($this->properties as $property) { + $body .= ' '.$property->getContent()."\n"; + } + foreach ($this->methods as $method) { + $lines = explode("\n", $method->getContent()); + foreach ($lines as $line) { + $body .= ($line ? ' '.$line : '')."\n"; + } + } + + $content = strtr(' $this->namespace, 'REQUIRE' => $require, 'USE' => $use, 'CLASS' => $this->getName(), 'IMPLEMENTS' => $implements, 'BODY' => $body]); + + return $content; + } + + public function addRequire(self $class): void + { + $this->require[] = $class; + } + + public function addUse(string $class): void + { + $this->use[$class] = true; + } + + public function addImplements(string $interface): void + { + $this->implements[] = '\\'.ltrim($interface, '\\'); + } + + public function addMethod(string $name, string $body, array $params = []): void + { + $this->methods[] = new Method(strtr($body, ['NAME' => $this->camelCase($name)] + $params)); + } + + public function addProperty(string $name, string $classType = null, string $defaultValue = null): Property + { + $property = new Property($name, '_' !== $name[0] ? $this->camelCase($name) : $name); + if (null !== $classType) { + $property->setType($classType); + } + $this->properties[] = $property; + $defaultValue = null !== $defaultValue ? sprintf(' = %s', $defaultValue) : ''; + $property->setContent(sprintf('private $%s%s;', $property->getName(), $defaultValue)); + + return $property; + } + + public function getProperties(): array + { + return $this->properties; + } + + private function camelCase(string $input): string + { + $output = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $input)))); + + return preg_replace('#\W#', '', $output); + } + + public function getName(): string + { + return $this->name; + } + + public function getNamespace(): string + { + return $this->namespace; + } + + public function getFqcn(): string + { + return '\\'.$this->namespace.'\\'.$this->name; + } + + public function setAllowExtraKeys(bool $allowExtraKeys): void + { + $this->allowExtraKeys = $allowExtraKeys; + } + + public function shouldAllowExtraKeys(): bool + { + return $this->allowExtraKeys; + } +} diff --git a/vendor/symfony/config/Builder/ConfigBuilderGenerator.php b/vendor/symfony/config/Builder/ConfigBuilderGenerator.php new file mode 100644 index 0000000..397ce7e --- /dev/null +++ b/vendor/symfony/config/Builder/ConfigBuilderGenerator.php @@ -0,0 +1,581 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Builder; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\BooleanNode; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\EnumNode; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\FloatNode; +use Symfony\Component\Config\Definition\IntegerNode; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\PrototypedArrayNode; +use Symfony\Component\Config\Definition\ScalarNode; +use Symfony\Component\Config\Definition\VariableNode; +use Symfony\Component\Config\Loader\ParamConfigurator; + +/** + * Generate ConfigBuilders to help create valid config. + * + * @author Tobias Nyholm + */ +class ConfigBuilderGenerator implements ConfigBuilderGeneratorInterface +{ + /** + * @var ClassBuilder[] + */ + private array $classes = []; + private string $outputDir; + + public function __construct(string $outputDir) + { + $this->outputDir = $outputDir; + } + + /** + * @return \Closure that will return the root config class + */ + public function build(ConfigurationInterface $configuration): \Closure + { + $this->classes = []; + + $rootNode = $configuration->getConfigTreeBuilder()->buildTree(); + $rootClass = new ClassBuilder('Symfony\\Config', $rootNode->getName()); + + $path = $this->getFullPath($rootClass); + if (!is_file($path)) { + // Generate the class if the file not exists + $this->classes[] = $rootClass; + $this->buildNode($rootNode, $rootClass, $this->getSubNamespace($rootClass)); + $rootClass->addImplements(ConfigBuilderInterface::class); + $rootClass->addMethod('getExtensionAlias', ' +public function NAME(): string +{ + return \'ALIAS\'; +}', ['ALIAS' => $rootNode->getPath()]); + + $this->writeClasses(); + } + + $loader = \Closure::fromCallable(function () use ($path, $rootClass) { + require_once $path; + $className = $rootClass->getFqcn(); + + return new $className(); + }); + + return $loader; + } + + private function getFullPath(ClassBuilder $class): string + { + $directory = $this->outputDir.\DIRECTORY_SEPARATOR.$class->getDirectory(); + if (!is_dir($directory)) { + @mkdir($directory, 0777, true); + } + + return $directory.\DIRECTORY_SEPARATOR.$class->getFilename(); + } + + private function writeClasses(): void + { + foreach ($this->classes as $class) { + $this->buildConstructor($class); + $this->buildToArray($class); + if ($class->getProperties()) { + $class->addProperty('_usedProperties', null, '[]'); + } + $this->buildSetExtraKey($class); + + file_put_contents($this->getFullPath($class), $class->build()); + } + + $this->classes = []; + } + + private function buildNode(NodeInterface $node, ClassBuilder $class, string $namespace): void + { + if (!$node instanceof ArrayNode) { + throw new \LogicException('The node was expected to be an ArrayNode. This Configuration includes an edge case not supported yet.'); + } + + foreach ($node->getChildren() as $child) { + switch (true) { + case $child instanceof ScalarNode: + $this->handleScalarNode($child, $class); + break; + case $child instanceof PrototypedArrayNode: + $this->handlePrototypedArrayNode($child, $class, $namespace); + break; + case $child instanceof VariableNode: + $this->handleVariableNode($child, $class); + break; + case $child instanceof ArrayNode: + $this->handleArrayNode($child, $class, $namespace); + break; + default: + throw new \RuntimeException(sprintf('Unknown node "%s".', \get_class($child))); + } + } + } + + private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $namespace): void + { + $childClass = new ClassBuilder($namespace, $node->getName()); + $childClass->setAllowExtraKeys($node->shouldIgnoreExtraKeys()); + $class->addRequire($childClass); + $this->classes[] = $childClass; + + $hasNormalizationClosures = $this->hasNormalizationClosures($node); + $property = $class->addProperty( + $node->getName(), + $this->getType($childClass->getFqcn(), $hasNormalizationClosures) + ); + $body = $hasNormalizationClosures ? ' +/** + * @return CLASS|$this + */ +public function NAME(mixed $value = []): CLASS|static +{ + if (!\is_array($value)) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = $value; + + return $this; + } + + if (!$this->PROPERTY instanceof CLASS) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = new CLASS($value); + } elseif (0 < \func_num_args()) { + throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); + } + + return $this->PROPERTY; +}' : ' +public function NAME(array $value = []): CLASS +{ + if (null === $this->PROPERTY) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = new CLASS($value); + } elseif (0 < \func_num_args()) { + throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); + } + + return $this->PROPERTY; +}'; + $class->addUse(InvalidConfigurationException::class); + $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]); + + $this->buildNode($node, $childClass, $this->getSubNamespace($childClass)); + } + + private function handleVariableNode(VariableNode $node, ClassBuilder $class): void + { + $comment = $this->getComment($node); + $property = $class->addProperty($node->getName()); + $class->addUse(ParamConfigurator::class); + + $body = ' +/** +COMMENT * + * @return $this + */ +public function NAME(mixed $valueDEFAULT): static +{ + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = $value; + + return $this; +}'; + $class->addMethod($node->getName(), $body, [ + 'PROPERTY' => $property->getName(), + 'COMMENT' => $comment, + 'DEFAULT' => $node->hasDefaultValue() ? ' = '.var_export($node->getDefaultValue(), true) : '', + ]); + } + + private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuilder $class, string $namespace): void + { + $name = $this->getSingularName($node); + $prototype = $node->getPrototype(); + $methodName = $name; + $hasNormalizationClosures = $this->hasNormalizationClosures($node) || $this->hasNormalizationClosures($prototype); + + $parameterType = $this->getParameterType($prototype); + if (null !== $parameterType || $prototype instanceof ScalarNode) { + $class->addUse(ParamConfigurator::class); + $property = $class->addProperty($node->getName()); + if (null === $key = $node->getKeyAttribute()) { + // This is an array of values; don't use singular name + $body = ' +/** + * @param PHPDOC_TYPE $value + * + * @return $this + */ +public function NAME(TYPE $value): static +{ + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = $value; + + return $this; +}'; + + $class->addMethod($node->getName(), $body, [ + 'PROPERTY' => $property->getName(), + 'TYPE' => $hasNormalizationClosures ? 'mixed' : 'ParamConfigurator|array', + 'PHPDOC_TYPE' => $hasNormalizationClosures ? 'mixed' : sprintf('ParamConfigurator|list', '' === $parameterType ? 'mixed' : $parameterType), + ]); + } else { + $body = ' +/** + * @return $this + */ +public function NAME(string $VAR, TYPE $VALUE): static +{ + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY[$VAR] = $VALUE; + + return $this; +}'; + + $class->addMethod($methodName, $body, [ + 'PROPERTY' => $property->getName(), + 'TYPE' => $hasNormalizationClosures || '' === $parameterType ? 'mixed' : 'ParamConfigurator|'.$parameterType, + 'VAR' => '' === $key ? 'key' : $key, + 'VALUE' => 'value' === $key ? 'data' : 'value', + ]); + } + + return; + } + + $childClass = new ClassBuilder($namespace, $name); + if ($prototype instanceof ArrayNode) { + $childClass->setAllowExtraKeys($prototype->shouldIgnoreExtraKeys()); + } + $class->addRequire($childClass); + $this->classes[] = $childClass; + + $property = $class->addProperty( + $node->getName(), + $this->getType($childClass->getFqcn().'[]', $hasNormalizationClosures) + ); + + if (null === $key = $node->getKeyAttribute()) { + $body = $hasNormalizationClosures ? ' +/** + * @return CLASS|$this + */ +public function NAME(mixed $value = []): CLASS|static +{ + $this->_usedProperties[\'PROPERTY\'] = true; + if (!\is_array($value)) { + $this->PROPERTY[] = $value; + + return $this; + } + + return $this->PROPERTY[] = new CLASS($value); +}' : ' +public function NAME(array $value = []): CLASS +{ + $this->_usedProperties[\'PROPERTY\'] = true; + + return $this->PROPERTY[] = new CLASS($value); +}'; + $class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]); + } else { + $body = $hasNormalizationClosures ? ' +/** + * @return CLASS|$this + */ +public function NAME(string $VAR, mixed $VALUE = []): CLASS|static +{ + if (!\is_array($VALUE)) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY[$VAR] = $VALUE; + + return $this; + } + + if (!isset($this->PROPERTY[$VAR]) || !$this->PROPERTY[$VAR] instanceof CLASS) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY[$VAR] = new CLASS($VALUE); + } elseif (1 < \func_num_args()) { + throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); + } + + return $this->PROPERTY[$VAR]; +}' : ' +public function NAME(string $VAR, array $VALUE = []): CLASS +{ + if (!isset($this->PROPERTY[$VAR])) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY[$VAR] = new CLASS($VALUE); + } elseif (1 < \func_num_args()) { + throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); + } + + return $this->PROPERTY[$VAR]; +}'; + $class->addUse(InvalidConfigurationException::class); + $class->addMethod($methodName, $body, [ + 'PROPERTY' => $property->getName(), + 'CLASS' => $childClass->getFqcn(), + 'VAR' => '' === $key ? 'key' : $key, + 'VALUE' => 'value' === $key ? 'data' : 'value', + ]); + } + + $this->buildNode($prototype, $childClass, $namespace.'\\'.$childClass->getName()); + } + + private function handleScalarNode(ScalarNode $node, ClassBuilder $class): void + { + $comment = $this->getComment($node); + $property = $class->addProperty($node->getName()); + $class->addUse(ParamConfigurator::class); + + $body = ' +/** +COMMENT * @return $this + */ +public function NAME($value): static +{ + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = $value; + + return $this; +}'; + + $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment]); + } + + private function getParameterType(NodeInterface $node): ?string + { + if ($node instanceof BooleanNode) { + return 'bool'; + } + + if ($node instanceof IntegerNode) { + return 'int'; + } + + if ($node instanceof FloatNode) { + return 'float'; + } + + if ($node instanceof EnumNode) { + return ''; + } + + if ($node instanceof PrototypedArrayNode && $node->getPrototype() instanceof ScalarNode) { + // This is just an array of variables + return 'array'; + } + + if ($node instanceof VariableNode) { + // mixed + return ''; + } + + return null; + } + + private function getComment(VariableNode $node): string + { + $comment = ''; + if ('' !== $info = (string) $node->getInfo()) { + $comment .= ' * '.$info."\n"; + } + + foreach ((array) ($node->getExample() ?? []) as $example) { + $comment .= ' * @example '.$example."\n"; + } + + if ('' !== $default = $node->getDefaultValue()) { + $comment .= ' * @default '.(null === $default ? 'null' : var_export($default, true))."\n"; + } + + if ($node instanceof EnumNode) { + $comment .= sprintf(' * @param ParamConfigurator|%s $value', implode('|', array_map(function ($a) { + return var_export($a, true); + }, $node->getValues())))."\n"; + } else { + $parameterType = $this->getParameterType($node); + if (null === $parameterType || '' === $parameterType) { + $parameterType = 'mixed'; + } + $comment .= ' * @param ParamConfigurator|'.$parameterType.' $value'."\n"; + } + + if ($node->isDeprecated()) { + $comment .= ' * @deprecated '.$node->getDeprecation($node->getName(), $node->getParent()->getName())['message']."\n"; + } + + return $comment; + } + + /** + * Pick a good singular name. + */ + private function getSingularName(PrototypedArrayNode $node): string + { + $name = $node->getName(); + if ('s' !== substr($name, -1)) { + return $name; + } + + $parent = $node->getParent(); + $mappings = $parent instanceof ArrayNode ? $parent->getXmlRemappings() : []; + foreach ($mappings as $map) { + if ($map[1] === $name) { + $name = $map[0]; + break; + } + } + + return $name; + } + + private function buildToArray(ClassBuilder $class): void + { + $body = '$output = [];'; + foreach ($class->getProperties() as $p) { + $code = '$this->PROPERTY'; + if (null !== $p->getType()) { + if ($p->isArray()) { + $code = $p->areScalarsAllowed() + ? 'array_map(function ($v) { return $v instanceof CLASS ? $v->toArray() : $v; }, $this->PROPERTY)' + : 'array_map(function ($v) { return $v->toArray(); }, $this->PROPERTY)' + ; + } else { + $code = $p->areScalarsAllowed() + ? '$this->PROPERTY instanceof CLASS ? $this->PROPERTY->toArray() : $this->PROPERTY' + : '$this->PROPERTY->toArray()' + ; + } + } + + $body .= strtr(' + if (isset($this->_usedProperties[\'PROPERTY\'])) { + $output[\'ORG_NAME\'] = '.$code.'; + }', ['PROPERTY' => $p->getName(), 'ORG_NAME' => $p->getOriginalName(), 'CLASS' => $p->getType()]); + } + + $extraKeys = $class->shouldAllowExtraKeys() ? ' + $this->_extraKeys' : ''; + + $class->addMethod('toArray', ' +public function NAME(): array +{ + '.$body.' + + return $output'.$extraKeys.'; +}'); + } + + private function buildConstructor(ClassBuilder $class): void + { + $body = ''; + foreach ($class->getProperties() as $p) { + $code = '$value[\'ORG_NAME\']'; + if (null !== $p->getType()) { + if ($p->isArray()) { + $code = $p->areScalarsAllowed() + ? 'array_map(function ($v) { return \is_array($v) ? new '.$p->getType().'($v) : $v; }, $value[\'ORG_NAME\'])' + : 'array_map(function ($v) { return new '.$p->getType().'($v); }, $value[\'ORG_NAME\'])' + ; + } else { + $code = $p->areScalarsAllowed() + ? '\is_array($value[\'ORG_NAME\']) ? new '.$p->getType().'($value[\'ORG_NAME\']) : $value[\'ORG_NAME\']' + : 'new '.$p->getType().'($value[\'ORG_NAME\'])' + ; + } + } + + $body .= strtr(' + if (array_key_exists(\'ORG_NAME\', $value)) { + $this->_usedProperties[\'PROPERTY\'] = true; + $this->PROPERTY = '.$code.'; + unset($value[\'ORG_NAME\']); + } +', ['PROPERTY' => $p->getName(), 'ORG_NAME' => $p->getOriginalName()]); + } + + if ($class->shouldAllowExtraKeys()) { + $body .= ' + $this->_extraKeys = $value; +'; + } else { + $body .= ' + if ([] !== $value) { + throw new InvalidConfigurationException(sprintf(\'The following keys are not supported by "%s": \', __CLASS__).implode(\', \', array_keys($value))); + }'; + + $class->addUse(InvalidConfigurationException::class); + } + + $class->addMethod('__construct', ' +public function __construct(array $value = []) +{'.$body.' +}'); + } + + private function buildSetExtraKey(ClassBuilder $class): void + { + if (!$class->shouldAllowExtraKeys()) { + return; + } + + $class->addUse(ParamConfigurator::class); + + $class->addProperty('_extraKeys'); + + $class->addMethod('set', ' +/** + * @param ParamConfigurator|mixed $value + * + * @return $this + */ +public function NAME(string $key, mixed $value): static +{ + $this->_extraKeys[$key] = $value; + + return $this; +}'); + } + + private function getSubNamespace(ClassBuilder $rootClass): string + { + return sprintf('%s\\%s', $rootClass->getNamespace(), substr($rootClass->getName(), 0, -6)); + } + + private function hasNormalizationClosures(NodeInterface $node): bool + { + try { + $r = new \ReflectionProperty($node, 'normalizationClosures'); + } catch (\ReflectionException $e) { + return false; + } + $r->setAccessible(true); + + return [] !== $r->getValue($node); + } + + private function getType(string $classType, bool $hasNormalizationClosures): string + { + return $classType.($hasNormalizationClosures ? '|scalar' : ''); + } +} diff --git a/vendor/symfony/config/Builder/ConfigBuilderGeneratorInterface.php b/vendor/symfony/config/Builder/ConfigBuilderGeneratorInterface.php new file mode 100644 index 0000000..c52c9e5 --- /dev/null +++ b/vendor/symfony/config/Builder/ConfigBuilderGeneratorInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Builder; + +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * Generates ConfigBuilders to help create valid config. + * + * @author Tobias Nyholm + */ +interface ConfigBuilderGeneratorInterface +{ + /** + * @return \Closure that will return the root config class + */ + public function build(ConfigurationInterface $configuration): \Closure; +} diff --git a/vendor/symfony/config/Builder/ConfigBuilderInterface.php b/vendor/symfony/config/Builder/ConfigBuilderInterface.php new file mode 100644 index 0000000..fd3129c --- /dev/null +++ b/vendor/symfony/config/Builder/ConfigBuilderInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Builder; + +/** + * A ConfigBuilder provides helper methods to build a large complex array. + * + * @author Tobias Nyholm + */ +interface ConfigBuilderInterface +{ + /** + * Gets all configuration represented as an array. + */ + public function toArray(): array; + + /** + * Gets the alias for the extension which config we are building. + */ + public function getExtensionAlias(): string; +} diff --git a/vendor/symfony/config/Builder/Method.php b/vendor/symfony/config/Builder/Method.php new file mode 100644 index 0000000..c97c986 --- /dev/null +++ b/vendor/symfony/config/Builder/Method.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Builder; + +/** + * Represents a method when building classes. + * + * @internal + * + * @author Tobias Nyholm + */ +class Method +{ + private string $content; + + public function __construct(string $content) + { + $this->content = $content; + } + + public function getContent(): string + { + return $this->content; + } +} diff --git a/vendor/symfony/config/Builder/Property.php b/vendor/symfony/config/Builder/Property.php new file mode 100644 index 0000000..27c8f2c --- /dev/null +++ b/vendor/symfony/config/Builder/Property.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Builder; + +/** + * Represents a property when building classes. + * + * @internal + * + * @author Tobias Nyholm + */ +class Property +{ + private string $name; + private string $originalName; + private bool $array = false; + private bool $scalarsAllowed = false; + private ?string $type = null; + private ?string $content = null; + + public function __construct(string $originalName, string $name) + { + $this->name = $name; + $this->originalName = $originalName; + } + + public function getName(): string + { + return $this->name; + } + + public function getOriginalName(): string + { + return $this->originalName; + } + + public function setType(string $type): void + { + $this->array = false; + $this->type = $type; + + if ('|scalar' === substr($type, -7)) { + $this->scalarsAllowed = true; + $this->type = $type = substr($type, 0, -7); + } + + if ('[]' === substr($type, -2)) { + $this->array = true; + $this->type = substr($type, 0, -2); + } + } + + public function getType(): ?string + { + return $this->type; + } + + public function getContent(): ?string + { + return $this->content; + } + + public function setContent(string $content): void + { + $this->content = $content; + } + + public function isArray(): bool + { + return $this->array; + } + + public function areScalarsAllowed(): bool + { + return $this->scalarsAllowed; + } +} diff --git a/vendor/symfony/config/CHANGELOG.md b/vendor/symfony/config/CHANGELOG.md new file mode 100644 index 0000000..bc34ee5 --- /dev/null +++ b/vendor/symfony/config/CHANGELOG.md @@ -0,0 +1,134 @@ +CHANGELOG +========= + +6.0 +--- + + * Remove `BaseNode::getDeprecationMessage()` + +5.3.0 +----- + + * Add support for generating `ConfigBuilder` for extensions + +5.1.0 +----- + + * updated the signature of method `NodeDefinition::setDeprecated()` to `NodeDefinition::setDeprecation(string $package, string $version, string $message)` + * updated the signature of method `BaseNode::setDeprecated()` to `BaseNode::setDeprecation(string $package, string $version, string $message)` + * deprecated passing a null message to `BaseNode::setDeprecated()` to un-deprecate a node + * deprecated `BaseNode::getDeprecationMessage()`, use `BaseNode::getDeprecation()` instead + +5.0.0 +----- + + * Dropped support for constructing a `TreeBuilder` without passing root node information. + * Removed the `root()` method in `TreeBuilder`, pass the root node information to the constructor instead + * Added method `getChildNodeDefinitions()` to ParentNodeDefinitionInterface + * Removed `FileLoaderLoadException`, use `LoaderLoadException` instead + +4.4.0 +----- + + * added a way to exclude patterns of resources from being imported by the `import()` method + +4.3.0 +----- + + * deprecated using environment variables with `cannotBeEmpty()` if the value is validated with `validate()` + * made `Resource\*` classes final and not implement `Serializable` anymore + * deprecated the `root()` method in `TreeBuilder`, pass the root node information to the constructor instead + +4.2.0 +----- + + * deprecated constructing a `TreeBuilder` without passing root node information + * renamed `FileLoaderLoadException` to `LoaderLoadException` + +4.1.0 +----- + + * added `setPathSeparator` method to `NodeBuilder` class + * added third `$pathSeparator` constructor argument to `BaseNode` + * the `Processor` class has been made final + +4.0.0 +----- + + * removed `ConfigCachePass` + +3.4.0 +----- + + * added `setDeprecated()` method to indicate a deprecated node + * added `XmlUtils::parse()` method to parse an XML string + * deprecated `ConfigCachePass` + +3.3.0 +----- + + * added `ReflectionClassResource` class + * added second `$exists` constructor argument to `ClassExistenceResource` + * made `ClassExistenceResource` work with interfaces and traits + * added `ConfigCachePass` (originally in FrameworkBundle) + * added `castToArray()` helper to turn any config value into an array + +3.0.0 +----- + + * removed `ReferenceDumper` class + * removed the `ResourceInterface::isFresh()` method + * removed `BCResourceInterfaceChecker` class + * removed `ResourceInterface::getResource()` method + +2.8.0 +----- + +The edge case of defining just one value for nodes of type Enum is now allowed: + +```php +$rootNode + ->children() + ->enumNode('variable') + ->values(['value']) + ->end() + ->end() +; +``` + +Before: `InvalidArgumentException` (variable must contain at least two +distinct elements). +After: the code will work as expected and it will restrict the values of the +`variable` option to just `value`. + + * deprecated the `ResourceInterface::isFresh()` method. If you implement custom resource types and they + can be validated that way, make them implement the new `SelfCheckingResourceInterface`. + * deprecated the getResource() method in ResourceInterface. You can still call this method + on concrete classes implementing the interface, but it does not make sense at the interface + level as you need to know about the particular type of resource at hand to understand the + semantics of the returned value. + +2.7.0 +----- + + * added `ConfigCacheInterface`, `ConfigCacheFactoryInterface` and a basic `ConfigCacheFactory` + implementation to delegate creation of ConfigCache instances + +2.2.0 +----- + + * added `ArrayNodeDefinition::canBeEnabled()` and `ArrayNodeDefinition::canBeDisabled()` + to ease configuration when some sections are respectively disabled / enabled + by default. + * added a `normalizeKeys()` method for array nodes (to avoid key normalization) + * added numerical type handling for config definitions + * added convenience methods for optional configuration sections to `ArrayNodeDefinition` + * added a utils class for XML manipulations + +2.1.0 +----- + + * added a way to add documentation on configuration + * implemented `Serializable` on resources + * `LoaderResolverInterface` is now used instead of `LoaderResolver` for type + hinting diff --git a/vendor/symfony/config/ConfigCache.php b/vendor/symfony/config/ConfigCache.php new file mode 100644 index 0000000..8951941 --- /dev/null +++ b/vendor/symfony/config/ConfigCache.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\SelfCheckingResourceChecker; + +/** + * ConfigCache caches arbitrary content in files on disk. + * + * When in debug mode, those metadata resources that implement + * \Symfony\Component\Config\Resource\SelfCheckingResourceInterface will + * be used to check cache freshness. + * + * @author Fabien Potencier + * @author Matthias Pigulla + */ +class ConfigCache extends ResourceCheckerConfigCache +{ + private bool $debug; + + /** + * @param string $file The absolute cache path + * @param bool $debug Whether debugging is enabled or not + */ + public function __construct(string $file, bool $debug) + { + $this->debug = $debug; + + $checkers = []; + if (true === $this->debug) { + $checkers = [new SelfCheckingResourceChecker()]; + } + + parent::__construct($file, $checkers); + } + + /** + * Checks if the cache is still fresh. + * + * This implementation always returns true when debug is off and the + * cache file exists. + */ + public function isFresh(): bool + { + if (!$this->debug && is_file($this->getPath())) { + return true; + } + + return parent::isFresh(); + } +} diff --git a/vendor/symfony/config/ConfigCacheFactory.php b/vendor/symfony/config/ConfigCacheFactory.php new file mode 100644 index 0000000..86351c4 --- /dev/null +++ b/vendor/symfony/config/ConfigCacheFactory.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +/** + * Basic implementation of ConfigCacheFactoryInterface that + * creates an instance of the default ConfigCache. + * + * This factory and/or cache do not support cache validation + * by means of ResourceChecker instances (that is, service-based). + * + * @author Matthias Pigulla + */ +class ConfigCacheFactory implements ConfigCacheFactoryInterface +{ + private bool $debug; + + /** + * @param bool $debug The debug flag to pass to ConfigCache + */ + public function __construct(bool $debug) + { + $this->debug = $debug; + } + + /** + * {@inheritdoc} + */ + public function cache(string $file, callable $callback): ConfigCacheInterface + { + $cache = new ConfigCache($file, $this->debug); + if (!$cache->isFresh()) { + $callback($cache); + } + + return $cache; + } +} diff --git a/vendor/symfony/config/ConfigCacheFactoryInterface.php b/vendor/symfony/config/ConfigCacheFactoryInterface.php new file mode 100644 index 0000000..01c90d1 --- /dev/null +++ b/vendor/symfony/config/ConfigCacheFactoryInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +/** + * Interface for a ConfigCache factory. This factory creates + * an instance of ConfigCacheInterface and initializes the + * cache if necessary. + * + * @author Matthias Pigulla + */ +interface ConfigCacheFactoryInterface +{ + /** + * Creates a cache instance and (re-)initializes it if necessary. + * + * @param string $file The absolute cache file path + * @param callable $callable The callable to be executed when the cache needs to be filled (i. e. is not fresh). The cache will be passed as the only parameter to this callback + */ + public function cache(string $file, callable $callable): ConfigCacheInterface; +} diff --git a/vendor/symfony/config/ConfigCacheInterface.php b/vendor/symfony/config/ConfigCacheInterface.php new file mode 100644 index 0000000..e1763fc --- /dev/null +++ b/vendor/symfony/config/ConfigCacheInterface.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * Interface for ConfigCache. + * + * @author Matthias Pigulla + */ +interface ConfigCacheInterface +{ + /** + * Gets the cache file path. + */ + public function getPath(): string; + + /** + * Checks if the cache is still fresh. + * + * This check should take the metadata passed to the write() method into consideration. + */ + public function isFresh(): bool; + + /** + * Writes the given content into the cache file. Metadata will be stored + * independently and can be used to check cache freshness at a later time. + * + * @param string $content The content to write into the cache + * @param ResourceInterface[]|null $metadata An array of ResourceInterface instances + * + * @throws \RuntimeException When the cache file cannot be written + */ + public function write(string $content, array $metadata = null); +} diff --git a/vendor/symfony/config/Definition/ArrayNode.php b/vendor/symfony/config/Definition/ArrayNode.php new file mode 100644 index 0000000..07d01a6 --- /dev/null +++ b/vendor/symfony/config/Definition/ArrayNode.php @@ -0,0 +1,402 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; + +/** + * Represents an Array node in the config tree. + * + * @author Johannes M. Schmitt + */ +class ArrayNode extends BaseNode implements PrototypeNodeInterface +{ + protected $xmlRemappings = []; + protected $children = []; + protected $allowFalse = false; + protected $allowNewKeys = true; + protected $addIfNotSet = false; + protected $performDeepMerging = true; + protected $ignoreExtraKeys = false; + protected $removeExtraKeys = true; + protected $normalizeKeys = true; + + public function setNormalizeKeys(bool $normalizeKeys) + { + $this->normalizeKeys = $normalizeKeys; + } + + /** + * {@inheritdoc} + * + * Namely, you mostly have foo_bar in YAML while you have foo-bar in XML. + * After running this method, all keys are normalized to foo_bar. + * + * If you have a mixed key like foo-bar_moo, it will not be altered. + * The key will also not be altered if the target key already exists. + */ + protected function preNormalize(mixed $value): mixed + { + if (!$this->normalizeKeys || !\is_array($value)) { + return $value; + } + + $normalized = []; + + foreach ($value as $k => $v) { + if (str_contains($k, '-') && !str_contains($k, '_') && !\array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) { + $normalized[$normalizedKey] = $v; + } else { + $normalized[$k] = $v; + } + } + + return $normalized; + } + + /** + * Retrieves the children of this node. + * + * @return array + */ + public function getChildren(): array + { + return $this->children; + } + + /** + * Sets the xml remappings that should be performed. + * + * @param array $remappings An array of the form [[string, string]] + */ + public function setXmlRemappings(array $remappings) + { + $this->xmlRemappings = $remappings; + } + + /** + * Gets the xml remappings that should be performed. + * + * @return array an array of the form [[string, string]] + */ + public function getXmlRemappings(): array + { + return $this->xmlRemappings; + } + + /** + * Sets whether to add default values for this array if it has not been + * defined in any of the configuration files. + */ + public function setAddIfNotSet(bool $boolean) + { + $this->addIfNotSet = $boolean; + } + + /** + * Sets whether false is allowed as value indicating that the array should be unset. + */ + public function setAllowFalse(bool $allow) + { + $this->allowFalse = $allow; + } + + /** + * Sets whether new keys can be defined in subsequent configurations. + */ + public function setAllowNewKeys(bool $allow) + { + $this->allowNewKeys = $allow; + } + + /** + * Sets if deep merging should occur. + */ + public function setPerformDeepMerging(bool $boolean) + { + $this->performDeepMerging = $boolean; + } + + /** + * Whether extra keys should just be ignored without an exception. + * + * @param bool $boolean To allow extra keys + * @param bool $remove To remove extra keys + */ + public function setIgnoreExtraKeys(bool $boolean, bool $remove = true) + { + $this->ignoreExtraKeys = $boolean; + $this->removeExtraKeys = $this->ignoreExtraKeys && $remove; + } + + /** + * Returns true when extra keys should be ignored without an exception. + */ + public function shouldIgnoreExtraKeys(): bool + { + return $this->ignoreExtraKeys; + } + + /** + * {@inheritdoc} + */ + public function setName(string $name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function hasDefaultValue(): bool + { + return $this->addIfNotSet; + } + + /** + * {@inheritdoc} + */ + public function getDefaultValue(): mixed + { + if (!$this->hasDefaultValue()) { + throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath())); + } + + $defaults = []; + foreach ($this->children as $name => $child) { + if ($child->hasDefaultValue()) { + $defaults[$name] = $child->getDefaultValue(); + } + } + + return $defaults; + } + + /** + * Adds a child node. + * + * @throws \InvalidArgumentException when the child node has no name + * @throws \InvalidArgumentException when the child node's name is not unique + */ + public function addChild(NodeInterface $node) + { + $name = $node->getName(); + if ('' === $name) { + throw new \InvalidArgumentException('Child nodes must be named.'); + } + if (isset($this->children[$name])) { + throw new \InvalidArgumentException(sprintf('A child node named "%s" already exists.', $name)); + } + + $this->children[$name] = $node; + } + + /** + * {@inheritdoc} + * + * @throws UnsetKeyException + * @throws InvalidConfigurationException if the node doesn't have enough children + */ + protected function finalizeValue(mixed $value): mixed + { + if (false === $value) { + throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s.', $this->getPath(), json_encode($value))); + } + + foreach ($this->children as $name => $child) { + if (!\array_key_exists($name, $value)) { + if ($child->isRequired()) { + $message = sprintf('The child config "%s" under "%s" must be configured', $name, $this->getPath()); + if ($child->getInfo()) { + $message .= sprintf(': %s', $child->getInfo()); + } else { + $message .= '.'; + } + $ex = new InvalidConfigurationException($message); + $ex->setPath($this->getPath()); + + throw $ex; + } + + if ($child->hasDefaultValue()) { + $value[$name] = $child->getDefaultValue(); + } + + continue; + } + + if ($child->isDeprecated()) { + $deprecation = $child->getDeprecation($name, $this->getPath()); + trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']); + } + + try { + $value[$name] = $child->finalize($value[$name]); + } catch (UnsetKeyException $e) { + unset($value[$name]); + } + } + + return $value; + } + + /** + * {@inheritdoc} + */ + protected function validateType(mixed $value) + { + if (!\is_array($value) && (!$this->allowFalse || false !== $value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "array", but got "%s"', $this->getPath(), get_debug_type($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * {@inheritdoc} + * + * @throws InvalidConfigurationException + */ + protected function normalizeValue(mixed $value): mixed + { + if (false === $value) { + return $value; + } + + $value = $this->remapXml($value); + + $normalized = []; + foreach ($value as $name => $val) { + if (isset($this->children[$name])) { + try { + $normalized[$name] = $this->children[$name]->normalize($val); + } catch (UnsetKeyException $e) { + } + unset($value[$name]); + } elseif (!$this->removeExtraKeys) { + $normalized[$name] = $val; + } + } + + // if extra fields are present, throw exception + if (\count($value) && !$this->ignoreExtraKeys) { + $proposals = array_keys($this->children); + sort($proposals); + $guesses = []; + + foreach (array_keys($value) as $subject) { + $minScore = \INF; + foreach ($proposals as $proposal) { + $distance = levenshtein($subject, $proposal); + if ($distance <= $minScore && $distance < 3) { + $guesses[$proposal] = $distance; + $minScore = $distance; + } + } + } + + $msg = sprintf('Unrecognized option%s "%s" under "%s"', 1 === \count($value) ? '' : 's', implode(', ', array_keys($value)), $this->getPath()); + + if (\count($guesses)) { + asort($guesses); + $msg .= sprintf('. Did you mean "%s"?', implode('", "', array_keys($guesses))); + } else { + $msg .= sprintf('. Available option%s %s "%s".', 1 === \count($proposals) ? '' : 's', 1 === \count($proposals) ? 'is' : 'are', implode('", "', $proposals)); + } + + $ex = new InvalidConfigurationException($msg); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $normalized; + } + + /** + * Remaps multiple singular values to a single plural value. + */ + protected function remapXml(array $value): array + { + foreach ($this->xmlRemappings as [$singular, $plural]) { + if (!isset($value[$singular])) { + continue; + } + + $value[$plural] = Processor::normalizeConfig($value, $singular, $plural); + unset($value[$singular]); + } + + return $value; + } + + /** + * {@inheritdoc} + * + * @throws InvalidConfigurationException + * @throws \RuntimeException + */ + protected function mergeValues(mixed $leftSide, mixed $rightSide): mixed + { + if (false === $rightSide) { + // if this is still false after the last config has been merged the + // finalization pass will take care of removing this key entirely + return false; + } + + if (false === $leftSide || !$this->performDeepMerging) { + return $rightSide; + } + + foreach ($rightSide as $k => $v) { + // no conflict + if (!\array_key_exists($k, $leftSide)) { + if (!$this->allowNewKeys) { + $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file. If you are trying to overwrite an element, make sure you redefine it with the same name.', $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + + $leftSide[$k] = $v; + continue; + } + + if (!isset($this->children[$k])) { + if (!$this->ignoreExtraKeys || $this->removeExtraKeys) { + throw new \RuntimeException('merge() expects a normalized config array.'); + } + + $leftSide[$k] = $v; + continue; + } + + $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v); + } + + return $leftSide; + } + + /** + * {@inheritdoc} + */ + protected function allowPlaceholders(): bool + { + return false; + } +} diff --git a/vendor/symfony/config/Definition/BaseNode.php b/vendor/symfony/config/Definition/BaseNode.php new file mode 100644 index 0000000..d227336 --- /dev/null +++ b/vendor/symfony/config/Definition/BaseNode.php @@ -0,0 +1,510 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\Exception; +use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; + +/** + * The base node class. + * + * @author Johannes M. Schmitt + */ +abstract class BaseNode implements NodeInterface +{ + public const DEFAULT_PATH_SEPARATOR = '.'; + + private static array $placeholderUniquePrefixes = []; + private static array $placeholders = []; + + protected $name; + protected $parent; + protected $normalizationClosures = []; + protected $finalValidationClosures = []; + protected $allowOverwrite = true; + protected $required = false; + protected $deprecation = []; + protected $equivalentValues = []; + protected $attributes = []; + protected $pathSeparator; + + private mixed $handlingPlaceholder = null; + + /** + * @throws \InvalidArgumentException if the name contains a period + */ + public function __construct(?string $name, NodeInterface $parent = null, string $pathSeparator = self::DEFAULT_PATH_SEPARATOR) + { + if (str_contains($name = (string) $name, $pathSeparator)) { + throw new \InvalidArgumentException('The name must not contain ".'.$pathSeparator.'".'); + } + + $this->name = $name; + $this->parent = $parent; + $this->pathSeparator = $pathSeparator; + } + + /** + * Register possible (dummy) values for a dynamic placeholder value. + * + * Matching configuration values will be processed with a provided value, one by one. After a provided value is + * successfully processed the configuration value is returned as is, thus preserving the placeholder. + * + * @internal + */ + public static function setPlaceholder(string $placeholder, array $values): void + { + if (!$values) { + throw new \InvalidArgumentException('At least one value must be provided.'); + } + + self::$placeholders[$placeholder] = $values; + } + + /** + * Adds a common prefix for dynamic placeholder values. + * + * Matching configuration values will be skipped from being processed and are returned as is, thus preserving the + * placeholder. An exact match provided by {@see setPlaceholder()} might take precedence. + * + * @internal + */ + public static function setPlaceholderUniquePrefix(string $prefix): void + { + self::$placeholderUniquePrefixes[] = $prefix; + } + + /** + * Resets all current placeholders available. + * + * @internal + */ + public static function resetPlaceholders(): void + { + self::$placeholderUniquePrefixes = []; + self::$placeholders = []; + } + + public function setAttribute(string $key, mixed $value) + { + $this->attributes[$key] = $value; + } + + public function getAttribute(string $key, mixed $default = null): mixed + { + return $this->attributes[$key] ?? $default; + } + + public function hasAttribute(string $key): bool + { + return isset($this->attributes[$key]); + } + + public function getAttributes(): array + { + return $this->attributes; + } + + public function setAttributes(array $attributes) + { + $this->attributes = $attributes; + } + + public function removeAttribute(string $key) + { + unset($this->attributes[$key]); + } + + /** + * Sets an info message. + */ + public function setInfo(string $info) + { + $this->setAttribute('info', $info); + } + + /** + * Returns info message. + */ + public function getInfo(): ?string + { + return $this->getAttribute('info'); + } + + /** + * Sets the example configuration for this node. + */ + public function setExample(string|array $example) + { + $this->setAttribute('example', $example); + } + + /** + * Retrieves the example configuration for this node. + */ + public function getExample(): string|array|null + { + return $this->getAttribute('example'); + } + + /** + * Adds an equivalent value. + */ + public function addEquivalentValue(mixed $originalValue, mixed $equivalentValue) + { + $this->equivalentValues[] = [$originalValue, $equivalentValue]; + } + + /** + * Set this node as required. + */ + public function setRequired(bool $boolean) + { + $this->required = $boolean; + } + + /** + * Sets this node as deprecated. + * + * @param string $package The name of the composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message the deprecation message to use + * + * You can use %node% and %path% placeholders in your message to display, + * respectively, the node name and its complete path + */ + public function setDeprecated(string $package, string $version, string $message = 'The child node "%node%" at path "%path%" is deprecated.') + { + $this->deprecation = [ + 'package' => $package, + 'version' => $version, + 'message' => $message, + ]; + } + + /** + * Sets if this node can be overridden. + */ + public function setAllowOverwrite(bool $allow) + { + $this->allowOverwrite = $allow; + } + + /** + * Sets the closures used for normalization. + * + * @param \Closure[] $closures An array of Closures used for normalization + */ + public function setNormalizationClosures(array $closures) + { + $this->normalizationClosures = $closures; + } + + /** + * Sets the closures used for final validation. + * + * @param \Closure[] $closures An array of Closures used for final validation + */ + public function setFinalValidationClosures(array $closures) + { + $this->finalValidationClosures = $closures; + } + + /** + * {@inheritdoc} + */ + public function isRequired(): bool + { + return $this->required; + } + + /** + * Checks if this node is deprecated. + */ + public function isDeprecated(): bool + { + return (bool) $this->deprecation; + } + + /** + * @param string $node The configuration node name + * @param string $path The path of the node + */ + public function getDeprecation(string $node, string $path): array + { + return [ + 'package' => $this->deprecation['package'], + 'version' => $this->deprecation['version'], + 'message' => strtr($this->deprecation['message'], ['%node%' => $node, '%path%' => $path]), + ]; + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getPath(): string + { + if (null !== $this->parent) { + return $this->parent->getPath().$this->pathSeparator.$this->name; + } + + return $this->name; + } + + /** + * {@inheritdoc} + */ + final public function merge(mixed $leftSide, mixed $rightSide): mixed + { + if (!$this->allowOverwrite) { + throw new ForbiddenOverwriteException(sprintf('Configuration path "%s" cannot be overwritten. You have to define all options for this path, and any of its sub-paths in one configuration section.', $this->getPath())); + } + + if ($leftSide !== $leftPlaceholders = self::resolvePlaceholderValue($leftSide)) { + foreach ($leftPlaceholders as $leftPlaceholder) { + $this->handlingPlaceholder = $leftSide; + try { + $this->merge($leftPlaceholder, $rightSide); + } finally { + $this->handlingPlaceholder = null; + } + } + + return $rightSide; + } + + if ($rightSide !== $rightPlaceholders = self::resolvePlaceholderValue($rightSide)) { + foreach ($rightPlaceholders as $rightPlaceholder) { + $this->handlingPlaceholder = $rightSide; + try { + $this->merge($leftSide, $rightPlaceholder); + } finally { + $this->handlingPlaceholder = null; + } + } + + return $rightSide; + } + + $this->doValidateType($leftSide); + $this->doValidateType($rightSide); + + return $this->mergeValues($leftSide, $rightSide); + } + + /** + * {@inheritdoc} + */ + final public function normalize(mixed $value): mixed + { + $value = $this->preNormalize($value); + + // run custom normalization closures + foreach ($this->normalizationClosures as $closure) { + $value = $closure($value); + } + + // resolve placeholder value + if ($value !== $placeholders = self::resolvePlaceholderValue($value)) { + foreach ($placeholders as $placeholder) { + $this->handlingPlaceholder = $value; + try { + $this->normalize($placeholder); + } finally { + $this->handlingPlaceholder = null; + } + } + + return $value; + } + + // replace value with their equivalent + foreach ($this->equivalentValues as $data) { + if ($data[0] === $value) { + $value = $data[1]; + } + } + + // validate type + $this->doValidateType($value); + + // normalize value + return $this->normalizeValue($value); + } + + /** + * Normalizes the value before any other normalization is applied. + */ + protected function preNormalize(mixed $value): mixed + { + return $value; + } + + /** + * Returns parent node for this node. + */ + public function getParent(): ?NodeInterface + { + return $this->parent; + } + + /** + * {@inheritdoc} + */ + final public function finalize(mixed $value): mixed + { + if ($value !== $placeholders = self::resolvePlaceholderValue($value)) { + foreach ($placeholders as $placeholder) { + $this->handlingPlaceholder = $value; + try { + $this->finalize($placeholder); + } finally { + $this->handlingPlaceholder = null; + } + } + + return $value; + } + + $this->doValidateType($value); + + $value = $this->finalizeValue($value); + + // Perform validation on the final value if a closure has been set. + // The closure is also allowed to return another value. + foreach ($this->finalValidationClosures as $closure) { + try { + $value = $closure($value); + } catch (Exception $e) { + if ($e instanceof UnsetKeyException && null !== $this->handlingPlaceholder) { + continue; + } + + throw $e; + } catch (\Exception $e) { + throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": ', $this->getPath()).$e->getMessage(), $e->getCode(), $e); + } + } + + return $value; + } + + /** + * Validates the type of a Node. + * + * @throws InvalidTypeException when the value is invalid + */ + abstract protected function validateType(mixed $value); + + /** + * Normalizes the value. + */ + abstract protected function normalizeValue(mixed $value): mixed; + + /** + * Merges two values together. + */ + abstract protected function mergeValues(mixed $leftSide, mixed $rightSide): mixed; + + /** + * Finalizes a value. + */ + abstract protected function finalizeValue(mixed $value): mixed; + + /** + * Tests if placeholder values are allowed for this node. + */ + protected function allowPlaceholders(): bool + { + return true; + } + + /** + * Tests if a placeholder is being handled currently. + */ + protected function isHandlingPlaceholder(): bool + { + return null !== $this->handlingPlaceholder; + } + + /** + * Gets allowed dynamic types for this node. + */ + protected function getValidPlaceholderTypes(): array + { + return []; + } + + private static function resolvePlaceholderValue(mixed $value): mixed + { + if (\is_string($value)) { + if (isset(self::$placeholders[$value])) { + return self::$placeholders[$value]; + } + + foreach (self::$placeholderUniquePrefixes as $placeholderUniquePrefix) { + if (str_starts_with($value, $placeholderUniquePrefix)) { + return []; + } + } + } + + return $value; + } + + private function doValidateType(mixed $value): void + { + if (null !== $this->handlingPlaceholder && !$this->allowPlaceholders()) { + $e = new InvalidTypeException(sprintf('A dynamic value is not compatible with a "%s" node type at path "%s".', static::class, $this->getPath())); + $e->setPath($this->getPath()); + + throw $e; + } + + if (null === $this->handlingPlaceholder || null === $value) { + $this->validateType($value); + + return; + } + + $knownTypes = array_keys(self::$placeholders[$this->handlingPlaceholder]); + $validTypes = $this->getValidPlaceholderTypes(); + + if ($validTypes && array_diff($knownTypes, $validTypes)) { + $e = new InvalidTypeException(sprintf( + 'Invalid type for path "%s". Expected %s, but got %s.', + $this->getPath(), + 1 === \count($validTypes) ? '"'.reset($validTypes).'"' : 'one of "'.implode('", "', $validTypes).'"', + 1 === \count($knownTypes) ? '"'.reset($knownTypes).'"' : 'one of "'.implode('", "', $knownTypes).'"' + )); + if ($hint = $this->getInfo()) { + $e->addHint($hint); + } + $e->setPath($this->getPath()); + + throw $e; + } + + $this->validateType($value); + } +} diff --git a/vendor/symfony/config/Definition/BooleanNode.php b/vendor/symfony/config/Definition/BooleanNode.php new file mode 100644 index 0000000..7798567 --- /dev/null +++ b/vendor/symfony/config/Definition/BooleanNode.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a Boolean value in the config tree. + * + * @author Johannes M. Schmitt + */ +class BooleanNode extends ScalarNode +{ + /** + * {@inheritdoc} + */ + protected function validateType(mixed $value) + { + if (!\is_bool($value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "bool", but got "%s".', $this->getPath(), get_debug_type($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * {@inheritdoc} + */ + protected function isValueEmpty(mixed $value): bool + { + // a boolean value cannot be empty + return false; + } + + /** + * {@inheritdoc} + */ + protected function getValidPlaceholderTypes(): array + { + return ['bool']; + } +} diff --git a/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php b/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php new file mode 100644 index 0000000..f81f624 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php @@ -0,0 +1,525 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\PrototypedArrayNode; + +/** + * This class provides a fluent interface for defining an array node. + * + * @author Johannes M. Schmitt + */ +class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinitionInterface +{ + protected $performDeepMerging = true; + protected $ignoreExtraKeys = false; + protected $removeExtraKeys = true; + protected $children = []; + protected $prototype; + protected $atLeastOne = false; + protected $allowNewKeys = true; + protected $key; + protected $removeKeyItem; + protected $addDefaults = false; + protected $addDefaultChildren = false; + protected $nodeBuilder; + protected $normalizeKeys = true; + + /** + * {@inheritdoc} + */ + public function __construct(?string $name, NodeParentInterface $parent = null) + { + parent::__construct($name, $parent); + + $this->nullEquivalent = []; + $this->trueEquivalent = []; + } + + /** + * {@inheritdoc} + */ + public function setBuilder(NodeBuilder $builder) + { + $this->nodeBuilder = $builder; + } + + /** + * {@inheritdoc} + */ + public function children(): NodeBuilder + { + return $this->getNodeBuilder(); + } + + /** + * Sets a prototype for child nodes. + */ + public function prototype(string $type): NodeDefinition + { + return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this); + } + + public function variablePrototype(): VariableNodeDefinition + { + return $this->prototype('variable'); + } + + public function scalarPrototype(): ScalarNodeDefinition + { + return $this->prototype('scalar'); + } + + public function booleanPrototype(): BooleanNodeDefinition + { + return $this->prototype('boolean'); + } + + public function integerPrototype(): IntegerNodeDefinition + { + return $this->prototype('integer'); + } + + public function floatPrototype(): FloatNodeDefinition + { + return $this->prototype('float'); + } + + public function arrayPrototype(): self + { + return $this->prototype('array'); + } + + public function enumPrototype(): EnumNodeDefinition + { + return $this->prototype('enum'); + } + + /** + * Adds the default value if the node is not set in the configuration. + * + * This method is applicable to concrete nodes only (not to prototype nodes). + * If this function has been called and the node is not set during the finalization + * phase, it's default value will be derived from its children default values. + * + * @return $this + */ + public function addDefaultsIfNotSet(): static + { + $this->addDefaults = true; + + return $this; + } + + /** + * Adds children with a default value when none are defined. + * + * This method is applicable to prototype nodes only. + * + * @param int|string|array|null $children The number of children|The child name|The children names to be added + * + * @return $this + */ + public function addDefaultChildrenIfNoneSet(int|string|array $children = null): static + { + $this->addDefaultChildren = $children; + + return $this; + } + + /** + * Requires the node to have at least one element. + * + * This method is applicable to prototype nodes only. + * + * @return $this + */ + public function requiresAtLeastOneElement(): static + { + $this->atLeastOne = true; + + return $this; + } + + /** + * Disallows adding news keys in a subsequent configuration. + * + * If used all keys have to be defined in the same configuration file. + * + * @return $this + */ + public function disallowNewKeysInSubsequentConfigs(): static + { + $this->allowNewKeys = false; + + return $this; + } + + /** + * Sets a normalization rule for XML configurations. + * + * @param string $singular The key to remap + * @param string|null $plural The plural of the key for irregular plurals + * + * @return $this + */ + public function fixXmlConfig(string $singular, string $plural = null): static + { + $this->normalization()->remap($singular, $plural); + + return $this; + } + + /** + * Sets the attribute which value is to be used as key. + * + * This is useful when you have an indexed array that should be an + * associative array. You can select an item from within the array + * to be the key of the particular item. For example, if "id" is the + * "key", then: + * + * [ + * ['id' => 'my_name', 'foo' => 'bar'], + * ]; + * + * becomes + * + * [ + * 'my_name' => ['foo' => 'bar'], + * ]; + * + * If you'd like "'id' => 'my_name'" to still be present in the resulting + * array, then you can set the second argument of this method to false. + * + * This method is applicable to prototype nodes only. + * + * @param string $name The name of the key + * @param bool $removeKeyItem Whether or not the key item should be removed + * + * @return $this + */ + public function useAttributeAsKey(string $name, bool $removeKeyItem = true): static + { + $this->key = $name; + $this->removeKeyItem = $removeKeyItem; + + return $this; + } + + /** + * Sets whether the node can be unset. + * + * @return $this + */ + public function canBeUnset(bool $allow = true): static + { + $this->merge()->allowUnset($allow); + + return $this; + } + + /** + * Adds an "enabled" boolean to enable the current section. + * + * By default, the section is disabled. If any configuration is specified then + * the node will be automatically enabled: + * + * enableableArrayNode: {enabled: true, ...} # The config is enabled & default values get overridden + * enableableArrayNode: ~ # The config is enabled & use the default values + * enableableArrayNode: true # The config is enabled & use the default values + * enableableArrayNode: {other: value, ...} # The config is enabled & default values get overridden + * enableableArrayNode: {enabled: false, ...} # The config is disabled + * enableableArrayNode: false # The config is disabled + * + * @return $this + */ + public function canBeEnabled(): static + { + $this + ->addDefaultsIfNotSet() + ->treatFalseLike(['enabled' => false]) + ->treatTrueLike(['enabled' => true]) + ->treatNullLike(['enabled' => true]) + ->beforeNormalization() + ->ifArray() + ->then(function (array $v) { + $v['enabled'] = $v['enabled'] ?? true; + + return $v; + }) + ->end() + ->children() + ->booleanNode('enabled') + ->defaultFalse() + ; + + return $this; + } + + /** + * Adds an "enabled" boolean to enable the current section. + * + * By default, the section is enabled. + * + * @return $this + */ + public function canBeDisabled(): static + { + $this + ->addDefaultsIfNotSet() + ->treatFalseLike(['enabled' => false]) + ->treatTrueLike(['enabled' => true]) + ->treatNullLike(['enabled' => true]) + ->children() + ->booleanNode('enabled') + ->defaultTrue() + ; + + return $this; + } + + /** + * Disables the deep merging of the node. + * + * @return $this + */ + public function performNoDeepMerging(): static + { + $this->performDeepMerging = false; + + return $this; + } + + /** + * Allows extra config keys to be specified under an array without + * throwing an exception. + * + * Those config values are ignored and removed from the resulting + * array. This should be used only in special cases where you want + * to send an entire configuration array through a special tree that + * processes only part of the array. + * + * @param bool $remove Whether to remove the extra keys + * + * @return $this + */ + public function ignoreExtraKeys(bool $remove = true): static + { + $this->ignoreExtraKeys = true; + $this->removeExtraKeys = $remove; + + return $this; + } + + /** + * Sets whether to enable key normalization. + * + * @return $this + */ + public function normalizeKeys(bool $bool): static + { + $this->normalizeKeys = $bool; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function append(NodeDefinition $node): static + { + $this->children[$node->name] = $node->setParent($this); + + return $this; + } + + /** + * Returns a node builder to be used to add children and prototype. + */ + protected function getNodeBuilder(): NodeBuilder + { + if (null === $this->nodeBuilder) { + $this->nodeBuilder = new NodeBuilder(); + } + + return $this->nodeBuilder->setParent($this); + } + + /** + * {@inheritdoc} + */ + protected function createNode(): NodeInterface + { + if (null === $this->prototype) { + $node = new ArrayNode($this->name, $this->parent, $this->pathSeparator); + + $this->validateConcreteNode($node); + + $node->setAddIfNotSet($this->addDefaults); + + foreach ($this->children as $child) { + $child->parent = $node; + $node->addChild($child->getNode()); + } + } else { + $node = new PrototypedArrayNode($this->name, $this->parent, $this->pathSeparator); + + $this->validatePrototypeNode($node); + + if (null !== $this->key) { + $node->setKeyAttribute($this->key, $this->removeKeyItem); + } + + if (true === $this->atLeastOne || false === $this->allowEmptyValue) { + $node->setMinNumberOfElements(1); + } + + if ($this->default) { + if (!\is_array($this->defaultValue)) { + throw new \InvalidArgumentException(sprintf('%s: the default value of an array node has to be an array.', $node->getPath())); + } + + $node->setDefaultValue($this->defaultValue); + } + + if (false !== $this->addDefaultChildren) { + $node->setAddChildrenIfNoneSet($this->addDefaultChildren); + if ($this->prototype instanceof static && null === $this->prototype->prototype) { + $this->prototype->addDefaultsIfNotSet(); + } + } + + $this->prototype->parent = $node; + $node->setPrototype($this->prototype->getNode()); + } + + $node->setAllowNewKeys($this->allowNewKeys); + $node->addEquivalentValue(null, $this->nullEquivalent); + $node->addEquivalentValue(true, $this->trueEquivalent); + $node->addEquivalentValue(false, $this->falseEquivalent); + $node->setPerformDeepMerging($this->performDeepMerging); + $node->setRequired($this->required); + $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys); + $node->setNormalizeKeys($this->normalizeKeys); + + if ($this->deprecation) { + $node->setDeprecated($this->deprecation['package'], $this->deprecation['version'], $this->deprecation['message']); + } + + if (null !== $this->normalization) { + $node->setNormalizationClosures($this->normalization->before); + $node->setXmlRemappings($this->normalization->remappings); + } + + if (null !== $this->merge) { + $node->setAllowOverwrite($this->merge->allowOverwrite); + $node->setAllowFalse($this->merge->allowFalse); + } + + if (null !== $this->validation) { + $node->setFinalValidationClosures($this->validation->rules); + } + + return $node; + } + + /** + * Validate the configuration of a concrete node. + * + * @throws InvalidDefinitionException + */ + protected function validateConcreteNode(ArrayNode $node) + { + $path = $node->getPath(); + + if (null !== $this->key) { + throw new InvalidDefinitionException(sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s".', $path)); + } + + if (false === $this->allowEmptyValue) { + throw new InvalidDefinitionException(sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s".', $path)); + } + + if (true === $this->atLeastOne) { + throw new InvalidDefinitionException(sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s".', $path)); + } + + if ($this->default) { + throw new InvalidDefinitionException(sprintf('->defaultValue() is not applicable to concrete nodes at path "%s".', $path)); + } + + if (false !== $this->addDefaultChildren) { + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s".', $path)); + } + } + + /** + * Validate the configuration of a prototype node. + * + * @throws InvalidDefinitionException + */ + protected function validatePrototypeNode(PrototypedArrayNode $node) + { + $path = $node->getPath(); + + if ($this->addDefaults) { + throw new InvalidDefinitionException(sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s".', $path)); + } + + if (false !== $this->addDefaultChildren) { + if ($this->default) { + throw new InvalidDefinitionException(sprintf('A default value and default children might not be used together at path "%s".', $path)); + } + + if (null !== $this->key && (null === $this->addDefaultChildren || \is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s".', $path)); + } + + if (null === $this->key && (\is_string($this->addDefaultChildren) || \is_array($this->addDefaultChildren))) { + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s".', $path)); + } + } + } + + /** + * @return NodeDefinition[] + */ + public function getChildNodeDefinitions(): array + { + return $this->children; + } + + /** + * Finds a node defined by the given $nodePath. + * + * @param string $nodePath The path of the node to find. e.g "doctrine.orm.mappings" + */ + public function find(string $nodePath): NodeDefinition + { + $firstPathSegment = (false === $pathSeparatorPos = strpos($nodePath, $this->pathSeparator)) + ? $nodePath + : substr($nodePath, 0, $pathSeparatorPos); + + if (null === $node = ($this->children[$firstPathSegment] ?? null)) { + throw new \RuntimeException(sprintf('Node with name "%s" does not exist in the current node "%s".', $firstPathSegment, $this->name)); + } + + if (false === $pathSeparatorPos) { + return $node; + } + + return $node->find(substr($nodePath, $pathSeparatorPos + \strlen($this->pathSeparator))); + } +} diff --git a/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php b/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php new file mode 100644 index 0000000..2f26975 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\BooleanNode; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class BooleanNodeDefinition extends ScalarNodeDefinition +{ + /** + * {@inheritdoc} + */ + public function __construct(?string $name, NodeParentInterface $parent = null) + { + parent::__construct($name, $parent); + + $this->nullEquivalent = true; + } + + /** + * Instantiate a Node. + */ + protected function instantiateNode(): BooleanNode + { + return new BooleanNode($this->name, $this->parent, $this->pathSeparator); + } + + /** + * {@inheritdoc} + * + * @throws InvalidDefinitionException + */ + public function cannotBeEmpty(): static + { + throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to BooleanNodeDefinition.'); + } +} diff --git a/vendor/symfony/config/Definition/Builder/BuilderAwareInterface.php b/vendor/symfony/config/Definition/Builder/BuilderAwareInterface.php new file mode 100644 index 0000000..f30b873 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/BuilderAwareInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * An interface that can be implemented by nodes which build other nodes. + * + * @author Roland Franssen + */ +interface BuilderAwareInterface +{ + /** + * Sets a custom children builder. + */ + public function setBuilder(NodeBuilder $builder); +} diff --git a/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php b/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php new file mode 100644 index 0000000..4ad47c2 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\EnumNode; + +/** + * Enum Node Definition. + * + * @author Johannes M. Schmitt + */ +class EnumNodeDefinition extends ScalarNodeDefinition +{ + private array $values; + + /** + * @return $this + */ + public function values(array $values): static + { + $values = array_unique($values); + + if (empty($values)) { + throw new \InvalidArgumentException('->values() must be called with at least one value.'); + } + + $this->values = $values; + + return $this; + } + + /** + * Instantiate a Node. + * + * @throws \RuntimeException + */ + protected function instantiateNode(): EnumNode + { + if (!isset($this->values)) { + throw new \RuntimeException('You must call ->values() on enum nodes.'); + } + + return new EnumNode($this->name, $this->parent, $this->values, $this->pathSeparator); + } +} diff --git a/vendor/symfony/config/Definition/Builder/ExprBuilder.php b/vendor/symfony/config/Definition/Builder/ExprBuilder.php new file mode 100644 index 0000000..a9f158e --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/ExprBuilder.php @@ -0,0 +1,242 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; + +/** + * This class builds an if expression. + * + * @author Johannes M. Schmitt + * @author Christophe Coevoet + */ +class ExprBuilder +{ + protected $node; + public $ifPart; + public $thenPart; + + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Marks the expression as being always used. + * + * @return $this + */ + public function always(\Closure $then = null): static + { + $this->ifPart = function () { return true; }; + + if (null !== $then) { + $this->thenPart = $then; + } + + return $this; + } + + /** + * Sets a closure to use as tests. + * + * The default one tests if the value is true. + * + * @return $this + */ + public function ifTrue(\Closure $closure = null): static + { + if (null === $closure) { + $closure = function ($v) { return true === $v; }; + } + + $this->ifPart = $closure; + + return $this; + } + + /** + * Tests if the value is a string. + * + * @return $this + */ + public function ifString(): static + { + $this->ifPart = function ($v) { return \is_string($v); }; + + return $this; + } + + /** + * Tests if the value is null. + * + * @return $this + */ + public function ifNull(): static + { + $this->ifPart = function ($v) { return null === $v; }; + + return $this; + } + + /** + * Tests if the value is empty. + * + * @return $this + */ + public function ifEmpty(): static + { + $this->ifPart = function ($v) { return empty($v); }; + + return $this; + } + + /** + * Tests if the value is an array. + * + * @return $this + */ + public function ifArray(): static + { + $this->ifPart = function ($v) { return \is_array($v); }; + + return $this; + } + + /** + * Tests if the value is in an array. + * + * @return $this + */ + public function ifInArray(array $array): static + { + $this->ifPart = function ($v) use ($array) { return \in_array($v, $array, true); }; + + return $this; + } + + /** + * Tests if the value is not in an array. + * + * @return $this + */ + public function ifNotInArray(array $array): static + { + $this->ifPart = function ($v) use ($array) { return !\in_array($v, $array, true); }; + + return $this; + } + + /** + * Transforms variables of any type into an array. + * + * @return $this + */ + public function castToArray(): static + { + $this->ifPart = function ($v) { return !\is_array($v); }; + $this->thenPart = function ($v) { return [$v]; }; + + return $this; + } + + /** + * Sets the closure to run if the test pass. + * + * @return $this + */ + public function then(\Closure $closure): static + { + $this->thenPart = $closure; + + return $this; + } + + /** + * Sets a closure returning an empty array. + * + * @return $this + */ + public function thenEmptyArray(): static + { + $this->thenPart = function () { return []; }; + + return $this; + } + + /** + * Sets a closure marking the value as invalid at processing time. + * + * if you want to add the value of the node in your message just use a %s placeholder. + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function thenInvalid(string $message): static + { + $this->thenPart = function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; + + return $this; + } + + /** + * Sets a closure unsetting this key of the array at processing time. + * + * @return $this + * + * @throws UnsetKeyException + */ + public function thenUnset(): static + { + $this->thenPart = function () { throw new UnsetKeyException('Unsetting key.'); }; + + return $this; + } + + /** + * Returns the related node. + * + * @throws \RuntimeException + */ + public function end(): NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition + { + if (null === $this->ifPart) { + throw new \RuntimeException('You must specify an if part.'); + } + if (null === $this->thenPart) { + throw new \RuntimeException('You must specify a then part.'); + } + + return $this->node; + } + + /** + * Builds the expressions. + * + * @param ExprBuilder[] $expressions An array of ExprBuilder instances to build + */ + public static function buildExpressions(array $expressions): array + { + foreach ($expressions as $k => $expr) { + if ($expr instanceof self) { + $if = $expr->ifPart; + $then = $expr->thenPart; + $expressions[$k] = function ($v) use ($if, $then) { + return $if($v) ? $then($v) : $v; + }; + } + } + + return $expressions; + } +} diff --git a/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php b/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php new file mode 100644 index 0000000..337e971 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\FloatNode; + +/** + * This class provides a fluent interface for defining a float node. + * + * @author Jeanmonod David + */ +class FloatNodeDefinition extends NumericNodeDefinition +{ + /** + * Instantiates a Node. + */ + protected function instantiateNode(): FloatNode + { + return new FloatNode($this->name, $this->parent, $this->min, $this->max, $this->pathSeparator); + } +} diff --git a/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php b/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php new file mode 100644 index 0000000..2af81df --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\IntegerNode; + +/** + * This class provides a fluent interface for defining an integer node. + * + * @author Jeanmonod David + */ +class IntegerNodeDefinition extends NumericNodeDefinition +{ + /** + * Instantiates a Node. + */ + protected function instantiateNode(): IntegerNode + { + return new IntegerNode($this->name, $this->parent, $this->min, $this->max, $this->pathSeparator); + } +} diff --git a/vendor/symfony/config/Definition/Builder/MergeBuilder.php b/vendor/symfony/config/Definition/Builder/MergeBuilder.php new file mode 100644 index 0000000..f8980a6 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/MergeBuilder.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class builds merge conditions. + * + * @author Johannes M. Schmitt + */ +class MergeBuilder +{ + protected $node; + public $allowFalse = false; + public $allowOverwrite = true; + + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Sets whether the node can be unset. + * + * @return $this + */ + public function allowUnset(bool $allow = true): static + { + $this->allowFalse = $allow; + + return $this; + } + + /** + * Sets whether the node can be overwritten. + * + * @return $this + */ + public function denyOverwrite(bool $deny = true): static + { + $this->allowOverwrite = !$deny; + + return $this; + } + + /** + * Returns the related node. + */ + public function end(): NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition + { + return $this->node; + } +} diff --git a/vendor/symfony/config/Definition/Builder/NodeBuilder.php b/vendor/symfony/config/Definition/Builder/NodeBuilder.php new file mode 100644 index 0000000..89d6adb --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/NodeBuilder.php @@ -0,0 +1,201 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class provides a fluent interface for building a node. + * + * @author Johannes M. Schmitt + */ +class NodeBuilder implements NodeParentInterface +{ + protected $parent; + protected $nodeMapping; + + public function __construct() + { + $this->nodeMapping = [ + 'variable' => VariableNodeDefinition::class, + 'scalar' => ScalarNodeDefinition::class, + 'boolean' => BooleanNodeDefinition::class, + 'integer' => IntegerNodeDefinition::class, + 'float' => FloatNodeDefinition::class, + 'array' => ArrayNodeDefinition::class, + 'enum' => EnumNodeDefinition::class, + ]; + } + + /** + * Set the parent node. + * + * @return $this + */ + public function setParent(ParentNodeDefinitionInterface $parent = null): static + { + $this->parent = $parent; + + return $this; + } + + /** + * Creates a child array node. + */ + public function arrayNode(string $name): ArrayNodeDefinition + { + return $this->node($name, 'array'); + } + + /** + * Creates a child scalar node. + */ + public function scalarNode(string $name): ScalarNodeDefinition + { + return $this->node($name, 'scalar'); + } + + /** + * Creates a child Boolean node. + */ + public function booleanNode(string $name): BooleanNodeDefinition + { + return $this->node($name, 'boolean'); + } + + /** + * Creates a child integer node. + */ + public function integerNode(string $name): IntegerNodeDefinition + { + return $this->node($name, 'integer'); + } + + /** + * Creates a child float node. + */ + public function floatNode(string $name): FloatNodeDefinition + { + return $this->node($name, 'float'); + } + + /** + * Creates a child EnumNode. + */ + public function enumNode(string $name): EnumNodeDefinition + { + return $this->node($name, 'enum'); + } + + /** + * Creates a child variable node. + */ + public function variableNode(string $name): VariableNodeDefinition + { + return $this->node($name, 'variable'); + } + + /** + * Returns the parent node. + * + * @return NodeDefinition&ParentNodeDefinitionInterface + */ + public function end() + { + return $this->parent; + } + + /** + * Creates a child node. + * + * @throws \RuntimeException When the node type is not registered + * @throws \RuntimeException When the node class is not found + */ + public function node(?string $name, string $type): NodeDefinition + { + $class = $this->getNodeClass($type); + + $node = new $class($name); + + $this->append($node); + + return $node; + } + + /** + * Appends a node definition. + * + * Usage: + * + * $node = new ArrayNodeDefinition('name') + * ->children() + * ->scalarNode('foo')->end() + * ->scalarNode('baz')->end() + * ->append($this->getBarNodeDefinition()) + * ->end() + * ; + * + * @return $this + */ + public function append(NodeDefinition $node): static + { + if ($node instanceof BuilderAwareInterface) { + $builder = clone $this; + $builder->setParent(null); + $node->setBuilder($builder); + } + + if (null !== $this->parent) { + $this->parent->append($node); + // Make this builder the node parent to allow for a fluid interface + $node->setParent($this); + } + + return $this; + } + + /** + * Adds or overrides a node Type. + * + * @param string $type The name of the type + * @param string $class The fully qualified name the node definition class + * + * @return $this + */ + public function setNodeClass(string $type, string $class): static + { + $this->nodeMapping[strtolower($type)] = $class; + + return $this; + } + + /** + * Returns the class name of the node definition. + * + * @throws \RuntimeException When the node type is not registered + * @throws \RuntimeException When the node class is not found + */ + protected function getNodeClass(string $type): string + { + $type = strtolower($type); + + if (!isset($this->nodeMapping[$type])) { + throw new \RuntimeException(sprintf('The node type "%s" is not registered.', $type)); + } + + $class = $this->nodeMapping[$type]; + + if (!class_exists($class)) { + throw new \RuntimeException(sprintf('The node class "%s" does not exist.', $class)); + } + + return $class; + } +} diff --git a/vendor/symfony/config/Definition/Builder/NodeDefinition.php b/vendor/symfony/config/Definition/Builder/NodeDefinition.php new file mode 100644 index 0000000..173b08c --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/NodeDefinition.php @@ -0,0 +1,342 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\BaseNode; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +use Symfony\Component\Config\Definition\NodeInterface; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +abstract class NodeDefinition implements NodeParentInterface +{ + protected $name; + protected $normalization; + protected $validation; + protected $defaultValue; + protected $default = false; + protected $required = false; + protected $deprecation = []; + protected $merge; + protected $allowEmptyValue = true; + protected $nullEquivalent; + protected $trueEquivalent = true; + protected $falseEquivalent = false; + protected $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR; + protected $parent; + protected $attributes = []; + + public function __construct(?string $name, NodeParentInterface $parent = null) + { + $this->parent = $parent; + $this->name = $name; + } + + /** + * Sets the parent node. + * + * @return $this + */ + public function setParent(NodeParentInterface $parent): static + { + $this->parent = $parent; + + return $this; + } + + /** + * Sets info message. + * + * @return $this + */ + public function info(string $info): static + { + return $this->attribute('info', $info); + } + + /** + * Sets example configuration. + * + * @return $this + */ + public function example(string|array $example): static + { + return $this->attribute('example', $example); + } + + /** + * Sets an attribute on the node. + * + * @return $this + */ + public function attribute(string $key, mixed $value): static + { + $this->attributes[$key] = $value; + + return $this; + } + + /** + * Returns the parent node. + */ + public function end(): NodeParentInterface|NodeBuilder|NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition|null + { + return $this->parent; + } + + /** + * Creates the node. + */ + public function getNode(bool $forceRootNode = false): NodeInterface + { + if ($forceRootNode) { + $this->parent = null; + } + + if (null !== $this->normalization) { + $this->normalization->before = ExprBuilder::buildExpressions($this->normalization->before); + } + + if (null !== $this->validation) { + $this->validation->rules = ExprBuilder::buildExpressions($this->validation->rules); + } + + $node = $this->createNode(); + if ($node instanceof BaseNode) { + $node->setAttributes($this->attributes); + } + + return $node; + } + + /** + * Sets the default value. + * + * @return $this + */ + public function defaultValue(mixed $value): static + { + $this->default = true; + $this->defaultValue = $value; + + return $this; + } + + /** + * Sets the node as required. + * + * @return $this + */ + public function isRequired(): static + { + $this->required = true; + + return $this; + } + + /** + * Sets the node as deprecated. + * + * @param string $package The name of the composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message the deprecation message to use + * + * You can use %node% and %path% placeholders in your message to display, + * respectively, the node name and its complete path + * + * @return $this + */ + public function setDeprecated(string $package, string $version, string $message = 'The child node "%node%" at path "%path%" is deprecated.'): static + { + $this->deprecation = [ + 'package' => $package, + 'version' => $version, + 'message' => $message, + ]; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains null. + * + * @return $this + */ + public function treatNullLike(mixed $value): static + { + $this->nullEquivalent = $value; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains true. + * + * @return $this + */ + public function treatTrueLike(mixed $value): static + { + $this->trueEquivalent = $value; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains false. + * + * @return $this + */ + public function treatFalseLike(mixed $value): static + { + $this->falseEquivalent = $value; + + return $this; + } + + /** + * Sets null as the default value. + * + * @return $this + */ + public function defaultNull(): static + { + return $this->defaultValue(null); + } + + /** + * Sets true as the default value. + * + * @return $this + */ + public function defaultTrue(): static + { + return $this->defaultValue(true); + } + + /** + * Sets false as the default value. + * + * @return $this + */ + public function defaultFalse(): static + { + return $this->defaultValue(false); + } + + /** + * Sets an expression to run before the normalization. + */ + public function beforeNormalization(): ExprBuilder + { + return $this->normalization()->before(); + } + + /** + * Denies the node value being empty. + * + * @return $this + */ + public function cannotBeEmpty(): static + { + $this->allowEmptyValue = false; + + return $this; + } + + /** + * Sets an expression to run for the validation. + * + * The expression receives the value of the node and must return it. It can + * modify it. + * An exception should be thrown when the node is not valid. + */ + public function validate(): ExprBuilder + { + return $this->validation()->rule(); + } + + /** + * Sets whether the node can be overwritten. + * + * @return $this + */ + public function cannotBeOverwritten(bool $deny = true): static + { + $this->merge()->denyOverwrite($deny); + + return $this; + } + + /** + * Gets the builder for validation rules. + */ + protected function validation(): ValidationBuilder + { + if (null === $this->validation) { + $this->validation = new ValidationBuilder($this); + } + + return $this->validation; + } + + /** + * Gets the builder for merging rules. + */ + protected function merge(): MergeBuilder + { + if (null === $this->merge) { + $this->merge = new MergeBuilder($this); + } + + return $this->merge; + } + + /** + * Gets the builder for normalization rules. + */ + protected function normalization(): NormalizationBuilder + { + if (null === $this->normalization) { + $this->normalization = new NormalizationBuilder($this); + } + + return $this->normalization; + } + + /** + * Instantiate and configure the node according to this definition. + * + * @throws InvalidDefinitionException When the definition is invalid + */ + abstract protected function createNode(): NodeInterface; + + /** + * Set PathSeparator to use. + * + * @return $this + */ + public function setPathSeparator(string $separator): static + { + if ($this instanceof ParentNodeDefinitionInterface) { + foreach ($this->getChildNodeDefinitions() as $child) { + $child->setPathSeparator($separator); + } + } + + $this->pathSeparator = $separator; + + return $this; + } +} diff --git a/vendor/symfony/config/Definition/Builder/NodeParentInterface.php b/vendor/symfony/config/Definition/Builder/NodeParentInterface.php new file mode 100644 index 0000000..305e993 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/NodeParentInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * An interface that must be implemented by all node parents. + * + * @author Victor Berchet + */ +interface NodeParentInterface +{ +} diff --git a/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php b/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php new file mode 100644 index 0000000..59f92ff --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class builds normalization conditions. + * + * @author Johannes M. Schmitt + */ +class NormalizationBuilder +{ + protected $node; + public $before = []; + public $remappings = []; + + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Registers a key to remap to its plural form. + * + * @param string $key The key to remap + * @param string|null $plural The plural of the key in case of irregular plural + * + * @return $this + */ + public function remap(string $key, string $plural = null): static + { + $this->remappings[] = [$key, null === $plural ? $key.'s' : $plural]; + + return $this; + } + + /** + * Registers a closure to run before the normalization or an expression builder to build it if null is provided. + * + * @return ExprBuilder|$this + */ + public function before(\Closure $closure = null): ExprBuilder|static + { + if (null !== $closure) { + $this->before[] = $closure; + + return $this; + } + + return $this->before[] = new ExprBuilder($this->node); + } +} diff --git a/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php b/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php new file mode 100644 index 0000000..83a6758 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; + +/** + * Abstract class that contains common code of integer and float node definitions. + * + * @author David Jeanmonod + */ +abstract class NumericNodeDefinition extends ScalarNodeDefinition +{ + protected $min; + protected $max; + + /** + * Ensures that the value is smaller than the given reference. + * + * @return $this + * + * @throws \InvalidArgumentException when the constraint is inconsistent + */ + public function max(int|float $max): static + { + if (isset($this->min) && $this->min > $max) { + throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s).', $max, $this->min)); + } + $this->max = $max; + + return $this; + } + + /** + * Ensures that the value is bigger than the given reference. + * + * @return $this + * + * @throws \InvalidArgumentException when the constraint is inconsistent + */ + public function min(int|float $min): static + { + if (isset($this->max) && $this->max < $min) { + throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s).', $min, $this->max)); + } + $this->min = $min; + + return $this; + } + + /** + * {@inheritdoc} + * + * @throws InvalidDefinitionException + */ + public function cannotBeEmpty(): static + { + throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to NumericNodeDefinition.'); + } +} diff --git a/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php b/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php new file mode 100644 index 0000000..7b8a7eb --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * An interface that must be implemented by nodes which can have children. + * + * @author Victor Berchet + */ +interface ParentNodeDefinitionInterface extends BuilderAwareInterface +{ + /** + * Returns a builder to add children nodes. + */ + public function children(): NodeBuilder; + + /** + * Appends a node definition. + * + * Usage: + * + * $node = $parentNode + * ->children() + * ->scalarNode('foo')->end() + * ->scalarNode('baz')->end() + * ->append($this->getBarNodeDefinition()) + * ->end() + * ; + * + * @return $this + */ + public function append(NodeDefinition $node): static; + + /** + * Gets the child node definitions. + * + * @return NodeDefinition[] + */ + public function getChildNodeDefinitions(): array; +} diff --git a/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php b/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php new file mode 100644 index 0000000..37a0af0 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\ScalarNode; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class ScalarNodeDefinition extends VariableNodeDefinition +{ + /** + * Instantiate a Node. + */ + protected function instantiateNode(): ScalarNode + { + return new ScalarNode($this->name, $this->parent, $this->pathSeparator); + } +} diff --git a/vendor/symfony/config/Definition/Builder/TreeBuilder.php b/vendor/symfony/config/Definition/Builder/TreeBuilder.php new file mode 100644 index 0000000..4a106d1 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/TreeBuilder.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\NodeInterface; + +/** + * This is the entry class for building a config tree. + * + * @author Johannes M. Schmitt + */ +class TreeBuilder implements NodeParentInterface +{ + protected $tree; + protected $root; + + public function __construct(string $name, string $type = 'array', NodeBuilder $builder = null) + { + $builder = $builder ?? new NodeBuilder(); + $this->root = $builder->node($name, $type)->setParent($this); + } + + /** + * @return NodeDefinition|ArrayNodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array') + */ + public function getRootNode(): NodeDefinition|ArrayNodeDefinition + { + return $this->root; + } + + /** + * Builds the tree. + * + * @throws \RuntimeException + */ + public function buildTree(): NodeInterface + { + if (null !== $this->tree) { + return $this->tree; + } + + return $this->tree = $this->root->getNode(true); + } + + public function setPathSeparator(string $separator) + { + // unset last built as changing path separator changes all nodes + $this->tree = null; + + $this->root->setPathSeparator($separator); + } +} diff --git a/vendor/symfony/config/Definition/Builder/ValidationBuilder.php b/vendor/symfony/config/Definition/Builder/ValidationBuilder.php new file mode 100644 index 0000000..1bee851 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/ValidationBuilder.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class builds validation conditions. + * + * @author Christophe Coevoet + */ +class ValidationBuilder +{ + protected $node; + public $rules = []; + + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Registers a closure to run as normalization or an expression builder to build it if null is provided. + * + * @return ExprBuilder|$this + */ + public function rule(\Closure $closure = null): ExprBuilder|static + { + if (null !== $closure) { + $this->rules[] = $closure; + + return $this; + } + + return $this->rules[] = new ExprBuilder($this->node); + } +} diff --git a/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php b/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php new file mode 100644 index 0000000..7ea15d3 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\VariableNode; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class VariableNodeDefinition extends NodeDefinition +{ + /** + * Instantiate a Node. + */ + protected function instantiateNode(): VariableNode + { + return new VariableNode($this->name, $this->parent, $this->pathSeparator); + } + + /** + * {@inheritdoc} + */ + protected function createNode(): NodeInterface + { + $node = $this->instantiateNode(); + + if (null !== $this->normalization) { + $node->setNormalizationClosures($this->normalization->before); + } + + if (null !== $this->merge) { + $node->setAllowOverwrite($this->merge->allowOverwrite); + } + + if (true === $this->default) { + $node->setDefaultValue($this->defaultValue); + } + + $node->setAllowEmptyValue($this->allowEmptyValue); + $node->addEquivalentValue(null, $this->nullEquivalent); + $node->addEquivalentValue(true, $this->trueEquivalent); + $node->addEquivalentValue(false, $this->falseEquivalent); + $node->setRequired($this->required); + + if ($this->deprecation) { + $node->setDeprecated($this->deprecation['package'], $this->deprecation['version'], $this->deprecation['message']); + } + + if (null !== $this->validation) { + $node->setFinalValidationClosures($this->validation->rules); + } + + return $node; + } +} diff --git a/vendor/symfony/config/Definition/ConfigurationInterface.php b/vendor/symfony/config/Definition/ConfigurationInterface.php new file mode 100644 index 0000000..7b5d443 --- /dev/null +++ b/vendor/symfony/config/Definition/ConfigurationInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; + +/** + * Configuration interface. + * + * @author Victor Berchet + */ +interface ConfigurationInterface +{ + /** + * Generates the configuration tree builder. + * + * @return TreeBuilder + */ + public function getConfigTreeBuilder(); +} diff --git a/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php b/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php new file mode 100644 index 0000000..0a78f7f --- /dev/null +++ b/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php @@ -0,0 +1,304 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Dumper; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\BaseNode; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\EnumNode; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\PrototypedArrayNode; + +/** + * Dumps an XML reference configuration for the given configuration/node instance. + * + * @author Wouter J + */ +class XmlReferenceDumper +{ + private ?string $reference = null; + + public function dump(ConfigurationInterface $configuration, string $namespace = null) + { + return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree(), $namespace); + } + + public function dumpNode(NodeInterface $node, string $namespace = null) + { + $this->reference = ''; + $this->writeNode($node, 0, true, $namespace); + $ref = $this->reference; + $this->reference = null; + + return $ref; + } + + private function writeNode(NodeInterface $node, int $depth = 0, bool $root = false, string $namespace = null) + { + $rootName = ($root ? 'config' : $node->getName()); + $rootNamespace = ($namespace ?: ($root ? 'http://example.org/schema/dic/'.$node->getName() : null)); + + // xml remapping + if ($node->getParent()) { + $remapping = array_filter($node->getParent()->getXmlRemappings(), function (array $mapping) use ($rootName) { + return $rootName === $mapping[1]; + }); + + if (\count($remapping)) { + [$singular] = current($remapping); + $rootName = $singular; + } + } + $rootName = str_replace('_', '-', $rootName); + + $rootAttributes = []; + $rootAttributeComments = []; + $rootChildren = []; + $rootComments = []; + + if ($node instanceof ArrayNode) { + $children = $node->getChildren(); + + // comments about the root node + if ($rootInfo = $node->getInfo()) { + $rootComments[] = $rootInfo; + } + + if ($rootNamespace) { + $rootComments[] = 'Namespace: '.$rootNamespace; + } + + // render prototyped nodes + if ($node instanceof PrototypedArrayNode) { + $prototype = $node->getPrototype(); + + $info = 'prototype'; + if (null !== $prototype->getInfo()) { + $info .= ': '.$prototype->getInfo(); + } + array_unshift($rootComments, $info); + + if ($key = $node->getKeyAttribute()) { + $rootAttributes[$key] = str_replace('-', ' ', $rootName).' '.$key; + } + + if ($prototype instanceof PrototypedArrayNode) { + $prototype->setName($key ?? ''); + $children = [$key => $prototype]; + } elseif ($prototype instanceof ArrayNode) { + $children = $prototype->getChildren(); + } else { + if ($prototype->hasDefaultValue()) { + $prototypeValue = $prototype->getDefaultValue(); + } else { + switch (\get_class($prototype)) { + case 'Symfony\Component\Config\Definition\ScalarNode': + $prototypeValue = 'scalar value'; + break; + + case 'Symfony\Component\Config\Definition\FloatNode': + case 'Symfony\Component\Config\Definition\IntegerNode': + $prototypeValue = 'numeric value'; + break; + + case 'Symfony\Component\Config\Definition\BooleanNode': + $prototypeValue = 'true|false'; + break; + + case 'Symfony\Component\Config\Definition\EnumNode': + $prototypeValue = implode('|', array_map('json_encode', $prototype->getValues())); + break; + + default: + $prototypeValue = 'value'; + } + } + } + } + + // get attributes and elements + foreach ($children as $child) { + if ($child instanceof ArrayNode) { + // get elements + $rootChildren[] = $child; + + continue; + } + + // get attributes + + // metadata + $name = str_replace('_', '-', $child->getName()); + $value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world + + // comments + $comments = []; + if ($child instanceof BaseNode && $info = $child->getInfo()) { + $comments[] = $info; + } + + if ($child instanceof BaseNode && $example = $child->getExample()) { + $comments[] = 'Example: '.(\is_array($example) ? implode(', ', $example) : $example); + } + + if ($child->isRequired()) { + $comments[] = 'Required'; + } + + if ($child instanceof BaseNode && $child->isDeprecated()) { + $deprecation = $child->getDeprecation($child->getName(), $node->getPath()); + $comments[] = sprintf('Deprecated (%s)', ($deprecation['package'] || $deprecation['version'] ? "Since {$deprecation['package']} {$deprecation['version']}: " : '').$deprecation['message']); + } + + if ($child instanceof EnumNode) { + $comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues())); + } + + if (\count($comments)) { + $rootAttributeComments[$name] = implode(";\n", $comments); + } + + // default values + if ($child->hasDefaultValue()) { + $value = $child->getDefaultValue(); + } + + // append attribute + $rootAttributes[$name] = $value; + } + } + + // render comments + + // root node comment + if (\count($rootComments)) { + foreach ($rootComments as $comment) { + $this->writeLine('', $depth); + } + } + + // attribute comments + if (\count($rootAttributeComments)) { + foreach ($rootAttributeComments as $attrName => $comment) { + $commentDepth = $depth + 4 + \strlen($attrName) + 2; + $commentLines = explode("\n", $comment); + $multiline = (\count($commentLines) > 1); + $comment = implode(\PHP_EOL.str_repeat(' ', $commentDepth), $commentLines); + + if ($multiline) { + $this->writeLine('', $depth); + } else { + $this->writeLine('', $depth); + } + } + } + + // render start tag + attributes + $rootIsVariablePrototype = isset($prototypeValue); + $rootIsEmptyTag = (0 === \count($rootChildren) && !$rootIsVariablePrototype); + $rootOpenTag = '<'.$rootName; + if (1 >= ($attributesCount = \count($rootAttributes))) { + if (1 === $attributesCount) { + $rootOpenTag .= sprintf(' %s="%s"', current(array_keys($rootAttributes)), $this->writeValue(current($rootAttributes))); + } + + $rootOpenTag .= $rootIsEmptyTag ? ' />' : '>'; + + if ($rootIsVariablePrototype) { + $rootOpenTag .= $prototypeValue.''; + } + + $this->writeLine($rootOpenTag, $depth); + } else { + $this->writeLine($rootOpenTag, $depth); + + $i = 1; + + foreach ($rootAttributes as $attrName => $attrValue) { + $attr = sprintf('%s="%s"', $attrName, $this->writeValue($attrValue)); + + $this->writeLine($attr, $depth + 4); + + if ($attributesCount === $i++) { + $this->writeLine($rootIsEmptyTag ? '/>' : '>', $depth); + + if ($rootIsVariablePrototype) { + $rootOpenTag .= $prototypeValue.''; + } + } + } + } + + // render children tags + foreach ($rootChildren as $child) { + $this->writeLine(''); + $this->writeNode($child, $depth + 4); + } + + // render end tag + if (!$rootIsEmptyTag && !$rootIsVariablePrototype) { + $this->writeLine(''); + + $rootEndTag = ''; + $this->writeLine($rootEndTag, $depth); + } + } + + /** + * Outputs a single config reference line. + */ + private function writeLine(string $text, int $indent = 0) + { + $indent = \strlen($text) + $indent; + $format = '%'.$indent.'s'; + + $this->reference .= sprintf($format, $text).\PHP_EOL; + } + + /** + * Renders the string conversion of the value. + */ + private function writeValue(mixed $value): string + { + if ('%%%%not_defined%%%%' === $value) { + return ''; + } + + if (\is_string($value) || is_numeric($value)) { + return $value; + } + + if (false === $value) { + return 'false'; + } + + if (true === $value) { + return 'true'; + } + + if (null === $value) { + return 'null'; + } + + if (empty($value)) { + return ''; + } + + if (\is_array($value)) { + return implode(',', $value); + } + + return ''; + } +} diff --git a/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php b/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php new file mode 100644 index 0000000..87abb6a --- /dev/null +++ b/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php @@ -0,0 +1,251 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Dumper; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\BaseNode; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\EnumNode; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\PrototypedArrayNode; +use Symfony\Component\Config\Definition\ScalarNode; +use Symfony\Component\Config\Definition\VariableNode; +use Symfony\Component\Yaml\Inline; + +/** + * Dumps a Yaml reference configuration for the given configuration/node instance. + * + * @author Kevin Bond + */ +class YamlReferenceDumper +{ + private ?string $reference = null; + + public function dump(ConfigurationInterface $configuration) + { + return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree()); + } + + public function dumpAtPath(ConfigurationInterface $configuration, string $path) + { + $rootNode = $node = $configuration->getConfigTreeBuilder()->buildTree(); + + foreach (explode('.', $path) as $step) { + if (!$node instanceof ArrayNode) { + throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s".', $rootNode->getName(), $path)); + } + + /** @var NodeInterface[] $children */ + $children = $node instanceof PrototypedArrayNode ? $this->getPrototypeChildren($node) : $node->getChildren(); + + foreach ($children as $child) { + if ($child->getName() === $step) { + $node = $child; + + continue 2; + } + } + + throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s".', $rootNode->getName(), $path)); + } + + return $this->dumpNode($node); + } + + public function dumpNode(NodeInterface $node) + { + $this->reference = ''; + $this->writeNode($node); + $ref = $this->reference; + $this->reference = null; + + return $ref; + } + + private function writeNode(NodeInterface $node, NodeInterface $parentNode = null, int $depth = 0, bool $prototypedArray = false) + { + $comments = []; + $default = ''; + $defaultArray = null; + $children = null; + $example = null; + if ($node instanceof BaseNode) { + $example = $node->getExample(); + } + + // defaults + if ($node instanceof ArrayNode) { + $children = $node->getChildren(); + + if ($node instanceof PrototypedArrayNode) { + $children = $this->getPrototypeChildren($node); + } + + if (!$children) { + if ($node->hasDefaultValue() && \count($defaultArray = $node->getDefaultValue())) { + $default = ''; + } elseif (!\is_array($example)) { + $default = '[]'; + } + } + } elseif ($node instanceof EnumNode) { + $comments[] = 'One of '.implode('; ', array_map('json_encode', $node->getValues())); + $default = $node->hasDefaultValue() ? Inline::dump($node->getDefaultValue()) : '~'; + } elseif (VariableNode::class === \get_class($node) && \is_array($example)) { + // If there is an array example, we are sure we dont need to print a default value + $default = ''; + } else { + $default = '~'; + + if ($node->hasDefaultValue()) { + $default = $node->getDefaultValue(); + + if (\is_array($default)) { + if (\count($defaultArray = $node->getDefaultValue())) { + $default = ''; + } elseif (!\is_array($example)) { + $default = '[]'; + } + } else { + $default = Inline::dump($default); + } + } + } + + // required? + if ($node->isRequired()) { + $comments[] = 'Required'; + } + + // deprecated? + if ($node instanceof BaseNode && $node->isDeprecated()) { + $deprecation = $node->getDeprecation($node->getName(), $parentNode ? $parentNode->getPath() : $node->getPath()); + $comments[] = sprintf('Deprecated (%s)', ($deprecation['package'] || $deprecation['version'] ? "Since {$deprecation['package']} {$deprecation['version']}: " : '').$deprecation['message']); + } + + // example + if ($example && !\is_array($example)) { + $comments[] = 'Example: '.Inline::dump($example); + } + + $default = '' != (string) $default ? ' '.$default : ''; + $comments = \count($comments) ? '# '.implode(', ', $comments) : ''; + + $key = $prototypedArray ? '-' : $node->getName().':'; + $text = rtrim(sprintf('%-21s%s %s', $key, $default, $comments), ' '); + + if ($node instanceof BaseNode && $info = $node->getInfo()) { + $this->writeLine(''); + // indenting multi-line info + $info = str_replace("\n", sprintf("\n%".($depth * 4).'s# ', ' '), $info); + $this->writeLine('# '.$info, $depth * 4); + } + + $this->writeLine($text, $depth * 4); + + // output defaults + if ($defaultArray) { + $this->writeLine(''); + + $message = \count($defaultArray) > 1 ? 'Defaults' : 'Default'; + + $this->writeLine('# '.$message.':', $depth * 4 + 4); + + $this->writeArray($defaultArray, $depth + 1); + } + + if (\is_array($example)) { + $this->writeLine(''); + + $message = \count($example) > 1 ? 'Examples' : 'Example'; + + $this->writeLine('# '.$message.':', $depth * 4 + 4); + + $this->writeArray(array_map([Inline::class, 'dump'], $example), $depth + 1); + } + + if ($children) { + foreach ($children as $childNode) { + $this->writeNode($childNode, $node, $depth + 1, $node instanceof PrototypedArrayNode && !$node->getKeyAttribute()); + } + } + } + + /** + * Outputs a single config reference line. + */ + private function writeLine(string $text, int $indent = 0) + { + $indent = \strlen($text) + $indent; + $format = '%'.$indent.'s'; + + $this->reference .= sprintf($format, $text)."\n"; + } + + private function writeArray(array $array, int $depth) + { + $isIndexed = array_values($array) === $array; + + foreach ($array as $key => $value) { + if (\is_array($value)) { + $val = ''; + } else { + $val = $value; + } + + if ($isIndexed) { + $this->writeLine('- '.$val, $depth * 4); + } else { + $this->writeLine(sprintf('%-20s %s', $key.':', $val), $depth * 4); + } + + if (\is_array($value)) { + $this->writeArray($value, $depth + 1); + } + } + } + + private function getPrototypeChildren(PrototypedArrayNode $node): array + { + $prototype = $node->getPrototype(); + $key = $node->getKeyAttribute(); + + // Do not expand prototype if it isn't an array node nor uses attribute as key + if (!$key && !$prototype instanceof ArrayNode) { + return $node->getChildren(); + } + + if ($prototype instanceof ArrayNode) { + $keyNode = new ArrayNode($key, $node); + $children = $prototype->getChildren(); + + if ($prototype instanceof PrototypedArrayNode && $prototype->getKeyAttribute()) { + $children = $this->getPrototypeChildren($prototype); + } + + // add children + foreach ($children as $childNode) { + $keyNode->addChild($childNode); + } + } else { + $keyNode = new ScalarNode($key, $node); + } + + $info = 'Prototype'; + if (null !== $prototype->getInfo()) { + $info .= ': '.$prototype->getInfo(); + } + $keyNode->setInfo($info); + + return [$key => $keyNode]; + } +} diff --git a/vendor/symfony/config/Definition/EnumNode.php b/vendor/symfony/config/Definition/EnumNode.php new file mode 100644 index 0000000..0001dd4 --- /dev/null +++ b/vendor/symfony/config/Definition/EnumNode.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * Node which only allows a finite set of values. + * + * @author Johannes M. Schmitt + */ +class EnumNode extends ScalarNode +{ + private array $values; + + public function __construct(?string $name, NodeInterface $parent = null, array $values = [], string $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR) + { + $values = array_unique($values); + if (empty($values)) { + throw new \InvalidArgumentException('$values must contain at least one element.'); + } + + parent::__construct($name, $parent, $pathSeparator); + $this->values = $values; + } + + public function getValues() + { + return $this->values; + } + + /** + * {@inheritdoc} + */ + protected function finalizeValue(mixed $value): mixed + { + $value = parent::finalizeValue($value); + + if (!\in_array($value, $this->values, true)) { + $ex = new InvalidConfigurationException(sprintf('The value %s is not allowed for path "%s". Permissible values: %s', json_encode($value), $this->getPath(), implode(', ', array_map('json_encode', $this->values)))); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + protected function allowPlaceholders(): bool + { + return false; + } +} diff --git a/vendor/symfony/config/Definition/Exception/DuplicateKeyException.php b/vendor/symfony/config/Definition/Exception/DuplicateKeyException.php new file mode 100644 index 0000000..48dd932 --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/DuplicateKeyException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * This exception is thrown whenever the key of an array is not unique. This can + * only be the case if the configuration is coming from an XML file. + * + * @author Johannes M. Schmitt + */ +class DuplicateKeyException extends InvalidConfigurationException +{ +} diff --git a/vendor/symfony/config/Definition/Exception/Exception.php b/vendor/symfony/config/Definition/Exception/Exception.php new file mode 100644 index 0000000..8933a49 --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/Exception.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * Base exception for all configuration exceptions. + * + * @author Johannes M. Schmitt + */ +class Exception extends \RuntimeException +{ +} diff --git a/vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.php b/vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.php new file mode 100644 index 0000000..726c07f --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * This exception is thrown when a configuration path is overwritten from a + * subsequent configuration file, but the entry node specifically forbids this. + * + * @author Johannes M. Schmitt + */ +class ForbiddenOverwriteException extends InvalidConfigurationException +{ +} diff --git a/vendor/symfony/config/Definition/Exception/InvalidConfigurationException.php b/vendor/symfony/config/Definition/Exception/InvalidConfigurationException.php new file mode 100644 index 0000000..4ecf351 --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/InvalidConfigurationException.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * A very general exception which can be thrown whenever non of the more specific + * exceptions is suitable. + * + * @author Johannes M. Schmitt + */ +class InvalidConfigurationException extends Exception +{ + private ?string $path = null; + private bool $containsHints = false; + + public function setPath(string $path) + { + $this->path = $path; + } + + public function getPath(): ?string + { + return $this->path; + } + + /** + * Adds extra information that is suffixed to the original exception message. + */ + public function addHint(string $hint) + { + if (!$this->containsHints) { + $this->message .= "\nHint: ".$hint; + $this->containsHints = true; + } else { + $this->message .= ', '.$hint; + } + } +} diff --git a/vendor/symfony/config/Definition/Exception/InvalidDefinitionException.php b/vendor/symfony/config/Definition/Exception/InvalidDefinitionException.php new file mode 100644 index 0000000..98310da --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/InvalidDefinitionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * Thrown when an error is detected in a node Definition. + * + * @author Victor Berchet + */ +class InvalidDefinitionException extends Exception +{ +} diff --git a/vendor/symfony/config/Definition/Exception/InvalidTypeException.php b/vendor/symfony/config/Definition/Exception/InvalidTypeException.php new file mode 100644 index 0000000..d7ca8c9 --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/InvalidTypeException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * This exception is thrown if an invalid type is encountered. + * + * @author Johannes M. Schmitt + */ +class InvalidTypeException extends InvalidConfigurationException +{ +} diff --git a/vendor/symfony/config/Definition/Exception/UnsetKeyException.php b/vendor/symfony/config/Definition/Exception/UnsetKeyException.php new file mode 100644 index 0000000..863181a --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/UnsetKeyException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * This exception is usually not encountered by the end-user, but only used + * internally to signal the parent scope to unset a key. + * + * @author Johannes M. Schmitt + */ +class UnsetKeyException extends Exception +{ +} diff --git a/vendor/symfony/config/Definition/FloatNode.php b/vendor/symfony/config/Definition/FloatNode.php new file mode 100644 index 0000000..235927f --- /dev/null +++ b/vendor/symfony/config/Definition/FloatNode.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a float value in the config tree. + * + * @author Jeanmonod David + */ +class FloatNode extends NumericNode +{ + /** + * {@inheritdoc} + */ + protected function validateType(mixed $value) + { + // Integers are also accepted, we just cast them + if (\is_int($value)) { + $value = (float) $value; + } + + if (!\is_float($value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "float", but got "%s".', $this->getPath(), get_debug_type($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * {@inheritdoc} + */ + protected function getValidPlaceholderTypes(): array + { + return ['float']; + } +} diff --git a/vendor/symfony/config/Definition/IntegerNode.php b/vendor/symfony/config/Definition/IntegerNode.php new file mode 100644 index 0000000..62643fe --- /dev/null +++ b/vendor/symfony/config/Definition/IntegerNode.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents an integer value in the config tree. + * + * @author Jeanmonod David + */ +class IntegerNode extends NumericNode +{ + /** + * {@inheritdoc} + */ + protected function validateType(mixed $value) + { + if (!\is_int($value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "int", but got "%s".', $this->getPath(), get_debug_type($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * {@inheritdoc} + */ + protected function getValidPlaceholderTypes(): array + { + return ['int']; + } +} diff --git a/vendor/symfony/config/Definition/NodeInterface.php b/vendor/symfony/config/Definition/NodeInterface.php new file mode 100644 index 0000000..d171587 --- /dev/null +++ b/vendor/symfony/config/Definition/NodeInterface.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * Common Interface among all nodes. + * + * In most cases, it is better to inherit from BaseNode instead of implementing + * this interface yourself. + * + * @author Johannes M. Schmitt + */ +interface NodeInterface +{ + /** + * Returns the name of the node. + */ + public function getName(): string; + + /** + * Returns the path of the node. + */ + public function getPath(): string; + + /** + * Returns true when the node is required. + */ + public function isRequired(): bool; + + /** + * Returns true when the node has a default value. + */ + public function hasDefaultValue(): bool; + + /** + * Returns the default value of the node. + * + * @throws \RuntimeException if the node has no default value + */ + public function getDefaultValue(): mixed; + + /** + * Normalizes a value. + * + * @throws InvalidTypeException if the value type is invalid + */ + public function normalize(mixed $value): mixed; + + /** + * Merges two values together. + * + * @throws ForbiddenOverwriteException if the configuration path cannot be overwritten + * @throws InvalidTypeException if the value type is invalid + */ + public function merge(mixed $leftSide, mixed $rightSide): mixed; + + /** + * Finalizes a value. + * + * @throws InvalidTypeException if the value type is invalid + * @throws InvalidConfigurationException if the value is invalid configuration + */ + public function finalize(mixed $value): mixed; +} diff --git a/vendor/symfony/config/Definition/NumericNode.php b/vendor/symfony/config/Definition/NumericNode.php new file mode 100644 index 0000000..4d3e013 --- /dev/null +++ b/vendor/symfony/config/Definition/NumericNode.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * This node represents a numeric value in the config tree. + * + * @author David Jeanmonod + */ +class NumericNode extends ScalarNode +{ + protected $min; + protected $max; + + public function __construct(?string $name, NodeInterface $parent = null, int|float $min = null, int|float $max = null, string $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR) + { + parent::__construct($name, $parent, $pathSeparator); + $this->min = $min; + $this->max = $max; + } + + /** + * {@inheritdoc} + */ + protected function finalizeValue(mixed $value): mixed + { + $value = parent::finalizeValue($value); + + $errorMsg = null; + if (isset($this->min) && $value < $this->min) { + $errorMsg = sprintf('The value %s is too small for path "%s". Should be greater than or equal to %s', $value, $this->getPath(), $this->min); + } + if (isset($this->max) && $value > $this->max) { + $errorMsg = sprintf('The value %s is too big for path "%s". Should be less than or equal to %s', $value, $this->getPath(), $this->max); + } + if (isset($errorMsg)) { + $ex = new InvalidConfigurationException($errorMsg); + $ex->setPath($this->getPath()); + throw $ex; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + protected function isValueEmpty(mixed $value): bool + { + // a numeric value cannot be empty + return false; + } +} diff --git a/vendor/symfony/config/Definition/Processor.php b/vendor/symfony/config/Definition/Processor.php new file mode 100644 index 0000000..c431408 --- /dev/null +++ b/vendor/symfony/config/Definition/Processor.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +/** + * This class is the entry point for config normalization/merging/finalization. + * + * @author Johannes M. Schmitt + * + * @final + */ +class Processor +{ + /** + * Processes an array of configurations. + * + * @param array $configs An array of configuration items to process + */ + public function process(NodeInterface $configTree, array $configs): array + { + $currentConfig = []; + foreach ($configs as $config) { + $config = $configTree->normalize($config); + $currentConfig = $configTree->merge($currentConfig, $config); + } + + return $configTree->finalize($currentConfig); + } + + /** + * Processes an array of configurations. + * + * @param array $configs An array of configuration items to process + */ + public function processConfiguration(ConfigurationInterface $configuration, array $configs): array + { + return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $configs); + } + + /** + * Normalizes a configuration entry. + * + * This method returns a normalize configuration array for a given key + * to remove the differences due to the original format (YAML and XML mainly). + * + * Here is an example. + * + * The configuration in XML: + * + * twig.extension.foo + * twig.extension.bar + * + * And the same configuration in YAML: + * + * extensions: ['twig.extension.foo', 'twig.extension.bar'] + * + * @param array $config A config array + * @param string $key The key to normalize + * @param string $plural The plural form of the key if it is irregular + */ + public static function normalizeConfig(array $config, string $key, string $plural = null): array + { + if (null === $plural) { + $plural = $key.'s'; + } + + if (isset($config[$plural])) { + return $config[$plural]; + } + + if (isset($config[$key])) { + if (\is_string($config[$key]) || !\is_int(key($config[$key]))) { + // only one + return [$config[$key]]; + } + + return $config[$key]; + } + + return []; + } +} diff --git a/vendor/symfony/config/Definition/PrototypeNodeInterface.php b/vendor/symfony/config/Definition/PrototypeNodeInterface.php new file mode 100644 index 0000000..b160aa9 --- /dev/null +++ b/vendor/symfony/config/Definition/PrototypeNodeInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +/** + * This interface must be implemented by nodes which can be used as prototypes. + * + * @author Johannes M. Schmitt + */ +interface PrototypeNodeInterface extends NodeInterface +{ + /** + * Sets the name of the node. + */ + public function setName(string $name); +} diff --git a/vendor/symfony/config/Definition/PrototypedArrayNode.php b/vendor/symfony/config/Definition/PrototypedArrayNode.php new file mode 100644 index 0000000..988cf0b --- /dev/null +++ b/vendor/symfony/config/Definition/PrototypedArrayNode.php @@ -0,0 +1,344 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\DuplicateKeyException; +use Symfony\Component\Config\Definition\Exception\Exception; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; + +/** + * Represents a prototyped Array node in the config tree. + * + * @author Johannes M. Schmitt + */ +class PrototypedArrayNode extends ArrayNode +{ + protected $prototype; + protected $keyAttribute; + protected $removeKeyAttribute = false; + protected $minNumberOfElements = 0; + protected $defaultValue = []; + protected $defaultChildren; + /** + * @var NodeInterface[] An array of the prototypes of the simplified value children + */ + private array $valuePrototypes = []; + + /** + * Sets the minimum number of elements that a prototype based node must + * contain. By default this is zero, meaning no elements. + */ + public function setMinNumberOfElements(int $number) + { + $this->minNumberOfElements = $number; + } + + /** + * Sets the attribute which value is to be used as key. + * + * This is useful when you have an indexed array that should be an + * associative array. You can select an item from within the array + * to be the key of the particular item. For example, if "id" is the + * "key", then: + * + * [ + * ['id' => 'my_name', 'foo' => 'bar'], + * ]; + * + * becomes + * + * [ + * 'my_name' => ['foo' => 'bar'], + * ]; + * + * If you'd like "'id' => 'my_name'" to still be present in the resulting + * array, then you can set the second argument of this method to false. + * + * @param string $attribute The name of the attribute which value is to be used as a key + * @param bool $remove Whether or not to remove the key + */ + public function setKeyAttribute(string $attribute, bool $remove = true) + { + $this->keyAttribute = $attribute; + $this->removeKeyAttribute = $remove; + } + + /** + * Retrieves the name of the attribute which value should be used as key. + */ + public function getKeyAttribute(): ?string + { + return $this->keyAttribute; + } + + /** + * Sets the default value of this node. + */ + public function setDefaultValue(array $value) + { + $this->defaultValue = $value; + } + + /** + * {@inheritdoc} + */ + public function hasDefaultValue(): bool + { + return true; + } + + /** + * Adds default children when none are set. + * + * @param int|string|array|null $children The number of children|The child name|The children names to be added + */ + public function setAddChildrenIfNoneSet(int|string|array|null $children = ['defaults']) + { + if (null === $children) { + $this->defaultChildren = ['defaults']; + } else { + $this->defaultChildren = \is_int($children) && $children > 0 ? range(1, $children) : (array) $children; + } + } + + /** + * {@inheritdoc} + * + * The default value could be either explicited or derived from the prototype + * default value. + */ + public function getDefaultValue(): mixed + { + if (null !== $this->defaultChildren) { + $default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : []; + $defaults = []; + foreach (array_values($this->defaultChildren) as $i => $name) { + $defaults[null === $this->keyAttribute ? $i : $name] = $default; + } + + return $defaults; + } + + return $this->defaultValue; + } + + /** + * Sets the node prototype. + */ + public function setPrototype(PrototypeNodeInterface $node) + { + $this->prototype = $node; + } + + /** + * Retrieves the prototype. + */ + public function getPrototype(): PrototypeNodeInterface + { + return $this->prototype; + } + + /** + * Disable adding concrete children for prototyped nodes. + * + * @throws Exception + */ + public function addChild(NodeInterface $node) + { + throw new Exception('A prototyped array node cannot have concrete children.'); + } + + /** + * {@inheritdoc} + */ + protected function finalizeValue(mixed $value): mixed + { + if (false === $value) { + throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s.', $this->getPath(), json_encode($value))); + } + + foreach ($value as $k => $v) { + $prototype = $this->getPrototypeForChild($k); + try { + $value[$k] = $prototype->finalize($v); + } catch (UnsetKeyException $e) { + unset($value[$k]); + } + } + + if (\count($value) < $this->minNumberOfElements) { + $ex = new InvalidConfigurationException(sprintf('The path "%s" should have at least %d element(s) defined.', $this->getPath(), $this->minNumberOfElements)); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } + + /** + * {@inheritdoc} + * + * @throws DuplicateKeyException + */ + protected function normalizeValue(mixed $value): mixed + { + if (false === $value) { + return $value; + } + + $value = $this->remapXml($value); + + $isList = array_is_list($value); + $normalized = []; + foreach ($value as $k => $v) { + if (null !== $this->keyAttribute && \is_array($v)) { + if (!isset($v[$this->keyAttribute]) && \is_int($k) && $isList) { + $ex = new InvalidConfigurationException(sprintf('The attribute "%s" must be set for path "%s".', $this->keyAttribute, $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } elseif (isset($v[$this->keyAttribute])) { + $k = $v[$this->keyAttribute]; + + if (\is_float($k)) { + $k = var_export($k, true); + } + + // remove the key attribute when required + if ($this->removeKeyAttribute) { + unset($v[$this->keyAttribute]); + } + + // if only "value" is left + if (array_keys($v) === ['value']) { + $v = $v['value']; + if ($this->prototype instanceof ArrayNode && ($children = $this->prototype->getChildren()) && \array_key_exists('value', $children)) { + $valuePrototype = current($this->valuePrototypes) ?: clone $children['value']; + $valuePrototype->parent = $this; + $originalClosures = $this->prototype->normalizationClosures; + if (\is_array($originalClosures)) { + $valuePrototypeClosures = $valuePrototype->normalizationClosures; + $valuePrototype->normalizationClosures = \is_array($valuePrototypeClosures) ? array_merge($originalClosures, $valuePrototypeClosures) : $originalClosures; + } + $this->valuePrototypes[$k] = $valuePrototype; + } + } + } + + if (\array_key_exists($k, $normalized)) { + $ex = new DuplicateKeyException(sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + $prototype = $this->getPrototypeForChild($k); + if (null !== $this->keyAttribute || !$isList) { + $normalized[$k] = $prototype->normalize($v); + } else { + $normalized[] = $prototype->normalize($v); + } + } + + return $normalized; + } + + /** + * {@inheritdoc} + */ + protected function mergeValues(mixed $leftSide, mixed $rightSide): mixed + { + if (false === $rightSide) { + // if this is still false after the last config has been merged the + // finalization pass will take care of removing this key entirely + return false; + } + + if (false === $leftSide || !$this->performDeepMerging) { + return $rightSide; + } + + $isList = array_is_list($rightSide); + foreach ($rightSide as $k => $v) { + // prototype, and key is irrelevant there are no named keys, append the element + if (null === $this->keyAttribute && $isList) { + $leftSide[] = $v; + continue; + } + + // no conflict + if (!\array_key_exists($k, $leftSide)) { + if (!$this->allowNewKeys) { + $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file.', $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + + $leftSide[$k] = $v; + continue; + } + + $prototype = $this->getPrototypeForChild($k); + $leftSide[$k] = $prototype->merge($leftSide[$k], $v); + } + + return $leftSide; + } + + /** + * Returns a prototype for the child node that is associated to $key in the value array. + * For general child nodes, this will be $this->prototype. + * But if $this->removeKeyAttribute is true and there are only two keys in the child node: + * one is same as this->keyAttribute and the other is 'value', then the prototype will be different. + * + * For example, assume $this->keyAttribute is 'name' and the value array is as follows: + * + * [ + * [ + * 'name' => 'name001', + * 'value' => 'value001' + * ] + * ] + * + * Now, the key is 0 and the child node is: + * + * [ + * 'name' => 'name001', + * 'value' => 'value001' + * ] + * + * When normalizing the value array, the 'name' element will removed from the child node + * and its value becomes the new key of the child node: + * + * [ + * 'name001' => ['value' => 'value001'] + * ] + * + * Now only 'value' element is left in the child node which can be further simplified into a string: + * + * ['name001' => 'value001'] + * + * Now, the key becomes 'name001' and the child node becomes 'value001' and + * the prototype of child node 'name001' should be a ScalarNode instead of an ArrayNode instance. + */ + private function getPrototypeForChild(string $key): mixed + { + $prototype = $this->valuePrototypes[$key] ?? $this->prototype; + $prototype->setName($key); + + return $prototype; + } +} diff --git a/vendor/symfony/config/Definition/ScalarNode.php b/vendor/symfony/config/Definition/ScalarNode.php new file mode 100644 index 0000000..50e0365 --- /dev/null +++ b/vendor/symfony/config/Definition/ScalarNode.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a scalar value in the config tree. + * + * The following values are considered scalars: + * * booleans + * * strings + * * null + * * integers + * * floats + * + * @author Johannes M. Schmitt + */ +class ScalarNode extends VariableNode +{ + /** + * {@inheritdoc} + */ + protected function validateType(mixed $value) + { + if (!\is_scalar($value) && null !== $value) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "scalar", but got "%s".', $this->getPath(), get_debug_type($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * {@inheritdoc} + */ + protected function isValueEmpty(mixed $value): bool + { + // assume environment variables are never empty (which in practice is likely to be true during runtime) + // not doing so breaks many configs that are valid today + if ($this->isHandlingPlaceholder()) { + return false; + } + + return null === $value || '' === $value; + } + + /** + * {@inheritdoc} + */ + protected function getValidPlaceholderTypes(): array + { + return ['bool', 'int', 'float', 'string']; + } +} diff --git a/vendor/symfony/config/Definition/VariableNode.php b/vendor/symfony/config/Definition/VariableNode.php new file mode 100644 index 0000000..a06e0e1 --- /dev/null +++ b/vendor/symfony/config/Definition/VariableNode.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * This node represents a value of variable type in the config tree. + * + * This node is intended for values of arbitrary type. + * Any PHP type is accepted as a value. + * + * @author Jeremy Mikola + */ +class VariableNode extends BaseNode implements PrototypeNodeInterface +{ + protected $defaultValueSet = false; + protected $defaultValue; + protected $allowEmptyValue = true; + + public function setDefaultValue(mixed $value) + { + $this->defaultValueSet = true; + $this->defaultValue = $value; + } + + /** + * {@inheritdoc} + */ + public function hasDefaultValue(): bool + { + return $this->defaultValueSet; + } + + /** + * {@inheritdoc} + */ + public function getDefaultValue(): mixed + { + $v = $this->defaultValue; + + return $v instanceof \Closure ? $v() : $v; + } + + /** + * Sets if this node is allowed to have an empty value. + * + * @param bool $boolean True if this entity will accept empty values + */ + public function setAllowEmptyValue(bool $boolean) + { + $this->allowEmptyValue = $boolean; + } + + /** + * {@inheritdoc} + */ + public function setName(string $name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + protected function validateType(mixed $value) + { + } + + /** + * {@inheritdoc} + */ + protected function finalizeValue(mixed $value): mixed + { + // deny environment variables only when using custom validators + // this avoids ever passing an empty value to final validation closures + if (!$this->allowEmptyValue && $this->isHandlingPlaceholder() && $this->finalValidationClosures) { + $e = new InvalidConfigurationException(sprintf('The path "%s" cannot contain an environment variable when empty values are not allowed by definition and are validated.', $this->getPath())); + if ($hint = $this->getInfo()) { + $e->addHint($hint); + } + $e->setPath($this->getPath()); + + throw $e; + } + + if (!$this->allowEmptyValue && $this->isValueEmpty($value)) { + $ex = new InvalidConfigurationException(sprintf('The path "%s" cannot contain an empty value, but got %s.', $this->getPath(), json_encode($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + protected function normalizeValue(mixed $value): mixed + { + return $value; + } + + /** + * {@inheritdoc} + */ + protected function mergeValues(mixed $leftSide, mixed $rightSide): mixed + { + return $rightSide; + } + + /** + * Evaluates if the given value is to be treated as empty. + * + * By default, PHP's empty() function is used to test for emptiness. This + * method may be overridden by subtypes to better match their understanding + * of empty data. + * + * @see finalizeValue() + */ + protected function isValueEmpty(mixed $value): bool + { + return empty($value); + } +} diff --git a/vendor/symfony/config/Exception/FileLoaderImportCircularReferenceException.php b/vendor/symfony/config/Exception/FileLoaderImportCircularReferenceException.php new file mode 100644 index 0000000..da0b55b --- /dev/null +++ b/vendor/symfony/config/Exception/FileLoaderImportCircularReferenceException.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Exception; + +/** + * Exception class for when a circular reference is detected when importing resources. + * + * @author Fabien Potencier + */ +class FileLoaderImportCircularReferenceException extends LoaderLoadException +{ + public function __construct(array $resources, int $code = 0, \Throwable $previous = null) + { + $message = sprintf('Circular reference detected in "%s" ("%s" > "%s").', $this->varToString($resources[0]), implode('" > "', $resources), $resources[0]); + + \Exception::__construct($message, $code, $previous); + } +} diff --git a/vendor/symfony/config/Exception/FileLocatorFileNotFoundException.php b/vendor/symfony/config/Exception/FileLocatorFileNotFoundException.php new file mode 100644 index 0000000..92700c1 --- /dev/null +++ b/vendor/symfony/config/Exception/FileLocatorFileNotFoundException.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Exception; + +/** + * File locator exception if a file does not exist. + * + * @author Leo Feyer + */ +class FileLocatorFileNotFoundException extends \InvalidArgumentException +{ + private array $paths; + + public function __construct(string $message = '', int $code = 0, \Throwable $previous = null, array $paths = []) + { + parent::__construct($message, $code, $previous); + + $this->paths = $paths; + } + + public function getPaths() + { + return $this->paths; + } +} diff --git a/vendor/symfony/config/Exception/LoaderLoadException.php b/vendor/symfony/config/Exception/LoaderLoadException.php new file mode 100644 index 0000000..97e804e --- /dev/null +++ b/vendor/symfony/config/Exception/LoaderLoadException.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Exception; + +/** + * Exception class for when a resource cannot be loaded or imported. + * + * @author Ryan Weaver + */ +class LoaderLoadException extends \Exception +{ + /** + * @param string $resource The resource that could not be imported + * @param string|null $sourceResource The original resource importing the new resource + * @param int $code The error code + * @param \Throwable|null $previous A previous exception + * @param string|null $type The type of resource + */ + public function __construct(string $resource, string $sourceResource = null, int $code = 0, \Throwable $previous = null, string $type = null) + { + $message = ''; + if ($previous) { + // Include the previous exception, to help the user see what might be the underlying cause + + // Trim the trailing period of the previous message. We only want 1 period remove so no rtrim... + if ('.' === substr($previous->getMessage(), -1)) { + $trimmedMessage = substr($previous->getMessage(), 0, -1); + $message .= sprintf('%s', $trimmedMessage).' in '; + } else { + $message .= sprintf('%s', $previous->getMessage()).' in '; + } + $message .= $resource.' '; + + // show tweaked trace to complete the human readable sentence + if (null === $sourceResource) { + $message .= sprintf('(which is loaded in resource "%s")', $resource); + } else { + $message .= sprintf('(which is being imported from "%s")', $sourceResource); + } + $message .= '.'; + + // if there's no previous message, present it the default way + } elseif (null === $sourceResource) { + $message .= sprintf('Cannot load resource "%s".', $resource); + } else { + $message .= sprintf('Cannot import resource "%s" from "%s".', $resource, $sourceResource); + } + + // Is the resource located inside a bundle? + if ('@' === $resource[0]) { + $parts = explode(\DIRECTORY_SEPARATOR, $resource); + $bundle = substr($parts[0], 1); + $message .= sprintf(' Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle); + $message .= sprintf(' If the bundle is registered, make sure the bundle path "%s" is not empty.', $resource); + } elseif (null !== $type) { + $message .= sprintf(' Make sure there is a loader supporting the "%s" type.', $type); + } + + parent::__construct($message, $code, $previous); + } + + protected function varToString(mixed $var) + { + if (\is_object($var)) { + return sprintf('Object(%s)', \get_class($var)); + } + + if (\is_array($var)) { + $a = []; + foreach ($var as $k => $v) { + $a[] = sprintf('%s => %s', $k, $this->varToString($v)); + } + + return sprintf('Array(%s)', implode(', ', $a)); + } + + if (\is_resource($var)) { + return sprintf('Resource(%s)', get_resource_type($var)); + } + + if (null === $var) { + return 'null'; + } + + if (false === $var) { + return 'false'; + } + + if (true === $var) { + return 'true'; + } + + return (string) $var; + } +} diff --git a/vendor/symfony/config/FileLocator.php b/vendor/symfony/config/FileLocator.php new file mode 100644 index 0000000..21122e5 --- /dev/null +++ b/vendor/symfony/config/FileLocator.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; + +/** + * FileLocator uses an array of pre-defined paths to find files. + * + * @author Fabien Potencier + */ +class FileLocator implements FileLocatorInterface +{ + protected $paths; + + /** + * @param string|string[] $paths A path or an array of paths where to look for resources + */ + public function __construct(string|array $paths = []) + { + $this->paths = (array) $paths; + } + + /** + * {@inheritdoc} + */ + public function locate(string $name, string $currentPath = null, bool $first = true) + { + if ('' === $name) { + throw new \InvalidArgumentException('An empty file name is not valid to be located.'); + } + + if ($this->isAbsolutePath($name)) { + if (!file_exists($name)) { + throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist.', $name), 0, null, [$name]); + } + + return $name; + } + + $paths = $this->paths; + + if (null !== $currentPath) { + array_unshift($paths, $currentPath); + } + + $paths = array_unique($paths); + $filepaths = $notfound = []; + + foreach ($paths as $path) { + if (@file_exists($file = $path.\DIRECTORY_SEPARATOR.$name)) { + if (true === $first) { + return $file; + } + $filepaths[] = $file; + } else { + $notfound[] = $file; + } + } + + if (!$filepaths) { + throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist (in: "%s").', $name, implode('", "', $paths)), 0, null, $notfound); + } + + return $filepaths; + } + + /** + * Returns whether the file path is an absolute path. + */ + private function isAbsolutePath(string $file): bool + { + if ('/' === $file[0] || '\\' === $file[0] + || (\strlen($file) > 3 && ctype_alpha($file[0]) + && ':' === $file[1] + && ('\\' === $file[2] || '/' === $file[2]) + ) + || null !== parse_url($file, \PHP_URL_SCHEME) + ) { + return true; + } + + return false; + } +} diff --git a/vendor/symfony/config/FileLocatorInterface.php b/vendor/symfony/config/FileLocatorInterface.php new file mode 100644 index 0000000..e3ca1d4 --- /dev/null +++ b/vendor/symfony/config/FileLocatorInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; + +/** + * @author Fabien Potencier + */ +interface FileLocatorInterface +{ + /** + * Returns a full path for a given file name. + * + * @param string $name The file name to locate + * @param string|null $currentPath The current path + * @param bool $first Whether to return the first occurrence or an array of filenames + * + * @return string|array The full path to the file or an array of file paths + * + * @throws \InvalidArgumentException If $name is empty + * @throws FileLocatorFileNotFoundException If a file is not found + */ + public function locate(string $name, string $currentPath = null, bool $first = true); +} diff --git a/vendor/symfony/config/LICENSE b/vendor/symfony/config/LICENSE new file mode 100644 index 0000000..0083704 --- /dev/null +++ b/vendor/symfony/config/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2023 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/config/Loader/DelegatingLoader.php b/vendor/symfony/config/Loader/DelegatingLoader.php new file mode 100644 index 0000000..29c2d30 --- /dev/null +++ b/vendor/symfony/config/Loader/DelegatingLoader.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +use Symfony\Component\Config\Exception\LoaderLoadException; + +/** + * DelegatingLoader delegates loading to other loaders using a loader resolver. + * + * This loader acts as an array of LoaderInterface objects - each having + * a chance to load a given resource (handled by the resolver) + * + * @author Fabien Potencier + */ +class DelegatingLoader extends Loader +{ + public function __construct(LoaderResolverInterface $resolver) + { + $this->resolver = $resolver; + } + + /** + * {@inheritdoc} + */ + public function load(mixed $resource, string $type = null): mixed + { + if (false === $loader = $this->resolver->resolve($resource, $type)) { + throw new LoaderLoadException($resource, null, 0, null, $type); + } + + return $loader->load($resource, $type); + } + + /** + * {@inheritdoc} + */ + public function supports(mixed $resource, string $type = null): bool + { + return false !== $this->resolver->resolve($resource, $type); + } +} diff --git a/vendor/symfony/config/Loader/FileLoader.php b/vendor/symfony/config/Loader/FileLoader.php new file mode 100644 index 0000000..c479f75 --- /dev/null +++ b/vendor/symfony/config/Loader/FileLoader.php @@ -0,0 +1,178 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException; +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; +use Symfony\Component\Config\Exception\LoaderLoadException; +use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Component\Config\Resource\FileExistenceResource; +use Symfony\Component\Config\Resource\GlobResource; + +/** + * FileLoader is the abstract class used by all built-in loaders that are file based. + * + * @author Fabien Potencier + */ +abstract class FileLoader extends Loader +{ + protected static $loading = []; + + protected $locator; + + private ?string $currentDir = null; + + public function __construct(FileLocatorInterface $locator, string $env = null) + { + $this->locator = $locator; + parent::__construct($env); + } + + /** + * Sets the current directory. + */ + public function setCurrentDir(string $dir) + { + $this->currentDir = $dir; + } + + /** + * Returns the file locator used by this loader. + */ + public function getLocator(): FileLocatorInterface + { + return $this->locator; + } + + /** + * Imports a resource. + * + * @param mixed $resource A Resource + * @param string|null $type The resource type or null if unknown + * @param bool $ignoreErrors Whether to ignore import errors or not + * @param string|null $sourceResource The original resource importing the new resource + * @param string|string[]|null $exclude Glob patterns to exclude from the import + * + * @return mixed + * + * @throws LoaderLoadException + * @throws FileLoaderImportCircularReferenceException + * @throws FileLocatorFileNotFoundException + */ + public function import(mixed $resource, string $type = null, bool $ignoreErrors = false, string $sourceResource = null, string|array $exclude = null) + { + if (\is_string($resource) && \strlen($resource) !== ($i = strcspn($resource, '*?{[')) && !str_contains($resource, "\n")) { + $excluded = []; + foreach ((array) $exclude as $pattern) { + foreach ($this->glob($pattern, true, $_, false, true) as $path => $info) { + // normalize Windows slashes and remove trailing slashes + $excluded[rtrim(str_replace('\\', '/', $path), '/')] = true; + } + } + + $ret = []; + $isSubpath = 0 !== $i && str_contains(substr($resource, 0, $i), '/'); + foreach ($this->glob($resource, false, $_, $ignoreErrors || !$isSubpath, false, $excluded) as $path => $info) { + if (null !== $res = $this->doImport($path, 'glob' === $type ? null : $type, $ignoreErrors, $sourceResource)) { + $ret[] = $res; + } + $isSubpath = true; + } + + if ($isSubpath) { + return isset($ret[1]) ? $ret : ($ret[0] ?? null); + } + } + + return $this->doImport($resource, $type, $ignoreErrors, $sourceResource); + } + + /** + * @internal + */ + protected function glob(string $pattern, bool $recursive, array|GlobResource &$resource = null, bool $ignoreErrors = false, bool $forExclusion = false, array $excluded = []) + { + if (\strlen($pattern) === $i = strcspn($pattern, '*?{[')) { + $prefix = $pattern; + $pattern = ''; + } elseif (0 === $i || !str_contains(substr($pattern, 0, $i), '/')) { + $prefix = '.'; + $pattern = '/'.$pattern; + } else { + $prefix = \dirname(substr($pattern, 0, 1 + $i)); + $pattern = substr($pattern, \strlen($prefix)); + } + + try { + $prefix = $this->locator->locate($prefix, $this->currentDir, true); + } catch (FileLocatorFileNotFoundException $e) { + if (!$ignoreErrors) { + throw $e; + } + + $resource = []; + foreach ($e->getPaths() as $path) { + $resource[] = new FileExistenceResource($path); + } + + return; + } + $resource = new GlobResource($prefix, $pattern, $recursive, $forExclusion, $excluded); + + yield from $resource; + } + + private function doImport(mixed $resource, string $type = null, bool $ignoreErrors = false, string $sourceResource = null) + { + try { + $loader = $this->resolve($resource, $type); + + if ($loader instanceof self && null !== $this->currentDir) { + $resource = $loader->getLocator()->locate($resource, $this->currentDir, false); + } + + $resources = \is_array($resource) ? $resource : [$resource]; + for ($i = 0; $i < $resourcesCount = \count($resources); ++$i) { + if (isset(self::$loading[$resources[$i]])) { + if ($i == $resourcesCount - 1) { + throw new FileLoaderImportCircularReferenceException(array_keys(self::$loading)); + } + } else { + $resource = $resources[$i]; + break; + } + } + self::$loading[$resource] = true; + + try { + $ret = $loader->load($resource, $type); + } finally { + unset(self::$loading[$resource]); + } + + return $ret; + } catch (FileLoaderImportCircularReferenceException $e) { + throw $e; + } catch (\Exception $e) { + if (!$ignoreErrors) { + // prevent embedded imports from nesting multiple exceptions + if ($e instanceof LoaderLoadException) { + throw $e; + } + + throw new LoaderLoadException($resource, $sourceResource, 0, $e, $type); + } + } + + return null; + } +} diff --git a/vendor/symfony/config/Loader/GlobFileLoader.php b/vendor/symfony/config/Loader/GlobFileLoader.php new file mode 100644 index 0000000..f52d0f7 --- /dev/null +++ b/vendor/symfony/config/Loader/GlobFileLoader.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * GlobFileLoader loads files from a glob pattern. + * + * @author Fabien Potencier + */ +class GlobFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load(mixed $resource, string $type = null): mixed + { + return $this->import($resource); + } + + /** + * {@inheritdoc} + */ + public function supports(mixed $resource, string $type = null): bool + { + return 'glob' === $type; + } +} diff --git a/vendor/symfony/config/Loader/Loader.php b/vendor/symfony/config/Loader/Loader.php new file mode 100644 index 0000000..e0974fb --- /dev/null +++ b/vendor/symfony/config/Loader/Loader.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +use Symfony\Component\Config\Exception\LoaderLoadException; + +/** + * Loader is the abstract class used by all built-in loaders. + * + * @author Fabien Potencier + */ +abstract class Loader implements LoaderInterface +{ + protected $resolver; + protected $env; + + public function __construct(string $env = null) + { + $this->env = $env; + } + + /** + * {@inheritdoc} + */ + public function getResolver(): LoaderResolverInterface + { + return $this->resolver; + } + + /** + * {@inheritdoc} + */ + public function setResolver(LoaderResolverInterface $resolver) + { + $this->resolver = $resolver; + } + + /** + * Imports a resource. + * + * @return mixed + */ + public function import(mixed $resource, string $type = null) + { + return $this->resolve($resource, $type)->load($resource, $type); + } + + /** + * Finds a loader able to load an imported resource. + * + * @throws LoaderLoadException If no loader is found + */ + public function resolve(mixed $resource, string $type = null): LoaderInterface + { + if ($this->supports($resource, $type)) { + return $this; + } + + $loader = null === $this->resolver ? false : $this->resolver->resolve($resource, $type); + + if (false === $loader) { + throw new LoaderLoadException($resource, null, 0, null, $type); + } + + return $loader; + } +} diff --git a/vendor/symfony/config/Loader/LoaderInterface.php b/vendor/symfony/config/Loader/LoaderInterface.php new file mode 100644 index 0000000..b94a437 --- /dev/null +++ b/vendor/symfony/config/Loader/LoaderInterface.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * LoaderInterface is the interface implemented by all loader classes. + * + * @author Fabien Potencier + */ +interface LoaderInterface +{ + /** + * Loads a resource. + * + * @return mixed + * + * @throws \Exception If something went wrong + */ + public function load(mixed $resource, string $type = null); + + /** + * Returns whether this class supports the given resource. + * + * @param mixed $resource A resource + * + * @return bool + */ + public function supports(mixed $resource, string $type = null); + + /** + * Gets the loader resolver. + * + * @return LoaderResolverInterface + */ + public function getResolver(); + + /** + * Sets the loader resolver. + */ + public function setResolver(LoaderResolverInterface $resolver); +} diff --git a/vendor/symfony/config/Loader/LoaderResolver.php b/vendor/symfony/config/Loader/LoaderResolver.php new file mode 100644 index 0000000..81e555f --- /dev/null +++ b/vendor/symfony/config/Loader/LoaderResolver.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * LoaderResolver selects a loader for a given resource. + * + * A resource can be anything (e.g. a full path to a config file or a Closure). + * Each loader determines whether it can load a resource and how. + * + * @author Fabien Potencier + */ +class LoaderResolver implements LoaderResolverInterface +{ + /** + * @var LoaderInterface[] An array of LoaderInterface objects + */ + private array $loaders = []; + + /** + * @param LoaderInterface[] $loaders An array of loaders + */ + public function __construct(array $loaders = []) + { + foreach ($loaders as $loader) { + $this->addLoader($loader); + } + } + + /** + * {@inheritdoc} + */ + public function resolve(mixed $resource, string $type = null): LoaderInterface|false + { + foreach ($this->loaders as $loader) { + if ($loader->supports($resource, $type)) { + return $loader; + } + } + + return false; + } + + public function addLoader(LoaderInterface $loader) + { + $this->loaders[] = $loader; + $loader->setResolver($this); + } + + /** + * Returns the registered loaders. + * + * @return LoaderInterface[] + */ + public function getLoaders(): array + { + return $this->loaders; + } +} diff --git a/vendor/symfony/config/Loader/LoaderResolverInterface.php b/vendor/symfony/config/Loader/LoaderResolverInterface.php new file mode 100644 index 0000000..076c520 --- /dev/null +++ b/vendor/symfony/config/Loader/LoaderResolverInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * LoaderResolverInterface selects a loader for a given resource. + * + * @author Fabien Potencier + */ +interface LoaderResolverInterface +{ + /** + * Returns a loader able to load the resource. + * + * @param string|null $type The resource type or null if unknown + */ + public function resolve(mixed $resource, string $type = null): LoaderInterface|false; +} diff --git a/vendor/symfony/config/Loader/ParamConfigurator.php b/vendor/symfony/config/Loader/ParamConfigurator.php new file mode 100644 index 0000000..d91de6a --- /dev/null +++ b/vendor/symfony/config/Loader/ParamConfigurator.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * Placeholder for a parameter. + * + * @author Tobias Nyholm + */ +class ParamConfigurator +{ + private string $name; + + public function __construct(string $name) + { + $this->name = $name; + } + + public function __toString(): string + { + return '%'.$this->name.'%'; + } +} diff --git a/vendor/symfony/config/README.md b/vendor/symfony/config/README.md new file mode 100644 index 0000000..10c2ddd --- /dev/null +++ b/vendor/symfony/config/README.md @@ -0,0 +1,15 @@ +Config Component +================ + +The Config component helps find, load, combine, autofill and validate +configuration values of any kind, whatever their source may be (YAML, XML, INI +files, or for instance a database). + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/config.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/config/Resource/ClassExistenceResource.php b/vendor/symfony/config/Resource/ClassExistenceResource.php new file mode 100644 index 0000000..c948ca5 --- /dev/null +++ b/vendor/symfony/config/Resource/ClassExistenceResource.php @@ -0,0 +1,232 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * ClassExistenceResource represents a class existence. + * Freshness is only evaluated against resource existence. + * + * The resource must be a fully-qualified class name. + * + * @author Fabien Potencier + * + * @final + */ +class ClassExistenceResource implements SelfCheckingResourceInterface +{ + private string $resource; + private ?array $exists = null; + + private static int $autoloadLevel = 0; + private static ?string $autoloadedClass = null; + private static array $existsCache = []; + + /** + * @param string $resource The fully-qualified class name + * @param bool|null $exists Boolean when the existency check has already been done + */ + public function __construct(string $resource, bool $exists = null) + { + $this->resource = $resource; + if (null !== $exists) { + $this->exists = [$exists, null]; + } + } + + public function __toString(): string + { + return $this->resource; + } + + public function getResource(): string + { + return $this->resource; + } + + /** + * {@inheritdoc} + * + * @throws \ReflectionException when a parent class/interface/trait is not found + */ + public function isFresh(int $timestamp): bool + { + $loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false); + + if (null !== $exists = &self::$existsCache[$this->resource]) { + if ($loaded) { + $exists = [true, null]; + } elseif (0 >= $timestamp && !$exists[0] && null !== $exists[1]) { + throw new \ReflectionException($exists[1]); + } + } elseif ([false, null] === $exists = [$loaded, null]) { + if (!self::$autoloadLevel++) { + spl_autoload_register(__CLASS__.'::throwOnRequiredClass'); + } + $autoloadedClass = self::$autoloadedClass; + self::$autoloadedClass = ltrim($this->resource, '\\'); + + try { + $exists[0] = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false); + } catch (\Exception $e) { + $exists[1] = $e->getMessage(); + + try { + self::throwOnRequiredClass($this->resource, $e); + } catch (\ReflectionException $e) { + if (0 >= $timestamp) { + throw $e; + } + } + } catch (\Throwable $e) { + $exists[1] = $e->getMessage(); + + throw $e; + } finally { + self::$autoloadedClass = $autoloadedClass; + if (!--self::$autoloadLevel) { + spl_autoload_unregister(__CLASS__.'::throwOnRequiredClass'); + } + } + } + + if (null === $this->exists) { + $this->exists = $exists; + } + + return $this->exists[0] xor !$exists[0]; + } + + /** + * @internal + */ + public function __sleep(): array + { + if (null === $this->exists) { + $this->isFresh(0); + } + + return ['resource', 'exists']; + } + + /** + * @internal + */ + public function __wakeup() + { + if (\is_bool($this->exists)) { + $this->exists = [$this->exists, null]; + } + } + + /** + * Throws a reflection exception when the passed class does not exist but is required. + * + * A class is considered "not required" when it's loaded as part of a "class_exists" or similar check. + * + * This function can be used as an autoload function to throw a reflection + * exception if the class was not found by previous autoload functions. + * + * A previous exception can be passed. In this case, the class is considered as being + * required totally, so if it doesn't exist, a reflection exception is always thrown. + * If it exists, the previous exception is rethrown. + * + * @throws \ReflectionException + * + * @internal + */ + public static function throwOnRequiredClass(string $class, \Exception $previous = null) + { + // If the passed class is the resource being checked, we shouldn't throw. + if (null === $previous && self::$autoloadedClass === $class) { + return; + } + + if (class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) { + if (null !== $previous) { + throw $previous; + } + + return; + } + + if ($previous instanceof \ReflectionException) { + throw $previous; + } + + $message = sprintf('Class "%s" not found.', $class); + + if (self::$autoloadedClass !== $class) { + $message = substr_replace($message, sprintf(' while loading "%s"', self::$autoloadedClass), -1, 0); + } + + if (null !== $previous) { + $message = $previous->getMessage(); + } + + $e = new \ReflectionException($message, 0, $previous); + + if (null !== $previous) { + throw $e; + } + + $trace = debug_backtrace(); + $autoloadFrame = [ + 'function' => 'spl_autoload_call', + 'args' => [$class], + ]; + + if (isset($trace[1])) { + $callerFrame = $trace[1]; + $i = 2; + } elseif (false !== $i = array_search($autoloadFrame, $trace, true)) { + $callerFrame = $trace[++$i]; + } else { + throw $e; + } + + if (isset($callerFrame['function']) && !isset($callerFrame['class'])) { + switch ($callerFrame['function']) { + case 'get_class_methods': + case 'get_class_vars': + case 'get_parent_class': + case 'is_a': + case 'is_subclass_of': + case 'class_exists': + case 'class_implements': + case 'class_parents': + case 'trait_exists': + case 'defined': + case 'interface_exists': + case 'method_exists': + case 'property_exists': + case 'is_callable': + return; + } + + $props = [ + 'file' => $callerFrame['file'] ?? null, + 'line' => $callerFrame['line'] ?? null, + 'trace' => \array_slice($trace, 1 + $i), + ]; + + foreach ($props as $p => $v) { + if (null !== $v) { + $r = new \ReflectionProperty(\Exception::class, $p); + $r->setAccessible(true); + $r->setValue($e, $v); + } + } + } + + throw $e; + } +} diff --git a/vendor/symfony/config/Resource/ComposerResource.php b/vendor/symfony/config/Resource/ComposerResource.php new file mode 100644 index 0000000..807878a --- /dev/null +++ b/vendor/symfony/config/Resource/ComposerResource.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * ComposerResource tracks the PHP version and Composer dependencies. + * + * @author Nicolas Grekas + * + * @final + */ +class ComposerResource implements SelfCheckingResourceInterface +{ + private array $vendors; + + private static array $runtimeVendors; + + public function __construct() + { + self::refresh(); + $this->vendors = self::$runtimeVendors; + } + + public function getVendors(): array + { + return array_keys($this->vendors); + } + + public function __toString(): string + { + return __CLASS__; + } + + /** + * {@inheritdoc} + */ + public function isFresh(int $timestamp): bool + { + self::refresh(); + + return array_values(self::$runtimeVendors) === array_values($this->vendors); + } + + private static function refresh() + { + self::$runtimeVendors = []; + + foreach (get_declared_classes() as $class) { + if ('C' === $class[0] && str_starts_with($class, 'ComposerAutoloaderInit')) { + $r = new \ReflectionClass($class); + $v = \dirname($r->getFileName(), 2); + if (is_file($v.'/composer/installed.json')) { + self::$runtimeVendors[$v] = @filemtime($v.'/composer/installed.json'); + } + } + } + } +} diff --git a/vendor/symfony/config/Resource/DirectoryResource.php b/vendor/symfony/config/Resource/DirectoryResource.php new file mode 100644 index 0000000..751e749 --- /dev/null +++ b/vendor/symfony/config/Resource/DirectoryResource.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * DirectoryResource represents a resources stored in a subdirectory tree. + * + * @author Fabien Potencier + * + * @final + */ +class DirectoryResource implements SelfCheckingResourceInterface +{ + private string $resource; + private ?string $pattern; + + /** + * @param string $resource The file path to the resource + * @param string|null $pattern A pattern to restrict monitored files + * + * @throws \InvalidArgumentException + */ + public function __construct(string $resource, string $pattern = null) + { + $resolvedResource = realpath($resource) ?: (file_exists($resource) ? $resource : false); + $this->pattern = $pattern; + + if (false === $resolvedResource || !is_dir($resolvedResource)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $resource)); + } + + $this->resource = $resolvedResource; + } + + public function __toString(): string + { + return md5(serialize([$this->resource, $this->pattern])); + } + + public function getResource(): string + { + return $this->resource; + } + + public function getPattern(): ?string + { + return $this->pattern; + } + + /** + * {@inheritdoc} + */ + public function isFresh(int $timestamp): bool + { + if (!is_dir($this->resource)) { + return false; + } + + if ($timestamp < filemtime($this->resource)) { + return false; + } + + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->resource), \RecursiveIteratorIterator::SELF_FIRST) as $file) { + // if regex filtering is enabled only check matching files + if ($this->pattern && $file->isFile() && !preg_match($this->pattern, $file->getBasename())) { + continue; + } + + // always monitor directories for changes, except the .. entries + // (otherwise deleted files wouldn't get detected) + if ($file->isDir() && str_ends_with($file, '/..')) { + continue; + } + + // for broken links + try { + $fileMTime = $file->getMTime(); + } catch (\RuntimeException $e) { + continue; + } + + // early return if a file's mtime exceeds the passed timestamp + if ($timestamp < $fileMTime) { + return false; + } + } + + return true; + } +} diff --git a/vendor/symfony/config/Resource/FileExistenceResource.php b/vendor/symfony/config/Resource/FileExistenceResource.php new file mode 100644 index 0000000..39c3d8b --- /dev/null +++ b/vendor/symfony/config/Resource/FileExistenceResource.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * FileExistenceResource represents a resource stored on the filesystem. + * Freshness is only evaluated against resource creation or deletion. + * + * The resource can be a file or a directory. + * + * @author Charles-Henri Bruyand + * + * @final + */ +class FileExistenceResource implements SelfCheckingResourceInterface +{ + private string $resource; + + private bool $exists; + + /** + * @param string $resource The file path to the resource + */ + public function __construct(string $resource) + { + $this->resource = $resource; + $this->exists = file_exists($resource); + } + + public function __toString(): string + { + return $this->resource; + } + + public function getResource(): string + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function isFresh(int $timestamp): bool + { + return file_exists($this->resource) === $this->exists; + } +} diff --git a/vendor/symfony/config/Resource/FileResource.php b/vendor/symfony/config/Resource/FileResource.php new file mode 100644 index 0000000..d10e8a5 --- /dev/null +++ b/vendor/symfony/config/Resource/FileResource.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * FileResource represents a resource stored on the filesystem. + * + * The resource can be a file or a directory. + * + * @author Fabien Potencier + * + * @final + */ +class FileResource implements SelfCheckingResourceInterface +{ + private string $resource; + + /** + * @param string $resource The file path to the resource + * + * @throws \InvalidArgumentException + */ + public function __construct(string $resource) + { + $resolvedResource = realpath($resource) ?: (file_exists($resource) ? $resource : false); + + if (false === $resolvedResource) { + throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $resource)); + } + + $this->resource = $resolvedResource; + } + + public function __toString(): string + { + return $this->resource; + } + + /** + * Returns the canonicalized, absolute path to the resource. + */ + public function getResource(): string + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function isFresh(int $timestamp): bool + { + return false !== ($filemtime = @filemtime($this->resource)) && $filemtime <= $timestamp; + } +} diff --git a/vendor/symfony/config/Resource/GlobResource.php b/vendor/symfony/config/Resource/GlobResource.php new file mode 100644 index 0000000..be35121 --- /dev/null +++ b/vendor/symfony/config/Resource/GlobResource.php @@ -0,0 +1,243 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\Glob; + +/** + * GlobResource represents a set of resources stored on the filesystem. + * + * Only existence/removal is tracked (not mtimes.) + * + * @author Nicolas Grekas + * + * @final + * + * @implements \IteratorAggregate + */ +class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface +{ + private string $prefix; + private string $pattern; + private bool $recursive; + private string $hash; + private bool $forExclusion; + private array $excludedPrefixes; + private int $globBrace; + + /** + * @param string $prefix A directory prefix + * @param string $pattern A glob pattern + * @param bool $recursive Whether directories should be scanned recursively or not + * + * @throws \InvalidArgumentException + */ + public function __construct(string $prefix, string $pattern, bool $recursive, bool $forExclusion = false, array $excludedPrefixes = []) + { + ksort($excludedPrefixes); + $resolvedPrefix = realpath($prefix) ?: (file_exists($prefix) ? $prefix : false); + $this->pattern = $pattern; + $this->recursive = $recursive; + $this->forExclusion = $forExclusion; + $this->excludedPrefixes = $excludedPrefixes; + $this->globBrace = \defined('GLOB_BRACE') ? \GLOB_BRACE : 0; + + if (false === $resolvedPrefix) { + throw new \InvalidArgumentException(sprintf('The path "%s" does not exist.', $prefix)); + } + + $this->prefix = $resolvedPrefix; + } + + public function getPrefix(): string + { + return $this->prefix; + } + + public function __toString(): string + { + return 'glob.'.$this->prefix.(int) $this->recursive.$this->pattern.(int) $this->forExclusion.implode("\0", $this->excludedPrefixes); + } + + /** + * {@inheritdoc} + */ + public function isFresh(int $timestamp): bool + { + $hash = $this->computeHash(); + $this->hash ??= $hash; + + return $this->hash === $hash; + } + + /** + * @internal + */ + public function __sleep(): array + { + $this->hash ??= $this->computeHash(); + + return ['prefix', 'pattern', 'recursive', 'hash', 'forExclusion', 'excludedPrefixes']; + } + + /** + * @internal + */ + public function __wakeup(): void + { + $this->globBrace = \defined('GLOB_BRACE') ? \GLOB_BRACE : 0; + } + + public function getIterator(): \Traversable + { + if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) { + return; + } + $prefix = str_replace('\\', '/', $this->prefix); + $paths = null; + + if ('' === $this->pattern && is_file($prefix)) { + $paths = [$this->prefix]; + } elseif (!str_starts_with($this->prefix, 'phar://') && !str_contains($this->pattern, '/**/')) { + if ($this->globBrace || !str_contains($this->pattern, '{')) { + $paths = glob($this->prefix.$this->pattern, \GLOB_NOSORT | $this->globBrace); + } elseif (!str_contains($this->pattern, '\\') || !preg_match('/\\\\[,{}]/', $this->pattern)) { + foreach ($this->expandGlob($this->pattern) as $p) { + $paths[] = glob($this->prefix.$p, \GLOB_NOSORT); + } + $paths = array_merge(...$paths); + } + } + + if (null !== $paths) { + natsort($paths); + foreach ($paths as $path) { + if ($this->excludedPrefixes) { + $normalizedPath = str_replace('\\', '/', $path); + do { + if (isset($this->excludedPrefixes[$dirPath = $normalizedPath])) { + continue 2; + } + } while ($prefix !== $dirPath && $dirPath !== $normalizedPath = \dirname($dirPath)); + } + + if (is_file($path)) { + yield $path => new \SplFileInfo($path); + } + if (!is_dir($path)) { + continue; + } + if ($this->forExclusion) { + yield $path => new \SplFileInfo($path); + continue; + } + if (!$this->recursive || isset($this->excludedPrefixes[str_replace('\\', '/', $path)])) { + continue; + } + $files = iterator_to_array(new \RecursiveIteratorIterator( + new \RecursiveCallbackFilterIterator( + new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), + function (\SplFileInfo $file, $path) { + return !isset($this->excludedPrefixes[str_replace('\\', '/', $path)]) && '.' !== $file->getBasename()[0]; + } + ), + \RecursiveIteratorIterator::LEAVES_ONLY + )); + uksort($files, 'strnatcmp'); + + foreach ($files as $path => $info) { + if ($info->isFile()) { + yield $path => $info; + } + } + } + + return; + } + + if (!class_exists(Finder::class)) { + throw new \LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $this->pattern)); + } + + if (is_file($prefix = $this->prefix)) { + $prefix = \dirname($prefix); + $pattern = basename($prefix).$this->pattern; + } else { + $pattern = $this->pattern; + } + + $finder = new Finder(); + $regex = Glob::toRegex($pattern); + if ($this->recursive) { + $regex = substr_replace($regex, '(/|$)', -2, 1); + } + + $prefixLen = \strlen($prefix); + foreach ($finder->followLinks()->sortByName()->in($prefix) as $path => $info) { + $normalizedPath = str_replace('\\', '/', $path); + if (!preg_match($regex, substr($normalizedPath, $prefixLen)) || !$info->isFile()) { + continue; + } + if ($this->excludedPrefixes) { + do { + if (isset($this->excludedPrefixes[$dirPath = $normalizedPath])) { + continue 2; + } + } while ($prefix !== $dirPath && $dirPath !== $normalizedPath = \dirname($dirPath)); + } + + yield $path => $info; + } + } + + private function computeHash(): string + { + $hash = hash_init('md5'); + + foreach ($this->getIterator() as $path => $info) { + hash_update($hash, $path."\n"); + } + + return hash_final($hash); + } + + private function expandGlob(string $pattern): array + { + $segments = preg_split('/\{([^{}]*+)\}/', $pattern, -1, \PREG_SPLIT_DELIM_CAPTURE); + $paths = [$segments[0]]; + $patterns = []; + + for ($i = 1; $i < \count($segments); $i += 2) { + $patterns = []; + + foreach (explode(',', $segments[$i]) as $s) { + foreach ($paths as $p) { + $patterns[] = $p.$s.$segments[1 + $i]; + } + } + + $paths = $patterns; + } + + $j = 0; + foreach ($patterns as $i => $p) { + if (str_contains($p, '{')) { + $p = $this->expandGlob($p); + array_splice($paths, $i + $j, 1, $p); + $j += \count($p) - 1; + } + } + + return $paths; + } +} diff --git a/vendor/symfony/config/Resource/ReflectionClassResource.php b/vendor/symfony/config/Resource/ReflectionClassResource.php new file mode 100644 index 0000000..6381d70 --- /dev/null +++ b/vendor/symfony/config/Resource/ReflectionClassResource.php @@ -0,0 +1,257 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; + +/** + * @author Nicolas Grekas + * + * @final + */ +class ReflectionClassResource implements SelfCheckingResourceInterface +{ + private array $files = []; + private string $className; + private \ReflectionClass $classReflector; + private array $excludedVendors = []; + private string $hash; + + public function __construct(\ReflectionClass $classReflector, array $excludedVendors = []) + { + $this->className = $classReflector->name; + $this->classReflector = $classReflector; + $this->excludedVendors = $excludedVendors; + } + + /** + * {@inheritdoc} + */ + public function isFresh(int $timestamp): bool + { + if (!isset($this->hash)) { + $this->hash = $this->computeHash(); + $this->loadFiles($this->classReflector); + } + + foreach ($this->files as $file => $v) { + if (false === $filemtime = @filemtime($file)) { + return false; + } + + if ($filemtime > $timestamp) { + return $this->hash === $this->computeHash(); + } + } + + return true; + } + + public function __toString(): string + { + return 'reflection.'.$this->className; + } + + /** + * @internal + */ + public function __sleep(): array + { + if (!isset($this->hash)) { + $this->hash = $this->computeHash(); + $this->loadFiles($this->classReflector); + } + + return ['files', 'className', 'hash']; + } + + private function loadFiles(\ReflectionClass $class) + { + foreach ($class->getInterfaces() as $v) { + $this->loadFiles($v); + } + do { + $file = $class->getFileName(); + if (false !== $file && is_file($file)) { + foreach ($this->excludedVendors as $vendor) { + if (str_starts_with($file, $vendor) && false !== strpbrk(substr($file, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) { + $file = false; + break; + } + } + if ($file) { + $this->files[$file] = null; + } + } + foreach ($class->getTraits() as $v) { + $this->loadFiles($v); + } + } while ($class = $class->getParentClass()); + } + + private function computeHash(): string + { + try { + $this->classReflector ??= new \ReflectionClass($this->className); + } catch (\ReflectionException $e) { + // the class does not exist anymore + return false; + } + $hash = hash_init('md5'); + + foreach ($this->generateSignature($this->classReflector) as $info) { + hash_update($hash, $info); + } + + return hash_final($hash); + } + + private function generateSignature(\ReflectionClass $class): iterable + { + $attributes = []; + foreach ($class->getAttributes() as $a) { + $attributes[] = [$a->getName(), \PHP_VERSION_ID >= 80100 ? (string) $a : $a->getArguments()]; + } + yield print_r($attributes, true); + $attributes = []; + + yield $class->getDocComment(); + yield (int) $class->isFinal(); + yield (int) $class->isAbstract(); + + if ($class->isTrait()) { + yield print_r(class_uses($class->name), true); + } else { + yield print_r(class_parents($class->name), true); + yield print_r(class_implements($class->name), true); + yield print_r($class->getConstants(), true); + } + + if (!$class->isInterface()) { + $defaults = $class->getDefaultProperties(); + + foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $p) { + foreach ($p->getAttributes() as $a) { + $attributes[] = [$a->getName(), \PHP_VERSION_ID >= 80100 ? (string) $a : $a->getArguments()]; + } + yield print_r($attributes, true); + $attributes = []; + + yield $p->getDocComment(); + yield $p->isDefault() ? '' : ''; + yield $p->isPublic() ? 'public' : 'protected'; + yield $p->isStatic() ? 'static' : ''; + yield '$'.$p->name; + yield print_r(isset($defaults[$p->name]) && !\is_object($defaults[$p->name]) ? $defaults[$p->name] : null, true); + } + } + + $defined = \Closure::bind(static function ($c) { return \defined($c); }, null, $class->name); + + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) { + foreach ($m->getAttributes() as $a) { + $attributes[] = [$a->getName(), \PHP_VERSION_ID >= 80100 ? (string) $a : $a->getArguments()]; + } + yield print_r($attributes, true); + $attributes = []; + + $defaults = []; + $parametersWithUndefinedConstants = []; + foreach ($m->getParameters() as $p) { + foreach ($p->getAttributes() as $a) { + $attributes[] = [$a->getName(), \PHP_VERSION_ID >= 80100 ? (string) $a : $a->getArguments()]; + } + yield print_r($attributes, true); + $attributes = []; + + if (!$p->isDefaultValueAvailable()) { + $defaults[$p->name] = null; + + continue; + } + + if (\PHP_VERSION_ID >= 80100) { + $defaults[$p->name] = (string) $p; + + continue; + } + + if (!$p->isDefaultValueConstant() || $defined($p->getDefaultValueConstantName())) { + $defaults[$p->name] = $p->getDefaultValue(); + + continue; + } + + $defaults[$p->name] = $p->getDefaultValueConstantName(); + $parametersWithUndefinedConstants[$p->name] = true; + } + + if (!$parametersWithUndefinedConstants) { + yield preg_replace('/^ @@.*/m', '', $m); + } else { + $t = $m->getReturnType(); + $stack = [ + $m->getDocComment(), + $m->getName(), + $m->isAbstract(), + $m->isFinal(), + $m->isStatic(), + $m->isPublic(), + $m->isPrivate(), + $m->isProtected(), + $m->returnsReference(), + $t instanceof \ReflectionNamedType ? ((string) $t->allowsNull()).$t->getName() : (string) $t, + ]; + + foreach ($m->getParameters() as $p) { + if (!isset($parametersWithUndefinedConstants[$p->name])) { + $stack[] = (string) $p; + } else { + $t = $p->getType(); + $stack[] = $p->isOptional(); + $stack[] = $t instanceof \ReflectionNamedType ? ((string) $t->allowsNull()).$t->getName() : (string) $t; + $stack[] = $p->isPassedByReference(); + $stack[] = $p->isVariadic(); + $stack[] = $p->getName(); + } + } + + yield implode(',', $stack); + } + + yield print_r($defaults, true); + } + + if ($class->isAbstract() || $class->isInterface() || $class->isTrait()) { + return; + } + + if (interface_exists(EventSubscriberInterface::class, false) && $class->isSubclassOf(EventSubscriberInterface::class)) { + yield EventSubscriberInterface::class; + yield print_r($class->name::getSubscribedEvents(), true); + } + + if (interface_exists(MessageSubscriberInterface::class, false) && $class->isSubclassOf(MessageSubscriberInterface::class)) { + yield MessageSubscriberInterface::class; + foreach ($class->name::getHandledMessages() as $key => $value) { + yield $key.print_r($value, true); + } + } + + if (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) { + yield ServiceSubscriberInterface::class; + yield print_r($class->name::getSubscribedServices(), true); + } + } +} diff --git a/vendor/symfony/config/Resource/ResourceInterface.php b/vendor/symfony/config/Resource/ResourceInterface.php new file mode 100644 index 0000000..4fbe321 --- /dev/null +++ b/vendor/symfony/config/Resource/ResourceInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * ResourceInterface is the interface that must be implemented by all Resource classes. + * + * @author Fabien Potencier + */ +interface ResourceInterface +{ + /** + * Returns a string representation of the Resource. + * + * This method is necessary to allow for resource de-duplication, for example by means + * of array_unique(). The string returned need not have a particular meaning, but has + * to be identical for different ResourceInterface instances referring to the same + * resource; and it should be unlikely to collide with that of other, unrelated + * resource instances. + */ + public function __toString(): string; +} diff --git a/vendor/symfony/config/Resource/SelfCheckingResourceChecker.php b/vendor/symfony/config/Resource/SelfCheckingResourceChecker.php new file mode 100644 index 0000000..88f2a26 --- /dev/null +++ b/vendor/symfony/config/Resource/SelfCheckingResourceChecker.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +use Symfony\Component\Config\ResourceCheckerInterface; + +/** + * Resource checker for instances of SelfCheckingResourceInterface. + * + * As these resources perform the actual check themselves, we can provide + * this class as a standard way of validating them. + * + * @author Matthias Pigulla + */ +class SelfCheckingResourceChecker implements ResourceCheckerInterface +{ + // Common shared cache, because this checker can be used in different + // situations. For example, when using the full stack framework, the router + // and the container have their own cache. But they may check the very same + // resources + private static array $cache = []; + + public function supports(ResourceInterface $metadata): bool + { + return $metadata instanceof SelfCheckingResourceInterface; + } + + /** + * @param SelfCheckingResourceInterface $resource + */ + public function isFresh(ResourceInterface $resource, int $timestamp): bool + { + $key = "$resource:$timestamp"; + + return self::$cache[$key] ?? self::$cache[$key] = $resource->isFresh($timestamp); + } +} diff --git a/vendor/symfony/config/Resource/SelfCheckingResourceInterface.php b/vendor/symfony/config/Resource/SelfCheckingResourceInterface.php new file mode 100644 index 0000000..197ff1f --- /dev/null +++ b/vendor/symfony/config/Resource/SelfCheckingResourceInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * Interface for Resources that can check for freshness autonomously, + * without special support from external services. + * + * @author Matthias Pigulla + */ +interface SelfCheckingResourceInterface extends ResourceInterface +{ + /** + * Returns true if the resource has not been updated since the given timestamp. + * + * @param int $timestamp The last time the resource was loaded + */ + public function isFresh(int $timestamp): bool; +} diff --git a/vendor/symfony/config/ResourceCheckerConfigCache.php b/vendor/symfony/config/ResourceCheckerConfigCache.php new file mode 100644 index 0000000..28ea9fa --- /dev/null +++ b/vendor/symfony/config/ResourceCheckerConfigCache.php @@ -0,0 +1,182 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; + +/** + * ResourceCheckerConfigCache uses instances of ResourceCheckerInterface + * to check whether cached data is still fresh. + * + * @author Matthias Pigulla + */ +class ResourceCheckerConfigCache implements ConfigCacheInterface +{ + private string $file; + + /** + * @var iterable + */ + private iterable $resourceCheckers; + + /** + * @param string $file The absolute cache path + * @param iterable $resourceCheckers The ResourceCheckers to use for the freshness check + */ + public function __construct(string $file, iterable $resourceCheckers = []) + { + $this->file = $file; + $this->resourceCheckers = $resourceCheckers; + } + + /** + * {@inheritdoc} + */ + public function getPath(): string + { + return $this->file; + } + + /** + * Checks if the cache is still fresh. + * + * This implementation will make a decision solely based on the ResourceCheckers + * passed in the constructor. + * + * The first ResourceChecker that supports a given resource is considered authoritative. + * Resources with no matching ResourceChecker will silently be ignored and considered fresh. + */ + public function isFresh(): bool + { + if (!is_file($this->file)) { + return false; + } + + if ($this->resourceCheckers instanceof \Traversable && !$this->resourceCheckers instanceof \Countable) { + $this->resourceCheckers = iterator_to_array($this->resourceCheckers); + } + + if (!\count($this->resourceCheckers)) { + return true; // shortcut - if we don't have any checkers we don't need to bother with the meta file at all + } + + $metadata = $this->getMetaFile(); + + if (!is_file($metadata)) { + return false; + } + + $meta = $this->safelyUnserialize($metadata); + + if (false === $meta) { + return false; + } + + $time = filemtime($this->file); + + foreach ($meta as $resource) { + foreach ($this->resourceCheckers as $checker) { + if (!$checker->supports($resource)) { + continue; // next checker + } + if ($checker->isFresh($resource, $time)) { + break; // no need to further check this resource + } + + return false; // cache is stale + } + // no suitable checker found, ignore this resource + } + + return true; + } + + /** + * Writes cache. + * + * @param string $content The content to write in the cache + * @param ResourceInterface[] $metadata An array of metadata + * + * @throws \RuntimeException When cache file can't be written + */ + public function write(string $content, array $metadata = null) + { + $mode = 0666; + $umask = umask(); + $filesystem = new Filesystem(); + $filesystem->dumpFile($this->file, $content); + try { + $filesystem->chmod($this->file, $mode, $umask); + } catch (IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + + if (null !== $metadata) { + $filesystem->dumpFile($this->getMetaFile(), serialize($metadata)); + try { + $filesystem->chmod($this->getMetaFile(), $mode, $umask); + } catch (IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + } + + if (\function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) { + @opcache_invalidate($this->file, true); + } + } + + /** + * Gets the meta file path. + */ + private function getMetaFile(): string + { + return $this->file.'.meta'; + } + + private function safelyUnserialize(string $file) + { + $meta = false; + $content = file_get_contents($file); + $signalingException = new \UnexpectedValueException(); + $prevUnserializeHandler = ini_set('unserialize_callback_func', self::class.'::handleUnserializeCallback'); + $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$prevErrorHandler, $signalingException) { + if (__FILE__ === $file) { + throw $signalingException; + } + + return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false; + }); + + try { + $meta = unserialize($content); + } catch (\Throwable $e) { + if ($e !== $signalingException) { + throw $e; + } + } finally { + restore_error_handler(); + ini_set('unserialize_callback_func', $prevUnserializeHandler); + } + + return $meta; + } + + /** + * @internal + */ + public static function handleUnserializeCallback(string $class) + { + trigger_error('Class not found: '.$class); + } +} diff --git a/vendor/symfony/config/ResourceCheckerConfigCacheFactory.php b/vendor/symfony/config/ResourceCheckerConfigCacheFactory.php new file mode 100644 index 0000000..3a94d3f --- /dev/null +++ b/vendor/symfony/config/ResourceCheckerConfigCacheFactory.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +/** + * A ConfigCacheFactory implementation that validates the + * cache with an arbitrary set of ResourceCheckers. + * + * @author Matthias Pigulla + */ +class ResourceCheckerConfigCacheFactory implements ConfigCacheFactoryInterface +{ + private iterable $resourceCheckers = []; + + /** + * @param iterable $resourceCheckers + */ + public function __construct(iterable $resourceCheckers = []) + { + $this->resourceCheckers = $resourceCheckers; + } + + /** + * {@inheritdoc} + */ + public function cache(string $file, callable $callable): ConfigCacheInterface + { + $cache = new ResourceCheckerConfigCache($file, $this->resourceCheckers); + if (!$cache->isFresh()) { + $callable($cache); + } + + return $cache; + } +} diff --git a/vendor/symfony/config/ResourceCheckerInterface.php b/vendor/symfony/config/ResourceCheckerInterface.php new file mode 100644 index 0000000..6b1c6c5 --- /dev/null +++ b/vendor/symfony/config/ResourceCheckerInterface.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * Interface for ResourceCheckers. + * + * When a ResourceCheckerConfigCache instance is checked for freshness, all its associated + * metadata resources are passed to ResourceCheckers. The ResourceCheckers + * can then inspect the resources and decide whether the cache can be considered + * fresh or not. + * + * @author Matthias Pigulla + * @author Benjamin Klotz + */ +interface ResourceCheckerInterface +{ + /** + * Queries the ResourceChecker whether it can validate a given + * resource or not. + * + * @return bool + */ + public function supports(ResourceInterface $metadata); + + /** + * Validates the resource. + * + * @param int $timestamp The timestamp at which the cache associated with this resource was created + * + * @return bool + */ + public function isFresh(ResourceInterface $resource, int $timestamp); +} diff --git a/vendor/symfony/config/Util/Exception/InvalidXmlException.php b/vendor/symfony/config/Util/Exception/InvalidXmlException.php new file mode 100644 index 0000000..155571c --- /dev/null +++ b/vendor/symfony/config/Util/Exception/InvalidXmlException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Util\Exception; + +/** + * Exception class for when XML parsing with an XSD schema file path or a callable validator produces errors unrelated + * to the actual XML parsing. + * + * @author Ole Rößner + */ +class InvalidXmlException extends XmlParsingException +{ +} diff --git a/vendor/symfony/config/Util/Exception/XmlParsingException.php b/vendor/symfony/config/Util/Exception/XmlParsingException.php new file mode 100644 index 0000000..9bceed6 --- /dev/null +++ b/vendor/symfony/config/Util/Exception/XmlParsingException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Util\Exception; + +/** + * Exception class for when XML cannot be parsed properly. + * + * @author Ole Rößner + */ +class XmlParsingException extends \InvalidArgumentException +{ +} diff --git a/vendor/symfony/config/Util/XmlUtils.php b/vendor/symfony/config/Util/XmlUtils.php new file mode 100644 index 0000000..7d1644b --- /dev/null +++ b/vendor/symfony/config/Util/XmlUtils.php @@ -0,0 +1,269 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Util; + +use Symfony\Component\Config\Util\Exception\InvalidXmlException; +use Symfony\Component\Config\Util\Exception\XmlParsingException; + +/** + * XMLUtils is a bunch of utility methods to XML operations. + * + * This class contains static methods only and is not meant to be instantiated. + * + * @author Fabien Potencier + * @author Martin Hasoň + * @author Ole Rößner + */ +class XmlUtils +{ + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Parses an XML string. + * + * @param string $content An XML string + * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation + * + * @throws XmlParsingException When parsing of XML file returns error + * @throws InvalidXmlException When parsing of XML with schema or callable produces any errors unrelated to the XML parsing itself + * @throws \RuntimeException When DOM extension is missing + */ + public static function parse(string $content, string|callable $schemaOrCallable = null): \DOMDocument + { + if (!\extension_loaded('dom')) { + throw new \LogicException('Extension DOM is required.'); + } + + $internalErrors = libxml_use_internal_errors(true); + libxml_clear_errors(); + + $dom = new \DOMDocument(); + $dom->validateOnParse = true; + if (!$dom->loadXML($content, \LIBXML_NONET | \LIBXML_COMPACT)) { + throw new XmlParsingException(implode("\n", static::getXmlErrors($internalErrors))); + } + + $dom->normalizeDocument(); + + libxml_use_internal_errors($internalErrors); + + foreach ($dom->childNodes as $child) { + if (\XML_DOCUMENT_TYPE_NODE === $child->nodeType) { + throw new XmlParsingException('Document types are not allowed.'); + } + } + + if (null !== $schemaOrCallable) { + $internalErrors = libxml_use_internal_errors(true); + libxml_clear_errors(); + + $e = null; + if (\is_callable($schemaOrCallable)) { + try { + $valid = $schemaOrCallable($dom, $internalErrors); + } catch (\Exception $e) { + $valid = false; + } + } elseif (is_file($schemaOrCallable)) { + $schemaSource = file_get_contents((string) $schemaOrCallable); + $valid = @$dom->schemaValidateSource($schemaSource); + } else { + libxml_use_internal_errors($internalErrors); + + throw new XmlParsingException(sprintf('Invalid XSD file: "%s".', $schemaOrCallable)); + } + + if (!$valid) { + $messages = static::getXmlErrors($internalErrors); + if (empty($messages)) { + throw new InvalidXmlException('The XML is not valid.', 0, $e); + } + throw new XmlParsingException(implode("\n", $messages), 0, $e); + } + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + + return $dom; + } + + /** + * Loads an XML file. + * + * @param string $file An XML file path + * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation + * + * @throws \InvalidArgumentException When loading of XML file returns error + * @throws XmlParsingException When XML parsing returns any errors + * @throws \RuntimeException When DOM extension is missing + */ + public static function loadFile(string $file, string|callable $schemaOrCallable = null): \DOMDocument + { + if (!is_file($file)) { + throw new \InvalidArgumentException(sprintf('Resource "%s" is not a file.', $file)); + } + + if (!is_readable($file)) { + throw new \InvalidArgumentException(sprintf('File "%s" is not readable.', $file)); + } + + $content = @file_get_contents($file); + + if ('' === trim($content)) { + throw new \InvalidArgumentException(sprintf('File "%s" does not contain valid XML, it is empty.', $file)); + } + + try { + return static::parse($content, $schemaOrCallable); + } catch (InvalidXmlException $e) { + throw new XmlParsingException(sprintf('The XML file "%s" is not valid.', $file), 0, $e->getPrevious()); + } + } + + /** + * Converts a \DOMElement object to a PHP array. + * + * The following rules applies during the conversion: + * + * * Each tag is converted to a key value or an array + * if there is more than one "value" + * + * * The content of a tag is set under a "value" key (bar) + * if the tag also has some nested tags + * + * * The attributes are converted to keys () + * + * * The nested-tags are converted to keys (bar) + * + * @param \DOMElement $element A \DOMElement instance + * @param bool $checkPrefix Check prefix in an element or an attribute name + */ + public static function convertDomElementToArray(\DOMElement $element, bool $checkPrefix = true): mixed + { + $prefix = (string) $element->prefix; + $empty = true; + $config = []; + foreach ($element->attributes as $name => $node) { + if ($checkPrefix && !\in_array((string) $node->prefix, ['', $prefix], true)) { + continue; + } + $config[$name] = static::phpize($node->value); + $empty = false; + } + + $nodeValue = false; + foreach ($element->childNodes as $node) { + if ($node instanceof \DOMText) { + if ('' !== trim($node->nodeValue)) { + $nodeValue = trim($node->nodeValue); + $empty = false; + } + } elseif ($checkPrefix && $prefix != (string) $node->prefix) { + continue; + } elseif (!$node instanceof \DOMComment) { + $value = static::convertDomElementToArray($node, $checkPrefix); + + $key = $node->localName; + if (isset($config[$key])) { + if (!\is_array($config[$key]) || !\is_int(key($config[$key]))) { + $config[$key] = [$config[$key]]; + } + $config[$key][] = $value; + } else { + $config[$key] = $value; + } + + $empty = false; + } + } + + if (false !== $nodeValue) { + $value = static::phpize($nodeValue); + if (\count($config)) { + $config['value'] = $value; + } else { + $config = $value; + } + } + + return !$empty ? $config : null; + } + + /** + * Converts an xml value to a PHP type. + */ + public static function phpize(string|\Stringable $value): mixed + { + $value = (string) $value; + $lowercaseValue = strtolower($value); + + switch (true) { + case 'null' === $lowercaseValue: + return null; + case ctype_digit($value): + case isset($value[1]) && '-' === $value[0] && ctype_digit(substr($value, 1)): + $raw = $value; + $cast = (int) $value; + + return self::isOctal($value) ? \intval($value, 8) : (($raw === (string) $cast) ? $cast : $raw); + case 'true' === $lowercaseValue: + return true; + case 'false' === $lowercaseValue: + return false; + case isset($value[1]) && '0b' == $value[0].$value[1] && preg_match('/^0b[01]*$/', $value): + return bindec($value); + case is_numeric($value): + return '0x' === $value[0].$value[1] ? hexdec($value) : (float) $value; + case preg_match('/^0x[0-9a-f]++$/i', $value): + return hexdec($value); + case preg_match('/^[+-]?[0-9]+(\.[0-9]+)?$/', $value): + return (float) $value; + default: + return $value; + } + } + + protected static function getXmlErrors(bool $internalErrors) + { + $errors = []; + foreach (libxml_get_errors() as $error) { + $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', + \LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', + $error->code, + trim($error->message), + $error->file ?: 'n/a', + $error->line, + $error->column + ); + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + + return $errors; + } + + private static function isOctal(string $str): bool + { + if ('-' === $str[0]) { + $str = substr($str, 1); + } + + return $str === '0'.decoct(\intval($str, 8)); + } +} diff --git a/vendor/symfony/config/composer.json b/vendor/symfony/config/composer.json new file mode 100644 index 0000000..1375ad8 --- /dev/null +++ b/vendor/symfony/config/composer.json @@ -0,0 +1,45 @@ +{ + "name": "symfony/config", + "type": "library", + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "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": ">=8.0.2", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/filesystem": "^5.4|^6.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php81": "^1.22" + }, + "require-dev": { + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "conflict": { + "symfony/finder": "<4.4" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Config\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/vendor/symfony/console/Application.php b/vendor/symfony/console/Application.php new file mode 100644 index 0000000..b01efff --- /dev/null +++ b/vendor/symfony/console/Application.php @@ -0,0 +1,1267 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\CompleteCommand; +use Symfony\Component\Console\Command\DumpCompletionCommand; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\LazyCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Command\SignalableCommandInterface; +use Symfony\Component\Console\CommandLoader\CommandLoaderInterface; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleErrorEvent; +use Symfony\Component\Console\Event\ConsoleSignalEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Exception\CommandNotFoundException; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Exception\NamespaceNotFoundException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputAwareInterface; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\SignalRegistry\SignalRegistry; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\ErrorHandler\ErrorHandler; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\Service\ResetInterface; + +/** + * An Application is the container for a collection of commands. + * + * It is the main entry point of a Console application. + * + * This class is optimized for a standard CLI environment. + * + * Usage: + * + * $app = new Application('myapp', '1.0 (stable)'); + * $app->add(new SimpleCommand()); + * $app->run(); + * + * @author Fabien Potencier + */ +class Application implements ResetInterface +{ + private array $commands = []; + private bool $wantHelps = false; + private $runningCommand = null; + private string $name; + private string $version; + private $commandLoader = null; + private bool $catchExceptions = true; + private bool $autoExit = true; + private $definition; + private $helperSet; + private $dispatcher = null; + private $terminal; + private string $defaultCommand; + private bool $singleCommand = false; + private bool $initialized = false; + private $signalRegistry; + private array $signalsToDispatchEvent = []; + + public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN') + { + $this->name = $name; + $this->version = $version; + $this->terminal = new Terminal(); + $this->defaultCommand = 'list'; + if (\defined('SIGINT') && SignalRegistry::isSupported()) { + $this->signalRegistry = new SignalRegistry(); + $this->signalsToDispatchEvent = [\SIGINT, \SIGTERM, \SIGUSR1, \SIGUSR2]; + } + } + + /** + * @final + */ + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + public function setCommandLoader(CommandLoaderInterface $commandLoader) + { + $this->commandLoader = $commandLoader; + } + + public function getSignalRegistry(): SignalRegistry + { + if (!$this->signalRegistry) { + throw new RuntimeException('Signals are not supported. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.'); + } + + return $this->signalRegistry; + } + + public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent) + { + $this->signalsToDispatchEvent = $signalsToDispatchEvent; + } + + /** + * Runs the current application. + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}. + */ + public function run(InputInterface $input = null, OutputInterface $output = null): int + { + if (\function_exists('putenv')) { + @putenv('LINES='.$this->terminal->getHeight()); + @putenv('COLUMNS='.$this->terminal->getWidth()); + } + + if (null === $input) { + $input = new ArgvInput(); + } + + if (null === $output) { + $output = new ConsoleOutput(); + } + + $renderException = function (\Throwable $e) use ($output) { + if ($output instanceof ConsoleOutputInterface) { + $this->renderThrowable($e, $output->getErrorOutput()); + } else { + $this->renderThrowable($e, $output); + } + }; + if ($phpHandler = set_exception_handler($renderException)) { + restore_exception_handler(); + if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) { + $errorHandler = true; + } elseif ($errorHandler = $phpHandler[0]->setExceptionHandler($renderException)) { + $phpHandler[0]->setExceptionHandler($errorHandler); + } + } + + $this->configureIO($input, $output); + + try { + $exitCode = $this->doRun($input, $output); + } catch (\Exception $e) { + if (!$this->catchExceptions) { + throw $e; + } + + $renderException($e); + + $exitCode = $e->getCode(); + if (is_numeric($exitCode)) { + $exitCode = (int) $exitCode; + if ($exitCode <= 0) { + $exitCode = 1; + } + } else { + $exitCode = 1; + } + } finally { + // if the exception handler changed, keep it + // otherwise, unregister $renderException + if (!$phpHandler) { + if (set_exception_handler($renderException) === $renderException) { + restore_exception_handler(); + } + restore_exception_handler(); + } elseif (!$errorHandler) { + $finalHandler = $phpHandler[0]->setExceptionHandler(null); + if ($finalHandler !== $renderException) { + $phpHandler[0]->setExceptionHandler($finalHandler); + } + } + } + + if ($this->autoExit) { + if ($exitCode > 255) { + $exitCode = 255; + } + + exit($exitCode); + } + + return $exitCode; + } + + /** + * Runs the current application. + * + * @return int 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(['--version', '-V'], true)) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + try { + // Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument. + $input->bind($this->getDefinition()); + } catch (ExceptionInterface $e) { + // Errors must be ignored, full binding/validation happens later when the command is known. + } + + $name = $this->getCommandName($input); + if (true === $input->hasParameterOption(['--help', '-h'], true)) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(['command_name' => $this->defaultCommand]); + } else { + $this->wantHelps = true; + } + } + + if (!$name) { + $name = $this->defaultCommand; + $definition = $this->getDefinition(); + $definition->setArguments(array_merge( + $definition->getArguments(), + [ + 'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name), + ] + )); + } + + try { + $this->runningCommand = null; + // the command name MUST be the first element of the input + $command = $this->find($name); + } catch (\Throwable $e) { + if (!($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) || 1 !== \count($alternatives = $e->getAlternatives()) || !$input->isInteractive()) { + if (null !== $this->dispatcher) { + $event = new ConsoleErrorEvent($input, $output, $e); + $this->dispatcher->dispatch($event, ConsoleEvents::ERROR); + + if (0 === $event->getExitCode()) { + return 0; + } + + $e = $event->getError(); + } + + throw $e; + } + + $alternative = $alternatives[0]; + + $style = new SymfonyStyle($input, $output); + $output->writeln(''); + $formattedBlock = (new FormatterHelper())->formatBlock(sprintf('Command "%s" is not defined.', $name), 'error', true); + $output->writeln($formattedBlock); + if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) { + if (null !== $this->dispatcher) { + $event = new ConsoleErrorEvent($input, $output, $e); + $this->dispatcher->dispatch($event, ConsoleEvents::ERROR); + + return $event->getExitCode(); + } + + return 1; + } + + $command = $this->find($alternative); + } + + if ($command instanceof LazyCommand) { + $command = $command->getCommand(); + } + + $this->runningCommand = $command; + $exitCode = $this->doRunCommand($command, $input, $output); + $this->runningCommand = null; + + return $exitCode; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + } + + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Get the helper set associated with the command. + */ + public function getHelperSet(): HelperSet + { + return $this->helperSet ??= $this->getDefaultHelperSet(); + } + + public function setDefinition(InputDefinition $definition) + { + $this->definition = $definition; + } + + /** + * Gets the InputDefinition related to this Application. + */ + public function getDefinition(): InputDefinition + { + $this->definition ??= $this->getDefaultInputDefinition(); + + if ($this->singleCommand) { + $inputDefinition = $this->definition; + $inputDefinition->setArguments(); + + return $inputDefinition; + } + + return $this->definition; + } + + /** + * Adds suggestions to $suggestions for the current completion input (e.g. option or argument). + */ + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ( + CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType() + && 'command' === $input->getCompletionName() + ) { + $commandNames = []; + foreach ($this->all() as $name => $command) { + // skip hidden commands and aliased commands as they already get added below + if ($command->isHidden() || $command->getName() !== $name) { + continue; + } + $commandNames[] = $command->getName(); + foreach ($command->getAliases() as $name) { + $commandNames[] = $name; + } + } + $suggestions->suggestValues(array_filter($commandNames)); + + return; + } + + if (CompletionInput::TYPE_OPTION_NAME === $input->getCompletionType()) { + $suggestions->suggestOptions($this->getDefinition()->getOptions()); + + return; + } + } + + /** + * Gets the help message. + */ + public function getHelp(): string + { + return $this->getLongVersion(); + } + + /** + * Gets whether to catch exceptions or not during commands execution. + */ + public function areExceptionsCaught(): bool + { + return $this->catchExceptions; + } + + /** + * Sets whether to catch exceptions or not during commands execution. + */ + public function setCatchExceptions(bool $boolean) + { + $this->catchExceptions = $boolean; + } + + /** + * Gets whether to automatically exit after a command execution or not. + */ + public function isAutoExitEnabled(): bool + { + return $this->autoExit; + } + + /** + * Sets whether to automatically exit after a command execution or not. + */ + public function setAutoExit(bool $boolean) + { + $this->autoExit = $boolean; + } + + /** + * Gets the name of the application. + */ + public function getName(): string + { + return $this->name; + } + + /** + * Sets the application name. + **/ + public function setName(string $name) + { + $this->name = $name; + } + + /** + * Gets the application version. + */ + public function getVersion(): string + { + return $this->version; + } + + /** + * Sets the application version. + */ + public function setVersion(string $version) + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + * + * @return string + */ + public function getLongVersion() + { + if ('UNKNOWN' !== $this->getName()) { + if ('UNKNOWN' !== $this->getVersion()) { + return sprintf('%s %s', $this->getName(), $this->getVersion()); + } + + return $this->getName(); + } + + return 'Console Tool'; + } + + /** + * Registers a new command. + */ + public function register(string $name): Command + { + return $this->add(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * If a Command is not enabled it will not be added. + * + * @param Command[] $commands An array of commands + */ + public function addCommands(array $commands) + { + foreach ($commands as $command) { + $this->add($command); + } + } + + /** + * Adds a command object. + * + * If a command with the same name already exists, it will be overridden. + * If the command is not enabled it will not be added. + * + * @return Command|null + */ + public function add(Command $command) + { + $this->init(); + + $command->setApplication($this); + + if (!$command->isEnabled()) { + $command->setApplication(null); + + return null; + } + + if (!$command instanceof LazyCommand) { + // Will throw if the command is not correctly initialized. + $command->getDefinition(); + } + + if (!$command->getName()) { + throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_debug_type($command))); + } + + $this->commands[$command->getName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->commands[$alias] = $command; + } + + return $command; + } + + /** + * Returns a registered command by name or alias. + * + * @return Command + * + * @throws CommandNotFoundException When given command name does not exist + */ + public function get(string $name) + { + $this->init(); + + if (!$this->has($name)) { + throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); + } + + // When the command has a different name than the one used at the command loader level + if (!isset($this->commands[$name])) { + throw new CommandNotFoundException(sprintf('The "%s" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".', $name)); + } + + $command = $this->commands[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + $helpCommand = $this->get('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * Returns true if the command exists, false otherwise. + */ + public function has(string $name): bool + { + $this->init(); + + return isset($this->commands[$name]) || ($this->commandLoader && $this->commandLoader->has($name) && $this->add($this->commandLoader->get($name))); + } + + /** + * Returns an array of all unique namespaces used by currently registered commands. + * + * It does not return the global namespace which always exists. + * + * @return string[] + */ + public function getNamespaces(): array + { + $namespaces = []; + foreach ($this->all() as $command) { + if ($command->isHidden()) { + continue; + } + + $namespaces[] = $this->extractAllNamespaces($command->getName()); + + foreach ($command->getAliases() as $alias) { + $namespaces[] = $this->extractAllNamespaces($alias); + } + } + + return array_values(array_unique(array_filter(array_merge([], ...$namespaces)))); + } + + /** + * Finds a registered namespace by a name or an abbreviation. + * + * @throws NamespaceNotFoundException When namespace is incorrect or ambiguous + */ + public function findNamespace(string $namespace): string + { + $allNamespaces = $this->getNamespaces(); + $expr = implode('[^:]*:', array_map('preg_quote', explode(':', $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 NamespaceNotFoundException($message, $alternatives); + } + + $exact = \in_array($namespace, $namespaces, true); + if (\count($namespaces) > 1 && !$exact) { + throw new NamespaceNotFoundException(sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); + } + + return $exact ? $namespace : reset($namespaces); + } + + /** + * Finds a command by name or alias. + * + * Contrary to get, this command tries to find the best + * match if you give it an abbreviation of a name or alias. + * + * @return Command + * + * @throws CommandNotFoundException When command name is incorrect or ambiguous + */ + public function find(string $name) + { + $this->init(); + + $aliases = []; + + foreach ($this->commands as $command) { + foreach ($command->getAliases() as $alias) { + if (!$this->has($alias)) { + $this->commands[$alias] = $command; + } + } + } + + if ($this->has($name)) { + return $this->get($name); + } + + $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands); + $expr = implode('[^:]*:', array_map('preg_quote', explode(':', $name))).'[^:]*'; + $commands = preg_grep('{^'.$expr.'}', $allCommands); + + if (empty($commands)) { + $commands = preg_grep('{^'.$expr.'}i', $allCommands); + } + + // if no commands matched or we just matched namespaces + if (empty($commands) || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) { + if (false !== $pos = strrpos($name, ':')) { + // check if a namespace exists and contains commands + $this->findNamespace(substr($name, 0, $pos)); + } + + $message = sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternatives($name, $allCommands)) { + // remove hidden commands + $alternatives = array_filter($alternatives, function ($name) { + return !$this->get($name)->isHidden(); + }); + + 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 CommandNotFoundException($message, array_values($alternatives)); + } + + // filter out aliases for commands which are already on the list + if (\count($commands) > 1) { + $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands; + $commands = array_unique(array_filter($commands, function ($nameOrAlias) use (&$commandList, $commands, &$aliases) { + if (!$commandList[$nameOrAlias] instanceof Command) { + $commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias); + } + + $commandName = $commandList[$nameOrAlias]->getName(); + + $aliases[$nameOrAlias] = $commandName; + + return $commandName === $nameOrAlias || !\in_array($commandName, $commands); + })); + } + + if (\count($commands) > 1) { + $usableWidth = $this->terminal->getWidth() - 10; + $abbrevs = array_values($commands); + $maxLen = 0; + foreach ($abbrevs as $abbrev) { + $maxLen = max(Helper::width($abbrev), $maxLen); + } + $abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen, &$commands) { + if ($commandList[$cmd]->isHidden()) { + unset($commands[array_search($cmd, $commands)]); + + return false; + } + + $abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription(); + + return Helper::width($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev; + }, array_values($commands)); + + if (\count($commands) > 1) { + $suggestions = $this->getAbbreviationSuggestions(array_filter($abbrevs)); + + throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $name, $suggestions), array_values($commands)); + } + } + + $command = $this->get(reset($commands)); + + if ($command->isHidden()) { + throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); + } + + return $command; + } + + /** + * Gets the commands (registered in the given namespace if provided). + * + * The array keys are the full names and the values the command instances. + * + * @return Command[] + */ + public function all(string $namespace = null) + { + $this->init(); + + if (null === $namespace) { + if (!$this->commandLoader) { + return $this->commands; + } + + $commands = $this->commands; + foreach ($this->commandLoader->getNames() as $name) { + if (!isset($commands[$name]) && $this->has($name)) { + $commands[$name] = $this->get($name); + } + } + + return $commands; + } + + $commands = []; + foreach ($this->commands as $name => $command) { + if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { + $commands[$name] = $command; + } + } + + if ($this->commandLoader) { + foreach ($this->commandLoader->getNames() as $name) { + if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1) && $this->has($name)) { + $commands[$name] = $this->get($name); + } + } + } + + return $commands; + } + + /** + * Returns an array of possible abbreviations given a set of names. + * + * @return string[][] + */ + public static function getAbbreviations(array $names): array + { + $abbrevs = []; + foreach ($names as $name) { + for ($len = \strlen($name); $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + $abbrevs[$abbrev][] = $name; + } + } + + return $abbrevs; + } + + public function renderThrowable(\Throwable $e, OutputInterface $output): void + { + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + + $this->doRenderThrowable($e, $output); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', OutputFormatter::escape(sprintf($this->runningCommand->getSynopsis(), $this->getName()))), OutputInterface::VERBOSITY_QUIET); + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } + + protected function doRenderThrowable(\Throwable $e, OutputInterface $output): void + { + do { + $message = trim($e->getMessage()); + if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $class = get_debug_type($e); + $title = sprintf(' [%s%s] ', $class, 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : ''); + $len = Helper::width($title); + } else { + $len = 0; + } + + if (str_contains($message, "@anonymous\0")) { + $message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) { + return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0]; + }, $message); + } + + $width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : \PHP_INT_MAX; + $lines = []; + foreach ('' !== $message ? preg_split('/\r?\n/', $message) : [] as $line) { + foreach ($this->splitStringByWidth($line, $width - 4) as $line) { + // pre-format lines to get the right string length + $lineLength = Helper::width($line) + 4; + $lines[] = [$line, $lineLength]; + + $len = max($lineLength, $len); + } + } + + $messages = []; + if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $messages[] = sprintf('%s', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a'))); + } + $messages[] = $emptyLine = sprintf('%s', str_repeat(' ', $len)); + if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $messages[] = sprintf('%s%s', $title, str_repeat(' ', max(0, $len - Helper::width($title)))); + } + foreach ($lines as $line) { + $messages[] = sprintf(' %s %s', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1])); + } + $messages[] = $emptyLine; + $messages[] = ''; + + $output->writeln($messages, OutputInterface::VERBOSITY_QUIET); + + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $output->writeln('Exception trace:', OutputInterface::VERBOSITY_QUIET); + + // exception related properties + $trace = $e->getTrace(); + + array_unshift($trace, [ + 'function' => '', + 'file' => $e->getFile() ?: 'n/a', + 'line' => $e->getLine() ?: 'n/a', + 'args' => [], + ]); + + for ($i = 0, $count = \count($trace); $i < $count; ++$i) { + $class = $trace[$i]['class'] ?? ''; + $type = $trace[$i]['type'] ?? ''; + $function = $trace[$i]['function'] ?? ''; + $file = $trace[$i]['file'] ?? 'n/a'; + $line = $trace[$i]['line'] ?? 'n/a'; + + $output->writeln(sprintf(' %s%s at %s:%s', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET); + } + + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } while ($e = $e->getPrevious()); + } + + /** + * Configures the input and output instances based on the user arguments and options. + */ + protected function configureIO(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(['--ansi'], true)) { + $output->setDecorated(true); + } elseif (true === $input->hasParameterOption(['--no-ansi'], true)) { + $output->setDecorated(false); + } + + if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) { + $input->setInteractive(false); + } + + switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) { + case -1: + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + break; + case 1: + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + break; + case 2: + $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); + break; + case 3: + $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); + break; + default: + $shellVerbosity = 0; + break; + } + + if (true === $input->hasParameterOption(['--quiet', '-q'], true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + $shellVerbosity = -1; + } else { + if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); + $shellVerbosity = 3; + } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); + $shellVerbosity = 2; + } elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + $shellVerbosity = 1; + } + } + + if (-1 === $shellVerbosity) { + $input->setInteractive(false); + } + + if (\function_exists('putenv')) { + @putenv('SHELL_VERBOSITY='.$shellVerbosity); + } + $_ENV['SHELL_VERBOSITY'] = $shellVerbosity; + $_SERVER['SHELL_VERBOSITY'] = $shellVerbosity; + } + + /** + * Runs the current command. + * + * If an event dispatcher has been attached to the application, + * events are also dispatched during the life-cycle of the command. + * + * @return int 0 if everything went fine, or an error code + */ + protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) + { + foreach ($command->getHelperSet() as $helper) { + if ($helper instanceof InputAwareInterface) { + $helper->setInput($input); + } + } + + if ($this->signalsToDispatchEvent) { + $commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : []; + + if ($commandSignals || null !== $this->dispatcher) { + if (!$this->signalRegistry) { + throw new RuntimeException('Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.'); + } + + if (Terminal::hasSttyAvailable()) { + $sttyMode = shell_exec('stty -g'); + + foreach ([\SIGINT, \SIGTERM] as $signal) { + $this->signalRegistry->register($signal, static function () use ($sttyMode) { + shell_exec('stty '.$sttyMode); + }); + } + } + } + + if (null !== $this->dispatcher) { + foreach ($this->signalsToDispatchEvent as $signal) { + $event = new ConsoleSignalEvent($command, $input, $output, $signal); + + $this->signalRegistry->register($signal, function ($signal, $hasNext) use ($event) { + $this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL); + + // No more handlers, we try to simulate PHP default behavior + if (!$hasNext) { + if (!\in_array($signal, [\SIGUSR1, \SIGUSR2], true)) { + exit(0); + } + } + }); + } + } + + foreach ($commandSignals as $signal) { + $this->signalRegistry->register($signal, [$command, 'handleSignal']); + } + } + + if (null === $this->dispatcher) { + return $command->run($input, $output); + } + + // bind before the console.command event, so the listeners have access to input options/arguments + try { + $command->mergeApplicationDefinition(); + $input->bind($command->getDefinition()); + } catch (ExceptionInterface $e) { + // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition + } + + $event = new ConsoleCommandEvent($command, $input, $output); + $e = null; + + try { + $this->dispatcher->dispatch($event, ConsoleEvents::COMMAND); + + if ($event->commandShouldRun()) { + $exitCode = $command->run($input, $output); + } else { + $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; + } + } catch (\Throwable $e) { + $event = new ConsoleErrorEvent($input, $output, $e, $command); + $this->dispatcher->dispatch($event, ConsoleEvents::ERROR); + $e = $event->getError(); + + if (0 === $exitCode = $event->getExitCode()) { + $e = null; + } + } + + $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); + $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE); + + if (null !== $e) { + throw $e; + } + + return $event->getExitCode(); + } + + /** + * Gets the name of the command based on input. + */ + protected function getCommandName(InputInterface $input): ?string + { + return $this->singleCommand ? $this->defaultCommand : $input->getFirstArgument(); + } + + /** + * Gets the default input definition. + */ + protected function getDefaultInputDefinition(): InputDefinition + { + return new InputDefinition([ + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display help for the given command. When no command is given display help for the '.$this->defaultCommand.' command'), + 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('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'), + new InputOption('--ansi', '', InputOption::VALUE_NEGATABLE, 'Force (or disable --no-ansi) ANSI output', null), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), + ]); + } + + /** + * Gets the default commands that should always be available. + * + * @return Command[] + */ + protected function getDefaultCommands(): array + { + return [new HelpCommand(), new ListCommand(), new CompleteCommand(), new DumpCompletionCommand()]; + } + + /** + * Gets the default helper set with the helpers that should always be available. + */ + protected function getDefaultHelperSet(): HelperSet + { + return new HelperSet([ + new FormatterHelper(), + new DebugFormatterHelper(), + new ProcessHelper(), + new QuestionHelper(), + ]); + } + + /** + * Returns abbreviated suggestions in string format. + */ + private function getAbbreviationSuggestions(array $abbrevs): string + { + return ' '.implode("\n ", $abbrevs); + } + + /** + * Returns the namespace part of the command name. + * + * This method is not part of public API and should not be used directly. + */ + public function extractNamespace(string $name, int $limit = null): string + { + $parts = explode(':', $name, -1); + + return implode(':', null === $limit ? $parts : \array_slice($parts, 0, $limit)); + } + + /** + * Finds alternative of $name among $collection, + * if nothing is found in $collection, try in $abbrevs. + * + * @return string[] + */ + private function findAlternatives(string $name, iterable $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 && str_contains($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 || str_contains($item, $name)) { + $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; + } + } + + $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); + ksort($alternatives, \SORT_NATURAL | \SORT_FLAG_CASE); + + return array_keys($alternatives); + } + + /** + * Sets the default Command name. + * + * @return $this + */ + public function setDefaultCommand(string $commandName, bool $isSingleCommand = false): static + { + $this->defaultCommand = explode('|', ltrim($commandName, '|'))[0]; + + if ($isSingleCommand) { + // Ensure the command exist + $this->find($commandName); + + $this->singleCommand = true; + } + + return $this; + } + + /** + * @internal + */ + public function isSingleCommand(): bool + { + return $this->singleCommand; + } + + private function splitStringByWidth(string $string, int $width): array + { + // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly. + // additionally, array_slice() is not enough as some character has doubled width. + // we need a function to split string not by character count but by string width + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return str_split($string, $width); + } + + $utf8String = mb_convert_encoding($string, 'utf8', $encoding); + $lines = []; + $line = ''; + + $offset = 0; + while (preg_match('/.{1,10000}/u', $utf8String, $m, 0, $offset)) { + $offset += \strlen($m[0]); + + foreach (preg_split('//u', $m[0]) as $char) { + // test if $char could be appended to current line + if (mb_strwidth($line.$char, 'utf8') <= $width) { + $line .= $char; + continue; + } + // if not, push current line to array and make new line + $lines[] = str_pad($line, $width); + $line = $char; + } + } + + $lines[] = \count($lines) ? str_pad($line, $width) : $line; + + mb_convert_variables($encoding, 'utf8', $lines); + + return $lines; + } + + /** + * Returns all namespaces of the command name. + * + * @return string[] + */ + private function extractAllNamespaces(string $name): array + { + // -1 as third argument is needed to skip the command short name when exploding + $parts = explode(':', $name, -1); + $namespaces = []; + + foreach ($parts as $part) { + if (\count($namespaces)) { + $namespaces[] = end($namespaces).':'.$part; + } else { + $namespaces[] = $part; + } + } + + return $namespaces; + } + + private function init() + { + if ($this->initialized) { + return; + } + $this->initialized = true; + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } +} diff --git a/vendor/symfony/console/Attribute/AsCommand.php b/vendor/symfony/console/Attribute/AsCommand.php new file mode 100644 index 0000000..b337f54 --- /dev/null +++ b/vendor/symfony/console/Attribute/AsCommand.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Attribute; + +/** + * Service tag to autoconfigure commands. + */ +#[\Attribute(\Attribute::TARGET_CLASS)] +class AsCommand +{ + public function __construct( + public string $name, + public ?string $description = null, + array $aliases = [], + bool $hidden = false, + ) { + if (!$hidden && !$aliases) { + return; + } + + $name = explode('|', $name); + $name = array_merge($name, $aliases); + + if ($hidden && '' !== $name[0]) { + array_unshift($name, ''); + } + + $this->name = implode('|', $name); + } +} diff --git a/vendor/symfony/console/CHANGELOG.md b/vendor/symfony/console/CHANGELOG.md new file mode 100644 index 0000000..9a93c79 --- /dev/null +++ b/vendor/symfony/console/CHANGELOG.md @@ -0,0 +1,226 @@ +CHANGELOG +========= + +6.0 +--- + + * `Command::setHidden()` has a default value (`true`) for `$hidden` parameter and is final + * Remove `Helper::strlen()`, use `Helper::width()` instead + * Remove `Helper::strlenWithoutDecoration()`, use `Helper::removeDecoration()` instead + * `AddConsoleCommandPass` can not be configured anymore + * Remove `HelperSet::setCommand()` and `getCommand()` without replacement + +5.4 +--- + + * Add `TesterTrait::assertCommandIsSuccessful()` to test command + * Deprecate `HelperSet::setCommand()` and `getCommand()` without replacement + +5.3 +--- + + * Add `GithubActionReporter` to render annotations in a Github Action + * Add `InputOption::VALUE_NEGATABLE` flag to handle `--foo`/`--no-foo` options + * Add the `Command::$defaultDescription` static property and the `description` attribute + on the `console.command` tag to allow the `list` command to instantiate commands lazily + * Add option `--short` to the `list` command + * Add support for bright colors + * Add `#[AsCommand]` attribute for declaring commands on PHP 8 + * Add `Helper::width()` and `Helper::length()` + * The `--ansi` and `--no-ansi` options now default to `null`. + +5.2.0 +----- + + * Added `SingleCommandApplication::setAutoExit()` to allow testing via `CommandTester` + * added support for multiline responses to questions through `Question::setMultiline()` + and `Question::isMultiline()` + * Added `SignalRegistry` class to stack signals handlers + * Added support for signals: + * Added `Application::getSignalRegistry()` and `Application::setSignalsToDispatchEvent()` methods + * Added `SignalableCommandInterface` interface + * Added `TableCellStyle` class to customize table cell + * Removed `php ` prefix invocation from help messages. + +5.1.0 +----- + + * `Command::setHidden()` is final since Symfony 5.1 + * Add `SingleCommandApplication` + * Add `Cursor` class + +5.0.0 +----- + + * removed support for finding hidden commands using an abbreviation, use the full name instead + * removed `TableStyle::setCrossingChar()` method in favor of `TableStyle::setDefaultCrossingChar()` + * removed `TableStyle::setHorizontalBorderChar()` method in favor of `TableStyle::setDefaultCrossingChars()` + * removed `TableStyle::getHorizontalBorderChar()` method in favor of `TableStyle::getBorderChars()` + * removed `TableStyle::setVerticalBorderChar()` method in favor of `TableStyle::setVerticalBorderChars()` + * removed `TableStyle::getVerticalBorderChar()` method in favor of `TableStyle::getBorderChars()` + * removed support for returning `null` from `Command::execute()`, return `0` instead + * `ProcessHelper::run()` accepts only `array|Symfony\Component\Process\Process` for its `command` argument + * `Application::setDispatcher` accepts only `Symfony\Contracts\EventDispatcher\EventDispatcherInterface` + for its `dispatcher` argument + * renamed `Application::renderException()` and `Application::doRenderException()` + to `renderThrowable()` and `doRenderThrowable()` respectively. + +4.4.0 +----- + + * deprecated finding hidden commands using an abbreviation, use the full name instead + * added `Question::setTrimmable` default to true to allow the answer to be trimmed + * added method `minSecondsBetweenRedraws()` and `maxSecondsBetweenRedraws()` on `ProgressBar` + * `Application` implements `ResetInterface` + * marked all dispatched event classes as `@final` + * added support for displaying table horizontally + * deprecated returning `null` from `Command::execute()`, return `0` instead + * Deprecated the `Application::renderException()` and `Application::doRenderException()` methods, + use `renderThrowable()` and `doRenderThrowable()` instead. + * added support for the `NO_COLOR` env var (https://no-color.org/) + +4.3.0 +----- + + * added support for hyperlinks + * added `ProgressBar::iterate()` method that simplify updating the progress bar when iterating + * added `Question::setAutocompleterCallback()` to provide a callback function + that dynamically generates suggestions as the user types + +4.2.0 +----- + + * allowed passing commands as `[$process, 'ENV_VAR' => 'value']` to + `ProcessHelper::run()` to pass environment variables + * deprecated passing a command as a string to `ProcessHelper::run()`, + pass it the command as an array of its arguments instead + * made the `ProcessHelper` class final + * added `WrappableOutputFormatterInterface::formatAndWrap()` (implemented in `OutputFormatter`) + * added `capture_stderr_separately` option to `CommandTester::execute()` + +4.1.0 +----- + + * added option to run suggested command if command is not found and only 1 alternative is available + * added option to modify console output and print multiple modifiable sections + * added support for iterable messages in output `write` and `writeln` methods + +4.0.0 +----- + + * `OutputFormatter` throws an exception when unknown options are used + * removed `QuestionHelper::setInputStream()/getInputStream()` + * removed `Application::getTerminalWidth()/getTerminalHeight()` and + `Application::setTerminalDimensions()/getTerminalDimensions()` + * removed `ConsoleExceptionEvent` + * removed `ConsoleEvents::EXCEPTION` + +3.4.0 +----- + + * added `SHELL_VERBOSITY` env var to control verbosity + * added `CommandLoaderInterface`, `FactoryCommandLoader` and PSR-11 + `ContainerCommandLoader` for commands lazy-loading + * added a case-insensitive command name matching fallback + * added static `Command::$defaultName/getDefaultName()`, allowing for + commands to be registered at compile time in the application command loader. + Setting the `$defaultName` property avoids the need for filling the `command` + attribute on the `console.command` tag when using `AddConsoleCommandPass`. + +3.3.0 +----- + + * added `ExceptionListener` + * added `AddConsoleCommandPass` (originally in FrameworkBundle) + * [BC BREAK] `Input::getOption()` no longer returns the default value for options + with value optional explicitly passed empty + * added console.error event to catch exceptions thrown by other listeners + * deprecated console.exception event in favor of console.error + * added ability to handle `CommandNotFoundException` through the + `console.error` event + * deprecated default validation in `SymfonyQuestionHelper::ask` + +3.2.0 +------ + + * added `setInputs()` method to CommandTester for ease testing of commands expecting inputs + * added `setStream()` and `getStream()` methods to Input (implement StreamableInputInterface) + * added StreamableInputInterface + * added LockableTrait + +3.1.0 +----- + + * added truncate method to FormatterHelper + * added setColumnWidth(s) method to Table + +2.8.3 +----- + + * remove readline support from the question helper as it caused issues + +2.8.0 +----- + + * use readline for user input in the question helper when available to allow + the use of arrow keys + +2.6.0 +----- + + * added a Process helper + * added a DebugFormatter helper + +2.5.0 +----- + + * deprecated the dialog helper (use the question helper instead) + * deprecated TableHelper in favor of Table + * deprecated ProgressHelper in favor of ProgressBar + * added ConsoleLogger + * added a question helper + * added a way to set the process name of a command + * added a way to set a default command instead of `ListCommand` + +2.4.0 +----- + + * added a way to force terminal dimensions + * added a convenient method to detect verbosity level + * [BC BREAK] made descriptors use output instead of returning a string + +2.3.0 +----- + + * added multiselect support to the select dialog helper + * added Table Helper for tabular data rendering + * added support for events in `Application` + * added a way to normalize EOLs in `ApplicationTester::getDisplay()` and `CommandTester::getDisplay()` + * added a way to set the progress bar progress via the `setCurrent` method + * added support for multiple InputOption shortcuts, written as `'-a|-b|-c'` + * added two additional verbosity levels, VERBOSITY_VERY_VERBOSE and VERBOSITY_DEBUG + +2.2.0 +----- + + * added support for colorization on Windows via ConEmu + * add a method to Dialog Helper to ask for a question and hide the response + * added support for interactive selections in console (DialogHelper::select()) + * added support for autocompletion as you type in Dialog Helper + +2.1.0 +----- + + * added ConsoleOutputInterface + * added the possibility to disable a command (Command::isEnabled()) + * added suggestions when a command does not exist + * added a --raw option to the list command + * added support for STDERR in the console output class (errors are now sent + to STDERR) + * made the defaults (helper set, commands, input definition) in Application + more easily customizable + * added support for the shell even if readline is not available + * added support for process isolation in Symfony shell via + `--process-isolation` switch + * added support for `--`, which disables options parsing after that point + (tokens will be parsed as arguments) diff --git a/vendor/symfony/console/CI/GithubActionReporter.php b/vendor/symfony/console/CI/GithubActionReporter.php new file mode 100644 index 0000000..a15c1ff --- /dev/null +++ b/vendor/symfony/console/CI/GithubActionReporter.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\CI; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Utility class for Github actions. + * + * @author Maxime Steinhausser + */ +class GithubActionReporter +{ + private $output; + + /** + * @see https://github.com/actions/toolkit/blob/5e5e1b7aacba68a53836a34db4a288c3c1c1585b/packages/core/src/command.ts#L80-L85 + */ + private const ESCAPED_DATA = [ + '%' => '%25', + "\r" => '%0D', + "\n" => '%0A', + ]; + + /** + * @see https://github.com/actions/toolkit/blob/5e5e1b7aacba68a53836a34db4a288c3c1c1585b/packages/core/src/command.ts#L87-L94 + */ + private const ESCAPED_PROPERTIES = [ + '%' => '%25', + "\r" => '%0D', + "\n" => '%0A', + ':' => '%3A', + ',' => '%2C', + ]; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + } + + public static function isGithubActionEnvironment(): bool + { + return false !== getenv('GITHUB_ACTIONS'); + } + + /** + * Output an error using the Github annotations format. + * + * @see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-error-message + */ + public function error(string $message, string $file = null, int $line = null, int $col = null): void + { + $this->log('error', $message, $file, $line, $col); + } + + /** + * Output a warning using the Github annotations format. + * + * @see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message + */ + public function warning(string $message, string $file = null, int $line = null, int $col = null): void + { + $this->log('warning', $message, $file, $line, $col); + } + + /** + * Output a debug log using the Github annotations format. + * + * @see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-a-debug-message + */ + public function debug(string $message, string $file = null, int $line = null, int $col = null): void + { + $this->log('debug', $message, $file, $line, $col); + } + + private function log(string $type, string $message, string $file = null, int $line = null, int $col = null): void + { + // Some values must be encoded. + $message = strtr($message, self::ESCAPED_DATA); + + if (!$file) { + // No file provided, output the message solely: + $this->output->writeln(sprintf('::%s::%s', $type, $message)); + + return; + } + + $this->output->writeln(sprintf('::%s file=%s,line=%s,col=%s::%s', $type, strtr($file, self::ESCAPED_PROPERTIES), strtr($line ?? 1, self::ESCAPED_PROPERTIES), strtr($col ?? 0, self::ESCAPED_PROPERTIES), $message)); + } +} diff --git a/vendor/symfony/console/Color.php b/vendor/symfony/console/Color.php new file mode 100644 index 0000000..7fcc507 --- /dev/null +++ b/vendor/symfony/console/Color.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Fabien Potencier + */ +final class Color +{ + private const COLORS = [ + 'black' => 0, + 'red' => 1, + 'green' => 2, + 'yellow' => 3, + 'blue' => 4, + 'magenta' => 5, + 'cyan' => 6, + 'white' => 7, + 'default' => 9, + ]; + + private const BRIGHT_COLORS = [ + 'gray' => 0, + 'bright-red' => 1, + 'bright-green' => 2, + 'bright-yellow' => 3, + 'bright-blue' => 4, + 'bright-magenta' => 5, + 'bright-cyan' => 6, + 'bright-white' => 7, + ]; + + private const AVAILABLE_OPTIONS = [ + '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 string $foreground; + private string $background; + private array $options = []; + + public function __construct(string $foreground = '', string $background = '', array $options = []) + { + $this->foreground = $this->parseColor($foreground); + $this->background = $this->parseColor($background, true); + + foreach ($options as $option) { + if (!isset(self::AVAILABLE_OPTIONS[$option])) { + throw new InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s).', $option, implode(', ', array_keys(self::AVAILABLE_OPTIONS)))); + } + + $this->options[$option] = self::AVAILABLE_OPTIONS[$option]; + } + } + + public function apply(string $text): string + { + return $this->set().$text.$this->unset(); + } + + public function set(): string + { + $setCodes = []; + if ('' !== $this->foreground) { + $setCodes[] = $this->foreground; + } + if ('' !== $this->background) { + $setCodes[] = $this->background; + } + foreach ($this->options as $option) { + $setCodes[] = $option['set']; + } + if (0 === \count($setCodes)) { + return ''; + } + + return sprintf("\033[%sm", implode(';', $setCodes)); + } + + public function unset(): string + { + $unsetCodes = []; + if ('' !== $this->foreground) { + $unsetCodes[] = 39; + } + if ('' !== $this->background) { + $unsetCodes[] = 49; + } + foreach ($this->options as $option) { + $unsetCodes[] = $option['unset']; + } + if (0 === \count($unsetCodes)) { + return ''; + } + + return sprintf("\033[%sm", implode(';', $unsetCodes)); + } + + private function parseColor(string $color, bool $background = false): string + { + if ('' === $color) { + return ''; + } + + if ('#' === $color[0]) { + $color = substr($color, 1); + + if (3 === \strlen($color)) { + $color = $color[0].$color[0].$color[1].$color[1].$color[2].$color[2]; + } + + if (6 !== \strlen($color)) { + throw new InvalidArgumentException(sprintf('Invalid "%s" color.', $color)); + } + + return ($background ? '4' : '3').$this->convertHexColorToAnsi(hexdec($color)); + } + + if (isset(self::COLORS[$color])) { + return ($background ? '4' : '3').self::COLORS[$color]; + } + + if (isset(self::BRIGHT_COLORS[$color])) { + return ($background ? '10' : '9').self::BRIGHT_COLORS[$color]; + } + + throw new InvalidArgumentException(sprintf('Invalid "%s" color; expected one of (%s).', $color, implode(', ', array_merge(array_keys(self::COLORS), array_keys(self::BRIGHT_COLORS))))); + } + + private function convertHexColorToAnsi(int $color): string + { + $r = ($color >> 16) & 255; + $g = ($color >> 8) & 255; + $b = $color & 255; + + // see https://github.com/termstandard/colors/ for more information about true color support + if ('truecolor' !== getenv('COLORTERM')) { + return (string) $this->degradeHexColorToAnsi($r, $g, $b); + } + + return sprintf('8;2;%d;%d;%d', $r, $g, $b); + } + + private function degradeHexColorToAnsi(int $r, int $g, int $b): int + { + if (0 === round($this->getSaturation($r, $g, $b) / 50)) { + return 0; + } + + return (round($b / 255) << 2) | (round($g / 255) << 1) | round($r / 255); + } + + private function getSaturation(int $r, int $g, int $b): int + { + $r = $r / 255; + $g = $g / 255; + $b = $b / 255; + $v = max($r, $g, $b); + + if (0 === $diff = $v - min($r, $g, $b)) { + return 0; + } + + return (int) $diff * 100 / $v; + } +} diff --git a/vendor/symfony/console/Command/Command.php b/vendor/symfony/console/Command/Command.php new file mode 100644 index 0000000..986d4bd --- /dev/null +++ b/vendor/symfony/console/Command/Command.php @@ -0,0 +1,676 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Base class for all commands. + * + * @author Fabien Potencier + */ +class Command +{ + // see https://tldp.org/LDP/abs/html/exitcodes.html + public const SUCCESS = 0; + public const FAILURE = 1; + public const INVALID = 2; + + /** + * @var string|null The default command name + */ + protected static $defaultName; + + /** + * @var string|null The default command description + */ + protected static $defaultDescription; + + private $application = null; + private ?string $name = null; + private ?string $processTitle = null; + private array $aliases = []; + private $definition; + private bool $hidden = false; + private string $help = ''; + private string $description = ''; + private $fullDefinition = null; + private bool $ignoreValidationErrors = false; + private ?\Closure $code = null; + private array $synopsis = []; + private array $usages = []; + private $helperSet = null; + + public static function getDefaultName(): ?string + { + $class = static::class; + + if ($attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) { + return $attribute[0]->newInstance()->name; + } + + $r = new \ReflectionProperty($class, 'defaultName'); + + return $class === $r->class ? static::$defaultName : null; + } + + public static function getDefaultDescription(): ?string + { + $class = static::class; + + if ($attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) { + return $attribute[0]->newInstance()->description; + } + + $r = new \ReflectionProperty($class, 'defaultDescription'); + + return $class === $r->class ? static::$defaultDescription : null; + } + + /** + * @param string|null $name The name of the command; passing null means it must be set in configure() + * + * @throws LogicException When the command name is empty + */ + public function __construct(string $name = null) + { + $this->definition = new InputDefinition(); + + if (null === $name && null !== $name = static::getDefaultName()) { + $aliases = explode('|', $name); + + if ('' === $name = array_shift($aliases)) { + $this->setHidden(true); + $name = array_shift($aliases); + } + + $this->setAliases($aliases); + } + + if (null !== $name) { + $this->setName($name); + } + + if ('' === $this->description) { + $this->setDescription(static::getDefaultDescription() ?? ''); + } + + $this->configure(); + } + + /** + * Ignores validation errors. + * + * This is mainly useful for the help command. + */ + public function ignoreValidationErrors() + { + $this->ignoreValidationErrors = true; + } + + public function setApplication(Application $application = null) + { + $this->application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + + $this->fullDefinition = null; + } + + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set. + */ + public function getHelperSet(): ?HelperSet + { + return $this->helperSet; + } + + /** + * Gets the application instance for this command. + */ + public function getApplication(): ?Application + { + return $this->application; + } + + /** + * Checks whether the command is enabled or not in the current environment. + * + * Override this to check for x or y and return false if the command cannot + * run properly under the current conditions. + * + * @return bool + */ + public function isEnabled() + { + return true; + } + + /** + * Configures the current command. + */ + protected function configure() + { + } + + /** + * Executes the current command. + * + * This method is not abstract because you can use this class + * as a concrete class. In this case, instead of defining the + * execute() method, you set the code to execute by passing + * a Closure to the setCode() method. + * + * @return int 0 if everything went fine, or an exit code + * + * @throws LogicException When this abstract method is not implemented + * + * @see setCode() + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + throw new LogicException('You must override the execute() method in the concrete command class.'); + } + + /** + * Interacts with the user. + * + * This method is executed before the InputDefinition is validated. + * This means that this is the only place where the command can + * interactively ask for values of missing required arguments. + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + } + + /** + * Initializes the command after the input has been bound and before the input + * is validated. + * + * This is mainly useful when a lot of commands extends one main command + * where some things need to be initialized based on the input arguments and options. + * + * @see InputInterface::bind() + * @see InputInterface::validate() + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + } + + /** + * Runs the command. + * + * The code to execute is either defined directly with the + * setCode() method or by overriding the execute() method + * in a sub-class. + * + * @return int The command exit code + * + * @throws ExceptionInterface When input binding fails. Bypass this by calling {@link ignoreValidationErrors()}. + * + * @see setCode() + * @see execute() + */ + public function run(InputInterface $input, OutputInterface $output): int + { + // add the application arguments and options + $this->mergeApplicationDefinition(); + + // bind the input against the command specific arguments/options + try { + $input->bind($this->getDefinition()); + } catch (ExceptionInterface $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if (null !== $this->processTitle) { + if (\function_exists('cli_set_process_title')) { + if (!@cli_set_process_title($this->processTitle)) { + if ('Darwin' === \PHP_OS) { + $output->writeln('Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.', OutputInterface::VERBOSITY_VERY_VERBOSE); + } else { + cli_set_process_title($this->processTitle); + } + } + } elseif (\function_exists('setproctitle')) { + setproctitle($this->processTitle); + } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) { + $output->writeln('Install the proctitle PECL to be able to change the process title.'); + } + } + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + // The command name argument is often omitted when a command is executed directly with its run() method. + // It would fail the validation if we didn't make sure the command argument is present, + // since it's required by the application. + if ($input->hasArgument('command') && null === $input->getArgument('command')) { + $input->setArgument('command', $this->getName()); + } + + $input->validate(); + + if ($this->code) { + $statusCode = ($this->code)($input, $output); + } else { + $statusCode = $this->execute($input, $output); + + if (!\is_int($statusCode)) { + throw new \TypeError(sprintf('Return value of "%s::execute()" must be of the type int, "%s" returned.', static::class, get_debug_type($statusCode))); + } + } + + return is_numeric($statusCode) ? (int) $statusCode : 0; + } + + /** + * Adds suggestions to $suggestions for the current completion input (e.g. option or argument). + */ + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + } + + /** + * Sets the code to execute when running this command. + * + * If this method is used, it overrides the code defined + * in the execute() method. + * + * @param callable $code A callable(InputInterface $input, OutputInterface $output) + * + * @return $this + * + * @throws InvalidArgumentException + * + * @see execute() + */ + public function setCode(callable $code): static + { + if ($code instanceof \Closure) { + $r = new \ReflectionFunction($code); + if (null === $r->getClosureThis()) { + set_error_handler(static function () {}); + try { + if ($c = \Closure::bind($code, $this)) { + $code = $c; + } + } finally { + restore_error_handler(); + } + } + } else { + $code = \Closure::fromCallable($code); + } + + $this->code = $code; + + return $this; + } + + /** + * Merges the application definition with the command definition. + * + * This method is not part of public API and should not be used directly. + * + * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments + * + * @internal + */ + public function mergeApplicationDefinition(bool $mergeArgs = true) + { + if (null === $this->application) { + return; + } + + $this->fullDefinition = new InputDefinition(); + $this->fullDefinition->setOptions($this->definition->getOptions()); + $this->fullDefinition->addOptions($this->application->getDefinition()->getOptions()); + + if ($mergeArgs) { + $this->fullDefinition->setArguments($this->application->getDefinition()->getArguments()); + $this->fullDefinition->addArguments($this->definition->getArguments()); + } else { + $this->fullDefinition->setArguments($this->definition->getArguments()); + } + } + + /** + * Sets an array of argument and option instances. + * + * @return $this + */ + public function setDefinition(array|InputDefinition $definition): static + { + if ($definition instanceof InputDefinition) { + $this->definition = $definition; + } else { + $this->definition->setDefinition($definition); + } + + $this->fullDefinition = null; + + return $this; + } + + /** + * Gets the InputDefinition attached to this Command. + */ + public function getDefinition(): InputDefinition + { + return $this->fullDefinition ?? $this->getNativeDefinition(); + } + + /** + * Gets the InputDefinition to be used to create representations of this Command. + * + * Can be overridden to provide the original command representation when it would otherwise + * be changed by merging with the application InputDefinition. + * + * This method is not part of public API and should not be used directly. + */ + public function getNativeDefinition(): InputDefinition + { + return $this->definition ?? throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class)); + } + + /** + * Adds an argument. + * + * @param int|null $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * + * @throws InvalidArgumentException When argument mode is not valid + * + * @return $this + */ + public function addArgument(string $name, int $mode = null, string $description = '', mixed $default = null): static + { + $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + if (null !== $this->fullDefinition) { + $this->fullDefinition->addArgument(new InputArgument($name, $mode, $description, $default)); + } + + return $this; + } + + /** + * Adds an option. + * + * @param $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param $mode The option mode: One of the InputOption::VALUE_* constants + * @param $default The default value (must be null for InputOption::VALUE_NONE) + * + * @throws InvalidArgumentException If option mode is invalid or incompatible + * + * @return $this + */ + public function addOption(string $name, string|array $shortcut = null, int $mode = null, string $description = '', mixed $default = null): static + { + $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + if (null !== $this->fullDefinition) { + $this->fullDefinition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + } + + return $this; + } + + /** + * Sets the name of the command. + * + * This method can set both the namespace and the name if + * you separate them by a colon (:) + * + * $command->setName('foo:bar'); + * + * @return $this + * + * @throws InvalidArgumentException When the name is invalid + */ + public function setName(string $name): static + { + $this->validateName($name); + + $this->name = $name; + + return $this; + } + + /** + * Sets the process title of the command. + * + * This feature should be used only when creating a long process command, + * like a daemon. + * + * @return $this + */ + public function setProcessTitle(string $title): static + { + $this->processTitle = $title; + + return $this; + } + + /** + * Returns the command name. + */ + public function getName(): ?string + { + return $this->name; + } + + /** + * @param bool $hidden Whether or not the command should be hidden from the list of commands + * + * @return $this + */ + public function setHidden(bool $hidden = true): static + { + $this->hidden = $hidden; + + return $this; + } + + /** + * @return bool whether the command should be publicly shown or not + */ + public function isHidden(): bool + { + return $this->hidden; + } + + /** + * Sets the description for the command. + * + * @return $this + */ + public function setDescription(string $description): static + { + $this->description = $description; + + return $this; + } + + /** + * Returns the description for the command. + */ + public function getDescription(): string + { + return $this->description; + } + + /** + * Sets the help for the command. + * + * @return $this + */ + public function setHelp(string $help): static + { + $this->help = $help; + + return $this; + } + + /** + * Returns the help for the command. + */ + public function getHelp(): string + { + return $this->help; + } + + /** + * Returns the processed help for the command replacing the %command.name% and + * %command.full_name% patterns with the real values dynamically. + */ + public function getProcessedHelp(): string + { + $name = $this->name; + $isSingleCommand = $this->application && $this->application->isSingleCommand(); + + $placeholders = [ + '%command.name%', + '%command.full_name%', + ]; + $replacements = [ + $name, + $isSingleCommand ? $_SERVER['PHP_SELF'] : $_SERVER['PHP_SELF'].' '.$name, + ]; + + return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription()); + } + + /** + * Sets the aliases for the command. + * + * @param string[] $aliases An array of aliases for the command + * + * @return $this + * + * @throws InvalidArgumentException When an alias is invalid + */ + public function setAliases(iterable $aliases): static + { + $list = []; + + foreach ($aliases as $alias) { + $this->validateName($alias); + $list[] = $alias; + } + + $this->aliases = \is_array($aliases) ? $aliases : $list; + + return $this; + } + + /** + * Returns the aliases for the command. + */ + public function getAliases(): array + { + return $this->aliases; + } + + /** + * Returns the synopsis for the command. + * + * @param bool $short Whether to show the short version of the synopsis (with options folded) or not + */ + 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]; + } + + /** + * Add a command usage example, it'll be prefixed with the command name. + * + * @return $this + */ + public function addUsage(string $usage): static + { + if (!str_starts_with($usage, $this->name)) { + $usage = sprintf('%s %s', $this->name, $usage); + } + + $this->usages[] = $usage; + + return $this; + } + + /** + * Returns alternative usages of the command. + */ + public function getUsages(): array + { + return $this->usages; + } + + /** + * Gets a helper instance by name. + * + * @throws LogicException if no HelperSet is defined + * @throws InvalidArgumentException if the helper is not defined + */ + public function getHelper(string $name): mixed + { + if (null === $this->helperSet) { + throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name)); + } + + return $this->helperSet->get($name); + } + + /** + * Validates a command name. + * + * It must be non-empty and parts can optionally be separated by ":". + * + * @throws InvalidArgumentException When the name is invalid + */ + private function validateName(string $name) + { + if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { + throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); + } + } +} diff --git a/vendor/symfony/console/Command/CompleteCommand.php b/vendor/symfony/console/Command/CompleteCommand.php new file mode 100644 index 0000000..11ada4e --- /dev/null +++ b/vendor/symfony/console/Command/CompleteCommand.php @@ -0,0 +1,205 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Completion\Output\BashCompletionOutput; +use Symfony\Component\Console\Completion\Output\CompletionOutputInterface; +use Symfony\Component\Console\Exception\CommandNotFoundException; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Responsible for providing the values to the shell completion. + * + * @author Wouter de Jong + */ +final class CompleteCommand extends Command +{ + protected static $defaultName = '|_complete'; + protected static $defaultDescription = 'Internal command to provide shell completion suggestions'; + + private $completionOutputs; + + private $isDebug = false; + + /** + * @param array> $completionOutputs A list of additional completion outputs, with shell name as key and FQCN as value + */ + public function __construct(array $completionOutputs = []) + { + // must be set before the parent constructor, as the property value is used in configure() + $this->completionOutputs = $completionOutputs + ['bash' => BashCompletionOutput::class]; + + parent::__construct(); + } + + protected function configure(): void + { + $this + ->addOption('shell', 's', InputOption::VALUE_REQUIRED, 'The shell type ("'.implode('", "', array_keys($this->completionOutputs)).'")') + ->addOption('input', 'i', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'An array of input tokens (e.g. COMP_WORDS or argv)') + ->addOption('current', 'c', InputOption::VALUE_REQUIRED, 'The index of the "input" array that the cursor is in (e.g. COMP_CWORD)') + ->addOption('symfony', 'S', InputOption::VALUE_REQUIRED, 'The version of the completion script') + ; + } + + protected function initialize(InputInterface $input, OutputInterface $output) + { + $this->isDebug = filter_var(getenv('SYMFONY_COMPLETION_DEBUG'), \FILTER_VALIDATE_BOOLEAN); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + try { + // uncomment when a bugfix or BC break has been introduced in the shell completion scripts + // $version = $input->getOption('symfony'); + // if ($version && version_compare($version, 'x.y', '>=')) { + // $message = sprintf('Completion script version is not supported ("%s" given, ">=x.y" required).', $version); + // $this->log($message); + + // $output->writeln($message.' Install the Symfony completion script again by using the "completion" command.'); + + // return 126; + // } + + $shell = $input->getOption('shell'); + if (!$shell) { + throw new \RuntimeException('The "--shell" option must be set.'); + } + + if (!$completionOutput = $this->completionOutputs[$shell] ?? false) { + throw new \RuntimeException(sprintf('Shell completion is not supported for your shell: "%s" (supported: "%s").', $shell, implode('", "', array_keys($this->completionOutputs)))); + } + + $completionInput = $this->createCompletionInput($input); + $suggestions = new CompletionSuggestions(); + + $this->log([ + '', + ''.date('Y-m-d H:i:s').'', + 'Input: ("|" indicates the cursor position)', + ' '.(string) $completionInput, + 'Command:', + ' '.(string) implode(' ', $_SERVER['argv']), + 'Messages:', + ]); + + $command = $this->findCommand($completionInput, $output); + if (null === $command) { + $this->log(' No command found, completing using the Application class.'); + + $this->getApplication()->complete($completionInput, $suggestions); + } elseif ( + $completionInput->mustSuggestArgumentValuesFor('command') + && $command->getName() !== $completionInput->getCompletionValue() + && !\in_array($completionInput->getCompletionValue(), $command->getAliases(), true) + ) { + $this->log(' No command found, completing using the Application class.'); + + // expand shortcut names ("cache:cl") into their full name ("cache:clear") + $suggestions->suggestValues(array_filter(array_merge([$command->getName()], $command->getAliases()))); + } else { + $command->mergeApplicationDefinition(); + $completionInput->bind($command->getDefinition()); + + if (CompletionInput::TYPE_OPTION_NAME === $completionInput->getCompletionType()) { + $this->log(' Completing option names for the '.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).' command.'); + + $suggestions->suggestOptions($command->getDefinition()->getOptions()); + } else { + $this->log([ + ' Completing using the '.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).' class.', + ' Completing '.$completionInput->getCompletionType().' for '.$completionInput->getCompletionName().'', + ]); + if (null !== $compval = $completionInput->getCompletionValue()) { + $this->log(' Current value: '.$compval.''); + } + + $command->complete($completionInput, $suggestions); + } + } + + /** @var CompletionOutputInterface $completionOutput */ + $completionOutput = new $completionOutput(); + + $this->log('Suggestions:'); + if ($options = $suggestions->getOptionSuggestions()) { + $this->log(' --'.implode(' --', array_map(function ($o) { return $o->getName(); }, $options))); + } elseif ($values = $suggestions->getValueSuggestions()) { + $this->log(' '.implode(' ', $values)); + } else { + $this->log(' No suggestions were provided'); + } + + $completionOutput->write($suggestions, $output); + } catch (\Throwable $e) { + $this->log([ + 'Error!', + (string) $e, + ]); + + if ($output->isDebug()) { + throw $e; + } + + return self::FAILURE; + } + + return self::SUCCESS; + } + + private function createCompletionInput(InputInterface $input): CompletionInput + { + $currentIndex = $input->getOption('current'); + if (!$currentIndex || !ctype_digit($currentIndex)) { + throw new \RuntimeException('The "--current" option must be set and it must be an integer.'); + } + + $completionInput = CompletionInput::fromTokens($input->getOption('input'), (int) $currentIndex); + + try { + $completionInput->bind($this->getApplication()->getDefinition()); + } catch (ExceptionInterface $e) { + } + + return $completionInput; + } + + private function findCommand(CompletionInput $completionInput, OutputInterface $output): ?Command + { + try { + $inputName = $completionInput->getFirstArgument(); + if (null === $inputName) { + return null; + } + + return $this->getApplication()->find($inputName); + } catch (CommandNotFoundException $e) { + } + + return null; + } + + private function log($messages): void + { + if (!$this->isDebug) { + return; + } + + $commandName = basename($_SERVER['argv'][0]); + file_put_contents(sys_get_temp_dir().'/sf_'.$commandName.'.log', implode(\PHP_EOL, (array) $messages).\PHP_EOL, \FILE_APPEND); + } +} diff --git a/vendor/symfony/console/Command/DumpCompletionCommand.php b/vendor/symfony/console/Command/DumpCompletionCommand.php new file mode 100644 index 0000000..518d606 --- /dev/null +++ b/vendor/symfony/console/Command/DumpCompletionCommand.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Process; + +/** + * Dumps the completion script for the current shell. + * + * @author Wouter de Jong + */ +final class DumpCompletionCommand extends Command +{ + protected static $defaultName = 'completion'; + protected static $defaultDescription = 'Dump the shell completion script'; + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestArgumentValuesFor('shell')) { + $suggestions->suggestValues($this->getSupportedShells()); + } + } + + protected function configure() + { + $fullCommand = $_SERVER['PHP_SELF']; + $commandName = basename($fullCommand); + $fullCommand = @realpath($fullCommand) ?: $fullCommand; + + $this + ->setHelp(<<%command.name% command dumps the shell completion script required +to use shell autocompletion (currently only bash completion is supported). + +Static installation +------------------- + +Dump the script to a global completion file and restart your shell: + + %command.full_name% bash | sudo tee /etc/bash_completion.d/{$commandName} + +Or dump the script to a local file and source it: + + %command.full_name% bash > completion.sh + + # source the file whenever you use the project + source completion.sh + + # or add this line at the end of your "~/.bashrc" file: + source /path/to/completion.sh + +Dynamic installation +-------------------- + +Add this to the end of your shell configuration file (e.g. "~/.bashrc"): + + eval "$({$fullCommand} completion bash)" +EOH + ) + ->addArgument('shell', InputArgument::OPTIONAL, 'The shell type (e.g. "bash"), the value of the "$SHELL" env var will be used if this is not given') + ->addOption('debug', null, InputOption::VALUE_NONE, 'Tail the completion debug log') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $commandName = basename($_SERVER['argv'][0]); + + if ($input->getOption('debug')) { + $this->tailDebugLog($commandName, $output); + + return self::SUCCESS; + } + + $shell = $input->getArgument('shell') ?? self::guessShell(); + $completionFile = __DIR__.'/../Resources/completion.'.$shell; + if (!file_exists($completionFile)) { + $supportedShells = $this->getSupportedShells(); + + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + if ($shell) { + $output->writeln(sprintf('Detected shell "%s", which is not supported by Symfony shell completion (supported shells: "%s").', $shell, implode('", "', $supportedShells))); + } else { + $output->writeln(sprintf('Shell not detected, Symfony shell completion only supports "%s").', implode('", "', $supportedShells))); + } + + return self::INVALID; + } + + $output->write(str_replace(['{{ COMMAND_NAME }}', '{{ VERSION }}'], [$commandName, $this->getApplication()->getVersion()], file_get_contents($completionFile))); + + return self::SUCCESS; + } + + private static function guessShell(): string + { + return basename($_SERVER['SHELL'] ?? ''); + } + + private function tailDebugLog(string $commandName, OutputInterface $output): void + { + $debugFile = sys_get_temp_dir().'/sf_'.$commandName.'.log'; + if (!file_exists($debugFile)) { + touch($debugFile); + } + $process = new Process(['tail', '-f', $debugFile], null, null, null, 0); + $process->run(function (string $type, string $line) use ($output): void { + $output->write($line); + }); + } + + /** + * @return string[] + */ + private function getSupportedShells(): array + { + return array_map(function ($f) { + return pathinfo($f, \PATHINFO_EXTENSION); + }, glob(__DIR__.'/../Resources/completion.*')); + } +} diff --git a/vendor/symfony/console/Command/HelpCommand.php b/vendor/symfony/console/Command/HelpCommand.php new file mode 100644 index 0000000..66f8593 --- /dev/null +++ b/vendor/symfony/console/Command/HelpCommand.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Descriptor\ApplicationDescription; +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * HelpCommand displays the help for a given command. + * + * @author Fabien Potencier + */ +class HelpCommand 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('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), + ]) + ->setDescription('Display help for a command') + ->setHelp(<<<'EOF' +The %command.name% command displays help for a given command: + + %command.full_name% list + +You can also output the help in other formats by using the --format option: + + %command.full_name% --format=xml list + +To display the list of available commands, please use the list command. +EOF + ) + ; + } + + public function setCommand(Command $command) + { + $this->command = $command; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->command ??= $this->getApplication()->find($input->getArgument('command_name')); + + $helper = new DescriptorHelper(); + $helper->describe($output, $this->command, [ + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + ]); + + unset($this->command); + + return 0; + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestArgumentValuesFor('command_name')) { + $descriptor = new ApplicationDescription($this->getApplication()); + $suggestions->suggestValues(array_keys($descriptor->getCommands())); + + return; + } + + if ($input->mustSuggestOptionValuesFor('format')) { + $helper = new DescriptorHelper(); + $suggestions->suggestValues($helper->getFormats()); + } + } +} diff --git a/vendor/symfony/console/Command/LazyCommand.php b/vendor/symfony/console/Command/LazyCommand.php new file mode 100644 index 0000000..aec4126 --- /dev/null +++ b/vendor/symfony/console/Command/LazyCommand.php @@ -0,0 +1,194 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Nicolas Grekas + */ +final class LazyCommand extends Command +{ + private $command; + private ?bool $isEnabled; + + public function __construct(string $name, array $aliases, string $description, bool $isHidden, \Closure $commandFactory, ?bool $isEnabled = true) + { + $this->setName($name) + ->setAliases($aliases) + ->setHidden($isHidden) + ->setDescription($description); + + $this->command = $commandFactory; + $this->isEnabled = $isEnabled; + } + + public function ignoreValidationErrors(): void + { + $this->getCommand()->ignoreValidationErrors(); + } + + public function setApplication(Application $application = null): void + { + if ($this->command instanceof parent) { + $this->command->setApplication($application); + } + + parent::setApplication($application); + } + + public function setHelperSet(HelperSet $helperSet): void + { + if ($this->command instanceof parent) { + $this->command->setHelperSet($helperSet); + } + + parent::setHelperSet($helperSet); + } + + public function isEnabled(): bool + { + return $this->isEnabled ?? $this->getCommand()->isEnabled(); + } + + public function run(InputInterface $input, OutputInterface $output): int + { + return $this->getCommand()->run($input, $output); + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $this->getCommand()->complete($input, $suggestions); + } + + public function setCode(callable $code): static + { + $this->getCommand()->setCode($code); + + return $this; + } + + /** + * @internal + */ + public function mergeApplicationDefinition(bool $mergeArgs = true): void + { + $this->getCommand()->mergeApplicationDefinition($mergeArgs); + } + + public function setDefinition(array|InputDefinition $definition): static + { + $this->getCommand()->setDefinition($definition); + + return $this; + } + + public function getDefinition(): InputDefinition + { + return $this->getCommand()->getDefinition(); + } + + public function getNativeDefinition(): InputDefinition + { + return $this->getCommand()->getNativeDefinition(); + } + + public function addArgument(string $name, int $mode = null, string $description = '', mixed $default = null): static + { + $this->getCommand()->addArgument($name, $mode, $description, $default); + + return $this; + } + + public function addOption(string $name, string|array $shortcut = null, int $mode = null, string $description = '', mixed $default = null): static + { + $this->getCommand()->addOption($name, $shortcut, $mode, $description, $default); + + return $this; + } + + public function setProcessTitle(string $title): static + { + $this->getCommand()->setProcessTitle($title); + + return $this; + } + + public function setHelp(string $help): static + { + $this->getCommand()->setHelp($help); + + return $this; + } + + public function getHelp(): string + { + return $this->getCommand()->getHelp(); + } + + public function getProcessedHelp(): string + { + return $this->getCommand()->getProcessedHelp(); + } + + public function getSynopsis(bool $short = false): string + { + return $this->getCommand()->getSynopsis($short); + } + + public function addUsage(string $usage): static + { + $this->getCommand()->addUsage($usage); + + return $this; + } + + public function getUsages(): array + { + return $this->getCommand()->getUsages(); + } + + public function getHelper(string $name): mixed + { + return $this->getCommand()->getHelper($name); + } + + public function getCommand(): parent + { + if (!$this->command instanceof \Closure) { + return $this->command; + } + + $command = $this->command = ($this->command)(); + $command->setApplication($this->getApplication()); + + if (null !== $this->getHelperSet()) { + $command->setHelperSet($this->getHelperSet()); + } + + $command->setName($this->getName()) + ->setAliases($this->getAliases()) + ->setHidden($this->isHidden()) + ->setDescription($this->getDescription()); + + // Will throw if the command is not correctly initialized. + $command->getDefinition(); + + return $command; + } +} diff --git a/vendor/symfony/console/Command/ListCommand.php b/vendor/symfony/console/Command/ListCommand.php new file mode 100644 index 0000000..5c7260f --- /dev/null +++ b/vendor/symfony/console/Command/ListCommand.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Descriptor\ApplicationDescription; +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * ListCommand displays the list of all available commands for the application. + * + * @author Fabien Potencier + */ +class ListCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('list') + ->setDefinition([ + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('short', null, InputOption::VALUE_NONE, 'To skip describing commands\' arguments'), + ]) + ->setDescription('List commands') + ->setHelp(<<<'EOF' +The %command.name% command lists all commands: + + %command.full_name% + +You can also display the commands for a specific namespace: + + %command.full_name% test + +You can also output the information in other formats by using the --format option: + + %command.full_name% --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + %command.full_name% --raw +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $helper = new DescriptorHelper(); + $helper->describe($output, $this->getApplication(), [ + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + 'namespace' => $input->getArgument('namespace'), + 'short' => $input->getOption('short'), + ]); + + return 0; + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestArgumentValuesFor('namespace')) { + $descriptor = new ApplicationDescription($this->getApplication()); + $suggestions->suggestValues(array_keys($descriptor->getNamespaces())); + + return; + } + + if ($input->mustSuggestOptionValuesFor('format')) { + $helper = new DescriptorHelper(); + $suggestions->suggestValues($helper->getFormats()); + } + } +} diff --git a/vendor/symfony/console/Command/LockableTrait.php b/vendor/symfony/console/Command/LockableTrait.php new file mode 100644 index 0000000..7969551 --- /dev/null +++ b/vendor/symfony/console/Command/LockableTrait.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Lock\LockFactory; +use Symfony\Component\Lock\Store\FlockStore; +use Symfony\Component\Lock\Store\SemaphoreStore; + +/** + * Basic lock feature for commands. + * + * @author Geoffrey Brier + */ +trait LockableTrait +{ + private $lock = null; + + /** + * Locks a command. + */ + private function lock(string $name = null, bool $blocking = false): bool + { + if (!class_exists(SemaphoreStore::class)) { + throw new LogicException('To enable the locking feature you must install the symfony/lock component.'); + } + + if (null !== $this->lock) { + throw new LogicException('A lock is already in place.'); + } + + if (SemaphoreStore::isSupported()) { + $store = new SemaphoreStore(); + } else { + $store = new FlockStore(); + } + + $this->lock = (new LockFactory($store))->createLock($name ?: $this->getName()); + if (!$this->lock->acquire($blocking)) { + $this->lock = null; + + return false; + } + + return true; + } + + /** + * Releases the command lock if there is one. + */ + private function release() + { + if ($this->lock) { + $this->lock->release(); + $this->lock = null; + } + } +} diff --git a/vendor/symfony/console/Command/SignalableCommandInterface.php b/vendor/symfony/console/Command/SignalableCommandInterface.php new file mode 100644 index 0000000..d439728 --- /dev/null +++ b/vendor/symfony/console/Command/SignalableCommandInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +/** + * Interface for command reacting to signal. + * + * @author Grégoire Pineau + */ +interface SignalableCommandInterface +{ + /** + * Returns the list of signals to subscribe. + */ + public function getSubscribedSignals(): array; + + /** + * The method will be called when the application is signaled. + */ + public function handleSignal(int $signal): void; +} diff --git a/vendor/symfony/console/CommandLoader/CommandLoaderInterface.php b/vendor/symfony/console/CommandLoader/CommandLoaderInterface.php new file mode 100644 index 0000000..b6b637c --- /dev/null +++ b/vendor/symfony/console/CommandLoader/CommandLoaderInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\CommandLoader; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * @author Robin Chalas + */ +interface CommandLoaderInterface +{ + /** + * Loads a command. + * + * @throws CommandNotFoundException + */ + public function get(string $name): Command; + + /** + * Checks if a command exists. + */ + public function has(string $name): bool; + + /** + * @return string[] + */ + public function getNames(): array; +} diff --git a/vendor/symfony/console/CommandLoader/ContainerCommandLoader.php b/vendor/symfony/console/CommandLoader/ContainerCommandLoader.php new file mode 100644 index 0000000..9b26577 --- /dev/null +++ b/vendor/symfony/console/CommandLoader/ContainerCommandLoader.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\CommandLoader; + +use Psr\Container\ContainerInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * Loads commands from a PSR-11 container. + * + * @author Robin Chalas + */ +class ContainerCommandLoader implements CommandLoaderInterface +{ + private $container; + private array $commandMap; + + /** + * @param array $commandMap An array with command names as keys and service ids as values + */ + public function __construct(ContainerInterface $container, array $commandMap) + { + $this->container = $container; + $this->commandMap = $commandMap; + } + + /** + * {@inheritdoc} + */ + public function get(string $name): Command + { + if (!$this->has($name)) { + throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); + } + + return $this->container->get($this->commandMap[$name]); + } + + /** + * {@inheritdoc} + */ + public function has(string $name): bool + { + return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]); + } + + /** + * {@inheritdoc} + */ + public function getNames(): array + { + return array_keys($this->commandMap); + } +} diff --git a/vendor/symfony/console/CommandLoader/FactoryCommandLoader.php b/vendor/symfony/console/CommandLoader/FactoryCommandLoader.php new file mode 100644 index 0000000..c55dc1d --- /dev/null +++ b/vendor/symfony/console/CommandLoader/FactoryCommandLoader.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\CommandLoader; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * A simple command loader using factories to instantiate commands lazily. + * + * @author Maxime Steinhausser + */ +class FactoryCommandLoader implements CommandLoaderInterface +{ + private array $factories; + + /** + * @param callable[] $factories Indexed by command names + */ + public function __construct(array $factories) + { + $this->factories = $factories; + } + + /** + * {@inheritdoc} + */ + public function has(string $name): bool + { + return isset($this->factories[$name]); + } + + /** + * {@inheritdoc} + */ + public function get(string $name): Command + { + if (!isset($this->factories[$name])) { + throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); + } + + $factory = $this->factories[$name]; + + return $factory(); + } + + /** + * {@inheritdoc} + */ + public function getNames(): array + { + return array_keys($this->factories); + } +} diff --git a/vendor/symfony/console/Completion/CompletionInput.php b/vendor/symfony/console/Completion/CompletionInput.php new file mode 100644 index 0000000..368b945 --- /dev/null +++ b/vendor/symfony/console/Completion/CompletionInput.php @@ -0,0 +1,249 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Completion; + +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * An input specialized for shell completion. + * + * This input allows unfinished option names or values and exposes what kind of + * completion is expected. + * + * @author Wouter de Jong + */ +final class CompletionInput extends ArgvInput +{ + public const TYPE_ARGUMENT_VALUE = 'argument_value'; + public const TYPE_OPTION_VALUE = 'option_value'; + public const TYPE_OPTION_NAME = 'option_name'; + public const TYPE_NONE = 'none'; + + private $tokens; + private $currentIndex; + private $completionType; + private $completionName = null; + private $completionValue = ''; + + /** + * Converts a terminal string into tokens. + * + * This is required for shell completions without COMP_WORDS support. + */ + public static function fromString(string $inputStr, int $currentIndex): self + { + preg_match_all('/(?<=^|\s)([\'"]?)(.+?)(?tokens = $tokens; + $input->currentIndex = $currentIndex; + + return $input; + } + + /** + * {@inheritdoc} + */ + public function bind(InputDefinition $definition): void + { + parent::bind($definition); + + $relevantToken = $this->getRelevantToken(); + if ('-' === $relevantToken[0]) { + // the current token is an input option: complete either option name or option value + [$optionToken, $optionValue] = explode('=', $relevantToken, 2) + ['', '']; + + $option = $this->getOptionFromToken($optionToken); + if (null === $option && !$this->isCursorFree()) { + $this->completionType = self::TYPE_OPTION_NAME; + $this->completionValue = $relevantToken; + + return; + } + + if (null !== $option && $option->acceptValue()) { + $this->completionType = self::TYPE_OPTION_VALUE; + $this->completionName = $option->getName(); + $this->completionValue = $optionValue ?: (!str_starts_with($optionToken, '--') ? substr($optionToken, 2) : ''); + + return; + } + } + + $previousToken = $this->tokens[$this->currentIndex - 1]; + if ('-' === $previousToken[0] && '' !== trim($previousToken, '-')) { + // check if previous option accepted a value + $previousOption = $this->getOptionFromToken($previousToken); + if (null !== $previousOption && $previousOption->acceptValue()) { + $this->completionType = self::TYPE_OPTION_VALUE; + $this->completionName = $previousOption->getName(); + $this->completionValue = $relevantToken; + + return; + } + } + + // complete argument value + $this->completionType = self::TYPE_ARGUMENT_VALUE; + + foreach ($this->definition->getArguments() as $argumentName => $argument) { + if (!isset($this->arguments[$argumentName])) { + break; + } + + $argumentValue = $this->arguments[$argumentName]; + $this->completionName = $argumentName; + if (\is_array($argumentValue)) { + $this->completionValue = $argumentValue ? $argumentValue[array_key_last($argumentValue)] : null; + } else { + $this->completionValue = $argumentValue; + } + } + + if ($this->currentIndex >= \count($this->tokens)) { + if (!isset($this->arguments[$argumentName]) || $this->definition->getArgument($argumentName)->isArray()) { + $this->completionName = $argumentName; + $this->completionValue = ''; + } else { + // we've reached the end + $this->completionType = self::TYPE_NONE; + $this->completionName = null; + $this->completionValue = ''; + } + } + } + + /** + * Returns the type of completion required. + * + * TYPE_ARGUMENT_VALUE when completing the value of an input argument + * TYPE_OPTION_VALUE when completing the value of an input option + * TYPE_OPTION_NAME when completing the name of an input option + * TYPE_NONE when nothing should be completed + * + * @return string One of self::TYPE_* constants. TYPE_OPTION_NAME and TYPE_NONE are already implemented by the Console component + */ + public function getCompletionType(): string + { + return $this->completionType; + } + + /** + * The name of the input option or argument when completing a value. + * + * @return string|null returns null when completing an option name + */ + public function getCompletionName(): ?string + { + return $this->completionName; + } + + /** + * The value already typed by the user (or empty string). + */ + public function getCompletionValue(): string + { + return $this->completionValue; + } + + public function mustSuggestOptionValuesFor(string $optionName): bool + { + return self::TYPE_OPTION_VALUE === $this->getCompletionType() && $optionName === $this->getCompletionName(); + } + + public function mustSuggestArgumentValuesFor(string $argumentName): bool + { + return self::TYPE_ARGUMENT_VALUE === $this->getCompletionType() && $argumentName === $this->getCompletionName(); + } + + protected function parseToken(string $token, bool $parseOptions): bool + { + try { + return parent::parseToken($token, $parseOptions); + } catch (RuntimeException $e) { + // suppress errors, completed input is almost never valid + } + + return $parseOptions; + } + + private function getOptionFromToken(string $optionToken): ?InputOption + { + $optionName = ltrim($optionToken, '-'); + if (!$optionName) { + return null; + } + + if ('-' === ($optionToken[1] ?? ' ')) { + // long option name + return $this->definition->hasOption($optionName) ? $this->definition->getOption($optionName) : null; + } + + // short option name + return $this->definition->hasShortcut($optionName[0]) ? $this->definition->getOptionForShortcut($optionName[0]) : null; + } + + /** + * The token of the cursor, or the last token if the cursor is at the end of the input. + */ + private function getRelevantToken(): string + { + return $this->tokens[$this->isCursorFree() ? $this->currentIndex - 1 : $this->currentIndex]; + } + + /** + * Whether the cursor is "free" (i.e. at the end of the input preceded by a space). + */ + private function isCursorFree(): bool + { + $nrOfTokens = \count($this->tokens); + if ($this->currentIndex > $nrOfTokens) { + throw new \LogicException('Current index is invalid, it must be the number of input tokens or one more.'); + } + + return $this->currentIndex >= $nrOfTokens; + } + + public function __toString() + { + $str = ''; + foreach ($this->tokens as $i => $token) { + $str .= $token; + + if ($this->currentIndex === $i) { + $str .= '|'; + } + + $str .= ' '; + } + + if ($this->currentIndex > $i) { + $str .= '|'; + } + + return rtrim($str); + } +} diff --git a/vendor/symfony/console/Completion/CompletionSuggestions.php b/vendor/symfony/console/Completion/CompletionSuggestions.php new file mode 100644 index 0000000..7191181 --- /dev/null +++ b/vendor/symfony/console/Completion/CompletionSuggestions.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Completion; + +use Symfony\Component\Console\Input\InputOption; + +/** + * Stores all completion suggestions for the current input. + * + * @author Wouter de Jong + */ +final class CompletionSuggestions +{ + private $valueSuggestions = []; + private $optionSuggestions = []; + + /** + * Add a suggested value for an input option or argument. + * + * @return $this + */ + public function suggestValue(string|Suggestion $value): static + { + $this->valueSuggestions[] = !$value instanceof Suggestion ? new Suggestion($value) : $value; + + return $this; + } + + /** + * Add multiple suggested values at once for an input option or argument. + * + * @param list $values + * + * @return $this + */ + public function suggestValues(array $values): static + { + foreach ($values as $value) { + $this->suggestValue($value); + } + + return $this; + } + + /** + * Add a suggestion for an input option name. + * + * @return $this + */ + public function suggestOption(InputOption $option): static + { + $this->optionSuggestions[] = $option; + + return $this; + } + + /** + * Add multiple suggestions for input option names at once. + * + * @param InputOption[] $options + * + * @return $this + */ + public function suggestOptions(array $options): static + { + foreach ($options as $option) { + $this->suggestOption($option); + } + + return $this; + } + + /** + * @return InputOption[] + */ + public function getOptionSuggestions(): array + { + return $this->optionSuggestions; + } + + /** + * @return Suggestion[] + */ + public function getValueSuggestions(): array + { + return $this->valueSuggestions; + } +} diff --git a/vendor/symfony/console/Completion/Output/BashCompletionOutput.php b/vendor/symfony/console/Completion/Output/BashCompletionOutput.php new file mode 100644 index 0000000..c6f76eb --- /dev/null +++ b/vendor/symfony/console/Completion/Output/BashCompletionOutput.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Completion\Output; + +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Wouter de Jong + */ +class BashCompletionOutput implements CompletionOutputInterface +{ + public function write(CompletionSuggestions $suggestions, OutputInterface $output): void + { + $values = $suggestions->getValueSuggestions(); + foreach ($suggestions->getOptionSuggestions() as $option) { + $values[] = '--'.$option->getName(); + if ($option->isNegatable()) { + $values[] = '--no-'.$option->getName(); + } + } + $output->writeln(implode("\n", $values)); + } +} diff --git a/vendor/symfony/console/Completion/Output/CompletionOutputInterface.php b/vendor/symfony/console/Completion/Output/CompletionOutputInterface.php new file mode 100644 index 0000000..659e596 --- /dev/null +++ b/vendor/symfony/console/Completion/Output/CompletionOutputInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Completion\Output; + +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Transforms the {@see CompletionSuggestions} object into output readable by the shell completion. + * + * @author Wouter de Jong + */ +interface CompletionOutputInterface +{ + public function write(CompletionSuggestions $suggestions, OutputInterface $output): void; +} diff --git a/vendor/symfony/console/Completion/Suggestion.php b/vendor/symfony/console/Completion/Suggestion.php new file mode 100644 index 0000000..ff156f8 --- /dev/null +++ b/vendor/symfony/console/Completion/Suggestion.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Completion; + +/** + * Represents a single suggested value. + * + * @author Wouter de Jong + */ +class Suggestion +{ + private string $value; + + public function __construct(string $value) + { + $this->value = $value; + } + + public function getValue(): string + { + return $this->value; + } + + public function __toString(): string + { + return $this->getValue(); + } +} diff --git a/vendor/symfony/console/ConsoleEvents.php b/vendor/symfony/console/ConsoleEvents.php new file mode 100644 index 0000000..6ae8f32 --- /dev/null +++ b/vendor/symfony/console/ConsoleEvents.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleErrorEvent; +use Symfony\Component\Console\Event\ConsoleSignalEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; + +/** + * Contains all events dispatched by an Application. + * + * @author Francesco Levorato + */ +final class ConsoleEvents +{ + /** + * The COMMAND event allows you to attach listeners before any command is + * executed by the console. It also allows you to modify the command, input and output + * before they are handed to the command. + * + * @Event("Symfony\Component\Console\Event\ConsoleCommandEvent") + */ + public const COMMAND = 'console.command'; + + /** + * The SIGNAL event allows you to perform some actions + * after the command execution was interrupted. + * + * @Event("Symfony\Component\Console\Event\ConsoleSignalEvent") + */ + public const SIGNAL = 'console.signal'; + + /** + * The TERMINATE event allows you to attach listeners after a command is + * executed by the console. + * + * @Event("Symfony\Component\Console\Event\ConsoleTerminateEvent") + */ + public const TERMINATE = 'console.terminate'; + + /** + * The ERROR event occurs when an uncaught exception or error appears. + * + * This event allows you to deal with the exception/error or + * to modify the thrown exception. + * + * @Event("Symfony\Component\Console\Event\ConsoleErrorEvent") + */ + public const ERROR = 'console.error'; + + /** + * Event aliases. + * + * These aliases can be consumed by RegisterListenersPass. + */ + public const ALIASES = [ + ConsoleCommandEvent::class => self::COMMAND, + ConsoleErrorEvent::class => self::ERROR, + ConsoleSignalEvent::class => self::SIGNAL, + ConsoleTerminateEvent::class => self::TERMINATE, + ]; +} diff --git a/vendor/symfony/console/Cursor.php b/vendor/symfony/console/Cursor.php new file mode 100644 index 0000000..995e3d7 --- /dev/null +++ b/vendor/symfony/console/Cursor.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Pierre du Plessis + */ +final class Cursor +{ + private $output; + private $input; + + /** + * @param resource|null $input + */ + public function __construct(OutputInterface $output, $input = null) + { + $this->output = $output; + $this->input = $input ?? (\defined('STDIN') ? \STDIN : fopen('php://input', 'r+')); + } + + /** + * @return $this + */ + public function moveUp(int $lines = 1): static + { + $this->output->write(sprintf("\x1b[%dA", $lines)); + + return $this; + } + + /** + * @return $this + */ + public function moveDown(int $lines = 1): static + { + $this->output->write(sprintf("\x1b[%dB", $lines)); + + return $this; + } + + /** + * @return $this + */ + public function moveRight(int $columns = 1): static + { + $this->output->write(sprintf("\x1b[%dC", $columns)); + + return $this; + } + + /** + * @return $this + */ + public function moveLeft(int $columns = 1): static + { + $this->output->write(sprintf("\x1b[%dD", $columns)); + + return $this; + } + + /** + * @return $this + */ + public function moveToColumn(int $column): static + { + $this->output->write(sprintf("\x1b[%dG", $column)); + + return $this; + } + + /** + * @return $this + */ + public function moveToPosition(int $column, int $row): static + { + $this->output->write(sprintf("\x1b[%d;%dH", $row + 1, $column)); + + return $this; + } + + /** + * @return $this + */ + public function savePosition(): static + { + $this->output->write("\x1b7"); + + return $this; + } + + /** + * @return $this + */ + public function restorePosition(): static + { + $this->output->write("\x1b8"); + + return $this; + } + + /** + * @return $this + */ + public function hide(): static + { + $this->output->write("\x1b[?25l"); + + return $this; + } + + /** + * @return $this + */ + public function show(): static + { + $this->output->write("\x1b[?25h\x1b[?0c"); + + return $this; + } + + /** + * Clears all the output from the current line. + * + * @return $this + */ + public function clearLine(): static + { + $this->output->write("\x1b[2K"); + + return $this; + } + + /** + * Clears all the output from the current line after the current position. + */ + public function clearLineAfter(): self + { + $this->output->write("\x1b[K"); + + return $this; + } + + /** + * Clears all the output from the cursors' current position to the end of the screen. + * + * @return $this + */ + public function clearOutput(): static + { + $this->output->write("\x1b[0J"); + + return $this; + } + + /** + * Clears the entire screen. + * + * @return $this + */ + public function clearScreen(): static + { + $this->output->write("\x1b[2J"); + + return $this; + } + + /** + * Returns the current cursor position as x,y coordinates. + */ + public function getCurrentPosition(): array + { + static $isTtySupported; + + if (null === $isTtySupported && \function_exists('proc_open')) { + $isTtySupported = (bool) @proc_open('echo 1 >/dev/null', [['file', '/dev/tty', 'r'], ['file', '/dev/tty', 'w'], ['file', '/dev/tty', 'w']], $pipes); + } + + if (!$isTtySupported) { + return [1, 1]; + } + + $sttyMode = shell_exec('stty -g'); + shell_exec('stty -icanon -echo'); + + @fwrite($this->input, "\033[6n"); + + $code = trim(fread($this->input, 1024)); + + shell_exec(sprintf('stty %s', $sttyMode)); + + sscanf($code, "\033[%d;%dR", $row, $col); + + return [$col, $row]; + } +} diff --git a/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php b/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php new file mode 100644 index 0000000..cfb2049 --- /dev/null +++ b/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\DependencyInjection; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\LazyCommand; +use Symfony\Component\Console\CommandLoader\ContainerCommandLoader; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\TypedReference; + +/** + * Registers console commands. + * + * @author Grégoire Pineau + */ +class AddConsoleCommandPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $commandServices = $container->findTaggedServiceIds('console.command', true); + $lazyCommandMap = []; + $lazyCommandRefs = []; + $serviceIds = []; + + foreach ($commandServices as $id => $tags) { + $definition = $container->getDefinition($id); + $definition->addTag('container.no_preload'); + $class = $container->getParameterBag()->resolveValue($definition->getClass()); + + if (isset($tags[0]['command'])) { + $aliases = $tags[0]['command']; + } else { + if (!$r = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + if (!$r->isSubclassOf(Command::class)) { + throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class)); + } + $aliases = str_replace('%', '%%', $class::getDefaultName() ?? ''); + } + + $aliases = explode('|', $aliases ?? ''); + $commandName = array_shift($aliases); + + if ($isHidden = '' === $commandName) { + $commandName = array_shift($aliases); + } + + if (null === $commandName) { + if (!$definition->isPublic() || $definition->isPrivate() || $definition->hasTag('container.private')) { + $commandId = 'console.command.public_alias.'.$id; + $container->setAlias($commandId, $id)->setPublic(true); + $id = $commandId; + } + $serviceIds[] = $id; + + continue; + } + + $description = $tags[0]['description'] ?? null; + + unset($tags[0]); + $lazyCommandMap[$commandName] = $id; + $lazyCommandRefs[$id] = new TypedReference($id, $class); + + foreach ($aliases as $alias) { + $lazyCommandMap[$alias] = $id; + } + + foreach ($tags as $tag) { + if (isset($tag['command'])) { + $aliases[] = $tag['command']; + $lazyCommandMap[$tag['command']] = $id; + } + + $description = $description ?? $tag['description'] ?? null; + } + + $definition->addMethodCall('setName', [$commandName]); + + if ($aliases) { + $definition->addMethodCall('setAliases', [$aliases]); + } + + if ($isHidden) { + $definition->addMethodCall('setHidden', [true]); + } + + if (!$description) { + if (!$r = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + if (!$r->isSubclassOf(Command::class)) { + throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class)); + } + $description = str_replace('%', '%%', $class::getDefaultDescription() ?? ''); + } + + if ($description) { + $definition->addMethodCall('setDescription', [$description]); + + $container->register('.'.$id.'.lazy', LazyCommand::class) + ->setArguments([$commandName, $aliases, $description, $isHidden, new ServiceClosureArgument($lazyCommandRefs[$id])]); + + $lazyCommandRefs[$id] = new Reference('.'.$id.'.lazy'); + } + } + + $container + ->register('console.command_loader', ContainerCommandLoader::class) + ->setPublic(true) + ->addTag('container.no_preload') + ->setArguments([ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap]); + + $container->setParameter('console.command.ids', $serviceIds); + } +} diff --git a/vendor/symfony/console/Descriptor/ApplicationDescription.php b/vendor/symfony/console/Descriptor/ApplicationDescription.php new file mode 100644 index 0000000..2fd311a --- /dev/null +++ b/vendor/symfony/console/Descriptor/ApplicationDescription.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * @author Jean-François Simon + * + * @internal + */ +class ApplicationDescription +{ + public const GLOBAL_NAMESPACE = '_global'; + + private $application; + private ?string $namespace; + private bool $showHidden; + private array $namespaces; + + /** + * @var array + */ + private array $commands; + + /** + * @var array + */ + private array $aliases = []; + + public function __construct(Application $application, string $namespace = null, bool $showHidden = false) + { + $this->application = $application; + $this->namespace = $namespace; + $this->showHidden = $showHidden; + } + + public function getNamespaces(): array + { + if (!isset($this->namespaces)) { + $this->inspectApplication(); + } + + return $this->namespaces; + } + + /** + * @return Command[] + */ + public function getCommands(): array + { + if (!isset($this->commands)) { + $this->inspectApplication(); + } + + return $this->commands; + } + + /** + * @throws CommandNotFoundException + */ + public function getCommand(string $name): Command + { + if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { + throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); + } + + return $this->commands[$name] ?? $this->aliases[$name]; + } + + private function inspectApplication() + { + $this->commands = []; + $this->namespaces = []; + + $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null); + foreach ($this->sortCommands($all) as $namespace => $commands) { + $names = []; + + /** @var Command $command */ + foreach ($commands as $name => $command) { + if (!$command->getName() || (!$this->showHidden && $command->isHidden())) { + continue; + } + + if ($command->getName() === $name) { + $this->commands[$name] = $command; + } else { + $this->aliases[$name] = $command; + } + + $names[] = $name; + } + + $this->namespaces[$namespace] = ['id' => $namespace, 'commands' => $names]; + } + } + + private function sortCommands(array $commands): array + { + $namespacedCommands = []; + $globalCommands = []; + $sortedCommands = []; + foreach ($commands as $name => $command) { + $key = $this->application->extractNamespace($name, 1); + if (\in_array($key, ['', self::GLOBAL_NAMESPACE], true)) { + $globalCommands[$name] = $command; + } else { + $namespacedCommands[$key][$name] = $command; + } + } + + if ($globalCommands) { + ksort($globalCommands); + $sortedCommands[self::GLOBAL_NAMESPACE] = $globalCommands; + } + + if ($namespacedCommands) { + ksort($namespacedCommands, \SORT_STRING); + foreach ($namespacedCommands as $key => $commandsSet) { + ksort($commandsSet); + $sortedCommands[$key] = $commandsSet; + } + } + + return $sortedCommands; + } +} diff --git a/vendor/symfony/console/Descriptor/Descriptor.php b/vendor/symfony/console/Descriptor/Descriptor.php new file mode 100644 index 0000000..a364830 --- /dev/null +++ b/vendor/symfony/console/Descriptor/Descriptor.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Jean-François Simon + * + * @internal + */ +abstract class Descriptor implements DescriptorInterface +{ + /** + * @var OutputInterface + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function describe(OutputInterface $output, object $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 Application: + $this->describeApplication($object, $options); + break; + default: + throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_debug_type($object))); + } + } + + /** + * Writes content to output. + */ + protected function write(string $content, bool $decorated = false) + { + $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW); + } + + /** + * Describes an InputArgument instance. + */ + abstract protected function describeInputArgument(InputArgument $argument, array $options = []); + + /** + * Describes an InputOption instance. + */ + abstract protected function describeInputOption(InputOption $option, array $options = []); + + /** + * Describes an InputDefinition instance. + */ + abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []); + + /** + * Describes a Command instance. + */ + abstract protected function describeCommand(Command $command, array $options = []); + + /** + * Describes an Application instance. + */ + abstract protected function describeApplication(Application $application, array $options = []); +} diff --git a/vendor/symfony/console/Descriptor/DescriptorInterface.php b/vendor/symfony/console/Descriptor/DescriptorInterface.php new file mode 100644 index 0000000..ebea303 --- /dev/null +++ b/vendor/symfony/console/Descriptor/DescriptorInterface.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Descriptor interface. + * + * @author Jean-François Simon + */ +interface DescriptorInterface +{ + public function describe(OutputInterface $output, object $object, array $options = []); +} diff --git a/vendor/symfony/console/Descriptor/JsonDescriptor.php b/vendor/symfony/console/Descriptor/JsonDescriptor.php new file mode 100644 index 0000000..1d28659 --- /dev/null +++ b/vendor/symfony/console/Descriptor/JsonDescriptor.php @@ -0,0 +1,181 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * JSON descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class JsonDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = []) + { + $this->writeData($this->getInputArgumentData($argument), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = []) + { + $this->writeData($this->getInputOptionData($option), $options); + if ($option->isNegatable()) { + $this->writeData($this->getInputOptionData($option, true), $options); + } + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = []) + { + $this->writeData($this->getInputDefinitionData($definition), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = []) + { + $this->writeData($this->getCommandData($command, $options['short'] ?? false), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = []) + { + $describedNamespace = $options['namespace'] ?? null; + $description = new ApplicationDescription($application, $describedNamespace, true); + $commands = []; + + foreach ($description->getCommands() as $command) { + $commands[] = $this->getCommandData($command, $options['short'] ?? false); + } + + $data = []; + if ('UNKNOWN' !== $application->getName()) { + $data['application']['name'] = $application->getName(); + if ('UNKNOWN' !== $application->getVersion()) { + $data['application']['version'] = $application->getVersion(); + } + } + + $data['commands'] = $commands; + + if ($describedNamespace) { + $data['namespace'] = $describedNamespace; + } else { + $data['namespaces'] = array_values($description->getNamespaces()); + } + + $this->writeData($data, $options); + } + + /** + * Writes data as json. + */ + private function writeData(array $data, array $options) + { + $flags = $options['json_encoding'] ?? 0; + + $this->write(json_encode($data, $flags)); + } + + private function getInputArgumentData(InputArgument $argument): array + { + return [ + 'name' => $argument->getName(), + 'is_required' => $argument->isRequired(), + 'is_array' => $argument->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()), + 'default' => \INF === $argument->getDefault() ? 'INF' : $argument->getDefault(), + ]; + } + + private function getInputOptionData(InputOption $option, bool $negated = false): array + { + return $negated ? [ + 'name' => '--no-'.$option->getName(), + 'shortcut' => '', + 'accept_value' => false, + 'is_value_required' => false, + 'is_multiple' => false, + 'description' => 'Negate the "--'.$option->getName().'" option', + 'default' => false, + ] : [ + 'name' => '--'.$option->getName(), + 'shortcut' => $option->getShortcut() ? '-'.str_replace('|', '|-', $option->getShortcut()) : '', + 'accept_value' => $option->acceptValue(), + 'is_value_required' => $option->isValueRequired(), + 'is_multiple' => $option->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()), + 'default' => \INF === $option->getDefault() ? 'INF' : $option->getDefault(), + ]; + } + + private function getInputDefinitionData(InputDefinition $definition): array + { + $inputArguments = []; + foreach ($definition->getArguments() as $name => $argument) { + $inputArguments[$name] = $this->getInputArgumentData($argument); + } + + $inputOptions = []; + foreach ($definition->getOptions() as $name => $option) { + $inputOptions[$name] = $this->getInputOptionData($option); + if ($option->isNegatable()) { + $inputOptions['no-'.$name] = $this->getInputOptionData($option, true); + } + } + + return ['arguments' => $inputArguments, 'options' => $inputOptions]; + } + + private function getCommandData(Command $command, bool $short = false): array + { + $data = [ + 'name' => $command->getName(), + 'description' => $command->getDescription(), + ]; + + if ($short) { + $data += [ + 'usage' => $command->getAliases(), + ]; + } else { + $command->mergeApplicationDefinition(false); + + $data += [ + 'usage' => array_merge([$command->getSynopsis()], $command->getUsages(), $command->getAliases()), + 'help' => $command->getProcessedHelp(), + 'definition' => $this->getInputDefinitionData($command->getDefinition()), + ]; + } + + $data['hidden'] = $command->isHidden(); + + return $data; + } +} diff --git a/vendor/symfony/console/Descriptor/MarkdownDescriptor.php b/vendor/symfony/console/Descriptor/MarkdownDescriptor.php new file mode 100644 index 0000000..21ceca6 --- /dev/null +++ b/vendor/symfony/console/Descriptor/MarkdownDescriptor.php @@ -0,0 +1,206 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Markdown descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class MarkdownDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + public function describe(OutputInterface $output, object $object, array $options = []) + { + $decorated = $output->isDecorated(); + $output->setDecorated(false); + + parent::describe($output, $object, $options); + + $output->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + protected function write(string $content, bool $decorated = true) + { + parent::write($content, $decorated); + } + + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = []) + { + $this->write( + '#### `'.($argument->getName() ?: '')."`\n\n" + .($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '') + .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n" + .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n" + .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = []) + { + $name = '--'.$option->getName(); + if ($option->isNegatable()) { + $name .= '|--no-'.$option->getName(); + } + if ($option->getShortcut()) { + $name .= '|-'.str_replace('|', '|-', $option->getShortcut()).''; + } + + $this->write( + '#### `'.$name.'`'."\n\n" + .($option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $option->getDescription())."\n\n" : '') + .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n" + .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n" + .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n" + .'* Is negatable: '.($option->isNegatable() ? 'yes' : 'no')."\n" + .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = []) + { + if ($showArguments = \count($definition->getArguments()) > 0) { + $this->write('### Arguments'); + foreach ($definition->getArguments() as $argument) { + $this->write("\n\n"); + if (null !== $describeInputArgument = $this->describeInputArgument($argument)) { + $this->write($describeInputArgument); + } + } + } + + if (\count($definition->getOptions()) > 0) { + if ($showArguments) { + $this->write("\n\n"); + } + + $this->write('### Options'); + foreach ($definition->getOptions() as $option) { + $this->write("\n\n"); + if (null !== $describeInputOption = $this->describeInputOption($option)) { + $this->write($describeInputOption); + } + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = []) + { + if ($options['short'] ?? false) { + $this->write( + '`'.$command->getName()."`\n" + .str_repeat('-', Helper::width($command->getName()) + 2)."\n\n" + .($command->getDescription() ? $command->getDescription()."\n\n" : '') + .'### Usage'."\n\n" + .array_reduce($command->getAliases(), function ($carry, $usage) { + return $carry.'* `'.$usage.'`'."\n"; + }) + ); + + return; + } + + $command->mergeApplicationDefinition(false); + + $this->write( + '`'.$command->getName()."`\n" + .str_repeat('-', Helper::width($command->getName()) + 2)."\n\n" + .($command->getDescription() ? $command->getDescription()."\n\n" : '') + .'### Usage'."\n\n" + .array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), function ($carry, $usage) { + return $carry.'* `'.$usage.'`'."\n"; + }) + ); + + if ($help = $command->getProcessedHelp()) { + $this->write("\n"); + $this->write($help); + } + + $definition = $command->getDefinition(); + if ($definition->getOptions() || $definition->getArguments()) { + $this->write("\n\n"); + $this->describeInputDefinition($definition); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = []) + { + $describedNamespace = $options['namespace'] ?? null; + $description = new ApplicationDescription($application, $describedNamespace); + $title = $this->getApplicationTitle($application); + + $this->write($title."\n".str_repeat('=', Helper::width($title))); + + foreach ($description->getNamespaces() as $namespace) { + if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->write("\n\n"); + $this->write('**'.$namespace['id'].':**'); + } + + $this->write("\n\n"); + $this->write(implode("\n", array_map(function ($commandName) use ($description) { + return sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName())); + }, $namespace['commands']))); + } + + foreach ($description->getCommands() as $command) { + $this->write("\n\n"); + if (null !== $describeCommand = $this->describeCommand($command, $options)) { + $this->write($describeCommand); + } + } + } + + private function getApplicationTitle(Application $application): string + { + if ('UNKNOWN' !== $application->getName()) { + if ('UNKNOWN' !== $application->getVersion()) { + return sprintf('%s %s', $application->getName(), $application->getVersion()); + } + + return $application->getName(); + } + + return 'Console Tool'; + } +} diff --git a/vendor/symfony/console/Descriptor/TextDescriptor.php b/vendor/symfony/console/Descriptor/TextDescriptor.php new file mode 100644 index 0000000..3f309f5 --- /dev/null +++ b/vendor/symfony/console/Descriptor/TextDescriptor.php @@ -0,0 +1,339 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Text descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class TextDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = []) + { + if (null !== $argument->getDefault() && (!\is_array($argument->getDefault()) || \count($argument->getDefault()))) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($argument->getDefault())); + } else { + $default = ''; + } + + $totalWidth = $options['total_width'] ?? Helper::width($argument->getName()); + $spacingWidth = $totalWidth - \strlen($argument->getName()); + + $this->writeText(sprintf(' %s %s%s%s', + $argument->getName(), + str_repeat(' ', $spacingWidth), + // + 4 = 2 spaces before , 2 spaces after + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $argument->getDescription()), + $default + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = []) + { + if ($option->acceptValue() && null !== $option->getDefault() && (!\is_array($option->getDefault()) || \count($option->getDefault()))) { + $default = sprintf(' [default: %s]', $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($option->isNegatable() ? '--%1$s|--no-%1$s' : '--%1$s%2$s', $option->getName(), $value) + ); + + $spacingWidth = $totalWidth - Helper::width($synopsis); + + $this->writeText(sprintf(' %s %s%s%s%s', + $synopsis, + str_repeat(' ', $spacingWidth), + // + 4 = 2 spaces before , 2 spaces after + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $option->getDescription()), + $default, + $option->isArray() ? ' (multiple values allowed)' : '' + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = []) + { + $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions()); + foreach ($definition->getArguments() as $argument) { + $totalWidth = max($totalWidth, Helper::width($argument->getName())); + } + + if ($definition->getArguments()) { + $this->writeText('Arguments:', $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('Options:', $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])); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = []) + { + $command->mergeApplicationDefinition(false); + + if ($description = $command->getDescription()) { + $this->writeText('Description:', $options); + $this->writeText("\n"); + $this->writeText(' '.$description); + $this->writeText("\n\n"); + } + + $this->writeText('Usage:', $options); + foreach (array_merge([$command->getSynopsis(true)], $command->getAliases(), $command->getUsages()) as $usage) { + $this->writeText("\n"); + $this->writeText(' '.OutputFormatter::escape($usage), $options); + } + $this->writeText("\n"); + + $definition = $command->getDefinition(); + if ($definition->getOptions() || $definition->getArguments()) { + $this->writeText("\n"); + $this->describeInputDefinition($definition, $options); + $this->writeText("\n"); + } + + $help = $command->getProcessedHelp(); + if ($help && $help !== $description) { + $this->writeText("\n"); + $this->writeText('Help:', $options); + $this->writeText("\n"); + $this->writeText(' '.str_replace("\n", "\n ", $help), $options); + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = []) + { + $describedNamespace = $options['namespace'] ?? null; + $description = new ApplicationDescription($application, $describedNamespace); + + if (isset($options['raw_text']) && $options['raw_text']) { + $width = $this->getColumnWidth($description->getCommands()); + + foreach ($description->getCommands() as $command) { + $this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options); + $this->writeText("\n"); + } + } else { + if ('' != $help = $application->getHelp()) { + $this->writeText("$help\n\n", $options); + } + + $this->writeText("Usage:\n", $options); + $this->writeText(" command [options] [arguments]\n\n", $options); + + $this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options); + + $this->writeText("\n"); + $this->writeText("\n"); + + $commands = $description->getCommands(); + $namespaces = $description->getNamespaces(); + if ($describedNamespace && $namespaces) { + // make sure all alias commands are included when describing a specific namespace + $describedNamespaceInfo = reset($namespaces); + foreach ($describedNamespaceInfo['commands'] as $name) { + $commands[$name] = $description->getCommand($name); + } + } + + // calculate max. width based on available commands per namespace + $width = $this->getColumnWidth(array_merge(...array_values(array_map(function ($namespace) use ($commands) { + return array_intersect($namespace['commands'], array_keys($commands)); + }, array_values($namespaces))))); + + if ($describedNamespace) { + $this->writeText(sprintf('Available commands for the "%s" namespace:', $describedNamespace), $options); + } else { + $this->writeText('Available commands:', $options); + } + + foreach ($namespaces as $namespace) { + $namespace['commands'] = array_filter($namespace['commands'], function ($name) use ($commands) { + return isset($commands[$name]); + }); + + if (!$namespace['commands']) { + continue; + } + + if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->writeText("\n"); + $this->writeText(' '.$namespace['id'].'', $options); + } + + foreach ($namespace['commands'] as $name) { + $this->writeText("\n"); + $spacingWidth = $width - Helper::width($name); + $command = $commands[$name]; + $commandAliases = $name === $command->getName() ? $this->getCommandAliasesText($command) : ''; + $this->writeText(sprintf(' %s%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options); + } + } + + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + private function writeText(string $content, array $options = []) + { + $this->write( + isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, + isset($options['raw_output']) ? !$options['raw_output'] : true + ); + } + + /** + * Formats command aliases to show them in the command description. + */ + private function getCommandAliasesText(Command $command): string + { + $text = ''; + $aliases = $command->getAliases(); + + if ($aliases) { + $text = '['.implode('|', $aliases).'] '; + } + + return $text; + } + + /** + * Formats input option/argument default value. + */ + private function formatDefaultValue(mixed $default): string + { + if (\INF === $default) { + return 'INF'; + } + + if (\is_string($default)) { + $default = OutputFormatter::escape($default); + } elseif (\is_array($default)) { + foreach ($default as $key => $value) { + if (\is_string($value)) { + $default[$key] = OutputFormatter::escape($value); + } + } + } + + return str_replace('\\\\', '\\', json_encode($default, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE)); + } + + /** + * @param array $commands + */ + private function getColumnWidth(array $commands): int + { + $widths = []; + + foreach ($commands as $command) { + if ($command instanceof Command) { + $widths[] = Helper::width($command->getName()); + foreach ($command->getAliases() as $alias) { + $widths[] = Helper::width($alias); + } + } else { + $widths[] = Helper::width($command); + } + } + + return $widths ? max($widths) + 2 : 0; + } + + /** + * @param InputOption[] $options + */ + private function calculateTotalWidthForOptions(array $options): int + { + $totalWidth = 0; + foreach ($options as $option) { + // "-" + shortcut + ", --" + name + $nameLength = 1 + max(Helper::width($option->getShortcut()), 1) + 4 + Helper::width($option->getName()); + if ($option->isNegatable()) { + $nameLength += 6 + Helper::width($option->getName()); // |--no- + name + } elseif ($option->acceptValue()) { + $valueLength = 1 + Helper::width($option->getName()); // = + value + $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ] + + $nameLength += $valueLength; + } + $totalWidth = max($totalWidth, $nameLength); + } + + return $totalWidth; + } +} diff --git a/vendor/symfony/console/Descriptor/XmlDescriptor.php b/vendor/symfony/console/Descriptor/XmlDescriptor.php new file mode 100644 index 0000000..4f7cd8b --- /dev/null +++ b/vendor/symfony/console/Descriptor/XmlDescriptor.php @@ -0,0 +1,247 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * XML descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class XmlDescriptor extends Descriptor +{ + public function getInputDefinitionDocument(InputDefinition $definition): \DOMDocument + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($definitionXML = $dom->createElement('definition')); + + $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments')); + foreach ($definition->getArguments() as $argument) { + $this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument)); + } + + $definitionXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($definition->getOptions() as $option) { + $this->appendDocument($optionsXML, $this->getInputOptionDocument($option)); + } + + return $dom; + } + + public function getCommandDocument(Command $command, bool $short = false): \DOMDocument + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($commandXML = $dom->createElement('command')); + + $commandXML->setAttribute('id', $command->getName()); + $commandXML->setAttribute('name', $command->getName()); + $commandXML->setAttribute('hidden', $command->isHidden() ? 1 : 0); + + $commandXML->appendChild($usagesXML = $dom->createElement('usages')); + + $commandXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription()))); + + if ($short) { + foreach ($command->getAliases() as $usage) { + $usagesXML->appendChild($dom->createElement('usage', $usage)); + } + } else { + $command->mergeApplicationDefinition(false); + + foreach (array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()) as $usage) { + $usagesXML->appendChild($dom->createElement('usage', $usage)); + } + + $commandXML->appendChild($helpXML = $dom->createElement('help')); + $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp()))); + + $definitionXML = $this->getInputDefinitionDocument($command->getDefinition()); + $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0)); + } + + return $dom; + } + + public function getApplicationDocument(Application $application, string $namespace = null, bool $short = false): \DOMDocument + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($rootXml = $dom->createElement('symfony')); + + if ('UNKNOWN' !== $application->getName()) { + $rootXml->setAttribute('name', $application->getName()); + if ('UNKNOWN' !== $application->getVersion()) { + $rootXml->setAttribute('version', $application->getVersion()); + } + } + + $rootXml->appendChild($commandsXML = $dom->createElement('commands')); + + $description = new ApplicationDescription($application, $namespace, true); + + if ($namespace) { + $commandsXML->setAttribute('namespace', $namespace); + } + + foreach ($description->getCommands() as $command) { + $this->appendDocument($commandsXML, $this->getCommandDocument($command, $short)); + } + + if (!$namespace) { + $rootXml->appendChild($namespacesXML = $dom->createElement('namespaces')); + + foreach ($description->getNamespaces() as $namespaceDescription) { + $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace')); + $namespaceArrayXML->setAttribute('id', $namespaceDescription['id']); + + foreach ($namespaceDescription['commands'] as $name) { + $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command')); + $commandXML->appendChild($dom->createTextNode($name)); + } + } + } + + return $dom; + } + + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = []) + { + $this->writeDocument($this->getInputArgumentDocument($argument)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = []) + { + $this->writeDocument($this->getInputOptionDocument($option)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = []) + { + $this->writeDocument($this->getInputDefinitionDocument($definition)); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = []) + { + $this->writeDocument($this->getCommandDocument($command, $options['short'] ?? false)); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = []) + { + $this->writeDocument($this->getApplicationDocument($application, $options['namespace'] ?? null, $options['short'] ?? false)); + } + + /** + * Appends document children to parent node. + */ + private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent) + { + foreach ($importedParent->childNodes as $childNode) { + $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true)); + } + } + + /** + * Writes DOM document. + */ + private function writeDocument(\DOMDocument $dom) + { + $dom->formatOutput = true; + $this->write($dom->saveXML()); + } + + private function getInputArgumentDocument(InputArgument $argument): \DOMDocument + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('argument')); + $objectXML->setAttribute('name', $argument->getName()); + $objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0); + $objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); + + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = \is_array($argument->getDefault()) ? $argument->getDefault() : (\is_bool($argument->getDefault()) ? [var_export($argument->getDefault(), true)] : ($argument->getDefault() ? [$argument->getDefault()] : [])); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + + return $dom; + } + + private function getInputOptionDocument(InputOption $option): \DOMDocument + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('option')); + $objectXML->setAttribute('name', '--'.$option->getName()); + $pos = strpos($option->getShortcut() ?? '', '|'); + if (false !== $pos) { + $objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos)); + $objectXML->setAttribute('shortcuts', '-'.str_replace('|', '|-', $option->getShortcut())); + } else { + $objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); + } + $objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0); + $objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0); + $objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); + + if ($option->acceptValue()) { + $defaults = \is_array($option->getDefault()) ? $option->getDefault() : (\is_bool($option->getDefault()) ? [var_export($option->getDefault(), true)] : ($option->getDefault() ? [$option->getDefault()] : [])); + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + + if (!empty($defaults)) { + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + } + + if ($option->isNegatable()) { + $dom->appendChild($objectXML = $dom->createElement('option')); + $objectXML->setAttribute('name', '--no-'.$option->getName()); + $objectXML->setAttribute('shortcut', ''); + $objectXML->setAttribute('accept_value', 0); + $objectXML->setAttribute('is_value_required', 0); + $objectXML->setAttribute('is_multiple', 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode('Negate the "--'.$option->getName().'" option')); + } + + return $dom; + } +} diff --git a/vendor/symfony/console/Event/ConsoleCommandEvent.php b/vendor/symfony/console/Event/ConsoleCommandEvent.php new file mode 100644 index 0000000..31c9ee9 --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleCommandEvent.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +/** + * Allows to do things before the command is executed, like skipping the command or changing the input. + * + * @author Fabien Potencier + */ +final class ConsoleCommandEvent extends ConsoleEvent +{ + /** + * The return code for skipped commands, this will also be passed into the terminate event. + */ + public const RETURN_CODE_DISABLED = 113; + + /** + * Indicates if the command should be run or skipped. + */ + private bool $commandShouldRun = true; + + /** + * Disables the command, so it won't be run. + */ + public function disableCommand(): bool + { + return $this->commandShouldRun = false; + } + + public function enableCommand(): bool + { + return $this->commandShouldRun = true; + } + + /** + * Returns true if the command is runnable, false otherwise. + */ + public function commandShouldRun(): bool + { + return $this->commandShouldRun; + } +} diff --git a/vendor/symfony/console/Event/ConsoleErrorEvent.php b/vendor/symfony/console/Event/ConsoleErrorEvent.php new file mode 100644 index 0000000..19bd4bf --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleErrorEvent.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to handle throwables thrown while running a command. + * + * @author Wouter de Jong + */ +final class ConsoleErrorEvent extends ConsoleEvent +{ + private \Throwable $error; + private int $exitCode; + + public function __construct(InputInterface $input, OutputInterface $output, \Throwable $error, Command $command = null) + { + parent::__construct($command, $input, $output); + + $this->error = $error; + } + + public function getError(): \Throwable + { + return $this->error; + } + + public function setError(\Throwable $error): void + { + $this->error = $error; + } + + public function setExitCode(int $exitCode): void + { + $this->exitCode = $exitCode; + + $r = new \ReflectionProperty($this->error, 'code'); + $r->setAccessible(true); + $r->setValue($this->error, $this->exitCode); + } + + public function getExitCode(): int + { + return $this->exitCode ?? (\is_int($this->error->getCode()) && 0 !== $this->error->getCode() ? $this->error->getCode() : 1); + } +} diff --git a/vendor/symfony/console/Event/ConsoleEvent.php b/vendor/symfony/console/Event/ConsoleEvent.php new file mode 100644 index 0000000..56b8a9a --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleEvent.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Contracts\EventDispatcher\Event; + +/** + * Allows to inspect input and output of a command. + * + * @author Francesco Levorato + */ +class ConsoleEvent extends Event +{ + protected $command; + + private $input; + private $output; + + public function __construct(?Command $command, InputInterface $input, OutputInterface $output) + { + $this->command = $command; + $this->input = $input; + $this->output = $output; + } + + /** + * Gets the command that is executed. + */ + public function getCommand(): ?Command + { + return $this->command; + } + + /** + * Gets the input instance. + */ + public function getInput(): InputInterface + { + return $this->input; + } + + /** + * Gets the output instance. + */ + public function getOutput(): OutputInterface + { + return $this->output; + } +} diff --git a/vendor/symfony/console/Event/ConsoleSignalEvent.php b/vendor/symfony/console/Event/ConsoleSignalEvent.php new file mode 100644 index 0000000..766af69 --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleSignalEvent.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author marie + */ +final class ConsoleSignalEvent extends ConsoleEvent +{ + private int $handlingSignal; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $handlingSignal) + { + parent::__construct($command, $input, $output); + $this->handlingSignal = $handlingSignal; + } + + public function getHandlingSignal(): int + { + return $this->handlingSignal; + } +} diff --git a/vendor/symfony/console/Event/ConsoleTerminateEvent.php b/vendor/symfony/console/Event/ConsoleTerminateEvent.php new file mode 100644 index 0000000..de63c8f --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleTerminateEvent.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to manipulate the exit code of a command after its execution. + * + * @author Francesco Levorato + */ +final class ConsoleTerminateEvent extends ConsoleEvent +{ + private int $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setExitCode($exitCode); + } + + public function setExitCode(int $exitCode): void + { + $this->exitCode = $exitCode; + } + + public function getExitCode(): int + { + return $this->exitCode; + } +} diff --git a/vendor/symfony/console/EventListener/ErrorListener.php b/vendor/symfony/console/EventListener/ErrorListener.php new file mode 100644 index 0000000..61bd9d3 --- /dev/null +++ b/vendor/symfony/console/EventListener/ErrorListener.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleErrorEvent; +use Symfony\Component\Console\Event\ConsoleEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @author James Halsall + * @author Robin Chalas + */ +class ErrorListener implements EventSubscriberInterface +{ + private $logger; + + public function __construct(LoggerInterface $logger = null) + { + $this->logger = $logger; + } + + public function onConsoleError(ConsoleErrorEvent $event) + { + if (null === $this->logger) { + return; + } + + $error = $event->getError(); + + if (!$inputString = $this->getInputString($event)) { + $this->logger->critical('An error occurred while using the console. Message: "{message}"', ['exception' => $error, 'message' => $error->getMessage()]); + + return; + } + + $this->logger->critical('Error thrown while running command "{command}". Message: "{message}"', ['exception' => $error, 'command' => $inputString, 'message' => $error->getMessage()]); + } + + public function onConsoleTerminate(ConsoleTerminateEvent $event) + { + if (null === $this->logger) { + return; + } + + $exitCode = $event->getExitCode(); + + if (0 === $exitCode) { + return; + } + + if (!$inputString = $this->getInputString($event)) { + $this->logger->debug('The console exited with code "{code}"', ['code' => $exitCode]); + + return; + } + + $this->logger->debug('Command "{command}" exited with code "{code}"', ['command' => $inputString, 'code' => $exitCode]); + } + + public static function getSubscribedEvents(): array + { + return [ + ConsoleEvents::ERROR => ['onConsoleError', -128], + ConsoleEvents::TERMINATE => ['onConsoleTerminate', -128], + ]; + } + + private static function getInputString(ConsoleEvent $event): ?string + { + $commandName = $event->getCommand() ? $event->getCommand()->getName() : null; + $input = $event->getInput(); + + if ($input instanceof \Stringable) { + if ($commandName) { + return str_replace(["'$commandName'", "\"$commandName\""], $commandName, (string) $input); + } + + return (string) $input; + } + + return $commandName; + } +} diff --git a/vendor/symfony/console/Exception/CommandNotFoundException.php b/vendor/symfony/console/Exception/CommandNotFoundException.php new file mode 100644 index 0000000..1e9f1c7 --- /dev/null +++ b/vendor/symfony/console/Exception/CommandNotFoundException.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect command name typed in the console. + * + * @author Jérôme Tamarelle + */ +class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface +{ + private array $alternatives; + + /** + * @param string $message Exception message to throw + * @param string[] $alternatives List of similar defined names + * @param int $code Exception code + * @param \Throwable|null $previous Previous exception used for the exception chaining + */ + public function __construct(string $message, array $alternatives = [], int $code = 0, \Throwable $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->alternatives = $alternatives; + } + + /** + * @return string[] + */ + public function getAlternatives(): array + { + return $this->alternatives; + } +} diff --git a/vendor/symfony/console/Exception/ExceptionInterface.php b/vendor/symfony/console/Exception/ExceptionInterface.php new file mode 100644 index 0000000..1624e13 --- /dev/null +++ b/vendor/symfony/console/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * ExceptionInterface. + * + * @author Jérôme Tamarelle + */ +interface ExceptionInterface extends \Throwable +{ +} diff --git a/vendor/symfony/console/Exception/InvalidArgumentException.php b/vendor/symfony/console/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..07cc0b6 --- /dev/null +++ b/vendor/symfony/console/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/InvalidOptionException.php b/vendor/symfony/console/Exception/InvalidOptionException.php new file mode 100644 index 0000000..5cf6279 --- /dev/null +++ b/vendor/symfony/console/Exception/InvalidOptionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect option name or value typed in the console. + * + * @author Jérôme Tamarelle + */ +class InvalidOptionException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/LogicException.php b/vendor/symfony/console/Exception/LogicException.php new file mode 100644 index 0000000..fc37b8d --- /dev/null +++ b/vendor/symfony/console/Exception/LogicException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/MissingInputException.php b/vendor/symfony/console/Exception/MissingInputException.php new file mode 100644 index 0000000..04f02ad --- /dev/null +++ b/vendor/symfony/console/Exception/MissingInputException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents failure to read input from stdin. + * + * @author Gabriel Ostrolucký + */ +class MissingInputException extends RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/NamespaceNotFoundException.php b/vendor/symfony/console/Exception/NamespaceNotFoundException.php new file mode 100644 index 0000000..dd16e45 --- /dev/null +++ b/vendor/symfony/console/Exception/NamespaceNotFoundException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect namespace typed in the console. + * + * @author Pierre du Plessis + */ +class NamespaceNotFoundException extends CommandNotFoundException +{ +} diff --git a/vendor/symfony/console/Exception/RuntimeException.php b/vendor/symfony/console/Exception/RuntimeException.php new file mode 100644 index 0000000..51d7d80 --- /dev/null +++ b/vendor/symfony/console/Exception/RuntimeException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Formatter/NullOutputFormatter.php b/vendor/symfony/console/Formatter/NullOutputFormatter.php new file mode 100644 index 0000000..d770e14 --- /dev/null +++ b/vendor/symfony/console/Formatter/NullOutputFormatter.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * @author Tien Xuan Vo + */ +final class NullOutputFormatter implements OutputFormatterInterface +{ + private $style; + + /** + * {@inheritdoc} + */ + public function format(?string $message): ?string + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getStyle(string $name): OutputFormatterStyleInterface + { + // to comply with the interface we must return a OutputFormatterStyleInterface + return $this->style ?? $this->style = new NullOutputFormatterStyle(); + } + + /** + * {@inheritdoc} + */ + public function hasStyle(string $name): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isDecorated(): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function setDecorated(bool $decorated): void + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function setStyle(string $name, OutputFormatterStyleInterface $style): void + { + // do nothing + } +} diff --git a/vendor/symfony/console/Formatter/NullOutputFormatterStyle.php b/vendor/symfony/console/Formatter/NullOutputFormatterStyle.php new file mode 100644 index 0000000..9232510 --- /dev/null +++ b/vendor/symfony/console/Formatter/NullOutputFormatterStyle.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * @author Tien Xuan Vo + */ +final class NullOutputFormatterStyle implements OutputFormatterStyleInterface +{ + /** + * {@inheritdoc} + */ + public function apply(string $text): string + { + return $text; + } + + /** + * {@inheritdoc} + */ + public function setBackground(string $color = null): void + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function setForeground(string $color = null): void + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function setOption(string $option): void + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function setOptions(array $options): void + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function unsetOption(string $option): void + { + // do nothing + } +} diff --git a/vendor/symfony/console/Formatter/OutputFormatter.php b/vendor/symfony/console/Formatter/OutputFormatter.php new file mode 100644 index 0000000..4a6ae91 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatter.php @@ -0,0 +1,280 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter class for console output. + * + * @author Konstantin Kudryashov + * @author Roland Franssen + */ +class OutputFormatter implements WrappableOutputFormatterInterface +{ + private bool $decorated; + private array $styles = []; + private $styleStack; + + public function __clone() + { + $this->styleStack = clone $this->styleStack; + foreach ($this->styles as $key => $value) { + $this->styles[$key] = clone $value; + } + } + + /** + * Escapes "<" and ">" special chars in given text. + */ + public static function escape(string $text): string + { + $text = preg_replace('/([^\\\\]|^)([<>])/', '$1\\\\$2', $text); + + return self::escapeTrailingBackslash($text); + } + + /** + * Escapes trailing "\" in given text. + * + * @internal + */ + public static function escapeTrailingBackslash(string $text): string + { + if (str_ends_with($text, '\\')) { + $len = \strlen($text); + $text = rtrim($text, '\\'); + $text = str_replace("\0", '', $text); + $text .= str_repeat("\0", $len - \strlen($text)); + } + + return $text; + } + + /** + * Initializes console output formatter. + * + * @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances + */ + public function __construct(bool $decorated = false, array $styles = []) + { + $this->decorated = $decorated; + + $this->setStyle('error', new OutputFormatterStyle('white', 'red')); + $this->setStyle('info', new OutputFormatterStyle('green')); + $this->setStyle('comment', new OutputFormatterStyle('yellow')); + $this->setStyle('question', new OutputFormatterStyle('black', 'cyan')); + + foreach ($styles as $name => $style) { + $this->setStyle($name, $style); + } + + $this->styleStack = new OutputFormatterStyleStack(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated(bool $decorated) + { + $this->decorated = $decorated; + } + + /** + * {@inheritdoc} + */ + public function isDecorated(): bool + { + return $this->decorated; + } + + /** + * {@inheritdoc} + */ + public function setStyle(string $name, OutputFormatterStyleInterface $style) + { + $this->styles[strtolower($name)] = $style; + } + + /** + * {@inheritdoc} + */ + public function hasStyle(string $name): bool + { + return isset($this->styles[strtolower($name)]); + } + + /** + * {@inheritdoc} + */ + public function getStyle(string $name): OutputFormatterStyleInterface + { + if (!$this->hasStyle($name)) { + throw new InvalidArgumentException(sprintf('Undefined style: "%s".', $name)); + } + + return $this->styles[strtolower($name)]; + } + + /** + * {@inheritdoc} + */ + public function format(?string $message): ?string + { + return $this->formatAndWrap($message, 0); + } + + /** + * {@inheritdoc} + */ + public function formatAndWrap(?string $message, int $width) + { + if (null === $message) { + return ''; + } + + $offset = 0; + $output = ''; + $openTagRegex = '[a-z](?:[^\\\\<>]*+ | \\\\.)*'; + $closeTagRegex = '[a-z][^<>]*+'; + $currentLineLength = 0; + preg_match_all("#<(($openTagRegex) | /($closeTagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $i => $match) { + $pos = $match[1]; + $text = $match[0]; + + if (0 != $pos && '\\' == $message[$pos - 1]) { + continue; + } + + // add the text up to the next tag + $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength); + $offset = $pos + \strlen($text); + + // opening tag? + if ($open = '/' != $text[1]) { + $tag = $matches[1][$i][0]; + } else { + $tag = $matches[3][$i][0] ?? ''; + } + + if (!$open && !$tag) { + // + $this->styleStack->pop(); + } elseif (null === $style = $this->createStyleFromString($tag)) { + $output .= $this->applyCurrentStyle($text, $output, $width, $currentLineLength); + } elseif ($open) { + $this->styleStack->push($style); + } else { + $this->styleStack->pop($style); + } + } + + $output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength); + + return strtr($output, ["\0" => '\\', '\\<' => '<', '\\>' => '>']); + } + + public function getStyleStack(): OutputFormatterStyleStack + { + return $this->styleStack; + } + + /** + * Tries to create new style instance from string. + */ + private function createStyleFromString(string $string): ?OutputFormatterStyleInterface + { + if (isset($this->styles[$string])) { + return $this->styles[$string]; + } + + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, \PREG_SET_ORDER)) { + return null; + } + + $style = new OutputFormatterStyle(); + foreach ($matches as $match) { + array_shift($match); + $match[0] = strtolower($match[0]); + + if ('fg' == $match[0]) { + $style->setForeground(strtolower($match[1])); + } elseif ('bg' == $match[0]) { + $style->setBackground(strtolower($match[1])); + } elseif ('href' === $match[0]) { + $url = preg_replace('{\\\\([<>])}', '$1', $match[1]); + $style->setHref($url); + } elseif ('options' === $match[0]) { + preg_match_all('([^,;]+)', strtolower($match[1]), $options); + $options = array_shift($options); + foreach ($options as $option) { + $style->setOption($option); + } + } else { + return null; + } + } + + return $style; + } + + /** + * Applies current style from stack to text, if must be applied. + */ + private function applyCurrentStyle(string $text, string $current, int $width, int &$currentLineLength): string + { + if ('' === $text) { + return ''; + } + + if (!$width) { + return $this->isDecorated() ? $this->styleStack->getCurrent()->apply($text) : $text; + } + + if (!$currentLineLength && '' !== $current) { + $text = ltrim($text); + } + + if ($currentLineLength) { + $prefix = substr($text, 0, $i = $width - $currentLineLength)."\n"; + $text = substr($text, $i); + } else { + $prefix = ''; + } + + preg_match('~(\\n)$~', $text, $matches); + $text = $prefix.preg_replace('~([^\\n]{'.$width.'})\\ *~', "\$1\n", $text); + $text = rtrim($text, "\n").($matches[1] ?? ''); + + if (!$currentLineLength && '' !== $current && "\n" !== substr($current, -1)) { + $text = "\n".$text; + } + + $lines = explode("\n", $text); + + foreach ($lines as $line) { + $currentLineLength += \strlen($line); + if ($width <= $currentLineLength) { + $currentLineLength = 0; + } + } + + if ($this->isDecorated()) { + foreach ($lines as $i => $line) { + $lines[$i] = $this->styleStack->getCurrent()->apply($line); + } + } + + return implode("\n", $lines); + } +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterInterface.php b/vendor/symfony/console/Formatter/OutputFormatterInterface.php new file mode 100644 index 0000000..b94e51d --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterInterface.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter interface for console output. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterInterface +{ + /** + * Sets the decorated flag. + */ + public function setDecorated(bool $decorated); + + /** + * Whether the output will decorate messages. + */ + public function isDecorated(): bool; + + /** + * Sets a new style. + */ + public function setStyle(string $name, OutputFormatterStyleInterface $style); + + /** + * Checks if output formatter has style with specified name. + */ + public function hasStyle(string $name): bool; + + /** + * Gets style options from style with specified name. + * + * @throws \InvalidArgumentException When style isn't defined + */ + public function getStyle(string $name): OutputFormatterStyleInterface; + + /** + * Formats a message according to the given styles. + */ + public function format(?string $message): ?string; +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyle.php b/vendor/symfony/console/Formatter/OutputFormatterStyle.php new file mode 100644 index 0000000..0a009e9 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyle.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Color; + +/** + * Formatter style class for defining styles. + * + * @author Konstantin Kudryashov + */ +class OutputFormatterStyle implements OutputFormatterStyleInterface +{ + private $color; + private string $foreground; + private string $background; + private array $options; + private ?string $href = null; + private bool $handlesHrefGracefully; + + /** + * Initializes output formatter style. + * + * @param string|null $foreground The style foreground color name + * @param string|null $background The style background color name + */ + public function __construct(string $foreground = null, string $background = null, array $options = []) + { + $this->color = new Color($this->foreground = $foreground ?: '', $this->background = $background ?: '', $this->options = $options); + } + + /** + * {@inheritdoc} + */ + public function setForeground(string $color = null) + { + $this->color = new Color($this->foreground = $color ?: '', $this->background, $this->options); + } + + /** + * {@inheritdoc} + */ + public function setBackground(string $color = null) + { + $this->color = new Color($this->foreground, $this->background = $color ?: '', $this->options); + } + + public function setHref(string $url): void + { + $this->href = $url; + } + + /** + * {@inheritdoc} + */ + public function setOption(string $option) + { + $this->options[] = $option; + $this->color = new Color($this->foreground, $this->background, $this->options); + } + + /** + * {@inheritdoc} + */ + public function unsetOption(string $option) + { + $pos = array_search($option, $this->options); + if (false !== $pos) { + unset($this->options[$pos]); + } + + $this->color = new Color($this->foreground, $this->background, $this->options); + } + + /** + * {@inheritdoc} + */ + public function setOptions(array $options) + { + $this->color = new Color($this->foreground, $this->background, $this->options = $options); + } + + /** + * {@inheritdoc} + */ + public function apply(string $text): string + { + $this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') + && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100); + + if (null !== $this->href && $this->handlesHrefGracefully) { + $text = "\033]8;;$this->href\033\\$text\033]8;;\033\\"; + } + + return $this->color->apply($text); + } +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php new file mode 100644 index 0000000..91d50aa --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter style interface for defining styles. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterStyleInterface +{ + /** + * Sets style foreground color. + */ + public function setForeground(string $color = null); + + /** + * Sets style background color. + */ + public function setBackground(string $color = null); + + /** + * Sets some specific style option. + */ + public function setOption(string $option); + + /** + * Unsets some specific style option. + */ + public function unsetOption(string $option); + + /** + * Sets multiple style options at once. + */ + public function setOptions(array $options); + + /** + * Applies the style to a given text. + */ + public function apply(string $text): string; +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php new file mode 100644 index 0000000..66f86a5 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Contracts\Service\ResetInterface; + +/** + * @author Jean-François Simon + */ +class OutputFormatterStyleStack implements ResetInterface +{ + /** + * @var OutputFormatterStyleInterface[] + */ + private array $styles = []; + + private $emptyStyle; + + public function __construct(OutputFormatterStyleInterface $emptyStyle = null) + { + $this->emptyStyle = $emptyStyle ?? new OutputFormatterStyle(); + $this->reset(); + } + + /** + * Resets stack (ie. empty internal arrays). + */ + public function reset() + { + $this->styles = []; + } + + /** + * Pushes a style in the stack. + */ + public function push(OutputFormatterStyleInterface $style) + { + $this->styles[] = $style; + } + + /** + * Pops a style from the stack. + * + * @throws InvalidArgumentException When style tags incorrectly nested + */ + public function pop(OutputFormatterStyleInterface $style = null): OutputFormatterStyleInterface + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + if (null === $style) { + return array_pop($this->styles); + } + + 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.'); + } + + /** + * Computes current style with stacks top codes. + */ + public function getCurrent(): OutputFormatterStyleInterface + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + return $this->styles[\count($this->styles) - 1]; + } + + /** + * @return $this + */ + public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle): static + { + $this->emptyStyle = $emptyStyle; + + return $this; + } + + public function getEmptyStyle(): OutputFormatterStyleInterface + { + return $this->emptyStyle; + } +} diff --git a/vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php b/vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php new file mode 100644 index 0000000..42319ee --- /dev/null +++ b/vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter interface for console output that supports word wrapping. + * + * @author Roland Franssen + */ +interface WrappableOutputFormatterInterface extends OutputFormatterInterface +{ + /** + * Formats a message according to the given styles, wrapping at `$width` (0 means no wrapping). + */ + public function formatAndWrap(?string $message, int $width); +} diff --git a/vendor/symfony/console/Helper/DebugFormatterHelper.php b/vendor/symfony/console/Helper/DebugFormatterHelper.php new file mode 100644 index 0000000..64c7cff --- /dev/null +++ b/vendor/symfony/console/Helper/DebugFormatterHelper.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Helps outputting debug information when running an external program from a command. + * + * An external program can be a Process, an HTTP request, or anything else. + * + * @author Fabien Potencier + */ +class DebugFormatterHelper extends Helper +{ + private const COLORS = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default']; + private array $started = []; + private int $count = -1; + + /** + * Starts a debug formatting session. + */ + public function start(string $id, string $message, string $prefix = 'RUN'): string + { + $this->started[$id] = ['border' => ++$this->count % \count(self::COLORS)]; + + return sprintf("%s %s %s\n", $this->getBorder($id), $prefix, $message); + } + + /** + * Adds progress to a formatting session. + */ + public function progress(string $id, string $buffer, bool $error = false, string $prefix = 'OUT', string $errorPrefix = 'ERR'): string + { + $message = ''; + + if ($error) { + if (isset($this->started[$id]['out'])) { + $message .= "\n"; + unset($this->started[$id]['out']); + } + if (!isset($this->started[$id]['err'])) { + $message .= sprintf('%s %s ', $this->getBorder($id), $errorPrefix); + $this->started[$id]['err'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $errorPrefix), $buffer); + } else { + if (isset($this->started[$id]['err'])) { + $message .= "\n"; + unset($this->started[$id]['err']); + } + if (!isset($this->started[$id]['out'])) { + $message .= sprintf('%s %s ', $this->getBorder($id), $prefix); + $this->started[$id]['out'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $prefix), $buffer); + } + + return $message; + } + + /** + * Stops a formatting session. + */ + public function stop(string $id, string $message, bool $successful, string $prefix = 'RES'): string + { + $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : ''; + + if ($successful) { + return sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + } + + $message = sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + + unset($this->started[$id]['out'], $this->started[$id]['err']); + + return $message; + } + + private function getBorder(string $id): string + { + return sprintf(' ', self::COLORS[$this->started[$id]['border']]); + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return 'debug_formatter'; + } +} diff --git a/vendor/symfony/console/Helper/DescriptorHelper.php b/vendor/symfony/console/Helper/DescriptorHelper.php new file mode 100644 index 0000000..63597c6 --- /dev/null +++ b/vendor/symfony/console/Helper/DescriptorHelper.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Descriptor\DescriptorInterface; +use Symfony\Component\Console\Descriptor\JsonDescriptor; +use Symfony\Component\Console\Descriptor\MarkdownDescriptor; +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * This class adds helper method to describe objects in various formats. + * + * @author Jean-François Simon + */ +class DescriptorHelper extends Helper +{ + /** + * @var DescriptorInterface[] + */ + private array $descriptors = []; + + public function __construct() + { + $this + ->register('txt', new TextDescriptor()) + ->register('xml', new XmlDescriptor()) + ->register('json', new JsonDescriptor()) + ->register('md', new MarkdownDescriptor()) + ; + } + + /** + * Describes an object if supported. + * + * Available options are: + * * format: string, the output format name + * * raw_text: boolean, sets output type as raw + * + * @throws InvalidArgumentException when the given format is not supported + */ + public function describe(OutputInterface $output, ?object $object, array $options = []) + { + $options = array_merge([ + 'raw_text' => false, + 'format' => 'txt', + ], $options); + + if (!isset($this->descriptors[$options['format']])) { + throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format'])); + } + + $descriptor = $this->descriptors[$options['format']]; + $descriptor->describe($output, $object, $options); + } + + /** + * Registers a descriptor. + * + * @return $this + */ + public function register(string $format, DescriptorInterface $descriptor): static + { + $this->descriptors[$format] = $descriptor; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return 'descriptor'; + } + + public function getFormats(): array + { + return array_keys($this->descriptors); + } +} diff --git a/vendor/symfony/console/Helper/Dumper.php b/vendor/symfony/console/Helper/Dumper.php new file mode 100644 index 0000000..5019095 --- /dev/null +++ b/vendor/symfony/console/Helper/Dumper.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\CliDumper; + +/** + * @author Roland Franssen + */ +final class Dumper +{ + private $output; + private $dumper; + private $cloner; + private \Closure $handler; + + public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null) + { + $this->output = $output; + $this->dumper = $dumper; + $this->cloner = $cloner; + + if (class_exists(CliDumper::class)) { + $this->handler = function ($var): string { + $dumper = $this->dumper ?? $this->dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR); + $dumper->setColors($this->output->isDecorated()); + + return rtrim($dumper->dump(($this->cloner ?? $this->cloner = new VarCloner())->cloneVar($var)->withRefHandles(false), true)); + }; + } else { + $this->handler = function ($var): string { + switch (true) { + case null === $var: + return 'null'; + case true === $var: + return 'true'; + case false === $var: + return 'false'; + case \is_string($var): + return '"'.$var.'"'; + default: + return rtrim(print_r($var, true)); + } + }; + } + } + + public function __invoke(mixed $var): string + { + return ($this->handler)($var); + } +} diff --git a/vendor/symfony/console/Helper/FormatterHelper.php b/vendor/symfony/console/Helper/FormatterHelper.php new file mode 100644 index 0000000..2d7d1fa --- /dev/null +++ b/vendor/symfony/console/Helper/FormatterHelper.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * The Formatter class provides helpers to format messages. + * + * @author Fabien Potencier + */ +class FormatterHelper extends Helper +{ + /** + * Formats a message within a section. + */ + public function formatSection(string $section, string $message, string $style = 'info'): string + { + return sprintf('<%s>[%s] %s', $style, $section, $style, $message); + } + + /** + * Formats a message as a block of text. + */ + public function formatBlock(string|array $messages, string $style, bool $large = false): string + { + if (!\is_array($messages)) { + $messages = [$messages]; + } + + $len = 0; + $lines = []; + foreach ($messages as $message) { + $message = OutputFormatter::escape($message); + $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); + $len = max(self::width($message) + ($large ? 4 : 2), $len); + } + + $messages = $large ? [str_repeat(' ', $len)] : []; + for ($i = 0; isset($lines[$i]); ++$i) { + $messages[] = $lines[$i].str_repeat(' ', $len - self::width($lines[$i])); + } + if ($large) { + $messages[] = str_repeat(' ', $len); + } + + for ($i = 0; isset($messages[$i]); ++$i) { + $messages[$i] = sprintf('<%s>%s', $style, $messages[$i], $style); + } + + return implode("\n", $messages); + } + + /** + * Truncates a message to the given length. + */ + public function truncate(string $message, int $length, string $suffix = '...'): string + { + $computedLength = $length - self::width($suffix); + + if ($computedLength > self::width($message)) { + return $message; + } + + return self::substr($message, 0, $length).$suffix; + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return 'formatter'; + } +} diff --git a/vendor/symfony/console/Helper/Helper.php b/vendor/symfony/console/Helper/Helper.php new file mode 100644 index 0000000..c626729 --- /dev/null +++ b/vendor/symfony/console/Helper/Helper.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\String\UnicodeString; + +/** + * Helper is the base class for all helper classes. + * + * @author Fabien Potencier + */ +abstract class Helper implements HelperInterface +{ + protected $helperSet = null; + + /** + * {@inheritdoc} + */ + public function setHelperSet(HelperSet $helperSet = null) + { + $this->helperSet = $helperSet; + } + + /** + * {@inheritdoc} + */ + public function getHelperSet(): ?HelperSet + { + return $this->helperSet; + } + + /** + * Returns the width of a string, using mb_strwidth if it is available. + * The width is how many characters positions the string will use. + */ + public static function width(?string $string): int + { + $string ?? $string = ''; + + if (preg_match('//u', $string)) { + return (new UnicodeString($string))->width(false); + } + + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return \strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + /** + * Returns the length of a string, using mb_strlen if it is available. + * The length is related to how many bytes the string will use. + */ + public static function length(?string $string): int + { + $string ?? $string = ''; + + if (preg_match('//u', $string)) { + return (new UnicodeString($string))->length(); + } + + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return \strlen($string); + } + + return mb_strlen($string, $encoding); + } + + /** + * Returns the subset of a string, using mb_substr if it is available. + */ + public static function substr(?string $string, int $from, int $length = null): string + { + $string ?? $string = ''; + + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return substr($string, $from, $length); + } + + return mb_substr($string, $from, $length, $encoding); + } + + public static function formatTime(int|float $secs) + { + static $timeFormats = [ + [0, '< 1 sec'], + [1, '1 sec'], + [2, 'secs', 1], + [60, '1 min'], + [120, 'mins', 60], + [3600, '1 hr'], + [7200, 'hrs', 3600], + [86400, '1 day'], + [172800, 'days', 86400], + ]; + + foreach ($timeFormats as $index => $format) { + if ($secs >= $format[0]) { + if ((isset($timeFormats[$index + 1]) && $secs < $timeFormats[$index + 1][0]) + || $index == \count($timeFormats) - 1 + ) { + if (2 == \count($format)) { + return $format[1]; + } + + return floor($secs / $format[2]).' '.$format[1]; + } + } + } + } + + public static function formatMemory(int $memory) + { + if ($memory >= 1024 * 1024 * 1024) { + return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024); + } + + if ($memory >= 1024 * 1024) { + return sprintf('%.1f MiB', $memory / 1024 / 1024); + } + + if ($memory >= 1024) { + return sprintf('%d KiB', $memory / 1024); + } + + return sprintf('%d B', $memory); + } + + public static function removeDecoration(OutputFormatterInterface $formatter, ?string $string) + { + $isDecorated = $formatter->isDecorated(); + $formatter->setDecorated(false); + // remove <...> formatting + $string = $formatter->format($string ?? ''); + // remove already formatted characters + $string = preg_replace("/\033\[[^m]*m/", '', $string ?? ''); + // remove terminal hyperlinks + $string = preg_replace('/\\033]8;[^;]*;[^\\033]*\\033\\\\/', '', $string ?? ''); + $formatter->setDecorated($isDecorated); + + return $string; + } +} diff --git a/vendor/symfony/console/Helper/HelperInterface.php b/vendor/symfony/console/Helper/HelperInterface.php new file mode 100644 index 0000000..1d2b7bf --- /dev/null +++ b/vendor/symfony/console/Helper/HelperInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * HelperInterface is the interface all helpers must implement. + * + * @author Fabien Potencier + */ +interface HelperInterface +{ + /** + * Sets the helper set associated with this helper. + */ + public function setHelperSet(HelperSet $helperSet = null); + + /** + * Gets the helper set associated with this helper. + */ + public function getHelperSet(): ?HelperSet; + + /** + * Returns the canonical name of this helper. + * + * @return string + */ + public function getName(); +} diff --git a/vendor/symfony/console/Helper/HelperSet.php b/vendor/symfony/console/Helper/HelperSet.php new file mode 100644 index 0000000..be0beca --- /dev/null +++ b/vendor/symfony/console/Helper/HelperSet.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * HelperSet represents a set of helpers to be used with a command. + * + * @author Fabien Potencier + * + * @implements \IteratorAggregate + */ +class HelperSet implements \IteratorAggregate +{ + /** @var array */ + private array $helpers = []; + + /** + * @param Helper[] $helpers An array of helper + */ + public function __construct(array $helpers = []) + { + foreach ($helpers as $alias => $helper) { + $this->set($helper, \is_int($alias) ? null : $alias); + } + } + + public function set(HelperInterface $helper, string $alias = null) + { + $this->helpers[$helper->getName()] = $helper; + if (null !== $alias) { + $this->helpers[$alias] = $helper; + } + + $helper->setHelperSet($this); + } + + /** + * Returns true if the helper if defined. + */ + public function has(string $name): bool + { + return isset($this->helpers[$name]); + } + + /** + * Gets a helper value. + * + * @throws InvalidArgumentException if the helper is not defined + */ + public function get(string $name): HelperInterface + { + if (!$this->has($name)) { + throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } + + return $this->helpers[$name]; + } + + public function getIterator(): \Traversable + { + return new \ArrayIterator($this->helpers); + } +} diff --git a/vendor/symfony/console/Helper/InputAwareHelper.php b/vendor/symfony/console/Helper/InputAwareHelper.php new file mode 100644 index 0000000..0d0dba2 --- /dev/null +++ b/vendor/symfony/console/Helper/InputAwareHelper.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Input\InputAwareInterface; +use Symfony\Component\Console\Input\InputInterface; + +/** + * An implementation of InputAwareInterface for Helpers. + * + * @author Wouter J + */ +abstract class InputAwareHelper extends Helper implements InputAwareInterface +{ + protected $input; + + /** + * {@inheritdoc} + */ + public function setInput(InputInterface $input) + { + $this->input = $input; + } +} diff --git a/vendor/symfony/console/Helper/ProcessHelper.php b/vendor/symfony/console/Helper/ProcessHelper.php new file mode 100644 index 0000000..e5ba4db --- /dev/null +++ b/vendor/symfony/console/Helper/ProcessHelper.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; + +/** + * The ProcessHelper class provides helpers to run external processes. + * + * @author Fabien Potencier + * + * @final + */ +class ProcessHelper extends Helper +{ + /** + * Runs an external process. + * + * @param array|Process $cmd An instance of Process or an array of the command and arguments + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + */ + public function run(OutputInterface $output, array|Process $cmd, string $error = null, callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process + { + if (!class_exists(Process::class)) { + throw new \LogicException('The ProcessHelper cannot be run as the Process component is not installed. Try running "compose require symfony/process".'); + } + + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + if ($cmd instanceof Process) { + $cmd = [$cmd]; + } + + if (\is_string($cmd[0] ?? null)) { + $process = new Process($cmd); + $cmd = []; + } elseif (($cmd[0] ?? null) instanceof Process) { + $process = $cmd[0]; + unset($cmd[0]); + } else { + throw new \InvalidArgumentException(sprintf('Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__)); + } + + if ($verbosity <= $output->getVerbosity()) { + $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine()))); + } + + if ($output->isDebug()) { + $callback = $this->wrapCallback($output, $process, $callback); + } + + $process->run($callback, $cmd); + + if ($verbosity <= $output->getVerbosity()) { + $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode()); + $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful())); + } + + if (!$process->isSuccessful() && null !== $error) { + $output->writeln(sprintf('%s', $this->escapeString($error))); + } + + return $process; + } + + /** + * 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 array|Process $cmd An instance of Process or a command to run + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @throws ProcessFailedException + * + * @see run() + */ + public function mustRun(OutputInterface $output, array|Process $cmd, string $error = null, callable $callback = null): Process + { + $process = $this->run($output, $cmd, $error, $callback); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + return $process; + } + + /** + * Wraps a Process callback to add debugging output. + */ + public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null): callable + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + return function ($type, $buffer) use ($output, $process, $callback, $formatter) { + $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type)); + + if (null !== $callback) { + $callback($type, $buffer); + } + }; + } + + private function escapeString(string $str): string + { + return str_replace('<', '\\<', $str); + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return 'process'; + } +} diff --git a/vendor/symfony/console/Helper/ProgressBar.php b/vendor/symfony/console/Helper/ProgressBar.php new file mode 100644 index 0000000..2b4a45f --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressBar.php @@ -0,0 +1,603 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Cursor; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\ConsoleSectionOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Terminal; + +/** + * The ProgressBar provides helpers to display progress output. + * + * @author Fabien Potencier + * @author Chris Jones + */ +final class ProgressBar +{ + public const FORMAT_VERBOSE = 'verbose'; + public const FORMAT_VERY_VERBOSE = 'very_verbose'; + public const FORMAT_DEBUG = 'debug'; + public const FORMAT_NORMAL = 'normal'; + + private const FORMAT_VERBOSE_NOMAX = 'verbose_nomax'; + private const FORMAT_VERY_VERBOSE_NOMAX = 'very_verbose_nomax'; + private const FORMAT_DEBUG_NOMAX = 'debug_nomax'; + private const FORMAT_NORMAL_NOMAX = 'normal_nomax'; + + private int $barWidth = 28; + private string $barChar; + private string $emptyBarChar = '-'; + private string $progressChar = '>'; + private ?string $format = null; + private ?string $internalFormat = null; + private ?int $redrawFreq = 1; + private int $writeCount = 0; + private float $lastWriteTime = 0; + private float $minSecondsBetweenRedraws = 0; + private float $maxSecondsBetweenRedraws = 1; + private $output; + private int $step = 0; + private ?int $max = null; + private int $startTime; + private int $stepWidth; + private float $percent = 0.0; + private array $messages = []; + private bool $overwrite = true; + private $terminal; + private ?string $previousMessage = null; + private $cursor; + + private static array $formatters; + private static array $formats; + + /** + * @param int $max Maximum steps (0 if unknown) + */ + public function __construct(OutputInterface $output, int $max = 0, float $minSecondsBetweenRedraws = 1 / 25) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $this->output = $output; + $this->setMaxSteps($max); + $this->terminal = new Terminal(); + + if (0 < $minSecondsBetweenRedraws) { + $this->redrawFreq = null; + $this->minSecondsBetweenRedraws = $minSecondsBetweenRedraws; + } + + if (!$this->output->isDecorated()) { + // disable overwrite when output does not support ANSI codes. + $this->overwrite = false; + + // set a reasonable redraw frequency so output isn't flooded + $this->redrawFreq = null; + } + + $this->startTime = time(); + $this->cursor = new Cursor($output); + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void + { + self::$formatters ??= self::initPlaceholderFormatters(); + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + */ + public static function getPlaceholderFormatterDefinition(string $name): ?callable + { + self::$formatters ??= self::initPlaceholderFormatters(); + + return self::$formatters[$name] ?? null; + } + + /** + * Sets a format for a given name. + * + * This method also allow you to override an existing format. + * + * @param string $name The format name + * @param string $format A format string + */ + public static function setFormatDefinition(string $name, string $format): void + { + self::$formats ??= self::initFormats(); + + self::$formats[$name] = $format; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + */ + public static function getFormatDefinition(string $name): ?string + { + self::$formats ??= self::initFormats(); + + return self::$formats[$name] ?? null; + } + + /** + * Associates a text with a named placeholder. + * + * The text is displayed when the progress bar is rendered but only + * when the corresponding placeholder is part of the custom format line + * (by wrapping the name with %). + * + * @param string $message The text to associate with the placeholder + * @param string $name The name of the placeholder + */ + public function setMessage(string $message, string $name = 'message') + { + $this->messages[$name] = $message; + } + + public function getMessage(string $name = 'message') + { + return $this->messages[$name]; + } + + public function getStartTime(): int + { + return $this->startTime; + } + + public function getMaxSteps(): int + { + return $this->max; + } + + public function getProgress(): int + { + return $this->step; + } + + private function getStepWidth(): int + { + return $this->stepWidth; + } + + public function getProgressPercent(): float + { + return $this->percent; + } + + public function getBarOffset(): float + { + return floor($this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? (int) (min(5, $this->barWidth / 15) * $this->writeCount) : $this->step) % $this->barWidth); + } + + public function getEstimated(): float + { + if (!$this->step) { + return 0; + } + + return round((time() - $this->startTime) / $this->step * $this->max); + } + + public function getRemaining(): float + { + if (!$this->step) { + return 0; + } + + return round((time() - $this->startTime) / $this->step * ($this->max - $this->step)); + } + + public function setBarWidth(int $size) + { + $this->barWidth = max(1, $size); + } + + public function getBarWidth(): int + { + return $this->barWidth; + } + + public function setBarCharacter(string $char) + { + $this->barChar = $char; + } + + public function getBarCharacter(): string + { + return $this->barChar ?? ($this->max ? '=' : $this->emptyBarChar); + } + + public function setEmptyBarCharacter(string $char) + { + $this->emptyBarChar = $char; + } + + public function getEmptyBarCharacter(): string + { + return $this->emptyBarChar; + } + + public function setProgressCharacter(string $char) + { + $this->progressChar = $char; + } + + public function getProgressCharacter(): string + { + return $this->progressChar; + } + + public function setFormat(string $format) + { + $this->format = null; + $this->internalFormat = $format; + } + + /** + * Sets the redraw frequency. + * + * @param int|null $freq The frequency in steps + */ + public function setRedrawFrequency(?int $freq) + { + $this->redrawFreq = null !== $freq ? max(1, $freq) : null; + } + + public function minSecondsBetweenRedraws(float $seconds): void + { + $this->minSecondsBetweenRedraws = $seconds; + } + + public function maxSecondsBetweenRedraws(float $seconds): void + { + $this->maxSecondsBetweenRedraws = $seconds; + } + + /** + * Returns an iterator that will automatically update the progress bar when iterated. + * + * @param int|null $max Number of steps to complete the bar (0 if indeterminate), if null it will be inferred from $iterable + */ + public function iterate(iterable $iterable, int $max = null): iterable + { + $this->start($max ?? (is_countable($iterable) ? \count($iterable) : 0)); + + foreach ($iterable as $key => $value) { + yield $key => $value; + + $this->advance(); + } + + $this->finish(); + } + + /** + * Starts the progress output. + * + * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged + */ + public function start(int $max = null) + { + $this->startTime = time(); + $this->step = 0; + $this->percent = 0.0; + + if (null !== $max) { + $this->setMaxSteps($max); + } + + $this->display(); + } + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + */ + public function advance(int $step = 1) + { + $this->setProgress($this->step + $step); + } + + /** + * Sets whether to overwrite the progressbar, false for new line. + */ + public function setOverwrite(bool $overwrite) + { + $this->overwrite = $overwrite; + } + + public function setProgress(int $step) + { + if ($this->max && $step > $this->max) { + $this->max = $step; + } elseif ($step < 0) { + $step = 0; + } + + $redrawFreq = $this->redrawFreq ?? (($this->max ?: 10) / 10); + $prevPeriod = (int) ($this->step / $redrawFreq); + $currPeriod = (int) ($step / $redrawFreq); + $this->step = $step; + $this->percent = $this->max ? (float) $this->step / $this->max : 0; + $timeInterval = microtime(true) - $this->lastWriteTime; + + // Draw regardless of other limits + if ($this->max === $step) { + $this->display(); + + return; + } + + // Throttling + if ($timeInterval < $this->minSecondsBetweenRedraws) { + return; + } + + // Draw each step period, but not too late + if ($prevPeriod !== $currPeriod || $timeInterval >= $this->maxSecondsBetweenRedraws) { + $this->display(); + } + } + + public function setMaxSteps(int $max) + { + $this->format = null; + $this->max = max(0, $max); + $this->stepWidth = $this->max ? Helper::width((string) $this->max) : 4; + } + + /** + * Finishes the progress output. + */ + public function finish(): void + { + if (!$this->max) { + $this->max = $this->step; + } + + if ($this->step === $this->max && !$this->overwrite) { + // prevent double 100% output + return; + } + + $this->setProgress($this->max); + } + + /** + * Outputs the current progress string. + */ + public function display(): void + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + $this->overwrite($this->buildLine()); + } + + /** + * Removes the progress bar from the current line. + * + * This is useful if you wish to write some output + * while a progress bar is running. + * Call display() to show the progress bar again. + */ + public function clear(): void + { + if (!$this->overwrite) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + $this->overwrite(''); + } + + private function setRealFormat(string $format) + { + // try to use the _nomax variant if available + if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) { + $this->format = self::getFormatDefinition($format.'_nomax'); + } elseif (null !== self::getFormatDefinition($format)) { + $this->format = self::getFormatDefinition($format); + } else { + $this->format = $format; + } + } + + /** + * Overwrites a previous message to the output. + */ + private function overwrite(string $message): void + { + if ($this->previousMessage === $message) { + return; + } + + $originalMessage = $message; + + if ($this->overwrite) { + if (null !== $this->previousMessage) { + if ($this->output instanceof ConsoleSectionOutput) { + $messageLines = explode("\n", $this->previousMessage); + $lineCount = \count($messageLines); + foreach ($messageLines as $messageLine) { + $messageLineLength = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $messageLine)); + if ($messageLineLength > $this->terminal->getWidth()) { + $lineCount += floor($messageLineLength / $this->terminal->getWidth()); + } + } + $this->output->clear($lineCount); + } else { + $lineCount = substr_count($this->previousMessage, "\n"); + for ($i = 0; $i < $lineCount; ++$i) { + $this->cursor->moveToColumn(1); + $this->cursor->clearLine(); + $this->cursor->moveUp(); + } + + $this->cursor->moveToColumn(1); + $this->cursor->clearLine(); + } + } + } elseif ($this->step > 0) { + $message = \PHP_EOL.$message; + } + + $this->previousMessage = $originalMessage; + $this->lastWriteTime = microtime(true); + + $this->output->write($message); + ++$this->writeCount; + } + + private function determineBestFormat(): string + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->max ? self::FORMAT_VERBOSE : self::FORMAT_VERBOSE_NOMAX; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + return $this->max ? self::FORMAT_VERY_VERBOSE : self::FORMAT_VERY_VERBOSE_NOMAX; + case OutputInterface::VERBOSITY_DEBUG: + return $this->max ? self::FORMAT_DEBUG : self::FORMAT_DEBUG_NOMAX; + default: + return $this->max ? self::FORMAT_NORMAL : self::FORMAT_NORMAL_NOMAX; + } + } + + private static function initPlaceholderFormatters(): array + { + return [ + 'bar' => function (self $bar, OutputInterface $output) { + $completeBars = $bar->getBarOffset(); + $display = str_repeat($bar->getBarCharacter(), $completeBars); + if ($completeBars < $bar->getBarWidth()) { + $emptyBars = $bar->getBarWidth() - $completeBars - Helper::length(Helper::removeDecoration($output->getFormatter(), $bar->getProgressCharacter())); + $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars); + } + + return $display; + }, + 'elapsed' => function (self $bar) { + return Helper::formatTime(time() - $bar->getStartTime()); + }, + 'remaining' => function (self $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); + } + + return Helper::formatTime($bar->getRemaining()); + }, + 'estimated' => function (self $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); + } + + return Helper::formatTime($bar->getEstimated()); + }, + 'memory' => function (self $bar) { + return Helper::formatMemory(memory_get_usage(true)); + }, + 'current' => function (self $bar) { + return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', \STR_PAD_LEFT); + }, + 'max' => function (self $bar) { + return $bar->getMaxSteps(); + }, + 'percent' => function (self $bar) { + return floor($bar->getProgressPercent() * 100); + }, + ]; + } + + private static function initFormats(): array + { + return [ + self::FORMAT_NORMAL => ' %current%/%max% [%bar%] %percent:3s%%', + self::FORMAT_NORMAL_NOMAX => ' %current% [%bar%]', + + self::FORMAT_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', + self::FORMAT_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%', + + self::FORMAT_VERY_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', + self::FORMAT_VERY_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%', + + self::FORMAT_DEBUG => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', + self::FORMAT_DEBUG_NOMAX => ' %current% [%bar%] %elapsed:6s% %memory:6s%', + ]; + } + + private function buildLine(): string + { + \assert(null !== $this->format); + + $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i"; + $callback = function ($matches) { + if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) { + $text = $formatter($this, $this->output); + } elseif (isset($this->messages[$matches[1]])) { + $text = $this->messages[$matches[1]]; + } else { + return $matches[0]; + } + + if (isset($matches[2])) { + $text = sprintf('%'.$matches[2], $text); + } + + return $text; + }; + $line = preg_replace_callback($regex, $callback, $this->format); + + // gets string length for each sub line with multiline format + $linesLength = array_map(function ($subLine) { + return Helper::width(Helper::removeDecoration($this->output->getFormatter(), rtrim($subLine, "\r"))); + }, explode("\n", $line)); + + $linesWidth = max($linesLength); + + $terminalWidth = $this->terminal->getWidth(); + if ($linesWidth <= $terminalWidth) { + return $line; + } + + $this->setBarWidth($this->barWidth - $linesWidth + $terminalWidth); + + return preg_replace_callback($regex, $callback, $this->format); + } +} diff --git a/vendor/symfony/console/Helper/ProgressIndicator.php b/vendor/symfony/console/Helper/ProgressIndicator.php new file mode 100644 index 0000000..c746f9b --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressIndicator.php @@ -0,0 +1,244 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Kevin Bond + */ +class ProgressIndicator +{ + private const FORMATS = [ + 'normal' => ' %indicator% %message%', + 'normal_no_ansi' => ' %message%', + + 'verbose' => ' %indicator% %message% (%elapsed:6s%)', + 'verbose_no_ansi' => ' %message% (%elapsed:6s%)', + + 'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)', + 'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)', + ]; + + private $output; + private int $startTime; + private ?string $format = null; + private ?string $message = null; + private array $indicatorValues; + private int $indicatorCurrent; + private int $indicatorChangeInterval; + private float $indicatorUpdateTime; + private bool $started = false; + + /** + * @var array + */ + private static array $formatters; + + /** + * @param int $indicatorChangeInterval Change interval in milliseconds + * @param array|null $indicatorValues Animated indicator characters + */ + public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null) + { + $this->output = $output; + + if (null === $format) { + $format = $this->determineBestFormat(); + } + + if (null === $indicatorValues) { + $indicatorValues = ['-', '\\', '|', '/']; + } + + $indicatorValues = array_values($indicatorValues); + + if (2 > \count($indicatorValues)) { + throw new InvalidArgumentException('Must have at least 2 indicator value characters.'); + } + + $this->format = self::getFormatDefinition($format); + $this->indicatorChangeInterval = $indicatorChangeInterval; + $this->indicatorValues = $indicatorValues; + $this->startTime = time(); + } + + /** + * Sets the current indicator message. + */ + public function setMessage(?string $message) + { + $this->message = $message; + + $this->display(); + } + + /** + * Starts the indicator output. + */ + public function start(string $message) + { + if ($this->started) { + throw new LogicException('Progress indicator already started.'); + } + + $this->message = $message; + $this->started = true; + $this->startTime = time(); + $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval; + $this->indicatorCurrent = 0; + + $this->display(); + } + + /** + * Advances the indicator. + */ + public function advance() + { + if (!$this->started) { + throw new LogicException('Progress indicator has not yet been started.'); + } + + if (!$this->output->isDecorated()) { + return; + } + + $currentTime = $this->getCurrentTimeInMilliseconds(); + + if ($currentTime < $this->indicatorUpdateTime) { + return; + } + + $this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval; + ++$this->indicatorCurrent; + + $this->display(); + } + + /** + * Finish the indicator with message. + * + * @param $message + */ + public function finish(string $message) + { + if (!$this->started) { + throw new LogicException('Progress indicator has not yet been started.'); + } + + $this->message = $message; + $this->display(); + $this->output->writeln(''); + $this->started = false; + } + + /** + * Gets the format for a given name. + */ + public static function getFormatDefinition(string $name): ?string + { + return self::FORMATS[$name] ?? null; + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + */ + public static function setPlaceholderFormatterDefinition(string $name, callable $callable) + { + self::$formatters ??= self::initPlaceholderFormatters(); + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name (including the delimiter char like %). + */ + public static function getPlaceholderFormatterDefinition(string $name): ?callable + { + self::$formatters ??= self::initPlaceholderFormatters(); + + return self::$formatters[$name] ?? null; + } + + private function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) { + if ($formatter = self::getPlaceholderFormatterDefinition($matches[1])) { + return $formatter($this); + } + + return $matches[0]; + }, $this->format ?? '')); + } + + private function determineBestFormat(): string + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + case OutputInterface::VERBOSITY_DEBUG: + return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi'; + default: + return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi'; + } + } + + /** + * Overwrites a previous message to the output. + */ + private function overwrite(string $message) + { + if ($this->output->isDecorated()) { + $this->output->write("\x0D\x1B[2K"); + $this->output->write($message); + } else { + $this->output->writeln($message); + } + } + + private function getCurrentTimeInMilliseconds(): float + { + return round(microtime(true) * 1000); + } + + /** + * @return array + */ + private static function initPlaceholderFormatters(): array + { + return [ + 'indicator' => function (self $indicator) { + return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)]; + }, + 'message' => function (self $indicator) { + return $indicator->message; + }, + 'elapsed' => function (self $indicator) { + return Helper::formatTime(time() - $indicator->startTime); + }, + 'memory' => function () { + return Helper::formatMemory(memory_get_usage(true)); + }, + ]; + } +} diff --git a/vendor/symfony/console/Helper/QuestionHelper.php b/vendor/symfony/console/Helper/QuestionHelper.php new file mode 100644 index 0000000..d53420b --- /dev/null +++ b/vendor/symfony/console/Helper/QuestionHelper.php @@ -0,0 +1,598 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Cursor; +use Symfony\Component\Console\Exception\MissingInputException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\StreamableInputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\ConsoleSectionOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Terminal; + +use function Symfony\Component\String\s; + +/** + * The QuestionHelper class provides helpers to interact with the user. + * + * @author Fabien Potencier + */ +class QuestionHelper extends Helper +{ + /** + * @var resource|null + */ + private $inputStream; + + private static bool $stty = true; + private static bool $stdinIsInteractive; + + /** + * Asks a question to the user. + * + * @return mixed The user answer + * + * @throws RuntimeException If there is no data to read in the input stream + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question): mixed + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + if (!$input->isInteractive()) { + return $this->getDefaultAnswer($question); + } + + if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) { + $this->inputStream = $stream; + } + + try { + if (!$question->getValidator()) { + return $this->doAsk($output, $question); + } + + $interviewer = function () use ($output, $question) { + return $this->doAsk($output, $question); + }; + + return $this->validateAttempts($interviewer, $output, $question); + } catch (MissingInputException $exception) { + $input->setInteractive(false); + + if (null === $fallbackOutput = $this->getDefaultAnswer($question)) { + throw $exception; + } + + return $fallbackOutput; + } + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return 'question'; + } + + /** + * Prevents usage of stty. + */ + public static function disableStty() + { + self::$stty = false; + } + + /** + * Asks the question to the user. + * + * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden + */ + private function doAsk(OutputInterface $output, Question $question): mixed + { + $this->writePrompt($output, $question); + + $inputStream = $this->inputStream ?: \STDIN; + $autocomplete = $question->getAutocompleterCallback(); + + if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) { + $ret = false; + if ($question->isHidden()) { + try { + $hiddenResponse = $this->getHiddenResponse($output, $inputStream, $question->isTrimmable()); + $ret = $question->isTrimmable() ? trim($hiddenResponse) : $hiddenResponse; + } catch (RuntimeException $e) { + if (!$question->isHiddenFallback()) { + throw $e; + } + } + } + + if (false === $ret) { + $ret = $this->readInput($inputStream, $question); + if (false === $ret) { + throw new MissingInputException('Aborted.'); + } + if ($question->isTrimmable()) { + $ret = trim($ret); + } + } + } else { + $autocomplete = $this->autocomplete($output, $question, $inputStream, $autocomplete); + $ret = $question->isTrimmable() ? trim($autocomplete) : $autocomplete; + } + + if ($output instanceof ConsoleSectionOutput) { + $output->addContent($ret); + } + + $ret = \strlen($ret) > 0 ? $ret : $question->getDefault(); + + if ($normalizer = $question->getNormalizer()) { + return $normalizer($ret); + } + + return $ret; + } + + private function getDefaultAnswer(Question $question): mixed + { + $default = $question->getDefault(); + + if (null === $default) { + return $default; + } + + if ($validator = $question->getValidator()) { + return \call_user_func($question->getValidator(), $default); + } elseif ($question instanceof ChoiceQuestion) { + $choices = $question->getChoices(); + + if (!$question->isMultiselect()) { + return $choices[$default] ?? $default; + } + + $default = explode(',', $default); + foreach ($default as $k => $v) { + $v = $question->isTrimmable() ? trim($v) : $v; + $default[$k] = $choices[$v] ?? $v; + } + } + + return $default; + } + + /** + * Outputs the question prompt. + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $message = $question->getQuestion(); + + if ($question instanceof ChoiceQuestion) { + $output->writeln(array_merge([ + $question->getQuestion(), + ], $this->formatChoiceQuestionChoices($question, 'info'))); + + $message = $question->getPrompt(); + } + + $output->write($message); + } + + /** + * @return string[] + */ + protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string $tag): array + { + $messages = []; + + $maxWidth = max(array_map([__CLASS__, 'width'], array_keys($choices = $question->getChoices()))); + + foreach ($choices as $key => $value) { + $padding = str_repeat(' ', $maxWidth - self::width($key)); + + $messages[] = sprintf(" [<$tag>%s$padding] %s", $key, $value); + } + + return $messages; + } + + /** + * Outputs an error message. + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) { + $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'); + } else { + $message = ''.$error->getMessage().''; + } + + $output->writeln($message); + } + + /** + * Autocompletes a question. + * + * @param resource $inputStream + */ + private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete): string + { + $cursor = new Cursor($output, $inputStream); + + $fullChoice = ''; + $ret = ''; + + $i = 0; + $ofs = -1; + $matches = $autocomplete($ret); + $numMatches = \count($matches); + + $sttyMode = shell_exec('stty -g'); + $isStdin = 'php://stdin' === (stream_get_meta_data($inputStream)['uri'] ?? null); + $r = [$inputStream]; + $w = []; + + // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) + shell_exec('stty -icanon -echo'); + + // Add highlighted text style + $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white')); + + // Read a keypress + while (!feof($inputStream)) { + while ($isStdin && 0 === @stream_select($r, $w, $w, 0, 100)) { + // Give signal handlers a chance to run + $r = [$inputStream]; + } + $c = fread($inputStream, 1); + + // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false. + if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) { + shell_exec('stty '.$sttyMode); + throw new MissingInputException('Aborted.'); + } elseif ("\177" === $c) { // Backspace Character + if (0 === $numMatches && 0 !== $i) { + --$i; + $cursor->moveLeft(s($fullChoice)->slice(-1)->width(false)); + + $fullChoice = self::substr($fullChoice, 0, $i); + } + + if (0 === $i) { + $ofs = -1; + $matches = $autocomplete($ret); + $numMatches = \count($matches); + } else { + $numMatches = 0; + } + + // Pop the last character off the end of our string + $ret = self::substr($ret, 0, $i); + } elseif ("\033" === $c) { + // Did we read an escape sequence? + $c .= fread($inputStream, 2); + + // A = Up Arrow. B = Down Arrow + 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 = (string) $matches[$ofs]; + // Echo out remaining chars for current match + $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)))); + $output->write($remainingCharacters); + $fullChoice .= $remainingCharacters; + $i = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding); + + $matches = array_filter( + $autocomplete($ret), + function ($match) use ($ret) { + return '' === $ret || str_starts_with($match, $ret); + } + ); + $numMatches = \count($matches); + $ofs = -1; + } + + if ("\n" === $c) { + $output->write($c); + break; + } + + $numMatches = 0; + } + + continue; + } else { + if ("\x80" <= $c) { + $c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]); + } + + $output->write($c); + $ret .= $c; + $fullChoice .= $c; + ++$i; + + $tempRet = $ret; + + if ($question instanceof ChoiceQuestion && $question->isMultiselect()) { + $tempRet = $this->mostRecentlyEnteredValue($fullChoice); + } + + $numMatches = 0; + $ofs = 0; + + foreach ($autocomplete($ret) as $value) { + // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) + if (str_starts_with($value, $tempRet)) { + $matches[$numMatches++] = $value; + } + } + } + + $cursor->clearLineAfter(); + + if ($numMatches > 0 && -1 !== $ofs) { + $cursor->savePosition(); + // Write highlighted text, complete the partially entered response + $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))); + $output->write(''.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).''); + $cursor->restorePosition(); + } + } + + // Reset stty so it behaves normally again + shell_exec('stty '.$sttyMode); + + return $fullChoice; + } + + private function mostRecentlyEnteredValue(string $entered): string + { + // Determine the most recent value that the user entered + if (!str_contains($entered, ',')) { + return $entered; + } + + $choices = explode(',', $entered); + if ('' !== $lastChoice = trim($choices[\count($choices) - 1])) { + return $lastChoice; + } + + return $entered; + } + + /** + * Gets a hidden response from user. + * + * @param resource $inputStream The handler resource + * @param bool $trimmable Is the answer trimmable + * + * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden + */ + private function getHiddenResponse(OutputInterface $output, $inputStream, bool $trimmable = true): string + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; + + // handle code running from a phar + if ('phar:' === substr(__FILE__, 0, 5)) { + $tmpExe = sys_get_temp_dir().'/hiddeninput.exe'; + copy($exe, $tmpExe); + $exe = $tmpExe; + } + + $sExec = shell_exec('"'.$exe.'"'); + $value = $trimmable ? rtrim($sExec) : $sExec; + $output->writeln(''); + + if (isset($tmpExe)) { + unlink($tmpExe); + } + + return $value; + } + + if (self::$stty && Terminal::hasSttyAvailable()) { + $sttyMode = shell_exec('stty -g'); + shell_exec('stty -echo'); + } elseif ($this->isInteractiveInput($inputStream)) { + throw new RuntimeException('Unable to hide the response.'); + } + + $value = fgets($inputStream, 4096); + + if (self::$stty && Terminal::hasSttyAvailable()) { + shell_exec('stty '.$sttyMode); + } + + if (false === $value) { + throw new MissingInputException('Aborted.'); + } + if ($trimmable) { + $value = trim($value); + } + $output->writeln(''); + + return $value; + } + + /** + * Validates an attempt. + * + * @param callable $interviewer A callable that will ask for a question and return the result + * + * @throws \Exception In case the max number of attempts has been reached and no valid response has been given + */ + private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question): mixed + { + $error = null; + $attempts = $question->getMaxAttempts(); + + while (null === $attempts || $attempts--) { + if (null !== $error) { + $this->writeError($output, $error); + } + + try { + return $question->getValidator()($interviewer()); + } catch (RuntimeException $e) { + throw $e; + } catch (\Exception $error) { + } + } + + throw $error; + } + + private function isInteractiveInput($inputStream): bool + { + if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) { + return false; + } + + if (isset(self::$stdinIsInteractive)) { + return self::$stdinIsInteractive; + } + + if (\function_exists('stream_isatty')) { + return self::$stdinIsInteractive = @stream_isatty(fopen('php://stdin', 'r')); + } + + if (\function_exists('posix_isatty')) { + return self::$stdinIsInteractive = @posix_isatty(fopen('php://stdin', 'r')); + } + + if (!\function_exists('exec')) { + return self::$stdinIsInteractive = true; + } + + exec('stty 2> /dev/null', $output, $status); + + return self::$stdinIsInteractive = 1 !== $status; + } + + /** + * Reads one or more lines of input and returns what is read. + * + * @param resource $inputStream The handler resource + * @param Question $question The question being asked + */ + private function readInput($inputStream, Question $question): string|false + { + if (!$question->isMultiline()) { + $cp = $this->setIOCodepage(); + $ret = fgets($inputStream, 4096); + + return $this->resetIOCodepage($cp, $ret); + } + + $multiLineStreamReader = $this->cloneInputStream($inputStream); + if (null === $multiLineStreamReader) { + return false; + } + + $ret = ''; + $cp = $this->setIOCodepage(); + while (false !== ($char = fgetc($multiLineStreamReader))) { + if (\PHP_EOL === "{$ret}{$char}") { + break; + } + $ret .= $char; + } + + return $this->resetIOCodepage($cp, $ret); + } + + private function setIOCodepage(): int + { + if (\function_exists('sapi_windows_cp_set')) { + $cp = sapi_windows_cp_get(); + sapi_windows_cp_set(sapi_windows_cp_get('oem')); + + return $cp; + } + + return 0; + } + + /** + * Sets console I/O to the specified code page and converts the user input. + */ + private function resetIOCodepage(int $cp, string|false $input): string|false + { + if (0 !== $cp) { + sapi_windows_cp_set($cp); + + if (false !== $input && '' !== $input) { + $input = sapi_windows_cp_conv(sapi_windows_cp_get('oem'), $cp, $input); + } + } + + return $input; + } + + /** + * Clones an input stream in order to act on one instance of the same + * stream without affecting the other instance. + * + * @param resource $inputStream The handler resource + * + * @return resource|null The cloned resource, null in case it could not be cloned + */ + private function cloneInputStream($inputStream) + { + $streamMetaData = stream_get_meta_data($inputStream); + $seekable = $streamMetaData['seekable'] ?? false; + $mode = $streamMetaData['mode'] ?? 'rb'; + $uri = $streamMetaData['uri'] ?? null; + + if (null === $uri) { + return null; + } + + $cloneStream = fopen($uri, $mode); + + // For seekable and writable streams, add all the same data to the + // cloned stream and then seek to the same offset. + if (true === $seekable && !\in_array($mode, ['r', 'rb', 'rt'])) { + $offset = ftell($inputStream); + rewind($inputStream); + stream_copy_to_stream($inputStream, $cloneStream); + fseek($inputStream, $offset); + fseek($cloneStream, $offset); + } + + return $cloneStream; + } +} diff --git a/vendor/symfony/console/Helper/SymfonyQuestionHelper.php b/vendor/symfony/console/Helper/SymfonyQuestionHelper.php new file mode 100644 index 0000000..01f94ab --- /dev/null +++ b/vendor/symfony/console/Helper/SymfonyQuestionHelper.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Symfony Style Guide compliant question helper. + * + * @author Kevin Bond + */ +class SymfonyQuestionHelper extends QuestionHelper +{ + /** + * {@inheritdoc} + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $text = OutputFormatter::escapeTrailingBackslash($question->getQuestion()); + $default = $question->getDefault(); + + if ($question->isMultiline()) { + $text .= sprintf(' (press %s to continue)', $this->getEofShortcut()); + } + + switch (true) { + case null === $default: + $text = sprintf(' %s:', $text); + + break; + + case $question instanceof ConfirmationQuestion: + $text = sprintf(' %s (yes/no) [%s]:', $text, $default ? 'yes' : 'no'); + + break; + + case $question instanceof ChoiceQuestion && $question->isMultiselect(): + $choices = $question->getChoices(); + $default = explode(',', $default); + + foreach ($default as $key => $value) { + $default[$key] = $choices[trim($value)]; + } + + $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape(implode(', ', $default))); + + break; + + case $question instanceof ChoiceQuestion: + $choices = $question->getChoices(); + $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape($choices[$default] ?? $default)); + + break; + + default: + $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape($default)); + } + + $output->writeln($text); + + $prompt = ' > '; + + if ($question instanceof ChoiceQuestion) { + $output->writeln($this->formatChoiceQuestionChoices($question, 'comment')); + + $prompt = $question->getPrompt(); + } + + $output->write($prompt); + } + + /** + * {@inheritdoc} + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if ($output instanceof SymfonyStyle) { + $output->newLine(); + $output->error($error->getMessage()); + + return; + } + + parent::writeError($output, $error); + } + + private function getEofShortcut(): string + { + if ('Windows' === \PHP_OS_FAMILY) { + return 'Ctrl+Z then Enter'; + } + + return 'Ctrl+D'; + } +} diff --git a/vendor/symfony/console/Helper/Table.php b/vendor/symfony/console/Helper/Table.php new file mode 100644 index 0000000..0d8da6c --- /dev/null +++ b/vendor/symfony/console/Helper/Table.php @@ -0,0 +1,849 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\WrappableOutputFormatterInterface; +use Symfony\Component\Console\Output\ConsoleSectionOutput; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Provides helpers to display a table. + * + * @author Fabien Potencier + * @author Саша Стаменковић + * @author Abdellatif Ait boudad + * @author Max Grigorian + * @author Dany Maillard + */ +class Table +{ + private const SEPARATOR_TOP = 0; + private const SEPARATOR_TOP_BOTTOM = 1; + private const SEPARATOR_MID = 2; + private const SEPARATOR_BOTTOM = 3; + private const BORDER_OUTSIDE = 0; + private const BORDER_INSIDE = 1; + + private ?string $headerTitle = null; + private ?string $footerTitle = null; + private array $headers = []; + private array $rows = []; + private bool $horizontal = false; + private array $effectiveColumnWidths = []; + private int $numberOfColumns; + private $output; + private $style; + private array $columnStyles = []; + private array $columnWidths = []; + private array $columnMaxWidths = []; + private bool $rendered = false; + + private static array $styles; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + + self::$styles ??= self::initStyles(); + + $this->setStyle('default'); + } + + /** + * Sets a style definition. + */ + public static function setStyleDefinition(string $name, TableStyle $style) + { + self::$styles ??= self::initStyles(); + + self::$styles[$name] = $style; + } + + /** + * Gets a style definition by name. + */ + public static function getStyleDefinition(string $name): TableStyle + { + self::$styles ??= self::initStyles(); + + return self::$styles[$name] ?? throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + /** + * Sets table style. + * + * @return $this + */ + public function setStyle(TableStyle|string $name): static + { + $this->style = $this->resolveStyle($name); + + return $this; + } + + /** + * Gets the current table style. + */ + public function getStyle(): TableStyle + { + return $this->style; + } + + /** + * Sets table column style. + * + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return $this + */ + public function setColumnStyle(int $columnIndex, TableStyle|string $name): static + { + $this->columnStyles[$columnIndex] = $this->resolveStyle($name); + + return $this; + } + + /** + * Gets the current style for a column. + * + * If style was not set, it returns the global table style. + */ + public function getColumnStyle(int $columnIndex): TableStyle + { + return $this->columnStyles[$columnIndex] ?? $this->getStyle(); + } + + /** + * Sets the minimum width of a column. + * + * @return $this + */ + public function setColumnWidth(int $columnIndex, int $width): static + { + $this->columnWidths[$columnIndex] = $width; + + return $this; + } + + /** + * Sets the minimum width of all columns. + * + * @return $this + */ + public function setColumnWidths(array $widths): static + { + $this->columnWidths = []; + foreach ($widths as $index => $width) { + $this->setColumnWidth($index, $width); + } + + return $this; + } + + /** + * Sets the maximum width of a column. + * + * Any cell within this column which contents exceeds the specified width will be wrapped into multiple lines, while + * formatted strings are preserved. + * + * @return $this + */ + public function setColumnMaxWidth(int $columnIndex, int $width): static + { + if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) { + throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, get_debug_type($this->output->getFormatter()))); + } + + $this->columnMaxWidths[$columnIndex] = $width; + + return $this; + } + + /** + * @return $this + */ + public function setHeaders(array $headers): static + { + $headers = array_values($headers); + if (!empty($headers) && !\is_array($headers[0])) { + $headers = [$headers]; + } + + $this->headers = $headers; + + return $this; + } + + public function setRows(array $rows) + { + $this->rows = []; + + return $this->addRows($rows); + } + + /** + * @return $this + */ + public function addRows(array $rows): static + { + foreach ($rows as $row) { + $this->addRow($row); + } + + return $this; + } + + /** + * @return $this + */ + public function addRow(TableSeparator|array $row): static + { + if ($row instanceof TableSeparator) { + $this->rows[] = $row; + + return $this; + } + + $this->rows[] = array_values($row); + + return $this; + } + + /** + * Adds a row to the table, and re-renders the table. + * + * @return $this + */ + public function appendRow(TableSeparator|array $row): static + { + if (!$this->output instanceof ConsoleSectionOutput) { + throw new RuntimeException(sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__)); + } + + if ($this->rendered) { + $this->output->clear($this->calculateRowCount()); + } + + $this->addRow($row); + $this->render(); + + return $this; + } + + /** + * @return $this + */ + public function setRow(int|string $column, array $row): static + { + $this->rows[$column] = $row; + + return $this; + } + + /** + * @return $this + */ + public function setHeaderTitle(?string $title): static + { + $this->headerTitle = $title; + + return $this; + } + + /** + * @return $this + */ + public function setFooterTitle(?string $title): static + { + $this->footerTitle = $title; + + return $this; + } + + /** + * @return $this + */ + public function setHorizontal(bool $horizontal = true): static + { + $this->horizontal = $horizontal; + + return $this; + } + + /** + * Renders table to output. + * + * Example: + * + * +---------------+-----------------------+------------------+ + * | ISBN | Title | Author | + * +---------------+-----------------------+------------------+ + * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | + * +---------------+-----------------------+------------------+ + */ + public function render() + { + $divider = new TableSeparator(); + if ($this->horizontal) { + $rows = []; + foreach ($this->headers[0] ?? [] as $i => $header) { + $rows[$i] = [$header]; + foreach ($this->rows as $row) { + if ($row instanceof TableSeparator) { + continue; + } + if (isset($row[$i])) { + $rows[$i][] = $row[$i]; + } elseif ($rows[$i][0] instanceof TableCell && $rows[$i][0]->getColspan() >= 2) { + // Noop, there is a "title" + } else { + $rows[$i][] = null; + } + } + } + } else { + $rows = array_merge($this->headers, [$divider], $this->rows); + } + + $this->calculateNumberOfColumns($rows); + + $rowGroups = $this->buildTableRows($rows); + $this->calculateColumnsWidth($rowGroups); + + $isHeader = !$this->horizontal; + $isFirstRow = $this->horizontal; + $hasTitle = (bool) $this->headerTitle; + + foreach ($rowGroups as $rowGroup) { + $isHeaderSeparatorRendered = false; + + foreach ($rowGroup as $row) { + if ($divider === $row) { + $isHeader = false; + $isFirstRow = true; + + continue; + } + + if ($row instanceof TableSeparator) { + $this->renderRowSeparator(); + + continue; + } + + if (!$row) { + continue; + } + + if ($isHeader && !$isHeaderSeparatorRendered) { + $this->renderRowSeparator( + $isHeader ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM, + $hasTitle ? $this->headerTitle : null, + $hasTitle ? $this->style->getHeaderTitleFormat() : null + ); + $hasTitle = false; + $isHeaderSeparatorRendered = true; + } + + if ($isFirstRow) { + $this->renderRowSeparator( + $isHeader ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM, + $hasTitle ? $this->headerTitle : null, + $hasTitle ? $this->style->getHeaderTitleFormat() : null + ); + $isFirstRow = false; + $hasTitle = false; + } + + if ($this->horizontal) { + $this->renderRow($row, $this->style->getCellRowFormat(), $this->style->getCellHeaderFormat()); + } else { + $this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat()); + } + } + } + $this->renderRowSeparator(self::SEPARATOR_BOTTOM, $this->footerTitle, $this->style->getFooterTitleFormat()); + + $this->cleanup(); + $this->rendered = true; + } + + /** + * Renders horizontal header separator. + * + * Example: + * + * +-----+-----------+-------+ + */ + private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null) + { + if (0 === $count = $this->numberOfColumns) { + return; + } + + $borders = $this->style->getBorderChars(); + if (!$borders[0] && !$borders[2] && !$this->style->getCrossingChar()) { + return; + } + + $crossings = $this->style->getCrossingChars(); + if (self::SEPARATOR_MID === $type) { + [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[2], $crossings[8], $crossings[0], $crossings[4]]; + } elseif (self::SEPARATOR_TOP === $type) { + [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[1], $crossings[2], $crossings[3]]; + } elseif (self::SEPARATOR_TOP_BOTTOM === $type) { + [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[9], $crossings[10], $crossings[11]]; + } else { + [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[7], $crossings[6], $crossings[5]]; + } + + $markup = $leftChar; + for ($column = 0; $column < $count; ++$column) { + $markup .= str_repeat($horizontal, $this->effectiveColumnWidths[$column]); + $markup .= $column === $count - 1 ? $rightChar : $midChar; + } + + if (null !== $title) { + $titleLength = Helper::width(Helper::removeDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title))); + $markupLength = Helper::width($markup); + if ($titleLength > $limit = $markupLength - 4) { + $titleLength = $limit; + $formatLength = Helper::width(Helper::removeDecoration($formatter, sprintf($titleFormat, ''))); + $formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...'); + } + + $titleStart = intdiv($markupLength - $titleLength, 2); + if (false === mb_detect_encoding($markup, null, true)) { + $markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength); + } else { + $markup = mb_substr($markup, 0, $titleStart).$formattedTitle.mb_substr($markup, $titleStart + $titleLength); + } + } + + $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup)); + } + + /** + * Renders vertical column separator. + */ + private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE): string + { + $borders = $this->style->getBorderChars(); + + return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]); + } + + /** + * Renders table row. + * + * Example: + * + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + */ + private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null) + { + $rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE); + $columns = $this->getRowColumns($row); + $last = \count($columns) - 1; + foreach ($columns as $i => $column) { + if ($firstCellFormat && 0 === $i) { + $rowContent .= $this->renderCell($row, $column, $firstCellFormat); + } else { + $rowContent .= $this->renderCell($row, $column, $cellFormat); + } + $rowContent .= $this->renderColumnSeparator($last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE); + } + $this->output->writeln($rowContent); + } + + /** + * Renders table cell with padding. + */ + private function renderCell(array $row, int $column, string $cellFormat): string + { + $cell = $row[$column] ?? ''; + $width = $this->effectiveColumnWidths[$column]; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // add the width of the following columns(numbers of colspan). + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) { + $width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn]; + } + } + + // str_pad won't work properly with multi-byte strings, we need to fix the padding + if (false !== $encoding = mb_detect_encoding($cell, null, true)) { + $width += \strlen($cell) - mb_strwidth($cell, $encoding); + } + + $style = $this->getColumnStyle($column); + + if ($cell instanceof TableSeparator) { + return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width)); + } + + $width += Helper::length($cell) - Helper::length(Helper::removeDecoration($this->output->getFormatter(), $cell)); + $content = sprintf($style->getCellRowContentFormat(), $cell); + + $padType = $style->getPadType(); + if ($cell instanceof TableCell && $cell->getStyle() instanceof TableCellStyle) { + $isNotStyledByTag = !preg_match('/^<(\w+|(\w+=[\w,]+;?)*)>.+<\/(\w+|(\w+=\w+;?)*)?>$/', $cell); + if ($isNotStyledByTag) { + $cellFormat = $cell->getStyle()->getCellFormat(); + if (!\is_string($cellFormat)) { + $tag = http_build_query($cell->getStyle()->getTagOptions(), '', ';'); + $cellFormat = '<'.$tag.'>%s'; + } + + if (strstr($content, '')) { + $content = str_replace('', '', $content); + $width -= 3; + } + if (strstr($content, '')) { + $content = str_replace('', '', $content); + $width -= \strlen(''); + } + } + + $padType = $cell->getStyle()->getPadByAlign(); + } + + return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $padType)); + } + + /** + * Calculate number of columns for this table. + */ + private function calculateNumberOfColumns(array $rows) + { + $columns = [0]; + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + $columns[] = $this->getNumberOfColumns($row); + } + + $this->numberOfColumns = max($columns); + } + + private function buildTableRows(array $rows): TableRows + { + /** @var WrappableOutputFormatterInterface $formatter */ + $formatter = $this->output->getFormatter(); + $unmergedRows = []; + for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) { + $rows = $this->fillNextRows($rows, $rowKey); + + // Remove any new line breaks and replace it with a new line + foreach ($rows[$rowKey] as $column => $cell) { + $colspan = $cell instanceof TableCell ? $cell->getColspan() : 1; + + if (isset($this->columnMaxWidths[$column]) && Helper::width(Helper::removeDecoration($formatter, $cell)) > $this->columnMaxWidths[$column]) { + $cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan); + } + if (!strstr($cell ?? '', "\n")) { + continue; + } + $escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell))); + $cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped; + $lines = explode("\n", str_replace("\n", "\n", $cell)); + foreach ($lines as $lineKey => $line) { + if ($colspan > 1) { + $line = new TableCell($line, ['colspan' => $colspan]); + } + if (0 === $lineKey) { + $rows[$rowKey][$column] = $line; + } else { + if (!\array_key_exists($rowKey, $unmergedRows) || !\array_key_exists($lineKey, $unmergedRows[$rowKey])) { + $unmergedRows[$rowKey][$lineKey] = $this->copyRow($rows, $rowKey); + } + $unmergedRows[$rowKey][$lineKey][$column] = $line; + } + } + } + } + + return new TableRows(function () use ($rows, $unmergedRows): \Traversable { + foreach ($rows as $rowKey => $row) { + $rowGroup = [$row instanceof TableSeparator ? $row : $this->fillCells($row)]; + + if (isset($unmergedRows[$rowKey])) { + foreach ($unmergedRows[$rowKey] as $row) { + $rowGroup[] = $row instanceof TableSeparator ? $row : $this->fillCells($row); + } + } + yield $rowGroup; + } + }); + } + + private function calculateRowCount(): int + { + $numberOfRows = \count(iterator_to_array($this->buildTableRows(array_merge($this->headers, [new TableSeparator()], $this->rows)))); + + if ($this->headers) { + ++$numberOfRows; // Add row for header separator + } + + if (\count($this->rows) > 0) { + ++$numberOfRows; // Add row for footer separator + } + + return $numberOfRows; + } + + /** + * fill rows that contains rowspan > 1. + * + * @throws InvalidArgumentException + */ + private function fillNextRows(array $rows, int $line): array + { + $unmergedRows = []; + foreach ($rows[$line] as $column => $cell) { + if (null !== $cell && !$cell instanceof TableCell && !\is_scalar($cell) && !$cell instanceof \Stringable) { + throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell))); + } + if ($cell instanceof TableCell && $cell->getRowspan() > 1) { + $nbLines = $cell->getRowspan() - 1; + $lines = [$cell]; + if (strstr($cell, "\n")) { + $lines = explode("\n", str_replace("\n", "\n", $cell)); + $nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; + + $rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]); + unset($lines[0]); + } + + // create a two dimensional array (rowspan x colspan) + $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows); + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + $value = $lines[$unmergedRowKey - $line] ?? ''; + $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]); + if ($nbLines === $unmergedRowKey - $line) { + break; + } + } + } + } + + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + // we need to know if $unmergedRow will be merged or inserted into $rows + if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { + foreach ($unmergedRow as $cellKey => $cell) { + // insert cell into row at cellKey position + array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]); + } + } else { + $row = $this->copyRow($rows, $unmergedRowKey - 1); + foreach ($unmergedRow as $column => $cell) { + if (!empty($cell)) { + $row[$column] = $unmergedRow[$column]; + } + } + array_splice($rows, $unmergedRowKey, 0, [$row]); + } + } + + return $rows; + } + + /** + * fill cells for a row that contains colspan > 1. + */ + private function fillCells(iterable $row) + { + $newRow = []; + + foreach ($row as $column => $cell) { + $newRow[] = $cell; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) { + // insert empty value at column position + $newRow[] = ''; + } + } + } + + return $newRow ?: $row; + } + + private function copyRow(array $rows, int $line): array + { + $row = $rows[$line]; + foreach ($row as $cellKey => $cellValue) { + $row[$cellKey] = ''; + if ($cellValue instanceof TableCell) { + $row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]); + } + } + + return $row; + } + + /** + * Gets number of columns by row. + */ + private function getNumberOfColumns(array $row): int + { + $columns = \count($row); + foreach ($row as $column) { + $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0; + } + + return $columns; + } + + /** + * Gets list of columns for the given row. + */ + private function getRowColumns(array $row): array + { + $columns = range(0, $this->numberOfColumns - 1); + foreach ($row as $cellKey => $cell) { + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // exclude grouped columns. + $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1)); + } + } + + return $columns; + } + + /** + * Calculates columns widths. + */ + private function calculateColumnsWidth(iterable $groups) + { + for ($column = 0; $column < $this->numberOfColumns; ++$column) { + $lengths = []; + foreach ($groups as $group) { + foreach ($group as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + foreach ($row as $i => $cell) { + if ($cell instanceof TableCell) { + $textContent = Helper::removeDecoration($this->output->getFormatter(), $cell); + $textLength = Helper::width($textContent); + if ($textLength > 0) { + $contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan())); + foreach ($contentColumns as $position => $content) { + $row[$i + $position] = $content; + } + } + } + } + + $lengths[] = $this->getCellWidth($row, $column); + } + } + + $this->effectiveColumnWidths[$column] = max($lengths) + Helper::width($this->style->getCellRowContentFormat()) - 2; + } + } + + private function getColumnSeparatorWidth(): int + { + return Helper::width(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3])); + } + + private function getCellWidth(array $row, int $column): int + { + $cellWidth = 0; + + if (isset($row[$column])) { + $cell = $row[$column]; + $cellWidth = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $cell)); + } + + $columnWidth = $this->columnWidths[$column] ?? 0; + $cellWidth = max($cellWidth, $columnWidth); + + return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth; + } + + /** + * Called after rendering to cleanup cache data. + */ + private function cleanup() + { + $this->effectiveColumnWidths = []; + unset($this->numberOfColumns); + } + + /** + * @return array + */ + private static function initStyles(): array + { + $borderless = new TableStyle(); + $borderless + ->setHorizontalBorderChars('=') + ->setVerticalBorderChars(' ') + ->setDefaultCrossingChar(' ') + ; + + $compact = new TableStyle(); + $compact + ->setHorizontalBorderChars('') + ->setVerticalBorderChars('') + ->setDefaultCrossingChar('') + ->setCellRowContentFormat('%s ') + ; + + $styleGuide = new TableStyle(); + $styleGuide + ->setHorizontalBorderChars('-') + ->setVerticalBorderChars(' ') + ->setDefaultCrossingChar(' ') + ->setCellHeaderFormat('%s') + ; + + $box = (new TableStyle()) + ->setHorizontalBorderChars('─') + ->setVerticalBorderChars('│') + ->setCrossingChars('┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├') + ; + + $boxDouble = (new TableStyle()) + ->setHorizontalBorderChars('═', '─') + ->setVerticalBorderChars('║', '│') + ->setCrossingChars('┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣') + ; + + return [ + 'default' => new TableStyle(), + 'borderless' => $borderless, + 'compact' => $compact, + 'symfony-style-guide' => $styleGuide, + 'box' => $box, + 'box-double' => $boxDouble, + ]; + } + + private function resolveStyle(TableStyle|string $name): TableStyle + { + if ($name instanceof TableStyle) { + return $name; + } + + return self::$styles[$name] ?? throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } +} diff --git a/vendor/symfony/console/Helper/TableCell.php b/vendor/symfony/console/Helper/TableCell.php new file mode 100644 index 0000000..394b2bc --- /dev/null +++ b/vendor/symfony/console/Helper/TableCell.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Abdellatif Ait boudad + */ +class TableCell +{ + private string $value; + private array $options = [ + 'rowspan' => 1, + 'colspan' => 1, + 'style' => null, + ]; + + public function __construct(string $value = '', array $options = []) + { + $this->value = $value; + + // check option names + if ($diff = array_diff(array_keys($options), array_keys($this->options))) { + throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff))); + } + + if (isset($options['style']) && !$options['style'] instanceof TableCellStyle) { + throw new InvalidArgumentException('The style option must be an instance of "TableCellStyle".'); + } + + $this->options = array_merge($this->options, $options); + } + + /** + * Returns the cell value. + */ + public function __toString(): string + { + return $this->value; + } + + /** + * Gets number of colspan. + */ + public function getColspan(): int + { + return (int) $this->options['colspan']; + } + + /** + * Gets number of rowspan. + */ + public function getRowspan(): int + { + return (int) $this->options['rowspan']; + } + + public function getStyle(): ?TableCellStyle + { + return $this->options['style']; + } +} diff --git a/vendor/symfony/console/Helper/TableCellStyle.php b/vendor/symfony/console/Helper/TableCellStyle.php new file mode 100644 index 0000000..65ae9e7 --- /dev/null +++ b/vendor/symfony/console/Helper/TableCellStyle.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Yewhen Khoptynskyi + */ +class TableCellStyle +{ + public const DEFAULT_ALIGN = 'left'; + + private const TAG_OPTIONS = [ + 'fg', + 'bg', + 'options', + ]; + + private const ALIGN_MAP = [ + 'left' => \STR_PAD_RIGHT, + 'center' => \STR_PAD_BOTH, + 'right' => \STR_PAD_LEFT, + ]; + + private array $options = [ + 'fg' => 'default', + 'bg' => 'default', + 'options' => null, + 'align' => self::DEFAULT_ALIGN, + 'cellFormat' => null, + ]; + + public function __construct(array $options = []) + { + if ($diff = array_diff(array_keys($options), array_keys($this->options))) { + throw new InvalidArgumentException(sprintf('The TableCellStyle does not support the following options: \'%s\'.', implode('\', \'', $diff))); + } + + if (isset($options['align']) && !\array_key_exists($options['align'], self::ALIGN_MAP)) { + throw new InvalidArgumentException(sprintf('Wrong align value. Value must be following: \'%s\'.', implode('\', \'', array_keys(self::ALIGN_MAP)))); + } + + $this->options = array_merge($this->options, $options); + } + + public function getOptions(): array + { + return $this->options; + } + + /** + * Gets options we need for tag for example fg, bg. + * + * @return string[] + */ + public function getTagOptions(): array + { + return array_filter( + $this->getOptions(), + function ($key) { + return \in_array($key, self::TAG_OPTIONS) && isset($this->options[$key]); + }, + \ARRAY_FILTER_USE_KEY + ); + } + + public function getPadByAlign(): int + { + return self::ALIGN_MAP[$this->getOptions()['align']]; + } + + public function getCellFormat(): ?string + { + return $this->getOptions()['cellFormat']; + } +} diff --git a/vendor/symfony/console/Helper/TableRows.php b/vendor/symfony/console/Helper/TableRows.php new file mode 100644 index 0000000..97d0772 --- /dev/null +++ b/vendor/symfony/console/Helper/TableRows.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * @internal + */ +class TableRows implements \IteratorAggregate +{ + private \Closure $generator; + + public function __construct(\Closure $generator) + { + $this->generator = $generator; + } + + public function getIterator(): \Traversable + { + return ($this->generator)(); + } +} diff --git a/vendor/symfony/console/Helper/TableSeparator.php b/vendor/symfony/console/Helper/TableSeparator.php new file mode 100644 index 0000000..e541c53 --- /dev/null +++ b/vendor/symfony/console/Helper/TableSeparator.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Marks a row as being a separator. + * + * @author Fabien Potencier + */ +class TableSeparator extends TableCell +{ + public function __construct(array $options = []) + { + parent::__construct('', $options); + } +} diff --git a/vendor/symfony/console/Helper/TableStyle.php b/vendor/symfony/console/Helper/TableStyle.php new file mode 100644 index 0000000..bbad98e --- /dev/null +++ b/vendor/symfony/console/Helper/TableStyle.php @@ -0,0 +1,362 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Defines the styles for a Table. + * + * @author Fabien Potencier + * @author Саша Стаменковић + * @author Dany Maillard + */ +class TableStyle +{ + private string $paddingChar = ' '; + private string $horizontalOutsideBorderChar = '-'; + private string $horizontalInsideBorderChar = '-'; + private string $verticalOutsideBorderChar = '|'; + private string $verticalInsideBorderChar = '|'; + private string $crossingChar = '+'; + private string $crossingTopRightChar = '+'; + private string $crossingTopMidChar = '+'; + private string $crossingTopLeftChar = '+'; + private string $crossingMidRightChar = '+'; + private string $crossingBottomRightChar = '+'; + private string $crossingBottomMidChar = '+'; + private string $crossingBottomLeftChar = '+'; + private string $crossingMidLeftChar = '+'; + private string $crossingTopLeftBottomChar = '+'; + private string $crossingTopMidBottomChar = '+'; + private string $crossingTopRightBottomChar = '+'; + private string $headerTitleFormat = ' %s '; + private string $footerTitleFormat = ' %s '; + private string $cellHeaderFormat = '%s'; + private string $cellRowFormat = '%s'; + private string $cellRowContentFormat = ' %s '; + private string $borderFormat = '%s'; + private int $padType = \STR_PAD_RIGHT; + + /** + * Sets padding character, used for cell padding. + * + * @return $this + */ + public function setPaddingChar(string $paddingChar): static + { + if (!$paddingChar) { + throw new LogicException('The padding char must not be empty.'); + } + + $this->paddingChar = $paddingChar; + + return $this; + } + + /** + * Gets padding character, used for cell padding. + */ + public function getPaddingChar(): string + { + return $this->paddingChar; + } + + /** + * Sets horizontal border characters. + * + * + * ╔═══════════════╤══════════════════════════╤══════════════════╗ + * 1 ISBN 2 Title │ Author ║ + * ╠═══════════════╪══════════════════════════╪══════════════════╣ + * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ + * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ + * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ + * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ + * ╚═══════════════╧══════════════════════════╧══════════════════╝ + * + * + * @return $this + */ + public function setHorizontalBorderChars(string $outside, string $inside = null): static + { + $this->horizontalOutsideBorderChar = $outside; + $this->horizontalInsideBorderChar = $inside ?? $outside; + + return $this; + } + + /** + * Sets vertical border characters. + * + * + * ╔═══════════════╤══════════════════════════╤══════════════════╗ + * ║ ISBN │ Title │ Author ║ + * ╠═══════1═══════╪══════════════════════════╪══════════════════╣ + * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ + * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ + * ╟───────2───────┼──────────────────────────┼──────────────────╢ + * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ + * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ + * ╚═══════════════╧══════════════════════════╧══════════════════╝ + * + * + * @return $this + */ + public function setVerticalBorderChars(string $outside, string $inside = null): static + { + $this->verticalOutsideBorderChar = $outside; + $this->verticalInsideBorderChar = $inside ?? $outside; + + return $this; + } + + /** + * Gets border characters. + * + * @internal + */ + public function getBorderChars(): array + { + return [ + $this->horizontalOutsideBorderChar, + $this->verticalOutsideBorderChar, + $this->horizontalInsideBorderChar, + $this->verticalInsideBorderChar, + ]; + } + + /** + * Sets crossing characters. + * + * Example: + * + * 1═══════════════2══════════════════════════2══════════════════3 + * ║ ISBN │ Title │ Author ║ + * 8'══════════════0'═════════════════════════0'═════════════════4' + * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ + * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ + * 8───────────────0──────────────────────────0──────────────────4 + * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ + * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ + * 7═══════════════6══════════════════════════6══════════════════5 + * + * + * @param string $cross Crossing char (see #0 of example) + * @param string $topLeft Top left char (see #1 of example) + * @param string $topMid Top mid char (see #2 of example) + * @param string $topRight Top right char (see #3 of example) + * @param string $midRight Mid right char (see #4 of example) + * @param string $bottomRight Bottom right char (see #5 of example) + * @param string $bottomMid Bottom mid char (see #6 of example) + * @param string $bottomLeft Bottom left char (see #7 of example) + * @param string $midLeft Mid left char (see #8 of example) + * @param string|null $topLeftBottom Top left bottom char (see #8' of example), equals to $midLeft if null + * @param string|null $topMidBottom Top mid bottom char (see #0' of example), equals to $cross if null + * @param string|null $topRightBottom Top right bottom char (see #4' of example), equals to $midRight if null + * + * @return $this + */ + public function setCrossingChars(string $cross, string $topLeft, string $topMid, string $topRight, string $midRight, string $bottomRight, string $bottomMid, string $bottomLeft, string $midLeft, string $topLeftBottom = null, string $topMidBottom = null, string $topRightBottom = null): static + { + $this->crossingChar = $cross; + $this->crossingTopLeftChar = $topLeft; + $this->crossingTopMidChar = $topMid; + $this->crossingTopRightChar = $topRight; + $this->crossingMidRightChar = $midRight; + $this->crossingBottomRightChar = $bottomRight; + $this->crossingBottomMidChar = $bottomMid; + $this->crossingBottomLeftChar = $bottomLeft; + $this->crossingMidLeftChar = $midLeft; + $this->crossingTopLeftBottomChar = $topLeftBottom ?? $midLeft; + $this->crossingTopMidBottomChar = $topMidBottom ?? $cross; + $this->crossingTopRightBottomChar = $topRightBottom ?? $midRight; + + return $this; + } + + /** + * Sets default crossing character used for each cross. + * + * @see {@link setCrossingChars()} for setting each crossing individually. + */ + public function setDefaultCrossingChar(string $char): self + { + return $this->setCrossingChars($char, $char, $char, $char, $char, $char, $char, $char, $char); + } + + /** + * Gets crossing character. + */ + public function getCrossingChar(): string + { + return $this->crossingChar; + } + + /** + * Gets crossing characters. + * + * @internal + */ + public function getCrossingChars(): array + { + return [ + $this->crossingChar, + $this->crossingTopLeftChar, + $this->crossingTopMidChar, + $this->crossingTopRightChar, + $this->crossingMidRightChar, + $this->crossingBottomRightChar, + $this->crossingBottomMidChar, + $this->crossingBottomLeftChar, + $this->crossingMidLeftChar, + $this->crossingTopLeftBottomChar, + $this->crossingTopMidBottomChar, + $this->crossingTopRightBottomChar, + ]; + } + + /** + * Sets header cell format. + * + * @return $this + */ + public function setCellHeaderFormat(string $cellHeaderFormat): static + { + $this->cellHeaderFormat = $cellHeaderFormat; + + return $this; + } + + /** + * Gets header cell format. + */ + public function getCellHeaderFormat(): string + { + return $this->cellHeaderFormat; + } + + /** + * Sets row cell format. + * + * @return $this + */ + public function setCellRowFormat(string $cellRowFormat): static + { + $this->cellRowFormat = $cellRowFormat; + + return $this; + } + + /** + * Gets row cell format. + */ + public function getCellRowFormat(): string + { + return $this->cellRowFormat; + } + + /** + * Sets row cell content format. + * + * @return $this + */ + public function setCellRowContentFormat(string $cellRowContentFormat): static + { + $this->cellRowContentFormat = $cellRowContentFormat; + + return $this; + } + + /** + * Gets row cell content format. + */ + public function getCellRowContentFormat(): string + { + return $this->cellRowContentFormat; + } + + /** + * Sets table border format. + * + * @return $this + */ + public function setBorderFormat(string $borderFormat): static + { + $this->borderFormat = $borderFormat; + + return $this; + } + + /** + * Gets table border format. + */ + public function getBorderFormat(): string + { + return $this->borderFormat; + } + + /** + * Sets cell padding type. + * + * @return $this + */ + public function setPadType(int $padType): static + { + if (!\in_array($padType, [\STR_PAD_LEFT, \STR_PAD_RIGHT, \STR_PAD_BOTH], true)) { + throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).'); + } + + $this->padType = $padType; + + return $this; + } + + /** + * Gets cell padding type. + */ + public function getPadType(): int + { + return $this->padType; + } + + public function getHeaderTitleFormat(): string + { + return $this->headerTitleFormat; + } + + /** + * @return $this + */ + public function setHeaderTitleFormat(string $format): static + { + $this->headerTitleFormat = $format; + + return $this; + } + + public function getFooterTitleFormat(): string + { + return $this->footerTitleFormat; + } + + /** + * @return $this + */ + public function setFooterTitleFormat(string $format): static + { + $this->footerTitleFormat = $format; + + return $this; + } +} diff --git a/vendor/symfony/console/Input/ArgvInput.php b/vendor/symfony/console/Input/ArgvInput.php new file mode 100644 index 0000000..4e90c81 --- /dev/null +++ b/vendor/symfony/console/Input/ArgvInput.php @@ -0,0 +1,376 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * ArgvInput represents an input coming from the CLI arguments. + * + * Usage: + * + * $input = new ArgvInput(); + * + * By default, the `$_SERVER['argv']` array is used for the input values. + * + * This can be overridden by explicitly passing the input values in the constructor: + * + * $input = new ArgvInput($_SERVER['argv']); + * + * If you pass it yourself, don't forget that the first element of the array + * is the name of the running application. + * + * When passing an argument to the constructor, be sure that it respects + * the same rules as the argv one. It's almost always better to use the + * `StringInput` when you want to provide your own input. + * + * @author Fabien Potencier + * + * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html + * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 + */ +class ArgvInput extends Input +{ + private array $tokens; + private array $parsed; + + public function __construct(array $argv = null, InputDefinition $definition = null) + { + $argv = $argv ?? $_SERVER['argv'] ?? []; + + // strip the application name + array_shift($argv); + + $this->tokens = $argv; + + parent::__construct($definition); + } + + protected function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * {@inheritdoc} + */ + protected function parse() + { + $parseOptions = true; + $this->parsed = $this->tokens; + while (null !== $token = array_shift($this->parsed)) { + $parseOptions = $this->parseToken($token, $parseOptions); + } + } + + protected function parseToken(string $token, bool $parseOptions): bool + { + if ($parseOptions && '' == $token) { + $this->parseArgument($token); + } elseif ($parseOptions && '--' == $token) { + return false; + } elseif ($parseOptions && str_starts_with($token, '--')) { + $this->parseLongOption($token); + } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); + } + + return $parseOptions; + } + + /** + * Parses a short option. + */ + private function parseShortOption(string $token) + { + $name = substr($token, 1); + + if (\strlen($name) > 1) { + if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { + // an option with a value (with no space) + $this->addShortOption($name[0], substr($name, 1)); + } else { + $this->parseShortOptionSet($name); + } + } else { + $this->addShortOption($name, null); + } + } + + /** + * Parses a short option set. + * + * @throws RuntimeException When option given doesn't exist + */ + private function parseShortOptionSet(string $name) + { + $len = \strlen($name); + for ($i = 0; $i < $len; ++$i) { + if (!$this->definition->hasShortcut($name[$i])) { + $encoding = mb_detect_encoding($name, null, true); + throw new RuntimeException(sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding))); + } + + $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); + } + } + } + + /** + * Parses a long option. + */ + private function parseLongOption(string $token) + { + $name = substr($token, 2); + + if (false !== $pos = strpos($name, '=')) { + if ('' === $value = substr($name, $pos + 1)) { + array_unshift($this->parsed, $value); + } + $this->addLongOption(substr($name, 0, $pos), $value); + } else { + $this->addLongOption($name, null); + } + } + + /** + * Parses an argument. + * + * @throws RuntimeException When too many arguments are given + */ + private function parseArgument(string $token) + { + $c = \count($this->arguments); + + // if input is expecting another argument, add it + if ($this->definition->hasArgument($c)) { + $arg = $this->definition->getArgument($c); + $this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token; + + // if last argument isArray(), append token to last argument + } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { + $arg = $this->definition->getArgument($c - 1); + $this->arguments[$arg->getName()][] = $token; + + // unexpected argument + } else { + $all = $this->definition->getArguments(); + $symfonyCommandName = null; + if (($inputArgument = $all[$key = array_key_first($all)] ?? null) && 'command' === $inputArgument->getName()) { + $symfonyCommandName = $this->arguments['command'] ?? null; + unset($all[$key]); + } + + if (\count($all)) { + if ($symfonyCommandName) { + $message = sprintf('Too many arguments to "%s" command, expected arguments "%s".', $symfonyCommandName, implode('" "', array_keys($all))); + } else { + $message = sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all))); + } + } elseif ($symfonyCommandName) { + $message = sprintf('No arguments expected for "%s" command, got "%s".', $symfonyCommandName, $token); + } else { + $message = sprintf('No arguments expected, got "%s".', $token); + } + + throw new RuntimeException($message); + } + } + + /** + * Adds a short option value. + * + * @throws RuntimeException When option given doesn't exist + */ + private function addShortOption(string $shortcut, mixed $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @throws RuntimeException When option given doesn't exist + */ + private function addLongOption(string $name, mixed $value) + { + if (!$this->definition->hasOption($name)) { + if (!$this->definition->hasNegation($name)) { + throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + $optionName = $this->definition->negationToName($name); + if (null !== $value) { + throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); + } + $this->options[$optionName] = false; + + return; + } + + $option = $this->definition->getOption($name); + + if (null !== $value && !$option->acceptValue()) { + throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); + } + + if (\in_array($value, ['', null], true) && $option->acceptValue() && \count($this->parsed)) { + // if option accepts an optional or mandatory argument + // let's see if there is one provided + $next = array_shift($this->parsed); + if ((isset($next[0]) && '-' !== $next[0]) || \in_array($next, ['', null], true)) { + $value = $next; + } 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() && !$option->isValueOptional()) { + $value = true; + } + } + + if ($option->isArray()) { + $this->options[$name][] = $value; + } else { + $this->options[$name] = $value; + } + } + + /** + * {@inheritdoc} + */ + public function getFirstArgument(): ?string + { + $isOption = false; + foreach ($this->tokens as $i => $token) { + if ($token && '-' === $token[0]) { + if (str_contains($token, '=') || !isset($this->tokens[$i + 1])) { + continue; + } + + // If it's a long option, consider that everything after "--" is the option name. + // Otherwise, use the last char (if it's a short option set, only the last one can take a value with space separator) + $name = '-' === $token[1] ? substr($token, 2) : substr($token, -1); + if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) { + // noop + } elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) { + $isOption = true; + } + + continue; + } + + if ($isOption) { + $isOption = false; + continue; + } + + return $token; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function hasParameterOption(string|array $values, bool $onlyParams = false): bool + { + $values = (array) $values; + + foreach ($this->tokens as $token) { + if ($onlyParams && '--' === $token) { + return false; + } + foreach ($values as $value) { + // Options with values: + // For long options, test for '--option=' at beginning + // For short options, test for '-o' at beginning + $leading = str_starts_with($value, '--') ? $value.'=' : $value; + if ($token === $value || '' !== $leading && str_starts_with($token, $leading)) { + return true; + } + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false): mixed + { + $values = (array) $values; + $tokens = $this->tokens; + + while (0 < \count($tokens)) { + $token = array_shift($tokens); + if ($onlyParams && '--' === $token) { + return $default; + } + + foreach ($values as $value) { + if ($token === $value) { + return array_shift($tokens); + } + // Options with values: + // For long options, test for '--option=' at beginning + // For short options, test for '-o' at beginning + $leading = str_starts_with($value, '--') ? $value.'=' : $value; + if ('' !== $leading && str_starts_with($token, $leading)) { + return substr($token, \strlen($leading)); + } + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + */ + public function __toString(): string + { + $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/symfony/console/Input/ArrayInput.php b/vendor/symfony/console/Input/ArrayInput.php new file mode 100644 index 0000000..fdb47df --- /dev/null +++ b/vendor/symfony/console/Input/ArrayInput.php @@ -0,0 +1,205 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\InvalidOptionException; + +/** + * ArrayInput represents an input provided as an array. + * + * Usage: + * + * $input = new ArrayInput(['command' => 'foo:bar', 'foo' => 'bar', '--bar' => 'foobar']); + * + * @author Fabien Potencier + */ +class ArrayInput extends Input +{ + private array $parameters; + + public function __construct(array $parameters, InputDefinition $definition = null) + { + $this->parameters = $parameters; + + parent::__construct($definition); + } + + /** + * {@inheritdoc} + */ + public function getFirstArgument(): ?string + { + foreach ($this->parameters as $param => $value) { + if ($param && \is_string($param) && '-' === $param[0]) { + continue; + } + + return $value; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function hasParameterOption(string|array $values, bool $onlyParams = false): bool + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (!\is_int($k)) { + $v = $k; + } + + if ($onlyParams && '--' === $v) { + return false; + } + + if (\in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false): mixed + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if ($onlyParams && ('--' === $k || (\is_int($k) && '--' === $v))) { + return $default; + } + + if (\is_int($k)) { + if (\in_array($v, $values)) { + return true; + } + } elseif (\in_array($k, $values)) { + return $v; + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + */ + public function __toString(): string + { + $params = []; + foreach ($this->parameters as $param => $val) { + if ($param && \is_string($param) && '-' === $param[0]) { + $glue = ('-' === $param[1]) ? '=' : ' '; + if (\is_array($val)) { + foreach ($val as $v) { + $params[] = $param.('' != $v ? $glue.$this->escapeToken($v) : ''); + } + } else { + $params[] = $param.('' != $val ? $glue.$this->escapeToken($val) : ''); + } + } else { + $params[] = \is_array($val) ? implode(' ', array_map([$this, 'escapeToken'], $val)) : $this->escapeToken($val); + } + } + + return implode(' ', $params); + } + + /** + * {@inheritdoc} + */ + protected function parse() + { + foreach ($this->parameters as $key => $value) { + if ('--' === $key) { + return; + } + if (str_starts_with($key, '--')) { + $this->addLongOption(substr($key, 2), $value); + } elseif (str_starts_with($key, '-')) { + $this->addShortOption(substr($key, 1), $value); + } else { + $this->addArgument($key, $value); + } + } + } + + /** + * Adds a short option value. + * + * @throws InvalidOptionException When option given doesn't exist + */ + private function addShortOption(string $shortcut, mixed $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @throws InvalidOptionException When option given doesn't exist + * @throws InvalidOptionException When a required value is missing + */ + private function addLongOption(string $name, mixed $value) + { + if (!$this->definition->hasOption($name)) { + if (!$this->definition->hasNegation($name)) { + throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name)); + } + + $optionName = $this->definition->negationToName($name); + $this->options[$optionName] = false; + + return; + } + + $option = $this->definition->getOption($name); + + if (null === $value) { + if ($option->isValueRequired()) { + throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name)); + } + + if (!$option->isValueOptional()) { + $value = true; + } + } + + $this->options[$name] = $value; + } + + /** + * Adds an argument value. + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + private function addArgument(string|int $name, mixed $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } +} diff --git a/vendor/symfony/console/Input/Input.php b/vendor/symfony/console/Input/Input.php new file mode 100644 index 0000000..1db503c --- /dev/null +++ b/vendor/symfony/console/Input/Input.php @@ -0,0 +1,211 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * Input is the base class for all concrete Input classes. + * + * Three concrete classes are provided by default: + * + * * `ArgvInput`: The input comes from the CLI arguments (argv) + * * `StringInput`: The input is provided as a string + * * `ArrayInput`: The input is provided as an array + * + * @author Fabien Potencier + */ +abstract class Input implements InputInterface, StreamableInputInterface +{ + protected $definition; + protected $stream; + protected $options = []; + protected $arguments = []; + protected $interactive = true; + + public function __construct(InputDefinition $definition = null) + { + if (null === $definition) { + $this->definition = new InputDefinition(); + } else { + $this->bind($definition); + $this->validate(); + } + } + + /** + * {@inheritdoc} + */ + public function bind(InputDefinition $definition) + { + $this->arguments = []; + $this->options = []; + $this->definition = $definition; + + $this->parse(); + } + + /** + * Processes command line arguments. + */ + abstract protected function parse(); + + /** + * {@inheritdoc} + */ + public function validate() + { + $definition = $this->definition; + $givenArguments = $this->arguments; + + $missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) { + return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired(); + }); + + if (\count($missingArguments) > 0) { + throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))); + } + } + + /** + * {@inheritdoc} + */ + public function isInteractive(): bool + { + return $this->interactive; + } + + /** + * {@inheritdoc} + */ + public function setInteractive(bool $interactive) + { + $this->interactive = $interactive; + } + + /** + * {@inheritdoc} + */ + public function getArguments(): array + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + /** + * {@inheritdoc} + */ + public function getArgument(string $name): mixed + { + 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(); + } + + /** + * {@inheritdoc} + */ + public function setArgument(string $name, mixed $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function hasArgument(string $name): bool + { + return $this->definition->hasArgument($name); + } + + /** + * {@inheritdoc} + */ + public function getOptions(): array + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + /** + * {@inheritdoc} + */ + public function getOption(string $name): mixed + { + if ($this->definition->hasNegation($name)) { + if (null === $value = $this->getOption($this->definition->negationToName($name))) { + return $value; + } + + return !$value; + } + + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + } + + /** + * {@inheritdoc} + */ + public function setOption(string $name, mixed $value) + { + if ($this->definition->hasNegation($name)) { + $this->options[$this->definition->negationToName($name)] = !$value; + + return; + } elseif (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + $this->options[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function hasOption(string $name): bool + { + return $this->definition->hasOption($name) || $this->definition->hasNegation($name); + } + + /** + * Escapes a token through escapeshellarg if it contains unsafe chars. + */ + public function escapeToken(string $token): string + { + return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token); + } + + /** + * {@inheritdoc} + */ + public function setStream($stream) + { + $this->stream = $stream; + } + + /** + * {@inheritdoc} + */ + public function getStream() + { + return $this->stream; + } +} diff --git a/vendor/symfony/console/Input/InputArgument.php b/vendor/symfony/console/Input/InputArgument.php new file mode 100644 index 0000000..1e666ee --- /dev/null +++ b/vendor/symfony/console/Input/InputArgument.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line argument. + * + * @author Fabien Potencier + */ +class InputArgument +{ + public const REQUIRED = 1; + public const OPTIONAL = 2; + public const IS_ARRAY = 4; + + private string $name; + private int $mode; + private string|int|bool|array|null|float $default; + private string $description; + + /** + * @param string $name The argument name + * @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only) + * + * @throws InvalidArgumentException When argument mode is not valid + */ + public function __construct(string $name, int $mode = null, string $description = '', string|bool|int|float|array $default = null) + { + if (null === $mode) { + $mode = self::OPTIONAL; + } elseif ($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); + } + + /** + * Returns the argument name. + */ + public function getName(): string + { + return $this->name; + } + + /** + * Returns true if the argument is required. + * + * @return bool true if parameter mode is self::REQUIRED, false otherwise + */ + public function isRequired(): bool + { + return self::REQUIRED === (self::REQUIRED & $this->mode); + } + + /** + * Returns true if the argument can take multiple values. + * + * @return bool true if mode is self::IS_ARRAY, false otherwise + */ + public function isArray(): bool + { + return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault(string|bool|int|float|array $default = null) + { + if ($this->isRequired() && 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; + } + + /** + * Returns the default value. + */ + public function getDefault(): string|bool|int|float|array|null + { + return $this->default; + } + + /** + * Returns the description text. + */ + public function getDescription(): string + { + return $this->description; + } +} diff --git a/vendor/symfony/console/Input/InputAwareInterface.php b/vendor/symfony/console/Input/InputAwareInterface.php new file mode 100644 index 0000000..5a288de --- /dev/null +++ b/vendor/symfony/console/Input/InputAwareInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * InputAwareInterface should be implemented by classes that depends on the + * Console Input. + * + * @author Wouter J + */ +interface InputAwareInterface +{ + /** + * Sets the Console Input. + */ + public function setInput(InputInterface $input); +} diff --git a/vendor/symfony/console/Input/InputDefinition.php b/vendor/symfony/console/Input/InputDefinition.php new file mode 100644 index 0000000..cb270d8 --- /dev/null +++ b/vendor/symfony/console/Input/InputDefinition.php @@ -0,0 +1,402 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * A InputDefinition represents a set of valid command line arguments and options. + * + * Usage: + * + * $definition = new InputDefinition([ + * new InputArgument('name', InputArgument::REQUIRED), + * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), + * ]); + * + * @author Fabien Potencier + */ +class InputDefinition +{ + private array $arguments = []; + private int $requiredCount = 0; + private $lastArrayArgument = null; + private $lastOptionalArgument = null; + private array $options = []; + private array $negations = []; + private array $shortcuts = []; + + /** + * @param array $definition An array of InputArgument and InputOption instance + */ + public function __construct(array $definition = []) + { + $this->setDefinition($definition); + } + + /** + * Sets the definition of the input. + */ + public function setDefinition(array $definition) + { + $arguments = []; + $options = []; + foreach ($definition as $item) { + if ($item instanceof InputOption) { + $options[] = $item; + } else { + $arguments[] = $item; + } + } + + $this->setArguments($arguments); + $this->setOptions($options); + } + + /** + * Sets the InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function setArguments(array $arguments = []) + { + $this->arguments = []; + $this->requiredCount = 0; + $this->lastOptionalArgument = null; + $this->lastArrayArgument = null; + $this->addArguments($arguments); + } + + /** + * Adds an array of InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function addArguments(?array $arguments = []) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + } + } + + /** + * @throws LogicException When incorrect argument is given + */ + public function addArgument(InputArgument $argument) + { + if (isset($this->arguments[$argument->getName()])) { + throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName())); + } + + if (null !== $this->lastArrayArgument) { + throw new LogicException(sprintf('Cannot add a required argument "%s" after an array argument "%s".', $argument->getName(), $this->lastArrayArgument->getName())); + } + + if ($argument->isRequired() && null !== $this->lastOptionalArgument) { + throw new LogicException(sprintf('Cannot add a required argument "%s" after an optional one "%s".', $argument->getName(), $this->lastOptionalArgument->getName())); + } + + if ($argument->isArray()) { + $this->lastArrayArgument = $argument; + } + + if ($argument->isRequired()) { + ++$this->requiredCount; + } else { + $this->lastOptionalArgument = $argument; + } + + $this->arguments[$argument->getName()] = $argument; + } + + /** + * Returns an InputArgument by name or by position. + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument(string|int $name): InputArgument + { + 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]; + } + + /** + * Returns true if an InputArgument object exists by name or position. + */ + public function hasArgument(string|int $name): bool + { + $arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments; + + return isset($arguments[$name]); + } + + /** + * Gets the array of InputArgument objects. + * + * @return InputArgument[] + */ + public function getArguments(): array + { + return $this->arguments; + } + + /** + * Returns the number of InputArguments. + */ + public function getArgumentCount(): int + { + return null !== $this->lastArrayArgument ? \PHP_INT_MAX : \count($this->arguments); + } + + /** + * Returns the number of required InputArguments. + */ + 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; + } + + /** + * Sets the InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function setOptions(array $options = []) + { + $this->options = []; + $this->shortcuts = []; + $this->negations = []; + $this->addOptions($options); + } + + /** + * Adds an array of InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function addOptions(array $options = []) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * @throws LogicException When option given already exist + */ + public function addOption(InputOption $option) + { + 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 (isset($this->negations[$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(); + } + } + + if ($option->isNegatable()) { + $negatedName = 'no-'.$option->getName(); + if (isset($this->options[$negatedName])) { + throw new LogicException(sprintf('An option named "%s" already exists.', $negatedName)); + } + $this->negations[$negatedName] = $option->getName(); + } + } + + /** + * Returns an InputOption by name. + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption(string $name): InputOption + { + if (!$this->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->options[$name]; + } + + /** + * Returns true if an InputOption object exists by name. + * + * This method can't be used to check if the user included the option when + * executing the command (use getOption() instead). + */ + public function hasOption(string $name): bool + { + return isset($this->options[$name]); + } + + /** + * Gets the array of InputOption objects. + * + * @return InputOption[] + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * Returns true if an InputOption object exists by shortcut. + */ + public function hasShortcut(string $name): bool + { + return isset($this->shortcuts[$name]); + } + + /** + * Returns true if an InputOption object exists by negated name. + */ + public function hasNegation(string $name): bool + { + return isset($this->negations[$name]); + } + + /** + * Gets an InputOption by shortcut. + */ + public function getOptionForShortcut(string $shortcut): InputOption + { + 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; + } + + /** + * Returns the InputOption name given a shortcut. + * + * @throws InvalidArgumentException When option given does not exist + * + * @internal + */ + public 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]; + } + + /** + * Returns the InputOption name given a negation. + * + * @throws InvalidArgumentException When option given does not exist + * + * @internal + */ + public function negationToName(string $negation): string + { + if (!isset($this->negations[$negation])) { + throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $negation)); + } + + return $this->negations[$negation]; + } + + /** + * Gets the synopsis. + */ + 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()) : ''; + $negation = $option->isNegatable() ? sprintf('|--no-%s', $option->getName()) : ''; + $elements[] = sprintf('[%s--%s%s%s]', $shortcut, $option->getName(), $value, $negation); + } + } + + if (\count($elements) && $this->getArguments()) { + $elements[] = '[--]'; + } + + $tail = ''; + foreach ($this->getArguments() as $argument) { + $element = '<'.$argument->getName().'>'; + if ($argument->isArray()) { + $element .= '...'; + } + + if (!$argument->isRequired()) { + $element = '['.$element; + $tail .= ']'; + } + + $elements[] = $element; + } + + return implode(' ', $elements).$tail; + } +} diff --git a/vendor/symfony/console/Input/InputInterface.php b/vendor/symfony/console/Input/InputInterface.php new file mode 100644 index 0000000..024da18 --- /dev/null +++ b/vendor/symfony/console/Input/InputInterface.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * InputInterface is the interface implemented by all input classes. + * + * @author Fabien Potencier + */ +interface InputInterface +{ + /** + * Returns the first argument from the raw parameters (not parsed). + */ + public function getFirstArgument(): ?string; + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * Does not necessarily return the correct result for short options + * when multiple flags are combined in the same option. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + */ + public function hasParameterOption(string|array $values, bool $onlyParams = false): bool; + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * Does not necessarily return the correct result for short options + * when multiple flags are combined in the same option. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param string|bool|int|float|array|null $default The default value to return if no result is found + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return mixed + */ + public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false); + + /** + * Binds the current Input instance with the given arguments and options. + * + * @throws RuntimeException + */ + public function bind(InputDefinition $definition); + + /** + * Validates the input. + * + * @throws RuntimeException When not enough arguments are given + */ + public function validate(); + + /** + * Returns all the given arguments merged with the default values. + * + * @return array + */ + public function getArguments(): array; + + /** + * Returns the argument value for a given argument name. + * + * @return mixed + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument(string $name); + + /** + * Sets an argument value by name. + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function setArgument(string $name, mixed $value); + + /** + * Returns true if an InputArgument object exists by name or position. + */ + public function hasArgument(string $name): bool; + + /** + * Returns all the given options merged with the default values. + * + * @return array + */ + public function getOptions(): array; + + /** + * Returns the option value for a given option name. + * + * @return mixed + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption(string $name); + + /** + * Sets an option value by name. + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function setOption(string $name, mixed $value); + + /** + * Returns true if an InputOption object exists by name. + */ + public function hasOption(string $name): bool; + + /** + * Is this input means interactive? + */ + public function isInteractive(): bool; + + /** + * Sets the input interactivity. + */ + public function setInteractive(bool $interactive); +} diff --git a/vendor/symfony/console/Input/InputOption.php b/vendor/symfony/console/Input/InputOption.php new file mode 100644 index 0000000..f9d74a8 --- /dev/null +++ b/vendor/symfony/console/Input/InputOption.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line option. + * + * @author Fabien Potencier + */ +class InputOption +{ + /** + * Do not accept input for the option (e.g. --yell). This is the default behavior of options. + */ + public const VALUE_NONE = 1; + + /** + * A value must be passed when the option is used (e.g. --iterations=5 or -i5). + */ + public const VALUE_REQUIRED = 2; + + /** + * The option may or may not have a value (e.g. --yell or --yell=loud). + */ + public const VALUE_OPTIONAL = 4; + + /** + * The option accepts multiple values (e.g. --dir=/foo --dir=/bar). + */ + public const VALUE_IS_ARRAY = 8; + + /** + * The option may have either positive or negative value (e.g. --ansi or --no-ansi). + */ + public const VALUE_NEGATABLE = 16; + + private string $name; + private string|array|null $shortcut; + private int $mode; + private string|int|bool|array|null|float $default; + private string $description; + + /** + * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int|null $mode The option mode: One of the VALUE_* constants + * @param string|bool|int|float|array|null $default The default value (must be null for self::VALUE_NONE) + * + * @throws InvalidArgumentException If option mode is invalid or incompatible + */ + public function __construct(string $name, string|array $shortcut = null, int $mode = null, string $description = '', string|bool|int|float|array $default = null) + { + if (str_starts_with($name, '--')) { + $name = substr($name, 2); + } + + if (empty($name)) { + throw new InvalidArgumentException('An option name cannot be empty.'); + } + + if (empty($shortcut)) { + $shortcut = null; + } + + if (null !== $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 ($mode >= (self::VALUE_NEGATABLE << 1) || $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.'); + } + if ($this->isNegatable() && $this->acceptValue()) { + throw new InvalidArgumentException('Impossible to have an option mode VALUE_NEGATABLE if the option also accepts a value.'); + } + + $this->setDefault($default); + } + + /** + * Returns the option shortcut. + */ + public function getShortcut(): ?string + { + return $this->shortcut; + } + + /** + * Returns the option name. + */ + public function getName(): string + { + return $this->name; + } + + /** + * Returns true if the option accepts a value. + * + * @return bool true if value mode is not self::VALUE_NONE, false otherwise + */ + public function acceptValue(): bool + { + return $this->isValueRequired() || $this->isValueOptional(); + } + + /** + * Returns true if the option requires a value. + * + * @return bool true if value mode is self::VALUE_REQUIRED, false otherwise + */ + public function isValueRequired(): bool + { + return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); + } + + /** + * Returns true if the option takes an optional value. + * + * @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise + */ + public function isValueOptional(): bool + { + return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); + } + + /** + * Returns true if the option can take multiple values. + * + * @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise + */ + public function isArray(): bool + { + return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); + } + + public function isNegatable(): bool + { + return self::VALUE_NEGATABLE === (self::VALUE_NEGATABLE & $this->mode); + } + + public function setDefault(string|bool|int|float|array $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() || $this->isNegatable() ? $default : false; + } + + /** + * Returns the default value. + */ + public function getDefault(): string|bool|int|float|array|null + { + return $this->default; + } + + /** + * Returns the description text. + */ + public function getDescription(): string + { + return $this->description; + } + + /** + * Checks whether the given option equals this one. + */ + public function equals(self $option): bool + { + return $option->getName() === $this->getName() + && $option->getShortcut() === $this->getShortcut() + && $option->getDefault() === $this->getDefault() + && $option->isNegatable() === $this->isNegatable() + && $option->isArray() === $this->isArray() + && $option->isValueRequired() === $this->isValueRequired() + && $option->isValueOptional() === $this->isValueOptional() + ; + } +} diff --git a/vendor/symfony/console/Input/StreamableInputInterface.php b/vendor/symfony/console/Input/StreamableInputInterface.php new file mode 100644 index 0000000..d7e462f --- /dev/null +++ b/vendor/symfony/console/Input/StreamableInputInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * StreamableInputInterface is the interface implemented by all input classes + * that have an input stream. + * + * @author Robin Chalas + */ +interface StreamableInputInterface extends InputInterface +{ + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + */ + public function setStream($stream); + + /** + * Returns the input stream. + * + * @return resource|null + */ + public function getStream(); +} diff --git a/vendor/symfony/console/Input/StringInput.php b/vendor/symfony/console/Input/StringInput.php new file mode 100644 index 0000000..56bb66c --- /dev/null +++ b/vendor/symfony/console/Input/StringInput.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * StringInput represents an input provided as a string. + * + * Usage: + * + * $input = new StringInput('foo --bar="foobar"'); + * + * @author Fabien Potencier + */ +class StringInput extends ArgvInput +{ + public const REGEX_STRING = '([^\s]+?)(?:\s|(?setTokens($this->tokenize($input)); + } + + /** + * Tokenizes a string. + * + * @throws InvalidArgumentException When unable to parse input (should never happen) + */ + private function tokenize(string $input): array + { + $tokens = []; + $length = \strlen($input); + $cursor = 0; + $token = null; + while ($cursor < $length) { + if ('\\' === $input[$cursor]) { + $token .= $input[++$cursor] ?? ''; + ++$cursor; + continue; + } + + if (preg_match('/\s+/A', $input, $match, 0, $cursor)) { + if (null !== $token) { + $tokens[] = $token; + $token = null; + } + } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, 0, $cursor)) { + $token .= $match[1].$match[2].stripcslashes(str_replace(['"\'', '\'"', '\'\'', '""'], '', substr($match[3], 1, -1))); + } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, 0, $cursor)) { + $token .= stripcslashes(substr($match[0], 1, -1)); + } elseif (preg_match('/'.self::REGEX_UNQUOTED_STRING.'/A', $input, $match, 0, $cursor)) { + $token .= $match[1]; + } else { + // should never happen + throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ...".', substr($input, $cursor, 10))); + } + + $cursor += \strlen($match[0]); + } + + if (null !== $token) { + $tokens[] = $token; + } + + return $tokens; + } +} diff --git a/vendor/symfony/console/LICENSE b/vendor/symfony/console/LICENSE new file mode 100644 index 0000000..0083704 --- /dev/null +++ b/vendor/symfony/console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2023 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/console/Logger/ConsoleLogger.php b/vendor/symfony/console/Logger/ConsoleLogger.php new file mode 100644 index 0000000..c9714c3 --- /dev/null +++ b/vendor/symfony/console/Logger/ConsoleLogger.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Logger; + +use Psr\Log\AbstractLogger; +use Psr\Log\InvalidArgumentException; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * PSR-3 compliant console logger. + * + * @author Kévin Dunglas + * + * @see https://www.php-fig.org/psr/psr-3/ + */ +class ConsoleLogger extends AbstractLogger +{ + public const INFO = 'info'; + public const ERROR = 'error'; + + private $output; + private array $verbosityLevelMap = [ + LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, + LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, + LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, + LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE, + LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE, + LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG, + ]; + private array $formatLevelMap = [ + LogLevel::EMERGENCY => self::ERROR, + LogLevel::ALERT => self::ERROR, + LogLevel::CRITICAL => self::ERROR, + LogLevel::ERROR => self::ERROR, + LogLevel::WARNING => self::INFO, + LogLevel::NOTICE => self::INFO, + LogLevel::INFO => self::INFO, + LogLevel::DEBUG => self::INFO, + ]; + private bool $errored = false; + + public function __construct(OutputInterface $output, array $verbosityLevelMap = [], array $formatLevelMap = []) + { + $this->output = $output; + $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap; + $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap; + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = []): void + { + if (!isset($this->verbosityLevelMap[$level])) { + throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level)); + } + + $output = $this->output; + + // Write to the error output if necessary and available + if (self::ERROR === $this->formatLevelMap[$level]) { + if ($this->output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + $this->errored = true; + } + + // the if condition check isn't necessary -- it's the same one that $output will do internally anyway. + // We only do it for efficiency here as the message formatting is relatively expensive. + if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) { + $output->writeln(sprintf('<%1$s>[%2$s] %3$s', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)), $this->verbosityLevelMap[$level]); + } + } + + /** + * Returns true when any messages have been logged at error levels. + */ + public function hasErrored(): bool + { + return $this->errored; + } + + /** + * Interpolates context values into the message placeholders. + * + * @author PHP Framework Interoperability Group + */ + private function interpolate(string $message, array $context): string + { + if (!str_contains($message, '{')) { + return $message; + } + + $replacements = []; + foreach ($context as $key => $val) { + if (null === $val || \is_scalar($val) || $val instanceof \Stringable) { + $replacements["{{$key}}"] = $val; + } elseif ($val instanceof \DateTimeInterface) { + $replacements["{{$key}}"] = $val->format(\DateTime::RFC3339); + } elseif (\is_object($val)) { + $replacements["{{$key}}"] = '[object '.\get_class($val).']'; + } else { + $replacements["{{$key}}"] = '['.\gettype($val).']'; + } + } + + return strtr($message, $replacements); + } +} diff --git a/vendor/symfony/console/Output/BufferedOutput.php b/vendor/symfony/console/Output/BufferedOutput.php new file mode 100644 index 0000000..784e309 --- /dev/null +++ b/vendor/symfony/console/Output/BufferedOutput.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * @author Jean-François Simon + */ +class BufferedOutput extends Output +{ + private string $buffer = ''; + + /** + * Empties buffer and returns its content. + */ + public function fetch(): string + { + $content = $this->buffer; + $this->buffer = ''; + + return $content; + } + + /** + * {@inheritdoc} + */ + protected function doWrite(string $message, bool $newline) + { + $this->buffer .= $message; + + if ($newline) { + $this->buffer .= \PHP_EOL; + } + } +} diff --git a/vendor/symfony/console/Output/ConsoleOutput.php b/vendor/symfony/console/Output/ConsoleOutput.php new file mode 100644 index 0000000..c6ba068 --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleOutput.php @@ -0,0 +1,168 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * ConsoleOutput is the default class for all CLI output. It uses STDOUT and STDERR. + * + * This class is a convenient wrapper around `StreamOutput` for both STDOUT and STDERR. + * + * $output = new ConsoleOutput(); + * + * This is equivalent to: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * $stdErr = new StreamOutput(fopen('php://stderr', 'w')); + * + * @author Fabien Potencier + */ +class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface +{ + private $stderr; + private array $consoleSectionOutputs = []; + + /** + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct(int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null) + { + parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter); + + if (null === $formatter) { + // for BC reasons, stdErr has it own Formatter only when user don't inject a specific formatter. + $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated); + + return; + } + + $actualDecorated = $this->isDecorated(); + $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter()); + + if (null === $decorated) { + $this->setDecorated($actualDecorated && $this->stderr->isDecorated()); + } + } + + /** + * Creates a new output section. + */ + public function section(): ConsoleSectionOutput + { + return new ConsoleSectionOutput($this->getStream(), $this->consoleSectionOutputs, $this->getVerbosity(), $this->isDecorated(), $this->getFormatter()); + } + + /** + * {@inheritdoc} + */ + public function setDecorated(bool $decorated) + { + parent::setDecorated($decorated); + $this->stderr->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->stderr->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity(int $level) + { + parent::setVerbosity($level); + $this->stderr->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getErrorOutput(): OutputInterface + { + return $this->stderr; + } + + /** + * {@inheritdoc} + */ + public function setErrorOutput(OutputInterface $error) + { + $this->stderr = $error; + } + + /** + * Returns true if current environment supports writing console output to + * STDOUT. + */ + protected function hasStdoutSupport(): bool + { + return false === $this->isRunningOS400(); + } + + /** + * Returns true if current environment supports writing console output to + * STDERR. + */ + protected function hasStderrSupport(): bool + { + return false === $this->isRunningOS400(); + } + + /** + * Checks if current executing environment is IBM iSeries (OS400), which + * doesn't properly convert character-encodings between ASCII to EBCDIC. + */ + private function isRunningOS400(): bool + { + $checks = [ + \function_exists('php_uname') ? php_uname('s') : '', + getenv('OSTYPE'), + \PHP_OS, + ]; + + return false !== stripos(implode(';', $checks), 'OS400'); + } + + /** + * @return resource + */ + private function openOutputStream() + { + if (!$this->hasStdoutSupport()) { + return fopen('php://output', 'w'); + } + + // Use STDOUT when possible to prevent from opening too many file descriptors + return \defined('STDOUT') ? \STDOUT : (@fopen('php://stdout', 'w') ?: fopen('php://output', 'w')); + } + + /** + * @return resource + */ + private function openErrorStream() + { + if (!$this->hasStderrSupport()) { + return fopen('php://output', 'w'); + } + + // Use STDERR when possible to prevent from opening too many file descriptors + return \defined('STDERR') ? \STDERR : (@fopen('php://stderr', 'w') ?: fopen('php://output', 'w')); + } +} diff --git a/vendor/symfony/console/Output/ConsoleOutputInterface.php b/vendor/symfony/console/Output/ConsoleOutputInterface.php new file mode 100644 index 0000000..6b4babc --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleOutputInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * ConsoleOutputInterface is the interface implemented by ConsoleOutput class. + * This adds information about stderr and section output stream. + * + * @author Dariusz Górecki + */ +interface ConsoleOutputInterface extends OutputInterface +{ + /** + * Gets the OutputInterface for errors. + */ + public function getErrorOutput(): OutputInterface; + + public function setErrorOutput(OutputInterface $error); + + public function section(): ConsoleSectionOutput; +} diff --git a/vendor/symfony/console/Output/ConsoleSectionOutput.php b/vendor/symfony/console/Output/ConsoleSectionOutput.php new file mode 100644 index 0000000..92dca79 --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleSectionOutput.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Terminal; + +/** + * @author Pierre du Plessis + * @author Gabriel Ostrolucký + */ +class ConsoleSectionOutput extends StreamOutput +{ + private array $content = []; + private int $lines = 0; + private array $sections; + private $terminal; + + /** + * @param resource $stream + * @param ConsoleSectionOutput[] $sections + */ + public function __construct($stream, array &$sections, int $verbosity, bool $decorated, OutputFormatterInterface $formatter) + { + parent::__construct($stream, $verbosity, $decorated, $formatter); + array_unshift($sections, $this); + $this->sections = &$sections; + $this->terminal = new Terminal(); + } + + /** + * Clears previous output for this section. + * + * @param int $lines Number of lines to clear. If null, then the entire output of this section is cleared + */ + public function clear(int $lines = null) + { + if (empty($this->content) || !$this->isDecorated()) { + return; + } + + if ($lines) { + array_splice($this->content, -($lines * 2)); // Multiply lines by 2 to cater for each new line added between content + } else { + $lines = $this->lines; + $this->content = []; + } + + $this->lines -= $lines; + + parent::doWrite($this->popStreamContentUntilCurrentSection($lines), false); + } + + /** + * Overwrites the previous output with a new message. + */ + public function overwrite(string|iterable $message) + { + $this->clear(); + $this->writeln($message); + } + + public function getContent(): string + { + return implode('', $this->content); + } + + /** + * @internal + */ + public function addContent(string $input) + { + foreach (explode(\PHP_EOL, $input) as $lineContent) { + $this->lines += ceil($this->getDisplayLength($lineContent) / $this->terminal->getWidth()) ?: 1; + $this->content[] = $lineContent; + $this->content[] = \PHP_EOL; + } + } + + /** + * {@inheritdoc} + */ + protected function doWrite(string $message, bool $newline) + { + if (!$this->isDecorated()) { + parent::doWrite($message, $newline); + + return; + } + + $erasedContent = $this->popStreamContentUntilCurrentSection(); + + $this->addContent($message); + + parent::doWrite($message, true); + parent::doWrite($erasedContent, false); + } + + /** + * At initial stage, cursor is at the end of stream output. This method makes cursor crawl upwards until it hits + * current section. Then it erases content it crawled through. Optionally, it erases part of current section too. + */ + private function popStreamContentUntilCurrentSection(int $numberOfLinesToClearFromCurrentSection = 0): string + { + $numberOfLinesToClear = $numberOfLinesToClearFromCurrentSection; + $erasedContent = []; + + foreach ($this->sections as $section) { + if ($section === $this) { + break; + } + + $numberOfLinesToClear += $section->lines; + $erasedContent[] = $section->getContent(); + } + + if ($numberOfLinesToClear > 0) { + // move cursor up n lines + parent::doWrite(sprintf("\x1b[%dA", $numberOfLinesToClear), false); + // erase to end of screen + parent::doWrite("\x1b[0J", false); + } + + return implode('', array_reverse($erasedContent)); + } + + private function getDisplayLength(string $text): int + { + return Helper::width(Helper::removeDecoration($this->getFormatter(), str_replace("\t", ' ', $text))); + } +} diff --git a/vendor/symfony/console/Output/NullOutput.php b/vendor/symfony/console/Output/NullOutput.php new file mode 100644 index 0000000..87214ec --- /dev/null +++ b/vendor/symfony/console/Output/NullOutput.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\NullOutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * NullOutput suppresses all output. + * + * $output = new NullOutput(); + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class NullOutput implements OutputInterface +{ + private $formatter; + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getFormatter(): OutputFormatterInterface + { + // to comply with the interface we must return a OutputFormatterInterface + return $this->formatter ??= new NullOutputFormatter(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated(bool $decorated) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function isDecorated(): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function setVerbosity(int $level) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getVerbosity(): int + { + return self::VERBOSITY_QUIET; + } + + /** + * {@inheritdoc} + */ + public function isQuiet(): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function isVerbose(): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isVeryVerbose(): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isDebug(): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL) + { + // do nothing + } +} diff --git a/vendor/symfony/console/Output/Output.php b/vendor/symfony/console/Output/Output.php new file mode 100644 index 0000000..58c1837 --- /dev/null +++ b/vendor/symfony/console/Output/Output.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * Base class for output classes. + * + * There are five levels of verbosity: + * + * * normal: no option passed (normal output) + * * verbose: -v (more output) + * * very verbose: -vv (highly extended output) + * * debug: -vvv (all debug output) + * * quiet: -q (no output) + * + * @author Fabien Potencier + */ +abstract class Output implements OutputInterface +{ + private int $verbosity; + private $formatter; + + /** + * @param int|null $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool $decorated Whether to decorate messages + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct(?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, OutputFormatterInterface $formatter = null) + { + $this->verbosity = $verbosity ?? self::VERBOSITY_NORMAL; + $this->formatter = $formatter ?? new OutputFormatter(); + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * {@inheritdoc} + */ + public function getFormatter(): OutputFormatterInterface + { + return $this->formatter; + } + + /** + * {@inheritdoc} + */ + public function setDecorated(bool $decorated) + { + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated(): bool + { + return $this->formatter->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity(int $level) + { + $this->verbosity = $level; + } + + /** + * {@inheritdoc} + */ + public function getVerbosity(): int + { + return $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function isQuiet(): bool + { + return self::VERBOSITY_QUIET === $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function isVerbose(): bool + { + return self::VERBOSITY_VERBOSE <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function isVeryVerbose(): bool + { + return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function isDebug(): bool + { + return self::VERBOSITY_DEBUG <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL) + { + $this->write($messages, true, $options); + } + + /** + * {@inheritdoc} + */ + public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL) + { + if (!is_iterable($messages)) { + $messages = [$messages]; + } + + $types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN; + $type = $types & $options ?: self::OUTPUT_NORMAL; + + $verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG; + $verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL; + + if ($verbosity > $this->getVerbosity()) { + return; + } + + foreach ($messages as $message) { + switch ($type) { + case OutputInterface::OUTPUT_NORMAL: + $message = $this->formatter->format($message); + break; + case OutputInterface::OUTPUT_RAW: + break; + case OutputInterface::OUTPUT_PLAIN: + $message = strip_tags($this->formatter->format($message)); + break; + } + + $this->doWrite($message ?? '', $newline); + } + } + + /** + * Writes a message to the output. + */ + abstract protected function doWrite(string $message, bool $newline); +} diff --git a/vendor/symfony/console/Output/OutputInterface.php b/vendor/symfony/console/Output/OutputInterface.php new file mode 100644 index 0000000..beb9218 --- /dev/null +++ b/vendor/symfony/console/Output/OutputInterface.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * OutputInterface is the interface implemented by all Output classes. + * + * @author Fabien Potencier + */ +interface OutputInterface +{ + public const VERBOSITY_QUIET = 16; + public const VERBOSITY_NORMAL = 32; + public const VERBOSITY_VERBOSE = 64; + public const VERBOSITY_VERY_VERBOSE = 128; + public const VERBOSITY_DEBUG = 256; + + public const OUTPUT_NORMAL = 1; + public const OUTPUT_RAW = 2; + public const OUTPUT_PLAIN = 4; + + /** + * Writes a message to the output. + * + * @param $newline Whether to add a newline + * @param $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function write(string|iterable $messages, bool $newline = false, int $options = 0); + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function writeln(string|iterable $messages, int $options = 0); + + /** + * Sets the verbosity of the output. + */ + public function setVerbosity(int $level); + + /** + * Gets the current verbosity of the output. + */ + public function getVerbosity(): int; + + /** + * Returns whether verbosity is quiet (-q). + */ + public function isQuiet(): bool; + + /** + * Returns whether verbosity is verbose (-v). + */ + public function isVerbose(): bool; + + /** + * Returns whether verbosity is very verbose (-vv). + */ + public function isVeryVerbose(): bool; + + /** + * Returns whether verbosity is debug (-vvv). + */ + public function isDebug(): bool; + + /** + * Sets the decorated flag. + */ + public function setDecorated(bool $decorated); + + /** + * Gets the decorated flag. + */ + public function isDecorated(): bool; + + public function setFormatter(OutputFormatterInterface $formatter); + + /** + * Returns current output formatter instance. + */ + public function getFormatter(): OutputFormatterInterface; +} diff --git a/vendor/symfony/console/Output/StreamOutput.php b/vendor/symfony/console/Output/StreamOutput.php new file mode 100644 index 0000000..ac58e41 --- /dev/null +++ b/vendor/symfony/console/Output/StreamOutput.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * StreamOutput writes the output to a given stream. + * + * Usage: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * As `StreamOutput` can use any stream, you can also use a file: + * + * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); + * + * @author Fabien Potencier + */ +class StreamOutput extends Output +{ + private $stream; + + /** + * @param resource $stream A stream resource + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + * + * @throws InvalidArgumentException When first argument is not a real stream + */ + public function __construct($stream, int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null) + { + if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) { + throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); + } + + $this->stream = $stream; + + if (null === $decorated) { + $decorated = $this->hasColorSupport(); + } + + parent::__construct($verbosity, $decorated, $formatter); + } + + /** + * Gets the stream attached to this StreamOutput instance. + * + * @return resource + */ + public function getStream() + { + return $this->stream; + } + + /** + * {@inheritdoc} + */ + protected function doWrite(string $message, bool $newline) + { + if ($newline) { + $message .= \PHP_EOL; + } + + @fwrite($this->stream, $message); + + fflush($this->stream); + } + + /** + * Returns true if the stream supports colorization. + * + * Colorization is disabled if not supported by the stream: + * + * This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo + * terminals via named pipes, so we can only check the environment. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler + * + * @return bool true if the stream supports colorization, false otherwise + */ + protected function hasColorSupport(): bool + { + // Follow https://no-color.org/ + if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) { + return false; + } + + if ('Hyper' === getenv('TERM_PROGRAM')) { + return true; + } + + if (\DIRECTORY_SEPARATOR === '\\') { + return (\function_exists('sapi_windows_vt100_support') + && @sapi_windows_vt100_support($this->stream)) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + } + + return stream_isatty($this->stream); + } +} diff --git a/vendor/symfony/console/Output/TrimmedBufferOutput.php b/vendor/symfony/console/Output/TrimmedBufferOutput.php new file mode 100644 index 0000000..0d375e0 --- /dev/null +++ b/vendor/symfony/console/Output/TrimmedBufferOutput.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * A BufferedOutput that keeps only the last N chars. + * + * @author Jérémy Derussé + */ +class TrimmedBufferOutput extends Output +{ + private int $maxLength; + private string $buffer = ''; + + public function __construct(int $maxLength, ?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, OutputFormatterInterface $formatter = null) + { + if ($maxLength <= 0) { + throw new InvalidArgumentException(sprintf('"%s()" expects a strictly positive maxLength. Got %d.', __METHOD__, $maxLength)); + } + + parent::__construct($verbosity, $decorated, $formatter); + $this->maxLength = $maxLength; + } + + /** + * Empties buffer and returns its content. + */ + public function fetch(): string + { + $content = $this->buffer; + $this->buffer = ''; + + return $content; + } + + /** + * {@inheritdoc} + */ + protected function doWrite(string $message, bool $newline) + { + $this->buffer .= $message; + + if ($newline) { + $this->buffer .= \PHP_EOL; + } + + $this->buffer = substr($this->buffer, 0 - $this->maxLength); + } +} diff --git a/vendor/symfony/console/Question/ChoiceQuestion.php b/vendor/symfony/console/Question/ChoiceQuestion.php new file mode 100644 index 0000000..e449ff6 --- /dev/null +++ b/vendor/symfony/console/Question/ChoiceQuestion.php @@ -0,0 +1,177 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Represents a choice question. + * + * @author Fabien Potencier + */ +class ChoiceQuestion extends Question +{ + private array $choices; + private bool $multiselect = false; + private string $prompt = ' > '; + private string $errorMessage = 'Value "%s" is invalid'; + + /** + * @param string $question The question to ask to the user + * @param array $choices The list of available choices + * @param mixed $default The default answer to return + */ + public function __construct(string $question, array $choices, mixed $default = null) + { + if (!$choices) { + throw new \LogicException('Choice question must have at least 1 choice available.'); + } + + parent::__construct($question, $default); + + $this->choices = $choices; + $this->setValidator($this->getDefaultValidator()); + $this->setAutocompleterValues($choices); + } + + /** + * Returns available choices. + */ + public function getChoices(): array + { + return $this->choices; + } + + /** + * Sets multiselect option. + * + * When multiselect is set to true, multiple choices can be answered. + * + * @return $this + */ + public function setMultiselect(bool $multiselect): static + { + $this->multiselect = $multiselect; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Returns whether the choices are multiselect. + */ + public function isMultiselect(): bool + { + return $this->multiselect; + } + + /** + * Gets the prompt for choices. + */ + public function getPrompt(): string + { + return $this->prompt; + } + + /** + * Sets the prompt for choices. + * + * @return $this + */ + public function setPrompt(string $prompt): static + { + $this->prompt = $prompt; + + return $this; + } + + /** + * Sets the error message for invalid values. + * + * The error message has a string placeholder (%s) for the invalid value. + * + * @return $this + */ + public function setErrorMessage(string $errorMessage): static + { + $this->errorMessage = $errorMessage; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + private function getDefaultValidator(): callable + { + $choices = $this->choices; + $errorMessage = $this->errorMessage; + $multiselect = $this->multiselect; + $isAssoc = $this->isAssoc($choices); + + return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) { + if ($multiselect) { + // Check for a separated comma values + if (!preg_match('/^[^,]+(?:,[^,]+)*$/', (string) $selected, $matches)) { + throw new InvalidArgumentException(sprintf($errorMessage, $selected)); + } + + $selectedChoices = explode(',', (string) $selected); + } else { + $selectedChoices = [$selected]; + } + + if ($this->isTrimmable()) { + foreach ($selectedChoices as $k => $v) { + $selectedChoices[$k] = trim((string) $v); + } + } + + $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 (false !== $result) { + $result = $choices[$result]; + } elseif (isset($choices[$value])) { + $result = $choices[$value]; + } + } elseif (false === $result && isset($choices[$value])) { + $result = $value; + } + + if (false === $result) { + throw new InvalidArgumentException(sprintf($errorMessage, $value)); + } + + // For associative choices, consistently return the key as string: + $multiselectChoices[] = $isAssoc ? (string) $result : $result; + } + + if ($multiselect) { + return $multiselectChoices; + } + + return current($multiselectChoices); + }; + } +} diff --git a/vendor/symfony/console/Question/ConfirmationQuestion.php b/vendor/symfony/console/Question/ConfirmationQuestion.php new file mode 100644 index 0000000..40eab24 --- /dev/null +++ b/vendor/symfony/console/Question/ConfirmationQuestion.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +/** + * Represents a yes/no question. + * + * @author Fabien Potencier + */ +class ConfirmationQuestion extends Question +{ + private string $trueAnswerRegex; + + /** + * @param string $question The question to ask to the user + * @param bool $default The default answer to return, true or false + * @param string $trueAnswerRegex A regex to match the "yes" answer + */ + public function __construct(string $question, bool $default = true, string $trueAnswerRegex = '/^y/i') + { + parent::__construct($question, $default); + + $this->trueAnswerRegex = $trueAnswerRegex; + $this->setNormalizer($this->getDefaultNormalizer()); + } + + /** + * Returns the default answer normalizer. + */ + private function getDefaultNormalizer(): callable + { + $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/symfony/console/Question/Question.php b/vendor/symfony/console/Question/Question.php new file mode 100644 index 0000000..f99e685 --- /dev/null +++ b/vendor/symfony/console/Question/Question.php @@ -0,0 +1,283 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a Question. + * + * @author Fabien Potencier + */ +class Question +{ + private string $question; + private ?int $attempts = null; + private bool $hidden = false; + private bool $hiddenFallback = true; + private ?\Closure $autocompleterCallback = null; + private ?\Closure $validator = null; + private string|int|bool|null|float $default; + private ?\Closure $normalizer = null; + private bool $trimmable = true; + private bool $multiline = false; + + /** + * @param string $question The question to ask to the user + * @param string|bool|int|float|null $default The default answer to return if the user enters nothing + */ + public function __construct(string $question, string|bool|int|float $default = null) + { + $this->question = $question; + $this->default = $default; + } + + /** + * Returns the question. + */ + public function getQuestion(): string + { + return $this->question; + } + + /** + * Returns the default answer. + */ + public function getDefault(): string|bool|int|float|null + { + return $this->default; + } + + /** + * Returns whether the user response accepts newline characters. + */ + public function isMultiline(): bool + { + return $this->multiline; + } + + /** + * Sets whether the user response should accept newline characters. + * + * @return $this + */ + public function setMultiline(bool $multiline): static + { + $this->multiline = $multiline; + + return $this; + } + + /** + * Returns whether the user response must be hidden. + */ + public function isHidden(): bool + { + return $this->hidden; + } + + /** + * Sets whether the user response must be hidden or not. + * + * @return $this + * + * @throws LogicException In case the autocompleter is also used + */ + public function setHidden(bool $hidden): static + { + if ($this->autocompleterCallback) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->hidden = $hidden; + + return $this; + } + + /** + * In case the response cannot be hidden, whether to fallback on non-hidden question or not. + */ + public function isHiddenFallback(): bool + { + return $this->hiddenFallback; + } + + /** + * Sets whether to fallback on non-hidden question if the response cannot be hidden. + * + * @return $this + */ + public function setHiddenFallback(bool $fallback): static + { + $this->hiddenFallback = $fallback; + + return $this; + } + + /** + * Gets values for the autocompleter. + */ + public function getAutocompleterValues(): ?iterable + { + $callback = $this->getAutocompleterCallback(); + + return $callback ? $callback('') : null; + } + + /** + * Sets values for the autocompleter. + * + * @return $this + * + * @throws LogicException + */ + public function setAutocompleterValues(?iterable $values): static + { + if (\is_array($values)) { + $values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values); + + $callback = static function () use ($values) { + return $values; + }; + } elseif ($values instanceof \Traversable) { + $valueCache = null; + $callback = static function () use ($values, &$valueCache) { + return $valueCache ?? $valueCache = iterator_to_array($values, false); + }; + } else { + $callback = null; + } + + return $this->setAutocompleterCallback($callback); + } + + /** + * Gets the callback function used for the autocompleter. + */ + public function getAutocompleterCallback(): ?callable + { + return $this->autocompleterCallback; + } + + /** + * Sets the callback function used for the autocompleter. + * + * The callback is passed the user input as argument and should return an iterable of corresponding suggestions. + * + * @return $this + */ + public function setAutocompleterCallback(callable $callback = null): static + { + if ($this->hidden && null !== $callback) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->autocompleterCallback = null === $callback || $callback instanceof \Closure ? $callback : \Closure::fromCallable($callback); + + return $this; + } + + /** + * Sets a validator for the question. + * + * @return $this + */ + public function setValidator(callable $validator = null): static + { + $this->validator = null === $validator || $validator instanceof \Closure ? $validator : \Closure::fromCallable($validator); + + return $this; + } + + /** + * Gets the validator for the question. + */ + public function getValidator(): ?callable + { + return $this->validator; + } + + /** + * Sets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @return $this + * + * @throws InvalidArgumentException in case the number of attempts is invalid + */ + public function setMaxAttempts(?int $attempts): static + { + if (null !== $attempts && $attempts < 1) { + throw new InvalidArgumentException('Maximum number of attempts must be a positive value.'); + } + + $this->attempts = $attempts; + + return $this; + } + + /** + * Gets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + */ + public function getMaxAttempts(): ?int + { + return $this->attempts; + } + + /** + * Sets a normalizer for the response. + * + * The normalizer can be a callable (a string), a closure or a class implementing __invoke. + * + * @return $this + */ + public function setNormalizer(callable $normalizer): static + { + $this->normalizer = $normalizer instanceof \Closure ? $normalizer : \Closure::fromCallable($normalizer); + + return $this; + } + + /** + * Gets the normalizer for the response. + * + * The normalizer can ba a callable (a string), a closure or a class implementing __invoke. + */ + public function getNormalizer(): ?callable + { + return $this->normalizer; + } + + protected function isAssoc(array $array) + { + return (bool) \count(array_filter(array_keys($array), 'is_string')); + } + + public function isTrimmable(): bool + { + return $this->trimmable; + } + + /** + * @return $this + */ + public function setTrimmable(bool $trimmable): static + { + $this->trimmable = $trimmable; + + return $this; + } +} diff --git a/vendor/symfony/console/README.md b/vendor/symfony/console/README.md new file mode 100644 index 0000000..c4c1299 --- /dev/null +++ b/vendor/symfony/console/README.md @@ -0,0 +1,36 @@ +Console Component +================= + +The Console component eases the creation of beautiful and testable command line +interfaces. + +Sponsor +------- + +The Console component for Symfony 5.4/6.0 is [backed][1] by [Les-Tilleuls.coop][2]. + +Les-Tilleuls.coop is a team of 50+ Symfony experts who can help you design, develop and +fix your projects. We provide a wide range of professional services including development, +consulting, coaching, training and audits. We also are highly skilled in JS, Go and DevOps. +We are a worker cooperative! + +Help Symfony by [sponsoring][3] its development! + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/console.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) + +Credits +------- + +`Resources/bin/hiddeninput.exe` is a third party binary provided within this +component. Find sources and license at https://github.com/Seldaek/hidden-input. + +[1]: https://symfony.com/backers +[2]: https://les-tilleuls.coop +[3]: https://symfony.com/sponsor diff --git a/vendor/symfony/console/Resources/bin/hiddeninput.exe b/vendor/symfony/console/Resources/bin/hiddeninput.exe new file mode 100644 index 0000000000000000000000000000000000000000..c8cf65e8d819e6e525121cf6b21f1c2429746038 GIT binary patch literal 9216 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 J0sQ~1{4brj`U(I5 literal 0 HcmV?d00001 diff --git a/vendor/symfony/console/Resources/completion.bash b/vendor/symfony/console/Resources/completion.bash new file mode 100644 index 0000000..64b87cc --- /dev/null +++ b/vendor/symfony/console/Resources/completion.bash @@ -0,0 +1,84 @@ +# This file is part of the Symfony package. +# +# (c) Fabien Potencier +# +# For the full copyright and license information, please view +# https://symfony.com/doc/current/contributing/code/license.html + +_sf_{{ COMMAND_NAME }}() { + # Use newline as only separator to allow space in completion values + IFS=$'\n' + local sf_cmd="${COMP_WORDS[0]}" + + # for an alias, get the real script behind it + sf_cmd_type=$(type -t $sf_cmd) + if [[ $sf_cmd_type == "alias" ]]; then + sf_cmd=$(alias $sf_cmd | sed -E "s/alias $sf_cmd='(.*)'/\1/") + elif [[ $sf_cmd_type == "file" ]]; then + sf_cmd=$(type -p $sf_cmd) + fi + + if [[ $sf_cmd_type != "function" && ! -x $sf_cmd ]]; then + return 1 + fi + + local cur prev words cword + _get_comp_words_by_ref -n := cur prev words cword + + local completecmd=("$sf_cmd" "_complete" "--no-interaction" "-sbash" "-c$cword" "-S{{ VERSION }}") + for w in ${words[@]}; do + w=$(printf -- '%b' "$w") + # remove quotes from typed values + quote="${w:0:1}" + if [ "$quote" == \' ]; then + w="${w%\'}" + w="${w#\'}" + elif [ "$quote" == \" ]; then + w="${w%\"}" + w="${w#\"}" + fi + # empty values are ignored + if [ ! -z "$w" ]; then + completecmd+=("-i$w") + fi + done + + local sfcomplete + if sfcomplete=$(${completecmd[@]} 2>&1); then + local quote suggestions + quote=${cur:0:1} + + # Use single quotes by default if suggestions contains backslash (FQCN) + if [ "$quote" == '' ] && [[ "$sfcomplete" =~ \\ ]]; then + quote=\' + fi + + if [ "$quote" == \' ]; then + # single quotes: no additional escaping (does not accept ' in values) + suggestions=$(for s in $sfcomplete; do printf $'%q%q%q\n' "$quote" "$s" "$quote"; done) + elif [ "$quote" == \" ]; then + # double quotes: double escaping for \ $ ` " + suggestions=$(for s in $sfcomplete; do + s=${s//\\/\\\\} + s=${s//\$/\\\$} + s=${s//\`/\\\`} + s=${s//\"/\\\"} + printf $'%q%q%q\n' "$quote" "$s" "$quote"; + done) + else + # no quotes: double escaping + suggestions=$(for s in $sfcomplete; do printf $'%q\n' $(printf '%q' "$s"); done) + fi + COMPREPLY=($(IFS=$'\n' compgen -W "$suggestions" -- $(printf -- "%q" "$cur"))) + __ltrim_colon_completions "$cur" + else + if [[ "$sfcomplete" != *"Command \"_complete\" is not defined."* ]]; then + >&2 echo + >&2 echo $sfcomplete + fi + + return 1 + fi +} + +complete -F _sf_{{ COMMAND_NAME }} {{ COMMAND_NAME }} diff --git a/vendor/symfony/console/SignalRegistry/SignalRegistry.php b/vendor/symfony/console/SignalRegistry/SignalRegistry.php new file mode 100644 index 0000000..f51c0c3 --- /dev/null +++ b/vendor/symfony/console/SignalRegistry/SignalRegistry.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\SignalRegistry; + +final class SignalRegistry +{ + private array $signalHandlers = []; + + public function __construct() + { + if (\function_exists('pcntl_async_signals')) { + pcntl_async_signals(true); + } + } + + public function register(int $signal, callable $signalHandler): void + { + if (!isset($this->signalHandlers[$signal])) { + $previousCallback = pcntl_signal_get_handler($signal); + + if (\is_callable($previousCallback)) { + $this->signalHandlers[$signal][] = $previousCallback; + } + } + + $this->signalHandlers[$signal][] = $signalHandler; + + pcntl_signal($signal, [$this, 'handle']); + } + + public static function isSupported(): bool + { + if (!\function_exists('pcntl_signal')) { + return false; + } + + if (\in_array('pcntl_signal', explode(',', \ini_get('disable_functions')))) { + return false; + } + + return true; + } + + /** + * @internal + */ + public function handle(int $signal): void + { + $count = \count($this->signalHandlers[$signal]); + + foreach ($this->signalHandlers[$signal] as $i => $signalHandler) { + $hasNext = $i !== $count - 1; + $signalHandler($signal, $hasNext); + } + } +} diff --git a/vendor/symfony/console/SingleCommandApplication.php b/vendor/symfony/console/SingleCommandApplication.php new file mode 100644 index 0000000..4f0b5ba --- /dev/null +++ b/vendor/symfony/console/SingleCommandApplication.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Grégoire Pineau + */ +class SingleCommandApplication extends Command +{ + private string $version = 'UNKNOWN'; + private bool $autoExit = true; + private bool $running = false; + + /** + * @return $this + */ + public function setVersion(string $version): static + { + $this->version = $version; + + return $this; + } + + /** + * @final + * + * @return $this + */ + public function setAutoExit(bool $autoExit): static + { + $this->autoExit = $autoExit; + + return $this; + } + + public function run(InputInterface $input = null, OutputInterface $output = null): int + { + if ($this->running) { + return parent::run($input, $output); + } + + // We use the command name as the application name + $application = new Application($this->getName() ?: 'UNKNOWN', $this->version); + $application->setAutoExit($this->autoExit); + // Fix the usage of the command displayed with "--help" + $this->setName($_SERVER['argv'][0]); + $application->add($this); + $application->setDefaultCommand($this->getName(), true); + + $this->running = true; + try { + $ret = $application->run($input, $output); + } finally { + $this->running = false; + } + + return $ret ?? 1; + } +} diff --git a/vendor/symfony/console/Style/OutputStyle.php b/vendor/symfony/console/Style/OutputStyle.php new file mode 100644 index 0000000..0b2ded3 --- /dev/null +++ b/vendor/symfony/console/Style/OutputStyle.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Decorates output to add console style guide helpers. + * + * @author Kevin Bond + */ +abstract class OutputStyle implements OutputInterface, StyleInterface +{ + private $output; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + } + + /** + * {@inheritdoc} + */ + public function newLine(int $count = 1) + { + $this->output->write(str_repeat(\PHP_EOL, $count)); + } + + public function createProgressBar(int $max = 0): ProgressBar + { + return new ProgressBar($this->output, $max); + } + + /** + * {@inheritdoc} + */ + public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL) + { + $this->output->write($messages, $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL) + { + $this->output->writeln($messages, $type); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity(int $level) + { + $this->output->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getVerbosity(): int + { + return $this->output->getVerbosity(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated(bool $decorated) + { + $this->output->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated(): bool + { + return $this->output->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->output->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function getFormatter(): OutputFormatterInterface + { + return $this->output->getFormatter(); + } + + /** + * {@inheritdoc} + */ + public function isQuiet(): bool + { + return $this->output->isQuiet(); + } + + /** + * {@inheritdoc} + */ + public function isVerbose(): bool + { + return $this->output->isVerbose(); + } + + /** + * {@inheritdoc} + */ + public function isVeryVerbose(): bool + { + return $this->output->isVeryVerbose(); + } + + /** + * {@inheritdoc} + */ + public function isDebug(): bool + { + return $this->output->isDebug(); + } + + protected function getErrorOutput() + { + if (!$this->output instanceof ConsoleOutputInterface) { + return $this->output; + } + + return $this->output->getErrorOutput(); + } +} diff --git a/vendor/symfony/console/Style/StyleInterface.php b/vendor/symfony/console/Style/StyleInterface.php new file mode 100644 index 0000000..0bb1233 --- /dev/null +++ b/vendor/symfony/console/Style/StyleInterface.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +/** + * Output style helpers. + * + * @author Kevin Bond + */ +interface StyleInterface +{ + /** + * Formats a command title. + */ + public function title(string $message); + + /** + * Formats a section title. + */ + public function section(string $message); + + /** + * Formats a list. + */ + public function listing(array $elements); + + /** + * Formats informational text. + */ + public function text(string|array $message); + + /** + * Formats a success result bar. + */ + public function success(string|array $message); + + /** + * Formats an error result bar. + */ + public function error(string|array $message); + + /** + * Formats an warning result bar. + */ + public function warning(string|array $message); + + /** + * Formats a note admonition. + */ + public function note(string|array $message); + + /** + * Formats a caution admonition. + */ + public function caution(string|array $message); + + /** + * Formats a table. + */ + public function table(array $headers, array $rows); + + /** + * Asks a question. + */ + public function ask(string $question, string $default = null, callable $validator = null): mixed; + + /** + * Asks a question with the user input hidden. + */ + public function askHidden(string $question, callable $validator = null): mixed; + + /** + * Asks for confirmation. + */ + public function confirm(string $question, bool $default = true): bool; + + /** + * Asks a choice question. + */ + public function choice(string $question, array $choices, mixed $default = null): mixed; + + /** + * Add newline(s). + */ + public function newLine(int $count = 1); + + /** + * Starts the progress output. + */ + public function progressStart(int $max = 0); + + /** + * Advances the progress output X steps. + */ + public function progressAdvance(int $step = 1); + + /** + * Finishes the progress output. + */ + public function progressFinish(); +} diff --git a/vendor/symfony/console/Style/SymfonyStyle.php b/vendor/symfony/console/Style/SymfonyStyle.php new file mode 100644 index 0000000..2730242 --- /dev/null +++ b/vendor/symfony/console/Style/SymfonyStyle.php @@ -0,0 +1,500 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\SymfonyQuestionHelper; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableCell; +use Symfony\Component\Console\Helper\TableSeparator; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\TrimmedBufferOutput; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Terminal; + +/** + * Output decorator helpers for the Symfony Style Guide. + * + * @author Kevin Bond + */ +class SymfonyStyle extends OutputStyle +{ + public const MAX_LINE_LENGTH = 120; + + private $input; + private $output; + private $questionHelper; + private $progressBar; + private int $lineLength; + private $bufferedOutput; + + public function __construct(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->bufferedOutput = new TrimmedBufferOutput(\DIRECTORY_SEPARATOR === '\\' ? 4 : 2, $output->getVerbosity(), false, clone $output->getFormatter()); + // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not. + $width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH; + $this->lineLength = min($width - (int) (\DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH); + + parent::__construct($this->output = $output); + } + + /** + * Formats a message as a block of text. + */ + public function block(string|array $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = true) + { + $messages = \is_array($messages) ? array_values($messages) : [$messages]; + + $this->autoPrependBlock(); + $this->writeln($this->createBlock($messages, $type, $style, $prefix, $padding, $escape)); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function title(string $message) + { + $this->autoPrependBlock(); + $this->writeln([ + sprintf('%s', OutputFormatter::escapeTrailingBackslash($message)), + sprintf('%s', str_repeat('=', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))), + ]); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function section(string $message) + { + $this->autoPrependBlock(); + $this->writeln([ + sprintf('%s', OutputFormatter::escapeTrailingBackslash($message)), + sprintf('%s', str_repeat('-', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))), + ]); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function listing(array $elements) + { + $this->autoPrependText(); + $elements = array_map(function ($element) { + return sprintf(' * %s', $element); + }, $elements); + + $this->writeln($elements); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function text(string|array $message) + { + $this->autoPrependText(); + + $messages = \is_array($message) ? array_values($message) : [$message]; + foreach ($messages as $message) { + $this->writeln(sprintf(' %s', $message)); + } + } + + /** + * Formats a command comment. + */ + public function comment(string|array $message) + { + $this->block($message, null, null, ' // ', false, false); + } + + /** + * {@inheritdoc} + */ + public function success(string|array $message) + { + $this->block($message, 'OK', 'fg=black;bg=green', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function error(string|array $message) + { + $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function warning(string|array $message) + { + $this->block($message, 'WARNING', 'fg=black;bg=yellow', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function note(string|array $message) + { + $this->block($message, 'NOTE', 'fg=yellow', ' ! '); + } + + /** + * Formats an info message. + */ + public function info(string|array $message) + { + $this->block($message, 'INFO', 'fg=green', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function caution(string|array $message) + { + $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true); + } + + /** + * {@inheritdoc} + */ + public function table(array $headers, array $rows) + { + $this->createTable() + ->setHeaders($headers) + ->setRows($rows) + ->render() + ; + + $this->newLine(); + } + + /** + * Formats a horizontal table. + */ + public function horizontalTable(array $headers, array $rows) + { + $this->createTable() + ->setHorizontal(true) + ->setHeaders($headers) + ->setRows($rows) + ->render() + ; + + $this->newLine(); + } + + /** + * Formats a list of key/value horizontally. + * + * Each row can be one of: + * * 'A title' + * * ['key' => 'value'] + * * new TableSeparator() + */ + public function definitionList(string|array|TableSeparator ...$list) + { + $headers = []; + $row = []; + foreach ($list as $value) { + if ($value instanceof TableSeparator) { + $headers[] = $value; + $row[] = $value; + continue; + } + if (\is_string($value)) { + $headers[] = new TableCell($value, ['colspan' => 2]); + $row[] = null; + continue; + } + if (!\is_array($value)) { + throw new InvalidArgumentException('Value should be an array, string, or an instance of TableSeparator.'); + } + $headers[] = key($value); + $row[] = current($value); + } + + $this->horizontalTable($headers, [$row]); + } + + /** + * {@inheritdoc} + */ + public function ask(string $question, string $default = null, callable $validator = null): mixed + { + $question = new Question($question, $default); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function askHidden(string $question, callable $validator = null): mixed + { + $question = new Question($question); + + $question->setHidden(true); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function confirm(string $question, bool $default = true): bool + { + return $this->askQuestion(new ConfirmationQuestion($question, $default)); + } + + /** + * {@inheritdoc} + */ + public function choice(string $question, array $choices, mixed $default = null): mixed + { + if (null !== $default) { + $values = array_flip($choices); + $default = $values[$default] ?? $default; + } + + return $this->askQuestion(new ChoiceQuestion($question, $choices, $default)); + } + + /** + * {@inheritdoc} + */ + public function progressStart(int $max = 0) + { + $this->progressBar = $this->createProgressBar($max); + $this->progressBar->start(); + } + + /** + * {@inheritdoc} + */ + public function progressAdvance(int $step = 1) + { + $this->getProgressBar()->advance($step); + } + + /** + * {@inheritdoc} + */ + public function progressFinish() + { + $this->getProgressBar()->finish(); + $this->newLine(2); + unset($this->progressBar); + } + + /** + * {@inheritdoc} + */ + public function createProgressBar(int $max = 0): ProgressBar + { + $progressBar = parent::createProgressBar($max); + + if ('\\' !== \DIRECTORY_SEPARATOR || 'Hyper' === getenv('TERM_PROGRAM')) { + $progressBar->setEmptyBarCharacter('░'); // light shade character \u2591 + $progressBar->setProgressCharacter(''); + $progressBar->setBarCharacter('▓'); // dark shade character \u2593 + } + + return $progressBar; + } + + /** + * @see ProgressBar::iterate() + */ + public function progressIterate(iterable $iterable, int $max = null): iterable + { + yield from $this->createProgressBar()->iterate($iterable, $max); + + $this->newLine(2); + } + + public function askQuestion(Question $question): mixed + { + if ($this->input->isInteractive()) { + $this->autoPrependBlock(); + } + + $this->questionHelper ??= new SymfonyQuestionHelper(); + + $answer = $this->questionHelper->ask($this->input, $this, $question); + + if ($this->input->isInteractive()) { + $this->newLine(); + $this->bufferedOutput->write("\n"); + } + + return $answer; + } + + /** + * {@inheritdoc} + */ + public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL) + { + if (!is_iterable($messages)) { + $messages = [$messages]; + } + + foreach ($messages as $message) { + parent::writeln($message, $type); + $this->writeBuffer($message, true, $type); + } + } + + /** + * {@inheritdoc} + */ + public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL) + { + if (!is_iterable($messages)) { + $messages = [$messages]; + } + + foreach ($messages as $message) { + parent::write($message, $newline, $type); + $this->writeBuffer($message, $newline, $type); + } + } + + /** + * {@inheritdoc} + */ + public function newLine(int $count = 1) + { + parent::newLine($count); + $this->bufferedOutput->write(str_repeat("\n", $count)); + } + + /** + * Returns a new instance which makes use of stderr if available. + */ + public function getErrorStyle(): self + { + return new self($this->input, $this->getErrorOutput()); + } + + public function createTable(): Table + { + $output = $this->output instanceof ConsoleOutputInterface ? $this->output->section() : $this->output; + $style = clone Table::getStyleDefinition('symfony-style-guide'); + $style->setCellHeaderFormat('%s'); + + return (new Table($output))->setStyle($style); + } + + private function getProgressBar(): ProgressBar + { + return $this->progressBar + ?? throw new RuntimeException('The ProgressBar is not started.'); + } + + private function autoPrependBlock(): void + { + $chars = substr(str_replace(\PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2); + + if (!isset($chars[0])) { + $this->newLine(); // empty history, so we should start with a new line. + + return; + } + // Prepend new line for each non LF chars (This means no blank line was output before) + $this->newLine(2 - substr_count($chars, "\n")); + } + + private function autoPrependText(): void + { + $fetched = $this->bufferedOutput->fetch(); + // Prepend new line if last char isn't EOL: + if (!str_ends_with($fetched, "\n")) { + $this->newLine(); + } + } + + private function writeBuffer(string $message, bool $newLine, int $type): void + { + // We need to know if the last chars are PHP_EOL + $this->bufferedOutput->write($message, $newLine, $type); + } + + private function createBlock(iterable $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false): array + { + $indentLength = 0; + $prefixLength = Helper::width(Helper::removeDecoration($this->getFormatter(), $prefix)); + $lines = []; + + if (null !== $type) { + $type = sprintf('[%s] ', $type); + $indentLength = \strlen($type); + $lineIndentation = str_repeat(' ', $indentLength); + } + + // wrap and add newlines for each element + foreach ($messages as $key => $message) { + if ($escape) { + $message = OutputFormatter::escape($message); + } + + $decorationLength = Helper::width($message) - Helper::width(Helper::removeDecoration($this->getFormatter(), $message)); + $messageLineLength = min($this->lineLength - $prefixLength - $indentLength + $decorationLength, $this->lineLength); + $messageLines = explode(\PHP_EOL, wordwrap($message, $messageLineLength, \PHP_EOL, true)); + foreach ($messageLines as $messageLine) { + $lines[] = $messageLine; + } + + if (\count($messages) > 1 && $key < \count($messages) - 1) { + $lines[] = ''; + } + } + + $firstLineIndex = 0; + if ($padding && $this->isDecorated()) { + $firstLineIndex = 1; + array_unshift($lines, ''); + $lines[] = ''; + } + + foreach ($lines as $i => &$line) { + if (null !== $type) { + $line = $firstLineIndex === $i ? $type.$line : $lineIndentation.$line; + } + + $line = $prefix.$line; + $line .= str_repeat(' ', max($this->lineLength - Helper::width(Helper::removeDecoration($this->getFormatter(), $line)), 0)); + + if ($style) { + $line = sprintf('<%s>%s', $style, $line); + } + } + + return $lines; + } +} diff --git a/vendor/symfony/console/Terminal.php b/vendor/symfony/console/Terminal.php new file mode 100644 index 0000000..80020c9 --- /dev/null +++ b/vendor/symfony/console/Terminal.php @@ -0,0 +1,168 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +class Terminal +{ + private static ?int $width = null; + private static ?int $height = null; + private static ?bool $stty = null; + + /** + * Gets the terminal width. + */ + public function getWidth(): int + { + $width = getenv('COLUMNS'); + if (false !== $width) { + return (int) trim($width); + } + + if (null === self::$width) { + self::initDimensions(); + } + + return self::$width ?: 80; + } + + /** + * Gets the terminal height. + */ + public function getHeight(): int + { + $height = getenv('LINES'); + if (false !== $height) { + return (int) trim($height); + } + + if (null === self::$height) { + self::initDimensions(); + } + + return self::$height ?: 50; + } + + /** + * @internal + */ + public static function hasSttyAvailable(): bool + { + if (null !== self::$stty) { + return self::$stty; + } + + // skip check if exec function is disabled + if (!\function_exists('exec')) { + return false; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = 0 === $exitcode; + } + + private static function initDimensions() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) { + // extract [w, H] from "wxh (WxH)" + // or [w, h] from "wxh" + self::$width = (int) $matches[1]; + self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2]; + } elseif (!self::hasVt100Support() && self::hasSttyAvailable()) { + // only use stty on Windows if the terminal does not support vt100 (e.g. Windows 7 + git-bash) + // testing for stty in a Windows 10 vt100-enabled console will implicitly disable vt100 support on STDOUT + self::initDimensionsUsingStty(); + } elseif (null !== $dimensions = self::getConsoleMode()) { + // extract [w, h] from "wxh" + self::$width = (int) $dimensions[0]; + self::$height = (int) $dimensions[1]; + } + } else { + self::initDimensionsUsingStty(); + } + } + + /** + * Returns whether STDOUT has vt100 support (some Windows 10+ configurations). + */ + private static function hasVt100Support(): bool + { + return \function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(fopen('php://stdout', 'w')); + } + + /** + * Initializes dimensions using the output of an stty columns line. + */ + private static function initDimensionsUsingStty() + { + if ($sttyString = self::getSttyColumns()) { + if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { + // extract [w, h] from "rows h; columns w;" + self::$width = (int) $matches[2]; + self::$height = (int) $matches[1]; + } elseif (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) { + // extract [w, h] from "; h rows; w columns" + self::$width = (int) $matches[2]; + self::$height = (int) $matches[1]; + } + } + } + + /** + * Runs and parses mode CON if it's available, suppressing any error output. + * + * @return int[]|null An array composed of the width and the height or null if it could not be parsed + */ + private static function getConsoleMode(): ?array + { + $info = self::readFromProcess('mode CON'); + + if (null === $info || !preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { + return null; + } + + return [(int) $matches[2], (int) $matches[1]]; + } + + /** + * Runs and parses stty -a if it's available, suppressing any error output. + */ + private static function getSttyColumns(): ?string + { + return self::readFromProcess('stty -a | grep columns'); + } + + private static function readFromProcess(string $command): ?string + { + if (!\function_exists('proc_open')) { + return null; + } + + $descriptorspec = [ + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; + + $process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); + if (!\is_resource($process)) { + return null; + } + + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + return $info; + } +} diff --git a/vendor/symfony/console/Tester/ApplicationTester.php b/vendor/symfony/console/Tester/ApplicationTester.php new file mode 100644 index 0000000..275a305 --- /dev/null +++ b/vendor/symfony/console/Tester/ApplicationTester.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; + +/** + * Eases the testing of console applications. + * + * When testing an application, don't forget to disable the auto exit flag: + * + * $application = new Application(); + * $application->setAutoExit(false); + * + * @author Fabien Potencier + */ +class ApplicationTester +{ + use TesterTrait; + + private $application; + + public function __construct(Application $application) + { + $this->application = $application; + } + + /** + * Executes the application. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * * capture_stderr_separately: Make output of stdOut and stdErr separately available + * + * @return int The command exit code + */ + public function run(array $input, array $options = []): int + { + $prevShellVerbosity = getenv('SHELL_VERBOSITY'); + + try { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + if ($this->inputs) { + $this->input->setStream(self::createStream($this->inputs)); + } + + $this->initOutput($options); + + return $this->statusCode = $this->application->run($this->input, $this->output); + } finally { + // SHELL_VERBOSITY is set by Application::configureIO so we need to unset/reset it + // to its previous value to avoid one test's verbosity to spread to the following tests + if (false === $prevShellVerbosity) { + if (\function_exists('putenv')) { + @putenv('SHELL_VERBOSITY'); + } + unset($_ENV['SHELL_VERBOSITY']); + unset($_SERVER['SHELL_VERBOSITY']); + } else { + if (\function_exists('putenv')) { + @putenv('SHELL_VERBOSITY='.$prevShellVerbosity); + } + $_ENV['SHELL_VERBOSITY'] = $prevShellVerbosity; + $_SERVER['SHELL_VERBOSITY'] = $prevShellVerbosity; + } + } + } +} diff --git a/vendor/symfony/console/Tester/CommandCompletionTester.php b/vendor/symfony/console/Tester/CommandCompletionTester.php new file mode 100644 index 0000000..ade7327 --- /dev/null +++ b/vendor/symfony/console/Tester/CommandCompletionTester.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; + +/** + * Eases the testing of command completion. + * + * @author Jérôme Tamarelle + */ +class CommandCompletionTester +{ + private $command; + + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Create completion suggestions from input tokens. + */ + public function complete(array $input): array + { + $currentIndex = \count($input); + if ('' === end($input)) { + array_pop($input); + } + array_unshift($input, $this->command->getName()); + + $completionInput = CompletionInput::fromTokens($input, $currentIndex); + $completionInput->bind($this->command->getDefinition()); + $suggestions = new CompletionSuggestions(); + + $this->command->complete($completionInput, $suggestions); + + $options = []; + foreach ($suggestions->getOptionSuggestions() as $option) { + $options[] = '--'.$option->getName(); + } + + return array_map('strval', array_merge($options, $suggestions->getValueSuggestions())); + } +} diff --git a/vendor/symfony/console/Tester/CommandTester.php b/vendor/symfony/console/Tester/CommandTester.php new file mode 100644 index 0000000..f6ee4b7 --- /dev/null +++ b/vendor/symfony/console/Tester/CommandTester.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; + +/** + * Eases the testing of console commands. + * + * @author Fabien Potencier + * @author Robin Chalas + */ +class CommandTester +{ + use TesterTrait; + + private $command; + + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Executes the command. + * + * Available execution options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * * capture_stderr_separately: Make output of stdOut and stdErr separately available + * + * @param array $input An array of command arguments and options + * @param array $options An array of execution options + * + * @return int The command exit code + */ + public function execute(array $input, array $options = []): int + { + // set the command name automatically if the application requires + // this argument and no command name was passed + if (!isset($input['command']) + && (null !== $application = $this->command->getApplication()) + && $application->getDefinition()->hasArgument('command') + ) { + $input = array_merge(['command' => $this->command->getName()], $input); + } + + $this->input = new ArrayInput($input); + // Use an in-memory input stream even if no inputs are set so that QuestionHelper::ask() does not rely on the blocking STDIN. + $this->input->setStream(self::createStream($this->inputs)); + + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + if (!isset($options['decorated'])) { + $options['decorated'] = false; + } + + $this->initOutput($options); + + return $this->statusCode = $this->command->run($this->input, $this->output); + } +} diff --git a/vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php b/vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php new file mode 100644 index 0000000..a473242 --- /dev/null +++ b/vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Console\Command\Command; + +final class CommandIsSuccessful extends Constraint +{ + /** + * {@inheritdoc} + */ + public function toString(): string + { + return 'is successful'; + } + + /** + * {@inheritdoc} + */ + protected function matches($other): bool + { + return Command::SUCCESS === $other; + } + + /** + * {@inheritdoc} + */ + protected function failureDescription($other): string + { + return 'the command '.$this->toString(); + } + + /** + * {@inheritdoc} + */ + protected function additionalFailureDescription($other): string + { + $mapping = [ + Command::FAILURE => 'Command failed.', + Command::INVALID => 'Command was invalid.', + ]; + + return $mapping[$other] ?? sprintf('Command returned exit status %d.', $other); + } +} diff --git a/vendor/symfony/console/Tester/TesterTrait.php b/vendor/symfony/console/Tester/TesterTrait.php new file mode 100644 index 0000000..b238f95 --- /dev/null +++ b/vendor/symfony/console/Tester/TesterTrait.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use PHPUnit\Framework\Assert; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Tester\Constraint\CommandIsSuccessful; + +/** + * @author Amrouche Hamza + */ +trait TesterTrait +{ + private $output; + private array $inputs = []; + private bool $captureStreamsIndependently = false; + private $input; + private int $statusCode; + + /** + * Gets the display returned by the last execution of the command or application. + * + * @throws \RuntimeException If it's called before the execute method + */ + public function getDisplay(bool $normalize = false): string + { + if (!isset($this->output)) { + throw new \RuntimeException('Output not initialized, did you execute the command before requesting the display?'); + } + + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(\PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the output written to STDERR by the application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + */ + public function getErrorOutput(bool $normalize = false): string + { + if (!$this->captureStreamsIndependently) { + throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.'); + } + + rewind($this->output->getErrorOutput()->getStream()); + + $display = stream_get_contents($this->output->getErrorOutput()->getStream()); + + if ($normalize) { + $display = str_replace(\PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the command or application. + */ + public function getInput(): InputInterface + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the command or application. + */ + public function getOutput(): OutputInterface + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the command or application. + * + * @throws \RuntimeException If it's called before the execute method + */ + public function getStatusCode(): int + { + return $this->statusCode ?? throw new \RuntimeException('Status code not initialized, did you execute the command before requesting the status code?'); + } + + public function assertCommandIsSuccessful(string $message = ''): void + { + Assert::assertThat($this->statusCode, new CommandIsSuccessful(), $message); + } + + /** + * Sets the user inputs. + * + * @param array $inputs An array of strings representing each input + * passed to the command input stream + * + * @return $this + */ + public function setInputs(array $inputs): static + { + $this->inputs = $inputs; + + return $this; + } + + /** + * Initializes the output property. + * + * Available options: + * + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * * capture_stderr_separately: Make output of stdOut and stdErr separately available + */ + private function initOutput(array $options) + { + $this->captureStreamsIndependently = \array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately']; + if (!$this->captureStreamsIndependently) { + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + } else { + $this->output = new ConsoleOutput( + $options['verbosity'] ?? ConsoleOutput::VERBOSITY_NORMAL, + $options['decorated'] ?? null + ); + + $errorOutput = new StreamOutput(fopen('php://memory', 'w', false)); + $errorOutput->setFormatter($this->output->getFormatter()); + $errorOutput->setVerbosity($this->output->getVerbosity()); + $errorOutput->setDecorated($this->output->isDecorated()); + + $reflectedOutput = new \ReflectionObject($this->output); + $strErrProperty = $reflectedOutput->getProperty('stderr'); + $strErrProperty->setAccessible(true); + $strErrProperty->setValue($this->output, $errorOutput); + + $reflectedParent = $reflectedOutput->getParentClass(); + $streamProperty = $reflectedParent->getProperty('stream'); + $streamProperty->setAccessible(true); + $streamProperty->setValue($this->output, fopen('php://memory', 'w', false)); + } + } + + /** + * @return resource + */ + private static function createStream(array $inputs) + { + $stream = fopen('php://memory', 'r+', false); + + foreach ($inputs as $input) { + fwrite($stream, $input.\PHP_EOL); + } + + rewind($stream); + + return $stream; + } +} diff --git a/vendor/symfony/console/composer.json b/vendor/symfony/console/composer.json new file mode 100644 index 0000000..7d3947f --- /dev/null +++ b/vendor/symfony/console/composer.json @@ -0,0 +1,56 @@ +{ + "name": "symfony/console", + "type": "library", + "description": "Eases the creation of beautiful and testable command line interfaces", + "keywords": ["console", "cli", "command line", "terminal"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.4|^6.0" + }, + "require-dev": { + "symfony/config": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0", + "psr/log": "^1|^2|^3" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "suggest": { + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "", + "psr/log": "For using the console logger" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Console\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/vendor/symfony/deprecation-contracts/.gitignore b/vendor/symfony/deprecation-contracts/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/deprecation-contracts/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/deprecation-contracts/CHANGELOG.md b/vendor/symfony/deprecation-contracts/CHANGELOG.md new file mode 100644 index 0000000..7932e26 --- /dev/null +++ b/vendor/symfony/deprecation-contracts/CHANGELOG.md @@ -0,0 +1,5 @@ +CHANGELOG +========= + +The changelog is maintained for all Symfony contracts at the following URL: +https://github.com/symfony/contracts/blob/main/CHANGELOG.md diff --git a/vendor/symfony/deprecation-contracts/LICENSE b/vendor/symfony/deprecation-contracts/LICENSE new file mode 100644 index 0000000..406242f --- /dev/null +++ b/vendor/symfony/deprecation-contracts/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020-2022 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/deprecation-contracts/README.md b/vendor/symfony/deprecation-contracts/README.md new file mode 100644 index 0000000..4957933 --- /dev/null +++ b/vendor/symfony/deprecation-contracts/README.md @@ -0,0 +1,26 @@ +Symfony Deprecation Contracts +============================= + +A generic function and convention to trigger deprecation notices. + +This package provides a single global function named `trigger_deprecation()` that triggers silenced deprecation notices. + +By using a custom PHP error handler such as the one provided by the Symfony ErrorHandler component, +the triggered deprecations can be caught and logged for later discovery, both on dev and prod environments. + +The function requires at least 3 arguments: + - the name of the Composer package that is triggering the deprecation + - the version of the package that introduced the deprecation + - the message of the deprecation + - more arguments can be provided: they will be inserted in the message using `printf()` formatting + +Example: +```php +trigger_deprecation('symfony/blockchain', '8.9', 'Using "%s" is deprecated, use "%s" instead.', 'bitcoin', 'fabcoin'); +``` + +This will generate the following message: +`Since symfony/blockchain 8.9: Using "bitcoin" is deprecated, use "fabcoin" instead.` + +While not necessarily recommended, the deprecation notices can be completely ignored by declaring an empty +`function trigger_deprecation() {}` in your application. diff --git a/vendor/symfony/deprecation-contracts/composer.json b/vendor/symfony/deprecation-contracts/composer.json new file mode 100644 index 0000000..1c1b4ba --- /dev/null +++ b/vendor/symfony/deprecation-contracts/composer.json @@ -0,0 +1,35 @@ +{ + "name": "symfony/deprecation-contracts", + "type": "library", + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=8.0.2" + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + } +} diff --git a/vendor/symfony/deprecation-contracts/function.php b/vendor/symfony/deprecation-contracts/function.php new file mode 100644 index 0000000..2d56512 --- /dev/null +++ b/vendor/symfony/deprecation-contracts/function.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (!function_exists('trigger_deprecation')) { + /** + * Triggers a silenced deprecation notice. + * + * @param string $package The name of the Composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message The message of the deprecation + * @param mixed ...$args Values to insert in the message using printf() formatting + * + * @author Nicolas Grekas + */ + function trigger_deprecation(string $package, string $version, string $message, mixed ...$args): void + { + @trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED); + } +} diff --git a/vendor/symfony/filesystem/CHANGELOG.md b/vendor/symfony/filesystem/CHANGELOG.md new file mode 100644 index 0000000..fcb7170 --- /dev/null +++ b/vendor/symfony/filesystem/CHANGELOG.md @@ -0,0 +1,82 @@ +CHANGELOG +========= + +5.4 +--- + + * Add `Path` class + * Add `$lock` argument to `Filesystem::appendToFile()` + +5.0.0 +----- + + * `Filesystem::dumpFile()` and `appendToFile()` don't accept arrays anymore + +4.4.0 +----- + + * support for passing a `null` value to `Filesystem::isAbsolutePath()` is deprecated and will be removed in 5.0 + * `tempnam()` now accepts a third argument `$suffix`. + +4.3.0 +----- + + * support for passing arrays to `Filesystem::dumpFile()` is deprecated and will be removed in 5.0 + * support for passing arrays to `Filesystem::appendToFile()` is deprecated and will be removed in 5.0 + +4.0.0 +----- + + * removed `LockHandler` + * Support for passing relative paths to `Filesystem::makePathRelative()` has been removed. + +3.4.0 +----- + + * support for passing relative paths to `Filesystem::makePathRelative()` is deprecated and will be removed in 4.0 + +3.3.0 +----- + + * added `appendToFile()` to append contents to existing files + +3.2.0 +----- + + * added `readlink()` as a platform independent method to read links + +3.0.0 +----- + + * removed `$mode` argument from `Filesystem::dumpFile()` + +2.8.0 +----- + + * added tempnam() a stream aware version of PHP's native tempnam() + +2.6.0 +----- + + * added LockHandler + +2.3.12 +------ + + * deprecated dumpFile() file mode argument. + +2.3.0 +----- + + * added the dumpFile() method to atomically write files + +2.2.0 +----- + + * added a delete option for the mirror() method + +2.1.0 +----- + + * 24eb396 : BC Break : mkdir() function now throws exception in case of failure instead of returning Boolean value + * created the component diff --git a/vendor/symfony/filesystem/Exception/ExceptionInterface.php b/vendor/symfony/filesystem/Exception/ExceptionInterface.php new file mode 100644 index 0000000..fc438d9 --- /dev/null +++ b/vendor/symfony/filesystem/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * Exception interface for all exceptions thrown by the component. + * + * @author Romain Neutron + */ +interface ExceptionInterface extends \Throwable +{ +} diff --git a/vendor/symfony/filesystem/Exception/FileNotFoundException.php b/vendor/symfony/filesystem/Exception/FileNotFoundException.php new file mode 100644 index 0000000..48b6408 --- /dev/null +++ b/vendor/symfony/filesystem/Exception/FileNotFoundException.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * Exception class thrown when a file couldn't be found. + * + * @author Fabien Potencier + * @author Christian Gärtner + */ +class FileNotFoundException extends IOException +{ + public function __construct(string $message = null, int $code = 0, \Throwable $previous = null, string $path = null) + { + if (null === $message) { + if (null === $path) { + $message = 'File could not be found.'; + } else { + $message = sprintf('File "%s" could not be found.', $path); + } + } + + parent::__construct($message, $code, $previous, $path); + } +} diff --git a/vendor/symfony/filesystem/Exception/IOException.php b/vendor/symfony/filesystem/Exception/IOException.php new file mode 100644 index 0000000..bcca860 --- /dev/null +++ b/vendor/symfony/filesystem/Exception/IOException.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * Exception class thrown when a filesystem operation failure happens. + * + * @author Romain Neutron + * @author Christian Gärtner + * @author Fabien Potencier + */ +class IOException extends \RuntimeException implements IOExceptionInterface +{ + private ?string $path; + + public function __construct(string $message, int $code = 0, \Throwable $previous = null, string $path = null) + { + $this->path = $path; + + parent::__construct($message, $code, $previous); + } + + /** + * {@inheritdoc} + */ + public function getPath(): ?string + { + return $this->path; + } +} diff --git a/vendor/symfony/filesystem/Exception/IOExceptionInterface.php b/vendor/symfony/filesystem/Exception/IOExceptionInterface.php new file mode 100644 index 0000000..90c71db --- /dev/null +++ b/vendor/symfony/filesystem/Exception/IOExceptionInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * IOException interface for file and input/output stream related exceptions thrown by the component. + * + * @author Christian Gärtner + */ +interface IOExceptionInterface extends ExceptionInterface +{ + /** + * Returns the associated path for the exception. + */ + public function getPath(): ?string; +} diff --git a/vendor/symfony/filesystem/Exception/InvalidArgumentException.php b/vendor/symfony/filesystem/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..abadc20 --- /dev/null +++ b/vendor/symfony/filesystem/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * @author Christian Flothmann + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/filesystem/Exception/RuntimeException.php b/vendor/symfony/filesystem/Exception/RuntimeException.php new file mode 100644 index 0000000..a7512dc --- /dev/null +++ b/vendor/symfony/filesystem/Exception/RuntimeException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * @author Théo Fidry + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/filesystem/Filesystem.php b/vendor/symfony/filesystem/Filesystem.php new file mode 100644 index 0000000..c96ed6a --- /dev/null +++ b/vendor/symfony/filesystem/Filesystem.php @@ -0,0 +1,737 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem; + +use Symfony\Component\Filesystem\Exception\FileNotFoundException; +use Symfony\Component\Filesystem\Exception\InvalidArgumentException; +use Symfony\Component\Filesystem\Exception\IOException; + +/** + * Provides basic utility to manipulate the file system. + * + * @author Fabien Potencier + */ +class Filesystem +{ + private static $lastError; + + /** + * Copies a file. + * + * If the target file is older than the origin file, it's always overwritten. + * If the target file is newer, it is overwritten only when the + * $overwriteNewerFiles option is set to true. + * + * @throws FileNotFoundException When originFile doesn't exist + * @throws IOException When copy fails + */ + public function copy(string $originFile, string $targetFile, bool $overwriteNewerFiles = false) + { + $originIsLocal = stream_is_local($originFile) || 0 === stripos($originFile, 'file://'); + if ($originIsLocal && !is_file($originFile)) { + throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile); + } + + $this->mkdir(\dirname($targetFile)); + + $doCopy = true; + if (!$overwriteNewerFiles && null === parse_url($originFile, \PHP_URL_HOST) && is_file($targetFile)) { + $doCopy = filemtime($originFile) > filemtime($targetFile); + } + + if ($doCopy) { + // https://bugs.php.net/64634 + if (!$source = self::box('fopen', $originFile, 'r')) { + throw new IOException(sprintf('Failed to copy "%s" to "%s" because source file could not be opened for reading: ', $originFile, $targetFile).self::$lastError, 0, null, $originFile); + } + + // Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default + if (!$target = self::box('fopen', $targetFile, 'w', false, stream_context_create(['ftp' => ['overwrite' => true]]))) { + throw new IOException(sprintf('Failed to copy "%s" to "%s" because target file could not be opened for writing: ', $originFile, $targetFile).self::$lastError, 0, null, $originFile); + } + + $bytesCopied = stream_copy_to_stream($source, $target); + fclose($source); + fclose($target); + unset($source, $target); + + if (!is_file($targetFile)) { + throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile); + } + + if ($originIsLocal) { + // Like `cp`, preserve executable permission bits + self::box('chmod', $targetFile, fileperms($targetFile) | (fileperms($originFile) & 0111)); + + if ($bytesCopied !== $bytesOrigin = filesize($originFile)) { + throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile); + } + } + } + } + + /** + * Creates a directory recursively. + * + * @throws IOException On any directory creation failure + */ + public function mkdir(string|iterable $dirs, int $mode = 0777) + { + foreach ($this->toIterable($dirs) as $dir) { + if (is_dir($dir)) { + continue; + } + + if (!self::box('mkdir', $dir, $mode, true) && !is_dir($dir)) { + throw new IOException(sprintf('Failed to create "%s": ', $dir).self::$lastError, 0, null, $dir); + } + } + } + + /** + * Checks the existence of files or directories. + */ + public function exists(string|iterable $files): bool + { + $maxPathLength = \PHP_MAXPATHLEN - 2; + + foreach ($this->toIterable($files) as $file) { + if (\strlen($file) > $maxPathLength) { + throw new IOException(sprintf('Could not check if file exist because path length exceeds %d characters.', $maxPathLength), 0, null, $file); + } + + if (!file_exists($file)) { + return false; + } + } + + return true; + } + + /** + * Sets access and modification time of file. + * + * @param int|null $time The touch time as a Unix timestamp, if not supplied the current system time is used + * @param int|null $atime The access time as a Unix timestamp, if not supplied the current system time is used + * + * @throws IOException When touch fails + */ + public function touch(string|iterable $files, int $time = null, int $atime = null) + { + foreach ($this->toIterable($files) as $file) { + if (!($time ? self::box('touch', $file, $time, $atime) : self::box('touch', $file))) { + throw new IOException(sprintf('Failed to touch "%s": ', $file).self::$lastError, 0, null, $file); + } + } + } + + /** + * Removes files or directories. + * + * @throws IOException When removal fails + */ + public function remove(string|iterable $files) + { + if ($files instanceof \Traversable) { + $files = iterator_to_array($files, false); + } elseif (!\is_array($files)) { + $files = [$files]; + } + + self::doRemove($files, false); + } + + private static function doRemove(array $files, bool $isRecursive): void + { + $files = array_reverse($files); + foreach ($files as $file) { + if (is_link($file)) { + // See https://bugs.php.net/52176 + if (!(self::box('unlink', $file) || '\\' !== \DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove symlink "%s": ', $file).self::$lastError); + } + } elseif (is_dir($file)) { + if (!$isRecursive) { + $tmpName = \dirname(realpath($file)).'/.'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-.')); + + if (file_exists($tmpName)) { + try { + self::doRemove([$tmpName], true); + } catch (IOException $e) { + } + } + + if (!file_exists($tmpName) && self::box('rename', $file, $tmpName)) { + $origFile = $file; + $file = $tmpName; + } else { + $origFile = null; + } + } + + $files = new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS); + self::doRemove(iterator_to_array($files, true), true); + + if (!self::box('rmdir', $file) && file_exists($file) && !$isRecursive) { + $lastError = self::$lastError; + + if (null !== $origFile && self::box('rename', $file, $origFile)) { + $file = $origFile; + } + + throw new IOException(sprintf('Failed to remove directory "%s": ', $file).$lastError); + } + } elseif (!self::box('unlink', $file) && (str_contains(self::$lastError, 'Permission denied') || file_exists($file))) { + throw new IOException(sprintf('Failed to remove file "%s": ', $file).self::$lastError); + } + } + } + + /** + * Change mode for an array of files or directories. + * + * @param int $mode The new mode (octal) + * @param int $umask The mode mask (octal) + * @param bool $recursive Whether change the mod recursively or not + * + * @throws IOException When the change fails + */ + public function chmod(string|iterable $files, int $mode, int $umask = 0000, bool $recursive = false) + { + foreach ($this->toIterable($files) as $file) { + if (\is_int($mode) && !self::box('chmod', $file, $mode & ~$umask)) { + throw new IOException(sprintf('Failed to chmod file "%s": ', $file).self::$lastError, 0, null, $file); + } + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chmod(new \FilesystemIterator($file), $mode, $umask, true); + } + } + } + + /** + * Change the owner of an array of files or directories. + * + * @param string|int $user A user name or number + * @param bool $recursive Whether change the owner recursively or not + * + * @throws IOException When the change fails + */ + public function chown(string|iterable $files, string|int $user, bool $recursive = false) + { + foreach ($this->toIterable($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chown(new \FilesystemIterator($file), $user, true); + } + if (is_link($file) && \function_exists('lchown')) { + if (!self::box('lchown', $file, $user)) { + throw new IOException(sprintf('Failed to chown file "%s": ', $file).self::$lastError, 0, null, $file); + } + } else { + if (!self::box('chown', $file, $user)) { + throw new IOException(sprintf('Failed to chown file "%s": ', $file).self::$lastError, 0, null, $file); + } + } + } + } + + /** + * Change the group of an array of files or directories. + * + * @param string|int $group A group name or number + * @param bool $recursive Whether change the group recursively or not + * + * @throws IOException When the change fails + */ + public function chgrp(string|iterable $files, string|int $group, bool $recursive = false) + { + foreach ($this->toIterable($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chgrp(new \FilesystemIterator($file), $group, true); + } + if (is_link($file) && \function_exists('lchgrp')) { + if (!self::box('lchgrp', $file, $group)) { + throw new IOException(sprintf('Failed to chgrp file "%s": ', $file).self::$lastError, 0, null, $file); + } + } else { + if (!self::box('chgrp', $file, $group)) { + throw new IOException(sprintf('Failed to chgrp file "%s": ', $file).self::$lastError, 0, null, $file); + } + } + } + } + + /** + * Renames a file or a directory. + * + * @throws IOException When target file or directory already exists + * @throws IOException When origin cannot be renamed + */ + public function rename(string $origin, string $target, bool $overwrite = false) + { + // we check that target does not exist + if (!$overwrite && $this->isReadable($target)) { + throw new IOException(sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target); + } + + if (!self::box('rename', $origin, $target)) { + if (is_dir($origin)) { + // See https://bugs.php.net/54097 & https://php.net/rename#113943 + $this->mirror($origin, $target, null, ['override' => $overwrite, 'delete' => $overwrite]); + $this->remove($origin); + + return; + } + throw new IOException(sprintf('Cannot rename "%s" to "%s": ', $origin, $target).self::$lastError, 0, null, $target); + } + } + + /** + * Tells whether a file exists and is readable. + * + * @throws IOException When windows path is longer than 258 characters + */ + private function isReadable(string $filename): bool + { + $maxPathLength = \PHP_MAXPATHLEN - 2; + + if (\strlen($filename) > $maxPathLength) { + throw new IOException(sprintf('Could not check if file is readable because path length exceeds %d characters.', $maxPathLength), 0, null, $filename); + } + + return is_readable($filename); + } + + /** + * Creates a symbolic link or copy a directory. + * + * @throws IOException When symlink fails + */ + public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false) + { + self::assertFunctionExists('symlink'); + + if ('\\' === \DIRECTORY_SEPARATOR) { + $originDir = strtr($originDir, '/', '\\'); + $targetDir = strtr($targetDir, '/', '\\'); + + if ($copyOnWindows) { + $this->mirror($originDir, $targetDir); + + return; + } + } + + $this->mkdir(\dirname($targetDir)); + + if (is_link($targetDir)) { + if (readlink($targetDir) === $originDir) { + return; + } + $this->remove($targetDir); + } + + if (!self::box('symlink', $originDir, $targetDir)) { + $this->linkException($originDir, $targetDir, 'symbolic'); + } + } + + /** + * Creates a hard link, or several hard links to a file. + * + * @param string|string[] $targetFiles The target file(s) + * + * @throws FileNotFoundException When original file is missing or not a file + * @throws IOException When link fails, including if link already exists + */ + public function hardlink(string $originFile, string|iterable $targetFiles) + { + self::assertFunctionExists('link'); + + if (!$this->exists($originFile)) { + throw new FileNotFoundException(null, 0, null, $originFile); + } + + if (!is_file($originFile)) { + throw new FileNotFoundException(sprintf('Origin file "%s" is not a file.', $originFile)); + } + + foreach ($this->toIterable($targetFiles) as $targetFile) { + if (is_file($targetFile)) { + if (fileinode($originFile) === fileinode($targetFile)) { + continue; + } + $this->remove($targetFile); + } + + if (!self::box('link', $originFile, $targetFile)) { + $this->linkException($originFile, $targetFile, 'hard'); + } + } + } + + /** + * @param string $linkType Name of the link type, typically 'symbolic' or 'hard' + */ + private function linkException(string $origin, string $target, string $linkType) + { + if (self::$lastError) { + if ('\\' === \DIRECTORY_SEPARATOR && str_contains(self::$lastError, 'error code(1314)')) { + throw new IOException(sprintf('Unable to create "%s" link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target); + } + } + throw new IOException(sprintf('Failed to create "%s" link from "%s" to "%s": ', $linkType, $origin, $target).self::$lastError, 0, null, $target); + } + + /** + * Resolves links in paths. + * + * With $canonicalize = false (default) + * - if $path does not exist or is not a link, returns null + * - if $path is a link, returns the next direct target of the link without considering the existence of the target + * + * With $canonicalize = true + * - if $path does not exist, returns null + * - if $path exists, returns its absolute fully resolved final version + */ + public function readlink(string $path, bool $canonicalize = false): ?string + { + if (!$canonicalize && !is_link($path)) { + return null; + } + + if ($canonicalize) { + if (!$this->exists($path)) { + return null; + } + + return realpath($path); + } + + return readlink($path); + } + + /** + * Given an existing path, convert it to a path relative to a given starting path. + */ + public function makePathRelative(string $endPath, string $startPath): string + { + if (!$this->isAbsolutePath($startPath)) { + throw new InvalidArgumentException(sprintf('The start path "%s" is not absolute.', $startPath)); + } + + if (!$this->isAbsolutePath($endPath)) { + throw new InvalidArgumentException(sprintf('The end path "%s" is not absolute.', $endPath)); + } + + // Normalize separators on Windows + if ('\\' === \DIRECTORY_SEPARATOR) { + $endPath = str_replace('\\', '/', $endPath); + $startPath = str_replace('\\', '/', $startPath); + } + + $splitDriveLetter = function ($path) { + return (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0])) + ? [substr($path, 2), strtoupper($path[0])] + : [$path, null]; + }; + + $splitPath = function ($path) { + $result = []; + + foreach (explode('/', trim($path, '/')) as $segment) { + if ('..' === $segment) { + array_pop($result); + } elseif ('.' !== $segment && '' !== $segment) { + $result[] = $segment; + } + } + + return $result; + }; + + [$endPath, $endDriveLetter] = $splitDriveLetter($endPath); + [$startPath, $startDriveLetter] = $splitDriveLetter($startPath); + + $startPathArr = $splitPath($startPath); + $endPathArr = $splitPath($endPath); + + if ($endDriveLetter && $startDriveLetter && $endDriveLetter != $startDriveLetter) { + // End path is on another drive, so no relative path exists + return $endDriveLetter.':/'.($endPathArr ? implode('/', $endPathArr).'/' : ''); + } + + // Find for which directory the common path stops + $index = 0; + while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { + ++$index; + } + + // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) + if (1 === \count($startPathArr) && '' === $startPathArr[0]) { + $depth = 0; + } else { + $depth = \count($startPathArr) - $index; + } + + // Repeated "../" for each level need to reach the common path + $traverser = str_repeat('../', $depth); + + $endPathRemainder = implode('/', \array_slice($endPathArr, $index)); + + // Construct $endPath from traversing to the common path, then to the remaining $endPath + $relativePath = $traverser.('' !== $endPathRemainder ? $endPathRemainder.'/' : ''); + + return '' === $relativePath ? './' : $relativePath; + } + + /** + * Mirrors a directory to another. + * + * Copies files and directories from the origin directory into the target directory. By default: + * + * - existing files in the target directory will be overwritten, except if they are newer (see the `override` option) + * - files in the target directory that do not exist in the source directory will not be deleted (see the `delete` option) + * + * @param \Traversable|null $iterator Iterator that filters which files and directories to copy, if null a recursive iterator is created + * @param array $options An array of boolean options + * Valid options are: + * - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false) + * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false) + * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) + * + * @throws IOException When file type is unknown + */ + public function mirror(string $originDir, string $targetDir, \Traversable $iterator = null, array $options = []) + { + $targetDir = rtrim($targetDir, '/\\'); + $originDir = rtrim($originDir, '/\\'); + $originDirLen = \strlen($originDir); + + if (!$this->exists($originDir)) { + throw new IOException(sprintf('The origin directory specified "%s" was not found.', $originDir), 0, null, $originDir); + } + + // Iterate in destination folder to remove obsolete entries + if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) { + $deleteIterator = $iterator; + if (null === $deleteIterator) { + $flags = \FilesystemIterator::SKIP_DOTS; + $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST); + } + $targetDirLen = \strlen($targetDir); + foreach ($deleteIterator as $file) { + $origin = $originDir.substr($file->getPathname(), $targetDirLen); + if (!$this->exists($origin)) { + $this->remove($file); + } + } + } + + $copyOnWindows = $options['copy_on_windows'] ?? false; + + if (null === $iterator) { + $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS; + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST); + } + + $this->mkdir($targetDir); + $filesCreatedWhileMirroring = []; + + foreach ($iterator as $file) { + if ($file->getPathname() === $targetDir || $file->getRealPath() === $targetDir || isset($filesCreatedWhileMirroring[$file->getRealPath()])) { + continue; + } + + $target = $targetDir.substr($file->getPathname(), $originDirLen); + $filesCreatedWhileMirroring[$target] = true; + + if (!$copyOnWindows && is_link($file)) { + $this->symlink($file->getLinkTarget(), $target); + } elseif (is_dir($file)) { + $this->mkdir($target); + } elseif (is_file($file)) { + $this->copy($file, $target, $options['override'] ?? false); + } else { + throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); + } + } + } + + /** + * Returns whether the file path is an absolute path. + */ + public function isAbsolutePath(string $file): bool + { + return '' !== $file && (strspn($file, '/\\', 0, 1) + || (\strlen($file) > 3 && ctype_alpha($file[0]) + && ':' === $file[1] + && strspn($file, '/\\', 2, 1) + ) + || null !== parse_url($file, \PHP_URL_SCHEME) + ); + } + + /** + * Creates a temporary file with support for custom stream wrappers. + * + * @param string $prefix The prefix of the generated temporary filename + * Note: Windows uses only the first three characters of prefix + * @param string $suffix The suffix of the generated temporary filename + * + * @return string The new temporary filename (with path), or throw an exception on failure + */ + public function tempnam(string $dir, string $prefix, string $suffix = ''): string + { + [$scheme, $hierarchy] = $this->getSchemeAndHierarchy($dir); + + // If no scheme or scheme is "file" or "gs" (Google Cloud) create temp file in local filesystem + if ((null === $scheme || 'file' === $scheme || 'gs' === $scheme) && '' === $suffix) { + // If tempnam failed or no scheme return the filename otherwise prepend the scheme + if ($tmpFile = self::box('tempnam', $hierarchy, $prefix)) { + if (null !== $scheme && 'gs' !== $scheme) { + return $scheme.'://'.$tmpFile; + } + + return $tmpFile; + } + + throw new IOException('A temporary file could not be created: '.self::$lastError); + } + + // Loop until we create a valid temp file or have reached 10 attempts + for ($i = 0; $i < 10; ++$i) { + // Create a unique filename + $tmpFile = $dir.'/'.$prefix.uniqid(mt_rand(), true).$suffix; + + // Use fopen instead of file_exists as some streams do not support stat + // Use mode 'x+' to atomically check existence and create to avoid a TOCTOU vulnerability + if (!$handle = self::box('fopen', $tmpFile, 'x+')) { + continue; + } + + // Close the file if it was successfully opened + self::box('fclose', $handle); + + return $tmpFile; + } + + throw new IOException('A temporary file could not be created: '.self::$lastError); + } + + /** + * Atomically dumps content into a file. + * + * @param string|resource $content The data to write into the file + * + * @throws IOException if the file cannot be written to + */ + public function dumpFile(string $filename, $content) + { + if (\is_array($content)) { + throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be string or resource, array given.', __METHOD__)); + } + + $dir = \dirname($filename); + + if (!is_dir($dir)) { + $this->mkdir($dir); + } + + // Will create a temp file with 0600 access rights + // when the filesystem supports chmod. + $tmpFile = $this->tempnam($dir, basename($filename)); + + try { + if (false === self::box('file_put_contents', $tmpFile, $content)) { + throw new IOException(sprintf('Failed to write file "%s": ', $filename).self::$lastError, 0, null, $filename); + } + + self::box('chmod', $tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask()); + + $this->rename($tmpFile, $filename, true); + } finally { + if (file_exists($tmpFile)) { + self::box('unlink', $tmpFile); + } + } + } + + /** + * Appends content to an existing file. + * + * @param string|resource $content The content to append + * @param bool $lock Whether the file should be locked when writing to it + * + * @throws IOException If the file is not writable + */ + public function appendToFile(string $filename, $content/* , bool $lock = false */) + { + if (\is_array($content)) { + throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be string or resource, array given.', __METHOD__)); + } + + $dir = \dirname($filename); + + if (!is_dir($dir)) { + $this->mkdir($dir); + } + + $lock = \func_num_args() > 2 && func_get_arg(2); + + if (false === self::box('file_put_contents', $filename, $content, \FILE_APPEND | ($lock ? \LOCK_EX : 0))) { + throw new IOException(sprintf('Failed to write file "%s": ', $filename).self::$lastError, 0, null, $filename); + } + } + + private function toIterable(string|iterable $files): iterable + { + return is_iterable($files) ? $files : [$files]; + } + + /** + * Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> [file, tmp]). + */ + private function getSchemeAndHierarchy(string $filename): array + { + $components = explode('://', $filename, 2); + + return 2 === \count($components) ? [$components[0], $components[1]] : [null, $components[0]]; + } + + private static function assertFunctionExists(string $func): void + { + if (!\function_exists($func)) { + throw new IOException(sprintf('Unable to perform filesystem operation because the "%s()" function has been disabled.', $func)); + } + } + + private static function box(string $func, mixed ...$args): mixed + { + self::assertFunctionExists($func); + + self::$lastError = null; + set_error_handler(__CLASS__.'::handleError'); + try { + return $func(...$args); + } finally { + restore_error_handler(); + } + } + + /** + * @internal + */ + public static function handleError(int $type, string $msg) + { + self::$lastError = $msg; + } +} diff --git a/vendor/symfony/filesystem/LICENSE b/vendor/symfony/filesystem/LICENSE new file mode 100644 index 0000000..0083704 --- /dev/null +++ b/vendor/symfony/filesystem/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2023 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/filesystem/Path.php b/vendor/symfony/filesystem/Path.php new file mode 100644 index 0000000..9aa3735 --- /dev/null +++ b/vendor/symfony/filesystem/Path.php @@ -0,0 +1,819 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem; + +use Symfony\Component\Filesystem\Exception\InvalidArgumentException; +use Symfony\Component\Filesystem\Exception\RuntimeException; + +/** + * Contains utility methods for handling path strings. + * + * The methods in this class are able to deal with both UNIX and Windows paths + * with both forward and backward slashes. All methods return normalized parts + * containing only forward slashes and no excess "." and ".." segments. + * + * @author Bernhard Schussek + * @author Thomas Schulz + * @author Théo Fidry + */ +final class Path +{ + /** + * The number of buffer entries that triggers a cleanup operation. + */ + private const CLEANUP_THRESHOLD = 1250; + + /** + * The buffer size after the cleanup operation. + */ + private const CLEANUP_SIZE = 1000; + + /** + * Buffers input/output of {@link canonicalize()}. + * + * @var array + */ + private static $buffer = []; + + /** + * @var int + */ + private static $bufferSize = 0; + + /** + * Canonicalizes the given path. + * + * During normalization, all slashes are replaced by forward slashes ("/"). + * Furthermore, all "." and ".." segments are removed as far as possible. + * ".." segments at the beginning of relative paths are not removed. + * + * ```php + * echo Path::canonicalize("\symfony\puli\..\css\style.css"); + * // => /symfony/css/style.css + * + * echo Path::canonicalize("../css/./style.css"); + * // => ../css/style.css + * ``` + * + * This method is able to deal with both UNIX and Windows paths. + */ + public static function canonicalize(string $path): string + { + if ('' === $path) { + return ''; + } + + // This method is called by many other methods in this class. Buffer + // the canonicalized paths to make up for the severe performance + // decrease. + if (isset(self::$buffer[$path])) { + return self::$buffer[$path]; + } + + // Replace "~" with user's home directory. + if ('~' === $path[0]) { + $path = self::getHomeDirectory().substr($path, 1); + } + + $path = self::normalize($path); + + [$root, $pathWithoutRoot] = self::split($path); + + $canonicalParts = self::findCanonicalParts($root, $pathWithoutRoot); + + // Add the root directory again + self::$buffer[$path] = $canonicalPath = $root.implode('/', $canonicalParts); + ++self::$bufferSize; + + // Clean up regularly to prevent memory leaks + if (self::$bufferSize > self::CLEANUP_THRESHOLD) { + self::$buffer = \array_slice(self::$buffer, -self::CLEANUP_SIZE, null, true); + self::$bufferSize = self::CLEANUP_SIZE; + } + + return $canonicalPath; + } + + /** + * Normalizes the given path. + * + * During normalization, all slashes are replaced by forward slashes ("/"). + * Contrary to {@link canonicalize()}, this method does not remove invalid + * or dot path segments. Consequently, it is much more efficient and should + * be used whenever the given path is known to be a valid, absolute system + * path. + * + * This method is able to deal with both UNIX and Windows paths. + */ + public static function normalize(string $path): string + { + return str_replace('\\', '/', $path); + } + + /** + * Returns the directory part of the path. + * + * This method is similar to PHP's dirname(), but handles various cases + * where dirname() returns a weird result: + * + * - dirname() does not accept backslashes on UNIX + * - dirname("C:/symfony") returns "C:", not "C:/" + * - dirname("C:/") returns ".", not "C:/" + * - dirname("C:") returns ".", not "C:/" + * - dirname("symfony") returns ".", not "" + * - dirname() does not canonicalize the result + * + * This method fixes these shortcomings and behaves like dirname() + * otherwise. + * + * The result is a canonical path. + * + * @return string The canonical directory part. Returns the root directory + * if the root directory is passed. Returns an empty string + * if a relative path is passed that contains no slashes. + * Returns an empty string if an empty string is passed. + */ + public static function getDirectory(string $path): string + { + if ('' === $path) { + return ''; + } + + $path = self::canonicalize($path); + + // Maintain scheme + if (false !== $schemeSeparatorPosition = strpos($path, '://')) { + $scheme = substr($path, 0, $schemeSeparatorPosition + 3); + $path = substr($path, $schemeSeparatorPosition + 3); + } else { + $scheme = ''; + } + + if (false === $dirSeparatorPosition = strrpos($path, '/')) { + return ''; + } + + // Directory equals root directory "/" + if (0 === $dirSeparatorPosition) { + return $scheme.'/'; + } + + // Directory equals Windows root "C:/" + if (2 === $dirSeparatorPosition && ctype_alpha($path[0]) && ':' === $path[1]) { + return $scheme.substr($path, 0, 3); + } + + return $scheme.substr($path, 0, $dirSeparatorPosition); + } + + /** + * Returns canonical path of the user's home directory. + * + * Supported operating systems: + * + * - UNIX + * - Windows8 and upper + * + * If your operating system or environment isn't supported, an exception is thrown. + * + * The result is a canonical path. + * + * @throws RuntimeException If your operating system or environment isn't supported + */ + public static function getHomeDirectory(): string + { + // For UNIX support + if (getenv('HOME')) { + return self::canonicalize(getenv('HOME')); + } + + // For >= Windows8 support + if (getenv('HOMEDRIVE') && getenv('HOMEPATH')) { + return self::canonicalize(getenv('HOMEDRIVE').getenv('HOMEPATH')); + } + + throw new RuntimeException("Cannot find the home directory path: Your environment or operating system isn't supported."); + } + + /** + * Returns the root directory of a path. + * + * The result is a canonical path. + * + * @return string The canonical root directory. Returns an empty string if + * the given path is relative or empty. + */ + public static function getRoot(string $path): string + { + if ('' === $path) { + return ''; + } + + // Maintain scheme + if (false !== $schemeSeparatorPosition = strpos($path, '://')) { + $scheme = substr($path, 0, $schemeSeparatorPosition + 3); + $path = substr($path, $schemeSeparatorPosition + 3); + } else { + $scheme = ''; + } + + $firstCharacter = $path[0]; + + // UNIX root "/" or "\" (Windows style) + if ('/' === $firstCharacter || '\\' === $firstCharacter) { + return $scheme.'/'; + } + + $length = \strlen($path); + + // Windows root + if ($length > 1 && ':' === $path[1] && ctype_alpha($firstCharacter)) { + // Special case: "C:" + if (2 === $length) { + return $scheme.$path.'/'; + } + + // Normal case: "C:/ or "C:\" + if ('/' === $path[2] || '\\' === $path[2]) { + return $scheme.$firstCharacter.$path[1].'/'; + } + } + + return ''; + } + + /** + * Returns the file name without the extension from a file path. + * + * @param string|null $extension if specified, only that extension is cut + * off (may contain leading dot) + */ + public static function getFilenameWithoutExtension(string $path, string $extension = null): string + { + if ('' === $path) { + return ''; + } + + if (null !== $extension) { + // remove extension and trailing dot + return rtrim(basename($path, $extension), '.'); + } + + return pathinfo($path, \PATHINFO_FILENAME); + } + + /** + * Returns the extension from a file path (without leading dot). + * + * @param bool $forceLowerCase forces the extension to be lower-case + */ + public static function getExtension(string $path, bool $forceLowerCase = false): string + { + if ('' === $path) { + return ''; + } + + $extension = pathinfo($path, \PATHINFO_EXTENSION); + + if ($forceLowerCase) { + $extension = self::toLower($extension); + } + + return $extension; + } + + /** + * Returns whether the path has an (or the specified) extension. + * + * @param string $path the path string + * @param string|string[]|null $extensions if null or not provided, checks if + * an extension exists, otherwise + * checks for the specified extension + * or array of extensions (with or + * without leading dot) + * @param bool $ignoreCase whether to ignore case-sensitivity + */ + public static function hasExtension(string $path, $extensions = null, bool $ignoreCase = false): bool + { + if ('' === $path) { + return false; + } + + $actualExtension = self::getExtension($path, $ignoreCase); + + // Only check if path has any extension + if ([] === $extensions || null === $extensions) { + return '' !== $actualExtension; + } + + if (\is_string($extensions)) { + $extensions = [$extensions]; + } + + foreach ($extensions as $key => $extension) { + if ($ignoreCase) { + $extension = self::toLower($extension); + } + + // remove leading '.' in extensions array + $extensions[$key] = ltrim($extension, '.'); + } + + return \in_array($actualExtension, $extensions, true); + } + + /** + * Changes the extension of a path string. + * + * @param string $path The path string with filename.ext to change. + * @param string $extension new extension (with or without leading dot) + * + * @return string the path string with new file extension + */ + public static function changeExtension(string $path, string $extension): string + { + if ('' === $path) { + return ''; + } + + $actualExtension = self::getExtension($path); + $extension = ltrim($extension, '.'); + + // No extension for paths + if ('/' === substr($path, -1)) { + return $path; + } + + // No actual extension in path + if (empty($actualExtension)) { + return $path.('.' === substr($path, -1) ? '' : '.').$extension; + } + + return substr($path, 0, -\strlen($actualExtension)).$extension; + } + + public static function isAbsolute(string $path): bool + { + if ('' === $path) { + return false; + } + + // Strip scheme + if (false !== $schemeSeparatorPosition = strpos($path, '://')) { + $path = substr($path, $schemeSeparatorPosition + 3); + } + + $firstCharacter = $path[0]; + + // UNIX root "/" or "\" (Windows style) + if ('/' === $firstCharacter || '\\' === $firstCharacter) { + return true; + } + + // Windows root + if (\strlen($path) > 1 && ctype_alpha($firstCharacter) && ':' === $path[1]) { + // Special case: "C:" + if (2 === \strlen($path)) { + return true; + } + + // Normal case: "C:/ or "C:\" + if ('/' === $path[2] || '\\' === $path[2]) { + return true; + } + } + + return false; + } + + public static function isRelative(string $path): bool + { + return !self::isAbsolute($path); + } + + /** + * Turns a relative path into an absolute path in canonical form. + * + * Usually, the relative path is appended to the given base path. Dot + * segments ("." and "..") are removed/collapsed and all slashes turned + * into forward slashes. + * + * ```php + * echo Path::makeAbsolute("../style.css", "/symfony/puli/css"); + * // => /symfony/puli/style.css + * ``` + * + * If an absolute path is passed, that path is returned unless its root + * directory is different than the one of the base path. In that case, an + * exception is thrown. + * + * ```php + * Path::makeAbsolute("/style.css", "/symfony/puli/css"); + * // => /style.css + * + * Path::makeAbsolute("C:/style.css", "C:/symfony/puli/css"); + * // => C:/style.css + * + * Path::makeAbsolute("C:/style.css", "/symfony/puli/css"); + * // InvalidArgumentException + * ``` + * + * If the base path is not an absolute path, an exception is thrown. + * + * The result is a canonical path. + * + * @param string $basePath an absolute base path + * + * @throws InvalidArgumentException if the base path is not absolute or if + * the given path is an absolute path with + * a different root than the base path + */ + public static function makeAbsolute(string $path, string $basePath): string + { + if ('' === $basePath) { + throw new InvalidArgumentException(sprintf('The base path must be a non-empty string. Got: "%s".', $basePath)); + } + + if (!self::isAbsolute($basePath)) { + throw new InvalidArgumentException(sprintf('The base path "%s" is not an absolute path.', $basePath)); + } + + if (self::isAbsolute($path)) { + return self::canonicalize($path); + } + + if (false !== $schemeSeparatorPosition = strpos($basePath, '://')) { + $scheme = substr($basePath, 0, $schemeSeparatorPosition + 3); + $basePath = substr($basePath, $schemeSeparatorPosition + 3); + } else { + $scheme = ''; + } + + return $scheme.self::canonicalize(rtrim($basePath, '/\\').'/'.$path); + } + + /** + * Turns a path into a relative path. + * + * The relative path is created relative to the given base path: + * + * ```php + * echo Path::makeRelative("/symfony/style.css", "/symfony/puli"); + * // => ../style.css + * ``` + * + * If a relative path is passed and the base path is absolute, the relative + * path is returned unchanged: + * + * ```php + * Path::makeRelative("style.css", "/symfony/puli/css"); + * // => style.css + * ``` + * + * If both paths are relative, the relative path is created with the + * assumption that both paths are relative to the same directory: + * + * ```php + * Path::makeRelative("style.css", "symfony/puli/css"); + * // => ../../../style.css + * ``` + * + * If both paths are absolute, their root directory must be the same, + * otherwise an exception is thrown: + * + * ```php + * Path::makeRelative("C:/symfony/style.css", "/symfony/puli"); + * // InvalidArgumentException + * ``` + * + * If the passed path is absolute, but the base path is not, an exception + * is thrown as well: + * + * ```php + * Path::makeRelative("/symfony/style.css", "symfony/puli"); + * // InvalidArgumentException + * ``` + * + * If the base path is not an absolute path, an exception is thrown. + * + * The result is a canonical path. + * + * @throws InvalidArgumentException if the base path is not absolute or if + * the given path has a different root + * than the base path + */ + public static function makeRelative(string $path, string $basePath): string + { + $path = self::canonicalize($path); + $basePath = self::canonicalize($basePath); + + [$root, $relativePath] = self::split($path); + [$baseRoot, $relativeBasePath] = self::split($basePath); + + // If the base path is given as absolute path and the path is already + // relative, consider it to be relative to the given absolute path + // already + if ('' === $root && '' !== $baseRoot) { + // If base path is already in its root + if ('' === $relativeBasePath) { + $relativePath = ltrim($relativePath, './\\'); + } + + return $relativePath; + } + + // If the passed path is absolute, but the base path is not, we + // cannot generate a relative path + if ('' !== $root && '' === $baseRoot) { + throw new InvalidArgumentException(sprintf('The absolute path "%s" cannot be made relative to the relative path "%s". You should provide an absolute base path instead.', $path, $basePath)); + } + + // Fail if the roots of the two paths are different + if ($baseRoot && $root !== $baseRoot) { + throw new InvalidArgumentException(sprintf('The path "%s" cannot be made relative to "%s", because they have different roots ("%s" and "%s").', $path, $basePath, $root, $baseRoot)); + } + + if ('' === $relativeBasePath) { + return $relativePath; + } + + // Build a "../../" prefix with as many "../" parts as necessary + $parts = explode('/', $relativePath); + $baseParts = explode('/', $relativeBasePath); + $dotDotPrefix = ''; + + // Once we found a non-matching part in the prefix, we need to add + // "../" parts for all remaining parts + $match = true; + + foreach ($baseParts as $index => $basePart) { + if ($match && isset($parts[$index]) && $basePart === $parts[$index]) { + unset($parts[$index]); + + continue; + } + + $match = false; + $dotDotPrefix .= '../'; + } + + return rtrim($dotDotPrefix.implode('/', $parts), '/'); + } + + /** + * Returns whether the given path is on the local filesystem. + */ + public static function isLocal(string $path): bool + { + return '' !== $path && false === strpos($path, '://'); + } + + /** + * Returns the longest common base path in canonical form of a set of paths or + * `null` if the paths are on different Windows partitions. + * + * Dot segments ("." and "..") are removed/collapsed and all slashes turned + * into forward slashes. + * + * ```php + * $basePath = Path::getLongestCommonBasePath( + * '/symfony/css/style.css', + * '/symfony/css/..' + * ); + * // => /symfony + * ``` + * + * The root is returned if no common base path can be found: + * + * ```php + * $basePath = Path::getLongestCommonBasePath( + * '/symfony/css/style.css', + * '/puli/css/..' + * ); + * // => / + * ``` + * + * If the paths are located on different Windows partitions, `null` is + * returned. + * + * ```php + * $basePath = Path::getLongestCommonBasePath( + * 'C:/symfony/css/style.css', + * 'D:/symfony/css/..' + * ); + * // => null + * ``` + */ + public static function getLongestCommonBasePath(string ...$paths): ?string + { + [$bpRoot, $basePath] = self::split(self::canonicalize(reset($paths))); + + for (next($paths); null !== key($paths) && '' !== $basePath; next($paths)) { + [$root, $path] = self::split(self::canonicalize(current($paths))); + + // If we deal with different roots (e.g. C:/ vs. D:/), it's time + // to quit + if ($root !== $bpRoot) { + return null; + } + + // Make the base path shorter until it fits into path + while (true) { + if ('.' === $basePath) { + // No more base paths + $basePath = ''; + + // next path + continue 2; + } + + // Prevent false positives for common prefixes + // see isBasePath() + if (0 === strpos($path.'/', $basePath.'/')) { + // next path + continue 2; + } + + $basePath = \dirname($basePath); + } + } + + return $bpRoot.$basePath; + } + + /** + * Joins two or more path strings into a canonical path. + */ + public static function join(string ...$paths): string + { + $finalPath = null; + $wasScheme = false; + + foreach ($paths as $path) { + if ('' === $path) { + continue; + } + + if (null === $finalPath) { + // For first part we keep slashes, like '/top', 'C:\' or 'phar://' + $finalPath = $path; + $wasScheme = (false !== strpos($path, '://')); + continue; + } + + // Only add slash if previous part didn't end with '/' or '\' + if (!\in_array(substr($finalPath, -1), ['/', '\\'])) { + $finalPath .= '/'; + } + + // If first part included a scheme like 'phar://' we allow \current part to start with '/', otherwise trim + $finalPath .= $wasScheme ? $path : ltrim($path, '/'); + $wasScheme = false; + } + + if (null === $finalPath) { + return ''; + } + + return self::canonicalize($finalPath); + } + + /** + * Returns whether a path is a base path of another path. + * + * Dot segments ("." and "..") are removed/collapsed and all slashes turned + * into forward slashes. + * + * ```php + * Path::isBasePath('/symfony', '/symfony/css'); + * // => true + * + * Path::isBasePath('/symfony', '/symfony'); + * // => true + * + * Path::isBasePath('/symfony', '/symfony/..'); + * // => false + * + * Path::isBasePath('/symfony', '/puli'); + * // => false + * ``` + */ + public static function isBasePath(string $basePath, string $ofPath): bool + { + $basePath = self::canonicalize($basePath); + $ofPath = self::canonicalize($ofPath); + + // Append slashes to prevent false positives when two paths have + // a common prefix, for example /base/foo and /base/foobar. + // Don't append a slash for the root "/", because then that root + // won't be discovered as common prefix ("//" is not a prefix of + // "/foobar/"). + return 0 === strpos($ofPath.'/', rtrim($basePath, '/').'/'); + } + + /** + * @return string[] + */ + private static function findCanonicalParts(string $root, string $pathWithoutRoot): array + { + $parts = explode('/', $pathWithoutRoot); + + $canonicalParts = []; + + // Collapse "." and "..", if possible + foreach ($parts as $part) { + if ('.' === $part || '' === $part) { + continue; + } + + // Collapse ".." with the previous part, if one exists + // Don't collapse ".." if the previous part is also ".." + if ('..' === $part && \count($canonicalParts) > 0 && '..' !== $canonicalParts[\count($canonicalParts) - 1]) { + array_pop($canonicalParts); + + continue; + } + + // Only add ".." prefixes for relative paths + if ('..' !== $part || '' === $root) { + $canonicalParts[] = $part; + } + } + + return $canonicalParts; + } + + /** + * Splits a canonical path into its root directory and the remainder. + * + * If the path has no root directory, an empty root directory will be + * returned. + * + * If the root directory is a Windows style partition, the resulting root + * will always contain a trailing slash. + * + * list ($root, $path) = Path::split("C:/symfony") + * // => ["C:/", "symfony"] + * + * list ($root, $path) = Path::split("C:") + * // => ["C:/", ""] + * + * @return array{string, string} an array with the root directory and the remaining relative path + */ + private static function split(string $path): array + { + if ('' === $path) { + return ['', '']; + } + + // Remember scheme as part of the root, if any + if (false !== $schemeSeparatorPosition = strpos($path, '://')) { + $root = substr($path, 0, $schemeSeparatorPosition + 3); + $path = substr($path, $schemeSeparatorPosition + 3); + } else { + $root = ''; + } + + $length = \strlen($path); + + // Remove and remember root directory + if (0 === strpos($path, '/')) { + $root .= '/'; + $path = $length > 1 ? substr($path, 1) : ''; + } elseif ($length > 1 && ctype_alpha($path[0]) && ':' === $path[1]) { + if (2 === $length) { + // Windows special case: "C:" + $root .= $path.'/'; + $path = ''; + } elseif ('/' === $path[2]) { + // Windows normal case: "C:/".. + $root .= substr($path, 0, 3); + $path = $length > 3 ? substr($path, 3) : ''; + } + } + + return [$root, $path]; + } + + private static function toLower(string $string): string + { + if (false !== $encoding = mb_detect_encoding($string, null, true)) { + return mb_strtolower($string, $encoding); + } + + return strtolower($string); + } + + private function __construct() + { + } +} diff --git a/vendor/symfony/filesystem/README.md b/vendor/symfony/filesystem/README.md new file mode 100644 index 0000000..f2f6d45 --- /dev/null +++ b/vendor/symfony/filesystem/README.md @@ -0,0 +1,13 @@ +Filesystem Component +==================== + +The Filesystem component provides basic utilities for the filesystem. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/filesystem.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/filesystem/composer.json b/vendor/symfony/filesystem/composer.json new file mode 100644 index 0000000..607dde3 --- /dev/null +++ b/vendor/symfony/filesystem/composer.json @@ -0,0 +1,30 @@ +{ + "name": "symfony/filesystem", + "type": "library", + "description": "Provides basic utilities for the filesystem", + "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": ">=8.0.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Filesystem\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/vendor/symfony/polyfill-ctype/Ctype.php b/vendor/symfony/polyfill-ctype/Ctype.php new file mode 100644 index 0000000..ba75a2c --- /dev/null +++ b/vendor/symfony/polyfill-ctype/Ctype.php @@ -0,0 +1,232 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Ctype; + +/** + * Ctype implementation through regex. + * + * @internal + * + * @author Gert de Pagter + */ +final class Ctype +{ + /** + * Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise. + * + * @see https://php.net/ctype-alnum + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_alnum($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text); + } + + /** + * Returns TRUE if every character in text is a letter, FALSE otherwise. + * + * @see https://php.net/ctype-alpha + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_alpha($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text); + } + + /** + * Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise. + * + * @see https://php.net/ctype-cntrl + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_cntrl($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text); + } + + /** + * Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise. + * + * @see https://php.net/ctype-digit + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_digit($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text); + } + + /** + * Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise. + * + * @see https://php.net/ctype-graph + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_graph($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text); + } + + /** + * Returns TRUE if every character in text is a lowercase letter. + * + * @see https://php.net/ctype-lower + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_lower($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text); + } + + /** + * Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all. + * + * @see https://php.net/ctype-print + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_print($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text); + } + + /** + * Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise. + * + * @see https://php.net/ctype-punct + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_punct($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text); + } + + /** + * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters. + * + * @see https://php.net/ctype-space + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_space($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text); + } + + /** + * Returns TRUE if every character in text is an uppercase letter. + * + * @see https://php.net/ctype-upper + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_upper($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text); + } + + /** + * Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise. + * + * @see https://php.net/ctype-xdigit + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_xdigit($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text); + } + + /** + * Converts integers to their char versions according to normal ctype behaviour, if needed. + * + * If an integer between -128 and 255 inclusive is provided, + * it is interpreted as the ASCII value of a single character + * (negative values have 256 added in order to allow characters in the Extended ASCII range). + * Any other integer is interpreted as a string containing the decimal digits of the integer. + * + * @param mixed $int + * @param string $function + * + * @return mixed + */ + private static function convert_int_to_char_for_ctype($int, $function) + { + if (!\is_int($int)) { + return $int; + } + + if ($int < -128 || $int > 255) { + return (string) $int; + } + + if (\PHP_VERSION_ID >= 80100) { + @trigger_error($function.'(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED); + } + + if ($int < 0) { + $int += 256; + } + + return \chr($int); + } +} diff --git a/vendor/symfony/polyfill-ctype/LICENSE b/vendor/symfony/polyfill-ctype/LICENSE new file mode 100644 index 0000000..7536cae --- /dev/null +++ b/vendor/symfony/polyfill-ctype/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-present 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-ctype/README.md b/vendor/symfony/polyfill-ctype/README.md new file mode 100644 index 0000000..b144d03 --- /dev/null +++ b/vendor/symfony/polyfill-ctype/README.md @@ -0,0 +1,12 @@ +Symfony Polyfill / Ctype +======================== + +This component provides `ctype_*` functions to users who run php versions without the ctype extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-ctype/bootstrap.php b/vendor/symfony/polyfill-ctype/bootstrap.php new file mode 100644 index 0000000..d54524b --- /dev/null +++ b/vendor/symfony/polyfill-ctype/bootstrap.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Ctype as p; + +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; +} + +if (!function_exists('ctype_alnum')) { + function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); } +} +if (!function_exists('ctype_alpha')) { + function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); } +} +if (!function_exists('ctype_cntrl')) { + function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); } +} +if (!function_exists('ctype_digit')) { + function ctype_digit($text) { return p\Ctype::ctype_digit($text); } +} +if (!function_exists('ctype_graph')) { + function ctype_graph($text) { return p\Ctype::ctype_graph($text); } +} +if (!function_exists('ctype_lower')) { + function ctype_lower($text) { return p\Ctype::ctype_lower($text); } +} +if (!function_exists('ctype_print')) { + function ctype_print($text) { return p\Ctype::ctype_print($text); } +} +if (!function_exists('ctype_punct')) { + function ctype_punct($text) { return p\Ctype::ctype_punct($text); } +} +if (!function_exists('ctype_space')) { + function ctype_space($text) { return p\Ctype::ctype_space($text); } +} +if (!function_exists('ctype_upper')) { + function ctype_upper($text) { return p\Ctype::ctype_upper($text); } +} +if (!function_exists('ctype_xdigit')) { + function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); } +} diff --git a/vendor/symfony/polyfill-ctype/bootstrap80.php b/vendor/symfony/polyfill-ctype/bootstrap80.php new file mode 100644 index 0000000..ab2f861 --- /dev/null +++ b/vendor/symfony/polyfill-ctype/bootstrap80.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Ctype as p; + +if (!function_exists('ctype_alnum')) { + function ctype_alnum(mixed $text): bool { return p\Ctype::ctype_alnum($text); } +} +if (!function_exists('ctype_alpha')) { + function ctype_alpha(mixed $text): bool { return p\Ctype::ctype_alpha($text); } +} +if (!function_exists('ctype_cntrl')) { + function ctype_cntrl(mixed $text): bool { return p\Ctype::ctype_cntrl($text); } +} +if (!function_exists('ctype_digit')) { + function ctype_digit(mixed $text): bool { return p\Ctype::ctype_digit($text); } +} +if (!function_exists('ctype_graph')) { + function ctype_graph(mixed $text): bool { return p\Ctype::ctype_graph($text); } +} +if (!function_exists('ctype_lower')) { + function ctype_lower(mixed $text): bool { return p\Ctype::ctype_lower($text); } +} +if (!function_exists('ctype_print')) { + function ctype_print(mixed $text): bool { return p\Ctype::ctype_print($text); } +} +if (!function_exists('ctype_punct')) { + function ctype_punct(mixed $text): bool { return p\Ctype::ctype_punct($text); } +} +if (!function_exists('ctype_space')) { + function ctype_space(mixed $text): bool { return p\Ctype::ctype_space($text); } +} +if (!function_exists('ctype_upper')) { + function ctype_upper(mixed $text): bool { return p\Ctype::ctype_upper($text); } +} +if (!function_exists('ctype_xdigit')) { + function ctype_xdigit(mixed $text): bool { return p\Ctype::ctype_xdigit($text); } +} diff --git a/vendor/symfony/polyfill-ctype/composer.json b/vendor/symfony/polyfill-ctype/composer.json new file mode 100644 index 0000000..b222fda --- /dev/null +++ b/vendor/symfony/polyfill-ctype/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/polyfill-ctype", + "type": "library", + "description": "Symfony polyfill for ctype functions", + "keywords": ["polyfill", "compatibility", "portable", "ctype"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Ctype\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/vendor/symfony/polyfill-intl-grapheme/Grapheme.php b/vendor/symfony/polyfill-intl-grapheme/Grapheme.php new file mode 100644 index 0000000..5373f16 --- /dev/null +++ b/vendor/symfony/polyfill-intl-grapheme/Grapheme.php @@ -0,0 +1,247 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Intl\Grapheme; + +\define('SYMFONY_GRAPHEME_CLUSTER_RX', ((float) \PCRE_VERSION < 10 ? (float) \PCRE_VERSION >= 8.32 : (float) \PCRE_VERSION >= 10.39) ? '\X' : Grapheme::GRAPHEME_CLUSTER_RX); + +/** + * Partial intl implementation in pure PHP. + * + * Implemented: + * - grapheme_extract - Extract a sequence of grapheme clusters from a text buffer, which must be encoded in UTF-8 + * - grapheme_stripos - Find position (in grapheme units) of first occurrence of a case-insensitive string + * - grapheme_stristr - Returns part of haystack string from the first occurrence of case-insensitive needle to the end of haystack + * - grapheme_strlen - Get string length in grapheme units + * - grapheme_strpos - Find position (in grapheme units) of first occurrence of a string + * - grapheme_strripos - Find position (in grapheme units) of last occurrence of a case-insensitive string + * - grapheme_strrpos - Find position (in grapheme units) of last occurrence of a string + * - grapheme_strstr - Returns part of haystack string from the first occurrence of needle to the end of haystack + * - grapheme_substr - Return part of a string + * + * @author Nicolas Grekas + * + * @internal + */ +final class Grapheme +{ + // (CRLF|([ZWNJ-ZWJ]|T+|L*(LV?V+|LV|LVT)T*|L+|[^Control])[Extend]*|[Control]) + // This regular expression is a work around for http://bugs.exim.org/1279 + public const GRAPHEME_CLUSTER_RX = '(?:\r\n|(?:[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-ᅟ]*(?:[가개갸걔거게겨계고과괘괴교구궈궤귀규그긔기까깨꺄꺠꺼께껴꼐꼬꽈꽤꾀꾜꾸꿔꿰뀌뀨끄끠끼나내냐냬너네녀녜노놔놰뇌뇨누눠눼뉘뉴느늬니다대댜댸더데뎌뎨도돠돼되됴두둬뒈뒤듀드듸디따때땨떄떠떼뗘뗴또똬뙈뙤뚀뚜뚸뛔뛰뜌뜨띄띠라래랴럐러레려례로롸뢔뢰료루뤄뤠뤼류르릐리마매먀먜머메며몌모뫄뫠뫼묘무뭐뭬뮈뮤므믜미바배뱌뱨버베벼볘보봐봬뵈뵤부붜붸뷔뷰브븨비빠빼뺘뺴뻐뻬뼈뼤뽀뽜뽸뾔뾰뿌뿨쀄쀠쀼쁘쁴삐사새샤섀서세셔셰소솨쇄쇠쇼수숴쉐쉬슈스싀시싸쌔쌰썌써쎄쎠쎼쏘쏴쐐쐬쑈쑤쒀쒜쒸쓔쓰씌씨아애야얘어에여예오와왜외요우워웨위유으의이자재쟈쟤저제져졔조좌좨죄죠주줘줴쥐쥬즈즤지짜째쨔쨰쩌쩨쪄쪠쪼쫘쫴쬐쬬쭈쭤쮀쮜쮸쯔쯰찌차채챠챼처체쳐쳬초촤쵀최쵸추춰췌취츄츠츼치카캐캬컈커케켜켸코콰쾌쾨쿄쿠쿼퀘퀴큐크킈키타태탸턔터테텨톄토톼퇘퇴툐투퉈퉤튀튜트틔티파패퍄퍠퍼페펴폐포퐈퐤푀표푸풔풰퓌퓨프픠피하해햐햬허헤혀혜호화홰회효후훠훼휘휴흐희히]?[ᅠ-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-ᅟ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{200D}\x{1D165}\x{1D16E}-\x{1D172}]*|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}])'; + + private const CASE_FOLD = [ + ['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"], + ['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'], + ]; + + public static function grapheme_extract($s, $size, $type = \GRAPHEME_EXTR_COUNT, $start = 0, &$next = 0) + { + if (0 > $start) { + $start = \strlen($s) + $start; + } + + if (!\is_scalar($s)) { + $hasError = false; + set_error_handler(function () use (&$hasError) { $hasError = true; }); + $next = substr($s, $start); + restore_error_handler(); + if ($hasError) { + substr($s, $start); + $s = ''; + } else { + $s = $next; + } + } else { + $s = substr($s, $start); + } + $size = (int) $size; + $type = (int) $type; + $start = (int) $start; + + if (\GRAPHEME_EXTR_COUNT !== $type && \GRAPHEME_EXTR_MAXBYTES !== $type && \GRAPHEME_EXTR_MAXCHARS !== $type) { + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError('grapheme_extract(): Argument #3 ($type) must be one of GRAPHEME_EXTR_COUNT, GRAPHEME_EXTR_MAXBYTES, or GRAPHEME_EXTR_MAXCHARS'); + } + + if (!isset($s[0]) || 0 > $size || 0 > $start) { + return false; + } + if (0 === $size) { + return ''; + } + + $next = $start; + + $s = preg_split('/('.SYMFONY_GRAPHEME_CLUSTER_RX.')/u', "\r\n".$s, $size + 1, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE); + + if (!isset($s[1])) { + return false; + } + + $i = 1; + $ret = ''; + + do { + if (\GRAPHEME_EXTR_COUNT === $type) { + --$size; + } elseif (\GRAPHEME_EXTR_MAXBYTES === $type) { + $size -= \strlen($s[$i]); + } else { + $size -= iconv_strlen($s[$i], 'UTF-8//IGNORE'); + } + + if ($size >= 0) { + $ret .= $s[$i]; + } + } while (isset($s[++$i]) && $size > 0); + + $next += \strlen($ret); + + return $ret; + } + + public static function grapheme_strlen($s) + { + preg_replace('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', '', $s, -1, $len); + + return 0 === $len && '' !== $s ? null : $len; + } + + public static function grapheme_substr($s, $start, $len = null) + { + if (null === $len) { + $len = 2147483647; + } + + preg_match_all('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', $s, $s); + + $slen = \count($s[0]); + $start = (int) $start; + + if (0 > $start) { + $start += $slen; + } + if (0 > $start) { + if (\PHP_VERSION_ID < 80000) { + return false; + } + + $start = 0; + } + if ($start >= $slen) { + return \PHP_VERSION_ID >= 80000 ? '' : false; + } + + $rem = $slen - $start; + + if (0 > $len) { + $len += $rem; + } + if (0 === $len) { + return ''; + } + if (0 > $len) { + return \PHP_VERSION_ID >= 80000 ? '' : false; + } + if ($len > $rem) { + $len = $rem; + } + + return implode('', \array_slice($s[0], $start, $len)); + } + + public static function grapheme_strpos($s, $needle, $offset = 0) + { + return self::grapheme_position($s, $needle, $offset, 0); + } + + public static function grapheme_stripos($s, $needle, $offset = 0) + { + return self::grapheme_position($s, $needle, $offset, 1); + } + + public static function grapheme_strrpos($s, $needle, $offset = 0) + { + return self::grapheme_position($s, $needle, $offset, 2); + } + + public static function grapheme_strripos($s, $needle, $offset = 0) + { + return self::grapheme_position($s, $needle, $offset, 3); + } + + public static function grapheme_stristr($s, $needle, $beforeNeedle = false) + { + return mb_stristr($s, $needle, $beforeNeedle, 'UTF-8'); + } + + public static function grapheme_strstr($s, $needle, $beforeNeedle = false) + { + return mb_strstr($s, $needle, $beforeNeedle, 'UTF-8'); + } + + private static function grapheme_position($s, $needle, $offset, $mode) + { + $needle = (string) $needle; + if (80000 > \PHP_VERSION_ID && !preg_match('/./us', $needle)) { + return false; + } + $s = (string) $s; + if (!preg_match('/./us', $s)) { + return false; + } + if ($offset > 0) { + $s = self::grapheme_substr($s, $offset); + } elseif ($offset < 0) { + if (2 > $mode) { + $offset += self::grapheme_strlen($s); + $s = self::grapheme_substr($s, $offset); + if (0 > $offset) { + $offset = 0; + } + } elseif (0 > $offset += self::grapheme_strlen($needle)) { + $s = self::grapheme_substr($s, 0, $offset); + $offset = 0; + } else { + $offset = 0; + } + } + + // As UTF-8 is self-synchronizing, and we have ensured the strings are valid UTF-8, + // we can use normal binary string functions here. For case-insensitive searches, + // case fold the strings first. + $caseInsensitive = $mode & 1; + $reverse = $mode & 2; + if ($caseInsensitive) { + // Use the same case folding mode as mbstring does for mb_stripos(). + // Stick to SIMPLE case folding to avoid changing the length of the string, which + // might result in offsets being shifted. + $mode = \defined('MB_CASE_FOLD_SIMPLE') ? \MB_CASE_FOLD_SIMPLE : \MB_CASE_LOWER; + $s = mb_convert_case($s, $mode, 'UTF-8'); + $needle = mb_convert_case($needle, $mode, 'UTF-8'); + + if (!\defined('MB_CASE_FOLD_SIMPLE')) { + $s = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $s); + $needle = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $needle); + } + } + if ($reverse) { + $needlePos = strrpos($s, $needle); + } else { + $needlePos = strpos($s, $needle); + } + + return false !== $needlePos ? self::grapheme_strlen(substr($s, 0, $needlePos)) + $offset : false; + } +} diff --git a/vendor/symfony/polyfill-intl-grapheme/LICENSE b/vendor/symfony/polyfill-intl-grapheme/LICENSE new file mode 100644 index 0000000..6e3afce --- /dev/null +++ b/vendor/symfony/polyfill-intl-grapheme/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-present 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-grapheme/README.md b/vendor/symfony/polyfill-intl-grapheme/README.md new file mode 100644 index 0000000..f55d92c --- /dev/null +++ b/vendor/symfony/polyfill-intl-grapheme/README.md @@ -0,0 +1,31 @@ +Symfony Polyfill / Intl: Grapheme +================================= + +This component provides a partial, native PHP implementation of the +[Grapheme functions](https://php.net/intl.grapheme) from the +[Intl](https://php.net/intl) extension. + +- [`grapheme_extract`](https://php.net/grapheme_extract): Extract a sequence of grapheme + clusters from a text buffer, which must be encoded in UTF-8 +- [`grapheme_stripos`](https://php.net/grapheme_stripos): Find position (in grapheme units) + of first occurrence of a case-insensitive string +- [`grapheme_stristr`](https://php.net/grapheme_stristr): Returns part of haystack string + from the first occurrence of case-insensitive needle to the end of haystack +- [`grapheme_strlen`](https://php.net/grapheme_strlen): Get string length in grapheme units +- [`grapheme_strpos`](https://php.net/grapheme_strpos): Find position (in grapheme units) + of first occurrence of a string +- [`grapheme_strripos`](https://php.net/grapheme_strripos): Find position (in grapheme units) + of last occurrence of a case-insensitive string +- [`grapheme_strrpos`](https://php.net/grapheme_strrpos): Find position (in grapheme units) + of last occurrence of a string +- [`grapheme_strstr`](https://php.net/grapheme_strstr): Returns part of haystack string from + the first occurrence of needle to the end of haystack +- [`grapheme_substr`](https://php.net/grapheme_substr): Return part of a string + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-intl-grapheme/bootstrap.php b/vendor/symfony/polyfill-intl-grapheme/bootstrap.php new file mode 100644 index 0000000..a9ea03c --- /dev/null +++ b/vendor/symfony/polyfill-intl-grapheme/bootstrap.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Intl\Grapheme as p; + +if (extension_loaded('intl')) { + return; +} + +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; +} + +if (!defined('GRAPHEME_EXTR_COUNT')) { + define('GRAPHEME_EXTR_COUNT', 0); +} +if (!defined('GRAPHEME_EXTR_MAXBYTES')) { + define('GRAPHEME_EXTR_MAXBYTES', 1); +} +if (!defined('GRAPHEME_EXTR_MAXCHARS')) { + define('GRAPHEME_EXTR_MAXCHARS', 2); +} + +if (!function_exists('grapheme_extract')) { + function grapheme_extract($haystack, $size, $type = 0, $start = 0, &$next = 0) { return p\Grapheme::grapheme_extract($haystack, $size, $type, $start, $next); } +} +if (!function_exists('grapheme_stripos')) { + function grapheme_stripos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_stripos($haystack, $needle, $offset); } +} +if (!function_exists('grapheme_stristr')) { + function grapheme_stristr($haystack, $needle, $beforeNeedle = false) { return p\Grapheme::grapheme_stristr($haystack, $needle, $beforeNeedle); } +} +if (!function_exists('grapheme_strlen')) { + function grapheme_strlen($input) { return p\Grapheme::grapheme_strlen($input); } +} +if (!function_exists('grapheme_strpos')) { + function grapheme_strpos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strpos($haystack, $needle, $offset); } +} +if (!function_exists('grapheme_strripos')) { + function grapheme_strripos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strripos($haystack, $needle, $offset); } +} +if (!function_exists('grapheme_strrpos')) { + function grapheme_strrpos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strrpos($haystack, $needle, $offset); } +} +if (!function_exists('grapheme_strstr')) { + function grapheme_strstr($haystack, $needle, $beforeNeedle = false) { return p\Grapheme::grapheme_strstr($haystack, $needle, $beforeNeedle); } +} +if (!function_exists('grapheme_substr')) { + function grapheme_substr($string, $offset, $length = null) { return p\Grapheme::grapheme_substr($string, $offset, $length); } +} diff --git a/vendor/symfony/polyfill-intl-grapheme/bootstrap80.php b/vendor/symfony/polyfill-intl-grapheme/bootstrap80.php new file mode 100644 index 0000000..b8c0786 --- /dev/null +++ b/vendor/symfony/polyfill-intl-grapheme/bootstrap80.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Intl\Grapheme as p; + +if (!defined('GRAPHEME_EXTR_COUNT')) { + define('GRAPHEME_EXTR_COUNT', 0); +} +if (!defined('GRAPHEME_EXTR_MAXBYTES')) { + define('GRAPHEME_EXTR_MAXBYTES', 1); +} +if (!defined('GRAPHEME_EXTR_MAXCHARS')) { + define('GRAPHEME_EXTR_MAXCHARS', 2); +} + +if (!function_exists('grapheme_extract')) { + function grapheme_extract(?string $haystack, ?int $size, ?int $type = GRAPHEME_EXTR_COUNT, ?int $offset = 0, &$next = null): string|false { return p\Grapheme::grapheme_extract((string) $haystack, (int) $size, (int) $type, (int) $offset, $next); } +} +if (!function_exists('grapheme_stripos')) { + function grapheme_stripos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_stripos((string) $haystack, (string) $needle, (int) $offset); } +} +if (!function_exists('grapheme_stristr')) { + function grapheme_stristr(?string $haystack, ?string $needle, ?bool $beforeNeedle = false): string|false { return p\Grapheme::grapheme_stristr((string) $haystack, (string) $needle, (bool) $beforeNeedle); } +} +if (!function_exists('grapheme_strlen')) { + function grapheme_strlen(?string $string): int|false|null { return p\Grapheme::grapheme_strlen((string) $string); } +} +if (!function_exists('grapheme_strpos')) { + function grapheme_strpos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strpos((string) $haystack, (string) $needle, (int) $offset); } +} +if (!function_exists('grapheme_strripos')) { + function grapheme_strripos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strripos((string) $haystack, (string) $needle, (int) $offset); } +} +if (!function_exists('grapheme_strrpos')) { + function grapheme_strrpos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strrpos((string) $haystack, (string) $needle, (int) $offset); } +} +if (!function_exists('grapheme_strstr')) { + function grapheme_strstr(?string $haystack, ?string $needle, ?bool $beforeNeedle = false): string|false { return p\Grapheme::grapheme_strstr((string) $haystack, (string) $needle, (bool) $beforeNeedle); } +} +if (!function_exists('grapheme_substr')) { + function grapheme_substr(?string $string, ?int $offset, ?int $length = null): string|false { return p\Grapheme::grapheme_substr((string) $string, (int) $offset, $length); } +} diff --git a/vendor/symfony/polyfill-intl-grapheme/composer.json b/vendor/symfony/polyfill-intl-grapheme/composer.json new file mode 100644 index 0000000..a20d3fa --- /dev/null +++ b/vendor/symfony/polyfill-intl-grapheme/composer.json @@ -0,0 +1,35 @@ +{ + "name": "symfony/polyfill-intl-grapheme", + "type": "library", + "description": "Symfony polyfill for intl's grapheme_* functions", + "keywords": ["polyfill", "shim", "compatibility", "portable", "intl", "grapheme"], + "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" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Intl\\Grapheme\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-intl": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "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 100644 index 0000000..6e3afce --- /dev/null +++ b/vendor/symfony/polyfill-intl-normalizer/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-present 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 100644 index 0000000..81704ab --- /dev/null +++ b/vendor/symfony/polyfill-intl-normalizer/Normalizer.php @@ -0,0 +1,310 @@ + + * + * 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 + * + * @internal + */ +class Normalizer +{ + public const FORM_D = \Normalizer::FORM_D; + public const FORM_KD = \Normalizer::FORM_KD; + public const FORM_C = \Normalizer::FORM_C; + public const FORM_KC = \Normalizer::FORM_KC; + public const NFD = \Normalizer::NFD; + public const NFKD = \Normalizer::NFKD; + public const NFC = \Normalizer::NFC; + public const NFKC = \Normalizer::NFKC; + + private static $C; + private static $D; + private static $KD; + private static $cC; + private static $ulenMask = ["\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(string $s, int $form = self::FORM_C) + { + if (!\in_array($form, [self::NFD, self::NFKD, self::NFC, self::NFKC])) { + return false; + } + 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(string $s, int $form = self::FORM_C) + { + 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; + } + + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError('normalizer_normalize(): Argument #2 ($form) must be a a valid normalization form'); + } + + 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 = $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 = []; + $i = 0; + $len = \strlen($s); + + while ($i < $len) { + if ($s[$i] < "\x80") { + // ASCII chars + + if ($c) { + ksort($c); + $result .= implode('', $c); + $c = []; + } + + $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 = $compatMap[$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 = []; + } + + $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 100644 index 0000000..b9b762e --- /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/main/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 100644 index 0000000..0fdfc89 --- /dev/null +++ b/vendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php @@ -0,0 +1,17 @@ + 'À', + 'Á' => 'Á', + 'Â' => 'Â', + 'Ã' => 'Ã', + 'Ä' => 'Ä', + 'Å' => 'Å', + 'Ç' => 'Ç', + 'È' => 'È', + 'É' => 'É', + 'Ê' => 'Ê', + 'Ë' => 'Ë', + 'Ì' => 'Ì', + 'Í' => 'Í', + 'Î' => 'Î', + 'Ï' => 'Ï', + 'Ñ' => 'Ñ', + 'Ò' => 'Ò', + 'Ó' => 'Ó', + 'Ô' => 'Ô', + 'Õ' => 'Õ', + 'Ö' => 'Ö', + 'Ù' => 'Ù', + 'Ú' => 'Ú', + 'Û' => 'Û', + 'Ü' => 'Ü', + 'Ý' => 'Ý', + 'à' => 'à', + 'á' => 'á', + 'â' => 'â', + 'ã' => 'ã', + 'ä' => 'ä', + 'å' => 'å', + 'ç' => 'ç', + 'è' => 'è', + 'é' => 'é', + 'ê' => 'ê', + 'ë' => 'ë', + 'ì' => 'ì', + 'í' => 'í', + 'î' => 'î', + 'ï' => 'ï', + 'ñ' => 'ñ', + 'ò' => 'ò', + 'ó' => 'ó', + 'ô' => 'ô', + 'õ' => 'õ', + 'ö' => 'ö', + 'ù' => 'ù', + 'ú' => 'ú', + 'û' => 'û', + 'ü' => 'ü', + 'ý' => 'ý', + 'ÿ' => 'ÿ', + 'Ā' => 'Ā', + 'ā' => 'ā', + 'Ă' => 'Ă', + 'ă' => 'ă', + 'Ą' => 'Ą', + 'ą' => 'ą', + 'Ć' => 'Ć', + 'ć' => 'ć', + 'Ĉ' => 'Ĉ', + 'ĉ' => 'ĉ', + 'Ċ' => 'Ċ', + 'ċ' => 'ċ', + 'Č' => 'Č', + 'č' => 'č', + 'Ď' => 'Ď', + 'ď' => 'ď', + 'Ē' => 'Ē', + 'ē' => 'ē', + 'Ĕ' => 'Ĕ', + 'ĕ' => 'ĕ', + 'Ė' => 'Ė', + 'ė' => 'ė', + 'Ę' => 'Ę', + 'ę' => 'ę', + 'Ě' => 'Ě', + 'ě' => 'ě', + 'Ĝ' => 'Ĝ', + 'ĝ' => 'ĝ', + 'Ğ' => 'Ğ', + 'ğ' => 'ğ', + 'Ġ' => 'Ġ', + 'ġ' => 'ġ', + 'Ģ' => 'Ģ', + 'ģ' => 'ģ', + 'Ĥ' => 'Ĥ', + 'ĥ' => 'ĥ', + 'Ĩ' => 'Ĩ', + 'ĩ' => 'ĩ', + 'Ī' => 'Ī', + 'ī' => 'ī', + 'Ĭ' => 'Ĭ', + 'ĭ' => 'ĭ', + 'Į' => 'Į', + 'į' => 'į', + 'İ' => 'İ', + 'Ĵ' => 'Ĵ', + 'ĵ' => 'ĵ', + 'Ķ' => 'Ķ', + 'ķ' => 'ķ', + 'Ĺ' => 'Ĺ', + 'ĺ' => 'ĺ', + 'Ļ' => 'Ļ', + 'ļ' => 'ļ', + 'Ľ' => 'Ľ', + 'ľ' => 'ľ', + 'Ń' => 'Ń', + 'ń' => 'ń', + 'Ņ' => 'Ņ', + 'ņ' => 'ņ', + 'Ň' => 'Ň', + 'ň' => 'ň', + 'Ō' => 'Ō', + 'ō' => 'ō', + 'Ŏ' => 'Ŏ', + 'ŏ' => 'ŏ', + 'Ő' => 'Ő', + 'ő' => 'ő', + 'Ŕ' => 'Ŕ', + 'ŕ' => 'ŕ', + 'Ŗ' => 'Ŗ', + 'ŗ' => 'ŗ', + 'Ř' => 'Ř', + 'ř' => 'ř', + 'Ś' => 'Ś', + 'ś' => 'ś', + 'Ŝ' => 'Ŝ', + 'ŝ' => 'ŝ', + 'Ş' => 'Ş', + 'ş' => 'ş', + 'Š' => 'Š', + 'š' => 'š', + 'Ţ' => 'Ţ', + 'ţ' => 'ţ', + 'Ť' => 'Ť', + 'ť' => 'ť', + 'Ũ' => 'Ũ', + 'ũ' => 'ũ', + 'Ū' => 'Ū', + 'ū' => 'ū', + 'Ŭ' => 'Ŭ', + 'ŭ' => 'ŭ', + 'Ů' => 'Ů', + 'ů' => 'ů', + 'Ű' => 'Ű', + 'ű' => 'ű', + 'Ų' => 'Ų', + 'ų' => 'ų', + 'Ŵ' => 'Ŵ', + 'ŵ' => 'ŵ', + 'Ŷ' => 'Ŷ', + 'ŷ' => 'ŷ', + 'Ÿ' => 'Ÿ', + 'Ź' => 'Ź', + 'ź' => 'ź', + 'Ż' => 'Ż', + 'ż' => 'ż', + 'Ž' => 'Ž', + 'ž' => 'ž', + 'Ơ' => 'Ơ', + 'ơ' => 'ơ', + 'Ư' => 'Ư', + 'ư' => 'ư', + 'Ǎ' => 'Ǎ', + 'ǎ' => 'ǎ', + 'Ǐ' => 'Ǐ', + 'ǐ' => 'ǐ', + 'Ǒ' => 'Ǒ', + 'ǒ' => 'ǒ', + 'Ǔ' => 'Ǔ', + 'ǔ' => 'ǔ', + 'Ǖ' => 'Ǖ', + 'ǖ' => 'ǖ', + 'Ǘ' => 'Ǘ', + 'ǘ' => 'ǘ', + 'Ǚ' => 'Ǚ', + 'ǚ' => 'ǚ', + 'Ǜ' => 'Ǜ', + 'ǜ' => 'ǜ', + 'Ǟ' => 'Ǟ', + 'ǟ' => 'ǟ', + 'Ǡ' => 'Ǡ', + 'ǡ' => 'ǡ', + 'Ǣ' => 'Ǣ', + 'ǣ' => 'ǣ', + 'Ǧ' => 'Ǧ', + 'ǧ' => 'ǧ', + 'Ǩ' => 'Ǩ', + 'ǩ' => 'ǩ', + 'Ǫ' => 'Ǫ', + 'ǫ' => 'ǫ', + 'Ǭ' => 'Ǭ', + 'ǭ' => 'ǭ', + 'Ǯ' => 'Ǯ', + 'ǯ' => 'ǯ', + 'ǰ' => 'ǰ', + 'Ǵ' => 'Ǵ', + 'ǵ' => 'ǵ', + 'Ǹ' => 'Ǹ', + 'ǹ' => 'ǹ', + 'Ǻ' => 'Ǻ', + 'ǻ' => 'ǻ', + 'Ǽ' => 'Ǽ', + 'ǽ' => 'ǽ', + 'Ǿ' => 'Ǿ', + 'ǿ' => 'ǿ', + 'Ȁ' => 'Ȁ', + 'ȁ' => 'ȁ', + 'Ȃ' => 'Ȃ', + 'ȃ' => 'ȃ', + 'Ȅ' => 'Ȅ', + 'ȅ' => 'ȅ', + 'Ȇ' => 'Ȇ', + 'ȇ' => 'ȇ', + 'Ȉ' => 'Ȉ', + 'ȉ' => 'ȉ', + 'Ȋ' => 'Ȋ', + 'ȋ' => 'ȋ', + 'Ȍ' => 'Ȍ', + 'ȍ' => 'ȍ', + 'Ȏ' => 'Ȏ', + 'ȏ' => 'ȏ', + 'Ȑ' => 'Ȑ', + 'ȑ' => 'ȑ', + 'Ȓ' => 'Ȓ', + 'ȓ' => 'ȓ', + 'Ȕ' => 'Ȕ', + 'ȕ' => 'ȕ', + 'Ȗ' => 'Ȗ', + 'ȗ' => 'ȗ', + 'Ș' => 'Ș', + 'ș' => 'ș', + 'Ț' => 'Ț', + 'ț' => 'ț', + 'Ȟ' => 'Ȟ', + 'ȟ' => 'ȟ', + 'Ȧ' => 'Ȧ', + 'ȧ' => 'ȧ', + 'Ȩ' => 'Ȩ', + 'ȩ' => 'ȩ', + 'Ȫ' => 'Ȫ', + 'ȫ' => 'ȫ', + 'Ȭ' => 'Ȭ', + 'ȭ' => 'ȭ', + 'Ȯ' => 'Ȯ', + 'ȯ' => 'ȯ', + 'Ȱ' => 'Ȱ', + 'ȱ' => 'ȱ', + 'Ȳ' => 'Ȳ', + 'ȳ' => 'ȳ', + '΅' => '΅', + 'Ά' => 'Ά', + 'Έ' => 'Έ', + 'Ή' => 'Ή', + 'Ί' => 'Ί', + 'Ό' => 'Ό', + 'Ύ' => 'Ύ', + 'Ώ' => 'Ώ', + 'ΐ' => 'ΐ', + 'Ϊ' => 'Ϊ', + 'Ϋ' => 'Ϋ', + 'ά' => 'ά', + 'έ' => 'έ', + 'ή' => 'ή', + 'ί' => 'ί', + 'ΰ' => 'ΰ', + 'ϊ' => 'ϊ', + 'ϋ' => 'ϋ', + 'ό' => 'ό', + 'ύ' => 'ύ', + 'ώ' => 'ώ', + 'ϓ' => 'ϓ', + 'ϔ' => 'ϔ', + 'Ѐ' => 'Ѐ', + 'Ё' => 'Ё', + 'Ѓ' => 'Ѓ', + 'Ї' => 'Ї', + 'Ќ' => 'Ќ', + 'Ѝ' => 'Ѝ', + 'Ў' => 'Ў', + 'Й' => 'Й', + 'й' => 'й', + 'ѐ' => 'ѐ', + 'ё' => 'ё', + 'ѓ' => 'ѓ', + 'ї' => 'ї', + 'ќ' => 'ќ', + 'ѝ' => 'ѝ', + 'ў' => 'ў', + 'Ѷ' => 'Ѷ', + 'ѷ' => 'ѷ', + 'Ӂ' => 'Ӂ', + 'ӂ' => 'ӂ', + 'Ӑ' => 'Ӑ', + 'ӑ' => 'ӑ', + 'Ӓ' => 'Ӓ', + 'ӓ' => 'ӓ', + 'Ӗ' => 'Ӗ', + 'ӗ' => 'ӗ', + 'Ӛ' => 'Ӛ', + 'ӛ' => 'ӛ', + 'Ӝ' => 'Ӝ', + 'ӝ' => 'ӝ', + 'Ӟ' => 'Ӟ', + 'ӟ' => 'ӟ', + 'Ӣ' => 'Ӣ', + 'ӣ' => 'ӣ', + 'Ӥ' => 'Ӥ', + 'ӥ' => 'ӥ', + 'Ӧ' => 'Ӧ', + 'ӧ' => 'ӧ', + 'Ӫ' => 'Ӫ', + 'ӫ' => 'ӫ', + 'Ӭ' => 'Ӭ', + 'ӭ' => 'ӭ', + 'Ӯ' => 'Ӯ', + 'ӯ' => 'ӯ', + 'Ӱ' => 'Ӱ', + 'ӱ' => 'ӱ', + 'Ӳ' => 'Ӳ', + 'ӳ' => 'ӳ', + 'Ӵ' => 'Ӵ', + 'ӵ' => 'ӵ', + 'Ӹ' => 'Ӹ', + 'ӹ' => 'ӹ', + 'آ' => 'آ', + 'أ' => 'أ', + 'ؤ' => 'ؤ', + 'إ' => 'إ', + 'ئ' => 'ئ', + 'ۀ' => 'ۀ', + 'ۂ' => 'ۂ', + 'ۓ' => 'ۓ', + 'ऩ' => 'ऩ', + 'ऱ' => 'ऱ', + 'ऴ' => 'ऴ', + 'ো' => 'ো', + 'ৌ' => 'ৌ', + 'ୈ' => 'ୈ', + 'ୋ' => 'ୋ', + 'ୌ' => 'ୌ', + 'ஔ' => 'ஔ', + 'ொ' => 'ொ', + 'ோ' => 'ோ', + 'ௌ' => 'ௌ', + 'ై' => 'ై', + 'ೀ' => 'ೀ', + 'ೇ' => 'ೇ', + 'ೈ' => 'ೈ', + 'ೊ' => 'ೊ', + 'ೋ' => 'ೋ', + 'ൊ' => 'ൊ', + 'ോ' => 'ോ', + 'ൌ' => 'ൌ', + 'ේ' => 'ේ', + 'ො' => 'ො', + 'ෝ' => 'ෝ', + 'ෞ' => 'ෞ', + 'ဦ' => 'ဦ', + 'ᬆ' => 'ᬆ', + 'ᬈ' => 'ᬈ', + 'ᬊ' => 'ᬊ', + 'ᬌ' => 'ᬌ', + 'ᬎ' => 'ᬎ', + 'ᬒ' => 'ᬒ', + 'ᬻ' => 'ᬻ', + 'ᬽ' => 'ᬽ', + 'ᭀ' => 'ᭀ', + 'ᭁ' => 'ᭁ', + 'ᭃ' => 'ᭃ', + 'Ḁ' => 'Ḁ', + 'ḁ' => 'ḁ', + 'Ḃ' => 'Ḃ', + 'ḃ' => 'ḃ', + 'Ḅ' => 'Ḅ', + 'ḅ' => 'ḅ', + 'Ḇ' => 'Ḇ', + 'ḇ' => 'ḇ', + 'Ḉ' => 'Ḉ', + 'ḉ' => 'ḉ', + 'Ḋ' => 'Ḋ', + 'ḋ' => 'ḋ', + 'Ḍ' => 'Ḍ', + 'ḍ' => 'ḍ', + 'Ḏ' => 'Ḏ', + 'ḏ' => 'ḏ', + 'Ḑ' => 'Ḑ', + 'ḑ' => 'ḑ', + 'Ḓ' => 'Ḓ', + 'ḓ' => 'ḓ', + 'Ḕ' => 'Ḕ', + 'ḕ' => 'ḕ', + 'Ḗ' => 'Ḗ', + 'ḗ' => 'ḗ', + 'Ḙ' => 'Ḙ', + 'ḙ' => 'ḙ', + 'Ḛ' => 'Ḛ', + 'ḛ' => 'ḛ', + 'Ḝ' => 'Ḝ', + 'ḝ' => 'ḝ', + 'Ḟ' => 'Ḟ', + 'ḟ' => 'ḟ', + 'Ḡ' => 'Ḡ', + 'ḡ' => 'ḡ', + 'Ḣ' => 'Ḣ', + 'ḣ' => 'ḣ', + 'Ḥ' => 'Ḥ', + 'ḥ' => 'ḥ', + 'Ḧ' => 'Ḧ', + 'ḧ' => 'ḧ', + 'Ḩ' => 'Ḩ', + 'ḩ' => 'ḩ', + 'Ḫ' => 'Ḫ', + 'ḫ' => 'ḫ', + 'Ḭ' => 'Ḭ', + 'ḭ' => 'ḭ', + 'Ḯ' => 'Ḯ', + 'ḯ' => 'ḯ', + 'Ḱ' => 'Ḱ', + 'ḱ' => 'ḱ', + 'Ḳ' => 'Ḳ', + 'ḳ' => 'ḳ', + 'Ḵ' => 'Ḵ', + 'ḵ' => 'ḵ', + 'Ḷ' => 'Ḷ', + 'ḷ' => 'ḷ', + 'Ḹ' => 'Ḹ', + 'ḹ' => 'ḹ', + 'Ḻ' => 'Ḻ', + 'ḻ' => 'ḻ', + 'Ḽ' => 'Ḽ', + 'ḽ' => 'ḽ', + 'Ḿ' => 'Ḿ', + 'ḿ' => 'ḿ', + 'Ṁ' => 'Ṁ', + 'ṁ' => 'ṁ', + 'Ṃ' => 'Ṃ', + 'ṃ' => 'ṃ', + 'Ṅ' => 'Ṅ', + 'ṅ' => 'ṅ', + 'Ṇ' => 'Ṇ', + 'ṇ' => 'ṇ', + 'Ṉ' => 'Ṉ', + 'ṉ' => 'ṉ', + 'Ṋ' => 'Ṋ', + 'ṋ' => 'ṋ', + 'Ṍ' => 'Ṍ', + 'ṍ' => 'ṍ', + 'Ṏ' => 'Ṏ', + 'ṏ' => 'ṏ', + 'Ṑ' => 'Ṑ', + 'ṑ' => 'ṑ', + 'Ṓ' => 'Ṓ', + 'ṓ' => 'ṓ', + 'Ṕ' => 'Ṕ', + 'ṕ' => 'ṕ', + 'Ṗ' => 'Ṗ', + 'ṗ' => 'ṗ', + 'Ṙ' => 'Ṙ', + 'ṙ' => 'ṙ', + 'Ṛ' => 'Ṛ', + 'ṛ' => 'ṛ', + 'Ṝ' => 'Ṝ', + 'ṝ' => 'ṝ', + 'Ṟ' => 'Ṟ', + 'ṟ' => 'ṟ', + 'Ṡ' => 'Ṡ', + 'ṡ' => 'ṡ', + 'Ṣ' => 'Ṣ', + 'ṣ' => 'ṣ', + 'Ṥ' => 'Ṥ', + 'ṥ' => 'ṥ', + 'Ṧ' => 'Ṧ', + 'ṧ' => 'ṧ', + 'Ṩ' => 'Ṩ', + 'ṩ' => 'ṩ', + 'Ṫ' => 'Ṫ', + 'ṫ' => 'ṫ', + 'Ṭ' => 'Ṭ', + 'ṭ' => 'ṭ', + 'Ṯ' => 'Ṯ', + 'ṯ' => 'ṯ', + 'Ṱ' => 'Ṱ', + 'ṱ' => 'ṱ', + 'Ṳ' => 'Ṳ', + 'ṳ' => 'ṳ', + 'Ṵ' => 'Ṵ', + 'ṵ' => 'ṵ', + 'Ṷ' => 'Ṷ', + 'ṷ' => 'ṷ', + 'Ṹ' => 'Ṹ', + 'ṹ' => 'ṹ', + 'Ṻ' => 'Ṻ', + 'ṻ' => 'ṻ', + 'Ṽ' => 'Ṽ', + 'ṽ' => 'ṽ', + 'Ṿ' => 'Ṿ', + 'ṿ' => 'ṿ', + 'Ẁ' => 'Ẁ', + 'ẁ' => 'ẁ', + 'Ẃ' => 'Ẃ', + 'ẃ' => 'ẃ', + 'Ẅ' => 'Ẅ', + 'ẅ' => 'ẅ', + 'Ẇ' => 'Ẇ', + 'ẇ' => 'ẇ', + 'Ẉ' => 'Ẉ', + 'ẉ' => 'ẉ', + 'Ẋ' => 'Ẋ', + 'ẋ' => 'ẋ', + 'Ẍ' => 'Ẍ', + 'ẍ' => 'ẍ', + 'Ẏ' => 'Ẏ', + 'ẏ' => 'ẏ', + 'Ẑ' => 'Ẑ', + 'ẑ' => 'ẑ', + 'Ẓ' => 'Ẓ', + 'ẓ' => 'ẓ', + 'Ẕ' => 'Ẕ', + 'ẕ' => 'ẕ', + 'ẖ' => 'ẖ', + 'ẗ' => 'ẗ', + 'ẘ' => 'ẘ', + 'ẙ' => 'ẙ', + 'ẛ' => 'ẛ', + 'Ạ' => 'Ạ', + 'ạ' => 'ạ', + 'Ả' => 'Ả', + 'ả' => 'ả', + 'Ấ' => 'Ấ', + 'ấ' => 'ấ', + 'Ầ' => 'Ầ', + 'ầ' => 'ầ', + 'Ẩ' => 'Ẩ', + 'ẩ' => 'ẩ', + 'Ẫ' => 'Ẫ', + 'ẫ' => 'ẫ', + 'Ậ' => 'Ậ', + 'ậ' => 'ậ', + 'Ắ' => 'Ắ', + 'ắ' => 'ắ', + 'Ằ' => 'Ằ', + 'ằ' => 'ằ', + 'Ẳ' => 'Ẳ', + 'ẳ' => 'ẳ', + 'Ẵ' => 'Ẵ', + 'ẵ' => 'ẵ', + 'Ặ' => 'Ặ', + 'ặ' => 'ặ', + 'Ẹ' => 'Ẹ', + 'ẹ' => 'ẹ', + 'Ẻ' => 'Ẻ', + 'ẻ' => 'ẻ', + 'Ẽ' => 'Ẽ', + 'ẽ' => 'ẽ', + 'Ế' => 'Ế', + 'ế' => 'ế', + 'Ề' => 'Ề', + 'ề' => 'ề', + 'Ể' => 'Ể', + 'ể' => 'ể', + 'Ễ' => 'Ễ', + 'ễ' => 'ễ', + 'Ệ' => 'Ệ', + 'ệ' => 'ệ', + 'Ỉ' => 'Ỉ', + 'ỉ' => 'ỉ', + 'Ị' => 'Ị', + 'ị' => 'ị', + 'Ọ' => 'Ọ', + 'ọ' => 'ọ', + 'Ỏ' => 'Ỏ', + 'ỏ' => 'ỏ', + 'Ố' => 'Ố', + 'ố' => 'ố', + 'Ồ' => 'Ồ', + 'ồ' => 'ồ', + 'Ổ' => 'Ổ', + 'ổ' => 'ổ', + 'Ỗ' => 'Ỗ', + 'ỗ' => 'ỗ', + 'Ộ' => 'Ộ', + 'ộ' => 'ộ', + 'Ớ' => 'Ớ', + 'ớ' => 'ớ', + 'Ờ' => 'Ờ', + 'ờ' => 'ờ', + 'Ở' => 'Ở', + 'ở' => 'ở', + 'Ỡ' => 'Ỡ', + 'ỡ' => 'ỡ', + 'Ợ' => 'Ợ', + 'ợ' => 'ợ', + 'Ụ' => 'Ụ', + 'ụ' => 'ụ', + 'Ủ' => 'Ủ', + 'ủ' => 'ủ', + 'Ứ' => 'Ứ', + 'ứ' => 'ứ', + 'Ừ' => 'Ừ', + 'ừ' => 'ừ', + 'Ử' => 'Ử', + 'ử' => 'ử', + 'Ữ' => 'Ữ', + 'ữ' => 'ữ', + 'Ự' => 'Ự', + 'ự' => 'ự', + 'Ỳ' => 'Ỳ', + 'ỳ' => 'ỳ', + 'Ỵ' => 'Ỵ', + 'ỵ' => 'ỵ', + 'Ỷ' => 'Ỷ', + 'ỷ' => 'ỷ', + 'Ỹ' => 'Ỹ', + 'ỹ' => 'ỹ', + 'ἀ' => 'ἀ', + 'ἁ' => 'ἁ', + 'ἂ' => 'ἂ', + 'ἃ' => 'ἃ', + 'ἄ' => 'ἄ', + 'ἅ' => 'ἅ', + 'ἆ' => 'ἆ', + 'ἇ' => 'ἇ', + 'Ἀ' => 'Ἀ', + 'Ἁ' => 'Ἁ', + 'Ἂ' => 'Ἂ', + 'Ἃ' => 'Ἃ', + 'Ἄ' => 'Ἄ', + 'Ἅ' => 'Ἅ', + 'Ἆ' => 'Ἆ', + 'Ἇ' => 'Ἇ', + 'ἐ' => 'ἐ', + 'ἑ' => 'ἑ', + 'ἒ' => 'ἒ', + 'ἓ' => 'ἓ', + 'ἔ' => 'ἔ', + 'ἕ' => 'ἕ', + 'Ἐ' => 'Ἐ', + 'Ἑ' => 'Ἑ', + 'Ἒ' => 'Ἒ', + 'Ἓ' => 'Ἓ', + 'Ἔ' => 'Ἔ', + 'Ἕ' => 'Ἕ', + 'ἠ' => 'ἠ', + 'ἡ' => 'ἡ', + 'ἢ' => 'ἢ', + 'ἣ' => 'ἣ', + 'ἤ' => 'ἤ', + 'ἥ' => 'ἥ', + 'ἦ' => 'ἦ', + 'ἧ' => 'ἧ', + 'Ἠ' => 'Ἠ', + 'Ἡ' => 'Ἡ', + 'Ἢ' => 'Ἢ', + 'Ἣ' => 'Ἣ', + 'Ἤ' => 'Ἤ', + 'Ἥ' => 'Ἥ', + 'Ἦ' => 'Ἦ', + 'Ἧ' => 'Ἧ', + 'ἰ' => 'ἰ', + 'ἱ' => 'ἱ', + 'ἲ' => 'ἲ', + 'ἳ' => 'ἳ', + 'ἴ' => 'ἴ', + 'ἵ' => 'ἵ', + 'ἶ' => 'ἶ', + 'ἷ' => 'ἷ', + 'Ἰ' => 'Ἰ', + 'Ἱ' => 'Ἱ', + 'Ἲ' => 'Ἲ', + 'Ἳ' => 'Ἳ', + 'Ἴ' => 'Ἴ', + 'Ἵ' => 'Ἵ', + 'Ἶ' => 'Ἶ', + 'Ἷ' => 'Ἷ', + 'ὀ' => 'ὀ', + 'ὁ' => 'ὁ', + 'ὂ' => 'ὂ', + 'ὃ' => 'ὃ', + 'ὄ' => 'ὄ', + 'ὅ' => 'ὅ', + 'Ὀ' => 'Ὀ', + 'Ὁ' => 'Ὁ', + 'Ὂ' => 'Ὂ', + 'Ὃ' => 'Ὃ', + 'Ὄ' => 'Ὄ', + 'Ὅ' => 'Ὅ', + 'ὐ' => 'ὐ', + 'ὑ' => 'ὑ', + 'ὒ' => 'ὒ', + 'ὓ' => 'ὓ', + 'ὔ' => 'ὔ', + 'ὕ' => 'ὕ', + 'ὖ' => 'ὖ', + 'ὗ' => 'ὗ', + 'Ὑ' => 'Ὑ', + 'Ὓ' => 'Ὓ', + 'Ὕ' => 'Ὕ', + 'Ὗ' => 'Ὗ', + 'ὠ' => 'ὠ', + 'ὡ' => 'ὡ', + 'ὢ' => 'ὢ', + 'ὣ' => 'ὣ', + 'ὤ' => 'ὤ', + 'ὥ' => 'ὥ', + 'ὦ' => 'ὦ', + 'ὧ' => 'ὧ', + 'Ὠ' => 'Ὠ', + 'Ὡ' => 'Ὡ', + 'Ὢ' => 'Ὢ', + 'Ὣ' => 'Ὣ', + 'Ὤ' => 'Ὤ', + 'Ὥ' => 'Ὥ', + 'Ὦ' => 'Ὦ', + 'Ὧ' => 'Ὧ', + 'ὰ' => 'ὰ', + 'ὲ' => 'ὲ', + 'ὴ' => 'ὴ', + 'ὶ' => 'ὶ', + 'ὸ' => 'ὸ', + 'ὺ' => 'ὺ', + 'ὼ' => 'ὼ', + 'ᾀ' => 'ᾀ', + 'ᾁ' => 'ᾁ', + 'ᾂ' => 'ᾂ', + 'ᾃ' => 'ᾃ', + 'ᾄ' => 'ᾄ', + 'ᾅ' => 'ᾅ', + 'ᾆ' => 'ᾆ', + 'ᾇ' => 'ᾇ', + 'ᾈ' => 'ᾈ', + 'ᾉ' => 'ᾉ', + 'ᾊ' => 'ᾊ', + 'ᾋ' => 'ᾋ', + 'ᾌ' => 'ᾌ', + 'ᾍ' => 'ᾍ', + 'ᾎ' => 'ᾎ', + 'ᾏ' => 'ᾏ', + 'ᾐ' => 'ᾐ', + 'ᾑ' => 'ᾑ', + 'ᾒ' => 'ᾒ', + 'ᾓ' => 'ᾓ', + 'ᾔ' => 'ᾔ', + 'ᾕ' => 'ᾕ', + 'ᾖ' => 'ᾖ', + 'ᾗ' => 'ᾗ', + 'ᾘ' => 'ᾘ', + 'ᾙ' => 'ᾙ', + 'ᾚ' => 'ᾚ', + 'ᾛ' => 'ᾛ', + 'ᾜ' => 'ᾜ', + 'ᾝ' => 'ᾝ', + 'ᾞ' => 'ᾞ', + 'ᾟ' => 'ᾟ', + 'ᾠ' => 'ᾠ', + 'ᾡ' => 'ᾡ', + 'ᾢ' => 'ᾢ', + 'ᾣ' => 'ᾣ', + 'ᾤ' => 'ᾤ', + 'ᾥ' => 'ᾥ', + 'ᾦ' => 'ᾦ', + 'ᾧ' => 'ᾧ', + 'ᾨ' => 'ᾨ', + 'ᾩ' => 'ᾩ', + 'ᾪ' => 'ᾪ', + 'ᾫ' => 'ᾫ', + 'ᾬ' => 'ᾬ', + 'ᾭ' => 'ᾭ', + 'ᾮ' => 'ᾮ', + 'ᾯ' => 'ᾯ', + 'ᾰ' => 'ᾰ', + 'ᾱ' => 'ᾱ', + 'ᾲ' => 'ᾲ', + 'ᾳ' => 'ᾳ', + 'ᾴ' => 'ᾴ', + 'ᾶ' => 'ᾶ', + 'ᾷ' => 'ᾷ', + 'Ᾰ' => 'Ᾰ', + 'Ᾱ' => 'Ᾱ', + 'Ὰ' => 'Ὰ', + 'ᾼ' => 'ᾼ', + '῁' => '῁', + 'ῂ' => 'ῂ', + 'ῃ' => 'ῃ', + 'ῄ' => 'ῄ', + 'ῆ' => 'ῆ', + 'ῇ' => 'ῇ', + 'Ὲ' => 'Ὲ', + 'Ὴ' => 'Ὴ', + 'ῌ' => 'ῌ', + '῍' => '῍', + '῎' => '῎', + '῏' => '῏', + 'ῐ' => 'ῐ', + 'ῑ' => 'ῑ', + 'ῒ' => 'ῒ', + 'ῖ' => 'ῖ', + 'ῗ' => 'ῗ', + 'Ῐ' => 'Ῐ', + 'Ῑ' => 'Ῑ', + 'Ὶ' => 'Ὶ', + '῝' => '῝', + '῞' => '῞', + '῟' => '῟', + 'ῠ' => 'ῠ', + 'ῡ' => 'ῡ', + 'ῢ' => 'ῢ', + 'ῤ' => 'ῤ', + 'ῥ' => 'ῥ', + 'ῦ' => 'ῦ', + 'ῧ' => 'ῧ', + 'Ῠ' => 'Ῠ', + 'Ῡ' => 'Ῡ', + 'Ὺ' => 'Ὺ', + 'Ῥ' => 'Ῥ', + '῭' => '῭', + 'ῲ' => 'ῲ', + 'ῳ' => 'ῳ', + 'ῴ' => 'ῴ', + 'ῶ' => 'ῶ', + 'ῷ' => 'ῷ', + 'Ὸ' => 'Ὸ', + 'Ὼ' => 'Ὼ', + 'ῼ' => 'ῼ', + '↚' => '↚', + '↛' => '↛', + '↮' => '↮', + '⇍' => '⇍', + '⇎' => '⇎', + '⇏' => '⇏', + '∄' => '∄', + '∉' => '∉', + '∌' => '∌', + '∤' => '∤', + '∦' => '∦', + '≁' => '≁', + '≄' => '≄', + '≇' => '≇', + '≉' => '≉', + '≠' => '≠', + '≢' => '≢', + '≭' => '≭', + '≮' => '≮', + '≯' => '≯', + '≰' => '≰', + '≱' => '≱', + '≴' => '≴', + '≵' => '≵', + '≸' => '≸', + '≹' => '≹', + '⊀' => '⊀', + '⊁' => '⊁', + '⊄' => '⊄', + '⊅' => '⊅', + '⊈' => '⊈', + '⊉' => '⊉', + '⊬' => '⊬', + '⊭' => '⊭', + '⊮' => '⊮', + '⊯' => '⊯', + '⋠' => '⋠', + '⋡' => '⋡', + '⋢' => '⋢', + '⋣' => '⋣', + '⋪' => '⋪', + '⋫' => '⋫', + '⋬' => '⋬', + '⋭' => '⋭', + 'が' => 'が', + 'ぎ' => 'ぎ', + 'ぐ' => 'ぐ', + 'げ' => 'げ', + 'ご' => 'ご', + 'ざ' => 'ざ', + 'じ' => 'じ', + 'ず' => 'ず', + 'ぜ' => 'ぜ', + 'ぞ' => 'ぞ', + 'だ' => 'だ', + 'ぢ' => 'ぢ', + 'づ' => 'づ', + 'で' => 'で', + 'ど' => 'ど', + 'ば' => 'ば', + 'ぱ' => 'ぱ', + 'び' => 'び', + 'ぴ' => 'ぴ', + 'ぶ' => 'ぶ', + 'ぷ' => 'ぷ', + 'べ' => 'べ', + 'ぺ' => 'ぺ', + 'ぼ' => 'ぼ', + 'ぽ' => 'ぽ', + 'ゔ' => 'ゔ', + 'ゞ' => 'ゞ', + 'ガ' => 'ガ', + 'ギ' => 'ギ', + 'グ' => 'グ', + 'ゲ' => 'ゲ', + 'ゴ' => 'ゴ', + 'ザ' => 'ザ', + 'ジ' => 'ジ', + 'ズ' => 'ズ', + 'ゼ' => 'ゼ', + 'ゾ' => 'ゾ', + 'ダ' => 'ダ', + 'ヂ' => 'ヂ', + 'ヅ' => 'ヅ', + 'デ' => 'デ', + 'ド' => 'ド', + 'バ' => 'バ', + 'パ' => 'パ', + 'ビ' => 'ビ', + 'ピ' => 'ピ', + 'ブ' => 'ブ', + 'プ' => 'プ', + 'ベ' => 'ベ', + 'ペ' => 'ペ', + 'ボ' => 'ボ', + 'ポ' => 'ポ', + 'ヴ' => 'ヴ', + 'ヷ' => 'ヷ', + 'ヸ' => 'ヸ', + 'ヹ' => 'ヹ', + 'ヺ' => 'ヺ', + 'ヾ' => 'ヾ', + '𑂚' => '𑂚', + '𑂜' => '𑂜', + '𑂫' => '𑂫', + '𑄮' => '𑄮', + '𑄯' => '𑄯', + '𑍋' => '𑍋', + '𑍌' => '𑍌', + '𑒻' => '𑒻', + '𑒼' => '𑒼', + '𑒾' => '𑒾', + '𑖺' => '𑖺', + '𑖻' => '𑖻', + '𑤸' => '𑤸', +); 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 100644 index 0000000..5a3e8e0 --- /dev/null +++ b/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php @@ -0,0 +1,2065 @@ + 'À', + 'Á' => 'Á', + 'Â' => 'Â', + 'Ã' => 'Ã', + 'Ä' => 'Ä', + 'Å' => 'Å', + 'Ç' => 'Ç', + 'È' => 'È', + 'É' => 'É', + 'Ê' => 'Ê', + 'Ë' => 'Ë', + 'Ì' => 'Ì', + 'Í' => 'Í', + 'Î' => 'Î', + 'Ï' => 'Ï', + 'Ñ' => 'Ñ', + 'Ò' => 'Ò', + 'Ó' => 'Ó', + 'Ô' => 'Ô', + 'Õ' => 'Õ', + 'Ö' => 'Ö', + 'Ù' => 'Ù', + 'Ú' => 'Ú', + 'Û' => 'Û', + 'Ü' => 'Ü', + 'Ý' => 'Ý', + 'à' => 'à', + 'á' => 'á', + 'â' => 'â', + 'ã' => 'ã', + 'ä' => 'ä', + 'å' => 'å', + 'ç' => 'ç', + 'è' => 'è', + 'é' => 'é', + 'ê' => 'ê', + 'ë' => 'ë', + 'ì' => 'ì', + 'í' => 'í', + 'î' => 'î', + 'ï' => 'ï', + 'ñ' => 'ñ', + 'ò' => 'ò', + 'ó' => 'ó', + 'ô' => 'ô', + 'õ' => 'õ', + 'ö' => 'ö', + 'ù' => 'ù', + 'ú' => 'ú', + 'û' => 'û', + 'ü' => 'ü', + 'ý' => 'ý', + 'ÿ' => 'ÿ', + 'Ā' => 'Ā', + 'ā' => 'ā', + 'Ă' => 'Ă', + 'ă' => 'ă', + 'Ą' => 'Ą', + 'ą' => 'ą', + 'Ć' => 'Ć', + 'ć' => 'ć', + 'Ĉ' => 'Ĉ', + 'ĉ' => 'ĉ', + 'Ċ' => 'Ċ', + 'ċ' => 'ċ', + 'Č' => 'Č', + 'č' => 'č', + 'Ď' => 'Ď', + 'ď' => 'ď', + 'Ē' => 'Ē', + 'ē' => 'ē', + 'Ĕ' => 'Ĕ', + 'ĕ' => 'ĕ', + 'Ė' => 'Ė', + 'ė' => 'ė', + 'Ę' => 'Ę', + 'ę' => 'ę', + 'Ě' => 'Ě', + 'ě' => 'ě', + 'Ĝ' => 'Ĝ', + 'ĝ' => 'ĝ', + 'Ğ' => 'Ğ', + 'ğ' => 'ğ', + 'Ġ' => 'Ġ', + 'ġ' => 'ġ', + 'Ģ' => 'Ģ', + 'ģ' => 'ģ', + 'Ĥ' => 'Ĥ', + 'ĥ' => 'ĥ', + 'Ĩ' => 'Ĩ', + 'ĩ' => 'ĩ', + 'Ī' => 'Ī', + 'ī' => 'ī', + 'Ĭ' => 'Ĭ', + 'ĭ' => 'ĭ', + 'Į' => 'Į', + 'į' => 'į', + 'İ' => 'İ', + 'Ĵ' => 'Ĵ', + 'ĵ' => 'ĵ', + 'Ķ' => 'Ķ', + 'ķ' => 'ķ', + 'Ĺ' => 'Ĺ', + 'ĺ' => 'ĺ', + 'Ļ' => 'Ļ', + 'ļ' => 'ļ', + 'Ľ' => 'Ľ', + 'ľ' => 'ľ', + 'Ń' => 'Ń', + 'ń' => 'ń', + 'Ņ' => 'Ņ', + 'ņ' => 'ņ', + 'Ň' => 'Ň', + 'ň' => 'ň', + 'Ō' => 'Ō', + 'ō' => 'ō', + 'Ŏ' => 'Ŏ', + 'ŏ' => 'ŏ', + 'Ő' => 'Ő', + 'ő' => 'ő', + 'Ŕ' => 'Ŕ', + 'ŕ' => 'ŕ', + 'Ŗ' => 'Ŗ', + 'ŗ' => 'ŗ', + 'Ř' => 'Ř', + 'ř' => 'ř', + 'Ś' => 'Ś', + 'ś' => 'ś', + 'Ŝ' => 'Ŝ', + 'ŝ' => 'ŝ', + 'Ş' => 'Ş', + 'ş' => 'ş', + 'Š' => 'Š', + 'š' => 'š', + 'Ţ' => 'Ţ', + 'ţ' => 'ţ', + 'Ť' => 'Ť', + 'ť' => 'ť', + 'Ũ' => 'Ũ', + 'ũ' => 'ũ', + 'Ū' => 'Ū', + 'ū' => 'ū', + 'Ŭ' => 'Ŭ', + 'ŭ' => 'ŭ', + 'Ů' => 'Ů', + 'ů' => 'ů', + 'Ű' => 'Ű', + 'ű' => 'ű', + 'Ų' => 'Ų', + 'ų' => 'ų', + 'Ŵ' => 'Ŵ', + 'ŵ' => 'ŵ', + 'Ŷ' => 'Ŷ', + 'ŷ' => 'ŷ', + 'Ÿ' => 'Ÿ', + 'Ź' => 'Ź', + 'ź' => 'ź', + 'Ż' => 'Ż', + 'ż' => 'ż', + 'Ž' => 'Ž', + 'ž' => 'ž', + 'Ơ' => 'Ơ', + 'ơ' => 'ơ', + 'Ư' => 'Ư', + 'ư' => 'ư', + 'Ǎ' => 'Ǎ', + 'ǎ' => 'ǎ', + 'Ǐ' => 'Ǐ', + 'ǐ' => 'ǐ', + 'Ǒ' => 'Ǒ', + 'ǒ' => 'ǒ', + 'Ǔ' => 'Ǔ', + 'ǔ' => 'ǔ', + 'Ǖ' => 'Ǖ', + 'ǖ' => 'ǖ', + 'Ǘ' => 'Ǘ', + 'ǘ' => 'ǘ', + 'Ǚ' => 'Ǚ', + 'ǚ' => 'ǚ', + 'Ǜ' => 'Ǜ', + 'ǜ' => 'ǜ', + 'Ǟ' => 'Ǟ', + 'ǟ' => 'ǟ', + 'Ǡ' => 'Ǡ', + 'ǡ' => 'ǡ', + 'Ǣ' => 'Ǣ', + 'ǣ' => 'ǣ', + 'Ǧ' => 'Ǧ', + 'ǧ' => 'ǧ', + 'Ǩ' => 'Ǩ', + 'ǩ' => 'ǩ', + 'Ǫ' => 'Ǫ', + 'ǫ' => 'ǫ', + 'Ǭ' => 'Ǭ', + 'ǭ' => 'ǭ', + 'Ǯ' => 'Ǯ', + 'ǯ' => 'ǯ', + 'ǰ' => 'ǰ', + 'Ǵ' => 'Ǵ', + 'ǵ' => 'ǵ', + 'Ǹ' => 'Ǹ', + 'ǹ' => 'ǹ', + 'Ǻ' => 'Ǻ', + 'ǻ' => 'ǻ', + 'Ǽ' => 'Ǽ', + 'ǽ' => 'ǽ', + 'Ǿ' => 'Ǿ', + 'ǿ' => 'ǿ', + 'Ȁ' => 'Ȁ', + 'ȁ' => 'ȁ', + 'Ȃ' => 'Ȃ', + 'ȃ' => 'ȃ', + 'Ȅ' => 'Ȅ', + 'ȅ' => 'ȅ', + 'Ȇ' => 'Ȇ', + 'ȇ' => 'ȇ', + 'Ȉ' => 'Ȉ', + 'ȉ' => 'ȉ', + 'Ȋ' => 'Ȋ', + 'ȋ' => 'ȋ', + 'Ȍ' => 'Ȍ', + 'ȍ' => 'ȍ', + 'Ȏ' => 'Ȏ', + 'ȏ' => 'ȏ', + 'Ȑ' => 'Ȑ', + 'ȑ' => 'ȑ', + 'Ȓ' => 'Ȓ', + 'ȓ' => 'ȓ', + 'Ȕ' => 'Ȕ', + 'ȕ' => 'ȕ', + 'Ȗ' => 'Ȗ', + 'ȗ' => 'ȗ', + 'Ș' => 'Ș', + 'ș' => 'ș', + 'Ț' => 'Ț', + 'ț' => 'ț', + 'Ȟ' => 'Ȟ', + 'ȟ' => 'ȟ', + 'Ȧ' => 'Ȧ', + 'ȧ' => 'ȧ', + 'Ȩ' => 'Ȩ', + 'ȩ' => 'ȩ', + 'Ȫ' => 'Ȫ', + 'ȫ' => 'ȫ', + 'Ȭ' => 'Ȭ', + 'ȭ' => 'ȭ', + 'Ȯ' => 'Ȯ', + 'ȯ' => 'ȯ', + 'Ȱ' => 'Ȱ', + 'ȱ' => 'ȱ', + 'Ȳ' => 'Ȳ', + 'ȳ' => 'ȳ', + '̀' => '̀', + '́' => '́', + '̓' => '̓', + '̈́' => '̈́', + 'ʹ' => 'ʹ', + ';' => ';', + '΅' => '΅', + 'Ά' => 'Ά', + '·' => '·', + 'Έ' => 'Έ', + 'Ή' => 'Ή', + 'Ί' => 'Ί', + 'Ό' => 'Ό', + 'Ύ' => 'Ύ', + 'Ώ' => 'Ώ', + 'ΐ' => 'ΐ', + 'Ϊ' => 'Ϊ', + 'Ϋ' => 'Ϋ', + 'ά' => 'ά', + 'έ' => 'έ', + 'ή' => 'ή', + 'ί' => 'ί', + 'ΰ' => 'ΰ', + 'ϊ' => 'ϊ', + 'ϋ' => 'ϋ', + 'ό' => 'ό', + 'ύ' => 'ύ', + 'ώ' => 'ώ', + 'ϓ' => 'ϓ', + 'ϔ' => 'ϔ', + 'Ѐ' => 'Ѐ', + 'Ё' => 'Ё', + 'Ѓ' => 'Ѓ', + 'Ї' => 'Ї', + 'Ќ' => 'Ќ', + 'Ѝ' => 'Ѝ', + 'Ў' => 'Ў', + 'Й' => 'Й', + 'й' => 'й', + 'ѐ' => 'ѐ', + 'ё' => 'ё', + 'ѓ' => 'ѓ', + 'ї' => 'ї', + 'ќ' => 'ќ', + 'ѝ' => 'ѝ', + 'ў' => 'ў', + 'Ѷ' => 'Ѷ', + 'ѷ' => 'ѷ', + 'Ӂ' => 'Ӂ', + 'ӂ' => 'ӂ', + 'Ӑ' => 'Ӑ', + 'ӑ' => 'ӑ', + 'Ӓ' => 'Ӓ', + 'ӓ' => 'ӓ', + 'Ӗ' => 'Ӗ', + 'ӗ' => 'ӗ', + 'Ӛ' => 'Ӛ', + 'ӛ' => 'ӛ', + 'Ӝ' => 'Ӝ', + 'ӝ' => 'ӝ', + 'Ӟ' => 'Ӟ', + 'ӟ' => 'ӟ', + 'Ӣ' => 'Ӣ', + 'ӣ' => 'ӣ', + 'Ӥ' => 'Ӥ', + 'ӥ' => 'ӥ', + 'Ӧ' => 'Ӧ', + 'ӧ' => 'ӧ', + 'Ӫ' => 'Ӫ', + 'ӫ' => 'ӫ', + 'Ӭ' => 'Ӭ', + 'ӭ' => 'ӭ', + 'Ӯ' => 'Ӯ', + 'ӯ' => 'ӯ', + 'Ӱ' => 'Ӱ', + 'ӱ' => 'ӱ', + 'Ӳ' => 'Ӳ', + 'ӳ' => 'ӳ', + 'Ӵ' => 'Ӵ', + 'ӵ' => 'ӵ', + 'Ӹ' => 'Ӹ', + 'ӹ' => 'ӹ', + 'آ' => 'آ', + 'أ' => 'أ', + 'ؤ' => 'ؤ', + 'إ' => 'إ', + 'ئ' => 'ئ', + 'ۀ' => 'ۀ', + 'ۂ' => 'ۂ', + 'ۓ' => 'ۓ', + 'ऩ' => 'ऩ', + 'ऱ' => 'ऱ', + 'ऴ' => 'ऴ', + 'क़' => 'क़', + 'ख़' => 'ख़', + 'ग़' => 'ग़', + 'ज़' => 'ज़', + 'ड़' => 'ड़', + 'ढ़' => 'ढ़', + 'फ़' => 'फ़', + 'य़' => 'य़', + 'ো' => 'ো', + 'ৌ' => 'ৌ', + 'ড়' => 'ড়', + 'ঢ়' => 'ঢ়', + 'য়' => 'য়', + 'ਲ਼' => 'ਲ਼', + 'ਸ਼' => 'ਸ਼', + 'ਖ਼' => 'ਖ਼', + 'ਗ਼' => 'ਗ਼', + 'ਜ਼' => 'ਜ਼', + 'ਫ਼' => 'ਫ਼', + 'ୈ' => 'ୈ', + 'ୋ' => 'ୋ', + 'ୌ' => 'ୌ', + 'ଡ଼' => 'ଡ଼', + 'ଢ଼' => 'ଢ଼', + 'ஔ' => 'ஔ', + 'ொ' => 'ொ', + 'ோ' => 'ோ', + 'ௌ' => 'ௌ', + 'ై' => 'ై', + 'ೀ' => 'ೀ', + 'ೇ' => 'ೇ', + 'ೈ' => 'ೈ', + 'ೊ' => 'ೊ', + 'ೋ' => 'ೋ', + 'ൊ' => 'ൊ', + 'ോ' => 'ോ', + 'ൌ' => 'ൌ', + 'ේ' => 'ේ', + 'ො' => 'ො', + 'ෝ' => 'ෝ', + 'ෞ' => 'ෞ', + 'གྷ' => 'གྷ', + 'ཌྷ' => 'ཌྷ', + 'དྷ' => 'དྷ', + 'བྷ' => 'བྷ', + 'ཛྷ' => 'ཛྷ', + 'ཀྵ' => 'ཀྵ', + 'ཱི' => 'ཱི', + 'ཱུ' => 'ཱུ', + 'ྲྀ' => 'ྲྀ', + 'ླྀ' => 'ླྀ', + 'ཱྀ' => 'ཱྀ', + 'ྒྷ' => 'ྒྷ', + 'ྜྷ' => 'ྜྷ', + 'ྡྷ' => 'ྡྷ', + 'ྦྷ' => 'ྦྷ', + 'ྫྷ' => 'ྫྷ', + 'ྐྵ' => 'ྐྵ', + 'ဦ' => 'ဦ', + 'ᬆ' => 'ᬆ', + 'ᬈ' => 'ᬈ', + 'ᬊ' => 'ᬊ', + 'ᬌ' => 'ᬌ', + 'ᬎ' => 'ᬎ', + 'ᬒ' => 'ᬒ', + 'ᬻ' => 'ᬻ', + 'ᬽ' => 'ᬽ', + 'ᭀ' => 'ᭀ', + 'ᭁ' => 'ᭁ', + 'ᭃ' => 'ᭃ', + 'Ḁ' => 'Ḁ', + 'ḁ' => 'ḁ', + 'Ḃ' => 'Ḃ', + 'ḃ' => 'ḃ', + 'Ḅ' => 'Ḅ', + 'ḅ' => 'ḅ', + 'Ḇ' => 'Ḇ', + 'ḇ' => 'ḇ', + 'Ḉ' => 'Ḉ', + 'ḉ' => 'ḉ', + 'Ḋ' => 'Ḋ', + 'ḋ' => 'ḋ', + 'Ḍ' => 'Ḍ', + 'ḍ' => 'ḍ', + 'Ḏ' => 'Ḏ', + 'ḏ' => 'ḏ', + 'Ḑ' => 'Ḑ', + 'ḑ' => 'ḑ', + 'Ḓ' => 'Ḓ', + 'ḓ' => 'ḓ', + 'Ḕ' => 'Ḕ', + 'ḕ' => 'ḕ', + 'Ḗ' => 'Ḗ', + 'ḗ' => 'ḗ', + 'Ḙ' => 'Ḙ', + 'ḙ' => 'ḙ', + 'Ḛ' => 'Ḛ', + 'ḛ' => 'ḛ', + 'Ḝ' => 'Ḝ', + 'ḝ' => 'ḝ', + 'Ḟ' => 'Ḟ', + 'ḟ' => 'ḟ', + 'Ḡ' => 'Ḡ', + 'ḡ' => 'ḡ', + 'Ḣ' => 'Ḣ', + 'ḣ' => 'ḣ', + 'Ḥ' => 'Ḥ', + 'ḥ' => 'ḥ', + 'Ḧ' => 'Ḧ', + 'ḧ' => 'ḧ', + 'Ḩ' => 'Ḩ', + 'ḩ' => 'ḩ', + 'Ḫ' => 'Ḫ', + 'ḫ' => 'ḫ', + 'Ḭ' => 'Ḭ', + 'ḭ' => 'ḭ', + 'Ḯ' => 'Ḯ', + 'ḯ' => 'ḯ', + 'Ḱ' => 'Ḱ', + 'ḱ' => 'ḱ', + 'Ḳ' => 'Ḳ', + 'ḳ' => 'ḳ', + 'Ḵ' => 'Ḵ', + 'ḵ' => 'ḵ', + 'Ḷ' => 'Ḷ', + 'ḷ' => 'ḷ', + 'Ḹ' => 'Ḹ', + 'ḹ' => 'ḹ', + 'Ḻ' => 'Ḻ', + 'ḻ' => 'ḻ', + 'Ḽ' => 'Ḽ', + 'ḽ' => 'ḽ', + 'Ḿ' => 'Ḿ', + 'ḿ' => 'ḿ', + 'Ṁ' => 'Ṁ', + 'ṁ' => 'ṁ', + 'Ṃ' => 'Ṃ', + 'ṃ' => 'ṃ', + 'Ṅ' => 'Ṅ', + 'ṅ' => 'ṅ', + 'Ṇ' => 'Ṇ', + 'ṇ' => 'ṇ', + 'Ṉ' => 'Ṉ', + 'ṉ' => 'ṉ', + 'Ṋ' => 'Ṋ', + 'ṋ' => 'ṋ', + 'Ṍ' => 'Ṍ', + 'ṍ' => 'ṍ', + 'Ṏ' => 'Ṏ', + 'ṏ' => 'ṏ', + 'Ṑ' => 'Ṑ', + 'ṑ' => 'ṑ', + 'Ṓ' => 'Ṓ', + 'ṓ' => 'ṓ', + 'Ṕ' => 'Ṕ', + 'ṕ' => 'ṕ', + 'Ṗ' => 'Ṗ', + 'ṗ' => 'ṗ', + 'Ṙ' => 'Ṙ', + 'ṙ' => 'ṙ', + 'Ṛ' => 'Ṛ', + 'ṛ' => 'ṛ', + 'Ṝ' => 'Ṝ', + 'ṝ' => 'ṝ', + 'Ṟ' => 'Ṟ', + 'ṟ' => 'ṟ', + 'Ṡ' => 'Ṡ', + 'ṡ' => 'ṡ', + 'Ṣ' => 'Ṣ', + 'ṣ' => 'ṣ', + 'Ṥ' => 'Ṥ', + 'ṥ' => 'ṥ', + 'Ṧ' => 'Ṧ', + 'ṧ' => 'ṧ', + 'Ṩ' => 'Ṩ', + 'ṩ' => 'ṩ', + 'Ṫ' => 'Ṫ', + 'ṫ' => 'ṫ', + 'Ṭ' => 'Ṭ', + 'ṭ' => 'ṭ', + 'Ṯ' => 'Ṯ', + 'ṯ' => 'ṯ', + 'Ṱ' => 'Ṱ', + 'ṱ' => 'ṱ', + 'Ṳ' => 'Ṳ', + 'ṳ' => 'ṳ', + 'Ṵ' => 'Ṵ', + 'ṵ' => 'ṵ', + 'Ṷ' => 'Ṷ', + 'ṷ' => 'ṷ', + 'Ṹ' => 'Ṹ', + 'ṹ' => 'ṹ', + 'Ṻ' => 'Ṻ', + 'ṻ' => 'ṻ', + 'Ṽ' => 'Ṽ', + 'ṽ' => 'ṽ', + 'Ṿ' => 'Ṿ', + 'ṿ' => 'ṿ', + 'Ẁ' => 'Ẁ', + 'ẁ' => 'ẁ', + 'Ẃ' => 'Ẃ', + 'ẃ' => 'ẃ', + 'Ẅ' => 'Ẅ', + 'ẅ' => 'ẅ', + 'Ẇ' => 'Ẇ', + 'ẇ' => 'ẇ', + 'Ẉ' => 'Ẉ', + 'ẉ' => 'ẉ', + 'Ẋ' => 'Ẋ', + 'ẋ' => 'ẋ', + 'Ẍ' => 'Ẍ', + 'ẍ' => 'ẍ', + 'Ẏ' => 'Ẏ', + 'ẏ' => 'ẏ', + 'Ẑ' => 'Ẑ', + 'ẑ' => 'ẑ', + 'Ẓ' => 'Ẓ', + 'ẓ' => 'ẓ', + 'Ẕ' => 'Ẕ', + 'ẕ' => 'ẕ', + 'ẖ' => 'ẖ', + 'ẗ' => 'ẗ', + 'ẘ' => 'ẘ', + 'ẙ' => 'ẙ', + 'ẛ' => 'ẛ', + 'Ạ' => 'Ạ', + 'ạ' => 'ạ', + 'Ả' => 'Ả', + 'ả' => 'ả', + 'Ấ' => 'Ấ', + 'ấ' => 'ấ', + 'Ầ' => 'Ầ', + 'ầ' => 'ầ', + 'Ẩ' => 'Ẩ', + 'ẩ' => 'ẩ', + 'Ẫ' => 'Ẫ', + 'ẫ' => 'ẫ', + 'Ậ' => 'Ậ', + 'ậ' => 'ậ', + 'Ắ' => 'Ắ', + 'ắ' => 'ắ', + 'Ằ' => 'Ằ', + 'ằ' => 'ằ', + 'Ẳ' => 'Ẳ', + 'ẳ' => 'ẳ', + 'Ẵ' => 'Ẵ', + 'ẵ' => 'ẵ', + 'Ặ' => 'Ặ', + 'ặ' => 'ặ', + 'Ẹ' => 'Ẹ', + 'ẹ' => 'ẹ', + 'Ẻ' => 'Ẻ', + 'ẻ' => 'ẻ', + 'Ẽ' => 'Ẽ', + 'ẽ' => 'ẽ', + 'Ế' => 'Ế', + 'ế' => 'ế', + 'Ề' => 'Ề', + 'ề' => 'ề', + 'Ể' => 'Ể', + 'ể' => 'ể', + 'Ễ' => 'Ễ', + 'ễ' => 'ễ', + 'Ệ' => 'Ệ', + 'ệ' => 'ệ', + 'Ỉ' => 'Ỉ', + 'ỉ' => 'ỉ', + 'Ị' => 'Ị', + 'ị' => 'ị', + 'Ọ' => 'Ọ', + 'ọ' => 'ọ', + 'Ỏ' => 'Ỏ', + 'ỏ' => 'ỏ', + 'Ố' => 'Ố', + 'ố' => 'ố', + 'Ồ' => 'Ồ', + 'ồ' => 'ồ', + 'Ổ' => 'Ổ', + 'ổ' => 'ổ', + 'Ỗ' => 'Ỗ', + 'ỗ' => 'ỗ', + 'Ộ' => 'Ộ', + 'ộ' => 'ộ', + 'Ớ' => 'Ớ', + 'ớ' => 'ớ', + 'Ờ' => 'Ờ', + 'ờ' => 'ờ', + 'Ở' => 'Ở', + 'ở' => 'ở', + 'Ỡ' => 'Ỡ', + 'ỡ' => 'ỡ', + 'Ợ' => 'Ợ', + 'ợ' => 'ợ', + 'Ụ' => 'Ụ', + 'ụ' => 'ụ', + 'Ủ' => 'Ủ', + 'ủ' => 'ủ', + 'Ứ' => 'Ứ', + 'ứ' => 'ứ', + 'Ừ' => 'Ừ', + 'ừ' => 'ừ', + 'Ử' => 'Ử', + 'ử' => 'ử', + 'Ữ' => 'Ữ', + 'ữ' => 'ữ', + 'Ự' => 'Ự', + 'ự' => 'ự', + 'Ỳ' => 'Ỳ', + 'ỳ' => 'ỳ', + 'Ỵ' => 'Ỵ', + 'ỵ' => 'ỵ', + 'Ỷ' => 'Ỷ', + 'ỷ' => 'ỷ', + 'Ỹ' => 'Ỹ', + 'ỹ' => 'ỹ', + 'ἀ' => 'ἀ', + 'ἁ' => 'ἁ', + 'ἂ' => 'ἂ', + 'ἃ' => 'ἃ', + 'ἄ' => 'ἄ', + 'ἅ' => 'ἅ', + 'ἆ' => 'ἆ', + 'ἇ' => 'ἇ', + 'Ἀ' => 'Ἀ', + 'Ἁ' => 'Ἁ', + 'Ἂ' => 'Ἂ', + 'Ἃ' => 'Ἃ', + 'Ἄ' => 'Ἄ', + 'Ἅ' => 'Ἅ', + 'Ἆ' => 'Ἆ', + 'Ἇ' => 'Ἇ', + 'ἐ' => 'ἐ', + 'ἑ' => 'ἑ', + 'ἒ' => 'ἒ', + 'ἓ' => 'ἓ', + 'ἔ' => 'ἔ', + 'ἕ' => 'ἕ', + 'Ἐ' => 'Ἐ', + 'Ἑ' => 'Ἑ', + 'Ἒ' => 'Ἒ', + 'Ἓ' => 'Ἓ', + 'Ἔ' => 'Ἔ', + 'Ἕ' => 'Ἕ', + 'ἠ' => 'ἠ', + 'ἡ' => 'ἡ', + 'ἢ' => 'ἢ', + 'ἣ' => 'ἣ', + 'ἤ' => 'ἤ', + 'ἥ' => 'ἥ', + 'ἦ' => 'ἦ', + 'ἧ' => 'ἧ', + 'Ἠ' => 'Ἠ', + 'Ἡ' => 'Ἡ', + 'Ἢ' => 'Ἢ', + 'Ἣ' => 'Ἣ', + 'Ἤ' => 'Ἤ', + 'Ἥ' => 'Ἥ', + 'Ἦ' => 'Ἦ', + 'Ἧ' => 'Ἧ', + 'ἰ' => 'ἰ', + 'ἱ' => 'ἱ', + 'ἲ' => 'ἲ', + 'ἳ' => 'ἳ', + 'ἴ' => 'ἴ', + 'ἵ' => 'ἵ', + 'ἶ' => 'ἶ', + 'ἷ' => 'ἷ', + 'Ἰ' => 'Ἰ', + 'Ἱ' => 'Ἱ', + 'Ἲ' => 'Ἲ', + 'Ἳ' => 'Ἳ', + 'Ἴ' => 'Ἴ', + 'Ἵ' => 'Ἵ', + 'Ἶ' => 'Ἶ', + 'Ἷ' => 'Ἷ', + 'ὀ' => 'ὀ', + 'ὁ' => 'ὁ', + 'ὂ' => 'ὂ', + 'ὃ' => 'ὃ', + 'ὄ' => 'ὄ', + 'ὅ' => 'ὅ', + 'Ὀ' => 'Ὀ', + 'Ὁ' => 'Ὁ', + 'Ὂ' => 'Ὂ', + 'Ὃ' => 'Ὃ', + 'Ὄ' => 'Ὄ', + 'Ὅ' => 'Ὅ', + 'ὐ' => 'ὐ', + 'ὑ' => 'ὑ', + 'ὒ' => 'ὒ', + 'ὓ' => 'ὓ', + 'ὔ' => 'ὔ', + 'ὕ' => 'ὕ', + 'ὖ' => 'ὖ', + 'ὗ' => 'ὗ', + 'Ὑ' => 'Ὑ', + 'Ὓ' => 'Ὓ', + 'Ὕ' => 'Ὕ', + 'Ὗ' => 'Ὗ', + 'ὠ' => 'ὠ', + 'ὡ' => 'ὡ', + 'ὢ' => 'ὢ', + 'ὣ' => 'ὣ', + 'ὤ' => 'ὤ', + 'ὥ' => 'ὥ', + 'ὦ' => 'ὦ', + 'ὧ' => 'ὧ', + 'Ὠ' => 'Ὠ', + 'Ὡ' => 'Ὡ', + 'Ὢ' => 'Ὢ', + 'Ὣ' => 'Ὣ', + 'Ὤ' => 'Ὤ', + 'Ὥ' => 'Ὥ', + 'Ὦ' => 'Ὦ', + 'Ὧ' => 'Ὧ', + 'ὰ' => 'ὰ', + 'ά' => 'ά', + 'ὲ' => 'ὲ', + 'έ' => 'έ', + 'ὴ' => 'ὴ', + 'ή' => 'ή', + 'ὶ' => 'ὶ', + 'ί' => 'ί', + 'ὸ' => 'ὸ', + 'ό' => 'ό', + 'ὺ' => 'ὺ', + 'ύ' => 'ύ', + 'ὼ' => 'ὼ', + 'ώ' => 'ώ', + 'ᾀ' => 'ᾀ', + 'ᾁ' => 'ᾁ', + 'ᾂ' => 'ᾂ', + 'ᾃ' => 'ᾃ', + 'ᾄ' => 'ᾄ', + 'ᾅ' => 'ᾅ', + 'ᾆ' => 'ᾆ', + 'ᾇ' => 'ᾇ', + 'ᾈ' => 'ᾈ', + 'ᾉ' => 'ᾉ', + 'ᾊ' => 'ᾊ', + 'ᾋ' => 'ᾋ', + 'ᾌ' => 'ᾌ', + 'ᾍ' => 'ᾍ', + 'ᾎ' => 'ᾎ', + 'ᾏ' => 'ᾏ', + 'ᾐ' => 'ᾐ', + 'ᾑ' => 'ᾑ', + 'ᾒ' => 'ᾒ', + 'ᾓ' => 'ᾓ', + 'ᾔ' => 'ᾔ', + 'ᾕ' => 'ᾕ', + 'ᾖ' => 'ᾖ', + 'ᾗ' => 'ᾗ', + 'ᾘ' => 'ᾘ', + 'ᾙ' => 'ᾙ', + 'ᾚ' => 'ᾚ', + 'ᾛ' => 'ᾛ', + 'ᾜ' => 'ᾜ', + 'ᾝ' => 'ᾝ', + 'ᾞ' => 'ᾞ', + 'ᾟ' => 'ᾟ', + 'ᾠ' => 'ᾠ', + 'ᾡ' => 'ᾡ', + 'ᾢ' => 'ᾢ', + 'ᾣ' => 'ᾣ', + 'ᾤ' => 'ᾤ', + 'ᾥ' => 'ᾥ', + 'ᾦ' => 'ᾦ', + 'ᾧ' => 'ᾧ', + 'ᾨ' => 'ᾨ', + 'ᾩ' => 'ᾩ', + 'ᾪ' => 'ᾪ', + 'ᾫ' => 'ᾫ', + 'ᾬ' => 'ᾬ', + 'ᾭ' => 'ᾭ', + 'ᾮ' => 'ᾮ', + 'ᾯ' => 'ᾯ', + 'ᾰ' => 'ᾰ', + 'ᾱ' => 'ᾱ', + 'ᾲ' => 'ᾲ', + 'ᾳ' => 'ᾳ', + 'ᾴ' => 'ᾴ', + 'ᾶ' => 'ᾶ', + 'ᾷ' => 'ᾷ', + 'Ᾰ' => 'Ᾰ', + 'Ᾱ' => 'Ᾱ', + 'Ὰ' => 'Ὰ', + 'Ά' => 'Ά', + 'ᾼ' => 'ᾼ', + 'ι' => 'ι', + '῁' => '῁', + 'ῂ' => 'ῂ', + 'ῃ' => 'ῃ', + 'ῄ' => 'ῄ', + 'ῆ' => 'ῆ', + 'ῇ' => 'ῇ', + 'Ὲ' => 'Ὲ', + 'Έ' => 'Έ', + 'Ὴ' => 'Ὴ', + 'Ή' => 'Ή', + 'ῌ' => 'ῌ', + '῍' => '῍', + '῎' => '῎', + '῏' => '῏', + 'ῐ' => 'ῐ', + 'ῑ' => 'ῑ', + 'ῒ' => 'ῒ', + 'ΐ' => 'ΐ', + 'ῖ' => 'ῖ', + 'ῗ' => 'ῗ', + 'Ῐ' => 'Ῐ', + 'Ῑ' => 'Ῑ', + 'Ὶ' => 'Ὶ', + 'Ί' => 'Ί', + '῝' => '῝', + '῞' => '῞', + '῟' => '῟', + 'ῠ' => 'ῠ', + 'ῡ' => 'ῡ', + 'ῢ' => 'ῢ', + 'ΰ' => 'ΰ', + 'ῤ' => 'ῤ', + 'ῥ' => 'ῥ', + 'ῦ' => 'ῦ', + 'ῧ' => 'ῧ', + 'Ῠ' => 'Ῠ', + 'Ῡ' => 'Ῡ', + 'Ὺ' => 'Ὺ', + 'Ύ' => 'Ύ', + 'Ῥ' => 'Ῥ', + '῭' => '῭', + '΅' => '΅', + '`' => '`', + 'ῲ' => 'ῲ', + 'ῳ' => 'ῳ', + 'ῴ' => 'ῴ', + 'ῶ' => 'ῶ', + 'ῷ' => 'ῷ', + 'Ὸ' => 'Ὸ', + 'Ό' => 'Ό', + 'Ὼ' => 'Ὼ', + 'Ώ' => 'Ώ', + 'ῼ' => 'ῼ', + '´' => '´', + ' ' => ' ', + ' ' => ' ', + 'Ω' => 'Ω', + '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 100644 index 0000000..ec90f36 --- /dev/null +++ b/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php @@ -0,0 +1,876 @@ + 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 100644 index 0000000..1574902 --- /dev/null +++ b/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php @@ -0,0 +1,3695 @@ + ' ', + '¨' => ' ̈', + 'ª' => '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 100644 index 0000000..3608e5c --- /dev/null +++ b/vendor/symfony/polyfill-intl-normalizer/bootstrap.php @@ -0,0 +1,23 @@ + + * + * 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 (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; +} + +if (!function_exists('normalizer_is_normalized')) { + function normalizer_is_normalized($string, $form = p\Normalizer::FORM_C) { return p\Normalizer::isNormalized($string, $form); } +} +if (!function_exists('normalizer_normalize')) { + function normalizer_normalize($string, $form = p\Normalizer::FORM_C) { return p\Normalizer::normalize($string, $form); } +} diff --git a/vendor/symfony/polyfill-intl-normalizer/bootstrap80.php b/vendor/symfony/polyfill-intl-normalizer/bootstrap80.php new file mode 100644 index 0000000..e36d1a9 --- /dev/null +++ b/vendor/symfony/polyfill-intl-normalizer/bootstrap80.php @@ -0,0 +1,19 @@ + + * + * 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(?string $string, ?int $form = p\Normalizer::FORM_C): bool { return p\Normalizer::isNormalized((string) $string, (int) $form); } +} +if (!function_exists('normalizer_normalize')) { + function normalizer_normalize(?string $string, ?int $form = p\Normalizer::FORM_C): string|false { return p\Normalizer::normalize((string) $string, (int) $form); } +} diff --git a/vendor/symfony/polyfill-intl-normalizer/composer.json b/vendor/symfony/polyfill-intl-normalizer/composer.json new file mode 100644 index 0000000..1b93573 --- /dev/null +++ b/vendor/symfony/polyfill-intl-normalizer/composer.json @@ -0,0 +1,36 @@ +{ + "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": ">=7.1" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "suggest": { + "ext-intl": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "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 100644 index 0000000..6e3afce --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-present 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 100644 index 0000000..2e0b969 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -0,0 +1,947 @@ + + * + * 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_str_split - Convert a string to an array + * - 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 + * + * @internal + */ +final class Mbstring +{ + public const MB_CASE_FOLD = \PHP_INT_MAX; + + private const SIMPLE_CASE_FOLD = [ + ['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"], + ['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'], + ]; + + private static $encodingList = ['ASCII', 'UTF-8']; + private static $language = 'neutral'; + private static $internalEncoding = 'UTF-8'; + + public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) + { + if (\is_array($fromEncoding) || (null !== $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]+/', [__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, &...$vars) + { + $ok = true; + array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { + if (false === $v = self::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) || (80000 > \PHP_VERSION_ID && !$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 self::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) || (80000 > \PHP_VERSION_ID && !$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 = ["\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, [__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) { + static $caseFolding = null; + if (null === $caseFolding) { + $caseFolding = self::getData('caseFolding'); + } + $s = strtr($s, $caseFolding); + } + + static $lower = null; + if (null === $lower) { + $lower = self::getData('lowerCase'); + } + $map = $lower; + } + + static $ulenMask = ["\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; + } + + $normalizedEncoding = self::getEncoding($encoding); + + if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, ' ')) { + self::$internalEncoding = $normalizedEncoding; + + return true; + } + + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError(sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding)); + } + + public static function mb_language($lang = null) + { + if (null === $lang) { + return self::$language; + } + + switch ($normalizedLang = strtolower($lang)) { + case 'uni': + case 'neutral': + self::$language = $normalizedLang; + + return true; + } + + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError(sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang)); + } + + public static function mb_list_encodings() + { + return ['UTF-8']; + } + + public static function mb_encoding_aliases($encoding) + { + switch (strtoupper($encoding)) { + case 'UTF8': + case 'UTF-8': + return ['utf8']; + } + + return false; + } + + public static function mb_check_encoding($var = null, $encoding = null) + { + if (PHP_VERSION_ID < 70200 && \is_array($var)) { + trigger_error('mb_check_encoding() expects parameter 1 to be string, array given', \E_USER_WARNING); + + return null; + } + + if (null === $encoding) { + if (null === $var) { + return false; + } + $encoding = self::$internalEncoding; + } + + if (!\is_array($var)) { + return self::mb_detect_encoding($var, [$encoding]) || false !== @iconv($encoding, $encoding, $var); + } + + foreach ($var as $key => $value) { + if (!self::mb_check_encoding($key, $encoding)) { + return false; + } + if (!self::mb_check_encoding($value, $encoding)) { + return false; + } + } + + return true; + + } + + 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) { + if (80000 > \PHP_VERSION_ID) { + trigger_error(__METHOD__.': Empty delimiter', \E_USER_WARNING); + + return false; + } + + return 0; + } + + 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) { + if (0 > $offset += self::mb_strlen($needle)) { + $haystack = self::mb_substr($haystack, 0, $offset, $encoding); + } + $offset = 0; + } else { + $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); + } + } + + $pos = '' !== $needle || 80000 > \PHP_VERSION_ID + ? iconv_strrpos($haystack, $needle, $encoding) + : self::mb_strlen($haystack, $encoding); + + return false !== $pos ? $offset + $pos : false; + } + + public static function mb_str_split($string, $split_length = 1, $encoding = null) + { + if (null !== $string && !\is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) { + trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING); + + return null; + } + + if (1 > $split_length = (int) $split_length) { + if (80000 > \PHP_VERSION_ID) { + trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING); + + return false; + } + + throw new \ValueError('Argument #2 ($length) must be greater than 0'); + } + + if (null === $encoding) { + $encoding = mb_internal_encoding(); + } + + if ('UTF-8' === $encoding = self::getEncoding($encoding)) { + $rx = '/('; + while (65535 < $split_length) { + $rx .= '.{65535}'; + $split_length -= 65535; + } + $rx .= '.{'.$split_length.'})/us'; + + return preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); + } + + $result = []; + $length = mb_strlen($string, $encoding); + + for ($i = 0; $i < $length; $i += $split_length) { + $result[] = mb_substr($string, $i, $split_length, $encoding); + } + + return $result; + } + + 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 (null === $c) { + return 'none'; + } + if (0 === strcasecmp($c, 'none')) { + return true; + } + if (80000 > \PHP_VERSION_ID) { + return false; + } + if (\is_int($c) || 'long' === $c || 'entity' === $c) { + return false; + } + + throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint'); + } + + public static function mb_substr($s, $start, $length = null, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return (string) 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, $needle] = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], [ + self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding), + self::mb_convert_case($needle, \MB_CASE_LOWER, $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) { + $pos = strrpos($haystack, $needle); + } else { + $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, \MB_CASE_LOWER, $encoding); + $needle = self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding); + + $haystack = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $haystack); + $needle = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $needle); + + 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 = [ + '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; + } + + public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, string $encoding = null): string + { + if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) { + throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH'); + } + + if (null === $encoding) { + $encoding = self::mb_internal_encoding(); + } + + try { + $validEncoding = @self::mb_check_encoding('', $encoding); + } catch (\ValueError $e) { + throw new \ValueError(sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding)); + } + + // BC for PHP 7.3 and lower + if (!$validEncoding) { + throw new \ValueError(sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding)); + } + + if (self::mb_strlen($pad_string, $encoding) <= 0) { + throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string'); + } + + $paddingRequired = $length - self::mb_strlen($string, $encoding); + + if ($paddingRequired < 1) { + return $string; + } + + switch ($pad_type) { + case \STR_PAD_LEFT: + return self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding).$string; + case \STR_PAD_RIGHT: + return $string.self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding); + default: + $leftPaddingLength = floor($paddingRequired / 2); + $rightPaddingLength = $paddingRequired - $leftPaddingLength; + + return self::mb_substr(str_repeat($pad_string, $leftPaddingLength), 0, $leftPaddingLength, $encoding).$string.self::mb_substr(str_repeat($pad_string, $rightPaddingLength), 0, $rightPaddingLength, $encoding); + } + } + + 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; + } + + if ('UTF-8' === $encoding) { + return 'UTF-8'; + } + + $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 100644 index 0000000..478b40d --- /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](https://php.net/mbstring) extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php new file mode 100644 index 0000000..512bba0 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php @@ -0,0 +1,119 @@ + 'i̇', + 'µ' => 'μ', + 'ſ' => 's', + 'ͅ' => 'ι', + 'ς' => 'σ', + 'ϐ' => 'β', + 'ϑ' => 'θ', + 'ϕ' => 'φ', + 'ϖ' => 'π', + 'ϰ' => 'κ', + 'ϱ' => 'ρ', + 'ϵ' => 'ε', + 'ẛ' => 'ṡ', + 'ι' => 'ι', + 'ß' => 'ss', + 'ʼn' => 'ʼn', + 'ǰ' => 'ǰ', + 'ΐ' => 'ΐ', + 'ΰ' => 'ΰ', + 'և' => 'եւ', + 'ẖ' => 'ẖ', + 'ẗ' => 'ẗ', + 'ẘ' => 'ẘ', + 'ẙ' => 'ẙ', + 'ẚ' => 'aʾ', + 'ẞ' => 'ss', + 'ὐ' => 'ὐ', + 'ὒ' => 'ὒ', + 'ὔ' => 'ὔ', + 'ὖ' => 'ὖ', + 'ᾀ' => 'ἀι', + 'ᾁ' => 'ἁι', + 'ᾂ' => 'ἂι', + 'ᾃ' => 'ἃι', + 'ᾄ' => 'ἄι', + 'ᾅ' => 'ἅι', + 'ᾆ' => 'ἆι', + 'ᾇ' => 'ἇι', + 'ᾈ' => 'ἀι', + 'ᾉ' => 'ἁι', + 'ᾊ' => 'ἂι', + 'ᾋ' => 'ἃι', + 'ᾌ' => 'ἄι', + 'ᾍ' => 'ἅι', + 'ᾎ' => 'ἆι', + 'ᾏ' => 'ἇι', + 'ᾐ' => 'ἠι', + 'ᾑ' => 'ἡι', + 'ᾒ' => 'ἢι', + 'ᾓ' => 'ἣι', + 'ᾔ' => 'ἤι', + 'ᾕ' => 'ἥι', + 'ᾖ' => 'ἦι', + 'ᾗ' => 'ἧι', + 'ᾘ' => 'ἠι', + 'ᾙ' => 'ἡι', + 'ᾚ' => 'ἢι', + 'ᾛ' => 'ἣι', + 'ᾜ' => 'ἤι', + 'ᾝ' => 'ἥι', + 'ᾞ' => 'ἦι', + 'ᾟ' => 'ἧι', + 'ᾠ' => 'ὠι', + 'ᾡ' => 'ὡι', + 'ᾢ' => 'ὢι', + 'ᾣ' => 'ὣι', + 'ᾤ' => 'ὤι', + 'ᾥ' => 'ὥι', + 'ᾦ' => 'ὦι', + 'ᾧ' => 'ὧι', + 'ᾨ' => 'ὠι', + 'ᾩ' => 'ὡι', + 'ᾪ' => 'ὢι', + 'ᾫ' => 'ὣι', + 'ᾬ' => 'ὤι', + 'ᾭ' => 'ὥι', + 'ᾮ' => 'ὦι', + 'ᾯ' => 'ὧι', + 'ᾲ' => 'ὰι', + 'ᾳ' => 'αι', + 'ᾴ' => 'άι', + 'ᾶ' => 'ᾶ', + 'ᾷ' => 'ᾶι', + 'ᾼ' => 'αι', + 'ῂ' => 'ὴι', + 'ῃ' => 'ηι', + 'ῄ' => 'ήι', + 'ῆ' => 'ῆ', + 'ῇ' => 'ῆι', + 'ῌ' => 'ηι', + 'ῒ' => 'ῒ', + 'ῖ' => 'ῖ', + 'ῗ' => 'ῗ', + 'ῢ' => 'ῢ', + 'ῤ' => 'ῤ', + 'ῦ' => 'ῦ', + 'ῧ' => 'ῧ', + 'ῲ' => 'ὼι', + 'ῳ' => 'ωι', + 'ῴ' => 'ώι', + 'ῶ' => 'ῶ', + 'ῷ' => 'ῶι', + 'ῼ' => 'ωι', + 'ff' => 'ff', + 'fi' => 'fi', + 'fl' => 'fl', + 'ffi' => 'ffi', + 'ffl' => 'ffl', + 'ſt' => 'st', + 'st' => 'st', + 'ﬓ' => 'մն', + 'ﬔ' => 'մե', + 'ﬕ' => 'մի', + 'ﬖ' => 'վն', + 'ﬗ' => 'մխ', +]; diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php new file mode 100644 index 0000000..fac60b0 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php @@ -0,0 +1,1397 @@ + '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 100644 index 0000000..2a8f6e7 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php @@ -0,0 +1,5 @@ + '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', + '𐐨' => '𐐀', + '𐐩' => '𐐁', + '𐐪' => '𐐂', + '𐐫' => '𐐃', + '𐐬' => '𐐄', + '𐐭' => '𐐅', + '𐐮' => '𐐆', + '𐐯' => '𐐇', + '𐐰' => '𐐈', + '𐐱' => '𐐉', + '𐐲' => '𐐊', + '𐐳' => '𐐋', + '𐐴' => '𐐌', + '𐐵' => '𐐍', + '𐐶' => '𐐎', + '𐐷' => '𐐏', + '𐐸' => '𐐐', + '𐐹' => '𐐑', + '𐐺' => '𐐒', + '𐐻' => '𐐓', + '𐐼' => '𐐔', + '𐐽' => '𐐕', + '𐐾' => '𐐖', + '𐐿' => '𐐗', + '𐑀' => '𐐘', + '𐑁' => '𐐙', + '𐑂' => '𐐚', + '𐑃' => '𐐛', + '𐑄' => '𐐜', + '𐑅' => '𐐝', + '𐑆' => '𐐞', + '𐑇' => '𐐟', + '𐑈' => '𐐠', + '𐑉' => '𐐡', + '𐑊' => '𐐢', + '𐑋' => '𐐣', + '𐑌' => '𐐤', + '𐑍' => '𐐥', + '𐑎' => '𐐦', + '𐑏' => '𐐧', + '𐓘' => '𐒰', + '𐓙' => '𐒱', + '𐓚' => '𐒲', + '𐓛' => '𐒳', + '𐓜' => '𐒴', + '𐓝' => '𐒵', + '𐓞' => '𐒶', + '𐓟' => '𐒷', + '𐓠' => '𐒸', + '𐓡' => '𐒹', + '𐓢' => '𐒺', + '𐓣' => '𐒻', + '𐓤' => '𐒼', + '𐓥' => '𐒽', + '𐓦' => '𐒾', + '𐓧' => '𐒿', + '𐓨' => '𐓀', + '𐓩' => '𐓁', + '𐓪' => '𐓂', + '𐓫' => '𐓃', + '𐓬' => '𐓄', + '𐓭' => '𐓅', + '𐓮' => '𐓆', + '𐓯' => '𐓇', + '𐓰' => '𐓈', + '𐓱' => '𐓉', + '𐓲' => '𐓊', + '𐓳' => '𐓋', + '𐓴' => '𐓌', + '𐓵' => '𐓍', + '𐓶' => '𐓎', + '𐓷' => '𐓏', + '𐓸' => '𐓐', + '𐓹' => '𐓑', + '𐓺' => '𐓒', + '𐓻' => '𐓓', + '𐳀' => '𐲀', + '𐳁' => '𐲁', + '𐳂' => '𐲂', + '𐳃' => '𐲃', + '𐳄' => '𐲄', + '𐳅' => '𐲅', + '𐳆' => '𐲆', + '𐳇' => '𐲇', + '𐳈' => '𐲈', + '𐳉' => '𐲉', + '𐳊' => '𐲊', + '𐳋' => '𐲋', + '𐳌' => '𐲌', + '𐳍' => '𐲍', + '𐳎' => '𐲎', + '𐳏' => '𐲏', + '𐳐' => '𐲐', + '𐳑' => '𐲑', + '𐳒' => '𐲒', + '𐳓' => '𐲓', + '𐳔' => '𐲔', + '𐳕' => '𐲕', + '𐳖' => '𐲖', + '𐳗' => '𐲗', + '𐳘' => '𐲘', + '𐳙' => '𐲙', + '𐳚' => '𐲚', + '𐳛' => '𐲛', + '𐳜' => '𐲜', + '𐳝' => '𐲝', + '𐳞' => '𐲞', + '𐳟' => '𐲟', + '𐳠' => '𐲠', + '𐳡' => '𐲡', + '𐳢' => '𐲢', + '𐳣' => '𐲣', + '𐳤' => '𐲤', + '𐳥' => '𐲥', + '𐳦' => '𐲦', + '𐳧' => '𐲧', + '𐳨' => '𐲨', + '𐳩' => '𐲩', + '𐳪' => '𐲪', + '𐳫' => '𐲫', + '𐳬' => '𐲬', + '𐳭' => '𐲭', + '𐳮' => '𐲮', + '𐳯' => '𐲯', + '𐳰' => '𐲰', + '𐳱' => '𐲱', + '𐳲' => '𐲲', + '𑣀' => '𑢠', + '𑣁' => '𑢡', + '𑣂' => '𑢢', + '𑣃' => '𑢣', + '𑣄' => '𑢤', + '𑣅' => '𑢥', + '𑣆' => '𑢦', + '𑣇' => '𑢧', + '𑣈' => '𑢨', + '𑣉' => '𑢩', + '𑣊' => '𑢪', + '𑣋' => '𑢫', + '𑣌' => '𑢬', + '𑣍' => '𑢭', + '𑣎' => '𑢮', + '𑣏' => '𑢯', + '𑣐' => '𑢰', + '𑣑' => '𑢱', + '𑣒' => '𑢲', + '𑣓' => '𑢳', + '𑣔' => '𑢴', + '𑣕' => '𑢵', + '𑣖' => '𑢶', + '𑣗' => '𑢷', + '𑣘' => '𑢸', + '𑣙' => '𑢹', + '𑣚' => '𑢺', + '𑣛' => '𑢻', + '𑣜' => '𑢼', + '𑣝' => '𑢽', + '𑣞' => '𑢾', + '𑣟' => '𑢿', + '𖹠' => '𖹀', + '𖹡' => '𖹁', + '𖹢' => '𖹂', + '𖹣' => '𖹃', + '𖹤' => '𖹄', + '𖹥' => '𖹅', + '𖹦' => '𖹆', + '𖹧' => '𖹇', + '𖹨' => '𖹈', + '𖹩' => '𖹉', + '𖹪' => '𖹊', + '𖹫' => '𖹋', + '𖹬' => '𖹌', + '𖹭' => '𖹍', + '𖹮' => '𖹎', + '𖹯' => '𖹏', + '𖹰' => '𖹐', + '𖹱' => '𖹑', + '𖹲' => '𖹒', + '𖹳' => '𖹓', + '𖹴' => '𖹔', + '𖹵' => '𖹕', + '𖹶' => '𖹖', + '𖹷' => '𖹗', + '𖹸' => '𖹘', + '𖹹' => '𖹙', + '𖹺' => '𖹚', + '𖹻' => '𖹛', + '𖹼' => '𖹜', + '𖹽' => '𖹝', + '𖹾' => '𖹞', + '𖹿' => '𖹟', + '𞤢' => '𞤀', + '𞤣' => '𞤁', + '𞤤' => '𞤂', + '𞤥' => '𞤃', + '𞤦' => '𞤄', + '𞤧' => '𞤅', + '𞤨' => '𞤆', + '𞤩' => '𞤇', + '𞤪' => '𞤈', + '𞤫' => '𞤉', + '𞤬' => '𞤊', + '𞤭' => '𞤋', + '𞤮' => '𞤌', + '𞤯' => '𞤍', + '𞤰' => '𞤎', + '𞤱' => '𞤏', + '𞤲' => '𞤐', + '𞤳' => '𞤑', + '𞤴' => '𞤒', + '𞤵' => '𞤓', + '𞤶' => '𞤔', + '𞤷' => '𞤕', + '𞤸' => '𞤖', + '𞤹' => '𞤗', + '𞤺' => '𞤘', + '𞤻' => '𞤙', + '𞤼' => '𞤚', + '𞤽' => '𞤛', + '𞤾' => '𞤜', + '𞤿' => '𞤝', + '𞥀' => '𞤞', + '𞥁' => '𞤟', + '𞥂' => '𞤠', + '𞥃' => '𞤡', + 'ß' => 'SS', + 'ff' => 'FF', + 'fi' => 'FI', + 'fl' => 'FL', + 'ffi' => 'FFI', + 'ffl' => 'FFL', + 'ſt' => 'ST', + 'st' => 'ST', + 'և' => 'ԵՒ', + 'ﬓ' => 'ՄՆ', + 'ﬔ' => 'ՄԵ', + 'ﬕ' => 'ՄԻ', + 'ﬖ' => 'ՎՆ', + 'ﬗ' => 'ՄԽ', + 'ʼn' => 'ʼN', + 'ΐ' => 'Ϊ́', + 'ΰ' => 'Ϋ́', + 'ǰ' => 'J̌', + 'ẖ' => 'H̱', + 'ẗ' => 'T̈', + 'ẘ' => 'W̊', + 'ẙ' => 'Y̊', + 'ẚ' => 'Aʾ', + 'ὐ' => 'Υ̓', + 'ὒ' => 'Υ̓̀', + 'ὔ' => 'Υ̓́', + 'ὖ' => 'Υ̓͂', + 'ᾶ' => 'Α͂', + 'ῆ' => 'Η͂', + 'ῒ' => 'Ϊ̀', + 'ΐ' => 'Ϊ́', + 'ῖ' => 'Ι͂', + 'ῗ' => 'Ϊ͂', + 'ῢ' => 'Ϋ̀', + 'ΰ' => 'Ϋ́', + 'ῤ' => 'Ρ̓', + 'ῦ' => 'Υ͂', + 'ῧ' => 'Ϋ͂', + 'ῶ' => 'Ω͂', + 'ᾈ' => 'ἈΙ', + 'ᾉ' => 'ἉΙ', + 'ᾊ' => 'ἊΙ', + 'ᾋ' => 'ἋΙ', + 'ᾌ' => 'ἌΙ', + 'ᾍ' => 'ἍΙ', + 'ᾎ' => 'ἎΙ', + 'ᾏ' => 'ἏΙ', + 'ᾘ' => 'ἨΙ', + 'ᾙ' => 'ἩΙ', + 'ᾚ' => 'ἪΙ', + 'ᾛ' => 'ἫΙ', + 'ᾜ' => 'ἬΙ', + 'ᾝ' => 'ἭΙ', + 'ᾞ' => 'ἮΙ', + 'ᾟ' => 'ἯΙ', + 'ᾨ' => 'ὨΙ', + 'ᾩ' => 'ὩΙ', + 'ᾪ' => 'ὪΙ', + 'ᾫ' => 'ὫΙ', + 'ᾬ' => 'ὬΙ', + 'ᾭ' => 'ὭΙ', + 'ᾮ' => 'ὮΙ', + 'ᾯ' => 'ὯΙ', + 'ᾼ' => 'ΑΙ', + 'ῌ' => 'ΗΙ', + 'ῼ' => 'ΩΙ', + 'ᾲ' => 'ᾺΙ', + 'ᾴ' => 'ΆΙ', + 'ῂ' => 'ῊΙ', + 'ῄ' => 'ΉΙ', + 'ῲ' => 'ῺΙ', + 'ῴ' => 'ΏΙ', + 'ᾷ' => 'Α͂Ι', + 'ῇ' => 'Η͂Ι', + 'ῷ' => 'Ω͂Ι', +); diff --git a/vendor/symfony/polyfill-mbstring/bootstrap.php b/vendor/symfony/polyfill-mbstring/bootstrap.php new file mode 100644 index 0000000..ecf1a03 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/bootstrap.php @@ -0,0 +1,151 @@ + + * + * 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 (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; +} + +if (!function_exists('mb_convert_encoding')) { + function mb_convert_encoding($string, $to_encoding, $from_encoding = null) { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); } +} +if (!function_exists('mb_decode_mimeheader')) { + function mb_decode_mimeheader($string) { return p\Mbstring::mb_decode_mimeheader($string); } +} +if (!function_exists('mb_encode_mimeheader')) { + function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); } +} +if (!function_exists('mb_decode_numericentity')) { + function mb_decode_numericentity($string, $map, $encoding = null) { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); } +} +if (!function_exists('mb_encode_numericentity')) { + function mb_encode_numericentity($string, $map, $encoding = null, $hex = false) { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); } +} +if (!function_exists('mb_convert_case')) { + function mb_convert_case($string, $mode, $encoding = null) { return p\Mbstring::mb_convert_case($string, $mode, $encoding); } +} +if (!function_exists('mb_internal_encoding')) { + function mb_internal_encoding($encoding = null) { return p\Mbstring::mb_internal_encoding($encoding); } +} +if (!function_exists('mb_language')) { + function mb_language($language = null) { return p\Mbstring::mb_language($language); } +} +if (!function_exists('mb_list_encodings')) { + function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } +} +if (!function_exists('mb_encoding_aliases')) { + function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } +} +if (!function_exists('mb_check_encoding')) { + function mb_check_encoding($value = null, $encoding = null) { return p\Mbstring::mb_check_encoding($value, $encoding); } +} +if (!function_exists('mb_detect_encoding')) { + function mb_detect_encoding($string, $encodings = null, $strict = false) { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); } +} +if (!function_exists('mb_detect_order')) { + function mb_detect_order($encoding = null) { return p\Mbstring::mb_detect_order($encoding); } +} +if (!function_exists('mb_parse_str')) { + function mb_parse_str($string, &$result = []) { parse_str($string, $result); return (bool) $result; } +} +if (!function_exists('mb_strlen')) { + function mb_strlen($string, $encoding = null) { return p\Mbstring::mb_strlen($string, $encoding); } +} +if (!function_exists('mb_strpos')) { + function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strtolower')) { + function mb_strtolower($string, $encoding = null) { return p\Mbstring::mb_strtolower($string, $encoding); } +} +if (!function_exists('mb_strtoupper')) { + function mb_strtoupper($string, $encoding = null) { return p\Mbstring::mb_strtoupper($string, $encoding); } +} +if (!function_exists('mb_substitute_character')) { + function mb_substitute_character($substitute_character = null) { return p\Mbstring::mb_substitute_character($substitute_character); } +} +if (!function_exists('mb_substr')) { + function mb_substr($string, $start, $length = 2147483647, $encoding = null) { return p\Mbstring::mb_substr($string, $start, $length, $encoding); } +} +if (!function_exists('mb_stripos')) { + function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_stristr')) { + function mb_stristr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strrchr')) { + function mb_strrchr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strrichr')) { + function mb_strrichr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strripos')) { + function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strrpos')) { + function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strstr')) { + function mb_strstr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_get_info')) { + function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } +} +if (!function_exists('mb_http_output')) { + function mb_http_output($encoding = null) { return p\Mbstring::mb_http_output($encoding); } +} +if (!function_exists('mb_strwidth')) { + function mb_strwidth($string, $encoding = null) { return p\Mbstring::mb_strwidth($string, $encoding); } +} +if (!function_exists('mb_substr_count')) { + function mb_substr_count($haystack, $needle, $encoding = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); } +} +if (!function_exists('mb_output_handler')) { + function mb_output_handler($string, $status) { return p\Mbstring::mb_output_handler($string, $status); } +} +if (!function_exists('mb_http_input')) { + function mb_http_input($type = null) { return p\Mbstring::mb_http_input($type); } +} + +if (!function_exists('mb_convert_variables')) { + function mb_convert_variables($to_encoding, $from_encoding, &...$vars) { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); } +} + +if (!function_exists('mb_ord')) { + function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); } +} +if (!function_exists('mb_chr')) { + function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); } +} +if (!function_exists('mb_scrub')) { + function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); } +} +if (!function_exists('mb_str_split')) { + function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); } +} + +if (!function_exists('mb_str_pad')) { + function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); } +} + +if (extension_loaded('mbstring')) { + return; +} + +if (!defined('MB_CASE_UPPER')) { + define('MB_CASE_UPPER', 0); +} +if (!defined('MB_CASE_LOWER')) { + define('MB_CASE_LOWER', 1); +} +if (!defined('MB_CASE_TITLE')) { + define('MB_CASE_TITLE', 2); +} diff --git a/vendor/symfony/polyfill-mbstring/bootstrap80.php b/vendor/symfony/polyfill-mbstring/bootstrap80.php new file mode 100644 index 0000000..2f9fb5b --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/bootstrap80.php @@ -0,0 +1,147 @@ + + * + * 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_convert_encoding')) { + function mb_convert_encoding(array|string|null $string, ?string $to_encoding, array|string|null $from_encoding = null): array|string|false { return p\Mbstring::mb_convert_encoding($string ?? '', (string) $to_encoding, $from_encoding); } +} +if (!function_exists('mb_decode_mimeheader')) { + function mb_decode_mimeheader(?string $string): string { return p\Mbstring::mb_decode_mimeheader((string) $string); } +} +if (!function_exists('mb_encode_mimeheader')) { + function mb_encode_mimeheader(?string $string, ?string $charset = null, ?string $transfer_encoding = null, ?string $newline = "\r\n", ?int $indent = 0): string { return p\Mbstring::mb_encode_mimeheader((string) $string, $charset, $transfer_encoding, (string) $newline, (int) $indent); } +} +if (!function_exists('mb_decode_numericentity')) { + function mb_decode_numericentity(?string $string, array $map, ?string $encoding = null): string { return p\Mbstring::mb_decode_numericentity((string) $string, $map, $encoding); } +} +if (!function_exists('mb_encode_numericentity')) { + function mb_encode_numericentity(?string $string, array $map, ?string $encoding = null, ?bool $hex = false): string { return p\Mbstring::mb_encode_numericentity((string) $string, $map, $encoding, (bool) $hex); } +} +if (!function_exists('mb_convert_case')) { + function mb_convert_case(?string $string, ?int $mode, ?string $encoding = null): string { return p\Mbstring::mb_convert_case((string) $string, (int) $mode, $encoding); } +} +if (!function_exists('mb_internal_encoding')) { + function mb_internal_encoding(?string $encoding = null): string|bool { return p\Mbstring::mb_internal_encoding($encoding); } +} +if (!function_exists('mb_language')) { + function mb_language(?string $language = null): string|bool { return p\Mbstring::mb_language($language); } +} +if (!function_exists('mb_list_encodings')) { + function mb_list_encodings(): array { return p\Mbstring::mb_list_encodings(); } +} +if (!function_exists('mb_encoding_aliases')) { + function mb_encoding_aliases(?string $encoding): array { return p\Mbstring::mb_encoding_aliases((string) $encoding); } +} +if (!function_exists('mb_check_encoding')) { + function mb_check_encoding(array|string|null $value = null, ?string $encoding = null): bool { return p\Mbstring::mb_check_encoding($value, $encoding); } +} +if (!function_exists('mb_detect_encoding')) { + function mb_detect_encoding(?string $string, array|string|null $encodings = null, ?bool $strict = false): string|false { return p\Mbstring::mb_detect_encoding((string) $string, $encodings, (bool) $strict); } +} +if (!function_exists('mb_detect_order')) { + function mb_detect_order(array|string|null $encoding = null): array|bool { return p\Mbstring::mb_detect_order($encoding); } +} +if (!function_exists('mb_parse_str')) { + function mb_parse_str(?string $string, &$result = []): bool { parse_str((string) $string, $result); return (bool) $result; } +} +if (!function_exists('mb_strlen')) { + function mb_strlen(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strlen((string) $string, $encoding); } +} +if (!function_exists('mb_strpos')) { + function mb_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); } +} +if (!function_exists('mb_strtolower')) { + function mb_strtolower(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtolower((string) $string, $encoding); } +} +if (!function_exists('mb_strtoupper')) { + function mb_strtoupper(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtoupper((string) $string, $encoding); } +} +if (!function_exists('mb_substitute_character')) { + function mb_substitute_character(string|int|null $substitute_character = null): string|int|bool { return p\Mbstring::mb_substitute_character($substitute_character); } +} +if (!function_exists('mb_substr')) { + function mb_substr(?string $string, ?int $start, ?int $length = null, ?string $encoding = null): string { return p\Mbstring::mb_substr((string) $string, (int) $start, $length, $encoding); } +} +if (!function_exists('mb_stripos')) { + function mb_stripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_stripos((string) $haystack, (string) $needle, (int) $offset, $encoding); } +} +if (!function_exists('mb_stristr')) { + function mb_stristr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_stristr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } +} +if (!function_exists('mb_strrchr')) { + function mb_strrchr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrchr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } +} +if (!function_exists('mb_strrichr')) { + function mb_strrichr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrichr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } +} +if (!function_exists('mb_strripos')) { + function mb_strripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strripos((string) $haystack, (string) $needle, (int) $offset, $encoding); } +} +if (!function_exists('mb_strrpos')) { + function mb_strrpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strrpos((string) $haystack, (string) $needle, (int) $offset, $encoding); } +} +if (!function_exists('mb_strstr')) { + function mb_strstr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strstr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } +} +if (!function_exists('mb_get_info')) { + function mb_get_info(?string $type = 'all'): array|string|int|false { return p\Mbstring::mb_get_info((string) $type); } +} +if (!function_exists('mb_http_output')) { + function mb_http_output(?string $encoding = null): string|bool { return p\Mbstring::mb_http_output($encoding); } +} +if (!function_exists('mb_strwidth')) { + function mb_strwidth(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strwidth((string) $string, $encoding); } +} +if (!function_exists('mb_substr_count')) { + function mb_substr_count(?string $haystack, ?string $needle, ?string $encoding = null): int { return p\Mbstring::mb_substr_count((string) $haystack, (string) $needle, $encoding); } +} +if (!function_exists('mb_output_handler')) { + function mb_output_handler(?string $string, ?int $status): string { return p\Mbstring::mb_output_handler((string) $string, (int) $status); } +} +if (!function_exists('mb_http_input')) { + function mb_http_input(?string $type = null): array|string|false { return p\Mbstring::mb_http_input($type); } +} + +if (!function_exists('mb_convert_variables')) { + function mb_convert_variables(?string $to_encoding, array|string|null $from_encoding, mixed &$var, mixed &...$vars): string|false { return p\Mbstring::mb_convert_variables((string) $to_encoding, $from_encoding ?? '', $var, ...$vars); } +} + +if (!function_exists('mb_ord')) { + function mb_ord(?string $string, ?string $encoding = null): int|false { return p\Mbstring::mb_ord((string) $string, $encoding); } +} +if (!function_exists('mb_chr')) { + function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); } +} +if (!function_exists('mb_scrub')) { + function mb_scrub(?string $string, ?string $encoding = null): string { $encoding ??= mb_internal_encoding(); return mb_convert_encoding((string) $string, $encoding, $encoding); } +} +if (!function_exists('mb_str_split')) { + function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); } +} + +if (!function_exists('mb_str_pad')) { + function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); } +} + +if (extension_loaded('mbstring')) { + return; +} + +if (!defined('MB_CASE_UPPER')) { + define('MB_CASE_UPPER', 0); +} +if (!defined('MB_CASE_LOWER')) { + define('MB_CASE_LOWER', 1); +} +if (!defined('MB_CASE_TITLE')) { + define('MB_CASE_TITLE', 2); +} diff --git a/vendor/symfony/polyfill-mbstring/composer.json b/vendor/symfony/polyfill-mbstring/composer.json new file mode 100644 index 0000000..bd99d4b --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/composer.json @@ -0,0 +1,38 @@ +{ + "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": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/vendor/symfony/polyfill-php80/LICENSE b/vendor/symfony/polyfill-php80/LICENSE new file mode 100644 index 0000000..0ed3a24 --- /dev/null +++ b/vendor/symfony/polyfill-php80/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020-present 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-php80/Php80.php b/vendor/symfony/polyfill-php80/Php80.php new file mode 100644 index 0000000..362dd1a --- /dev/null +++ b/vendor/symfony/polyfill-php80/Php80.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php80; + +/** + * @author Ion Bazan + * @author Nico Oelgart + * @author Nicolas Grekas + * + * @internal + */ +final class Php80 +{ + public static function fdiv(float $dividend, float $divisor): float + { + return @($dividend / $divisor); + } + + public static function get_debug_type($value): string + { + switch (true) { + case null === $value: return 'null'; + case \is_bool($value): return 'bool'; + case \is_string($value): return 'string'; + case \is_array($value): return 'array'; + case \is_int($value): return 'int'; + case \is_float($value): return 'float'; + case \is_object($value): break; + case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class'; + default: + if (null === $type = @get_resource_type($value)) { + return 'unknown'; + } + + if ('Unknown' === $type) { + $type = 'closed'; + } + + return "resource ($type)"; + } + + $class = \get_class($value); + + if (false === strpos($class, '@')) { + return $class; + } + + return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous'; + } + + public static function get_resource_id($res): int + { + if (!\is_resource($res) && null === @get_resource_type($res)) { + throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res))); + } + + return (int) $res; + } + + public static function preg_last_error_msg(): string + { + switch (preg_last_error()) { + case \PREG_INTERNAL_ERROR: + return 'Internal error'; + case \PREG_BAD_UTF8_ERROR: + return 'Malformed UTF-8 characters, possibly incorrectly encoded'; + case \PREG_BAD_UTF8_OFFSET_ERROR: + return 'The offset did not correspond to the beginning of a valid UTF-8 code point'; + case \PREG_BACKTRACK_LIMIT_ERROR: + return 'Backtrack limit exhausted'; + case \PREG_RECURSION_LIMIT_ERROR: + return 'Recursion limit exhausted'; + case \PREG_JIT_STACKLIMIT_ERROR: + return 'JIT stack limit exhausted'; + case \PREG_NO_ERROR: + return 'No error'; + default: + return 'Unknown error'; + } + } + + public static function str_contains(string $haystack, string $needle): bool + { + return '' === $needle || false !== strpos($haystack, $needle); + } + + public static function str_starts_with(string $haystack, string $needle): bool + { + return 0 === strncmp($haystack, $needle, \strlen($needle)); + } + + public static function str_ends_with(string $haystack, string $needle): bool + { + if ('' === $needle || $needle === $haystack) { + return true; + } + + if ('' === $haystack) { + return false; + } + + $needleLength = \strlen($needle); + + return $needleLength <= \strlen($haystack) && 0 === substr_compare($haystack, $needle, -$needleLength); + } +} diff --git a/vendor/symfony/polyfill-php80/PhpToken.php b/vendor/symfony/polyfill-php80/PhpToken.php new file mode 100644 index 0000000..fe6e691 --- /dev/null +++ b/vendor/symfony/polyfill-php80/PhpToken.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php80; + +/** + * @author Fedonyuk Anton + * + * @internal + */ +class PhpToken implements \Stringable +{ + /** + * @var int + */ + public $id; + + /** + * @var string + */ + public $text; + + /** + * @var int + */ + public $line; + + /** + * @var int + */ + public $pos; + + public function __construct(int $id, string $text, int $line = -1, int $position = -1) + { + $this->id = $id; + $this->text = $text; + $this->line = $line; + $this->pos = $position; + } + + public function getTokenName(): ?string + { + if ('UNKNOWN' === $name = token_name($this->id)) { + $name = \strlen($this->text) > 1 || \ord($this->text) < 32 ? null : $this->text; + } + + return $name; + } + + /** + * @param int|string|array $kind + */ + public function is($kind): bool + { + foreach ((array) $kind as $value) { + if (\in_array($value, [$this->id, $this->text], true)) { + return true; + } + } + + return false; + } + + public function isIgnorable(): bool + { + return \in_array($this->id, [\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT, \T_OPEN_TAG], true); + } + + public function __toString(): string + { + return (string) $this->text; + } + + /** + * @return static[] + */ + public static function tokenize(string $code, int $flags = 0): array + { + $line = 1; + $position = 0; + $tokens = token_get_all($code, $flags); + foreach ($tokens as $index => $token) { + if (\is_string($token)) { + $id = \ord($token); + $text = $token; + } else { + [$id, $text, $line] = $token; + } + $tokens[$index] = new static($id, $text, $line, $position); + $position += \strlen($text); + } + + return $tokens; + } +} diff --git a/vendor/symfony/polyfill-php80/README.md b/vendor/symfony/polyfill-php80/README.md new file mode 100644 index 0000000..3816c55 --- /dev/null +++ b/vendor/symfony/polyfill-php80/README.md @@ -0,0 +1,25 @@ +Symfony Polyfill / Php80 +======================== + +This component provides features added to PHP 8.0 core: + +- [`Stringable`](https://php.net/stringable) interface +- [`fdiv`](https://php.net/fdiv) +- [`ValueError`](https://php.net/valueerror) class +- [`UnhandledMatchError`](https://php.net/unhandledmatcherror) class +- `FILTER_VALIDATE_BOOL` constant +- [`get_debug_type`](https://php.net/get_debug_type) +- [`PhpToken`](https://php.net/phptoken) class +- [`preg_last_error_msg`](https://php.net/preg_last_error_msg) +- [`str_contains`](https://php.net/str_contains) +- [`str_starts_with`](https://php.net/str_starts_with) +- [`str_ends_with`](https://php.net/str_ends_with) +- [`get_resource_id`](https://php.net/get_resource_id) + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php b/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php new file mode 100644 index 0000000..2b95542 --- /dev/null +++ b/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#[Attribute(Attribute::TARGET_CLASS)] +final class Attribute +{ + public const TARGET_CLASS = 1; + public const TARGET_FUNCTION = 2; + public const TARGET_METHOD = 4; + public const TARGET_PROPERTY = 8; + public const TARGET_CLASS_CONSTANT = 16; + public const TARGET_PARAMETER = 32; + public const TARGET_ALL = 63; + public const IS_REPEATABLE = 64; + + /** @var int */ + public $flags; + + public function __construct(int $flags = self::TARGET_ALL) + { + $this->flags = $flags; + } +} diff --git a/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php b/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php new file mode 100644 index 0000000..bd1212f --- /dev/null +++ b/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000 && extension_loaded('tokenizer')) { + class PhpToken extends Symfony\Polyfill\Php80\PhpToken + { + } +} diff --git a/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php b/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php new file mode 100644 index 0000000..7c62d75 --- /dev/null +++ b/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000) { + interface Stringable + { + /** + * @return string + */ + public function __toString(); + } +} diff --git a/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php b/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php new file mode 100644 index 0000000..01c6c6c --- /dev/null +++ b/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000) { + class UnhandledMatchError extends Error + { + } +} diff --git a/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php b/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php new file mode 100644 index 0000000..783dbc2 --- /dev/null +++ b/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000) { + class ValueError extends Error + { + } +} diff --git a/vendor/symfony/polyfill-php80/bootstrap.php b/vendor/symfony/polyfill-php80/bootstrap.php new file mode 100644 index 0000000..e5f7dbc --- /dev/null +++ b/vendor/symfony/polyfill-php80/bootstrap.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php80 as p; + +if (\PHP_VERSION_ID >= 80000) { + return; +} + +if (!defined('FILTER_VALIDATE_BOOL') && defined('FILTER_VALIDATE_BOOLEAN')) { + define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN); +} + +if (!function_exists('fdiv')) { + function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); } +} +if (!function_exists('preg_last_error_msg')) { + function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); } +} +if (!function_exists('str_contains')) { + function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('str_starts_with')) { + function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('str_ends_with')) { + function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('get_debug_type')) { + function get_debug_type($value): string { return p\Php80::get_debug_type($value); } +} +if (!function_exists('get_resource_id')) { + function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); } +} diff --git a/vendor/symfony/polyfill-php80/composer.json b/vendor/symfony/polyfill-php80/composer.json new file mode 100644 index 0000000..46ccde2 --- /dev/null +++ b/vendor/symfony/polyfill-php80/composer.json @@ -0,0 +1,37 @@ +{ + "name": "symfony/polyfill-php80", + "type": "library", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "keywords": ["polyfill", "shim", "compatibility", "portable"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php80\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "minimum-stability": "dev", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/vendor/symfony/polyfill-php81/LICENSE b/vendor/symfony/polyfill-php81/LICENSE new file mode 100644 index 0000000..99c6bdf --- /dev/null +++ b/vendor/symfony/polyfill-php81/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2021-present 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-php81/Php81.php b/vendor/symfony/polyfill-php81/Php81.php new file mode 100644 index 0000000..f0507b7 --- /dev/null +++ b/vendor/symfony/polyfill-php81/Php81.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php81; + +/** + * @author Nicolas Grekas + * + * @internal + */ +final class Php81 +{ + public static function array_is_list(array $array): bool + { + if ([] === $array || $array === array_values($array)) { + return true; + } + + $nextKey = -1; + + foreach ($array as $k => $v) { + if ($k !== ++$nextKey) { + return false; + } + } + + return true; + } +} diff --git a/vendor/symfony/polyfill-php81/README.md b/vendor/symfony/polyfill-php81/README.md new file mode 100644 index 0000000..c07ef78 --- /dev/null +++ b/vendor/symfony/polyfill-php81/README.md @@ -0,0 +1,18 @@ +Symfony Polyfill / Php81 +======================== + +This component provides features added to PHP 8.1 core: + +- [`array_is_list`](https://php.net/array_is_list) +- [`enum_exists`](https://php.net/enum-exists) +- [`MYSQLI_REFRESH_REPLICA`](https://php.net/mysqli.constants#constantmysqli-refresh-replica) constant +- [`ReturnTypeWillChange`](https://wiki.php.net/rfc/internal_method_return_types) +- [`CURLStringFile`](https://php.net/CURLStringFile) (but only if PHP >= 7.4 is used) + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php b/vendor/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php new file mode 100644 index 0000000..5ff93fc --- /dev/null +++ b/vendor/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID >= 70400 && extension_loaded('curl')) { + /** + * @property string $data + */ + class CURLStringFile extends CURLFile + { + private $data; + + public function __construct(string $data, string $postname, string $mime = 'application/octet-stream') + { + $this->data = $data; + parent::__construct('data://application/octet-stream;base64,'.base64_encode($data), $mime, $postname); + } + + public function __set(string $name, $value): void + { + if ('data' !== $name) { + $this->$name = $value; + + return; + } + + if (is_object($value) ? !method_exists($value, '__toString') : !is_scalar($value)) { + throw new TypeError('Cannot assign '.gettype($value).' to property CURLStringFile::$data of type string'); + } + + $this->name = 'data://application/octet-stream;base64,'.base64_encode($value); + } + + public function __isset(string $name): bool + { + return isset($this->$name); + } + + public function &__get(string $name) + { + return $this->$name; + } + } +} diff --git a/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php b/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php new file mode 100644 index 0000000..cb7720a --- /dev/null +++ b/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80100) { + #[Attribute(Attribute::TARGET_METHOD)] + final class ReturnTypeWillChange + { + public function __construct() + { + } + } +} diff --git a/vendor/symfony/polyfill-php81/bootstrap.php b/vendor/symfony/polyfill-php81/bootstrap.php new file mode 100644 index 0000000..9f872e0 --- /dev/null +++ b/vendor/symfony/polyfill-php81/bootstrap.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php81 as p; + +if (\PHP_VERSION_ID >= 80100) { + return; +} + +if (defined('MYSQLI_REFRESH_SLAVE') && !defined('MYSQLI_REFRESH_REPLICA')) { + define('MYSQLI_REFRESH_REPLICA', 64); +} + +if (!function_exists('array_is_list')) { + function array_is_list(array $array): bool { return p\Php81::array_is_list($array); } +} + +if (!function_exists('enum_exists')) { + function enum_exists(string $enum, bool $autoload = true): bool { return $autoload && class_exists($enum) && false; } +} diff --git a/vendor/symfony/polyfill-php81/composer.json b/vendor/symfony/polyfill-php81/composer.json new file mode 100644 index 0000000..381af79 --- /dev/null +++ b/vendor/symfony/polyfill-php81/composer.json @@ -0,0 +1,33 @@ +{ + "name": "symfony/polyfill-php81", + "type": "library", + "description": "Symfony polyfill backporting some PHP 8.1+ 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": ">=7.1" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php81\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "minimum-stability": "dev", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/vendor/symfony/service-contracts/.gitignore b/vendor/symfony/service-contracts/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/service-contracts/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/service-contracts/Attribute/Required.php b/vendor/symfony/service-contracts/Attribute/Required.php new file mode 100644 index 0000000..9df8511 --- /dev/null +++ b/vendor/symfony/service-contracts/Attribute/Required.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service\Attribute; + +/** + * A required dependency. + * + * This attribute indicates that a property holds a required dependency. The annotated property or method should be + * considered during the instantiation process of the containing class. + * + * @author Alexander M. Turek + */ +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY)] +final class Required +{ +} diff --git a/vendor/symfony/service-contracts/Attribute/SubscribedService.php b/vendor/symfony/service-contracts/Attribute/SubscribedService.php new file mode 100644 index 0000000..10d1bc3 --- /dev/null +++ b/vendor/symfony/service-contracts/Attribute/SubscribedService.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service\Attribute; + +use Symfony\Contracts\Service\ServiceSubscriberTrait; + +/** + * Use with {@see ServiceSubscriberTrait} to mark a method's return type + * as a subscribed service. + * + * @author Kevin Bond + */ +#[\Attribute(\Attribute::TARGET_METHOD)] +final class SubscribedService +{ + /** + * @param string|null $key The key to use for the service + * If null, use "ClassName::methodName" + */ + public function __construct( + public ?string $key = null + ) { + } +} diff --git a/vendor/symfony/service-contracts/CHANGELOG.md b/vendor/symfony/service-contracts/CHANGELOG.md new file mode 100644 index 0000000..7932e26 --- /dev/null +++ b/vendor/symfony/service-contracts/CHANGELOG.md @@ -0,0 +1,5 @@ +CHANGELOG +========= + +The changelog is maintained for all Symfony contracts at the following URL: +https://github.com/symfony/contracts/blob/main/CHANGELOG.md diff --git a/vendor/symfony/service-contracts/LICENSE b/vendor/symfony/service-contracts/LICENSE new file mode 100644 index 0000000..74cdc2d --- /dev/null +++ b/vendor/symfony/service-contracts/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-2022 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/service-contracts/README.md b/vendor/symfony/service-contracts/README.md new file mode 100644 index 0000000..41e054a --- /dev/null +++ b/vendor/symfony/service-contracts/README.md @@ -0,0 +1,9 @@ +Symfony Service 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/main/README.md for more information. diff --git a/vendor/symfony/service-contracts/ResetInterface.php b/vendor/symfony/service-contracts/ResetInterface.php new file mode 100644 index 0000000..1af1075 --- /dev/null +++ b/vendor/symfony/service-contracts/ResetInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service; + +/** + * Provides a way to reset an object to its initial state. + * + * When calling the "reset()" method on an object, it should be put back to its + * initial state. This usually means clearing any internal buffers and forwarding + * the call to internal dependencies. All properties of the object should be put + * back to the same state it had when it was first ready to use. + * + * This method could be called, for example, to recycle objects that are used as + * services, so that they can be used to handle several requests in the same + * process loop (note that we advise making your services stateless instead of + * implementing this interface when possible.) + */ +interface ResetInterface +{ + public function reset(); +} diff --git a/vendor/symfony/service-contracts/ServiceLocatorTrait.php b/vendor/symfony/service-contracts/ServiceLocatorTrait.php new file mode 100644 index 0000000..19d3e80 --- /dev/null +++ b/vendor/symfony/service-contracts/ServiceLocatorTrait.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service; + +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; + +// Help opcache.preload discover always-needed symbols +class_exists(ContainerExceptionInterface::class); +class_exists(NotFoundExceptionInterface::class); + +/** + * A trait to help implement ServiceProviderInterface. + * + * @author Robin Chalas + * @author Nicolas Grekas + */ +trait ServiceLocatorTrait +{ + private array $factories; + private array $loading = []; + private array $providedTypes; + + /** + * @param callable[] $factories + */ + public function __construct(array $factories) + { + $this->factories = $factories; + } + + /** + * {@inheritdoc} + */ + public function has(string $id): bool + { + return isset($this->factories[$id]); + } + + /** + * {@inheritdoc} + */ + public function get(string $id): mixed + { + if (!isset($this->factories[$id])) { + throw $this->createNotFoundException($id); + } + + if (isset($this->loading[$id])) { + $ids = array_values($this->loading); + $ids = \array_slice($this->loading, array_search($id, $ids)); + $ids[] = $id; + + throw $this->createCircularReferenceException($id, $ids); + } + + $this->loading[$id] = $id; + try { + return $this->factories[$id]($this); + } finally { + unset($this->loading[$id]); + } + } + + /** + * {@inheritdoc} + */ + public function getProvidedServices(): array + { + if (!isset($this->providedTypes)) { + $this->providedTypes = []; + + foreach ($this->factories as $name => $factory) { + if (!\is_callable($factory)) { + $this->providedTypes[$name] = '?'; + } else { + $type = (new \ReflectionFunction($factory))->getReturnType(); + + $this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').($type instanceof \ReflectionNamedType ? $type->getName() : $type) : '?'; + } + } + } + + return $this->providedTypes; + } + + private function createNotFoundException(string $id): NotFoundExceptionInterface + { + if (!$alternatives = array_keys($this->factories)) { + $message = 'is empty...'; + } else { + $last = array_pop($alternatives); + if ($alternatives) { + $message = sprintf('only knows about the "%s" and "%s" services.', implode('", "', $alternatives), $last); + } else { + $message = sprintf('only knows about the "%s" service.', $last); + } + } + + if ($this->loading) { + $message = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $message); + } else { + $message = sprintf('Service "%s" not found: the current service locator %s', $id, $message); + } + + return new class($message) extends \InvalidArgumentException implements NotFoundExceptionInterface { + }; + } + + private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface + { + return new class(sprintf('Circular reference detected for service "%s", path: "%s".', $id, implode(' -> ', $path))) extends \RuntimeException implements ContainerExceptionInterface { + }; + } +} diff --git a/vendor/symfony/service-contracts/ServiceProviderInterface.php b/vendor/symfony/service-contracts/ServiceProviderInterface.php new file mode 100644 index 0000000..c60ad0b --- /dev/null +++ b/vendor/symfony/service-contracts/ServiceProviderInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service; + +use Psr\Container\ContainerInterface; + +/** + * A ServiceProviderInterface exposes the identifiers and the types of services provided by a container. + * + * @author Nicolas Grekas + * @author Mateusz Sip + */ +interface ServiceProviderInterface extends ContainerInterface +{ + /** + * Returns an associative array of service types keyed by the identifiers provided by the current container. + * + * Examples: + * + * * ['logger' => 'Psr\Log\LoggerInterface'] means the object provides a service named "logger" that implements Psr\Log\LoggerInterface + * * ['foo' => '?'] means the container provides service name "foo" of unspecified type + * * ['bar' => '?Bar\Baz'] means the container provides a service "bar" of type Bar\Baz|null + * + * @return string[] The provided service types, keyed by service names + */ + public function getProvidedServices(): array; +} diff --git a/vendor/symfony/service-contracts/ServiceSubscriberInterface.php b/vendor/symfony/service-contracts/ServiceSubscriberInterface.php new file mode 100644 index 0000000..881ab97 --- /dev/null +++ b/vendor/symfony/service-contracts/ServiceSubscriberInterface.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service; + +/** + * A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method. + * + * The getSubscribedServices method returns an array of service types required by such instances, + * optionally keyed by the service names used internally. Service types that start with an interrogation + * mark "?" are optional, while the other ones are mandatory service dependencies. + * + * The injected service locators SHOULD NOT allow access to any other services not specified by the method. + * + * It is expected that ServiceSubscriber instances consume PSR-11-based service locators internally. + * This interface does not dictate any injection method for these service locators, although constructor + * injection is recommended. + * + * @author Nicolas Grekas + */ +interface ServiceSubscriberInterface +{ + /** + * Returns an array of service types required by such instances, optionally keyed by the service names used internally. + * + * For mandatory dependencies: + * + * * ['logger' => 'Psr\Log\LoggerInterface'] means the objects use the "logger" name + * internally to fetch a service which must implement Psr\Log\LoggerInterface. + * * ['loggers' => 'Psr\Log\LoggerInterface[]'] means the objects use the "loggers" name + * internally to fetch an iterable of Psr\Log\LoggerInterface instances. + * * ['Psr\Log\LoggerInterface'] is a shortcut for + * * ['Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface'] + * + * otherwise: + * + * * ['logger' => '?Psr\Log\LoggerInterface'] denotes an optional dependency + * * ['loggers' => '?Psr\Log\LoggerInterface[]'] denotes an optional iterable dependency + * * ['?Psr\Log\LoggerInterface'] is a shortcut for + * * ['Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface'] + * + * @return string[] The required service types, optionally keyed by service names + */ + public static function getSubscribedServices(): array; +} diff --git a/vendor/symfony/service-contracts/ServiceSubscriberTrait.php b/vendor/symfony/service-contracts/ServiceSubscriberTrait.php new file mode 100644 index 0000000..ee9d9d9 --- /dev/null +++ b/vendor/symfony/service-contracts/ServiceSubscriberTrait.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service; + +use Psr\Container\ContainerInterface; +use Symfony\Contracts\Service\Attribute\SubscribedService; + +/** + * Implementation of ServiceSubscriberInterface that determines subscribed services from + * method return types. Service ids are available as "ClassName::methodName". + * + * @author Kevin Bond + */ +trait ServiceSubscriberTrait +{ + /** @var ContainerInterface */ + protected $container; + + /** + * {@inheritdoc} + */ + public static function getSubscribedServices(): array + { + $services = method_exists(get_parent_class(self::class) ?: '', __FUNCTION__) ? parent::getSubscribedServices() : []; + + foreach ((new \ReflectionClass(self::class))->getMethods() as $method) { + if (self::class !== $method->getDeclaringClass()->name) { + continue; + } + + if (!$attribute = $method->getAttributes(SubscribedService::class)[0] ?? null) { + continue; + } + + if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) { + throw new \LogicException(sprintf('Cannot use "%s" on method "%s::%s()" (can only be used on non-static, non-abstract methods with no parameters).', SubscribedService::class, self::class, $method->name)); + } + + if (!$returnType = $method->getReturnType()) { + throw new \LogicException(sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class)); + } + + $serviceId = $returnType instanceof \ReflectionNamedType ? $returnType->getName() : (string) $returnType; + + if ($returnType->allowsNull()) { + $serviceId = '?'.$serviceId; + } + + $services[$attribute->newInstance()->key ?? self::class.'::'.$method->name] = $serviceId; + } + + return $services; + } + + /** + * @required + */ + public function setContainer(ContainerInterface $container): ?ContainerInterface + { + $this->container = $container; + + if (method_exists(get_parent_class(self::class) ?: '', __FUNCTION__)) { + return parent::setContainer($container); + } + + return null; + } +} diff --git a/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php b/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php new file mode 100644 index 0000000..88f6a06 --- /dev/null +++ b/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service\Test; + +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; +use Symfony\Contracts\Service\ServiceLocatorTrait; + +abstract class ServiceLocatorTest extends TestCase +{ + protected function getServiceLocator(array $factories): ContainerInterface + { + return new class($factories) implements ContainerInterface { + use ServiceLocatorTrait; + }; + } + + public function testHas() + { + $locator = $this->getServiceLocator([ + 'foo' => function () { return 'bar'; }, + 'bar' => function () { return 'baz'; }, + function () { return 'dummy'; }, + ]); + + $this->assertTrue($locator->has('foo')); + $this->assertTrue($locator->has('bar')); + $this->assertFalse($locator->has('dummy')); + } + + public function testGet() + { + $locator = $this->getServiceLocator([ + 'foo' => function () { return 'bar'; }, + 'bar' => function () { return 'baz'; }, + ]); + + $this->assertSame('bar', $locator->get('foo')); + $this->assertSame('baz', $locator->get('bar')); + } + + public function testGetDoesNotMemoize() + { + $i = 0; + $locator = $this->getServiceLocator([ + 'foo' => function () use (&$i) { + ++$i; + + return 'bar'; + }, + ]); + + $this->assertSame('bar', $locator->get('foo')); + $this->assertSame('bar', $locator->get('foo')); + $this->assertSame(2, $i); + } + + public function testThrowsOnUndefinedInternalService() + { + if (!$this->getExpectedException()) { + $this->expectException(\Psr\Container\NotFoundExceptionInterface::class); + $this->expectExceptionMessage('The service "foo" has a dependency on a non-existent service "bar". This locator only knows about the "foo" service.'); + } + $locator = $this->getServiceLocator([ + 'foo' => function () use (&$locator) { return $locator->get('bar'); }, + ]); + + $locator->get('foo'); + } + + public function testThrowsOnCircularReference() + { + $this->expectException(\Psr\Container\ContainerExceptionInterface::class); + $this->expectExceptionMessage('Circular reference detected for service "bar", path: "bar -> baz -> bar".'); + $locator = $this->getServiceLocator([ + 'foo' => function () use (&$locator) { return $locator->get('bar'); }, + 'bar' => function () use (&$locator) { return $locator->get('baz'); }, + 'baz' => function () use (&$locator) { return $locator->get('bar'); }, + ]); + + $locator->get('foo'); + } +} diff --git a/vendor/symfony/service-contracts/composer.json b/vendor/symfony/service-contracts/composer.json new file mode 100644 index 0000000..d3b047f --- /dev/null +++ b/vendor/symfony/service-contracts/composer.json @@ -0,0 +1,41 @@ +{ + "name": "symfony/service-contracts", + "type": "library", + "description": "Generic abstractions related to writing services", + "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": ">=8.0.2", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "autoload": { + "psr-4": { "Symfony\\Contracts\\Service\\": "" } + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + } +} diff --git a/vendor/symfony/string/AbstractString.php b/vendor/symfony/string/AbstractString.php new file mode 100644 index 0000000..cf96a83 --- /dev/null +++ b/vendor/symfony/string/AbstractString.php @@ -0,0 +1,716 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +use Symfony\Component\String\Exception\ExceptionInterface; +use Symfony\Component\String\Exception\InvalidArgumentException; +use Symfony\Component\String\Exception\RuntimeException; + +/** + * Represents a string of abstract characters. + * + * Unicode defines 3 types of "characters" (bytes, code points and grapheme clusters). + * This class is the abstract type to use as a type-hint when the logic you want to + * implement doesn't care about the exact variant it deals with. + * + * @author Nicolas Grekas + * @author Hugo Hamon + * + * @throws ExceptionInterface + */ +abstract class AbstractString implements \Stringable, \JsonSerializable +{ + public const PREG_PATTERN_ORDER = \PREG_PATTERN_ORDER; + public const PREG_SET_ORDER = \PREG_SET_ORDER; + public const PREG_OFFSET_CAPTURE = \PREG_OFFSET_CAPTURE; + public const PREG_UNMATCHED_AS_NULL = \PREG_UNMATCHED_AS_NULL; + + public const PREG_SPLIT = 0; + public const PREG_SPLIT_NO_EMPTY = \PREG_SPLIT_NO_EMPTY; + public const PREG_SPLIT_DELIM_CAPTURE = \PREG_SPLIT_DELIM_CAPTURE; + public const PREG_SPLIT_OFFSET_CAPTURE = \PREG_SPLIT_OFFSET_CAPTURE; + + protected $string = ''; + protected $ignoreCase = false; + + abstract public function __construct(string $string = ''); + + /** + * Unwraps instances of AbstractString back to strings. + * + * @return string[]|array + */ + public static function unwrap(array $values): array + { + foreach ($values as $k => $v) { + if ($v instanceof self) { + $values[$k] = $v->__toString(); + } elseif (\is_array($v) && $values[$k] !== $v = static::unwrap($v)) { + $values[$k] = $v; + } + } + + return $values; + } + + /** + * Wraps (and normalizes) strings in instances of AbstractString. + * + * @return static[]|array + */ + public static function wrap(array $values): array + { + $i = 0; + $keys = null; + + foreach ($values as $k => $v) { + if (\is_string($k) && '' !== $k && $k !== $j = (string) new static($k)) { + $keys = $keys ?? array_keys($values); + $keys[$i] = $j; + } + + if (\is_string($v)) { + $values[$k] = new static($v); + } elseif (\is_array($v) && $values[$k] !== $v = static::wrap($v)) { + $values[$k] = $v; + } + + ++$i; + } + + return null !== $keys ? array_combine($keys, $values) : $values; + } + + /** + * @param string|string[] $needle + */ + public function after(string|iterable $needle, bool $includeNeedle = false, int $offset = 0): static + { + $str = clone $this; + $i = \PHP_INT_MAX; + + if (\is_string($needle)) { + $needle = [$needle]; + } + + foreach ($needle as $n) { + $n = (string) $n; + $j = $this->indexOf($n, $offset); + + if (null !== $j && $j < $i) { + $i = $j; + $str->string = $n; + } + } + + if (\PHP_INT_MAX === $i) { + return $str; + } + + if (!$includeNeedle) { + $i += $str->length(); + } + + return $this->slice($i); + } + + /** + * @param string|string[] $needle + */ + public function afterLast(string|iterable $needle, bool $includeNeedle = false, int $offset = 0): static + { + $str = clone $this; + $i = null; + + if (\is_string($needle)) { + $needle = [$needle]; + } + + foreach ($needle as $n) { + $n = (string) $n; + $j = $this->indexOfLast($n, $offset); + + if (null !== $j && $j >= $i) { + $i = $offset = $j; + $str->string = $n; + } + } + + if (null === $i) { + return $str; + } + + if (!$includeNeedle) { + $i += $str->length(); + } + + return $this->slice($i); + } + + abstract public function append(string ...$suffix): static; + + /** + * @param string|string[] $needle + */ + public function before(string|iterable $needle, bool $includeNeedle = false, int $offset = 0): static + { + $str = clone $this; + $i = \PHP_INT_MAX; + + if (\is_string($needle)) { + $needle = [$needle]; + } + + foreach ($needle as $n) { + $n = (string) $n; + $j = $this->indexOf($n, $offset); + + if (null !== $j && $j < $i) { + $i = $j; + $str->string = $n; + } + } + + if (\PHP_INT_MAX === $i) { + return $str; + } + + if ($includeNeedle) { + $i += $str->length(); + } + + return $this->slice(0, $i); + } + + /** + * @param string|string[] $needle + */ + public function beforeLast(string|iterable $needle, bool $includeNeedle = false, int $offset = 0): static + { + $str = clone $this; + $i = null; + + if (\is_string($needle)) { + $needle = [$needle]; + } + + foreach ($needle as $n) { + $n = (string) $n; + $j = $this->indexOfLast($n, $offset); + + if (null !== $j && $j >= $i) { + $i = $offset = $j; + $str->string = $n; + } + } + + if (null === $i) { + return $str; + } + + if ($includeNeedle) { + $i += $str->length(); + } + + return $this->slice(0, $i); + } + + /** + * @return int[] + */ + public function bytesAt(int $offset): array + { + $str = $this->slice($offset, 1); + + return '' === $str->string ? [] : array_values(unpack('C*', $str->string)); + } + + abstract public function camel(): static; + + /** + * @return static[] + */ + abstract public function chunk(int $length = 1): array; + + public function collapseWhitespace(): static + { + $str = clone $this; + $str->string = trim(preg_replace("/(?:[ \n\r\t\x0C]{2,}+|[\n\r\t\x0C])/", ' ', $str->string), " \n\r\t\x0C"); + + return $str; + } + + /** + * @param string|string[] $needle + */ + public function containsAny(string|iterable $needle): bool + { + return null !== $this->indexOf($needle); + } + + /** + * @param string|string[] $suffix + */ + public function endsWith(string|iterable $suffix): bool + { + if (\is_string($suffix)) { + throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); + } + + foreach ($suffix as $s) { + if ($this->endsWith((string) $s)) { + return true; + } + } + + return false; + } + + public function ensureEnd(string $suffix): static + { + if (!$this->endsWith($suffix)) { + return $this->append($suffix); + } + + $suffix = preg_quote($suffix); + $regex = '{('.$suffix.')(?:'.$suffix.')++$}D'; + + return $this->replaceMatches($regex.($this->ignoreCase ? 'i' : ''), '$1'); + } + + public function ensureStart(string $prefix): static + { + $prefix = new static($prefix); + + if (!$this->startsWith($prefix)) { + return $this->prepend($prefix); + } + + $str = clone $this; + $i = $prefixLen = $prefix->length(); + + while ($this->indexOf($prefix, $i) === $i) { + $str = $str->slice($prefixLen); + $i += $prefixLen; + } + + return $str; + } + + /** + * @param string|string[] $string + */ + public function equalsTo(string|iterable $string): bool + { + if (\is_string($string)) { + throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); + } + + foreach ($string as $s) { + if ($this->equalsTo((string) $s)) { + return true; + } + } + + return false; + } + + abstract public function folded(): static; + + public function ignoreCase(): static + { + $str = clone $this; + $str->ignoreCase = true; + + return $str; + } + + /** + * @param string|string[] $needle + */ + public function indexOf(string|iterable $needle, int $offset = 0): ?int + { + if (\is_string($needle)) { + throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); + } + + $i = \PHP_INT_MAX; + + foreach ($needle as $n) { + $j = $this->indexOf((string) $n, $offset); + + if (null !== $j && $j < $i) { + $i = $j; + } + } + + return \PHP_INT_MAX === $i ? null : $i; + } + + /** + * @param string|string[] $needle + */ + public function indexOfLast(string|iterable $needle, int $offset = 0): ?int + { + if (\is_string($needle)) { + throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); + } + + $i = null; + + foreach ($needle as $n) { + $j = $this->indexOfLast((string) $n, $offset); + + if (null !== $j && $j >= $i) { + $i = $offset = $j; + } + } + + return $i; + } + + public function isEmpty(): bool + { + return '' === $this->string; + } + + abstract public function join(array $strings, string $lastGlue = null): static; + + public function jsonSerialize(): string + { + return $this->string; + } + + abstract public function length(): int; + + abstract public function lower(): static; + + /** + * Matches the string using a regular expression. + * + * Pass PREG_PATTERN_ORDER or PREG_SET_ORDER as $flags to get all occurrences matching the regular expression. + * + * @return array All matches in a multi-dimensional array ordered according to flags + */ + abstract public function match(string $regexp, int $flags = 0, int $offset = 0): array; + + abstract public function padBoth(int $length, string $padStr = ' '): static; + + abstract public function padEnd(int $length, string $padStr = ' '): static; + + abstract public function padStart(int $length, string $padStr = ' '): static; + + abstract public function prepend(string ...$prefix): static; + + public function repeat(int $multiplier): static + { + if (0 > $multiplier) { + throw new InvalidArgumentException(sprintf('Multiplier must be positive, %d given.', $multiplier)); + } + + $str = clone $this; + $str->string = str_repeat($str->string, $multiplier); + + return $str; + } + + abstract public function replace(string $from, string $to): static; + + abstract public function replaceMatches(string $fromRegexp, string|callable $to): static; + + abstract public function reverse(): static; + + abstract public function slice(int $start = 0, int $length = null): static; + + abstract public function snake(): static; + + abstract public function splice(string $replacement, int $start = 0, int $length = null): static; + + /** + * @return static[] + */ + public function split(string $delimiter, int $limit = null, int $flags = null): array + { + if (null === $flags) { + throw new \TypeError('Split behavior when $flags is null must be implemented by child classes.'); + } + + if ($this->ignoreCase) { + $delimiter .= 'i'; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + if (false === $chunks = preg_split($delimiter, $this->string, $limit, $flags)) { + $lastError = preg_last_error(); + + foreach (get_defined_constants(true)['pcre'] as $k => $v) { + if ($lastError === $v && '_ERROR' === substr($k, -6)) { + throw new RuntimeException('Splitting failed with '.$k.'.'); + } + } + + throw new RuntimeException('Splitting failed with unknown error code.'); + } + } finally { + restore_error_handler(); + } + + $str = clone $this; + + if (self::PREG_SPLIT_OFFSET_CAPTURE & $flags) { + foreach ($chunks as &$chunk) { + $str->string = $chunk[0]; + $chunk[0] = clone $str; + } + } else { + foreach ($chunks as &$chunk) { + $str->string = $chunk; + $chunk = clone $str; + } + } + + return $chunks; + } + + /** + * @param string|string[] $prefix + */ + public function startsWith(string|iterable $prefix): bool + { + if (\is_string($prefix)) { + throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); + } + + foreach ($prefix as $prefix) { + if ($this->startsWith((string) $prefix)) { + return true; + } + } + + return false; + } + + abstract public function title(bool $allWords = false): static; + + public function toByteString(string $toEncoding = null): ByteString + { + $b = new ByteString(); + + $toEncoding = \in_array($toEncoding, ['utf8', 'utf-8', 'UTF8'], true) ? 'UTF-8' : $toEncoding; + + if (null === $toEncoding || $toEncoding === $fromEncoding = $this instanceof AbstractUnicodeString || preg_match('//u', $b->string) ? 'UTF-8' : 'Windows-1252') { + $b->string = $this->string; + + return $b; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + try { + $b->string = mb_convert_encoding($this->string, $toEncoding, 'UTF-8'); + } catch (InvalidArgumentException $e) { + if (!\function_exists('iconv')) { + throw $e; + } + + $b->string = iconv('UTF-8', $toEncoding, $this->string); + } + } finally { + restore_error_handler(); + } + + return $b; + } + + public function toCodePointString(): CodePointString + { + return new CodePointString($this->string); + } + + public function toString(): string + { + return $this->string; + } + + public function toUnicodeString(): UnicodeString + { + return new UnicodeString($this->string); + } + + abstract public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static; + + abstract public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static; + + /** + * @param string|string[] $prefix + */ + public function trimPrefix($prefix): static + { + if (\is_array($prefix) || $prefix instanceof \Traversable) { + foreach ($prefix as $s) { + $t = $this->trimPrefix($s); + + if ($t->string !== $this->string) { + return $t; + } + } + + return clone $this; + } + + $str = clone $this; + + if ($prefix instanceof self) { + $prefix = $prefix->string; + } else { + $prefix = (string) $prefix; + } + + if ('' !== $prefix && \strlen($this->string) >= \strlen($prefix) && 0 === substr_compare($this->string, $prefix, 0, \strlen($prefix), $this->ignoreCase)) { + $str->string = substr($this->string, \strlen($prefix)); + } + + return $str; + } + + abstract public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static; + + /** + * @param string|string[] $suffix + */ + public function trimSuffix($suffix): static + { + if (\is_array($suffix) || $suffix instanceof \Traversable) { + foreach ($suffix as $s) { + $t = $this->trimSuffix($s); + + if ($t->string !== $this->string) { + return $t; + } + } + + return clone $this; + } + + $str = clone $this; + + if ($suffix instanceof self) { + $suffix = $suffix->string; + } else { + $suffix = (string) $suffix; + } + + if ('' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase)) { + $str->string = substr($this->string, 0, -\strlen($suffix)); + } + + return $str; + } + + public function truncate(int $length, string $ellipsis = '', bool $cut = true): static + { + $stringLength = $this->length(); + + if ($stringLength <= $length) { + return clone $this; + } + + $ellipsisLength = '' !== $ellipsis ? (new static($ellipsis))->length() : 0; + + if ($length < $ellipsisLength) { + $ellipsisLength = 0; + } + + if (!$cut) { + if (null === $length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1)) { + return clone $this; + } + + $length += $ellipsisLength; + } + + $str = $this->slice(0, $length - $ellipsisLength); + + return $ellipsisLength ? $str->trimEnd()->append($ellipsis) : $str; + } + + abstract public function upper(): static; + + /** + * Returns the printable length on a terminal. + */ + abstract public function width(bool $ignoreAnsiDecoration = true): int; + + public function wordwrap(int $width = 75, string $break = "\n", bool $cut = false): static + { + $lines = '' !== $break ? $this->split($break) : [clone $this]; + $chars = []; + $mask = ''; + + if (1 === \count($lines) && '' === $lines[0]->string) { + return $lines[0]; + } + + foreach ($lines as $i => $line) { + if ($i) { + $chars[] = $break; + $mask .= '#'; + } + + foreach ($line->chunk() as $char) { + $chars[] = $char->string; + $mask .= ' ' === $char->string ? ' ' : '?'; + } + } + + $string = ''; + $j = 0; + $b = $i = -1; + $mask = wordwrap($mask, $width, '#', $cut); + + while (false !== $b = strpos($mask, '#', $b + 1)) { + for (++$i; $i < $b; ++$i) { + $string .= $chars[$j]; + unset($chars[$j++]); + } + + if ($break === $chars[$j] || ' ' === $chars[$j]) { + unset($chars[$j++]); + } + + $string .= $break; + } + + $str = clone $this; + $str->string = $string.implode('', $chars); + + return $str; + } + + public function __sleep(): array + { + return ['string']; + } + + public function __clone() + { + $this->ignoreCase = false; + } + + public function __toString(): string + { + return $this->string; + } +} diff --git a/vendor/symfony/string/AbstractUnicodeString.php b/vendor/symfony/string/AbstractUnicodeString.php new file mode 100644 index 0000000..00096df --- /dev/null +++ b/vendor/symfony/string/AbstractUnicodeString.php @@ -0,0 +1,606 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +use Symfony\Component\String\Exception\ExceptionInterface; +use Symfony\Component\String\Exception\InvalidArgumentException; +use Symfony\Component\String\Exception\RuntimeException; + +/** + * Represents a string of abstract Unicode characters. + * + * Unicode defines 3 types of "characters" (bytes, code points and grapheme clusters). + * This class is the abstract type to use as a type-hint when the logic you want to + * implement is Unicode-aware but doesn't care about code points vs grapheme clusters. + * + * @author Nicolas Grekas + * + * @throws ExceptionInterface + */ +abstract class AbstractUnicodeString extends AbstractString +{ + public const NFC = \Normalizer::NFC; + public const NFD = \Normalizer::NFD; + public const NFKC = \Normalizer::NFKC; + public const NFKD = \Normalizer::NFKD; + + // all ASCII letters sorted by typical frequency of occurrence + private const 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"; + + // the subset of folded case mappings that is not in lower case mappings + private const FOLD_FROM = ['İ', 'µ', 'ſ', "\xCD\x85", 'ς', 'ϐ', 'ϑ', 'ϕ', 'ϖ', 'ϰ', 'ϱ', 'ϵ', 'ẛ', "\xE1\xBE\xBE", 'ß', 'İ', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'և', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ẞ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'ᾐ', 'ᾑ', 'ᾒ', 'ᾓ', 'ᾔ', 'ᾕ', 'ᾖ', 'ᾗ', 'ᾘ', 'ᾙ', 'ᾚ', 'ᾛ', 'ᾜ', 'ᾝ', 'ᾞ', 'ᾟ', 'ᾠ', 'ᾡ', 'ᾢ', 'ᾣ', 'ᾤ', 'ᾥ', 'ᾦ', 'ᾧ', 'ᾨ', 'ᾩ', 'ᾪ', 'ᾫ', 'ᾬ', 'ᾭ', 'ᾮ', 'ᾯ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'ᾼ', 'ῂ', 'ῃ', 'ῄ', 'ῆ', 'ῇ', 'ῌ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ῲ', 'ῳ', 'ῴ', 'ῶ', 'ῷ', 'ῼ', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'ſt', 'st', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ']; + private const FOLD_TO = ['i̇', 'μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', 'ṡ', 'ι', 'ss', 'i̇', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'եւ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'aʾ', 'ss', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὰι', 'αι', 'άι', 'ᾶ', 'ᾶι', 'αι', 'ὴι', 'ηι', 'ήι', 'ῆ', 'ῆι', 'ηι', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ὼι', 'ωι', 'ώι', 'ῶ', 'ῶι', 'ωι', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'st', 'st', 'մն', 'մե', 'մի', 'վն', 'մխ']; + + // the subset of upper case mappings that map one code point to many code points + private const UPPER_FROM = ['ß', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'ſt', 'st', 'և', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ', 'ʼn', 'ΐ', 'ΰ', 'ǰ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾶ', 'ῆ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ῶ']; + private const UPPER_TO = ['SS', 'FF', 'FI', 'FL', 'FFI', 'FFL', 'ST', 'ST', 'ԵՒ', 'ՄՆ', 'ՄԵ', 'ՄԻ', 'ՎՆ', 'ՄԽ', 'ʼN', 'Ϊ́', 'Ϋ́', 'J̌', 'H̱', 'T̈', 'W̊', 'Y̊', 'Aʾ', 'Υ̓', 'Υ̓̀', 'Υ̓́', 'Υ̓͂', 'Α͂', 'Η͂', 'Ϊ̀', 'Ϊ́', 'Ι͂', 'Ϊ͂', 'Ϋ̀', 'Ϋ́', 'Ρ̓', 'Υ͂', 'Ϋ͂', 'Ω͂']; + + // the subset of https://github.com/unicode-org/cldr/blob/master/common/transforms/Latin-ASCII.xml that is not in NFKD + private const TRANSLIT_FROM = ['Æ', 'Ð', 'Ø', 'Þ', 'ß', 'æ', 'ð', 'ø', 'þ', 'Đ', 'đ', 'Ħ', 'ħ', 'ı', 'ĸ', 'Ŀ', 'ŀ', 'Ł', 'ł', 'ʼn', 'Ŋ', 'ŋ', 'Œ', 'œ', 'Ŧ', 'ŧ', 'ƀ', 'Ɓ', 'Ƃ', 'ƃ', 'Ƈ', 'ƈ', 'Ɖ', 'Ɗ', 'Ƌ', 'ƌ', 'Ɛ', 'Ƒ', 'ƒ', 'Ɠ', 'ƕ', 'Ɩ', 'Ɨ', 'Ƙ', 'ƙ', 'ƚ', 'Ɲ', 'ƞ', 'Ƣ', 'ƣ', 'Ƥ', 'ƥ', 'ƫ', 'Ƭ', 'ƭ', 'Ʈ', 'Ʋ', 'Ƴ', 'ƴ', 'Ƶ', 'ƶ', 'DŽ', 'Dž', 'dž', 'Ǥ', 'ǥ', 'ȡ', 'Ȥ', 'ȥ', 'ȴ', 'ȵ', 'ȶ', 'ȷ', 'ȸ', 'ȹ', 'Ⱥ', 'Ȼ', 'ȼ', 'Ƚ', 'Ⱦ', 'ȿ', 'ɀ', 'Ƀ', 'Ʉ', 'Ɇ', 'ɇ', 'Ɉ', 'ɉ', 'Ɍ', 'ɍ', 'Ɏ', 'ɏ', 'ɓ', 'ɕ', 'ɖ', 'ɗ', 'ɛ', 'ɟ', 'ɠ', 'ɡ', 'ɢ', 'ɦ', 'ɧ', 'ɨ', 'ɪ', 'ɫ', 'ɬ', 'ɭ', 'ɱ', 'ɲ', 'ɳ', 'ɴ', 'ɶ', 'ɼ', 'ɽ', 'ɾ', 'ʀ', 'ʂ', 'ʈ', 'ʉ', 'ʋ', 'ʏ', 'ʐ', 'ʑ', 'ʙ', 'ʛ', 'ʜ', 'ʝ', 'ʟ', 'ʠ', 'ʣ', 'ʥ', 'ʦ', 'ʪ', 'ʫ', 'ᴀ', 'ᴁ', 'ᴃ', 'ᴄ', 'ᴅ', 'ᴆ', 'ᴇ', 'ᴊ', 'ᴋ', 'ᴌ', 'ᴍ', 'ᴏ', 'ᴘ', 'ᴛ', 'ᴜ', 'ᴠ', 'ᴡ', 'ᴢ', 'ᵫ', 'ᵬ', 'ᵭ', 'ᵮ', 'ᵯ', 'ᵰ', 'ᵱ', 'ᵲ', 'ᵳ', 'ᵴ', 'ᵵ', 'ᵶ', 'ᵺ', 'ᵻ', 'ᵽ', 'ᵾ', 'ᶀ', 'ᶁ', 'ᶂ', 'ᶃ', 'ᶄ', 'ᶅ', 'ᶆ', 'ᶇ', 'ᶈ', 'ᶉ', 'ᶊ', 'ᶌ', 'ᶍ', 'ᶎ', 'ᶏ', 'ᶑ', 'ᶒ', 'ᶓ', 'ᶖ', 'ᶙ', 'ẚ', 'ẜ', 'ẝ', 'ẞ', 'Ỻ', 'ỻ', 'Ỽ', 'ỽ', 'Ỿ', 'ỿ', '©', '®', '₠', '₢', '₣', '₤', '₧', '₺', '₹', 'ℌ', '℞', '㎧', '㎮', '㏆', '㏗', '㏞', '㏟', '¼', '½', '¾', '⅓', '⅔', '⅕', '⅖', '⅗', '⅘', '⅙', '⅚', '⅛', '⅜', '⅝', '⅞', '⅟', '〇', '‘', '’', '‚', '‛', '“', '”', '„', '‟', '′', '″', '〝', '〞', '«', '»', '‹', '›', '‐', '‑', '‒', '–', '—', '―', '︱', '︲', '﹘', '‖', '⁄', '⁅', '⁆', '⁎', '、', '。', '〈', '〉', '《', '》', '〔', '〕', '〘', '〙', '〚', '〛', '︑', '︒', '︹', '︺', '︽', '︾', '︿', '﹀', '﹑', '﹝', '﹞', '⦅', '⦆', '。', '、', '×', '÷', '−', '∕', '∖', '∣', '∥', '≪', '≫', '⦅', '⦆']; + private const TRANSLIT_TO = ['AE', 'D', 'O', 'TH', 'ss', 'ae', 'd', 'o', 'th', 'D', 'd', 'H', 'h', 'i', 'q', 'L', 'l', 'L', 'l', '\'n', 'N', 'n', 'OE', 'oe', 'T', 't', 'b', 'B', 'B', 'b', 'C', 'c', 'D', 'D', 'D', 'd', 'E', 'F', 'f', 'G', 'hv', 'I', 'I', 'K', 'k', 'l', 'N', 'n', 'OI', 'oi', 'P', 'p', 't', 'T', 't', 'T', 'V', 'Y', 'y', 'Z', 'z', 'DZ', 'Dz', 'dz', 'G', 'g', 'd', 'Z', 'z', 'l', 'n', 't', 'j', 'db', 'qp', 'A', 'C', 'c', 'L', 'T', 's', 'z', 'B', 'U', 'E', 'e', 'J', 'j', 'R', 'r', 'Y', 'y', 'b', 'c', 'd', 'd', 'e', 'j', 'g', 'g', 'G', 'h', 'h', 'i', 'I', 'l', 'l', 'l', 'm', 'n', 'n', 'N', 'OE', 'r', 'r', 'r', 'R', 's', 't', 'u', 'v', 'Y', 'z', 'z', 'B', 'G', 'H', 'j', 'L', 'q', 'dz', 'dz', 'ts', 'ls', 'lz', 'A', 'AE', 'B', 'C', 'D', 'D', 'E', 'J', 'K', 'L', 'M', 'O', 'P', 'T', 'U', 'V', 'W', 'Z', 'ue', 'b', 'd', 'f', 'm', 'n', 'p', 'r', 'r', 's', 't', 'z', 'th', 'I', 'p', 'U', 'b', 'd', 'f', 'g', 'k', 'l', 'm', 'n', 'p', 'r', 's', 'v', 'x', 'z', 'a', 'd', 'e', 'e', 'i', 'u', 'a', 's', 's', 'SS', 'LL', 'll', 'V', 'v', 'Y', 'y', '(C)', '(R)', 'CE', 'Cr', 'Fr.', 'L.', 'Pts', 'TL', 'Rs', 'x', 'Rx', 'm/s', 'rad/s', 'C/kg', 'pH', 'V/m', 'A/m', ' 1/4', ' 1/2', ' 3/4', ' 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/', '0', '\'', '\'', ',', '\'', '"', '"', ',,', '"', '\'', '"', '"', '"', '<<', '>>', '<', '>', '-', '-', '-', '-', '-', '-', '-', '-', '-', '||', '/', '[', ']', '*', ',', '.', '<', '>', '<<', '>>', '[', ']', '[', ']', '[', ']', ',', '.', '[', ']', '<<', '>>', '<', '>', ',', '[', ']', '((', '))', '.', ',', '*', '/', '-', '/', '\\', '|', '||', '<<', '>>', '((', '))']; + + private static $transliterators = []; + private static $tableZero; + private static $tableWide; + + public static function fromCodePoints(int ...$codes): static + { + $string = ''; + + foreach ($codes as $code) { + if (0x80 > $code %= 0x200000) { + $string .= \chr($code); + } elseif (0x800 > $code) { + $string .= \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); + } elseif (0x10000 > $code) { + $string .= \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); + } else { + $string .= \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); + } + } + + return new static($string); + } + + /** + * Generic UTF-8 to ASCII transliteration. + * + * Install the intl extension for best results. + * + * @param string[]|\Transliterator[]|\Closure[] $rules See "*-Latin" rules from Transliterator::listIDs() + */ + public function ascii(array $rules = []): self + { + $str = clone $this; + $s = $str->string; + $str->string = ''; + + array_unshift($rules, 'nfd'); + $rules[] = 'latin-ascii'; + + if (\function_exists('transliterator_transliterate')) { + $rules[] = 'any-latin/bgn'; + } + + $rules[] = 'nfkd'; + $rules[] = '[:nonspacing mark:] remove'; + + while (\strlen($s) - 1 > $i = strspn($s, self::ASCII)) { + if (0 < --$i) { + $str->string .= substr($s, 0, $i); + $s = substr($s, $i); + } + + if (!$rule = array_shift($rules)) { + $rules = []; // An empty rule interrupts the next ones + } + + if ($rule instanceof \Transliterator) { + $s = $rule->transliterate($s); + } elseif ($rule instanceof \Closure) { + $s = $rule($s); + } elseif ($rule) { + if ('nfd' === $rule = strtolower($rule)) { + normalizer_is_normalized($s, self::NFD) ?: $s = normalizer_normalize($s, self::NFD); + } elseif ('nfkd' === $rule) { + normalizer_is_normalized($s, self::NFKD) ?: $s = normalizer_normalize($s, self::NFKD); + } elseif ('[:nonspacing mark:] remove' === $rule) { + $s = preg_replace('/\p{Mn}++/u', '', $s); + } elseif ('latin-ascii' === $rule) { + $s = str_replace(self::TRANSLIT_FROM, self::TRANSLIT_TO, $s); + } elseif ('de-ascii' === $rule) { + $s = preg_replace("/([AUO])\u{0308}(?=\p{Ll})/u", '$1e', $s); + $s = str_replace(["a\u{0308}", "o\u{0308}", "u\u{0308}", "A\u{0308}", "O\u{0308}", "U\u{0308}"], ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'], $s); + } elseif (\function_exists('transliterator_transliterate')) { + if (null === $transliterator = self::$transliterators[$rule] ?? self::$transliterators[$rule] = \Transliterator::create($rule)) { + if ('any-latin/bgn' === $rule) { + $rule = 'any-latin'; + $transliterator = self::$transliterators[$rule] ?? self::$transliterators[$rule] = \Transliterator::create($rule); + } + + if (null === $transliterator) { + throw new InvalidArgumentException(sprintf('Unknown transliteration rule "%s".', $rule)); + } + + self::$transliterators['any-latin/bgn'] = $transliterator; + } + + $s = $transliterator->transliterate($s); + } + } elseif (!\function_exists('iconv')) { + $s = preg_replace('/[^\x00-\x7F]/u', '?', $s); + } else { + $s = @preg_replace_callback('/[^\x00-\x7F]/u', static function ($c) { + $c = (string) iconv('UTF-8', 'ASCII//TRANSLIT', $c[0]); + + if ('' === $c && '' === iconv('UTF-8', 'ASCII//TRANSLIT', '²')) { + throw new \LogicException(sprintf('"%s" requires a translit-able iconv implementation, try installing "gnu-libiconv" if you\'re using Alpine Linux.', static::class)); + } + + return 1 < \strlen($c) ? ltrim($c, '\'`"^~') : ('' !== $c ? $c : '?'); + }, $s); + } + } + + $str->string .= $s; + + return $str; + } + + public function camel(): static + { + $str = clone $this; + $str->string = str_replace(' ', '', preg_replace_callback('/\b.(?![A-Z]{2,})/u', static function ($m) use (&$i) { + return 1 === ++$i ? ('İ' === $m[0] ? 'i̇' : mb_strtolower($m[0], 'UTF-8')) : mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8'); + }, preg_replace('/[^\pL0-9]++/u', ' ', $this->string))); + + return $str; + } + + /** + * @return int[] + */ + public function codePointsAt(int $offset): array + { + $str = $this->slice($offset, 1); + + if ('' === $str->string) { + return []; + } + + $codePoints = []; + + foreach (preg_split('//u', $str->string, -1, \PREG_SPLIT_NO_EMPTY) as $c) { + $codePoints[] = mb_ord($c, 'UTF-8'); + } + + return $codePoints; + } + + public function folded(bool $compat = true): static + { + $str = clone $this; + + if (!$compat || !\defined('Normalizer::NFKC_CF')) { + $str->string = normalizer_normalize($str->string, $compat ? \Normalizer::NFKC : \Normalizer::NFC); + $str->string = mb_strtolower(str_replace(self::FOLD_FROM, self::FOLD_TO, $this->string), 'UTF-8'); + } else { + $str->string = normalizer_normalize($str->string, \Normalizer::NFKC_CF); + } + + return $str; + } + + public function join(array $strings, string $lastGlue = null): static + { + $str = clone $this; + + $tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : ''; + $str->string = implode($this->string, $strings).$tail; + + if (!preg_match('//u', $str->string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + return $str; + } + + public function lower(): static + { + $str = clone $this; + $str->string = mb_strtolower(str_replace('İ', 'i̇', $str->string), 'UTF-8'); + + return $str; + } + + public function match(string $regexp, int $flags = 0, int $offset = 0): array + { + $match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match'; + + if ($this->ignoreCase) { + $regexp .= 'i'; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + if (false === $match($regexp.'u', $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { + $lastError = preg_last_error(); + + foreach (get_defined_constants(true)['pcre'] as $k => $v) { + if ($lastError === $v && '_ERROR' === substr($k, -6)) { + throw new RuntimeException('Matching failed with '.$k.'.'); + } + } + + throw new RuntimeException('Matching failed with unknown error code.'); + } + } finally { + restore_error_handler(); + } + + return $matches; + } + + public function normalize(int $form = self::NFC): static + { + if (!\in_array($form, [self::NFC, self::NFD, self::NFKC, self::NFKD])) { + throw new InvalidArgumentException('Unsupported normalization form.'); + } + + $str = clone $this; + normalizer_is_normalized($str->string, $form) ?: $str->string = normalizer_normalize($str->string, $form); + + return $str; + } + + public function padBoth(int $length, string $padStr = ' '): static + { + if ('' === $padStr || !preg_match('//u', $padStr)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $pad = clone $this; + $pad->string = $padStr; + + return $this->pad($length, $pad, \STR_PAD_BOTH); + } + + public function padEnd(int $length, string $padStr = ' '): static + { + if ('' === $padStr || !preg_match('//u', $padStr)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $pad = clone $this; + $pad->string = $padStr; + + return $this->pad($length, $pad, \STR_PAD_RIGHT); + } + + public function padStart(int $length, string $padStr = ' '): static + { + if ('' === $padStr || !preg_match('//u', $padStr)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $pad = clone $this; + $pad->string = $padStr; + + return $this->pad($length, $pad, \STR_PAD_LEFT); + } + + public function replaceMatches(string $fromRegexp, string|callable $to): static + { + if ($this->ignoreCase) { + $fromRegexp .= 'i'; + } + + if (\is_array($to) || $to instanceof \Closure) { + $replace = 'preg_replace_callback'; + $to = static function (array $m) use ($to): string { + $to = $to($m); + + if ('' !== $to && (!\is_string($to) || !preg_match('//u', $to))) { + throw new InvalidArgumentException('Replace callback must return a valid UTF-8 string.'); + } + + return $to; + }; + } elseif ('' !== $to && !preg_match('//u', $to)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } else { + $replace = 'preg_replace'; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + if (null === $string = $replace($fromRegexp.'u', $to, $this->string)) { + $lastError = preg_last_error(); + + foreach (get_defined_constants(true)['pcre'] as $k => $v) { + if ($lastError === $v && '_ERROR' === substr($k, -6)) { + throw new RuntimeException('Matching failed with '.$k.'.'); + } + } + + throw new RuntimeException('Matching failed with unknown error code.'); + } + } finally { + restore_error_handler(); + } + + $str = clone $this; + $str->string = $string; + + return $str; + } + + public function reverse(): static + { + $str = clone $this; + $str->string = implode('', array_reverse(preg_split('/(\X)/u', $str->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY))); + + return $str; + } + + public function snake(): static + { + $str = $this->camel(); + $str->string = mb_strtolower(preg_replace(['/(\p{Lu}+)(\p{Lu}\p{Ll})/u', '/([\p{Ll}0-9])(\p{Lu})/u'], '\1_\2', $str->string), 'UTF-8'); + + return $str; + } + + public function title(bool $allWords = false): static + { + $str = clone $this; + + $limit = $allWords ? -1 : 1; + + $str->string = preg_replace_callback('/\b./u', static function (array $m): string { + return mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8'); + }, $str->string, $limit); + + return $str; + } + + public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static + { + if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { + throw new InvalidArgumentException('Invalid UTF-8 chars.'); + } + $chars = preg_quote($chars); + + $str = clone $this; + $str->string = preg_replace("{^[$chars]++|[$chars]++$}uD", '', $str->string); + + return $str; + } + + public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static + { + if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { + throw new InvalidArgumentException('Invalid UTF-8 chars.'); + } + $chars = preg_quote($chars); + + $str = clone $this; + $str->string = preg_replace("{[$chars]++$}uD", '', $str->string); + + return $str; + } + + public function trimPrefix($prefix): static + { + if (!$this->ignoreCase) { + return parent::trimPrefix($prefix); + } + + $str = clone $this; + + if ($prefix instanceof \Traversable) { + $prefix = iterator_to_array($prefix, false); + } elseif ($prefix instanceof parent) { + $prefix = $prefix->string; + } + + $prefix = implode('|', array_map('preg_quote', (array) $prefix)); + $str->string = preg_replace("{^(?:$prefix)}iuD", '', $this->string); + + return $str; + } + + public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static + { + if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { + throw new InvalidArgumentException('Invalid UTF-8 chars.'); + } + $chars = preg_quote($chars); + + $str = clone $this; + $str->string = preg_replace("{^[$chars]++}uD", '', $str->string); + + return $str; + } + + public function trimSuffix($suffix): static + { + if (!$this->ignoreCase) { + return parent::trimSuffix($suffix); + } + + $str = clone $this; + + if ($suffix instanceof \Traversable) { + $suffix = iterator_to_array($suffix, false); + } elseif ($suffix instanceof parent) { + $suffix = $suffix->string; + } + + $suffix = implode('|', array_map('preg_quote', (array) $suffix)); + $str->string = preg_replace("{(?:$suffix)$}iuD", '', $this->string); + + return $str; + } + + public function upper(): static + { + $str = clone $this; + $str->string = mb_strtoupper($str->string, 'UTF-8'); + + return $str; + } + + public function width(bool $ignoreAnsiDecoration = true): int + { + $width = 0; + $s = str_replace(["\x00", "\x05", "\x07"], '', $this->string); + + if (false !== strpos($s, "\r")) { + $s = str_replace(["\r\n", "\r"], "\n", $s); + } + + if (!$ignoreAnsiDecoration) { + $s = preg_replace('/[\p{Cc}\x7F]++/u', '', $s); + } + + foreach (explode("\n", $s) as $s) { + if ($ignoreAnsiDecoration) { + $s = preg_replace('/(?:\x1B(?: + \[ [\x30-\x3F]*+ [\x20-\x2F]*+ [\x40-\x7E] + | [P\]X^_] .*? \x1B\\\\ + | [\x41-\x7E] + )|[\p{Cc}\x7F]++)/xu', '', $s); + } + + $lineWidth = $this->wcswidth($s); + + if ($lineWidth > $width) { + $width = $lineWidth; + } + } + + return $width; + } + + private function pad(int $len, self $pad, int $type): static + { + $sLen = $this->length(); + + if ($len <= $sLen) { + return clone $this; + } + + $padLen = $pad->length(); + $freeLen = $len - $sLen; + $len = $freeLen % $padLen; + + switch ($type) { + case \STR_PAD_RIGHT: + return $this->append(str_repeat($pad->string, intdiv($freeLen, $padLen)).($len ? $pad->slice(0, $len) : '')); + + case \STR_PAD_LEFT: + return $this->prepend(str_repeat($pad->string, intdiv($freeLen, $padLen)).($len ? $pad->slice(0, $len) : '')); + + case \STR_PAD_BOTH: + $freeLen /= 2; + + $rightLen = ceil($freeLen); + $len = $rightLen % $padLen; + $str = $this->append(str_repeat($pad->string, intdiv($rightLen, $padLen)).($len ? $pad->slice(0, $len) : '')); + + $leftLen = floor($freeLen); + $len = $leftLen % $padLen; + + return $str->prepend(str_repeat($pad->string, intdiv($leftLen, $padLen)).($len ? $pad->slice(0, $len) : '')); + + default: + throw new InvalidArgumentException('Invalid padding type.'); + } + } + + /** + * Based on https://github.com/jquast/wcwidth, a Python implementation of https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c. + */ + private function wcswidth(string $string): int + { + $width = 0; + + foreach (preg_split('//u', $string, -1, \PREG_SPLIT_NO_EMPTY) as $c) { + $codePoint = mb_ord($c, 'UTF-8'); + + if (0 === $codePoint // NULL + || 0x034F === $codePoint // COMBINING GRAPHEME JOINER + || (0x200B <= $codePoint && 0x200F >= $codePoint) // ZERO WIDTH SPACE to RIGHT-TO-LEFT MARK + || 0x2028 === $codePoint // LINE SEPARATOR + || 0x2029 === $codePoint // PARAGRAPH SEPARATOR + || (0x202A <= $codePoint && 0x202E >= $codePoint) // LEFT-TO-RIGHT EMBEDDING to RIGHT-TO-LEFT OVERRIDE + || (0x2060 <= $codePoint && 0x2063 >= $codePoint) // WORD JOINER to INVISIBLE SEPARATOR + ) { + continue; + } + + // Non printable characters + if (32 > $codePoint // C0 control characters + || (0x07F <= $codePoint && 0x0A0 > $codePoint) // C1 control characters and DEL + ) { + return -1; + } + + if (null === self::$tableZero) { + self::$tableZero = require __DIR__.'/Resources/data/wcswidth_table_zero.php'; + } + + if ($codePoint >= self::$tableZero[0][0] && $codePoint <= self::$tableZero[$ubound = \count(self::$tableZero) - 1][1]) { + $lbound = 0; + while ($ubound >= $lbound) { + $mid = floor(($lbound + $ubound) / 2); + + if ($codePoint > self::$tableZero[$mid][1]) { + $lbound = $mid + 1; + } elseif ($codePoint < self::$tableZero[$mid][0]) { + $ubound = $mid - 1; + } else { + continue 2; + } + } + } + + if (null === self::$tableWide) { + self::$tableWide = require __DIR__.'/Resources/data/wcswidth_table_wide.php'; + } + + if ($codePoint >= self::$tableWide[0][0] && $codePoint <= self::$tableWide[$ubound = \count(self::$tableWide) - 1][1]) { + $lbound = 0; + while ($ubound >= $lbound) { + $mid = floor(($lbound + $ubound) / 2); + + if ($codePoint > self::$tableWide[$mid][1]) { + $lbound = $mid + 1; + } elseif ($codePoint < self::$tableWide[$mid][0]) { + $ubound = $mid - 1; + } else { + $width += 2; + + continue 2; + } + } + } + + ++$width; + } + + return $width; + } +} diff --git a/vendor/symfony/string/ByteString.php b/vendor/symfony/string/ByteString.php new file mode 100644 index 0000000..639d643 --- /dev/null +++ b/vendor/symfony/string/ByteString.php @@ -0,0 +1,493 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +use Symfony\Component\String\Exception\ExceptionInterface; +use Symfony\Component\String\Exception\InvalidArgumentException; +use Symfony\Component\String\Exception\RuntimeException; + +/** + * Represents a binary-safe string of bytes. + * + * @author Nicolas Grekas + * @author Hugo Hamon + * + * @throws ExceptionInterface + */ +class ByteString extends AbstractString +{ + private const ALPHABET_ALPHANUMERIC = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; + + public function __construct(string $string = '') + { + $this->string = $string; + } + + /* + * The following method was derived from code of the Hack Standard Library (v4.40 - 2020-05-03) + * + * https://github.com/hhvm/hsl/blob/80a42c02f036f72a42f0415e80d6b847f4bf62d5/src/random/private.php#L16 + * + * Code subject to the MIT license (https://github.com/hhvm/hsl/blob/master/LICENSE). + * + * Copyright (c) 2004-2020, Facebook, Inc. (https://www.facebook.com/) + */ + + public static function fromRandom(int $length = 16, string $alphabet = null): self + { + if ($length <= 0) { + throw new InvalidArgumentException(sprintf('A strictly positive length is expected, "%d" given.', $length)); + } + + $alphabet = $alphabet ?? self::ALPHABET_ALPHANUMERIC; + $alphabetSize = \strlen($alphabet); + $bits = (int) ceil(log($alphabetSize, 2.0)); + if ($bits <= 0 || $bits > 56) { + throw new InvalidArgumentException('The length of the alphabet must in the [2^1, 2^56] range.'); + } + + $ret = ''; + while ($length > 0) { + $urandomLength = (int) ceil(2 * $length * $bits / 8.0); + $data = random_bytes($urandomLength); + $unpackedData = 0; + $unpackedBits = 0; + for ($i = 0; $i < $urandomLength && $length > 0; ++$i) { + // Unpack 8 bits + $unpackedData = ($unpackedData << 8) | \ord($data[$i]); + $unpackedBits += 8; + + // While we have enough bits to select a character from the alphabet, keep + // consuming the random data + for (; $unpackedBits >= $bits && $length > 0; $unpackedBits -= $bits) { + $index = ($unpackedData & ((1 << $bits) - 1)); + $unpackedData >>= $bits; + // Unfortunately, the alphabet size is not necessarily a power of two. + // Worst case, it is 2^k + 1, which means we need (k+1) bits and we + // have around a 50% chance of missing as k gets larger + if ($index < $alphabetSize) { + $ret .= $alphabet[$index]; + --$length; + } + } + } + } + + return new static($ret); + } + + public function bytesAt(int $offset): array + { + $str = $this->string[$offset] ?? ''; + + return '' === $str ? [] : [\ord($str)]; + } + + public function append(string ...$suffix): static + { + $str = clone $this; + $str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix); + + return $str; + } + + public function camel(): static + { + $str = clone $this; + + $parts = explode(' ', trim(ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $this->string)))); + $parts[0] = 1 !== \strlen($parts[0]) && ctype_upper($parts[0]) ? $parts[0] : lcfirst($parts[0]); + $str->string = implode('', $parts); + + return $str; + } + + public function chunk(int $length = 1): array + { + if (1 > $length) { + throw new InvalidArgumentException('The chunk length must be greater than zero.'); + } + + if ('' === $this->string) { + return []; + } + + $str = clone $this; + $chunks = []; + + foreach (str_split($this->string, $length) as $chunk) { + $str->string = $chunk; + $chunks[] = clone $str; + } + + return $chunks; + } + + public function endsWith(string|iterable|AbstractString $suffix): bool + { + if ($suffix instanceof AbstractString) { + $suffix = $suffix->string; + } elseif (!\is_string($suffix)) { + return parent::endsWith($suffix); + } + + return '' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase); + } + + public function equalsTo(string|iterable|AbstractString $string): bool + { + if ($string instanceof AbstractString) { + $string = $string->string; + } elseif (!\is_string($string)) { + return parent::equalsTo($string); + } + + if ('' !== $string && $this->ignoreCase) { + return 0 === strcasecmp($string, $this->string); + } + + return $string === $this->string; + } + + public function folded(): static + { + $str = clone $this; + $str->string = strtolower($str->string); + + return $str; + } + + public function indexOf(string|iterable|AbstractString $needle, int $offset = 0): ?int + { + if ($needle instanceof AbstractString) { + $needle = $needle->string; + } elseif (!\is_string($needle)) { + return parent::indexOf($needle, $offset); + } + + if ('' === $needle) { + return null; + } + + $i = $this->ignoreCase ? stripos($this->string, $needle, $offset) : strpos($this->string, $needle, $offset); + + return false === $i ? null : $i; + } + + public function indexOfLast(string|iterable|AbstractString $needle, int $offset = 0): ?int + { + if ($needle instanceof AbstractString) { + $needle = $needle->string; + } elseif (!\is_string($needle)) { + return parent::indexOfLast($needle, $offset); + } + + if ('' === $needle) { + return null; + } + + $i = $this->ignoreCase ? strripos($this->string, $needle, $offset) : strrpos($this->string, $needle, $offset); + + return false === $i ? null : $i; + } + + public function isUtf8(): bool + { + return '' === $this->string || preg_match('//u', $this->string); + } + + public function join(array $strings, string $lastGlue = null): static + { + $str = clone $this; + + $tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : ''; + $str->string = implode($this->string, $strings).$tail; + + return $str; + } + + public function length(): int + { + return \strlen($this->string); + } + + public function lower(): static + { + $str = clone $this; + $str->string = strtolower($str->string); + + return $str; + } + + public function match(string $regexp, int $flags = 0, int $offset = 0): array + { + $match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match'; + + if ($this->ignoreCase) { + $regexp .= 'i'; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + if (false === $match($regexp, $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { + $lastError = preg_last_error(); + + foreach (get_defined_constants(true)['pcre'] as $k => $v) { + if ($lastError === $v && '_ERROR' === substr($k, -6)) { + throw new RuntimeException('Matching failed with '.$k.'.'); + } + } + + throw new RuntimeException('Matching failed with unknown error code.'); + } + } finally { + restore_error_handler(); + } + + return $matches; + } + + public function padBoth(int $length, string $padStr = ' '): static + { + $str = clone $this; + $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_BOTH); + + return $str; + } + + public function padEnd(int $length, string $padStr = ' '): static + { + $str = clone $this; + $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_RIGHT); + + return $str; + } + + public function padStart(int $length, string $padStr = ' '): static + { + $str = clone $this; + $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_LEFT); + + return $str; + } + + public function prepend(string ...$prefix): static + { + $str = clone $this; + $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$str->string; + + return $str; + } + + public function replace(string $from, string $to): static + { + $str = clone $this; + + if ('' !== $from) { + $str->string = $this->ignoreCase ? str_ireplace($from, $to, $this->string) : str_replace($from, $to, $this->string); + } + + return $str; + } + + public function replaceMatches(string $fromRegexp, string|callable $to): static + { + if ($this->ignoreCase) { + $fromRegexp .= 'i'; + } + + $replace = \is_array($to) || $to instanceof \Closure ? 'preg_replace_callback' : 'preg_replace'; + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + if (null === $string = $replace($fromRegexp, $to, $this->string)) { + $lastError = preg_last_error(); + + foreach (get_defined_constants(true)['pcre'] as $k => $v) { + if ($lastError === $v && '_ERROR' === substr($k, -6)) { + throw new RuntimeException('Matching failed with '.$k.'.'); + } + } + + throw new RuntimeException('Matching failed with unknown error code.'); + } + } finally { + restore_error_handler(); + } + + $str = clone $this; + $str->string = $string; + + return $str; + } + + public function reverse(): static + { + $str = clone $this; + $str->string = strrev($str->string); + + return $str; + } + + public function slice(int $start = 0, int $length = null): static + { + $str = clone $this; + $str->string = (string) substr($this->string, $start, $length ?? \PHP_INT_MAX); + + return $str; + } + + public function snake(): static + { + $str = $this->camel(); + $str->string = strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1_\2', $str->string)); + + return $str; + } + + public function splice(string $replacement, int $start = 0, int $length = null): static + { + $str = clone $this; + $str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX); + + return $str; + } + + public function split(string $delimiter, int $limit = null, int $flags = null): array + { + if (1 > $limit = $limit ?? \PHP_INT_MAX) { + throw new InvalidArgumentException('Split limit must be a positive integer.'); + } + + if ('' === $delimiter) { + throw new InvalidArgumentException('Split delimiter is empty.'); + } + + if (null !== $flags) { + return parent::split($delimiter, $limit, $flags); + } + + $str = clone $this; + $chunks = $this->ignoreCase + ? preg_split('{'.preg_quote($delimiter).'}iD', $this->string, $limit) + : explode($delimiter, $this->string, $limit); + + foreach ($chunks as &$chunk) { + $str->string = $chunk; + $chunk = clone $str; + } + + return $chunks; + } + + public function startsWith(string|iterable|AbstractString $prefix): bool + { + if ($prefix instanceof AbstractString) { + $prefix = $prefix->string; + } elseif (!\is_string($prefix)) { + return parent::startsWith($prefix); + } + + return '' !== $prefix && 0 === ($this->ignoreCase ? strncasecmp($this->string, $prefix, \strlen($prefix)) : strncmp($this->string, $prefix, \strlen($prefix))); + } + + public function title(bool $allWords = false): static + { + $str = clone $this; + $str->string = $allWords ? ucwords($str->string) : ucfirst($str->string); + + return $str; + } + + public function toUnicodeString(string $fromEncoding = null): UnicodeString + { + return new UnicodeString($this->toCodePointString($fromEncoding)->string); + } + + public function toCodePointString(string $fromEncoding = null): CodePointString + { + $u = new CodePointString(); + + if (\in_array($fromEncoding, [null, 'utf8', 'utf-8', 'UTF8', 'UTF-8'], true) && preg_match('//u', $this->string)) { + $u->string = $this->string; + + return $u; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + try { + $validEncoding = false !== mb_detect_encoding($this->string, $fromEncoding ?? 'Windows-1252', true); + } catch (InvalidArgumentException $e) { + if (!\function_exists('iconv')) { + throw $e; + } + + $u->string = iconv($fromEncoding ?? 'Windows-1252', 'UTF-8', $this->string); + + return $u; + } + } finally { + restore_error_handler(); + } + + if (!$validEncoding) { + throw new InvalidArgumentException(sprintf('Invalid "%s" string.', $fromEncoding ?? 'Windows-1252')); + } + + $u->string = mb_convert_encoding($this->string, 'UTF-8', $fromEncoding ?? 'Windows-1252'); + + return $u; + } + + public function trim(string $chars = " \t\n\r\0\x0B\x0C"): static + { + $str = clone $this; + $str->string = trim($str->string, $chars); + + return $str; + } + + public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C"): static + { + $str = clone $this; + $str->string = rtrim($str->string, $chars); + + return $str; + } + + public function trimStart(string $chars = " \t\n\r\0\x0B\x0C"): static + { + $str = clone $this; + $str->string = ltrim($str->string, $chars); + + return $str; + } + + public function upper(): static + { + $str = clone $this; + $str->string = strtoupper($str->string); + + return $str; + } + + public function width(bool $ignoreAnsiDecoration = true): int + { + $string = preg_match('//u', $this->string) ? $this->string : preg_replace('/[\x80-\xFF]/', '?', $this->string); + + return (new CodePointString($string))->width($ignoreAnsiDecoration); + } +} diff --git a/vendor/symfony/string/CHANGELOG.md b/vendor/symfony/string/CHANGELOG.md new file mode 100644 index 0000000..53af364 --- /dev/null +++ b/vendor/symfony/string/CHANGELOG.md @@ -0,0 +1,35 @@ +CHANGELOG +========= + +5.4 +--- + + * Add `trimSuffix()` and `trimPrefix()` methods + +5.3 +--- + + * Made `AsciiSlugger` fallback to parent locale's symbolsMap + +5.2.0 +----- + + * added a `FrenchInflector` class + +5.1.0 +----- + + * added the `AbstractString::reverse()` method + * made `AbstractString::width()` follow POSIX.1-2001 + * added `LazyString` which provides memoizing stringable objects + * The component is not marked as `@experimental` anymore + * added the `s()` helper method to get either an `UnicodeString` or `ByteString` instance, + depending of the input string UTF-8 compliancy + * added `$cut` parameter to `Symfony\Component\String\AbstractString::truncate()` + * added `AbstractString::containsAny()` + * allow passing a string of custom characters to `ByteString::fromRandom()` + +5.0.0 +----- + + * added the component as experimental diff --git a/vendor/symfony/string/CodePointString.php b/vendor/symfony/string/CodePointString.php new file mode 100644 index 0000000..926ff79 --- /dev/null +++ b/vendor/symfony/string/CodePointString.php @@ -0,0 +1,260 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +use Symfony\Component\String\Exception\ExceptionInterface; +use Symfony\Component\String\Exception\InvalidArgumentException; + +/** + * Represents a string of Unicode code points encoded as UTF-8. + * + * @author Nicolas Grekas + * @author Hugo Hamon + * + * @throws ExceptionInterface + */ +class CodePointString extends AbstractUnicodeString +{ + public function __construct(string $string = '') + { + if ('' !== $string && !preg_match('//u', $string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $this->string = $string; + } + + public function append(string ...$suffix): static + { + $str = clone $this; + $str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix); + + if (!preg_match('//u', $str->string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + return $str; + } + + public function chunk(int $length = 1): array + { + if (1 > $length) { + throw new InvalidArgumentException('The chunk length must be greater than zero.'); + } + + if ('' === $this->string) { + return []; + } + + $rx = '/('; + while (65535 < $length) { + $rx .= '.{65535}'; + $length -= 65535; + } + $rx .= '.{'.$length.'})/us'; + + $str = clone $this; + $chunks = []; + + foreach (preg_split($rx, $this->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY) as $chunk) { + $str->string = $chunk; + $chunks[] = clone $str; + } + + return $chunks; + } + + public function codePointsAt(int $offset): array + { + $str = $offset ? $this->slice($offset, 1) : $this; + + return '' === $str->string ? [] : [mb_ord($str->string, 'UTF-8')]; + } + + public function endsWith(string|iterable|AbstractString $suffix): bool + { + if ($suffix instanceof AbstractString) { + $suffix = $suffix->string; + } elseif (!\is_string($suffix)) { + return parent::endsWith($suffix); + } + + if ('' === $suffix || !preg_match('//u', $suffix)) { + return false; + } + + if ($this->ignoreCase) { + return preg_match('{'.preg_quote($suffix).'$}iuD', $this->string); + } + + return \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix)); + } + + public function equalsTo(string|iterable|AbstractString $string): bool + { + if ($string instanceof AbstractString) { + $string = $string->string; + } elseif (!\is_string($string)) { + return parent::equalsTo($string); + } + + if ('' !== $string && $this->ignoreCase) { + return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8'); + } + + return $string === $this->string; + } + + public function indexOf(string|iterable|AbstractString $needle, int $offset = 0): ?int + { + if ($needle instanceof AbstractString) { + $needle = $needle->string; + } elseif (!\is_string($needle)) { + return parent::indexOf($needle, $offset); + } + + if ('' === $needle) { + return null; + } + + $i = $this->ignoreCase ? mb_stripos($this->string, $needle, $offset, 'UTF-8') : mb_strpos($this->string, $needle, $offset, 'UTF-8'); + + return false === $i ? null : $i; + } + + public function indexOfLast(string|iterable|AbstractString $needle, int $offset = 0): ?int + { + if ($needle instanceof AbstractString) { + $needle = $needle->string; + } elseif (!\is_string($needle)) { + return parent::indexOfLast($needle, $offset); + } + + if ('' === $needle) { + return null; + } + + $i = $this->ignoreCase ? mb_strripos($this->string, $needle, $offset, 'UTF-8') : mb_strrpos($this->string, $needle, $offset, 'UTF-8'); + + return false === $i ? null : $i; + } + + public function length(): int + { + return mb_strlen($this->string, 'UTF-8'); + } + + public function prepend(string ...$prefix): static + { + $str = clone $this; + $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$this->string; + + if (!preg_match('//u', $str->string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + return $str; + } + + public function replace(string $from, string $to): static + { + $str = clone $this; + + if ('' === $from || !preg_match('//u', $from)) { + return $str; + } + + if ('' !== $to && !preg_match('//u', $to)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + if ($this->ignoreCase) { + $str->string = implode($to, preg_split('{'.preg_quote($from).'}iuD', $this->string)); + } else { + $str->string = str_replace($from, $to, $this->string); + } + + return $str; + } + + public function slice(int $start = 0, int $length = null): static + { + $str = clone $this; + $str->string = mb_substr($this->string, $start, $length, 'UTF-8'); + + return $str; + } + + public function splice(string $replacement, int $start = 0, int $length = null): static + { + if (!preg_match('//u', $replacement)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $str = clone $this; + $start = $start ? \strlen(mb_substr($this->string, 0, $start, 'UTF-8')) : 0; + $length = $length ? \strlen(mb_substr($this->string, $start, $length, 'UTF-8')) : $length; + $str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX); + + return $str; + } + + public function split(string $delimiter, int $limit = null, int $flags = null): array + { + if (1 > $limit = $limit ?? \PHP_INT_MAX) { + throw new InvalidArgumentException('Split limit must be a positive integer.'); + } + + if ('' === $delimiter) { + throw new InvalidArgumentException('Split delimiter is empty.'); + } + + if (null !== $flags) { + return parent::split($delimiter.'u', $limit, $flags); + } + + if (!preg_match('//u', $delimiter)) { + throw new InvalidArgumentException('Split delimiter is not a valid UTF-8 string.'); + } + + $str = clone $this; + $chunks = $this->ignoreCase + ? preg_split('{'.preg_quote($delimiter).'}iuD', $this->string, $limit) + : explode($delimiter, $this->string, $limit); + + foreach ($chunks as &$chunk) { + $str->string = $chunk; + $chunk = clone $str; + } + + return $chunks; + } + + public function startsWith(string|iterable|AbstractString $prefix): bool + { + if ($prefix instanceof AbstractString) { + $prefix = $prefix->string; + } elseif (!\is_string($prefix)) { + return parent::startsWith($prefix); + } + + if ('' === $prefix || !preg_match('//u', $prefix)) { + return false; + } + + if ($this->ignoreCase) { + return 0 === mb_stripos($this->string, $prefix, 0, 'UTF-8'); + } + + return 0 === strncmp($this->string, $prefix, \strlen($prefix)); + } +} diff --git a/vendor/symfony/string/Exception/ExceptionInterface.php b/vendor/symfony/string/Exception/ExceptionInterface.php new file mode 100644 index 0000000..3619786 --- /dev/null +++ b/vendor/symfony/string/Exception/ExceptionInterface.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Exception; + +interface ExceptionInterface extends \Throwable +{ +} diff --git a/vendor/symfony/string/Exception/InvalidArgumentException.php b/vendor/symfony/string/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..6aa586b --- /dev/null +++ b/vendor/symfony/string/Exception/InvalidArgumentException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Exception; + +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/string/Exception/RuntimeException.php b/vendor/symfony/string/Exception/RuntimeException.php new file mode 100644 index 0000000..77cb091 --- /dev/null +++ b/vendor/symfony/string/Exception/RuntimeException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Exception; + +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/string/Inflector/EnglishInflector.php b/vendor/symfony/string/Inflector/EnglishInflector.php new file mode 100644 index 0000000..9f2fac6 --- /dev/null +++ b/vendor/symfony/string/Inflector/EnglishInflector.php @@ -0,0 +1,511 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Inflector; + +final class EnglishInflector implements InflectorInterface +{ + /** + * Map English plural to singular suffixes. + * + * @see http://english-zone.com/spelling/plurals.html + */ + private const PLURAL_MAP = [ + // First entry: plural suffix, reversed + // Second entry: length of plural suffix + // Third entry: Whether the suffix may succeed a vocal + // Fourth entry: Whether the suffix may succeed a consonant + // Fifth entry: singular suffix, normal + + // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) + ['a', 1, true, true, ['on', 'um']], + + // nebulae (nebula) + ['ea', 2, true, true, 'a'], + + // services (service) + ['secivres', 8, true, true, 'service'], + + // mice (mouse), lice (louse) + ['eci', 3, false, true, 'ouse'], + + // geese (goose) + ['esee', 4, false, true, 'oose'], + + // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius) + ['i', 1, true, true, 'us'], + + // men (man), women (woman) + ['nem', 3, true, true, 'man'], + + // children (child) + ['nerdlihc', 8, true, true, 'child'], + + // oxen (ox) + ['nexo', 4, false, false, 'ox'], + + // indices (index), appendices (appendix), prices (price) + ['seci', 4, false, true, ['ex', 'ix', 'ice']], + + // selfies (selfie) + ['seifles', 7, true, true, 'selfie'], + + // zombies (zombie) + ['seibmoz', 7, true, true, 'zombie'], + + // movies (movie) + ['seivom', 6, true, true, 'movie'], + + // conspectuses (conspectus), prospectuses (prospectus) + ['sesutcep', 8, true, true, 'pectus'], + + // feet (foot) + ['teef', 4, true, true, 'foot'], + + // geese (goose) + ['eseeg', 5, true, true, 'goose'], + + // teeth (tooth) + ['hteet', 5, true, true, 'tooth'], + + // news (news) + ['swen', 4, true, true, 'news'], + + // series (series) + ['seires', 6, true, true, 'series'], + + // babies (baby) + ['sei', 3, false, true, 'y'], + + // accesses (access), addresses (address), kisses (kiss) + ['sess', 4, true, false, 'ss'], + + // analyses (analysis), ellipses (ellipsis), fungi (fungus), + // neuroses (neurosis), theses (thesis), emphases (emphasis), + // oases (oasis), crises (crisis), houses (house), bases (base), + // atlases (atlas) + ['ses', 3, true, true, ['s', 'se', 'sis']], + + // objectives (objective), alternative (alternatives) + ['sevit', 5, true, true, 'tive'], + + // drives (drive) + ['sevird', 6, false, true, 'drive'], + + // lives (life), wives (wife) + ['sevi', 4, false, true, 'ife'], + + // moves (move) + ['sevom', 5, true, true, 'move'], + + // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf), caves (cave), staves (staff) + ['sev', 3, true, true, ['f', 've', 'ff']], + + // axes (axis), axes (ax), axes (axe) + ['sexa', 4, false, false, ['ax', 'axe', 'axis']], + + // indexes (index), matrixes (matrix) + ['sex', 3, true, false, 'x'], + + // quizzes (quiz) + ['sezz', 4, true, false, 'z'], + + // bureaus (bureau) + ['suae', 4, false, true, 'eau'], + + // fees (fee), trees (tree), employees (employee) + ['see', 3, true, true, 'ee'], + + // edges (edge) + ['segd', 4, true, true, 'dge'], + + // roses (rose), garages (garage), cassettes (cassette), + // waltzes (waltz), heroes (hero), bushes (bush), arches (arch), + // shoes (shoe) + ['se', 2, true, true, ['', 'e']], + + // tags (tag) + ['s', 1, true, true, ''], + + // chateaux (chateau) + ['xuae', 4, false, true, 'eau'], + + // people (person) + ['elpoep', 6, true, true, 'person'], + ]; + + /** + * Map English singular to plural suffixes. + * + * @see http://english-zone.com/spelling/plurals.html + */ + private const SINGULAR_MAP = [ + // First entry: singular suffix, reversed + // Second entry: length of singular suffix + // Third entry: Whether the suffix may succeed a vocal + // Fourth entry: Whether the suffix may succeed a consonant + // Fifth entry: plural suffix, normal + + // criterion (criteria) + ['airetirc', 8, false, false, 'criterion'], + + // nebulae (nebula) + ['aluben', 6, false, false, 'nebulae'], + + // children (child) + ['dlihc', 5, true, true, 'children'], + + // prices (price) + ['eci', 3, false, true, 'ices'], + + // services (service) + ['ecivres', 7, true, true, 'services'], + + // lives (life), wives (wife) + ['efi', 3, false, true, 'ives'], + + // selfies (selfie) + ['eifles', 6, true, true, 'selfies'], + + // movies (movie) + ['eivom', 5, true, true, 'movies'], + + // lice (louse) + ['esuol', 5, false, true, 'lice'], + + // mice (mouse) + ['esuom', 5, false, true, 'mice'], + + // geese (goose) + ['esoo', 4, false, true, 'eese'], + + // houses (house), bases (base) + ['es', 2, true, true, 'ses'], + + // geese (goose) + ['esoog', 5, true, true, 'geese'], + + // caves (cave) + ['ev', 2, true, true, 'ves'], + + // drives (drive) + ['evird', 5, false, true, 'drives'], + + // objectives (objective), alternative (alternatives) + ['evit', 4, true, true, 'tives'], + + // moves (move) + ['evom', 4, true, true, 'moves'], + + // staves (staff) + ['ffats', 5, true, true, 'staves'], + + // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf) + ['ff', 2, true, true, 'ffs'], + + // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf) + ['f', 1, true, true, ['fs', 'ves']], + + // arches (arch) + ['hc', 2, true, true, 'ches'], + + // bushes (bush) + ['hs', 2, true, true, 'shes'], + + // teeth (tooth) + ['htoot', 5, true, true, 'teeth'], + + // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) + ['mu', 2, true, true, 'a'], + + // men (man), women (woman) + ['nam', 3, true, true, 'men'], + + // people (person) + ['nosrep', 6, true, true, ['persons', 'people']], + + // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) + ['noi', 3, true, true, 'ions'], + + // coupon (coupons) + ['nop', 3, true, true, 'pons'], + + // seasons (season), treasons (treason), poisons (poison), lessons (lesson) + ['nos', 3, true, true, 'sons'], + + // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) + ['no', 2, true, true, 'a'], + + // echoes (echo) + ['ohce', 4, true, true, 'echoes'], + + // heroes (hero) + ['oreh', 4, true, true, 'heroes'], + + // atlases (atlas) + ['salta', 5, true, true, 'atlases'], + + // irises (iris) + ['siri', 4, true, true, 'irises'], + + // analyses (analysis), ellipses (ellipsis), neuroses (neurosis) + // theses (thesis), emphases (emphasis), oases (oasis), + // crises (crisis) + ['sis', 3, true, true, 'ses'], + + // accesses (access), addresses (address), kisses (kiss) + ['ss', 2, true, false, 'sses'], + + // syllabi (syllabus) + ['suballys', 8, true, true, 'syllabi'], + + // buses (bus) + ['sub', 3, true, true, 'buses'], + + // circuses (circus) + ['suc', 3, true, true, 'cuses'], + + // conspectuses (conspectus), prospectuses (prospectus) + ['sutcep', 6, true, true, 'pectuses'], + + // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius) + ['su', 2, true, true, 'i'], + + // news (news) + ['swen', 4, true, true, 'news'], + + // feet (foot) + ['toof', 4, true, true, 'feet'], + + // chateaux (chateau), bureaus (bureau) + ['uae', 3, false, true, ['eaus', 'eaux']], + + // oxen (ox) + ['xo', 2, false, false, 'oxen'], + + // hoaxes (hoax) + ['xaoh', 4, true, false, 'hoaxes'], + + // indices (index) + ['xedni', 5, false, true, ['indicies', 'indexes']], + + // boxes (box) + ['xo', 2, false, true, 'oxes'], + + // indexes (index), matrixes (matrix) + ['x', 1, true, false, ['cies', 'xes']], + + // appendices (appendix) + ['xi', 2, false, true, 'ices'], + + // babies (baby) + ['y', 1, false, true, 'ies'], + + // quizzes (quiz) + ['ziuq', 4, true, false, 'quizzes'], + + // waltzes (waltz) + ['z', 1, true, true, 'zes'], + ]; + + /** + * A list of words which should not be inflected, reversed. + */ + private const UNINFLECTED = [ + '', + + // data + 'atad', + + // deer + 'reed', + + // feedback + 'kcabdeef', + + // fish + 'hsif', + + // info + 'ofni', + + // moose + 'esoom', + + // series + 'seires', + + // sheep + 'peehs', + + // species + 'seiceps', + ]; + + /** + * {@inheritdoc} + */ + public function singularize(string $plural): array + { + $pluralRev = strrev($plural); + $lowerPluralRev = strtolower($pluralRev); + $pluralLength = \strlen($lowerPluralRev); + + // Check if the word is one which is not inflected, return early if so + if (\in_array($lowerPluralRev, self::UNINFLECTED, true)) { + return [$plural]; + } + + // The outer loop iterates over the entries of the plural table + // The inner loop $j iterates over the characters of the plural suffix + // in the plural table to compare them with the characters of the actual + // given plural suffix + foreach (self::PLURAL_MAP as $map) { + $suffix = $map[0]; + $suffixLength = $map[1]; + $j = 0; + + // Compare characters in the plural table and of the suffix of the + // given plural one by one + while ($suffix[$j] === $lowerPluralRev[$j]) { + // Let $j point to the next character + ++$j; + + // Successfully compared the last character + // Add an entry with the singular suffix to the singular array + if ($j === $suffixLength) { + // Is there any character preceding the suffix in the plural string? + if ($j < $pluralLength) { + $nextIsVocal = false !== strpos('aeiou', $lowerPluralRev[$j]); + + if (!$map[2] && $nextIsVocal) { + // suffix may not succeed a vocal but next char is one + break; + } + + if (!$map[3] && !$nextIsVocal) { + // suffix may not succeed a consonant but next char is one + break; + } + } + + $newBase = substr($plural, 0, $pluralLength - $suffixLength); + $newSuffix = $map[4]; + + // Check whether the first character in the plural suffix + // is uppercased. If yes, uppercase the first character in + // the singular suffix too + $firstUpper = ctype_upper($pluralRev[$j - 1]); + + if (\is_array($newSuffix)) { + $singulars = []; + + foreach ($newSuffix as $newSuffixEntry) { + $singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry); + } + + return $singulars; + } + + return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)]; + } + + // Suffix is longer than word + if ($j === $pluralLength) { + break; + } + } + } + + // Assume that plural and singular is identical + return [$plural]; + } + + /** + * {@inheritdoc} + */ + public function pluralize(string $singular): array + { + $singularRev = strrev($singular); + $lowerSingularRev = strtolower($singularRev); + $singularLength = \strlen($lowerSingularRev); + + // Check if the word is one which is not inflected, return early if so + if (\in_array($lowerSingularRev, self::UNINFLECTED, true)) { + return [$singular]; + } + + // The outer loop iterates over the entries of the singular table + // The inner loop $j iterates over the characters of the singular suffix + // in the singular table to compare them with the characters of the actual + // given singular suffix + foreach (self::SINGULAR_MAP as $map) { + $suffix = $map[0]; + $suffixLength = $map[1]; + $j = 0; + + // Compare characters in the singular table and of the suffix of the + // given plural one by one + + while ($suffix[$j] === $lowerSingularRev[$j]) { + // Let $j point to the next character + ++$j; + + // Successfully compared the last character + // Add an entry with the plural suffix to the plural array + if ($j === $suffixLength) { + // Is there any character preceding the suffix in the plural string? + if ($j < $singularLength) { + $nextIsVocal = false !== strpos('aeiou', $lowerSingularRev[$j]); + + if (!$map[2] && $nextIsVocal) { + // suffix may not succeed a vocal but next char is one + break; + } + + if (!$map[3] && !$nextIsVocal) { + // suffix may not succeed a consonant but next char is one + break; + } + } + + $newBase = substr($singular, 0, $singularLength - $suffixLength); + $newSuffix = $map[4]; + + // Check whether the first character in the singular suffix + // is uppercased. If yes, uppercase the first character in + // the singular suffix too + $firstUpper = ctype_upper($singularRev[$j - 1]); + + if (\is_array($newSuffix)) { + $plurals = []; + + foreach ($newSuffix as $newSuffixEntry) { + $plurals[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry); + } + + return $plurals; + } + + return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)]; + } + + // Suffix is longer than word + if ($j === $singularLength) { + break; + } + } + } + + // Assume that plural is singular with a trailing `s` + return [$singular.'s']; + } +} diff --git a/vendor/symfony/string/Inflector/FrenchInflector.php b/vendor/symfony/string/Inflector/FrenchInflector.php new file mode 100644 index 0000000..612c8f2 --- /dev/null +++ b/vendor/symfony/string/Inflector/FrenchInflector.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Inflector; + +/** + * French inflector. + * + * This class does only inflect nouns; not adjectives nor composed words like "soixante-dix". + */ +final class FrenchInflector implements InflectorInterface +{ + /** + * A list of all rules for pluralise. + * + * @see https://la-conjugaison.nouvelobs.com/regles/grammaire/le-pluriel-des-noms-121.php + */ + private const PLURALIZE_REGEXP = [ + // First entry: regexp + // Second entry: replacement + + // Words finishing with "s", "x" or "z" are invariables + // Les mots finissant par "s", "x" ou "z" sont invariables + ['/(s|x|z)$/i', '\1'], + + // Words finishing with "eau" are pluralized with a "x" + // Les mots finissant par "eau" prennent tous un "x" au pluriel + ['/(eau)$/i', '\1x'], + + // Words finishing with "au" are pluralized with a "x" excepted "landau" + // Les mots finissant par "au" prennent un "x" au pluriel sauf "landau" + ['/^(landau)$/i', '\1s'], + ['/(au)$/i', '\1x'], + + // Words finishing with "eu" are pluralized with a "x" excepted "pneu", "bleu", "émeu" + // Les mots finissant en "eu" prennent un "x" au pluriel sauf "pneu", "bleu", "émeu" + ['/^(pneu|bleu|émeu)$/i', '\1s'], + ['/(eu)$/i', '\1x'], + + // Words finishing with "al" are pluralized with a "aux" excepted + // Les mots finissant en "al" se terminent en "aux" sauf + ['/^(bal|carnaval|caracal|chacal|choral|corral|étal|festival|récital|val)$/i', '\1s'], + ['/al$/i', '\1aux'], + + // Aspirail, bail, corail, émail, fermail, soupirail, travail, vantail et vitrail font leur pluriel en -aux + ['/^(aspir|b|cor|ém|ferm|soupir|trav|vant|vitr)ail$/i', '\1aux'], + + // Bijou, caillou, chou, genou, hibou, joujou et pou qui prennent un x au pluriel + ['/^(bij|caill|ch|gen|hib|jouj|p)ou$/i', '\1oux'], + + // Invariable words + ['/^(cinquante|soixante|mille)$/i', '\1'], + + // French titles + ['/^(mon|ma)(sieur|dame|demoiselle|seigneur)$/', 'mes\2s'], + ['/^(Mon|Ma)(sieur|dame|demoiselle|seigneur)$/', 'Mes\2s'], + ]; + + /** + * A list of all rules for singularize. + */ + private const SINGULARIZE_REGEXP = [ + // First entry: regexp + // Second entry: replacement + + // Aspirail, bail, corail, émail, fermail, soupirail, travail, vantail et vitrail font leur pluriel en -aux + ['/((aspir|b|cor|ém|ferm|soupir|trav|vant|vitr))aux$/i', '\1ail'], + + // Words finishing with "eau" are pluralized with a "x" + // Les mots finissant par "eau" prennent tous un "x" au pluriel + ['/(eau)x$/i', '\1'], + + // Words finishing with "al" are pluralized with a "aux" expected + // Les mots finissant en "al" se terminent en "aux" sauf + ['/(amir|anim|arsen|boc|can|capit|capor|chev|crist|génér|hopit|hôpit|idé|journ|littor|loc|m|mét|minér|princip|radic|termin)aux$/i', '\1al'], + + // Words finishing with "au" are pluralized with a "x" excepted "landau" + // Les mots finissant par "au" prennent un "x" au pluriel sauf "landau" + ['/(au)x$/i', '\1'], + + // Words finishing with "eu" are pluralized with a "x" excepted "pneu", "bleu", "émeu" + // Les mots finissant en "eu" prennent un "x" au pluriel sauf "pneu", "bleu", "émeu" + ['/(eu)x$/i', '\1'], + + // Words finishing with "ou" are pluralized with a "s" excepted bijou, caillou, chou, genou, hibou, joujou, pou + // Les mots finissant par "ou" prennent un "s" sauf bijou, caillou, chou, genou, hibou, joujou, pou + ['/(bij|caill|ch|gen|hib|jouj|p)oux$/i', '\1ou'], + + // French titles + ['/^mes(dame|demoiselle)s$/', 'ma\1'], + ['/^Mes(dame|demoiselle)s$/', 'Ma\1'], + ['/^mes(sieur|seigneur)s$/', 'mon\1'], + ['/^Mes(sieur|seigneur)s$/', 'Mon\1'], + + // Default rule + ['/s$/i', ''], + ]; + + /** + * A list of words which should not be inflected. + * This list is only used by singularize. + */ + private const UNINFLECTED = '/^(abcès|accès|abus|albatros|anchois|anglais|autobus|bois|brebis|carquois|cas|chas|colis|concours|corps|cours|cyprès|décès|devis|discours|dos|embarras|engrais|entrelacs|excès|fils|fois|gâchis|gars|glas|héros|intrus|jars|jus|kermès|lacis|legs|lilas|marais|mars|matelas|mépris|mets|mois|mors|obus|os|palais|paradis|parcours|pardessus|pays|plusieurs|poids|pois|pouls|printemps|processus|progrès|puits|pus|rabais|radis|recors|recours|refus|relais|remords|remous|rictus|rhinocéros|repas|rubis|sans|sas|secours|sens|souris|succès|talus|tapis|tas|taudis|temps|tiers|univers|velours|verglas|vernis|virus)$/i'; + + /** + * {@inheritdoc} + */ + public function singularize(string $plural): array + { + if ($this->isInflectedWord($plural)) { + return [$plural]; + } + + foreach (self::SINGULARIZE_REGEXP as $rule) { + [$regexp, $replace] = $rule; + + if (1 === preg_match($regexp, $plural)) { + return [preg_replace($regexp, $replace, $plural)]; + } + } + + return [$plural]; + } + + /** + * {@inheritdoc} + */ + public function pluralize(string $singular): array + { + if ($this->isInflectedWord($singular)) { + return [$singular]; + } + + foreach (self::PLURALIZE_REGEXP as $rule) { + [$regexp, $replace] = $rule; + + if (1 === preg_match($regexp, $singular)) { + return [preg_replace($regexp, $replace, $singular)]; + } + } + + return [$singular.'s']; + } + + private function isInflectedWord(string $word): bool + { + return 1 === preg_match(self::UNINFLECTED, $word); + } +} diff --git a/vendor/symfony/string/Inflector/InflectorInterface.php b/vendor/symfony/string/Inflector/InflectorInterface.php new file mode 100644 index 0000000..67f2834 --- /dev/null +++ b/vendor/symfony/string/Inflector/InflectorInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Inflector; + +interface InflectorInterface +{ + /** + * Returns the singular forms of a string. + * + * If the method can't determine the form with certainty, several possible singulars are returned. + * + * @return string[] + */ + public function singularize(string $plural): array; + + /** + * Returns the plural forms of a string. + * + * If the method can't determine the form with certainty, several possible plurals are returned. + * + * @return string[] + */ + public function pluralize(string $singular): array; +} diff --git a/vendor/symfony/string/LICENSE b/vendor/symfony/string/LICENSE new file mode 100644 index 0000000..5c7ba05 --- /dev/null +++ b/vendor/symfony/string/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019-2023 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/string/LazyString.php b/vendor/symfony/string/LazyString.php new file mode 100644 index 0000000..3733078 --- /dev/null +++ b/vendor/symfony/string/LazyString.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +/** + * A string whose value is computed lazily by a callback. + * + * @author Nicolas Grekas + */ +class LazyString implements \Stringable, \JsonSerializable +{ + private \Closure|string $value; + + /** + * @param callable|array $callback A callable or a [Closure, method] lazy-callable + */ + public static function fromCallable(callable|array $callback, mixed ...$arguments): static + { + if (\is_array($callback) && !\is_callable($callback) && !(($callback[0] ?? null) instanceof \Closure || 2 < \count($callback))) { + throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a callable or a [Closure, method] lazy-callable, "%s" given.', __METHOD__, '['.implode(', ', array_map('get_debug_type', $callback)).']')); + } + + $lazyString = new static(); + $lazyString->value = static function () use (&$callback, &$arguments, &$value): string { + if (null !== $arguments) { + if (!\is_callable($callback)) { + $callback[0] = $callback[0](); + $callback[1] = $callback[1] ?? '__invoke'; + } + $value = $callback(...$arguments); + $callback = self::getPrettyName($callback); + $arguments = null; + } + + return $value ?? ''; + }; + + return $lazyString; + } + + public static function fromStringable(string|int|float|bool|\Stringable $value): static + { + if (\is_object($value)) { + return static::fromCallable([$value, '__toString']); + } + + $lazyString = new static(); + $lazyString->value = (string) $value; + + return $lazyString; + } + + /** + * Tells whether the provided value can be cast to string. + */ + final public static function isStringable(mixed $value): bool + { + return \is_string($value) || $value instanceof \Stringable || \is_scalar($value); + } + + /** + * Casts scalars and stringable objects to strings. + * + * @throws \TypeError When the provided value is not stringable + */ + final public static function resolve(\Stringable|string|int|float|bool $value): string + { + return $value; + } + + public function __toString(): string + { + if (\is_string($this->value)) { + return $this->value; + } + + try { + return $this->value = ($this->value)(); + } catch (\Throwable $e) { + if (\TypeError::class === \get_class($e) && __FILE__ === $e->getFile()) { + $type = explode(', ', $e->getMessage()); + $type = substr(array_pop($type), 0, -\strlen(' returned')); + $r = new \ReflectionFunction($this->value); + $callback = $r->getStaticVariables()['callback']; + + $e = new \TypeError(sprintf('Return value of %s() passed to %s::fromCallable() must be of the type string, %s returned.', $callback, static::class, $type)); + } + + throw $e; + } + } + + public function __sleep(): array + { + $this->__toString(); + + return ['value']; + } + + public function jsonSerialize(): string + { + return $this->__toString(); + } + + private function __construct() + { + } + + private static function getPrettyName(callable $callback): string + { + if (\is_string($callback)) { + return $callback; + } + + if (\is_array($callback)) { + $class = \is_object($callback[0]) ? get_debug_type($callback[0]) : $callback[0]; + $method = $callback[1]; + } elseif ($callback instanceof \Closure) { + $r = new \ReflectionFunction($callback); + + if (false !== strpos($r->name, '{closure}') || !$class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { + return $r->name; + } + + $class = $class->name; + $method = $r->name; + } else { + $class = get_debug_type($callback); + $method = '__invoke'; + } + + return $class.'::'.$method; + } +} diff --git a/vendor/symfony/string/README.md b/vendor/symfony/string/README.md new file mode 100644 index 0000000..9c7e1e1 --- /dev/null +++ b/vendor/symfony/string/README.md @@ -0,0 +1,14 @@ +String Component +================ + +The String component provides an object-oriented API to strings and deals +with bytes, UTF-8 code points and grapheme clusters in a unified way. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/string.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/string/Resources/data/wcswidth_table_wide.php b/vendor/symfony/string/Resources/data/wcswidth_table_wide.php new file mode 100644 index 0000000..5a647e6 --- /dev/null +++ b/vendor/symfony/string/Resources/data/wcswidth_table_wide.php @@ -0,0 +1,1143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +if (!\function_exists(u::class)) { + function u(?string $string = ''): UnicodeString + { + return new UnicodeString($string ?? ''); + } +} + +if (!\function_exists(b::class)) { + function b(?string $string = ''): ByteString + { + return new ByteString($string ?? ''); + } +} + +if (!\function_exists(s::class)) { + /** + * @return UnicodeString|ByteString + */ + function s(?string $string = ''): AbstractString + { + $string = $string ?? ''; + + return preg_match('//u', $string) ? new UnicodeString($string) : new ByteString($string); + } +} diff --git a/vendor/symfony/string/Slugger/AsciiSlugger.php b/vendor/symfony/string/Slugger/AsciiSlugger.php new file mode 100644 index 0000000..548a6b9 --- /dev/null +++ b/vendor/symfony/string/Slugger/AsciiSlugger.php @@ -0,0 +1,176 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Slugger; + +use Symfony\Component\String\AbstractUnicodeString; +use Symfony\Component\String\UnicodeString; +use Symfony\Contracts\Translation\LocaleAwareInterface; + +if (!interface_exists(LocaleAwareInterface::class)) { + throw new \LogicException('You cannot use the "Symfony\Component\String\Slugger\AsciiSlugger" as the "symfony/translation-contracts" package is not installed. Try running "composer require symfony/translation-contracts".'); +} + +/** + * @author Titouan Galopin + */ +class AsciiSlugger implements SluggerInterface, LocaleAwareInterface +{ + private const LOCALE_TO_TRANSLITERATOR_ID = [ + 'am' => 'Amharic-Latin', + 'ar' => 'Arabic-Latin', + 'az' => 'Azerbaijani-Latin', + 'be' => 'Belarusian-Latin', + 'bg' => 'Bulgarian-Latin', + 'bn' => 'Bengali-Latin', + 'de' => 'de-ASCII', + 'el' => 'Greek-Latin', + 'fa' => 'Persian-Latin', + 'he' => 'Hebrew-Latin', + 'hy' => 'Armenian-Latin', + 'ka' => 'Georgian-Latin', + 'kk' => 'Kazakh-Latin', + 'ky' => 'Kirghiz-Latin', + 'ko' => 'Korean-Latin', + 'mk' => 'Macedonian-Latin', + 'mn' => 'Mongolian-Latin', + 'or' => 'Oriya-Latin', + 'ps' => 'Pashto-Latin', + 'ru' => 'Russian-Latin', + 'sr' => 'Serbian-Latin', + 'sr_Cyrl' => 'Serbian-Latin', + 'th' => 'Thai-Latin', + 'tk' => 'Turkmen-Latin', + 'uk' => 'Ukrainian-Latin', + 'uz' => 'Uzbek-Latin', + 'zh' => 'Han-Latin', + ]; + + private ?string $defaultLocale; + private \Closure|array $symbolsMap = [ + 'en' => ['@' => 'at', '&' => 'and'], + ]; + + /** + * Cache of transliterators per locale. + * + * @var \Transliterator[] + */ + private array $transliterators = []; + + public function __construct(string $defaultLocale = null, array|\Closure $symbolsMap = null) + { + $this->defaultLocale = $defaultLocale; + $this->symbolsMap = $symbolsMap ?? $this->symbolsMap; + } + + /** + * {@inheritdoc} + */ + public function setLocale(string $locale) + { + $this->defaultLocale = $locale; + } + + /** + * {@inheritdoc} + */ + public function getLocale(): string + { + return $this->defaultLocale; + } + + /** + * {@inheritdoc} + */ + public function slug(string $string, string $separator = '-', string $locale = null): AbstractUnicodeString + { + $locale = $locale ?? $this->defaultLocale; + + $transliterator = []; + if ($locale && ('de' === $locale || 0 === strpos($locale, 'de_'))) { + // Use the shortcut for German in UnicodeString::ascii() if possible (faster and no requirement on intl) + $transliterator = ['de-ASCII']; + } elseif (\function_exists('transliterator_transliterate') && $locale) { + $transliterator = (array) $this->createTransliterator($locale); + } + + if ($this->symbolsMap instanceof \Closure) { + // If the symbols map is passed as a closure, there is no need to fallback to the parent locale + // as the closure can just provide substitutions for all locales of interest. + $symbolsMap = $this->symbolsMap; + array_unshift($transliterator, static function ($s) use ($symbolsMap, $locale) { + return $symbolsMap($s, $locale); + }); + } + + $unicodeString = (new UnicodeString($string))->ascii($transliterator); + + if (\is_array($this->symbolsMap)) { + $map = null; + if (isset($this->symbolsMap[$locale])) { + $map = $this->symbolsMap[$locale]; + } else { + $parent = self::getParentLocale($locale); + if ($parent && isset($this->symbolsMap[$parent])) { + $map = $this->symbolsMap[$parent]; + } + } + if ($map) { + foreach ($map as $char => $replace) { + $unicodeString = $unicodeString->replace($char, ' '.$replace.' '); + } + } + } + + return $unicodeString + ->replaceMatches('/[^A-Za-z0-9]++/', $separator) + ->trim($separator) + ; + } + + private function createTransliterator(string $locale): ?\Transliterator + { + if (\array_key_exists($locale, $this->transliterators)) { + return $this->transliterators[$locale]; + } + + // Exact locale supported, cache and return + if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$locale] ?? null) { + return $this->transliterators[$locale] = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id); + } + + // Locale not supported and no parent, fallback to any-latin + if (!$parent = self::getParentLocale($locale)) { + return $this->transliterators[$locale] = null; + } + + // Try to use the parent locale (ie. try "de" for "de_AT") and cache both locales + if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$parent] ?? null) { + $transliterator = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id); + } + + return $this->transliterators[$locale] = $this->transliterators[$parent] = $transliterator ?? null; + } + + private static function getParentLocale(?string $locale): ?string + { + if (!$locale) { + return null; + } + if (false === $str = strrchr($locale, '_')) { + // no parent locale + return null; + } + + return substr($locale, 0, -\strlen($str)); + } +} diff --git a/vendor/symfony/string/Slugger/SluggerInterface.php b/vendor/symfony/string/Slugger/SluggerInterface.php new file mode 100644 index 0000000..c679ed9 --- /dev/null +++ b/vendor/symfony/string/Slugger/SluggerInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Slugger; + +use Symfony\Component\String\AbstractUnicodeString; + +/** + * Creates a URL-friendly slug from a given string. + * + * @author Titouan Galopin + */ +interface SluggerInterface +{ + /** + * Creates a slug for the given string and locale, using appropriate transliteration when needed. + */ + public function slug(string $string, string $separator = '-', string $locale = null): AbstractUnicodeString; +} diff --git a/vendor/symfony/string/UnicodeString.php b/vendor/symfony/string/UnicodeString.php new file mode 100644 index 0000000..70cf4c5 --- /dev/null +++ b/vendor/symfony/string/UnicodeString.php @@ -0,0 +1,358 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +use Symfony\Component\String\Exception\ExceptionInterface; +use Symfony\Component\String\Exception\InvalidArgumentException; + +/** + * Represents a string of Unicode grapheme clusters encoded as UTF-8. + * + * A letter followed by combining characters (accents typically) form what Unicode defines + * as a grapheme cluster: a character as humans mean it in written texts. This class knows + * about the concept and won't split a letter apart from its combining accents. It also + * ensures all string comparisons happen on their canonically-composed representation, + * ignoring e.g. the order in which accents are listed when a letter has many of them. + * + * @see https://unicode.org/reports/tr15/ + * + * @author Nicolas Grekas + * @author Hugo Hamon + * + * @throws ExceptionInterface + */ +class UnicodeString extends AbstractUnicodeString +{ + public function __construct(string $string = '') + { + $this->string = normalizer_is_normalized($string) ? $string : normalizer_normalize($string); + + if (false === $this->string) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + } + + public function append(string ...$suffix): static + { + $str = clone $this; + $str->string = $this->string.(1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix)); + normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); + + if (false === $str->string) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + return $str; + } + + public function chunk(int $length = 1): array + { + if (1 > $length) { + throw new InvalidArgumentException('The chunk length must be greater than zero.'); + } + + if ('' === $this->string) { + return []; + } + + $rx = '/('; + while (65535 < $length) { + $rx .= '\X{65535}'; + $length -= 65535; + } + $rx .= '\X{'.$length.'})/u'; + + $str = clone $this; + $chunks = []; + + foreach (preg_split($rx, $this->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY) as $chunk) { + $str->string = $chunk; + $chunks[] = clone $str; + } + + return $chunks; + } + + public function endsWith(string|iterable|AbstractString $suffix): bool + { + if ($suffix instanceof AbstractString) { + $suffix = $suffix->string; + } elseif (!\is_string($suffix)) { + return parent::endsWith($suffix); + } + + $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; + normalizer_is_normalized($suffix, $form) ?: $suffix = normalizer_normalize($suffix, $form); + + if ('' === $suffix || false === $suffix) { + return false; + } + + if ($this->ignoreCase) { + return 0 === mb_stripos(grapheme_extract($this->string, \strlen($suffix), \GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix)), $suffix, 0, 'UTF-8'); + } + + return $suffix === grapheme_extract($this->string, \strlen($suffix), \GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix)); + } + + public function equalsTo(string|iterable|AbstractString $string): bool + { + if ($string instanceof AbstractString) { + $string = $string->string; + } elseif (!\is_string($string)) { + return parent::equalsTo($string); + } + + $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; + normalizer_is_normalized($string, $form) ?: $string = normalizer_normalize($string, $form); + + if ('' !== $string && false !== $string && $this->ignoreCase) { + return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8'); + } + + return $string === $this->string; + } + + public function indexOf(string|iterable|AbstractString $needle, int $offset = 0): ?int + { + if ($needle instanceof AbstractString) { + $needle = $needle->string; + } elseif (!\is_string($needle)) { + return parent::indexOf($needle, $offset); + } + + $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; + normalizer_is_normalized($needle, $form) ?: $needle = normalizer_normalize($needle, $form); + + if ('' === $needle || false === $needle) { + return null; + } + + try { + $i = $this->ignoreCase ? grapheme_stripos($this->string, $needle, $offset) : grapheme_strpos($this->string, $needle, $offset); + } catch (\ValueError $e) { + return null; + } + + return false === $i ? null : $i; + } + + public function indexOfLast(string|iterable|AbstractString $needle, int $offset = 0): ?int + { + if ($needle instanceof AbstractString) { + $needle = $needle->string; + } elseif (!\is_string($needle)) { + return parent::indexOfLast($needle, $offset); + } + + $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; + normalizer_is_normalized($needle, $form) ?: $needle = normalizer_normalize($needle, $form); + + if ('' === $needle || false === $needle) { + return null; + } + + $string = $this->string; + + if (0 > $offset) { + // workaround https://bugs.php.net/74264 + if (0 > $offset += grapheme_strlen($needle)) { + $string = grapheme_substr($string, 0, $offset); + } + $offset = 0; + } + + $i = $this->ignoreCase ? grapheme_strripos($string, $needle, $offset) : grapheme_strrpos($string, $needle, $offset); + + return false === $i ? null : $i; + } + + public function join(array $strings, string $lastGlue = null): static + { + $str = parent::join($strings, $lastGlue); + normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); + + return $str; + } + + public function length(): int + { + return grapheme_strlen($this->string); + } + + public function normalize(int $form = self::NFC): static + { + $str = clone $this; + + if (\in_array($form, [self::NFC, self::NFKC], true)) { + normalizer_is_normalized($str->string, $form) ?: $str->string = normalizer_normalize($str->string, $form); + } elseif (!\in_array($form, [self::NFD, self::NFKD], true)) { + throw new InvalidArgumentException('Unsupported normalization form.'); + } elseif (!normalizer_is_normalized($str->string, $form)) { + $str->string = normalizer_normalize($str->string, $form); + $str->ignoreCase = null; + } + + return $str; + } + + public function prepend(string ...$prefix): static + { + $str = clone $this; + $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$this->string; + normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); + + if (false === $str->string) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + return $str; + } + + public function replace(string $from, string $to): static + { + $str = clone $this; + normalizer_is_normalized($from) ?: $from = normalizer_normalize($from); + + if ('' !== $from && false !== $from) { + $tail = $str->string; + $result = ''; + $indexOf = $this->ignoreCase ? 'grapheme_stripos' : 'grapheme_strpos'; + + while ('' !== $tail && false !== $i = $indexOf($tail, $from)) { + $slice = grapheme_substr($tail, 0, $i); + $result .= $slice.$to; + $tail = substr($tail, \strlen($slice) + \strlen($from)); + } + + $str->string = $result.$tail; + normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); + + if (false === $str->string) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + } + + return $str; + } + + public function replaceMatches(string $fromRegexp, string|callable $to): static + { + $str = parent::replaceMatches($fromRegexp, $to); + normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); + + return $str; + } + + public function slice(int $start = 0, int $length = null): static + { + $str = clone $this; + + $str->string = (string) grapheme_substr($this->string, $start, $length ?? 2147483647); + + return $str; + } + + public function splice(string $replacement, int $start = 0, int $length = null): static + { + $str = clone $this; + + $start = $start ? \strlen(grapheme_substr($this->string, 0, $start)) : 0; + $length = $length ? \strlen(grapheme_substr($this->string, $start, $length ?? 2147483647)) : $length; + $str->string = substr_replace($this->string, $replacement, $start, $length ?? 2147483647); + normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); + + if (false === $str->string) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + return $str; + } + + public function split(string $delimiter, int $limit = null, int $flags = null): array + { + if (1 > $limit = $limit ?? 2147483647) { + throw new InvalidArgumentException('Split limit must be a positive integer.'); + } + + if ('' === $delimiter) { + throw new InvalidArgumentException('Split delimiter is empty.'); + } + + if (null !== $flags) { + return parent::split($delimiter.'u', $limit, $flags); + } + + normalizer_is_normalized($delimiter) ?: $delimiter = normalizer_normalize($delimiter); + + if (false === $delimiter) { + throw new InvalidArgumentException('Split delimiter is not a valid UTF-8 string.'); + } + + $str = clone $this; + $tail = $this->string; + $chunks = []; + $indexOf = $this->ignoreCase ? 'grapheme_stripos' : 'grapheme_strpos'; + + while (1 < $limit && false !== $i = $indexOf($tail, $delimiter)) { + $str->string = grapheme_substr($tail, 0, $i); + $chunks[] = clone $str; + $tail = substr($tail, \strlen($str->string) + \strlen($delimiter)); + --$limit; + } + + $str->string = $tail; + $chunks[] = clone $str; + + return $chunks; + } + + public function startsWith(string|iterable|AbstractString $prefix): bool + { + if ($prefix instanceof AbstractString) { + $prefix = $prefix->string; + } elseif (!\is_string($prefix)) { + return parent::startsWith($prefix); + } + + $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; + normalizer_is_normalized($prefix, $form) ?: $prefix = normalizer_normalize($prefix, $form); + + if ('' === $prefix || false === $prefix) { + return false; + } + + if ($this->ignoreCase) { + return 0 === mb_stripos(grapheme_extract($this->string, \strlen($prefix), \GRAPHEME_EXTR_MAXBYTES), $prefix, 0, 'UTF-8'); + } + + return $prefix === grapheme_extract($this->string, \strlen($prefix), \GRAPHEME_EXTR_MAXBYTES); + } + + public function __wakeup() + { + if (!\is_string($this->string)) { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + + normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string); + } + + public function __clone() + { + if (null === $this->ignoreCase) { + normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string); + } + + $this->ignoreCase = false; + } +} diff --git a/vendor/symfony/string/composer.json b/vendor/symfony/string/composer.json new file mode 100644 index 0000000..187323f --- /dev/null +++ b/vendor/symfony/string/composer.json @@ -0,0 +1,42 @@ +{ + "name": "symfony/string", + "type": "library", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "keywords": ["string", "utf8", "utf-8", "grapheme", "i18n", "unicode"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=8.0.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\String\\": "" }, + "files": [ "Resources/functions.php" ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/vendor/symfony/translation-contracts/.gitignore b/vendor/symfony/translation-contracts/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/translation-contracts/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/translation-contracts/CHANGELOG.md b/vendor/symfony/translation-contracts/CHANGELOG.md new file mode 100644 index 0000000..7932e26 --- /dev/null +++ b/vendor/symfony/translation-contracts/CHANGELOG.md @@ -0,0 +1,5 @@ +CHANGELOG +========= + +The changelog is maintained for all Symfony contracts at the following URL: +https://github.com/symfony/contracts/blob/main/CHANGELOG.md diff --git a/vendor/symfony/translation-contracts/LICENSE b/vendor/symfony/translation-contracts/LICENSE new file mode 100644 index 0000000..74cdc2d --- /dev/null +++ b/vendor/symfony/translation-contracts/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-2022 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 100644 index 0000000..6923b97 --- /dev/null +++ b/vendor/symfony/translation-contracts/LocaleAwareInterface.php @@ -0,0 +1,27 @@ + + * + * 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. + * + * @throws \InvalidArgumentException If the locale contains invalid characters + */ + public function setLocale(string $locale); + + /** + * Returns the current locale. + */ + public function getLocale(): string; +} diff --git a/vendor/symfony/translation-contracts/README.md b/vendor/symfony/translation-contracts/README.md new file mode 100644 index 0000000..42e5c51 --- /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/main/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 100644 index 0000000..4e5999d --- /dev/null +++ b/vendor/symfony/translation-contracts/Test/TranslatorTest.php @@ -0,0 +1,384 @@ + + * + * 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 +{ + private $defaultLocale; + + protected function setUp(): void + { + $this->defaultLocale = \Locale::getDefault(); + \Locale::setDefault('en'); + } + + protected function tearDown(): void + { + \Locale::setDefault($this->defaultLocale); + } + + public function getTranslator(): TranslatorInterface + { + 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(); + + $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number])); + } + + /** + * @requires extension intl + * + * @dataProvider getTransChoiceTests + */ + public function testTransChoiceWithDefaultLocale($expected, $id, $number) + { + $translator = $this->getTranslator(); + + $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number])); + } + + /** + * @dataProvider getTransChoiceTests + */ + public function testTransChoiceWithEnUsPosix($expected, $id, $number) + { + $translator = $this->getTranslator(); + $translator->setLocale('en_US_POSIX'); + + $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number])); + } + + public function testGetSetLocale() + { + $translator = $this->getTranslator(); + + $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, $locale = null) + { + $translator = $this->getTranslator(); + + $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number], null, $locale)); + } + + public function testReturnMessageIfExactlyOneStandardRuleIsGiven() + { + $translator = $this->getTranslator(); + + $this->assertEquals('There are two apples', $translator->trans('There are two apples', ['%count%' => 2])); + } + + /** + * @dataProvider getNonMatchingMessages + */ + public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number) + { + $this->expectException(\InvalidArgumentException::class); + $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], + + // Floating values + ['1.5 liters', '%count% liter|%count% liters', 1.5], + ['1.5 litre', '%count% litre|%count% litres', 1.5, 'fr'], + + // Negative values + ['-1 degree', '%count% degree|%count% degrees', -1], + ['-1 degré', '%count% degré|%count% degrés', -1], + ['-1.5 degrees', '%count% degree|%count% degrees', -1.5], + ['-1.5 degré', '%count% degré|%count% degrés', -1.5, 'fr'], + ['-2 degrees', '%count% degree|%count% degrees', -2], + ['-2 degrés', '%count% degré|%count% degrés', -2], + ]; + } + + /** + * @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. + */ + public function successLangcodes(): array + { + return [ + ['1', ['ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky']], + ['2', ['nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM', 'en_US_POSIX']], + ['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(): array + { + 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 + */ + protected function validateMatrix(string $nplural, array $matrix, bool $expectSuccess = true) + { + foreach ($matrix as $langCode => $data) { + $indexes = array_flip($data); + if ($expectSuccess) { + $this->assertCount($nplural, $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/TranslatableInterface.php b/vendor/symfony/translation-contracts/TranslatableInterface.php new file mode 100644 index 0000000..47fd6fa --- /dev/null +++ b/vendor/symfony/translation-contracts/TranslatableInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Translation; + +/** + * @author Nicolas Grekas + */ +interface TranslatableInterface +{ + public function trans(TranslatorInterface $translator, string $locale = null): string; +} diff --git a/vendor/symfony/translation-contracts/TranslatorInterface.php b/vendor/symfony/translation-contracts/TranslatorInterface.php new file mode 100644 index 0000000..018db07 --- /dev/null +++ b/vendor/symfony/translation-contracts/TranslatorInterface.php @@ -0,0 +1,68 @@ + + * + * 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 + */ +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 + * + * @throws \InvalidArgumentException If the locale contains invalid characters + */ + public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string; + + /** + * Returns the default locale. + */ + public function getLocale(): string; +} diff --git a/vendor/symfony/translation-contracts/TranslatorTrait.php b/vendor/symfony/translation-contracts/TranslatorTrait.php new file mode 100644 index 0000000..9c264bd --- /dev/null +++ b/vendor/symfony/translation-contracts/TranslatorTrait.php @@ -0,0 +1,260 @@ + + * + * 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 + */ +trait TranslatorTrait +{ + private ?string $locale = null; + + /** + * {@inheritdoc} + */ + public function setLocale(string $locale) + { + $this->locale = $locale; + } + + /** + * {@inheritdoc} + */ + public function getLocale(): string + { + return $this->locale ?: (class_exists(\Locale::class) ? \Locale::getDefault() : 'en'); + } + + /** + * {@inheritdoc} + */ + public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null): string + { + if (null === $id || '' === $id) { + return ''; + } + + if (!isset($parameters['%count%']) || !is_numeric($parameters['%count%'])) { + return strtr($id, $parameters); + } + + $number = (float) $parameters['%count%']; + $locale = $locale ?: $this->getLocale(); + + $parts = []; + if (preg_match('/^\|++$/', $id)) { + $parts = explode('|', $id); + } elseif (preg_match_all('/(?:\|\||[^\|])++/', $id, $matches)) { + $parts = $matches[0]; + } + + $intervalRegexp = <<<'EOF' +/^(?P + ({\s* + (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*) + \s*}) + + | + + (?P[\[\]]) + \s* + (?P-Inf|\-?\d+(\.\d+)?) + \s*,\s* + (?P\+?Inf|\-?\d+(\.\d+)?) + \s* + (?P[\[\]]) +)\s*(?P.*?)$/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(float $number, string $locale): int + { + $number = abs($number); + + switch ('pt_BR' !== $locale && 'en_US_POSIX' !== $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 'en_US_POSIX': + 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 ($number < 2) ? 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 100644 index 0000000..875242f --- /dev/null +++ b/vendor/symfony/translation-contracts/composer.json @@ -0,0 +1,37 @@ +{ + "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": ">=8.0.2" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "autoload": { + "psr-4": { "Symfony\\Contracts\\Translation\\": "" } + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + } +} diff --git a/vendor/symfony/translation/CHANGELOG.md b/vendor/symfony/translation/CHANGELOG.md new file mode 100644 index 0000000..160b5e6 --- /dev/null +++ b/vendor/symfony/translation/CHANGELOG.md @@ -0,0 +1,176 @@ +CHANGELOG +========= + +5.4 +--- + + * Add `github` format & autodetection to render errors as annotations when + running the XLIFF linter command in a Github Actions environment. + * Translation providers are not experimental anymore + +5.3 +--- + + * Add `translation:pull` and `translation:push` commands to manage translations with third-party providers + * Add `TranslatorBagInterface::getCatalogues` method + * Add support to load XLIFF string in `XliffFileLoader` + +5.2.0 +----- + + * added support for calling `trans` with ICU formatted messages + * added `PseudoLocalizationTranslator` + * added `TranslatableMessage` objects that represent a message that can be translated + * added the `t()` function to easily create `TranslatableMessage` objects + * Added support for extracting messages from `TranslatableMessage` objects + +5.1.0 +----- + + * added support for `name` attribute on `unit` element from xliff2 to be used as a translation key instead of always the `source` element + +5.0.0 +----- + + * removed support for using `null` as the locale in `Translator` + * removed `TranslatorInterface` + * removed `MessageSelector` + * removed `ChoiceMessageFormatterInterface` + * removed `PluralizationRule` + * removed `Interval` + * removed `transChoice()` methods, use the trans() method instead with a %count% parameter + * removed `FileDumper::setBackup()` and `TranslationWriter::disableBackup()` + * removed `MessageFormatter::choiceFormat()` + * added argument `$filename` to `PhpExtractor::parseTokens()` + * removed support for implicit STDIN usage in the `lint:xliff` command, use `lint:xliff -` (append a dash) instead to make it explicit. + +4.4.0 +----- + + * deprecated support for using `null` as the locale in `Translator` + * deprecated accepting STDIN implicitly when using the `lint:xliff` command, use `lint:xliff -` (append a dash) instead to make it explicit. + * Marked the `TranslationDataCollector` class as `@final`. + +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 `` section to the Xliff 2.0 dumper. + * Improved Xliff 2.0 loader to load `` 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 100644 index 0000000..43a52fa --- /dev/null +++ b/vendor/symfony/translation/Catalogue/AbstractOperation.php @@ -0,0 +1,203 @@ + + * + * 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 + */ +abstract class AbstractOperation implements OperationInterface +{ + public const OBSOLETE_BATCH = 'obsolete'; + public const NEW_BATCH = 'new'; + public const ALL_BATCH = 'all'; + + 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(): array + { + if (null === $this->domains) { + $domains = []; + foreach ([$this->source, $this->target] as $catalogue) { + foreach ($catalogue->getDomains() as $domain) { + $domains[$domain] = $domain; + + if ($catalogue->all($domainIcu = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX)) { + $domains[$domainIcu] = $domainIcu; + } + } + } + + $this->domains = array_values($domains); + } + + return $this->domains; + } + + /** + * {@inheritdoc} + */ + public function getMessages(string $domain): array + { + if (!\in_array($domain, $this->getDomains())) { + throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain)); + } + + if (!isset($this->messages[$domain][self::ALL_BATCH])) { + $this->processDomain($domain); + } + + return $this->messages[$domain][self::ALL_BATCH]; + } + + /** + * {@inheritdoc} + */ + public function getNewMessages(string $domain): array + { + if (!\in_array($domain, $this->getDomains())) { + throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain)); + } + + if (!isset($this->messages[$domain][self::NEW_BATCH])) { + $this->processDomain($domain); + } + + return $this->messages[$domain][self::NEW_BATCH]; + } + + /** + * {@inheritdoc} + */ + public function getObsoleteMessages(string $domain): array + { + if (!\in_array($domain, $this->getDomains())) { + throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain)); + } + + if (!isset($this->messages[$domain][self::OBSOLETE_BATCH])) { + $this->processDomain($domain); + } + + return $this->messages[$domain][self::OBSOLETE_BATCH]; + } + + /** + * {@inheritdoc} + */ + public function getResult(): MessageCatalogueInterface + { + foreach ($this->getDomains() as $domain) { + if (!isset($this->messages[$domain])) { + $this->processDomain($domain); + } + } + + return $this->result; + } + + /** + * @param self::*_BATCH $batch + */ + public function moveMessagesToIntlDomainsIfPossible(string $batch = self::ALL_BATCH): void + { + // If MessageFormatter class does not exists, intl domains are not supported. + if (!class_exists(\MessageFormatter::class)) { + return; + } + + foreach ($this->getDomains() as $domain) { + $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; + switch ($batch) { + case self::OBSOLETE_BATCH: $messages = $this->getObsoleteMessages($domain); break; + case self::NEW_BATCH: $messages = $this->getNewMessages($domain); break; + case self::ALL_BATCH: $messages = $this->getMessages($domain); break; + default: throw new \InvalidArgumentException(sprintf('$batch argument must be one of ["%s", "%s", "%s"].', self::ALL_BATCH, self::NEW_BATCH, self::OBSOLETE_BATCH)); + } + + if (!$messages || (!$this->source->all($intlDomain) && $this->source->all($domain))) { + continue; + } + + $result = $this->getResult(); + $allIntlMessages = $result->all($intlDomain); + $currentMessages = array_diff_key($messages, $result->all($domain)); + $result->replace($currentMessages, $domain); + $result->replace($allIntlMessages + $messages, $intlDomain); + } + } + + /** + * 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(string $domain); +} diff --git a/vendor/symfony/translation/Catalogue/MergeOperation.php b/vendor/symfony/translation/Catalogue/MergeOperation.php new file mode 100644 index 0000000..87db2fb --- /dev/null +++ b/vendor/symfony/translation/Catalogue/MergeOperation.php @@ -0,0 +1,60 @@ + + * + * 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 + */ +class MergeOperation extends AbstractOperation +{ + /** + * {@inheritdoc} + */ + protected function processDomain(string $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; + $d = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain; + $this->result->add([$id => $message], $d); + if (null !== $keyMetadata = $this->source->getMetadata($id, $d)) { + $this->result->setMetadata($id, $keyMetadata, $d); + } + } + + 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; + $d = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain; + $this->result->add([$id => $message], $d); + if (null !== $keyMetadata = $this->target->getMetadata($id, $d)) { + $this->result->setMetadata($id, $keyMetadata, $d); + } + } + } + } +} diff --git a/vendor/symfony/translation/Catalogue/OperationInterface.php b/vendor/symfony/translation/Catalogue/OperationInterface.php new file mode 100644 index 0000000..1fe9534 --- /dev/null +++ b/vendor/symfony/translation/Catalogue/OperationInterface.php @@ -0,0 +1,61 @@ + + * + * 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 + */ +interface OperationInterface +{ + /** + * Returns domains affected by operation. + */ + public function getDomains(): array; + + /** + * Returns all valid messages ('all') after operation. + */ + public function getMessages(string $domain): array; + + /** + * Returns new messages ('new') after operation. + */ + public function getNewMessages(string $domain): array; + + /** + * Returns obsolete messages ('obsolete') after operation. + */ + public function getObsoleteMessages(string $domain): array; + + /** + * Returns resulting catalogue ('result'). + */ + public function getResult(): MessageCatalogueInterface; +} diff --git a/vendor/symfony/translation/Catalogue/TargetOperation.php b/vendor/symfony/translation/Catalogue/TargetOperation.php new file mode 100644 index 0000000..682b575 --- /dev/null +++ b/vendor/symfony/translation/Catalogue/TargetOperation.php @@ -0,0 +1,74 @@ + + * + * 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 + */ +class TargetOperation extends AbstractOperation +{ + /** + * {@inheritdoc} + */ + protected function processDomain(string $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; + $d = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain; + $this->result->add([$id => $message], $d); + if (null !== $keyMetadata = $this->source->getMetadata($id, $d)) { + $this->result->setMetadata($id, $keyMetadata, $d); + } + } 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; + $d = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain; + $this->result->add([$id => $message], $d); + if (null !== $keyMetadata = $this->target->getMetadata($id, $d)) { + $this->result->setMetadata($id, $keyMetadata, $d); + } + } + } + } +} diff --git a/vendor/symfony/translation/Command/TranslationPullCommand.php b/vendor/symfony/translation/Command/TranslationPullCommand.php new file mode 100644 index 0000000..f30e64d --- /dev/null +++ b/vendor/symfony/translation/Command/TranslationPullCommand.php @@ -0,0 +1,187 @@ + + * + * 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\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +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\Catalogue\TargetOperation; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Provider\TranslationProviderCollection; +use Symfony\Component\Translation\Reader\TranslationReaderInterface; +use Symfony\Component\Translation\Writer\TranslationWriterInterface; + +/** + * @author Mathieu Santostefano + */ +#[AsCommand(name: 'translation:pull', description: 'Pull translations from a given provider.')] +final class TranslationPullCommand extends Command +{ + use TranslationTrait; + + private $providerCollection; + private $writer; + private $reader; + private string $defaultLocale; + private array $transPaths; + private array $enabledLocales; + + public function __construct(TranslationProviderCollection $providerCollection, TranslationWriterInterface $writer, TranslationReaderInterface $reader, string $defaultLocale, array $transPaths = [], array $enabledLocales = []) + { + $this->providerCollection = $providerCollection; + $this->writer = $writer; + $this->reader = $reader; + $this->defaultLocale = $defaultLocale; + $this->transPaths = $transPaths; + $this->enabledLocales = $enabledLocales; + + parent::__construct(); + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestArgumentValuesFor('provider')) { + $suggestions->suggestValues($this->providerCollection->keys()); + + return; + } + + if ($input->mustSuggestOptionValuesFor('domains')) { + $provider = $this->providerCollection->get($input->getArgument('provider')); + + if ($provider && method_exists($provider, 'getDomains')) { + $domains = $provider->getDomains(); + $suggestions->suggestValues($domains); + } + + return; + } + + if ($input->mustSuggestOptionValuesFor('locales')) { + $suggestions->suggestValues($this->enabledLocales); + + return; + } + + if ($input->mustSuggestOptionValuesFor('format')) { + $suggestions->suggestValues(['php', 'xlf', 'xlf12', 'xlf20', 'po', 'mo', 'yml', 'yaml', 'ts', 'csv', 'json', 'ini', 'res']); + } + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $keys = $this->providerCollection->keys(); + $defaultProvider = 1 === \count($keys) ? $keys[0] : null; + + $this + ->setDefinition([ + new InputArgument('provider', null !== $defaultProvider ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'The provider to pull translations from.', $defaultProvider), + new InputOption('force', null, InputOption::VALUE_NONE, 'Override existing translations with provider ones (it will delete not synchronized messages).'), + new InputOption('intl-icu', null, InputOption::VALUE_NONE, 'Associated to --force option, it will write messages in "%domain%+intl-icu.%locale%.xlf" files.'), + new InputOption('domains', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the domains to pull.'), + new InputOption('locales', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the locales to pull.'), + new InputOption('format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format.', 'xlf12'), + ]) + ->setHelp(<<<'EOF' +The %command.name% command pulls translations from the given provider. Only +new translations are pulled, existing ones are not overwritten. + +You can overwrite existing translations (and remove the missing ones on local side) by using the --force flag: + + php %command.full_name% --force provider + +Full example: + + php %command.full_name% provider --force --domains=messages --domains=validators --locales=en + +This command pulls all translations associated with the messages and validators domains for the en locale. +Local translations for the specified domains and locale are deleted if they're not present on the provider and overwritten if it's the case. +Local translations for others domains and locales are ignored. +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $provider = $this->providerCollection->get($input->getArgument('provider')); + $force = $input->getOption('force'); + $intlIcu = $input->getOption('intl-icu'); + $locales = $input->getOption('locales') ?: $this->enabledLocales; + $domains = $input->getOption('domains'); + $format = $input->getOption('format'); + $xliffVersion = '1.2'; + + if ($intlIcu && !$force) { + $io->note('--intl-icu option only has an effect when used with --force. Here, it will be ignored.'); + } + + switch ($format) { + case 'xlf20': $xliffVersion = '2.0'; + // no break + case 'xlf12': $format = 'xlf'; + } + + $writeOptions = [ + 'path' => end($this->transPaths), + 'xliff_version' => $xliffVersion, + 'default_locale' => $this->defaultLocale, + ]; + + if (!$domains) { + $domains = $provider->getDomains(); + } + + $providerTranslations = $provider->read($domains, $locales); + + if ($force) { + foreach ($providerTranslations->getCatalogues() as $catalogue) { + $operation = new TargetOperation(new MessageCatalogue($catalogue->getLocale()), $catalogue); + if ($intlIcu) { + $operation->moveMessagesToIntlDomainsIfPossible(); + } + $this->writer->write($operation->getResult(), $format, $writeOptions); + } + + $io->success(sprintf('Local translations has been updated from "%s" (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); + + return 0; + } + + $localTranslations = $this->readLocalTranslations($locales, $domains, $this->transPaths); + + // Append pulled translations to local ones. + $localTranslations->addBag($providerTranslations->diff($localTranslations)); + + foreach ($localTranslations->getCatalogues() as $catalogue) { + $this->writer->write($catalogue, $format, $writeOptions); + } + + $io->success(sprintf('New translations from "%s" has been written locally (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); + + return 0; + } +} diff --git a/vendor/symfony/translation/Command/TranslationPushCommand.php b/vendor/symfony/translation/Command/TranslationPushCommand.php new file mode 100644 index 0000000..795e68c --- /dev/null +++ b/vendor/symfony/translation/Command/TranslationPushCommand.php @@ -0,0 +1,188 @@ + + * + * 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\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Exception\InvalidArgumentException; +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\Provider\FilteringProvider; +use Symfony\Component\Translation\Provider\TranslationProviderCollection; +use Symfony\Component\Translation\Reader\TranslationReaderInterface; +use Symfony\Component\Translation\TranslatorBag; + +/** + * @author Mathieu Santostefano + */ +#[AsCommand(name: 'translation:push', description: 'Push translations to a given provider.')] +final class TranslationPushCommand extends Command +{ + use TranslationTrait; + + private $providers; + private $reader; + private array $transPaths; + private array $enabledLocales; + + public function __construct(TranslationProviderCollection $providers, TranslationReaderInterface $reader, array $transPaths = [], array $enabledLocales = []) + { + $this->providers = $providers; + $this->reader = $reader; + $this->transPaths = $transPaths; + $this->enabledLocales = $enabledLocales; + + parent::__construct(); + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestArgumentValuesFor('provider')) { + $suggestions->suggestValues($this->providers->keys()); + + return; + } + + if ($input->mustSuggestOptionValuesFor('domains')) { + $provider = $this->providers->get($input->getArgument('provider')); + + if ($provider && method_exists($provider, 'getDomains')) { + $domains = $provider->getDomains(); + $suggestions->suggestValues($domains); + } + + return; + } + + if ($input->mustSuggestOptionValuesFor('locales')) { + $suggestions->suggestValues($this->enabledLocales); + } + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $keys = $this->providers->keys(); + $defaultProvider = 1 === \count($keys) ? $keys[0] : null; + + $this + ->setDefinition([ + new InputArgument('provider', null !== $defaultProvider ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'The provider to push translations to.', $defaultProvider), + new InputOption('force', null, InputOption::VALUE_NONE, 'Override existing translations with local ones (it will delete not synchronized messages).'), + new InputOption('delete-missing', null, InputOption::VALUE_NONE, 'Delete translations available on provider but not locally.'), + new InputOption('domains', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the domains to push.'), + new InputOption('locales', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the locales to push.', $this->enabledLocales), + ]) + ->setHelp(<<<'EOF' +The %command.name% command pushes translations to the given provider. Only new +translations are pushed, existing ones are not overwritten. + +You can overwrite existing translations by using the --force flag: + + php %command.full_name% --force provider + +You can delete provider translations which are not present locally by using the --delete-missing flag: + + php %command.full_name% --delete-missing provider + +Full example: + + php %command.full_name% provider --force --delete-missing --domains=messages --domains=validators --locales=en + +This command pushes all translations associated with the messages and validators domains for the en locale. +Provider translations for the specified domains and locale are deleted if they're not present locally and overwritten if it's the case. +Provider translations for others domains and locales are ignored. +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $provider = $this->providers->get($input->getArgument('provider')); + + if (!$this->enabledLocales) { + throw new InvalidArgumentException(sprintf('You must define "framework.translator.enabled_locales" or "framework.translator.providers.%s.locales" config key in order to work with translation providers.', parse_url($provider, \PHP_URL_SCHEME))); + } + + $io = new SymfonyStyle($input, $output); + $domains = $input->getOption('domains'); + $locales = $input->getOption('locales'); + $force = $input->getOption('force'); + $deleteMissing = $input->getOption('delete-missing'); + + if (!$domains && $provider instanceof FilteringProvider) { + $domains = $provider->getDomains(); + } + + // Reading local translations must be done after retrieving the domains from the provider + // in order to manage only translations from configured domains + $localTranslations = $this->readLocalTranslations($locales, $domains, $this->transPaths); + + if (!$domains) { + $domains = $this->getDomainsFromTranslatorBag($localTranslations); + } + + if (!$deleteMissing && $force) { + $provider->write($localTranslations); + + $io->success(sprintf('All local translations has been sent to "%s" (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); + + return 0; + } + + $providerTranslations = $provider->read($domains, $locales); + + if ($deleteMissing) { + $provider->delete($providerTranslations->diff($localTranslations)); + + $io->success(sprintf('Missing translations on "%s" has been deleted (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); + + // Read provider translations again, after missing translations deletion, + // to avoid push freshly deleted translations. + $providerTranslations = $provider->read($domains, $locales); + } + + $translationsToWrite = $localTranslations->diff($providerTranslations); + + if ($force) { + $translationsToWrite->addBag($localTranslations->intersect($providerTranslations)); + } + + $provider->write($translationsToWrite); + + $io->success(sprintf('%s local translations has been sent to "%s" (for "%s" locale(s), and "%s" domain(s)).', $force ? 'All' : 'New', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); + + return 0; + } + + private function getDomainsFromTranslatorBag(TranslatorBag $translatorBag): array + { + $domains = []; + + foreach ($translatorBag->getCatalogues() as $catalogue) { + $domains += $catalogue->getDomains(); + } + + return array_unique($domains); + } +} diff --git a/vendor/symfony/translation/Command/TranslationTrait.php b/vendor/symfony/translation/Command/TranslationTrait.php new file mode 100644 index 0000000..eafaffd --- /dev/null +++ b/vendor/symfony/translation/Command/TranslationTrait.php @@ -0,0 +1,77 @@ + + * + * 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\Translation\MessageCatalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; +use Symfony\Component\Translation\TranslatorBag; + +/** + * @internal + */ +trait TranslationTrait +{ + private function readLocalTranslations(array $locales, array $domains, array $transPaths): TranslatorBag + { + $bag = new TranslatorBag(); + + foreach ($locales as $locale) { + $catalogue = new MessageCatalogue($locale); + foreach ($transPaths as $path) { + $this->reader->read($path, $catalogue); + } + + if ($domains) { + foreach ($domains as $domain) { + $bag->addCatalogue($this->filterCatalogue($catalogue, $domain)); + } + } else { + $bag->addCatalogue($catalogue); + } + } + + return $bag; + } + + private function filterCatalogue(MessageCatalogue $catalogue, string $domain): MessageCatalogue + { + $filteredCatalogue = new MessageCatalogue($catalogue->getLocale()); + + // extract intl-icu messages only + $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; + if ($intlMessages = $catalogue->all($intlDomain)) { + $filteredCatalogue->add($intlMessages, $intlDomain); + } + + // extract all messages and subtract intl-icu messages + if ($messages = array_diff($catalogue->all($domain), $intlMessages)) { + $filteredCatalogue->add($messages, $domain); + } + foreach ($catalogue->getResources() as $resource) { + $filteredCatalogue->addResource($resource); + } + + if ($metadata = $catalogue->getMetadata('', $intlDomain)) { + foreach ($metadata as $k => $v) { + $filteredCatalogue->setMetadata($k, $v, $intlDomain); + } + } + + if ($metadata = $catalogue->getMetadata('', $domain)) { + foreach ($metadata as $k => $v) { + $filteredCatalogue->setMetadata($k, $v, $domain); + } + } + + return $filteredCatalogue; + } +} diff --git a/vendor/symfony/translation/Command/XliffLintCommand.php b/vendor/symfony/translation/Command/XliffLintCommand.php new file mode 100644 index 0000000..f062fb7 --- /dev/null +++ b/vendor/symfony/translation/Command/XliffLintCommand.php @@ -0,0 +1,284 @@ + + * + * 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\Attribute\AsCommand; +use Symfony\Component\Console\CI\GithubActionReporter; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +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\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Util\XliffUtils; + +/** + * Validates XLIFF files syntax and outputs encountered errors. + * + * @author Grégoire Pineau + * @author Robin Chalas + * @author Javier Eguiluz + */ +#[AsCommand(name: 'lint:xliff', description: 'Lint an XLIFF file and outputs encountered errors')] +class XliffLintCommand extends Command +{ + private string $format; + private bool $displayCorrectFiles; + private ?\Closure $directoryIteratorProvider; + private ?\Closure $isReadableProvider; + private bool $requireStrictFileNames; + + public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null, bool $requireStrictFileNames = true) + { + parent::__construct($name); + + $this->directoryIteratorProvider = null === $directoryIteratorProvider || $directoryIteratorProvider instanceof \Closure ? $directoryIteratorProvider : \Closure::fromCallable($directoryIteratorProvider); + $this->isReadableProvider = null === $isReadableProvider || $isReadableProvider instanceof \Closure ? $isReadableProvider : \Closure::fromCallable($isReadableProvider); + $this->requireStrictFileNames = $requireStrictFileNames; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN') + ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format') + ->setHelp(<<%command.name% command lints an XLIFF file and outputs to STDOUT +the first encountered syntax error. + +You can validates XLIFF contents passed from STDIN: + + cat filename | php %command.full_name% - + +You can also validate the syntax of a file: + + php %command.full_name% filename + +Or of a whole directory: + + php %command.full_name% dirname + php %command.full_name% dirname --format=json + +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $filenames = (array) $input->getArgument('filename'); + $this->format = $input->getOption('format') ?? (GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt'); + $this->displayCorrectFiles = $output->isVerbose(); + + if (['-'] === $filenames) { + return $this->display($io, [$this->validate(file_get_contents('php://stdin'))]); + } + + if (!$filenames) { + throw new RuntimeException('Please provide a filename or pipe file content to 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(string $content, string $file = null): array + { + $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)) { + $normalizedLocalePattern = sprintf('(%s|%s)', preg_quote($targetLanguage, '/'), 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|xliff)/', $normalizedLocalePattern) : sprintf('/^(?:.*\.(?i:%s)|(?i:%s)\..*)\.(?:xlf|xliff)/', $normalizedLocalePattern, $normalizedLocalePattern); + + 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); + case 'github': + return $this->displayTxt($io, $files, true); + default: + throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format)); + } + } + + private function displayTxt(SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = false) + { + $countFiles = \count($filesInfo); + $erroredFiles = 0; + $githubReporter = $errorAsGithubAnnotations ? new GithubActionReporter($io) : null; + + foreach ($filesInfo as $info) { + if ($info['valid'] && $this->displayCorrectFiles) { + $io->comment('OK'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + } elseif (!$info['valid']) { + ++$erroredFiles; + $io->text(' ERROR '.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + $io->listing(array_map(function ($error) use ($info, $githubReporter) { + // general document errors have a '-1' line number + $line = -1 === $error['line'] ? null : $error['line']; + + if ($githubReporter) { + $githubReporter->error($error['message'], $info['file'], $line, null !== $line ? $error['column'] : null); + } + + return null === $line ? $error['message'] : sprintf('Line %d, Column %d: %s', $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(string $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 getDirectoryIterator(string $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(string $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; + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestOptionValuesFor('format')) { + $suggestions->suggestValues(['txt', 'json', 'github']); + } + } +} diff --git a/vendor/symfony/translation/DataCollector/TranslationDataCollector.php b/vendor/symfony/translation/DataCollector/TranslationDataCollector.php new file mode 100644 index 0000000..0f7901d --- /dev/null +++ b/vendor/symfony/translation/DataCollector/TranslationDataCollector.php @@ -0,0 +1,160 @@ + + * + * 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; +use Symfony\Component\VarDumper\Cloner\Data; + +/** + * @author Abdellatif Ait boudad + * + * @final + */ +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, \Throwable $exception = null) + { + $this->data['locale'] = $this->translator->getLocale(); + $this->data['fallback_locales'] = $this->translator->getFallbackLocales(); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = []; + } + + public function getMessages(): array|Data + { + return $this->data['messages'] ?? []; + } + + public function getCountMissings(): int + { + return $this->data[DataCollectorTranslator::MESSAGE_MISSING] ?? 0; + } + + public function getCountFallbacks(): int + { + return $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] ?? 0; + } + + public function getCountDefines(): int + { + return $this->data[DataCollectorTranslator::MESSAGE_DEFINED] ?? 0; + } + + public function getLocale() + { + return !empty($this->data['locale']) ? $this->data['locale'] : null; + } + + /** + * @internal + */ + public function getFallbackLocales() + { + return (isset($this->data['fallback_locales']) && \count($this->data['fallback_locales']) > 0) ? $this->data['fallback_locales'] : []; + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return 'translation'; + } + + private function sanitizeCollectedMessages(array $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(array $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 $string, int $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 100644 index 0000000..2a08b09 --- /dev/null +++ b/vendor/symfony/translation/DataCollectorTranslator.php @@ -0,0 +1,162 @@ + + * + * 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\Contracts\Translation\LocaleAwareInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * @author Abdellatif Ait boudad + */ +class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface, WarmableInterface +{ + public const MESSAGE_DEFINED = 0; + public const MESSAGE_MISSING = 1; + public const MESSAGE_EQUALS_FALLBACK = 2; + + private $translator; + private array $messages = []; + + /** + * @param TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator + */ + public function __construct(TranslatorInterface $translator) + { + if (!$translator instanceof TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) { + throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', get_debug_type($translator))); + } + + $this->translator = $translator; + } + + /** + * {@inheritdoc} + */ + public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null): string + { + $trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale); + $this->collectMessage($locale, $domain, $id, $trans, $parameters); + + return $trans; + } + + /** + * {@inheritdoc} + */ + public function setLocale(string $locale) + { + $this->translator->setLocale($locale); + } + + /** + * {@inheritdoc} + */ + public function getLocale(): string + { + return $this->translator->getLocale(); + } + + /** + * {@inheritdoc} + */ + public function getCatalogue(string $locale = null): MessageCatalogueInterface + { + return $this->translator->getCatalogue($locale); + } + + /** + * {@inheritdoc} + */ + public function getCatalogues(): array + { + return $this->translator->getCatalogues(); + } + + /** + * {@inheritdoc} + * + * @return string[] + */ + public function warmUp(string $cacheDir): array + { + if ($this->translator instanceof WarmableInterface) { + return (array) $this->translator->warmUp($cacheDir); + } + + return []; + } + + /** + * Gets the fallback locales. + */ + public function getFallbackLocales(): array + { + 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(string $method, array $args) + { + return $this->translator->{$method}(...$args); + } + + public function getCollectedMessages(): array + { + return $this->messages; + } + + private function collectMessage(?string $locale, ?string $domain, string $id, string $translation, ?array $parameters = []) + { + if (null === $domain) { + $domain = 'messages'; + } + + $catalogue = $this->translator->getCatalogue($locale); + $locale = $catalogue->getLocale(); + $fallbackLocale = null; + 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)) { + $fallbackLocale = $fallbackCatalogue->getLocale(); + break; + } + $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue(); + } + } else { + $state = self::MESSAGE_MISSING; + } + + $this->messages[] = [ + 'locale' => $locale, + 'fallbackLocale' => $fallbackLocale, + '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 100644 index 0000000..4020a07 --- /dev/null +++ b/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php @@ -0,0 +1,35 @@ + + * + * 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 +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('translation.writer')) { + return; + } + + $definition = $container->getDefinition('translation.writer'); + + foreach ($container->findTaggedServiceIds('translation.dumper', 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 100644 index 0000000..ee7c47a --- /dev/null +++ b/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php @@ -0,0 +1,40 @@ + + * + * 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 +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('translation.extractor')) { + return; + } + + $definition = $container->getDefinition('translation.extractor'); + + foreach ($container->findTaggedServiceIds('translation.extractor', 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 100644 index 0000000..be79cda --- /dev/null +++ b/vendor/symfony/translation/DependencyInjection/TranslatorPass.php @@ -0,0 +1,74 @@ + + * + * 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 +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('translator.default')) { + return; + } + + $loaders = []; + $loaderRefs = []; + foreach ($container->findTaggedServiceIds('translation.loader', 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('translation.reader')) { + $definition = $container->getDefinition('translation.reader'); + foreach ($loaders as $id => $formats) { + foreach ($formats as $format) { + $definition->addMethodCall('addLoader', [$format, $loaderRefs[$id]]); + } + } + } + + $container + ->findDefinition('translator.default') + ->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(1)); + if ($container->hasDefinition('console.command.translation_debug')) { + $definition = $container->getDefinition('console.command.translation_debug'); + $definition->replaceArgument(4, $container->getParameter('twig.default_path')); + + if (\count($definition->getArguments()) > 6) { + $definition->replaceArgument(6, $paths); + } + } + if ($container->hasDefinition('console.command.translation_extract')) { + $definition = $container->getDefinition('console.command.translation_extract'); + $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 100644 index 0000000..b85c066 --- /dev/null +++ b/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php @@ -0,0 +1,147 @@ + + * + * 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 + */ +class TranslatorPathsPass extends AbstractRecursivePass +{ + private int $level = 0; + + /** + * @var array + */ + private array $paths = []; + + /** + * @var array + */ + private array $definitions = []; + + /** + * @var array> + */ + private array $controllers = []; + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('translator')) { + return; + } + + foreach ($this->findControllerArguments($container) as $controller => $argument) { + $id = substr($controller, 0, strpos($controller, ':') ?: \strlen($controller)); + if ($container->hasDefinition($id)) { + [$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(); + foreach ($r->getTraits() as $trait) { + $paths[] = $trait->getFileName(); + } + } + } + if ($paths) { + if ($container->hasDefinition('console.command.translation_debug')) { + $definition = $container->getDefinition('console.command.translation_debug'); + $definition->replaceArgument(6, array_merge($definition->getArgument(6), $paths)); + } + if ($container->hasDefinition('console.command.translation_extract')) { + $definition = $container->getDefinition('console.command.translation_extract'); + $definition->replaceArgument(7, array_merge($definition->getArgument(7), $paths)); + } + } + } finally { + $this->level = 0; + $this->paths = []; + $this->definitions = []; + } + } + + protected function processValue(mixed $value, bool $isRoot = false): mixed + { + if ($value instanceof Reference) { + if ('translator' === (string) $value) { + 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('argument_resolver.service')) { + $argument = $container->getDefinition('argument_resolver.service')->getArgument(0); + if ($argument instanceof Reference) { + $argument = $container->getDefinition($argument); + } + + return $argument->getArgument(0); + } + + if ($container->hasDefinition('debug.'.'argument_resolver.service')) { + $argument = $container->getDefinition('debug.'.'argument_resolver.service')->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 100644 index 0000000..0bd3f5e --- /dev/null +++ b/vendor/symfony/translation/Dumper/CsvFileDumper.php @@ -0,0 +1,60 @@ + + * + * 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 string $delimiter = ';'; + private string $enclosure = '"'; + + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string + { + $handle = fopen('php://memory', 'r+'); + + 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. + */ + public function setCsvControl(string $delimiter = ';', string $enclosure = '"') + { + $this->delimiter = $delimiter; + $this->enclosure = $enclosure; + } + + /** + * {@inheritdoc} + */ + protected function getExtension(): string + { + return 'csv'; + } +} diff --git a/vendor/symfony/translation/Dumper/DumperInterface.php b/vendor/symfony/translation/Dumper/DumperInterface.php new file mode 100644 index 0000000..7cdaef5 --- /dev/null +++ b/vendor/symfony/translation/Dumper/DumperInterface.php @@ -0,0 +1,30 @@ + + * + * 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 + */ +interface DumperInterface +{ + /** + * Dumps the message catalogue. + * + * @param array $options Options that are used by the dumper + */ + public function dump(MessageCatalogue $messages, array $options = []); +} diff --git a/vendor/symfony/translation/Dumper/FileDumper.php b/vendor/symfony/translation/Dumper/FileDumper.php new file mode 100644 index 0000000..6bad4ff --- /dev/null +++ b/vendor/symfony/translation/Dumper/FileDumper.php @@ -0,0 +1,108 @@ + + * + * 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 + */ +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(string $relativePathTemplate) + { + $this->relativePathTemplate = $relativePathTemplate; + } + + /** + * {@inheritdoc} + */ + public function dump(MessageCatalogue $messages, array $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. + */ + abstract public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string; + + /** + * Gets the file extension of the dumper. + */ + abstract protected function getExtension(): string; + + /** + * 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 100644 index 0000000..f13f86c --- /dev/null +++ b/vendor/symfony/translation/Dumper/IcuResFileDumper.php @@ -0,0 +1,104 @@ + + * + * 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, string $domain, array $options = []): string + { + $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(string $data): ?string + { + $padding = \strlen($data) % 4; + + return $padding ? str_repeat("\xAA", 4 - $padding) : null; + } + + private function getPosition(string $data) + { + return (\strlen($data) + 28) / 4; + } + + /** + * {@inheritdoc} + */ + protected function getExtension(): string + { + return 'res'; + } +} diff --git a/vendor/symfony/translation/Dumper/IniFileDumper.php b/vendor/symfony/translation/Dumper/IniFileDumper.php new file mode 100644 index 0000000..75032be --- /dev/null +++ b/vendor/symfony/translation/Dumper/IniFileDumper.php @@ -0,0 +1,45 @@ + + * + * 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, string $domain, array $options = []): string + { + $output = ''; + + foreach ($messages->all($domain) as $source => $target) { + $escapeTarget = str_replace('"', '\"', $target); + $output .= $source.'="'.$escapeTarget."\"\n"; + } + + return $output; + } + + /** + * {@inheritdoc} + */ + protected function getExtension(): string + { + return 'ini'; + } +} diff --git a/vendor/symfony/translation/Dumper/JsonFileDumper.php b/vendor/symfony/translation/Dumper/JsonFileDumper.php new file mode 100644 index 0000000..1102730 --- /dev/null +++ b/vendor/symfony/translation/Dumper/JsonFileDumper.php @@ -0,0 +1,40 @@ + + * + * 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, string $domain, array $options = []): string + { + $flags = $options['json_encoding'] ?? \JSON_PRETTY_PRINT; + + return json_encode($messages->all($domain), $flags); + } + + /** + * {@inheritdoc} + */ + protected function getExtension(): string + { + return 'json'; + } +} diff --git a/vendor/symfony/translation/Dumper/MoFileDumper.php b/vendor/symfony/translation/Dumper/MoFileDumper.php new file mode 100644 index 0000000..1ea5462 --- /dev/null +++ b/vendor/symfony/translation/Dumper/MoFileDumper.php @@ -0,0 +1,82 @@ + + * + * 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, string $domain, array $options = []): string + { + $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(): string + { + return 'mo'; + } + + private function writeLong(mixed $str): string + { + return pack('V*', $str); + } +} diff --git a/vendor/symfony/translation/Dumper/PhpFileDumper.php b/vendor/symfony/translation/Dumper/PhpFileDumper.php new file mode 100644 index 0000000..565d893 --- /dev/null +++ b/vendor/symfony/translation/Dumper/PhpFileDumper.php @@ -0,0 +1,38 @@ + + * + * 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 + */ +class PhpFileDumper extends FileDumper +{ + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string + { + return "all($domain), true).";\n"; + } + + /** + * {@inheritdoc} + */ + protected function getExtension(): string + { + return 'php'; + } +} diff --git a/vendor/symfony/translation/Dumper/PoFileDumper.php b/vendor/symfony/translation/Dumper/PoFileDumper.php new file mode 100644 index 0000000..313e504 --- /dev/null +++ b/vendor/symfony/translation/Dumper/PoFileDumper.php @@ -0,0 +1,137 @@ + + * + * 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, string $domain, array $options = []): string + { + $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']), ':'); + } + + $sourceRules = $this->getStandardRules($source); + $targetRules = $this->getStandardRules($target); + if (2 == \count($sourceRules) && [] !== $targetRules) { + $output .= sprintf('msgid "%s"'."\n", $this->escape($sourceRules[0])); + $output .= sprintf('msgid_plural "%s"'."\n", $this->escape($sourceRules[1])); + foreach ($targetRules as $i => $targetRule) { + $output .= sprintf('msgstr[%d] "%s"'."\n", $i, $this->escape($targetRule)); + } + } else { + $output .= sprintf('msgid "%s"'."\n", $this->escape($source)); + $output .= sprintf('msgstr "%s"'."\n", $this->escape($target)); + } + } + + return $output; + } + + private function getStandardRules(string $id) + { + // Partly copied from TranslatorTrait::trans. + $parts = []; + if (preg_match('/^\|++$/', $id)) { + $parts = explode('|', $id); + } elseif (preg_match_all('/(?:\|\||[^\|])++/', $id, $matches)) { + $parts = $matches[0]; + } + + $intervalRegexp = <<<'EOF' +/^(?P + ({\s* + (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*) + \s*}) + + | + + (?P[\[\]]) + \s* + (?P-Inf|\-?\d+(\.\d+)?) + \s*,\s* + (?P\+?Inf|\-?\d+(\.\d+)?) + \s* + (?P[\[\]]) +)\s*(?P.*?)$/xs +EOF; + + $standardRules = []; + foreach ($parts as $part) { + $part = trim(str_replace('||', '|', $part)); + + if (preg_match($intervalRegexp, $part)) { + // Explicit rule is not a standard rule. + return []; + } else { + $standardRules[] = $part; + } + } + + return $standardRules; + } + + /** + * {@inheritdoc} + */ + protected function getExtension(): string + { + return 'po'; + } + + private function escape(string $str): string + { + return addcslashes($str, "\0..\37\42\134"); + } + + private function formatComments(string|array $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 100644 index 0000000..819409f --- /dev/null +++ b/vendor/symfony/translation/Dumper/QtFileDumper.php @@ -0,0 +1,61 @@ + + * + * 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 + */ +class QtFileDumper extends FileDumper +{ + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string + { + $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(): string + { + return 'ts'; + } +} diff --git a/vendor/symfony/translation/Dumper/XliffFileDumper.php b/vendor/symfony/translation/Dumper/XliffFileDumper.php new file mode 100644 index 0000000..b8a109a --- /dev/null +++ b/vendor/symfony/translation/Dumper/XliffFileDumper.php @@ -0,0 +1,203 @@ + + * + * 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 + */ +class XliffFileDumper extends FileDumper +{ + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string + { + $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); + } + + throw new InvalidArgumentException(sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion)); + } + + /** + * {@inheritdoc} + */ + protected function getExtension(): string + { + return 'xlf'; + } + + private function dumpXliff1(string $defaultLocale, MessageCatalogue $messages, ?string $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(string $defaultLocale, MessageCatalogue $messages, ?string $domain) + { + $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 (str_ends_with($domain, MessageCatalogue::INTL_DOMAIN_SUFFIX)) { + $xliffFile->setAttribute('id', substr($domain, 0, -\strlen(MessageCatalogue::INTL_DOMAIN_SUFFIX)).'.'.$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), '/+', '._')); + + if (\strlen($source) <= 80) { + $translation->setAttribute('name', $source); + } + + $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($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(); + } + + private function hasMetadataArrayInfo(string $key, array $metadata = null): bool + { + return is_iterable($metadata[$key] ?? null); + } +} diff --git a/vendor/symfony/translation/Dumper/YamlFileDumper.php b/vendor/symfony/translation/Dumper/YamlFileDumper.php new file mode 100644 index 0000000..d0cfbef --- /dev/null +++ b/vendor/symfony/translation/Dumper/YamlFileDumper.php @@ -0,0 +1,62 @@ + + * + * 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 + */ +class YamlFileDumper extends FileDumper +{ + private string $extension; + + public function __construct(string $extension = 'yml') + { + $this->extension = $extension; + } + + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string + { + if (!class_exists(Yaml::class)) { + 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(): string + { + return $this->extension; + } +} diff --git a/vendor/symfony/translation/Exception/ExceptionInterface.php b/vendor/symfony/translation/Exception/ExceptionInterface.php new file mode 100644 index 0000000..8f9c54e --- /dev/null +++ b/vendor/symfony/translation/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * 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 + */ +interface ExceptionInterface extends \Throwable +{ +} diff --git a/vendor/symfony/translation/Exception/IncompleteDsnException.php b/vendor/symfony/translation/Exception/IncompleteDsnException.php new file mode 100644 index 0000000..cb0ce02 --- /dev/null +++ b/vendor/symfony/translation/Exception/IncompleteDsnException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +class IncompleteDsnException extends InvalidArgumentException +{ + public function __construct(string $message, string $dsn = null, \Throwable $previous = null) + { + if ($dsn) { + $message = sprintf('Invalid "%s" provider DSN: ', $dsn).$message; + } + + parent::__construct($message, 0, $previous); + } +} diff --git a/vendor/symfony/translation/Exception/InvalidArgumentException.php b/vendor/symfony/translation/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..90d0669 --- /dev/null +++ b/vendor/symfony/translation/Exception/InvalidArgumentException.php @@ -0,0 +1,21 @@ + + * + * 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 + */ +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 100644 index 0000000..cf07943 --- /dev/null +++ b/vendor/symfony/translation/Exception/InvalidResourceException.php @@ -0,0 +1,21 @@ + + * + * 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 + */ +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 100644 index 0000000..9019c7e --- /dev/null +++ b/vendor/symfony/translation/Exception/LogicException.php @@ -0,0 +1,21 @@ + + * + * 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 + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/translation/Exception/MissingRequiredOptionException.php b/vendor/symfony/translation/Exception/MissingRequiredOptionException.php new file mode 100644 index 0000000..2b5f808 --- /dev/null +++ b/vendor/symfony/translation/Exception/MissingRequiredOptionException.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +/** + * @author Oskar Stark + */ +class MissingRequiredOptionException extends IncompleteDsnException +{ + public function __construct(string $option, string $dsn = null, \Throwable $previous = null) + { + $message = sprintf('The option "%s" is required but missing.', $option); + + parent::__construct($message, $dsn, $previous); + } +} diff --git a/vendor/symfony/translation/Exception/NotFoundResourceException.php b/vendor/symfony/translation/Exception/NotFoundResourceException.php new file mode 100644 index 0000000..cff73ae --- /dev/null +++ b/vendor/symfony/translation/Exception/NotFoundResourceException.php @@ -0,0 +1,21 @@ + + * + * 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 + */ +class NotFoundResourceException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/translation/Exception/ProviderException.php b/vendor/symfony/translation/Exception/ProviderException.php new file mode 100644 index 0000000..331ff75 --- /dev/null +++ b/vendor/symfony/translation/Exception/ProviderException.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +use Symfony\Contracts\HttpClient\ResponseInterface; + +/** + * @author Fabien Potencier + */ +class ProviderException extends RuntimeException implements ProviderExceptionInterface +{ + private $response; + private string $debug; + + public function __construct(string $message, ResponseInterface $response, int $code = 0, \Exception $previous = null) + { + $this->response = $response; + $this->debug = $response->getInfo('debug') ?? ''; + + parent::__construct($message, $code, $previous); + } + + public function getResponse(): ResponseInterface + { + return $this->response; + } + + public function getDebug(): string + { + return $this->debug; + } +} diff --git a/vendor/symfony/translation/Exception/ProviderExceptionInterface.php b/vendor/symfony/translation/Exception/ProviderExceptionInterface.php new file mode 100644 index 0000000..922e827 --- /dev/null +++ b/vendor/symfony/translation/Exception/ProviderExceptionInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +/** + * @author Fabien Potencier + */ +interface ProviderExceptionInterface extends ExceptionInterface +{ + /* + * Returns debug info coming from the Symfony\Contracts\HttpClient\ResponseInterface + */ + public function getDebug(): string; +} diff --git a/vendor/symfony/translation/Exception/RuntimeException.php b/vendor/symfony/translation/Exception/RuntimeException.php new file mode 100644 index 0000000..dcd7940 --- /dev/null +++ b/vendor/symfony/translation/Exception/RuntimeException.php @@ -0,0 +1,21 @@ + + * + * 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 + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/translation/Exception/UnsupportedSchemeException.php b/vendor/symfony/translation/Exception/UnsupportedSchemeException.php new file mode 100644 index 0000000..7fbaa8f --- /dev/null +++ b/vendor/symfony/translation/Exception/UnsupportedSchemeException.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +use Symfony\Component\Translation\Bridge; +use Symfony\Component\Translation\Provider\Dsn; + +class UnsupportedSchemeException extends LogicException +{ + private const SCHEME_TO_PACKAGE_MAP = [ + 'crowdin' => [ + 'class' => Bridge\Crowdin\CrowdinProviderFactory::class, + 'package' => 'symfony/crowdin-translation-provider', + ], + 'loco' => [ + 'class' => Bridge\Loco\LocoProviderFactory::class, + 'package' => 'symfony/loco-translation-provider', + ], + 'lokalise' => [ + 'class' => Bridge\Lokalise\LokaliseProviderFactory::class, + 'package' => 'symfony/lokalise-translation-provider', + ], + ]; + + public function __construct(Dsn $dsn, string $name = null, array $supported = []) + { + $provider = $dsn->getScheme(); + if (false !== $pos = strpos($provider, '+')) { + $provider = substr($provider, 0, $pos); + } + $package = self::SCHEME_TO_PACKAGE_MAP[$provider] ?? null; + if ($package && !class_exists($package['class'])) { + parent::__construct(sprintf('Unable to synchronize translations via "%s" as the provider is not installed; try running "composer require %s".', $provider, $package['package'])); + + return; + } + + $message = sprintf('The "%s" scheme is not supported', $dsn->getScheme()); + if ($name && $supported) { + $message .= sprintf('; supported schemes for translation provider "%s" are: "%s"', $name, implode('", "', $supported)); + } + + parent::__construct($message.'.'); + } +} diff --git a/vendor/symfony/translation/Extractor/AbstractFileExtractor.php b/vendor/symfony/translation/Extractor/AbstractFileExtractor.php new file mode 100644 index 0000000..4c088b9 --- /dev/null +++ b/vendor/symfony/translation/Extractor/AbstractFileExtractor.php @@ -0,0 +1,67 @@ + + * + * 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 + */ +abstract class AbstractFileExtractor +{ + protected function extractFiles(string|iterable $resource): iterable + { + if (is_iterable($resource)) { + $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); + } + + /** + * @throws InvalidArgumentException + */ + protected function isFile(string $file): bool + { + if (!is_file($file)) { + throw new InvalidArgumentException(sprintf('The "%s" file does not exist.', $file)); + } + + return true; + } + + /** + * @return bool + */ + abstract protected function canBeExtracted(string $file); + + /** + * @return iterable + */ + abstract protected function extractFromDirectory(string|array $resource); +} diff --git a/vendor/symfony/translation/Extractor/ChainExtractor.php b/vendor/symfony/translation/Extractor/ChainExtractor.php new file mode 100644 index 0000000..e58e82f --- /dev/null +++ b/vendor/symfony/translation/Extractor/ChainExtractor.php @@ -0,0 +1,57 @@ + + * + * 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 + */ +class ChainExtractor implements ExtractorInterface +{ + /** + * The extractors. + * + * @var ExtractorInterface[] + */ + private array $extractors = []; + + /** + * Adds a loader to the translation extractor. + */ + public function addExtractor(string $format, ExtractorInterface $extractor) + { + $this->extractors[$format] = $extractor; + } + + /** + * {@inheritdoc} + */ + public function setPrefix(string $prefix) + { + foreach ($this->extractors as $extractor) { + $extractor->setPrefix($prefix); + } + } + + /** + * {@inheritdoc} + */ + public function extract(string|iterable $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 100644 index 0000000..b76a7f2 --- /dev/null +++ b/vendor/symfony/translation/Extractor/ExtractorInterface.php @@ -0,0 +1,35 @@ + + * + * 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 + */ +interface ExtractorInterface +{ + /** + * Extracts translation messages from files, a file or a directory to the catalogue. + * + * @param string|iterable $resource Files, a file or a directory + */ + public function extract(string|iterable $resource, MessageCatalogue $catalogue); + + /** + * Sets the prefix that should be used for new found messages. + */ + public function setPrefix(string $prefix); +} diff --git a/vendor/symfony/translation/Extractor/PhpExtractor.php b/vendor/symfony/translation/Extractor/PhpExtractor.php new file mode 100644 index 0000000..1b86cc5 --- /dev/null +++ b/vendor/symfony/translation/Extractor/PhpExtractor.php @@ -0,0 +1,330 @@ + + * + * 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 + */ +class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface +{ + public const MESSAGE_TOKEN = 300; + public const METHOD_ARGUMENTS_TOKEN = 1000; + public const DOMAIN_TOKEN = 1001; + + /** + * Prefix for new found message. + */ + private string $prefix = ''; + + /** + * The sequence that captures translation messages. + */ + protected $sequences = [ + [ + '->', + 'trans', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ], + [ + '->', + 'trans', + '(', + self::MESSAGE_TOKEN, + ], + [ + 'new', + 'TranslatableMessage', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ], + [ + 'new', + 'TranslatableMessage', + '(', + self::MESSAGE_TOKEN, + ], + [ + 'new', + '\\', + 'Symfony', + '\\', + 'Component', + '\\', + 'Translation', + '\\', + 'TranslatableMessage', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ], + [ + 'new', + '\Symfony\Component\Translation\TranslatableMessage', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ], + [ + 'new', + '\\', + 'Symfony', + '\\', + 'Component', + '\\', + 'Translation', + '\\', + 'TranslatableMessage', + '(', + self::MESSAGE_TOKEN, + ], + [ + 'new', + '\Symfony\Component\Translation\TranslatableMessage', + '(', + self::MESSAGE_TOKEN, + ], + [ + 't', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ], + [ + 't', + '(', + self::MESSAGE_TOKEN, + ], + ]; + + /** + * {@inheritdoc} + */ + public function extract(string|iterable $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(string $prefix) + { + $this->prefix = $prefix; + } + + /** + * Normalizes a token. + */ + protected function normalizeToken(mixed $token): ?string + { + 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: + if ($indentation = strspn($t[1], ' ')) { + $docPartWithLineBreaks = $docPart; + $docPart = ''; + + foreach (preg_split('~(\r\n|\n|\r)~', $docPartWithLineBreaks, -1, \PREG_SPLIT_DELIM_CAPTURE) as $str) { + if (\in_array($str, ["\r\n", "\n", "\r"], true)) { + $docPart .= $str; + } else { + $docPart .= substr($str, $indentation); + } + } + } + + $message .= PhpStringTokenParser::parseDocString($docToken, $docPart); + $docToken = ''; + $docPart = ''; + break; + case \T_WHITESPACE: + break; + default: + break 2; + } + } + + return $message; + } + + /** + * Extracts trans message from PHP tokens. + */ + protected function parseTokens(array $tokens, MessageCatalogue $catalog, string $filename) + { + $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; + } + } + } + } + + /** + * @throws \InvalidArgumentException + */ + protected function canBeExtracted(string $file): bool + { + return $this->isFile($file) && 'php' === pathinfo($file, \PATHINFO_EXTENSION); + } + + /** + * {@inheritdoc} + */ + protected function extractFromDirectory(string|array $directory): iterable + { + if (!class_exists(Finder::class)) { + throw new \LogicException(sprintf('You cannot use "%s" as the "symfony/finder" package is not installed. Try running "composer require symfony/finder".', static::class)); + } + + $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 100644 index 0000000..7fbd37c --- /dev/null +++ b/vendor/symfony/translation/Extractor/PhpStringTokenParser.php @@ -0,0 +1,136 @@ + + * + * 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 + */ + public static function parse(string $str): string + { + $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 + */ + public static function parseEscapeSequences(string $str, string $quote = null): string + { + 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(array $matches): string + { + $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 (<< + * + * 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 + * @author Abdellatif Ait boudad + */ +class IntlFormatter implements IntlFormatterInterface +{ + private $hasMessageFormatter; + private $cache = []; + + /** + * {@inheritdoc} + */ + public function formatIntl(string $message, string $locale, array $parameters = []): string + { + // MessageFormatter constructor throws an exception if the message is empty + if ('' === $message) { + return ''; + } + + 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): ', 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): ', $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 100644 index 0000000..02fc6ac --- /dev/null +++ b/vendor/symfony/translation/Formatter/IntlFormatterInterface.php @@ -0,0 +1,27 @@ + + * + * 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 + */ +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 100644 index 0000000..68821b1 --- /dev/null +++ b/vendor/symfony/translation/Formatter/MessageFormatter.php @@ -0,0 +1,56 @@ + + * + * 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\Contracts\Translation\TranslatorInterface; + +// Help opcache.preload discover always-needed symbols +class_exists(IntlFormatter::class); + +/** + * @author Abdellatif Ait boudad + */ +class MessageFormatter implements MessageFormatterInterface, IntlFormatterInterface +{ + private $translator; + private $intlFormatter; + + /** + * @param TranslatorInterface|null $translator An identity translator to use as selector for pluralization + */ + public function __construct(TranslatorInterface $translator = null, IntlFormatterInterface $intlFormatter = null) + { + $this->translator = $translator ?? new IdentityTranslator(); + $this->intlFormatter = $intlFormatter ?? new IntlFormatter(); + } + + /** + * {@inheritdoc} + */ + public function format(string $message, string $locale, array $parameters = []): string + { + 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); + } +} diff --git a/vendor/symfony/translation/Formatter/MessageFormatterInterface.php b/vendor/symfony/translation/Formatter/MessageFormatterInterface.php new file mode 100644 index 0000000..d5c41c1 --- /dev/null +++ b/vendor/symfony/translation/Formatter/MessageFormatterInterface.php @@ -0,0 +1,28 @@ + + * + * 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 + * @author Abdellatif Ait boudad + */ +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 + */ + public function format(string $message, string $locale, array $parameters = []): string; +} diff --git a/vendor/symfony/translation/IdentityTranslator.php b/vendor/symfony/translation/IdentityTranslator.php new file mode 100644 index 0000000..46875ed --- /dev/null +++ b/vendor/symfony/translation/IdentityTranslator.php @@ -0,0 +1,26 @@ + + * + * 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\Contracts\Translation\LocaleAwareInterface; +use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorTrait; + +/** + * IdentityTranslator does not translate anything. + * + * @author Fabien Potencier + */ +class IdentityTranslator implements TranslatorInterface, LocaleAwareInterface +{ + use TranslatorTrait; +} diff --git a/vendor/symfony/translation/LICENSE b/vendor/symfony/translation/LICENSE new file mode 100644 index 0000000..0083704 --- /dev/null +++ b/vendor/symfony/translation/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2023 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 100644 index 0000000..35de9ef --- /dev/null +++ b/vendor/symfony/translation/Loader/ArrayLoader.php @@ -0,0 +1,58 @@ + + * + * 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 + */ +class ArrayLoader implements LoaderInterface +{ + /** + * {@inheritdoc} + */ + public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue + { + $resource = $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' + */ + private function flatten(array $messages): array + { + $result = []; + foreach ($messages as $key => $value) { + if (\is_array($value)) { + foreach ($this->flatten($value) as $k => $v) { + $result[$key.'.'.$k] = $v; + } + } else { + $result[$key] = $value; + } + } + + return $result; + } +} diff --git a/vendor/symfony/translation/Loader/CsvFileLoader.php b/vendor/symfony/translation/Loader/CsvFileLoader.php new file mode 100644 index 0000000..76b00b1 --- /dev/null +++ b/vendor/symfony/translation/Loader/CsvFileLoader.php @@ -0,0 +1,65 @@ + + * + * 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ć + */ +class CsvFileLoader extends FileLoader +{ + private string $delimiter = ';'; + private string $enclosure = '"'; + private string $escape = '\\'; + + /** + * {@inheritdoc} + */ + protected function loadResource(string $resource): array + { + $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 (false === $data) { + continue; + } + + 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. + */ + public function setCsvControl(string $delimiter = ';', string $enclosure = '"', string $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 100644 index 0000000..e170d76 --- /dev/null +++ b/vendor/symfony/translation/Loader/FileLoader.php @@ -0,0 +1,62 @@ + + * + * 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; + +/** + * @author Abdellatif Ait boudad + */ +abstract class FileLoader extends ArrayLoader +{ + /** + * {@inheritdoc} + */ + public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue + { + 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(FileResource::class)) { + $catalogue->addResource(new FileResource($resource)); + } + + return $catalogue; + } + + /** + * @throws InvalidResourceException if stream content has an invalid format + */ + abstract protected function loadResource(string $resource): array; +} diff --git a/vendor/symfony/translation/Loader/IcuDatFileLoader.php b/vendor/symfony/translation/Loader/IcuDatFileLoader.php new file mode 100644 index 0000000..c3ca5fd --- /dev/null +++ b/vendor/symfony/translation/Loader/IcuDatFileLoader.php @@ -0,0 +1,61 @@ + + * + * 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(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue + { + 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(FileResource::class)) { + $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 100644 index 0000000..54c48f8 --- /dev/null +++ b/vendor/symfony/translation/Loader/IcuResFileLoader.php @@ -0,0 +1,89 @@ + + * + * 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(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue + { + 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(DirectoryResource::class)) { + $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 + */ + protected function flatten(\ResourceBundle $rb, array &$messages = [], string $path = null): array + { + 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 100644 index 0000000..04e294d --- /dev/null +++ b/vendor/symfony/translation/Loader/IniFileLoader.php @@ -0,0 +1,28 @@ + + * + * 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(string $resource): array + { + 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 100644 index 0000000..67a8d58 --- /dev/null +++ b/vendor/symfony/translation/Loader/JsonFileLoader.php @@ -0,0 +1,60 @@ + + * + * 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(string $resource): array + { + $messages = []; + if ($data = file_get_contents($resource)) { + $messages = json_decode($data, true); + + if (0 < $errorCode = json_last_error()) { + throw new InvalidResourceException('Error parsing JSON: '.$this->getJSONErrorMessage($errorCode)); + } + } + + return $messages; + } + + /** + * Translates JSON_ERROR_* constant into meaningful message. + */ + private function getJSONErrorMessage(int $errorCode): string + { + 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 100644 index 0000000..29d5560 --- /dev/null +++ b/vendor/symfony/translation/Loader/LoaderInterface.php @@ -0,0 +1,32 @@ + + * + * 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 + */ +interface LoaderInterface +{ + /** + * Loads a locale. + * + * @throws NotFoundResourceException when the resource cannot be found + * @throws InvalidResourceException when the resource cannot be loaded + */ + public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue; +} diff --git a/vendor/symfony/translation/Loader/MoFileLoader.php b/vendor/symfony/translation/Loader/MoFileLoader.php new file mode 100644 index 0000000..b0c8913 --- /dev/null +++ b/vendor/symfony/translation/Loader/MoFileLoader.php @@ -0,0 +1,140 @@ + + * + * 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 an MO file as well as + * detecting if the machine used to create that file was little endian. + */ + public const MO_LITTLE_ENDIAN_MAGIC = 0x950412DE; + + /** + * Magic used for validating the format of an MO file as well as + * detecting if the machine used to create that file was big endian. + */ + public const MO_BIG_ENDIAN_MAGIC = 0xDE120495; + + /** + * The size of the header of an MO file in bytes. + */ + public 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(string $resource): array + { + $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 (str_contains($singularId, "\000")) { + [$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 (str_contains($translated, "\000")) { + $translated = explode("\000", $translated); + } + + $ids = ['singular' => $singularId, 'plural' => $pluralId]; + $item = compact('ids', 'translated'); + + if (!empty($item['ids']['singular'])) { + $id = $item['ids']['singular']; + if (isset($item['ids']['plural'])) { + $id .= '|'.$item['ids']['plural']; + } + $messages[$id] = stripcslashes(implode('|', (array) $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 100644 index 0000000..ae299c2 --- /dev/null +++ b/vendor/symfony/translation/Loader/PhpFileLoader.php @@ -0,0 +1,42 @@ + + * + * 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 + */ +class PhpFileLoader extends FileLoader +{ + private static ?array $cache = []; + + /** + * {@inheritdoc} + */ + protected function loadResource(string $resource): array + { + if ([] === self::$cache && \function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN))) { + self::$cache = null; + } + + if (null === self::$cache) { + return require $resource; + } + + if (isset(self::$cache[$resource])) { + return self::$cache[$resource]; + } + + return self::$cache[$resource] = require $resource; + } +} diff --git a/vendor/symfony/translation/Loader/PoFileLoader.php b/vendor/symfony/translation/Loader/PoFileLoader.php new file mode 100644 index 0000000..6df1614 --- /dev/null +++ b/vendor/symfony/translation/Loader/PoFileLoader.php @@ -0,0 +1,149 @@ + + * + * 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 https://github.com/UnionOfRAD/lithium + * @copyright Copyright (c) 2012, Clemens Tolboom + */ +class PoFileLoader extends FileLoader +{ + /** + * Parses portable object (PO) format. + * + * From https://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(string $resource): array + { + $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 (!empty($item['ids']['singular'])) { + $id = stripcslashes($item['ids']['singular']); + if (isset($item['ids']['plural'])) { + $id .= '|'.stripcslashes($item['ids']['plural']); + } + + $translated = (array) $item['translated']; + // PO are by definition indexed so sort by index. + ksort($translated); + // Make sure every index is filled. + end($translated); + $count = key($translated); + // Fill missing spots with '-'. + $empties = array_fill(0, $count + 1, '-'); + $translated += $empties; + ksort($translated); + + $messages[$id] = stripcslashes(implode('|', $translated)); + } + } +} diff --git a/vendor/symfony/translation/Loader/QtFileLoader.php b/vendor/symfony/translation/Loader/QtFileLoader.php new file mode 100644 index 0000000..6d5582d --- /dev/null +++ b/vendor/symfony/translation/Loader/QtFileLoader.php @@ -0,0 +1,82 @@ + + * + * 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\Exception\RuntimeException; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * QtFileLoader loads translations from QT Translations XML files. + * + * @author Benjamin Eberlei + */ +class QtFileLoader implements LoaderInterface +{ + /** + * {@inheritdoc} + */ + public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue + { + if (!class_exists(XmlUtils::class)) { + throw new RuntimeException('Loading translations from the QT format requires the Symfony Config component.'); + } + + 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(FileResource::class)) { + $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 100644 index 0000000..670e199 --- /dev/null +++ b/vendor/symfony/translation/Loader/XliffFileLoader.php @@ -0,0 +1,232 @@ + + * + * 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\Exception\InvalidXmlException; +use Symfony\Component\Config\Util\Exception\XmlParsingException; +use Symfony\Component\Config\Util\XmlUtils; +use Symfony\Component\Translation\Exception\InvalidResourceException; +use Symfony\Component\Translation\Exception\NotFoundResourceException; +use Symfony\Component\Translation\Exception\RuntimeException; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Util\XliffUtils; + +/** + * XliffFileLoader loads translations from XLIFF files. + * + * @author Fabien Potencier + */ +class XliffFileLoader implements LoaderInterface +{ + /** + * {@inheritdoc} + */ + public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue + { + if (!class_exists(XmlUtils::class)) { + throw new RuntimeException('Loading translations from the Xliff format requires the Symfony Config component.'); + } + + if (!$this->isXmlString($resource)) { + 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)); + } + + if (!is_file($resource)) { + throw new InvalidResourceException(sprintf('This is neither a file nor an XLIFF string "%s".', $resource)); + } + } + + try { + if ($this->isXmlString($resource)) { + $dom = XmlUtils::parse($resource); + } else { + $dom = XmlUtils::loadFile($resource); + } + } catch (\InvalidArgumentException|XmlParsingException|InvalidXmlException $e) { + throw new InvalidResourceException(sprintf('Unable to load "%s": ', $resource).$e->getMessage(), $e->getCode(), $e); + } + + if ($errors = XliffUtils::validateSchema($dom)) { + throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: ', $resource).XliffUtils::getErrorsAsString($errors)); + } + + $catalogue = new MessageCatalogue($locale); + $this->extract($dom, $catalogue, $domain); + + if (is_file($resource) && class_exists(FileResource::class)) { + $catalogue->addResource(new FileResource($resource)); + } + + return $catalogue; + } + + private function extract(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain) + { + $xliffVersion = XliffUtils::getVersionNumber($dom); + + 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. + */ + private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain) + { + $xml = simplexml_import_dom($dom); + $encoding = $dom->encoding ? strtoupper($dom->encoding) : null; + + $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 = $dom->encoding ? strtoupper($dom->encoding) : null; + + $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0'); + + foreach ($xml->xpath('//xliff:unit') as $unit) { + foreach ($unit->segment as $segment) { + $attributes = $unit->attributes(); + $source = $attributes['name'] ?? $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) ($segment->target ?? $segment->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; + } + + private function isXmlString(string $resource): bool + { + return 0 === strpos($resource, ' + * + * 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 + */ +class YamlFileLoader extends FileLoader +{ + private $yamlParser; + + /** + * {@inheritdoc} + */ + protected function loadResource(string $resource): array + { + if (null === $this->yamlParser) { + if (!class_exists(\Symfony\Component\Yaml\Parser::class)) { + 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('The file "%s" does not contain valid YAML: ', $resource).$e->getMessage(), 0, $e); + } + + if (null !== $messages && !\is_array($messages)) { + throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource)); + } + + return $messages ?: []; + } +} diff --git a/vendor/symfony/translation/LoggingTranslator.php b/vendor/symfony/translation/LoggingTranslator.php new file mode 100644 index 0000000..8c8441c --- /dev/null +++ b/vendor/symfony/translation/LoggingTranslator.php @@ -0,0 +1,129 @@ + + * + * 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\Contracts\Translation\LocaleAwareInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * @author Abdellatif Ait boudad + */ +class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface +{ + private $translator; + private $logger; + + /** + * @param TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator The translator must implement TranslatorBagInterface + */ + public function __construct(TranslatorInterface $translator, LoggerInterface $logger) + { + if (!$translator instanceof TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) { + throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', get_debug_type($translator))); + } + + $this->translator = $translator; + $this->logger = $logger; + } + + /** + * {@inheritdoc} + */ + public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null): string + { + $trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale); + $this->log($id, $domain, $locale); + + return $trans; + } + + /** + * {@inheritdoc} + */ + public function setLocale(string $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(): string + { + return $this->translator->getLocale(); + } + + /** + * {@inheritdoc} + */ + public function getCatalogue(string $locale = null): MessageCatalogueInterface + { + return $this->translator->getCatalogue($locale); + } + + /** + * {@inheritdoc} + */ + public function getCatalogues(): array + { + return $this->translator->getCatalogues(); + } + + /** + * Gets the fallback locales. + */ + public function getFallbackLocales(): array + { + 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(string $method, array $args) + { + return $this->translator->{$method}(...$args); + } + + /** + * Logs for missing translations. + */ + private function log(string $id, ?string $domain, ?string $locale) + { + if (null === $domain) { + $domain = 'messages'; + } + + $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 100644 index 0000000..848594b --- /dev/null +++ b/vendor/symfony/translation/MessageCatalogue.php @@ -0,0 +1,309 @@ + + * + * 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 + */ +class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface +{ + private array $messages = []; + private array $metadata = []; + private array $resources = []; + private string $locale; + private $fallbackCatalogue = null; + private ?self $parent = null; + + /** + * @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(): string + { + return $this->locale; + } + + /** + * {@inheritdoc} + */ + public function getDomains(): array + { + $domains = []; + + foreach ($this->messages as $domain => $messages) { + if (str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) { + $domain = substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX)); + } + $domains[$domain] = $domain; + } + + return array_values($domains); + } + + /** + * {@inheritdoc} + */ + public function all(string $domain = null): array + { + if (null !== $domain) { + // skip messages merge if intl-icu requested explicitly + if (str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) { + return $this->messages[$domain] ?? []; + } + + return ($this->messages[$domain.self::INTL_DOMAIN_SUFFIX] ?? []) + ($this->messages[$domain] ?? []); + } + + $allMessages = []; + + foreach ($this->messages as $domain => $messages) { + if (str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) { + $domain = substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX)); + $allMessages[$domain] = $messages + ($allMessages[$domain] ?? []); + } else { + $allMessages[$domain] = ($allMessages[$domain] ?? []) + $messages; + } + } + + return $allMessages; + } + + /** + * {@inheritdoc} + */ + public function set(string $id, string $translation, string $domain = 'messages') + { + $this->add([$id => $translation], $domain); + } + + /** + * {@inheritdoc} + */ + public function has(string $id, string $domain = 'messages'): bool + { + 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(string $id, string $domain = 'messages'): bool + { + return isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id]); + } + + /** + * {@inheritdoc} + */ + public function get(string $id, string $domain = 'messages'): string + { + 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(array $messages, string $domain = 'messages') + { + unset($this->messages[$domain], $this->messages[$domain.self::INTL_DOMAIN_SUFFIX]); + + $this->add($messages, $domain); + } + + /** + * {@inheritdoc} + */ + public function add(array $messages, string $domain = 'messages') + { + $altDomain = str_ends_with($domain, self::INTL_DOMAIN_SUFFIX) ? substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX)) : $domain.self::INTL_DOMAIN_SUFFIX; + foreach ($messages as $id => $message) { + unset($this->messages[$altDomain][$id]); + $this->messages[$domain][$id] = $message; + } + + if ([] === ($this->messages[$altDomain] ?? null)) { + unset($this->messages[$altDomain]); + } + } + + /** + * {@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(): ?MessageCatalogueInterface + { + return $this->fallbackCatalogue; + } + + /** + * {@inheritdoc} + */ + public function getResources(): array + { + return array_values($this->resources); + } + + /** + * {@inheritdoc} + */ + public function addResource(ResourceInterface $resource) + { + $this->resources[$resource->__toString()] = $resource; + } + + /** + * {@inheritdoc} + */ + public function getMetadata(string $key = '', string $domain = 'messages'): mixed + { + 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]; + } + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function setMetadata(string $key, mixed $value, string $domain = 'messages') + { + $this->metadata[$domain][$key] = $value; + } + + /** + * {@inheritdoc} + */ + public function deleteMetadata(string $key = '', string $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 100644 index 0000000..75e3a2f --- /dev/null +++ b/vendor/symfony/translation/MessageCatalogueInterface.php @@ -0,0 +1,124 @@ + + * + * 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 + */ +interface MessageCatalogueInterface +{ + public const INTL_DOMAIN_SUFFIX = '+intl-icu'; + + /** + * Gets the catalogue locale. + */ + public function getLocale(): string; + + /** + * Gets the domains. + */ + public function getDomains(): array; + + /** + * Gets the messages within a given domain. + * + * If $domain is null, it returns all messages. + * + * @param string $domain The domain name + */ + public function all(string $domain = null): array; + + /** + * 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(string $id, string $translation, string $domain = 'messages'); + + /** + * Checks if a message has a translation. + * + * @param string $id The message id + * @param string $domain The domain name + */ + public function has(string $id, string $domain = 'messages'): bool; + + /** + * 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 + */ + public function defines(string $id, string $domain = 'messages'): bool; + + /** + * Gets a message translation. + * + * @param string $id The message id + * @param string $domain The domain name + */ + public function get(string $id, string $domain = 'messages'): string; + + /** + * Sets translations for a given domain. + * + * @param array $messages An array of translations + * @param string $domain The domain name + */ + public function replace(array $messages, string $domain = 'messages'); + + /** + * Adds translations for a given domain. + * + * @param array $messages An array of translations + * @param string $domain The domain name + */ + public function add(array $messages, string $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. + */ + public function getFallbackCatalogue(): ?self; + + /** + * Returns an array of resources loaded to build this collection. + * + * @return ResourceInterface[] + */ + public function getResources(): array; + + /** + * Adds a resource for this collection. + */ + public function addResource(ResourceInterface $resource); +} diff --git a/vendor/symfony/translation/MetadataAwareInterface.php b/vendor/symfony/translation/MetadataAwareInterface.php new file mode 100644 index 0000000..2eaaceb --- /dev/null +++ b/vendor/symfony/translation/MetadataAwareInterface.php @@ -0,0 +1,44 @@ + + * + * 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 + */ +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. + * + * @return mixed The value that was set or an array with the domains/keys or null + */ + public function getMetadata(string $key = '', string $domain = 'messages'): mixed; + + /** + * Adds metadata to a message domain. + */ + public function setMetadata(string $key, mixed $value, string $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. + */ + public function deleteMetadata(string $key = '', string $domain = 'messages'); +} diff --git a/vendor/symfony/translation/Provider/AbstractProviderFactory.php b/vendor/symfony/translation/Provider/AbstractProviderFactory.php new file mode 100644 index 0000000..17442fd --- /dev/null +++ b/vendor/symfony/translation/Provider/AbstractProviderFactory.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\Exception\IncompleteDsnException; + +abstract class AbstractProviderFactory implements ProviderFactoryInterface +{ + public function supports(Dsn $dsn): bool + { + return \in_array($dsn->getScheme(), $this->getSupportedSchemes(), true); + } + + /** + * @return string[] + */ + abstract protected function getSupportedSchemes(): array; + + protected function getUser(Dsn $dsn): string + { + if (null === $user = $dsn->getUser()) { + throw new IncompleteDsnException('User is not set.', $dsn->getOriginalDsn()); + } + + return $user; + } + + protected function getPassword(Dsn $dsn): string + { + if (null === $password = $dsn->getPassword()) { + throw new IncompleteDsnException('Password is not set.', $dsn->getOriginalDsn()); + } + + return $password; + } +} diff --git a/vendor/symfony/translation/Provider/Dsn.php b/vendor/symfony/translation/Provider/Dsn.php new file mode 100644 index 0000000..0f74d17 --- /dev/null +++ b/vendor/symfony/translation/Provider/Dsn.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Exception\MissingRequiredOptionException; + +/** + * @author Fabien Potencier + * @author Oskar Stark + */ +final class Dsn +{ + private ?string $scheme; + private ?string $host; + private ?string $user; + private ?string $password; + private ?int $port; + private ?string $path; + private array $options = []; + private string $originalDsn; + + public function __construct(string $dsn) + { + $this->originalDsn = $dsn; + + if (false === $parsedDsn = parse_url($dsn)) { + throw new InvalidArgumentException(sprintf('The "%s" translation provider DSN is invalid.', $dsn)); + } + + if (!isset($parsedDsn['scheme'])) { + throw new InvalidArgumentException(sprintf('The "%s" translation provider DSN must contain a scheme.', $dsn)); + } + $this->scheme = $parsedDsn['scheme']; + + if (!isset($parsedDsn['host'])) { + throw new InvalidArgumentException(sprintf('The "%s" translation provider DSN must contain a host (use "default" by default).', $dsn)); + } + $this->host = $parsedDsn['host']; + + $this->user = '' !== ($parsedDsn['user'] ?? '') ? urldecode($parsedDsn['user']) : null; + $this->password = '' !== ($parsedDsn['pass'] ?? '') ? urldecode($parsedDsn['pass']) : null; + $this->port = $parsedDsn['port'] ?? null; + $this->path = $parsedDsn['path'] ?? null; + parse_str($parsedDsn['query'] ?? '', $this->options); + } + + public function getScheme(): string + { + return $this->scheme; + } + + public function getHost(): string + { + return $this->host; + } + + public function getUser(): ?string + { + return $this->user; + } + + public function getPassword(): ?string + { + return $this->password; + } + + public function getPort(int $default = null): ?int + { + return $this->port ?? $default; + } + + public function getOption(string $key, mixed $default = null) + { + return $this->options[$key] ?? $default; + } + + public function getRequiredOption(string $key) + { + if (!\array_key_exists($key, $this->options) || '' === trim($this->options[$key])) { + throw new MissingRequiredOptionException($key); + } + + return $this->options[$key]; + } + + public function getOptions(): array + { + return $this->options; + } + + public function getPath(): ?string + { + return $this->path; + } + + public function getOriginalDsn(): string + { + return $this->originalDsn; + } +} diff --git a/vendor/symfony/translation/Provider/FilteringProvider.php b/vendor/symfony/translation/Provider/FilteringProvider.php new file mode 100644 index 0000000..a43fedc --- /dev/null +++ b/vendor/symfony/translation/Provider/FilteringProvider.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\TranslatorBag; +use Symfony\Component\Translation\TranslatorBagInterface; + +/** + * Filters domains and locales between the Translator config values and those specific to each provider. + * + * @author Mathieu Santostefano + */ +class FilteringProvider implements ProviderInterface +{ + private $provider; + private array $locales; + private array $domains; + + public function __construct(ProviderInterface $provider, array $locales, array $domains = []) + { + $this->provider = $provider; + $this->locales = $locales; + $this->domains = $domains; + } + + public function __toString(): string + { + return (string) $this->provider; + } + + /** + * {@inheritdoc} + */ + public function write(TranslatorBagInterface $translatorBag): void + { + $this->provider->write($translatorBag); + } + + public function read(array $domains, array $locales): TranslatorBag + { + $domains = !$this->domains ? $domains : array_intersect($this->domains, $domains); + $locales = array_intersect($this->locales, $locales); + + return $this->provider->read($domains, $locales); + } + + public function delete(TranslatorBagInterface $translatorBag): void + { + $this->provider->delete($translatorBag); + } + + public function getDomains(): array + { + return $this->domains; + } +} diff --git a/vendor/symfony/translation/Provider/NullProvider.php b/vendor/symfony/translation/Provider/NullProvider.php new file mode 100644 index 0000000..f00392e --- /dev/null +++ b/vendor/symfony/translation/Provider/NullProvider.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\TranslatorBag; +use Symfony\Component\Translation\TranslatorBagInterface; + +/** + * @author Mathieu Santostefano + */ +class NullProvider implements ProviderInterface +{ + public function __toString(): string + { + return 'null'; + } + + public function write(TranslatorBagInterface $translatorBag, bool $override = false): void + { + } + + public function read(array $domains, array $locales): TranslatorBag + { + return new TranslatorBag(); + } + + public function delete(TranslatorBagInterface $translatorBag): void + { + } +} diff --git a/vendor/symfony/translation/Provider/NullProviderFactory.php b/vendor/symfony/translation/Provider/NullProviderFactory.php new file mode 100644 index 0000000..f350f16 --- /dev/null +++ b/vendor/symfony/translation/Provider/NullProviderFactory.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\Exception\UnsupportedSchemeException; + +/** + * @author Mathieu Santostefano + */ +final class NullProviderFactory extends AbstractProviderFactory +{ + public function create(Dsn $dsn): ProviderInterface + { + if ('null' === $dsn->getScheme()) { + return new NullProvider(); + } + + throw new UnsupportedSchemeException($dsn, 'null', $this->getSupportedSchemes()); + } + + protected function getSupportedSchemes(): array + { + return ['null']; + } +} diff --git a/vendor/symfony/translation/Provider/ProviderFactoryInterface.php b/vendor/symfony/translation/Provider/ProviderFactoryInterface.php new file mode 100644 index 0000000..3fd4494 --- /dev/null +++ b/vendor/symfony/translation/Provider/ProviderFactoryInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\Exception\IncompleteDsnException; +use Symfony\Component\Translation\Exception\UnsupportedSchemeException; + +interface ProviderFactoryInterface +{ + /** + * @throws UnsupportedSchemeException + * @throws IncompleteDsnException + */ + public function create(Dsn $dsn): ProviderInterface; + + public function supports(Dsn $dsn): bool; +} diff --git a/vendor/symfony/translation/Provider/ProviderInterface.php b/vendor/symfony/translation/Provider/ProviderInterface.php new file mode 100644 index 0000000..a32193f --- /dev/null +++ b/vendor/symfony/translation/Provider/ProviderInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\TranslatorBag; +use Symfony\Component\Translation\TranslatorBagInterface; + +interface ProviderInterface +{ + public function __toString(): string; + + /** + * Translations available in the TranslatorBag only must be created. + * Translations available in both the TranslatorBag and on the provider + * must be overwritten. + * Translations available on the provider only must be kept. + */ + public function write(TranslatorBagInterface $translatorBag): void; + + public function read(array $domains, array $locales): TranslatorBag; + + public function delete(TranslatorBagInterface $translatorBag): void; +} diff --git a/vendor/symfony/translation/Provider/TranslationProviderCollection.php b/vendor/symfony/translation/Provider/TranslationProviderCollection.php new file mode 100644 index 0000000..61ac641 --- /dev/null +++ b/vendor/symfony/translation/Provider/TranslationProviderCollection.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; + +/** + * @author Mathieu Santostefano + */ +final class TranslationProviderCollection +{ + /** + * @var array + */ + private $providers; + + /** + * @param array $providers + */ + public function __construct(iterable $providers) + { + $this->providers = \is_array($providers) ? $providers : iterator_to_array($providers); + } + + public function __toString(): string + { + return '['.implode(',', array_keys($this->providers)).']'; + } + + public function has(string $name): bool + { + return isset($this->providers[$name]); + } + + public function get(string $name): ProviderInterface + { + if (!$this->has($name)) { + throw new InvalidArgumentException(sprintf('Provider "%s" not found. Available: "%s".', $name, (string) $this)); + } + + return $this->providers[$name]; + } + + public function keys(): array + { + return array_keys($this->providers); + } +} diff --git a/vendor/symfony/translation/Provider/TranslationProviderCollectionFactory.php b/vendor/symfony/translation/Provider/TranslationProviderCollectionFactory.php new file mode 100644 index 0000000..6300c87 --- /dev/null +++ b/vendor/symfony/translation/Provider/TranslationProviderCollectionFactory.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\Exception\UnsupportedSchemeException; + +/** + * @author Mathieu Santostefano + */ +class TranslationProviderCollectionFactory +{ + private iterable $factories; + private array $enabledLocales; + + /** + * @param iterable $factories + */ + public function __construct(iterable $factories, array $enabledLocales) + { + $this->factories = $factories; + $this->enabledLocales = $enabledLocales; + } + + public function fromConfig(array $config): TranslationProviderCollection + { + $providers = []; + foreach ($config as $name => $currentConfig) { + $providers[$name] = $this->fromDsnObject( + new Dsn($currentConfig['dsn']), + !$currentConfig['locales'] ? $this->enabledLocales : $currentConfig['locales'], + !$currentConfig['domains'] ? [] : $currentConfig['domains'] + ); + } + + return new TranslationProviderCollection($providers); + } + + public function fromDsnObject(Dsn $dsn, array $locales, array $domains = []): ProviderInterface + { + foreach ($this->factories as $factory) { + if ($factory->supports($dsn)) { + return new FilteringProvider($factory->create($dsn), $locales, $domains); + } + } + + throw new UnsupportedSchemeException($dsn); + } +} diff --git a/vendor/symfony/translation/PseudoLocalizationTranslator.php b/vendor/symfony/translation/PseudoLocalizationTranslator.php new file mode 100644 index 0000000..ba7319d --- /dev/null +++ b/vendor/symfony/translation/PseudoLocalizationTranslator.php @@ -0,0 +1,368 @@ + + * + * 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\Contracts\Translation\TranslatorInterface; + +/** + * This translator should only be used in a development environment. + */ +final class PseudoLocalizationTranslator implements TranslatorInterface +{ + private const EXPANSION_CHARACTER = '~'; + + private $translator; + private bool $accents; + private float $expansionFactor; + private bool $brackets; + private bool $parseHTML; + + /** + * @var string[] + */ + private array $localizableHTMLAttributes; + + /** + * Available options: + * * accents: + * type: boolean + * default: true + * description: replace ASCII characters of the translated string with accented versions or similar characters + * example: if true, "foo" => "ƒöö". + * + * * expansion_factor: + * type: float + * default: 1 + * validation: it must be greater than or equal to 1 + * description: expand the translated string by the given factor with spaces and tildes + * example: if 2, "foo" => "~foo ~" + * + * * brackets: + * type: boolean + * default: true + * description: wrap the translated string with brackets + * example: if true, "foo" => "[foo]" + * + * * parse_html: + * type: boolean + * default: false + * description: parse the translated string as HTML - looking for HTML tags has a performance impact but allows to preserve them from alterations - it also allows to compute the visible translated string length which is useful to correctly expand ot when it contains HTML + * warning: unclosed tags are unsupported, they will be fixed (closed) by the parser - eg, "foo
bar" => "foo
bar
" + * + * * localizable_html_attributes: + * type: string[] + * default: [] + * description: the list of HTML attributes whose values can be altered - it is only useful when the "parse_html" option is set to true + * example: if ["title"], and with the "accents" option set to true, "Profile" => "Þŕöƒîļé" - if "title" was not in the "localizable_html_attributes" list, the title attribute data would be left unchanged. + */ + public function __construct(TranslatorInterface $translator, array $options = []) + { + $this->translator = $translator; + $this->accents = $options['accents'] ?? true; + + if (1.0 > ($this->expansionFactor = $options['expansion_factor'] ?? 1.0)) { + throw new \InvalidArgumentException('The expansion factor must be greater than or equal to 1.'); + } + + $this->brackets = $options['brackets'] ?? true; + + $this->parseHTML = $options['parse_html'] ?? false; + if ($this->parseHTML && !$this->accents && 1.0 === $this->expansionFactor) { + $this->parseHTML = false; + } + + $this->localizableHTMLAttributes = $options['localizable_html_attributes'] ?? []; + } + + /** + * {@inheritdoc} + */ + public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string + { + $trans = ''; + $visibleText = ''; + + foreach ($this->getParts($this->translator->trans($id, $parameters, $domain, $locale)) as [$visible, $localizable, $text]) { + if ($visible) { + $visibleText .= $text; + } + + if (!$localizable) { + $trans .= $text; + + continue; + } + + $this->addAccents($trans, $text); + } + + $this->expand($trans, $visibleText); + + $this->addBrackets($trans); + + return $trans; + } + + public function getLocale(): string + { + return $this->translator->getLocale(); + } + + private function getParts(string $originalTrans): array + { + if (!$this->parseHTML) { + return [[true, true, $originalTrans]]; + } + + $html = mb_encode_numericentity($originalTrans, [0x80, 0xFFFF, 0, 0xFFFF], mb_detect_encoding($originalTrans, null, true) ?: 'UTF-8'); + + $useInternalErrors = libxml_use_internal_errors(true); + + $dom = new \DOMDocument(); + $dom->loadHTML(''.$html.''); + + libxml_clear_errors(); + libxml_use_internal_errors($useInternalErrors); + + return $this->parseNode($dom->childNodes->item(1)->childNodes->item(0)->childNodes->item(0)); + } + + private function parseNode(\DOMNode $node): array + { + $parts = []; + + foreach ($node->childNodes as $childNode) { + if (!$childNode instanceof \DOMElement) { + $parts[] = [true, true, $childNode->nodeValue]; + + continue; + } + + $parts[] = [false, false, '<'.$childNode->tagName]; + + /** @var \DOMAttr $attribute */ + foreach ($childNode->attributes as $attribute) { + $parts[] = [false, false, ' '.$attribute->nodeName.'="']; + + $localizableAttribute = \in_array($attribute->nodeName, $this->localizableHTMLAttributes, true); + foreach (preg_split('/(&(?:amp|quot|#039|lt|gt);+)/', htmlspecialchars($attribute->nodeValue, \ENT_QUOTES, 'UTF-8'), -1, \PREG_SPLIT_DELIM_CAPTURE) as $i => $match) { + if ('' === $match) { + continue; + } + + $parts[] = [false, $localizableAttribute && 0 === $i % 2, $match]; + } + + $parts[] = [false, false, '"']; + } + + $parts[] = [false, false, '>']; + + $parts = array_merge($parts, $this->parseNode($childNode, $parts)); + + $parts[] = [false, false, 'tagName.'>']; + } + + return $parts; + } + + private function addAccents(string &$trans, string $text): void + { + $trans .= $this->accents ? strtr($text, [ + ' ' => ' ', + '!' => '¡', + '"' => '″', + '#' => '♯', + '$' => '€', + '%' => '‰', + '&' => '⅋', + '\'' => '´', + '(' => '{', + ')' => '}', + '*' => '⁎', + '+' => '⁺', + ',' => '،', + '-' => '‐', + '.' => '·', + '/' => '⁄', + '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' => 'Ž', + '[' => '⁅', + '\\' => '∖', + ']' => '⁆', + '^' => '˄', + '_' => '‿', + '`' => '‵', + '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' => 'ž', + '{' => '(', + '|' => '¦', + '}' => ')', + '~' => '˞', + ]) : $text; + } + + private function expand(string &$trans, string $visibleText): void + { + if (1.0 >= $this->expansionFactor) { + return; + } + + $visibleLength = $this->strlen($visibleText); + $missingLength = (int) ceil($visibleLength * $this->expansionFactor) - $visibleLength; + if ($this->brackets) { + $missingLength -= 2; + } + + if (0 >= $missingLength) { + return; + } + + $words = []; + $wordsCount = 0; + foreach (preg_split('/ +/', $visibleText, -1, \PREG_SPLIT_NO_EMPTY) as $word) { + $wordLength = $this->strlen($word); + + if ($wordLength >= $missingLength) { + continue; + } + + if (!isset($words[$wordLength])) { + $words[$wordLength] = 0; + } + + ++$words[$wordLength]; + ++$wordsCount; + } + + if (!$words) { + $trans .= 1 === $missingLength ? self::EXPANSION_CHARACTER : ' '.str_repeat(self::EXPANSION_CHARACTER, $missingLength - 1); + + return; + } + + arsort($words, \SORT_NUMERIC); + + $longestWordLength = max(array_keys($words)); + + while (true) { + $r = mt_rand(1, $wordsCount); + + foreach ($words as $length => $count) { + $r -= $count; + if ($r <= 0) { + break; + } + } + + $trans .= ' '.str_repeat(self::EXPANSION_CHARACTER, $length); + + $missingLength -= $length + 1; + + if (0 === $missingLength) { + return; + } + + while ($longestWordLength >= $missingLength) { + $wordsCount -= $words[$longestWordLength]; + unset($words[$longestWordLength]); + + if (!$words) { + $trans .= 1 === $missingLength ? self::EXPANSION_CHARACTER : ' '.str_repeat(self::EXPANSION_CHARACTER, $missingLength - 1); + + return; + } + + $longestWordLength = max(array_keys($words)); + } + } + } + + private function addBrackets(string &$trans): void + { + if (!$this->brackets) { + return; + } + + $trans = '['.$trans.']'; + } + + private function strlen(string $s): int + { + return false === ($encoding = mb_detect_encoding($s, null, true)) ? \strlen($s) : mb_strlen($s, $encoding); + } +} diff --git a/vendor/symfony/translation/README.md b/vendor/symfony/translation/README.md new file mode 100644 index 0000000..adda9a5 --- /dev/null +++ b/vendor/symfony/translation/README.md @@ -0,0 +1,48 @@ +Translation Component +===================== + +The Translation component provides tools to internationalize your application. + +Getting Started +--------------- + +``` +$ composer require symfony/translation +``` + +```php +use Symfony\Component\Translation\Translator; +use Symfony\Component\Translation\Loader\ArrayLoader; + +$translator = new Translator('fr_FR'); +$translator->addLoader('array', new ArrayLoader()); +$translator->addResource('array', [ + 'Hello World!' => 'Bonjour !', +], 'fr_FR'); + +echo $translator->trans('Hello World!'); // outputs « Bonjour ! » +``` + +Sponsor +------- + +The Translation component for Symfony 5.4/6.0 is [backed][1] by: + + * [Crowdin][2], a cloud-based localization management software helping teams to go global and stay agile. + * [Lokalise][3], a continuous localization and translation management platform that integrates into your development workflow so you can ship localized products, faster. + +Help Symfony by [sponsoring][4] its development! + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/translation.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) + +[1]: https://symfony.com/backers +[2]: https://crowdin.com +[3]: https://lokalise.com +[4]: https://symfony.com/sponsor diff --git a/vendor/symfony/translation/Reader/TranslationReader.php b/vendor/symfony/translation/Reader/TranslationReader.php new file mode 100644 index 0000000..bbc687e --- /dev/null +++ b/vendor/symfony/translation/Reader/TranslationReader.php @@ -0,0 +1,62 @@ + + * + * 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 + */ +class TranslationReader implements TranslationReaderInterface +{ + /** + * Loaders used for import. + * + * @var array + */ + private array $loaders = []; + + /** + * Adds a loader to the translation extractor. + * + * @param string $format The format of the loader + */ + public function addLoader(string $format, LoaderInterface $loader) + { + $this->loaders[$format] = $loader; + } + + /** + * {@inheritdoc} + */ + public function read(string $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 100644 index 0000000..bc37204 --- /dev/null +++ b/vendor/symfony/translation/Reader/TranslationReaderInterface.php @@ -0,0 +1,27 @@ + + * + * 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 + */ +interface TranslationReaderInterface +{ + /** + * Reads translation messages from a directory to the catalogue. + */ + public function read(string $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 100644 index 0000000..4fe814c --- /dev/null +++ b/vendor/symfony/translation/Resources/bin/translation-status.php @@ -0,0 +1,278 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if ('cli' !== \PHP_SAPI) { + throw new Exception('This script must be run from the command line.'); +} + +$usageInstructions = << false, + // NULL = analyze all locales + 'locale_to_analyze' => null, + // append --incomplete to only show incomplete languages + 'include_completed_languages' => true, + // 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 > 4) { + echo str_replace('translation-status.php', $argv[0], $usageInstructions); + exit(1); +} + +foreach (array_slice($argv, 1) as $argumentOrOption) { + if ('--incomplete' === $argumentOrOption) { + $config['include_completed_languages'] = false; + continue; + } + + if (str_starts_with($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; +$totalTranslationMismatches = 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))); + $totalTranslationMismatches += array_sum(array_map(function ($translation) { + return count($translation['mismatches']); + }, array_values($translationStatus))); + + printTranslationStatus($originalFilePath, $translationStatus, $config['verbose_output'], $config['include_completed_languages']); +} + +exit($totalTranslationMismatches > 0 ? 1 : 0); + +function findTranslationFiles($originalFilePath, $localeToAnalyze) +{ + $translations = []; + + $translationsDir = dirname($originalFilePath); + $originalFileName = basename($originalFilePath); + $translationFileNamePattern = str_replace('.en.', '.*.', $originalFileName); + + $translationFiles = glob($translationsDir.'/'.$translationFileNamePattern, \GLOB_NOSORT); + sort($translationFiles); + 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); + $mismatches = findTransUnitMismatches($allTranslationKeys, $translatedKeys); + + $translationStatus[$locale] = [ + 'total' => count($allTranslationKeys), + 'translated' => count($translatedKeys), + 'missingKeys' => $missingKeys, + 'mismatches' => $mismatches, + ]; + $translationStatus[$locale]['is_completed'] = isTranslationCompleted($translationStatus[$locale]); + } + + return $translationStatus; +} + +function isTranslationCompleted(array $translationStatus): bool +{ + return $translationStatus['total'] === $translationStatus['translated'] && 0 === count($translationStatus['mismatches']); +} + +function printTranslationStatus($originalFilePath, $translationStatus, $verboseOutput, $includeCompletedLanguages) +{ + printTitle($originalFilePath); + printTable($translationStatus, $verboseOutput, $includeCompletedLanguages); + 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; +} + +/** + * Check whether the trans-unit id and source match with the base translation. + */ +function findTransUnitMismatches(array $baseTranslationKeys, array $translatedKeys): array +{ + $mismatches = []; + + foreach ($baseTranslationKeys as $translationId => $translationKey) { + if (!isset($translatedKeys[$translationId])) { + continue; + } + if ($translatedKeys[$translationId] !== $translationKey) { + $mismatches[$translationId] = [ + 'found' => $translatedKeys[$translationId], + 'expected' => $translationKey, + ]; + } + } + + return $mismatches; +} + +function printTitle($title) +{ + echo $title.\PHP_EOL; + echo str_repeat('=', strlen($title)).\PHP_EOL.\PHP_EOL; +} + +function printTable($translations, $verboseOutput, bool $includeCompletedLanguages) +{ + if (0 === count($translations)) { + echo 'No translations found'; + + return; + } + $longestLocaleNameLength = max(array_map('strlen', array_keys($translations))); + + foreach ($translations as $locale => $translation) { + if (!$includeCompletedLanguages && $translation['is_completed']) { + continue; + } + + if ($translation['translated'] > $translation['total']) { + textColorRed(); + } elseif (count($translation['mismatches']) > 0) { + textColorRed(); + } elseif ($translation['is_completed']) { + textColorGreen(); + } + + echo sprintf( + '| Locale: %-'.$longestLocaleNameLength.'s | Translated: %2d/%2d | Mismatches: %d |', + $locale, + $translation['translated'], + $translation['total'], + count($translation['mismatches']) + ).\PHP_EOL; + + textColorNormal(); + + $shouldBeClosed = false; + if (true === $verboseOutput && count($translation['missingKeys']) > 0) { + echo '| Missing Translations:'.\PHP_EOL; + + foreach ($translation['missingKeys'] as $id => $content) { + echo sprintf('| (id=%s) %s', $id, $content).\PHP_EOL; + } + $shouldBeClosed = true; + } + if (true === $verboseOutput && count($translation['mismatches']) > 0) { + echo '| Mismatches between trans-unit id and source:'.\PHP_EOL; + + foreach ($translation['mismatches'] as $id => $content) { + echo sprintf('| (id=%s) Expected: %s', $id, $content['expected']).\PHP_EOL; + echo sprintf('| Found: %s', $content['found']).\PHP_EOL; + } + $shouldBeClosed = true; + } + if ($shouldBeClosed) { + echo str_repeat('-', 80).\PHP_EOL; + } + } +} + +function textColorGreen() +{ + echo "\033[32m"; +} + +function textColorRed() +{ + echo "\033[31m"; +} + +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 100644 index 0000000..32a33cd --- /dev/null +++ b/vendor/symfony/translation/Resources/data/parents.json @@ -0,0 +1,141 @@ +{ + "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_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_MV": "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_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", + "ff_Adlm": "root", + "hi_Latn": "en_IN", + "ks_Deva": "root", + "nb": "no", + "nn": "no", + "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", + "sd_Deva": "root", + "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/functions.php b/vendor/symfony/translation/Resources/functions.php new file mode 100644 index 0000000..901d2f8 --- /dev/null +++ b/vendor/symfony/translation/Resources/functions.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +if (!\function_exists(t::class)) { + /** + * @author Nate Wiebe + */ + function t(string $message, array $parameters = [], string $domain = null): TranslatableMessage + { + return new TranslatableMessage($message, $parameters, $domain); + } +} 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 100644 index 0000000..dface62 --- /dev/null +++ b/vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd @@ -0,0 +1,2223 @@ + + + + + + + + + + + + + + + Values for the attribute 'context-type'. + + + + + Indicates a database content. + + + + + Indicates the content of an element within an XML document. + + + + + Indicates the name of an element within an XML document. + + + + + Indicates the line number from the sourcefile (see context-type="sourcefile") where the <source> is found. + + + + + Indicates a the number of parameters contained within the <source>. + + + + + Indicates notes pertaining to the parameters in the <source>. + + + + + Indicates the content of a record within a database. + + + + + Indicates the name of a record within a database. + + + + + 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 <file> attribute in that this sourcefile is one of many that make up that file. + + + + + + + Values for the attribute 'count-type'. + + + + + 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. + + + + + Indicates the count units are translation units existing already in the same document. + + + + + Indicates a total count. + + + + + + + Values for the attribute 'ctype' when used other elements than <ph> or <x>. + + + + + Indicates a run of bolded text. + + + + + Indicates a run of text in italics. + + + + + Indicates a run of underlined text. + + + + + Indicates a run of hyper-text. + + + + + + + Values for the attribute 'ctype' when used with <ph> or <x>. + + + + + Indicates a inline image. + + + + + Indicates a page break. + + + + + Indicates a line break. + + + + + + + + + + + + Values for the attribute 'datatype'. + + + + + Indicates Active Server Page data. + + + + + Indicates C source file data. + + + + + Indicates Channel Definition Format (CDF) data. + + + + + Indicates ColdFusion data. + + + + + Indicates C++ source file data. + + + + + Indicates C-Sharp data. + + + + + Indicates strings from C, ASM, and driver files data. + + + + + Indicates comma-separated values data. + + + + + Indicates database data. + + + + + Indicates portions of document that follows data and contains metadata. + + + + + Indicates portions of document that precedes data and contains metadata. + + + + + Indicates data from standard UI file operations dialogs (e.g., Open, Save, Save As, Export, Import). + + + + + Indicates standard user input screen data. + + + + + Indicates HyperText Markup Language (HTML) data - document instance. + + + + + Indicates content within an HTML document’s <body> element. + + + + + Indicates Windows INI file data. + + + + + Indicates Interleaf data. + + + + + Indicates Java source file data (extension '.java'). + + + + + Indicates Java property resource bundle data. + + + + + Indicates Java list resource bundle data. + + + + + Indicates JavaScript source file data. + + + + + Indicates JScript source file data. + + + + + Indicates information relating to formatting. + + + + + Indicates LISP source file data. + + + + + Indicates information relating to margin formats. + + + + + Indicates a file containing menu. + + + + + Indicates numerically identified string table. + + + + + Indicates Maker Interchange Format (MIF) data. + + + + + Indicates that the datatype attribute value is a MIME Type value and is defined in the mime-type attribute. + + + + + Indicates GNU Machine Object data. + + + + + Indicates Message Librarian strings created by Novell's Message Librarian Tool. + + + + + Indicates information to be displayed at the bottom of each page of a document. + + + + + Indicates information to be displayed at the top of each page of a document. + + + + + Indicates a list of property values (e.g., settings within INI files or preferences dialog). + + + + + Indicates Pascal source file data. + + + + + Indicates Hypertext Preprocessor data. + + + + + Indicates plain text file (no formatting other than, possibly, wrapping). + + + + + Indicates GNU Portable Object file. + + + + + Indicates dynamically generated user defined document. e.g. Oracle Report, Crystal Report, etc. + + + + + Indicates Windows .NET binary resources. + + + + + Indicates Windows .NET Resources. + + + + + Indicates Rich Text Format (RTF) data. + + + + + Indicates Standard Generalized Markup Language (SGML) data - document instance. + + + + + Indicates Standard Generalized Markup Language (SGML) data - Document Type Definition (DTD). + + + + + Indicates Scalable Vector Graphic (SVG) data. + + + + + Indicates VisualBasic Script source file. + + + + + Indicates warning message. + + + + + Indicates Windows (Win32) resources (i.e. resources extracted from an RC script, a message file, or a compiled file). + + + + + Indicates Extensible HyperText Markup Language (XHTML) data - document instance. + + + + + Indicates Extensible Markup Language (XML) data - document instance. + + + + + Indicates Extensible Markup Language (XML) data - Document Type Definition (DTD). + + + + + Indicates Extensible Stylesheet Language (XSL) data. + + + + + Indicates XUL elements. + + + + + + + Values for the attribute 'mtype'. + + + + + Indicates the marked text is an abbreviation. + + + + + ISO-12620 2.1.8: A term resulting from the omission of any part of the full term while designating the same concept. + + + + + 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'). + + + + + 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'). + + + + + ISO-12620: A proper-name term, such as the name of an agency or other proper entity. + + + + + 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. + + + + + ISO-12620 2.1.5: A synonym for an international scientific term that is used in general discourse in a given language. + + + + + Indicates the marked text is a date and/or time. + + + + + 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. + + + + + ISO-12620 2.1.7: The complete representation of a term for which there is an abbreviated form. + + + + + ISO-12620 2.1.14: Figures, symbols or the like used to express a concept briefly, such as a mathematical or chemical formula. + + + + + ISO-12620 2.1.1: The concept designation that has been chosen to head a terminological record. + + + + + 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'). + + + + + ISO-12620 2.1.4: A term that is part of an international scientific nomenclature as adopted by an appropriate scientific body. + + + + + ISO-12620 2.1.6: A term that has the same or nearly identical orthographic or phonemic form in many languages. + + + + + 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. + + + + + ISO-12620 2.1.17: A unit to track object. + + + + + Indicates the marked text is a name. + + + + + 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. + + + + + ISO-12620 2.1.17.2: A unique alphanumeric designation assigned to an object in a manufacturing system. + + + + + Indicates the marked text is a phrase. + + + + + 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. + + + + + Indicates the marked text should not be translated. + + + + + 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. + + + + + Indicates that the marked text represents a segment. + + + + + ISO-12620 2.1.18.2: A fixed, lexicalized phrase. + + + + + 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'). + + + + + 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. + + + + + ISO-12620 2.1.19: A fixed chunk of recurring text. + + + + + ISO-12620 2.1.13: A designation of a concept by letters, numerals, pictograms or any combination thereof. + + + + + 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. + + + + + ISO-12620 2.1.18.3: Phraseological unit in a language that expresses the same semantic content as another phrase in that same language. + + + + + Indicates the marked text is a term. + + + + + 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. + + + + + 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. + + + + + 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'). + + + + + ISO-12620 2.1.9: One of the alternate forms of a term. + + + + + + + Values for the attribute 'restype'. + + + + + Indicates a Windows RC AUTO3STATE control. + + + + + Indicates a Windows RC AUTOCHECKBOX control. + + + + + Indicates a Windows RC AUTORADIOBUTTON control. + + + + + Indicates a Windows RC BEDIT control. + + + + + Indicates a bitmap, for example a BITMAP resource in Windows. + + + + + Indicates a button object, for example a BUTTON control Windows. + + + + + Indicates a caption, such as the caption of a dialog box. + + + + + Indicates the cell in a table, for example the content of the <td> element in HTML. + + + + + Indicates check box object, for example a CHECKBOX control in Windows. + + + + + Indicates a menu item with an associated checkbox. + + + + + Indicates a list box, but with a check-box for each item. + + + + + Indicates a color selection dialog. + + + + + Indicates a combination of edit box and listbox object, for example a COMBOBOX control in Windows. + + + + + Indicates an initialization entry of an extended combobox DLGINIT resource block. (code 0x1234). + + + + + Indicates an initialization entry of a combobox DLGINIT resource block (code 0x0403). + + + + + Indicates a UI base class element that cannot be represented by any other element. + + + + + Indicates a context menu. + + + + + Indicates a Windows RC CTEXT control. + + + + + Indicates a cursor, for example a CURSOR resource in Windows. + + + + + Indicates a date/time picker. + + + + + Indicates a Windows RC DEFPUSHBUTTON control. + + + + + Indicates a dialog box. + + + + + Indicates a Windows RC DLGINIT resource block. + + + + + Indicates an edit box object, for example an EDIT control in Windows. + + + + + Indicates a filename. + + + + + Indicates a file dialog. + + + + + Indicates a footnote. + + + + + Indicates a font name. + + + + + Indicates a footer. + + + + + Indicates a frame object. + + + + + Indicates a XUL grid element. + + + + + Indicates a groupbox object, for example a GROUPBOX control in Windows. + + + + + Indicates a header item. + + + + + Indicates a heading, such has the content of <h1>, <h2>, etc. in HTML. + + + + + Indicates a Windows RC HEDIT control. + + + + + Indicates a horizontal scrollbar. + + + + + Indicates an icon, for example an ICON resource in Windows. + + + + + Indicates a Windows RC IEDIT control. + + + + + Indicates keyword list, such as the content of the Keywords meta-data in HTML, or a K footnote in WinHelp RTF. + + + + + Indicates a label object. + + + + + Indicates a label that is also a HTML link (not necessarily a URL). + + + + + Indicates a list (a group of list-items, for example an <ol> or <ul> element in HTML). + + + + + Indicates a listbox object, for example an LISTBOX control in Windows. + + + + + Indicates an list item (an entry in a list). + + + + + Indicates a Windows RC LTEXT control. + + + + + Indicates a menu (a group of menu-items). + + + + + Indicates a toolbar containing one or more tope level menus. + + + + + Indicates a menu item (an entry in a menu). + + + + + Indicates a XUL menuseparator element. + + + + + Indicates a message, for example an entry in a MESSAGETABLE resource in Windows. + + + + + Indicates a calendar control. + + + + + Indicates an edit box beside a spin control. + + + + + Indicates a catch all for rectangular areas. + + + + + Indicates a standalone menu not necessarily associated with a menubar. + + + + + Indicates a pushbox object, for example a PUSHBOX control in Windows. + + + + + Indicates a Windows RC PUSHBUTTON control. + + + + + Indicates a radio button object. + + + + + Indicates a menuitem with associated radio button. + + + + + Indicates raw data resources for an application. + + + + + Indicates a row in a table. + + + + + Indicates a Windows RC RTEXT control. + + + + + Indicates a user navigable container used to show a portion of a document. + + + + + Indicates a generic divider object (e.g. menu group separator). + + + + + Windows accelerators, shortcuts in resource or property files. + + + + + Indicates a UI control to indicate process activity but not progress. + + + + + Indicates a splitter bar. + + + + + Indicates a Windows RC STATE3 control. + + + + + Indicates a window for providing feedback to the users, like 'read-only', etc. + + + + + Indicates a string, for example an entry in a STRINGTABLE resource in Windows. + + + + + Indicates a layers of controls with a tab to select layers. + + + + + Indicates a display and edits regular two-dimensional tables of cells. + + + + + Indicates a XUL textbox element. + + + + + Indicates a UI button that can be toggled to on or off state. + + + + + Indicates an array of controls, usually buttons. + + + + + Indicates a pop up tool tip text. + + + + + Indicates a bar with a pointer indicating a position within a certain range. + + + + + Indicates a control that displays a set of hierarchical data. + + + + + Indicates a URI (URN or URL). + + + + + Indicates a Windows RC USERBUTTON control. + + + + + Indicates a user-defined control like CONTROL control in Windows. + + + + + Indicates the text of a variable. + + + + + Indicates version information about a resource like VERSIONINFO in Windows. + + + + + Indicates a vertical scrollbar. + + + + + Indicates a graphical window. + + + + + + + Values for the attribute 'size-unit'. + + + + + Indicates a size in 8-bit bytes. + + + + + Indicates a size in Unicode characters. + + + + + Indicates a size in columns. Used for HTML text area. + + + + + Indicates a size in centimeters. + + + + + Indicates a size in dialog units, as defined in Windows resources. + + + + + Indicates a size in 'font-size' units (as defined in CSS). + + + + + Indicates a size in 'x-height' units (as defined in CSS). + + + + + 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' + + + + + Indicates a size in inches. + + + + + Indicates a size in millimeters. + + + + + Indicates a size in percentage. + + + + + Indicates a size in pixels. + + + + + Indicates a size in point. + + + + + Indicates a size in rows. Used for HTML text area. + + + + + + + Values for the attribute 'state'. + + + + + Indicates the terminating state. + + + + + Indicates only non-textual information needs adaptation. + + + + + Indicates both text and non-textual information needs adaptation. + + + + + Indicates only non-textual information needs review. + + + + + Indicates both text and non-textual information needs review. + + + + + Indicates that only the text of the item needs to be reviewed. + + + + + Indicates that the item needs to be translated. + + + + + Indicates that the item is new. For example, translation units that were not in a previous version of the document. + + + + + Indicates that changes are reviewed and approved. + + + + + Indicates that the item has been translated. + + + + + + + Values for the attribute 'state-qualifier'. + + + + + 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. + + + + + 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.). + + + + + Indicates a match based on matching IDs (in addition to matching text). + + + + + Indicates a translation derived from a glossary. + + + + + Indicates a translation derived from existing translation. + + + + + Indicates a translation derived from machine translation. + + + + + Indicates a translation derived from a translation repository. + + + + + Indicates a translation derived from a translation memory. + + + + + Indicates the translation is suggested by machine translation. + + + + + Indicates that the item has been rejected because of incorrect grammar. + + + + + Indicates that the item has been rejected because it is incorrect. + + + + + Indicates that the item has been rejected because it is too long or too short. + + + + + Indicates that the item has been rejected because of incorrect spelling. + + + + + Indicates the translation is suggested by translation memory. + + + + + + + Values for the attribute 'unit'. + + + + + Refers to words. + + + + + Refers to pages. + + + + + Refers to <trans-unit> elements. + + + + + Refers to <bin-unit> elements. + + + + + Refers to glyphs. + + + + + Refers to <trans-unit> and/or <bin-unit> elements. + + + + + Refers to the occurrences of instances defined by the count-type value. + + + + + Refers to characters. + + + + + Refers to lines. + + + + + Refers to sentences. + + + + + Refers to paragraphs. + + + + + Refers to segments. + + + + + Refers to placeables (inline elements). + + + + + + + Values for the attribute 'priority'. + + + + + Highest priority. + + + + + High priority. + + + + + High priority, but not as important as 2. + + + + + High priority, but not as important as 3. + + + + + Medium priority, but more important than 6. + + + + + Medium priority, but less important than 5. + + + + + Low priority, but more important than 8. + + + + + Low priority, but more important than 9. + + + + + Low priority. + + + + + Lowest priority. + + + + + + + + + This value indicates that all properties can be reformatted. This value must be used alone. + + + + + This value indicates that no properties should be reformatted. This value must be used alone. + + + + + + + + + + + + + This value indicates that all information in the coord attribute can be modified. + + + + + This value indicates that the x information in the coord attribute can be modified. + + + + + This value indicates that the y information in the coord attribute can be modified. + + + + + This value indicates that the cx information in the coord attribute can be modified. + + + + + This value indicates that the cy information in the coord attribute can be modified. + + + + + This value indicates that all the information in the font attribute can be modified. + + + + + This value indicates that the name information in the font attribute can be modified. + + + + + This value indicates that the size information in the font attribute can be modified. + + + + + This value indicates that the weight information in the font attribute can be modified. + + + + + This value indicates that the information in the css-style attribute can be modified. + + + + + This value indicates that the information in the style attribute can be modified. + + + + + This value indicates that the information in the exstyle attribute can be modified. + + + + + + + + + + + + + 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. + + + + + Indicates that the context-group is used to specify where the term was found in the translatable source. Thus, it is not displayed. + + + + + Indicates that the context information should be used during translation memory lookups. Thus, it is not displayed. + + + + + + + + + Represents a translation proposal from a translation memory or other resource. + + + + + Represents a previous version of the target element. + + + + + Represents a rejected version of the target element. + + + + + Represents a translation to be used for reference purposes only, for example from a related product or a different language. + + + + + Represents a proposed translation that was used for the translation of the trans-unit, possibly modified. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Values for the attribute 'coord'. + + + + + + + + Version values: 1.0 and 1.1 are allowed for backward compatibility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 100644 index 0000000..963232f --- /dev/null +++ b/vendor/symfony/translation/Resources/schemas/xliff-core-2.0.xsd @@ -0,0 +1,411 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/translation/Resources/schemas/xml.xsd b/vendor/symfony/translation/Resources/schemas/xml.xsd new file mode 100644 index 0000000..a46162a --- /dev/null +++ b/vendor/symfony/translation/Resources/schemas/xml.xsd @@ -0,0 +1,309 @@ + + + + + + +
+

About the XML namespace

+ +
+

+ + This schema document describes the XML namespace, in a form + suitable for import by other schema documents. +

+

+ See + http://www.w3.org/XML/1998/namespace.html and + + http://www.w3.org/TR/REC-xml for information + about this namespace. +

+ +

+ 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. +

+

+ See further below in this document for more information about how to refer to this schema document from your own + XSD schema documents and about the + namespace-versioning policy governing this schema document. +

+
+
+ +
+
+ + + + +
+ +

lang (as an attribute name)

+

+ + 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.

+ +
+
+

Notes

+

+ 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. +

+

+ + See BCP 47 at + http://www.rfc-editor.org/rfc/bcp/bcp47.txt + and the IANA language subtag registry at + + http://www.iana.org/assignments/language-subtag-registry + for further information. +

+

+ + The union allows for the 'un-declaration' of xml:lang with + the empty string. +

+
+
+
+ + + + + + + + + + +
+ + + + + +
+ +

space (as an attribute name)

+

+ 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.

+ +
+
+
+ + + + + + + +
+ + + + +
+ +

base (as an attribute name)

+

+ 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.

+ +

+ See http://www.w3.org/TR/xmlbase/ + for information about this attribute. +

+ +
+
+
+
+ + + + +
+ +

id (as an attribute name)

+

+ + 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.

+ +

+ See http://www.w3.org/TR/xml-id/ + for information about this attribute. +

+
+
+
+ +
+ + + + + + + + + + + +
+ +

Father (in any context at all)

+ +
+

+ 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: +

+
+

+ + 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". +

+
+
+
+
+
+ + + + +
+

About this schema document

+ +
+

+ This schema defines attributes and an attribute group suitable + for use by schemas wishing to allow xml:base, + xml:lang, xml:space or + xml:id attributes on elements they define. +

+ +

+ To enable this, such a schema must import this schema for + the XML namespace, e.g. as follows: +

+
+          <schema.. .>
+          .. .
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+     
+

+ or +

+
+
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+     
+

+ Subsequently, qualified reference to any of the attributes or the + group defined below will have the desired effect, e.g. +

+
+          <type.. .>
+          .. .
+           <attributeGroup ref="xml:specialAttrs"/>
+     
+

+ will define a type which will schema-validate an instance element + with any of those attributes. +

+ +
+
+
+
+ + + +
+

Versioning policy for this schema document

+ +
+

+ In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + + http://www.w3.org/2009/01/xml.xsd. +

+

+ At the date of issue it can also be found at + + http://www.w3.org/2001/xml.xsd. +

+ +

+ 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 + http://www.w3.org/2001/xml.xsd + + will change accordingly; the version at + + http://www.w3.org/2009/01/xml.xsd + + will not change. +

+

+ + Previous dated (and unchanging) versions of this schema + document are at: +

+ +
+
+
+
+ +
diff --git a/vendor/symfony/translation/Test/ProviderFactoryTestCase.php b/vendor/symfony/translation/Test/ProviderFactoryTestCase.php new file mode 100644 index 0000000..d6510e0 --- /dev/null +++ b/vendor/symfony/translation/Test/ProviderFactoryTestCase.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Test; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\Translation\Dumper\XliffFileDumper; +use Symfony\Component\Translation\Exception\IncompleteDsnException; +use Symfony\Component\Translation\Exception\UnsupportedSchemeException; +use Symfony\Component\Translation\Loader\LoaderInterface; +use Symfony\Component\Translation\Provider\Dsn; +use Symfony\Component\Translation\Provider\ProviderFactoryInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +/** + * A test case to ease testing a translation provider factory. + * + * @author Mathieu Santostefano + * + * @internal + */ +abstract class ProviderFactoryTestCase extends TestCase +{ + protected $client; + protected $logger; + protected string $defaultLocale; + protected $loader; + protected $xliffFileDumper; + + abstract public function createFactory(): ProviderFactoryInterface; + + /** + * @return iterable + */ + abstract public function supportsProvider(): iterable; + + /** + * @return iterable + */ + abstract public function createProvider(): iterable; + + /** + * @return iterable + */ + public function unsupportedSchemeProvider(): iterable + { + return []; + } + + /** + * @return iterable + */ + public function incompleteDsnProvider(): iterable + { + return []; + } + + /** + * @dataProvider supportsProvider + */ + public function testSupports(bool $expected, string $dsn) + { + $factory = $this->createFactory(); + + $this->assertSame($expected, $factory->supports(new Dsn($dsn))); + } + + /** + * @dataProvider createProvider + */ + public function testCreate(string $expected, string $dsn) + { + $factory = $this->createFactory(); + $provider = $factory->create(new Dsn($dsn)); + + $this->assertSame($expected, (string) $provider); + } + + /** + * @dataProvider unsupportedSchemeProvider + */ + public function testUnsupportedSchemeException(string $dsn, string $message = null) + { + $factory = $this->createFactory(); + + $dsn = new Dsn($dsn); + + $this->expectException(UnsupportedSchemeException::class); + if (null !== $message) { + $this->expectExceptionMessage($message); + } + + $factory->create($dsn); + } + + /** + * @dataProvider incompleteDsnProvider + */ + public function testIncompleteDsnException(string $dsn, string $message = null) + { + $factory = $this->createFactory(); + + $dsn = new Dsn($dsn); + + $this->expectException(IncompleteDsnException::class); + if (null !== $message) { + $this->expectExceptionMessage($message); + } + + $factory->create($dsn); + } + + protected function getClient(): HttpClientInterface + { + return $this->client ??= new MockHttpClient(); + } + + protected function getLogger(): LoggerInterface + { + return $this->logger ??= $this->createMock(LoggerInterface::class); + } + + protected function getDefaultLocale(): string + { + return $this->defaultLocale ??= 'en'; + } + + protected function getLoader(): LoaderInterface + { + return $this->loader ??= $this->createMock(LoaderInterface::class); + } + + protected function getXliffFileDumper(): XliffFileDumper + { + return $this->xliffFileDumper ??= $this->createMock(XliffFileDumper::class); + } +} diff --git a/vendor/symfony/translation/Test/ProviderTestCase.php b/vendor/symfony/translation/Test/ProviderTestCase.php new file mode 100644 index 0000000..5ae2682 --- /dev/null +++ b/vendor/symfony/translation/Test/ProviderTestCase.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Test; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\Translation\Dumper\XliffFileDumper; +use Symfony\Component\Translation\Loader\LoaderInterface; +use Symfony\Component\Translation\Provider\ProviderInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +/** + * A test case to ease testing a translation provider. + * + * @author Mathieu Santostefano + * + * @internal + */ +abstract class ProviderTestCase extends TestCase +{ + protected $client; + protected $logger; + protected string $defaultLocale; + protected $loader; + protected $xliffFileDumper; + + abstract public function createProvider(HttpClientInterface $client, LoaderInterface $loader, LoggerInterface $logger, string $defaultLocale, string $endpoint): ProviderInterface; + + /** + * @return iterable + */ + abstract public function toStringProvider(): iterable; + + /** + * @dataProvider toStringProvider + */ + public function testToString(ProviderInterface $provider, string $expected) + { + $this->assertSame($expected, (string) $provider); + } + + protected function getClient(): MockHttpClient + { + return $this->client ??= new MockHttpClient(); + } + + protected function getLoader(): LoaderInterface + { + return $this->loader ??= $this->createMock(LoaderInterface::class); + } + + protected function getLogger(): LoggerInterface + { + return $this->logger ??= $this->createMock(LoggerInterface::class); + } + + protected function getDefaultLocale(): string + { + return $this->defaultLocale ??= 'en'; + } + + protected function getXliffFileDumper(): XliffFileDumper + { + return $this->xliffFileDumper ??= $this->createMock(XliffFileDumper::class); + } +} diff --git a/vendor/symfony/translation/TranslatableMessage.php b/vendor/symfony/translation/TranslatableMessage.php new file mode 100644 index 0000000..b1a3b6b --- /dev/null +++ b/vendor/symfony/translation/TranslatableMessage.php @@ -0,0 +1,62 @@ + + * + * 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\Contracts\Translation\TranslatableInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * @author Nate Wiebe + */ +class TranslatableMessage implements TranslatableInterface +{ + private string $message; + private array $parameters; + private ?string $domain; + + public function __construct(string $message, array $parameters = [], string $domain = null) + { + $this->message = $message; + $this->parameters = $parameters; + $this->domain = $domain; + } + + public function __toString(): string + { + return $this->getMessage(); + } + + public function getMessage(): string + { + return $this->message; + } + + public function getParameters(): array + { + return $this->parameters; + } + + public function getDomain(): ?string + { + return $this->domain; + } + + public function trans(TranslatorInterface $translator, string $locale = null): string + { + return $translator->trans($this->getMessage(), array_map( + static function ($parameter) use ($translator, $locale) { + return $parameter instanceof TranslatableInterface ? $parameter->trans($translator, $locale) : $parameter; + }, + $this->getParameters() + ), $this->getDomain(), $locale); + } +} diff --git a/vendor/symfony/translation/Translator.php b/vendor/symfony/translation/Translator.php new file mode 100644 index 0000000..05e84d0 --- /dev/null +++ b/vendor/symfony/translation/Translator.php @@ -0,0 +1,469 @@ + + * + * 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\NotFoundResourceException; +use Symfony\Component\Translation\Exception\RuntimeException; +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\Contracts\Translation\LocaleAwareInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +// Help opcache.preload discover always-needed symbols +class_exists(MessageCatalogue::class); + +/** + * @author Fabien Potencier + */ +class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface +{ + /** + * @var MessageCatalogueInterface[] + */ + protected $catalogues = []; + + private string $locale; + + /** + * @var string[] + */ + private array $fallbackLocales = []; + + /** + * @var LoaderInterface[] + */ + private array $loaders = []; + + private array $resources = []; + + private $formatter; + + private ?string $cacheDir; + + private bool $debug; + + private array $cacheVary; + + private $configCacheFactory; + + private array $parentLocales; + + private bool $hasIntlFormatter; + + /** + * @throws InvalidArgumentException If a locale contains invalid characters + */ + public function __construct(string $locale, MessageFormatterInterface $formatter = null, string $cacheDir = null, bool $debug = false, array $cacheVary = []) + { + $this->setLocale($locale); + + if (null === $formatter) { + $formatter = new MessageFormatter(); + } + + $this->formatter = $formatter; + $this->cacheDir = $cacheDir; + $this->debug = $debug; + $this->cacheVary = $cacheVary; + $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()) + */ + public function addLoader(string $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 + * + * @throws InvalidArgumentException If the locale contains invalid characters + */ + public function addResource(string $format, mixed $resource, string $locale, string $domain = null) + { + if (null === $domain) { + $domain = 'messages'; + } + + $this->assertValidLocale($locale); + $locale ?: $locale = class_exists(\Locale::class) ? \Locale::getDefault() : 'en'; + + $this->resources[$locale][] = [$format, $resource, $domain]; + + if (\in_array($locale, $this->fallbackLocales)) { + $this->catalogues = []; + } else { + unset($this->catalogues[$locale]); + } + } + + /** + * {@inheritdoc} + */ + public function setLocale(string $locale) + { + $this->assertValidLocale($locale); + $this->locale = $locale; + } + + /** + * {@inheritdoc} + */ + public function getLocale(): string + { + return $this->locale ?: (class_exists(\Locale::class) ? \Locale::getDefault() : 'en'); + } + + /** + * Sets the fallback locales. + * + * @param string[] $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 = $this->cacheVary['fallback_locales'] = $locales; + } + + /** + * Gets the fallback locales. + * + * @internal + */ + public function getFallbackLocales(): array + { + return $this->fallbackLocales; + } + + /** + * {@inheritdoc} + */ + public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null): string + { + if (null === $id || '' === $id) { + return ''; + } + + if (null === $domain) { + $domain = 'messages'; + } + + $catalogue = $this->getCatalogue($locale); + $locale = $catalogue->getLocale(); + while (!$catalogue->defines($id, $domain)) { + if ($cat = $catalogue->getFallbackCatalogue()) { + $catalogue = $cat; + $locale = $catalogue->getLocale(); + } else { + break; + } + } + + $len = \strlen(MessageCatalogue::INTL_DOMAIN_SUFFIX); + if ($this->hasIntlFormatter + && ($catalogue->defines($id, $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX) + || (\strlen($domain) > $len && 0 === substr_compare($domain, MessageCatalogue::INTL_DOMAIN_SUFFIX, -$len, $len))) + ) { + return $this->formatter->formatIntl($catalogue->get($id, $domain), $locale, $parameters); + } + + return $this->formatter->format($catalogue->get($id, $domain), $locale, $parameters); + } + + /** + * {@inheritdoc} + */ + public function getCatalogue(string $locale = null): MessageCatalogueInterface + { + if (!$locale) { + $locale = $this->getLocale(); + } else { + $this->assertValidLocale($locale); + } + + if (!isset($this->catalogues[$locale])) { + $this->loadCatalogue($locale); + } + + return $this->catalogues[$locale]; + } + + /** + * {@inheritdoc} + */ + public function getCatalogues(): array + { + return array_values($this->catalogues); + } + + /** + * Gets the loaders. + * + * @return LoaderInterface[] + */ + protected function getLoaders(): array + { + return $this->loaders; + } + + protected function loadCatalogue(string $locale) + { + if (null === $this->cacheDir) { + $this->initializeCatalogue($locale); + } else { + $this->initializeCacheCatalogue($locale); + } + } + + protected function initializeCatalogue(string $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(string $locale, ConfigCacheInterface $cache): void + { + $this->initializeCatalogue($locale); + $fallbackContent = $this->getFallbackContent($this->catalogues[$locale]); + + $content = sprintf(<<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(string $locale): string + { + return $this->cacheDir.'/catalogue.'.$locale.'.'.strtr(substr(base64_encode(hash('sha256', serialize($this->cacheVary), true)), 0, 7), '/', '_').'.php'; + } + + /** + * @internal + */ + protected function doLoadCatalogue(string $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]])) { + if (\is_string($resource[1])) { + throw new RuntimeException(sprintf('No loader is registered for the "%s" format when loading the "%s" resource.', $resource[0], $resource[1])); + } + + throw new RuntimeException(sprintf('No loader is registered for the "%s" format.', $resource[0])); + } + $this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2])); + } + } + } + + private function loadFallbackCatalogues(string $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(string $locale) + { + $this->parentLocales ??= json_decode(file_get_contents(__DIR__.'/Resources/data/parents.json'), true); + + $originLocale = $locale; + $locales = []; + + while ($locale) { + $parent = $this->parentLocales[$locale] ?? null; + + if ($parent) { + $locale = 'root' !== $parent ? $parent : null; + } elseif (\function_exists('locale_parse')) { + $localeSubTags = locale_parse($locale); + $locale = null; + if (1 < \count($localeSubTags)) { + array_pop($localeSubTags); + $locale = locale_compose($localeSubTags) ?: null; + } + } elseif ($i = strrpos($locale, '_') ?: strrpos($locale, '-')) { + $locale = substr($locale, 0, $i); + } else { + $locale = null; + } + + if (null !== $locale) { + $locales[] = $locale; + } + } + + foreach ($this->fallbackLocales as $fallback) { + if ($fallback === $originLocale) { + continue; + } + + $locales[] = $fallback; + } + + return array_unique($locales); + } + + /** + * Asserts that the locale is valid, throws an Exception if not. + * + * @throws InvalidArgumentException If the locale contains invalid characters + */ + protected function assertValidLocale(string $locale) + { + if (!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 + { + $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/TranslatorBag.php b/vendor/symfony/translation/TranslatorBag.php new file mode 100644 index 0000000..9be3458 --- /dev/null +++ b/vendor/symfony/translation/TranslatorBag.php @@ -0,0 +1,108 @@ + + * + * 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\Catalogue\AbstractOperation; +use Symfony\Component\Translation\Catalogue\TargetOperation; + +final class TranslatorBag implements TranslatorBagInterface +{ + /** @var MessageCatalogue[] */ + private array $catalogues = []; + + public function addCatalogue(MessageCatalogue $catalogue): void + { + if (null !== $existingCatalogue = $this->getCatalogue($catalogue->getLocale())) { + $catalogue->addCatalogue($existingCatalogue); + } + + $this->catalogues[$catalogue->getLocale()] = $catalogue; + } + + public function addBag(TranslatorBagInterface $bag): void + { + foreach ($bag->getCatalogues() as $catalogue) { + $this->addCatalogue($catalogue); + } + } + + /** + * {@inheritdoc} + */ + public function getCatalogue(string $locale = null): MessageCatalogueInterface + { + if (null === $locale || !isset($this->catalogues[$locale])) { + $this->catalogues[$locale] = new MessageCatalogue($locale); + } + + return $this->catalogues[$locale]; + } + + /** + * {@inheritdoc} + */ + public function getCatalogues(): array + { + return array_values($this->catalogues); + } + + public function diff(TranslatorBagInterface $diffBag): self + { + $diff = new self(); + + foreach ($this->catalogues as $locale => $catalogue) { + if (null === $diffCatalogue = $diffBag->getCatalogue($locale)) { + $diff->addCatalogue($catalogue); + + continue; + } + + $operation = new TargetOperation($diffCatalogue, $catalogue); + $operation->moveMessagesToIntlDomainsIfPossible(AbstractOperation::NEW_BATCH); + $newCatalogue = new MessageCatalogue($locale); + + foreach ($operation->getDomains() as $domain) { + $newCatalogue->add($operation->getNewMessages($domain), $domain); + } + + $diff->addCatalogue($newCatalogue); + } + + return $diff; + } + + public function intersect(TranslatorBagInterface $intersectBag): self + { + $diff = new self(); + + foreach ($this->catalogues as $locale => $catalogue) { + if (null === $intersectCatalogue = $intersectBag->getCatalogue($locale)) { + continue; + } + + $operation = new TargetOperation($catalogue, $intersectCatalogue); + $operation->moveMessagesToIntlDomainsIfPossible(AbstractOperation::OBSOLETE_BATCH); + $obsoleteCatalogue = new MessageCatalogue($locale); + + foreach ($operation->getDomains() as $domain) { + $obsoleteCatalogue->add( + array_diff($operation->getMessages($domain), $operation->getNewMessages($domain)), + $domain + ); + } + + $diff->addCatalogue($obsoleteCatalogue); + } + + return $diff; + } +} diff --git a/vendor/symfony/translation/TranslatorBagInterface.php b/vendor/symfony/translation/TranslatorBagInterface.php new file mode 100644 index 0000000..a787acf --- /dev/null +++ b/vendor/symfony/translation/TranslatorBagInterface.php @@ -0,0 +1,36 @@ + + * + * 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; + +/** + * @author Abdellatif Ait boudad + */ +interface TranslatorBagInterface +{ + /** + * Gets the catalogue by locale. + * + * @param string|null $locale The locale or null to use the default + * + * @throws InvalidArgumentException If the locale contains invalid characters + */ + public function getCatalogue(string $locale = null): MessageCatalogueInterface; + + /** + * Returns all catalogues of the instance. + * + * @return MessageCatalogueInterface[] + */ + public function getCatalogues(): array; +} diff --git a/vendor/symfony/translation/Util/ArrayConverter.php b/vendor/symfony/translation/Util/ArrayConverter.php new file mode 100644 index 0000000..60b8be6 --- /dev/null +++ b/vendor/symfony/translation/Util/ArrayConverter.php @@ -0,0 +1,97 @@ + + * + * 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 + */ +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 + */ + public static function expandToTree(array $messages): array + { + $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, string $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 100644 index 0000000..85ecc85 --- /dev/null +++ b/vendor/symfony/translation/Util/XliffUtils.php @@ -0,0 +1,191 @@ + + * + * 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 + */ +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); + if ($shouldEnable = self::shouldEnableEntityLoader()) { + $disableEntities = libxml_disable_entity_loader(false); + } + try { + $isValid = @$dom->schemaValidateSource(self::getSchema($xliffVersion)); + if (!$isValid) { + return self::getXmlErrors($internalErrors); + } + } finally { + if ($shouldEnable) { + libxml_disable_entity_loader($disableEntities); + } + } + + $dom->normalizeDocument(); + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + + return []; + } + + private static function shouldEnableEntityLoader(): bool + { + static $dom, $schema; + if (null === $dom) { + $dom = new \DOMDocument(); + $dom->loadXML(''); + + $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); + register_shutdown_function(static function () use ($tmpfile) { + @unlink($tmpfile); + }); + $schema = ' + + +'; + file_put_contents($tmpfile, ' + + + +'); + } + + return !@$dom->schemaValidateSource($schema); + } + + 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 100644 index 0000000..5dd3a5c --- /dev/null +++ b/vendor/symfony/translation/Writer/TranslationWriter.php @@ -0,0 +1,71 @@ + + * + * 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 + */ +class TranslationWriter implements TranslationWriterInterface +{ + /** + * @var array + */ + private array $dumpers = []; + + /** + * Adds a dumper to the writer. + */ + public function addDumper(string $format, DumperInterface $dumper) + { + $this->dumpers[$format] = $dumper; + } + + /** + * Obtains the list of supported formats. + */ + public function getFormats(): array + { + return array_keys($this->dumpers); + } + + /** + * Writes translation from the catalogue according to the selected format. + * + * @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, string $format, array $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 100644 index 0000000..4321309 --- /dev/null +++ b/vendor/symfony/translation/Writer/TranslationWriterInterface.php @@ -0,0 +1,33 @@ + + * + * 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 + */ +interface TranslationWriterInterface +{ + /** + * Writes translation from the catalogue according to the selected format. + * + * @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, string $format, array $options = []); +} diff --git a/vendor/symfony/translation/composer.json b/vendor/symfony/translation/composer.json new file mode 100644 index 0000000..abe8b97 --- /dev/null +++ b/vendor/symfony/translation/composer.json @@ -0,0 +1,60 @@ +{ + "name": "symfony/translation", + "type": "library", + "description": "Provides tools to internationalize your application", + "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": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.3|^3.0" + }, + "require-dev": { + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/intl": "^5.4|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "psr/log": "^1|^2|^3" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4", + "symfony/console": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "suggest": { + "symfony/config": "", + "symfony/yaml": "", + "psr/log-implementation": "To use logging capability in translator" + }, + "autoload": { + "files": [ "Resources/functions.php" ], + "psr-4": { "Symfony\\Component\\Translation\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/vendor/symfony/var-dumper/CHANGELOG.md b/vendor/symfony/var-dumper/CHANGELOG.md new file mode 100644 index 0000000..f58ed31 --- /dev/null +++ b/vendor/symfony/var-dumper/CHANGELOG.md @@ -0,0 +1,72 @@ +CHANGELOG +========= + +5.4 +--- + + * Add ability to style integer and double values independently + * Add casters for Symfony's UUIDs and ULIDs + * Add support for `Fiber` + +5.2.0 +----- + + * added support for PHPUnit `--colors` option + * added `VAR_DUMPER_FORMAT=server` env var value support + * prevent replacing the handler when the `VAR_DUMPER_FORMAT` env var is set + +5.1.0 +----- + + * added `RdKafka` support + +4.4.0 +----- + + * added `VarDumperTestTrait::setUpVarDumper()` and `VarDumperTestTrait::tearDownVarDumper()` + to configure casters & flags to use in tests + * added `ImagineCaster` and infrastructure to dump images + * added the stamps of a message after it is dispatched in `TraceableMessageBus` and `MessengerDataCollector` collected data + * added `UuidCaster` + * made all casters final + * added support for the `NO_COLOR` env var (https://no-color.org/) + +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 `$filter = 0` + argument and moves `$message = ''` argument at 4th position. + * the `VarDumperTestTrait::assertDumpMatchesFormat()` method expects a 3rd `$filter = 0` + 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 100644 index 0000000..dc3b621 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/AmqpCaster.php @@ -0,0 +1,212 @@ + + * + * 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 + * + * @final + */ +class AmqpCaster +{ + private const 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 const EXCHANGE_TYPES = [ + \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, bool $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, bool $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, bool $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, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'flags' => self::extractFlags($c->getFlags()), + ]; + + $type = isset(self::EXCHANGE_TYPES[$c->getType()]) ? new ConstStub(self::EXCHANGE_TYPES[$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, bool $isNested, int $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(int $flags): ConstStub + { + $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 100644 index 0000000..3109874 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/ArgsStub.php @@ -0,0 +1,80 @@ + + * + * 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 + */ +class ArgsStub extends EnumStub +{ + private static array $parameters = []; + + public function __construct(array $args, string $function, ?string $class) + { + [$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(string $function, ?string $class): array + { + 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 100644 index 0000000..890f531 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/Caster.php @@ -0,0 +1,170 @@ + + * + * 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 + * + * @final + */ +class Caster +{ + public const EXCLUDE_VERBOSE = 1; + public const EXCLUDE_VIRTUAL = 2; + public const EXCLUDE_DYNAMIC = 4; + public const EXCLUDE_PUBLIC = 8; + public const EXCLUDE_PROTECTED = 16; + public const EXCLUDE_PRIVATE = 32; + public const EXCLUDE_NULL = 64; + public const EXCLUDE_EMPTY = 128; + public const EXCLUDE_NOT_IMPORTANT = 256; + public const EXCLUDE_STRICT = 512; + + public const PREFIX_VIRTUAL = "\0~\0"; + public const PREFIX_DYNAMIC = "\0+\0"; + public const PREFIX_PROTECTED = "\0*\0"; + + /** + * Casts objects to arrays and adds the dynamic property prefix. + * + * @param bool $hasDebugInfo Whether the __debugInfo method exists on $obj or not + */ + public static function castObject(object $obj, string $class, bool $hasDebugInfo = false, string $debugClass = null): array + { + if ($hasDebugInfo) { + try { + $debugInfo = $obj->__debugInfo(); + } catch (\Throwable $e) { + // ignore failing __debugInfo() + $hasDebugInfo = false; + } + } + + $a = $obj instanceof \Closure ? [] : (array) $obj; + + if ($obj instanceof \__PHP_Incomplete_Class) { + return $a; + } + + if ($a) { + static $publicProperties = []; + $debugClass = $debugClass ?? get_debug_type($obj); + + $i = 0; + $prefixedKeys = []; + foreach ($a as $k => $v) { + if ("\0" !== ($k[0] ?? '')) { + if (!isset($publicProperties[$class])) { + foreach ((new \ReflectionClass($class))->getProperties(\ReflectionProperty::IS_PUBLIC) as $prop) { + $publicProperties[$class][$prop->name] = true; + } + } + if (!isset($publicProperties[$class][$k])) { + $prefixedKeys[$i] = self::PREFIX_DYNAMIC.$k; + } + } elseif ($debugClass !== $class && 1 === strpos($k, $class)) { + $prefixedKeys[$i] = "\0".$debugClass.strrchr($k, "\0"); + } + ++$i; + } + if ($prefixedKeys) { + $keys = array_keys($a); + foreach ($prefixedKeys as $i => $k) { + $keys[$i] = $k; + } + $a = array_combine($keys, $a); + } + } + + if ($hasDebugInfo && \is_array($debugInfo)) { + foreach ($debugInfo as $k => $v) { + if (!isset($k[0]) || "\0" !== $k[0]) { + if (\array_key_exists(self::PREFIX_DYNAMIC.$k, $a)) { + continue; + } + $k = self::PREFIX_VIRTUAL.$k; + } + + unset($a[$k]); + $a[$k] = $v; + } + } + + 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 + */ + public static function filter(array $a, int $filter, array $listedProperties = [], ?int &$count = 0): array + { + $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, bool $isNested): array + { + 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 100644 index 0000000..1ac6d0a --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/ClassStub.php @@ -0,0 +1,106 @@ + + * + * 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 + */ +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|array|string $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 (str_contains($identifier, "@anonymous\0")) { + $this->value = $identifier = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) { + return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0]; + }, $identifier); + } + + if (null !== $callable && $r instanceof \ReflectionFunctionAbstract) { + $s = ReflectionCaster::castFunctionAbstract($r, [], new Stub(), true, Caster::EXCLUDE_VERBOSE); + $s = ReflectionCaster::getSignature($s); + + if (str_ends_with($identifier, '()')) { + $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(mixed $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 100644 index 0000000..d7d1812 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/ConstStub.php @@ -0,0 +1,33 @@ + + * + * 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 + */ +class ConstStub extends Stub +{ + public function __construct(string $name, string|int|float $value = null) + { + $this->class = $name; + $this->value = 1 < \func_num_args() ? $value : $name; + } + + public function __toString(): string + { + 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 100644 index 0000000..0e4fb36 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/CutArrayStub.php @@ -0,0 +1,30 @@ + + * + * 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 + */ +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 100644 index 0000000..b5a96a0 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/CutStub.php @@ -0,0 +1,64 @@ + + * + * 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 + */ +class CutStub extends Stub +{ + public function __construct(mixed $value) + { + $this->value = $value; + + switch (\gettype($value)) { + case 'object': + $this->type = self::TYPE_OBJECT; + $this->class = \get_class($value); + + if ($value instanceof \Closure) { + ReflectionCaster::castClosure($value, [], $this, true, Caster::EXCLUDE_VERBOSE); + } + + $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 100644 index 0000000..4dd16e0 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/DOMCaster.php @@ -0,0 +1,304 @@ + + * + * 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 + * + * @final + */ +class DOMCaster +{ + private const ERROR_CODES = [ + \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 const NODE_TYPES = [ + \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, bool $isNested) + { + $k = Caster::PREFIX_PROTECTED.'code'; + if (isset($a[$k], self::ERROR_CODES[$a[$k]])) { + $a[$k] = new ConstStub(self::ERROR_CODES[$a[$k]], $a[$k]); + } + + return $a; + } + + public static function castLength($dom, array $a, Stub $stub, bool $isNested) + { + $a += [ + 'length' => $dom->length, + ]; + + return $a; + } + + public static function castImplementation(\DOMImplementation $dom, array $a, Stub $stub, bool $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, bool $isNested) + { + $a += [ + 'nodeName' => $dom->nodeName, + 'nodeValue' => new CutStub($dom->nodeValue), + 'nodeType' => new ConstStub(self::NODE_TYPES[$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, bool $isNested) + { + $a += [ + 'nodeName' => $dom->nodeName, + 'nodeValue' => new CutStub($dom->nodeValue), + 'nodeType' => new ConstStub(self::NODE_TYPES[$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, bool $isNested, int $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, bool $isNested) + { + $a += [ + 'data' => $dom->data, + 'length' => $dom->length, + ]; + + return $a; + } + + public static function castAttr(\DOMAttr $dom, array $a, Stub $stub, bool $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, bool $isNested) + { + $a += [ + 'tagName' => $dom->tagName, + 'schemaTypeInfo' => $dom->schemaTypeInfo, + ]; + + return $a; + } + + public static function castText(\DOMText $dom, array $a, Stub $stub, bool $isNested) + { + $a += [ + 'wholeText' => $dom->wholeText, + ]; + + return $a; + } + + public static function castTypeinfo(\DOMTypeinfo $dom, array $a, Stub $stub, bool $isNested) + { + $a += [ + 'typeName' => $dom->typeName, + 'typeNamespace' => $dom->typeNamespace, + ]; + + return $a; + } + + public static function castDomError(\DOMDomError $dom, array $a, Stub $stub, bool $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, bool $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, bool $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, bool $isNested) + { + $a += [ + 'publicId' => $dom->publicId, + 'systemId' => $dom->systemId, + ]; + + return $a; + } + + public static function castEntity(\DOMEntity $dom, array $a, Stub $stub, bool $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, bool $isNested) + { + $a += [ + 'target' => $dom->target, + 'data' => $dom->data, + ]; + + return $a; + } + + public static function castXPath(\DOMXPath $dom, array $a, Stub $stub, bool $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 100644 index 0000000..18641fb --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/DateCaster.php @@ -0,0 +1,127 @@ + + * + * 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 + * + * @final + */ +class DateCaster +{ + private const PERIOD_LIMIT = 3; + + public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, bool $isNested, int $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") : '') + ; + + unset( + $a[Caster::PREFIX_DYNAMIC.'date'], + $a[Caster::PREFIX_DYNAMIC.'timezone'], + $a[Caster::PREFIX_DYNAMIC.'timezone_type'] + ); + $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, bool $isNested, int $filter) + { + $now = new \DateTimeImmutable('@0', new \DateTimeZone('UTC')); + $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): string + { + $format = '%R '; + + if (0 === $i->y && 0 === $i->m && ($i->h >= 24 || $i->i >= 60 || $i->s >= 60)) { + $d = new \DateTimeImmutable('@0', new \DateTimeZone('UTC')); + $i = $d->diff($d->add($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, bool $isNested, int $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, bool $isNested, int $filter) + { + $dates = []; + foreach (clone $p as $i => $d) { + if (self::PERIOD_LIMIT === $i) { + $now = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); + $dates[] = sprintf('%s more', ($end = $p->getEndDate()) + ? ceil(($end->format('U.u') - $d->format('U.u')) / ((int) $now->add($p->getDateInterval())->format('U.u') - (int) $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()), + $p->include_start_date ? '[' : ']', + self::formatDateTime($p->getStartDate()), + ($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end).(\PHP_VERSION_ID >= 80200 && $p->include_end_date ? ']' : '[') : '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, string $extra = ''): string + { + return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra); + } + + private static function formatSeconds(string $s, string $us): string + { + 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 100644 index 0000000..129b2cb --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/DoctrineCaster.php @@ -0,0 +1,62 @@ + + * + * 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 + * + * @final + */ +class DoctrineCaster +{ + public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, bool $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, bool $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, bool $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 100644 index 0000000..b34b670 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/DsCaster.php @@ -0,0 +1,70 @@ + + * + * 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 + * + * @final + */ +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 100644 index 0000000..22112af --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/DsPairStub.php @@ -0,0 +1,28 @@ + + * + * 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 + */ +class DsPairStub extends Stub +{ + public function __construct(string|int $key, mixed $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 100644 index 0000000..7a4e98a --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/EnumStub.php @@ -0,0 +1,30 @@ + + * + * 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 + */ +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 100644 index 0000000..8e517e0 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/ExceptionCaster.php @@ -0,0 +1,388 @@ + + * + * 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\ErrorHandler\Exception\SilencedErrorContext; +use Symfony\Component\VarDumper\Cloner\Stub; +use Symfony\Component\VarDumper\Exception\ThrowingCasterException; + +/** + * Casts common Exception classes to array representation. + * + * @author Nicolas Grekas + * + * @final + */ +class ExceptionCaster +{ + public static int $srcContext = 1; + public static bool $traceArgs = true; + public static array $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 array $framesCache = []; + + public static function castError(\Error $e, array $a, Stub $stub, bool $isNested, int $filter = 0) + { + return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter); + } + + public static function castException(\Exception $e, array $a, Stub $stub, bool $isNested, int $filter = 0) + { + return self::filterExceptionArray($stub->class, $a, "\0Exception\0", $filter); + } + + public static function castErrorException(\ErrorException $e, array $a, Stub $stub, bool $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, bool $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_debug_type($a[$xPrefix.'previous']); + 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, bool $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, bool $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' => $f['object'] ?? null, + 'class' => $f['class'] ?? null, + 'type' => $f['type'] ?? null, + '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 (str_starts_with($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'], $f['function'] ?? null, $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, bool $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]; + } + $src = $f['line']; + $srcKey = $f['file']; + $ellipsis = new LinkStub($srcKey, 0); + $srcAttr = 'collapse='.(int) $ellipsis->inVendor; + $ellipsisTail = $ellipsis->attr['ellipsis-tail'] ?? 0; + $ellipsis = $ellipsis->attr['ellipsis'] ?? 0; + + if (is_file($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 = null; + if (isset($f['object'])) { + $template = $f['object']; + } elseif ((new \ReflectionClass($f['class']))->isInstantiable()) { + $template = unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class'])); + } + if (null !== $template) { + $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') || !is_file($templatePath = $template->getSourceContext()->getPath())) { + $templatePath = null; + } + if ($templateSrc) { + $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, 'twig', $templatePath, $f); + $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']]; + } + } + } + } + if ($srcKey == $f['file']) { + $src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, 'php', $f['file'], $f); + $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(string $xClass, array $a, string $xPrefix, int $filter): array + { + 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']) && str_contains($a[Caster::PREFIX_PROTECTED.'message'], "@anonymous\0")) { + $a[Caster::PREFIX_PROTECTED.'message'] = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) { + return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@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(array &$trace, ?string $class, string $file, int $line): void + { + 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(string $srcLines, int $line, int $srcContext, string $lang, ?string $file, array $frame): EnumStub + { + $srcLines = explode("\n", $srcLines); + $src = []; + + for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) { + $src[] = ($srcLines[$i] ?? '')."\n"; + } + + if ($frame['function'] ?? false) { + $stub = new CutStub(new \stdClass()); + $stub->class = (isset($frame['class']) ? $frame['class'].$frame['type'] : '').$frame['function']; + $stub->type = Stub::TYPE_OBJECT; + $stub->attr['cut_hash'] = true; + $stub->attr['file'] = $frame['file']; + $stub->attr['line'] = $frame['line']; + + try { + $caller = isset($frame['class']) ? new \ReflectionMethod($frame['class'], $frame['function']) : new \ReflectionFunction($frame['function']); + $stub->class .= ReflectionCaster::getSignature(ReflectionCaster::castFunctionAbstract($caller, [], $stub, true, Caster::EXCLUDE_VERBOSE)); + + if ($f = $caller->getFileName()) { + $stub->attr['file'] = $f; + $stub->attr['line'] = $caller->getStartLine(); + } + } catch (\ReflectionException $e) { + // ignore fake class/function + } + + $srcLines = ["\0~separator=\0" => $stub]; + } else { + $stub = null; + $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, $stub ? 'in '.$stub->class : ''); + 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/FiberCaster.php b/vendor/symfony/var-dumper/Caster/FiberCaster.php new file mode 100644 index 0000000..c74a9e5 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/FiberCaster.php @@ -0,0 +1,43 @@ + + * + * 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 Fiber related classes to array representation. + * + * @author Grégoire Pineau + */ +final class FiberCaster +{ + public static function castFiber(\Fiber $fiber, array $a, Stub $stub, bool $isNested, int $filter = 0) + { + $prefix = Caster::PREFIX_VIRTUAL; + + if ($fiber->isTerminated()) { + $status = 'terminated'; + } elseif ($fiber->isRunning()) { + $status = 'running'; + } elseif ($fiber->isSuspended()) { + $status = 'suspended'; + } elseif ($fiber->isStarted()) { + $status = 'started'; + } else { + $status = 'not started'; + } + + $a[$prefix.'status'] = $status; + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/FrameStub.php b/vendor/symfony/var-dumper/Caster/FrameStub.php new file mode 100644 index 0000000..8786755 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/FrameStub.php @@ -0,0 +1,30 @@ + + * + * 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 + */ +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 100644 index 0000000..b018cc7 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/GmpCaster.php @@ -0,0 +1,32 @@ + + * + * 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 + * @author Nicolas Grekas + * + * @final + */ +class GmpCaster +{ + public static function castGmp(\GMP $gmp, array $a, Stub $stub, bool $isNested, int $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/ImagineCaster.php b/vendor/symfony/var-dumper/Caster/ImagineCaster.php new file mode 100644 index 0000000..d1289da --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/ImagineCaster.php @@ -0,0 +1,37 @@ + + * + * 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 Imagine\Image\ImageInterface; +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * @author Grégoire Pineau + */ +final class ImagineCaster +{ + public static function castImage(ImageInterface $c, array $a, Stub $stub, bool $isNested): array + { + $imgData = $c->get('png'); + if (\strlen($imgData) > 1 * 1000 * 1000) { + $a += [ + Caster::PREFIX_VIRTUAL.'image' => new ConstStub($c->getSize()), + ]; + } else { + $a += [ + Caster::PREFIX_VIRTUAL.'image' => new ImgStub($imgData, 'image/png', $c->getSize()), + ]; + } + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/ImgStub.php b/vendor/symfony/var-dumper/Caster/ImgStub.php new file mode 100644 index 0000000..a16681f --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/ImgStub.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +/** + * @author Grégoire Pineau + */ +class ImgStub extends ConstStub +{ + public function __construct(string $data, string $contentType, string $size = '') + { + $this->value = ''; + $this->attr['img-data'] = $data; + $this->attr['img-size'] = $size; + $this->attr['content-type'] = $contentType; + } +} diff --git a/vendor/symfony/var-dumper/Caster/IntlCaster.php b/vendor/symfony/var-dumper/Caster/IntlCaster.php new file mode 100644 index 0000000..1ed91d4 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/IntlCaster.php @@ -0,0 +1,172 @@ + + * + * 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 + * @author Jan Schädlich + * + * @final + */ +class IntlCaster +{ + public static function castMessageFormatter(\MessageFormatter $c, array $a, Stub $stub, bool $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, bool $isNested, int $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, bool $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, bool $isNested, int $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, bool $isNested, int $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(object $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 100644 index 0000000..36e0d3c --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/LinkStub.php @@ -0,0 +1,108 @@ + + * + * 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 + */ +class LinkStub extends ConstStub +{ + public $inVendor = false; + + private static array $vendorRoots; + private static array $composerRoots = []; + + public function __construct(string $label, int $line = 0, string $href = null) + { + $this->value = $label; + + if (null === $href) { + $href = $label; + } + if (!\is_string($href)) { + return; + } + if (str_starts_with($href, 'file://')) { + if ($href === $label) { + $label = substr($label, 7); + } + $href = substr($href, 7); + } elseif (str_contains($href, '://')) { + $this->attr['href'] = $href; + + return; + } + if (!is_file($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(string $file, bool &$inVendor) + { + if (!isset(self::$vendorRoots)) { + self::$vendorRoots = []; + + foreach (get_declared_classes() as $class) { + if ('C' === $class[0] && str_starts_with($class, 'ComposerAutoloaderInit')) { + $r = new \ReflectionClass($class); + $v = \dirname($r->getFileName(), 2); + if (is_file($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 = str_starts_with($file, $root)) { + return $root; + } + } + + $parent = $dir; + while (!@is_file($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 100644 index 0000000..d6baa25 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/MemcachedCaster.php @@ -0,0 +1,81 @@ + + * + * 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 + * + * @final + */ +class MemcachedCaster +{ + private static array $optionConstants; + private static array $defaultOptions; + + public static function castMemcached(\Memcached $c, array $a, Stub $stub, bool $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): array + { + 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(): array + { + $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(): array + { + $reflectedMemcached = new \ReflectionClass(\Memcached::class); + + $optionConstants = []; + foreach ($reflectedMemcached->getConstants() as $constantKey => $value) { + if (str_starts_with($constantKey, 'OPT_')) { + $optionConstants[$constantKey] = $value; + } + } + + return $optionConstants; + } +} diff --git a/vendor/symfony/var-dumper/Caster/MysqliCaster.php b/vendor/symfony/var-dumper/Caster/MysqliCaster.php new file mode 100644 index 0000000..bfe6f08 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/MysqliCaster.php @@ -0,0 +1,33 @@ + + * + * 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 + * + * @internal + */ +final class MysqliCaster +{ + public static function castMysqliDriver(\mysqli_driver $c, array $a, Stub $stub, bool $isNested): array + { + foreach ($a as $k => $v) { + if (isset($c->$k)) { + $a[$k] = $c->$k; + } + } + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/PdoCaster.php b/vendor/symfony/var-dumper/Caster/PdoCaster.php new file mode 100644 index 0000000..140473b --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/PdoCaster.php @@ -0,0 +1,122 @@ + + * + * 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 + * + * @final + */ +class PdoCaster +{ + private const PDO_ATTRIBUTES = [ + '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, bool $isNested) + { + $attr = []; + $errmode = $c->getAttribute(\PDO::ATTR_ERRMODE); + $c->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + + foreach (self::PDO_ATTRIBUTES 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, bool $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 100644 index 0000000..d8e5b52 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/PgSqlCaster.php @@ -0,0 +1,156 @@ + + * + * 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 + * + * @final + */ +class PgSqlCaster +{ + private const PARAM_CODES = [ + 'server_encoding', + 'client_encoding', + 'is_superuser', + 'session_authorization', + 'DateStyle', + 'TimeZone', + 'IntervalStyle', + 'integer_datetimes', + 'application_name', + 'standard_conforming_strings', + ]; + + private const TRANSACTION_STATUS = [ + \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 const RESULT_STATUS = [ + \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 const DIAG_CODES = [ + '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, bool $isNested) + { + $a['seek position'] = pg_lo_tell($lo); + + return $a; + } + + public static function castLink($link, array $a, Stub $stub, bool $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::TRANSACTION_STATUS[$a['transaction']])) { + $a['transaction'] = new ConstStub(self::TRANSACTION_STATUS[$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::PARAM_CODES 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, bool $isNested) + { + $a['num rows'] = pg_num_rows($result); + $a['status'] = pg_result_status($result); + if (isset(self::RESULT_STATUS[$a['status']])) { + $a['status'] = new ConstStub(self::RESULT_STATUS[$a['status']], $a['status']); + } + $a['command-completion tag'] = pg_result_status($result, \PGSQL_STATUS_STRING); + + if (-1 === $a['num rows']) { + foreach (self::DIAG_CODES 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 100644 index 0000000..e712019 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php @@ -0,0 +1,33 @@ + + * + * 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 + * + * @final + */ +class ProxyManagerCaster +{ + public static function castProxy(ProxyInterface $c, array $a, Stub $stub, bool $isNested) + { + if ($parent = get_parent_class($c)) { + $stub->class .= ' - '.$parent; + } + $stub->class .= '@proxy'; + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/RdKafkaCaster.php b/vendor/symfony/var-dumper/Caster/RdKafkaCaster.php new file mode 100644 index 0000000..8053363 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/RdKafkaCaster.php @@ -0,0 +1,186 @@ + + * + * 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 RdKafka\Conf; +use RdKafka\Exception as RdKafkaException; +use RdKafka\KafkaConsumer; +use RdKafka\Message; +use RdKafka\Metadata\Broker as BrokerMetadata; +use RdKafka\Metadata\Collection as CollectionMetadata; +use RdKafka\Metadata\Partition as PartitionMetadata; +use RdKafka\Metadata\Topic as TopicMetadata; +use RdKafka\Topic; +use RdKafka\TopicConf; +use RdKafka\TopicPartition; +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts RdKafka related classes to array representation. + * + * @author Romain Neutron + */ +class RdKafkaCaster +{ + public static function castKafkaConsumer(KafkaConsumer $c, array $a, Stub $stub, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + try { + $assignment = $c->getAssignment(); + } catch (RdKafkaException $e) { + $assignment = []; + } + + $a += [ + $prefix.'subscription' => $c->getSubscription(), + $prefix.'assignment' => $assignment, + ]; + + $a += self::extractMetadata($c); + + return $a; + } + + public static function castTopic(Topic $c, array $a, Stub $stub, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'name' => $c->getName(), + ]; + + return $a; + } + + public static function castTopicPartition(TopicPartition $c, array $a) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'offset' => $c->getOffset(), + $prefix.'partition' => $c->getPartition(), + $prefix.'topic' => $c->getTopic(), + ]; + + return $a; + } + + public static function castMessage(Message $c, array $a, Stub $stub, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'errstr' => $c->errstr(), + ]; + + return $a; + } + + public static function castConf(Conf $c, array $a, Stub $stub, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + foreach ($c->dump() as $key => $value) { + $a[$prefix.$key] = $value; + } + + return $a; + } + + public static function castTopicConf(TopicConf $c, array $a, Stub $stub, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + foreach ($c->dump() as $key => $value) { + $a[$prefix.$key] = $value; + } + + return $a; + } + + public static function castRdKafka(\RdKafka $c, array $a, Stub $stub, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'out_q_len' => $c->getOutQLen(), + ]; + + $a += self::extractMetadata($c); + + return $a; + } + + public static function castCollectionMetadata(CollectionMetadata $c, array $a, Stub $stub, bool $isNested) + { + $a += iterator_to_array($c); + + return $a; + } + + public static function castTopicMetadata(TopicMetadata $c, array $a, Stub $stub, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'name' => $c->getTopic(), + $prefix.'partitions' => $c->getPartitions(), + ]; + + return $a; + } + + public static function castPartitionMetadata(PartitionMetadata $c, array $a, Stub $stub, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'id' => $c->getId(), + $prefix.'err' => $c->getErr(), + $prefix.'leader' => $c->getLeader(), + ]; + + return $a; + } + + public static function castBrokerMetadata(BrokerMetadata $c, array $a, Stub $stub, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'id' => $c->getId(), + $prefix.'host' => $c->getHost(), + $prefix.'port' => $c->getPort(), + ]; + + return $a; + } + + private static function extractMetadata(KafkaConsumer|\RdKafka $c) + { + $prefix = Caster::PREFIX_VIRTUAL; + + try { + $m = $c->getMetadata(true, null, 500); + } catch (RdKafkaException $e) { + return []; + } + + return [ + $prefix.'orig_broker_id' => $m->getOrigBrokerId(), + $prefix.'orig_broker_name' => $m->getOrigBrokerName(), + $prefix.'brokers' => $m->getBrokers(), + $prefix.'topics' => $m->getTopics(), + ]; + } +} diff --git a/vendor/symfony/var-dumper/Caster/RedisCaster.php b/vendor/symfony/var-dumper/Caster/RedisCaster.php new file mode 100644 index 0000000..eac25a1 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/RedisCaster.php @@ -0,0 +1,149 @@ + + * + * 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 + * + * @final + */ +class RedisCaster +{ + private const SERIALIZERS = [ + \Redis::SERIALIZER_NONE => 'NONE', + \Redis::SERIALIZER_PHP => 'PHP', + 2 => 'IGBINARY', // Optional Redis::SERIALIZER_IGBINARY + ]; + + private const MODES = [ + \Redis::ATOMIC => 'ATOMIC', + \Redis::MULTI => 'MULTI', + \Redis::PIPELINE => 'PIPELINE', + ]; + + private const COMPRESSION_MODES = [ + 0 => 'NONE', // Redis::COMPRESSION_NONE + 1 => 'LZF', // Redis::COMPRESSION_LZF + ]; + + private const FAILOVER_OPTIONS = [ + \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, bool $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::MODES[$mode]) ? new ConstStub(self::MODES[$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, bool $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, bool $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_OPTIONS[$failover]) ? new ConstStub(self::FAILOVER_OPTIONS[$failover], $failover) : $failover, + ]), + ]; + + return $a; + } + + private static function getRedisOptions(\Redis|\RedisArray|\RedisCluster $redis, array $options = []): EnumStub + { + $serializer = $redis->getOption(\Redis::OPT_SERIALIZER); + if (\is_array($serializer)) { + foreach ($serializer as &$v) { + if (isset(self::SERIALIZERS[$v])) { + $v = new ConstStub(self::SERIALIZERS[$v], $v); + } + } + } elseif (isset(self::SERIALIZERS[$serializer])) { + $serializer = new ConstStub(self::SERIALIZERS[$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_MODES[$v])) { + $v = new ConstStub(self::COMPRESSION_MODES[$v], $v); + } + } + } elseif (isset(self::COMPRESSION_MODES[$compression])) { + $compression = new ConstStub(self::COMPRESSION_MODES[$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 100644 index 0000000..ef5f15d --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/ReflectionCaster.php @@ -0,0 +1,440 @@ + + * + * 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 + * + * @final + */ +class ReflectionCaster +{ + public const UNSET_CLOSURE_FILE_INFO = ['Closure' => __CLASS__.'::unsetClosureFileInfo']; + + private const EXTRA_MAP = [ + '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, bool $isNested, int $filter = 0) + { + $prefix = Caster::PREFIX_VIRTUAL; + $c = new \ReflectionFunction($c); + + $a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter); + + if (!str_contains($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, bool $isNested) + { + // 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, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + if ($c instanceof \ReflectionNamedType) { + $a += [ + $prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : (string) $c, + $prefix.'allowsNull' => $c->allowsNull(), + $prefix.'isBuiltin' => $c->isBuiltin(), + ]; + } elseif ($c instanceof \ReflectionUnionType || $c instanceof \ReflectionIntersectionType) { + $a[$prefix.'allowsNull'] = $c->allowsNull(); + self::addMap($a, $c, [ + 'types' => 'getTypes', + ]); + } else { + $a[$prefix.'allowsNull'] = $c->allowsNull(); + } + + return $a; + } + + public static function castAttribute(\ReflectionAttribute $c, array $a, Stub $stub, bool $isNested) + { + self::addMap($a, $c, [ + 'name' => 'getName', + 'arguments' => 'getArguments', + ]); + + return $a; + } + + public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + if ($c->getThis()) { + $a[$prefix.'this'] = new CutStub($c->getThis()); + } + $function = $c->getFunction(); + $frame = [ + '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() - (int) (\PHP_VERSION_ID < 80100), + ]); + $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'] = $function[$prefix.'src']; + } + + $a[Caster::PREFIX_VIRTUAL.'closed'] = false; + + return $a; + } + + public static function castClass(\ReflectionClass $c, array $a, Stub $stub, bool $isNested, int $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' => 'getReflectionConstants', + ]); + + foreach ($c->getProperties() as $n) { + $a[$prefix.'properties'][$n->name] = $n; + } + + foreach ($c->getMethods() as $n) { + $a[$prefix.'methods'][$n->name] = $n; + } + + self::addAttributes($a, $c, $prefix); + + if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { + self::addExtra($a, $c); + } + + return $a; + } + + public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, array $a, Stub $stub, bool $isNested, int $filter = 0) + { + $prefix = Caster::PREFIX_VIRTUAL; + + self::addMap($a, $c, [ + 'returnsReference' => 'returnsReference', + 'returnType' => 'getReturnType', + 'class' => \PHP_VERSION_ID >= 80111 ? 'getClosureCalledClass' : 'getClosureScopeClass', + 'this' => 'getClosureThis', + ]); + + if (isset($a[$prefix.'returnType'])) { + $v = $a[$prefix.'returnType']; + $v = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; + $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType'] instanceof \ReflectionNamedType && $a[$prefix.'returnType']->allowsNull() && 'mixed' !== $v ? '?'.$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']); + } + + self::addAttributes($a, $c, $prefix); + + if (!($filter & Caster::EXCLUDE_VERBOSE) && $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 castClassConstant(\ReflectionClassConstant $c, array $a, Stub $stub, bool $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); + $a[Caster::PREFIX_VIRTUAL.'value'] = $c->getValue(); + + self::addAttributes($a, $c); + + return $a; + } + + public static function castMethod(\ReflectionMethod $c, array $a, Stub $stub, bool $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); + + return $a; + } + + public static function castParameter(\ReflectionParameter $c, array $a, Stub $stub, bool $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + self::addMap($a, $c, [ + 'position' => 'getPosition', + 'isVariadic' => 'isVariadic', + 'byReference' => 'isPassedByReference', + 'allowsNull' => 'allowsNull', + ]); + + self::addAttributes($a, $c, $prefix); + + if ($v = $c->getType()) { + $a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; + } + + 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']); + } + + if ($c->isOptional()) { + 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, bool $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); + + self::addAttributes($a, $c); + self::addExtra($a, $c); + + return $a; + } + + public static function castReference(\ReflectionReference $c, array $a, Stub $stub, bool $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'id'] = $c->getId(); + + return $a; + } + + public static function castExtension(\ReflectionExtension $c, array $a, Stub $stub, bool $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, bool $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 (!$type instanceof \ReflectionNamedType) { + $signature .= $type.' '; + } else { + if (!$param->isOptional() && $param->allowsNull() && 'mixed' !== $type->getName()) { + $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) && !str_contains($v, '\\') ? "'{$v}'" : "'…".\strlen($v)."'"; + } elseif (\is_bool($v)) { + $signature .= $v ? 'true' : 'false'; + } elseif (\is_object($v)) { + $signature .= 'new '.substr(strrchr('\\'.get_debug_type($v), '\\'), 1); + } 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(array &$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::EXTRA_MAP, ''); + + if ($x) { + $a[Caster::PREFIX_VIRTUAL.'extra'] = new EnumStub($x); + } + } + + private static function addMap(array &$a, object $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL) + { + foreach ($map as $k => $m) { + if ('isDisabled' === $k) { + continue; + } + + if (method_exists($c, $m) && false !== ($m = $c->$m()) && null !== $m) { + $a[$prefix.$k] = $m instanceof \Reflector ? $m->name : $m; + } + } + } + + private static function addAttributes(array &$a, \Reflector $c, string $prefix = Caster::PREFIX_VIRTUAL): void + { + foreach ($c->getAttributes() as $n) { + $a[$prefix.'attributes'][] = $n; + } + } +} diff --git a/vendor/symfony/var-dumper/Caster/ResourceCaster.php b/vendor/symfony/var-dumper/Caster/ResourceCaster.php new file mode 100644 index 0000000..4e597f8 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/ResourceCaster.php @@ -0,0 +1,100 @@ + + * + * 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 + * + * @final + */ +class ResourceCaster +{ + public static function castCurl(\CurlHandle $h, array $a, Stub $stub, bool $isNested): array + { + return curl_getinfo($h); + } + + public static function castDba($dba, array $a, Stub $stub, bool $isNested) + { + $list = dba_list(); + $a['file'] = $list[(int) $dba]; + + return $a; + } + + public static function castProcess($process, array $a, Stub $stub, bool $isNested) + { + return proc_get_status($process); + } + + public static function castStream($stream, array $a, Stub $stub, bool $isNested) + { + $a = stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested); + if ($a['uri'] ?? false) { + $a['uri'] = new LinkStub($a['uri']); + } + + return $a; + } + + public static function castStreamContext($stream, array $a, Stub $stub, bool $isNested) + { + return @stream_context_get_params($stream) ?: $a; + } + + public static function castGd($gd, array $a, Stub $stub, bool $isNested) + { + $a['size'] = imagesx($gd).'x'.imagesy($gd); + $a['trueColor'] = imageistruecolor($gd); + + return $a; + } + + public static function castMysqlLink($h, array $a, Stub $stub, bool $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, bool $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 100644 index 0000000..a51cace --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/SplCaster.php @@ -0,0 +1,234 @@ + + * + * 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 + * + * @final + */ +class SplCaster +{ + private const SPL_FILE_OBJECT_FLAGS = [ + \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, bool $isNested) + { + return self::castSplArray($c, $a, $stub, $isNested); + } + + public static function castArrayIterator(\ArrayIterator $c, array $a, Stub $stub, bool $isNested) + { + return self::castSplArray($c, $a, $stub, $isNested); + } + + public static function castHeap(\Iterator $c, array $a, Stub $stub, bool $isNested) + { + $a += [ + Caster::PREFIX_VIRTUAL.'heap' => iterator_to_array(clone $c), + ]; + + return $a; + } + + public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, Stub $stub, bool $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, bool $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; + unset($a["\0SplFileInfo\0fileName"]); + unset($a["\0SplFileInfo\0pathName"]); + + try { + $c->isReadable(); + } catch (\RuntimeException $e) { + if ('Object not initialized' !== $e->getMessage()) { + throw $e; + } + + $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; + + return $a; + } catch (\Error $e) { + if ('Object not initialized' !== $e->getMessage()) { + throw $e; + } + + $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; + + return $a; + } + + foreach ($map as $key => $accessor) { + try { + $a[$prefix.$key] = $c->$accessor(); + } catch (\Exception $e) { + } + } + + if ($a[$prefix.'realPath'] ?? false) { + $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, bool $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::SPL_FILE_OBJECT_FLAGS 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 castObjectStorage(\SplObjectStorage $c, array $a, Stub $stub, bool $isNested) + { + $storage = []; + unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967 + unset($a["\0SplObjectStorage\0storage"]); + + $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, bool $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'innerIterator'] = $c->getInnerIterator(); + + return $a; + } + + public static function castWeakReference(\WeakReference $c, array $a, Stub $stub, bool $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'object'] = $c->get(); + + return $a; + } + + private static function castSplArray(\ArrayObject|\ArrayIterator $c, array $a, Stub $stub, bool $isNested): array + { + $prefix = Caster::PREFIX_VIRTUAL; + $flags = $c->getFlags(); + + if (!($flags & \ArrayObject::STD_PROP_LIST)) { + $c->setFlags(\ArrayObject::STD_PROP_LIST); + $a = Caster::castObject($c, \get_class($c), method_exists($c, '__debugInfo'), $stub->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()); + } + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/StubCaster.php b/vendor/symfony/var-dumper/Caster/StubCaster.php new file mode 100644 index 0000000..32ead7c --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/StubCaster.php @@ -0,0 +1,84 @@ + + * + * 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 + * + * @final + */ +class StubCaster +{ + public static function castStub(Stub $c, array $a, Stub $stub, bool $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, bool $isNested) + { + return $isNested ? $c->preservedSubset : $a; + } + + public static function cutInternals($obj, array $a, Stub $stub, bool $isNested) + { + if ($isNested) { + $stub->cut += \count($a); + + return []; + } + + return $a; + } + + public static function castEnum(EnumStub $c, array $a, Stub $stub, bool $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 100644 index 0000000..08428b9 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/SymfonyCaster.php @@ -0,0 +1,97 @@ + + * + * 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\Uid\Ulid; +use Symfony\Component\Uid\Uuid; +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * @final + */ +class SymfonyCaster +{ + private const REQUEST_GETTERS = [ + 'pathInfo' => 'getPathInfo', + 'requestUri' => 'getRequestUri', + 'baseUrl' => 'getBaseUrl', + 'basePath' => 'getBasePath', + 'method' => 'getMethod', + 'format' => 'getRequestFormat', + ]; + + public static function castRequest(Request $request, array $a, Stub $stub, bool $isNested) + { + $clone = null; + + foreach (self::REQUEST_GETTERS as $prop => $getter) { + $key = Caster::PREFIX_PROTECTED.$prop; + if (\array_key_exists($key, $a) && null === $a[$key]) { + if (null === $clone) { + $clone = clone $request; + } + $a[Caster::PREFIX_VIRTUAL.$prop] = $clone->{$getter}(); + } + } + + return $a; + } + + public static function castHttpClient($client, array $a, Stub $stub, bool $isNested) + { + $multiKey = sprintf("\0%s\0multi", \get_class($client)); + if (isset($a[$multiKey])) { + $a[$multiKey] = new CutStub($a[$multiKey]); + } + + return $a; + } + + public static function castHttpClientResponse($response, array $a, Stub $stub, bool $isNested) + { + $stub->cut += \count($a); + $a = []; + + foreach ($response->getInfo() as $k => $v) { + $a[Caster::PREFIX_VIRTUAL.$k] = $v; + } + + return $a; + } + + public static function castUuid(Uuid $uuid, array $a, Stub $stub, bool $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'toBase58'] = $uuid->toBase58(); + $a[Caster::PREFIX_VIRTUAL.'toBase32'] = $uuid->toBase32(); + + // symfony/uid >= 5.3 + if (method_exists($uuid, 'getDateTime')) { + $a[Caster::PREFIX_VIRTUAL.'time'] = $uuid->getDateTime()->format('Y-m-d H:i:s.u \U\T\C'); + } + + return $a; + } + + public static function castUlid(Ulid $ulid, array $a, Stub $stub, bool $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'toBase58'] = $ulid->toBase58(); + $a[Caster::PREFIX_VIRTUAL.'toRfc4122'] = $ulid->toRfc4122(); + + // symfony/uid >= 5.3 + if (method_exists($ulid, 'getDateTime')) { + $a[Caster::PREFIX_VIRTUAL.'time'] = $ulid->getDateTime()->format('Y-m-d H:i:s.v \U\T\C'); + } + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/TraceStub.php b/vendor/symfony/var-dumper/Caster/TraceStub.php new file mode 100644 index 0000000..5eea1c8 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/TraceStub.php @@ -0,0 +1,36 @@ + + * + * 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 + */ +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/UuidCaster.php b/vendor/symfony/var-dumper/Caster/UuidCaster.php new file mode 100644 index 0000000..b102774 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/UuidCaster.php @@ -0,0 +1,30 @@ + + * + * 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 Ramsey\Uuid\UuidInterface; +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * @author Grégoire Pineau + */ +final class UuidCaster +{ + public static function castRamseyUuid(UuidInterface $c, array $a, Stub $stub, bool $isNested): array + { + $a += [ + Caster::PREFIX_VIRTUAL.'uuid' => (string) $c, + ]; + + return $a; + } +} diff --git a/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php b/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php new file mode 100644 index 0000000..5b45565 --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php @@ -0,0 +1,91 @@ + + * + * 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é + * + * @final + */ +class XmlReaderCaster +{ + private const NODE_TYPES = [ + \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, bool $isNested) + { + try { + $properties = [ + 'LOADDTD' => @$reader->getParserProperty(\XMLReader::LOADDTD), + 'DEFAULTATTRS' => @$reader->getParserProperty(\XMLReader::DEFAULTATTRS), + 'VALIDATE' => @$reader->getParserProperty(\XMLReader::VALIDATE), + 'SUBST_ENTITIES' => @$reader->getParserProperty(\XMLReader::SUBST_ENTITIES), + ]; + } catch (\Error $e) { + $properties = [ + 'LOADDTD' => false, + 'DEFAULTATTRS' => false, + 'VALIDATE' => false, + 'SUBST_ENTITIES' => false, + ]; + } + + $props = Caster::PREFIX_VIRTUAL.'parserProperties'; + $info = [ + 'localName' => $reader->localName, + 'prefix' => $reader->prefix, + 'nodeType' => new ConstStub(self::NODE_TYPES[$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 => $properties, + ]; + + 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 100644 index 0000000..ba55fce --- /dev/null +++ b/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php @@ -0,0 +1,63 @@ + + * + * 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 + * + * @final + */ +class XmlResourceCaster +{ + private const XML_ERRORS = [ + \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, bool $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::XML_ERRORS[$a['error_code']])) { + $a['error_code'] = new ConstStub(self::XML_ERRORS[$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 100644 index 0000000..b835c03 --- /dev/null +++ b/vendor/symfony/var-dumper/Cloner/AbstractCloner.php @@ -0,0 +1,388 @@ + + * + * 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 + */ +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'], + + 'Fiber' => ['Symfony\Component\VarDumper\Caster\FiberCaster', 'castFiber'], + + 'Closure' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'], + 'Generator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'], + 'ReflectionType' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'], + 'ReflectionAttribute' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castAttribute'], + 'ReflectionGenerator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'], + 'ReflectionClass' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'], + 'ReflectionClassConstant' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClassConstant'], + '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'], + 'Doctrine\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + + '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\Bridge\Monolog\Logger' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Symfony\Component\DependencyInjection\ContainerInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Symfony\Component\EventDispatcher\EventDispatcherInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Symfony\Component\HttpClient\AmpHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], + 'Symfony\Component\HttpClient\CurlHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], + 'Symfony\Component\HttpClient\NativeHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], + 'Symfony\Component\HttpClient\Response\AmpResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], + 'Symfony\Component\HttpClient\Response\CurlResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], + 'Symfony\Component\HttpClient\Response\NativeResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], + 'Symfony\Component\HttpFoundation\Request' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'], + 'Symfony\Component\Uid\Ulid' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castUlid'], + 'Symfony\Component\Uid\Uuid' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castUuid'], + '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\ErrorHandler\Exception\SilencedErrorContext' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'], + + 'Imagine\Image\ImageInterface' => ['Symfony\Component\VarDumper\Caster\ImagineCaster', 'castImage'], + + 'Ramsey\Uuid\UuidInterface' => ['Symfony\Component\VarDumper\Caster\UuidCaster', 'castRamseyUuid'], + + 'ProxyManager\Proxy\ProxyInterface' => ['Symfony\Component\VarDumper\Caster\ProxyManagerCaster', 'castProxy'], + 'PHPUnit_Framework_MockObject_MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'PHPUnit\Framework\MockObject\MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'PHPUnit\Framework\MockObject\Stub' => ['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'], + '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'], + + 'mysqli_driver' => ['Symfony\Component\VarDumper\Caster\MysqliCaster', 'castMysqliDriver'], + + 'CurlHandle' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'], + + ':dba' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], + ':dba persistent' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], + + 'GdImage' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'], + ':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'], + + 'OpenSSLCertificate' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'], + ':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'], + + 'XmlParser' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], + ':xml' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], + + 'RdKafka' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castRdKafka'], + 'RdKafka\Conf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castConf'], + 'RdKafka\KafkaConsumer' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castKafkaConsumer'], + 'RdKafka\Metadata\Broker' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castBrokerMetadata'], + 'RdKafka\Metadata\Collection' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castCollectionMetadata'], + 'RdKafka\Metadata\Partition' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castPartitionMetadata'], + 'RdKafka\Metadata\Topic' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicMetadata'], + 'RdKafka\Message' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castMessage'], + 'RdKafka\Topic' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopic'], + 'RdKafka\TopicPartition' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicPartition'], + 'RdKafka\TopicConf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicConf'], + ]; + + protected $maxItems = 2500; + protected $maxString = -1; + protected $minDepth = 1; + + /** + * @var array> + */ + private array $casters = []; + + /** + * @var callable|null + */ + private $prevErrorHandler; + + private array $classInfo = []; + private int $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. + */ + public function setMaxItems(int $maxItems) + { + $this->maxItems = $maxItems; + } + + /** + * Sets the maximum cloned length for strings. + */ + public function setMaxString(int $maxString) + { + $this->maxString = $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. + */ + public function setMinDepth(int $minDepth) + { + $this->minDepth = $minDepth; + } + + /** + * Clones a PHP variable. + * + * @param int $filter A bit field of Caster::EXCLUDE_* constants + */ + public function cloneVar(mixed $var, int $filter = 0): Data + { + $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. + */ + abstract protected function doClone(mixed $var): array; + + /** + * Casts an object to an array representation. + * + * @param bool $isNested True if the object is nested in the dumped structure + */ + protected function castObject(Stub $stub, bool $isNested): array + { + $obj = $stub->value; + $class = $stub->class; + + if (str_contains($class, "@anonymous\0")) { + $stub->class = get_debug_type($obj); + } + if (isset($this->classInfo[$class])) { + [$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, $stub->class); + + 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 bool $isNested True if the object is nested in the dumped structure + */ + protected function castResource(Stub $stub, bool $isNested): array + { + $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 100644 index 0000000..5a8e2e4 --- /dev/null +++ b/vendor/symfony/var-dumper/Cloner/ClonerInterface.php @@ -0,0 +1,23 @@ + + * + * 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 + */ +interface ClonerInterface +{ + /** + * Clones a PHP variable. + */ + public function cloneVar(mixed $var): Data; +} diff --git a/vendor/symfony/var-dumper/Cloner/Cursor.php b/vendor/symfony/var-dumper/Cloner/Cursor.php new file mode 100644 index 0000000..1fd796d --- /dev/null +++ b/vendor/symfony/var-dumper/Cloner/Cursor.php @@ -0,0 +1,43 @@ + + * + * 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 + */ +class Cursor +{ + public const HASH_INDEXED = Stub::ARRAY_INDEXED; + public const HASH_ASSOC = Stub::ARRAY_ASSOC; + public const HASH_OBJECT = Stub::TYPE_OBJECT; + public 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 100644 index 0000000..6ecb883 --- /dev/null +++ b/vendor/symfony/var-dumper/Cloner/Data.php @@ -0,0 +1,422 @@ + + * + * 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\Dumper\ContextProvider\SourceContextProvider; + +/** + * @author Nicolas Grekas + */ +class Data implements \ArrayAccess, \Countable, \IteratorAggregate +{ + private array $data; + private int $position = 0; + private int|string $key = 0; + private int $maxDepth = 20; + private int $maxItemsPerDepth = -1; + private int $useRefHandles = -1; + private array $context = []; + + /** + * @param array $data An array as returned by ClonerInterface::cloneVar() + */ + public function __construct(array $data) + { + $this->data = $data; + } + + public function getType(): ?string + { + $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'; + } + + return null; + } + + /** + * Returns a native representation of the original value. + * + * @param array|bool $recursive Whether values should be resolved recursively or not + * + * @return string|int|float|bool|array|Data[]|null + */ + public function getValue(array|bool $recursive = false): string|int|float|bool|array|null + { + $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(): int + { + return \count($this->getValue()); + } + + public function getIterator(): \Traversable + { + if (!\is_array($value = $this->getValue())) { + throw new \LogicException(sprintf('"%s" object holds non-iterable type "%s".', self::class, get_debug_type($value))); + } + + yield from $value; + } + + public function __get(string $key) + { + if (null !== $data = $this->seek($key)) { + $item = $this->getStub($data->data[$data->position][$data->key]); + + return $item instanceof Stub || [] === $item ? $data : $item; + } + + return null; + } + + public function __isset(string $key): bool + { + return null !== $this->seek($key); + } + + public function offsetExists(mixed $key): bool + { + return $this->__isset($key); + } + + public function offsetGet(mixed $key): mixed + { + return $this->__get($key); + } + + public function offsetSet(mixed $key, mixed $value): void + { + throw new \BadMethodCallException(self::class.' objects are immutable.'); + } + + public function offsetUnset(mixed $key): void + { + throw new \BadMethodCallException(self::class.' objects are immutable.'); + } + + public function __toString(): string + { + $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. + */ + public function withMaxDepth(int $maxDepth): static + { + $data = clone $this; + $data->maxDepth = $maxDepth; + + return $data; + } + + /** + * Limits the number of elements per depth level. + */ + public function withMaxItemsPerDepth(int $maxItemsPerDepth): static + { + $data = clone $this; + $data->maxItemsPerDepth = $maxItemsPerDepth; + + return $data; + } + + /** + * Enables/disables objects' identifiers tracking. + * + * @param bool $useRefHandles False to hide global ref. handles + */ + public function withRefHandles(bool $useRefHandles): static + { + $data = clone $this; + $data->useRefHandles = $useRefHandles ? -1 : 0; + + return $data; + } + + public function withContext(array $context): static + { + $data = clone $this; + $data->context = $context; + + return $data; + } + + /** + * Seeks to a specific key in nested data structures. + */ + public function seek(string|int $key): ?static + { + $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 null; + } + $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 null; + } + + $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]; + $cursor = new Cursor(); + + if ($cursor->attr = $this->context[SourceContextProvider::class] ?? []) { + $cursor->attr['if_links'] = true; + $cursor->hashType = -1; + $dumper->dumpScalar($cursor, 'default', '^'); + $cursor->attr = ['if_links' => true]; + $dumper->dumpScalar($cursor, 'default', ' '); + $cursor->hashType = 0; + } + + $this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]); + } + + /** + * Depth-first dumping of items. + * + * @param mixed $item A Stub object or the original value being dumped + */ + private function dumpItem(DumperInterface $dumper, Cursor $cursor, array &$refs, mixed $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 = 0 < $item->handle ? $item->refCount : 0; + } + $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. + * + * @return int The final number of removed items + */ + private function dumpChildren(DumperInterface $dumper, Cursor $parentCursor, array &$refs, array $children, int $hashCut, int $hashType, bool $dumpKeys): int + { + $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(mixed $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 100644 index 0000000..61d02d2 --- /dev/null +++ b/vendor/symfony/var-dumper/Cloner/DumperInterface.php @@ -0,0 +1,53 @@ + + * + * 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 + */ +interface DumperInterface +{ + /** + * Dumps a scalar value. + */ + public function dumpScalar(Cursor $cursor, string $type, string|int|float|bool|null $value); + + /** + * Dumps a string. + * + * @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, string $str, bool $bin, int $cut); + + /** + * Dumps while entering an hash. + * + * @param int $type A Cursor::HASH_* const for the type of hash + * @param string|int|null $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, int $type, string|int|null $class, bool $hasChild); + + /** + * Dumps while leaving an hash. + * + * @param int $type A Cursor::HASH_* const for the type of hash + * @param string|int|null $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, int $type, string|int|null $class, bool $hasChild, int $cut); +} diff --git a/vendor/symfony/var-dumper/Cloner/Stub.php b/vendor/symfony/var-dumper/Cloner/Stub.php new file mode 100644 index 0000000..1c5b887 --- /dev/null +++ b/vendor/symfony/var-dumper/Cloner/Stub.php @@ -0,0 +1,67 @@ + + * + * 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 + */ +class Stub +{ + public const TYPE_REF = 1; + public const TYPE_STRING = 2; + public const TYPE_ARRAY = 3; + public const TYPE_OBJECT = 4; + public const TYPE_RESOURCE = 5; + + public const STRING_BINARY = 1; + public const STRING_UTF8 = 2; + + public const ARRAY_ASSOC = 1; + public 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 array $defaultProperties = []; + + /** + * @internal + */ + public function __sleep(): array + { + $properties = []; + + if (!isset(self::$defaultProperties[$c = static::class])) { + 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 100644 index 0000000..9afcc34 --- /dev/null +++ b/vendor/symfony/var-dumper/Cloner/VarCloner.php @@ -0,0 +1,286 @@ + + * + * 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 + */ +class VarCloner extends AbstractCloner +{ + private static string $gid; + private static array $arrayCache = []; + + /** + * {@inheritdoc} + */ + protected function doClone(mixed $var): array + { + $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 + $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 + + $gid = self::$gid ??= md5(random_bytes(6)); // 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]; + foreach ($vals as $k => $v) { + // $v is the original value or a stub object in case of hard references + + $zvalRef = ($r = \ReflectionReference::fromArrayElement($vals, $k)) ? $r->getId() : null; + + if ($zvalRef) { + $vals[$k] = &$stub; // Break hard references to make $queue completely + unset($stub); // independent from the original structure + if (null !== $vals[$k] = $hardRefs[$zvalRef] ?? null) { + $v = $vals[$k]; + if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) { + ++$v->value->refCount; + } + ++$v->refCount; + continue; + } + $vals[$k] = new Stub(); + $vals[$k]->value = $v; + $vals[$k]->handle = ++$refsCounter; + $hardRefs[$zvalRef] = $vals[$k]; + } + // Create $stub when the original value $v cannot 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; + + if (\PHP_VERSION_ID >= 80100) { + $stub->class = array_is_list($v) ? Stub::ARRAY_INDEXED : Stub::ARRAY_ASSOC; + $a = $v; + break; + } + + $stub->class = Stub::ARRAY_INDEXED; + + $j = -1; + foreach ($v as $gk => $gv) { + if ($gk !== ++$j) { + $stub->class = Stub::ARRAY_ASSOC; + $a = $v; + $a[$gid] = true; + break; + } + } + + // Copies of $GLOBALS have very strange behavior, + // let's detect them with some black magic + if (isset($v[$gid])) { + unset($v[$gid]); + $a = []; + foreach ($v as $gk => &$gv) { + if ($v === $gv && !isset($hardRefs[\ReflectionReference::fromArrayElement($v, $gk)->getId()])) { + unset($v); + $v = new Stub(); + $v->value = [$v->cut = \count($gv), Stub::TYPE_ARRAY => 0]; + $v->handle = -1; + $gv = &$a[$gk]; + $hardRefs[\ReflectionReference::fromArrayElement($a, $gk)->getId()] = &$gv; + $gv = $v; + } + + $a[$gk] = &$gv; + } + unset($gv); + } else { + $a = $v; + } + break; + + case \is_object($v): + 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, true); + 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 (!$zvalRef) { + $vals[$k] = $stub; + } else { + $hardRefs[$zvalRef]->value = $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 100644 index 0000000..e3d5f1d --- /dev/null +++ b/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php @@ -0,0 +1,79 @@ + + * + * 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\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 + * + * @final + */ +class CliDescriptor implements DumpDescriptorInterface +{ + private $dumper; + private mixed $lastIdentifier = null; + + public function __construct(CliDumper $dumper) + { + $this->dumper = $dumper; + } + + 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', (int) $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']); + if ($fileLink = $source['file_link'] ?? null) { + $sourceInfo = sprintf('%s', $fileLink, $sourceInfo); + } + $rows[] = ['source', $sourceInfo]; + $file = $source['file_relative'] ?? $source['file']; + $rows[] = ['file', $file]; + } + + $io->table([], $rows); + + $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 100644 index 0000000..267d27b --- /dev/null +++ b/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php @@ -0,0 +1,23 @@ + + * + * 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 + */ +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 100644 index 0000000..1c0d80a --- /dev/null +++ b/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php @@ -0,0 +1,119 @@ + + * + * 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 + * + * @final + */ +class HtmlDescriptor implements DumpDescriptorInterface +{ + private $dumper; + private bool $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(""); + $this->initialized = true; + } + + $title = '-'; + if (isset($context['request'])) { + $request = $context['request']; + $controller = "{$this->dumper->dump($request['controller'], true, ['maxDepth' => 0])}"; + $title = sprintf('%s %s', $request['method'], $uri = $request['uri'], $uri); + $dedupIdentifier = $request['identifier']; + } elseif (isset($context['cli'])) { + $title = '$ '.$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('%s', $source['file_link'], $sourceDescription); + } + } + + $isoDate = $this->extractDate($context, 'c'); + $tags = array_filter([ + 'controller' => $controller ?? null, + 'project dir' => $projectDir ?? null, + ]); + + $output->writeln(<< +
+
+

$title

+ +
+ {$this->renderTags($tags)} +
+
+

+ $sourceDescription +

+ {$this->dumper->dump($data, true)} +
+ +HTML + ); + } + + private function extractDate(array $context, string $format = 'r'): string + { + return date($format, (int) $context['timestamp']); + } + + private function renderTags(array $tags): string + { + if (!$tags) { + return ''; + } + + $renderedTags = ''; + foreach ($tags as $key => $value) { + $renderedTags .= sprintf('
  • %s%s
  • ', $key, $value); + } + + return << +
      + $renderedTags +
    +
    +HTML; + } +} diff --git a/vendor/symfony/var-dumper/Command/ServerDumpCommand.php b/vendor/symfony/var-dumper/Command/ServerDumpCommand.php new file mode 100644 index 0000000..13dd475 --- /dev/null +++ b/vendor/symfony/var-dumper/Command/ServerDumpCommand.php @@ -0,0 +1,112 @@ + + * + * 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\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +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 + * + * @final + */ +#[AsCommand(name: 'server:dump', description: 'Start a dump server that collects and displays dumps in a single place')] +class ServerDumpCommand extends Command +{ + private $server; + + /** @var DumpDescriptorInterface[] */ + private array $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() + { + $this + ->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format (%s)', implode(', ', $this->getAvailableFormats())), 'cli') + ->setHelp(<<<'EOF' +%command.name% starts a dump server that collects and displays +dumps in a single place for debugging you application: + + php %command.full_name% + +You can consult dumped data in HTML format in your browser by providing the --format=html option +and redirecting the output to a file: + + php %command.full_name% --format="html" > dump.html + +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $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); + }); + + return 0; + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestOptionValuesFor('format')) { + $suggestions->suggestValues($this->getAvailableFormats()); + } + } + + private function getAvailableFormats(): array + { + return array_keys($this->descriptors); + } +} diff --git a/vendor/symfony/var-dumper/Dumper/AbstractDumper.php b/vendor/symfony/var-dumper/Dumper/AbstractDumper.php new file mode 100644 index 0000000..3c2f55f --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/AbstractDumper.php @@ -0,0 +1,197 @@ + + * + * 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 + */ +abstract class AbstractDumper implements DataDumperInterface, DumperInterface +{ + public const DUMP_LIGHT_ARRAY = 1; + public const DUMP_STRING_LENGTH = 2; + public const DUMP_COMMA_SEPARATOR = 4; + public const DUMP_TRAILING_COMMA = 8; + + public static $defaultOutput = 'php://output'; + + protected $line = ''; + protected $lineDumper; + protected $outputStream; + protected $decimalPoint = '.'; + protected $indentPad = ' '; + protected $flags; + + private string $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->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 = $this->outputStream ?? $this->lineDumper; + + if (\is_callable($output)) { + $this->outputStream = null; + $this->lineDumper = $output; + } else { + if (\is_string($output)) { + $output = fopen($output, 'w'); + } + $this->outputStream = $output; + $this->lineDumper = [$this, 'echoLine']; + } + + return $prev; + } + + /** + * Sets the default character encoding to use for non-UTF8 strings. + * + * @return string The previous charset + */ + public function setCharset(string $charset): string + { + $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(string $pad): string + { + $prev = $this->indentPad; + $this->indentPad = $pad; + + return $prev; + } + + /** + * Dumps 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): ?string + { + 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+'); + } + 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); + } + } + + return null; + } + + /** + * 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(int $depth) + { + ($this->lineDumper)($this->line, $depth, $this->indentPad); + $this->line = ''; + } + + /** + * Generic line dumper callback. + */ + protected function echoLine(string $line, int $depth, string $indentPad) + { + if (-1 !== $depth) { + fwrite($this->outputStream, str_repeat($indentPad, $depth).$line."\n"); + } + } + + /** + * Converts a non-UTF-8 string to UTF-8. + */ + protected function utf8Encode(?string $s): ?string + { + if (null === $s || 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 100644 index 0000000..065b918 --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/CliDumper.php @@ -0,0 +1,643 @@ + + * + * 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 + */ +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' => '0;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 array $displayOptions = [ + 'fileLinkFormat' => null, + ]; + + private bool $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#L%l'; + } + + /** + * Enables/disables colored output. + */ + public function setColors(bool $colors) + { + $this->colors = $colors; + } + + /** + * Sets the maximum number of characters per line for dumped strings. + */ + public function setMaxStringWidth(int $maxStringWidth) + { + $this->maxStringWidth = $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, string $type, string|int|float|bool|null $value) + { + $this->dumpKey($cursor); + + $style = 'const'; + $attr = $cursor->attr; + + switch ($type) { + case 'default': + $style = 'default'; + break; + + case 'integer': + $style = 'num'; + + if (isset($this->styles['integer'])) { + $style = 'integer'; + } + + break; + + case 'double': + $style = 'num'; + + if (isset($this->styles['float'])) { + $style = 'float'; + } + + 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 (!str_contains($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, string $str, bool $bin, int $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 = $bin && false !== strpos($str, "\0") ? [$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, int $type, string|int|null $class, bool $hasChild) + { + if (null === $this->colors) { + $this->colors = $this->supportsColors(); + } + + $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).(empty($attr['cut_hash']) ? ' {' : '') : '{'; + } 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).' [' : '['; + } + + if (($cursor->softRefCount || 0 < $cursor->softRefHandle) && empty($attr['cut_hash'])) { + $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, int $type, string|int|null $class, bool $hasChild, int $cut) + { + if (empty($cursor->attr['cut_hash'])) { + $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 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, bool $hasChild, int $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. + */ + 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).($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 + */ + protected function style(string $style, string $value, array $attr = []): string + { + if (null === $this->colors) { + $this->colors = $this->supportsColors(); + } + + $this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') + && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100); + + 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']) && str_starts_with($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 .= $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 && str_ends_with($value, $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'], $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\\"; + } + } elseif ($attr['if_links'] ?? false) { + return ''; + } + + return $value; + } + + protected function supportsColors(): bool + { + 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': + case '--colors=always': + return static::$defaultColors = true; + + case '--no-ansi': + case '--color=no': + case '--color=none': + case '--color=never': + case '--colors=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', 'w') : $this->outputStream; + + return static::$defaultColors = $this->hasColorSupport($h); + } + + /** + * {@inheritdoc} + */ + protected function dumpLine(int $depth, bool $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 (-1 === $cursor->hashType) { + return; + } + + 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 + */ + private function hasColorSupport(mixed $stream): bool + { + if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) { + return false; + } + + // Follow https://no-color.org/ + if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) { + 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'); + } + + return stream_isatty($stream); + } + + /** + * 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. + */ + private function isWindowsTrueColor(): bool + { + $result = 183 <= getenv('ANSICON_VER') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM') + || 'Hyper' === getenv('TERM_PROGRAM'); + + if (!$result) { + $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(string $file, int $line) + { + if ($fmt = $this->displayOptions['fileLinkFormat']) { + return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file.'#L'.$line); + } + + 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 100644 index 0000000..38f8789 --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php @@ -0,0 +1,32 @@ + + * + * 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 + */ +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 100644 index 0000000..532aa0f --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php @@ -0,0 +1,22 @@ + + * + * 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 + */ +interface ContextProviderInterface +{ + 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 100644 index 0000000..3684a47 --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php @@ -0,0 +1,51 @@ + + * + * 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 + */ +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 100644 index 0000000..8ef6e36 --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php @@ -0,0 +1,126 @@ + + * + * 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 + * @author Maxime Steinhausser + */ +final class SourceContextProvider implements ContextProviderInterface +{ + private int $limit; + private ?string $charset; + private ?string $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'] ?? $file; + $line = $trace[$i]['line'] ?? $line; + + while (++$i < $this->limit) { + if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && !str_starts_with($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[] = ''.$this->htmlEncode($src[$i - 1]).''; + } + + $fileExcerpt = '
      '.implode("\n", $fileExcerpt).'
    '; + } + } + 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 (str_starts_with($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/ContextualizedDumper.php b/vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php new file mode 100644 index 0000000..18ab56e --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php @@ -0,0 +1,43 @@ + + * + * 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; + +/** + * @author Kévin Thérage + */ +class ContextualizedDumper implements DataDumperInterface +{ + private $wrappedDumper; + private array $contextProviders; + + /** + * @param ContextProviderInterface[] $contextProviders + */ + public function __construct(DataDumperInterface $wrappedDumper, array $contextProviders) + { + $this->wrappedDumper = $wrappedDumper; + $this->contextProviders = $contextProviders; + } + + public function dump(Data $data) + { + $context = []; + foreach ($this->contextProviders as $contextProvider) { + $context[\get_class($contextProvider)] = $contextProvider->getContext(); + } + + $this->wrappedDumper->dump($data->withContext($context)); + } +} diff --git a/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php b/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php new file mode 100644 index 0000000..b173bcc --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php @@ -0,0 +1,24 @@ + + * + * 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 + */ +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 100644 index 0000000..e74888d --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/HtmlDumper.php @@ -0,0 +1,986 @@ + + * + * 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 + */ +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 = '
    ';
    +    protected $dumpSuffix = '
    '; + protected $dumpId = 'sf-dump'; + protected $colors = true; + protected $headerIsDumped = false; + protected $lastDepth = -1; + protected $styles; + + private array $displayOptions = [ + 'maxDepth' => 1, + 'maxStringLength' => 160, + 'fileLinkFormat' => null, + ]; + private array $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. + */ + public function setDumpHeader(?string $header) + { + $this->dumpHeader = $header; + } + + /** + * Sets an HTML prefix and suffix that will encapse every single dump. + */ + public function setDumpBoundaries(string $prefix, string $suffix) + { + $this->dumpPrefix = $prefix; + $this->dumpSuffix = $suffix; + } + + /** + * {@inheritdoc} + */ + public function dump(Data $data, $output = null, array $extraDisplayOptions = []): ?string + { + $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 = $this->outputStream ?? $this->lineDumper; + + if (null !== $this->dumpHeader) { + return $this->dumpHeader; + } + + $line = str_replace('{$options}', json_encode($this->displayOptions, \JSON_FORCE_OBJECT), <<<'EOHTML' +'.$this->dumpHeader; + } + + /** + * {@inheritdoc} + */ + public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut) + { + if ('' === $str && isset($cursor->attr['img-data'], $cursor->attr['content-type'])) { + $this->dumpKey($cursor); + $this->line .= $this->style('default', $cursor->attr['img-size'] ?? '', []); + $this->line .= $cursor->depth >= $this->displayOptions['maxDepth'] ? ' ' : ' '; + $this->endValue($cursor); + $this->line .= $this->indentPad; + $this->line .= sprintf('', $cursor->attr['content-type'], base64_encode($cursor->attr['img-data'])); + $this->endValue($cursor); + } else { + parent::dumpString($cursor, $str, $bin, $cut); + } + } + + /** + * {@inheritdoc} + */ + public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild) + { + if (Cursor::HASH_OBJECT === $type) { + $cursor->attr['depth'] = $cursor->depth; + } + parent::enterHash($cursor, $type, $class, false); + + if ($cursor->skipChildren || $cursor->depth >= $this->displayOptions['maxDepth']) { + $cursor->skipChildren = false; + $eol = ' class=sf-dump-compact>'; + } else { + $this->expandNextHash = false; + $eol = ' class=sf-dump-expanded>'; + } + + if ($hasChild) { + $this->line .= 'dumpId, $r); + } + $this->line .= $eol; + $this->dumpLine($cursor->depth); + } + } + + /** + * {@inheritdoc} + */ + public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut) + { + $this->dumpEllipsis($cursor, $hasChild, $cut); + if ($hasChild) { + $this->line .= ''; + } + parent::leaveHash($cursor, $type, $class, $hasChild, 0); + } + + /** + * {@inheritdoc} + */ + protected function style(string $style, string $value, array $attr = []): string + { + if ('' === $value) { + return ''; + } + + $v = esc($value); + + if ('ref' === $style) { + if (empty($attr['count'])) { + return sprintf('%s', $v); + } + $r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1); + + return sprintf('%s', $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 && 0 < ($attr['depth'] ?? 0) && false !== $c = strrpos($value, '\\')) { + $style .= ' title=""'; + $attr += [ + 'ellipsis' => \strlen($value) - $c, + 'ellipsis-type' => 'note', + 'ellipsis-tail' => 1, + ]; + } 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: `%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('%s', $class, substr($v, 0, -\strlen($label))); + + if (!empty($attr['ellipsis-tail'])) { + $tail = \strlen(esc(substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']))); + $v .= sprintf('%s%s', $class, substr($label, 0, $tail), substr($label, $tail)); + } else { + $v .= $label; + } + } + + $v = "".preg_replace_callback(static::$controlCharsRx, function ($c) use ($map) { + $s = $b = ''; + }, $v).''; + + if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) { + $attr['href'] = $href; + } + if (isset($attr['href'])) { + $target = isset($attr['file']) ? '' : ' target="_blank"'; + $v = sprintf('%s', esc($this->utf8Encode($attr['href'])), $target, $v); + } + if (isset($attr['lang'])) { + $v = sprintf('%s', esc($attr['lang']), $v); + } + + return $v; + } + + /** + * {@inheritdoc} + */ + protected function dumpLine(int $depth, bool $endOfValue = false) + { + if (-1 === $this->lastDepth) { + $this->line = sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line; + } + if ($this->headerIsDumped !== ($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_encode_numericentity($this->line, [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8'); + + if (-1 === $depth) { + AbstractDumper::dumpLine(0); + } + AbstractDumper::dumpLine($depth); + } + + private function getSourceLink(string $file, int $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(string $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 100644 index 0000000..94795bf --- /dev/null +++ b/vendor/symfony/var-dumper/Dumper/ServerDumper.php @@ -0,0 +1,53 @@ + + * + * 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 + */ +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 100644 index 0000000..122f0d3 --- /dev/null +++ b/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php @@ -0,0 +1,26 @@ + + * + * 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 + */ +class ThrowingCasterException extends \Exception +{ + /** + * @param \Throwable $prev The exception thrown from the caster + */ + public function __construct(\Throwable $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 100644 index 0000000..72412a6 --- /dev/null +++ b/vendor/symfony/var-dumper/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2023 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 100644 index 0000000..a0da8c9 --- /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..f398fce --- /dev/null +++ b/vendor/symfony/var-dumper/Resources/bin/var-dump-server @@ -0,0 +1,67 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if ('cli' !== PHP_SAPI) { + throw new Exception('This script must be run from the command line.'); +} + +/** + * Starts a dump server to collect and output dumps on a single place with multiple formats support. + * + * @author Maxime Steinhausser + */ + +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 100644 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 100644 index 0000000..6221a4d --- /dev/null +++ b/vendor/symfony/var-dumper/Resources/functions/dump.php @@ -0,0 +1,50 @@ + + * + * 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 + */ + function dump(mixed $var, mixed ...$moreVars): mixed + { + 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')) { + /** + * @return never + */ + function dd(...$vars): void + { + if (!in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && !headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + + foreach ($vars as $v) { + VarDumper::dump($v); + } + + exit(1); + } +} diff --git a/vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js b/vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js new file mode 100644 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 100644 index 0000000..97b5b94 --- /dev/null +++ b/vendor/symfony/var-dumper/Server/Connection.php @@ -0,0 +1,99 @@ + + * + * 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 + */ +class Connection +{ + private string $host; + private array $contextProviders; + + /** + * @var resource|null + */ + 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 (!str_contains($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(int $t, string $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 100644 index 0000000..6a43c12 --- /dev/null +++ b/vendor/symfony/var-dumper/Server/DumpServer.php @@ -0,0 +1,115 @@ + + * + * 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 + * + * @final + */ +class DumpServer +{ + private string $host; + private $logger; + + /** + * @var resource|null + */ + private $socket; + + public function __construct(string $host, LoggerInterface $logger = null) + { + if (!str_contains($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": ', $this->host).$errstr.' '.$errno); + } + } + + public function listen(callable $callback): void + { + if (null === $this->socket) { + $this->start(); + } + + foreach ($this->getMessages() as $clientId => $message) { + if ($this->logger) { + $this->logger->info('Received a payload from client {clientId}', ['clientId' => $clientId]); + } + + $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; + } + + [$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 100644 index 0000000..a202185 --- /dev/null +++ b/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php @@ -0,0 +1,84 @@ + + * + * 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 + */ +trait VarDumperTestTrait +{ + /** + * @internal + */ + private array $varDumperConfig = [ + 'casters' => [], + 'flags' => null, + ]; + + protected function setUpVarDumper(array $casters, int $flags = null): void + { + $this->varDumperConfig['casters'] = $casters; + $this->varDumperConfig['flags'] = $flags; + } + + /** + * @after + */ + protected function tearDownVarDumper(): void + { + $this->varDumperConfig['casters'] = []; + $this->varDumperConfig['flags'] = null; + } + + public function assertDumpEquals(mixed $expected, mixed $data, int $filter = 0, string $message = '') + { + $this->assertSame($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); + } + + public function assertDumpMatchesFormat(mixed $expected, mixed $data, int $filter = 0, string $message = '') + { + $this->assertStringMatchesFormat($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); + } + + protected function getDump(mixed $data, string|int $key = null, int $filter = 0): ?string + { + if (null === $flags = $this->varDumperConfig['flags']) { + $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->addCasters($this->varDumperConfig['casters']); + $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 null; + } + + return rtrim($dumper->dump($data, true)); + } + + private function prepareExpectation(mixed $expected, int $filter): string + { + if (!\is_string($expected)) { + $expected = $this->getDump($expected, null, $filter); + } + + return rtrim($expected); + } +} diff --git a/vendor/symfony/var-dumper/VarDumper.php b/vendor/symfony/var-dumper/VarDumper.php new file mode 100644 index 0000000..c0d0741 --- /dev/null +++ b/vendor/symfony/var-dumper/VarDumper.php @@ -0,0 +1,112 @@ + + * + * 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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\VarDumper\Caster\ReflectionCaster; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\CliDumper; +use Symfony\Component\VarDumper\Dumper\ContextProvider\CliContextProvider; +use Symfony\Component\VarDumper\Dumper\ContextProvider\RequestContextProvider; +use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; +use Symfony\Component\VarDumper\Dumper\ContextualizedDumper; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; +use Symfony\Component\VarDumper\Dumper\ServerDumper; + +// Load the global dump() function +require_once __DIR__.'/Resources/functions/dump.php'; + +/** + * @author Nicolas Grekas + */ +class VarDumper +{ + /** + * @var callable|null + */ + private static $handler; + + public static function dump(mixed $var) + { + if (null === self::$handler) { + self::register(); + } + + return (self::$handler)($var); + } + + public static function setHandler(callable $callable = null): ?callable + { + $prevHandler = self::$handler; + + // Prevent replacing the handler with expected format as soon as the env var was set: + if (isset($_SERVER['VAR_DUMPER_FORMAT'])) { + return $prevHandler; + } + + self::$handler = $callable; + + return $prevHandler; + } + + private static function register(): void + { + $cloner = new VarCloner(); + $cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); + + $format = $_SERVER['VAR_DUMPER_FORMAT'] ?? null; + switch (true) { + case 'html' === $format: + $dumper = new HtmlDumper(); + break; + case 'cli' === $format: + $dumper = new CliDumper(); + break; + case 'server' === $format: + case $format && 'tcp' === parse_url($format, \PHP_URL_SCHEME): + $host = 'server' === $format ? $_SERVER['VAR_DUMPER_SERVER'] ?? '127.0.0.1:9912' : $format; + $dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? new CliDumper() : new HtmlDumper(); + $dumper = new ServerDumper($host, $dumper, self::getDefaultContextProviders()); + break; + default: + $dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? new CliDumper() : new HtmlDumper(); + } + + if (!$dumper instanceof ServerDumper) { + $dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]); + } + + self::$handler = function ($var) use ($cloner, $dumper) { + $dumper->dump($cloner->cloneVar($var)); + }; + } + + private static function getDefaultContextProviders(): array + { + $contextProviders = []; + + if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && class_exists(Request::class)) { + $requestStack = new RequestStack(); + $requestStack->push(Request::createFromGlobals()); + $contextProviders['request'] = new RequestContextProvider($requestStack); + } + + $fileLinkFormatter = class_exists(FileLinkFormatter::class) ? new FileLinkFormatter(null, $requestStack ?? null) : null; + + return $contextProviders + [ + 'cli' => new CliContextProvider(), + 'source' => new SourceContextProvider(null, null, $fileLinkFormatter), + ]; + } +} diff --git a/vendor/symfony/var-dumper/composer.json b/vendor/symfony/var-dumper/composer.json new file mode 100644 index 0000000..db04f58 --- /dev/null +++ b/vendor/symfony/var-dumper/composer.json @@ -0,0 +1,49 @@ +{ + "name": "symfony/var-dumper", + "type": "library", + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "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": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.4" + }, + "conflict": { + "phpunit/phpunit": "<5.4.3", + "symfony/console": "<5.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" +} diff --git a/vendor/symfony/var-exporter/CHANGELOG.md b/vendor/symfony/var-exporter/CHANGELOG.md new file mode 100644 index 0000000..3406c30 --- /dev/null +++ b/vendor/symfony/var-exporter/CHANGELOG.md @@ -0,0 +1,12 @@ +CHANGELOG +========= + +5.1.0 +----- + + * added argument `array &$foundClasses` to `VarExporter::export()` to ease with preloading exported values + +4.2.0 +----- + + * added the component diff --git a/vendor/symfony/var-exporter/Exception/ClassNotFoundException.php b/vendor/symfony/var-exporter/Exception/ClassNotFoundException.php new file mode 100644 index 0000000..4cebe44 --- /dev/null +++ b/vendor/symfony/var-exporter/Exception/ClassNotFoundException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter\Exception; + +class ClassNotFoundException extends \Exception implements ExceptionInterface +{ + public function __construct(string $class, \Throwable $previous = null) + { + parent::__construct(sprintf('Class "%s" not found.', $class), 0, $previous); + } +} diff --git a/vendor/symfony/var-exporter/Exception/ExceptionInterface.php b/vendor/symfony/var-exporter/Exception/ExceptionInterface.php new file mode 100644 index 0000000..adfaed4 --- /dev/null +++ b/vendor/symfony/var-exporter/Exception/ExceptionInterface.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter\Exception; + +interface ExceptionInterface extends \Throwable +{ +} diff --git a/vendor/symfony/var-exporter/Exception/NotInstantiableTypeException.php b/vendor/symfony/var-exporter/Exception/NotInstantiableTypeException.php new file mode 100644 index 0000000..771ee61 --- /dev/null +++ b/vendor/symfony/var-exporter/Exception/NotInstantiableTypeException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter\Exception; + +class NotInstantiableTypeException extends \Exception implements ExceptionInterface +{ + public function __construct(string $type, \Throwable $previous = null) + { + parent::__construct(sprintf('Type "%s" is not instantiable.', $type), 0, $previous); + } +} diff --git a/vendor/symfony/var-exporter/Instantiator.php b/vendor/symfony/var-exporter/Instantiator.php new file mode 100644 index 0000000..38fce27 --- /dev/null +++ b/vendor/symfony/var-exporter/Instantiator.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter; + +use Symfony\Component\VarExporter\Exception\ExceptionInterface; +use Symfony\Component\VarExporter\Exception\NotInstantiableTypeException; +use Symfony\Component\VarExporter\Internal\Hydrator; +use Symfony\Component\VarExporter\Internal\Registry; + +/** + * A utility class to create objects without calling their constructor. + * + * @author Nicolas Grekas + */ +final class Instantiator +{ + /** + * Creates an object and sets its properties without calling its constructor nor any other methods. + * + * For example: + * + * // creates an empty instance of Foo + * Instantiator::instantiate(Foo::class); + * + * // creates a Foo instance and sets one of its properties + * Instantiator::instantiate(Foo::class, ['propertyName' => $propertyValue]); + * + * // creates a Foo instance and sets a private property defined on its parent Bar class + * Instantiator::instantiate(Foo::class, [], [ + * Bar::class => ['privateBarProperty' => $propertyValue], + * ]); + * + * Instances of ArrayObject, ArrayIterator and SplObjectStorage can be created + * by using the special "\0" property name to define their internal value: + * + * // creates an SplObjectStorage where $info1 is attached to $obj1, etc. + * Instantiator::instantiate(SplObjectStorage::class, ["\0" => [$obj1, $info1, $obj2, $info2...]]); + * + * // creates an ArrayObject populated with $inputArray + * Instantiator::instantiate(ArrayObject::class, ["\0" => [$inputArray]]); + * + * @param string $class The class of the instance to create + * @param array $properties The properties to set on the instance + * @param array $privateProperties The private properties to set on the instance, + * keyed by their declaring class + * + * @throws ExceptionInterface When the instance cannot be created + */ + public static function instantiate(string $class, array $properties = [], array $privateProperties = []): object + { + $reflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); + + if (Registry::$cloneable[$class]) { + $wrappedInstance = [clone Registry::$prototypes[$class]]; + } elseif (Registry::$instantiableWithoutConstructor[$class]) { + $wrappedInstance = [$reflector->newInstanceWithoutConstructor()]; + } elseif (null === Registry::$prototypes[$class]) { + throw new NotInstantiableTypeException($class); + } elseif ($reflector->implementsInterface('Serializable') && !method_exists($class, '__unserialize')) { + $wrappedInstance = [unserialize('C:'.\strlen($class).':"'.$class.'":0:{}')]; + } else { + $wrappedInstance = [unserialize('O:'.\strlen($class).':"'.$class.'":0:{}')]; + } + + if ($properties) { + $privateProperties[$class] = isset($privateProperties[$class]) ? $properties + $privateProperties[$class] : $properties; + } + + foreach ($privateProperties as $class => $properties) { + if (!$properties) { + continue; + } + foreach ($properties as $name => $value) { + // because they're also used for "unserialization", hydrators + // deal with array of instances, so we need to wrap values + $properties[$name] = [$value]; + } + (Hydrator::$hydrators[$class] ?? Hydrator::getHydrator($class))($properties, $wrappedInstance); + } + + return $wrappedInstance[0]; + } +} diff --git a/vendor/symfony/var-exporter/Internal/Exporter.php b/vendor/symfony/var-exporter/Internal/Exporter.php new file mode 100644 index 0000000..6ee3ee7 --- /dev/null +++ b/vendor/symfony/var-exporter/Internal/Exporter.php @@ -0,0 +1,410 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter\Internal; + +use Symfony\Component\VarExporter\Exception\NotInstantiableTypeException; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class Exporter +{ + /** + * Prepares an array of values for VarExporter. + * + * For performance this method is public and has no type-hints. + * + * @param array &$values + * @param \SplObjectStorage $objectsPool + * @param array &$refsPool + * @param int &$objectsCount + * @param bool &$valuesAreStatic + * + * @throws NotInstantiableTypeException When a value cannot be serialized + */ + public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount, &$valuesAreStatic): array + { + $refs = $values; + foreach ($values as $k => $value) { + if (\is_resource($value)) { + throw new NotInstantiableTypeException(get_resource_type($value).' resource'); + } + $refs[$k] = $objectsPool; + + if ($isRef = !$valueIsStatic = $values[$k] !== $objectsPool) { + $values[$k] = &$value; // Break hard references to make $values completely + unset($value); // independent from the original structure + $refs[$k] = $value = $values[$k]; + if ($value instanceof Reference && 0 > $value->id) { + $valuesAreStatic = false; + ++$value->count; + continue; + } + $refsPool[] = [&$refs[$k], $value, &$value]; + $refs[$k] = $values[$k] = new Reference(-\count($refsPool), $value); + } + + if (\is_array($value)) { + if ($value) { + $value = self::prepare($value, $objectsPool, $refsPool, $objectsCount, $valueIsStatic); + } + goto handle_value; + } elseif (!\is_object($value) || $value instanceof \UnitEnum) { + goto handle_value; + } + + $valueIsStatic = false; + if (isset($objectsPool[$value])) { + ++$objectsCount; + $value = new Reference($objectsPool[$value][0]); + goto handle_value; + } + + $class = \get_class($value); + $reflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); + + if ($reflector->hasMethod('__serialize')) { + if (!$reflector->getMethod('__serialize')->isPublic()) { + throw new \Error(sprintf('Call to %s method "%s::__serialize()".', $reflector->getMethod('__serialize')->isProtected() ? 'protected' : 'private', $class)); + } + + if (!\is_array($properties = $value->__serialize())) { + throw new \TypeError($class.'::__serialize() must return an array'); + } + + goto prepare_value; + } + + $properties = []; + $sleep = null; + $proto = Registry::$prototypes[$class]; + + if (($value instanceof \ArrayIterator || $value instanceof \ArrayObject) && null !== $proto) { + // ArrayIterator and ArrayObject need special care because their "flags" + // option changes the behavior of the (array) casting operator. + [$arrayValue, $properties] = self::getArrayObjectProperties($value, $proto); + + // populates Registry::$prototypes[$class] with a new instance + Registry::getClassReflector($class, Registry::$instantiableWithoutConstructor[$class], Registry::$cloneable[$class]); + } elseif ($value instanceof \SplObjectStorage && Registry::$cloneable[$class] && null !== $proto) { + // By implementing Serializable, SplObjectStorage breaks + // internal references; let's deal with it on our own. + foreach (clone $value as $v) { + $properties[] = $v; + $properties[] = $value[$v]; + } + $properties = ['SplObjectStorage' => ["\0" => $properties]]; + $arrayValue = (array) $value; + } elseif ($value instanceof \Serializable + || $value instanceof \__PHP_Incomplete_Class + || \PHP_VERSION_ID < 80200 && $value instanceof \DatePeriod + ) { + ++$objectsCount; + $objectsPool[$value] = [$id = \count($objectsPool), serialize($value), [], 0]; + $value = new Reference($id); + goto handle_value; + } else { + if (method_exists($class, '__sleep')) { + if (!\is_array($sleep = $value->__sleep())) { + trigger_error('serialize(): __sleep should return an array only containing the names of instance-variables to serialize', \E_USER_NOTICE); + $value = null; + goto handle_value; + } + $sleep = array_flip($sleep); + } + + $arrayValue = (array) $value; + } + + $proto = (array) $proto; + + foreach ($arrayValue as $name => $v) { + $i = 0; + $n = (string) $name; + if ('' === $n || "\0" !== $n[0]) { + $c = \PHP_VERSION_ID >= 80100 && $reflector->hasProperty($n) && ($p = $reflector->getProperty($n))->isReadOnly() ? $p->class : 'stdClass'; + } elseif ('*' === $n[1]) { + $n = substr($n, 3); + $c = $reflector->getProperty($n)->class; + if ('Error' === $c) { + $c = 'TypeError'; + } elseif ('Exception' === $c) { + $c = 'ErrorException'; + } + } else { + $i = strpos($n, "\0", 2); + $c = substr($n, 1, $i - 1); + $n = substr($n, 1 + $i); + } + if (null !== $sleep) { + if (!isset($sleep[$n]) || ($i && $c !== $class)) { + unset($arrayValue[$name]); + continue; + } + $sleep[$n] = false; + } + if (!\array_key_exists($name, $proto) || $proto[$name] !== $v || "\x00Error\x00trace" === $name || "\x00Exception\x00trace" === $name) { + $properties[$c][$n] = $v; + } + } + if ($sleep) { + foreach ($sleep as $n => $v) { + if (false !== $v) { + trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $n), \E_USER_NOTICE); + } + } + } + if (method_exists($class, '__unserialize')) { + $properties = $arrayValue; + } + + prepare_value: + $objectsPool[$value] = [$id = \count($objectsPool)]; + $properties = self::prepare($properties, $objectsPool, $refsPool, $objectsCount, $valueIsStatic); + ++$objectsCount; + $objectsPool[$value] = [$id, $class, $properties, method_exists($class, '__unserialize') ? -$objectsCount : (method_exists($class, '__wakeup') ? $objectsCount : 0)]; + + $value = new Reference($id); + + handle_value: + if ($isRef) { + unset($value); // Break the hard reference created above + } elseif (!$valueIsStatic) { + $values[$k] = $value; + } + $valuesAreStatic = $valueIsStatic && $valuesAreStatic; + } + + return $values; + } + + public static function export($value, string $indent = '') + { + switch (true) { + case \is_int($value) || \is_float($value): return var_export($value, true); + case [] === $value: return '[]'; + case false === $value: return 'false'; + case true === $value: return 'true'; + case null === $value: return 'null'; + case '' === $value: return "''"; + case $value instanceof \UnitEnum: return '\\'.ltrim(var_export($value, true), '\\'); + } + + if ($value instanceof Reference) { + if (0 <= $value->id) { + return '$o['.$value->id.']'; + } + if (!$value->count) { + return self::export($value->value, $indent); + } + $value = -$value->id; + + return '&$r['.$value.']'; + } + $subIndent = $indent.' '; + + if (\is_string($value)) { + $code = sprintf("'%s'", addcslashes($value, "'\\")); + + $code = preg_replace_callback("/((?:[\\0\\r\\n]|\u{202A}|\u{202B}|\u{202D}|\u{202E}|\u{2066}|\u{2067}|\u{2068}|\u{202C}|\u{2069})++)(.)/", function ($m) use ($subIndent) { + $m[1] = sprintf('\'."%s".\'', str_replace( + ["\0", "\r", "\n", "\u{202A}", "\u{202B}", "\u{202D}", "\u{202E}", "\u{2066}", "\u{2067}", "\u{2068}", "\u{202C}", "\u{2069}", '\n\\'], + ['\0', '\r', '\n', '\u{202A}', '\u{202B}', '\u{202D}', '\u{202E}', '\u{2066}', '\u{2067}', '\u{2068}', '\u{202C}', '\u{2069}', '\n"'."\n".$subIndent.'."\\'], + $m[1] + )); + + if ("'" === $m[2]) { + return substr($m[1], 0, -2); + } + + if ('n".\'' === substr($m[1], -4)) { + return substr_replace($m[1], "\n".$subIndent.".'".$m[2], -2); + } + + return $m[1].$m[2]; + }, $code, -1, $count); + + if ($count && str_starts_with($code, "''.")) { + $code = substr($code, 3); + } + + return $code; + } + + if (\is_array($value)) { + $j = -1; + $code = ''; + foreach ($value as $k => $v) { + $code .= $subIndent; + if (!\is_int($k) || 1 !== $k - $j) { + $code .= self::export($k, $subIndent).' => '; + } + if (\is_int($k) && $k > $j) { + $j = $k; + } + $code .= self::export($v, $subIndent).",\n"; + } + + return "[\n".$code.$indent.']'; + } + + if ($value instanceof Values) { + $code = $subIndent."\$r = [],\n"; + foreach ($value->values as $k => $v) { + $code .= $subIndent.'$r['.$k.'] = '.self::export($v, $subIndent).",\n"; + } + + return "[\n".$code.$indent.']'; + } + + if ($value instanceof Registry) { + return self::exportRegistry($value, $indent, $subIndent); + } + + if ($value instanceof Hydrator) { + return self::exportHydrator($value, $indent, $subIndent); + } + + throw new \UnexpectedValueException(sprintf('Cannot export value of type "%s".', get_debug_type($value))); + } + + private static function exportRegistry(Registry $value, string $indent, string $subIndent): string + { + $code = ''; + $serializables = []; + $seen = []; + $prototypesAccess = 0; + $factoriesAccess = 0; + $r = '\\'.Registry::class; + $j = -1; + + foreach ($value->classes as $k => $class) { + if (':' === ($class[1] ?? null)) { + $serializables[$k] = $class; + continue; + } + if (!Registry::$instantiableWithoutConstructor[$class]) { + if (is_subclass_of($class, 'Serializable') && !method_exists($class, '__unserialize')) { + $serializables[$k] = 'C:'.\strlen($class).':"'.$class.'":0:{}'; + } else { + $serializables[$k] = 'O:'.\strlen($class).':"'.$class.'":0:{}'; + } + if (is_subclass_of($class, 'Throwable')) { + $eol = is_subclass_of($class, 'Error') ? "\0Error\0" : "\0Exception\0"; + $serializables[$k] = substr_replace($serializables[$k], '1:{s:'.(5 + \strlen($eol)).':"'.$eol.'trace";a:0:{}}', -4); + } + continue; + } + $code .= $subIndent.(1 !== $k - $j ? $k.' => ' : ''); + $j = $k; + $eol = ",\n"; + $c = '['.self::export($class).']'; + + if ($seen[$class] ?? false) { + if (Registry::$cloneable[$class]) { + ++$prototypesAccess; + $code .= 'clone $p'.$c; + } else { + ++$factoriesAccess; + $code .= '$f'.$c.'()'; + } + } else { + $seen[$class] = true; + if (Registry::$cloneable[$class]) { + $code .= 'clone ('.($prototypesAccess++ ? '$p' : '($p = &'.$r.'::$prototypes)').$c.' ?? '.$r.'::p'; + } else { + $code .= '('.($factoriesAccess++ ? '$f' : '($f = &'.$r.'::$factories)').$c.' ?? '.$r.'::f'; + $eol = '()'.$eol; + } + $code .= '('.substr($c, 1, -1).'))'; + } + $code .= $eol; + } + + if (1 === $prototypesAccess) { + $code = str_replace('($p = &'.$r.'::$prototypes)', $r.'::$prototypes', $code); + } + if (1 === $factoriesAccess) { + $code = str_replace('($f = &'.$r.'::$factories)', $r.'::$factories', $code); + } + if ('' !== $code) { + $code = "\n".$code.$indent; + } + + if ($serializables) { + $code = $r.'::unserialize(['.$code.'], '.self::export($serializables, $indent).')'; + } else { + $code = '['.$code.']'; + } + + return '$o = '.$code; + } + + private static function exportHydrator(Hydrator $value, string $indent, string $subIndent): string + { + $code = ''; + foreach ($value->properties as $class => $properties) { + $code .= $subIndent.' '.self::export($class).' => '.self::export($properties, $subIndent.' ').",\n"; + } + + $code = [ + self::export($value->registry, $subIndent), + self::export($value->values, $subIndent), + '' !== $code ? "[\n".$code.$subIndent.']' : '[]', + self::export($value->value, $subIndent), + self::export($value->wakeups, $subIndent), + ]; + + return '\\'.\get_class($value)."::hydrate(\n".$subIndent.implode(",\n".$subIndent, $code)."\n".$indent.')'; + } + + /** + * @param \ArrayIterator|\ArrayObject $value + * @param \ArrayIterator|\ArrayObject $proto + */ + private static function getArrayObjectProperties($value, $proto): array + { + $reflector = $value instanceof \ArrayIterator ? 'ArrayIterator' : 'ArrayObject'; + $reflector = Registry::$reflectors[$reflector] ?? Registry::getClassReflector($reflector); + + $properties = [ + $arrayValue = (array) $value, + $reflector->getMethod('getFlags')->invoke($value), + $value instanceof \ArrayObject ? $reflector->getMethod('getIteratorClass')->invoke($value) : 'ArrayIterator', + ]; + + $reflector = $reflector->getMethod('setFlags'); + $reflector->invoke($proto, \ArrayObject::STD_PROP_LIST); + + if ($properties[1] & \ArrayObject::STD_PROP_LIST) { + $reflector->invoke($value, 0); + $properties[0] = (array) $value; + } else { + $reflector->invoke($value, \ArrayObject::STD_PROP_LIST); + $arrayValue = (array) $value; + } + $reflector->invoke($value, $properties[1]); + + if ([[], 0, 'ArrayIterator'] === $properties) { + $properties = []; + } else { + if ('ArrayIterator' === $properties[2]) { + unset($properties[2]); + } + $properties = [$reflector->class => ["\0" => $properties]]; + } + + return [$arrayValue, $properties]; + } +} diff --git a/vendor/symfony/var-exporter/Internal/Hydrator.php b/vendor/symfony/var-exporter/Internal/Hydrator.php new file mode 100644 index 0000000..5ed6bdc --- /dev/null +++ b/vendor/symfony/var-exporter/Internal/Hydrator.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter\Internal; + +use Symfony\Component\VarExporter\Exception\ClassNotFoundException; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class Hydrator +{ + public static $hydrators = []; + + public $registry; + public $values; + public $properties; + public $value; + public $wakeups; + + public function __construct(?Registry $registry, ?Values $values, array $properties, $value, array $wakeups) + { + $this->registry = $registry; + $this->values = $values; + $this->properties = $properties; + $this->value = $value; + $this->wakeups = $wakeups; + } + + public static function hydrate($objects, $values, $properties, $value, $wakeups) + { + foreach ($properties as $class => $vars) { + (self::$hydrators[$class] ?? self::getHydrator($class))($vars, $objects); + } + foreach ($wakeups as $k => $v) { + if (\is_array($v)) { + $objects[-$k]->__unserialize($v); + } else { + $objects[$v]->__wakeup(); + } + } + + return $value; + } + + public static function getHydrator($class) + { + switch ($class) { + case 'stdClass': + return self::$hydrators[$class] = static function ($properties, $objects) { + foreach ($properties as $name => $values) { + foreach ($values as $i => $v) { + $objects[$i]->$name = $v; + } + } + }; + + case 'ErrorException': + return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, new class() extends \ErrorException { + }); + + case 'TypeError': + return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, new class() extends \Error { + }); + + case 'SplObjectStorage': + return self::$hydrators[$class] = static function ($properties, $objects) { + foreach ($properties as $name => $values) { + if ("\0" === $name) { + foreach ($values as $i => $v) { + for ($j = 0; $j < \count($v); ++$j) { + $objects[$i]->attach($v[$j], $v[++$j]); + } + } + continue; + } + foreach ($values as $i => $v) { + $objects[$i]->$name = $v; + } + } + }; + } + + if (!class_exists($class) && !interface_exists($class, false) && !trait_exists($class, false)) { + throw new ClassNotFoundException($class); + } + $classReflector = new \ReflectionClass($class); + + switch ($class) { + case 'ArrayIterator': + case 'ArrayObject': + $constructor = \Closure::fromCallable([$classReflector->getConstructor(), 'invokeArgs']); + + return self::$hydrators[$class] = static function ($properties, $objects) use ($constructor) { + foreach ($properties as $name => $values) { + if ("\0" !== $name) { + foreach ($values as $i => $v) { + $objects[$i]->$name = $v; + } + } + } + foreach ($properties["\0"] ?? [] as $i => $v) { + $constructor($objects[$i], $v); + } + }; + } + + if (!$classReflector->isInternal()) { + return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, $class); + } + + if ($classReflector->name !== $class) { + return self::$hydrators[$classReflector->name] ?? self::getHydrator($classReflector->name); + } + + $propertySetters = []; + foreach ($classReflector->getProperties() as $propertyReflector) { + if (!$propertyReflector->isStatic()) { + $propertyReflector->setAccessible(true); + $propertySetters[$propertyReflector->name] = \Closure::fromCallable([$propertyReflector, 'setValue']); + } + } + + if (!$propertySetters) { + return self::$hydrators[$class] = self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'); + } + + return self::$hydrators[$class] = static function ($properties, $objects) use ($propertySetters) { + foreach ($properties as $name => $values) { + if ($setValue = $propertySetters[$name] ?? null) { + foreach ($values as $i => $v) { + $setValue($objects[$i], $v); + } + continue; + } + foreach ($values as $i => $v) { + $objects[$i]->$name = $v; + } + } + }; + } +} diff --git a/vendor/symfony/var-exporter/Internal/Reference.php b/vendor/symfony/var-exporter/Internal/Reference.php new file mode 100644 index 0000000..e371c07 --- /dev/null +++ b/vendor/symfony/var-exporter/Internal/Reference.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter\Internal; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class Reference +{ + public $id; + public $value; + public $count = 0; + + public function __construct(int $id, $value = null) + { + $this->id = $id; + $this->value = $value; + } +} diff --git a/vendor/symfony/var-exporter/Internal/Registry.php b/vendor/symfony/var-exporter/Internal/Registry.php new file mode 100644 index 0000000..a9fb061 --- /dev/null +++ b/vendor/symfony/var-exporter/Internal/Registry.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter\Internal; + +use Symfony\Component\VarExporter\Exception\ClassNotFoundException; +use Symfony\Component\VarExporter\Exception\NotInstantiableTypeException; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class Registry +{ + public static $reflectors = []; + public static $prototypes = []; + public static $factories = []; + public static $cloneable = []; + public static $instantiableWithoutConstructor = []; + + public $classes = []; + + public function __construct(array $classes) + { + $this->classes = $classes; + } + + public static function unserialize($objects, $serializables) + { + $unserializeCallback = ini_set('unserialize_callback_func', __CLASS__.'::getClassReflector'); + + try { + foreach ($serializables as $k => $v) { + $objects[$k] = unserialize($v); + } + } finally { + ini_set('unserialize_callback_func', $unserializeCallback); + } + + return $objects; + } + + public static function p($class) + { + self::getClassReflector($class, true, true); + + return self::$prototypes[$class]; + } + + public static function f($class) + { + $reflector = self::$reflectors[$class] ?? self::getClassReflector($class, true, false); + + return self::$factories[$class] = \Closure::fromCallable([$reflector, 'newInstanceWithoutConstructor']); + } + + public static function getClassReflector($class, $instantiableWithoutConstructor = false, $cloneable = null) + { + if (!($isClass = class_exists($class)) && !interface_exists($class, false) && !trait_exists($class, false)) { + throw new ClassNotFoundException($class); + } + $reflector = new \ReflectionClass($class); + + if ($instantiableWithoutConstructor) { + $proto = $reflector->newInstanceWithoutConstructor(); + } elseif (!$isClass || $reflector->isAbstract()) { + throw new NotInstantiableTypeException($class); + } elseif ($reflector->name !== $class) { + $reflector = self::$reflectors[$name = $reflector->name] ?? self::getClassReflector($name, false, $cloneable); + self::$cloneable[$class] = self::$cloneable[$name]; + self::$instantiableWithoutConstructor[$class] = self::$instantiableWithoutConstructor[$name]; + self::$prototypes[$class] = self::$prototypes[$name]; + + return self::$reflectors[$class] = $reflector; + } else { + try { + $proto = $reflector->newInstanceWithoutConstructor(); + $instantiableWithoutConstructor = true; + } catch (\ReflectionException $e) { + $proto = $reflector->implementsInterface('Serializable') && !method_exists($class, '__unserialize') ? 'C:' : 'O:'; + if ('C:' === $proto && !$reflector->getMethod('unserialize')->isInternal()) { + $proto = null; + } else { + try { + $proto = @unserialize($proto.\strlen($class).':"'.$class.'":0:{}'); + } catch (\Exception $e) { + if (__FILE__ !== $e->getFile()) { + throw $e; + } + throw new NotInstantiableTypeException($class, $e); + } + if (false === $proto) { + throw new NotInstantiableTypeException($class); + } + } + } + if (null !== $proto && !$proto instanceof \Throwable && !$proto instanceof \Serializable && !method_exists($class, '__sleep') && !method_exists($class, '__serialize')) { + try { + serialize($proto); + } catch (\Exception $e) { + throw new NotInstantiableTypeException($class, $e); + } + } + } + + if (null === $cloneable) { + if (($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) && (!$proto instanceof \Serializable && !method_exists($proto, '__wakeup') && !method_exists($class, '__unserialize'))) { + throw new NotInstantiableTypeException($class); + } + + $cloneable = $reflector->isCloneable() && !$reflector->hasMethod('__clone'); + } + + self::$cloneable[$class] = $cloneable; + self::$instantiableWithoutConstructor[$class] = $instantiableWithoutConstructor; + self::$prototypes[$class] = $proto; + + if ($proto instanceof \Throwable) { + static $setTrace; + + if (null === $setTrace) { + $setTrace = [ + new \ReflectionProperty(\Error::class, 'trace'), + new \ReflectionProperty(\Exception::class, 'trace'), + ]; + $setTrace[0]->setAccessible(true); + $setTrace[1]->setAccessible(true); + $setTrace[0] = \Closure::fromCallable([$setTrace[0], 'setValue']); + $setTrace[1] = \Closure::fromCallable([$setTrace[1], 'setValue']); + } + + $setTrace[$proto instanceof \Exception]($proto, []); + } + + return self::$reflectors[$class] = $reflector; + } +} diff --git a/vendor/symfony/var-exporter/Internal/Values.php b/vendor/symfony/var-exporter/Internal/Values.php new file mode 100644 index 0000000..21ae04e --- /dev/null +++ b/vendor/symfony/var-exporter/Internal/Values.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter\Internal; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class Values +{ + public $values; + + public function __construct(array $values) + { + $this->values = $values; + } +} diff --git a/vendor/symfony/var-exporter/LICENSE b/vendor/symfony/var-exporter/LICENSE new file mode 100644 index 0000000..99757d5 --- /dev/null +++ b/vendor/symfony/var-exporter/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-2023 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-exporter/README.md b/vendor/symfony/var-exporter/README.md new file mode 100644 index 0000000..a34e4c2 --- /dev/null +++ b/vendor/symfony/var-exporter/README.md @@ -0,0 +1,38 @@ +VarExporter Component +===================== + +The VarExporter component allows exporting any serializable PHP data structure to +plain PHP code. While doing so, it preserves all the semantics associated with +the serialization mechanism of PHP (`__wakeup`, `__sleep`, `Serializable`, +`__serialize`, `__unserialize`). + +It also provides an instantiator that allows creating and populating objects +without calling their constructor nor any other methods. + +The reason to use this component *vs* `serialize()` or +[igbinary](https://github.com/igbinary/igbinary) is performance: thanks to +OPcache, the resulting code is significantly faster and more memory efficient +than using `unserialize()` or `igbinary_unserialize()`. + +Unlike `var_export()`, this works on any serializable PHP value. + +It also provides a few improvements over `var_export()`/`serialize()`: + + * the output is PSR-2 compatible; + * the output can be re-indented without messing up with `\r` or `\n` in the data + * missing classes throw a `ClassNotFoundException` instead of being unserialized to + `PHP_Incomplete_Class` objects; + * references involving `SplObjectStorage`, `ArrayObject` or `ArrayIterator` + instances are preserved; + * `Reflection*`, `IteratorIterator` and `RecursiveIteratorIterator` classes + throw an exception when being serialized (their unserialized version is broken + anyway, see https://bugs.php.net/76737). + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/var_exporter.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-exporter/VarExporter.php b/vendor/symfony/var-exporter/VarExporter.php new file mode 100644 index 0000000..3e2a4cc --- /dev/null +++ b/vendor/symfony/var-exporter/VarExporter.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter; + +use Symfony\Component\VarExporter\Exception\ExceptionInterface; +use Symfony\Component\VarExporter\Internal\Exporter; +use Symfony\Component\VarExporter\Internal\Hydrator; +use Symfony\Component\VarExporter\Internal\Registry; +use Symfony\Component\VarExporter\Internal\Values; + +/** + * Exports serializable PHP values to PHP code. + * + * VarExporter allows serializing PHP data structures to plain PHP code (like var_export()) + * while preserving all the semantics associated with serialize() (unlike var_export()). + * + * By leveraging OPcache, the generated PHP code is faster than doing the same with unserialize(). + * + * @author Nicolas Grekas + */ +final class VarExporter +{ + /** + * Exports a serializable PHP value to PHP code. + * + * @param bool &$isStaticValue Set to true after execution if the provided value is static, false otherwise + * @param bool &$classes Classes found in the value are added to this list as both keys and values + * + * @throws ExceptionInterface When the provided value cannot be serialized + */ + public static function export(mixed $value, bool &$isStaticValue = null, array &$foundClasses = []): string + { + $isStaticValue = true; + + if (!\is_object($value) && !(\is_array($value) && $value) && !\is_resource($value) || $value instanceof \UnitEnum) { + return Exporter::export($value); + } + + $objectsPool = new \SplObjectStorage(); + $refsPool = []; + $objectsCount = 0; + + try { + $value = Exporter::prepare([$value], $objectsPool, $refsPool, $objectsCount, $isStaticValue)[0]; + } finally { + $references = []; + foreach ($refsPool as $i => $v) { + if ($v[0]->count) { + $references[1 + $i] = $v[2]; + } + $v[0] = $v[1]; + } + } + + if ($isStaticValue) { + return Exporter::export($value); + } + + $classes = []; + $values = []; + $states = []; + foreach ($objectsPool as $i => $v) { + [, $class, $values[], $wakeup] = $objectsPool[$v]; + $foundClasses[$class] = $classes[] = $class; + + if (0 < $wakeup) { + $states[$wakeup] = $i; + } elseif (0 > $wakeup) { + $states[-$wakeup] = [$i, array_pop($values)]; + $values[] = []; + } + } + ksort($states); + + $wakeups = [null]; + foreach ($states as $k => $v) { + if (\is_array($v)) { + $wakeups[-$v[0]] = $v[1]; + } else { + $wakeups[] = $v; + } + } + + if (null === $wakeups[0]) { + unset($wakeups[0]); + } + + $properties = []; + foreach ($values as $i => $vars) { + foreach ($vars as $class => $values) { + foreach ($values as $name => $v) { + $properties[$class][$name][$i] = $v; + } + } + } + + if ($classes || $references) { + $value = new Hydrator(new Registry($classes), $references ? new Values($references) : null, $properties, $value, $wakeups); + } else { + $isStaticValue = true; + } + + return Exporter::export($value); + } +} diff --git a/vendor/symfony/var-exporter/composer.json b/vendor/symfony/var-exporter/composer.json new file mode 100644 index 0000000..3bf21a6 --- /dev/null +++ b/vendor/symfony/var-exporter/composer.json @@ -0,0 +1,31 @@ +{ + "name": "symfony/var-exporter", + "type": "library", + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "keywords": ["export", "serialize", "instantiate", "hydrate", "construct", "clone"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=8.0.2" + }, + "require-dev": { + "symfony/var-dumper": "^5.4|^6.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\VarExporter\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/vendor/vlucas/phpdotenv/LICENSE b/vendor/vlucas/phpdotenv/LICENSE new file mode 100644 index 0000000..922c552 --- /dev/null +++ b/vendor/vlucas/phpdotenv/LICENSE @@ -0,0 +1,30 @@ +BSD 3-Clause License + +Copyright (c) 2014, Graham Campbell. +Copyright (c) 2013, Vance Lucas. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the copyright holder nor the names of its + contributors may 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 HOLDER 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/vlucas/phpdotenv/composer.json b/vendor/vlucas/phpdotenv/composer.json new file mode 100644 index 0000000..fb972d7 --- /dev/null +++ b/vendor/vlucas/phpdotenv/composer.json @@ -0,0 +1,60 @@ +{ + "name": "vlucas/phpdotenv", + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": ["env", "dotenv", "environment"], + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "require": { + "php": "^7.2.5 || ^8.0", + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.2", + "phpoption/phpoption": "^1.9.2", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "ext-filter": "*", + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit":"^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Dotenv\\Tests\\": "tests/Dotenv/" + } + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + }, + "preferred-install": "dist" + }, + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + } +} diff --git a/vendor/vlucas/phpdotenv/src/Dotenv.php b/vendor/vlucas/phpdotenv/src/Dotenv.php new file mode 100644 index 0000000..0460ced --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Dotenv.php @@ -0,0 +1,267 @@ +store = $store; + $this->parser = $parser; + $this->loader = $loader; + $this->repository = $repository; + } + + /** + * Create a new dotenv instance. + * + * @param \Dotenv\Repository\RepositoryInterface $repository + * @param string|string[] $paths + * @param string|string[]|null $names + * @param bool $shortCircuit + * @param string|null $fileEncoding + * + * @return \Dotenv\Dotenv + */ + public static function create(RepositoryInterface $repository, $paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null) + { + $builder = $names === null ? StoreBuilder::createWithDefaultName() : StoreBuilder::createWithNoNames(); + + foreach ((array) $paths as $path) { + $builder = $builder->addPath($path); + } + + foreach ((array) $names as $name) { + $builder = $builder->addName($name); + } + + if ($shortCircuit) { + $builder = $builder->shortCircuit(); + } + + return new self($builder->fileEncoding($fileEncoding)->make(), new Parser(), new Loader(), $repository); + } + + /** + * Create a new mutable dotenv instance with default repository. + * + * @param string|string[] $paths + * @param string|string[]|null $names + * @param bool $shortCircuit + * @param string|null $fileEncoding + * + * @return \Dotenv\Dotenv + */ + public static function createMutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null) + { + $repository = RepositoryBuilder::createWithDefaultAdapters()->make(); + + return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding); + } + + /** + * Create a new mutable dotenv instance with default repository with the putenv adapter. + * + * @param string|string[] $paths + * @param string|string[]|null $names + * @param bool $shortCircuit + * @param string|null $fileEncoding + * + * @return \Dotenv\Dotenv + */ + public static function createUnsafeMutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null) + { + $repository = RepositoryBuilder::createWithDefaultAdapters() + ->addAdapter(PutenvAdapter::class) + ->make(); + + return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding); + } + + /** + * Create a new immutable dotenv instance with default repository. + * + * @param string|string[] $paths + * @param string|string[]|null $names + * @param bool $shortCircuit + * @param string|null $fileEncoding + * + * @return \Dotenv\Dotenv + */ + public static function createImmutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null) + { + $repository = RepositoryBuilder::createWithDefaultAdapters()->immutable()->make(); + + return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding); + } + + /** + * Create a new immutable dotenv instance with default repository with the putenv adapter. + * + * @param string|string[] $paths + * @param string|string[]|null $names + * @param bool $shortCircuit + * @param string|null $fileEncoding + * + * @return \Dotenv\Dotenv + */ + public static function createUnsafeImmutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null) + { + $repository = RepositoryBuilder::createWithDefaultAdapters() + ->addAdapter(PutenvAdapter::class) + ->immutable() + ->make(); + + return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding); + } + + /** + * Create a new dotenv instance with an array backed repository. + * + * @param string|string[] $paths + * @param string|string[]|null $names + * @param bool $shortCircuit + * @param string|null $fileEncoding + * + * @return \Dotenv\Dotenv + */ + public static function createArrayBacked($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null) + { + $repository = RepositoryBuilder::createWithNoAdapters()->addAdapter(ArrayAdapter::class)->make(); + + return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding); + } + + /** + * Parse the given content and resolve nested variables. + * + * This method behaves just like load(), only without mutating your actual + * environment. We do this by using an array backed repository. + * + * @param string $content + * + * @throws \Dotenv\Exception\InvalidFileException + * + * @return array + */ + public static function parse(string $content) + { + $repository = RepositoryBuilder::createWithNoAdapters()->addAdapter(ArrayAdapter::class)->make(); + + $phpdotenv = new self(new StringStore($content), new Parser(), new Loader(), $repository); + + return $phpdotenv->load(); + } + + /** + * Read and load environment file(s). + * + * @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidEncodingException|\Dotenv\Exception\InvalidFileException + * + * @return array + */ + public function load() + { + $entries = $this->parser->parse($this->store->read()); + + return $this->loader->load($this->repository, $entries); + } + + /** + * Read and load environment file(s), silently failing if no files can be read. + * + * @throws \Dotenv\Exception\InvalidEncodingException|\Dotenv\Exception\InvalidFileException + * + * @return array + */ + public function safeLoad() + { + try { + return $this->load(); + } catch (InvalidPathException $e) { + // suppressing exception + return []; + } + } + + /** + * Required ensures that the specified variables exist, and returns a new validator object. + * + * @param string|string[] $variables + * + * @return \Dotenv\Validator + */ + public function required($variables) + { + return (new Validator($this->repository, (array) $variables))->required(); + } + + /** + * Returns a new validator object that won't check if the specified variables exist. + * + * @param string|string[] $variables + * + * @return \Dotenv\Validator + */ + public function ifPresent($variables) + { + return new Validator($this->repository, (array) $variables); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php b/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php new file mode 100644 index 0000000..1e80f53 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php @@ -0,0 +1,12 @@ + + */ + public function load(RepositoryInterface $repository, array $entries) + { + return \array_reduce($entries, static function (array $vars, Entry $entry) use ($repository) { + $name = $entry->getName(); + + $value = $entry->getValue()->map(static function (Value $value) use ($repository) { + return Resolver::resolve($repository, $value); + }); + + if ($value->isDefined()) { + $inner = $value->get(); + if ($repository->set($name, $inner)) { + return \array_merge($vars, [$name => $inner]); + } + } else { + if ($repository->clear($name)) { + return \array_merge($vars, [$name => null]); + } + } + + return $vars; + }, []); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Loader/LoaderInterface.php b/vendor/vlucas/phpdotenv/src/Loader/LoaderInterface.php new file mode 100644 index 0000000..275d98e --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Loader/LoaderInterface.php @@ -0,0 +1,20 @@ + + */ + public function load(RepositoryInterface $repository, array $entries); +} diff --git a/vendor/vlucas/phpdotenv/src/Loader/Resolver.php b/vendor/vlucas/phpdotenv/src/Loader/Resolver.php new file mode 100644 index 0000000..36d7a4b --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Loader/Resolver.php @@ -0,0 +1,65 @@ +getVars(), static function (string $s, int $i) use ($repository) { + return Str::substr($s, 0, $i).self::resolveVariable($repository, Str::substr($s, $i)); + }, $value->getChars()); + } + + /** + * Resolve a single nested variable. + * + * @param \Dotenv\Repository\RepositoryInterface $repository + * @param string $str + * + * @return string + */ + private static function resolveVariable(RepositoryInterface $repository, string $str) + { + return Regex::replaceCallback( + '/\A\${([a-zA-Z0-9_.]+)}/', + static function (array $matches) use ($repository) { + return Option::fromValue($repository->get($matches[1])) + ->getOrElse($matches[0]); + }, + $str, + 1 + )->success()->getOrElse($str); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Parser/Entry.php b/vendor/vlucas/phpdotenv/src/Parser/Entry.php new file mode 100644 index 0000000..7570f58 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Parser/Entry.php @@ -0,0 +1,59 @@ +name = $name; + $this->value = $value; + } + + /** + * Get the entry name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get the entry value. + * + * @return \PhpOption\Option<\Dotenv\Parser\Value> + */ + public function getValue() + { + /** @var \PhpOption\Option<\Dotenv\Parser\Value> */ + return Option::fromValue($this->value); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Parser/EntryParser.php b/vendor/vlucas/phpdotenv/src/Parser/EntryParser.php new file mode 100644 index 0000000..e286840 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Parser/EntryParser.php @@ -0,0 +1,300 @@ + + */ + public static function parse(string $entry) + { + return self::splitStringIntoParts($entry)->flatMap(static function (array $parts) { + [$name, $value] = $parts; + + return self::parseName($name)->flatMap(static function (string $name) use ($value) { + /** @var Result */ + $parsedValue = $value === null ? Success::create(null) : self::parseValue($value); + + return $parsedValue->map(static function (?Value $value) use ($name) { + return new Entry($name, $value); + }); + }); + }); + } + + /** + * Split the compound string into parts. + * + * @param string $line + * + * @return \GrahamCampbell\ResultType\Result + */ + private static function splitStringIntoParts(string $line) + { + /** @var array{string,string|null} */ + $result = Str::pos($line, '=')->map(static function () use ($line) { + return \array_map('trim', \explode('=', $line, 2)); + })->getOrElse([$line, null]); + + if ($result[0] === '') { + /** @var \GrahamCampbell\ResultType\Result */ + return Error::create(self::getErrorMessage('an unexpected equals', $line)); + } + + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create($result); + } + + /** + * Parse the given variable name. + * + * That is, strip the optional quotes and leading "export" from the + * variable name. We wrap the answer in a result type. + * + * @param string $name + * + * @return \GrahamCampbell\ResultType\Result + */ + private static function parseName(string $name) + { + if (Str::len($name) > 8 && Str::substr($name, 0, 6) === 'export' && \ctype_space(Str::substr($name, 6, 1))) { + $name = \ltrim(Str::substr($name, 6)); + } + + if (self::isQuotedName($name)) { + $name = Str::substr($name, 1, -1); + } + + if (!self::isValidName($name)) { + /** @var \GrahamCampbell\ResultType\Result */ + return Error::create(self::getErrorMessage('an invalid name', $name)); + } + + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create($name); + } + + /** + * Is the given variable name quoted? + * + * @param string $name + * + * @return bool + */ + private static function isQuotedName(string $name) + { + if (Str::len($name) < 3) { + return false; + } + + $first = Str::substr($name, 0, 1); + $last = Str::substr($name, -1, 1); + + return ($first === '"' && $last === '"') || ($first === '\'' && $last === '\''); + } + + /** + * Is the given variable name valid? + * + * @param string $name + * + * @return bool + */ + private static function isValidName(string $name) + { + return Regex::matches('~(*UTF8)\A[\p{Ll}\p{Lu}\p{M}\p{N}_.]+\z~', $name)->success()->getOrElse(false); + } + + /** + * Parse the given variable value. + * + * This has the effect of stripping quotes and comments, dealing with + * special characters, and locating nested variables, but not resolving + * them. Formally, we run a finite state automaton with an output tape: a + * transducer. We wrap the answer in a result type. + * + * @param string $value + * + * @return \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value,string> + */ + private static function parseValue(string $value) + { + if (\trim($value) === '') { + /** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value,string> */ + return Success::create(Value::blank()); + } + + return \array_reduce(\iterator_to_array(Lexer::lex($value)), static function (Result $data, string $token) { + return $data->flatMap(static function (array $data) use ($token) { + return self::processToken($data[1], $token)->map(static function (array $val) use ($data) { + return [$data[0]->append($val[0], $val[1]), $val[2]]; + }); + }); + }, Success::create([Value::blank(), self::INITIAL_STATE]))->flatMap(static function (array $result) { + /** @psalm-suppress DocblockTypeContradiction */ + if (in_array($result[1], self::REJECT_STATES, true)) { + /** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value,string> */ + return Error::create('a missing closing quote'); + } + + /** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value,string> */ + return Success::create($result[0]); + })->mapError(static function (string $err) use ($value) { + return self::getErrorMessage($err, $value); + }); + } + + /** + * Process the given token. + * + * @param int $state + * @param string $token + * + * @return \GrahamCampbell\ResultType\Result + */ + private static function processToken(int $state, string $token) + { + switch ($state) { + case self::INITIAL_STATE: + if ($token === '\'') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create(['', false, self::SINGLE_QUOTED_STATE]); + } elseif ($token === '"') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create(['', false, self::DOUBLE_QUOTED_STATE]); + } elseif ($token === '#') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create(['', false, self::COMMENT_STATE]); + } elseif ($token === '$') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create([$token, true, self::UNQUOTED_STATE]); + } else { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create([$token, false, self::UNQUOTED_STATE]); + } + case self::UNQUOTED_STATE: + if ($token === '#') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create(['', false, self::COMMENT_STATE]); + } elseif (\ctype_space($token)) { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create(['', false, self::WHITESPACE_STATE]); + } elseif ($token === '$') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create([$token, true, self::UNQUOTED_STATE]); + } else { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create([$token, false, self::UNQUOTED_STATE]); + } + case self::SINGLE_QUOTED_STATE: + if ($token === '\'') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create(['', false, self::WHITESPACE_STATE]); + } else { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create([$token, false, self::SINGLE_QUOTED_STATE]); + } + case self::DOUBLE_QUOTED_STATE: + if ($token === '"') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create(['', false, self::WHITESPACE_STATE]); + } elseif ($token === '\\') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create(['', false, self::ESCAPE_SEQUENCE_STATE]); + } elseif ($token === '$') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create([$token, true, self::DOUBLE_QUOTED_STATE]); + } else { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create([$token, false, self::DOUBLE_QUOTED_STATE]); + } + case self::ESCAPE_SEQUENCE_STATE: + if ($token === '"' || $token === '\\') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create([$token, false, self::DOUBLE_QUOTED_STATE]); + } elseif ($token === '$') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create([$token, false, self::DOUBLE_QUOTED_STATE]); + } else { + $first = Str::substr($token, 0, 1); + if (\in_array($first, ['f', 'n', 'r', 't', 'v'], true)) { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create([\stripcslashes('\\'.$first).Str::substr($token, 1), false, self::DOUBLE_QUOTED_STATE]); + } else { + /** @var \GrahamCampbell\ResultType\Result */ + return Error::create('an unexpected escape sequence'); + } + } + case self::WHITESPACE_STATE: + if ($token === '#') { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create(['', false, self::COMMENT_STATE]); + } elseif (!\ctype_space($token)) { + /** @var \GrahamCampbell\ResultType\Result */ + return Error::create('unexpected whitespace'); + } else { + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create(['', false, self::WHITESPACE_STATE]); + } + case self::COMMENT_STATE: + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create(['', false, self::COMMENT_STATE]); + default: + throw new \Error('Parser entered invalid state.'); + } + } + + /** + * Generate a friendly error message. + * + * @param string $cause + * @param string $subject + * + * @return string + */ + private static function getErrorMessage(string $cause, string $subject) + { + return \sprintf( + 'Encountered %s at [%s].', + $cause, + \strtok($subject, "\n") + ); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Parser/Lexer.php b/vendor/vlucas/phpdotenv/src/Parser/Lexer.php new file mode 100644 index 0000000..981af24 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Parser/Lexer.php @@ -0,0 +1,58 @@ + + */ + public static function lex(string $content) + { + static $regex; + + if ($regex === null) { + $regex = '(('.\implode(')|(', self::PATTERNS).'))A'; + } + + $offset = 0; + + while (isset($content[$offset])) { + if (!\preg_match($regex, $content, $matches, 0, $offset)) { + throw new \Error(\sprintf('Lexer encountered unexpected character [%s].', $content[$offset])); + } + + $offset += \strlen($matches[0]); + + yield $matches[0]; + } + } +} diff --git a/vendor/vlucas/phpdotenv/src/Parser/Lines.php b/vendor/vlucas/phpdotenv/src/Parser/Lines.php new file mode 100644 index 0000000..6497993 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Parser/Lines.php @@ -0,0 +1,127 @@ +map(static function () use ($line) { + return self::looksLikeMultilineStop($line, true) === false; + })->getOrElse(false); + } + + /** + * Determine if the given line can be the start of a multiline variable. + * + * @param string $line + * @param bool $started + * + * @return bool + */ + private static function looksLikeMultilineStop(string $line, bool $started) + { + if ($line === '"') { + return true; + } + + return Regex::occurrences('/(?=([^\\\\]"))/', \str_replace('\\\\', '', $line))->map(static function (int $count) use ($started) { + return $started ? $count > 1 : $count >= 1; + })->success()->getOrElse(false); + } + + /** + * Determine if the line in the file is a comment or whitespace. + * + * @param string $line + * + * @return bool + */ + private static function isCommentOrWhitespace(string $line) + { + $line = \trim($line); + + return $line === '' || (isset($line[0]) && $line[0] === '#'); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Parser/Parser.php b/vendor/vlucas/phpdotenv/src/Parser/Parser.php new file mode 100644 index 0000000..2d30dfd --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Parser/Parser.php @@ -0,0 +1,53 @@ +mapError(static function () { + return 'Could not split into separate lines.'; + })->flatMap(static function (array $lines) { + return self::process(Lines::process($lines)); + })->mapError(static function (string $error) { + throw new InvalidFileException(\sprintf('Failed to parse dotenv file. %s', $error)); + })->success()->get(); + } + + /** + * Convert the raw entries into proper entries. + * + * @param string[] $entries + * + * @return \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry[],string> + */ + private static function process(array $entries) + { + /** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry[],string> */ + return \array_reduce($entries, static function (Result $result, string $raw) { + return $result->flatMap(static function (array $entries) use ($raw) { + return EntryParser::parse($raw)->map(static function (Entry $entry) use ($entries) { + /** @var \Dotenv\Parser\Entry[] */ + return \array_merge($entries, [$entry]); + }); + }); + }, Success::create([])); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php b/vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php new file mode 100644 index 0000000..17cc42a --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php @@ -0,0 +1,19 @@ +chars = $chars; + $this->vars = $vars; + } + + /** + * Create an empty value instance. + * + * @return \Dotenv\Parser\Value + */ + public static function blank() + { + return new self('', []); + } + + /** + * Create a new value instance, appending the characters. + * + * @param string $chars + * @param bool $var + * + * @return \Dotenv\Parser\Value + */ + public function append(string $chars, bool $var) + { + return new self( + $this->chars.$chars, + $var ? \array_merge($this->vars, [Str::len($this->chars)]) : $this->vars + ); + } + + /** + * Get the string representation of the parsed value. + * + * @return string + */ + public function getChars() + { + return $this->chars; + } + + /** + * Get the locations of the variables in the value. + * + * @return int[] + */ + public function getVars() + { + $vars = $this->vars; + + \rsort($vars); + + return $vars; + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/AdapterInterface.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/AdapterInterface.php new file mode 100644 index 0000000..5604398 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/AdapterInterface.php @@ -0,0 +1,15 @@ + + */ + public static function create(); +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php new file mode 100644 index 0000000..af0aae1 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php @@ -0,0 +1,89 @@ + + */ + public static function create() + { + if (self::isSupported()) { + /** @var \PhpOption\Option */ + return Some::create(new self()); + } + + return None::create(); + } + + /** + * Determines if the adapter is supported. + * + * This happens if PHP is running as an Apache module. + * + * @return bool + */ + private static function isSupported() + { + return \function_exists('apache_getenv') && \function_exists('apache_setenv'); + } + + /** + * Read an environment variable, if it exists. + * + * @param non-empty-string $name + * + * @return \PhpOption\Option + */ + public function read(string $name) + { + /** @var \PhpOption\Option */ + return Option::fromValue(apache_getenv($name))->filter(static function ($value) { + return \is_string($value) && $value !== ''; + }); + } + + /** + * Write to an environment variable, if possible. + * + * @param non-empty-string $name + * @param string $value + * + * @return bool + */ + public function write(string $name, string $value) + { + return apache_setenv($name, $value); + } + + /** + * Delete an environment variable, if possible. + * + * @param non-empty-string $name + * + * @return bool + */ + public function delete(string $name) + { + return apache_setenv($name, ''); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php new file mode 100644 index 0000000..df64cf6 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php @@ -0,0 +1,80 @@ + + */ + private $variables; + + /** + * Create a new array adapter instance. + * + * @return void + */ + private function __construct() + { + $this->variables = []; + } + + /** + * Create a new instance of the adapter, if it is available. + * + * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface> + */ + public static function create() + { + /** @var \PhpOption\Option */ + return Some::create(new self()); + } + + /** + * Read an environment variable, if it exists. + * + * @param non-empty-string $name + * + * @return \PhpOption\Option + */ + public function read(string $name) + { + return Option::fromArraysValue($this->variables, $name); + } + + /** + * Write to an environment variable, if possible. + * + * @param non-empty-string $name + * @param string $value + * + * @return bool + */ + public function write(string $name, string $value) + { + $this->variables[$name] = $value; + + return true; + } + + /** + * Delete an environment variable, if possible. + * + * @param non-empty-string $name + * + * @return bool + */ + public function delete(string $name) + { + unset($this->variables[$name]); + + return true; + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php new file mode 100644 index 0000000..9eb1947 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php @@ -0,0 +1,89 @@ + + */ + public static function create() + { + /** @var \PhpOption\Option */ + return Some::create(new self()); + } + + /** + * Read an environment variable, if it exists. + * + * @param non-empty-string $name + * + * @return \PhpOption\Option + */ + public function read(string $name) + { + /** @var \PhpOption\Option */ + return Option::fromArraysValue($_ENV, $name) + ->filter(static function ($value) { + return \is_scalar($value); + }) + ->map(static function ($value) { + if ($value === false) { + return 'false'; + } + + if ($value === true) { + return 'true'; + } + + /** @psalm-suppress PossiblyInvalidCast */ + return (string) $value; + }); + } + + /** + * Write to an environment variable, if possible. + * + * @param non-empty-string $name + * @param string $value + * + * @return bool + */ + public function write(string $name, string $value) + { + $_ENV[$name] = $value; + + return true; + } + + /** + * Delete an environment variable, if possible. + * + * @param non-empty-string $name + * + * @return bool + */ + public function delete(string $name) + { + unset($_ENV[$name]); + + return true; + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/GuardedWriter.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/GuardedWriter.php new file mode 100644 index 0000000..fed8b9b --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/GuardedWriter.php @@ -0,0 +1,85 @@ +writer = $writer; + $this->allowList = $allowList; + } + + /** + * Write to an environment variable, if possible. + * + * @param non-empty-string $name + * @param string $value + * + * @return bool + */ + public function write(string $name, string $value) + { + // Don't set non-allowed variables + if (!$this->isAllowed($name)) { + return false; + } + + // Set the value on the inner writer + return $this->writer->write($name, $value); + } + + /** + * Delete an environment variable, if possible. + * + * @param non-empty-string $name + * + * @return bool + */ + public function delete(string $name) + { + // Don't clear non-allowed variables + if (!$this->isAllowed($name)) { + return false; + } + + // Set the value on the inner writer + return $this->writer->delete($name); + } + + /** + * Determine if the given variable is allowed. + * + * @param non-empty-string $name + * + * @return bool + */ + private function isAllowed(string $name) + { + return \in_array($name, $this->allowList, true); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/ImmutableWriter.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/ImmutableWriter.php new file mode 100644 index 0000000..399e6f9 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/ImmutableWriter.php @@ -0,0 +1,110 @@ + + */ + private $loaded; + + /** + * Create a new immutable writer instance. + * + * @param \Dotenv\Repository\Adapter\WriterInterface $writer + * @param \Dotenv\Repository\Adapter\ReaderInterface $reader + * + * @return void + */ + public function __construct(WriterInterface $writer, ReaderInterface $reader) + { + $this->writer = $writer; + $this->reader = $reader; + $this->loaded = []; + } + + /** + * Write to an environment variable, if possible. + * + * @param non-empty-string $name + * @param string $value + * + * @return bool + */ + public function write(string $name, string $value) + { + // Don't overwrite existing environment variables + // Ruby's dotenv does this with `ENV[key] ||= value` + if ($this->isExternallyDefined($name)) { + return false; + } + + // Set the value on the inner writer + if (!$this->writer->write($name, $value)) { + return false; + } + + // Record that we have loaded the variable + $this->loaded[$name] = ''; + + return true; + } + + /** + * Delete an environment variable, if possible. + * + * @param non-empty-string $name + * + * @return bool + */ + public function delete(string $name) + { + // Don't clear existing environment variables + if ($this->isExternallyDefined($name)) { + return false; + } + + // Clear the value on the inner writer + if (!$this->writer->delete($name)) { + return false; + } + + // Leave the variable as fair game + unset($this->loaded[$name]); + + return true; + } + + /** + * Determine if the given variable is externally defined. + * + * That is, is it an "existing" variable. + * + * @param non-empty-string $name + * + * @return bool + */ + private function isExternallyDefined(string $name) + { + return $this->reader->read($name)->isDefined() && !isset($this->loaded[$name]); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiReader.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiReader.php new file mode 100644 index 0000000..0cfda6f --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiReader.php @@ -0,0 +1,48 @@ +readers = $readers; + } + + /** + * Read an environment variable, if it exists. + * + * @param non-empty-string $name + * + * @return \PhpOption\Option + */ + public function read(string $name) + { + foreach ($this->readers as $reader) { + $result = $reader->read($name); + if ($result->isDefined()) { + return $result; + } + } + + return None::create(); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiWriter.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiWriter.php new file mode 100644 index 0000000..15a9d8f --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiWriter.php @@ -0,0 +1,64 @@ +writers = $writers; + } + + /** + * Write to an environment variable, if possible. + * + * @param non-empty-string $name + * @param string $value + * + * @return bool + */ + public function write(string $name, string $value) + { + foreach ($this->writers as $writers) { + if (!$writers->write($name, $value)) { + return false; + } + } + + return true; + } + + /** + * Delete an environment variable, if possible. + * + * @param non-empty-string $name + * + * @return bool + */ + public function delete(string $name) + { + foreach ($this->writers as $writers) { + if (!$writers->delete($name)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php new file mode 100644 index 0000000..6d017cd --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php @@ -0,0 +1,91 @@ + + */ + public static function create() + { + if (self::isSupported()) { + /** @var \PhpOption\Option */ + return Some::create(new self()); + } + + return None::create(); + } + + /** + * Determines if the adapter is supported. + * + * @return bool + */ + private static function isSupported() + { + return \function_exists('getenv') && \function_exists('putenv'); + } + + /** + * Read an environment variable, if it exists. + * + * @param non-empty-string $name + * + * @return \PhpOption\Option + */ + public function read(string $name) + { + /** @var \PhpOption\Option */ + return Option::fromValue(\getenv($name), false)->filter(static function ($value) { + return \is_string($value); + }); + } + + /** + * Write to an environment variable, if possible. + * + * @param non-empty-string $name + * @param string $value + * + * @return bool + */ + public function write(string $name, string $value) + { + \putenv("$name=$value"); + + return true; + } + + /** + * Delete an environment variable, if possible. + * + * @param non-empty-string $name + * + * @return bool + */ + public function delete(string $name) + { + \putenv($name); + + return true; + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php new file mode 100644 index 0000000..306a63f --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php @@ -0,0 +1,17 @@ + + */ + public function read(string $name); +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReplacingWriter.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReplacingWriter.php new file mode 100644 index 0000000..98c0f04 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReplacingWriter.php @@ -0,0 +1,104 @@ + + */ + private $seen; + + /** + * Create a new replacement writer instance. + * + * @param \Dotenv\Repository\Adapter\WriterInterface $writer + * @param \Dotenv\Repository\Adapter\ReaderInterface $reader + * + * @return void + */ + public function __construct(WriterInterface $writer, ReaderInterface $reader) + { + $this->writer = $writer; + $this->reader = $reader; + $this->seen = []; + } + + /** + * Write to an environment variable, if possible. + * + * @param non-empty-string $name + * @param string $value + * + * @return bool + */ + public function write(string $name, string $value) + { + if ($this->exists($name)) { + return $this->writer->write($name, $value); + } + + // succeed if nothing to do + return true; + } + + /** + * Delete an environment variable, if possible. + * + * @param non-empty-string $name + * + * @return bool + */ + public function delete(string $name) + { + if ($this->exists($name)) { + return $this->writer->delete($name); + } + + // succeed if nothing to do + return true; + } + + /** + * Does the given environment variable exist. + * + * Returns true if it currently exists, or existed at any point in the past + * that we are aware of. + * + * @param non-empty-string $name + * + * @return bool + */ + private function exists(string $name) + { + if (isset($this->seen[$name])) { + return true; + } + + if ($this->reader->read($name)->isDefined()) { + $this->seen[$name] = ''; + + return true; + } + + return false; + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php new file mode 100644 index 0000000..f93b6e5 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php @@ -0,0 +1,89 @@ + + */ + public static function create() + { + /** @var \PhpOption\Option */ + return Some::create(new self()); + } + + /** + * Read an environment variable, if it exists. + * + * @param non-empty-string $name + * + * @return \PhpOption\Option + */ + public function read(string $name) + { + /** @var \PhpOption\Option */ + return Option::fromArraysValue($_SERVER, $name) + ->filter(static function ($value) { + return \is_scalar($value); + }) + ->map(static function ($value) { + if ($value === false) { + return 'false'; + } + + if ($value === true) { + return 'true'; + } + + /** @psalm-suppress PossiblyInvalidCast */ + return (string) $value; + }); + } + + /** + * Write to an environment variable, if possible. + * + * @param non-empty-string $name + * @param string $value + * + * @return bool + */ + public function write(string $name, string $value) + { + $_SERVER[$name] = $value; + + return true; + } + + /** + * Delete an environment variable, if possible. + * + * @param non-empty-string $name + * + * @return bool + */ + public function delete(string $name) + { + unset($_SERVER[$name]); + + return true; + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php b/vendor/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php new file mode 100644 index 0000000..4cb3d61 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php @@ -0,0 +1,27 @@ +reader = $reader; + $this->writer = $writer; + } + + /** + * Determine if the given environment variable is defined. + * + * @param string $name + * + * @return bool + */ + public function has(string $name) + { + return '' !== $name && $this->reader->read($name)->isDefined(); + } + + /** + * Get an environment variable. + * + * @param string $name + * + * @throws \InvalidArgumentException + * + * @return string|null + */ + public function get(string $name) + { + if ('' === $name) { + throw new InvalidArgumentException('Expected name to be a non-empty string.'); + } + + return $this->reader->read($name)->getOrElse(null); + } + + /** + * Set an environment variable. + * + * @param string $name + * @param string $value + * + * @throws \InvalidArgumentException + * + * @return bool + */ + public function set(string $name, string $value) + { + if ('' === $name) { + throw new InvalidArgumentException('Expected name to be a non-empty string.'); + } + + return $this->writer->write($name, $value); + } + + /** + * Clear an environment variable. + * + * @param string $name + * + * @throws \InvalidArgumentException + * + * @return bool + */ + public function clear(string $name) + { + if ('' === $name) { + throw new InvalidArgumentException('Expected name to be a non-empty string.'); + } + + return $this->writer->delete($name); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php b/vendor/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php new file mode 100644 index 0000000..a042f9a --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php @@ -0,0 +1,272 @@ +readers = $readers; + $this->writers = $writers; + $this->immutable = $immutable; + $this->allowList = $allowList; + } + + /** + * Create a new repository builder instance with no adapters added. + * + * @return \Dotenv\Repository\RepositoryBuilder + */ + public static function createWithNoAdapters() + { + return new self(); + } + + /** + * Create a new repository builder instance with the default adapters added. + * + * @return \Dotenv\Repository\RepositoryBuilder + */ + public static function createWithDefaultAdapters() + { + $adapters = \iterator_to_array(self::defaultAdapters()); + + return new self($adapters, $adapters); + } + + /** + * Return the array of default adapters. + * + * @return \Generator<\Dotenv\Repository\Adapter\AdapterInterface> + */ + private static function defaultAdapters() + { + foreach (self::DEFAULT_ADAPTERS as $adapter) { + $instance = $adapter::create(); + if ($instance->isDefined()) { + yield $instance->get(); + } + } + } + + /** + * Determine if the given name if of an adapterclass. + * + * @param string $name + * + * @return bool + */ + private static function isAnAdapterClass(string $name) + { + if (!\class_exists($name)) { + return false; + } + + return (new ReflectionClass($name))->implementsInterface(AdapterInterface::class); + } + + /** + * Creates a repository builder with the given reader added. + * + * Accepts either a reader instance, or a class-string for an adapter. If + * the adapter is not supported, then we silently skip adding it. + * + * @param \Dotenv\Repository\Adapter\ReaderInterface|string $reader + * + * @throws \InvalidArgumentException + * + * @return \Dotenv\Repository\RepositoryBuilder + */ + public function addReader($reader) + { + if (!(\is_string($reader) && self::isAnAdapterClass($reader)) && !($reader instanceof ReaderInterface)) { + throw new InvalidArgumentException( + \sprintf( + 'Expected either an instance of %s or a class-string implementing %s', + ReaderInterface::class, + AdapterInterface::class + ) + ); + } + + $optional = Some::create($reader)->flatMap(static function ($reader) { + return \is_string($reader) ? $reader::create() : Some::create($reader); + }); + + $readers = \array_merge($this->readers, \iterator_to_array($optional)); + + return new self($readers, $this->writers, $this->immutable, $this->allowList); + } + + /** + * Creates a repository builder with the given writer added. + * + * Accepts either a writer instance, or a class-string for an adapter. If + * the adapter is not supported, then we silently skip adding it. + * + * @param \Dotenv\Repository\Adapter\WriterInterface|string $writer + * + * @throws \InvalidArgumentException + * + * @return \Dotenv\Repository\RepositoryBuilder + */ + public function addWriter($writer) + { + if (!(\is_string($writer) && self::isAnAdapterClass($writer)) && !($writer instanceof WriterInterface)) { + throw new InvalidArgumentException( + \sprintf( + 'Expected either an instance of %s or a class-string implementing %s', + WriterInterface::class, + AdapterInterface::class + ) + ); + } + + $optional = Some::create($writer)->flatMap(static function ($writer) { + return \is_string($writer) ? $writer::create() : Some::create($writer); + }); + + $writers = \array_merge($this->writers, \iterator_to_array($optional)); + + return new self($this->readers, $writers, $this->immutable, $this->allowList); + } + + /** + * Creates a repository builder with the given adapter added. + * + * Accepts either an adapter instance, or a class-string for an adapter. If + * the adapter is not supported, then we silently skip adding it. We will + * add the adapter as both a reader and a writer. + * + * @param \Dotenv\Repository\Adapter\WriterInterface|string $adapter + * + * @throws \InvalidArgumentException + * + * @return \Dotenv\Repository\RepositoryBuilder + */ + public function addAdapter($adapter) + { + if (!(\is_string($adapter) && self::isAnAdapterClass($adapter)) && !($adapter instanceof AdapterInterface)) { + throw new InvalidArgumentException( + \sprintf( + 'Expected either an instance of %s or a class-string implementing %s', + WriterInterface::class, + AdapterInterface::class + ) + ); + } + + $optional = Some::create($adapter)->flatMap(static function ($adapter) { + return \is_string($adapter) ? $adapter::create() : Some::create($adapter); + }); + + $readers = \array_merge($this->readers, \iterator_to_array($optional)); + $writers = \array_merge($this->writers, \iterator_to_array($optional)); + + return new self($readers, $writers, $this->immutable, $this->allowList); + } + + /** + * Creates a repository builder with mutability enabled. + * + * @return \Dotenv\Repository\RepositoryBuilder + */ + public function immutable() + { + return new self($this->readers, $this->writers, true, $this->allowList); + } + + /** + * Creates a repository builder with the given allow list. + * + * @param string[]|null $allowList + * + * @return \Dotenv\Repository\RepositoryBuilder + */ + public function allowList(array $allowList = null) + { + return new self($this->readers, $this->writers, $this->immutable, $allowList); + } + + /** + * Creates a new repository instance. + * + * @return \Dotenv\Repository\RepositoryInterface + */ + public function make() + { + $reader = new MultiReader($this->readers); + $writer = new MultiWriter($this->writers); + + if ($this->immutable) { + $writer = new ImmutableWriter($writer, $reader); + } + + if ($this->allowList !== null) { + $writer = new GuardedWriter($writer, $this->allowList); + } + + return new AdapterRepository($reader, $writer); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Repository/RepositoryInterface.php b/vendor/vlucas/phpdotenv/src/Repository/RepositoryInterface.php new file mode 100644 index 0000000..d9b18a4 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Repository/RepositoryInterface.php @@ -0,0 +1,51 @@ + + */ + public static function read(array $filePaths, bool $shortCircuit = true, string $fileEncoding = null) + { + $output = []; + + foreach ($filePaths as $filePath) { + $content = self::readFromFile($filePath, $fileEncoding); + if ($content->isDefined()) { + $output[$filePath] = $content->get(); + if ($shortCircuit) { + break; + } + } + } + + return $output; + } + + /** + * Read the given file. + * + * @param string $path + * @param string|null $encoding + * + * @throws \Dotenv\Exception\InvalidEncodingException + * + * @return \PhpOption\Option + */ + private static function readFromFile(string $path, string $encoding = null) + { + /** @var Option */ + $content = Option::fromValue(@\file_get_contents($path), false); + + return $content->flatMap(static function (string $content) use ($encoding) { + return Str::utf8($content, $encoding)->mapError(static function (string $error) { + throw new InvalidEncodingException($error); + })->success(); + }); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Store/FileStore.php b/vendor/vlucas/phpdotenv/src/Store/FileStore.php new file mode 100644 index 0000000..43f6135 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Store/FileStore.php @@ -0,0 +1,72 @@ +filePaths = $filePaths; + $this->shortCircuit = $shortCircuit; + $this->fileEncoding = $fileEncoding; + } + + /** + * Read the content of the environment file(s). + * + * @throws \Dotenv\Exception\InvalidEncodingException|\Dotenv\Exception\InvalidPathException + * + * @return string + */ + public function read() + { + if ($this->filePaths === []) { + throw new InvalidPathException('At least one environment file path must be provided.'); + } + + $contents = Reader::read($this->filePaths, $this->shortCircuit, $this->fileEncoding); + + if (\count($contents) > 0) { + return \implode("\n", $contents); + } + + throw new InvalidPathException( + \sprintf('Unable to read any of the environment file(s) at [%s].', \implode(', ', $this->filePaths)) + ); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Store/StoreBuilder.php b/vendor/vlucas/phpdotenv/src/Store/StoreBuilder.php new file mode 100644 index 0000000..304117f --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Store/StoreBuilder.php @@ -0,0 +1,141 @@ +paths = $paths; + $this->names = $names; + $this->shortCircuit = $shortCircuit; + $this->fileEncoding = $fileEncoding; + } + + /** + * Create a new store builder instance with no names. + * + * @return \Dotenv\Store\StoreBuilder + */ + public static function createWithNoNames() + { + return new self(); + } + + /** + * Create a new store builder instance with the default name. + * + * @return \Dotenv\Store\StoreBuilder + */ + public static function createWithDefaultName() + { + return new self([], [self::DEFAULT_NAME]); + } + + /** + * Creates a store builder with the given path added. + * + * @param string $path + * + * @return \Dotenv\Store\StoreBuilder + */ + public function addPath(string $path) + { + return new self(\array_merge($this->paths, [$path]), $this->names, $this->shortCircuit, $this->fileEncoding); + } + + /** + * Creates a store builder with the given name added. + * + * @param string $name + * + * @return \Dotenv\Store\StoreBuilder + */ + public function addName(string $name) + { + return new self($this->paths, \array_merge($this->names, [$name]), $this->shortCircuit, $this->fileEncoding); + } + + /** + * Creates a store builder with short circuit mode enabled. + * + * @return \Dotenv\Store\StoreBuilder + */ + public function shortCircuit() + { + return new self($this->paths, $this->names, true, $this->fileEncoding); + } + + /** + * Creates a store builder with the specified file encoding. + * + * @param string|null $fileEncoding + * + * @return \Dotenv\Store\StoreBuilder + */ + public function fileEncoding(string $fileEncoding = null) + { + return new self($this->paths, $this->names, $this->shortCircuit, $fileEncoding); + } + + /** + * Creates a new store instance. + * + * @return \Dotenv\Store\StoreInterface + */ + public function make() + { + return new FileStore( + Paths::filePaths($this->paths, $this->names), + $this->shortCircuit, + $this->fileEncoding + ); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Store/StoreInterface.php b/vendor/vlucas/phpdotenv/src/Store/StoreInterface.php new file mode 100644 index 0000000..6f5b986 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Store/StoreInterface.php @@ -0,0 +1,17 @@ +content = $content; + } + + /** + * Read the content of the environment file(s). + * + * @return string + */ + public function read() + { + return $this->content; + } +} diff --git a/vendor/vlucas/phpdotenv/src/Util/Regex.php b/vendor/vlucas/phpdotenv/src/Util/Regex.php new file mode 100644 index 0000000..52c1578 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Util/Regex.php @@ -0,0 +1,112 @@ + + */ + public static function matches(string $pattern, string $subject) + { + return self::pregAndWrap(static function (string $subject) use ($pattern) { + return @\preg_match($pattern, $subject) === 1; + }, $subject); + } + + /** + * Perform a preg match all, wrapping up the result. + * + * @param string $pattern + * @param string $subject + * + * @return \GrahamCampbell\ResultType\Result + */ + public static function occurrences(string $pattern, string $subject) + { + return self::pregAndWrap(static function (string $subject) use ($pattern) { + return (int) @\preg_match_all($pattern, $subject); + }, $subject); + } + + /** + * Perform a preg replace callback, wrapping up the result. + * + * @param string $pattern + * @param callable $callback + * @param string $subject + * @param int|null $limit + * + * @return \GrahamCampbell\ResultType\Result + */ + public static function replaceCallback(string $pattern, callable $callback, string $subject, int $limit = null) + { + return self::pregAndWrap(static function (string $subject) use ($pattern, $callback, $limit) { + return (string) @\preg_replace_callback($pattern, $callback, $subject, $limit ?? -1); + }, $subject); + } + + /** + * Perform a preg split, wrapping up the result. + * + * @param string $pattern + * @param string $subject + * + * @return \GrahamCampbell\ResultType\Result + */ + public static function split(string $pattern, string $subject) + { + return self::pregAndWrap(static function (string $subject) use ($pattern) { + /** @var string[] */ + return (array) @\preg_split($pattern, $subject); + }, $subject); + } + + /** + * Perform a preg operation, wrapping up the result. + * + * @template V + * + * @param callable(string):V $operation + * @param string $subject + * + * @return \GrahamCampbell\ResultType\Result + */ + private static function pregAndWrap(callable $operation, string $subject) + { + $result = $operation($subject); + + if (\preg_last_error() !== \PREG_NO_ERROR) { + /** @var \GrahamCampbell\ResultType\Result */ + return Error::create(\preg_last_error_msg()); + } + + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create($result); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Util/Str.php b/vendor/vlucas/phpdotenv/src/Util/Str.php new file mode 100644 index 0000000..087e236 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Util/Str.php @@ -0,0 +1,98 @@ + + */ + public static function utf8(string $input, string $encoding = null) + { + if ($encoding !== null && !\in_array($encoding, \mb_list_encodings(), true)) { + /** @var \GrahamCampbell\ResultType\Result */ + return Error::create( + \sprintf('Illegal character encoding [%s] specified.', $encoding) + ); + } + $converted = $encoding === null ? + @\mb_convert_encoding($input, 'UTF-8') : + @\mb_convert_encoding($input, 'UTF-8', $encoding); + /** + * this is for support UTF-8 with BOM encoding + * @see https://en.wikipedia.org/wiki/Byte_order_mark + * @see https://github.com/vlucas/phpdotenv/issues/500 + */ + if (\substr($converted, 0, 3) == "\xEF\xBB\xBF") { + $converted = \substr($converted, 3); + } + /** @var \GrahamCampbell\ResultType\Result */ + return Success::create($converted); + } + + /** + * Search for a given substring of the input. + * + * @param string $haystack + * @param string $needle + * + * @return \PhpOption\Option + */ + public static function pos(string $haystack, string $needle) + { + /** @var \PhpOption\Option */ + return Option::fromValue(\mb_strpos($haystack, $needle, 0, 'UTF-8'), false); + } + + /** + * Grab the specified substring of the input. + * + * @param string $input + * @param int $start + * @param int|null $length + * + * @return string + */ + public static function substr(string $input, int $start, int $length = null) + { + return \mb_substr($input, $start, $length, 'UTF-8'); + } + + /** + * Compute the length of the given string. + * + * @param string $input + * + * @return int + */ + public static function len(string $input) + { + return \mb_strlen($input, 'UTF-8'); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Validator.php b/vendor/vlucas/phpdotenv/src/Validator.php new file mode 100644 index 0000000..0c04ab6 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Validator.php @@ -0,0 +1,209 @@ +repository = $repository; + $this->variables = $variables; + } + + /** + * Assert that each variable is present. + * + * @throws \Dotenv\Exception\ValidationException + * + * @return \Dotenv\Validator + */ + public function required() + { + return $this->assert( + static function (?string $value) { + return $value !== null; + }, + 'is missing' + ); + } + + /** + * Assert that each variable is not empty. + * + * @throws \Dotenv\Exception\ValidationException + * + * @return \Dotenv\Validator + */ + public function notEmpty() + { + return $this->assertNullable( + static function (string $value) { + return Str::len(\trim($value)) > 0; + }, + 'is empty' + ); + } + + /** + * Assert that each specified variable is an integer. + * + * @throws \Dotenv\Exception\ValidationException + * + * @return \Dotenv\Validator + */ + public function isInteger() + { + return $this->assertNullable( + static function (string $value) { + return \ctype_digit($value); + }, + 'is not an integer' + ); + } + + /** + * Assert that each specified variable is a boolean. + * + * @throws \Dotenv\Exception\ValidationException + * + * @return \Dotenv\Validator + */ + public function isBoolean() + { + return $this->assertNullable( + static function (string $value) { + if ($value === '') { + return false; + } + + return \filter_var($value, \FILTER_VALIDATE_BOOLEAN, \FILTER_NULL_ON_FAILURE) !== null; + }, + 'is not a boolean' + ); + } + + /** + * Assert that each variable is amongst the given choices. + * + * @param string[] $choices + * + * @throws \Dotenv\Exception\ValidationException + * + * @return \Dotenv\Validator + */ + public function allowedValues(array $choices) + { + return $this->assertNullable( + static function (string $value) use ($choices) { + return \in_array($value, $choices, true); + }, + \sprintf('is not one of [%s]', \implode(', ', $choices)) + ); + } + + /** + * Assert that each variable matches the given regular expression. + * + * @param string $regex + * + * @throws \Dotenv\Exception\ValidationException + * + * @return \Dotenv\Validator + */ + public function allowedRegexValues(string $regex) + { + return $this->assertNullable( + static function (string $value) use ($regex) { + return Regex::matches($regex, $value)->success()->getOrElse(false); + }, + \sprintf('does not match "%s"', $regex) + ); + } + + /** + * Assert that the callback returns true for each variable. + * + * @param callable(?string):bool $callback + * @param string $message + * + * @throws \Dotenv\Exception\ValidationException + * + * @return \Dotenv\Validator + */ + public function assert(callable $callback, string $message) + { + $failing = []; + + foreach ($this->variables as $variable) { + if ($callback($this->repository->get($variable)) === false) { + $failing[] = \sprintf('%s %s', $variable, $message); + } + } + + if (\count($failing) > 0) { + throw new ValidationException(\sprintf( + 'One or more environment variables failed assertions: %s.', + \implode(', ', $failing) + )); + } + + return $this; + } + + /** + * Assert that the callback returns true for each variable. + * + * Skip checking null variable values. + * + * @param callable(string):bool $callback + * @param string $message + * + * @throws \Dotenv\Exception\ValidationException + * + * @return \Dotenv\Validator + */ + public function assertNullable(callable $callback, string $message) + { + return $this->assert( + static function (?string $value) use ($callback) { + if ($value === null) { + return true; + } + + return $callback($value); + }, + $message + ); + } +} diff --git a/vendor/voku/portable-ascii/CHANGELOG.md b/vendor/voku/portable-ascii/CHANGELOG.md new file mode 100644 index 0000000..12fc393 --- /dev/null +++ b/vendor/voku/portable-ascii/CHANGELOG.md @@ -0,0 +1,202 @@ +# Changelog + +### 2.0.1 (2022-03-08) + +- "To people of Russia": There is a war in Ukraine right now. The forces of the Russian Federation are attacking civilians. +- optimize some phpdocs + +### 2.0.0 (2022-01-24) + +- prefer "Russian - Passport (2013), ICAO" instead of "Russian - GOST 7.79-2000(B)" +- fix "Ukrainian" char-mapping (thanks to @Andr1yk0) +- fix "Persian" char-mapping (thanks to @frost-cyber) + +### 1.6.1 (2022-01-24) + +- revert: prefer "Russian - Passport (2013), ICAO" instead of "Russian - GOST 7.79-2000(B)" +- revert: fix "Ukrainian" char-mapping (thanks to @Andr1yk0) +- revert: fix "Persian" char-mapping (thanks to @frost-cyber) + +### 1.6.0 (2022-01-24) + +- prefer "Russian - Passport (2013), ICAO" instead of "Russian - GOST 7.79-2000(B)" +- fix "Ukrainian" char-mapping (thanks to @Andr1yk0) +- fix "Persian" char-mapping (thanks to @frost-cyber) +- fix "ASCII::normalize_whitespace()" -> "CARRIAGE RETURN" is more like "
    " and no "\n" +- add "ASCII::to_ascii_remap()" -> this method will return broken characters and is only for special cases + +### 1.5.6 (2020-11-12) + +- "ASCII::normalize_whitespace()" -> can now also remove "control characters" if needed v2 + +### 1.5.5 (2020-11-12) + +- fix "Greeklish" char-mapping (thanks @sebdesign) +- "ASCII::normalize_whitespace()" -> can now also remove "control characters" if needed + +### 1.5.4 (2020-11-08) + +- add some missing replacements in U+23xx page (thanks @marcoffee) +- fix "Russian" char-mapping (thanks @ilyahoilik) +- running test with PHP 8.0 rc3 + +### 1.5.3 (2020-07-23) + +- fix "Georgian" char-mapping (thanks @waska14) + +### 1.5.2 (2020-06-16) + +- add "Bengali" (bn) language support (thanks @eliyas5044) +- fix "Portuguese" char-mapping +- reduce the file size (removed extra comments from "avian2/unidecode") + +### 1.5.1 (2020-05-26) + +- fix merge ASCII transliterations from "avian2/unidecode" (python) + -> https://github.com/avian2/unidecode/ + +### 1.5.0 (2020-05-24) + +- merge ASCII transliterations from "avian2/unidecode" (python) + -> https://github.com/avian2/unidecode/ + +### 1.4.11 (2020-05-23) + +- "composer.json" -> remove "autoload-dev" stuff from "autoload" +- "voku/php-readme-helper" -> auto-generate the API documentation in the README + +### 1.4.10 (2020-03-13) + +- ASCII::to_ascii() -> fix extra symbol handling in the regex +- ASCII::to_ascii() -> fix for languages with multi-length-special-char (e.g. Greek -> 'ει' => 'i') + +### 1.4.9 (2020-03-06) + +- ASCII::to_slugify() -> fix php warning from empty "separator" + +### 1.4.8 (2020-02-06) + +- small optimization for "ASCII::to_ascii()" performance + +### 1.4.7 (2020-01-27) + +- fix possible wrong type from "getDataIfExists()" -> e.g. a bug reported where "/data/" was modified +- inline variables +- do not use "=== true" for "bool"-types + +### 1.4.6 (2019-12-23) + +- optimize "ASCII::to_ascii()" performance +- add "armenian" chars +- add "ASCII:getAllLanguages()" + +### 1.4.5 (2019-12-19) + +- use "@psalm-pure" v2 + +### 1.4.4 (2019-12-19) + +- use "@psalm-pure" + +### 1.4.3 (2019-12-19) + +- use "@psalm-immutable" + +### 1.4.2 (2019-12-13) + +- optimize the performance v2 +- more fixes for non-ascii regex + +### 1.4.1 (2019-12-13) + +- fix regex for non-ascii + +### 1.4.0 (2019-12-13) + +- optimize the performance, via single char replacements + +### 1.3.6 (2019-12-13) + +- "ascii_extras" -> convert the static content into ascii + -> e.g.: instead of replacing "+" with "più" we use "piu" (Italian), because we want to use ascii anyway + +### 1.3.5 (2019-11-11) + +- fix "ASCII::remove_invisible_characters()" -> do not remove invisible encoded url strings by default + +### 1.3.4 (2019-10-14) + +- fix static cache for "ASCII::charsArrayWithOneLanguage" + +### 1.3.3 (2019-10-14) + +- fix "Turkish" mapping -> 'ä' -> 'a' + +### 1.3.2 (2019-10-14) + +- fix language parameter usage with e.g. "de_DE" +- re-add missing "extra"-mapping chars + +### 1.3.1 (2019-10-13) + +- fix "ASCII::to_slugify" -> remove unicode chars +- add more test for ascii chars in the mapping +- fix non ascii chars in the mapping + +### 1.3.0 (2019-10-12) + +- add transliteration "fr" (was supported before, but with chars from other languages) +- add transliteration "ru" - Passport (2013), ICAO +- add transliteration "ru" - GOST 7.79-2000(B) +- add transliteration "el" - greeklish +- add transliteration "zh" +- add transliteration "nl" +- add transliteration "it" +- add transliteration "mk" +- add transliteration "pt" +- add constants -> ASCII::*LANGUAGE_CODES +- add more special latin chars / (currency) symbols +- add simple tests for all supported languages +- optimize "Russian" to ASCII (via "translit.ru") +- optimize performance of string replacement +- optimize performance of array merging +- optimize phpdoc comments +- "ASCII::to_transliterate" -> use "transliterator_create" + static cache +- "ASCII::to_ascii" -> fix "remove unsupported chars" +- "ASCII::to_ascii" -> add some more special chars +- run/fix static analyse via "pslam" + "phpstan" +- auto fix code style via "php-cs-fixer" +- fix transliteration for "german" +- fix transliteration for "persian" (thanks @mardep) +- fix transliteration for "polish" (thanks @dariusz.drobisz) +- fix transliteration for "bulgarian" (thanks @mkosturkov) +- fix transliteration for "croatian" (thanks @ludifonovac) +- fix transliteration for "serbian" (thanks @ludifonovac) +- fix transliteration for "swedish" (thanks @nicholasruunu) +- fix transliteration for "france" (thanks @sharptsa) +- fix transliteration for "serbian" (thanks @nikolaposa) +- fix transliteration for "czech" (thanks @slepic) + +### 1.2.3 (2019-09-10) + +- fix language depending ASCII chars (the order matters) + +### 1.2.2 (2019-09-10) + +- fix bulgarian ASCII chars | thanks @bgphp + +### 1.2.1 (2019-09-07) + +- "charsArray()" -> add access to "ASCII::$ASCII_MAPS*"" + +### 1.2.0 (2019-09-07) + +- "to_slugify()" -> use the extra ascii array + +### 1.1.0 (2019-09-07) + +- add + split extra ascii replacements + +### 1.0.0 (2019-09-05) + +- initial commit \ No newline at end of file diff --git a/vendor/voku/portable-ascii/LICENSE.txt b/vendor/voku/portable-ascii/LICENSE.txt new file mode 100644 index 0000000..b6ba47e --- /dev/null +++ b/vendor/voku/portable-ascii/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (C) 2019 Lars Moelleken + +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/voku/portable-ascii/README.md b/vendor/voku/portable-ascii/README.md new file mode 100644 index 0000000..3ce36d6 --- /dev/null +++ b/vendor/voku/portable-ascii/README.md @@ -0,0 +1,451 @@ +[//]: # (AUTO-GENERATED BY "PHP README Helper": base file -> docs/base.md) +[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) + +[![Build Status](https://github.com/voku/portable-ascii/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/voku/portable-ascii/actions) +[![Build status](https://ci.appveyor.com/api/projects/status/gnejjnk7qplr7f5t/branch/master?svg=true)](https://ci.appveyor.com/project/voku/portable-ascii/branch/master) +[![codecov.io](https://codecov.io/github/voku/portable-ascii/coverage.svg?branch=master)](https://codecov.io/github/voku/portable-ascii?branch=master) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/997c9bb10d1c4791967bdf2e42013e8e)](https://www.codacy.com/app/voku/portable-ascii) +[![Latest Stable Version](https://poser.pugx.org/voku/portable-ascii/v/stable)](https://packagist.org/packages/voku/portable-ascii) +[![Total Downloads](https://poser.pugx.org/voku/portable-ascii/downloads)](https://packagist.org/packages/voku/portable-ascii) +[![License](https://poser.pugx.org/voku/portable-ascii/license)](https://packagist.org/packages/voku/portable-ascii) +[![Donate to this project using Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.me/moelleken) +[![Donate to this project using Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://www.patreon.com/voku) + +# 🔡 Portable ASCII + +## Description + +It is written in PHP (PHP 7+) and can work without "mbstring", "iconv" or any other extra encoding php-extension on your server. + +The benefit of Portable ASCII is that it is easy to use, easy to bundle. + +The project based on ... ++ Sean M. Burke's work (https://metacpan.org/pod/Text::Unidecode) ++ Tomaz Solc's work (https://pypi.org/project/Unidecode/) ++ Portable UTF-8 work (https://github.com/voku/portable-utf8) ++ Daniel St. Jules's work (https://github.com/danielstjules/Stringy) ++ Johnny Broadway's work (https://github.com/jbroadway/urlify) ++ and many cherry-picks from "github"-gists and "Stack Overflow"-snippets ... + +## Index + +* [Alternative](#alternative) +* [Install](#install-portable-ascii-via-composer-require) +* [Why Portable ASCII?](#why-portable-ascii) +* [Requirements and Recommendations](#requirements-and-recommendations) +* [Usage](#usage) +* [Class methods](#class-methods) +* [Unit Test](#unit-test) +* [License and Copyright](#license-and-copyright) + +## Alternative + +If you like a more Object Oriented Way to edit strings, then you can take a look at [voku/Stringy](https://github.com/voku/Stringy), it's a fork of "danielstjules/Stringy" but it used the "Portable ASCII"-Class and some extra methods. + +```php +// Portable ASCII +use voku\helper\ASCII; +ASCII::to_transliterate('déjà σσς iıii'); // 'deja sss iiii' + +// voku/Stringy +use Stringy\Stringy as S; +$stringy = S::create('déjà σσς iıii'); +$stringy->toTransliterate(); // 'deja sss iiii' +``` + +## Install "Portable ASCII" via "composer require" +```shell +composer require voku/portable-ascii +``` + +## Why Portable ASCII?[]() +I need ASCII char handling in different classes and before I added this functions into "Portable UTF-8", +but this repo is more modular and portable, because it has no dependencies. + +## Requirements and Recommendations + +* No extensions are required to run this library. Portable ASCII only needs PCRE library that is available by default since PHP 4.2.0 and cannot be disabled since PHP 5.3.0. "\u" modifier support in PCRE for ASCII handling is not a must. +* PHP 7.0 is the minimum requirement +* PHP 8.0 is also supported + +## Usage + +Example: ASCII::to_ascii() +```php + echo ASCII::to_ascii('�Düsseldorf�', 'de'); + + // will output + // Duesseldorf + + echo ASCII::to_ascii('�Düsseldorf�', 'en'); + + // will output + // Dusseldorf +``` + +# Portable ASCII | API + +The API from the "ASCII"-Class is written as small static methods. + + +## Class methods + +

    charsArray +charsArrayWithMultiLanguageValues +charsArrayWithOneLanguage +charsArrayWithSingleLanguageValues +
    clean +getAllLanguages +is_ascii +normalize_msword +
    normalize_whitespace +remove_invisible_characters +to_ascii +to_ascii_remap +
    to_filename +to_slugify +to_transliterate +
    + +#### charsArray(bool $replace_extra_symbols): array + +Returns an replacement array for ASCII methods. + +EXAMPLE: +$array = ASCII::charsArray(); +var_dump($array['ru']['б']); // 'b' + + +**Parameters:** +- `bool $replace_extra_symbols [optional]

    Add some more replacements e.g. "£" with " pound ".

    ` + +**Return:** +- `array` + +-------- + +#### charsArrayWithMultiLanguageValues(bool $replace_extra_symbols): array + +Returns an replacement array for ASCII methods with a mix of multiple languages. + +EXAMPLE: +$array = ASCII::charsArrayWithMultiLanguageValues(); +var_dump($array['b']); // ['β', 'б', 'ဗ', 'ბ', 'ب'] + + +**Parameters:** +- `bool $replace_extra_symbols [optional]

    Add some more replacements e.g. "£" with " pound ".

    ` + +**Return:** +- `array

    An array of replacements.

    ` + +-------- + +#### charsArrayWithOneLanguage(string $language, bool $replace_extra_symbols, bool $asOrigReplaceArray): array + +Returns an replacement array for ASCII methods with one language. + +For example, German will map 'ä' to 'ae', while other languages +will simply return e.g. 'a'. + +EXAMPLE: +$array = ASCII::charsArrayWithOneLanguage('ru'); +$tmpKey = \array_search('yo', $array['replace']); +echo $array['orig'][$tmpKey]; // 'ё' + + +**Parameters:** +- `ASCII::* $language [optional]

    Language of the source string e.g.: en, de_at, or de-ch. +(default is 'en') | ASCII::*_LANGUAGE_CODE

    ` +- `bool $replace_extra_symbols [optional]

    Add some more replacements e.g. "£" with " pound ".

    ` +- `bool $asOrigReplaceArray [optional]

    TRUE === return {orig: string[], replace: string[]} +array

    ` + +**Return:** +- `array

    An array of replacements.

    ` + +-------- + +#### charsArrayWithSingleLanguageValues(bool $replace_extra_symbols, bool $asOrigReplaceArray): array + +Returns an replacement array for ASCII methods with multiple languages. + +EXAMPLE: +$array = ASCII::charsArrayWithSingleLanguageValues(); +$tmpKey = \array_search('hnaik', $array['replace']); +echo $array['orig'][$tmpKey]; // '၌' + + +**Parameters:** +- `bool $replace_extra_symbols [optional]

    Add some more replacements e.g. "£" with " pound ".

    ` +- `bool $asOrigReplaceArray [optional]

    TRUE === return {orig: string[], replace: string[]} +array

    ` + +**Return:** +- `array

    An array of replacements.

    ` + +-------- + +#### clean(string $str, bool $normalize_whitespace, bool $keep_non_breaking_space, bool $normalize_msword, bool $remove_invisible_characters): string + +Accepts a string and removes all non-UTF-8 characters from it + extras if needed. + +**Parameters:** +- `string $str

    The string to be sanitized.

    ` +- `bool $normalize_whitespace [optional]

    Set to true, if you need to normalize the +whitespace.

    ` +- `bool $keep_non_breaking_space [optional]

    Set to true, to keep non-breaking-spaces, in +combination with +$normalize_whitespace

    ` +- `bool $normalize_msword [optional]

    Set to true, if you need to normalize MS Word chars +e.g.: "…" +=> "..."

    ` +- `bool $remove_invisible_characters [optional]

    Set to false, if you not want to remove invisible +characters e.g.: "\0"

    ` + +**Return:** +- `string

    A clean UTF-8 string.

    ` + +-------- + +#### getAllLanguages(): string[] + +Get all languages from the constants "ASCII::.*LANGUAGE_CODE". + +**Parameters:** +__nothing__ + +**Return:** +- `string[]` + +-------- + +#### is_ascii(string $str): bool + +Checks if a string is 7 bit ASCII. + +EXAMPLE: +ASCII::is_ascii('白'); // false + + +**Parameters:** +- `string $str

    The string to check.

    ` + +**Return:** +- `bool

    +true if it is ASCII
    +false otherwise +

    ` + +-------- + +#### normalize_msword(string $str): string + +Returns a string with smart quotes, ellipsis characters, and dashes from +Windows-1252 (commonly used in Word documents) replaced by their ASCII +equivalents. + +EXAMPLE: +ASCII::normalize_msword('„Abcdef…”'); // '"Abcdef..."' + + +**Parameters:** +- `string $str

    The string to be normalized.

    ` + +**Return:** +- `string

    A string with normalized characters for commonly used chars in Word documents.

    ` + +-------- + +#### normalize_whitespace(string $str, bool $keepNonBreakingSpace, bool $keepBidiUnicodeControls, bool $normalize_control_characters): string + +Normalize the whitespace. + +EXAMPLE: +ASCII::normalize_whitespace("abc-\xc2\xa0-öäü-\xe2\x80\xaf-\xE2\x80\xAC", true); // "abc-\xc2\xa0-öäü- -" + + +**Parameters:** +- `string $str

    The string to be normalized.

    ` +- `bool $keepNonBreakingSpace [optional]

    Set to true, to keep non-breaking-spaces.

    ` +- `bool $keepBidiUnicodeControls [optional]

    Set to true, to keep non-printable (for the web) +bidirectional text chars.

    ` +- `bool $normalize_control_characters [optional]

    Set to true, to convert e.g. LINE-, PARAGRAPH-SEPARATOR with "\n" and LINE TABULATION with "\t".

    ` + +**Return:** +- `string

    A string with normalized whitespace.

    ` + +-------- + +#### remove_invisible_characters(string $str, bool $url_encoded, string $replacement, bool $keep_basic_control_characters): string + +Remove invisible characters from a string. + +e.g.: This prevents sandwiching null characters between ascii characters, like Java\0script. + +copy&past from https://github.com/bcit-ci/CodeIgniter/blob/develop/system/core/Common.php + +**Parameters:** +- `string $str` +- `bool $url_encoded` +- `string $replacement` +- `bool $keep_basic_control_characters` + +**Return:** +- `string` + +-------- + +#### to_ascii(string $str, string $language, bool $remove_unsupported_chars, bool $replace_extra_symbols, bool $use_transliterate, bool|null $replace_single_chars_only): string + +Returns an ASCII version of the string. A set of non-ASCII characters are +replaced with their closest ASCII counterparts, and the rest are removed +by default. The language or locale of the source string can be supplied +for language-specific transliteration in any of the following formats: +en, en_GB, or en-GB. For example, passing "de" results in "äöü" mapping +to "aeoeue" rather than "aou" as in other languages. + +EXAMPLE: +ASCII::to_ascii('�Düsseldorf�', 'en'); // Dusseldorf + + +**Parameters:** +- `string $str

    The input string.

    ` +- `ASCII::* $language [optional]

    Language of the source string. +(default is 'en') | ASCII::*_LANGUAGE_CODE

    ` +- `bool $remove_unsupported_chars [optional]

    Whether or not to remove the +unsupported characters.

    ` +- `bool $replace_extra_symbols [optional]

    Add some more replacements e.g. "£" with " pound +".

    ` +- `bool $use_transliterate [optional]

    Use ASCII::to_transliterate() for unknown chars.

    ` +- `bool|null $replace_single_chars_only [optional]

    Single char replacement is better for the +performance, but some languages need to replace more then one char +at the same time. | NULL === auto-setting, depended on the +language

    ` + +**Return:** +- `string

    A string that contains only ASCII characters.

    ` + +-------- + +#### to_ascii_remap(string $str1, string $str2): string[] + +WARNING: This method will return broken characters and is only for special cases. + +Convert two UTF-8 encoded string to a single-byte strings suitable for +functions that need the same string length after the conversion. + +The function simply uses (and updates) a tailored dynamic encoding +(in/out map parameter) where non-ascii characters are remapped to +the range [128-255] in order of appearance. + +**Parameters:** +- `string $str1` +- `string $str2` + +**Return:** +- `string[]` + +-------- + +#### to_filename(string $str, bool $use_transliterate, string $fallback_char): string + +Convert given string to safe filename (and keep string case). + +EXAMPLE: +ASCII::to_filename('שדגשדג.png', true)); // 'shdgshdg.png' + + +**Parameters:** +- `string $str` +- `bool $use_transliterate

    ASCII::to_transliterate() is used by default - unsafe characters are +simply replaced with hyphen otherwise.

    ` +- `string $fallback_char` + +**Return:** +- `string

    A string that contains only safe characters for a filename.

    ` + +-------- + +#### to_slugify(string $str, string $separator, string $language, string[] $replacements, bool $replace_extra_symbols, bool $use_str_to_lower, bool $use_transliterate): string + +Converts the string into an URL slug. This includes replacing non-ASCII +characters with their closest ASCII equivalents, removing remaining +non-ASCII and non-alphanumeric characters, and replacing whitespace with +$separator. The separator defaults to a single dash, and the string +is also converted to lowercase. The language of the source string can +also be supplied for language-specific transliteration. + +**Parameters:** +- `string $str` +- `string $separator [optional]

    The string used to replace whitespace.

    ` +- `ASCII::* $language [optional]

    Language of the source string. +(default is 'en') | ASCII::*_LANGUAGE_CODE

    ` +- `array $replacements [optional]

    A map of replaceable strings.

    ` +- `bool $replace_extra_symbols [optional]

    Add some more replacements e.g. "£" with " +pound ".

    ` +- `bool $use_str_to_lower [optional]

    Use "string to lower" for the input.

    ` +- `bool $use_transliterate [optional]

    Use ASCII::to_transliterate() for unknown +chars.

    ` + +**Return:** +- `string

    A string that has been converted to an URL slug.

    ` + +-------- + +#### to_transliterate(string $str, string|null $unknown, bool $strict): string + +Returns an ASCII version of the string. A set of non-ASCII characters are +replaced with their closest ASCII counterparts, and the rest are removed +unless instructed otherwise. + +EXAMPLE: +ASCII::to_transliterate('déjà σσς iıii'); // 'deja sss iiii' + + +**Parameters:** +- `string $str

    The input string.

    ` +- `string|null $unknown [optional]

    Character use if character unknown. (default is '?') +But you can also use NULL to keep the unknown chars.

    ` +- `bool $strict [optional]

    Use "transliterator_transliterate()" from PHP-Intl` + +**Return:** +- `string

    A String that contains only ASCII characters.

    ` + +-------- + + + +## Unit Test + +1) [Composer](https://getcomposer.org) is a prerequisite for running the tests. + +``` +composer install +``` + +2) The tests can be executed by running this command from the root directory: + +```bash +./vendor/bin/phpunit +``` + +### Support + +For support and donations please visit [Github](https://github.com/voku/portable-ascii/) | [Issues](https://github.com/voku/portable-ascii/issues) | [PayPal](https://paypal.me/moelleken) | [Patreon](https://www.patreon.com/voku). + +For status updates and release announcements please visit [Releases](https://github.com/voku/portable-ascii/releases) | [Twitter](https://twitter.com/suckup_de) | [Patreon](https://www.patreon.com/voku/posts). + +For professional support please contact [me](https://about.me/voku). + +### Thanks + +- Thanks to [GitHub](https://github.com) (Microsoft) for hosting the code and a good infrastructure including Issues-Managment, etc. +- Thanks to [IntelliJ](https://www.jetbrains.com) as they make the best IDEs for PHP and they gave me an open source license for PhpStorm! +- Thanks to [Travis CI](https://travis-ci.com/) for being the most awesome, easiest continous integration tool out there! +- Thanks to [StyleCI](https://styleci.io/) for the simple but powerful code style check. +- Thanks to [PHPStan](https://github.com/phpstan/phpstan) && [Psalm](https://github.com/vimeo/psalm) for really great Static analysis tools and for discover bugs in the code! + +### License and Copyright + +Released under the MIT License - see `LICENSE.txt` for details. diff --git a/vendor/voku/portable-ascii/composer.json b/vendor/voku/portable-ascii/composer.json new file mode 100644 index 0000000..99dee4f --- /dev/null +++ b/vendor/voku/portable-ascii/composer.json @@ -0,0 +1,37 @@ +{ + "name": "voku/portable-ascii", + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "type": "library", + "keywords": [ + "clean", + "php", + "ascii" + ], + "homepage": "https://github.com/voku/portable-ascii", + "license": "MIT", + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "http://www.moelleken.org/" + } + ], + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "autoload-dev": { + "psr-4": { + "voku\\tests\\": "tests/" + } + } +} diff --git a/vendor/voku/portable-ascii/src/voku/helper/ASCII.php b/vendor/voku/portable-ascii/src/voku/helper/ASCII.php new file mode 100644 index 0000000..7d8b66d --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/ASCII.php @@ -0,0 +1,1501 @@ +>|null + */ + private static $ASCII_MAPS; + + /** + * @var array>|null + */ + private static $ASCII_MAPS_AND_EXTRAS; + + /** + * @var array>|null + */ + private static $ASCII_EXTRAS; + + /** + * @var array|null + */ + private static $ORD; + + /** + * @var array|null + */ + private static $LANGUAGE_MAX_KEY; + + /** + * url: https://en.wikipedia.org/wiki/Wikipedia:ASCII#ASCII_printable_characters + * + * @var string + */ + private static $REGEX_ASCII = "[^\x09\x10\x13\x0A\x0D\x20-\x7E]"; + + /** + * bidirectional text chars + * + * url: https://www.w3.org/International/questions/qa-bidi-unicode-controls + * + * @var array + */ + private static $BIDI_UNI_CODE_CONTROLS_TABLE = [ + // LEFT-TO-RIGHT EMBEDDING (use -> dir = "ltr") + 8234 => "\xE2\x80\xAA", + // RIGHT-TO-LEFT EMBEDDING (use -> dir = "rtl") + 8235 => "\xE2\x80\xAB", + // POP DIRECTIONAL FORMATTING // (use -> ) + 8236 => "\xE2\x80\xAC", + // LEFT-TO-RIGHT OVERRIDE // (use -> ) + 8237 => "\xE2\x80\xAD", + // RIGHT-TO-LEFT OVERRIDE // (use -> ) + 8238 => "\xE2\x80\xAE", + // LEFT-TO-RIGHT ISOLATE // (use -> dir = "ltr") + 8294 => "\xE2\x81\xA6", + // RIGHT-TO-LEFT ISOLATE // (use -> dir = "rtl") + 8295 => "\xE2\x81\xA7", + // FIRST STRONG ISOLATE // (use -> dir = "auto") + 8296 => "\xE2\x81\xA8", + // POP DIRECTIONAL ISOLATE + 8297 => "\xE2\x81\xA9", + ]; + + /** + * Get all languages from the constants "ASCII::.*LANGUAGE_CODE". + * + * @return string[] + * + * @phpstan-return array + */ + public static function getAllLanguages(): array + { + // init + static $LANGUAGES = []; + + if ($LANGUAGES !== []) { + return $LANGUAGES; + } + + foreach ((new \ReflectionClass(__CLASS__))->getConstants() as $constant => $lang) { + if (\strpos($constant, 'EXTRA') !== false) { + $LANGUAGES[\strtolower($constant)] = $lang; + } else { + $LANGUAGES[\strtolower(\str_replace('_LANGUAGE_CODE', '', $constant))] = $lang; + } + } + + return $LANGUAGES; + } + + /** + * Returns an replacement array for ASCII methods. + * + * EXAMPLE: + * $array = ASCII::charsArray(); + * var_dump($array['ru']['б']); // 'b' + * + * + * @psalm-suppress InvalidNullableReturnType - we use the prepare* methods here, so we don't get NULL here + * + * @param bool $replace_extra_symbols [optional]

    Add some more replacements e.g. "£" with " pound ".

    + * + * @psalm-pure + * + * @return array + * + * @phpstan-return array> + */ + public static function charsArray(bool $replace_extra_symbols = false): array + { + if ($replace_extra_symbols) { + self::prepareAsciiAndExtrasMaps(); + + return self::$ASCII_MAPS_AND_EXTRAS ?? []; + } + + self::prepareAsciiMaps(); + + return self::$ASCII_MAPS ?? []; + } + + /** + * Returns an replacement array for ASCII methods with a mix of multiple languages. + * + * EXAMPLE: + * $array = ASCII::charsArrayWithMultiLanguageValues(); + * var_dump($array['b']); // ['β', 'б', 'ဗ', 'ბ', 'ب'] + * + * + * @param bool $replace_extra_symbols [optional]

    Add some more replacements e.g. "£" with " pound ".

    + * + * @psalm-pure + * + * @return array + *

    An array of replacements.

    + * + * @phpstan-return array> + */ + public static function charsArrayWithMultiLanguageValues(bool $replace_extra_symbols = false): array + { + /** @var array>> */ + static $CHARS_ARRAY = []; + $cacheKey = '' . $replace_extra_symbols; + + if (isset($CHARS_ARRAY[$cacheKey])) { + return $CHARS_ARRAY[$cacheKey]; + } + + // init + $return = []; + $language_all_chars = self::charsArrayWithSingleLanguageValues( + $replace_extra_symbols, + false + ); + + /** @noinspection PhpSillyAssignmentInspection - hack for phpstan */ + /** @var array $language_all_chars */ + $language_all_chars = $language_all_chars; + + /** @noinspection AlterInForeachInspection */ + foreach ($language_all_chars as $key => &$value) { + $return[$value][] = $key; + } + + $CHARS_ARRAY[$cacheKey] = $return; + + /** @var array> $return - hack for phpstan */ + return $return; + } + + /** + * Returns an replacement array for ASCII methods with one language. + * + * For example, German will map 'ä' to 'ae', while other languages + * will simply return e.g. 'a'. + * + * EXAMPLE: + * $array = ASCII::charsArrayWithOneLanguage('ru'); + * $tmpKey = \array_search('yo', $array['replace']); + * echo $array['orig'][$tmpKey]; // 'ё' + * + * + * @psalm-suppress InvalidNullableReturnType - we use the prepare* methods here, so we don't get NULL here + * + * @param string $language [optional]

    Language of the source string e.g.: en, de_at, or de-ch. + * (default is 'en') | ASCII::*_LANGUAGE_CODE

    + * @param bool $replace_extra_symbols [optional]

    Add some more replacements e.g. "£" with " pound ".

    + * @param bool $asOrigReplaceArray [optional]

    TRUE === return {orig: string[], replace: string[]} + * array

    + * + * @psalm-pure + * + * @return array + *

    An array of replacements.

    + * + * @phpstan-param ASCII::*_LANGUAGE_CODE $language + * @phpstan-return array{orig: string[], replace: string[]}|array + */ + public static function charsArrayWithOneLanguage( + string $language = self::ENGLISH_LANGUAGE_CODE, + bool $replace_extra_symbols = false, + bool $asOrigReplaceArray = true + ): array { + $language = self::get_language($language); + + // init + /** @var array|array{orig: string[], replace: string[]}>> */ + static $CHARS_ARRAY = []; + $cacheKey = '' . $replace_extra_symbols . '-' . $asOrigReplaceArray; + + // check static cache + if (isset($CHARS_ARRAY[$cacheKey][$language])) { + return $CHARS_ARRAY[$cacheKey][$language]; + } + + if ($replace_extra_symbols) { + self::prepareAsciiAndExtrasMaps(); + + /** @noinspection DuplicatedCode */ + if (isset(self::$ASCII_MAPS_AND_EXTRAS[$language])) { + $tmpArray = self::$ASCII_MAPS_AND_EXTRAS[$language]; + + if ($asOrigReplaceArray) { + $CHARS_ARRAY[$cacheKey][$language] = [ + 'orig' => \array_keys($tmpArray), + 'replace' => \array_values($tmpArray), + ]; + } else { + $CHARS_ARRAY[$cacheKey][$language] = $tmpArray; + } + } else { + /** @noinspection NestedPositiveIfStatementsInspection */ + if ($asOrigReplaceArray) { + $CHARS_ARRAY[$cacheKey][$language] = [ + 'orig' => [], + 'replace' => [], + ]; + } else { + $CHARS_ARRAY[$cacheKey][$language] = []; + } + } + } else { + self::prepareAsciiMaps(); + + /** @noinspection DuplicatedCode */ + if (isset(self::$ASCII_MAPS[$language])) { + $tmpArray = self::$ASCII_MAPS[$language]; + + if ($asOrigReplaceArray) { + $CHARS_ARRAY[$cacheKey][$language] = [ + 'orig' => \array_keys($tmpArray), + 'replace' => \array_values($tmpArray), + ]; + } else { + $CHARS_ARRAY[$cacheKey][$language] = $tmpArray; + } + } else { + /** @noinspection NestedPositiveIfStatementsInspection */ + if ($asOrigReplaceArray) { + $CHARS_ARRAY[$cacheKey][$language] = [ + 'orig' => [], + 'replace' => [], + ]; + } else { + $CHARS_ARRAY[$cacheKey][$language] = []; + } + } + } + + return $CHARS_ARRAY[$cacheKey][$language] ?? ['orig' => [], 'replace' => []]; + } + + /** + * Returns an replacement array for ASCII methods with multiple languages. + * + * EXAMPLE: + * $array = ASCII::charsArrayWithSingleLanguageValues(); + * $tmpKey = \array_search('hnaik', $array['replace']); + * echo $array['orig'][$tmpKey]; // '၌' + * + * + * @param bool $replace_extra_symbols [optional]

    Add some more replacements e.g. "£" with " pound ".

    + * @param bool $asOrigReplaceArray [optional]

    TRUE === return {orig: string[], replace: string[]} + * array

    + * + * @psalm-pure + * + * @return array + *

    An array of replacements.

    + * + * @phpstan-return array{orig: string[], replace: string[]}|array + */ + public static function charsArrayWithSingleLanguageValues( + bool $replace_extra_symbols = false, + bool $asOrigReplaceArray = true + ): array { + // init + /** @var array|array{orig: string[], replace: string[]}> */ + static $CHARS_ARRAY = []; + $cacheKey = '' . $replace_extra_symbols . '-' . $asOrigReplaceArray; + + if (isset($CHARS_ARRAY[$cacheKey])) { + return $CHARS_ARRAY[$cacheKey]; + } + + if ($replace_extra_symbols) { + self::prepareAsciiAndExtrasMaps(); + + /** @noinspection AlterInForeachInspection */ + /** @psalm-suppress PossiblyNullIterator - we use the prepare* methods here, so we don't get NULL here */ + foreach (self::$ASCII_MAPS_AND_EXTRAS ?? [] as &$map) { + $CHARS_ARRAY[$cacheKey][] = $map; + } + } else { + self::prepareAsciiMaps(); + + /** @noinspection AlterInForeachInspection */ + /** @psalm-suppress PossiblyNullIterator - we use the prepare* methods here, so we don't get NULL here */ + foreach (self::$ASCII_MAPS ?? [] as &$map) { + $CHARS_ARRAY[$cacheKey][] = $map; + } + } + + /** @phpstan-ignore-next-line - ... error? */ + $CHARS_ARRAY[$cacheKey] = \array_merge([], ...$CHARS_ARRAY[$cacheKey]); + + if ($asOrigReplaceArray) { + $CHARS_ARRAY[$cacheKey] = [ + 'orig' => \array_keys($CHARS_ARRAY[$cacheKey]), + 'replace' => \array_values($CHARS_ARRAY[$cacheKey]), + ]; + } + + return $CHARS_ARRAY[$cacheKey]; + } + + /** + * Accepts a string and removes all non-UTF-8 characters from it + extras if needed. + * + * @param string $str

    The string to be sanitized.

    + * @param bool $normalize_whitespace [optional]

    Set to true, if you need to normalize the + * whitespace.

    + * @param bool $normalize_msword [optional]

    Set to true, if you need to normalize MS Word chars + * e.g.: "…" + * => "..."

    + * @param bool $keep_non_breaking_space [optional]

    Set to true, to keep non-breaking-spaces, in + * combination with + * $normalize_whitespace

    + * @param bool $remove_invisible_characters [optional]

    Set to false, if you not want to remove invisible + * characters e.g.: "\0"

    + * + * @psalm-pure + * + * @return string + *

    A clean UTF-8 string.

    + */ + public static function clean( + string $str, + bool $normalize_whitespace = true, + bool $keep_non_breaking_space = false, + bool $normalize_msword = true, + bool $remove_invisible_characters = true + ): string { + // http://stackoverflow.com/questions/1401317/remove-non-utf8-characters-from-string + // caused connection reset problem on larger strings + + $regex = '/ + ( + (?: [\x00-\x7F] # single-byte sequences 0xxxxxxx + | [\xC0-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx + | [\xE0-\xEF][\x80-\xBF]{2} # triple-byte sequences 1110xxxx 10xxxxxx * 2 + | [\xF0-\xF7][\x80-\xBF]{3} # quadruple-byte sequence 11110xxx 10xxxxxx * 3 + ){1,100} # ...one or more times + ) + | ( [\x80-\xBF] ) # invalid byte in range 10000000 - 10111111 + | ( [\xC0-\xFF] ) # invalid byte in range 11000000 - 11111111 + /x'; + $str = (string) \preg_replace($regex, '$1', $str); + + if ($normalize_whitespace) { + $str = self::normalize_whitespace($str, $keep_non_breaking_space); + } + + if ($normalize_msword) { + $str = self::normalize_msword($str); + } + + if ($remove_invisible_characters) { + $str = self::remove_invisible_characters($str); + } + + return $str; + } + + /** + * Checks if a string is 7 bit ASCII. + * + * EXAMPLE: + * ASCII::is_ascii('白'); // false + * + * + * @param string $str

    The string to check.

    + * + * @psalm-pure + * + * @return bool + *

    + * true if it is ASCII
    + * false otherwise + *

    + */ + public static function is_ascii(string $str): bool + { + if ($str === '') { + return true; + } + + return !\preg_match('/' . self::$REGEX_ASCII . '/', $str); + } + + /** + * Returns a string with smart quotes, ellipsis characters, and dashes from + * Windows-1252 (commonly used in Word documents) replaced by their ASCII + * equivalents. + * + * EXAMPLE: + * ASCII::normalize_msword('„Abcdef…”'); // '"Abcdef..."' + * + * + * @param string $str

    The string to be normalized.

    + * + * @psalm-pure + * + * @return string + *

    A string with normalized characters for commonly used chars in Word documents.

    + */ + public static function normalize_msword(string $str): string + { + if ($str === '') { + return ''; + } + + /** @var array{orig: string[], replace: string[]} */ + static $MSWORD_CACHE = ['orig' => [], 'replace' => []]; + + if (empty($MSWORD_CACHE['orig'])) { + self::prepareAsciiMaps(); + + /** @var array */ + $map = self::$ASCII_MAPS[self::EXTRA_MSWORD_CHARS_LANGUAGE_CODE] ?? []; + + $MSWORD_CACHE = [ + 'orig' => \array_keys($map), + 'replace' => \array_values($map), + ]; + } + + return \str_replace($MSWORD_CACHE['orig'], $MSWORD_CACHE['replace'], $str); + } + + /** + * Normalize the whitespace. + * + * EXAMPLE: + * ASCII::normalize_whitespace("abc-\xc2\xa0-öäü-\xe2\x80\xaf-\xE2\x80\xAC", true); // "abc-\xc2\xa0-öäü- -" + * + * + * @param string $str

    The string to be normalized.

    + * @param bool $keepNonBreakingSpace [optional]

    Set to true, to keep non-breaking-spaces.

    + * @param bool $keepBidiUnicodeControls [optional]

    Set to true, to keep non-printable (for the web) + * bidirectional text chars.

    + * @param bool $normalize_control_characters [optional]

    Set to true, to convert e.g. LINE-, PARAGRAPH-SEPARATOR with "\n" and LINE TABULATION with "\t".

    + * + * @psalm-pure + * + * @return string + *

    A string with normalized whitespace.

    + */ + public static function normalize_whitespace( + string $str, + bool $keepNonBreakingSpace = false, + bool $keepBidiUnicodeControls = false, + bool $normalize_control_characters = false + ): string { + if ($str === '') { + return ''; + } + + /** @var array> */ + static $WHITESPACE_CACHE = []; + $cacheKey = (int) $keepNonBreakingSpace; + + if ($normalize_control_characters) { + $str = \str_replace( + [ + "\x0d\x0c", // 'END OF LINE' + "\xe2\x80\xa8", // 'LINE SEPARATOR' + "\xe2\x80\xa9", // 'PARAGRAPH SEPARATOR' + "\x0c", // 'FORM FEED' // "\f" + "\x0b", // 'VERTICAL TAB' // "\v" + ], + [ + "\n", + "\n", + "\n", + "\n", + "\t", + ], + $str + ); + } + + if (!isset($WHITESPACE_CACHE[$cacheKey])) { + self::prepareAsciiMaps(); + + $WHITESPACE_CACHE[$cacheKey] = self::$ASCII_MAPS[self::EXTRA_WHITESPACE_CHARS_LANGUAGE_CODE] ?? []; + + if ($keepNonBreakingSpace) { + unset($WHITESPACE_CACHE[$cacheKey]["\xc2\xa0"]); + } + + $WHITESPACE_CACHE[$cacheKey] = array_keys($WHITESPACE_CACHE[$cacheKey]); + } + + if (!$keepBidiUnicodeControls) { + /** @var array|null */ + static $BIDI_UNICODE_CONTROLS_CACHE = null; + + if ($BIDI_UNICODE_CONTROLS_CACHE === null) { + $BIDI_UNICODE_CONTROLS_CACHE = self::$BIDI_UNI_CODE_CONTROLS_TABLE; + } + + $str = \str_replace($BIDI_UNICODE_CONTROLS_CACHE, '', $str); + } + + return \str_replace($WHITESPACE_CACHE[$cacheKey], ' ', $str); + } + + /** + * Remove invisible characters from a string. + * + * e.g.: This prevents sandwiching null characters between ascii characters, like Java\0script. + * + * copy&past from https://github.com/bcit-ci/CodeIgniter/blob/develop/system/core/Common.php + * + * @param string $str + * @param bool $url_encoded + * @param string $replacement + * @param bool $keep_basic_control_characters + * + * @psalm-pure + * + * @return string + */ + public static function remove_invisible_characters( + string $str, + bool $url_encoded = false, + string $replacement = '', + bool $keep_basic_control_characters = true + ): string { + // init + $non_displayables = []; + + // every control character except: + // - newline (dec 10), + // - carriage return (dec 13), + // - horizontal tab (dec 09) + if ($url_encoded) { + $non_displayables[] = '/%0[0-8bcefBCEF]/'; // url encoded 00-08, 11, 12, 14, 15 + $non_displayables[] = '/%1[0-9a-fA-F]/'; // url encoded 16-31 + } + + if ($keep_basic_control_characters) { + $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 + } else { + $str = self::normalize_whitespace($str, false, false, true); + $non_displayables[] = '/[^\P{C}\s]/u'; + } + + do { + $str = (string) \preg_replace($non_displayables, $replacement, $str, -1, $count); + } while ($count !== 0); + + return $str; + } + + /** + * WARNING: This method will return broken characters and is only for special cases. + * + * Convert two UTF-8 encoded string to a single-byte strings suitable for + * functions that need the same string length after the conversion. + * + * The function simply uses (and updates) a tailored dynamic encoding + * (in/out map parameter) where non-ascii characters are remapped to + * the range [128-255] in order of appearance. + * + * @param string $str1 + * @param string $str2 + * + * @return string[] + * + * @phpstan-return array{0: string, 1: string} + */ + public static function to_ascii_remap(string $str1, string $str2): array + { + $charMap = []; + $str1 = self::to_ascii_remap_intern($str1, $charMap); + $str2 = self::to_ascii_remap_intern($str2, $charMap); + + return [$str1, $str2]; + } + + /** + * Returns an ASCII version of the string. A set of non-ASCII characters are + * replaced with their closest ASCII counterparts, and the rest are removed + * by default. The language or locale of the source string can be supplied + * for language-specific transliteration in any of the following formats: + * en, en_GB, or en-GB. For example, passing "de" results in "äöü" mapping + * to "aeoeue" rather than "aou" as in other languages. + * + * EXAMPLE: + * ASCII::to_ascii('�Düsseldorf�', 'en'); // Dusseldorf + * + * + * @param string $str

    The input string.

    + * @param string $language [optional]

    Language of the source string. + * (default is 'en') | ASCII::*_LANGUAGE_CODE

    + * @param bool $remove_unsupported_chars [optional]

    Whether or not to remove the + * unsupported characters.

    + * @param bool $replace_extra_symbols [optional]

    Add some more replacements e.g. "£" with " pound + * ".

    + * @param bool $use_transliterate [optional]

    Use ASCII::to_transliterate() for unknown chars.

    + * @param bool|null $replace_single_chars_only [optional]

    Single char replacement is better for the + * performance, but some languages need to replace more then one char + * at the same time. | NULL === auto-setting, depended on the + * language

    + * + * @psalm-pure + * + * @return string + *

    A string that contains only ASCII characters.

    + * + * @phpstan-param ASCII::*_LANGUAGE_CODE $language + */ + public static function to_ascii( + string $str, + string $language = self::ENGLISH_LANGUAGE_CODE, + bool $remove_unsupported_chars = true, + bool $replace_extra_symbols = false, + bool $use_transliterate = false, + bool $replace_single_chars_only = null + ): string { + if ($str === '') { + return ''; + } + + /** @phpstan-var ASCII::*_LANGUAGE_CODE - hack for phpstan */ + $language = self::get_language($language); + + static $EXTRA_SYMBOLS_CACHE = null; + + /** @var array> */ + static $REPLACE_HELPER_CACHE = []; + $cacheKey = $language . '-' . $replace_extra_symbols; + + if (!isset($REPLACE_HELPER_CACHE[$cacheKey])) { + $langAll = self::charsArrayWithSingleLanguageValues($replace_extra_symbols, false); + + $langSpecific = self::charsArrayWithOneLanguage($language, $replace_extra_symbols, false); + + if ($langSpecific === []) { + $REPLACE_HELPER_CACHE[$cacheKey] = $langAll; + } else { + $REPLACE_HELPER_CACHE[$cacheKey] = \array_merge([], $langAll, $langSpecific); + } + } + + if ( + $replace_extra_symbols + && + $EXTRA_SYMBOLS_CACHE === null + ) { + $EXTRA_SYMBOLS_CACHE = []; + foreach (self::$ASCII_EXTRAS ?? [] as $extrasDataTmp) { + foreach ($extrasDataTmp as $extrasDataKeyTmp => $extrasDataValueTmp) { + $EXTRA_SYMBOLS_CACHE[$extrasDataKeyTmp] = $extrasDataKeyTmp; + } + } + $EXTRA_SYMBOLS_CACHE = \implode('', $EXTRA_SYMBOLS_CACHE); + } + + $charDone = []; + if (\preg_match_all('/' . self::$REGEX_ASCII . ($replace_extra_symbols ? '|[' . $EXTRA_SYMBOLS_CACHE . ']' : '') . '/u', $str, $matches)) { + if (!$replace_single_chars_only) { + if (self::$LANGUAGE_MAX_KEY === null) { + self::$LANGUAGE_MAX_KEY = self::getData('ascii_language_max_key'); + } + + $maxKeyLength = self::$LANGUAGE_MAX_KEY[$language] ?? 0; + + if ($maxKeyLength >= 5) { + foreach ($matches[0] as $keyTmp => $char) { + if (isset($matches[0][$keyTmp + 4])) { + $fiveChars = $matches[0][$keyTmp + 0] . $matches[0][$keyTmp + 1] . $matches[0][$keyTmp + 2] . $matches[0][$keyTmp + 3] . $matches[0][$keyTmp + 4]; + } else { + $fiveChars = null; + } + if ( + $fiveChars + && + !isset($charDone[$fiveChars]) + && + isset($REPLACE_HELPER_CACHE[$cacheKey][$fiveChars]) + && + \strpos($str, $fiveChars) !== false + ) { + // DEBUG + //\var_dump($str, $fiveChars, $REPLACE_HELPER_CACHE[$cacheKey][$fiveChars]); + + $charDone[$fiveChars] = true; + $str = \str_replace($fiveChars, $REPLACE_HELPER_CACHE[$cacheKey][$fiveChars], $str); + + // DEBUG + //\var_dump($str, "\n"); + } + } + } + + if ($maxKeyLength >= 4) { + foreach ($matches[0] as $keyTmp => $char) { + if (isset($matches[0][$keyTmp + 3])) { + $fourChars = $matches[0][$keyTmp + 0] . $matches[0][$keyTmp + 1] . $matches[0][$keyTmp + 2] . $matches[0][$keyTmp + 3]; + } else { + $fourChars = null; + } + if ( + $fourChars + && + !isset($charDone[$fourChars]) + && + isset($REPLACE_HELPER_CACHE[$cacheKey][$fourChars]) + && + \strpos($str, $fourChars) !== false + ) { + // DEBUG + //\var_dump($str, $fourChars, $REPLACE_HELPER_CACHE[$cacheKey][$fourChars]); + + $charDone[$fourChars] = true; + $str = \str_replace($fourChars, $REPLACE_HELPER_CACHE[$cacheKey][$fourChars], $str); + + // DEBUG + //\var_dump($str, "\n"); + } + } + } + + foreach ($matches[0] as $keyTmp => $char) { + if (isset($matches[0][$keyTmp + 2])) { + $threeChars = $matches[0][$keyTmp + 0] . $matches[0][$keyTmp + 1] . $matches[0][$keyTmp + 2]; + } else { + $threeChars = null; + } + if ( + $threeChars + && + !isset($charDone[$threeChars]) + && + isset($REPLACE_HELPER_CACHE[$cacheKey][$threeChars]) + && + \strpos($str, $threeChars) !== false + ) { + // DEBUG + //\var_dump($str, $threeChars, $REPLACE_HELPER_CACHE[$cacheKey][$threeChars]); + + $charDone[$threeChars] = true; + $str = \str_replace($threeChars, $REPLACE_HELPER_CACHE[$cacheKey][$threeChars], $str); + + // DEBUG + //\var_dump($str, "\n"); + } + } + + foreach ($matches[0] as $keyTmp => $char) { + if (isset($matches[0][$keyTmp + 1])) { + $twoChars = $matches[0][$keyTmp + 0] . $matches[0][$keyTmp + 1]; + } else { + $twoChars = null; + } + if ( + $twoChars + && + !isset($charDone[$twoChars]) + && + isset($REPLACE_HELPER_CACHE[$cacheKey][$twoChars]) + && + \strpos($str, $twoChars) !== false + ) { + // DEBUG + //\var_dump($str, $twoChars, $REPLACE_HELPER_CACHE[$cacheKey][$twoChars]); + + $charDone[$twoChars] = true; + $str = \str_replace($twoChars, $REPLACE_HELPER_CACHE[$cacheKey][$twoChars], $str); + + // DEBUG + //\var_dump($str, "\n"); + } + } + } + + foreach ($matches[0] as $char) { + if ( + !isset($charDone[$char]) + && + isset($REPLACE_HELPER_CACHE[$cacheKey][$char]) + && + \strpos($str, $char) !== false + ) { + // DEBUG + //\var_dump($str, $char, $REPLACE_HELPER_CACHE[$cacheKey][$char]); + + $charDone[$char] = true; + $str = \str_replace($char, $REPLACE_HELPER_CACHE[$cacheKey][$char], $str); + + // DEBUG + //\var_dump($str, "\n"); + } + } + } + + /** @psalm-suppress PossiblyNullOperand - we use the prepare* methods here, so we don't get NULL here */ + if (!isset(self::$ASCII_MAPS[$language])) { + $use_transliterate = true; + } + + if ($use_transliterate) { + $str = self::to_transliterate($str, null, false); + } + + if ($remove_unsupported_chars) { + $str = (string) \str_replace(["\n\r", "\n", "\r", "\t"], ' ', $str); + $str = (string) \preg_replace('/' . self::$REGEX_ASCII . '/', '', $str); + } + + return $str; + } + + /** + * Convert given string to safe filename (and keep string case). + * + * EXAMPLE: + * ASCII::to_filename('שדגשדג.png', true)); // 'shdgshdg.png' + * + * + * @param string $str + * @param bool $use_transliterate

    ASCII::to_transliterate() is used by default - unsafe characters are + * simply replaced with hyphen otherwise.

    + * @param string $fallback_char + * + * @psalm-pure + * + * @return string + *

    A string that contains only safe characters for a filename.

    + */ + public static function to_filename( + string $str, + bool $use_transliterate = true, + string $fallback_char = '-' + ): string { + if ($use_transliterate) { + $str = self::to_transliterate($str, $fallback_char); + } + + $fallback_char_escaped = \preg_quote($fallback_char, '/'); + + $str = (string) \preg_replace( + [ + '/[^' . $fallback_char_escaped . '.\\-a-zA-Z\d\\s]/', // 1) remove un-needed chars + '/\s+/u', // 2) convert spaces to $fallback_char + '/[' . $fallback_char_escaped . ']+/u', // 3) remove double $fallback_char's + ], + [ + '', + $fallback_char, + $fallback_char, + ], + $str + ); + + return \trim($str, $fallback_char); + } + + /** + * Converts the string into an URL slug. This includes replacing non-ASCII + * characters with their closest ASCII equivalents, removing remaining + * non-ASCII and non-alphanumeric characters, and replacing whitespace with + * $separator. The separator defaults to a single dash, and the string + * is also converted to lowercase. The language of the source string can + * also be supplied for language-specific transliteration. + * + * @param string $str + * @param string $separator [optional]

    The string used to replace whitespace.

    + * @param string $language [optional]

    Language of the source string. + * (default is 'en') | ASCII::*_LANGUAGE_CODE

    + * @param array $replacements [optional]

    A map of replaceable strings.

    + * @param bool $replace_extra_symbols [optional]

    Add some more replacements e.g. "£" with " + * pound ".

    + * @param bool $use_str_to_lower [optional]

    Use "string to lower" for the input.

    + * @param bool $use_transliterate [optional]

    Use ASCII::to_transliterate() for unknown + * chars.

    + * @psalm-pure + * + * @return string + *

    A string that has been converted to an URL slug.

    + * + * @phpstan-param ASCII::*_LANGUAGE_CODE $language + */ + public static function to_slugify( + string $str, + string $separator = '-', + string $language = self::ENGLISH_LANGUAGE_CODE, + array $replacements = [], + bool $replace_extra_symbols = false, + bool $use_str_to_lower = true, + bool $use_transliterate = false + ): string { + if ($str === '') { + return ''; + } + + foreach ($replacements as $from => $to) { + $str = \str_replace($from, $to, $str); + } + + $str = self::to_ascii( + $str, + $language, + false, + $replace_extra_symbols, + $use_transliterate + ); + + $str = \str_replace('@', $separator, $str); + + $str = (string) \preg_replace( + '/[^a-zA-Z\\d\\s\\-_' . \preg_quote($separator, '/') . ']/', + '', + $str + ); + + if ($use_str_to_lower) { + $str = \strtolower($str); + } + + $str = (string) \preg_replace('/^[\'\\s]+|[\'\\s]+$/', '', $str); + $str = (string) \preg_replace('/\\B([A-Z])/', '-\1', $str); + $str = (string) \preg_replace('/[\\-_\\s]+/', $separator, $str); + + $l = \strlen($separator); + if ($l && \strpos($str, $separator) === 0) { + $str = (string) \substr($str, $l); + } + + if (\substr($str, -$l) === $separator) { + $str = (string) \substr($str, 0, \strlen($str) - $l); + } + + return $str; + } + + /** + * Returns an ASCII version of the string. A set of non-ASCII characters are + * replaced with their closest ASCII counterparts, and the rest are removed + * unless instructed otherwise. + * + * EXAMPLE: + * ASCII::to_transliterate('déjà σσς iıii'); // 'deja sss iiii' + * + * + * @param string $str

    The input string.

    + * @param string|null $unknown [optional]

    Character use if character unknown. (default is '?') + * But you can also use NULL to keep the unknown chars.

    + * @param bool $strict [optional]

    Use "transliterator_transliterate()" from PHP-Intl + * + * @psalm-pure + * + * @return string + *

    A String that contains only ASCII characters.

    + * + * @noinspection ParameterDefaultValueIsNotNullInspection + */ + public static function to_transliterate( + string $str, + $unknown = '?', + bool $strict = false + ): string { + /** @var array|null */ + static $UTF8_TO_TRANSLIT = null; + + /** null|\Transliterator */ + static $TRANSLITERATOR = null; + + /** @var bool|null */ + static $SUPPORT_INTL = null; + + if ($str === '') { + return ''; + } + + if ($SUPPORT_INTL === null) { + $SUPPORT_INTL = \extension_loaded('intl'); + } + + // check if we only have ASCII, first (better performance) + $str_tmp = $str; + if (self::is_ascii($str)) { + return $str; + } + + $str = self::clean($str); + + // check again, if we only have ASCII, now ... + if ( + $str_tmp !== $str + && + self::is_ascii($str) + ) { + return $str; + } + + if ( + $strict + && + $SUPPORT_INTL === true + ) { + if (!isset($TRANSLITERATOR)) { + // INFO: see "*-Latin" rules via "transliterator_list_ids()" + /** @var \Transliterator */ + $TRANSLITERATOR = \transliterator_create('NFKC; [:Nonspacing Mark:] Remove; NFKC; Any-Latin; Latin-ASCII;'); + } + + // INFO: https://unicode.org/cldr/utility/character.jsp + $str_tmp = \transliterator_transliterate($TRANSLITERATOR, $str); + + if ($str_tmp !== false) { + + // check again, if we only have ASCII, now ... + if ( + $str_tmp !== $str + && + self::is_ascii($str_tmp) + ) { + return $str_tmp; + } + + $str = $str_tmp; + } + } + + if (self::$ORD === null) { + self::$ORD = self::getData('ascii_ord'); + } + + \preg_match_all('/.|[^\x00]$/us', $str, $array_tmp); + $chars = $array_tmp[0]; + $ord = null; + $str_tmp = ''; + foreach ($chars as &$c) { + $ordC0 = self::$ORD[$c[0]]; + + if ($ordC0 >= 0 && $ordC0 <= 127) { + $str_tmp .= $c; + + continue; + } + + $ordC1 = self::$ORD[$c[1]]; + + // ASCII - next please + if ($ordC0 >= 192 && $ordC0 <= 223) { + $ord = ($ordC0 - 192) * 64 + ($ordC1 - 128); + } + + if ($ordC0 >= 224) { + $ordC2 = self::$ORD[$c[2]]; + + if ($ordC0 <= 239) { + $ord = ($ordC0 - 224) * 4096 + ($ordC1 - 128) * 64 + ($ordC2 - 128); + } + + if ($ordC0 >= 240) { + $ordC3 = self::$ORD[$c[3]]; + + if ($ordC0 <= 247) { + $ord = ($ordC0 - 240) * 262144 + ($ordC1 - 128) * 4096 + ($ordC2 - 128) * 64 + ($ordC3 - 128); + } + + // We only process valid UTF-8 chars (<= 4 byte), so we don't need this code here ... + /* + if ($ordC0 >= 248) { + $ordC4 = self::$ORD[$c[4]]; + + if ($ordC0 <= 251) { + $ord = ($ordC0 - 248) * 16777216 + ($ordC1 - 128) * 262144 + ($ordC2 - 128) * 4096 + ($ordC3 - 128) * 64 + ($ordC4 - 128); + } + + if ($ordC0 >= 252) { + $ordC5 = self::$ORD[$c[5]]; + + if ($ordC0 <= 253) { + $ord = ($ordC0 - 252) * 1073741824 + ($ordC1 - 128) * 16777216 + ($ordC2 - 128) * 262144 + ($ordC3 - 128) * 4096 + ($ordC4 - 128) * 64 + ($ordC5 - 128); + } + } + } + */ + } + } + + if ( + $ordC0 === 254 + || + $ordC0 === 255 + || + $ord === null + ) { + $str_tmp .= $unknown ?? $c; + + continue; + } + + $bank = $ord >> 8; + if (!isset($UTF8_TO_TRANSLIT[$bank])) { + $UTF8_TO_TRANSLIT[$bank] = self::getDataIfExists(\sprintf('x%03x', $bank)); + } + + $new_char = $ord & 255; + + if (isset($UTF8_TO_TRANSLIT[$bank][$new_char])) { + + // keep for debugging + /* + echo "file: " . sprintf('x%02x', $bank) . "\n"; + echo "char: " . $c . "\n"; + echo "ord: " . $ord . "\n"; + echo "new_char: " . $new_char . "\n"; + echo "new_char: " . mb_chr($new_char) . "\n"; + echo "ascii: " . $UTF8_TO_TRANSLIT[$bank][$new_char] . "\n"; + echo "bank:" . $bank . "\n\n"; + */ + + $new_char = $UTF8_TO_TRANSLIT[$bank][$new_char]; + + /** @noinspection MissingOrEmptyGroupStatementInspection */ + /** @noinspection PhpStatementHasEmptyBodyInspection */ + if ($unknown === null && $new_char === '') { + // nothing + } elseif ( + $new_char === '[?]' + || + $new_char === '[?] ' + ) { + $c = $unknown ?? $c; + } else { + $c = $new_char; + } + } else { + + // keep for debugging missing chars + /* + echo "file: " . sprintf('x%02x', $bank) . "\n"; + echo "char: " . $c . "\n"; + echo "ord: " . $ord . "\n"; + echo "new_char: " . $new_char . "\n"; + echo "new_char: " . mb_chr($new_char) . "\n"; + echo "bank:" . $bank . "\n\n"; + */ + + $c = $unknown ?? $c; + } + + $str_tmp .= $c; + } + + return $str_tmp; + } + + /** + * WARNING: This method will return broken characters and is only for special cases. + * + * Convert a UTF-8 encoded string to a single-byte string suitable for + * functions that need the same string length after the conversion. + * + * The function simply uses (and updates) a tailored dynamic encoding + * (in/out map parameter) where non-ascii characters are remapped to + * the range [128-255] in order of appearance. + * + * Thus, it supports up to 128 different multibyte code points max over + * the whole set of strings sharing this encoding. + * + * Source: https://github.com/KEINOS/mb_levenshtein + * + * @param string $str

    UTF-8 string to be converted to extended ASCII.

    + * @param array $map

    Internal-Map of code points to ASCII characters.

    + * + * @return string + *

    Mapped borken string.

    + * + * @phpstan-param array $map + */ + private static function to_ascii_remap_intern(string $str, array &$map): string + { + // find all utf-8 characters + $matches = []; + if (!\preg_match_all('/[\xC0-\xF7][\x80-\xBF]+/', $str, $matches)) { + return $str; // plain ascii string + } + + // update the encoding map with the characters not already met + $mapCount = \count($map); + foreach ($matches[0] as $mbc) { + if (!isset($map[$mbc])) { + $map[$mbc] = \chr(128 + $mapCount); + ++$mapCount; + } + } + + // finally, remap non-ascii characters + return \strtr($str, $map); + } + + /** + * Get the language from a string. + * + * e.g.: de_at -> de_at + * de_DE -> de + * DE_DE -> de + * de-de -> de + * + * @noinspection ReturnTypeCanBeDeclaredInspection + * + * @param string $language + * + * @psalm-pure + * + * @return string + */ + private static function get_language(string $language) + { + if ($language === '') { + return ''; + } + + if ( + \strpos($language, '_') === false + && + \strpos($language, '-') === false + ) { + return \strtolower($language); + } + + $language = \str_replace('-', '_', \strtolower($language)); + + $regex = '/(?[a-z]+)_\g{first}/'; + + return (string) \preg_replace($regex, '$1', $language); + } + + /** + * Get data from "/data/*.php". + * + * @noinspection ReturnTypeCanBeDeclaredInspection + * + * @param string $file + * + * @psalm-pure + * + * @return array + */ + private static function getData(string $file) + { + /** @noinspection PhpIncludeInspection */ + /** @noinspection UsingInclusionReturnValueInspection */ + /** @psalm-suppress UnresolvableInclude */ + return include __DIR__ . '/data/' . $file . '.php'; + } + + /** + * Get data from "/data/*.php". + * + * @param string $file + * + * @psalm-pure + * + * @return array + */ + private static function getDataIfExists(string $file): array + { + $file = __DIR__ . '/data/' . $file . '.php'; + /** @psalm-suppress ImpureFunctionCall */ + if (\is_file($file)) { + /** @noinspection PhpIncludeInspection */ + /** @noinspection UsingInclusionReturnValueInspection */ + /** @psalm-suppress UnresolvableInclude */ + return include $file; + } + + return []; + } + + /** + * @psalm-pure + * + * @return void + */ + private static function prepareAsciiAndExtrasMaps() + { + if (self::$ASCII_MAPS_AND_EXTRAS === null) { + self::prepareAsciiMaps(); + self::prepareAsciiExtras(); + + /** @psalm-suppress PossiblyNullArgument - we use the prepare* methods here, so we don't get NULL here */ + self::$ASCII_MAPS_AND_EXTRAS = \array_merge_recursive( + self::$ASCII_MAPS ?? [], + self::$ASCII_EXTRAS ?? [] + ); + } + } + + /** + * @psalm-pure + * + * @return void + */ + private static function prepareAsciiMaps() + { + if (self::$ASCII_MAPS === null) { + self::$ASCII_MAPS = self::getData('ascii_by_languages'); + } + } + + /** + * @psalm-pure + * + * @return void + */ + private static function prepareAsciiExtras() + { + if (self::$ASCII_EXTRAS === null) { + self::$ASCII_EXTRAS = self::getData('ascii_extras_by_languages'); + } + } +} diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/ascii_by_languages.php b/vendor/voku/portable-ascii/src/voku/helper/data/ascii_by_languages.php new file mode 100644 index 0000000..68c3f9d --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/ascii_by_languages.php @@ -0,0 +1,2950 @@ + [ + 'Á' => 'A', + 'á' => 'a', + 'Ä' => 'A', + 'ä' => 'a', + 'À' => 'A', + 'à' => 'a', + 'Â' => 'A', + 'â' => 'a', + 'É' => 'E', + 'é' => 'e', + 'Ë' => 'E', + 'ë' => 'e', + 'È' => 'E', + 'è' => 'e', + 'Ê' => 'E', + 'ê' => 'e', + 'Í' => 'I', + 'í' => 'i', + 'Ï' => 'I', + 'ï' => 'i', + 'Ì' => 'I', + 'ì' => 'i', + 'Î' => 'I', + 'î' => 'i', + 'Ó' => 'O', + 'ó' => 'o', + 'Ö' => 'O', + 'ö' => 'o', + 'Ò' => 'O', + 'ò' => 'o', + 'Ô' => 'O', + 'ô' => 'o', + 'Ú' => 'U', + 'ú' => 'u', + 'Ü' => 'U', + 'ü' => 'u', + 'Ù' => 'U', + 'ù' => 'u', + 'Û' => 'U', + 'û' => 'u', + 'Ý' => 'Y', + 'ý' => 'y', + 'Ÿ' => 'Y', + ], + // Italian + 'it' => [ + 'à' => 'a', + 'À' => 'A', + 'é' => 'e', + 'É' => 'E', + 'è' => 'e', + 'È' => 'E', + 'ì' => 'i', + 'Ì' => 'I', + 'Ò' => 'O', + 'ò' => 'o', + 'ù' => 'u', + 'Ù' => 'U', + ], + // Macedonian + 'mk' => [ + 'А' => 'A', + 'Б' => 'B', + 'В' => 'V', + 'Г' => 'G', + 'Д' => 'D', + 'Ѓ' => 'Gj', + 'Е' => 'E', + 'Ж' => 'Zh', + 'З' => 'Z', + 'Ѕ' => 'Dz', + 'И' => 'I', + 'Ј' => 'J', + 'К' => 'K', + 'Л' => 'L', + 'Љ' => 'Lj', + 'М' => 'M', + 'Н' => 'N', + 'Њ' => 'Nj', + 'О' => 'O', + 'П' => 'P', + 'Р' => 'R', + 'С' => 'S', + 'Т' => 'T', + 'Ќ' => 'Kj', + 'У' => 'U', + 'Ф' => 'F', + 'Х' => 'H', + 'Ц' => 'C', + 'Ч' => 'Ch', + 'Џ' => 'Dj', + 'Ш' => 'Sh', + 'а' => 'a', + 'б' => 'b', + 'в' => 'v', + 'г' => 'g', + 'д' => 'd', + 'ѓ' => 'gj', + 'е' => 'e', + 'ж' => 'zh', + 'з' => 'z', + 'ѕ' => 'dz', + 'и' => 'i', + 'ј' => 'j', + 'к' => 'k', + 'л' => 'l', + 'љ' => 'lj', + 'м' => 'm', + 'н' => 'n', + 'њ' => 'nj', + 'о' => 'o', + 'п' => 'p', + 'р' => 'r', + 'с' => 's', + 'т' => 't', + 'ќ' => 'kj', + 'у' => 'u', + 'ф' => 'f', + 'х' => 'h', + 'ц' => 'c', + 'ч' => 'ch', + 'џ' => 'dj', + 'ш' => 'sh', + ], + // Portuguese (Brazil) + 'pt' => [ + 'æ' => 'ae', + 'ǽ' => 'ae', + 'À' => 'A', + 'Á' => 'A', + 'Â' => 'A', + 'Ã' => 'A', + 'Å' => 'AA', + 'Ǻ' => 'A', + 'Ă' => 'A', + 'Ǎ' => 'A', + 'Æ' => 'AE', + 'Ǽ' => 'AE', + 'à' => 'a', + 'á' => 'a', + 'â' => 'a', + 'ã' => 'a', + 'å' => 'aa', + 'ǻ' => 'a', + 'ă' => 'a', + 'ǎ' => 'a', + 'ª' => 'a', + 'Ĉ' => 'C', + 'Ċ' => 'C', + 'Ç' => 'C', + 'ç' => 'c', + 'ĉ' => 'c', + 'ċ' => 'c', + 'Ð' => 'Dj', + 'Đ' => 'D', + 'ð' => 'dj', + 'đ' => 'd', + 'È' => 'E', + 'É' => 'E', + 'Ê' => 'E', + 'Ë' => 'E', + 'Ĕ' => 'E', + 'Ė' => 'E', + 'è' => 'e', + 'é' => 'e', + 'ê' => 'e', + 'ë' => 'e', + 'ĕ' => 'e', + 'ė' => 'e', + 'ƒ' => 'f', + 'Ĝ' => 'G', + 'Ġ' => 'G', + 'ĝ' => 'g', + 'ġ' => 'g', + 'Ĥ' => 'H', + 'Ħ' => 'H', + 'ĥ' => 'h', + 'ħ' => 'h', + 'Ì' => 'I', + 'Í' => 'I', + 'Î' => 'I', + 'Ï' => 'I', + 'Ĩ' => 'I', + 'Ĭ' => 'I', + 'Ǐ' => 'I', + 'Į' => 'I', + 'IJ' => 'IJ', + 'ì' => 'i', + 'í' => 'i', + 'î' => 'i', + 'ï' => 'i', + 'ĩ' => 'i', + 'ĭ' => 'i', + 'ǐ' => 'i', + 'į' => 'i', + 'ij' => 'ij', + 'Ĵ' => 'J', + 'ĵ' => 'j', + 'Ĺ' => 'L', + 'Ľ' => 'L', + 'Ŀ' => 'L', + 'ĺ' => 'l', + 'ľ' => 'l', + 'ŀ' => 'l', + 'Ñ' => 'N', + 'ñ' => 'n', + 'ʼn' => 'n', + 'Ò' => 'O', + 'Ó' => 'O', + 'Ô' => 'O', + 'Õ' => 'O', + 'Ō' => 'O', + 'Ŏ' => 'O', + 'Ǒ' => 'O', + 'Ő' => 'O', + 'Ơ' => 'O', + 'Ø' => 'OE', + 'Ǿ' => 'O', + 'Œ' => 'OE', + 'ò' => 'o', + 'ó' => 'o', + 'ô' => 'o', + 'õ' => 'o', + 'ō' => 'o', + 'ŏ' => 'o', + 'ǒ' => 'o', + 'ő' => 'o', + 'ơ' => 'o', + 'ø' => 'oe', + 'ǿ' => 'o', + 'º' => 'o', + 'œ' => 'oe', + 'Ŕ' => 'R', + 'Ŗ' => 'R', + 'ŕ' => 'r', + 'ŗ' => 'r', + 'Ŝ' => 'S', + 'Ș' => 'S', + 'ŝ' => 's', + 'ș' => 's', + 'ſ' => 's', + 'Ţ' => 'T', + 'Ț' => 'T', + 'Ŧ' => 'T', + 'Þ' => 'TH', + 'ţ' => 't', + 'ț' => 't', + 'ŧ' => 't', + 'þ' => 'th', + 'Ù' => 'U', + 'Ú' => 'U', + 'Û' => 'U', + 'Ü' => 'U', + 'Ũ' => 'U', + 'Ŭ' => 'U', + 'Ű' => 'U', + 'Ų' => 'U', + 'Ư' => 'U', + 'Ǔ' => 'U', + 'Ǖ' => 'U', + 'Ǘ' => 'U', + 'Ǚ' => 'U', + 'Ǜ' => 'U', + 'ù' => 'u', + 'ú' => 'u', + 'û' => 'u', + 'ü' => 'u', + 'ũ' => 'u', + 'ŭ' => 'u', + 'ű' => 'u', + 'ų' => 'u', + 'ư' => 'u', + 'ǔ' => 'u', + 'ǖ' => 'u', + 'ǘ' => 'u', + 'ǚ' => 'u', + 'ǜ' => 'u', + 'Ŵ' => 'W', + 'ŵ' => 'w', + 'Ý' => 'Y', + 'Ÿ' => 'Y', + 'Ŷ' => 'Y', + 'ý' => 'y', + 'ÿ' => 'y', + 'ŷ' => 'y', + ], + // Greek(lish) (Elláda) + 'el__greeklish' => [ + 'ΑΥ' => 'AU', + 'ΑΎ' => 'AU', + 'Αυ' => 'Au', + 'Αύ' => 'Au', + 'ΕΊ' => 'EI', + 'ΕΙ' => 'EI', + 'Ει' => 'EI', + 'ΕΥ' => 'EU', + 'ΕΎ' => 'EU', + 'Εί' => 'Ei', + 'Ευ' => 'Eu', + 'Εύ' => 'Eu', + 'ΟΙ' => 'OI', + 'ΟΊ' => 'OI', + 'ΟΥ' => 'OU', + 'ΟΎ' => 'OU', + 'Οι' => 'Oi', + 'Οί' => 'Oi', + 'Ου' => 'Ou', + 'Ού' => 'Ou', + 'ΥΙ' => 'YI', + 'ΎΙ' => 'YI', + 'Υι' => 'Yi', + 'Ύι' => 'Yi', + 'ΥΊ' => 'Yi', + 'Υί' => 'Yi', + 'αυ' => 'au', + 'αύ' => 'au', + 'εί' => 'ei', + 'ει' => 'ei', + 'ευ' => 'eu', + 'εύ' => 'eu', + 'οι' => 'oi', + 'οί' => 'oi', + 'ου' => 'ou', + 'ού' => 'ou', + 'υι' => 'yi', + 'ύι' => 'yi', + 'υί' => 'yi', + 'Α' => 'A', + 'Ά' => 'A', + 'Β' => 'B', + 'Δ' => 'D', + 'Ε' => 'E', + 'Έ' => 'E', + 'Φ' => 'F', + 'Γ' => 'G', + 'Η' => 'H', + 'Ή' => 'H', + 'Ι' => 'I', + 'Ί' => 'I', + 'Ϊ' => 'I', + 'Κ' => 'K', + 'Ξ' => 'Ks', + 'Λ' => 'L', + 'Μ' => 'M', + 'Ν' => 'N', + 'Π' => 'N', + 'Ο' => 'O', + 'Ό' => 'O', + 'Ψ' => 'Ps', + 'Ρ' => 'R', + 'Σ' => 'S', + 'Τ' => 'T', + 'Θ' => 'Th', + 'Ω' => 'W', + 'Ώ' => 'W', + 'Χ' => 'X', + 'ϒ' => 'Y', + 'Υ' => 'Y', + 'Ύ' => 'Y', + 'Ϋ' => 'Y', + 'Ζ' => 'Z', + 'α' => 'a', + 'ά' => 'a', + 'β' => 'b', + 'δ' => 'd', + 'ε' => 'e', + 'έ' => 'e', + 'φ' => 'f', + 'γ' => 'g', + 'η' => 'h', + 'ή' => 'h', + 'ι' => 'i', + 'ί' => 'i', + 'ϊ' => 'i', + 'ΐ' => 'i', + 'κ' => 'k', + 'ξ' => 'ks', + 'λ' => 'l', + 'μ' => 'm', + 'ν' => 'n', + 'ο' => 'o', + 'ό' => 'o', + 'π' => 'p', + 'ψ' => 'ps', + 'ρ' => 'r', + 'σ' => 's', + 'ς' => 's', + 'τ' => 't', + 'ϑ' => 'th', + 'θ' => 'th', + 'ϐ' => 'v', + 'ω' => 'w', + 'ώ' => 'w', + 'χ' => 'x', + 'υ' => 'y', + 'ύ' => 'y', + 'ΰ' => 'y', + 'ϋ' => 'y', + 'ζ' => 'z', + ], + // Greek (Elláda) + 'el' => [ + 'ΑΥ' => 'AU', + 'Αυ' => 'Au', + 'ΟΥ' => 'U', + 'Ου' => 'u', + 'ΕΥ' => 'EF', + 'Ευ' => 'Ef', + 'ΕΙ' => 'I', + 'Ει' => 'I', + 'ΟΙ' => 'I', + 'Οι' => 'I', + 'ΥΙ' => 'I', + 'Υι' => 'I', + 'ΑΎ' => 'AU', + 'Αύ' => 'Au', + 'ΟΎ' => 'OU', + 'Ού' => 'Ou', + 'ΕΎ' => 'EU', + 'Εύ' => 'Eu', + 'ΕΊ' => 'I', + 'Εί' => 'I', + 'ΟΊ' => 'I', + 'Οί' => 'I', + 'ΎΙ' => 'I', + 'Ύι' => 'I', + 'ΥΊ' => 'I', + 'Υί' => 'I', + 'αυ' => 'au', + 'ου' => 'u', + 'ευ' => 'ef', + 'ει' => 'i', + 'οι' => 'i', + 'υι' => 'i', + 'αύ' => 'au', + 'ού' => 'ou', + 'εύ' => 'eu', + 'εί' => 'i', + 'οί' => 'i', + 'ύι' => 'i', + 'υί' => 'i', + 'α' => 'a', + 'β' => 'v', + 'γ' => 'gh', + 'δ' => 'd', + 'ε' => 'e', + 'ζ' => 'z', + 'η' => 'i', + 'θ' => 'th', + 'ι' => 'i', + 'κ' => 'k', + 'λ' => 'l', + 'μ' => 'm', + 'ν' => 'n', + 'ξ' => 'ks', + 'ο' => 'o', + 'π' => 'p', + 'ρ' => 'r', + 'σ' => 's', + 'τ' => 't', + 'υ' => 'i', + 'φ' => 'f', + 'χ' => 'kh', + 'ψ' => 'ps', + 'ω' => 'o', + 'ά' => 'a', + 'έ' => 'e', + 'ί' => 'i', + 'ό' => 'o', + 'ϒ' => 'Y', + 'ύ' => 'y', + 'ή' => 'i', + 'ώ' => 'w', + 'ς' => 's', + 'ϊ' => 'i', + 'ΰ' => 'y', + 'ϋ' => 'y', + 'ΐ' => 'i', + 'Α' => 'A', + 'Β' => 'B', + 'Γ' => 'G', + 'Δ' => 'D', + 'Ε' => 'E', + 'Ζ' => 'Z', + 'Η' => 'H', + 'Θ' => 'Th', + 'Ι' => 'I', + 'Κ' => 'K', + 'Λ' => 'L', + 'Μ' => 'M', + 'Ν' => 'N', + 'Ξ' => 'Ks', + 'Ο' => 'O', + 'Π' => 'P', + 'Ρ' => 'R', + 'Σ' => 'S', + 'Τ' => 'T', + 'Υ' => 'Y', + 'Φ' => 'F', + 'Χ' => 'X', + 'Ψ' => 'Ps', + 'Ω' => 'O', + 'Ά' => 'A', + 'Έ' => 'E', + 'Ί' => 'I', + 'Ό' => 'O', + 'Ύ' => 'Y', + 'Ή' => 'I', + 'Ώ' => 'W', + 'Ϊ' => 'I', + 'Ϋ' => 'Y', + 'ϐ' => 'v', + 'ϑ' => 'th', + ], + // Hindi + 'hi' => [ + 'अ' => 'a', + 'आ' => 'aa', + 'ए' => 'e', + 'ई' => 'ii', + 'ऍ' => 'ei', + 'ऎ' => 'ae', + 'ऐ' => 'ai', + 'इ' => 'i', + 'ओ' => 'o', + 'ऑ' => 'oi', + 'ऒ' => 'oii', + 'ऊ' => 'uu', + 'औ' => 'ou', + 'उ' => 'u', + 'ब' => 'B', + 'भ' => 'Bha', + 'च' => 'Ca', + 'छ' => 'Chha', + 'ड' => 'Da', + 'ढ' => 'Dha', + 'फ' => 'Fa', + 'फ़' => 'Fi', + 'ग' => 'Ga', + 'घ' => 'Gha', + 'ग़' => 'Ghi', + 'ह' => 'Ha', + 'ज' => 'Ja', + 'झ' => 'Jha', + 'क' => 'Ka', + 'ख' => 'Kha', + 'ख़' => 'Khi', + 'ल' => 'L', + 'ळ' => 'Li', + 'ऌ' => 'Li', + 'ऴ' => 'Lii', + 'ॡ' => 'Lii', + 'म' => 'Ma', + 'न' => 'Na', + 'ङ' => 'Na', + 'ञ' => 'Nia', + 'ण' => 'Nae', + 'ऩ' => 'Ni', + 'ॐ' => 'oms', + 'प' => 'Pa', + 'क़' => 'Qi', + 'र' => 'Ra', + 'ऋ' => 'Ri', + 'ॠ' => 'Ri', + 'ऱ' => 'Ri', + 'स' => 'Sa', + 'श' => 'Sha', + 'ष' => 'Shha', + 'ट' => 'Ta', + 'त' => 'Ta', + 'ठ' => 'Tha', + 'द' => 'Tha', + 'थ' => 'Tha', + 'ध' => 'Thha', + 'ड़' => 'ugDha', + 'ढ़' => 'ugDhha', + 'व' => 'Va', + 'य' => 'Ya', + 'य़' => 'Yi', + 'ज़' => 'Za', + ], + // Armenian + 'hy' => [ + 'Ա' => 'A', + 'Բ' => 'B', + 'Գ' => 'G', + 'Դ' => 'D', + 'Ե' => 'E', + 'Զ' => 'Z', + 'Է' => 'E', + 'Ը' => 'Y', + 'Թ' => 'Th', + 'Ժ' => 'Zh', + 'Ի' => 'I', + 'Լ' => 'L', + 'Խ' => 'Kh', + 'Ծ' => 'Ts', + 'Կ' => 'K', + 'Հ' => 'H', + 'Ձ' => 'Dz', + 'Ղ' => 'Gh', + 'Ճ' => 'Tch', + 'Մ' => 'M', + 'Յ' => 'Y', + 'Ն' => 'N', + 'Շ' => 'Sh', + 'Ո' => 'Vo', + 'Չ' => 'Ch', + 'Պ' => 'P', + 'Ջ' => 'J', + 'Ռ' => 'R', + 'Ս' => 'S', + 'Վ' => 'V', + 'Տ' => 'T', + 'Ր' => 'R', + 'Ց' => 'C', + 'Ւ' => 'u', + 'Փ' => 'Ph', + 'Ք' => 'Q', + 'և' => 'ev', + 'Օ' => 'O', + 'Ֆ' => 'F', + 'ա' => 'a', + 'բ' => 'b', + 'գ' => 'g', + 'դ' => 'd', + 'ե' => 'e', + 'զ' => 'z', + 'է' => 'e', + 'ը' => 'y', + 'թ' => 'th', + 'ժ' => 'zh', + 'ի' => 'i', + 'լ' => 'l', + 'խ' => 'kh', + 'ծ' => 'ts', + 'կ' => 'k', + 'հ' => 'h', + 'ձ' => 'dz', + 'ղ' => 'gh', + 'ճ' => 'tch', + 'մ' => 'm', + 'յ' => 'y', + 'ն' => 'n', + 'շ' => 'sh', + 'ո' => 'vo', + 'չ' => 'ch', + 'պ' => 'p', + 'ջ' => 'j', + 'ռ' => 'r', + 'ս' => 's', + 'վ' => 'v', + 'տ' => 't', + 'ր' => 'r', + 'ց' => 'c', + 'ւ' => 'u', + 'փ' => 'ph', + 'ք' => 'q', + 'օ' => 'o', + 'ֆ' => 'f', + ], + // Swedish + 'sv' => [ + 'Ä' => 'A', + 'ä' => 'a', + 'Å' => 'A', + 'å' => 'a', + 'Ö' => 'O', + 'ö' => 'o', + ], + // Turkmen + 'tk' => [ + 'Ç' => 'C', + 'Ä' => 'A', + 'Ž' => 'Z', + 'Ň' => 'N', + 'Ö' => 'O', + 'Ş' => 'S', + 'Ü' => 'U', + 'Ý' => 'Y', + 'ç' => 'c', + 'ä' => 'a', + 'ž' => 'z', + 'ň' => 'n', + 'ö' => 'o', + 'ş' => 's', + 'ü' => 'u', + 'ý' => 'y', + ], + // Turkish + 'tr' => [ + 'ň' => 'n', + 'Ň' => 'N', + 'ş' => 's', + 'Ş' => 'S', + 'ı' => 'i', + 'İ' => 'I', + 'ç' => 'c', + 'Ç' => 'C', + 'ä' => 'a', + 'Ä' => 'A', + 'ü' => 'u', + 'Ü' => 'U', + 'ö' => 'o', + 'Ö' => 'O', + 'ğ' => 'g', + 'Ğ' => 'G', + 'ý' => 'y', + 'Ý' => 'Y', + 'ž' => 'z', + 'Ž' => 'Z', + ], + // Bulgarian + 'bg' => [ + 'ьо' => 'yo', + 'А' => 'A', + 'Б' => 'B', + 'В' => 'V', + 'Г' => 'G', + 'Д' => 'D', + 'Е' => 'E', + 'Ж' => 'Zh', + 'З' => 'Z', + 'И' => 'I', + 'Й' => 'Y', + 'К' => 'K', + 'Л' => 'L', + 'М' => 'M', + 'Н' => 'N', + 'О' => 'O', + 'П' => 'P', + 'Р' => 'R', + 'С' => 'S', + 'Т' => 'T', + 'У' => 'U', + 'Ф' => 'F', + 'Х' => 'H', + 'Ц' => 'C', + 'Ч' => 'Ch', + 'Ш' => 'Sh', + 'Щ' => 'Sht', + 'Ъ' => 'A', + 'Ь' => '', + 'Ю' => 'Yu', + 'Я' => 'Ya', + 'а' => 'a', + 'б' => 'b', + 'в' => 'v', + 'г' => 'g', + 'д' => 'd', + 'е' => 'e', + 'ж' => 'zh', + 'з' => 'z', + 'и' => 'i', + 'й' => 'y', + 'к' => 'k', + 'л' => 'l', + 'м' => 'm', + 'н' => 'n', + 'о' => 'o', + 'п' => 'p', + 'р' => 'r', + 'с' => 's', + 'т' => 't', + 'у' => 'u', + 'ф' => 'f', + 'х' => 'h', + 'ц' => 'c', + 'ч' => 'ch', + 'ш' => 'sh', + 'щ' => 'sht', + 'ъ' => 'a', + 'ь' => '', + 'ю' => 'yu', + 'я' => 'ya', + ], + // Hungarian + 'hu' => [ + 'Á' => 'A', + 'Ē' => 'E', + 'É' => 'E', + 'Í' => 'I', + 'Ó' => 'O', + 'Ö' => 'O', + 'Ő' => 'O', + 'Ú' => 'U', + 'Ü' => 'U', + 'Ű' => 'U', + 'á' => 'a', + 'ē' => 'e', + 'é' => 'e', + 'í' => 'i', + 'ó' => 'o', + 'ö' => 'o', + 'ő' => 'o', + 'ú' => 'u', + 'ü' => 'u', + 'ű' => 'u', + ], + // Myanmar (Burmese) + 'my' => [ + 'န်ုပ်' => 'nub', + 'ောင်' => 'aung', + 'ိုက်' => 'aik', + 'ိုဒ်' => 'ok', + 'ိုင်' => 'aing', + 'ိုလ်' => 'ol', + 'ေါင်' => 'aung', + 'သြော' => 'aw', + 'ောက်' => 'auk', + 'ိတ်' => 'eik', + 'ုတ်' => 'ok', + 'ုန်' => 'on', + 'ေတ်' => 'it', + 'ုဒ်' => 'ait', + 'ာန်' => 'an', + 'ိန်' => 'ein', + 'ွတ်' => 'ut', + 'ေါ်' => 'aw', + 'ွန်' => 'un', + 'ိပ်' => 'eik', + 'ုပ်' => 'ok', + 'ွပ်' => 'ut', + 'ိမ်' => 'ein', + 'ုမ်' => 'on', + 'ော်' => 'aw', + 'ွမ်' => 'un', + 'က်' => 'et', + 'ေါ' => 'aw', + 'ော' => 'aw', + 'ျွ' => 'ywa', + 'ြွ' => 'yw', + 'ို' => 'o', + 'ုံ' => 'on', + 'တ်' => 'at', + 'င်' => 'in', + 'ည်' => 'i', + 'ဒ်' => 'd', + 'န်' => 'an', + 'ပ်' => 'at', + 'မ်' => 'an', + 'စျ' => 'za', + 'ယ်' => 'e', + 'ဉ်' => 'in', + 'စ်' => 'it', + 'ိံ' => 'ein', + 'ဲ' => 'e', + 'း' => '', + 'ာ' => 'a', + 'ါ' => 'a', + 'ေ' => 'e', + 'ံ' => 'an', + 'ိ' => 'i', + 'ီ' => 'i', + 'ု' => 'u', + 'ူ' => 'u', + '်' => 'at', + '္' => '', + '့' => '', + 'က' => 'k', + '၉' => '9', + 'တ' => 't', + 'ရ' => 'ya', + 'ယ' => 'y', + 'မ' => 'm', + 'ဘ' => 'ba', + 'ဗ' => 'b', + 'ဖ' => 'pa', + 'ပ' => 'p', + 'န' => 'n', + 'ဓ' => 'da', + 'ဒ' => 'd', + 'ထ' => 'ta', + 'ဏ' => 'na', + 'ဝ' => 'w', + 'ဎ' => 'da', + 'ဍ' => 'd', + 'ဌ' => 'ta', + 'ဋ' => 't', + 'ည' => 'ny', + 'ဇ' => 'z', + 'ဆ' => 'sa', + 'စ' => 's', + 'င' => 'ng', + 'ဃ' => 'ga', + 'ဂ' => 'g', + 'လ' => 'l', + 'သ' => 'th', + '၈' => '8', + 'ဩ' => 'aw', + 'ခ' => 'kh', + '၆' => '6', + '၅' => '5', + '၄' => '4', + '၃' => '3', + '၂' => '2', + '၁' => '1', + '၀' => '0', + '၌' => 'hnaik', + '၍' => 'ywae', + 'ဪ' => 'aw', + 'ဦ' => '-u', + 'ဟ' => 'h', + 'ဉ' => 'u', + 'ဤ' => '-i', + 'ဣ' => 'i', + '၏' => '-e', + 'ဧ' => 'e', + 'ှ' => 'h', + 'ွ' => 'w', + 'ျ' => 'ya', + 'ြ' => 'y', + 'အ' => 'a', + 'ဠ' => 'la', + '၇' => '7', + ], + // Croatian (Hrvatska) + 'hr' => [ + 'DŽ' => 'DZ', + 'Dž' => 'Dz', + 'dž' => 'dz', + 'DZ' => 'DZ', + 'Dz' => 'Dz', + 'dz' => 'dz', + 'IJ' => 'IJ', + 'ij' => 'ij', + 'LJ' => 'LJ', + 'Lj' => 'Lj', + 'lj' => 'lj', + 'NJ' => 'NJ', + 'Nj' => 'Nj', + 'nj' => 'nj', + 'ž' => 'z', + 'Ž' => 'Z', + 'đ' => 'dj', + 'Đ' => 'Dj', + 'č' => 'c', + 'Č' => 'C', + 'ć' => 'c', + 'Ć' => 'C', + 'š' => 's', + 'Š' => 'S', + ], + // Finnish + 'fi' => [ + 'Ä' => 'A', + 'Ö' => 'O', + 'ä' => 'a', + 'ö' => 'o', + ], + // Georgian (Kartvelian) + 'ka' => [ + 'ა' => 'a', + 'ბ' => 'b', + 'გ' => 'g', + 'დ' => 'd', + 'ე' => 'e', + 'ვ' => 'v', + 'ზ' => 'z', + 'თ' => 't', + 'ი' => 'i', + 'კ' => 'k', + 'ლ' => 'l', + 'მ' => 'm', + 'ნ' => 'n', + 'ო' => 'o', + 'პ' => 'p', + 'ჟ' => 'zh', + 'რ' => 'r', + 'ს' => 's', + 'ტ' => 't', + 'უ' => 'u', + 'ფ' => 'f', + 'ქ' => 'q', + 'ღ' => 'gh', + 'ყ' => 'y', + 'შ' => 'sh', + 'ჩ' => 'ch', + 'ც' => 'ts', + 'ძ' => 'dz', + 'წ' => 'ts', + 'ჭ' => 'ch', + 'ხ' => 'kh', + 'ჯ' => 'j', + 'ჰ' => 'h', + ], + // Russian + 'ru' => [ + 'А' => 'A', + 'а' => 'a', + 'Б' => 'B', + 'б' => 'b', + 'В' => 'V', + 'в' => 'v', + 'Г' => 'G', + 'г' => 'g', + 'Д' => 'D', + 'д' => 'd', + 'Е' => 'E', + 'е' => 'e', + 'Ё' => 'Yo', + 'ё' => 'yo', + 'Ж' => 'Zh', + 'ж' => 'zh', + 'З' => 'Z', + 'з' => 'z', + 'И' => 'I', + 'и' => 'i', + 'Й' => 'Y', + 'й' => 'y', + 'К' => 'K', + 'к' => 'k', + 'Л' => 'L', + 'л' => 'l', + 'М' => 'M', + 'м' => 'm', + 'Н' => 'N', + 'н' => 'n', + 'О' => 'O', + 'о' => 'o', + 'П' => 'P', + 'п' => 'p', + 'Р' => 'R', + 'р' => 'r', + 'С' => 'S', + 'с' => 's', + 'Т' => 'T', + 'т' => 't', + 'У' => 'U', + 'у' => 'u', + 'Ф' => 'F', + 'ф' => 'f', + 'Х' => 'H', + 'х' => 'h', + 'Ц' => 'Ts', + 'ц' => 'ts', + 'Ч' => 'Ch', + 'ч' => 'ch', + 'ш' => 'sh', + 'Ш' => 'Sh', + 'Щ' => 'Sch', + 'щ' => 'sch', + 'Ъ' => '', + 'ъ' => '', + 'Ы' => 'Y', + 'ы' => 'y', + 'Ь' => '', + 'ь' => '', + 'Э' => 'E', + 'э' => 'e', + 'Ю' => 'Yu', + 'ю' => 'yu', + 'Я' => 'Ya', + 'я' => 'ya', + ], + // Russian - GOST 7.79-2000(B) + // -> https://en.m.wikipedia.org/wiki/Romanization_of_Russian#content-collapsible-block-1 + 'ru__gost_2000_b' => [ + 'А' => 'A', + 'а' => 'a', + 'Б' => 'B', + 'б' => 'b', + 'В' => 'V', + 'в' => 'v', + 'Г' => 'G', + 'г' => 'g', + 'Д' => 'D', + 'д' => 'd', + 'Е' => 'E', + 'е' => 'e', + 'Ё' => 'Yo', + 'ё' => 'yo', + 'Ж' => 'Zh', + 'ж' => 'zh', + 'З' => 'Z', + 'з' => 'z', + 'И' => 'i', + 'и' => 'i', + 'Й' => 'i', + 'й' => 'i', + 'К' => 'K', + 'к' => 'k', + 'Л' => 'L', + 'л' => 'l', + 'М' => 'M', + 'м' => 'm', + 'Н' => 'N', + 'н' => 'n', + 'О' => 'O', + 'о' => 'o', + 'П' => 'P', + 'п' => 'p', + 'Р' => 'R', + 'р' => 'r', + 'С' => 'S', + 'с' => 's', + 'Т' => 'T', + 'т' => 't', + 'У' => 'U', + 'у' => 'u', + 'Ф' => 'F', + 'ф' => 'f', + 'Х' => 'X', + 'х' => 'x', + 'Ц' => 'Cz', + 'ц' => 'cz', + 'Ч' => 'Ch', + 'ч' => 'ch', + 'ш' => 'sh', + 'Ш' => 'Sh', + 'Щ' => 'Shh', + 'щ' => 'shh', + 'Ъ' => '', + 'ъ' => '', + 'Ы' => 'Y\'', + 'ы' => 'y\'', + 'Ь' => '', + 'ь' => '', + 'Э' => 'E\'', + 'э' => 'e\'', + 'Ю' => 'Yu', + 'ю' => 'yu', + 'Я' => 'Ya', + 'я' => 'ya', + 'І' => 'I', + 'і' => 'i', + 'Ѳ' => 'Fh', + 'ѳ' => 'fh', + 'Ѣ' => 'Ye', + 'ѣ' => 'ye', + 'Ѵ' => 'Yh', + 'ѵ' => 'yh', + 'Є' => '', + 'є' => '', + 'Ѥ' => '', + 'ѥ' => '', + 'Ѕ' => 'Js', + 'ѕ' => 'js', + 'Ꙋ' => '', + 'ꙋ' => '', + 'Ѡ' => '', + 'ѡ' => '', + 'Ѿ' => '', + 'ѿ' => '', + 'Ѫ' => '', + 'ѫ' => '', + 'Ѧ' => '', + 'ѧ' => '', + 'Ѭ' => '', + 'ѭ' => '', + 'Ѩ' => '', + 'ѩ' => '', + 'Ѯ' => '', + 'ѯ' => '', + 'Ѱ' => '', + 'ѱ' => '', + ], + // Russian - Passport (2013), ICAO + // -> https://en.m.wikipedia.org/wiki/Romanization_of_Russian#content-collapsible-block-1 + 'ru__passport_2013' => [ + 'А' => 'A', + 'а' => 'a', + 'Б' => 'B', + 'б' => 'b', + 'В' => 'V', + 'в' => 'v', + 'Г' => 'G', + 'г' => 'g', + 'Д' => 'D', + 'д' => 'd', + 'Е' => 'E', + 'е' => 'e', + 'Ё' => 'E', + 'ё' => 'e', + 'Ж' => 'Zh', + 'ж' => 'zh', + 'З' => 'Z', + 'з' => 'z', + 'И' => 'i', + 'и' => 'i', + 'Й' => 'i', + 'й' => 'i', + 'К' => 'K', + 'к' => 'k', + 'Л' => 'L', + 'л' => 'l', + 'М' => 'M', + 'м' => 'm', + 'Н' => 'N', + 'н' => 'n', + 'О' => 'O', + 'о' => 'o', + 'П' => 'P', + 'п' => 'p', + 'Р' => 'R', + 'р' => 'r', + 'С' => 'S', + 'с' => 's', + 'Т' => 'T', + 'т' => 't', + 'У' => 'U', + 'у' => 'u', + 'Ф' => 'F', + 'ф' => 'f', + 'Х' => 'Kh', + 'х' => 'kh', + 'Ц' => 'Ts', + 'ц' => 'ts', + 'Ч' => 'Ch', + 'ч' => 'ch', + 'ш' => 'sh', + 'Ш' => 'Sh', + 'Щ' => 'Shch', + 'щ' => 'shch', + 'Ъ' => 'Ie', + 'ъ' => 'ie', + 'Ы' => 'Y', + 'ы' => 'y', + 'Ь' => '', + 'ь' => '', + 'Э' => 'E', + 'э' => 'e', + 'Ю' => 'Iu', + 'ю' => 'iu', + 'Я' => 'Ia', + 'я' => 'ia', + 'І' => '', + 'і' => '', + 'Ѳ' => '', + 'ѳ' => '', + 'Ѣ' => '', + 'ѣ' => '', + 'Ѵ' => '', + 'ѵ' => '', + 'Є' => '', + 'є' => '', + 'Ѥ' => '', + 'ѥ' => '', + 'Ѕ' => '', + 'ѕ' => '', + 'Ꙋ' => '', + 'ꙋ' => '', + 'Ѡ' => '', + 'ѡ' => '', + 'Ѿ' => '', + 'ѿ' => '', + 'Ѫ' => '', + 'ѫ' => '', + 'Ѧ' => '', + 'ѧ' => '', + 'Ѭ' => '', + 'ѭ' => '', + 'Ѩ' => '', + 'ѩ' => '', + 'Ѯ' => '', + 'ѯ' => '', + 'Ѱ' => '', + 'ѱ' => '', + ], + // Ukrainian + // -> https://zakon.rada.gov.ua/laws/show/55-2010-%D0%BF?lang=en + 'uk' => [ + 'Г' => 'H', + 'г' => 'h', + 'Ґ' => 'G', + 'ґ' => 'g', + 'Є' => 'Ye', + 'є' => 'ye', + 'И' => 'Y', + 'и' => 'y', + 'І' => 'I', + 'і' => 'i', + 'Ї' => 'Yi', + 'ї' => 'yi', + 'Й' => 'Y', + 'й' => 'y', + 'Х' => 'Kh', + 'х' => 'kh', + 'Ц' => 'Ts', + 'ц' => 'ts', + 'Ч' => 'Ch', + 'ч' => 'ch', + 'Ш' => 'Sh', + 'ш' => 'sh', + 'Щ' => 'Shch', + 'щ' => 'shch', + ], + // Kazakh + 'kk' => [ + 'Ә' => 'A', + 'Ғ' => 'G', + 'Қ' => 'Q', + 'Ң' => 'N', + 'Ө' => 'O', + 'Ұ' => 'U', + 'Ү' => 'U', + 'Һ' => 'H', + 'ә' => 'a', + 'ғ' => 'g', + 'қ' => 'q', + 'ң' => 'n', + 'ө' => 'o', + 'ұ' => 'u', + 'ү' => 'u', + 'һ' => 'h', + ], + // Czech + 'cs' => [ + 'á' => 'a', + 'Á' => 'A', + 'č' => 'c', + 'Č' => 'C', + 'ď' => 'd', + 'Ď' => 'D', + 'é' => 'e', + 'É' => 'E', + 'ě' => 'e', + 'Ě' => 'E', + 'í' => 'i', + 'Í' => 'I', + 'ň' => 'n', + 'Ň' => 'N', + 'ó' => 'o', + 'Ó' => 'O', + 'ř' => 'r', + 'Ř' => 'R', + 'š' => 's', + 'Š' => 'S', + 'ť' => 't', + 'Ť' => 'T', + 'ú' => 'u', + 'Ú' => 'U', + 'ů' => 'u', + 'Ů' => 'U', + 'ý' => 'y', + 'Ý' => 'Y', + 'ž' => 'z', + 'Ž' => 'Z', + ], + // Danish + 'da' => [ + 'Æ' => 'Ae', + 'æ' => 'ae', + 'Ø' => 'Oe', + 'ø' => 'oe', + 'Å' => 'Aa', + 'å' => 'aa', + 'É' => 'E', + 'é' => 'e', + ], + // Polish + 'pl' => [ + 'ą' => 'a', + 'ć' => 'c', + 'ę' => 'e', + 'ł' => 'l', + 'ń' => 'n', + 'ó' => 'o', + 'ś' => 's', + 'ź' => 'z', + 'ż' => 'z', + 'Ą' => 'A', + 'Ć' => 'C', + 'Ę' => 'E', + 'Ł' => 'L', + 'Ń' => 'N', + 'Ó' => 'O', + 'Ś' => 'S', + 'Ź' => 'Z', + 'Ż' => 'Z', + ], + // Romanian + 'ro' => [ + 'ă' => 'a', + 'â' => 'a', + 'Ă' => 'A', + 'Â' => 'A', + 'î' => 'i', + 'Î' => 'I', + 'ș' => 's', + 'ş' => 's', + 'Ş' => 'S', + 'Ș' => 'S', + 'ț' => 't', + 'ţ' => 't', + 'Ţ' => 'T', + 'Ț' => 'T', + ], + // Esperanto + 'eo' => [ + 'ĉ' => 'cx', + 'ĝ' => 'gx', + 'ĥ' => 'hx', + 'ĵ' => 'jx', + 'ŝ' => 'sx', + 'ŭ' => 'ux', + 'Ĉ' => 'CX', + 'Ĝ' => 'GX', + 'Ĥ' => 'HX', + 'Ĵ' => 'JX', + 'Ŝ' => 'SX', + 'Ŭ' => 'UX', + ], + // Estonian + 'et' => [ + 'Š' => 'S', + 'Ž' => 'Z', + 'Õ' => 'O', + 'Ä' => 'A', + 'Ö' => 'O', + 'Ü' => 'U', + 'š' => 's', + 'ž' => 'z', + 'õ' => 'o', + 'ä' => 'a', + 'ö' => 'o', + 'ü' => 'u', + ], + // Latvian + 'lv' => [ + 'ā' => 'a', + 'č' => 'c', + 'ē' => 'e', + 'ģ' => 'g', + 'ī' => 'i', + 'ķ' => 'k', + 'ļ' => 'l', + 'ņ' => 'n', + 'š' => 's', + 'ū' => 'u', + 'ž' => 'z', + 'Ā' => 'A', + 'Č' => 'C', + 'Ē' => 'E', + 'Ģ' => 'G', + 'Ī' => 'i', + 'Ķ' => 'k', + 'Ļ' => 'L', + 'Ņ' => 'N', + 'Š' => 'S', + 'Ū' => 'u', + 'Ž' => 'Z', + ], + // Lithuanian + 'lt' => [ + 'ą' => 'a', + 'č' => 'c', + 'ę' => 'e', + 'ė' => 'e', + 'į' => 'i', + 'š' => 's', + 'ų' => 'u', + 'ū' => 'u', + 'ž' => 'z', + 'Ą' => 'A', + 'Č' => 'C', + 'Ę' => 'E', + 'Ė' => 'E', + 'Į' => 'I', + 'Š' => 'S', + 'Ų' => 'U', + 'Ū' => 'U', + 'Ž' => 'Z', + ], + // Norwegian + 'no' => [ + 'Æ' => 'AE', + 'æ' => 'ae', + 'Ø' => 'OE', + 'ø' => 'oe', + 'Å' => 'AA', + 'å' => 'aa', + ], + // Vietnamese + 'vi' => [ + 'Á' => 'A', + 'À' => 'A', + 'Ả' => 'A', + 'Ã' => 'A', + 'Ạ' => 'A', + 'Ă' => 'A', + 'Ắ' => 'A', + 'Ằ' => 'A', + 'Ẳ' => 'A', + 'Ẵ' => 'A', + 'Ặ' => 'A', + 'Â' => 'A', + 'Ấ' => 'A', + 'Ầ' => 'A', + 'Ẩ' => 'A', + 'Ẫ' => 'A', + 'Ậ' => 'A', + 'á' => 'a', + 'à' => 'a', + 'ả' => 'a', + 'ã' => 'a', + 'ạ' => 'a', + 'ă' => 'a', + 'ắ' => 'a', + 'ằ' => 'a', + 'ẳ' => 'a', + 'ẵ' => 'a', + 'ặ' => 'a', + 'â' => 'a', + 'ấ' => 'a', + 'ầ' => 'a', + 'ẩ' => 'a', + 'ẫ' => 'a', + 'ậ' => 'a', + 'É' => 'E', + 'È' => 'E', + 'Ẻ' => 'E', + 'Ẽ' => 'E', + 'Ẹ' => 'E', + 'Ê' => 'E', + 'Ế' => 'E', + 'Ề' => 'E', + 'Ể' => 'E', + 'Ễ' => 'E', + 'Ệ' => 'E', + 'é' => 'e', + 'è' => 'e', + 'ẻ' => 'e', + 'ẽ' => 'e', + 'ẹ' => 'e', + 'ê' => 'e', + 'ế' => 'e', + 'ề' => 'e', + 'ể' => 'e', + 'ễ' => 'e', + 'ệ' => 'e', + 'Í' => 'I', + 'Ì' => 'I', + 'Ỉ' => 'I', + 'Ĩ' => 'I', + 'Ị' => 'I', + 'í' => 'i', + 'ì' => 'i', + 'ỉ' => 'i', + 'ĩ' => 'i', + 'ị' => 'i', + 'Ó' => 'O', + 'Ò' => 'O', + 'Ỏ' => 'O', + 'Õ' => 'O', + 'Ọ' => 'O', + 'Ô' => 'O', + 'Ố' => 'O', + 'Ồ' => 'O', + 'Ổ' => 'O', + 'Ỗ' => 'O', + 'Ộ' => 'O', + 'Ơ' => 'O', + 'Ớ' => 'O', + 'Ờ' => 'O', + 'Ở' => 'O', + 'Ỡ' => 'O', + 'Ợ' => 'O', + 'ó' => 'o', + 'ò' => 'o', + 'ỏ' => 'o', + 'õ' => 'o', + 'ọ' => 'o', + 'ô' => 'o', + 'ố' => 'o', + 'ồ' => 'o', + 'ổ' => 'o', + 'ỗ' => 'o', + 'ộ' => 'o', + 'ơ' => 'o', + 'ớ' => 'o', + 'ờ' => 'o', + 'ở' => 'o', + 'ỡ' => 'o', + 'ợ' => 'o', + 'Ú' => 'U', + 'Ù' => 'U', + 'Ủ' => 'U', + 'Ũ' => 'U', + 'Ụ' => 'U', + 'Ư' => 'U', + 'Ứ' => 'U', + 'Ừ' => 'U', + 'Ử' => 'U', + 'Ữ' => 'U', + 'Ự' => 'U', + 'ú' => 'u', + 'ù' => 'u', + 'ủ' => 'u', + 'ũ' => 'u', + 'ụ' => 'u', + 'ư' => 'u', + 'ứ' => 'u', + 'ừ' => 'u', + 'ử' => 'u', + 'ữ' => 'u', + 'ự' => 'u', + 'Ý' => 'Y', + 'Ỳ' => 'Y', + 'Ỷ' => 'Y', + 'Ỹ' => 'Y', + 'Ỵ' => 'Y', + 'ý' => 'y', + 'ỳ' => 'y', + 'ỷ' => 'y', + 'ỹ' => 'y', + 'ỵ' => 'y', + 'Đ' => 'D', + 'đ' => 'd', + ], + // Persian (Farsi) + 'fa' => [ + 'ا' => 'a', + 'ب' => 'b', + 'پ' => 'p', + 'ت' => 't', + 'ث' => 's', + 'ج' => 'j', + 'چ' => 'ch', + 'ح' => 'h', + 'خ' => 'kh', + 'د' => 'd', + 'ذ' => 'z', + 'ر' => 'r', + 'ز' => 'z', + 'س' => 's', + 'ش' => 'sh', + 'ص' => 's', + 'ض' => 'z', + 'ط' => 't', + 'ظ' => 'z', + 'ع' => 'a', + 'غ' => 'gh', + 'ف' => 'f', + 'ق' => 'gh', + 'ک' => 'k', + 'گ' => 'g', + 'ل' => 'l', + 'ژ' => 'zh', + 'ك' => 'k', + 'م' => 'm', + 'ن' => 'n', + 'ه' => 'h', + 'و' => 'o', + 'ی' => 'y', + 'آ' => 'a', + '٠' => '0', + '١' => '1', + '٢' => '2', + '٣' => '3', + '٤' => '4', + '٥' => '5', + '٦' => '6', + '٧' => '7', + '٨' => '8', + '٩' => '9', + ], + // Arabic + 'ar' => [ + 'أ' => 'a', + 'ب' => 'b', + 'ت' => 't', + 'ث' => 'th', + 'ج' => 'g', + 'ح' => 'h', + 'خ' => 'kh', + 'د' => 'd', + 'ذ' => 'th', + 'ر' => 'r', + 'ز' => 'z', + 'س' => 's', + 'ش' => 'sh', + 'ص' => 's', + 'ض' => 'd', + 'ط' => 't', + 'ظ' => 'th', + 'ع' => 'aa', + 'غ' => 'gh', + 'ف' => 'f', + 'ق' => 'k', + 'ك' => 'k', + 'ل' => 'l', + 'م' => 'm', + 'ن' => 'n', + 'ه' => 'h', + 'و' => 'o', + 'ي' => 'y', + 'ا' => 'a', + 'إ' => 'a', + 'آ' => 'a', + 'ؤ' => 'o', + 'ئ' => 'y', + 'ء' => 'aa', + '٠' => '0', + '١' => '1', + '٢' => '2', + '٣' => '3', + '٤' => '4', + '٥' => '5', + '٦' => '6', + '٧' => '7', + '٨' => '8', + '٩' => '9', + ], + // Serbian + 'sr' => [ + 'đ' => 'dj', + 'ž' => 'z', + 'ć' => 'c', + 'č' => 'c', + 'š' => 's', + 'Đ' => 'Dj', + 'Ž' => 'Z', + 'Ć' => 'C', + 'Č' => 'C', + 'Š' => 'S', + 'а' => 'a', + 'б' => 'b', + 'в' => 'v', + 'г' => 'g', + 'д' => 'd', + 'ђ' => 'dj', + 'е' => 'e', + 'ж' => 'z', + 'з' => 'z', + 'и' => 'i', + 'ј' => 'j', + 'к' => 'k', + 'л' => 'l', + 'љ' => 'lj', + 'м' => 'm', + 'н' => 'n', + 'њ' => 'nj', + 'о' => 'o', + 'п' => 'p', + 'р' => 'r', + 'с' => 's', + 'т' => 't', + 'ћ' => 'c', + 'у' => 'u', + 'ф' => 'f', + 'х' => 'h', + 'ц' => 'c', + 'ч' => 'c', + 'џ' => 'dz', + 'ш' => 's', + 'А' => 'A', + 'Б' => 'B', + 'В' => 'V', + 'Г' => 'G', + 'Д' => 'D', + 'Ђ' => 'Dj', + 'Е' => 'E', + 'Ж' => 'Z', + 'З' => 'Z', + 'И' => 'I', + 'Ј' => 'j', + 'К' => 'K', + 'Л' => 'L', + 'Љ' => 'Lj', + 'М' => 'M', + 'Н' => 'N', + 'Њ' => 'Nj', + 'О' => 'O', + 'П' => 'P', + 'Р' => 'R', + 'С' => 'S', + 'Т' => 'T', + 'Ћ' => 'C', + 'У' => 'U', + 'Ф' => 'F', + 'Х' => 'H', + 'Ц' => 'C', + 'Ч' => 'C', + 'Џ' => 'Dz', + 'Ш' => 'S', + ], + // Serbian - Cyrillic + 'sr__cyr' => [ + 'а' => 'a', + 'б' => 'b', + 'в' => 'v', + 'г' => 'g', + 'д' => 'd', + 'ђ' => 'dj', + 'е' => 'e', + 'ж' => 'z', + 'з' => 'z', + 'и' => 'i', + 'ј' => 'j', + 'к' => 'k', + 'л' => 'l', + 'љ' => 'lj', + 'м' => 'm', + 'н' => 'n', + 'њ' => 'nj', + 'о' => 'o', + 'п' => 'p', + 'р' => 'r', + 'с' => 's', + 'т' => 't', + 'ћ' => 'c', + 'у' => 'u', + 'ф' => 'f', + 'х' => 'h', + 'ц' => 'c', + 'ч' => 'c', + 'џ' => 'dz', + 'ш' => 's', + 'А' => 'A', + 'Б' => 'B', + 'В' => 'V', + 'Г' => 'G', + 'Д' => 'D', + 'Ђ' => 'Dj', + 'Е' => 'E', + 'Ж' => 'Z', + 'З' => 'Z', + 'И' => 'I', + 'Ј' => 'j', + 'К' => 'K', + 'Л' => 'L', + 'Љ' => 'Lj', + 'М' => 'M', + 'Н' => 'N', + 'Њ' => 'Nj', + 'О' => 'O', + 'П' => 'P', + 'Р' => 'R', + 'С' => 'S', + 'Т' => 'T', + 'Ћ' => 'C', + 'У' => 'U', + 'Ф' => 'F', + 'Х' => 'H', + 'Ц' => 'C', + 'Ч' => 'C', + 'Џ' => 'Dz', + 'Ш' => 'S', + ], + // Serbian - Latin + 'sr__lat' => [ + 'đ' => 'dj', + 'ž' => 'z', + 'ć' => 'c', + 'č' => 'c', + 'š' => 's', + 'Đ' => 'Dj', + 'Ž' => 'Z', + 'Ć' => 'C', + 'Č' => 'C', + 'Š' => 'S', + ], + // Azerbaijani + 'az' => [ + 'ç' => 'c', + 'ə' => 'e', + 'ğ' => 'g', + 'ı' => 'i', + 'ö' => 'o', + 'ş' => 's', + 'ü' => 'u', + 'Ç' => 'C', + 'Ə' => 'E', + 'Ğ' => 'G', + 'İ' => 'I', + 'Ö' => 'O', + 'Ş' => 'S', + 'Ü' => 'U', + ], + // Slovak + 'sk' => [ + 'á' => 'a', + 'ä' => 'a', + 'č' => 'c', + 'ď' => 'd', + 'é' => 'e', + 'í' => 'i', + 'ľ' => 'l', + 'ĺ' => 'l', + 'ň' => 'n', + 'ó' => 'o', + 'ô' => 'o', + 'ŕ' => 'r', + 'š' => 's', + 'ť' => 't', + 'ú' => 'u', + 'ý' => 'y', + 'ž' => 'z', + 'Á' => 'A', + 'Ä' => 'A', + 'Č' => 'C', + 'Ď' => 'D', + 'É' => 'E', + 'Í' => 'I', + 'Ľ' => 'L', + 'Ĺ' => 'L', + 'Ň' => 'N', + 'Ó' => 'O', + 'Ô' => 'O', + 'Ŕ' => 'R', + 'Š' => 'S', + 'Ť' => 'T', + 'Ú' => 'U', + 'Ý' => 'Y', + 'Ž' => 'Z', + ], + // French + 'fr' => [ + 'Æ' => 'AE', + 'æ' => 'ae', + 'Œ' => 'OE', + 'œ' => 'oe', + 'â' => 'a', + 'Â' => 'A', + 'à' => 'a', + 'À' => 'A', + 'ä' => 'a', + 'Ä' => 'A', + 'ç' => 'c', + 'Ç' => 'C', + 'é' => 'e', + 'É' => 'E', + 'ê' => 'e', + 'Ê' => 'E', + 'ë' => 'e', + 'Ë' => 'E', + 'è' => 'e', + 'È' => 'E', + 'ï' => 'i', + 'î' => 'i', + 'Ï' => 'I', + 'Î' => 'I', + 'ÿ' => 'y', + 'Ÿ' => 'Y', + 'ô' => 'o', + 'Ô' => 'O', + 'ö' => 'o', + 'Ö' => 'O', + 'û' => 'u', + 'Û' => 'U', + 'ù' => 'u', + 'Ù' => 'U', + 'ü' => 'u', + 'Ü' => 'U', + ], + // Austrian (French) + 'fr_at' => [ + 'ß' => 'sz', + 'ẞ' => 'SZ', + 'Æ' => 'AE', + 'æ' => 'ae', + 'Œ' => 'OE', + 'œ' => 'oe', + 'â' => 'a', + 'Â' => 'A', + 'à' => 'a', + 'À' => 'A', + 'ä' => 'a', + 'Ä' => 'A', + 'ç' => 'c', + 'Ç' => 'C', + 'é' => 'e', + 'É' => 'E', + 'ê' => 'e', + 'Ê' => 'E', + 'ë' => 'e', + 'Ë' => 'E', + 'è' => 'e', + 'È' => 'E', + 'ï' => 'i', + 'î' => 'i', + 'Ï' => 'I', + 'Î' => 'I', + 'ÿ' => 'y', + 'Ÿ' => 'Y', + 'ô' => 'o', + 'Ô' => 'O', + 'ö' => 'o', + 'Ö' => 'O', + 'û' => 'u', + 'Û' => 'U', + 'ù' => 'u', + 'Ù' => 'U', + 'ü' => 'u', + 'Ü' => 'U', + ], + // Switzerland (French) + 'fr_ch' => [ + 'ß' => 'ss', + 'ẞ' => 'SS', + 'Æ' => 'AE', + 'æ' => 'ae', + 'Œ' => 'OE', + 'œ' => 'oe', + 'â' => 'a', + 'Â' => 'A', + 'à' => 'a', + 'À' => 'A', + 'ä' => 'a', + 'Ä' => 'A', + 'ç' => 'c', + 'Ç' => 'C', + 'é' => 'e', + 'É' => 'E', + 'ê' => 'e', + 'Ê' => 'E', + 'ë' => 'e', + 'Ë' => 'E', + 'è' => 'e', + 'È' => 'E', + 'ï' => 'i', + 'î' => 'i', + 'Ï' => 'I', + 'Î' => 'I', + 'ÿ' => 'y', + 'Ÿ' => 'Y', + 'ô' => 'o', + 'Ô' => 'O', + 'ö' => 'o', + 'Ö' => 'O', + 'û' => 'u', + 'Û' => 'U', + 'ù' => 'u', + 'Ù' => 'U', + 'ü' => 'u', + 'Ü' => 'U', + ], + // German + 'de' => [ + 'Ä' => 'Ae', + 'Ö' => 'Oe', + 'Ü' => 'Ue', + 'ä' => 'ae', + 'ö' => 'oe', + 'ü' => 'ue', + 'ß' => 'ss', + 'ẞ' => 'SS', + ], + // Austrian (German) + 'de_at' => [ + 'Ä' => 'Ae', + 'Ö' => 'Oe', + 'Ü' => 'Ue', + 'ä' => 'ae', + 'ö' => 'oe', + 'ü' => 'ue', + 'ß' => 'sz', + 'ẞ' => 'SZ', + ], + // Switzerland (German) + 'de_ch' => [ + 'Ä' => 'Ae', + 'Ö' => 'Oe', + 'Ü' => 'Ue', + 'ä' => 'ae', + 'ö' => 'oe', + 'ü' => 'ue', + 'ß' => 'ss', + 'ẞ' => 'SS', + ], + // Bengali (Bangla) + 'bn' => [ + 'ভ্ল' => 'vl', + 'পশ' => 'psh', + 'ব্ধ' => 'bdh', + 'ব্জ' => 'bj', + 'ব্দ' => 'bd', + 'ব্ব' => 'bb', + 'ব্ল' => 'bl', + 'ভ' => 'v', + 'ব' => 'b', + 'চ্ঞ' => 'cNG', + 'চ্ছ' => 'cch', + 'চ্চ' => 'cc', + 'ছ' => 'ch', + 'চ' => 'c', + 'ধ্ন' => 'dhn', + 'ধ্ম' => 'dhm', + 'দ্ঘ' => 'dgh', + 'দ্ধ' => 'ddh', + 'দ্ভ' => 'dv', + 'দ্ম' => 'dm', + 'ড্ড' => 'DD', + 'ঢ' => 'Dh', + 'ধ' => 'dh', + 'দ্গ' => 'dg', + 'দ্দ' => 'dd', + 'ড' => 'D', + 'দ' => 'd', + '।' => '.', + 'ঘ্ন' => 'Ghn', + 'গ্ধ' => 'Gdh', + 'গ্ণ' => 'GN', + 'গ্ন' => 'Gn', + 'গ্ম' => 'Gm', + 'গ্ল' => 'Gl', + 'জ্ঞ' => 'jNG', + 'ঘ' => 'Gh', + 'গ' => 'g', + 'হ্ণ' => 'hN', + 'হ্ন' => 'hn', + 'হ্ম' => 'hm', + 'হ্ল' => 'hl', + 'হ' => 'h', + 'জ্ঝ' => 'jjh', + 'ঝ' => 'jh', + 'জ্জ' => 'jj', + 'জ' => 'j', + 'ক্ষ্ণ' => 'kxN', + 'ক্ষ্ম' => 'kxm', + 'ক্ষ' => 'ksh', + 'কশ' => 'ksh', + 'ক্ক' => 'kk', + 'ক্ট' => 'kT', + 'ক্ত' => 'kt', + 'ক্ল' => 'kl', + 'ক্স' => 'ks', + 'খ' => 'kh', + 'ক' => 'k', + 'ল্ভ' => 'lv', + 'ল্ধ' => 'ldh', + 'লখ' => 'lkh', + 'লঘ' => 'lgh', + 'লফ' => 'lph', + 'ল্ক' => 'lk', + 'ল্গ' => 'lg', + 'ল্ট' => 'lT', + 'ল্ড' => 'lD', + 'ল্প' => 'lp', + 'ল্ম' => 'lm', + 'ল্ল' => 'll', + 'ল্ব' => 'lb', + 'ল' => 'l', + 'ম্থ' => 'mth', + 'ম্ফ' => 'mf', + 'ম্ভ' => 'mv', + 'মপ্ল' => 'mpl', + 'ম্ন' => 'mn', + 'ম্প' => 'mp', + 'ম্ম' => 'mm', + 'ম্ল' => 'ml', + 'ম্ব' => 'mb', + 'ম' => 'm', + '০' => '0', + '১' => '1', + '২' => '2', + '৩' => '3', + '৪' => '4', + '৫' => '5', + '৬' => '6', + '৭' => '7', + '৮' => '8', + '৯' => '9', + 'ঙ্ক্ষ' => 'Ngkx', + 'ঞ্ছ' => 'nch', + 'ঙ্ঘ' => 'ngh', + 'ঙ্খ' => 'nkh', + 'ঞ্ঝ' => 'njh', + 'ঙ্গৌ' => 'ngOU', + 'ঙ্গৈ' => 'ngOI', + 'ঞ্চ' => 'nc', + 'ঙ্ক' => 'nk', + 'ঙ্ষ' => 'Ngx', + 'ঙ্গ' => 'ngo', + 'ঙ্ম' => 'Ngm', + 'ঞ্জ' => 'nj', + 'ন্ধ' => 'ndh', + 'ন্ঠ' => 'nTh', + 'ণ্ঠ' => 'NTh', + 'ন্থ' => 'nth', + 'ঙ্গা' => 'nga', + 'ঙ্গি' => 'ngi', + 'ঙ্গী' => 'ngI', + 'ঙ্গু' => 'ngu', + 'ঙ্গূ' => 'ngU', + 'ঙ্গে' => 'nge', + 'ঙ্গো' => 'ngO', + 'ণ্ঢ' => 'NDh', + 'নশ' => 'nsh', + 'ঙর' => 'Ngr', + 'ঞর' => 'NGr', + 'ংর' => 'ngr', + 'ঙ' => 'Ng', + 'ঞ' => 'NG', + 'ং' => 'ng', + 'ন্ন' => 'nn', + 'ণ্ণ' => 'NN', + 'ণ্ন' => 'Nn', + 'ন্ম' => 'nm', + 'ণ্ম' => 'Nm', + 'ন্দ' => 'nd', + 'ন্ট' => 'nT', + 'ণ্ট' => 'NT', + 'ন্ড' => 'nD', + 'ণ্ড' => 'ND', + 'ন্ত' => 'nt', + 'ন্স' => 'ns', + 'ন' => 'n', + 'ণ' => 'N', + 'ৈ' => 'OI', + 'ৌ' => 'OU', + 'ো' => 'O', + 'ঐ' => 'OI', + 'ঔ' => 'OU', + 'অ' => 'o', + 'ও' => 'oo', + 'ফ্ল' => 'fl', + 'প্ট' => 'pT', + 'প্ত' => 'pt', + 'প্ন' => 'pn', + 'প্প' => 'pp', + 'প্ল' => 'pl', + 'প্স' => 'ps', + 'ফ' => 'f', + 'প' => 'p', + 'ৃ' => 'rri', + 'ঋ' => 'rri', + 'রর‍্য' => 'rry', + '্র্য' => 'ry', + '্রর' => 'rr', + 'ড়্গ' => 'Rg', + 'ঢ়' => 'Rh', + 'ড়' => 'R', + 'র' => 'r', + '্র' => 'r', + 'শ্ছ' => 'Sch', + 'ষ্ঠ' => 'ShTh', + 'ষ্ফ' => 'Shf', + 'স্ক্ল' => 'skl', + 'স্খ' => 'skh', + 'স্থ' => 'sth', + 'স্ফ' => 'sf', + 'শ্চ' => 'Sc', + 'শ্ত' => 'St', + 'শ্ন' => 'Sn', + 'শ্ম' => 'Sm', + 'শ্ল' => 'Sl', + 'ষ্ক' => 'Shk', + 'ষ্ট' => 'ShT', + 'ষ্ণ' => 'ShN', + 'ষ্প' => 'Shp', + 'ষ্ম' => 'Shm', + 'স্প্ল' => 'spl', + 'স্ক' => 'sk', + 'স্ট' => 'sT', + 'স্ত' => 'st', + 'স্ন' => 'sn', + 'স্প' => 'sp', + 'স্ম' => 'sm', + 'স্ল' => 'sl', + 'শ' => 'S', + 'ষ' => 'Sh', + 'স' => 's', + 'ু' => 'u', + 'উ' => 'u', + 'অ্য' => 'oZ', + 'ত্থ' => 'tth', + 'ৎ' => 'tt', + 'ট্ট' => 'TT', + 'ট্ম' => 'Tm', + 'ঠ' => 'Th', + 'ত্ন' => 'tn', + 'ত্ম' => 'tm', + 'থ' => 'th', + 'ত্ত' => 'tt', + 'ট' => 'T', + 'ত' => 't', + 'অ্যা' => 'AZ', + 'া' => 'a', + 'আ' => 'a', + 'য়া' => 'ya', + 'য়' => 'y', + 'ি' => 'i', + 'ই' => 'i', + 'ী' => 'ee', + 'ঈ' => 'ee', + 'ূ' => 'uu', + 'ঊ' => 'uu', + 'ে' => 'e', + 'এ' => 'e', + 'য' => 'z', + '্য' => 'Z', + 'ইয়' => 'y', + 'ওয়' => 'w', + '্ব' => 'w', + 'এক্স' => 'x', + 'ঃ' => ':', + 'ঁ' => 'nn', + '্‌' => '', + ], + // English + 'en' => [ + ], + // Latin (+ Cyrillic ?) chars + // + // -> Mix of languages, but we need to keep this here, so that different languages can handle there own behavior. + 'latin' => [ + '˚' => '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', + '௰' => '10', + '௱' => '100', + '௲' => '1000', + 'Ꜳ' => 'AA', + 'ꜳ' => 'aa', + 'Æ' => 'AE', + 'æ' => 'ae', + 'Ǽ' => 'AE', + 'ǽ' => 'ae', + 'Ꜵ' => 'AO', + 'ꜵ' => 'ao', + 'Ꜷ' => 'AU', + 'ꜷ' => 'au', + 'Ꜹ' => 'AV', + 'ꜹ' => 'av', + 'Ꜻ' => 'av', + 'ꜻ' => 'av', + 'Ꜽ' => 'AY', + 'ꜽ' => 'ay', + 'ȸ' => 'db', + 'ʣ' => 'dz', + 'ʥ' => 'dz', + 'ʤ' => 'dezh', + '🙰' => 'et', + 'ff' => 'ff', + 'ffi' => 'ffi', + 'ffl' => 'ffl', + 'fi' => 'fi', + 'fl' => 'fl', + 'ʩ' => 'feng', + 'IJ' => 'IJ', + 'ij' => 'ij', + 'ʪ' => 'ls', + 'ʫ' => 'lz', + 'ɮ' => 'lezh', + 'ȹ' => 'qp', + 'ʨ' => 'tc', + 'ʦ' => 'ts', + 'ʧ' => 'tesh', + 'Œ' => 'OE', + 'œ' => 'oe', + 'Ꝏ' => 'OO', + 'ꝏ' => 'oo', + 'ẞ' => 'SS', + 'ß' => 'ss', + 'st' => 'st', + 'ſt' => 'st', + 'Ꜩ' => 'TZ', + 'ꜩ' => 'tz', + 'ᵫ' => 'ue', + 'Aι' => 'Ai', + 'αι' => 'ai', + 'Ει' => 'Ei', + 'ει' => 'ei', + 'Οι' => 'Oi', + 'οι' => 'oi', + 'Ου' => 'Oy', + 'ου' => 'oy', + 'Υι' => 'Yi', + 'υι' => 'yi', + 'ἀ' => 'a', + 'ἁ' => 'a', + 'ἂ' => 'a', + 'ἃ' => 'a', + 'ἄ' => 'a', + 'ἅ' => 'a', + 'ἆ' => 'a', + 'ἇ' => 'a', + 'Ἀ' => 'A', + 'Ἁ' => 'A', + 'Ἂ' => 'A', + 'Ἃ' => 'A', + 'Ἄ' => 'A', + 'Ἅ' => 'A', + 'Ἆ' => 'A', + 'Ἇ' => 'A', + 'ᾰ' => 'a', + 'ᾱ' => 'a', + 'ᾲ' => 'a', + 'ᾳ' => 'a', + 'ᾴ' => 'a', + 'ᾶ' => 'a', + 'ᾷ' => 'a', + 'Ᾰ' => 'A', + 'Ᾱ' => 'A', + 'Ὰ' => 'A', + 'Ά' => 'A', + 'ᾼ' => 'A', + 'Ä' => 'A', + 'ä' => 'a', + 'À' => 'A', + 'à' => 'a', + 'Á' => 'A', + 'á' => 'a', + 'Â' => 'A', + 'â' => 'a', + 'Ã' => 'A', + 'ã' => 'a', + 'A̧' => 'A', + 'a̧' => 'a', + 'Ą' => 'A', + 'ą' => 'a', + 'Ⱥ' => 'A', + 'ⱥ' => 'a', + 'Å' => 'A', + 'å' => 'a', + 'Ǻ' => 'A', + 'ǻ' => 'a', + 'Ă' => 'A', + 'ă' => 'a', + 'Ǎ' => 'A', + 'ǎ' => 'a', + 'Ȧ' => 'A', + 'ȧ' => 'a', + 'Ạ' => 'A', + 'ạ' => 'a', + 'Ā' => 'A', + 'ā' => 'a', + 'ª' => 'a', + 'Ɓ' => 'B', + 'Ѣ' => 'E', + 'ѣ' => 'e', + 'Ç' => 'C', + 'ç' => 'c', + 'Ĉ' => 'C', + 'ĉ' => 'c', + 'C̈' => 'C', + 'c̈' => 'c', + 'C̨' => 'C', + 'c̨' => 'c', + 'Ȼ' => 'C', + 'ȼ' => 'c', + 'Č' => 'C', + 'č' => 'c', + 'Ć' => 'C', + 'ć' => 'c', + 'C̀' => 'C', + 'c̀' => 'c', + 'Ċ' => 'C', + 'ċ' => 'c', + 'C̣' => 'C', + 'c̣' => 'c', + 'C̄' => 'C', + 'c̄' => 'c', + 'C̃' => 'C', + 'c̃' => 'c', + 'Ð' => 'D', + 'Đ' => 'D', + 'ð' => 'd', + 'đ' => 'd', + 'È' => 'E', + 'É' => 'E', + 'Ê' => 'E', + 'Ë' => 'E', + 'Ĕ' => 'E', + 'Ė' => 'E', + 'Ȩ' => 'E', + 'ȩ' => 'e', + 'Ę' => 'E', + 'ę' => 'e', + 'Ɇ' => 'E', + 'ɇ' => 'e', + 'Ě' => 'E', + 'ě' => 'e', + 'Ẹ' => 'E', + 'ẹ' => 'e', + 'Ē' => 'E', + 'ē' => 'e', + 'Ẽ' => 'E', + 'ẽ' => 'e', + 'è' => 'e', + 'é' => 'e', + 'ê' => 'e', + 'ë' => 'e', + 'ĕ' => 'e', + 'ė' => 'e', + 'ƒ' => 'f', + 'Ѳ' => 'F', + 'ѳ' => 'f', + 'Ĝ' => 'G', + 'Ġ' => 'G', + 'ĝ' => 'g', + 'ġ' => 'g', + 'Ĥ' => 'H', + 'Ħ' => 'H', + 'ĥ' => 'h', + 'ħ' => 'h', + 'Ì' => 'I', + 'Í' => 'I', + 'Î' => 'I', + 'Ï' => 'I', + 'Ĩ' => 'I', + 'Ĭ' => 'I', + 'Ǐ' => 'I', + 'Į' => 'I', + 'ì' => 'i', + 'í' => 'i', + 'î' => 'i', + 'ï' => 'i', + 'ĩ' => 'i', + 'ĭ' => 'i', + 'ǐ' => 'i', + 'į' => 'i', + 'І' => 'I', + 'і' => 'i', + 'I̧' => 'I', + 'i̧' => 'i', + 'Ɨ' => 'I', + 'ɨ' => 'i', + 'İ' => 'I', + 'i' => 'i', + 'Ị' => 'I', + 'ị' => 'i', + 'Ī' => 'I', + 'ī' => 'i', + 'Ĵ' => 'J', + 'ĵ' => 'j', + 'J́́' => 'J', + 'j́' => 'j', + 'J̀̀' => 'J', + 'j̀' => 'j', + 'J̈' => 'J', + 'j̈' => 'j', + 'J̧' => 'J', + 'j̧' => 'j', + 'J̨' => 'J', + 'j̨' => 'j', + 'Ɉ' => 'J', + 'ɉ' => 'j', + 'J̌' => 'J', + 'ǰ' => 'j', + 'J̇' => 'J', + 'j' => 'j', + 'J̣' => 'J', + 'j̣' => 'j', + 'J̄' => 'J', + 'j̄' => 'j', + 'J̃' => 'J', + 'j̃' => 'j', + 'Й' => 'i', + 'й' => 'i', + 'ĸ' => 'k', + 'Ĺ' => 'L', + 'Ľ' => 'L', + 'Ŀ' => 'L', + 'ĺ' => 'l', + 'ľ' => 'l', + 'ŀ' => 'l', + 'L̀' => 'L', + 'l̀' => 'l', + 'L̂' => 'L', + 'l̂' => 'l', + 'L̈' => 'L', + 'l̈' => 'l', + 'Ļ' => 'L', + 'ļ' => 'l', + 'L̨' => 'L', + 'l̨' => 'l', + 'Ł' => 'L', + 'ł' => 'l', + 'Ƚ' => 'L', + 'ƚ' => 'l', + 'L̇' => 'L', + 'l̇' => 'l', + 'Ḷ' => 'L', + 'ḷ' => 'l', + 'L̄' => 'L', + 'l̄' => 'l', + 'L̃' => 'L', + 'l̃' => 'l', + 'Ñ' => 'N', + 'ñ' => 'n', + 'Ŋ' => 'N', + 'ŋ' => 'n', + 'ʼn' => 'n', + 'Ń' => 'N', + 'ń' => 'n', + 'Ǹ' => 'N', + 'ǹ' => 'n', + 'N̂' => 'N', + 'n̂' => 'n', + 'N̈' => 'N', + 'n̈' => 'n', + 'Ņ' => 'N', + 'ņ' => 'n', + 'N̨' => 'N', + 'n̨' => 'n', + 'Ꞥ' => 'N', + 'ꞥ' => 'n', + 'Ň' => 'N', + 'ň' => 'n', + 'Ṅ' => 'N', + 'ṅ' => 'n', + 'Ṇ' => 'N', + 'ṇ' => 'n', + 'N̄' => 'N', + 'n̄' => 'n', + 'Ö' => 'O', + 'Ò' => 'O', + 'Ó' => 'O', + 'Ô' => 'O', + 'Õ' => 'O', + 'Ō' => 'O', + 'Ŏ' => 'O', + 'Ǒ' => 'O', + 'Ő' => 'O', + 'Ơ' => 'O', + 'Ø' => 'O', + 'Ǿ' => 'O', + 'ö' => 'o', + 'ò' => 'o', + 'ó' => 'o', + 'ô' => 'o', + 'õ' => 'o', + 'ō' => 'o', + 'ŏ' => 'o', + 'ǒ' => 'o', + 'ő' => 'o', + 'ơ' => 'o', + 'ø' => 'o', + 'ǿ' => 'o', + 'º' => 'o', + 'O̧' => 'O', + 'o̧' => 'o', + 'Ǫ' => 'O', + 'ǫ' => 'o', + 'Ɵ' => 'O', + 'ɵ' => 'o', + 'Ȯ' => 'O', + 'ȯ' => 'o', + 'Ọ' => 'O', + 'ọ' => 'o', + 'Ŕ' => 'R', + 'Ŗ' => 'R', + 'ŕ' => 'r', + 'ŗ' => 'r', + 'Ŝ' => 'S', + 'Ș' => 'S', + 'ș' => 's', + 'Ś' => 'S', + 'ś' => 's', + 'S̀' => 'S', + 's̀' => 's', + 'Ŝ̀' => 'S', + 'ŝ' => 's', + 'S̈' => 'S', + 's̈' => 's', + 'Ş' => 'S', + 'ş' => 's', + 'S̨' => 'S', + 's̨' => 's', + 'Ꞩ' => 'S', + 'ꞩ' => 's', + 'Š' => 'S', + 'š' => 's', + 'Ṡ' => 'S', + 'ṡ' => 's', + 'Ṣ' => 'S', + 'ṣ' => 's', + 'S̄' => 'S', + 's̄' => 's', + 'S̃' => 'S', + 's̃' => 's', + 'ſ' => 's', + 'Ţ' => 'T', + 'Ț' => 'T', + 'Ŧ' => 'T', + 'Þ' => 'TH', + 'ţ' => 't', + 'ț' => 't', + 'ŧ' => 't', + 'þ' => 'th', + 'T́' => 'T', + 't́' => 't', + 'T̀' => 'T', + 't̀' => 't', + 'T̂' => 'T', + 't̂' => 't', + 'T̈' => 'T', + 'ẗ' => 't', + 'T̨' => 'T', + 't̨' => 't', + 'Ⱦ' => 'T', + 'ⱦ' => 't', + 'Ť' => 'T', + 'ť' => 't', + 'Ṫ' => 'T', + 'ṫ' => 't', + 'Ṭ' => 'T', + 'ṭ' => 't', + 'T̄' => 'T', + 't̄' => 't', + 'T̃' => 'T', + 't̃' => 't', + 'Ü' => 'U', + 'Ù' => 'U', + 'Ú' => 'U', + 'Û' => 'U', + 'Ũ' => 'U', + 'Ŭ' => 'U', + 'Ű' => 'U', + 'Ų' => 'U', + 'Ư' => 'U', + 'Ǔ' => 'U', + 'Ǖ' => 'U', + 'Ǘ' => 'U', + 'Ǚ' => 'U', + 'Ǜ' => 'U', + 'ü' => 'u', + 'ù' => 'u', + 'ú' => 'u', + 'û' => 'u', + 'ũ' => 'u', + 'ŭ' => 'u', + 'ű' => 'u', + 'ų' => 'u', + 'ư' => 'u', + 'ǔ' => 'u', + 'ǖ' => 'u', + 'ǘ' => 'u', + 'ǚ' => 'u', + 'ǜ' => 'u', + 'U̧' => 'U', + 'u̧' => 'u', + 'Ʉ' => 'U', + 'ʉ' => 'u', + 'U̇' => 'U', + 'u̇' => 'u', + 'Ụ' => 'U', + 'ụ' => 'u', + 'Ū' => 'U', + 'ū' => 'u', + 'Ʊ' => 'U', + 'ʊ' => 'u', + 'Ŵ' => 'W', + 'ŵ' => 'w', + 'Ẁ' => 'W', + 'ẁ' => 'w', + 'Ẃ' => 'W', + 'ẃ' => 'w', + 'Ẅ' => 'W', + 'ẅ' => 'w', + 'Ѵ' => 'I', + 'ѵ' => 'i', + 'Ꙗ' => 'Ja', + 'ꙗ' => 'ja', + 'Є' => 'Je', + 'є' => 'je', + 'Ѥ' => 'Je', + 'ѥ' => 'je', + 'Ѕ' => 'Dz', + 'ѕ' => 'dz', + 'Ꙋ' => 'U', + 'ꙋ' => 'u', + 'Ѡ' => 'O', + 'ѡ' => 'o', + 'Ѿ' => 'Ot', + 'ѿ' => 'ot', + 'Ѫ' => 'U', + 'ѫ' => 'u', + 'Ѧ' => 'Ja', + 'ѧ' => 'ja', + 'Ѭ' => 'Ju', + 'ѭ' => 'ju', + 'Ѩ' => 'Ja', + 'ѩ' => 'Ja', + 'Ѯ' => 'Ks', + 'ѯ' => 'ks', + 'Ѱ' => 'Ps', + 'ѱ' => 'ps', + 'Х' => 'X', + 'х' => 'x', + 'Ý' => 'Y', + 'Ÿ' => 'Y', + 'Ŷ' => 'Y', + 'ý' => 'y', + 'ÿ' => 'y', + 'ŷ' => 'y', + 'Ỳ' => 'Y', + 'ỳ' => 'y', + 'Y̧' => 'Y', + 'y̧' => 'y', + 'Y̨' => 'Y', + 'y̨' => 'y', + 'Ɏ' => 'Y', + 'ɏ' => 'y', + 'Y̌' => 'Y', + 'y̌' => 'y', + 'Ẏ' => 'Y', + 'ẏ' => 'y', + 'Ỵ' => 'Y', + 'ỵ' => 'y', + 'Ȳ' => 'Y', + 'ȳ' => 'y', + 'Ỹ' => 'Y', + 'ỹ' => 'y', + 'Щ' => 'Shh', + 'щ' => 'shh', + 'Ź' => 'Z', + 'ź' => 'z', + 'Z̀' => 'Z', + 'z̀' => 'z', + 'Ẑ' => 'Z', + 'ẑ' => 'z', + 'Z̈' => 'Z', + 'z̈' => 'z', + 'Z̧' => 'Z', + 'z̧' => 'z', + 'Z̨' => 'Z', + 'z̨' => 'z', + 'Ƶ' => 'Z', + 'ƶ' => 'z', + 'Ž' => 'Z', + 'ž' => 'z', + 'Ż' => 'Z', + 'ż' => 'z', + 'Ẓ' => 'Z', + 'ẓ' => 'z', + 'Z̄' => 'Z', + 'z̄' => 'z', + 'Z̃' => 'Z', + 'z̃' => 'z', + ], + // whitespace chars + ' ' => [ + "\xc2\xa0" => ' ', // 'NO-BREAK SPACE' + "\xe1\x9a\x80" => ' ', // 'OGHAM SPACE MARK' + "\xe2\x80\x80" => ' ', // 'EN QUAD' + "\xe2\x80\x81" => ' ', // 'EM QUAD' + "\xe2\x80\x82" => ' ', // 'EN SPACE' + "\xe2\x80\x83" => ' ', // 'EM SPACE' + "\xe2\x80\x84" => ' ', // 'THREE-PER-EM SPACE' + "\xe2\x80\x85" => ' ', // 'FOUR-PER-EM SPACE' + "\xe2\x80\x86" => ' ', // 'SIX-PER-EM SPACE' + "\xe2\x80\x87" => ' ', // 'FIGURE SPACE' + "\xe2\x80\x88" => ' ', // 'PUNCTUATION SPACE' + "\xe2\x80\x89" => ' ', // 'THIN SPACE' + "\xe2\x80\x8a" => ' ', // 'HAIR SPACE' + "\xe2\x80\xa8" => ' ', // 'LINE SEPARATOR' + "\xe2\x80\xa9" => ' ', // 'PARAGRAPH SEPARATOR' + "\xe2\x80\x8b" => ' ', // 'ZERO WIDTH SPACE' + "\xe2\x80\xaf" => ' ', // 'NARROW NO-BREAK SPACE' + "\xe2\x81\x9f" => ' ', // 'MEDIUM MATHEMATICAL SPACE' + "\xe3\x80\x80" => ' ', // 'IDEOGRAPHIC SPACE' + "\xef\xbe\xa0" => ' ', // 'HALFWIDTH HANGUL FILLER' + ], + // commonly used in Word documents + 'msword' => [ + "\xc2\xab" => '<<', // « (U+00AB) in UTF-8 + "\xc2\xbb" => '>>', // » (U+00BB) in UTF-8 + "\xe2\x80\x98" => "'", // ‘ (U+2018) in UTF-8 + "\xe2\x80\x99" => "'", // ’ (U+2019) in UTF-8 + "\xe2\x80\x9a" => "'", // ‚ (U+201A) in UTF-8 + "\xe2\x80\x9b" => "'", // ‛ (U+201B) in UTF-8 + "\xe2\x80\x9c" => '"', // “ (U+201C) in UTF-8 + "\xe2\x80\x9d" => '"', // ” (U+201D) in UTF-8 + "\xe2\x80\x9e" => '"', // „ (U+201E) in UTF-8 + "\xe2\x80\x9f" => '"', // ‟ (U+201F) in UTF-8 + "\xe2\x80\xb9" => "'", // ‹ (U+2039) in UTF-8 + "\xe2\x80\xba" => "'", // › (U+203A) in UTF-8 + "\xe2\x80\x93" => '-', // – (U+2013) in UTF-8 + "\xe2\x80\x94" => '-', // — (U+2014) in UTF-8 + "\xe2\x80\xa6" => '...', // … (U+2026) in UTF-8 + ], + // Currency + // + // url => https://en.wikipedia.org/wiki/Currency_symbol + 'currency_short' => [ + '€' => 'EUR', + '$' => '$', + '₢' => 'Cr', + '₣' => 'Fr.', + '£' => 'PS', + '₤' => 'L.', + 'ℳ' => 'M', + '₥' => 'mil', + '₦' => 'N', + '₧' => 'Pts', + '₨' => 'Rs', + 'රු' => 'LKR', + 'ரூ' => 'LKR', + '௹' => 'Rs', + 'रू' => 'NPR', + '₹' => 'Rs', + '૱' => 'Rs', + '₩' => 'W', + '₪' => 'NS', + '₸' => 'KZT', + '₫' => 'D', + '֏' => 'AMD', + '₭' => 'K', + '₺' => 'TL', + '₼' => 'AZN', + '₮' => 'T', + '₯' => 'Dr', + '₲' => 'PYG', + '₾' => 'GEL', + '₳' => 'ARA', + '₴' => 'UAH', + '₽' => 'RUB', + '₵' => 'GHS', + '₡' => 'CL', + '¢' => 'c', + '¥' => 'YEN', + '円' => 'JPY', + '৳' => 'BDT', + '元' => 'CNY', + '﷼' => 'SAR', + '៛' => 'KR', + '₠' => 'ECU', + '¤' => '$?', + '฿' => 'THB', + '؋' => 'AFN', + ], +]; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/ascii_extras_by_languages.php b/vendor/voku/portable-ascii/src/voku/helper/data/ascii_extras_by_languages.php new file mode 100644 index 0000000..afe31ae --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/ascii_extras_by_languages.php @@ -0,0 +1,759 @@ + [ + '=' => ' gelijk ', + '%' => ' procent ', + '∑' => ' som ', + '∆' => ' delta ', + '∞' => ' oneindig ', + '♥' => ' love ', + '&' => ' en ', + '+' => ' plus ', + ], + // Italian + 'it' => [ + '=' => ' uguale ', + '%' => ' percent ', + '∑' => ' somma ', + '∆' => ' delta ', + '∞' => ' infinito ', + '♥' => ' amore ', + '&' => ' e ', + '+' => ' piu ', + ], + // Macedonian + 'mk' => [ + '=' => ' ednakva ', + '%' => ' procenti ', + '∑' => ' zbir ', + '∆' => ' delta ', + '∞' => ' beskonecnost ', + '♥' => ' loveubov ', + '&' => ' i ', + '+' => ' plus ', + ], + // Portuguese (Brazil) + 'pt' => [ + '=' => ' igual ', + '%' => ' por cento ', + '∑' => ' soma ', + '∆' => ' delta ', + '∞' => ' infinito ', + '♥' => ' amor ', + '&' => ' e ', + '+' => ' mais ', + ], + // Greek(lish) (Elláda) + 'el__greeklish' => [ + '=' => ' isos ', + '%' => ' tois ekato ', + '∑' => ' athroisma ', + '∆' => ' delta ', + '∞' => ' apeiro ', + '♥' => ' agape ', + '&' => ' kai ', + '+' => ' syn ', + ], + // Greek (Elláda) + 'el' => [ + '=' => ' isos ', + '%' => ' tois ekato ', + '∑' => ' athroisma ', + '∆' => ' delta ', + '∞' => ' apeiro ', + '♥' => ' agape ', + '&' => ' kai ', + '+' => ' syn ', + ], + // Hindi + 'hi' => [ + '=' => ' samana ', + '%' => ' paratisata ', + '∑' => ' yoga ', + '∆' => ' dalata ', + '∞' => ' anata ', + '♥' => ' payara ', + '&' => ' aura ', + '+' => ' palasa ', + ], + // Armenian + 'hy' => [ + '=' => ' havasar ', + '%' => ' tvokvos ', + '∑' => ' gvoumar ', + '∆' => ' delta ', + '∞' => ' ansahmanvouthyvoun ', + '♥' => ' ser ', + '&' => ' ev ', + '+' => ' gvoumarats ', + ], + // Swedish + 'sv' => [ + '=' => ' lika ', + '%' => ' procent ', + '∑' => ' summa ', + '∆' => ' delta ', + '∞' => ' oandlighet ', + '♥' => ' alskar ', + '&' => ' och ', + '+' => ' plus ', + ], + // Turkmen + 'tk' => [ + '=' => ' den ', + '%' => ' yuzde ', + '∑' => ' jem ', + '∆' => ' delta ', + '∞' => ' mudimilik ', + '♥' => ' soygi ', + '&' => ' we ', + '+' => ' yzy ', + ], + // Turkish + 'tr' => [ + '=' => ' esit ', + '%' => ' yuzde ', + '∑' => ' Toplam ', + '∆' => ' delta ', + '∞' => ' sonsuzluk ', + '♥' => ' ask ', + '&' => ' ve ', + '+' => ' arti ', + ], + // Bulgarian + 'bg' => [ + '=' => ' raven ', + '%' => ' na sto ', + '∑' => ' suma ', + '∆' => ' delta ', + '∞' => ' bezkrajnost ', + '♥' => ' obicam ', + '&' => ' i ', + '+' => ' plus ', + ], + // Hungarian + 'hu' => [ + '=' => ' Egyenlo ', + '%' => ' Szazalek ', + '∑' => ' osszeg ', + '∆' => ' delta ', + '∞' => ' vegtelenitett ', + '♥' => ' love ', + '&' => ' Es ', + '+' => ' Plusz ', + ], + // Myanmar (Burmese) + 'my' => [ + '=' => ' ttn:ttnnym? ', + '%' => ' raakhngnn:k ', + '∑' => ' ld ', + '∆' => ' m?cwk?n:pe? ', + '∞' => ' ach:m ', + '♥' => ' mettttaa ', + '&' => ' n ', + '+' => ' ape?ng: ', + ], + // Croatian (Hrvatska) + 'hr' => [ + '=' => ' Jednaki ', + '%' => ' Posto ', + '∑' => ' zbroj ', + '∆' => ' Delta ', + '∞' => ' beskonacno ', + '♥' => ' ljubav ', + '&' => ' I ', + '+' => ' Plus ', + ], + // Finnish + 'fi' => [ + '=' => ' Sama ', + '%' => ' Prosenttia ', + '∑' => ' sum ', + '∆' => ' delta ', + '∞' => ' aareton ', + '♥' => ' rakkautta ', + '&' => ' Ja ', + '+' => ' Plus ', + ], + // Georgian (Kartvelian) + 'ka' => [ + '=' => ' tanasts\'ori ', + '%' => ' p\'rotsent\'i ', + '∑' => ' tankha ', + '∆' => ' delt\'a ', + '∞' => ' usasrulo ', + '♥' => ' siq\'varuli ', + '&' => ' da ', + '+' => ' p\'lus ', + ], + // Russian + 'ru' => [ + '=' => ' ravnyj ', + '%' => ' procent ', + '∑' => ' summa ', + '∆' => ' del\'ta ', + '∞' => ' beskonecnost\' ', + '♥' => ' lublu ', + '&' => ' i ', + '+' => ' plus ', + ], + // Russian - GOST 7.79-2000(B) + 'ru__gost_2000_b' => [ + '=' => ' ravnyj ', + '%' => ' procent ', + '∑' => ' summa ', + '∆' => ' del\'ta ', + '∞' => ' beskonecnost\' ', + '♥' => ' lublu ', + '&' => ' i ', + '+' => ' plus ', + ], + // Russian - Passport (2013), ICAO + 'ru__passport_2013' => [ + '=' => ' ravnyj ', + '%' => ' procent ', + '∑' => ' summa ', + '∆' => ' del\'ta ', + '∞' => ' beskonecnost\' ', + '♥' => ' lublu ', + '&' => ' i ', + '+' => ' plus ', + ], + // Ukrainian + 'uk' => [ + '=' => ' rivnij ', + '%' => ' vidsotkiv ', + '∑' => ' suma ', + '∆' => ' del\'ta ', + '∞' => ' neskincennist\' ', + '♥' => ' lubov ', + '&' => ' i ', + '+' => ' plus ', + ], + // Kazakh + 'kk' => [ + '=' => ' ten\' ', + '%' => ' Pajyzdar ', + '∑' => ' zalpy ', + '∆' => ' ajyrmasylyk, ', + '∞' => ' seksiz ', + '♥' => ' mahabbat ', + '&' => ' z@ne ', + '+' => ' plus ', + ], + // Czech + 'cs' => [ + '=' => ' rovnat se ', + '%' => ' procento ', + '∑' => ' soucet ', + '∆' => ' delta ', + '∞' => ' nekonecno ', + '♥' => ' laska ', + '&' => ' a ', + '+' => ' plus ', + ], + // Danish + 'da' => [ + '=' => ' Lige ', + '%' => ' Prozent ', + '∑' => ' sum ', + '∆' => ' delta ', + '∞' => ' uendelig ', + '♥' => ' kaerlighed ', + '&' => ' Og ', + '+' => ' Plus ', + ], + // Polish + 'pl' => [ + '=' => ' rowny ', + '%' => ' procent ', + '∑' => ' suma ', + '∆' => ' delta ', + '∞' => ' nieskonczonosc ', + '♥' => ' milosc ', + '&' => ' i ', + '+' => ' plus ', + ], + // Romanian + 'ro' => [ + '=' => ' egal ', + '%' => ' la suta ', + '∑' => ' suma ', + '∆' => ' delta ', + '∞' => ' infinit ', + '♥' => ' dragoste ', + '&' => ' si ', + '+' => ' la care se adauga ', + ], + // Esperanto + 'eo' => [ + '=' => ' Egalaj ', + '%' => ' Procento ', + '∑' => ' sumo ', + '∆' => ' delto ', + '∞' => ' senfina ', + '♥' => ' amo ', + '&' => ' Kaj ', + '+' => ' Pli ', + ], + // Estonian + 'et' => [ + '=' => ' Vordsed ', + '%' => ' Protsenti ', + '∑' => ' summa ', + '∆' => ' o ', + '∞' => ' loputut ', + '♥' => ' armastus ', + '&' => ' Ja ', + '+' => ' Pluss ', + ], + // Latvian + 'lv' => [ + '=' => ' vienads ', + '%' => ' procents ', + '∑' => ' summa ', + '∆' => ' delta ', + '∞' => ' bezgaliba ', + '♥' => ' milestiba ', + '&' => ' un ', + '+' => ' pluss ', + ], + // Lithuanian + 'lt' => [ + '=' => ' lygus ', + '%' => ' procentu ', + '∑' => ' suma ', + '∆' => ' delta ', + '∞' => ' begalybe ', + '♥' => ' meile ', + '&' => ' ir ', + '+' => ' plius ', + ], + // Norwegian + 'no' => [ + '=' => ' Lik ', + '%' => ' Prosent ', + '∑' => ' sum ', + '∆' => ' delta ', + '∞' => ' uendelig ', + '♥' => ' kjaerlighet ', + '&' => ' Og ', + '+' => ' Pluss ', + ], + // Vietnamese + 'vi' => [ + '=' => ' cong bang ', + '%' => ' phan tram ', + '∑' => ' tong so ', + '∆' => ' dong bang ', + '∞' => ' vo cuc ', + '♥' => ' Yeu ', + '&' => ' va ', + '+' => ' them ', + ], + // Arabic + 'ar' => [ + '=' => ' mtsawy ', + '%' => ' nsbh mywyh ', + '∑' => ' mjmw\' ', + '∆' => ' dlta ', + '∞' => ' ma la nhayt ', + '♥' => ' hb ', + '&' => ' w ', + '+' => ' zayd ', + ], + // Persian (Farsi) + 'fa' => [ + '=' => ' brabr ', + '%' => ' dr sd ', + '∑' => ' mjmw\' ', + '∆' => ' dlta ', + '∞' => ' by nhayt ', + '♥' => ' \'shq ', + '&' => ' w ', + '+' => ' bh \'lawh ', + ], + // Serbian + 'sr' => [ + '=' => ' jednak ', + '%' => ' procenat ', + '∑' => ' zbir ', + '∆' => ' delta ', + '∞' => ' beskraj ', + '♥' => ' lubav ', + '&' => ' i ', + '+' => ' vise ', + ], + // Serbian - Cyrillic + 'sr__cyr' => [ + '=' => ' jednak ', + '%' => ' procenat ', + '∑' => ' zbir ', + '∆' => ' delta ', + '∞' => ' beskraj ', + '♥' => ' lubav ', + '&' => ' i ', + '+' => ' vise ', + ], + // Serbian - Latin + 'sr__lat' => [ + '=' => ' jednak ', + '%' => ' procenat ', + '∑' => ' zbir ', + '∆' => ' delta ', + '∞' => ' beskraj ', + '♥' => ' lubav ', + '&' => ' i ', + '+' => ' vise ', + ], + // Azerbaijani + 'az' => [ + '=' => ' b@rab@r ', + '%' => ' faiz ', + '∑' => ' m@bl@g ', + '∆' => ' delta ', + '∞' => ' sonsuzluq ', + '♥' => ' sevgi ', + '&' => ' v@ ', + '+' => ' plus ', + ], + // Slovak + 'sk' => [ + '=' => ' rovny ', + '%' => ' percento ', + '∑' => ' sucet ', + '∆' => ' delta ', + '∞' => ' infinity ', + '♥' => ' milovat ', + '&' => ' a ', + '+' => ' viac ', + ], + // French + 'fr' => [ + '=' => ' Egal ', + '%' => ' Pourcentage ', + '∑' => ' somme ', + '∆' => ' delta ', + '∞' => ' infini ', + '♥' => ' amour ', + '&' => ' Et ', + '+' => ' Plus ', + ], + // Austrian (French) + 'fr_at' => [ + '=' => ' Egal ', + '%' => ' Pourcentage ', + '∑' => ' somme ', + '∆' => ' delta ', + '∞' => ' infini ', + '♥' => ' amour ', + '&' => ' Et ', + '+' => ' Plus ', + ], + // Switzerland (French) + 'fr_ch' => [ + '=' => ' Egal ', + '%' => ' Pourcentage ', + '∑' => ' somme ', + '∆' => ' delta ', + '∞' => ' infini ', + '♥' => ' amour ', + '&' => ' Et ', + '+' => ' Plus ', + ], + // German + 'de' => [ + '=' => ' gleich ', + '%' => ' Prozent ', + '∑' => ' gesamt ', + '∆' => ' Unterschied ', + '∞' => ' undendlich ', + '♥' => ' liebe ', + '&' => ' und ', + '+' => ' plus ', + ], + // Austrian (German) + 'de_at' => [ + '=' => ' gleich ', + '%' => ' Prozent ', + '∑' => ' gesamt ', + '∆' => ' Unterschied ', + '∞' => ' undendlich ', + '♥' => ' liebe ', + '&' => ' und ', + '+' => ' plus ', + ], + // Switzerland (German) + 'de_ch' => [ + '=' => ' gleich ', + '%' => ' Prozent ', + '∑' => ' gesamt ', + '∆' => ' Unterschied ', + '∞' => ' undendlich ', + '♥' => ' liebe ', + '&' => ' und ', + '+' => ' plus ', + ], + // Bengali (Bangla) + 'bn' => [ + '=' => ' Saman ', + '%' => ' Satakora ', + '∑' => ' Samasti ', + '∆' => ' Badhip ', + '∞' => ' Ananta ', + '♥' => ' Valobasa ', + '&' => ' Abong ', + '+' => ' Songzojon ', + ], + // English + 'en' => [ + '=' => ' equal ', + '%' => ' percent ', + '∑' => ' sum ', + '∆' => ' delta ', + '∞' => ' infinity ', + '♥' => ' love ', + '&' => ' and ', + '+' => ' plus ', + ], + // Currency + // + // url: https://en.wikipedia.org/wiki/Currency_symbol + 'currency' => [ + '€' => ' Euro ', + '$' => ' Dollar ', + '₢' => ' cruzeiro ', + '₣' => ' French franc ', + '£' => ' pound ', + '₤' => ' lira ', // Italian + '₶' => ' livre tournois ', + 'ℳ' => ' mark ', + '₥' => ' mill ', + '₦' => ' naira ', + '₧' => ' peseta ', + '₨' => ' rupee ', + 'රු' => ' rupee ', // Sri Lankan + 'ரூ' => ' rupee ', // Sri Lankan + '௹' => ' rupee ', // Tamil + 'रू' => ' rupee ', // Nepalese + '₹' => ' rupee ', // Indian + '૱' => ' rupee ', // Gujarat + '₩' => ' won ', + '₪' => ' new shequel ', + '₸' => ' tenge ', + '₫' => ' dong ', + '֏' => ' dram ', + '₭' => ' kip ', + '₺' => ' lira ', // Turkish + '₼' => ' manat ', + '₮' => ' tugrik ', + '₯' => ' drachma ', + '₰' => ' pfennig ', + '₷' => ' spesmilo ', + '₱' => ' peso ', // Philippine + '﷼‎' => ' riyal ', + '₲' => ' guarani ', + '₾' => ' lari ', + '₳' => ' austral ', + '₴' => ' hryvnia ', + '₽' => ' ruble ', + '₵' => ' cedi ', + '₡' => ' colon ', + '¢' => ' cent ', + '¥' => ' yen ', + '円' => ' yen ', + '৳' => ' taka ', + '元' => ' yuan ', + '﷼' => ' riyal ', + '៛' => ' riel ', + '₠' => ' European Currency ', + '¤' => ' currency ', + '฿' => ' baht ', + '؋' => ' afghani ', + ], + // Temperature + // + // url: https://en.wikipedia.org/wiki/Conversion_of_units_of_temperature + 'temperature' => [ + '°De' => ' Delisle ', + '°Re' => ' Reaumur ', // Réaumur + '°Ro' => ' Romer ', // Rømer + '°R' => ' Rankine ', + '°C' => ' Celsius ', + '°F' => ' Fahrenheit ', + '°N' => ' Newton ', + ], + 'latin_symbols' => [ + '=' => '=', + '%' => '%', + '∑' => '∑', + '∆' => '∆', + '∞' => '∞', + '♥' => '♥', + '&' => '&', + '+' => '+', + // --- + '©' => ' (c) ', + '®' => ' (r) ', + '@' => ' (at) ', + '№' => ' No. ', + '℞' => ' Rx ', + '[' => '[', + '\' => '\\', + ']' => ']', + '^' => '^', + '_' => '_', + '`' => '`', + '‐' => '-', + '‑' => '-', + '‒' => '-', + '–' => '-', + '−' => '-', + '—' => '-', + '―' => '-', + '﹘' => '-', + '│' => '|', + '∖' => '\\', + '∕' => '/', + '⁄' => '/', + '←' => '<-', + '→' => '->', + '↑' => '|', + '↓' => '|', + '⁅' => '[', + '⁆' => ']', + '⁎' => '*', + '、' => ',', + '。' => '.', + '〈' => '<', + '〉' => '>', + '《' => '<<', + '》' => '>>', + '〔' => '[', + '〕' => ']', + '〘' => '[', + '〙' => ']', + '〚' => '[', + '〛' => ']', + '﹝' => '[', + '﹞' => ']', + '︹' => '[', + '︺' => ']', + '﹇' => '[', + '﹈' => ']', + '︐' => ',', + '︑' => ',', + '︒' => '.', + '︓' => ':', + '︔' => ';', + '︕' => '!', + '︖' => '?', + '︙' => '...', + '︰' => '..', + '︵' => '(', + '︶' => ')', + '﹙' => '(', + '﹚' => ')', + '︷' => '{', + '︸' => '}', + '﹛' => '{', + '﹜' => '}', + '︽' => '<<', + '︾' => '>>', + '︿' => '<', + '﹀' => '>', + '×' => '*', + '÷' => '/', + '≪' => '<<', + '≫' => '>>', + '⦅' => '((', + '⦆' => '))', + '〇' => '0', + '′' => '\'', + '〝' => '"', + '〞' => '"', + '«' => '<<', + '»' => '>>', + '‘' => "'", + '’' => "'", + '‚' => ',', + '‛' => "'", + '“' => '"', + '”' => '"', + '„' => '"', + '‟' => '"', + '‹' => '<', + '›' => '>', + '․' => '.', + '‥' => '..', + '…' => '...', + '″' => '"', + '‴' => '\'\'\'', + '‶' => '``', + '‷' => '```', + '‼' => '!!', + '⁇' => '??', + '⁈' => '?!', + '⁉' => '!?', + '⁗' => '````', + '⩴' => '::=', + '⩵' => '==', + '⩶' => '===', + '﹔' => ';', + '﹕' => ':', + '﹖' => '?', + '﹗' => '!', + '﹍' => '_', + '﹎' => '_', + '﹏' => '_', + '﹐' => ',', + '﹑' => ',', + '﹒' => '.', + '﹟' => '#', + '﹠' => '&', + '﹡' => '*', + '﹢' => '+', + '﹣' => '-', + '﹤' => '<', + '﹥' => '>', + '﹦' => '=', + '﹨' => '\\', + '﹩' => '$', + '﹪' => '%', + '﹫' => '@', + '!' => '!', + '"' => '"', + '#' => '#', + '$' => '$', + '%' => '%', + '&' => '&', + ''' => '\'', + '(' => '(', + ')' => ')', + '*' => '*', + '+' => '+', + ',' => ',', + '-' => '-', + '.' => '.', + '/' => '/', + ':' => ':', + ';' => ';', + '<' => '<', + '=' => '=', + '>' => '>', + '?' => '?', + '@' => '@', + '{' => '{', + '|' => '|', + '}' => '}', + '~' => '~', + '⦅' => '((', + '⦆' => '))', + '¬' => '!', + ' ̄' => '-', + '¦' => '|', + '■' => '#', + ], +]; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/ascii_language_max_key.php b/vendor/voku/portable-ascii/src/voku/helper/data/ascii_language_max_key.php new file mode 100644 index 0000000..da81ae2 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/ascii_language_max_key.php @@ -0,0 +1,65 @@ + 0, + 'tk' => 1, + 'th' => 0, + 'ps' => 0, + 'or' => 0, + 'mn' => 0, + 'ko' => 0, + 'ky' => 0, + 'hy' => 1, + 'bn' => 5, + 'be' => 0, + 'am' => 0, + 'ja' => 0, + 'zh' => 0, + 'nl' => 1, + 'it' => 1, + 'mk' => 1, + 'pt' => 1, + 'el__greeklish' => 2, + 'el' => 2, + 'hi' => 2, + 'sv' => 1, + 'tr' => 1, + 'bg' => 2, + 'hu' => 1, + 'my' => 5, + 'hr' => 2, + 'fi' => 1, + 'ka' => 1, + 'ru' => 1, + 'ru__gost_2000_b' => 1, + 'ru__passport_2013' => 1, + 'uk' => 1, + 'kk' => 1, + 'cs' => 1, + 'da' => 1, + 'pl' => 1, + 'ro' => 1, + 'eo' => 1, + 'et' => 1, + 'lv' => 1, + 'lt' => 1, + 'no' => 1, + 'vi' => 1, + 'ar' => 1, + 'fa' => 1, + 'sr' => 1, + 'sr__cyr' => 1, + 'sr__lat' => 1, + 'az' => 1, + 'sk' => 1, + 'fr' => 1, + 'fr_at' => 1, + 'fr_ch' => 1, + 'de' => 1, + 'de_at' => 1, + 'de_ch' => 1, + 'en' => 0, + 'latin' => 3, + ' ' => 1, + 'msword' => 1, +]; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/ascii_ord.php b/vendor/voku/portable-ascii/src/voku/helper/data/ascii_ord.php new file mode 100644 index 0000000..142318c --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/ascii_ord.php @@ -0,0 +1 @@ + 0, "\x00" => 0, "\x01" => 1, "\x02" => 2, "\x03" => 3, "\x04" => 4, "\x05" => 5, "\x06" => 6, "\x07" => 7, "\x08" => 8, "\x09" => 9, "\x0A" => 10, "\x0B" => 11, "\x0C" => 12, "\x0D" => 13, "\x0E" => 14, "\x0F" => 15, "\x10" => 16, "\x11" => 17, "\x12" => 18, "\x13" => 19, "\x14" => 20, "\x15" => 21, "\x16" => 22, "\x17" => 23, "\x18" => 24, "\x19" => 25, "\x1A" => 26, "\x1B" => 27, "\x1C" => 28, "\x1D" => 29, "\x1E" => 30, "\x1F" => 31, "\x20" => 32, "\x21" => 33, "\x22" => 34, "\x23" => 35, "\x24" => 36, "\x25" => 37, "\x26" => 38, "\x27" => 39, "\x28" => 40, "\x29" => 41, "\x2A" => 42, "\x2B" => 43, "\x2C" => 44, "\x2D" => 45, "\x2E" => 46, "\x2F" => 47, "\x30" => 48, "\x31" => 49, "\x32" => 50, "\x33" => 51, "\x34" => 52, "\x35" => 53, "\x36" => 54, "\x37" => 55, "\x38" => 56, "\x39" => 57, "\x3A" => 58, "\x3B" => 59, "\x3C" => 60, "\x3D" => 61, "\x3E" => 62, "\x3F" => 63, "\x40" => 64, "\x41" => 65, "\x42" => 66, "\x43" => 67, "\x44" => 68, "\x45" => 69, "\x46" => 70, "\x47" => 71, "\x48" => 72, "\x49" => 73, "\x4A" => 74, "\x4B" => 75, "\x4C" => 76, "\x4D" => 77, "\x4E" => 78, "\x4F" => 79, "\x50" => 80, "\x51" => 81, "\x52" => 82, "\x53" => 83, "\x54" => 84, "\x55" => 85, "\x56" => 86, "\x57" => 87, "\x58" => 88, "\x59" => 89, "\x5A" => 90, "\x5B" => 91, "\x5C" => 92, "\x5D" => 93, "\x5E" => 94, "\x5F" => 95, "\x60" => 96, "\x61" => 97, "\x62" => 98, "\x63" => 99, "\x64" => 100, "\x65" => 101, "\x66" => 102, "\x67" => 103, "\x68" => 104, "\x69" => 105, "\x6A" => 106, "\x6B" => 107, "\x6C" => 108, "\x6D" => 109, "\x6E" => 110, "\x6F" => 111, "\x70" => 112, "\x71" => 113, "\x72" => 114, "\x73" => 115, "\x74" => 116, "\x75" => 117, "\x76" => 118, "\x77" => 119, "\x78" => 120, "\x79" => 121, "\x7A" => 122, "\x7B" => 123, "\x7C" => 124, "\x7D" => 125, "\x7E" => 126, "\x7F" => 127, "\x80" => 128, "\x81" => 129, "\x82" => 130, "\x83" => 131, "\x84" => 132, "\x85" => 133, "\x86" => 134, "\x87" => 135, "\x88" => 136, "\x89" => 137, "\x8A" => 138, "\x8B" => 139, "\x8C" => 140, "\x8D" => 141, "\x8E" => 142, "\x8F" => 143, "\x90" => 144, "\x91" => 145, "\x92" => 146, "\x93" => 147, "\x94" => 148, "\x95" => 149, "\x96" => 150, "\x97" => 151, "\x98" => 152, "\x99" => 153, "\x9A" => 154, "\x9B" => 155, "\x9C" => 156, "\x9D" => 157, "\x9E" => 158, "\x9F" => 159, "\xA0" => 160, "\xA1" => 161, "\xA2" => 162, "\xA3" => 163, "\xA4" => 164, "\xA5" => 165, "\xA6" => 166, "\xA7" => 167, "\xA8" => 168, "\xA9" => 169, "\xAA" => 170, "\xAB" => 171, "\xAC" => 172, "\xAD" => 173, "\xAE" => 174, "\xAF" => 175, "\xB0" => 176, "\xB1" => 177, "\xB2" => 178, "\xB3" => 179, "\xB4" => 180, "\xB5" => 181, "\xB6" => 182, "\xB7" => 183, "\xB8" => 184, "\xB9" => 185, "\xBA" => 186, "\xBB" => 187, "\xBC" => 188, "\xBD" => 189, "\xBE" => 190, "\xBF" => 191, "\xC0" => 192, "\xC1" => 193, "\xC2" => 194, "\xC3" => 195, "\xC4" => 196, "\xC5" => 197, "\xC6" => 198, "\xC7" => 199, "\xC8" => 200, "\xC9" => 201, "\xCA" => 202, "\xCB" => 203, "\xCC" => 204, "\xCD" => 205, "\xCE" => 206, "\xCF" => 207, "\xD0" => 208, "\xD1" => 209, "\xD2" => 210, "\xD3" => 211, "\xD4" => 212, "\xD5" => 213, "\xD6" => 214, "\xD7" => 215, "\xD8" => 216, "\xD9" => 217, "\xDA" => 218, "\xDB" => 219, "\xDC" => 220, "\xDD" => 221, "\xDE" => 222, "\xDF" => 223, "\xE0" => 224, "\xE1" => 225, "\xE2" => 226, "\xE3" => 227, "\xE4" => 228, "\xE5" => 229, "\xE6" => 230, "\xE7" => 231, "\xE8" => 232, "\xE9" => 233, "\xEA" => 234, "\xEB" => 235, "\xEC" => 236, "\xED" => 237, "\xEE" => 238, "\xEF" => 239, "\xF0" => 240, "\xF1" => 241, "\xF2" => 242, "\xF3" => 243, "\xF4" => 244, "\xF5" => 245, "\xF6" => 246, "\xF7" => 247, "\xF8" => 248, "\xF9" => 249, "\xFA" => 250, "\xFB" => 251, "\xFC" => 252, "\xFD" => 253, "\xFE" => 254, "\xFF" => 255]; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x000.php b/vendor/voku/portable-ascii/src/voku/helper/data/x000.php new file mode 100644 index 0000000..6c9d81f --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x000.php @@ -0,0 +1,16 @@ +', '?', '@', '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', '{', '|', '}', '~', '', 'EUR', // "\xc2\x80" => "\xe2\x82\xac" => EURO SIGN + '', ',', 'f', ',,', // "\xc2\x84" => "\xe2\x80\x9e" => DOUBLE LOW-9 QUOTATION MARK + '...', // "\xc2\x85" => "\xe2\x80\xa6" => HORIZONTAL ELLIPSIS + '+', '++', // "\xc2\x87" => "\xe2\x80\xa1" => DOUBLE DAGGER + '^', '%0', // "\xc2\x89" => "\xe2\x80\xb0" => PER MILLE SIGN + 'S', '<', 'OE', // "\xc2\x8c" => "\xc5\x92" => LATIN CAPITAL LIGATURE OE + '', 'Z', '', '', '\'', // "\xc2\x91" => "\xe2\x80\x98" => LEFT SINGLE QUOTATION MARK + '\'', // "\xc2\x92" => "\xe2\x80\x99" => RIGHT SINGLE QUOTATION MARK + '"', '"', '*', '-', '--', // "\xc2\x97" => "\xe2\x80\x94" => EM DASH + '~', 'tm', 's', '>', 'oe', '', 'z', 'Y', ' ', '!', 'C/', 'PS', '$?', 'Y=', '|', 'SS', '"', '(c)', 'a', '<<', '!', '', '(r)', '-', 'deg', '+-', '2', '3', '\'', 'u', 'P', '*', ',', '1', 'o', '>>', '1/4', '1/2', '3/4', '?', 'A', 'A', 'A', 'A', // Not "AE" - used in languages other than German + 'A', 'A', 'AE', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', // Not "OE" - used in languages other than German + 'O', 'x', 'O', 'U', 'U', 'U', // Not "UE" - used in languages other than German + 'U', 'Y', 'Th', 'ss', 'a', 'a', 'a', 'a', // Not "ae" - used in languages other than German + 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'd', 'n', 'o', 'o', 'o', 'o', // Not "oe" - used in languages other than German + 'o', '/', 'o', 'u', 'u', 'u', // Not "ue" - used in languages other than German + 'u', 'y', 'th', 'y', ]; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x001.php b/vendor/voku/portable-ascii/src/voku/helper/data/x001.php new file mode 100644 index 0000000..87fb12f --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x001.php @@ -0,0 +1 @@ +', '^', 'V', '^', 'V', '\'', '-', '/', '\\', ',', '_', '\\', '/', ':', '.', '`', '\'', '^', 'V', '+', '-', 'V', '.', '@', ',', '~', '"', 'R', 'X', 'G', 'l', 's', 'x', '?', '', '', '', '', '', '', '', 'V', '=', '"', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]']; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x003.php b/vendor/voku/portable-ascii/src/voku/helper/data/x003.php new file mode 100644 index 0000000..3d02b86 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x003.php @@ -0,0 +1 @@ +', '[?]', '[?]', '[?]', 'f', 'v', 'u', 'yr', 'y', 'w', 'th', 'th', 'a', 'o', 'ac', 'ae', 'o', 'o', 'o', 'oe', 'on', 'r', 'k', 'c', 'k', 'g', 'ng', 'g', 'g', 'w', 'h', 'h', 'h', 'h', 'n', 'n', 'n', 'i', 'e', 'j', 'g', 'ae', 'a', 'eo', 'p', 'z', 's', 's', 's', 'c', 'z', 't', 't', 'd', 'b', 'b', 'p', 'p', 'e', 'm', 'm', 'm', 'l', 'l', 'ng', 'ng', 'd', 'o', 'ear', 'ior', 'qu', 'qu', 'qu', 's', 'yr', 'yr', 'yr', 'q', 'x', '.', ':', '+', '17', '18', '19', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]']; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x017.php b/vendor/voku/portable-ascii/src/voku/helper/data/x017.php new file mode 100644 index 0000000..8f2a7ca --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x017.php @@ -0,0 +1 @@ +', '.', '..', '...', '.', "\n", + "\n\n", + '', '', '', '', '', ' ', '%0', '%00', '\'', '\'\'', '\'\'\'', '`', '``', '```', '^', '<', '>', '*', '!!', '!?', '-', '_', '-', '^', '***', '--', '/', '-[', ']-', '??', '?!', '!?', '7', 'PP', '(]', '[)', '*', '[?]', '[?]', '[?]', '%', '~', '[?]', '[?]', '[?]', "''''", // 0x57 + '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', ' ', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '', '', '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', '[?]', '[?]', '[?]', 'ECU', 'CL', 'Cr', 'Fr.', 'L.', 'mil', 'N', 'Pts', 'Rs', 'W', 'NS', 'D', 'EUR', 'K', 'T', 'Dr', 'Pf', 'P', 'G', 'A', 'UAH', 'C|', 'L', 'Sm', 'T', 'Rs', 'L', 'M', 'm', 'R', 'l', 'BTC', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '[?]', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', ]; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x021.php b/vendor/voku/portable-ascii/src/voku/helper/data/x021.php new file mode 100644 index 0000000..1643d67 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x021.php @@ -0,0 +1 @@ +=', '<=', '>=', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]']; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x023.php b/vendor/voku/portable-ascii/src/voku/helper/data/x023.php new file mode 100644 index 0000000..b8f4ca0 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x023.php @@ -0,0 +1 @@ + ', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]']; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x024.php b/vendor/voku/portable-ascii/src/voku/helper/data/x024.php new file mode 100644 index 0000000..26abcc6 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x024.php @@ -0,0 +1 @@ +', '>', '>', '>', '>', '>', 'V', 'V', 'V', 'V', '<', '<', '<', '<', '<', '<', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '#', '#', '#', '#', '#', '^', '^', '^', 'O', '#', '#', '#', '#', 'O', 'O', 'O', 'O', '/', '\\\\', '\\\\', '#', '#', '#', '#', '/']; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x026.php b/vendor/voku/portable-ascii/src/voku/helper/data/x026.php new file mode 100644 index 0000000..0c97de3 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x026.php @@ -0,0 +1 @@ + ', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]']; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x028.php b/vendor/voku/portable-ascii/src/voku/helper/data/x028.php new file mode 100644 index 0000000..9585d91 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x028.php @@ -0,0 +1 @@ +', 'n', 't', 'q', ',', '*', '5', '<', '-', 'u', '8', 'v', '.', '%', '[', '$', '+', 'x', '!', '&', ';', ':', '4', '\\', '0', 'z', '7', '(', '_', '?', 'w', ']', '#', 'y', ')', '=', '[d7]', '[d17]', '[d27]', '[d127]', '[d37]', '[d137]', '[d237]', '[d1237]', '[d47]', '[d147]', '[d247]', '[d1247]', '[d347]', '[d1347]', '[d2347]', '[d12347]', '[d57]', '[d157]', '[d257]', '[d1257]', '[d357]', '[d1357]', '[d2357]', '[d12357]', '[d457]', '[d1457]', '[d2457]', '[d12457]', '[d3457]', '[d13457]', '[d23457]', '[d123457]', '[d67]', '[d167]', '[d267]', '[d1267]', '[d367]', '[d1367]', '[d2367]', '[d12367]', '[d467]', '[d1467]', '[d2467]', '[d12467]', '[d3467]', '[d13467]', '[d23467]', '[d123467]', '[d567]', '[d1567]', '[d2567]', '[d12567]', '[d3567]', '[d13567]', '[d23567]', '[d123567]', '[d4567]', '[d14567]', '[d24567]', '[d124567]', '[d34567]', '[d134567]', '[d234567]', '[d1234567]', '[d8]', '[d18]', '[d28]', '[d128]', '[d38]', '[d138]', '[d238]', '[d1238]', '[d48]', '[d148]', '[d248]', '[d1248]', '[d348]', '[d1348]', '[d2348]', '[d12348]', '[d58]', '[d158]', '[d258]', '[d1258]', '[d358]', '[d1358]', '[d2358]', '[d12358]', '[d458]', '[d1458]', '[d2458]', '[d12458]', '[d3458]', '[d13458]', '[d23458]', '[d123458]', '[d68]', '[d168]', '[d268]', '[d1268]', '[d368]', '[d1368]', '[d2368]', '[d12368]', '[d468]', '[d1468]', '[d2468]', '[d12468]', '[d3468]', '[d13468]', '[d23468]', '[d123468]', '[d568]', '[d1568]', '[d2568]', '[d12568]', '[d3568]', '[d13568]', '[d23568]', '[d123568]', '[d4568]', '[d14568]', '[d24568]', '[d124568]', '[d34568]', '[d134568]', '[d234568]', '[d1234568]', '[d78]', '[d178]', '[d278]', '[d1278]', '[d378]', '[d1378]', '[d2378]', '[d12378]', '[d478]', '[d1478]', '[d2478]', '[d12478]', '[d3478]', '[d13478]', '[d23478]', '[d123478]', '[d578]', '[d1578]', '[d2578]', '[d12578]', '[d3578]', '[d13578]', '[d23578]', '[d123578]', '[d4578]', '[d14578]', '[d24578]', '[d124578]', '[d34578]', '[d134578]', '[d234578]', '[d1234578]', '[d678]', '[d1678]', '[d2678]', '[d12678]', '[d3678]', '[d13678]', '[d23678]', '[d123678]', '[d4678]', '[d14678]', '[d24678]', '[d124678]', '[d34678]', '[d134678]', '[d234678]', '[d1234678]', '[d5678]', '[d15678]', '[d25678]', '[d125678]', '[d35678]', '[d135678]', '[d235678]', '[d1235678]', '[d45678]', '[d145678]', '[d245678]', '[d1245678]', '[d345678]', '[d1345678]', '[d2345678]', '[d12345678]']; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x029.php b/vendor/voku/portable-ascii/src/voku/helper/data/x029.php new file mode 100644 index 0000000..5162de3 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x029.php @@ -0,0 +1 @@ +', '%', '[?]', '[?]', '>', '=', '[?]', '/', '-', '~', '\\', '/', '~', '~', '|-', '-|', '[?]', '[?]', '[?]', '[?]', '<=', '=>', '((', '))', '[?]', '[?]', '::', '[?]', '?', '\'', 'o', '.', ',', '.', ',', ';', '[?]', '[?]', '[?]', '[?]', '----', '------', 'x', '|', '[?]', '[?]', '=', ',', '"', '`--', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?]', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]']; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x02f.php b/vendor/voku/portable-ascii/src/voku/helper/data/x02f.php new file mode 100644 index 0000000..5147b57 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x02f.php @@ -0,0 +1 @@ + ', '<<', '>> ', '[', '] ', '{', '} ', '[(', ')] ', '@', 'X ', '[', '] ', '[[', ']] ', '((', ')) ', '[[', ']] ', '~ ', '``', '\'\'', ',,', '@', '1', '2', '3', '4', '5', '6', '7', '8', '9', '', '', '', '', '', '', '~', '+', '+', '+', '+', '', '@', ' // ', '+10+', '+20+', '+30+', '[?]', '[?]', '[?]', '', '', '[?]', 'a', 'a', 'i', 'i', 'u', 'u', 'e', 'e', 'o', 'o', 'ka', 'ga', 'ki', 'gi', 'ku', 'gu', 'ke', 'ge', 'ko', 'go', 'sa', 'za', 'shi', // 0x57 + 'zi', 'su', 'zu', 'se', 'ze', 'so', 'zo', 'ta', 'da', 'chi', // 0x61 + 'di', 'tsu', // 0x63 + 'tsu', // 0x64 + 'du', 'te', 'de', 'to', 'do', 'na', 'ni', 'nu', 'ne', 'no', 'ha', 'ba', 'pa', 'hi', 'bi', 'pi', 'hu', 'bu', 'pu', 'he', 'be', 'pe', 'ho', 'bo', 'po', 'ma', 'mi', 'mu', 'me', 'mo', 'ya', 'ya', 'yu', 'yu', 'yo', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'wa', 'wi', 'we', 'wo', 'n', 'vu', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '"', '"', '[?]', '[?]', 'a', 'a', 'i', 'i', 'u', 'u', 'e', 'e', 'o', 'o', 'ka', 'ga', 'ki', 'gi', 'ku', 'gu', 'ke', 'ge', 'ko', 'go', 'sa', 'za', 'shi', // 0xb7 + 'zi', 'su', 'zu', 'se', 'ze', 'so', 'zo', 'ta', 'da', 'chi', // 0xc1 + 'di', 'tsu', // 0xc3 + 'tsu', // 0xc4 + 'du', 'te', 'de', 'to', 'do', 'na', 'ni', 'nu', 'ne', 'no', 'ha', 'ba', 'pa', 'hi', 'bi', 'pi', 'hu', 'bu', 'pu', 'he', 'be', 'pe', 'ho', 'bo', 'po', 'ma', 'mi', 'mu', 'me', 'mo', 'ya', 'ya', 'yu', 'yu', 'yo', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'wa', 'wi', 'we', 'wo', 'n', 'vu', 'ka', 'ke', 'va', 'vi', 've', 'vo', '', '', '"', '"', ]; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x031.php b/vendor/voku/portable-ascii/src/voku/helper/data/x031.php new file mode 100644 index 0000000..72c0260 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x031.php @@ -0,0 +1 @@ +>', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '(g)', '(n)', '(d)', '(r)', '(m)', '(b)', '(s)', '()', '(j)', '(c)', '(k)', '(t)', '(p)', '(h)', '(ga)', '(na)', '(da)', '(ra)', '(ma)', '(ba)', '(sa)', '(a)', '(ja)', '(ca)', '(ka)', '(ta)', '(pa)', '(ha)', '[?]', '[?]', '[?]', 'KIS ', '(1) ', '(2) ', '(3) ', '(4) ', '(5) ', '(6) ', '(7) ', '(8) ', '(9) ', '(10) ', '(Yue) ', '(Huo) ', '(Shui) ', '(Mu) ', '(Jin) ', '(Tu) ', '(Ri) ', '(Zhu) ', '(You) ', '(She) ', '(Ming) ', '(Te) ', '(Cai) ', '(Zhu) ', '(Lao) ', '(Mi) ', '(Nan) ', '(Nu) ', '(Shi) ', '(You) ', '(Yin) ', '(Zhu) ', '(Xiang) ', '(Xiu) ', '(Xie) ', '(Zheng) ', '(Shang) ', '(Zhong) ', '(Xia) ', '(Zuo) ', '(You) ', '(Yi) ', '(Zong) ', '(Xue) ', '(Jian) ', '(Qi) ', '(Zi) ', '(Xie) ', '(Ye) ', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '1M', '2M', '3M', '4M', '5M', '6M', '7M', '8M', '9M', '10M', '11M', '12M', 'Hg', 'erg', 'eV', 'LTD', 'a', 'i', 'u', 'u', 'o', 'ka', 'ki', 'ku', 'ke', 'ko', 'sa', 'si', 'su', 'se', 'so', 'ta', 'ti', 'tu', 'te', 'to', 'na', 'ni', 'nu', 'ne', 'no', 'ha', 'hi', 'hu', 'he', 'ho', 'ma', 'mi', 'mu', 'me', 'mo', 'ya', 'yu', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'wi', 'we', 'wo']; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x033.php b/vendor/voku/portable-ascii/src/voku/helper/data/x033.php new file mode 100644 index 0000000..8505337 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x033.php @@ -0,0 +1 @@ +> ', '<', '> ', '[', '] ', '{', '}', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '', '', '', ',', ',', '.', '', ';', ':', '?', '!', '-', '(', ')', '{', '}', '{', '}', '#', '&', '*', '+', '-', '<', '>', '=', '', '\\', '$', '%', '@', '[?]', '[?]', '[?]', '[?]', '', '', '', '[?]', '', '[?]', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '[?]', '[?]', '']; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x0ff.php b/vendor/voku/portable-ascii/src/voku/helper/data/x0ff.php new file mode 100644 index 0000000..b3a1539 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x0ff.php @@ -0,0 +1 @@ +', '?', '@', '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', '{', '|', '}', '~', '[?]', '[?]', '.', '[', ']', ',', '*', 'wo', 'a', 'i', 'u', 'e', 'o', 'ya', 'yu', 'yo', 'tu', '+', 'a', 'i', 'u', 'e', 'o', 'ka', 'ki', 'ku', 'ke', 'ko', 'sa', 'si', 'su', 'se', 'so', 'ta', 'ti', 'tu', 'te', 'to', 'na', 'ni', 'nu', 'ne', 'no', 'ha', 'hi', 'hu', 'he', 'ho', 'ma', 'mi', 'mu', 'me', 'mo', 'ya', 'yu', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'n', ':', ';', '', 'g', 'gg', 'gs', 'n', 'nj', 'nh', 'd', 'dd', 'r', 'lg', 'lm', 'lb', 'ls', 'lt', 'lp', 'rh', 'm', 'b', 'bb', 'bs', 's', 'ss', '', 'j', 'jj', 'c', 'k', 't', 'p', 'h', '[?]', '[?]', '[?]', 'a', 'ae', 'ya', 'yae', 'eo', 'e', '[?]', '[?]', 'yeo', 'ye', 'o', 'wa', 'wae', 'oe', '[?]', '[?]', 'yo', 'u', 'weo', 'we', 'wi', 'yu', '[?]', '[?]', 'eu', 'yi', 'i', '[?]', '[?]', '[?]', '/C', 'PS', '!', '-', '|', 'Y=', 'W=', '[?]', '|', '-', '|', '-', '|', '#', 'O', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '{', '|', '}', '', '', '', '']; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x1d4.php b/vendor/voku/portable-ascii/src/voku/helper/data/x1d4.php new file mode 100644 index 0000000..ad8d3b2 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x1d4.php @@ -0,0 +1 @@ + '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', 52 => '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', 78 => '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', 104 => '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', 130 => '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', 156 => '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', 181 => 'Z', 182 => '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', 208 => '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', 234 => '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']; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x1d5.php b/vendor/voku/portable-ascii/src/voku/helper/data/x1d5.php new file mode 100644 index 0000000..a2a9b90 --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x1d5.php @@ -0,0 +1,4 @@ + 'w', 'x', 'y', 'z', 4 => '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', 30 => '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', 56 => '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', 82 => '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', 108 => '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', 134 => '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', 160 => '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', 186 => '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', 212 => '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', 238 => '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', ]; diff --git a/vendor/voku/portable-ascii/src/voku/helper/data/x1d6.php b/vendor/voku/portable-ascii/src/voku/helper/data/x1d6.php new file mode 100644 index 0000000..315ef5e --- /dev/null +++ b/vendor/voku/portable-ascii/src/voku/helper/data/x1d6.php @@ -0,0 +1 @@ + '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', 80 => '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', 112 => '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', 230 => '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', ]; diff --git a/vendor/webman/captcha/.gitignore b/vendor/webman/captcha/.gitignore new file mode 100644 index 0000000..4383716 --- /dev/null +++ b/vendor/webman/captcha/.gitignore @@ -0,0 +1,5 @@ +.idea/ +demo/*.jpg +demo/*.pgm +demo/temp/ +vendor/ diff --git a/vendor/webman/captcha/.travis.yml b/vendor/webman/captcha/.travis.yml new file mode 100644 index 0000000..5d2dbf9 --- /dev/null +++ b/vendor/webman/captcha/.travis.yml @@ -0,0 +1,16 @@ +language: php + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - 7.1 + - 7.2 + - hhvm + +script: + - composer install + - phpunit diff --git a/vendor/webman/captcha/LICENSE b/vendor/webman/captcha/LICENSE new file mode 100644 index 0000000..62f991a --- /dev/null +++ b/vendor/webman/captcha/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) <2012-2017> Grégoire Passault + +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/webman/captcha/README.md b/vendor/webman/captcha/README.md new file mode 100644 index 0000000..58e1544 --- /dev/null +++ b/vendor/webman/captcha/README.md @@ -0,0 +1,142 @@ +Captcha +======= + +![Captchas examples](http://gregwar.com/captchas.png) + +This is a fork of [Gregwar/Captcha](https://github.com/Gregwar/Captcha) + +Installation +============ + +With composer : + +``` +composer require webman/captcha +``` + +Usage +===== + +You can create a captcha with the `CaptchaBuilder` : + +```php +build(); +``` + +You can then save it to a file : + +```php +save('out.jpg'); +``` + +Or output it directly : + +```php +output(); +``` + +Or inline it directly in the HTML page: + +```php + +``` + +You'll be able to get the code and compare it with a user input : + +```php +getPhrase(); +``` + +You can compare the phrase with user input: +```php +if($builder->testPhrase($userInput)) { + // instructions if user phrase is good +} +else { + // user phrase is wrong +} +``` + +API +=== + +You can use theses functions : + +* **__construct($phrase = null)**, constructs the builder with the given phrase, if the phrase is null, a random one will be generated +* **getPhrase()**, allow you to get the phrase contents +* **setDistortion($distortion)**, enable or disable the distortion, call it before `build()` +* **isOCRReadable()**, returns `true` if the OCR can be read using the `ocrad` software, you'll need to have shell_exec enabled, imagemagick and ocrad installed +* **buildAgainstOCR($width = 150, $height = 40, $font = null)**, builds a code until it is not readable by `ocrad` +* **build($width = 150, $height = 40, $font = null)**, builds a code with the given $width, $height and $font. By default, a random font will be used from the library +* **save($filename, $quality = 80)**, saves the captcha into a jpeg in the $filename, with the given quality +* **get($quality = 80)**, returns the jpeg data +* **output($quality = 80)**, directly outputs the jpeg code to a browser +* **setBackgroundColor($r, $g, $b)**, sets the background color to force it (this will disable many effects and is not recommended) +* **setBackgroundImages(array($imagepath1, $imagePath2))**, Sets custom background images to be used as captcha background. It is recommended to disable image effects when passing custom images for background (ignore_all_effects). A random image is selected from the list passed, the full paths to the image files must be passed. +* **setInterpolation($interpolate)**, enable or disable the interpolation (enabled by default), disabling it will be quicker but the images will look uglier +* **setIgnoreAllEffects($ignoreAllEffects)**, disable all effects on the captcha image. Recommended to use when passing custom background images for the captcha. +* **testPhrase($phrase)**, returns true if the given phrase is good +* **setMaxBehindLines($lines)**, sets the maximum number of lines behind the code +* **setMaxFrontLines($lines)**, sets the maximum number of lines on the front of the code + +If you want to change the number of character, you can call the phrase builder directly using +extra parameters: + +```php +use Webman\Captcha\CaptchaBuilder; +use Webman\Captcha\PhraseBuilder; + +// Will build phrases of 3 characters +$phraseBuilder = new PhraseBuilder(4); + +// Will build phrases of 5 characters, only digits +$phraseBuilder = new PhraseBuilder(5, '0123456789'); + +// Pass it as first argument of CaptchaBuilder, passing it the phrase +// builder +$captcha = new CaptchaBuilder(null, $phraseBuilder); +``` + +You can also pass directly the wanted phrase to the builder: + +```php +// Building a Captcha with the "hello" phrase +$captcha = new CaptchaBuilder('hello'); +``` + +Complete example +================ + +If you want to see an example you can have a look at the ``demo/form.php``, which uses ``demo/session.php`` to +render a captcha and check it after the submission + +Symfony Bundle +================ + +You can have a look at the following repository to enjoy the Symfony 2 bundle packaging this captcha generator : +https://github.com/Gregwar/CaptchaBundle + +Yii2 Extension +=============== + +You can use the following extension for integrating with Yii2 Framework : +https://github.com/juliardi/yii2-captcha + +License +======= + +This library is under MIT license, have a look to the `LICENSE` file diff --git a/vendor/webman/captcha/composer.json b/vendor/webman/captcha/composer.json new file mode 100644 index 0000000..7406567 --- /dev/null +++ b/vendor/webman/captcha/composer.json @@ -0,0 +1,35 @@ +{ + "name": "webman/captcha", + "type": "library", + "description": "Captcha generator", + "keywords": ["captcha", "spam", "bot"], + "license": "MIT", + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net" + }, + { + "name": "Grégoire Passault", + "email": "g.passault@gmail.com", + "homepage": "http://www.gregwar.com/" + }, + { + "name": "Jeremy Livingston", + "email": "jeremy.j.livingston@gmail.com" + } + ], + "require": { + "php": ">=5.6.0", + "ext-gd": "*", + "ext-mbstring": "*" + }, + "autoload": { + "psr-4": { + "Webman\\Captcha\\": "src" + } + }, + "require-dev": { + "phpunit/phpunit": "^6.4" + } +} diff --git a/vendor/webman/captcha/demo/demo.php b/vendor/webman/captcha/demo/demo.php new file mode 100644 index 0000000..51e71ce --- /dev/null +++ b/vendor/webman/captcha/demo/demo.php @@ -0,0 +1,11 @@ +build() + ->save('out.jpg') +; diff --git a/vendor/webman/captcha/demo/fingerprint.php b/vendor/webman/captcha/demo/fingerprint.php new file mode 100644 index 0000000..17570d3 --- /dev/null +++ b/vendor/webman/captcha/demo/fingerprint.php @@ -0,0 +1,12 @@ +build() + ->getFingerprint() +); + +echo "\n"; diff --git a/vendor/webman/captcha/demo/form.php b/vendor/webman/captcha/demo/form.php new file mode 100644 index 0000000..2957b7c --- /dev/null +++ b/vendor/webman/captcha/demo/form.php @@ -0,0 +1,32 @@ + + + +Captcha is valid !"; + } else { + echo "

    Captcha is not valid!

    "; + } + // The phrase can't be used twice + unset($_SESSION['phrase']); + } +?> +
    + Copy the CAPTCHA: + + + + +
    + diff --git a/vendor/webman/captcha/demo/index.php b/vendor/webman/captcha/demo/index.php new file mode 100644 index 0000000..e543883 --- /dev/null +++ b/vendor/webman/captcha/demo/index.php @@ -0,0 +1,15 @@ + + + + + + +

    Captchas gallery

    + + + + +
    + + + diff --git a/vendor/webman/captcha/demo/inline.php b/vendor/webman/captcha/demo/inline.php new file mode 100644 index 0000000..75d9382 --- /dev/null +++ b/vendor/webman/captcha/demo/inline.php @@ -0,0 +1,22 @@ +build(); + +?> + + + + + + +

    Inline Captcha

    + +
    + Phrase: getPhrase(); ?> + + + diff --git a/vendor/webman/captcha/demo/ocr.php b/vendor/webman/captcha/demo/ocr.php new file mode 100644 index 0000000..3f766dc --- /dev/null +++ b/vendor/webman/captcha/demo/ocr.php @@ -0,0 +1,39 @@ +setDistortion(false) + ->build() + ; + + if ($captcha->isOCRReadable()) { + $passed++; + $captcha->save("passed$passed.jpg"); + echo "passed at ocr... "; + } else { + echo "failed... "; + } + + echo "pass rate: ".round(100*$passed/($i+1),2)."%\n"; +} + +echo "\n"; +echo "Over, $passed/$tests readed with OCR\n"; diff --git a/vendor/webman/captcha/demo/output.php b/vendor/webman/captcha/demo/output.php new file mode 100644 index 0000000..b6d628a --- /dev/null +++ b/vendor/webman/captcha/demo/output.php @@ -0,0 +1,12 @@ +build() + ->output() +; diff --git a/vendor/webman/captcha/demo/session.php b/vendor/webman/captcha/demo/session.php new file mode 100644 index 0000000..187a404 --- /dev/null +++ b/vendor/webman/captcha/demo/session.php @@ -0,0 +1,22 @@ +getPhrase(); + +// Setting the header to image jpeg because we here render an image +header('Content-Type: image/jpeg'); + +// Running the actual rendering of the captcha image +$captcha + ->build() + ->output() +; diff --git a/vendor/webman/captcha/phpunit.xml.dist b/vendor/webman/captcha/phpunit.xml.dist new file mode 100644 index 0000000..f17e3b4 --- /dev/null +++ b/vendor/webman/captcha/phpunit.xml.dist @@ -0,0 +1,15 @@ + + + + + + ./tests/ + + + + + + ./src + + + diff --git a/vendor/webman/captcha/src/CaptchaBuilder.php b/vendor/webman/captcha/src/CaptchaBuilder.php new file mode 100644 index 0000000..65490aa --- /dev/null +++ b/vendor/webman/captcha/src/CaptchaBuilder.php @@ -0,0 +1,773 @@ + + * @author Jeremy Livingston + */ +#[AllowDynamicProperties] +class CaptchaBuilder implements CaptchaBuilderInterface +{ + /** + * @var int|bool + */ + public $background; + + /** + * @var array + */ + protected $fingerprint = array(); + + /** + * @var bool + */ + protected $useFingerprint = false; + + /** + * @var array + */ + protected $textColor = array(); + + /** + * @var array + */ + protected $lineColor = null; + + /** + * @var array + */ + protected $backgroundColor = null; + + /** + * @var array + */ + protected $backgroundImages = array(); + + /** + * @var resource + */ + protected $contents = null; + + /** + * @var string + */ + protected $phrase = null; + + /** + * @var PhraseBuilderInterface + */ + protected $builder; + + /** + * @var bool + */ + protected $distortion = true; + + /** + * The maximum number of lines to draw in front of + * the image. null - use default algorithm + */ + protected $maxFrontLines = null; + + /** + * The maximum number of lines to draw behind + * the image. null - use default algorithm + */ + protected $maxBehindLines = null; + + /** + * The maximum angle of char + */ + protected $maxAngle = 8; + + /** + * The maximum offset of char + */ + protected $maxOffset = 5; + + /** + * Is the interpolation enabled ? + * + * @var bool + */ + protected $interpolation = true; + + /** + * Ignore all effects + * + * @var bool + */ + protected $ignoreAllEffects = false; + + /** + * Allowed image types for the background images + * + * @var array + */ + protected $allowedBackgroundImageTypes = array('image/png', 'image/jpeg', 'image/gif'); + + /** + * The image contents + */ + public function getContents() + { + return $this->contents; + } + + /** + * Enable/Disables the interpolation + * + * @param $interpolate bool True to enable, false to disable + * + * @return CaptchaBuilder + */ + public function setInterpolation($interpolate = true) + { + $this->interpolation = $interpolate; + + return $this; + } + + /** + * Temporary dir, for OCR check + */ + public $tempDir = 'temp/'; + + public function __construct($phrase = null, PhraseBuilderInterface $builder = null) + { + if ($builder === null) { + $this->builder = new PhraseBuilder; + } else { + $this->builder = $builder; + } + + $this->phrase = is_string($phrase) ? $phrase : $this->builder->build($phrase); + } + + /** + * Setting the phrase + */ + public function setPhrase($phrase) + { + $this->phrase = (string) $phrase; + } + + /** + * Enables/disable distortion + */ + public function setDistortion($distortion) + { + $this->distortion = (bool) $distortion; + + return $this; + } + + public function setMaxBehindLines($maxBehindLines) + { + $this->maxBehindLines = $maxBehindLines; + + return $this; + } + + public function setMaxFrontLines($maxFrontLines) + { + $this->maxFrontLines = $maxFrontLines; + + return $this; + } + + public function setMaxAngle($maxAngle) + { + $this->maxAngle = $maxAngle; + + return $this; + } + + public function setMaxOffset($maxOffset) + { + $this->maxOffset = $maxOffset; + + return $this; + } + + /** + * Gets the captcha phrase + */ + public function getPhrase() + { + return $this->phrase; + } + + /** + * Returns true if the given phrase is good + */ + public function testPhrase($phrase) + { + return ($this->builder->niceize($phrase) == $this->builder->niceize($this->getPhrase())); + } + + /** + * Instantiation + */ + public static function create($phrase = null) + { + return new self($phrase); + } + + /** + * Sets the text color to use + */ + public function setTextColor($r, $g, $b) + { + $this->textColor = array($r, $g, $b); + + return $this; + } + + /** + * Sets the background color to use + */ + public function setBackgroundColor($r, $g, $b) + { + $this->backgroundColor = array($r, $g, $b); + + return $this; + } + + public function setLineColor($r, $g, $b) + { + $this->lineColor = array($r, $g, $b); + + return $this; + } + + /** + * Sets the ignoreAllEffects value + * + * @param bool $ignoreAllEffects + * @return CaptchaBuilder + */ + public function setIgnoreAllEffects($ignoreAllEffects) + { + $this->ignoreAllEffects = $ignoreAllEffects; + + return $this; + } + + /** + * Sets the list of background images to use (one image is randomly selected) + */ + public function setBackgroundImages(array $backgroundImages) + { + $this->backgroundImages = $backgroundImages; + + return $this; + } + + /** + * Draw lines over the image + */ + protected function drawLine($image, $width, $height, $tcol = null) + { + if ($this->lineColor === null) { + $red = $this->rand(100, 255); + $green = $this->rand(100, 255); + $blue = $this->rand(100, 255); + } else { + $red = $this->lineColor[0]; + $green = $this->lineColor[1]; + $blue = $this->lineColor[2]; + } + + if ($tcol === null) { + $tcol = imagecolorallocate($image, $red, $green, $blue); + } + + if ($this->rand(0, 1)) { // Horizontal + $Xa = $this->rand(0, $width/2); + $Ya = $this->rand(0, $height); + $Xb = $this->rand($width/2, $width); + $Yb = $this->rand(0, $height); + } else { // Vertical + $Xa = $this->rand(0, $width); + $Ya = $this->rand(0, $height/2); + $Xb = $this->rand(0, $width); + $Yb = $this->rand($height/2, $height); + } + imagesetthickness($image, $this->rand(1, 3)); + imageline($image, $Xa, $Ya, $Xb, $Yb, $tcol); + } + + /** + * Apply some post effects + */ + protected function postEffect($image) + { + if (!function_exists('imagefilter')) { + return; + } + + if ($this->backgroundColor != null || $this->textColor != null) { + return; + } + + // Negate ? + if ($this->rand(0, 1) == 0) { + imagefilter($image, IMG_FILTER_NEGATE); + } + + // Edge ? + if ($this->rand(0, 10) == 0) { + imagefilter($image, IMG_FILTER_EDGEDETECT); + } + + // Contrast + imagefilter($image, IMG_FILTER_CONTRAST, $this->rand(-50, 10)); + + // Colorize + if ($this->rand(0, 5) == 0) { + imagefilter($image, IMG_FILTER_COLORIZE, $this->rand(-80, 50), $this->rand(-80, 50), $this->rand(-80, 50)); + } + } + + /** + * Writes the phrase on the image + */ + protected function writePhrase($image, $phrase, $font, $width, $height) + { + $length = mb_strlen($phrase); + if ($length === 0) { + return \imagecolorallocate($image, 0, 0, 0); + } + + // Gets the text size and start position + $size = intval($width / $length) - $this->rand(0, 3) - 1; + $box = \imagettfbbox($size, 0, $font, $phrase); + $textWidth = $box[2] - $box[0]; + $textHeight = $box[1] - $box[7]; + $x = intval(($width - $textWidth) / 2); + $y = intval(($height - $textHeight) / 2) + $size; + + if (!$this->textColor) { + $textColor = array($this->rand(0, 150), $this->rand(0, 150), $this->rand(0, 150)); + } else { + $textColor = $this->textColor; + } + $col = \imagecolorallocate($image, $textColor[0], $textColor[1], $textColor[2]); + + // Write the letters one by one, with random angle + for ($i=0; $i<$length; $i++) { + $symbol = mb_substr($phrase, $i, 1); + $box = \imagettfbbox($size, 0, $font, $symbol); + $w = $box[2] - $box[0]; + $angle = $this->rand(-$this->maxAngle, $this->maxAngle); + $offset = $this->rand(-$this->maxOffset, $this->maxOffset); + \imagettftext($image, $size, $angle, $x, $y + $offset, $col, $font, $symbol); + $x += $w; + } + + return $col; + } + + /** + * Try to read the code against an OCR + */ + public function isOCRReadable() + { + if (!is_dir($this->tempDir)) { + @mkdir($this->tempDir, 0755, true); + } + + $tempj = $this->tempDir . uniqid('captcha', true) . '.jpg'; + $tempp = $this->tempDir . uniqid('captcha', true) . '.pgm'; + + $this->save($tempj); + shell_exec("convert $tempj $tempp"); + $value = trim(strtolower(shell_exec("ocrad $tempp"))); + + @unlink($tempj); + @unlink($tempp); + + return $this->testPhrase($value); + } + + /** + * Builds while the code is readable against an OCR + */ + public function buildAgainstOCR($width = 150, $height = 40, $font = null, $fingerprint = null) + { + do { + $this->build($width, $height, $font, $fingerprint); + } while ($this->isOCRReadable()); + } + + /** + * Generate the image + */ + public function build($width = 150, $height = 40, $font = null, $fingerprint = null) + { + if (null !== $fingerprint) { + $this->fingerprint = $fingerprint; + $this->useFingerprint = true; + } else { + $this->fingerprint = array(); + $this->useFingerprint = false; + } + + if ($font === null) { + $font = $this->getFontPath(__DIR__ . '/Font/captcha'.$this->rand(0, 5).'.ttf'); + } + + if (empty($this->backgroundImages)) { + // if background images list is not set, use a color fill as a background + $image = imagecreatetruecolor($width, $height); + if ($this->backgroundColor == null) { + $bg = imagecolorallocate($image, $this->rand(200, 255), $this->rand(200, 255), $this->rand(200, 255)); + } else { + $color = $this->backgroundColor; + $bg = imagecolorallocate($image, $color[0], $color[1], $color[2]); + } + $this->background = $bg; + imagefill($image, 0, 0, $bg); + } else { + // use a random background image + $randomBackgroundImage = $this->backgroundImages[rand(0, count($this->backgroundImages)-1)]; + + $imageType = $this->validateBackgroundImage($randomBackgroundImage); + + $image = $this->createBackgroundImageFromType($randomBackgroundImage, $imageType); + } + + // Apply effects + if (!$this->ignoreAllEffects) { + $square = $width * $height; + $effects = $this->rand($square/3000, $square/2000); + + // set the maximum number of lines to draw in front of the text + if ($this->maxBehindLines != null && $this->maxBehindLines > 0) { + $effects = min($this->maxBehindLines, $effects); + } + + if ($this->maxBehindLines !== 0) { + for ($e = 0; $e < $effects; $e++) { + $this->drawLine($image, $width, $height); + } + } + } + + // Write CAPTCHA text + $color = $this->writePhrase($image, $this->phrase, $font, $width, $height); + + // Apply effects + if (!$this->ignoreAllEffects) { + $square = $width * $height; + $effects = $this->rand($square/3000, $square/2000); + + // set the maximum number of lines to draw in front of the text + if ($this->maxFrontLines != null && $this->maxFrontLines > 0) { + $effects = min($this->maxFrontLines, $effects); + } + + if ($this->maxFrontLines !== 0) { + for ($e = 0; $e < $effects; $e++) { + $this->drawLine($image, $width, $height, $color); + } + } + } + + // Distort the image + if ($this->distortion && !$this->ignoreAllEffects) { + $image = $this->distort($image, $width, $height, $bg); + } + + // Post effects + if (!$this->ignoreAllEffects) { + $this->postEffect($image); + } + + $this->contents = $image; + + return $this; + } + + /** + * @param $font + * @return string + */ + protected function getFontPath($font) + { + static $fontPathMap = []; + if (!\class_exists(\Phar::class, false) || !\Phar::running()) { + return $font; + } + + $tmpPath = sys_get_temp_dir() ?: '/tmp'; + if (function_exists('runtime_path')) { + $tmpPath = runtime_path('tmp'); + if (!is_dir($tmpPath)) { + mkdir($tmpPath, 0777, true); + } + } + $filePath = "$tmpPath/" . basename($font); + clearstatcache(); + if (!isset($fontPathMap[$font]) || !is_file($filePath)) { + file_put_contents($filePath, file_get_contents($font)); + $fontPathMap[$font] = $filePath; + } + return $fontPathMap[$font]; + } + + /** + * Distorts the image + */ + public function distort($image, $width, $height, $bg) + { + $contents = imagecreatetruecolor($width, $height); + $X = $this->rand(0, $width); + $Y = $this->rand(0, $height); + $phase = $this->rand(0, 10); + $scale = 1.1 + $this->rand(0, 10000) / 30000; + for ($x = 0; $x < $width; $x++) { + for ($y = 0; $y < $height; $y++) { + $Vx = $x - $X; + $Vy = $y - $Y; + $Vn = sqrt($Vx * $Vx + $Vy * $Vy); + + if ($Vn != 0) { + $Vn2 = $Vn + 4 * sin($Vn / 30); + $nX = $X + ($Vx * $Vn2 / $Vn); + $nY = $Y + ($Vy * $Vn2 / $Vn); + } else { + $nX = $X; + $nY = $Y; + } + $nY = $nY + $scale * sin($phase + $nX * 0.2); + + if ($this->interpolation) { + $p = $this->interpolate( + $nX - floor($nX), + $nY - floor($nY), + $this->getCol($image, floor($nX), floor($nY), $bg), + $this->getCol($image, ceil($nX), floor($nY), $bg), + $this->getCol($image, floor($nX), ceil($nY), $bg), + $this->getCol($image, ceil($nX), ceil($nY), $bg) + ); + } else { + $p = $this->getCol($image, round($nX), round($nY), $bg); + } + + if ($p == 0) { + $p = $bg; + } + + imagesetpixel($contents, $x, $y, $p); + } + } + + return $contents; + } + + /** + * Saves the Captcha to a jpeg file + */ + public function save($filename, $quality = 90) + { + imagejpeg($this->contents, $filename, $quality); + } + + /** + * Gets the image GD + */ + public function getGd() + { + return $this->contents; + } + + /** + * Gets the image contents + */ + public function get($quality = 90) + { + ob_start(); + $this->output($quality); + + return ob_get_clean(); + } + + /** + * Gets the HTML inline base64 + */ + public function inline($quality = 90) + { + return 'data:image/jpeg;base64,' . base64_encode($this->get($quality)); + } + + /** + * Outputs the image + */ + public function output($quality = 90) + { + imagejpeg($this->contents, null, $quality); + } + + /** + * @return array + */ + public function getFingerprint() + { + return $this->fingerprint; + } + + /** + * Returns a random number or the next number in the + * fingerprint + */ + protected function rand($min, $max) + { + if (!is_array($this->fingerprint)) { + $this->fingerprint = array(); + } + + if ($this->useFingerprint) { + $value = current($this->fingerprint); + next($this->fingerprint); + } else { + $value = mt_rand(intval($min), intval($max)); + $this->fingerprint[] = $value; + } + + return $value; + } + + /** + * @param $x + * @param $y + * @param $nw + * @param $ne + * @param $sw + * @param $se + * + * @return int + */ + protected function interpolate($x, $y, $nw, $ne, $sw, $se) + { + list($r0, $g0, $b0) = $this->getRGB($nw); + list($r1, $g1, $b1) = $this->getRGB($ne); + list($r2, $g2, $b2) = $this->getRGB($sw); + list($r3, $g3, $b3) = $this->getRGB($se); + + $cx = 1.0 - $x; + $cy = 1.0 - $y; + + $m0 = $cx * $r0 + $x * $r1; + $m1 = $cx * $r2 + $x * $r3; + $r = (int) ($cy * $m0 + $y * $m1); + + $m0 = $cx * $g0 + $x * $g1; + $m1 = $cx * $g2 + $x * $g3; + $g = (int) ($cy * $m0 + $y * $m1); + + $m0 = $cx * $b0 + $x * $b1; + $m1 = $cx * $b2 + $x * $b3; + $b = (int) ($cy * $m0 + $y * $m1); + + return ($r << 16) | ($g << 8) | $b; + } + + /** + * @param $image + * @param $x + * @param $y + * + * @return int + */ + protected function getCol($image, $x, $y, $background) + { + $L = imagesx($image); + $H = imagesy($image); + if ($x < 0 || $x >= $L || $y < 0 || $y >= $H) { + return $background; + } + + return imagecolorat($image, $x, $y); + } + + /** + * @param $col + * + * @return array + */ + protected function getRGB($col) + { + return array( + (int) ($col >> 16) & 0xff, + (int) ($col >> 8) & 0xff, + (int) ($col) & 0xff, + ); + } + + /** + * Validate the background image path. Return the image type if valid + * + * @param string $backgroundImage + * @return string + * @throws Exception + */ + protected function validateBackgroundImage($backgroundImage) + { + // check if file exists + if (!file_exists($backgroundImage)) { + $backgroundImageExploded = explode('/', $backgroundImage); + $imageFileName = count($backgroundImageExploded) > 1? $backgroundImageExploded[count($backgroundImageExploded)-1] : $backgroundImage; + + throw new Exception('Invalid background image: ' . $imageFileName); + } + + // check image type + $finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension + $imageType = finfo_file($finfo, $backgroundImage); + finfo_close($finfo); + + if (!in_array($imageType, $this->allowedBackgroundImageTypes)) { + throw new Exception('Invalid background image type! Allowed types are: ' . join(', ', $this->allowedBackgroundImageTypes)); + } + + return $imageType; + } + + /** + * Create background image from type + * + * @param string $backgroundImage + * @param string $imageType + * @return resource + * @throws Exception + */ + protected function createBackgroundImageFromType($backgroundImage, $imageType) + { + switch ($imageType) { + case 'image/jpeg': + $image = imagecreatefromjpeg($backgroundImage); + break; + case 'image/png': + $image = imagecreatefrompng($backgroundImage); + break; + case 'image/gif': + $image = imagecreatefromgif($backgroundImage); + break; + + default: + throw new Exception('Not supported file type for background image!'); + break; + } + + return $image; + } +} diff --git a/vendor/webman/captcha/src/CaptchaBuilderInterface.php b/vendor/webman/captcha/src/CaptchaBuilderInterface.php new file mode 100644 index 0000000..420dbbb --- /dev/null +++ b/vendor/webman/captcha/src/CaptchaBuilderInterface.php @@ -0,0 +1,29 @@ +g-y`84XoT{ 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 literal 0 HcmV?d00001 diff --git a/vendor/webman/captcha/src/Font/captcha1.ttf b/vendor/webman/captcha/src/Font/captcha1.ttf new file mode 100644 index 0000000000000000000000000000000000000000..bb1abf6b6696261ffdfd5c17b74dfb03ce7c72b5 GIT binary patch literal 76232 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

    @KU02>Big?7$LN=!il-X-8& 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 E3yqV3z5oCK literal 0 HcmV?d00001 diff --git a/vendor/webman/captcha/src/Font/captcha4.ttf b/vendor/webman/captcha/src/Font/captcha4.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ab154440d796a815274ff79060759b5d1668b430 GIT binary patch literal 906980 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�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̒dz(ZXuWiHi+(LXqp&g4*p_aOu6~eU8 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 i+k5}X>yE|ETruO?A*Tt-O6eJ literal 0 HcmV?d00001 diff --git a/vendor/webman/captcha/src/Font/captcha5.ttf b/vendor/webman/captcha/src/Font/captcha5.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f7cd998656b6ca8b1fb2e82fe63fa19f97c4c2c0 GIT binary patch literal 49724 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 literal 0 HcmV?d00001 diff --git a/vendor/webman/captcha/src/ImageFileHandler.php b/vendor/webman/captcha/src/ImageFileHandler.php new file mode 100644 index 0000000..488b8b2 --- /dev/null +++ b/vendor/webman/captcha/src/ImageFileHandler.php @@ -0,0 +1,78 @@ + + * @author Jeremy Livingston + */ +class ImageFileHandler +{ + /** + * Name of folder for captcha images + * @var string + */ + protected $imageFolder; + + /** + * Absolute path to public web folder + * @var string + */ + protected $webPath; + + /** + * Frequency of garbage collection in fractions of 1 + * @var int + */ + protected $gcFreq; + + /** + * Maximum age of images in minutes + * @var int + */ + protected $expiration; + + /** + * @param $imageFolder + * @param $webPath + * @param $gcFreq + * @param $expiration + */ + public function __construct($imageFolder, $webPath, $gcFreq, $expiration) + { + $this->imageFolder = $imageFolder; + $this->webPath = $webPath; + $this->gcFreq = $gcFreq; + $this->expiration = $expiration; + } + + /** + * Saves the provided image content as a file + * + * @param string $contents + * + * @return string + */ + public function saveAsFile($contents) + { + $this->createFolderIfMissing(); + + $filename = md5(uniqid()) . '.jpg'; + $filePath = $this->webPath . '/' . $this->imageFolder . '/' . $filename; + imagejpeg($contents, $filePath, 15); + + return '/' . $this->imageFolder . '/' . $filename; + } + + /** + * Creates the folder if it doesn't exist + */ + protected function createFolderIfMissing() + { + if (!file_exists($this->webPath . '/' . $this->imageFolder)) { + mkdir($this->webPath . '/' . $this->imageFolder, 0755); + } + } +} diff --git a/vendor/webman/captcha/src/PhraseBuilder.php b/vendor/webman/captcha/src/PhraseBuilder.php new file mode 100644 index 0000000..b96f6c5 --- /dev/null +++ b/vendor/webman/captcha/src/PhraseBuilder.php @@ -0,0 +1,75 @@ + + */ +class PhraseBuilder implements PhraseBuilderInterface +{ + /** + * @var int + */ + public $length; + + /** + * @var string + */ + public $charset; + /** + * Constructs a PhraseBuilder with given parameters + */ + public function __construct($length = 5, $charset = 'abcdefghijklmnpqrstuvwxyz123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ') + { + $this->length = $length; + $this->charset = $charset; + } + + /** + * Generates random phrase of given length with given charset + */ + public function build($length = null, $charset = null) + { + if ($length !== null) { + $this->length = $length; + } + if ($charset !== null) { + $this->charset = $charset; + } + + $phrase = ''; + $chars = str_split($this->charset); + + for ($i = 0; $i < $this->length; $i++) { + $phrase .= $chars[array_rand($chars)]; + } + + return $phrase; + } + + /** + * "Niceize" a code + */ + public function niceize($str) + { + return self::doNiceize($str); + } + + /** + * A static helper to niceize + */ + public static function doNiceize($str) + { + return strtr(strtolower($str), '01', 'ol'); + } + + /** + * A static helper to compare + */ + public static function comparePhrases($str1, $str2) + { + return self::doNiceize($str1) === self::doNiceize($str2); + } +} diff --git a/vendor/webman/captcha/src/PhraseBuilderInterface.php b/vendor/webman/captcha/src/PhraseBuilderInterface.php new file mode 100644 index 0000000..e6ca680 --- /dev/null +++ b/vendor/webman/captcha/src/PhraseBuilderInterface.php @@ -0,0 +1,21 @@ + + */ +interface PhraseBuilderInterface +{ + /** + * Generates random phrase of given length with given charset + */ + public function build(); + + /** + * "Niceize" a code + */ + public function niceize($str); +} diff --git a/vendor/webman/captcha/tests/CaptchaBuilderTest.php b/vendor/webman/captcha/tests/CaptchaBuilderTest.php new file mode 100644 index 0000000..c6eaeb2 --- /dev/null +++ b/vendor/webman/captcha/tests/CaptchaBuilderTest.php @@ -0,0 +1,30 @@ +build() + ->save('out.jpg') + ; + + $this->assertTrue(file_exists(__DIR__.'/../out.jpg')); + } + + public function testFingerPrint() + { + $int = count(CaptchaBuilder::create() + ->build() + ->getFingerprint() + ); + + $this->assertTrue(is_int($int)); + } +} \ No newline at end of file diff --git a/vendor/webman/event/LICENSE b/vendor/webman/event/LICENSE new file mode 100644 index 0000000..e0b0695 --- /dev/null +++ b/vendor/webman/event/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 webman + +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/webman/event/README.md b/vendor/webman/event/README.md new file mode 100644 index 0000000..b042b12 --- /dev/null +++ b/vendor/webman/event/README.md @@ -0,0 +1,4 @@ +# event +webman event plugin + +https://www.workerman.net/plugin/64 diff --git a/vendor/webman/event/composer.json b/vendor/webman/event/composer.json new file mode 100644 index 0000000..9cf55f2 --- /dev/null +++ b/vendor/webman/event/composer.json @@ -0,0 +1,13 @@ +{ + "name": "webman/event", + "type": "library", + "license": "MIT", + "description": "Webman event plugin", + "require": { + }, + "autoload": { + "psr-4": { + "Webman\\Event\\": "src" + } + } +} \ No newline at end of file diff --git a/vendor/webman/event/src/BootStrap.php b/vendor/webman/event/src/BootStrap.php new file mode 100644 index 0000000..3b1aba5 --- /dev/null +++ b/vendor/webman/event/src/BootStrap.php @@ -0,0 +1,90 @@ + $events) { + // 支持排序,1 2 3 ... 9 a b c...z + ksort($events, SORT_NATURAL); + foreach ($events as $callbacks) { + foreach ($callbacks as $callback) { + Event::on($name, $callback); + } + } + } + } + + /** + * @param $callbacks + * @return array|mixed + */ + protected static function convertCallable($callbacks) + { + if (\is_array($callbacks)) { + $callback = \array_values($callbacks); + if (isset($callback[1]) && \is_string($callback[0]) && \class_exists($callback[0])) { + return [Container::get($callback[0]), $callback[1]]; + } + } + return $callbacks; + } + + /** + * @param $configs + * @return void + */ + protected static function getEvents($configs) + { + foreach ($configs as $config) { + if (!is_array($config)) { + continue; + } + if (isset($config['event']) && is_array($config['event']) && !isset($config['event']['app']['enable'])) { + foreach ($config['event'] as $event_name => $callbacks) { + $callbacks = static::convertCallable($callbacks); + if (is_callable($callbacks)) { + static::$events[$event_name][] = [$callbacks]; + continue; + } + if (!is_array($callbacks)) { + $msg = "Events: $event_name => " .var_export($callbacks, true) . " is not callable\n"; + echo $msg; + Log::error($msg); + continue; + } + ksort($callbacks, SORT_NATURAL); + foreach ($callbacks as $id => $callback) { + $callback = static::convertCallable($callback); + if (is_callable($callback)) { + static::$events[$event_name][$id][] = $callback; + continue; + } + $msg = "Events: $event_name => " . var_export($callback, true) . " is not callable\n"; + echo $msg; + Log::error($msg); + } + } + unset($config['event']); + } + static::getEvents($config); + } + } + +} diff --git a/vendor/webman/event/src/Event.php b/vendor/webman/event/src/Event.php new file mode 100644 index 0000000..5a0f4fb --- /dev/null +++ b/vendor/webman/event/src/Event.php @@ -0,0 +1,161 @@ +error($e); + } + continue; + } + $responses[] = $response; + if ($halt && !is_null($response)) { + return $response; + } + if ($response === false) { + break; + } + } + return $halt ? null : $responses; + } + + /** + * @param mixed $event_name + * @param mixed $data + * @param bool $halt + * @return array|null|mixed + */ + public static function dispatch($event_name, $data, bool $halt = false) + { + $listeners = static::getListeners($event_name); + $responses = []; + foreach ($listeners as $listener) { + $response = $listener($data, $event_name); + $responses[] = $response; + if ($halt && !is_null($response)) { + return $response; + } + if ($response === false) { + break; + } + } + return $halt ? null : $responses; + } + + /** + * @return array + */ + public static function list(): array + { + $listeners = []; + foreach (static::$eventMap as $event_name => $callback_items) { + foreach ($callback_items as $id => $callback_item) { + $listeners[$id] = [$event_name, $callback_item]; + } + } + foreach (static::$prefixEventMap as $event_name => $callback_items) { + foreach ($callback_items as $id => $callback_item) { + $listeners[$id] = [$event_name . '*', $callback_item]; + } + } + ksort($listeners); + return $listeners; + } + + /** + * @param mixed $event_name + * @return callable[] + */ + public static function getListeners($event_name): array + { + $listeners = static::$eventMap[$event_name] ?? []; + foreach (static::$prefixEventMap as $name => $callback_items) { + if (strpos($event_name, $name) === 0) { + $listeners = array_merge($listeners, $callback_items); + } + } + ksort($listeners); + return $listeners; + } + + /** + * @param mixed $event_name + * @return bool + */ + public static function hasListener($event_name): bool + { + return !empty(static::getListeners($event_name)); + } +} diff --git a/vendor/webman/event/src/EventListCommand.php b/vendor/webman/event/src/EventListCommand.php new file mode 100644 index 0000000..e3d3afd --- /dev/null +++ b/vendor/webman/event/src/EventListCommand.php @@ -0,0 +1,50 @@ + $item) { + $event_name = $item[0]; + $callback = $item[1]; + if (is_array($callback) && is_object($callback[0])) { + $callback[0] = get_class($callback[0]); + } + $cb = $callback instanceof \Closure ? 'Closure' : (is_array($callback) ? json_encode($callback) : var_export($callback, 1)); + $rows[] = [$id, $event_name, $cb]; + } + + $table = new Table($output); + $table->setHeaders($headers); + $table->setRows($rows); + $table->render(); + return self::SUCCESS; + } + +} diff --git a/vendor/webman/event/src/Install.php b/vendor/webman/event/src/Install.php new file mode 100644 index 0000000..5a7712d --- /dev/null +++ b/vendor/webman/event/src/Install.php @@ -0,0 +1,82 @@ + 'config/plugin/webman/event', +); + + /** + * Install + * @return void + */ + public static function install() + { + static::installByRelation(); + $event_config_path = config_path() . '/event.php'; + if (!is_file($event_config_path)) { + file_put_contents($event_config_path, " $dest) { + if ($pos = strrpos($dest, '/')) { + $parent_dir = base_path().'/'.substr($dest, 0, $pos); + if (!is_dir($parent_dir)) { + mkdir($parent_dir, 0777, true); + } + } + //symlink(__DIR__ . "/$source", base_path()."/$dest"); + copy_dir(__DIR__ . "/$source", base_path()."/$dest"); + echo "Create $dest +"; + } + } + + /** + * uninstallByRelation + * @return void + */ + public static function uninstallByRelation() + { + foreach (static::$pathRelation as $source => $dest) { + $path = base_path()."/$dest"; + if (!is_dir($path) && !is_file($path)) { + continue; + } + echo "Remove $dest +"; + if (is_file($path) || is_link($path)) { + unlink($path); + continue; + } + remove_dir($path); + } + } + +} \ No newline at end of file diff --git a/vendor/webman/event/src/config/plugin/webman/event/app.php b/vendor/webman/event/src/config/plugin/webman/event/app.php new file mode 100644 index 0000000..8f9c426 --- /dev/null +++ b/vendor/webman/event/src/config/plugin/webman/event/app.php @@ -0,0 +1,4 @@ + true, +]; \ No newline at end of file diff --git a/vendor/webman/event/src/config/plugin/webman/event/bootstrap.php b/vendor/webman/event/src/config/plugin/webman/event/bootstrap.php new file mode 100644 index 0000000..e5b09ba --- /dev/null +++ b/vendor/webman/event/src/config/plugin/webman/event/bootstrap.php @@ -0,0 +1,17 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + Webman\Event\BootStrap::class, +]; diff --git a/vendor/webman/event/src/config/plugin/webman/event/command.php b/vendor/webman/event/src/config/plugin/webman/event/command.php new file mode 100644 index 0000000..e860cf7 --- /dev/null +++ b/vendor/webman/event/src/config/plugin/webman/event/command.php @@ -0,0 +1,7 @@ +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 100644 index 0000000..2561bdf --- /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": ">=4.0.20" + }, + "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 100644 index 0000000..e6a87ed --- /dev/null +++ b/vendor/workerman/crontab/example/test.php @@ -0,0 +1,16 @@ +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 100644 index 0000000..9bee915 --- /dev/null +++ b/vendor/workerman/crontab/src/Crontab.php @@ -0,0 +1,177 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Crontab; +use Workerman\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 string $rule + * @param callable $callback + * @param string $name + */ + public function __construct($rule, $callback, $name = '') + { + $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); + } + +} diff --git a/vendor/workerman/crontab/src/Parser.php b/vendor/workerman/crontab/src/Parser.php new file mode 100644 index 0000000..e9a9a86 --- /dev/null +++ b/vendor/workerman/crontab/src/Parser.php @@ -0,0 +1,171 @@ +, 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 . + */ + +/** + * Provides basic cron syntax parsing functionality + * + * @author: Jan Konieczny , 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/validation/CHANGELOG.md b/vendor/workerman/validation/CHANGELOG.md new file mode 100644 index 0000000..9d082d4 --- /dev/null +++ b/vendor/workerman/validation/CHANGELOG.md @@ -0,0 +1,161 @@ +# Changes in Respect\Validation 2.x + +## 2.3 + +Versioning Changes: + + - Dropped support for PHP 7.4. + - Updated dev dependencies + +Deprecations: + + - Symfony façade validators are no longer supported and were + removed. + +Fixes: + + - `KeySet` now reports which extra keys are causing the rule to fail. + +Changes: + + - You can no longer wrap `KeySet` in `Not`. + - `Phone` now uses `giggsey/libphonenumber-for-php`, this package needs + to be installed if you want to use this validator. + - `Phone` now supports the parameter `$countryCode` to validate phones + of a specific country. + +## 2.2.4 + +Meta: + + - CHANGELOG.md is being written once again to provide an overview + of active changes to the API and codebase. + +Versioning Changes: + + - Dropped PHP 7.3 support. + - Added support for PHP 8.0 and PHP 8.1. This will be the + last release with PHP 7.4 support. Support for PHP 8.2 is considered + experimental, local development should be done at 8.1. + +Deprecations: + + - Zend Framework façade validators are no longer supported and were + removed. + - Symfony façade validators are no longer suggested, and will be + removed in release 2.3. + - v::dateTime('z') is not supported anymore in PHP8, and should not be relied upon + +Fixes: + - Updated bin/update-currency-codes to fetch XML from another source. + - Updated bin/update-iso-codes to new file format. + - Updated regionals (CountryCode.php, CurrencyCode.php, Tld.php) (2023-02-13). + - Added RuPay card validation (thanks @rakshit087) + - Fixed `v::decimal()` for float values (thanks @scruwi) + - Added `v::portugueseNif()` to validate _Número de Identificação Fiscal_ in Portugal (thanks @goncalo-andrade). + - Allow 5-digit and 6-digit postal codes for Cambodia (thanks @omega3000) + - `v::intval()` now handles negative values with trailing zeroes better (thanks @l-x) + +## 2.2.x + +Changelogs between 1.1.0 and 2.2.4 are available only through `git log` and GitHub Releases. + +# Changes in Respect\Validation 1.x + +All notable changes of the Respect\Validation releases are documented in this file. + +## 1.1.0 - 2016-04-24 + +### Added + +- Create "Fibonacci" rule (#637) +- Create "IdentityCard" rule (#632) +- Create "Image" rule (#621) +- Create "LanguageCode" rule (#597) +- Create "Pesel" rule (#616) +- Create "PhpLabel" rule (#652) + +### Changed + +- Allow the define brands for credit card validation (#661) +- Define names for the child of Not rule (#641) +- Ensure namespace separator on appended prefixes (#666) +- Length gets length of integers (#643) +- Set template for the only rule in the chain (#663) +- Throw an exception when age is not an integer (#667) +- Use "{less/greater} than or equal to" phrasing (#604) + +## 1.0.0 - 2015-10-24 + +### Added + +- Add "alpha-3" and "numeric" formats for "CountryCode" rule (#530) +- Add support for PHP 7 (#426) +- Create "BoolVal" rule (#583) +- Create "Bsn" rule (#450) +- Create "CallableType" rule (#397) +- Create "Countable" rule (#566) +- Create "CurrencyCode" rule (#567) +- Create "Extension" rule (#360) +- Create "Factor" rule (#405) +- Create "Finite" rule (#397) +- Create "FloatType" rule (#565) +- Create "Identical" rule (#442) +- Create "Imei" rule (#590) +- Create "Infinite" rule (#397) +- Create "IntType" rule (#451) +- Create "Iterable" rule (#570) +- Create "KeyNested" rule (#429) +- Create "KeySet" rule (#374) +- Create "KeyValue" rule (#441) +- Create "Mimetype" rule (#361) +- Create "NotBlank" rule (#443) +- Create "NotOptional" rule (#448) +- Create "Optional" rule (#423) +- Create "ResourceType" rule (#397) +- Create "ScalarVal" rule (#397) +- Create "Size" rule (#359) +- Create "SubdivisionCode" rule for 252 countries (#411) +- Create "VideoUrl" rule (#410) +- Create method `getMessages()` for nested exceptions (#406) + +### Changed + +- Add country code to the message of "PostalCode" exception rule (#413) +- Make "ArrayVal" validate only if the input can be used as an array (#574) +- Make "Between" rule inclusive (#445) +- Make "Max" rule inclusive (#445) +- Make "Min" rule inclusive (#445) +- New generic top-level domains (#368) +- On `AbstractRelated` (`Attribute`, `Call` and `Key`) define names for child rules (#365) +- On exceptions, convert `Array` to string (#387) +- On exceptions, convert `Exception` to string (#399) +- On exceptions, convert `Traversable` to string (#399) +- On exceptions, convert resources to string (#399) +- On exceptions, do not display parent message then rule has only one child (#407) +- On exceptions, improve `Object` conversion to string (#399) +- On exceptions, improve conversion of all values by using JSON (#399) +- On exceptions, nested messages are displayed in a Markdown list (#588) +- Rename exception class "AbstractGroupedException" to "GroupedValidationException" (#591) +- Rename exception class "AbstractNestedException" to "NestedValidationException" (#591) +- Rename rule "Arr" to "ArrayVal" +- Rename rule "Bool" to "BoolType" (#426) +- Rename rule "False" to "FalseVal" (#426) +- Rename rule "Float" to "FloatVal" (#426) +- Rename rule "Int" to "IntVal" (#426) +- Rename rule "NullValue" to "NullType" +- Rename rule "Object" to "ObjectType" +- Rename rule "String" to "StringType" (#426) +- Rename rule "True" to "TrueVal" (#426) +- Use `filter_var()` on "TrueVal" and "FalseVal" rules (#409) + +### Removed + +- Drop support for PHP 5.3 (#466) +- Remove `addOr()` shortcut (#444) +- Remove `NestedValidationExceptionInterface` interface (#591) +- Remove `not()` shortcut (#444) +- Remove `ValidationExceptionInterface` interface (#591) +- Remove identical checking from "Equals" rule (#442) +- Removed Deprecated Rules (#277) +- Validation rules do not accept an empty string by default (#422) diff --git a/vendor/workerman/validation/CONTRIBUTING.md b/vendor/workerman/validation/CONTRIBUTING.md new file mode 100644 index 0000000..a760063 --- /dev/null +++ b/vendor/workerman/validation/CONTRIBUTING.md @@ -0,0 +1,244 @@ +# Contributing + +Contributions to Respect\Validation are always welcome. You make our lives +easier by sending us your contributions through [pull requests][]. + +Pull requests for bug fixes must be based on the oldest stable version's branch +whereas pull requests for new features must be based on the `master` branch. + +Due to time constraints, we are not always able to respond as quickly as we +would like. Please do not take delays personal and feel free to remind us here, +on IRC, or on Gitter if you feel that we forgot to respond. + +Please see the [project documentation][] before proceeding. You should also know +about [PHP-FIG][]'s standards and basic unit testing, but we're sure you can +learn that just by looking at other rules. Pick the simple ones like `ArrayType` +to begin. + +Before writing anything, feature or bug fix: +- Check if there is already an issue related to it (opened or closed) and if + someone is already working on that; + - If there is not, [open an issue][] and notify everybody that you're going + to work on that; + - If there is, create a comment to notify everybody that you're going to + work on that. +- Make sure that what you need is not done yet + +## Adding a new validator + +A common validator (rule) on Respect\Validation is composed of three classes: + + * `library/Rules/YourRuleName.php`: the rule itself + * `library/Exceptions/YourRuleNameException.php`: the exception thrown by the rule + * `tests/unit/Rules/YourRuleNameTest.php`: tests for the rule + +The classes are pretty straightforward. In the sample below, we're going to +create a validator that validates if a string is equal to "Hello World". + +- Classes should be `final` unless they are used in a different scope; +- Properties should be `private` unless they are used in a different scope; +- Classes should use strict typing; +- Some docblocks are required. + +### Creating the rule + +The rule itself needs to implement the `Validatable` interface but, it is +convenient to just extend the `AbstractRule` class. +Doing that, you'll only need to declare one method: `validate($input)`. +This method must return `true` or `false`. + +If your validator class is `HelloWorld`, it will be available as `v::helloWorld()` +and will natively have support for chaining and everything else. + +```php + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Explain in one sentence what this rule does. + * + * @author Your Name + */ +final class HelloWorld extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return $input === 'Hello World'; + } +} +``` + +### Creating the rule exception + +Just that and we're done with the rule code. The Exception requires you to +declare messages used by `assert()` and `check()`. Messages are declared in +affirmative and negative moods, so if anyone calls `v::not(v::helloWorld())` the +library will show the appropriate message. + +```php + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Your Name + */ +final class HelloWorldException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} must be a Hello World', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} must not be a Hello World', + ] + ]; +} +``` + +### Creating unit tests + +Finally, we need to test if everything is running smooth. We have `RuleTestCase` +that allows us to make easier to test rules, but you fell free to use the +`PHPUnit\Framework\TestCase` if you want or you need it's necessary. + +The `RuleTestCase` extends PHPUnit's `PHPUnit\Framework\TestCase` class, so you +are able to use any methods of it. By extending `RuleTestCase` you should +implement two methods that should return a [data provider][] with the rule as +first item of the arrays: + +- `providerForValidInput`: Will test when `validate()` should return `true` +- `providerForInvalidInput`: Will test when `validate()` should return `false` + +```php + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Test\RuleTestCase; + +/** + * @group rule + * + * @covers \Respect\Validation\Rules\HelloWorld + * + * @author Your Name + */ +final class HelloWorldTest extends RuleTestCase +{ + /** + * {@inheritDoc} + */ + public static function providerForValidInput(): array + { + $rule = new HelloWorld(); + + return [ + [$rule, 'Hello World'], + ]; + } + + /** + * {@inheritDoc} + */ + public static function providerForInvalidInput(): array + { + $rule = new HelloWorld(); + + return [ + [$rule, 'Not a hello'], + [$rule, 'Hello darkness, my old friend'], + [$rule, 'Hello is it me you\'re looking for?'], + ]; + } +} +``` + +If the constructor of your rule accepts arguments you may create specific tests +for it other than what is covered by `RuleTestCase`. + +### Helping us a little bit more + +You rule will be accepted only with these 3 files (rule, exception and unit test), +but if you really want to help us, you can follow the example of [ArrayType][] by: + +- Adding your new rule on the `Validator`'s class docblock; +- Writing a documentation for your new rule; +- Creating integration tests with PHPT. + +As we already said, none of them are required but you will help us a lot. + +## Documentation + +Our docs at https://respect-validation.readthedocs.io are generated from our +Markdown files. Add your brand new rule and it should be soon available. + +## Running Tests + +After run `composer install` on the library's root directory you must run PHPUnit. + +### Linux + +You can test the project using the commands: +```sh +$ vendor/bin/phpunit +``` + +or + +```sh +$ composer phpunit +``` + +### Windows + +You can test the project using the commands: +```sh +> vendor\bin\phpunit +``` + +or + +```sh +> composer phpunit +``` + +No test should fail. + +You can tweak the PHPUnit's settings by copying `phpunit.xml.dist` to `phpunit.xml` +and changing it according to your needs. + +[ArrayType]: https://github.com/Respect/Validation/commit/f08a1fa +[data provider]: https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers "PHPUnit Data Providers" +[open an issue]: http://github.com/Respect/Validation/issues +[PHP-FIG]: http://www.php-fig.org "PHP Framework Interop Group" +[project documentation]: https://respect-validation.readthedocs.io/ "Respect\Validation documentation" +[pull requests]: http://help.github.com/pull-requests "GitHub pull requests" diff --git a/vendor/workerman/validation/LICENSE b/vendor/workerman/validation/LICENSE new file mode 100644 index 0000000..1b92eaf --- /dev/null +++ b/vendor/workerman/validation/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Alexandre Gomes Gaigalas + +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/validation/README.md b/vendor/workerman/validation/README.md new file mode 100644 index 0000000..b81b4a1 --- /dev/null +++ b/vendor/workerman/validation/README.md @@ -0,0 +1,20 @@ +# [Respect\Validation 验证器](https://github.com/Respect/Validation) 汉化版 +# Respect\Validation + +[![Build Status](https://img.shields.io/github/actions/workflow/status/Respect/Validation/continuous-integration.yml?branch=master&style=flat-square)](https://github.com/Respect/Validation/actions/workflows/continuous-integration.yml) +[![Code Coverage](https://img.shields.io/codecov/c/github/Respect/Validation?style=flat-square)](https://codecov.io/gh/Respect/Validation) +[![Latest Stable Version](https://img.shields.io/packagist/v/respect/validation.svg?style=flat-square)](https://packagist.org/packages/respect/validation) +[![Total Downloads](https://img.shields.io/packagist/dt/respect/validation.svg?style=flat-square)](https://packagist.org/packages/respect/validation) +[![License](https://img.shields.io/packagist/l/respect/validation.svg?style=flat-square)](https://packagist.org/packages/respect/validation) + +The most awesome validation engine ever created for PHP. + +- Complex rules made simple: `v::numericVal()->positive()->between(1, 255)->validate($input)`. +- [Granularity control](docs/feature-guide.md#validation-methods) for advanced reporting. +- [More than 150](docs/list-of-rules.md) (fully tested) validation rules. +- [A concrete API](docs/concrete-api.md) for non fluent usage. + +Learn More: + +* [Documentation](https://respect-validation.readthedocs.io) +* [How to contribute](CONTRIBUTING.md) diff --git a/vendor/workerman/validation/composer.json b/vendor/workerman/validation/composer.json new file mode 100644 index 0000000..c89f527 --- /dev/null +++ b/vendor/workerman/validation/composer.json @@ -0,0 +1,73 @@ +{ + "name": "workerman/validation", + "description": "The most awesome validation engine ever created for PHP. Respect/Validation 汉化版本", + "keywords": ["respect", "validation", "validator"], + "type": "library", + "homepage": "http://respect.github.io/Validation/", + "license": "MIT", + "authors": [ + { + "name": "Respect/Validation Contributors", + "homepage": "https://github.com/Respect/Validation/graphs/contributors" + } + ], + "config": { + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + }, + "require": { + "php": "^8.0 || ^8.1 || ^8.2", + "respect/stringifier": "^0.2.0", + "symfony/polyfill-mbstring": "^1.2" + }, + "require-dev": { + "egulias/email-validator": "^3.0", + "giggsey/libphonenumber-for-php-lite": "^8.13", + "malukenho/docheader": "^1.0", + "mikey179/vfsstream": "^1.6", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.6", + "psr/http-message": "^1.0", + "respect/coding-standard": "^3.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "suggest": { + "ext-bcmath": "Arbitrary Precision Mathematics", + "ext-fileinfo": "File Information", + "ext-mbstring": "Multibyte String Functions", + "egulias/email-validator": "Improves the Email rule if available", + "giggsey/libphonenumber-for-php-lite": "Enables the phone rule if available" + }, + "autoload": { + "psr-4": { + "Respect\\Validation\\": "library/" + } + }, + "autoload-dev": { + "psr-4": { + "Respect\\Validation\\": "tests/unit/", + "Respect\\Validation\\Test\\": "tests/library/" + }, + "files": [ + "tests/integration/lib/helpers.php" + ] + }, + "scripts": { + "docheader": "vendor/bin/docheader check library/ tests/", + "phpcs": "vendor/bin/phpcs", + "phpstan": "vendor/bin/phpstan analyze", + "phpunit": "vendor/bin/phpunit", + "phpunit-integration": "vendor/bin/phpunit --testsuite=integration", + "phpunit-unit": "vendor/bin/phpunit --testsuite=unit", + "qa": [ + "@docheader", + "@phpcs", + "@phpstan", + "@phpunit" + ] + } +} diff --git a/vendor/workerman/validation/data/domain/public-suffix/AC.php b/vendor/workerman/validation/data/domain/public-suffix/AC.php new file mode 100644 index 0000000..5b4fc8c --- /dev/null +++ b/vendor/workerman/validation/data/domain/public-suffix/AC.php @@ -0,0 +1,11 @@ + "Andorra", + "subdivisions" => [ + "02" => "Canillo", + "03" => "Encamp", + "04" => "La Massana", + "05" => "Ordino", + "06" => "Sant Julià de Lòria", + "07" => "Andorra la Vella", + "08" => "Escaldes-Engordany" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AE.php b/vendor/workerman/validation/data/iso_3166-2/AE.php new file mode 100644 index 0000000..933d160 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AE.php @@ -0,0 +1,13 @@ + "United Arab Emirates", + "subdivisions" => [ + "AJ" => "‘Ajmān", + "AZ" => "Abū Z̧aby", + "DU" => "Dubayy", + "FU" => "Al Fujayrah", + "RK" => "Ra’s al Khaymah", + "SH" => "Ash Shāriqah", + "UQ" => "Umm al Qaywayn" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AF.php b/vendor/workerman/validation/data/iso_3166-2/AF.php new file mode 100644 index 0000000..d33f2f5 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AF.php @@ -0,0 +1,40 @@ + "Afghanistan", + "subdivisions" => [ + "BAL" => "Balkh", + "BAM" => "Bāmyān", + "BDG" => "Bādghīs", + "BDS" => "Badakhshān", + "BGL" => "Baghlān", + "DAY" => "Dāykundī", + "FRA" => "Farāh", + "FYB" => "Fāryāb", + "GHA" => "Ghaznī", + "GHO" => "Ghōr", + "HEL" => "Helmand", + "HER" => "Herāt", + "JOW" => "Jowzjān", + "KAB" => "Kābul", + "KAN" => "Kandahār", + "KAP" => "Kāpīsā", + "KDZ" => "Kunduz", + "KHO" => "Khōst", + "KNR" => "Kunaṟ", + "LAG" => "Laghmān", + "LOG" => "Lōgar", + "NAN" => "Nangarhār", + "NIM" => "Nīmrōz", + "NUR" => "Nūristān", + "PAN" => "Panjshayr", + "PAR" => "Parwān", + "PIA" => "Paktiyā", + "PKA" => "Paktīkā", + "SAM" => "Samangān", + "SAR" => "Sar-e Pul", + "TAK" => "Takhār", + "URU" => "Uruzgān", + "WAR" => "Wardak", + "ZAB" => "Zābul" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AG.php b/vendor/workerman/validation/data/iso_3166-2/AG.php new file mode 100644 index 0000000..6524330 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AG.php @@ -0,0 +1,14 @@ + "Antigua and Barbuda", + "subdivisions" => [ + "03" => "Saint George", + "04" => "Saint John", + "05" => "Saint Mary", + "06" => "Saint Paul", + "07" => "Saint Peter", + "08" => "Saint Philip", + "10" => "Barbuda", + "11" => "Redonda" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AI.php b/vendor/workerman/validation/data/iso_3166-2/AI.php new file mode 100644 index 0000000..97d787e --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AI.php @@ -0,0 +1,6 @@ + "Anguilla", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AL.php b/vendor/workerman/validation/data/iso_3166-2/AL.php new file mode 100644 index 0000000..dfc6db5 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AL.php @@ -0,0 +1,18 @@ + "Albania", + "subdivisions" => [ + "01" => "Berat", + "02" => "Durrës", + "03" => "Elbasan", + "04" => "Fier", + "05" => "Gjirokastër", + "06" => "Korçë", + "07" => "Kukës", + "08" => "Lezhë", + "09" => "Dibër", + "10" => "Shkodër", + "11" => "Tiranë", + "12" => "Vlorë" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AM.php b/vendor/workerman/validation/data/iso_3166-2/AM.php new file mode 100644 index 0000000..08e79d6 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AM.php @@ -0,0 +1,17 @@ + "Armenia", + "subdivisions" => [ + "AG" => "Aragac̣otn", + "AR" => "Ararat", + "AV" => "Armavir", + "ER" => "Erevan", + "GR" => "Geġark'unik'", + "KT" => "Kotayk'", + "LO" => "Loṙi", + "SH" => "Širak", + "SU" => "Syunik'", + "TV" => "Tavuš", + "VD" => "Vayoć Jor" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AO.php b/vendor/workerman/validation/data/iso_3166-2/AO.php new file mode 100644 index 0000000..a57d8d1 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AO.php @@ -0,0 +1,24 @@ + "Angola", + "subdivisions" => [ + "BGO" => "Bengo", + "BGU" => "Benguela", + "BIE" => "Bié", + "CAB" => "Cabinda", + "CCU" => "Cuando Cubango", + "CNN" => "Cunene", + "CNO" => "Cuanza-Norte", + "CUS" => "Cuanza-Sul", + "HUA" => "Huambo", + "HUI" => "Huíla", + "LNO" => "Lunda-Norte", + "LSU" => "Lunda-Sul", + "LUA" => "Luanda", + "MAL" => "Malange", + "MOX" => "Moxico", + "NAM" => "Namibe", + "UIG" => "Uíge", + "ZAI" => "Zaire" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AQ.php b/vendor/workerman/validation/data/iso_3166-2/AQ.php new file mode 100644 index 0000000..bfca8b7 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AQ.php @@ -0,0 +1,6 @@ + "Antarctica", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AR.php b/vendor/workerman/validation/data/iso_3166-2/AR.php new file mode 100644 index 0000000..b10c5fe --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AR.php @@ -0,0 +1,30 @@ + "Argentina", + "subdivisions" => [ + "A" => "Salta", + "B" => "Buenos Aires", + "C" => "Ciudad Autónoma de Buenos Aires", + "D" => "San Luis", + "E" => "Entre Ríos", + "F" => "La Rioja", + "G" => "Santiago del Estero", + "H" => "Chaco", + "J" => "San Juan", + "K" => "Catamarca", + "L" => "La Pampa", + "M" => "Mendoza", + "N" => "Misiones", + "P" => "Formosa", + "Q" => "Neuquén", + "R" => "Río Negro", + "S" => "Santa Fe", + "T" => "Tucumán", + "U" => "Chubut", + "V" => "Tierra del Fuego", + "W" => "Corrientes", + "X" => "Córdoba", + "Y" => "Jujuy", + "Z" => "Santa Cruz" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AS.php b/vendor/workerman/validation/data/iso_3166-2/AS.php new file mode 100644 index 0000000..2f1a65c --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AS.php @@ -0,0 +1,6 @@ + "American Samoa", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AT.php b/vendor/workerman/validation/data/iso_3166-2/AT.php new file mode 100644 index 0000000..6d62a19 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AT.php @@ -0,0 +1,15 @@ + "Austria", + "subdivisions" => [ + "1" => "Burgenland", + "2" => "Kärnten", + "3" => "Niederösterreich", + "4" => "Oberösterreich", + "5" => "Salzburg", + "6" => "Steiermark", + "7" => "Tirol", + "8" => "Vorarlberg", + "9" => "Wien" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AU.php b/vendor/workerman/validation/data/iso_3166-2/AU.php new file mode 100644 index 0000000..2823163 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AU.php @@ -0,0 +1,14 @@ + "Australia", + "subdivisions" => [ + "ACT" => "Australian Capital Territory", + "NSW" => "New South Wales", + "NT" => "Northern Territory", + "QLD" => "Queensland", + "SA" => "South Australia", + "TAS" => "Tasmania", + "VIC" => "Victoria", + "WA" => "Western Australia" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AW.php b/vendor/workerman/validation/data/iso_3166-2/AW.php new file mode 100644 index 0000000..86fe0ee --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AW.php @@ -0,0 +1,6 @@ + "Aruba", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AX.php b/vendor/workerman/validation/data/iso_3166-2/AX.php new file mode 100644 index 0000000..85b0881 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AX.php @@ -0,0 +1,6 @@ + "Åland Islands", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/AZ.php b/vendor/workerman/validation/data/iso_3166-2/AZ.php new file mode 100644 index 0000000..bdc2253 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/AZ.php @@ -0,0 +1,84 @@ + "Azerbaijan", + "subdivisions" => [ + "ABS" => "Abşeron", + "AGA" => "Ağstafa", + "AGC" => "Ağcabədi", + "AGM" => "Ağdam", + "AGS" => "Ağdaş", + "AGU" => "Ağsu", + "AST" => "Astara", + "BA" => "Bakı", + "BAB" => "Babək", + "BAL" => "Balakən", + "BAR" => "Bərdə", + "BEY" => "Beyləqan", + "BIL" => "Biləsuvar", + "CAB" => "Cəbrayıl", + "CAL" => "Cəlilabad", + "CUL" => "Culfa", + "DAS" => "Daşkəsən", + "FUZ" => "Füzuli", + "GA" => "Gəncə", + "GAD" => "Gədəbəy", + "GOR" => "Goranboy", + "GOY" => "Göyçay", + "GYG" => "Göygöl", + "HAC" => "Hacıqabul", + "IMI" => "İmişli", + "ISM" => "İsmayıllı", + "KAL" => "Kəlbəcər", + "KAN" => "Kǝngǝrli", + "KUR" => "Kürdəmir", + "LA" => "Lənkəran", + "LAC" => "Laçın", + "LAN" => "Lənkəran", + "LER" => "Lerik", + "MAS" => "Masallı", + "MI" => "Mingəçevir", + "NA" => "Naftalan", + "NEF" => "Neftçala", + "NV" => "Naxçıvan", + "NX" => "Naxçıvan", + "OGU" => "Oğuz", + "ORD" => "Ordubad", + "QAB" => "Qəbələ", + "QAX" => "Qax", + "QAZ" => "Qazax", + "QBA" => "Quba", + "QBI" => "Qubadlı", + "QOB" => "Qobustan", + "QUS" => "Qusar", + "SA" => "Şəki", + "SAB" => "Sabirabad", + "SAD" => "Sədərək", + "SAH" => "Şahbuz", + "SAK" => "Şəki", + "SAL" => "Salyan", + "SAR" => "Şərur", + "SAT" => "Saatlı", + "SBN" => "Şabran", + "SIY" => "Siyəzən", + "SKR" => "Şəmkir", + "SM" => "Sumqayıt", + "SMI" => "Şamaxı", + "SMX" => "Samux", + "SR" => "Şirvan", + "SUS" => "Şuşa", + "TAR" => "Tərtər", + "TOV" => "Tovuz", + "UCA" => "Ucar", + "XA" => "Xankəndi", + "XAC" => "Xaçmaz", + "XCI" => "Xocalı", + "XIZ" => "Xızı", + "XVD" => "Xocavənd", + "YAR" => "Yardımlı", + "YE" => "Yevlax", + "YEV" => "Yevlax", + "ZAN" => "Zəngilan", + "ZAQ" => "Zaqatala", + "ZAR" => "Zərdab" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BA.php b/vendor/workerman/validation/data/iso_3166-2/BA.php new file mode 100644 index 0000000..082aa26 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BA.php @@ -0,0 +1,9 @@ + "Bosnia and Herzegovina", + "subdivisions" => [ + "BIH" => "Federacija Bosne i Hercegovine", + "BRC" => "Brčko distrikt", + "SRP" => "Republika Srpska" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BB.php b/vendor/workerman/validation/data/iso_3166-2/BB.php new file mode 100644 index 0000000..0ef4a92 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BB.php @@ -0,0 +1,17 @@ + "Barbados", + "subdivisions" => [ + "01" => "Christ Church", + "02" => "Saint Andrew", + "03" => "Saint George", + "04" => "Saint James", + "05" => "Saint John", + "06" => "Saint Joseph", + "07" => "Saint Lucy", + "08" => "Saint Michael", + "09" => "Saint Peter", + "10" => "Saint Philip", + "11" => "Saint Thomas" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BD.php b/vendor/workerman/validation/data/iso_3166-2/BD.php new file mode 100644 index 0000000..fe62cb2 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BD.php @@ -0,0 +1,78 @@ + "Bangladesh", + "subdivisions" => [ + "01" => "Bandarban", + "02" => "Barguna", + "03" => "Bogura", + "04" => "Brahmanbaria", + "05" => "Bagerhat", + "06" => "Barishal", + "07" => "Bhola", + "08" => "Cumilla", + "09" => "Chandpur", + "10" => "Chattogram", + "11" => "Cox's Bazar", + "12" => "Chuadanga", + "13" => "Dhaka", + "14" => "Dinajpur", + "15" => "Faridpur", + "16" => "Feni", + "17" => "Gopalganj", + "18" => "Gazipur", + "19" => "Gaibandha", + "20" => "Habiganj", + "21" => "Jamalpur", + "22" => "Jashore", + "23" => "Jhenaidah", + "24" => "Joypurhat", + "25" => "Jhalakathi", + "26" => "Kishoreganj", + "27" => "Khulna", + "28" => "Kurigram", + "29" => "Khagrachhari", + "30" => "Kushtia", + "31" => "Lakshmipur", + "32" => "Lalmonirhat", + "33" => "Manikganj", + "34" => "Mymensingh", + "35" => "Munshiganj", + "36" => "Madaripur", + "37" => "Magura", + "38" => "Moulvibazar", + "39" => "Meherpur", + "40" => "Narayanganj", + "41" => "Netrakona", + "42" => "Narsingdi", + "43" => "Narail", + "44" => "Natore", + "45" => "Chapai Nawabganj", + "46" => "Nilphamari", + "47" => "Noakhali", + "48" => "Naogaon", + "49" => "Pabna", + "50" => "Pirojpur", + "51" => "Patuakhali", + "52" => "Panchagarh", + "53" => "Rajbari", + "54" => "Rajshahi", + "55" => "Rangpur", + "56" => "Rangamati", + "57" => "Sherpur", + "58" => "Satkhira", + "59" => "Sirajganj", + "60" => "Sylhet", + "61" => "Sunamganj", + "62" => "Shariatpur", + "63" => "Tangail", + "64" => "Thakurgaon", + "A" => "Barishal", + "B" => "Chattogram", + "C" => "Dhaka", + "D" => "Khulna", + "E" => "Rajshahi", + "F" => "Rangpur", + "G" => "Sylhet", + "H" => "Mymensingh" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BE.php b/vendor/workerman/validation/data/iso_3166-2/BE.php new file mode 100644 index 0000000..5ed5f81 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BE.php @@ -0,0 +1,19 @@ + "Belgium", + "subdivisions" => [ + "BRU" => "Brussels Hoofdstedelijk Gewest", + "VAN" => "Antwerpen", + "VBR" => "Vlaams-Brabant", + "VLG" => "Vlaams Gewest", + "VLI" => "Limburg", + "VOV" => "Oost-Vlaanderen", + "VWV" => "West-Vlaanderen", + "WAL" => "wallonne, Région", + "WBR" => "Brabant wallon", + "WHT" => "Hainaut", + "WLG" => "Liège", + "WLX" => "Luxembourg", + "WNA" => "Namur" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BF.php b/vendor/workerman/validation/data/iso_3166-2/BF.php new file mode 100644 index 0000000..ded9e49 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BF.php @@ -0,0 +1,64 @@ + "Burkina Faso", + "subdivisions" => [ + "01" => "Boucle du Mouhoun", + "02" => "Cascades", + "03" => "Centre", + "04" => "Centre-Est", + "05" => "Centre-Nord", + "06" => "Centre-Ouest", + "07" => "Centre-Sud", + "08" => "Est", + "09" => "Hauts-Bassins", + "10" => "Nord", + "11" => "Plateau-Central", + "12" => "Sahel", + "13" => "Sud-Ouest", + "BAL" => "Balé", + "BAM" => "Bam", + "BAN" => "Banwa", + "BAZ" => "Bazèga", + "BGR" => "Bougouriba", + "BLG" => "Boulgou", + "BLK" => "Boulkiemdé", + "COM" => "Comoé", + "GAN" => "Ganzourgou", + "GNA" => "Gnagna", + "GOU" => "Gourma", + "HOU" => "Houet", + "IOB" => "Ioba", + "KAD" => "Kadiogo", + "KEN" => "Kénédougou", + "KMD" => "Komondjari", + "KMP" => "Kompienga", + "KOP" => "Koulpélogo", + "KOS" => "Kossi", + "KOT" => "Kouritenga", + "KOW" => "Kourwéogo", + "LER" => "Léraba", + "LOR" => "Loroum", + "MOU" => "Mouhoun", + "NAM" => "Namentenga", + "NAO" => "Nahouri", + "NAY" => "Nayala", + "NOU" => "Noumbiel", + "OUB" => "Oubritenga", + "OUD" => "Oudalan", + "PAS" => "Passoré", + "PON" => "Poni", + "SEN" => "Séno", + "SIS" => "Sissili", + "SMT" => "Sanmatenga", + "SNG" => "Sanguié", + "SOM" => "Soum", + "SOR" => "Sourou", + "TAP" => "Tapoa", + "TUI" => "Tuy", + "YAG" => "Yagha", + "YAT" => "Yatenga", + "ZIR" => "Ziro", + "ZON" => "Zondoma", + "ZOU" => "Zoundwéogo" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BG.php b/vendor/workerman/validation/data/iso_3166-2/BG.php new file mode 100644 index 0000000..e5f2f92 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BG.php @@ -0,0 +1,34 @@ + "Bulgaria", + "subdivisions" => [ + "01" => "Blagoevgrad", + "02" => "Burgas", + "03" => "Varna", + "04" => "Veliko Tarnovo", + "05" => "Vidin", + "06" => "Vratsa", + "07" => "Gabrovo", + "08" => "Dobrich", + "09" => "Kardzhali", + "10" => "Kyustendil", + "11" => "Lovech", + "12" => "Montana", + "13" => "Pazardzhik", + "14" => "Pernik", + "15" => "Pleven", + "16" => "Plovdiv", + "17" => "Razgrad", + "18" => "Ruse", + "19" => "Silistra", + "20" => "Sliven", + "21" => "Smolyan", + "22" => "Sofia (stolitsa)", + "23" => "Sofia", + "24" => "Stara Zagora", + "25" => "Targovishte", + "26" => "Haskovo", + "27" => "Shumen", + "28" => "Yambol" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BH.php b/vendor/workerman/validation/data/iso_3166-2/BH.php new file mode 100644 index 0000000..70302db --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BH.php @@ -0,0 +1,10 @@ + "Bahrain", + "subdivisions" => [ + "13" => "Al ‘Āşimah", + "14" => "Al Janūbīyah", + "15" => "Al Muḩarraq", + "17" => "Ash Shamālīyah" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BI.php b/vendor/workerman/validation/data/iso_3166-2/BI.php new file mode 100644 index 0000000..6604d5f --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BI.php @@ -0,0 +1,24 @@ + "Burundi", + "subdivisions" => [ + "BB" => "Bubanza", + "BL" => "Bujumbura Rural", + "BM" => "Bujumbura Mairie", + "BR" => "Bururi", + "CA" => "Cankuzo", + "CI" => "Cibitoke", + "GI" => "Gitega", + "KI" => "Kirundo", + "KR" => "Karuzi", + "KY" => "Kayanza", + "MA" => "Makamba", + "MU" => "Muramvya", + "MW" => "Mwaro", + "MY" => "Muyinga", + "NG" => "Ngozi", + "RM" => "Rumonge", + "RT" => "Rutana", + "RY" => "Ruyigi" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BJ.php b/vendor/workerman/validation/data/iso_3166-2/BJ.php new file mode 100644 index 0000000..26b02a4 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BJ.php @@ -0,0 +1,18 @@ + "Benin", + "subdivisions" => [ + "AK" => "Atacora", + "AL" => "Alibori", + "AQ" => "Atlantique", + "BO" => "Borgou", + "CO" => "Collines", + "DO" => "Donga", + "KO" => "Couffo", + "LI" => "Littoral", + "MO" => "Mono", + "OU" => "Ouémé", + "PL" => "Plateau", + "ZO" => "Zou" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BL.php b/vendor/workerman/validation/data/iso_3166-2/BL.php new file mode 100644 index 0000000..200df0e --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BL.php @@ -0,0 +1,6 @@ + "Saint Barthélemy", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BM.php b/vendor/workerman/validation/data/iso_3166-2/BM.php new file mode 100644 index 0000000..0a00878 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BM.php @@ -0,0 +1,6 @@ + "Bermuda", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BN.php b/vendor/workerman/validation/data/iso_3166-2/BN.php new file mode 100644 index 0000000..6318e0e --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BN.php @@ -0,0 +1,10 @@ + "Brunei Darussalam", + "subdivisions" => [ + "BE" => "Belait", + "BM" => "Brunei-Muara", + "TE" => "Temburong", + "TU" => "Tutong" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BO.php b/vendor/workerman/validation/data/iso_3166-2/BO.php new file mode 100644 index 0000000..8baa3e0 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BO.php @@ -0,0 +1,15 @@ + "Bolivia, Plurinational State of", + "subdivisions" => [ + "B" => "El Beni", + "C" => "Cochabamba", + "H" => "Chuquisaca", + "L" => "La Paz", + "N" => "Pando", + "O" => "Oruro", + "P" => "Potosí", + "S" => "Santa Cruz", + "T" => "Tarija" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BQ.php b/vendor/workerman/validation/data/iso_3166-2/BQ.php new file mode 100644 index 0000000..983a08d --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BQ.php @@ -0,0 +1,9 @@ + "Bonaire, Sint Eustatius and Saba", + "subdivisions" => [ + "BO" => "Bonaire", + "SA" => "Saba", + "SE" => "Sint Eustatius" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BR.php b/vendor/workerman/validation/data/iso_3166-2/BR.php new file mode 100644 index 0000000..99e152b --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BR.php @@ -0,0 +1,33 @@ + "Brazil", + "subdivisions" => [ + "AC" => "Acre", + "AL" => "Alagoas", + "AM" => "Amazonas", + "AP" => "Amapá", + "BA" => "Bahia", + "CE" => "Ceará", + "DF" => "Distrito Federal", + "ES" => "Espírito Santo", + "GO" => "Goiás", + "MA" => "Maranhão", + "MG" => "Minas Gerais", + "MS" => "Mato Grosso do Sul", + "MT" => "Mato Grosso", + "PA" => "Pará", + "PB" => "Paraíba", + "PE" => "Pernambuco", + "PI" => "Piauí", + "PR" => "Paraná", + "RJ" => "Rio de Janeiro", + "RN" => "Rio Grande do Norte", + "RO" => "Rondônia", + "RR" => "Roraima", + "RS" => "Rio Grande do Sul", + "SC" => "Santa Catarina", + "SE" => "Sergipe", + "SP" => "São Paulo", + "TO" => "Tocantins" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BS.php b/vendor/workerman/validation/data/iso_3166-2/BS.php new file mode 100644 index 0000000..94ca85d --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BS.php @@ -0,0 +1,38 @@ + "Bahamas", + "subdivisions" => [ + "AK" => "Acklins", + "BI" => "Bimini", + "BP" => "Black Point", + "BY" => "Berry Islands", + "CE" => "Central Eleuthera", + "CI" => "Cat Island", + "CK" => "Crooked Island and Long Cay", + "CO" => "Central Abaco", + "CS" => "Central Andros", + "EG" => "East Grand Bahama", + "EX" => "Exuma", + "FP" => "City of Freeport", + "GC" => "Grand Cay", + "HI" => "Harbour Island", + "HT" => "Hope Town", + "IN" => "Inagua", + "LI" => "Long Island", + "MC" => "Mangrove Cay", + "MG" => "Mayaguana", + "MI" => "Moore's Island", + "NE" => "North Eleuthera", + "NO" => "North Abaco", + "NP" => "New Providence", + "NS" => "North Andros", + "RC" => "Rum Cay", + "RI" => "Ragged Island", + "SA" => "South Andros", + "SE" => "South Eleuthera", + "SO" => "South Abaco", + "SS" => "San Salvador", + "SW" => "Spanish Wells", + "WG" => "West Grand Bahama" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BT.php b/vendor/workerman/validation/data/iso_3166-2/BT.php new file mode 100644 index 0000000..2b09526 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BT.php @@ -0,0 +1,26 @@ + "Bhutan", + "subdivisions" => [ + "11" => "Paro", + "12" => "Chhukha", + "13" => "Haa", + "14" => "Samtse", + "15" => "Thimphu", + "21" => "Tsirang", + "22" => "Dagana", + "23" => "Punakha", + "24" => "Wangdue Phodrang", + "31" => "Sarpang", + "32" => "Trongsa", + "33" => "Bumthang", + "34" => "Zhemgang", + "41" => "Trashigang", + "42" => "Monggar", + "43" => "Pema Gatshel", + "44" => "Lhuentse", + "45" => "Samdrup Jongkhar", + "GA" => "Gasa", + "TY" => "Trashi Yangtse" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BV.php b/vendor/workerman/validation/data/iso_3166-2/BV.php new file mode 100644 index 0000000..2fcabda --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BV.php @@ -0,0 +1,6 @@ + "Bouvet Island", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BW.php b/vendor/workerman/validation/data/iso_3166-2/BW.php new file mode 100644 index 0000000..2d60fd8 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BW.php @@ -0,0 +1,22 @@ + "Botswana", + "subdivisions" => [ + "CE" => "Central", + "CH" => "Chobe", + "FR" => "Francistown", + "GA" => "Gaborone", + "GH" => "Ghanzi", + "JW" => "Jwaneng", + "KG" => "Kgalagadi", + "KL" => "Kgatleng", + "KW" => "Kweneng", + "LO" => "Lobatse", + "NE" => "North East", + "NW" => "North West", + "SE" => "South East", + "SO" => "Southern", + "SP" => "Selibe Phikwe", + "ST" => "Sowa Town" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BY.php b/vendor/workerman/validation/data/iso_3166-2/BY.php new file mode 100644 index 0000000..94fc0b8 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BY.php @@ -0,0 +1,13 @@ + "Belarus", + "subdivisions" => [ + "BR" => "Bresckaja voblasć", + "HM" => "Gorod Minsk", + "HO" => "Gomel'skaja oblast'", + "HR" => "Grodnenskaja oblast'", + "MA" => "Mahilioŭskaja voblasć", + "MI" => "Minskaja oblast'", + "VI" => "Viciebskaja voblasć" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/BZ.php b/vendor/workerman/validation/data/iso_3166-2/BZ.php new file mode 100644 index 0000000..cef38b7 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/BZ.php @@ -0,0 +1,12 @@ + "Belize", + "subdivisions" => [ + "BZ" => "Belize", + "CY" => "Cayo", + "CZL" => "Corozal", + "OW" => "Orange Walk", + "SC" => "Stann Creek", + "TOL" => "Toledo" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CA.php b/vendor/workerman/validation/data/iso_3166-2/CA.php new file mode 100644 index 0000000..e936a8f --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CA.php @@ -0,0 +1,19 @@ + "Canada", + "subdivisions" => [ + "AB" => "Alberta", + "BC" => "British Columbia", + "MB" => "Manitoba", + "NB" => "New Brunswick", + "NL" => "Newfoundland and Labrador", + "NS" => "Nova Scotia", + "NT" => "Northwest Territories", + "NU" => "Nunavut", + "ON" => "Ontario", + "PE" => "Prince Edward Island", + "QC" => "Quebec", + "SK" => "Saskatchewan", + "YT" => "Yukon" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CC.php b/vendor/workerman/validation/data/iso_3166-2/CC.php new file mode 100644 index 0000000..ed4f7a5 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CC.php @@ -0,0 +1,6 @@ + "Cocos (Keeling) Islands", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CD.php b/vendor/workerman/validation/data/iso_3166-2/CD.php new file mode 100644 index 0000000..cea0ac7 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CD.php @@ -0,0 +1,32 @@ + "Congo, The Democratic Republic of the", + "subdivisions" => [ + "BC" => "Kongo Central", + "BU" => "Bas-Uélé", + "EQ" => "Équateur", + "HK" => "Haut-Katanga", + "HL" => "Haut-Lomami", + "HU" => "Haut-Uélé", + "IT" => "Ituri", + "KC" => "Kasaï Central", + "KE" => "Kasaï Oriental", + "KG" => "Kwango", + "KL" => "Kwilu", + "KN" => "Kinshasa", + "KS" => "Kasaï", + "LO" => "Lomami", + "LU" => "Lualaba", + "MA" => "Maniema", + "MN" => "Mai-Ndombe", + "MO" => "Mongala", + "NK" => "Nord-Kivu", + "NU" => "Nord-Ubangi", + "SA" => "Sankuru", + "SK" => "Sud-Kivu", + "SU" => "Sud-Ubangi", + "TA" => "Tanganyika", + "TO" => "Tshopo", + "TU" => "Tshuapa" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CF.php b/vendor/workerman/validation/data/iso_3166-2/CF.php new file mode 100644 index 0000000..698d79c --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CF.php @@ -0,0 +1,23 @@ + "Central African Republic", + "subdivisions" => [ + "AC" => "Ouham", + "BB" => "Bamingui-Bangoran", + "BGF" => "Bangui", + "BK" => "Basse-Kotto", + "HK" => "Haute-Kotto", + "HM" => "Haut-Mbomou", + "HS" => "Haute-Sangha / Mambéré-Kadéï", + "KB" => "Gribingui", + "KG" => "Kemö-Gïrïbïngï", + "LB" => "Lobaye", + "MB" => "Mbomou", + "MP" => "Ombella-Mpoko", + "NM" => "Nana-Mambéré", + "OP" => "Ouham-Pendé", + "SE" => "Sangha", + "UK" => "Ouaka", + "VK" => "Vakaga" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CG.php b/vendor/workerman/validation/data/iso_3166-2/CG.php new file mode 100644 index 0000000..61e3ccc --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CG.php @@ -0,0 +1,18 @@ + "Congo", + "subdivisions" => [ + "11" => "Bouenza", + "12" => "Pool", + "13" => "Sangha", + "14" => "Plateaux", + "15" => "Cuvette-Ouest", + "16" => "Pointe-Noire", + "2" => "Lékoumou", + "5" => "Kouilou", + "7" => "Likouala", + "8" => "Cuvette", + "9" => "Niari", + "BZV" => "Brazzaville" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CH.php b/vendor/workerman/validation/data/iso_3166-2/CH.php new file mode 100644 index 0000000..71aba7d --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CH.php @@ -0,0 +1,32 @@ + "Switzerland", + "subdivisions" => [ + "AG" => "Aargau", + "AI" => "Appenzell Innerrhoden", + "AR" => "Appenzell Ausserrhoden", + "BE" => "Bern", + "BL" => "Basel-Landschaft", + "BS" => "Basel-Stadt", + "FR" => "Freiburg", + "GE" => "Genève", + "GL" => "Glarus", + "GR" => "Graubünden", + "JU" => "Jura", + "LU" => "Luzern", + "NE" => "Neuchâtel", + "NW" => "Nidwalden", + "OW" => "Obwalden", + "SG" => "Sankt Gallen", + "SH" => "Schaffhausen", + "SO" => "Solothurn", + "SZ" => "Schwyz", + "TG" => "Thurgau", + "TI" => "Ticino", + "UR" => "Uri", + "VD" => "Vaud", + "VS" => "Valais", + "ZG" => "Zug", + "ZH" => "Zürich" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CI.php b/vendor/workerman/validation/data/iso_3166-2/CI.php new file mode 100644 index 0000000..192c4b8 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CI.php @@ -0,0 +1,20 @@ + "Côte d'Ivoire", + "subdivisions" => [ + "AB" => "Abidjan", + "BS" => "Bas-Sassandra", + "CM" => "Comoé", + "DN" => "Denguélé", + "GD" => "Gôh-Djiboua", + "LC" => "Lacs", + "LG" => "Lagunes", + "MG" => "Montagnes", + "SM" => "Sassandra-Marahoué", + "SV" => "Savanes", + "VB" => "Vallée du Bandama", + "WR" => "Woroba", + "YM" => "Yamoussoukro", + "ZZ" => "Zanzan" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CK.php b/vendor/workerman/validation/data/iso_3166-2/CK.php new file mode 100644 index 0000000..1cb9cd6 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CK.php @@ -0,0 +1,6 @@ + "Cook Islands", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CL.php b/vendor/workerman/validation/data/iso_3166-2/CL.php new file mode 100644 index 0000000..a8fe1d8 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CL.php @@ -0,0 +1,22 @@ + "Chile", + "subdivisions" => [ + "AI" => "Aisén del General Carlos Ibañez del Campo", + "AN" => "Antofagasta", + "AP" => "Arica y Parinacota", + "AR" => "La Araucanía", + "AT" => "Atacama", + "BI" => "Biobío", + "CO" => "Coquimbo", + "LI" => "Libertador General Bernardo O'Higgins", + "LL" => "Los Lagos", + "LR" => "Los Ríos", + "MA" => "Magallanes", + "ML" => "Maule", + "NB" => "Ñuble", + "RM" => "Región Metropolitana de Santiago", + "TA" => "Tarapacá", + "VS" => "Valparaíso" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CM.php b/vendor/workerman/validation/data/iso_3166-2/CM.php new file mode 100644 index 0000000..c323565 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CM.php @@ -0,0 +1,16 @@ + "Cameroon", + "subdivisions" => [ + "AD" => "Adamaoua", + "CE" => "Centre", + "EN" => "Far North", + "ES" => "East", + "LT" => "Littoral", + "NO" => "North", + "NW" => "North-West", + "OU" => "West", + "SU" => "South", + "SW" => "South-West" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CN.php b/vendor/workerman/validation/data/iso_3166-2/CN.php new file mode 100644 index 0000000..75b684d --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CN.php @@ -0,0 +1,40 @@ + "China", + "subdivisions" => [ + "AH" => "Anhui Sheng", + "BJ" => "Beijing Shi", + "CQ" => "Chongqing Shi", + "FJ" => "Fujian Sheng", + "GD" => "Guangdong Sheng", + "GS" => "Gansu Sheng", + "GX" => "Guangxi Zhuangzu Zizhiqu", + "GZ" => "Guizhou Sheng", + "HA" => "Henan Sheng", + "HB" => "Hubei Sheng", + "HE" => "Hebei Sheng", + "HI" => "Hainan Sheng", + "HK" => "Hong Kong SAR", + "HL" => "Heilongjiang Sheng", + "HN" => "Hunan Sheng", + "JL" => "Jilin Sheng", + "JS" => "Jiangsu Sheng", + "JX" => "Jiangxi Sheng", + "LN" => "Liaoning Sheng", + "MO" => "Macao SAR", + "NM" => "Nei Mongol Zizhiqu", + "NX" => "Ningxia Huizi Zizhiqu", + "QH" => "Qinghai Sheng", + "SC" => "Sichuan Sheng", + "SD" => "Shandong Sheng", + "SH" => "Shanghai Shi", + "SN" => "Shaanxi Sheng", + "SX" => "Shanxi Sheng", + "TJ" => "Tianjin Shi", + "TW" => "Taiwan Sheng", + "XJ" => "Xinjiang Uygur Zizhiqu", + "XZ" => "Xizang Zizhiqu", + "YN" => "Yunnan Sheng", + "ZJ" => "Zhejiang Sheng" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CO.php b/vendor/workerman/validation/data/iso_3166-2/CO.php new file mode 100644 index 0000000..0b41056 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CO.php @@ -0,0 +1,39 @@ + "Colombia", + "subdivisions" => [ + "AMA" => "Amazonas", + "ANT" => "Antioquia", + "ARA" => "Arauca", + "ATL" => "Atlántico", + "BOL" => "Bolívar", + "BOY" => "Boyacá", + "CAL" => "Caldas", + "CAQ" => "Caquetá", + "CAS" => "Casanare", + "CAU" => "Cauca", + "CES" => "Cesar", + "CHO" => "Chocó", + "COR" => "Córdoba", + "CUN" => "Cundinamarca", + "DC" => "Distrito Capital de Bogotá", + "GUA" => "Guainía", + "GUV" => "Guaviare", + "HUI" => "Huila", + "LAG" => "La Guajira", + "MAG" => "Magdalena", + "MET" => "Meta", + "NAR" => "Nariño", + "NSA" => "Norte de Santander", + "PUT" => "Putumayo", + "QUI" => "Quindío", + "RIS" => "Risaralda", + "SAN" => "Santander", + "SAP" => "San Andrés, Providencia y Santa Catalina", + "SUC" => "Sucre", + "TOL" => "Tolima", + "VAC" => "Valle del Cauca", + "VAU" => "Vaupés", + "VID" => "Vichada" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CR.php b/vendor/workerman/validation/data/iso_3166-2/CR.php new file mode 100644 index 0000000..342444a --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CR.php @@ -0,0 +1,13 @@ + "Costa Rica", + "subdivisions" => [ + "A" => "Alajuela", + "C" => "Cartago", + "G" => "Guanacaste", + "H" => "Heredia", + "L" => "Limón", + "P" => "Puntarenas", + "SJ" => "San José" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CU.php b/vendor/workerman/validation/data/iso_3166-2/CU.php new file mode 100644 index 0000000..8158d2c --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CU.php @@ -0,0 +1,22 @@ + "Cuba", + "subdivisions" => [ + "01" => "Pinar del Río", + "03" => "La Habana", + "04" => "Matanzas", + "05" => "Villa Clara", + "06" => "Cienfuegos", + "07" => "Sancti Spíritus", + "08" => "Ciego de Ávila", + "09" => "Camagüey", + "10" => "Las Tunas", + "11" => "Holguín", + "12" => "Granma", + "13" => "Santiago de Cuba", + "14" => "Guantánamo", + "15" => "Artemisa", + "16" => "Mayabeque", + "99" => "Isla de la Juventud" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CV.php b/vendor/workerman/validation/data/iso_3166-2/CV.php new file mode 100644 index 0000000..2fb6111 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CV.php @@ -0,0 +1,30 @@ + "Cabo Verde", + "subdivisions" => [ + "B" => "Ilhas de Barlavento", + "BR" => "Brava", + "BV" => "Boa Vista", + "CA" => "Santa Catarina", + "CF" => "Santa Catarina do Fogo", + "CR" => "Santa Cruz", + "MA" => "Maio", + "MO" => "Mosteiros", + "PA" => "Paul", + "PN" => "Porto Novo", + "PR" => "Praia", + "RB" => "Ribeira Brava", + "RG" => "Ribeira Grande", + "RS" => "Ribeira Grande de Santiago", + "S" => "Ilhas de Sotavento", + "SD" => "São Domingos", + "SF" => "São Filipe", + "SL" => "Sal", + "SM" => "São Miguel", + "SO" => "São Lourenço dos Órgãos", + "SS" => "São Salvador do Mundo", + "SV" => "São Vicente", + "TA" => "Tarrafal", + "TS" => "Tarrafal de São Nicolau" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CW.php b/vendor/workerman/validation/data/iso_3166-2/CW.php new file mode 100644 index 0000000..a9e06ef --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CW.php @@ -0,0 +1,6 @@ + "Curaçao", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CX.php b/vendor/workerman/validation/data/iso_3166-2/CX.php new file mode 100644 index 0000000..b226971 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CX.php @@ -0,0 +1,6 @@ + "Christmas Island", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CY.php b/vendor/workerman/validation/data/iso_3166-2/CY.php new file mode 100644 index 0000000..8e9a4c5 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CY.php @@ -0,0 +1,12 @@ + "Cyprus", + "subdivisions" => [ + "01" => "Lefkosia", + "02" => "Lemesos", + "03" => "Larnaka", + "04" => "Ammochostos", + "05" => "Baf", + "06" => "Girne" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/CZ.php b/vendor/workerman/validation/data/iso_3166-2/CZ.php new file mode 100644 index 0000000..f532a4a --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/CZ.php @@ -0,0 +1,96 @@ + "Czechia", + "subdivisions" => [ + "10" => "Praha, Hlavní město", + "20" => "Středočeský kraj", + "201" => "Benešov", + "202" => "Beroun", + "203" => "Kladno", + "204" => "Kolín", + "205" => "Kutná Hora", + "206" => "Mělník", + "207" => "Mladá Boleslav", + "208" => "Nymburk", + "209" => "Praha-východ", + "20A" => "Praha-západ", + "20B" => "Příbram", + "20C" => "Rakovník", + "31" => "Jihočeský kraj", + "311" => "České Budějovice", + "312" => "Český Krumlov", + "313" => "Jindřichův Hradec", + "314" => "Písek", + "315" => "Prachatice", + "316" => "Strakonice", + "317" => "Tábor", + "32" => "Plzeňský kraj", + "321" => "Domažlice", + "322" => "Klatovy", + "323" => "Plzeň-město", + "324" => "Plzeň-jih", + "325" => "Plzeň-sever", + "326" => "Rokycany", + "327" => "Tachov", + "41" => "Karlovarský kraj", + "411" => "Cheb", + "412" => "Karlovy Vary", + "413" => "Sokolov", + "42" => "Ústecký kraj", + "421" => "Děčín", + "422" => "Chomutov", + "423" => "Litoměřice", + "424" => "Louny", + "425" => "Most", + "426" => "Teplice", + "427" => "Ústí nad Labem", + "51" => "Liberecký kraj", + "511" => "Česká Lípa", + "512" => "Jablonec nad Nisou", + "513" => "Liberec", + "514" => "Semily", + "52" => "Královéhradecký kraj", + "521" => "Hradec Králové", + "522" => "Jičín", + "523" => "Náchod", + "524" => "Rychnov nad Kněžnou", + "525" => "Trutnov", + "53" => "Pardubický kraj", + "531" => "Chrudim", + "532" => "Pardubice", + "533" => "Svitavy", + "534" => "Ústí nad Orlicí", + "63" => "Kraj Vysočina", + "631" => "Havlíčkův Brod", + "632" => "Jihlava", + "633" => "Pelhřimov", + "634" => "Třebíč", + "635" => "Žďár nad Sázavou", + "64" => "Jihomoravský kraj", + "641" => "Blansko", + "642" => "Brno-město", + "643" => "Brno-venkov", + "644" => "Břeclav", + "645" => "Hodonín", + "646" => "Vyškov", + "647" => "Znojmo", + "71" => "Olomoucký kraj", + "711" => "Jeseník", + "712" => "Olomouc", + "713" => "Prostějov", + "714" => "Přerov", + "715" => "Šumperk", + "72" => "Zlínský kraj", + "721" => "Kroměříž", + "722" => "Uherské Hradiště", + "723" => "Vsetín", + "724" => "Zlín", + "80" => "Moravskoslezský kraj", + "801" => "Bruntál", + "802" => "Frýdek-Místek", + "803" => "Karviná", + "804" => "Nový Jičín", + "805" => "Opava", + "806" => "Ostrava-město" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/DE.php b/vendor/workerman/validation/data/iso_3166-2/DE.php new file mode 100644 index 0000000..fe5c855 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/DE.php @@ -0,0 +1,22 @@ + "Germany", + "subdivisions" => [ + "BB" => "Brandenburg", + "BE" => "Berlin", + "BW" => "Baden-Württemberg", + "BY" => "Bayern", + "HB" => "Bremen", + "HE" => "Hessen", + "HH" => "Hamburg", + "MV" => "Mecklenburg-Vorpommern", + "NI" => "Niedersachsen", + "NW" => "Nordrhein-Westfalen", + "RP" => "Rheinland-Pfalz", + "SH" => "Schleswig-Holstein", + "SL" => "Saarland", + "SN" => "Sachsen", + "ST" => "Sachsen-Anhalt", + "TH" => "Thüringen" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/DJ.php b/vendor/workerman/validation/data/iso_3166-2/DJ.php new file mode 100644 index 0000000..9eba57d --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/DJ.php @@ -0,0 +1,12 @@ + "Djibouti", + "subdivisions" => [ + "AR" => "Arta", + "AS" => "Ali Sabieh", + "DI" => "Dikhil", + "DJ" => "Djibouti", + "OB" => "Awbūk", + "TA" => "Tadjourah" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/DK.php b/vendor/workerman/validation/data/iso_3166-2/DK.php new file mode 100644 index 0000000..771fa45 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/DK.php @@ -0,0 +1,11 @@ + "Denmark", + "subdivisions" => [ + "81" => "Nordjylland", + "82" => "Midtjylland", + "83" => "Syddanmark", + "84" => "Hovedstaden", + "85" => "Sjælland" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/DM.php b/vendor/workerman/validation/data/iso_3166-2/DM.php new file mode 100644 index 0000000..cd95930 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/DM.php @@ -0,0 +1,16 @@ + "Dominica", + "subdivisions" => [ + "02" => "Saint Andrew", + "03" => "Saint David", + "04" => "Saint George", + "05" => "Saint John", + "06" => "Saint Joseph", + "07" => "Saint Luke", + "08" => "Saint Mark", + "09" => "Saint Patrick", + "10" => "Saint Paul", + "11" => "Saint Peter" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/DO.php b/vendor/workerman/validation/data/iso_3166-2/DO.php new file mode 100644 index 0000000..8698a86 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/DO.php @@ -0,0 +1,48 @@ + "Dominican Republic", + "subdivisions" => [ + "01" => "Distrito Nacional (Santo Domingo)", + "02" => "Azua", + "03" => "Baoruco", + "04" => "Barahona", + "05" => "Dajabón", + "06" => "Duarte", + "07" => "Elías Piña", + "08" => "El Seibo", + "09" => "Espaillat", + "10" => "Independencia", + "11" => "La Altagracia", + "12" => "La Romana", + "13" => "La Vega", + "14" => "María Trinidad Sánchez", + "15" => "Monte Cristi", + "16" => "Pedernales", + "17" => "Peravia", + "18" => "Puerto Plata", + "19" => "Hermanas Mirabal", + "20" => "Samaná", + "21" => "San Cristóbal", + "22" => "San Juan", + "23" => "San Pedro de Macorís", + "24" => "Sánchez Ramírez", + "25" => "Santiago", + "26" => "Santiago Rodríguez", + "27" => "Valverde", + "28" => "Monseñor Nouel", + "29" => "Monte Plata", + "30" => "Hato Mayor", + "31" => "San José de Ocoa", + "32" => "Santo Domingo", + "33" => "Cibao Nordeste", + "34" => "Cibao Noroeste", + "35" => "Cibao Norte", + "36" => "Cibao Sur", + "37" => "El Valle", + "38" => "Enriquillo", + "39" => "Higuamo", + "40" => "Ozama", + "41" => "Valdesia", + "42" => "Yuma" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/DZ.php b/vendor/workerman/validation/data/iso_3166-2/DZ.php new file mode 100644 index 0000000..766ca0e --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/DZ.php @@ -0,0 +1,54 @@ + "Algeria", + "subdivisions" => [ + "01" => "Adrar", + "02" => "Chlef", + "03" => "Laghouat", + "04" => "Oum el Bouaghi", + "05" => "Batna", + "06" => "Béjaïa", + "07" => "Biskra", + "08" => "Béchar", + "09" => "Blida", + "10" => "Bouira", + "11" => "Tamanrasset", + "12" => "Tébessa", + "13" => "Tlemcen", + "14" => "Tiaret", + "15" => "Tizi Ouzou", + "16" => "Alger", + "17" => "Djelfa", + "18" => "Jijel", + "19" => "Sétif", + "20" => "Saïda", + "21" => "Skikda", + "22" => "Sidi Bel Abbès", + "23" => "Annaba", + "24" => "Guelma", + "25" => "Constantine", + "26" => "Médéa", + "27" => "Mostaganem", + "28" => "M'sila", + "29" => "Mascara", + "30" => "Ouargla", + "31" => "Oran", + "32" => "El Bayadh", + "33" => "Illizi", + "34" => "Bordj Bou Arréridj", + "35" => "Boumerdès", + "36" => "El Tarf", + "37" => "Tindouf", + "38" => "Tissemsilt", + "39" => "El Oued", + "40" => "Khenchela", + "41" => "Souk Ahras", + "42" => "Tipaza", + "43" => "Mila", + "44" => "Aïn Defla", + "45" => "Naama", + "46" => "Aïn Témouchent", + "47" => "Ghardaïa", + "48" => "Relizane" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/EC.php b/vendor/workerman/validation/data/iso_3166-2/EC.php new file mode 100644 index 0000000..4104bed --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/EC.php @@ -0,0 +1,30 @@ + "Ecuador", + "subdivisions" => [ + "A" => "Azuay", + "B" => "Bolívar", + "C" => "Carchi", + "D" => "Orellana", + "E" => "Esmeraldas", + "F" => "Cañar", + "G" => "Guayas", + "H" => "Chimborazo", + "I" => "Imbabura", + "L" => "Loja", + "M" => "Manabí", + "N" => "Napo", + "O" => "El Oro", + "P" => "Pichincha", + "R" => "Los Ríos", + "S" => "Morona Santiago", + "SD" => "Santo Domingo de los Tsáchilas", + "SE" => "Santa Elena", + "T" => "Tungurahua", + "U" => "Sucumbíos", + "W" => "Galápagos", + "X" => "Cotopaxi", + "Y" => "Pastaza", + "Z" => "Zamora Chinchipe" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/EE.php b/vendor/workerman/validation/data/iso_3166-2/EE.php new file mode 100644 index 0000000..4e90f79 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/EE.php @@ -0,0 +1,100 @@ + "Estonia", + "subdivisions" => [ + "130" => "Alutaguse", + "141" => "Anija", + "142" => "Antsla", + "171" => "Elva", + "184" => "Haapsalu", + "191" => "Haljala", + "198" => "Harku", + "205" => "Hiiumaa", + "214" => "Häädemeeste", + "245" => "Jõelähtme", + "247" => "Jõgeva", + "251" => "Jõhvi", + "255" => "Järva", + "272" => "Kadrina", + "283" => "Kambja", + "284" => "Kanepi", + "291" => "Kastre", + "293" => "Kehtna", + "296" => "Keila", + "303" => "Kihnu", + "305" => "Kiili", + "317" => "Kohila", + "321" => "Kohtla-Järve", + "338" => "Kose", + "353" => "Kuusalu", + "37" => "Harjumaa", + "39" => "Hiiumaa", + "424" => "Loksa", + "430" => "Lääneranna", + "431" => "Lääne-Harju", + "432" => "Luunja", + "441" => "Lääne-Nigula", + "442" => "Lüganuse", + "446" => "Maardu", + "45" => "Ida-Virumaa", + "478" => "Muhu", + "480" => "Mulgi", + "486" => "Mustvee", + "50" => "Jõgevamaa", + "503" => "Märjamaa", + "511" => "Narva", + "514" => "Narva-Jõesuu", + "52" => "Järvamaa", + "528" => "Nõo", + "557" => "Otepää", + "56" => "Läänemaa", + "567" => "Paide", + "586" => "Peipsiääre", + "60" => "Lääne-Virumaa", + "615" => "Põhja-Sakala", + "618" => "Põltsamaa", + "622" => "Põlva", + "624" => "Pärnu", + "638" => "Põhja-Pärnumaa", + "64" => "Põlvamaa", + "651" => "Raasiku", + "653" => "Rae", + "661" => "Rakvere", + "663" => "Rakvere", + "668" => "Rapla", + "68" => "Pärnumaa", + "689" => "Ruhnu", + "698" => "Rõuge", + "708" => "Räpina", + "71" => "Raplamaa", + "712" => "Saarde", + "714" => "Saaremaa", + "719" => "Saku", + "726" => "Saue", + "732" => "Setomaa", + "735" => "Sillamäe", + "74" => "Saaremaa", + "784" => "Tallinn", + "79" => "Tartumaa", + "792" => "Tapa", + "793" => "Tartu", + "796" => "Tartu", + "803" => "Toila", + "809" => "Tori", + "81" => "Valgamaa", + "824" => "Tõrva", + "834" => "Türi", + "84" => "Viljandimaa", + "855" => "Valga", + "87" => "Võrumaa", + "890" => "Viimsi", + "897" => "Viljandi", + "899" => "Viljandi", + "901" => "Vinni", + "903" => "Viru-Nigula", + "907" => "Vormsi", + "917" => "Võru", + "919" => "Võru", + "928" => "Väike-Maarja" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/EG.php b/vendor/workerman/validation/data/iso_3166-2/EG.php new file mode 100644 index 0000000..e0fdf93 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/EG.php @@ -0,0 +1,33 @@ + "Egypt", + "subdivisions" => [ + "ALX" => "Al Iskandarīyah", + "ASN" => "Aswān", + "AST" => "Asyūţ", + "BA" => "Al Baḩr al Aḩmar", + "BH" => "Al Buḩayrah", + "BNS" => "Banī Suwayf", + "C" => "Al Qāhirah", + "DK" => "Ad Daqahlīyah", + "DT" => "Dumyāţ", + "FYM" => "Al Fayyūm", + "GH" => "Al Gharbīyah", + "GZ" => "Al Jīzah", + "IS" => "Al Ismā'īlīyah", + "JS" => "Janūb Sīnā'", + "KB" => "Al Qalyūbīyah", + "KFS" => "Kafr ash Shaykh", + "KN" => "Qinā", + "LX" => "Al Uqşur", + "MN" => "Al Minyā", + "MNF" => "Al Minūfīyah", + "MT" => "Maţrūḩ", + "PTS" => "Būr Sa‘īd", + "SHG" => "Sūhāj", + "SHR" => "Ash Sharqīyah", + "SIN" => "Shamāl Sīnā'", + "SUZ" => "As Suways", + "WAD" => "Al Wādī al Jadīd" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/EH.php b/vendor/workerman/validation/data/iso_3166-2/EH.php new file mode 100644 index 0000000..43bf8e2 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/EH.php @@ -0,0 +1,6 @@ + "Western Sahara", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/ER.php b/vendor/workerman/validation/data/iso_3166-2/ER.php new file mode 100644 index 0000000..274973d --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/ER.php @@ -0,0 +1,12 @@ + "Eritrea", + "subdivisions" => [ + "AN" => "Ansabā", + "DK" => "Debubawi K’eyyĭḥ Baḥri", + "DU" => "Al Janūbī", + "GB" => "Gash-Barka", + "MA" => "Al Awsaţ", + "SK" => "Semienawi K’eyyĭḥ Baḥri" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/ES.php b/vendor/workerman/validation/data/iso_3166-2/ES.php new file mode 100644 index 0000000..02f43a8 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/ES.php @@ -0,0 +1,75 @@ + "Spain", + "subdivisions" => [ + "A" => "Alacant*", + "AB" => "Albacete", + "AL" => "Almería", + "AN" => "Andalucía", + "AR" => "Aragón", + "AS" => "Asturias, Principado de", + "AV" => "Ávila", + "B" => "Barcelona [Barcelona]", + "BA" => "Badajoz", + "BI" => "Bizkaia", + "BU" => "Burgos", + "C" => "A Coruña [La Coruña]", + "CA" => "Cádiz", + "CB" => "Cantabria", + "CC" => "Cáceres", + "CE" => "Ceuta", + "CL" => "Castilla y León", + "CM" => "Castilla-La Mancha", + "CN" => "Canarias", + "CO" => "Córdoba", + "CR" => "Ciudad Real", + "CS" => "Castelló*", + "CT" => "Catalunya [Cataluña]", + "CU" => "Cuenca", + "EX" => "Extremadura", + "GA" => "Galicia [Galicia]", + "GC" => "Las Palmas", + "GI" => "Girona [Gerona]", + "GR" => "Granada", + "GU" => "Guadalajara", + "H" => "Huelva", + "HU" => "Huesca", + "IB" => "Illes Balears [Islas Baleares]", + "J" => "Jaén", + "L" => "Lleida [Lérida]", + "LE" => "León", + "LO" => "La Rioja", + "LU" => "Lugo [Lugo]", + "M" => "Madrid", + "MA" => "Málaga", + "MC" => "Murcia, Región de", + "MD" => "Madrid, Comunidad de", + "ML" => "Melilla", + "MU" => "Murcia", + "NA" => "Nafarroa*", + "NC" => "Nafarroako Foru Komunitatea*", + "O" => "Asturias", + "OR" => "Ourense [Orense]", + "P" => "Palencia", + "PM" => "Illes Balears [Islas Baleares]", + "PO" => "Pontevedra [Pontevedra]", + "PV" => "Euskal Herria", + "RI" => "La Rioja", + "S" => "Cantabria", + "SA" => "Salamanca", + "SE" => "Sevilla", + "SG" => "Segovia", + "SO" => "Soria", + "SS" => "Gipuzkoa", + "T" => "Tarragona [Tarragona]", + "TE" => "Teruel", + "TF" => "Santa Cruz de Tenerife", + "TO" => "Toledo", + "V" => "Valencia", + "VA" => "Valladolid", + "VC" => "Valenciana, Comunidad", + "VI" => "Araba*", + "Z" => "Zaragoza", + "ZA" => "Zamora" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/ET.php b/vendor/workerman/validation/data/iso_3166-2/ET.php new file mode 100644 index 0000000..85b5322 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/ET.php @@ -0,0 +1,17 @@ + "Ethiopia", + "subdivisions" => [ + "AA" => "Addis Ababa", + "AF" => "Afar", + "AM" => "Amara", + "BE" => "Benshangul-Gumaz", + "DD" => "Dire Dawa", + "GA" => "Gambela Peoples", + "HA" => "Harari People", + "OR" => "Oromia", + "SN" => "Southern Nations, Nationalities and Peoples", + "SO" => "Somali", + "TI" => "Tigrai" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/FI.php b/vendor/workerman/validation/data/iso_3166-2/FI.php new file mode 100644 index 0000000..21287a6 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/FI.php @@ -0,0 +1,25 @@ + "Finland", + "subdivisions" => [ + "01" => "Åland", + "02" => "Etelä-Karjala", + "03" => "Etelä-Pohjanmaa", + "04" => "Etelä-Savo", + "05" => "Kainuu", + "06" => "Kanta-Häme", + "07" => "Keski-Pohjanmaa", + "08" => "Keski-Suomi", + "09" => "Kymenlaakso", + "10" => "Lappi", + "11" => "Pirkanmaa", + "12" => "Pohjanmaa", + "13" => "Pohjois-Karjala", + "14" => "Pohjois-Pohjanmaa", + "15" => "Pohjois-Savo", + "16" => "Päijät-Häme", + "17" => "Satakunta", + "18" => "Uusimaa", + "19" => "Varsinais-Suomi" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/FJ.php b/vendor/workerman/validation/data/iso_3166-2/FJ.php new file mode 100644 index 0000000..34a180a --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/FJ.php @@ -0,0 +1,25 @@ + "Fiji", + "subdivisions" => [ + "01" => "Ba", + "02" => "Bua", + "03" => "Cakaudrove", + "04" => "Kadavu", + "05" => "Lau", + "06" => "Lomaiviti", + "07" => "Macuata", + "08" => "Nadroga and Navosa", + "09" => "Naitasiri", + "10" => "Namosi", + "11" => "Ra", + "12" => "Rewa", + "13" => "Serua", + "14" => "Tailevu", + "C" => "Central", + "E" => "Eastern", + "N" => "Northern", + "R" => "Rotuma", + "W" => "Western" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/FK.php b/vendor/workerman/validation/data/iso_3166-2/FK.php new file mode 100644 index 0000000..19b63fa --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/FK.php @@ -0,0 +1,6 @@ + "Falkland Islands (Malvinas)", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/FM.php b/vendor/workerman/validation/data/iso_3166-2/FM.php new file mode 100644 index 0000000..61c9116 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/FM.php @@ -0,0 +1,10 @@ + "Micronesia, Federated States of", + "subdivisions" => [ + "KSA" => "Kosrae", + "PNI" => "Pohnpei", + "TRK" => "Chuuk", + "YAP" => "Yap" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/FO.php b/vendor/workerman/validation/data/iso_3166-2/FO.php new file mode 100644 index 0000000..15a3e40 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/FO.php @@ -0,0 +1,6 @@ + "Faroe Islands", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/FR.php b/vendor/workerman/validation/data/iso_3166-2/FR.php new file mode 100644 index 0000000..8526e82 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/FR.php @@ -0,0 +1,133 @@ + "France", + "subdivisions" => [ + "01" => "Ain", + "02" => "Aisne", + "03" => "Allier", + "04" => "Alpes-de-Haute-Provence", + "05" => "Hautes-Alpes", + "06" => "Alpes-Maritimes", + "07" => "Ardèche", + "08" => "Ardennes", + "09" => "Ariège", + "10" => "Aube", + "11" => "Aude", + "12" => "Aveyron", + "13" => "Bouches-du-Rhône", + "14" => "Calvados", + "15" => "Cantal", + "16" => "Charente", + "17" => "Charente-Maritime", + "18" => "Cher", + "19" => "Corrèze", + "20R" => "Corse", + "21" => "Côte-d'Or", + "22" => "Côtes-d'Armor", + "23" => "Creuse", + "24" => "Dordogne", + "25" => "Doubs", + "26" => "Drôme", + "27" => "Eure", + "28" => "Eure-et-Loir", + "29" => "Finistère", + "2A" => "Corse-du-Sud", + "2B" => "Haute-Corse", + "30" => "Gard", + "31" => "Haute-Garonne", + "32" => "Gers", + "33" => "Gironde", + "34" => "Hérault", + "35" => "Ille-et-Vilaine", + "36" => "Indre", + "37" => "Indre-et-Loire", + "38" => "Isère", + "39" => "Jura", + "40" => "Landes", + "41" => "Loir-et-Cher", + "42" => "Loire", + "43" => "Haute-Loire", + "44" => "Loire-Atlantique", + "45" => "Loiret", + "46" => "Lot", + "47" => "Lot-et-Garonne", + "48" => "Lozère", + "49" => "Maine-et-Loire", + "50" => "Manche", + "51" => "Marne", + "52" => "Haute-Marne", + "53" => "Mayenne", + "54" => "Meurthe-et-Moselle", + "55" => "Meuse", + "56" => "Morbihan", + "57" => "Moselle", + "58" => "Nièvre", + "59" => "Nord", + "60" => "Oise", + "61" => "Orne", + "62" => "Pas-de-Calais", + "63" => "Puy-de-Dôme", + "64" => "Pyrénées-Atlantiques", + "65" => "Hautes-Pyrénées", + "66" => "Pyrénées-Orientales", + "67" => "Bas-Rhin", + "68" => "Haut-Rhin", + "69" => "Rhône", + "70" => "Haute-Saône", + "71" => "Saône-et-Loire", + "72" => "Sarthe", + "73" => "Savoie", + "74" => "Haute-Savoie", + "75" => "Paris", + "76" => "Seine-Maritime", + "77" => "Seine-et-Marne", + "78" => "Yvelines", + "79" => "Deux-Sèvres", + "80" => "Somme", + "81" => "Tarn", + "82" => "Tarn-et-Garonne", + "83" => "Var", + "84" => "Vaucluse", + "85" => "Vendée", + "86" => "Vienne", + "87" => "Haute-Vienne", + "88" => "Vosges", + "89" => "Yonne", + "90" => "Territoire de Belfort", + "91" => "Essonne", + "92" => "Hauts-de-Seine", + "93" => "Seine-Saint-Denis", + "94" => "Val-de-Marne", + "95" => "Val-d'Oise", + "971" => "Guadeloupe", + "972" => "Martinique", + "973" => "Guyane (française)", + "974" => "La Réunion", + "976" => "Mayotte", + "ARA" => "Auvergne-Rhône-Alpes", + "BFC" => "Bourgogne-Franche-Comté", + "BL" => "Saint-Barthélemy", + "BRE" => "Bretagne", + "CP" => "Clipperton", + "CVL" => "Centre-Val de Loire", + "GES" => "Grand-Est", + "GF" => "Guyane (française)", + "GP" => "Guadeloupe", + "HDF" => "Hauts-de-France", + "IDF" => "Île-de-France", + "MF" => "Saint-Martin", + "MQ" => "Martinique", + "NAQ" => "Nouvelle-Aquitaine", + "NC" => "Nouvelle-Calédonie", + "NOR" => "Normandie", + "OCC" => "Occitanie", + "PAC" => "Provence-Alpes-Côte-d’Azur", + "PDL" => "Pays-de-la-Loire", + "PF" => "Polynésie française", + "PM" => "Saint-Pierre-et-Miquelon", + "RE" => "La Réunion", + "TF" => "Terres australes françaises", + "WF" => "Wallis-et-Futuna", + "YT" => "Mayotte" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GA.php b/vendor/workerman/validation/data/iso_3166-2/GA.php new file mode 100644 index 0000000..db88eb0 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GA.php @@ -0,0 +1,15 @@ + "Gabon", + "subdivisions" => [ + "1" => "Estuaire", + "2" => "Haut-Ogooué", + "3" => "Moyen-Ogooué", + "4" => "Ngounié", + "5" => "Nyanga", + "6" => "Ogooué-Ivindo", + "7" => "Ogooué-Lolo", + "8" => "Ogooué-Maritime", + "9" => "Woleu-Ntem" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GB.php b/vendor/workerman/validation/data/iso_3166-2/GB.php new file mode 100644 index 0000000..269753e --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GB.php @@ -0,0 +1,226 @@ + "United Kingdom", + "subdivisions" => [ + "ABC" => "Armagh City, Banbridge and Craigavon", + "ABD" => "Aberdeenshire", + "ABE" => "Aberdeen City", + "AGB" => "Argyll and Bute", + "AGY" => "Isle of Anglesey [Sir Ynys Môn GB-YNM]", + "AND" => "Ards and North Down", + "ANN" => "Antrim and Newtownabbey", + "ANS" => "Angus", + "BAS" => "Bath and North East Somerset", + "BBD" => "Blackburn with Darwen", + "BCP" => "Bournemouth, Christchurch and Poole", + "BDF" => "Bedford", + "BDG" => "Barking and Dagenham", + "BEN" => "Brent", + "BEX" => "Bexley", + "BFS" => "Belfast City", + "BGE" => "Bridgend [Pen-y-bont ar Ogwr GB-POG]", + "BGW" => "Blaenau Gwent", + "BIR" => "Birmingham", + "BKM" => "Buckinghamshire", + "BNE" => "Barnet", + "BNH" => "Brighton and Hove", + "BNS" => "Barnsley", + "BOL" => "Bolton", + "BPL" => "Blackpool", + "BRC" => "Bracknell Forest", + "BRD" => "Bradford", + "BRY" => "Bromley", + "BST" => "Bristol, City of", + "BUR" => "Bury", + "CAM" => "Cambridgeshire", + "CAY" => "Caerphilly [Caerffili GB-CAF]", + "CBF" => "Central Bedfordshire", + "CCG" => "Causeway Coast and Glens", + "CGN" => "Ceredigion [Sir Ceredigion]", + "CHE" => "Cheshire East", + "CHW" => "Cheshire West and Chester", + "CLD" => "Calderdale", + "CLK" => "Clackmannanshire", + "CMA" => "Cumbria", + "CMD" => "Camden", + "CMN" => "Carmarthenshire [Sir Gaerfyrddin GB-GFY]", + "CON" => "Cornwall", + "COV" => "Coventry", + "CRF" => "Cardiff [Caerdydd GB-CRD]", + "CRY" => "Croydon", + "CWY" => "Conwy", + "DAL" => "Darlington", + "DBY" => "Derbyshire", + "DEN" => "Denbighshire [Sir Ddinbych GB-DDB]", + "DER" => "Derby", + "DEV" => "Devon", + "DGY" => "Dumfries and Galloway", + "DNC" => "Doncaster", + "DND" => "Dundee City", + "DOR" => "Dorset", + "DRS" => "Derry and Strabane", + "DUD" => "Dudley", + "DUR" => "Durham, County", + "EAL" => "Ealing", + "EAY" => "East Ayrshire", + "EDH" => "Edinburgh, City of", + "EDU" => "East Dunbartonshire", + "ELN" => "East Lothian", + "ELS" => "Eilean Siar", + "ENF" => "Enfield", + "ENG" => "England", + "ERW" => "East Renfrewshire", + "ERY" => "East Riding of Yorkshire", + "ESS" => "Essex", + "ESX" => "East Sussex", + "FAL" => "Falkirk", + "FIF" => "Fife", + "FLN" => "Flintshire [Sir y Fflint GB-FFL]", + "FMO" => "Fermanagh and Omagh", + "GAT" => "Gateshead", + "GLG" => "Glasgow City", + "GLS" => "Gloucestershire", + "GRE" => "Greenwich", + "GWN" => "Gwynedd", + "HAL" => "Halton", + "HAM" => "Hampshire", + "HAV" => "Havering", + "HCK" => "Hackney", + "HEF" => "Herefordshire", + "HIL" => "Hillingdon", + "HLD" => "Highland", + "HMF" => "Hammersmith and Fulham", + "HNS" => "Hounslow", + "HPL" => "Hartlepool", + "HRT" => "Hertfordshire", + "HRW" => "Harrow", + "HRY" => "Haringey", + "IOS" => "Isles of Scilly", + "IOW" => "Isle of Wight", + "ISL" => "Islington", + "IVC" => "Inverclyde", + "KEC" => "Kensington and Chelsea", + "KEN" => "Kent", + "KHL" => "Kingston upon Hull", + "KIR" => "Kirklees", + "KTT" => "Kingston upon Thames", + "KWL" => "Knowsley", + "LAN" => "Lancashire", + "LBC" => "Lisburn and Castlereagh", + "LBH" => "Lambeth", + "LCE" => "Leicester", + "LDS" => "Leeds", + "LEC" => "Leicestershire", + "LEW" => "Lewisham", + "LIN" => "Lincolnshire", + "LIV" => "Liverpool", + "LND" => "London, City of", + "LUT" => "Luton", + "MAN" => "Manchester", + "MDB" => "Middlesbrough", + "MDW" => "Medway", + "MEA" => "Mid and East Antrim", + "MIK" => "Milton Keynes", + "MLN" => "Midlothian", + "MON" => "Monmouthshire [Sir Fynwy GB-FYN]", + "MRT" => "Merton", + "MRY" => "Moray", + "MTY" => "Merthyr Tydfil [Merthyr Tudful GB-MTU]", + "MUL" => "Mid-Ulster", + "NAY" => "North Ayrshire", + "NBL" => "Northumberland", + "NEL" => "North East Lincolnshire", + "NET" => "Newcastle upon Tyne", + "NFK" => "Norfolk", + "NGM" => "Nottingham", + "NIR" => "Northern Ireland", + "NLK" => "North Lanarkshire", + "NLN" => "North Lincolnshire", + "NMD" => "Newry, Mourne and Down", + "NSM" => "North Somerset", + "NTH" => "Northamptonshire", + "NTL" => "Neath Port Talbot [Castell-nedd Port Talbot GB-CTL]", + "NTT" => "Nottinghamshire", + "NTY" => "North Tyneside", + "NWM" => "Newham", + "NWP" => "Newport [Casnewydd GB-CNW]", + "NYK" => "North Yorkshire", + "OLD" => "Oldham", + "ORK" => "Orkney Islands", + "OXF" => "Oxfordshire", + "PEM" => "Pembrokeshire [Sir Benfro GB-BNF]", + "PKN" => "Perth and Kinross", + "PLY" => "Plymouth", + "POR" => "Portsmouth", + "POW" => "Powys", + "PTE" => "Peterborough", + "RCC" => "Redcar and Cleveland", + "RCH" => "Rochdale", + "RCT" => "Rhondda Cynon Taff [Rhondda CynonTaf]", + "RDB" => "Redbridge", + "RDG" => "Reading", + "RFW" => "Renfrewshire", + "RIC" => "Richmond upon Thames", + "ROT" => "Rotherham", + "RUT" => "Rutland", + "SAW" => "Sandwell", + "SAY" => "South Ayrshire", + "SCB" => "Scottish Borders", + "SCT" => "Scotland", + "SFK" => "Suffolk", + "SFT" => "Sefton", + "SGC" => "South Gloucestershire", + "SHF" => "Sheffield", + "SHN" => "St. Helens", + "SHR" => "Shropshire", + "SKP" => "Stockport", + "SLF" => "Salford", + "SLG" => "Slough", + "SLK" => "South Lanarkshire", + "SND" => "Sunderland", + "SOL" => "Solihull", + "SOM" => "Somerset", + "SOS" => "Southend-on-Sea", + "SRY" => "Surrey", + "STE" => "Stoke-on-Trent", + "STG" => "Stirling", + "STH" => "Southampton", + "STN" => "Sutton", + "STS" => "Staffordshire", + "STT" => "Stockton-on-Tees", + "STY" => "South Tyneside", + "SWA" => "Swansea [Abertawe GB-ATA]", + "SWD" => "Swindon", + "SWK" => "Southwark", + "TAM" => "Tameside", + "TFW" => "Telford and Wrekin", + "THR" => "Thurrock", + "TOB" => "Torbay", + "TOF" => "Torfaen [Tor-faen]", + "TRF" => "Trafford", + "TWH" => "Tower Hamlets", + "VGL" => "Vale of Glamorgan, The [Bro Morgannwg GB-BMG]", + "WAR" => "Warwickshire", + "WBK" => "West Berkshire", + "WDU" => "West Dunbartonshire", + "WFT" => "Waltham Forest", + "WGN" => "Wigan", + "WIL" => "Wiltshire", + "WKF" => "Wakefield", + "WLL" => "Walsall", + "WLN" => "West Lothian", + "WLS" => "Wales [Cymru GB-CYM]", + "WLV" => "Wolverhampton", + "WND" => "Wandsworth", + "WNM" => "Windsor and Maidenhead", + "WOK" => "Wokingham", + "WOR" => "Worcestershire", + "WRL" => "Wirral", + "WRT" => "Warrington", + "WRX" => "Wrexham [Wrecsam GB-WRC]", + "WSM" => "Westminster", + "WSX" => "West Sussex", + "YOR" => "York", + "ZET" => "Shetland Islands" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GD.php b/vendor/workerman/validation/data/iso_3166-2/GD.php new file mode 100644 index 0000000..a9adc56 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GD.php @@ -0,0 +1,13 @@ + "Grenada", + "subdivisions" => [ + "01" => "Saint Andrew", + "02" => "Saint David", + "03" => "Saint George", + "04" => "Saint John", + "05" => "Saint Mark", + "06" => "Saint Patrick", + "10" => "Southern Grenadine Islands" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GE.php b/vendor/workerman/validation/data/iso_3166-2/GE.php new file mode 100644 index 0000000..13a2c56 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GE.php @@ -0,0 +1,18 @@ + "Georgia", + "subdivisions" => [ + "AB" => "Abkhazia", + "AJ" => "Ajaria", + "GU" => "Guria", + "IM" => "Imereti", + "KA" => "K'akheti", + "KK" => "Kvemo Kartli", + "MM" => "Mtskheta-Mtianeti", + "RL" => "Rach'a-Lechkhumi-Kvemo Svaneti", + "SJ" => "Samtskhe-Javakheti", + "SK" => "Shida Kartli", + "SZ" => "Samegrelo-Zemo Svaneti", + "TB" => "Tbilisi" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GF.php b/vendor/workerman/validation/data/iso_3166-2/GF.php new file mode 100644 index 0000000..0c1ca47 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GF.php @@ -0,0 +1,6 @@ + "French Guiana", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GG.php b/vendor/workerman/validation/data/iso_3166-2/GG.php new file mode 100644 index 0000000..7256a55 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GG.php @@ -0,0 +1,6 @@ + "Guernsey", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GH.php b/vendor/workerman/validation/data/iso_3166-2/GH.php new file mode 100644 index 0000000..ed4c140 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GH.php @@ -0,0 +1,22 @@ + "Ghana", + "subdivisions" => [ + "AA" => "Greater Accra", + "AF" => "Ahafo", + "AH" => "Ashanti", + "BE" => "Bono East", + "BO" => "Bono", + "CP" => "Central", + "EP" => "Eastern", + "NE" => "North East", + "NP" => "Northern", + "OT" => "Oti", + "SV" => "Savannah", + "TV" => "Volta", + "UE" => "Upper East", + "UW" => "Upper West", + "WN" => "Western North", + "WP" => "Western" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GI.php b/vendor/workerman/validation/data/iso_3166-2/GI.php new file mode 100644 index 0000000..420d142 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GI.php @@ -0,0 +1,6 @@ + "Gibraltar", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GL.php b/vendor/workerman/validation/data/iso_3166-2/GL.php new file mode 100644 index 0000000..d5f1226 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GL.php @@ -0,0 +1,11 @@ + "Greenland", + "subdivisions" => [ + "AV" => "Avannaata Kommunia", + "KU" => "Kommune Kujalleq", + "QE" => "Qeqqata Kommunia", + "QT" => "Kommune Qeqertalik", + "SM" => "Kommuneqarfik Sermersooq" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GM.php b/vendor/workerman/validation/data/iso_3166-2/GM.php new file mode 100644 index 0000000..00ca257 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GM.php @@ -0,0 +1,12 @@ + "Gambia", + "subdivisions" => [ + "B" => "Banjul", + "L" => "Lower River", + "M" => "Central River", + "N" => "North Bank", + "U" => "Upper River", + "W" => "Western" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GN.php b/vendor/workerman/validation/data/iso_3166-2/GN.php new file mode 100644 index 0000000..8c8756b --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GN.php @@ -0,0 +1,47 @@ + "Guinea", + "subdivisions" => [ + "B" => "Boké", + "BE" => "Beyla", + "BF" => "Boffa", + "BK" => "Boké", + "C" => "Conakry", + "CO" => "Coyah", + "D" => "Kindia", + "DB" => "Dabola", + "DI" => "Dinguiraye", + "DL" => "Dalaba", + "DU" => "Dubréka", + "F" => "Faranah", + "FA" => "Faranah", + "FO" => "Forécariah", + "FR" => "Fria", + "GA" => "Gaoual", + "GU" => "Guékédou", + "K" => "Kankan", + "KA" => "Kankan", + "KB" => "Koubia", + "KD" => "Kindia", + "KE" => "Kérouané", + "KN" => "Koundara", + "KO" => "Kouroussa", + "KS" => "Kissidougou", + "L" => "Labé", + "LA" => "Labé", + "LE" => "Lélouma", + "LO" => "Lola", + "M" => "Mamou", + "MC" => "Macenta", + "MD" => "Mandiana", + "ML" => "Mali", + "MM" => "Mamou", + "N" => "Nzérékoré", + "NZ" => "Nzérékoré", + "PI" => "Pita", + "SI" => "Siguiri", + "TE" => "Télimélé", + "TO" => "Tougué", + "YO" => "Yomou" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GP.php b/vendor/workerman/validation/data/iso_3166-2/GP.php new file mode 100644 index 0000000..b4dc81b --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GP.php @@ -0,0 +1,6 @@ + "Guadeloupe", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GQ.php b/vendor/workerman/validation/data/iso_3166-2/GQ.php new file mode 100644 index 0000000..a2020a0 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GQ.php @@ -0,0 +1,16 @@ + "Equatorial Guinea", + "subdivisions" => [ + "AN" => "Annobon", + "BN" => "Bioko Nord", + "BS" => "Bioko Sud", + "C" => "Região Continental", + "CS" => "Centro Sud", + "DJ" => "Djibloho", + "I" => "Região Insular", + "KN" => "Kié-Ntem", + "LI" => "Litoral", + "WN" => "Wele-Nzas" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GR.php b/vendor/workerman/validation/data/iso_3166-2/GR.php new file mode 100644 index 0000000..5047a6b --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GR.php @@ -0,0 +1,20 @@ + "Greece", + "subdivisions" => [ + "69" => "Ágion Óros", + "A" => "Anatolikí Makedonía kai Thráki", + "B" => "Kentrikí Makedonía", + "C" => "Dytikí Makedonía", + "D" => "Ípeiros", + "E" => "Thessalía", + "F" => "Ionía Nísia", + "G" => "Dytikí Elláda", + "H" => "Stereá Elláda", + "I" => "Attikí", + "J" => "Pelopónnisos", + "K" => "Vóreio Aigaío", + "L" => "Nótio Aigaío", + "M" => "Kríti" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GS.php b/vendor/workerman/validation/data/iso_3166-2/GS.php new file mode 100644 index 0000000..39e0c35 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GS.php @@ -0,0 +1,6 @@ + "South Georgia and the South Sandwich Islands", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GT.php b/vendor/workerman/validation/data/iso_3166-2/GT.php new file mode 100644 index 0000000..deab276 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GT.php @@ -0,0 +1,28 @@ + "Guatemala", + "subdivisions" => [ + "AV" => "Alta Verapaz", + "BV" => "Baja Verapaz", + "CM" => "Chimaltenango", + "CQ" => "Chiquimula", + "ES" => "Escuintla", + "GU" => "Guatemala", + "HU" => "Huehuetenango", + "IZ" => "Izabal", + "JA" => "Jalapa", + "JU" => "Jutiapa", + "PE" => "Petén", + "PR" => "El Progreso", + "QC" => "Quiché", + "QZ" => "Quetzaltenango", + "RE" => "Retalhuleu", + "SA" => "Sacatepéquez", + "SM" => "San Marcos", + "SO" => "Sololá", + "SR" => "Santa Rosa", + "SU" => "Suchitepéquez", + "TO" => "Totonicapán", + "ZA" => "Zacapa" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GU.php b/vendor/workerman/validation/data/iso_3166-2/GU.php new file mode 100644 index 0000000..f876507 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GU.php @@ -0,0 +1,6 @@ + "Guam", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GW.php b/vendor/workerman/validation/data/iso_3166-2/GW.php new file mode 100644 index 0000000..52cd368 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GW.php @@ -0,0 +1,18 @@ + "Guinea-Bissau", + "subdivisions" => [ + "BA" => "Bafatá", + "BL" => "Bolama / Bijagós", + "BM" => "Biombo", + "BS" => "Bissau", + "CA" => "Cacheu", + "GA" => "Gabú", + "L" => "Leste", + "N" => "Norte", + "OI" => "Oio", + "QU" => "Quinara", + "S" => "Sul", + "TO" => "Tombali" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/GY.php b/vendor/workerman/validation/data/iso_3166-2/GY.php new file mode 100644 index 0000000..92de694 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/GY.php @@ -0,0 +1,16 @@ + "Guyana", + "subdivisions" => [ + "BA" => "Barima-Waini", + "CU" => "Cuyuni-Mazaruni", + "DE" => "Demerara-Mahaica", + "EB" => "East Berbice-Corentyne", + "ES" => "Essequibo Islands-West Demerara", + "MA" => "Mahaica-Berbice", + "PM" => "Pomeroon-Supenaam", + "PT" => "Potaro-Siparuni", + "UD" => "Upper Demerara-Berbice", + "UT" => "Upper Takutu-Upper Essequibo" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/HK.php b/vendor/workerman/validation/data/iso_3166-2/HK.php new file mode 100644 index 0000000..006f197 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/HK.php @@ -0,0 +1,6 @@ + "Hong Kong", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/HM.php b/vendor/workerman/validation/data/iso_3166-2/HM.php new file mode 100644 index 0000000..53a55d8 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/HM.php @@ -0,0 +1,6 @@ + "Heard Island and McDonald Islands", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/HN.php b/vendor/workerman/validation/data/iso_3166-2/HN.php new file mode 100644 index 0000000..26eb361 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/HN.php @@ -0,0 +1,24 @@ + "Honduras", + "subdivisions" => [ + "AT" => "Atlántida", + "CH" => "Choluteca", + "CL" => "Colón", + "CM" => "Comayagua", + "CP" => "Copán", + "CR" => "Cortés", + "EP" => "El Paraíso", + "FM" => "Francisco Morazán", + "GD" => "Gracias a Dios", + "IB" => "Islas de la Bahía", + "IN" => "Intibucá", + "LE" => "Lempira", + "LP" => "La Paz", + "OC" => "Ocotepeque", + "OL" => "Olancho", + "SB" => "Santa Bárbara", + "VA" => "Valle", + "YO" => "Yoro" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/HR.php b/vendor/workerman/validation/data/iso_3166-2/HR.php new file mode 100644 index 0000000..67122d1 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/HR.php @@ -0,0 +1,27 @@ + "Croatia", + "subdivisions" => [ + "01" => "Zagrebačka županija", + "02" => "Krapinsko-zagorska županija", + "03" => "Sisačko-moslavačka županija", + "04" => "Karlovačka županija", + "05" => "Varaždinska županija", + "06" => "Koprivničko-križevačka županija", + "07" => "Bjelovarsko-bilogorska županija", + "08" => "Primorsko-goranska županija", + "09" => "Ličko-senjska županija", + "10" => "Virovitičko-podravska županija", + "11" => "Požeško-slavonska županija", + "12" => "Brodsko-posavska županija", + "13" => "Zadarska županija", + "14" => "Osječko-baranjska županija", + "15" => "Šibensko-kninska županija", + "16" => "Vukovarsko-srijemska županija", + "17" => "Splitsko-dalmatinska županija", + "18" => "Istarska županija", + "19" => "Dubrovačko-neretvanska županija", + "20" => "Međimurska županija", + "21" => "Grad Zagreb" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/HT.php b/vendor/workerman/validation/data/iso_3166-2/HT.php new file mode 100644 index 0000000..5b5750d --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/HT.php @@ -0,0 +1,16 @@ + "Haiti", + "subdivisions" => [ + "AR" => "Artibonite", + "CE" => "Centre", + "GA" => "Grandans", + "ND" => "Nord", + "NE" => "Nord-Est", + "NI" => "Nip", + "NO" => "Nord-Ouest", + "OU" => "Lwès", + "SD" => "Sid", + "SE" => "Sidès" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/HU.php b/vendor/workerman/validation/data/iso_3166-2/HU.php new file mode 100644 index 0000000..356df38 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/HU.php @@ -0,0 +1,49 @@ + "Hungary", + "subdivisions" => [ + "BA" => "Baranya", + "BC" => "Békéscsaba", + "BE" => "Békés", + "BK" => "Bács-Kiskun", + "BU" => "Budapest", + "BZ" => "Borsod-Abaúj-Zemplén", + "CS" => "Csongrád", + "DE" => "Debrecen", + "DU" => "Dunaújváros", + "EG" => "Eger", + "ER" => "Érd", + "FE" => "Fejér", + "GS" => "Győr-Moson-Sopron", + "GY" => "Győr", + "HB" => "Hajdú-Bihar", + "HE" => "Heves", + "HV" => "Hódmezővásárhely", + "JN" => "Jász-Nagykun-Szolnok", + "KE" => "Komárom-Esztergom", + "KM" => "Kecskemét", + "KV" => "Kaposvár", + "MI" => "Miskolc", + "NK" => "Nagykanizsa", + "NO" => "Nógrád", + "NY" => "Nyíregyháza", + "PE" => "Pest", + "PS" => "Pécs", + "SD" => "Szeged", + "SF" => "Székesfehérvár", + "SH" => "Szombathely", + "SK" => "Szolnok", + "SN" => "Sopron", + "SO" => "Somogy", + "SS" => "Szekszárd", + "ST" => "Salgótarján", + "SZ" => "Szabolcs-Szatmár-Bereg", + "TB" => "Tatabánya", + "TO" => "Tolna", + "VA" => "Vas", + "VE" => "Veszprém", + "VM" => "Veszprém", + "ZA" => "Zala", + "ZE" => "Zalaegerszeg" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/ID.php b/vendor/workerman/validation/data/iso_3166-2/ID.php new file mode 100644 index 0000000..15a057c --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/ID.php @@ -0,0 +1,47 @@ + "Indonesia", + "subdivisions" => [ + "AC" => "Aceh", + "BA" => "Bali", + "BB" => "Kepulauan Bangka Belitung", + "BE" => "Bengkulu", + "BT" => "Banten", + "GO" => "Gorontalo", + "JA" => "Jambi", + "JB" => "Jawa Barat", + "JI" => "Jawa Timur", + "JK" => "Jakarta Raya", + "JT" => "Jawa Tengah", + "JW" => "Jawa", + "KA" => "Kalimantan", + "KB" => "Kalimantan Barat", + "KI" => "Kalimantan Timur", + "KR" => "Kepulauan Riau", + "KS" => "Kalimantan Selatan", + "KT" => "Kalimantan Tengah", + "KU" => "Kalimantan Utara", + "LA" => "Lampung", + "MA" => "Maluku", + "ML" => "Maluku", + "MU" => "Maluku Utara", + "NB" => "Nusa Tenggara Barat", + "NT" => "Nusa Tenggara Timur", + "NU" => "Nusa Tenggara", + "PA" => "Papua", + "PB" => "Papua Barat", + "PP" => "Papua", + "RI" => "Riau", + "SA" => "Sulawesi Utara", + "SB" => "Sumatera Barat", + "SG" => "Sulawesi Tenggara", + "SL" => "Sulawesi", + "SM" => "Sumatera", + "SN" => "Sulawesi Selatan", + "SR" => "Sulawesi Barat", + "SS" => "Sumatera Selatan", + "ST" => "Sulawesi Tengah", + "SU" => "Sumatera Utara", + "YO" => "Yogyakarta" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/IE.php b/vendor/workerman/validation/data/iso_3166-2/IE.php new file mode 100644 index 0000000..097ee1f --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/IE.php @@ -0,0 +1,36 @@ + "Ireland", + "subdivisions" => [ + "C" => "Connaught", + "CE" => "Clare", + "CN" => "Cavan", + "CO" => "Cork", + "CW" => "Carlow", + "D" => "Dublin", + "DL" => "Donegal", + "G" => "Galway", + "KE" => "Kildare", + "KK" => "Kilkenny", + "KY" => "Kerry", + "L" => "Leinster", + "LD" => "Longford", + "LH" => "Louth", + "LK" => "Limerick", + "LM" => "Leitrim", + "LS" => "Laois", + "M" => "Munster", + "MH" => "Meath", + "MN" => "Monaghan", + "MO" => "Mayo", + "OY" => "Offaly", + "RN" => "Roscommon", + "SO" => "Sligo", + "TA" => "Tipperary", + "U" => "Ulster", + "WD" => "Waterford", + "WH" => "Westmeath", + "WW" => "Wicklow", + "WX" => "Wexford" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/IL.php b/vendor/workerman/validation/data/iso_3166-2/IL.php new file mode 100644 index 0000000..a242ef3 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/IL.php @@ -0,0 +1,12 @@ + "Israel", + "subdivisions" => [ + "D" => "Al Janūbī", + "HA" => "H̱efa", + "JM" => "Al Quds", + "M" => "Al Awsaţ", + "TA" => "Tall Abīb", + "Z" => "Ash Shamālī" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/IM.php b/vendor/workerman/validation/data/iso_3166-2/IM.php new file mode 100644 index 0000000..4697912 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/IM.php @@ -0,0 +1,6 @@ + "Isle of Man", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/IN.php b/vendor/workerman/validation/data/iso_3166-2/IN.php new file mode 100644 index 0000000..b4c6b90 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/IN.php @@ -0,0 +1,42 @@ + "India", + "subdivisions" => [ + "AN" => "Andaman and Nicobar Islands", + "AP" => "Andhra Pradesh", + "AR" => "Arunāchal Pradesh", + "AS" => "Assam", + "BR" => "Bihār", + "CH" => "Chandīgarh", + "CT" => "Chhattīsgarh", + "DH" => "Dādra and Nagar Haveli and Damān and Diu", + "DL" => "Delhi", + "GA" => "Goa", + "GJ" => "Gujarāt", + "HP" => "Himāchal Pradesh", + "HR" => "Haryāna", + "JH" => "Jhārkhand", + "JK" => "Jammu and Kashmīr", + "KA" => "Karnātaka", + "KL" => "Kerala", + "LA" => "Ladākh", + "LD" => "Lakshadweep", + "MH" => "Mahārāshtra", + "ML" => "Meghālaya", + "MN" => "Manipur", + "MP" => "Madhya Pradesh", + "MZ" => "Mizoram", + "NL" => "Nāgāland", + "OR" => "Odisha", + "PB" => "Punjab", + "PY" => "Puducherry", + "RJ" => "Rājasthān", + "SK" => "Sikkim", + "TG" => "Telangāna", + "TN" => "Tamil Nādu", + "TR" => "Tripura", + "UP" => "Uttar Pradesh", + "UT" => "Uttarākhand", + "WB" => "West Bengal" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/IO.php b/vendor/workerman/validation/data/iso_3166-2/IO.php new file mode 100644 index 0000000..e453119 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/IO.php @@ -0,0 +1,6 @@ + "British Indian Ocean Territory", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/IQ.php b/vendor/workerman/validation/data/iso_3166-2/IQ.php new file mode 100644 index 0000000..c7bf486 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/IQ.php @@ -0,0 +1,24 @@ + "Iraq", + "subdivisions" => [ + "AN" => "Al Anbār", + "AR" => "Arbīl", + "BA" => "Al Başrah", + "BB" => "Bābil", + "BG" => "Baghdād", + "DA" => "Dahūk", + "DI" => "Diyālá", + "DQ" => "Dhī Qār", + "KA" => "Karbalā’", + "KI" => "Kirkūk", + "MA" => "Maysān", + "MU" => "Al Muthanná", + "NA" => "An Najaf", + "NI" => "Nīnawá", + "QA" => "Al Qādisīyah", + "SD" => "Şalāḩ ad Dīn", + "SU" => "As Sulaymānīyah", + "WA" => "Wāsiţ" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/IR.php b/vendor/workerman/validation/data/iso_3166-2/IR.php new file mode 100644 index 0000000..ec394e0 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/IR.php @@ -0,0 +1,37 @@ + "Iran, Islamic Republic of", + "subdivisions" => [ + "00" => "Markazī", + "01" => "Gīlān", + "02" => "Māzandarān", + "03" => "Āz̄ārbāyjān-e Shārqī", + "04" => "Āz̄ārbāyjān-e Ghārbī", + "05" => "Kermānshāh", + "06" => "Khūzestān", + "07" => "Fārs", + "08" => "Kermān", + "09" => "Khorāsān-e Raẕavī", + "10" => "Eşfahān", + "11" => "Sīstān va Balūchestān", + "12" => "Kordestān", + "13" => "Hamadān", + "14" => "Chahār Maḩāl va Bakhtīārī", + "15" => "Lorestān", + "16" => "Īlām", + "17" => "Kohgīlūyeh va Bowyer Aḩmad", + "18" => "Būshehr", + "19" => "Zanjān", + "20" => "Semnān", + "21" => "Yazd", + "22" => "Hormozgān", + "23" => "Tehrān", + "24" => "Ardabīl", + "25" => "Qom", + "26" => "Qazvīn", + "27" => "Golestān", + "28" => "Khorāsān-e Shomālī", + "29" => "Khorāsān-e Jonūbī", + "30" => "Alborz" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/IS.php b/vendor/workerman/validation/data/iso_3166-2/IS.php new file mode 100644 index 0000000..0cbceab --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/IS.php @@ -0,0 +1,86 @@ + "Iceland", + "subdivisions" => [ + "1" => "Höfuðborgarsvæði", + "2" => "Suðurnes", + "3" => "Vesturland", + "4" => "Vestfirðir", + "5" => "Norðurland vestra", + "6" => "Norðurland eystra", + "7" => "Austurland", + "8" => "Suðurland", + "AKH" => "Akrahreppur", + "AKN" => "Akraneskaupstaður", + "AKU" => "Akureyrarbær", + "ARN" => "Árneshreppur", + "ASA" => "Ásahreppur", + "BFJ" => "Borgarfjarðarhreppur", + "BLA" => "Bláskógabyggð", + "BLO" => "Blönduósbær", + "BOG" => "Borgarbyggð", + "BOL" => "Bolungarvíkurkaupstaður", + "DAB" => "Dalabyggð", + "DAV" => "Dalvíkurbyggð", + "DJU" => "Djúpavogshreppur", + "EOM" => "Eyja- og Miklaholtshreppur", + "EYF" => "Eyjafjarðarsveit", + "FJD" => "Fjarðabyggð", + "FJL" => "Fjallabyggð", + "FLA" => "Flóahreppur", + "FLD" => "Fljótsdalshérað", + "FLR" => "Fljótsdalshreppur", + "GAR" => "Garðabær", + "GOG" => "Grímsnes- og Grafningshreppur", + "GRN" => "Grindavíkurbær", + "GRU" => "Grundarfjarðarbær", + "GRY" => "Grýtubakkahreppur", + "HAF" => "Hafnarfjarðarkaupstaður", + "HEL" => "Helgafellssveit", + "HRG" => "Hörgársveit", + "HRU" => "Hrunamannahreppur", + "HUT" => "Húnavatnshreppur", + "HUV" => "Húnaþing vestra", + "HVA" => "Hvalfjarðarsveit", + "HVE" => "Hveragerðisbær", + "ISA" => "Ísafjarðarbær", + "KAL" => "Kaldrananeshreppur", + "KJO" => "Kjósarhreppur", + "KOP" => "Kópavogsbær", + "LAN" => "Langanesbyggð", + "MOS" => "Mosfellsbær", + "MYR" => "Mýrdalshreppur", + "NOR" => "Norðurþing", + "RGE" => "Rangárþing eystra", + "RGY" => "Rangárþing ytra", + "RHH" => "Reykhólahreppur", + "RKN" => "Reykjanesbær", + "RKV" => "Reykjavíkurborg", + "SBH" => "Svalbarðshreppur", + "SBT" => "Svalbarðsstrandarhreppur", + "SDN" => "Suðurnesjabær", + "SDV" => "Súðavíkurhreppur", + "SEL" => "Seltjarnarnesbær", + "SEY" => "Seyðisfjarðarkaupstaður", + "SFA" => "Sveitarfélagið Árborg", + "SHF" => "Sveitarfélagið Hornafjörður", + "SKF" => "Skaftárhreppur", + "SKG" => "Skagabyggð", + "SKO" => "Skorradalshreppur", + "SKU" => "Skútustaðahreppur", + "SNF" => "Snæfellsbær", + "SOG" => "Skeiða- og Gnúpverjahreppur", + "SOL" => "Sveitarfélagið Ölfus", + "SSF" => "Sveitarfélagið Skagafjörður", + "SSS" => "Sveitarfélagið Skagaströnd", + "STR" => "Strandabyggð", + "STY" => "Stykkishólmsbær", + "SVG" => "Sveitarfélagið Vogar", + "TAL" => "Tálknafjarðarhreppur", + "THG" => "Þingeyjarsveit", + "TJO" => "Tjörneshreppur", + "VEM" => "Vestmannaeyjabær", + "VER" => "Vesturbyggð", + "VOP" => "Vopnafjarðarhreppur" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/IT.php b/vendor/workerman/validation/data/iso_3166-2/IT.php new file mode 100644 index 0000000..3e48f37 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/IT.php @@ -0,0 +1,132 @@ + "Italy", + "subdivisions" => [ + "21" => "Piemonte", + "23" => "Val d'Aoste", + "25" => "Lombardia", + "32" => "Trentino-Alto Adige", + "34" => "Veneto", + "36" => "Friuli Venezia Giulia", + "42" => "Liguria", + "45" => "Emilia-Romagna", + "52" => "Toscana", + "55" => "Umbria", + "57" => "Marche", + "62" => "Lazio", + "65" => "Abruzzo", + "67" => "Molise", + "72" => "Campania", + "75" => "Puglia", + "77" => "Basilicata", + "78" => "Calabria", + "82" => "Sicilia", + "88" => "Sardegna", + "AG" => "Agrigento", + "AL" => "Alessandria", + "AN" => "Ancona", + "AP" => "Ascoli Piceno", + "AQ" => "L'Aquila", + "AR" => "Arezzo", + "AT" => "Asti", + "AV" => "Avellino", + "BA" => "Bari", + "BG" => "Bergamo", + "BI" => "Biella", + "BL" => "Belluno", + "BN" => "Benevento", + "BO" => "Bologna", + "BR" => "Brindisi", + "BS" => "Brescia", + "BT" => "Barletta-Andria-Trani", + "BZ" => "Bolzano", + "CA" => "Cagliari", + "CB" => "Campobasso", + "CE" => "Caserta", + "CH" => "Chieti", + "CL" => "Caltanissetta", + "CN" => "Cuneo", + "CO" => "Como", + "CR" => "Cremona", + "CS" => "Cosenza", + "CT" => "Catania", + "CZ" => "Catanzaro", + "EN" => "Enna", + "FC" => "Forlì-Cesena", + "FE" => "Ferrara", + "FG" => "Foggia", + "FI" => "Firenze", + "FM" => "Fermo", + "FR" => "Frosinone", + "GE" => "Genova", + "GO" => "Gorizia", + "GR" => "Grosseto", + "IM" => "Imperia", + "IS" => "Isernia", + "KR" => "Crotone", + "LC" => "Lecco", + "LE" => "Lecce", + "LI" => "Livorno", + "LO" => "Lodi", + "LT" => "Latina", + "LU" => "Lucca", + "MB" => "Monza e Brianza", + "MC" => "Macerata", + "ME" => "Messina", + "MI" => "Milano", + "MN" => "Mantova", + "MO" => "Modena", + "MS" => "Massa-Carrara", + "MT" => "Matera", + "NA" => "Napoli", + "NO" => "Novara", + "NU" => "Nuoro", + "OR" => "Oristano", + "PA" => "Palermo", + "PC" => "Piacenza", + "PD" => "Padova", + "PE" => "Pescara", + "PG" => "Perugia", + "PI" => "Pisa", + "PN" => "Pordenone", + "PO" => "Prato", + "PR" => "Parma", + "PT" => "Pistoia", + "PU" => "Pesaro e Urbino", + "PV" => "Pavia", + "PZ" => "Potenza", + "RA" => "Ravenna", + "RC" => "Reggio Calabria", + "RE" => "Reggio Emilia", + "RG" => "Ragusa", + "RI" => "Rieti", + "RM" => "Roma", + "RN" => "Rimini", + "RO" => "Rovigo", + "SA" => "Salerno", + "SI" => "Siena", + "SO" => "Sondrio", + "SP" => "La Spezia", + "SR" => "Siracusa", + "SS" => "Sassari", + "SU" => "Sud Sardegna", + "SV" => "Savona", + "TA" => "Taranto", + "TE" => "Teramo", + "TN" => "Trento", + "TO" => "Torino", + "TP" => "Trapani", + "TR" => "Terni", + "TS" => "Trieste", + "TV" => "Treviso", + "UD" => "Udine", + "VA" => "Varese", + "VB" => "Verbano-Cusio-Ossola", + "VC" => "Vercelli", + "VE" => "Venezia", + "VI" => "Vicenza", + "VR" => "Verona", + "VT" => "Viterbo", + "VV" => "Vibo Valentia" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/JE.php b/vendor/workerman/validation/data/iso_3166-2/JE.php new file mode 100644 index 0000000..b32f92e --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/JE.php @@ -0,0 +1,6 @@ + "Jersey", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/JM.php b/vendor/workerman/validation/data/iso_3166-2/JM.php new file mode 100644 index 0000000..1da6b7f --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/JM.php @@ -0,0 +1,20 @@ + "Jamaica", + "subdivisions" => [ + "01" => "Kingston", + "02" => "Saint Andrew", + "03" => "Saint Thomas", + "04" => "Portland", + "05" => "Saint Mary", + "06" => "Saint Ann", + "07" => "Trelawny", + "08" => "Saint James", + "09" => "Hanover", + "10" => "Westmoreland", + "11" => "Saint Elizabeth", + "12" => "Manchester", + "13" => "Clarendon", + "14" => "Saint Catherine" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/JO.php b/vendor/workerman/validation/data/iso_3166-2/JO.php new file mode 100644 index 0000000..0e5f151 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/JO.php @@ -0,0 +1,18 @@ + "Jordan", + "subdivisions" => [ + "AJ" => "‘Ajlūn", + "AM" => "Al ‘A̅şimah", + "AQ" => "Al ‘Aqabah", + "AT" => "Aţ Ţafīlah", + "AZ" => "Az Zarqā’", + "BA" => "Al Balqā’", + "IR" => "Irbid", + "JA" => "Jarash", + "KA" => "Al Karak", + "MA" => "Al Mafraq", + "MD" => "Mādabā", + "MN" => "Ma‘ān" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/JP.php b/vendor/workerman/validation/data/iso_3166-2/JP.php new file mode 100644 index 0000000..e79bd76 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/JP.php @@ -0,0 +1,53 @@ + "Japan", + "subdivisions" => [ + "01" => "Hokkaido", + "02" => "Aomori", + "03" => "Iwate", + "04" => "Miyagi", + "05" => "Akita", + "06" => "Yamagata", + "07" => "Fukushima", + "08" => "Ibaraki", + "09" => "Tochigi", + "10" => "Gunma", + "11" => "Saitama", + "12" => "Chiba", + "13" => "Tokyo", + "14" => "Kanagawa", + "15" => "Niigata", + "16" => "Toyama", + "17" => "Ishikawa", + "18" => "Fukui", + "19" => "Yamanashi", + "20" => "Nagano", + "21" => "Gifu", + "22" => "Shizuoka", + "23" => "Aichi", + "24" => "Mie", + "25" => "Shiga", + "26" => "Kyoto", + "27" => "Osaka", + "28" => "Hyogo", + "29" => "Nara", + "30" => "Wakayama", + "31" => "Tottori", + "32" => "Shimane", + "33" => "Okayama", + "34" => "Hiroshima", + "35" => "Yamaguchi", + "36" => "Tokushima", + "37" => "Kagawa", + "38" => "Ehime", + "39" => "Kochi", + "40" => "Fukuoka", + "41" => "Saga", + "42" => "Nagasaki", + "43" => "Kumamoto", + "44" => "Oita", + "45" => "Miyazaki", + "46" => "Kagoshima", + "47" => "Okinawa" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/KE.php b/vendor/workerman/validation/data/iso_3166-2/KE.php new file mode 100644 index 0000000..5684949 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/KE.php @@ -0,0 +1,53 @@ + "Kenya", + "subdivisions" => [ + "01" => "Baringo", + "02" => "Bomet", + "03" => "Bungoma", + "04" => "Busia", + "05" => "Elgeyo/Marakwet", + "06" => "Embu", + "07" => "Garissa", + "08" => "Homa Bay", + "09" => "Isiolo", + "10" => "Kajiado", + "11" => "Kakamega", + "12" => "Kericho", + "13" => "Kiambu", + "14" => "Kilifi", + "15" => "Kirinyaga", + "16" => "Kisii", + "17" => "Kisumu", + "18" => "Kitui", + "19" => "Kwale", + "20" => "Laikipia", + "21" => "Lamu", + "22" => "Machakos", + "23" => "Makueni", + "24" => "Mandera", + "25" => "Marsabit", + "26" => "Meru", + "27" => "Migori", + "28" => "Mombasa", + "29" => "Murang'a", + "30" => "Nairobi City", + "31" => "Nakuru", + "32" => "Nandi", + "33" => "Narok", + "34" => "Nyamira", + "35" => "Nyandarua", + "36" => "Nyeri", + "37" => "Samburu", + "38" => "Siaya", + "39" => "Taita/Taveta", + "40" => "Tana River", + "41" => "Tharaka-Nithi", + "42" => "Trans Nzoia", + "43" => "Turkana", + "44" => "Uasin Gishu", + "45" => "Vihiga", + "46" => "Wajir", + "47" => "West Pokot" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/KG.php b/vendor/workerman/validation/data/iso_3166-2/KG.php new file mode 100644 index 0000000..21b4615 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/KG.php @@ -0,0 +1,15 @@ + "Kyrgyzstan", + "subdivisions" => [ + "B" => "Batken", + "C" => "Chuyskaya oblast'", + "GB" => "Bishkek Shaary", + "GO" => "Gorod Osh", + "J" => "Dzhalal-Abadskaya oblast'", + "N" => "Naryn", + "O" => "Osh", + "T" => "Talas", + "Y" => "Issyk-Kul'skaja oblast'" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/KH.php b/vendor/workerman/validation/data/iso_3166-2/KH.php new file mode 100644 index 0000000..f7399bc --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/KH.php @@ -0,0 +1,31 @@ + "Cambodia", + "subdivisions" => [ + "1" => "Banteay Mean Choăy", + "10" => "Kracheh", + "11" => "Mondol Kiri", + "12" => "Phnom Penh", + "13" => "Preah Vihear", + "14" => "Prey Veaeng", + "15" => "Pousaat", + "16" => "Rotanak Kiri", + "17" => "Siem Reab", + "18" => "Preah Sihanouk", + "19" => "Stoĕng Trêng", + "2" => "Baat Dambang", + "20" => "Svaay Rieng", + "21" => "Taakaev", + "22" => "Otdar Mean Chey", + "23" => "Kaeb", + "24" => "Pailin", + "25" => "Tbong Khmum", + "3" => "Kampong Chaam", + "4" => "Kampong Chhnang", + "5" => "Kampong Spueu", + "6" => "Kampong Thum", + "7" => "Kampot", + "8" => "Kandaal", + "9" => "Kaoh Kong" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/KI.php b/vendor/workerman/validation/data/iso_3166-2/KI.php new file mode 100644 index 0000000..889715e --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/KI.php @@ -0,0 +1,9 @@ + "Kiribati", + "subdivisions" => [ + "G" => "Gilbert Islands", + "L" => "Line Islands", + "P" => "Phoenix Islands" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/KM.php b/vendor/workerman/validation/data/iso_3166-2/KM.php new file mode 100644 index 0000000..5d648e1 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/KM.php @@ -0,0 +1,9 @@ + "Comoros", + "subdivisions" => [ + "A" => "Andjouân", + "G" => "Andjazîdja", + "M" => "Mohéli" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/KN.php b/vendor/workerman/validation/data/iso_3166-2/KN.php new file mode 100644 index 0000000..4d6f8e4 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/KN.php @@ -0,0 +1,22 @@ + "Saint Kitts and Nevis", + "subdivisions" => [ + "01" => "Christ Church Nichola Town", + "02" => "Saint Anne Sandy Point", + "03" => "Saint George Basseterre", + "04" => "Saint George Gingerland", + "05" => "Saint James Windward", + "06" => "Saint John Capisterre", + "07" => "Saint John Figtree", + "08" => "Saint Mary Cayon", + "09" => "Saint Paul Capisterre", + "10" => "Saint Paul Charlestown", + "11" => "Saint Peter Basseterre", + "12" => "Saint Thomas Lowland", + "13" => "Saint Thomas Middle Island", + "15" => "Trinity Palmetto Point", + "K" => "Saint Kitts", + "N" => "Nevis" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/KP.php b/vendor/workerman/validation/data/iso_3166-2/KP.php new file mode 100644 index 0000000..9a179ac --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/KP.php @@ -0,0 +1,18 @@ + "Korea, Democratic People's Republic of", + "subdivisions" => [ + "01" => "P'yǒngyang", + "02" => "P'yǒngan-namdo", + "03" => "P'yǒngan-bukto", + "04" => "Chagang-do", + "05" => "Hwanghae-namdo", + "06" => "Hwanghae-bukto", + "07" => "Kangweonto", + "08" => "Hamgyǒng-namdo", + "09" => "Hamgyǒng-bukto", + "10" => "Ryanggang-do", + "13" => "Raseon", + "14" => "Nampho" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/KR.php b/vendor/workerman/validation/data/iso_3166-2/KR.php new file mode 100644 index 0000000..10c621e --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/KR.php @@ -0,0 +1,23 @@ + "Korea, Republic of", + "subdivisions" => [ + "11" => "Seoul-teukbyeolsi", + "26" => "Busan-gwangyeoksi", + "27" => "Daegu-gwangyeoksi", + "28" => "Incheon-gwangyeoksi", + "29" => "Gwangju-gwangyeoksi", + "30" => "Daejeon-gwangyeoksi", + "31" => "Ulsan-gwangyeoksi", + "41" => "Gyeonggi-do", + "42" => "Gangwon-do", + "43" => "Chungcheongbuk-do", + "44" => "Chungcheongnam-do", + "45" => "Jeollabuk-do", + "46" => "Jeollanam-do", + "47" => "Gyeongsangbuk-do", + "48" => "Gyeongsangnam-do", + "49" => "Jeju-teukbyeoljachido", + "50" => "Sejong" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/KW.php b/vendor/workerman/validation/data/iso_3166-2/KW.php new file mode 100644 index 0000000..0c37a80 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/KW.php @@ -0,0 +1,12 @@ + "Kuwait", + "subdivisions" => [ + "AH" => "Al Aḩmadī", + "FA" => "Al Farwānīyah", + "HA" => "Ḩawallī", + "JA" => "Al Jahrā’", + "KU" => "Al ‘Āşimah", + "MU" => "Mubārak al Kabīr" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/KY.php b/vendor/workerman/validation/data/iso_3166-2/KY.php new file mode 100644 index 0000000..65d14bf --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/KY.php @@ -0,0 +1,6 @@ + "Cayman Islands", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/KZ.php b/vendor/workerman/validation/data/iso_3166-2/KZ.php new file mode 100644 index 0000000..f0b64f9 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/KZ.php @@ -0,0 +1,23 @@ + "Kazakhstan", + "subdivisions" => [ + "AKM" => "Akmolinskaja oblast'", + "AKT" => "Aktjubinskaja oblast'", + "ALA" => "Almaty", + "ALM" => "Almatinskaja oblast'", + "AST" => "Nur-Sultan", + "ATY" => "Atyrauskaja oblast'", + "KAR" => "Karagandinskaja oblast'", + "KUS" => "Kostanajskaja oblast'", + "KZY" => "Kyzylordinskaja oblast'", + "MAN" => "Mangghystaū oblysy", + "PAV" => "Pavlodar oblysy", + "SEV" => "Severo-Kazahstanskaja oblast'", + "SHY" => "Shymkent", + "VOS" => "Shyghys Qazaqstan oblysy", + "YUZ" => "Turkestankaya oblast'", + "ZAP" => "Batys Qazaqstan oblysy", + "ZHA" => "Zhambyl oblysy" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/LA.php b/vendor/workerman/validation/data/iso_3166-2/LA.php new file mode 100644 index 0000000..9f59d2f --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/LA.php @@ -0,0 +1,24 @@ + "Lao People's Democratic Republic", + "subdivisions" => [ + "AT" => "Attapu", + "BK" => "Bokèo", + "BL" => "Bolikhamxai", + "CH" => "Champasak", + "HO" => "Houaphan", + "KH" => "Khammouan", + "LM" => "Louang Namtha", + "LP" => "Louangphabang", + "OU" => "Oudômxai", + "PH" => "Phôngsali", + "SL" => "Salavan", + "SV" => "Savannakhét", + "VI" => "Viangchan", + "VT" => "Viangchan", + "XA" => "Xaignabouli", + "XE" => "Xékong", + "XI" => "Xiangkhouang", + "XS" => "Xaisômboun" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/LB.php b/vendor/workerman/validation/data/iso_3166-2/LB.php new file mode 100644 index 0000000..6681d1e --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/LB.php @@ -0,0 +1,14 @@ + "Lebanon", + "subdivisions" => [ + "AK" => "Aakkâr", + "AS" => "Ash Shimāl", + "BA" => "Bayrūt", + "BH" => "Baalbek-Hermel", + "BI" => "Al Biqā‘", + "JA" => "Al Janūb", + "JL" => "Jabal Lubnān", + "NA" => "An Nabaţīyah" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/LC.php b/vendor/workerman/validation/data/iso_3166-2/LC.php new file mode 100644 index 0000000..a03206b --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/LC.php @@ -0,0 +1,16 @@ + "Saint Lucia", + "subdivisions" => [ + "01" => "Anse la Raye", + "02" => "Castries", + "03" => "Choiseul", + "05" => "Dennery", + "06" => "Gros Islet", + "07" => "Laborie", + "08" => "Micoud", + "10" => "Soufrière", + "11" => "Vieux Fort", + "12" => "Canaries" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/LI.php b/vendor/workerman/validation/data/iso_3166-2/LI.php new file mode 100644 index 0000000..4d6a137 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/LI.php @@ -0,0 +1,17 @@ + "Liechtenstein", + "subdivisions" => [ + "01" => "Balzers", + "02" => "Eschen", + "03" => "Gamprin", + "04" => "Mauren", + "05" => "Planken", + "06" => "Ruggell", + "07" => "Schaan", + "08" => "Schellenberg", + "09" => "Triesen", + "10" => "Triesenberg", + "11" => "Vaduz" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/LK.php b/vendor/workerman/validation/data/iso_3166-2/LK.php new file mode 100644 index 0000000..7da27ef --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/LK.php @@ -0,0 +1,40 @@ + "Sri Lanka", + "subdivisions" => [ + "1" => "Western Province", + "11" => "Colombo", + "12" => "Gampaha", + "13" => "Kalutara", + "2" => "Central Province", + "21" => "Kandy", + "22" => "Matale", + "23" => "Nuwara Eliya", + "3" => "Southern Province", + "31" => "Galle", + "32" => "Matara", + "33" => "Hambantota", + "4" => "Northern Province", + "41" => "Jaffna", + "42" => "Kilinochchi", + "43" => "Mannar", + "44" => "Vavuniya", + "45" => "Mullaittivu", + "5" => "Eastern Province", + "51" => "Batticaloa", + "52" => "Ampara", + "53" => "Trincomalee", + "6" => "North Western Province", + "61" => "Kurunegala", + "62" => "Puttalam", + "7" => "North Central Province", + "71" => "Anuradhapura", + "72" => "Polonnaruwa", + "8" => "Uva Province", + "81" => "Badulla", + "82" => "Monaragala", + "9" => "Sabaragamuwa Province", + "91" => "Ratnapura", + "92" => "Kegalla" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/LR.php b/vendor/workerman/validation/data/iso_3166-2/LR.php new file mode 100644 index 0000000..8ed3d51 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/LR.php @@ -0,0 +1,21 @@ + "Liberia", + "subdivisions" => [ + "BG" => "Bong", + "BM" => "Bomi", + "CM" => "Grand Cape Mount", + "GB" => "Grand Bassa", + "GG" => "Grand Gedeh", + "GK" => "Grand Kru", + "GP" => "Gbarpolu", + "LO" => "Lofa", + "MG" => "Margibi", + "MO" => "Montserrado", + "MY" => "Maryland", + "NI" => "Nimba", + "RG" => "River Gee", + "RI" => "River Cess", + "SI" => "Sinoe" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/LS.php b/vendor/workerman/validation/data/iso_3166-2/LS.php new file mode 100644 index 0000000..8d4a93c --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/LS.php @@ -0,0 +1,16 @@ + "Lesotho", + "subdivisions" => [ + "A" => "Maseru", + "B" => "Botha-Bothe", + "C" => "Leribe", + "D" => "Berea", + "E" => "Mafeteng", + "F" => "Mohale's Hoek", + "G" => "Quthing", + "H" => "Qacha's Nek", + "J" => "Mokhotlong", + "K" => "Thaba-Tseka" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/LT.php b/vendor/workerman/validation/data/iso_3166-2/LT.php new file mode 100644 index 0000000..c9ac5fd --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/LT.php @@ -0,0 +1,76 @@ + "Lithuania", + "subdivisions" => [ + "01" => "Akmenė", + "02" => "Alytaus miestas", + "03" => "Alytus", + "04" => "Anykščiai", + "05" => "Birštono", + "06" => "Biržai", + "07" => "Druskininkai", + "08" => "Elektrėnai", + "09" => "Ignalina", + "10" => "Jonava", + "11" => "Joniškis", + "12" => "Jurbarkas", + "13" => "Kaišiadorys", + "14" => "Kalvarijos", + "15" => "Kauno miestas", + "16" => "Kaunas", + "17" => "Kazlų Rūdos", + "18" => "Kėdainiai", + "19" => "Kelmė", + "20" => "Klaipėdos miestas", + "21" => "Klaipėda", + "22" => "Kretinga", + "23" => "Kupiškis", + "24" => "Lazdijai", + "25" => "Marijampolė", + "26" => "Mažeikiai", + "27" => "Molėtai", + "28" => "Neringa", + "29" => "Pagėgiai", + "30" => "Pakruojis", + "31" => "Palangos miestas", + "32" => "Panevėžio miestas", + "33" => "Panevėžys", + "34" => "Pasvalys", + "35" => "Plungė", + "36" => "Prienai", + "37" => "Radviliškis", + "38" => "Raseiniai", + "39" => "Rietavo", + "40" => "Rokiškis", + "41" => "Šakiai", + "42" => "Šalčininkai", + "43" => "Šiaulių miestas", + "44" => "Šiauliai", + "45" => "Šilalė", + "46" => "Šilutė", + "47" => "Širvintos", + "48" => "Skuodas", + "49" => "Švenčionys", + "50" => "Tauragė", + "51" => "Telšiai", + "52" => "Trakai", + "53" => "Ukmergė", + "54" => "Utena", + "55" => "Varėna", + "56" => "Vilkaviškis", + "57" => "Vilniaus miestas", + "58" => "Vilnius", + "59" => "Visaginas", + "60" => "Zarasai", + "AL" => "Alytaus apskritis", + "KL" => "Klaipėdos apskritis", + "KU" => "Kauno apskritis", + "MR" => "Marijampolės apskritis", + "PN" => "Panevėžio apskritis", + "SA" => "Šiaulių apskritis", + "TA" => "Tauragės apskritis", + "TE" => "Telšių apskritis", + "UT" => "Utenos apskritis", + "VL" => "Vilniaus apskritis" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/LU.php b/vendor/workerman/validation/data/iso_3166-2/LU.php new file mode 100644 index 0000000..068916b --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/LU.php @@ -0,0 +1,18 @@ + "Luxembourg", + "subdivisions" => [ + "CA" => "Capellen", + "CL" => "Clerf", + "DI" => "Diekirch", + "EC" => "Echternach", + "ES" => "Esch an der Alzette", + "GR" => "Grevenmacher", + "LU" => "Luxembourg", + "ME" => "Mersch", + "RD" => "Redange", + "RM" => "Remich", + "VD" => "Veianen", + "WI" => "Wiltz" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/LV.php b/vendor/workerman/validation/data/iso_3166-2/LV.php new file mode 100644 index 0000000..b62d2ba --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/LV.php @@ -0,0 +1,125 @@ + "Latvia", + "subdivisions" => [ + "001" => "Aglonas novads", + "002" => "Aizkraukles novads", + "003" => "Aizputes novads", + "004" => "Aknīstes novads", + "005" => "Alojas novads", + "006" => "Alsungas novads", + "007" => "Alūksnes novads", + "008" => "Amatas novads", + "009" => "Apes novads", + "010" => "Auces novads", + "011" => "Ādažu novads", + "012" => "Babītes novads", + "013" => "Baldones novads", + "014" => "Baltinavas novads", + "015" => "Balvu novads", + "016" => "Bauskas novads", + "017" => "Beverīnas novads", + "018" => "Brocēnu novads", + "019" => "Burtnieku novads", + "020" => "Carnikavas novads", + "021" => "Cesvaines novads", + "022" => "Cēsu novads", + "023" => "Ciblas novads", + "024" => "Dagdas novads", + "025" => "Daugavpils novads", + "026" => "Dobeles novads", + "027" => "Dundagas novads", + "028" => "Durbes novads", + "029" => "Engures novads", + "030" => "Ērgļu novads", + "031" => "Garkalnes novads", + "032" => "Grobiņas novads", + "033" => "Gulbenes novads", + "034" => "Iecavas novads", + "035" => "Ikšķiles novads", + "036" => "Ilūkstes novads", + "037" => "Inčukalna novads", + "038" => "Jaunjelgavas novads", + "039" => "Jaunpiebalgas novads", + "040" => "Jaunpils novads", + "041" => "Jelgavas novads", + "042" => "Jēkabpils novads", + "043" => "Kandavas novads", + "044" => "Kārsavas novads", + "045" => "Kocēnu novads", + "046" => "Kokneses novads", + "047" => "Krāslavas novads", + "048" => "Krimuldas novads", + "049" => "Krustpils novads", + "050" => "Kuldīgas novads", + "051" => "Ķeguma novads", + "052" => "Ķekavas novads", + "053" => "Lielvārdes novads", + "054" => "Limbažu novads", + "055" => "Līgatnes novads", + "056" => "Līvānu novads", + "057" => "Lubānas novads", + "058" => "Ludzas novads", + "059" => "Madonas novads", + "060" => "Mazsalacas novads", + "061" => "Mālpils novads", + "062" => "Mārupes novads", + "063" => "Mērsraga novads", + "064" => "Naukšēnu novads", + "065" => "Neretas novads", + "066" => "Nīcas novads", + "067" => "Ogres novads", + "068" => "Olaines novads", + "069" => "Ozolnieku novads", + "070" => "Pārgaujas novads", + "071" => "Pāvilostas novads", + "072" => "Pļaviņu novads", + "073" => "Preiļu novads", + "074" => "Priekules novads", + "075" => "Priekuļu novads", + "076" => "Raunas novads", + "077" => "Rēzeknes novads", + "078" => "Riebiņu novads", + "079" => "Rojas novads", + "080" => "Ropažu novads", + "081" => "Rucavas novads", + "082" => "Rugāju novads", + "083" => "Rundāles novads", + "084" => "Rūjienas novads", + "085" => "Salas novads", + "086" => "Salacgrīvas novads", + "087" => "Salaspils novads", + "088" => "Saldus novads", + "089" => "Saulkrastu novads", + "090" => "Sējas novads", + "091" => "Siguldas novads", + "092" => "Skrīveru novads", + "093" => "Skrundas novads", + "094" => "Smiltenes novads", + "095" => "Stopiņu novads", + "096" => "Strenču novads", + "097" => "Talsu novads", + "098" => "Tērvetes novads", + "099" => "Tukuma novads", + "100" => "Vaiņodes novads", + "101" => "Valkas novads", + "102" => "Varakļānu novads", + "103" => "Vārkavas novads", + "104" => "Vecpiebalgas novads", + "105" => "Vecumnieku novads", + "106" => "Ventspils novads", + "107" => "Viesītes novads", + "108" => "Viļakas novads", + "109" => "Viļānu novads", + "110" => "Zilupes novads", + "DGV" => "Daugavpils", + "JEL" => "Jelgava", + "JKB" => "Jēkabpils", + "JUR" => "Jūrmala", + "LPX" => "Liepāja", + "REZ" => "Rēzekne", + "RIX" => "Rīga", + "VEN" => "Ventspils", + "VMR" => "Valmiera" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/LY.php b/vendor/workerman/validation/data/iso_3166-2/LY.php new file mode 100644 index 0000000..c1b5c49 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/LY.php @@ -0,0 +1,28 @@ + "Libya", + "subdivisions" => [ + "BA" => "Banghāzī", + "BU" => "Al Buţnān", + "DR" => "Darnah", + "GT" => "Ghāt", + "JA" => "Al Jabal al Akhḑar", + "JG" => "Al Jabal al Gharbī", + "JI" => "Al Jafārah", + "JU" => "Al Jufrah", + "KF" => "Al Kufrah", + "MB" => "Al Marqab", + "MI" => "Mişrātah", + "MJ" => "Al Marj", + "MQ" => "Murzuq", + "NL" => "Nālūt", + "NQ" => "An Nuqāţ al Khams", + "SB" => "Sabhā", + "SR" => "Surt", + "TB" => "Ţarābulus", + "WA" => "Al Wāḩāt", + "WD" => "Wādī al Ḩayāt", + "WS" => "Wādī ash Shāţi’", + "ZA" => "Az Zāwiyah" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MA.php b/vendor/workerman/validation/data/iso_3166-2/MA.php new file mode 100644 index 0000000..29f65b8 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MA.php @@ -0,0 +1,93 @@ + "Morocco", + "subdivisions" => [ + "01" => "Tanger-Tétouan-Al Hoceïma", + "02" => "L'Oriental", + "03" => "Fès-Meknès", + "04" => "Rabat-Salé-Kénitra", + "05" => "Béni Mellal-Khénifra", + "06" => "Casablanca-Settat", + "07" => "Marrakech-Safi", + "08" => "Drâa-Tafilalet", + "09" => "Souss-Massa", + "10" => "Guelmim-Oued Noun (EH-partial)", + "11" => "Laâyoune-Sakia El Hamra (EH-partial)", + "12" => "Dakhla-Oued Ed-Dahab (EH)", + "AGD" => "Agadir-Ida-Ou-Tanane", + "AOU" => "Aousserd (EH)", + "ASZ" => "Assa-Zag (EH-partial)", + "AZI" => "Azilal", + "BEM" => "Béni Mellal", + "BER" => "Berkane", + "BES" => "Benslimane", + "BOD" => "Boujdour (EH)", + "BOM" => "Boulemane", + "BRR" => "Berrechid", + "CAS" => "Casablanca", + "CHE" => "Chefchaouen", + "CHI" => "Chichaoua", + "CHT" => "Chtouka-Ait Baha", + "DRI" => "Driouch", + "ERR" => "Errachidia", + "ESI" => "Essaouira", + "ESM" => "Es-Semara (EH-partial)", + "FAH" => "Fahs-Anjra", + "FES" => "Fès", + "FIG" => "Figuig", + "FQH" => "Fquih Ben Salah", + "GUE" => "Guelmim", + "GUF" => "Guercif", + "HAJ" => "El Hajeb", + "HAO" => "Al Haouz", + "HOC" => "Al Hoceïma", + "IFR" => "Ifrane", + "INE" => "Inezgane-Ait Melloul", + "JDI" => "El Jadida", + "JRA" => "Jerada", + "KEN" => "Kénitra", + "KES" => "El Kelâa des Sraghna", + "KHE" => "Khémisset", + "KHN" => "Khénifra", + "KHO" => "Khouribga", + "LAA" => "Laâyoune (EH)", + "LAR" => "Larache", + "MAR" => "Marrakech", + "MDF" => "M’diq-Fnideq", + "MED" => "Médiouna", + "MEK" => "Meknès", + "MID" => "Midelt", + "MOH" => "Mohammadia", + "MOU" => "Moulay Yacoub", + "NAD" => "Nador", + "NOU" => "Nouaceur", + "OUA" => "Ouarzazate", + "OUD" => "Oued Ed-Dahab (EH)", + "OUJ" => "Oujda-Angad", + "OUZ" => "Ouezzane", + "RAB" => "Rabat", + "REH" => "Rehamna", + "SAF" => "Safi", + "SAL" => "Salé", + "SEF" => "Sefrou", + "SET" => "Settat", + "SIB" => "Sidi Bennour", + "SIF" => "Sidi Ifni", + "SIK" => "Sidi Kacem", + "SIL" => "Sidi Slimane", + "SKH" => "Skhirate-Témara", + "TAF" => "Tarfaya (EH-partial)", + "TAI" => "Taourirt", + "TAO" => "Taounate", + "TAR" => "Taroudannt", + "TAT" => "Tata", + "TAZ" => "Taza", + "TET" => "Tétouan", + "TIN" => "Tinghir", + "TIZ" => "Tiznit", + "TNG" => "Tanger-Assilah", + "TNT" => "Tan-Tan (EH-partial)", + "YUS" => "Youssoufia", + "ZAG" => "Zagora" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MC.php b/vendor/workerman/validation/data/iso_3166-2/MC.php new file mode 100644 index 0000000..368bdc9 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MC.php @@ -0,0 +1,23 @@ + "Monaco", + "subdivisions" => [ + "CL" => "La Colle", + "CO" => "La Condamine", + "FO" => "Fontvieille", + "GA" => "La Gare", + "JE" => "Jardin Exotique", + "LA" => "Larvotto", + "MA" => "Malbousquet", + "MC" => "Monte-Carlo", + "MG" => "Moneghetti", + "MO" => "Monaco-Ville", + "MU" => "Moulins", + "PH" => "Port-Hercule", + "SD" => "Sainte-Dévote", + "SO" => "La Source", + "SP" => "Spélugues", + "SR" => "Saint-Roman", + "VR" => "Vallon de la Rousse" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MD.php b/vendor/workerman/validation/data/iso_3166-2/MD.php new file mode 100644 index 0000000..a89dd89 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MD.php @@ -0,0 +1,43 @@ + "Moldova, Republic of", + "subdivisions" => [ + "AN" => "Anenii Noi", + "BA" => "Bălți", + "BD" => "Bender [Tighina]", + "BR" => "Briceni", + "BS" => "Basarabeasca", + "CA" => "Cahul", + "CL" => "Călărași", + "CM" => "Cimișlia", + "CR" => "Criuleni", + "CS" => "Căușeni", + "CT" => "Cantemir", + "CU" => "Chișinău", + "DO" => "Dondușeni", + "DR" => "Drochia", + "DU" => "Dubăsari", + "ED" => "Edineț", + "FA" => "Fălești", + "FL" => "Florești", + "GA" => "Găgăuzia, Unitatea teritorială autonomă (UTAG)", + "GL" => "Glodeni", + "HI" => "Hîncești", + "IA" => "Ialoveni", + "LE" => "Leova", + "NI" => "Nisporeni", + "OC" => "Ocnița", + "OR" => "Orhei", + "RE" => "Rezina", + "RI" => "Rîșcani", + "SD" => "Șoldănești", + "SI" => "Sîngerei", + "SN" => "Stînga Nistrului, unitatea teritorială din", + "SO" => "Soroca", + "ST" => "Strășeni", + "SV" => "Ștefan Vodă", + "TA" => "Taraclia", + "TE" => "Telenești", + "UN" => "Ungheni" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/ME.php b/vendor/workerman/validation/data/iso_3166-2/ME.php new file mode 100644 index 0000000..ae677f6 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/ME.php @@ -0,0 +1,30 @@ + "Montenegro", + "subdivisions" => [ + "01" => "Andrijevica", + "02" => "Bar", + "03" => "Berane", + "04" => "Bijelo Polje", + "05" => "Budva", + "06" => "Cetinje", + "07" => "Danilovgrad", + "08" => "Herceg-Novi", + "09" => "Kolašin", + "10" => "Kotor", + "11" => "Mojkovac", + "12" => "Nikšić", + "13" => "Plav", + "14" => "Pljevlja", + "15" => "Plužine", + "16" => "Podgorica", + "17" => "Rožaje", + "18" => "Šavnik", + "19" => "Tivat", + "20" => "Ulcinj", + "21" => "Žabljak", + "22" => "Gusinje", + "23" => "Petnjica", + "24" => "Tuzi" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MF.php b/vendor/workerman/validation/data/iso_3166-2/MF.php new file mode 100644 index 0000000..c1f2ede --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MF.php @@ -0,0 +1,6 @@ + "Saint Martin (French part)", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MG.php b/vendor/workerman/validation/data/iso_3166-2/MG.php new file mode 100644 index 0000000..e25c1ed --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MG.php @@ -0,0 +1,12 @@ + "Madagascar", + "subdivisions" => [ + "A" => "Toamasina", + "D" => "Antsiranana", + "F" => "Fianarantsoa", + "M" => "Mahajanga", + "T" => "Antananarivo", + "U" => "Toliara" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MH.php b/vendor/workerman/validation/data/iso_3166-2/MH.php new file mode 100644 index 0000000..fe920bf --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MH.php @@ -0,0 +1,32 @@ + "Marshall Islands", + "subdivisions" => [ + "ALK" => "Ailuk", + "ALL" => "Ailinglaplap", + "ARN" => "Arno", + "AUR" => "Aur", + "EBO" => "Ebon", + "ENI" => "Enewetak & Ujelang", + "JAB" => "Jabat", + "JAL" => "Jaluit", + "KIL" => "Bikini & Kili", + "KWA" => "Kwajalein", + "L" => "Ralik chain", + "LAE" => "Lae", + "LIB" => "Lib", + "LIK" => "Likiep", + "MAJ" => "Majuro", + "MAL" => "Maloelap", + "MEJ" => "Mejit", + "MIL" => "Mili", + "NMK" => "Namdrik", + "NMU" => "Namu", + "RON" => "Rongelap", + "T" => "Ratak chain", + "UJA" => "Ujae", + "UTI" => "Utrik", + "WTH" => "Wotho", + "WTJ" => "Wotje" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MK.php b/vendor/workerman/validation/data/iso_3166-2/MK.php new file mode 100644 index 0000000..f93e22d --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MK.php @@ -0,0 +1,86 @@ + "North Macedonia", + "subdivisions" => [ + "101" => "Veles", + "102" => "Gradsko", + "103" => "Demir Kapija", + "104" => "Kavadarci", + "105" => "Lozovo", + "106" => "Negotino", + "107" => "Rosoman", + "108" => "Sveti Nikole", + "109" => "Čaška", + "201" => "Berovo", + "202" => "Vinica", + "203" => "Delčevo", + "204" => "Zrnovci", + "205" => "Karbinci", + "206" => "Kočani", + "207" => "Makedonska Kamenica", + "208" => "Pehčevo", + "209" => "Probištip", + "210" => "Češinovo-Obleševo", + "211" => "Štip", + "301" => "Vevčani", + "303" => "Debar", + "304" => "Debrca", + "307" => "Kičevo", + "308" => "Makedonski Brod", + "310" => "Ohrid", + "311" => "Plasnica", + "312" => "Struga", + "313" => "Centar Župa", + "401" => "Bogdanci", + "402" => "Bosilovo", + "403" => "Valandovo", + "404" => "Vasilevo", + "405" => "Gevgelija", + "406" => "Dojran", + "407" => "Konče", + "408" => "Novo Selo", + "409" => "Radoviš", + "410" => "Strumica", + "501" => "Bitola", + "502" => "Demir Hisar", + "503" => "Dolneni", + "504" => "Krivogaštani", + "505" => "Kruševo", + "506" => "Mogila", + "507" => "Novaci", + "508" => "Prilep", + "509" => "Resen", + "601" => "Bogovinje", + "602" => "Brvenica", + "603" => "Vrapčište", + "604" => "Gostivar", + "605" => "Želino", + "606" => "Jegunovce", + "607" => "Mavrovo i Rostuše", + "608" => "Tearce", + "609" => "Tetovo", + "701" => "Kratovo", + "702" => "Kriva Palanka", + "703" => "Kumanovo", + "704" => "Lipkovo", + "705" => "Rankovce", + "706" => "Staro Nagoričane", + "801" => "Aerodrom †", + "802" => "Aračinovo", + "803" => "Butel †", + "804" => "Gazi Baba †", + "805" => "Gjorče Petrov †", + "806" => "Zelenikovo", + "807" => "Ilinden", + "808" => "Karpoš †", + "809" => "Kisela Voda †", + "810" => "Petrovec", + "811" => "Saraj †", + "812" => "Sopište", + "813" => "Studeničani", + "814" => "Centar †", + "815" => "Čair †", + "816" => "Čučer-Sandevo", + "817" => "Šuto Orizari †" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/ML.php b/vendor/workerman/validation/data/iso_3166-2/ML.php new file mode 100644 index 0000000..0c00f75 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/ML.php @@ -0,0 +1,17 @@ + "Mali", + "subdivisions" => [ + "1" => "Kayes", + "10" => "Taoudénit", + "2" => "Koulikoro", + "3" => "Sikasso", + "4" => "Ségou", + "5" => "Mopti", + "6" => "Tombouctou", + "7" => "Gao", + "8" => "Kidal", + "9" => "Ménaka", + "BKO" => "Bamako" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MM.php b/vendor/workerman/validation/data/iso_3166-2/MM.php new file mode 100644 index 0000000..ea051a0 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MM.php @@ -0,0 +1,21 @@ + "Myanmar", + "subdivisions" => [ + "01" => "Sagaing", + "02" => "Bago", + "03" => "Magway", + "04" => "Mandalay", + "05" => "Tanintharyi", + "06" => "Yangon", + "07" => "Ayeyarwady", + "11" => "Kachin", + "12" => "Kayah", + "13" => "Kayin", + "14" => "Chin", + "15" => "Mon", + "16" => "Rakhine", + "17" => "Shan", + "18" => "Nay Pyi Taw" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MN.php b/vendor/workerman/validation/data/iso_3166-2/MN.php new file mode 100644 index 0000000..1656361 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MN.php @@ -0,0 +1,28 @@ + "Mongolia", + "subdivisions" => [ + "035" => "Orhon", + "037" => "Darhan uul", + "039" => "Hentiy", + "041" => "Hövsgöl", + "043" => "Hovd", + "046" => "Uvs", + "047" => "Töv", + "049" => "Selenge", + "051" => "Sühbaatar", + "053" => "Ömnögovĭ", + "055" => "Övörhangay", + "057" => "Dzavhan", + "059" => "Dundgovĭ", + "061" => "Dornod", + "063" => "Dornogovĭ", + "064" => "Govĭ-Sümber", + "065" => "Govĭ-Altay", + "067" => "Bulgan", + "069" => "Bayanhongor", + "071" => "Bayan-Ölgiy", + "073" => "Arhangay", + "1" => "Ulaanbaatar" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MO.php b/vendor/workerman/validation/data/iso_3166-2/MO.php new file mode 100644 index 0000000..2e33840 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MO.php @@ -0,0 +1,6 @@ + "Macao", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MP.php b/vendor/workerman/validation/data/iso_3166-2/MP.php new file mode 100644 index 0000000..41eecf2 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MP.php @@ -0,0 +1,6 @@ + "Northern Mariana Islands", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MQ.php b/vendor/workerman/validation/data/iso_3166-2/MQ.php new file mode 100644 index 0000000..21c3335 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MQ.php @@ -0,0 +1,6 @@ + "Martinique", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MR.php b/vendor/workerman/validation/data/iso_3166-2/MR.php new file mode 100644 index 0000000..8a0761b --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MR.php @@ -0,0 +1,21 @@ + "Mauritania", + "subdivisions" => [ + "01" => "Hodh ech Chargui", + "02" => "Hodh el Gharbi", + "03" => "Assaba", + "04" => "Gorgol", + "05" => "Brakna", + "06" => "Trarza", + "07" => "Adrar", + "08" => "Dakhlet Nouâdhibou", + "09" => "Tagant", + "10" => "Guidimaka", + "11" => "Tiris Zemmour", + "12" => "Inchiri", + "13" => "Nouakchott Ouest", + "14" => "Nouakchott Nord", + "15" => "Nouakchott Sud" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MS.php b/vendor/workerman/validation/data/iso_3166-2/MS.php new file mode 100644 index 0000000..245a80f --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MS.php @@ -0,0 +1,6 @@ + "Montserrat", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MT.php b/vendor/workerman/validation/data/iso_3166-2/MT.php new file mode 100644 index 0000000..bb2c2b8 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MT.php @@ -0,0 +1,74 @@ + "Malta", + "subdivisions" => [ + "01" => "Attard", + "02" => "Balzan", + "03" => "Birgu", + "04" => "Birkirkara", + "05" => "Birżebbuġa", + "06" => "Bormla", + "07" => "Dingli", + "08" => "Fgura", + "09" => "Floriana", + "10" => "Fontana", + "11" => "Gudja", + "12" => "Gżira", + "13" => "Għajnsielem", + "14" => "Għarb", + "15" => "Għargħur", + "16" => "Għasri", + "17" => "Għaxaq", + "18" => "Ħamrun", + "19" => "Iklin", + "20" => "Isla", + "21" => "Kalkara", + "22" => "Kerċem", + "23" => "Kirkop", + "24" => "Lija", + "25" => "Luqa", + "26" => "Marsa", + "27" => "Marsaskala", + "28" => "Marsaxlokk", + "29" => "Mdina", + "30" => "Mellieħa", + "31" => "Mġarr", + "32" => "Mosta", + "33" => "Mqabba", + "34" => "Msida", + "35" => "Mtarfa", + "36" => "Munxar", + "37" => "Nadur", + "38" => "Naxxar", + "39" => "Paola", + "40" => "Pembroke", + "41" => "Pietà", + "42" => "Qala", + "43" => "Qormi", + "44" => "Qrendi", + "45" => "Rabat Gozo", + "46" => "Rabat Malta", + "47" => "Safi", + "48" => "Saint Julian's", + "49" => "Saint John", + "50" => "Saint Lawrence", + "51" => "Saint Paul's Bay", + "52" => "Sannat", + "53" => "Saint Lucia's", + "54" => "Santa Venera", + "55" => "Siġġiewi", + "56" => "Sliema", + "57" => "Swieqi", + "58" => "Ta' Xbiex", + "59" => "Tarxien", + "60" => "Valletta", + "61" => "Xagħra", + "62" => "Xewkija", + "63" => "Xgħajra", + "64" => "Żabbar", + "65" => "Żebbuġ Gozo", + "66" => "Żebbuġ Malta", + "67" => "Żejtun", + "68" => "Żurrieq" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MU.php b/vendor/workerman/validation/data/iso_3166-2/MU.php new file mode 100644 index 0000000..289df29 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MU.php @@ -0,0 +1,18 @@ + "Mauritius", + "subdivisions" => [ + "AG" => "Agalega Islands", + "BL" => "Black River", + "CC" => "Cargados Carajos Shoals", + "FL" => "Flacq", + "GP" => "Grand Port", + "MO" => "Moka", + "PA" => "Pamplemousses", + "PL" => "Port Louis", + "PW" => "Plaines Wilhems", + "RO" => "Rodrigues Island", + "RR" => "Rivière du Rempart", + "SA" => "Savanne" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MV.php b/vendor/workerman/validation/data/iso_3166-2/MV.php new file mode 100644 index 0000000..0426727 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MV.php @@ -0,0 +1,27 @@ + "Maldives", + "subdivisions" => [ + "00" => "South Ari Atoll", + "01" => "Addu City", + "02" => "North Ari Atoll", + "03" => "Faadhippolhu", + "04" => "Felidhu Atoll", + "05" => "Hahdhunmathi", + "07" => "North Thiladhunmathi", + "08" => "Kolhumadulu", + "12" => "Mulaku Atoll", + "13" => "North Maalhosmadulu", + "14" => "North Nilandhe Atoll", + "17" => "South Nilandhe Atoll", + "20" => "South Maalhosmadulu", + "23" => "South Thiladhunmathi", + "24" => "North Miladhunmadulu", + "25" => "South Miladhunmadulu", + "26" => "Male Atoll", + "27" => "North Huvadhu Atoll", + "28" => "South Huvadhu Atoll", + "29" => "Fuvammulah", + "MLE" => "Male" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MW.php b/vendor/workerman/validation/data/iso_3166-2/MW.php new file mode 100644 index 0000000..9be5b7f --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MW.php @@ -0,0 +1,37 @@ + "Malawi", + "subdivisions" => [ + "BA" => "Balaka", + "BL" => "Blantyre", + "C" => "Central Region", + "CK" => "Chikwawa", + "CR" => "Chiradzulu", + "CT" => "Chitipa", + "DE" => "Dedza", + "DO" => "Dowa", + "KR" => "Karonga", + "KS" => "Kasungu", + "LI" => "Lilongwe", + "LK" => "Likoma", + "MC" => "Mchinji", + "MG" => "Mangochi", + "MH" => "Machinga", + "MU" => "Mulanje", + "MW" => "Mwanza", + "MZ" => "Mzimba", + "N" => "Northern Region", + "NB" => "Nkhata Bay", + "NE" => "Neno", + "NI" => "Ntchisi", + "NK" => "Nkhotakota", + "NS" => "Nsanje", + "NU" => "Ntcheu", + "PH" => "Phalombe", + "RU" => "Rumphi", + "S" => "Southern Region", + "SA" => "Salima", + "TH" => "Thyolo", + "ZO" => "Zomba" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MX.php b/vendor/workerman/validation/data/iso_3166-2/MX.php new file mode 100644 index 0000000..8b63b99 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MX.php @@ -0,0 +1,38 @@ + "Mexico", + "subdivisions" => [ + "AGU" => "Aguascalientes", + "BCN" => "Baja California", + "BCS" => "Baja California Sur", + "CAM" => "Campeche", + "CHH" => "Chihuahua", + "CHP" => "Chiapas", + "CMX" => "Ciudad de México", + "COA" => "Coahuila de Zaragoza", + "COL" => "Colima", + "DUR" => "Durango", + "GRO" => "Guerrero", + "GUA" => "Guanajuato", + "HID" => "Hidalgo", + "JAL" => "Jalisco", + "MEX" => "México", + "MIC" => "Michoacán de Ocampo", + "MOR" => "Morelos", + "NAY" => "Nayarit", + "NLE" => "Nuevo León", + "OAX" => "Oaxaca", + "PUE" => "Puebla", + "QUE" => "Querétaro", + "ROO" => "Quintana Roo", + "SIN" => "Sinaloa", + "SLP" => "San Luis Potosí", + "SON" => "Sonora", + "TAB" => "Tabasco", + "TAM" => "Tamaulipas", + "TLA" => "Tlaxcala", + "VER" => "Veracruz de Ignacio de la Llave", + "YUC" => "Yucatán", + "ZAC" => "Zacatecas" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MY.php b/vendor/workerman/validation/data/iso_3166-2/MY.php new file mode 100644 index 0000000..9828884 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MY.php @@ -0,0 +1,22 @@ + "Malaysia", + "subdivisions" => [ + "01" => "Johor", + "02" => "Kedah", + "03" => "Kelantan", + "04" => "Melaka", + "05" => "Negeri Sembilan", + "06" => "Pahang", + "07" => "Pulau Pinang", + "08" => "Perak", + "09" => "Perlis", + "10" => "Selangor", + "11" => "Terengganu", + "12" => "Sabah", + "13" => "Sarawak", + "14" => "Wilayah Persekutuan Kuala Lumpur", + "15" => "Wilayah Persekutuan Labuan", + "16" => "Wilayah Persekutuan Putrajaya" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/MZ.php b/vendor/workerman/validation/data/iso_3166-2/MZ.php new file mode 100644 index 0000000..dfc9c35 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/MZ.php @@ -0,0 +1,17 @@ + "Mozambique", + "subdivisions" => [ + "A" => "Niassa", + "B" => "Manica", + "G" => "Gaza", + "I" => "Inhambane", + "L" => "Maputo", + "MPM" => "Maputo", + "N" => "Nampula", + "P" => "Cabo Delgado", + "Q" => "Zambézia", + "S" => "Sofala", + "T" => "Tete" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/NA.php b/vendor/workerman/validation/data/iso_3166-2/NA.php new file mode 100644 index 0000000..f3709ab --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/NA.php @@ -0,0 +1,20 @@ + "Namibia", + "subdivisions" => [ + "CA" => "Zambezi", + "ER" => "Erongo", + "HA" => "Hardap", + "KA" => "//Karas", + "KE" => "Kavango East", + "KH" => "Khomas", + "KU" => "Kunene", + "KW" => "Kavango West", + "OD" => "Otjozondjupa", + "OH" => "Omaheke", + "ON" => "Oshana", + "OS" => "Omusati", + "OT" => "Oshikoto", + "OW" => "Ohangwena" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/NC.php b/vendor/workerman/validation/data/iso_3166-2/NC.php new file mode 100644 index 0000000..ca1111b --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/NC.php @@ -0,0 +1,6 @@ + "New Caledonia", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/NE.php b/vendor/workerman/validation/data/iso_3166-2/NE.php new file mode 100644 index 0000000..520e352 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/NE.php @@ -0,0 +1,14 @@ + "Niger", + "subdivisions" => [ + "1" => "Agadez", + "2" => "Diffa", + "3" => "Dosso", + "4" => "Maradi", + "5" => "Tahoua", + "6" => "Tillabéri", + "7" => "Zinder", + "8" => "Niamey" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/NF.php b/vendor/workerman/validation/data/iso_3166-2/NF.php new file mode 100644 index 0000000..14dbb26 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/NF.php @@ -0,0 +1,6 @@ + "Norfolk Island", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/NG.php b/vendor/workerman/validation/data/iso_3166-2/NG.php new file mode 100644 index 0000000..3c25c07 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/NG.php @@ -0,0 +1,43 @@ + "Nigeria", + "subdivisions" => [ + "AB" => "Abia", + "AD" => "Adamawa", + "AK" => "Akwa Ibom", + "AN" => "Anambra", + "BA" => "Bauchi", + "BE" => "Benue", + "BO" => "Borno", + "BY" => "Bayelsa", + "CR" => "Cross River", + "DE" => "Delta", + "EB" => "Ebonyi", + "ED" => "Edo", + "EK" => "Ekiti", + "EN" => "Enugu", + "FC" => "Abuja Federal Capital Territory", + "GO" => "Gombe", + "IM" => "Imo", + "JI" => "Jigawa", + "KD" => "Kaduna", + "KE" => "Kebbi", + "KN" => "Kano", + "KO" => "Kogi", + "KT" => "Katsina", + "KW" => "Kwara", + "LA" => "Lagos", + "NA" => "Nasarawa", + "NI" => "Niger", + "OG" => "Ogun", + "ON" => "Ondo", + "OS" => "Osun", + "OY" => "Oyo", + "PL" => "Plateau", + "RI" => "Rivers", + "SO" => "Sokoto", + "TA" => "Taraba", + "YO" => "Yobe", + "ZA" => "Zamfara" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/NI.php b/vendor/workerman/validation/data/iso_3166-2/NI.php new file mode 100644 index 0000000..b17e901 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/NI.php @@ -0,0 +1,23 @@ + "Nicaragua", + "subdivisions" => [ + "AN" => "Costa Caribe Norte", + "AS" => "Costa Caribe Sur", + "BO" => "Boaco", + "CA" => "Carazo", + "CI" => "Chinandega", + "CO" => "Chontales", + "ES" => "Estelí", + "GR" => "Granada", + "JI" => "Jinotega", + "LE" => "León", + "MD" => "Madriz", + "MN" => "Managua", + "MS" => "Masaya", + "MT" => "Matagalpa", + "NS" => "Nueva Segovia", + "RI" => "Rivas", + "SJ" => "Río San Juan" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/NL.php b/vendor/workerman/validation/data/iso_3166-2/NL.php new file mode 100644 index 0000000..07efdab --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/NL.php @@ -0,0 +1,24 @@ + "Netherlands", + "subdivisions" => [ + "AW" => "Aruba", + "BQ1" => "Bonaire", + "BQ2" => "Saba", + "BQ3" => "Sint Eustatius", + "CW" => "Curaçao", + "DR" => "Drenthe", + "FL" => "Flevoland", + "FR" => "Fryslân", + "GE" => "Gelderland", + "GR" => "Groningen", + "LI" => "Limburg", + "NB" => "Noord-Brabant", + "NH" => "Noord-Holland", + "OV" => "Overijssel", + "SX" => "Sint Maarten", + "UT" => "Utrecht", + "ZE" => "Zeeland", + "ZH" => "Zuid-Holland" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/NO.php b/vendor/workerman/validation/data/iso_3166-2/NO.php new file mode 100644 index 0000000..1a5957c --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/NO.php @@ -0,0 +1,19 @@ + "Norway", + "subdivisions" => [ + "03" => "Oslo", + "11" => "Rogaland", + "15" => "Møre og Romsdal", + "18" => "Nordland", + "21" => "Svalbard (Arctic Region)", + "22" => "Jan Mayen (Arctic Region)", + "30" => "Viken", + "34" => "Innlandet", + "38" => "Vestfold og Telemark", + "42" => "Agder", + "46" => "Vestland", + "50" => "Trööndelage", + "54" => "Romssa ja Finnmárkku" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/NP.php b/vendor/workerman/validation/data/iso_3166-2/NP.php new file mode 100644 index 0000000..7070ab7 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/NP.php @@ -0,0 +1,32 @@ + "Nepal", + "subdivisions" => [ + "1" => "Central", + "2" => "Mid Western", + "3" => "Western", + "4" => "Eastern", + "5" => "Far Western", + "BA" => "Bagmati", + "BH" => "Bheri", + "DH" => "Dhawalagiri", + "GA" => "Gandaki", + "JA" => "Janakpur", + "KA" => "Karnali", + "KO" => "Kosi", + "LU" => "Lumbini", + "MA" => "Mahakali", + "ME" => "Mechi", + "NA" => "Narayani", + "P1" => "Province 1", + "P2" => "Province 2", + "P3" => "Bāgmatī", + "P4" => "Gandaki", + "P5" => "Province 5", + "P6" => "Karnali", + "P7" => "Sudūr Pashchim", + "RA" => "Rapti", + "SA" => "Sagarmatha", + "SE" => "Seti" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/NR.php b/vendor/workerman/validation/data/iso_3166-2/NR.php new file mode 100644 index 0000000..1a0c257 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/NR.php @@ -0,0 +1,20 @@ + "Nauru", + "subdivisions" => [ + "01" => "Aiwo", + "02" => "Anabar", + "03" => "Anetan", + "04" => "Anibare", + "05" => "Baitsi", + "06" => "Boe", + "07" => "Buada", + "08" => "Denigomodu", + "09" => "Ewa", + "10" => "Ijuw", + "11" => "Meneng", + "12" => "Nibok", + "13" => "Uaboe", + "14" => "Yaren" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/NU.php b/vendor/workerman/validation/data/iso_3166-2/NU.php new file mode 100644 index 0000000..8a53680 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/NU.php @@ -0,0 +1,6 @@ + "Niue", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/NZ.php b/vendor/workerman/validation/data/iso_3166-2/NZ.php new file mode 100644 index 0000000..e3b2511 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/NZ.php @@ -0,0 +1,23 @@ + "New Zealand", + "subdivisions" => [ + "AUK" => "Auckland", + "BOP" => "Bay of Plenty", + "CAN" => "Canterbury", + "CIT" => "Chatham Islands Territory", + "GIS" => "Gisborne", + "HKB" => "Hawke's Bay", + "MBH" => "Marlborough", + "MWT" => "Manawatu-Wanganui", + "NSN" => "Nelson", + "NTL" => "Northland", + "OTA" => "Otago", + "STL" => "Southland", + "TAS" => "Tasman", + "TKI" => "Taranaki", + "WGN" => "Wellington", + "WKO" => "Waikato", + "WTC" => "West Coast" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/OM.php b/vendor/workerman/validation/data/iso_3166-2/OM.php new file mode 100644 index 0000000..afc71da --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/OM.php @@ -0,0 +1,17 @@ + "Oman", + "subdivisions" => [ + "BJ" => "Janūb al Bāţinah", + "BS" => "Shamāl al Bāţinah", + "BU" => "Al Buraymī", + "DA" => "Ad Dākhilīyah", + "MA" => "Masqaţ", + "MU" => "Musandam", + "SJ" => "Janūb ash Sharqīyah", + "SS" => "Shamāl ash Sharqīyah", + "WU" => "Al Wusţá", + "ZA" => "Az̧ Z̧āhirah", + "ZU" => "Z̧ufār" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PA.php b/vendor/workerman/validation/data/iso_3166-2/PA.php new file mode 100644 index 0000000..aa024f7 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PA.php @@ -0,0 +1,19 @@ + "Panama", + "subdivisions" => [ + "1" => "Bocas del Toro", + "10" => "Panamá Oeste", + "2" => "Coclé", + "3" => "Colón", + "4" => "Chiriquí", + "5" => "Darién", + "6" => "Herrera", + "7" => "Los Santos", + "8" => "Panamá", + "9" => "Veraguas", + "EM" => "Emberá", + "KY" => "Guna Yala", + "NB" => "Ngöbe-Buglé" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PE.php b/vendor/workerman/validation/data/iso_3166-2/PE.php new file mode 100644 index 0000000..d8683d5 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PE.php @@ -0,0 +1,32 @@ + "Peru", + "subdivisions" => [ + "AMA" => "Amarumayu", + "ANC" => "Ancash", + "APU" => "Apurimaq", + "ARE" => "Arequipa", + "AYA" => "Ayacucho", + "CAJ" => "Cajamarca", + "CAL" => "El Callao", + "CUS" => "Cusco", + "HUC" => "Huánuco", + "HUV" => "Huancavelica", + "ICA" => "Ica", + "JUN" => "Hunin", + "LAL" => "La Libertad", + "LAM" => "Lambayeque", + "LIM" => "Lima", + "LMA" => "Lima hatun llaqta", + "LOR" => "Loreto", + "MDD" => "Madre de Dios", + "MOQ" => "Moquegua", + "PAS" => "Pasco", + "PIU" => "Piura", + "PUN" => "Puno", + "SAM" => "San Martin", + "TAC" => "Tacna", + "TUM" => "Tumbes", + "UCA" => "Ucayali" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PF.php b/vendor/workerman/validation/data/iso_3166-2/PF.php new file mode 100644 index 0000000..5f0980a --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PF.php @@ -0,0 +1,6 @@ + "French Polynesia", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PG.php b/vendor/workerman/validation/data/iso_3166-2/PG.php new file mode 100644 index 0000000..635cd3a --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PG.php @@ -0,0 +1,28 @@ + "Papua New Guinea", + "subdivisions" => [ + "CPK" => "Chimbu", + "CPM" => "Central", + "EBR" => "East New Britain", + "EHG" => "Eastern Highlands", + "EPW" => "Enga", + "ESW" => "East Sepik", + "GPK" => "Gulf", + "HLA" => "Hela", + "JWK" => "Jiwaka", + "MBA" => "Milne Bay", + "MPL" => "Morobe", + "MPM" => "Madang", + "MRL" => "Manus", + "NCD" => "National Capital District (Port Moresby)", + "NIK" => "New Ireland", + "NPP" => "Northern", + "NSB" => "Bougainville", + "SAN" => "West Sepik", + "SHM" => "Southern Highlands", + "WBK" => "West New Britain", + "WHM" => "Western Highlands", + "WPD" => "Western" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PH.php b/vendor/workerman/validation/data/iso_3166-2/PH.php new file mode 100644 index 0000000..e38cbde --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PH.php @@ -0,0 +1,104 @@ + "Philippines", + "subdivisions" => [ + "00" => "National Capital Region", + "01" => "Ilocos (Region I)", + "02" => "Cagayan Valley (Region II)", + "03" => "Central Luzon (Region III)", + "05" => "Bicol (Region V)", + "06" => "Western Visayas (Region VI)", + "07" => "Central Visayas (Region VII)", + "08" => "Eastern Visayas (Region VIII)", + "09" => "Zamboanga Peninsula (Region IX)", + "10" => "Northern Mindanao (Region X)", + "11" => "Davao (Region XI)", + "12" => "Soccsksargen (Region XII)", + "13" => "Caraga (Region XIII)", + "14" => "Autonomous Region in Muslim Mindanao (ARMM)", + "15" => "Cordillera Administrative Region (CAR)", + "40" => "Calabarzon (Region IV-A)", + "41" => "Mimaropa (Region IV-B)", + "ABR" => "Abra", + "AGN" => "Agusan del Norte", + "AGS" => "Agusan del Sur", + "AKL" => "Aklan", + "ALB" => "Albay", + "ANT" => "Antique", + "APA" => "Apayao", + "AUR" => "Aurora", + "BAN" => "Bataan", + "BAS" => "Basilan", + "BEN" => "Benguet", + "BIL" => "Biliran", + "BOH" => "Bohol", + "BTG" => "Batangas", + "BTN" => "Batanes", + "BUK" => "Bukidnon", + "BUL" => "Bulacan", + "CAG" => "Cagayan", + "CAM" => "Camiguin", + "CAN" => "Camarines Norte", + "CAP" => "Capiz", + "CAS" => "Camarines Sur", + "CAT" => "Catanduanes", + "CAV" => "Cavite", + "CEB" => "Cebu", + "COM" => "Davao de Oro", + "DAO" => "Davao Oriental", + "DAS" => "Davao del Sur", + "DAV" => "Davao del Norte", + "DIN" => "Dinagat Islands", + "DVO" => "Davao Occidental", + "EAS" => "Eastern Samar", + "GUI" => "Guimaras", + "IFU" => "Ifugao", + "ILI" => "Iloilo", + "ILN" => "Ilocos Norte", + "ILS" => "Ilocos Sur", + "ISA" => "Isabela", + "KAL" => "Kalinga", + "LAG" => "Laguna", + "LAN" => "Lanao del Norte", + "LAS" => "Lanao del Sur", + "LEY" => "Leyte", + "LUN" => "La Union", + "MAD" => "Marinduque", + "MAG" => "Maguindanao", + "MAS" => "Masbate", + "MDC" => "Mindoro Occidental", + "MDR" => "Mindoro Oriental", + "MOU" => "Mountain Province", + "MSC" => "Misamis Occidental", + "MSR" => "Misamis Oriental", + "NCO" => "Cotabato", + "NEC" => "Negros Occidental", + "NER" => "Negros Oriental", + "NSA" => "Northern Samar", + "NUE" => "Nueva Ecija", + "NUV" => "Nueva Vizcaya", + "PAM" => "Pampanga", + "PAN" => "Pangasinan", + "PLW" => "Palawan", + "QUE" => "Quezon", + "QUI" => "Quirino", + "RIZ" => "Rizal", + "ROM" => "Romblon", + "SAR" => "Sarangani", + "SCO" => "South Cotabato", + "SIG" => "Siquijor", + "SLE" => "Southern Leyte", + "SLU" => "Sulu", + "SOR" => "Sorsogon", + "SUK" => "Sultan Kudarat", + "SUN" => "Surigao del Norte", + "SUR" => "Surigao del Sur", + "TAR" => "Tarlac", + "TAW" => "Tawi-Tawi", + "WSA" => "Samar", + "ZAN" => "Zamboanga del Norte", + "ZAS" => "Zamboanga del Sur", + "ZMB" => "Zambales", + "ZSI" => "Zamboanga Sibugay" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PK.php b/vendor/workerman/validation/data/iso_3166-2/PK.php new file mode 100644 index 0000000..a6883c6 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PK.php @@ -0,0 +1,13 @@ + "Pakistan", + "subdivisions" => [ + "BA" => "Balochistan", + "GB" => "Gilgit-Baltistan", + "IS" => "Islamabad", + "JK" => "Azad Jammu and Kashmir", + "KP" => "Khyber Pakhtunkhwa", + "PB" => "Punjab", + "SD" => "Sindh" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PL.php b/vendor/workerman/validation/data/iso_3166-2/PL.php new file mode 100644 index 0000000..b7a29be --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PL.php @@ -0,0 +1,22 @@ + "Poland", + "subdivisions" => [ + "02" => "Dolnośląskie", + "04" => "Kujawsko-pomorskie", + "06" => "Lubelskie", + "08" => "Lubuskie", + "10" => "Łódzkie", + "12" => "Małopolskie", + "14" => "Mazowieckie", + "16" => "Opolskie", + "18" => "Podkarpackie", + "20" => "Podlaskie", + "22" => "Pomorskie", + "24" => "Śląskie", + "26" => "Świętokrzyskie", + "28" => "Warmińsko-mazurskie", + "30" => "Wielkopolskie", + "32" => "Zachodniopomorskie" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PM.php b/vendor/workerman/validation/data/iso_3166-2/PM.php new file mode 100644 index 0000000..3fe454f --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PM.php @@ -0,0 +1,6 @@ + "Saint Pierre and Miquelon", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PN.php b/vendor/workerman/validation/data/iso_3166-2/PN.php new file mode 100644 index 0000000..b02ad2b --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PN.php @@ -0,0 +1,6 @@ + "Pitcairn", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PR.php b/vendor/workerman/validation/data/iso_3166-2/PR.php new file mode 100644 index 0000000..820e512 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PR.php @@ -0,0 +1,6 @@ + "Puerto Rico", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PS.php b/vendor/workerman/validation/data/iso_3166-2/PS.php new file mode 100644 index 0000000..a42378c --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PS.php @@ -0,0 +1,22 @@ + "Palestine, State of", + "subdivisions" => [ + "BTH" => "Bethlehem", + "DEB" => "Deir El Balah", + "GZA" => "Gaza", + "HBN" => "Hebron", + "JEM" => "Jerusalem", + "JEN" => "Jenin", + "JRH" => "Jericho and Al Aghwar", + "KYS" => "Khan Yunis", + "NBS" => "Nablus", + "NGZ" => "North Gaza", + "QQA" => "Qalqilya", + "RBH" => "Ramallah", + "RFH" => "Rafah", + "SLT" => "Salfit", + "TBS" => "Tubas", + "TKM" => "Tulkarm" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PT.php b/vendor/workerman/validation/data/iso_3166-2/PT.php new file mode 100644 index 0000000..1f0f798 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PT.php @@ -0,0 +1,26 @@ + "Portugal", + "subdivisions" => [ + "01" => "Aveiro", + "02" => "Beja", + "03" => "Braga", + "04" => "Bragança", + "05" => "Castelo Branco", + "06" => "Coimbra", + "07" => "Évora", + "08" => "Faro", + "09" => "Guarda", + "10" => "Leiria", + "11" => "Lisboa", + "12" => "Portalegre", + "13" => "Porto", + "14" => "Santarém", + "15" => "Setúbal", + "16" => "Viana do Castelo", + "17" => "Vila Real", + "18" => "Viseu", + "20" => "Região Autónoma dos Açores", + "30" => "Região Autónoma da Madeira" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PW.php b/vendor/workerman/validation/data/iso_3166-2/PW.php new file mode 100644 index 0000000..864834f --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PW.php @@ -0,0 +1,22 @@ + "Palau", + "subdivisions" => [ + "002" => "Aimeliik", + "004" => "Airai", + "010" => "Angaur", + "050" => "Hatohobei", + "100" => "Kayangel", + "150" => "Koror", + "212" => "Melekeok", + "214" => "Ngaraard", + "218" => "Ngarchelong", + "222" => "Ngardmau", + "224" => "Ngatpang", + "226" => "Ngchesar", + "227" => "Ngeremlengui", + "228" => "Ngiwal", + "350" => "Peleliu", + "370" => "Sonsorol" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/PY.php b/vendor/workerman/validation/data/iso_3166-2/PY.php new file mode 100644 index 0000000..a64c860 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/PY.php @@ -0,0 +1,24 @@ + "Paraguay", + "subdivisions" => [ + "1" => "Concepción", + "10" => "Alto Paraná", + "11" => "Central", + "12" => "Ñeembucú", + "13" => "Amambay", + "14" => "Canindeyú", + "15" => "Presidente Hayes", + "16" => "Alto Paraguay", + "19" => "Boquerón", + "2" => "San Pedro", + "3" => "Cordillera", + "4" => "Guairá", + "5" => "Caaguazú", + "6" => "Caazapá", + "7" => "Itapúa", + "8" => "Misiones", + "9" => "Paraguarí", + "ASU" => "Asunción" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/QA.php b/vendor/workerman/validation/data/iso_3166-2/QA.php new file mode 100644 index 0000000..7592474 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/QA.php @@ -0,0 +1,14 @@ + "Qatar", + "subdivisions" => [ + "DA" => "Ad Dawḩah", + "KH" => "Al Khawr wa adh Dhakhīrah", + "MS" => "Ash Shamāl", + "RA" => "Ar Rayyān", + "SH" => "Ash Shīḩānīyah", + "US" => "Umm Şalāl", + "WA" => "Al Wakrah", + "ZA" => "Az̧ Z̧a‘āyin" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/RE.php b/vendor/workerman/validation/data/iso_3166-2/RE.php new file mode 100644 index 0000000..4d650ec --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/RE.php @@ -0,0 +1,6 @@ + "Réunion", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/RO.php b/vendor/workerman/validation/data/iso_3166-2/RO.php new file mode 100644 index 0000000..227298a --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/RO.php @@ -0,0 +1,48 @@ + "Romania", + "subdivisions" => [ + "AB" => "Alba", + "AG" => "Argeș", + "AR" => "Arad", + "B" => "București", + "BC" => "Bacău", + "BH" => "Bihor", + "BN" => "Bistrița-Năsăud", + "BR" => "Brăila", + "BT" => "Botoșani", + "BV" => "Brașov", + "BZ" => "Buzău", + "CJ" => "Cluj", + "CL" => "Călărași", + "CS" => "Caraș-Severin", + "CT" => "Constanța", + "CV" => "Covasna", + "DB" => "Dâmbovița", + "DJ" => "Dolj", + "GJ" => "Gorj", + "GL" => "Galați", + "GR" => "Giurgiu", + "HD" => "Hunedoara", + "HR" => "Harghita", + "IF" => "Ilfov", + "IL" => "Ialomița", + "IS" => "Iași", + "MH" => "Mehedinți", + "MM" => "Maramureș", + "MS" => "Mureș", + "NT" => "Neamț", + "OT" => "Olt", + "PH" => "Prahova", + "SB" => "Sibiu", + "SJ" => "Sălaj", + "SM" => "Satu Mare", + "SV" => "Suceava", + "TL" => "Tulcea", + "TM" => "Timiș", + "TR" => "Teleorman", + "VL" => "Vâlcea", + "VN" => "Vrancea", + "VS" => "Vaslui" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/RS.php b/vendor/workerman/validation/data/iso_3166-2/RS.php new file mode 100644 index 0000000..d8f6c3f --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/RS.php @@ -0,0 +1,38 @@ + "Serbia", + "subdivisions" => [ + "00" => "Beograd", + "01" => "Severnobački okrug", + "02" => "Srednjebanatski okrug", + "03" => "Severnobanatski okrug", + "04" => "Južnobanatski okrug", + "05" => "Zapadnobački okrug", + "06" => "Južnobački okrug", + "07" => "Sremski okrug", + "08" => "Mačvanski okrug", + "09" => "Kolubarski okrug", + "10" => "Podunavski okrug", + "11" => "Braničevski okrug", + "12" => "Šumadijski okrug", + "13" => "Pomoravski okrug", + "14" => "Borski okrug", + "15" => "Zaječarski okrug", + "16" => "Zlatiborski okrug", + "17" => "Moravički okrug", + "18" => "Raški okrug", + "19" => "Rasinski okrug", + "20" => "Nišavski okrug", + "21" => "Toplički okrug", + "22" => "Pirotski okrug", + "23" => "Jablanički okrug", + "24" => "Pčinjski okrug", + "25" => "Kosovski okrug", + "26" => "Pećki okrug", + "27" => "Prizrenski okrug", + "28" => "Kosovsko-Mitrovački okrug", + "29" => "Kosovsko-Pomoravski okrug", + "KM" => "Kosovo-Metohija", + "VO" => "Vojvodina" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/RU.php b/vendor/workerman/validation/data/iso_3166-2/RU.php new file mode 100644 index 0000000..a4e195c --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/RU.php @@ -0,0 +1,89 @@ + "Russian Federation", + "subdivisions" => [ + "AD" => "Adygeja, Respublika", + "AL" => "Altaj, Respublika", + "ALT" => "Altajskij kraj", + "AMU" => "Amurskaja oblast'", + "ARK" => "Arhangel'skaja oblast'", + "AST" => "Astrahanskaja oblast'", + "BA" => "Bashkortostan, Respublika", + "BEL" => "Belgorodskaja oblast'", + "BRY" => "Brjanskaja oblast'", + "BU" => "Burjatija, Respublika", + "CE" => "Chechenskaya Respublika", + "CHE" => "Chelyabinskaya oblast'", + "CHU" => "Chukotskiy avtonomnyy okrug", + "CU" => "Chuvashskaya Respublika", + "DA" => "Dagestan, Respublika", + "IN" => "Ingushetiya, Respublika", + "IRK" => "Irkutskaja oblast'", + "IVA" => "Ivanovskaja oblast'", + "KAM" => "Kamchatskiy kray", + "KB" => "Kabardino-Balkarskaja Respublika", + "KC" => "Karachayevo-Cherkesskaya Respublika", + "KDA" => "Krasnodarskij kraj", + "KEM" => "Kemerovskaja oblast'", + "KGD" => "Kaliningradskaja oblast'", + "KGN" => "Kurganskaja oblast'", + "KHA" => "Habarovskij kraj", + "KHM" => "Hanty-Mansijskij avtonomnyj okrug", + "KIR" => "Kirovskaja oblast'", + "KK" => "Hakasija, Respublika", + "KL" => "Kalmykija, Respublika", + "KLU" => "Kaluzhskaya oblast'", + "KO" => "Komi, Respublika", + "KOS" => "Kostromskaja oblast'", + "KR" => "Karelija, Respublika", + "KRS" => "Kurskaja oblast'", + "KYA" => "Krasnojarskij kraj", + "LEN" => "Leningradskaja oblast'", + "LIP" => "Lipeckaja oblast'", + "MAG" => "Magadanskaja oblast'", + "ME" => "Marij Èl, Respublika", + "MO" => "Mordovija, Respublika", + "MOS" => "Moskovskaja oblast'", + "MOW" => "Moskva", + "MUR" => "Murmanskaja oblast'", + "NEN" => "Neneckij avtonomnyj okrug", + "NGR" => "Novgorodskaja oblast'", + "NIZ" => "Nizhegorodskaya oblast'", + "NVS" => "Novosibirskaja oblast'", + "OMS" => "Omskaja oblast'", + "ORE" => "Orenburgskaja oblast'", + "ORL" => "Orlovskaja oblast'", + "PER" => "Permskij kraj", + "PNZ" => "Penzenskaja oblast'", + "PRI" => "Primorskij kraj", + "PSK" => "Pskovskaja oblast'", + "ROS" => "Rostovskaja oblast'", + "RYA" => "Rjazanskaja oblast'", + "SA" => "Saha, Respublika", + "SAK" => "Sahalinskaja oblast'", + "SAM" => "Samarskaja oblast'", + "SAR" => "Saratovskaja oblast'", + "SE" => "Severnaja Osetija, Respublika", + "SMO" => "Smolenskaja oblast'", + "SPE" => "Sankt-Peterburg", + "STA" => "Stavropol'skij kraj", + "SVE" => "Sverdlovskaja oblast'", + "TA" => "Tatarstan, Respublika", + "TAM" => "Tambovskaja oblast'", + "TOM" => "Tomskaja oblast'", + "TUL" => "Tul'skaja oblast'", + "TVE" => "Tverskaja oblast'", + "TY" => "Tyva, Respublika", + "TYU" => "Tjumenskaja oblast'", + "UD" => "Udmurtskaja Respublika", + "ULY" => "Ul'janovskaja oblast'", + "VGG" => "Volgogradskaja oblast'", + "VLA" => "Vladimirskaja oblast'", + "VLG" => "Vologodskaja oblast'", + "VOR" => "Voronezhskaya oblast'", + "YAN" => "Jamalo-Neneckij avtonomnyj okrug", + "YAR" => "Jaroslavskaja oblast'", + "YEV" => "Evrejskaja avtonomnaja oblast'", + "ZAB" => "Zabajkal'skij kraj" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/RW.php b/vendor/workerman/validation/data/iso_3166-2/RW.php new file mode 100644 index 0000000..bc3720c --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/RW.php @@ -0,0 +1,11 @@ + "Rwanda", + "subdivisions" => [ + "01" => "City of Kigali", + "02" => "Eastern", + "03" => "Northern", + "04" => "Western", + "05" => "Southern" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SA.php b/vendor/workerman/validation/data/iso_3166-2/SA.php new file mode 100644 index 0000000..50a3165 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SA.php @@ -0,0 +1,19 @@ + "Saudi Arabia", + "subdivisions" => [ + "01" => "Ar Riyāḑ", + "02" => "Makkah al Mukarramah", + "03" => "Al Madīnah al Munawwarah", + "04" => "Ash Sharqīyah", + "05" => "Al Qaşīm", + "06" => "Ḩā'il", + "07" => "Tabūk", + "08" => "Al Ḩudūd ash Shamālīyah", + "09" => "Jāzān", + "10" => "Najrān", + "11" => "Al Bāḩah", + "12" => "Al Jawf", + "14" => "'Asīr" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SB.php b/vendor/workerman/validation/data/iso_3166-2/SB.php new file mode 100644 index 0000000..0b3b6e7 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SB.php @@ -0,0 +1,16 @@ + "Solomon Islands", + "subdivisions" => [ + "CE" => "Central", + "CH" => "Choiseul", + "CT" => "Capital Territory (Honiara)", + "GU" => "Guadalcanal", + "IS" => "Isabel", + "MK" => "Makira-Ulawa", + "ML" => "Malaita", + "RB" => "Rennell and Bellona", + "TE" => "Temotu", + "WE" => "Western" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SC.php b/vendor/workerman/validation/data/iso_3166-2/SC.php new file mode 100644 index 0000000..5c22e8f --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SC.php @@ -0,0 +1,33 @@ + "Seychelles", + "subdivisions" => [ + "01" => "Anse aux Pins", + "02" => "Anse Boileau", + "03" => "Anse Etoile", + "04" => "Au Cap", + "05" => "Anse Royale", + "06" => "Baie Lazare", + "07" => "Baie Sainte Anne", + "08" => "Beau Vallon", + "09" => "Bel Air", + "10" => "Bel Ombre", + "11" => "Cascade", + "12" => "Glacis", + "13" => "Grand Anse Mahe", + "14" => "Grand Anse Praslin", + "15" => "La Digue", + "16" => "English River", + "17" => "Mont Buxton", + "18" => "Mont Fleuri", + "19" => "Plaisance", + "20" => "Pointe Larue", + "21" => "Port Glaud", + "22" => "Saint Louis", + "23" => "Takamaka", + "24" => "Les Mamelles", + "25" => "Roche Caiman", + "26" => "Ile Perseverance I", + "27" => "Ile Perseverance II" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SD.php b/vendor/workerman/validation/data/iso_3166-2/SD.php new file mode 100644 index 0000000..ad17338 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SD.php @@ -0,0 +1,24 @@ + "Sudan", + "subdivisions" => [ + "DC" => "Central Darfur", + "DE" => "East Darfur", + "DN" => "North Darfur", + "DS" => "South Darfur", + "DW" => "West Darfur", + "GD" => "Gedaref", + "GK" => "West Kordofan", + "GZ" => "Gezira", + "KA" => "Kassala", + "KH" => "Khartoum", + "KN" => "North Kordofan", + "KS" => "South Kordofan", + "NB" => "Blue Nile", + "NO" => "Northern", + "NR" => "River Nile", + "NW" => "White Nile", + "RS" => "Red Sea", + "SI" => "Sennar" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SE.php b/vendor/workerman/validation/data/iso_3166-2/SE.php new file mode 100644 index 0000000..d4564e3 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SE.php @@ -0,0 +1,27 @@ + "Sweden", + "subdivisions" => [ + "AB" => "Stockholms län [SE-01]", + "AC" => "Västerbottens län [SE-24]", + "BD" => "Norrbottens län [SE-25]", + "C" => "Uppsala län [SE-03]", + "D" => "Södermanlands län [SE-04]", + "E" => "Östergötlands län [SE-05]", + "F" => "Jönköpings län [SE-06]", + "G" => "Kronobergs län [SE-07]", + "H" => "Kalmar län [SE-08]", + "I" => "Gotlands län [SE-09]", + "K" => "Blekinge län [SE-10]", + "M" => "Skåne län [SE-12]", + "N" => "Hallands län [SE-13]", + "O" => "Västra Götalands län [SE-14]", + "S" => "Värmlands län [SE-17]", + "T" => "Örebro län [SE-18]", + "U" => "Västmanlands län [SE-19]", + "W" => "Dalarnas län [SE-20]", + "X" => "Gävleborgs län [SE-21]", + "Y" => "Västernorrlands län [SE-22]", + "Z" => "Jämtlands län [SE-23]" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SG.php b/vendor/workerman/validation/data/iso_3166-2/SG.php new file mode 100644 index 0000000..2cdc5f0 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SG.php @@ -0,0 +1,11 @@ + "Singapore", + "subdivisions" => [ + "01" => "Central Singapore", + "02" => "North East", + "03" => "North West", + "04" => "South East", + "05" => "South West" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SH.php b/vendor/workerman/validation/data/iso_3166-2/SH.php new file mode 100644 index 0000000..e62fbc9 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SH.php @@ -0,0 +1,9 @@ + "Saint Helena, Ascension and Tristan da Cunha", + "subdivisions" => [ + "AC" => "Ascension", + "HL" => "Saint Helena", + "TA" => "Tristan da Cunha" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SI.php b/vendor/workerman/validation/data/iso_3166-2/SI.php new file mode 100644 index 0000000..3dddbe5 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SI.php @@ -0,0 +1,218 @@ + "Slovenia", + "subdivisions" => [ + "001" => "Ajdovščina", + "002" => "Beltinci", + "003" => "Bled", + "004" => "Bohinj", + "005" => "Borovnica", + "006" => "Bovec", + "007" => "Brda", + "008" => "Brezovica", + "009" => "Brežice", + "010" => "Tišina", + "011" => "Celje", + "012" => "Cerklje na Gorenjskem", + "013" => "Cerknica", + "014" => "Cerkno", + "015" => "Črenšovci", + "016" => "Črna na Koroškem", + "017" => "Črnomelj", + "018" => "Destrnik", + "019" => "Divača", + "020" => "Dobrepolje", + "021" => "Dobrova-Polhov Gradec", + "022" => "Dol pri Ljubljani", + "023" => "Domžale", + "024" => "Dornava", + "025" => "Dravograd", + "026" => "Duplek", + "027" => "Gorenja vas-Poljane", + "028" => "Gorišnica", + "029" => "Gornja Radgona", + "030" => "Gornji Grad", + "031" => "Gornji Petrovci", + "032" => "Grosuplje", + "033" => "Šalovci", + "034" => "Hrastnik", + "035" => "Hrpelje-Kozina", + "036" => "Idrija", + "037" => "Ig", + "038" => "Ilirska Bistrica", + "039" => "Ivančna Gorica", + "040" => "Izola", + "041" => "Jesenice", + "042" => "Juršinci", + "043" => "Kamnik", + "044" => "Kanal", + "045" => "Kidričevo", + "046" => "Kobarid", + "047" => "Kobilje", + "048" => "Kočevje", + "049" => "Komen", + "050" => "Koper", + "051" => "Kozje", + "052" => "Kranj", + "053" => "Kranjska Gora", + "054" => "Krško", + "055" => "Kungota", + "056" => "Kuzma", + "057" => "Laško", + "058" => "Lenart", + "059" => "Lendava", + "060" => "Litija", + "061" => "Ljubljana", + "062" => "Ljubno", + "063" => "Ljutomer", + "064" => "Logatec", + "065" => "Loška dolina", + "066" => "Loški Potok", + "067" => "Luče", + "068" => "Lukovica", + "069" => "Majšperk", + "070" => "Maribor", + "071" => "Medvode", + "072" => "Mengeš", + "073" => "Metlika", + "074" => "Mežica", + "075" => "Miren-Kostanjevica", + "076" => "Mislinja", + "077" => "Moravče", + "078" => "Moravske Toplice", + "079" => "Mozirje", + "080" => "Murska Sobota", + "081" => "Muta", + "082" => "Naklo", + "083" => "Nazarje", + "084" => "Nova Gorica", + "085" => "Novo Mesto", + "086" => "Odranci", + "087" => "Ormož", + "088" => "Osilnica", + "089" => "Pesnica", + "090" => "Piran", + "091" => "Pivka", + "092" => "Podčetrtek", + "093" => "Podvelka", + "094" => "Postojna", + "095" => "Preddvor", + "096" => "Ptuj", + "097" => "Puconci", + "098" => "Rače-Fram", + "099" => "Radeče", + "100" => "Radenci", + "101" => "Radlje ob Dravi", + "102" => "Radovljica", + "103" => "Ravne na Koroškem", + "104" => "Ribnica", + "105" => "Rogašovci", + "106" => "Rogaška Slatina", + "107" => "Rogatec", + "108" => "Ruše", + "109" => "Semič", + "110" => "Sevnica", + "111" => "Sežana", + "112" => "Slovenj Gradec", + "113" => "Slovenska Bistrica", + "114" => "Slovenske Konjice", + "115" => "Starše", + "116" => "Sveti Jurij ob Ščavnici", + "117" => "Šenčur", + "118" => "Šentilj", + "119" => "Šentjernej", + "120" => "Šentjur", + "121" => "Škocjan", + "122" => "Škofja Loka", + "123" => "Škofljica", + "124" => "Šmarje pri Jelšah", + "125" => "Šmartno ob Paki", + "126" => "Šoštanj", + "127" => "Štore", + "128" => "Tolmin", + "129" => "Trbovlje", + "130" => "Trebnje", + "131" => "Tržič", + "132" => "Turnišče", + "133" => "Velenje", + "134" => "Velike Lašče", + "135" => "Videm", + "136" => "Vipava", + "137" => "Vitanje", + "138" => "Vodice", + "139" => "Vojnik", + "140" => "Vrhnika", + "141" => "Vuzenica", + "142" => "Zagorje ob Savi", + "143" => "Zavrč", + "144" => "Zreče", + "146" => "Železniki", + "147" => "Žiri", + "148" => "Benedikt", + "149" => "Bistrica ob Sotli", + "150" => "Bloke", + "151" => "Braslovče", + "152" => "Cankova", + "153" => "Cerkvenjak", + "154" => "Dobje", + "155" => "Dobrna", + "156" => "Dobrovnik", + "157" => "Dolenjske Toplice", + "158" => "Grad", + "159" => "Hajdina", + "160" => "Hoče-Slivnica", + "161" => "Hodoš", + "162" => "Horjul", + "163" => "Jezersko", + "164" => "Komenda", + "165" => "Kostel", + "166" => "Križevci", + "167" => "Lovrenc na Pohorju", + "168" => "Markovci", + "169" => "Miklavž na Dravskem polju", + "170" => "Mirna Peč", + "171" => "Oplotnica", + "172" => "Podlehnik", + "173" => "Polzela", + "174" => "Prebold", + "175" => "Prevalje", + "176" => "Razkrižje", + "177" => "Ribnica na Pohorju", + "178" => "Selnica ob Dravi", + "179" => "Sodražica", + "180" => "Solčava", + "181" => "Sveta Ana", + "182" => "Sveti Andraž v Slovenskih goricah", + "183" => "Šempeter-Vrtojba", + "184" => "Tabor", + "185" => "Trnovska Vas", + "186" => "Trzin", + "187" => "Velika Polana", + "188" => "Veržej", + "189" => "Vransko", + "190" => "Žalec", + "191" => "Žetale", + "192" => "Žirovnica", + "193" => "Žužemberk", + "194" => "Šmartno pri Litiji", + "195" => "Apače", + "196" => "Cirkulane", + "197" => "Kosanjevica na Krki", + "198" => "Makole", + "199" => "Mokronog-Trebelno", + "200" => "Poljčane", + "201" => "Renče-Vogrsko", + "202" => "Središče ob Dravi", + "203" => "Straža", + "204" => "Sveta Trojica v Slovenskih goricah", + "205" => "Sveti Tomaž", + "206" => "Šmarješke Toplice", + "207" => "Gorje", + "208" => "Log-Dragomer", + "209" => "Rečica ob Savinji", + "210" => "Sveti Jurij v Slovenskih goricah", + "211" => "Šentrupert", + "212" => "Mirna", + "213" => "Ankaran" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SJ.php b/vendor/workerman/validation/data/iso_3166-2/SJ.php new file mode 100644 index 0000000..d6ec3a2 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SJ.php @@ -0,0 +1,6 @@ + "Svalbard and Jan Mayen", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SK.php b/vendor/workerman/validation/data/iso_3166-2/SK.php new file mode 100644 index 0000000..a465575 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SK.php @@ -0,0 +1,14 @@ + "Slovakia", + "subdivisions" => [ + "BC" => "Banskobystrický kraj", + "BL" => "Bratislavský kraj", + "KI" => "Košický kraj", + "NI" => "Nitriansky kraj", + "PV" => "Prešovský kraj", + "TA" => "Trnavský kraj", + "TC" => "Trenčiansky kraj", + "ZI" => "Žilinský kraj" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SL.php b/vendor/workerman/validation/data/iso_3166-2/SL.php new file mode 100644 index 0000000..ece10b2 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SL.php @@ -0,0 +1,11 @@ + "Sierra Leone", + "subdivisions" => [ + "E" => "Eastern", + "N" => "Northern", + "NW" => "North Western", + "S" => "Southern", + "W" => "Western Area (Freetown)" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SM.php b/vendor/workerman/validation/data/iso_3166-2/SM.php new file mode 100644 index 0000000..d49dd04 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SM.php @@ -0,0 +1,15 @@ + "San Marino", + "subdivisions" => [ + "01" => "Acquaviva", + "02" => "Chiesanuova", + "03" => "Domagnano", + "04" => "Faetano", + "05" => "Fiorentino", + "06" => "Borgo Maggiore", + "07" => "Città di San Marino", + "08" => "Montegiardino", + "09" => "Serravalle" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SN.php b/vendor/workerman/validation/data/iso_3166-2/SN.php new file mode 100644 index 0000000..60119ce --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SN.php @@ -0,0 +1,20 @@ + "Senegal", + "subdivisions" => [ + "DB" => "Diourbel", + "DK" => "Dakar", + "FK" => "Fatick", + "KA" => "Kaffrine", + "KD" => "Kolda", + "KE" => "Kédougou", + "KL" => "Kaolack", + "LG" => "Louga", + "MT" => "Matam", + "SE" => "Sédhiou", + "SL" => "Saint-Louis", + "TC" => "Tambacounda", + "TH" => "Thiès", + "ZG" => "Ziguinchor" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SO.php b/vendor/workerman/validation/data/iso_3166-2/SO.php new file mode 100644 index 0000000..3ab88a4 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SO.php @@ -0,0 +1,24 @@ + "Somalia", + "subdivisions" => [ + "AW" => "Awdal", + "BK" => "Bakool", + "BN" => "Banaadir", + "BR" => "Bari", + "BY" => "Bay", + "GA" => "Galguduud", + "GE" => "Gedo", + "HI" => "Hiiraan", + "JD" => "Jubbada Dhexe", + "JH" => "Jubbada Hoose", + "MU" => "Mudug", + "NU" => "Nugaal", + "SA" => "Sanaag", + "SD" => "Shabeellaha Dhexe", + "SH" => "Shabeellaha Hoose", + "SO" => "Sool", + "TO" => "Togdheer", + "WO" => "Woqooyi Galbeed" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SR.php b/vendor/workerman/validation/data/iso_3166-2/SR.php new file mode 100644 index 0000000..e835eef --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SR.php @@ -0,0 +1,16 @@ + "Suriname", + "subdivisions" => [ + "BR" => "Brokopondo", + "CM" => "Commewijne", + "CR" => "Coronie", + "MA" => "Marowijne", + "NI" => "Nickerie", + "PM" => "Paramaribo", + "PR" => "Para", + "SA" => "Saramacca", + "SI" => "Sipaliwini", + "WA" => "Wanica" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SS.php b/vendor/workerman/validation/data/iso_3166-2/SS.php new file mode 100644 index 0000000..28b6d17 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SS.php @@ -0,0 +1,16 @@ + "South Sudan", + "subdivisions" => [ + "BN" => "Northern Bahr el Ghazal", + "BW" => "Western Bahr el Ghazal", + "EC" => "Central Equatoria", + "EE" => "Eastern Equatoria", + "EW" => "Western Equatoria", + "JG" => "Jonglei", + "LK" => "Lakes", + "NU" => "Upper Nile", + "UY" => "Unity", + "WR" => "Warrap" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/ST.php b/vendor/workerman/validation/data/iso_3166-2/ST.php new file mode 100644 index 0000000..2dcb997 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/ST.php @@ -0,0 +1,13 @@ + "Sao Tome and Principe", + "subdivisions" => [ + "01" => "Água Grande", + "02" => "Cantagalo", + "03" => "Caué", + "04" => "Lembá", + "05" => "Lobata", + "06" => "Mé-Zóchi", + "P" => "Príncipe" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SV.php b/vendor/workerman/validation/data/iso_3166-2/SV.php new file mode 100644 index 0000000..e28919d --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SV.php @@ -0,0 +1,20 @@ + "El Salvador", + "subdivisions" => [ + "AH" => "Ahuachapán", + "CA" => "Cabañas", + "CH" => "Chalatenango", + "CU" => "Cuscatlán", + "LI" => "La Libertad", + "MO" => "Morazán", + "PA" => "La Paz", + "SA" => "Santa Ana", + "SM" => "San Miguel", + "SO" => "Sonsonate", + "SS" => "San Salvador", + "SV" => "San Vicente", + "UN" => "La Unión", + "US" => "Usulután" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SX.php b/vendor/workerman/validation/data/iso_3166-2/SX.php new file mode 100644 index 0000000..ec9de88 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SX.php @@ -0,0 +1,6 @@ + "Sint Maarten (Dutch part)", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SY.php b/vendor/workerman/validation/data/iso_3166-2/SY.php new file mode 100644 index 0000000..185cf27 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SY.php @@ -0,0 +1,20 @@ + "Syrian Arab Republic", + "subdivisions" => [ + "DI" => "Dimashq", + "DR" => "Dar'ā", + "DY" => "Dayr az Zawr", + "HA" => "Al Ḩasakah", + "HI" => "Ḩimş", + "HL" => "Ḩalab", + "HM" => "Ḩamāh", + "ID" => "Idlib", + "LA" => "Al Lādhiqīyah", + "QU" => "Al Qunayţirah", + "RA" => "Ar Raqqah", + "RD" => "Rīf Dimashq", + "SU" => "As Suwaydā'", + "TA" => "Ţarţūs" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/SZ.php b/vendor/workerman/validation/data/iso_3166-2/SZ.php new file mode 100644 index 0000000..a930525 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/SZ.php @@ -0,0 +1,10 @@ + "Eswatini", + "subdivisions" => [ + "HH" => "Hhohho", + "LU" => "Lubombo", + "MA" => "Manzini", + "SH" => "Shiselweni" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TC.php b/vendor/workerman/validation/data/iso_3166-2/TC.php new file mode 100644 index 0000000..64fdafa --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TC.php @@ -0,0 +1,6 @@ + "Turks and Caicos Islands", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TD.php b/vendor/workerman/validation/data/iso_3166-2/TD.php new file mode 100644 index 0000000..ba324e2 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TD.php @@ -0,0 +1,29 @@ + "Chad", + "subdivisions" => [ + "BA" => "Al Baţḩā’", + "BG" => "Bahr el Ghazal", + "BO" => "Borkou", + "CB" => "Chari-Baguirmi", + "EE" => "Ennedi-Est", + "EO" => "Ennedi-Ouest", + "GR" => "Guéra", + "HL" => "Hadjer Lamis", + "KA" => "Kanem", + "LC" => "Al Buḩayrah", + "LO" => "Logone-Occidental", + "LR" => "Logone-Oriental", + "MA" => "Mandoul", + "MC" => "Moyen-Chari", + "ME" => "Mayo-Kebbi-Est", + "MO" => "Mayo-Kebbi-Ouest", + "ND" => "Madīnat Injamīnā", + "OD" => "Ouaddaï", + "SA" => "Salamat", + "SI" => "Sila", + "TA" => "Tandjilé", + "TI" => "Tibastī", + "WF" => "Wadi Fira" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TF.php b/vendor/workerman/validation/data/iso_3166-2/TF.php new file mode 100644 index 0000000..db7cd16 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TF.php @@ -0,0 +1,6 @@ + "French Southern Territories", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TG.php b/vendor/workerman/validation/data/iso_3166-2/TG.php new file mode 100644 index 0000000..58d97fe --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TG.php @@ -0,0 +1,11 @@ + "Togo", + "subdivisions" => [ + "C" => "Centrale", + "K" => "Kara", + "M" => "Maritime (Région)", + "P" => "Plateaux", + "S" => "Savanes" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TH.php b/vendor/workerman/validation/data/iso_3166-2/TH.php new file mode 100644 index 0000000..ef24873 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TH.php @@ -0,0 +1,84 @@ + "Thailand", + "subdivisions" => [ + "10" => "Krung Thep Maha Nakhon", + "11" => "Samut Prakan", + "12" => "Nonthaburi", + "13" => "Pathum Thani", + "14" => "Phra Nakhon Si Ayutthaya", + "15" => "Ang Thong", + "16" => "Lop Buri", + "17" => "Sing Buri", + "18" => "Chai Nat", + "19" => "Saraburi", + "20" => "Chon Buri", + "21" => "Rayong", + "22" => "Chanthaburi", + "23" => "Trat", + "24" => "Chachoengsao", + "25" => "Prachin Buri", + "26" => "Nakhon Nayok", + "27" => "Sa Kaeo", + "30" => "Nakhon Ratchasima", + "31" => "Buri Ram", + "32" => "Surin", + "33" => "Si Sa Ket", + "34" => "Ubon Ratchathani", + "35" => "Yasothon", + "36" => "Chaiyaphum", + "37" => "Amnat Charoen", + "38" => "Bueng Kan", + "39" => "Nong Bua Lam Phu", + "40" => "Khon Kaen", + "41" => "Udon Thani", + "42" => "Loei", + "43" => "Nong Khai", + "44" => "Maha Sarakham", + "45" => "Roi Et", + "46" => "Kalasin", + "47" => "Sakon Nakhon", + "48" => "Nakhon Phanom", + "49" => "Mukdahan", + "50" => "Chiang Mai", + "51" => "Lamphun", + "52" => "Lampang", + "53" => "Uttaradit", + "54" => "Phrae", + "55" => "Nan", + "56" => "Phayao", + "57" => "Chiang Rai", + "58" => "Mae Hong Son", + "60" => "Nakhon Sawan", + "61" => "Uthai Thani", + "62" => "Kamphaeng Phet", + "63" => "Tak", + "64" => "Sukhothai", + "65" => "Phitsanulok", + "66" => "Phichit", + "67" => "Phetchabun", + "70" => "Ratchaburi", + "71" => "Kanchanaburi", + "72" => "Suphan Buri", + "73" => "Nakhon Pathom", + "74" => "Samut Sakhon", + "75" => "Samut Songkhram", + "76" => "Phetchaburi", + "77" => "Prachuap Khiri Khan", + "80" => "Nakhon Si Thammarat", + "81" => "Krabi", + "82" => "Phangnga", + "83" => "Phuket", + "84" => "Surat Thani", + "85" => "Ranong", + "86" => "Chumphon", + "90" => "Songkhla", + "91" => "Satun", + "92" => "Trang", + "93" => "Phatthalung", + "94" => "Pattani", + "95" => "Yala", + "96" => "Narathiwat", + "S" => "Phatthaya" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TJ.php b/vendor/workerman/validation/data/iso_3166-2/TJ.php new file mode 100644 index 0000000..36da3cf --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TJ.php @@ -0,0 +1,11 @@ + "Tajikistan", + "subdivisions" => [ + "DU" => "Dushanbe", + "GB" => "Kŭhistoni Badakhshon", + "KT" => "Khatlon", + "RA" => "nohiyahoi tobei jumhurí", + "SU" => "Sughd" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TK.php b/vendor/workerman/validation/data/iso_3166-2/TK.php new file mode 100644 index 0000000..d147f21 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TK.php @@ -0,0 +1,6 @@ + "Tokelau", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TL.php b/vendor/workerman/validation/data/iso_3166-2/TL.php new file mode 100644 index 0000000..6dd0ce9 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TL.php @@ -0,0 +1,19 @@ + "Timor-Leste", + "subdivisions" => [ + "AL" => "Aileu", + "AN" => "Ainaro", + "BA" => "Baucau", + "BO" => "Bobonaro", + "CO" => "Cova Lima", + "DI" => "Díli", + "ER" => "Ermera", + "LA" => "Lautein", + "LI" => "Likisá", + "MF" => "Manufahi", + "MT" => "Manatuto", + "OE" => "Oekusi-Ambenu", + "VI" => "Vikeke" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TM.php b/vendor/workerman/validation/data/iso_3166-2/TM.php new file mode 100644 index 0000000..671d6b8 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TM.php @@ -0,0 +1,12 @@ + "Turkmenistan", + "subdivisions" => [ + "A" => "Ahal", + "B" => "Balkan", + "D" => "Daşoguz", + "L" => "Lebap", + "M" => "Mary", + "S" => "Aşgabat" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TN.php b/vendor/workerman/validation/data/iso_3166-2/TN.php new file mode 100644 index 0000000..5eb7dbd --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TN.php @@ -0,0 +1,30 @@ + "Tunisia", + "subdivisions" => [ + "11" => "Tunis", + "12" => "L'Ariana", + "13" => "Ben Arous", + "14" => "La Manouba", + "21" => "Nabeul", + "22" => "Zaghouan", + "23" => "Bizerte", + "31" => "Béja", + "32" => "Jendouba", + "33" => "Le Kef", + "34" => "Siliana", + "41" => "Kairouan", + "42" => "Kasserine", + "43" => "Sidi Bouzid", + "51" => "Sousse", + "52" => "Monastir", + "53" => "Mahdia", + "61" => "Sfax", + "71" => "Gafsa", + "72" => "Tozeur", + "73" => "Kébili", + "81" => "Gabès", + "82" => "Médenine", + "83" => "Tataouine" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TO.php b/vendor/workerman/validation/data/iso_3166-2/TO.php new file mode 100644 index 0000000..04334ed --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TO.php @@ -0,0 +1,11 @@ + "Tonga", + "subdivisions" => [ + "01" => "'Eua", + "02" => "Ha'apai", + "03" => "Niuas", + "04" => "Tongatapu", + "05" => "Vava'u" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TR.php b/vendor/workerman/validation/data/iso_3166-2/TR.php new file mode 100644 index 0000000..6af25ee --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TR.php @@ -0,0 +1,87 @@ + "Türkiye", + "subdivisions" => [ + "01" => "Adana", + "02" => "Adıyaman", + "03" => "Afyonkarahisar", + "04" => "Ağrı", + "05" => "Amasya", + "06" => "Ankara", + "07" => "Antalya", + "08" => "Artvin", + "09" => "Aydın", + "10" => "Balıkesir", + "11" => "Bilecik", + "12" => "Bingöl", + "13" => "Bitlis", + "14" => "Bolu", + "15" => "Burdur", + "16" => "Bursa", + "17" => "Çanakkale", + "18" => "Çankırı", + "19" => "Çorum", + "20" => "Denizli", + "21" => "Diyarbakır", + "22" => "Edirne", + "23" => "Elazığ", + "24" => "Erzincan", + "25" => "Erzurum", + "26" => "Eskişehir", + "27" => "Gaziantep", + "28" => "Giresun", + "29" => "Gümüşhane", + "30" => "Hakkâri", + "31" => "Hatay", + "32" => "Isparta", + "33" => "Mersin", + "34" => "İstanbul", + "35" => "İzmir", + "36" => "Kars", + "37" => "Kastamonu", + "38" => "Kayseri", + "39" => "Kırklareli", + "40" => "Kırşehir", + "41" => "Kocaeli", + "42" => "Konya", + "43" => "Kütahya", + "44" => "Malatya", + "45" => "Manisa", + "46" => "Kahramanmaraş", + "47" => "Mardin", + "48" => "Muğla", + "49" => "Muş", + "50" => "Nevşehir", + "51" => "Niğde", + "52" => "Ordu", + "53" => "Rize", + "54" => "Sakarya", + "55" => "Samsun", + "56" => "Siirt", + "57" => "Sinop", + "58" => "Sivas", + "59" => "Tekirdağ", + "60" => "Tokat", + "61" => "Trabzon", + "62" => "Tunceli", + "63" => "Şanlıurfa", + "64" => "Uşak", + "65" => "Van", + "66" => "Yozgat", + "67" => "Zonguldak", + "68" => "Aksaray", + "69" => "Bayburt", + "70" => "Karaman", + "71" => "Kırıkkale", + "72" => "Batman", + "73" => "Şırnak", + "74" => "Bartın", + "75" => "Ardahan", + "76" => "Iğdır", + "77" => "Yalova", + "78" => "Karabük", + "79" => "Kilis", + "80" => "Osmaniye", + "81" => "Düzce" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TT.php b/vendor/workerman/validation/data/iso_3166-2/TT.php new file mode 100644 index 0000000..3d34601 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TT.php @@ -0,0 +1,21 @@ + "Trinidad and Tobago", + "subdivisions" => [ + "ARI" => "Arima", + "CHA" => "Chaguanas", + "CTT" => "Couva-Tabaquite-Talparo", + "DMN" => "Diego Martin", + "MRC" => "Mayaro-Rio Claro", + "PED" => "Penal-Debe", + "POS" => "Port of Spain", + "PRT" => "Princes Town", + "PTF" => "Point Fortin", + "SFO" => "San Fernando", + "SGE" => "Sangre Grande", + "SIP" => "Siparia", + "SJL" => "San Juan-Laventille", + "TOB" => "Tobago", + "TUP" => "Tunapuna-Piarco" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TV.php b/vendor/workerman/validation/data/iso_3166-2/TV.php new file mode 100644 index 0000000..2db0e57 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TV.php @@ -0,0 +1,14 @@ + "Tuvalu", + "subdivisions" => [ + "FUN" => "Funafuti", + "NIT" => "Niutao", + "NKF" => "Nukufetau", + "NKL" => "Nukulaelae", + "NMA" => "Nanumea", + "NMG" => "Nanumaga", + "NUI" => "Nui", + "VAI" => "Vaitupu" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TW.php b/vendor/workerman/validation/data/iso_3166-2/TW.php new file mode 100644 index 0000000..48b7224 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TW.php @@ -0,0 +1,28 @@ + "Taiwan, Province of China", + "subdivisions" => [ + "CHA" => "Changhua", + "CYI" => "Chiayi", + "CYQ" => "Chiayi", + "HSQ" => "Hsinchu", + "HSZ" => "Hsinchu", + "HUA" => "Hualien", + "ILA" => "Yilan", + "KEE" => "Keelung", + "KHH" => "Kaohsiung", + "KIN" => "Kinmen", + "LIE" => "Lienchiang", + "MIA" => "Miaoli", + "NAN" => "Nantou", + "NWT" => "New Taipei", + "PEN" => "Penghu", + "PIF" => "Pingtung", + "TAO" => "Taoyuan", + "TNN" => "Tainan", + "TPE" => "Taipei", + "TTT" => "Taitung", + "TXG" => "Taichung", + "YUN" => "Yunlin" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/TZ.php b/vendor/workerman/validation/data/iso_3166-2/TZ.php new file mode 100644 index 0000000..6b01869 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/TZ.php @@ -0,0 +1,37 @@ + "Tanzania, United Republic of", + "subdivisions" => [ + "01" => "Arusha", + "02" => "Dar es Salaam", + "03" => "Dodoma", + "04" => "Iringa", + "05" => "Kagera", + "06" => "Pemba North", + "07" => "Zanzibar North", + "08" => "Kigoma", + "09" => "Kilimanjaro", + "10" => "Pemba South", + "11" => "Zanzibar South", + "12" => "Lindi", + "13" => "Mara", + "14" => "Mbeya", + "15" => "Zanzibar West", + "16" => "Morogoro", + "17" => "Mtwara", + "18" => "Mwanza", + "19" => "Coast", + "20" => "Rukwa", + "21" => "Ruvuma", + "22" => "Shinyanga", + "23" => "Singida", + "24" => "Tabora", + "25" => "Tanga", + "26" => "Manyara", + "27" => "Geita", + "28" => "Katavi", + "29" => "Njombe", + "30" => "Simiyu", + "31" => "Songwe" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/UA.php b/vendor/workerman/validation/data/iso_3166-2/UA.php new file mode 100644 index 0000000..9c4d641 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/UA.php @@ -0,0 +1,33 @@ + "Ukraine", + "subdivisions" => [ + "05" => "Vinnytska oblast", + "07" => "Volynska oblast", + "09" => "Luhanska oblast", + "12" => "Dnipropetrovska oblast", + "14" => "Donetska oblast", + "18" => "Zhytomyrska oblast", + "21" => "Zakarpatska oblast", + "23" => "Zaporizka oblast", + "26" => "Ivano-Frankivska oblast", + "30" => "Kyiv", + "32" => "Kyivska oblast", + "35" => "Kirovohradska oblast", + "40" => "Sevastopol", + "43" => "Avtonomna Respublika Krym", + "46" => "Lvivska oblast", + "48" => "Mykolaivska oblast", + "51" => "Odeska oblast", + "53" => "Poltavska oblast", + "56" => "Rivnenska oblast", + "59" => "Sumska oblast", + "61" => "Ternopilska oblast", + "63" => "Kharkivska oblast", + "65" => "Khersonska oblast", + "68" => "Khmelnytska oblast", + "71" => "Cherkaska oblast", + "74" => "Chernihivska oblast", + "77" => "Chernivetska oblast" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/UG.php b/vendor/workerman/validation/data/iso_3166-2/UG.php new file mode 100644 index 0000000..e8b0774 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/UG.php @@ -0,0 +1,145 @@ + "Uganda", + "subdivisions" => [ + "101" => "Kalangala", + "102" => "Kampala", + "103" => "Kiboga", + "104" => "Luwero", + "105" => "Masaka", + "106" => "Mpigi", + "107" => "Mubende", + "108" => "Mukono", + "109" => "Nakasongola", + "110" => "Rakai", + "111" => "Sembabule", + "112" => "Kayunga", + "113" => "Wakiso", + "114" => "Lyantonde", + "115" => "Mityana", + "116" => "Nakaseke", + "117" => "Buikwe", + "118" => "Bukomansibi", + "119" => "Butambala", + "120" => "Buvuma", + "121" => "Gomba", + "122" => "Kalungu", + "123" => "Kyankwanzi", + "124" => "Lwengo", + "125" => "Kyotera", + "126" => "Kasanda", + "201" => "Bugiri", + "202" => "Busia", + "203" => "Iganga", + "204" => "Jinja", + "205" => "Kamuli", + "206" => "Kapchorwa", + "207" => "Katakwi", + "208" => "Kumi", + "209" => "Mbale", + "210" => "Pallisa", + "211" => "Soroti", + "212" => "Tororo", + "213" => "Kaberamaido", + "214" => "Mayuge", + "215" => "Sironko", + "216" => "Amuria", + "217" => "Budaka", + "218" => "Bududa", + "219" => "Bukedea", + "220" => "Bukwo", + "221" => "Butaleja", + "222" => "Kaliro", + "223" => "Manafwa", + "224" => "Namutumba", + "225" => "Bulambuli", + "226" => "Buyende", + "227" => "Kibuku", + "228" => "Kween", + "229" => "Luuka", + "230" => "Namayingo", + "231" => "Ngora", + "232" => "Serere", + "233" => "Butebo", + "234" => "Namisindwa", + "235" => "Bugweri", + "236" => "Kapelebyong", + "237" => "Kalaki", + "301" => "Adjumani", + "302" => "Apac", + "303" => "Arua", + "304" => "Gulu", + "305" => "Kitgum", + "306" => "Kotido", + "307" => "Lira", + "308" => "Moroto", + "309" => "Moyo", + "310" => "Nebbi", + "311" => "Nakapiripirit", + "312" => "Pader", + "313" => "Yumbe", + "314" => "Abim", + "315" => "Amolatar", + "316" => "Amuru", + "317" => "Dokolo", + "318" => "Kaabong", + "319" => "Koboko", + "320" => "Maracha", + "321" => "Oyam", + "322" => "Agago", + "323" => "Alebtong", + "324" => "Amudat", + "325" => "Kole", + "326" => "Lamwo", + "327" => "Napak", + "328" => "Nwoya", + "329" => "Otuke", + "330" => "Zombo", + "331" => "Omoro", + "332" => "Pakwach", + "333" => "Kwania", + "334" => "Nabilatuk", + "335" => "Karenga", + "336" => "Madi-Okollo", + "337" => "Obongi", + "401" => "Bundibugyo", + "402" => "Bushenyi", + "403" => "Hoima", + "404" => "Kabale", + "405" => "Kabarole", + "406" => "Kasese", + "407" => "Kibaale", + "408" => "Kisoro", + "409" => "Masindi", + "410" => "Mbarara", + "411" => "Ntungamo", + "412" => "Rukungiri", + "413" => "Kamwenge", + "414" => "Kanungu", + "415" => "Kyenjojo", + "416" => "Buliisa", + "417" => "Ibanda", + "418" => "Isingiro", + "419" => "Kiruhura", + "420" => "Buhweju", + "421" => "Kiryandongo", + "422" => "Kyegegwa", + "423" => "Mitooma", + "424" => "Ntoroko", + "425" => "Rubirizi", + "426" => "Sheema", + "427" => "Kagadi", + "428" => "Kakumiro", + "429" => "Rubanda", + "430" => "Bunyangabu", + "431" => "Rukiga", + "432" => "Kikuube", + "433" => "Kazo", + "434" => "Kitagwenda", + "435" => "Rwampara", + "C" => "Central", + "E" => "Eastern", + "N" => "Northern", + "W" => "Western" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/UM.php b/vendor/workerman/validation/data/iso_3166-2/UM.php new file mode 100644 index 0000000..3cf5f59 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/UM.php @@ -0,0 +1,15 @@ + "United States Minor Outlying Islands", + "subdivisions" => [ + "67" => "Johnston Atoll", + "71" => "Midway Islands", + "76" => "Navassa Island", + "79" => "Wake Island", + "81" => "Baker Island", + "84" => "Howland Island", + "86" => "Jarvis Island", + "89" => "Kingman Reef", + "95" => "Palmyra Atoll" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/US.php b/vendor/workerman/validation/data/iso_3166-2/US.php new file mode 100644 index 0000000..06e7042 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/US.php @@ -0,0 +1,63 @@ + "United States", + "subdivisions" => [ + "AK" => "Alaska", + "AL" => "Alabama", + "AR" => "Arkansas", + "AS" => "American Samoa", + "AZ" => "Arizona", + "CA" => "California", + "CO" => "Colorado", + "CT" => "Connecticut", + "DC" => "District of Columbia", + "DE" => "Delaware", + "FL" => "Florida", + "GA" => "Georgia", + "GU" => "Guam", + "HI" => "Hawaii", + "IA" => "Iowa", + "ID" => "Idaho", + "IL" => "Illinois", + "IN" => "Indiana", + "KS" => "Kansas", + "KY" => "Kentucky", + "LA" => "Louisiana", + "MA" => "Massachusetts", + "MD" => "Maryland", + "ME" => "Maine", + "MI" => "Michigan", + "MN" => "Minnesota", + "MO" => "Missouri", + "MP" => "Northern Mariana Islands", + "MS" => "Mississippi", + "MT" => "Montana", + "NC" => "North Carolina", + "ND" => "North Dakota", + "NE" => "Nebraska", + "NH" => "New Hampshire", + "NJ" => "New Jersey", + "NM" => "New Mexico", + "NV" => "Nevada", + "NY" => "New York", + "OH" => "Ohio", + "OK" => "Oklahoma", + "OR" => "Oregon", + "PA" => "Pennsylvania", + "PR" => "Puerto Rico", + "RI" => "Rhode Island", + "SC" => "South Carolina", + "SD" => "South Dakota", + "TN" => "Tennessee", + "TX" => "Texas", + "UM" => "United States Minor Outlying Islands", + "UT" => "Utah", + "VA" => "Virginia", + "VI" => "Virgin Islands, U.S.", + "VT" => "Vermont", + "WA" => "Washington", + "WI" => "Wisconsin", + "WV" => "West Virginia", + "WY" => "Wyoming" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/UY.php b/vendor/workerman/validation/data/iso_3166-2/UY.php new file mode 100644 index 0000000..72e35e8 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/UY.php @@ -0,0 +1,25 @@ + "Uruguay", + "subdivisions" => [ + "AR" => "Artigas", + "CA" => "Canelones", + "CL" => "Cerro Largo", + "CO" => "Colonia", + "DU" => "Durazno", + "FD" => "Florida", + "FS" => "Flores", + "LA" => "Lavalleja", + "MA" => "Maldonado", + "MO" => "Montevideo", + "PA" => "Paysandú", + "RN" => "Río Negro", + "RO" => "Rocha", + "RV" => "Rivera", + "SA" => "Salto", + "SJ" => "San José", + "SO" => "Soriano", + "TA" => "Tacuarembó", + "TT" => "Treinta y Tres" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/UZ.php b/vendor/workerman/validation/data/iso_3166-2/UZ.php new file mode 100644 index 0000000..750d74d --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/UZ.php @@ -0,0 +1,20 @@ + "Uzbekistan", + "subdivisions" => [ + "AN" => "Andijon", + "BU" => "Buxoro", + "FA" => "Farg‘ona", + "JI" => "Jizzax", + "NG" => "Namangan", + "NW" => "Navoiy", + "QA" => "Qashqadaryo", + "QR" => "Qoraqalpog‘iston Respublikasi", + "SA" => "Samarqand", + "SI" => "Sirdaryo", + "SU" => "Surxondaryo", + "TK" => "Toshkent", + "TO" => "Toshkent", + "XO" => "Xorazm" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/VA.php b/vendor/workerman/validation/data/iso_3166-2/VA.php new file mode 100644 index 0000000..a7e0031 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/VA.php @@ -0,0 +1,6 @@ + "Holy See (Vatican City State)", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/VC.php b/vendor/workerman/validation/data/iso_3166-2/VC.php new file mode 100644 index 0000000..2e7bf97 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/VC.php @@ -0,0 +1,12 @@ + "Saint Vincent and the Grenadines", + "subdivisions" => [ + "01" => "Charlotte", + "02" => "Saint Andrew", + "03" => "Saint David", + "04" => "Saint George", + "05" => "Saint Patrick", + "06" => "Grenadines" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/VE.php b/vendor/workerman/validation/data/iso_3166-2/VE.php new file mode 100644 index 0000000..3598656 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/VE.php @@ -0,0 +1,31 @@ + "Venezuela, Bolivarian Republic of", + "subdivisions" => [ + "A" => "Distrito Capital", + "B" => "Anzoátegui", + "C" => "Apure", + "D" => "Aragua", + "E" => "Barinas", + "F" => "Bolívar", + "G" => "Carabobo", + "H" => "Cojedes", + "I" => "Falcón", + "J" => "Guárico", + "K" => "Lara", + "L" => "Mérida", + "M" => "Miranda", + "N" => "Monagas", + "O" => "Nueva Esparta", + "P" => "Portuguesa", + "R" => "Sucre", + "S" => "Táchira", + "T" => "Trujillo", + "U" => "Yaracuy", + "V" => "Zulia", + "W" => "Dependencias Federales", + "X" => "La Guaira", + "Y" => "Delta Amacuro", + "Z" => "Amazonas" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/VG.php b/vendor/workerman/validation/data/iso_3166-2/VG.php new file mode 100644 index 0000000..8be7c0c --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/VG.php @@ -0,0 +1,6 @@ + "Virgin Islands, British", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/VI.php b/vendor/workerman/validation/data/iso_3166-2/VI.php new file mode 100644 index 0000000..41096e3 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/VI.php @@ -0,0 +1,6 @@ + "Virgin Islands, U.S.", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/VN.php b/vendor/workerman/validation/data/iso_3166-2/VN.php new file mode 100644 index 0000000..917b686 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/VN.php @@ -0,0 +1,69 @@ + "Viet Nam", + "subdivisions" => [ + "01" => "Lai Châu", + "02" => "Lào Cai", + "03" => "Hà Giang", + "04" => "Cao Bằng", + "05" => "Sơn La", + "06" => "Yên Bái", + "07" => "Tuyên Quang", + "09" => "Lạng Sơn", + "13" => "Quảng Ninh", + "14" => "Hòa Bình", + "18" => "Ninh Bình", + "20" => "Thái Bình", + "21" => "Thanh Hóa", + "22" => "Nghệ An", + "23" => "Hà Tĩnh", + "24" => "Quảng Bình", + "25" => "Quảng Trị", + "26" => "Thừa Thiên-Huế", + "27" => "Quảng Nam", + "28" => "Kon Tum", + "29" => "Quảng Ngãi", + "30" => "Gia Lai", + "31" => "Bình Định", + "32" => "Phú Yên", + "33" => "Đắk Lắk", + "34" => "Khánh Hòa", + "35" => "Lâm Đồng", + "36" => "Ninh Thuận", + "37" => "Tây Ninh", + "39" => "Đồng Nai", + "40" => "Bình Thuận", + "41" => "Long An", + "43" => "Bà Rịa - Vũng Tàu", + "44" => "An Giang", + "45" => "Đồng Tháp", + "46" => "Tiền Giang", + "47" => "Kiến Giang", + "49" => "Vĩnh Long", + "50" => "Bến Tre", + "51" => "Trà Vinh", + "52" => "Sóc Trăng", + "53" => "Bắc Kạn", + "54" => "Bắc Giang", + "55" => "Bạc Liêu", + "56" => "Bắc Ninh", + "57" => "Bình Dương", + "58" => "Bình Phước", + "59" => "Cà Mau", + "61" => "Hải Dương", + "63" => "Hà Nam", + "66" => "Hưng Yên", + "67" => "Nam Định", + "68" => "Phú Thọ", + "69" => "Thái Nguyên", + "70" => "Vĩnh Phúc", + "71" => "Điện Biên", + "72" => "Đắk Nông", + "73" => "Hậu Giang", + "CT" => "Cần Thơ", + "DN" => "Đà Nẵng", + "HN" => "Hà Nội", + "HP" => "Hải Phòng", + "SG" => "Hồ Chí Minh" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/VU.php b/vendor/workerman/validation/data/iso_3166-2/VU.php new file mode 100644 index 0000000..41a57be --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/VU.php @@ -0,0 +1,12 @@ + "Vanuatu", + "subdivisions" => [ + "MAP" => "Malampa", + "PAM" => "Pénama", + "SAM" => "Sanma", + "SEE" => "Shéfa", + "TAE" => "Taféa", + "TOB" => "Torba" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/WF.php b/vendor/workerman/validation/data/iso_3166-2/WF.php new file mode 100644 index 0000000..d29bbc4 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/WF.php @@ -0,0 +1,9 @@ + "Wallis and Futuna", + "subdivisions" => [ + "AL" => "Alo", + "SG" => "Sigave", + "UV" => "Uvea" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/WS.php b/vendor/workerman/validation/data/iso_3166-2/WS.php new file mode 100644 index 0000000..b5cb4b7 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/WS.php @@ -0,0 +1,17 @@ + "Samoa", + "subdivisions" => [ + "AA" => "A'ana", + "AL" => "Aiga-i-le-Tai", + "AT" => "Atua", + "FA" => "Fa'asaleleaga", + "GE" => "Gaga'emauga", + "GI" => "Gagaifomauga", + "PA" => "Palauli", + "SA" => "Satupa'itea", + "TU" => "Tuamasaga", + "VF" => "Va'a-o-Fonoti", + "VS" => "Vaisigano" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/YE.php b/vendor/workerman/validation/data/iso_3166-2/YE.php new file mode 100644 index 0000000..c781874 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/YE.php @@ -0,0 +1,28 @@ + "Yemen", + "subdivisions" => [ + "AB" => "Abyan", + "AD" => "‘Adan", + "AM" => "‘Amrān", + "BA" => "Al Bayḑā’", + "DA" => "Aḑ Ḑāli‘", + "DH" => "Dhamār", + "HD" => "Ḩaḑramawt", + "HJ" => "Ḩajjah", + "HU" => "Al Ḩudaydah", + "IB" => "Ibb", + "JA" => "Al Jawf", + "LA" => "Laḩij", + "MA" => "Ma’rib", + "MR" => "Al Mahrah", + "MW" => "Al Maḩwīt", + "RA" => "Raymah", + "SA" => "Amānat al ‘Āşimah [city]", + "SD" => "Şāʻdah", + "SH" => "Shabwah", + "SN" => "Şanʻā’", + "SU" => "Arkhabīl Suquţrá", + "TA" => "Tāʻizz" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/YT.php b/vendor/workerman/validation/data/iso_3166-2/YT.php new file mode 100644 index 0000000..700a72d --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/YT.php @@ -0,0 +1,6 @@ + "Mayotte", + "subdivisions" => [ + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/ZA.php b/vendor/workerman/validation/data/iso_3166-2/ZA.php new file mode 100644 index 0000000..e1eb379 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/ZA.php @@ -0,0 +1,15 @@ + "South Africa", + "subdivisions" => [ + "EC" => "Eastern Cape", + "FS" => "Free State", + "GP" => "Gauteng", + "KZN" => "Kwazulu-Natal", + "LP" => "Limpopo", + "MP" => "Mpumalanga", + "NC" => "Northern Cape", + "NW" => "North-West", + "WC" => "Western Cape" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/ZM.php b/vendor/workerman/validation/data/iso_3166-2/ZM.php new file mode 100644 index 0000000..b08c5ba --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/ZM.php @@ -0,0 +1,16 @@ + "Zambia", + "subdivisions" => [ + "01" => "Western", + "02" => "Central", + "03" => "Eastern", + "04" => "Luapula", + "05" => "Northern", + "06" => "North-Western", + "07" => "Southern", + "08" => "Copperbelt", + "09" => "Lusaka", + "10" => "Muchinga" + ] +]; diff --git a/vendor/workerman/validation/data/iso_3166-2/ZW.php b/vendor/workerman/validation/data/iso_3166-2/ZW.php new file mode 100644 index 0000000..173ba66 --- /dev/null +++ b/vendor/workerman/validation/data/iso_3166-2/ZW.php @@ -0,0 +1,16 @@ + "Zimbabwe", + "subdivisions" => [ + "BU" => "Bulawayo", + "HA" => "Harare", + "MA" => "Manicaland", + "MC" => "Mashonaland Central", + "ME" => "Mashonaland East", + "MI" => "Midlands", + "MN" => "Matabeleland North", + "MS" => "Matabeleland South", + "MV" => "Masvingo", + "MW" => "Mashonaland West" + ] +]; diff --git a/vendor/workerman/validation/library/ChainedValidator.php b/vendor/workerman/validation/library/ChainedValidator.php new file mode 100644 index 0000000..a45e2fd --- /dev/null +++ b/vendor/workerman/validation/library/ChainedValidator.php @@ -0,0 +1,377 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation; + +use finfo; +use Respect\Validation\Rules\Key; + +interface ChainedValidator extends Validatable +{ + public function allOf(Validatable ...$rule): ChainedValidator; + + public function alnum(string ...$additionalChars): ChainedValidator; + + public function alpha(string ...$additionalChars): ChainedValidator; + + public function alwaysInvalid(): ChainedValidator; + + public function alwaysValid(): ChainedValidator; + + public function anyOf(Validatable ...$rule): ChainedValidator; + + public function arrayType(): ChainedValidator; + + public function arrayVal(): ChainedValidator; + + public function attribute( + string $reference, + ?Validatable $validator = null, + bool $mandatory = true + ): ChainedValidator; + + public function base(int $base, ?string $chars = null): ChainedValidator; + + public function base64(): ChainedValidator; + + /** + * @param mixed $minimum + * @param mixed $maximum + */ + public function between($minimum, $maximum): ChainedValidator; + + public function boolType(): ChainedValidator; + + public function boolVal(): ChainedValidator; + + public function bsn(): ChainedValidator; + + public function call(callable $callable, Validatable $rule): ChainedValidator; + + public function callableType(): ChainedValidator; + + public function callback(callable $callback): ChainedValidator; + + public function charset(string ...$charset): ChainedValidator; + + public function cnh(): ChainedValidator; + + public function cnpj(): ChainedValidator; + + public function control(string ...$additionalChars): ChainedValidator; + + public function consonant(string ...$additionalChars): ChainedValidator; + + /** + * @param mixed $containsValue + */ + public function contains($containsValue, bool $identical = false): ChainedValidator; + + /** + * @param mixed[] $needles + */ + public function containsAny(array $needles, bool $strictCompareArray = false): ChainedValidator; + + public function countable(): ChainedValidator; + + public function countryCode(?string $set = null): ChainedValidator; + + public function currencyCode(): ChainedValidator; + + public function cpf(): ChainedValidator; + + public function creditCard(?string $brand = null): ChainedValidator; + + public function date(string $format = 'Y-m-d'): ChainedValidator; + + public function dateTime(?string $format = null): ChainedValidator; + + public function decimal(int $decimals): ChainedValidator; + + public function digit(string ...$additionalChars): ChainedValidator; + + public function directory(): ChainedValidator; + + public function domain(bool $tldCheck = true): ChainedValidator; + + public function each(Validatable $rule): ChainedValidator; + + public function email(): ChainedValidator; + + /** + * @param mixed $endValue + */ + public function endsWith($endValue, bool $identical = false): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public function equals($compareTo): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public function equivalent($compareTo): ChainedValidator; + + public function even(): ChainedValidator; + + public function executable(): ChainedValidator; + + public function exists(): ChainedValidator; + + public function extension(string $extension): ChainedValidator; + + public function factor(int $dividend): ChainedValidator; + + public function falseVal(): ChainedValidator; + + public function fibonacci(): ChainedValidator; + + public function file(): ChainedValidator; + + /** + * @param mixed[]|int $options + */ + public function filterVar(int $filter, $options = null): ChainedValidator; + + public function finite(): ChainedValidator; + + public function floatVal(): ChainedValidator; + + public function floatType(): ChainedValidator; + + public function graph(string ...$additionalChars): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public function greaterThan($compareTo): ChainedValidator; + + public function hexRgbColor(): ChainedValidator; + + public function iban(): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public function identical($compareTo): ChainedValidator; + + public function image(?finfo $fileInfo = null): ChainedValidator; + + public function imei(): ChainedValidator; + + /** + * @param mixed[]|mixed $haystack + */ + public function in($haystack, bool $compareIdentical = false): ChainedValidator; + + public function infinite(): ChainedValidator; + + public function instance(string $instanceName): ChainedValidator; + + public function intVal(): ChainedValidator; + + public function intType(): ChainedValidator; + + public function ip(string $range = '*', ?int $options = null): ChainedValidator; + + public function isbn(): ChainedValidator; + + public function iterableType(): ChainedValidator; + + public function json(): ChainedValidator; + + public function key( + string $reference, + ?Validatable $referenceValidator = null, + bool $mandatory = true + ): ChainedValidator; + + public function keyNested( + string $reference, + ?Validatable $referenceValidator = null, + bool $mandatory = true + ): ChainedValidator; + + public function keySet(Key ...$rule): ChainedValidator; + + public function keyValue(string $comparedKey, string $ruleName, string $baseKey): ChainedValidator; + + public function languageCode(?string $set = null): ChainedValidator; + + public function leapDate(string $format): ChainedValidator; + + public function leapYear(): ChainedValidator; + + public function length(?int $min = null, ?int $max = null, bool $inclusive = true): ChainedValidator; + + public function lowercase(): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public function lessThan($compareTo): ChainedValidator; + + public function luhn(): ChainedValidator; + + public function macAddress(): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public function max($compareTo): ChainedValidator; + + public function maxAge(int $age, ?string $format = null): ChainedValidator; + + public function mimetype(string $mimetype): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public function min($compareTo): ChainedValidator; + + public function minAge(int $age, ?string $format = null): ChainedValidator; + + public function multiple(int $multipleOf): ChainedValidator; + + public function negative(): ChainedValidator; + + public function nfeAccessKey(): ChainedValidator; + + public function nif(): ChainedValidator; + + public function nip(): ChainedValidator; + + public function no(bool $useLocale = false): ChainedValidator; + + public function noneOf(Validatable ...$rule): ChainedValidator; + + public function not(Validatable $rule): ChainedValidator; + + public function notBlank(): ChainedValidator; + + public function notEmoji(): ChainedValidator; + + public function notEmpty(): ChainedValidator; + + public function notOptional(): ChainedValidator; + + public function noWhitespace(): ChainedValidator; + + public function nullable(Validatable $rule): ChainedValidator; + + public function nullType(): ChainedValidator; + + public function number(): ChainedValidator; + + public function numericVal(): ChainedValidator; + + public function objectType(): ChainedValidator; + + public function odd(): ChainedValidator; + + public function oneOf(Validatable ...$rule): ChainedValidator; + + public function optional(Validatable $rule): ChainedValidator; + + public function perfectSquare(): ChainedValidator; + + public function pesel(): ChainedValidator; + + public function phone(): ChainedValidator; + + public function mobile(): ChainedValidator; + public function phpLabel(): ChainedValidator; + + public function pis(): ChainedValidator; + + public function polishIdCard(): ChainedValidator; + + public function portugueseNif(): ChainedValidator; + + public function positive(): ChainedValidator; + + public function postalCode(string $countryCode): ChainedValidator; + + public function primeNumber(): ChainedValidator; + + public function printable(string ...$additionalChars): ChainedValidator; + + public function publicDomainSuffix(): ChainedValidator; + + public function punct(string ...$additionalChars): ChainedValidator; + + public function readable(): ChainedValidator; + + public function regex(string $regex): ChainedValidator; + + public function resourceType(): ChainedValidator; + + public function roman(): ChainedValidator; + + public function scalarVal(): ChainedValidator; + + public function size(?string $minSize = null, ?string $maxSize = null): ChainedValidator; + + public function slug(): ChainedValidator; + + public function sorted(string $direction): ChainedValidator; + + public function space(string ...$additionalChars): ChainedValidator; + + /** + * @param mixed $startValue + */ + public function startsWith($startValue, bool $identical = false): ChainedValidator; + + public function stringType(): ChainedValidator; + + public function stringVal(): ChainedValidator; + + public function subdivisionCode(string $countryCode): ChainedValidator; + + /** + * @param mixed[] $superset + */ + public function subset(array $superset): ChainedValidator; + + public function symbolicLink(): ChainedValidator; + + public function time(string $format = 'H:i:s'): ChainedValidator; + + public function tld(): ChainedValidator; + + public function trueVal(): ChainedValidator; + + public function type(string $type): ChainedValidator; + + public function unique(): ChainedValidator; + + public function uploaded(): ChainedValidator; + + public function uppercase(): ChainedValidator; + + public function url(): ChainedValidator; + + public function uuid(?int $version = null): ChainedValidator; + + public function version(): ChainedValidator; + + public function videoUrl(?string $service = null): ChainedValidator; + + public function vowel(string ...$additionalChars): ChainedValidator; + + public function when(Validatable $if, Validatable $then, ?Validatable $else = null): ChainedValidator; + + public function writable(): ChainedValidator; + + public function xdigit(string ...$additionalChars): ChainedValidator; + + public function yes(bool $useLocale = false): ChainedValidator; +} diff --git a/vendor/workerman/validation/library/Exceptions/AllOfException.php b/vendor/workerman/validation/library/Exceptions/AllOfException.php new file mode 100644 index 0000000..2072308 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/AllOfException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +class AllOfException extends GroupedValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::NONE => '{{name}} 必需符合以下规则', + self::SOME => '{{name}} 必需符合以下规则', + ], + self::MODE_NEGATIVE => [ + self::NONE => '{{name}} 不能符合以下规则', + self::SOME => '{{name}} 不能符合以下规则', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/AlnumException.php b/vendor/workerman/validation/library/Exceptions/AlnumException.php new file mode 100644 index 0000000..da72816 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/AlnumException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class AlnumException extends FilteredValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 只能包含字母(a-z)和数字(0-9)', + self::EXTRA => '{{name}} 只能包含字母(a-z)、数字(0-9)和 {{additionalChars}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含字母(a-z)或数字(0-9)', + self::EXTRA => '{{name}} 不能包含字母(a-z)、数字(0-9)或 {{additionalChars}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/AlphaException.php b/vendor/workerman/validation/library/Exceptions/AlphaException.php new file mode 100644 index 0000000..c29833f --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/AlphaException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class AlphaException extends FilteredValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 只能包含字母(a-z)', + self::EXTRA => '{{name}} 只能包含字母(a-z)和 {{additionalChars}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含字母(a-z)', + self::EXTRA => '{{name}} 不能包含字母(a-z)或 {{additionalChars}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/AlwaysInvalidException.php b/vendor/workerman/validation/library/Exceptions/AlwaysInvalidException.php new file mode 100644 index 0000000..70bd71b --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/AlwaysInvalidException.php @@ -0,0 +1,34 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author William Espindola + */ +final class AlwaysInvalidException extends ValidationException +{ + public const SIMPLE = 'simple'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 始终无效', + self::SIMPLE => '{{name}} 无效', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 始终有效', + self::SIMPLE => '{{name}} 有效', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/AlwaysValidException.php b/vendor/workerman/validation/library/Exceptions/AlwaysValidException.php new file mode 100644 index 0000000..e38a176 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/AlwaysValidException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author William Espindola + */ +final class AlwaysValidException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 始终有效', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 始终无效', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/AnyOfException.php b/vendor/workerman/validation/library/Exceptions/AnyOfException.php new file mode 100644 index 0000000..c7f7246 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/AnyOfException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class AnyOfException extends NestedValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '这些规则中至少有一个必须传递给 {{name}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '这些规则中至少有一个不能传递给 {{name}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ArrayTypeException.php b/vendor/workerman/validation/library/Exceptions/ArrayTypeException.php new file mode 100644 index 0000000..2544588 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ArrayTypeException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Emmerson Siqueira + * @author Henrique Moody + * @author João Torquato + */ +final class ArrayTypeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是array类型', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是array类型', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ArrayValException.php b/vendor/workerman/validation/library/Exceptions/ArrayValException.php new file mode 100644 index 0000000..a4bf796 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ArrayValException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class ArrayValException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是数组', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是数组', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/AttributeException.php b/vendor/workerman/validation/library/Exceptions/AttributeException.php new file mode 100644 index 0000000..9aaf4e0 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/AttributeException.php @@ -0,0 +1,45 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exceptions to be thrown by the Attribute Rule. + * + * @author Alexandre Gomes Gaigalas + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class AttributeException extends NestedValidationException implements NonOmissibleException +{ + public const NOT_PRESENT = 'not_present'; + public const INVALID = 'invalid'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::NOT_PRESENT => '属性 {{name}} 必须存在', + self::INVALID => '属性 {{name}} 必须有效', + ], + self::MODE_NEGATIVE => [ + self::NOT_PRESENT => '属性 {{name}} 不能存在', + self::INVALID => '属性 {{name}} 必须无效', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + return $this->getParam('hasReference') ? self::INVALID : self::NOT_PRESENT; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/Base64Exception.php b/vendor/workerman/validation/library/Exceptions/Base64Exception.php new file mode 100644 index 0000000..ac29a3d --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/Base64Exception.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Jens Segers + * @author William Espindola + */ +final class Base64Exception extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须采用Base64编码', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是Base64编码', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/BaseException.php b/vendor/workerman/validation/library/Exceptions/BaseException.php new file mode 100644 index 0000000..e4b929f --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/BaseException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Carlos André Ferrari + * @author Henrique Moody + * @author William Espindola + */ +final class BaseException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是基于 {{base}} 中的数字', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是基于 {{base}} 中的数字', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/BetweenException.php b/vendor/workerman/validation/library/Exceptions/BetweenException.php new file mode 100644 index 0000000..066132f --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/BetweenException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class BetweenException extends NestedValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须介于 {{minValue}} 和 {{maxValue}} 之间', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能介于 {{minValue}} 和 {{maxValue}} 之间', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/BoolTypeException.php b/vendor/workerman/validation/library/Exceptions/BoolTypeException.php new file mode 100644 index 0000000..c748ea3 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/BoolTypeException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exception class for BoolType rule. + * + * @author Devin Torres + * @author Henrique Moody + */ +final class BoolTypeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是boolean类型', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是boolean类型', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/BoolValException.php b/vendor/workerman/validation/library/Exceptions/BoolValException.php new file mode 100644 index 0000000..7fd23de --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/BoolValException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Emmerson Siqueira + * @author Henrique Moody + * @author William Espindola + */ +final class BoolValException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是布尔值', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是布尔值', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/BsnException.php b/vendor/workerman/validation/library/Exceptions/BsnException.php new file mode 100644 index 0000000..57278f1 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/BsnException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Ronald Drenth + * @author William Espindola + */ +final class BsnException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是BSN', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是BSN', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/CallException.php b/vendor/workerman/validation/library/Exceptions/CallException.php new file mode 100644 index 0000000..68b7b06 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/CallException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class CallException extends NestedValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '当使用 {{callable}} 时 {{input}} 必须有效', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '当使用 {{callable}} 时 {{input}} 必须无效', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/CallableTypeException.php b/vendor/workerman/validation/library/Exceptions/CallableTypeException.php new file mode 100644 index 0000000..bdd7fbe --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/CallableTypeException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exception class for CallableType rule. + * + * @author Henrique Moody + */ +final class CallableTypeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是可调用的', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是可调用的', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/CallbackException.php b/vendor/workerman/validation/library/Exceptions/CallbackException.php new file mode 100644 index 0000000..548ee14 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/CallbackException.php @@ -0,0 +1,19 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author William Espindola + */ +final class CallbackException extends NestedValidationException +{ +} diff --git a/vendor/workerman/validation/library/Exceptions/CharsetException.php b/vendor/workerman/validation/library/Exceptions/CharsetException.php new file mode 100644 index 0000000..6182c3a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/CharsetException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author William Espindola + */ +final class CharsetException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须在 {{charset}} 字符集中', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能在 {{charset}} 字符集中', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/CnhException.php b/vendor/workerman/validation/library/Exceptions/CnhException.php new file mode 100644 index 0000000..692188a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/CnhException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Kinn Coelho Julião + * @author William Espindola + */ +final class CnhException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的CNH号码', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的CNH号码', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/CnpjException.php b/vendor/workerman/validation/library/Exceptions/CnpjException.php new file mode 100644 index 0000000..7eaf67d --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/CnpjException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Leonn Leite + * @author William Espindola + */ +final class CnpjException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的CNPJ编号', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的CNPJ编号', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ComponentException.php b/vendor/workerman/validation/library/Exceptions/ComponentException.php new file mode 100644 index 0000000..dd252a9 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ComponentException.php @@ -0,0 +1,21 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +use Exception; +use Throwable; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +class ComponentException extends Exception implements Throwable +{ +} diff --git a/vendor/workerman/validation/library/Exceptions/ConsonantException.php b/vendor/workerman/validation/library/Exceptions/ConsonantException.php new file mode 100644 index 0000000..3aa7bca --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ConsonantException.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Danilo Correa + * @author Kleber Hamada Sato + */ +final class ConsonantException extends FilteredValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 只能包含辅音', + self::EXTRA => '{{name}} 只能包含辅音和 {{additionalChars}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含辅音', + self::EXTRA => '{{name}} 不能包含辅音或 {{additionalChars}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ContainsAnyException.php b/vendor/workerman/validation/library/Exceptions/ContainsAnyException.php new file mode 100644 index 0000000..67572d3 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ContainsAnyException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Kirill Dlussky + */ +final class ContainsAnyException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须包含至少一个值 {{needles}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含任何值 {{needles}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ContainsException.php b/vendor/workerman/validation/library/Exceptions/ContainsException.php new file mode 100644 index 0000000..3d31bb1 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ContainsException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author William Espindola + */ +final class ContainsException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须包含值 {{containsValue}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含值 {{containsValue}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ControlException.php b/vendor/workerman/validation/library/Exceptions/ControlException.php new file mode 100644 index 0000000..4c5f86b --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ControlException.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Andre Ramaciotti + * @author Danilo Correa + * @author Henrique Moody + */ +final class ControlException extends FilteredValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 只能包含控制字符', + self::EXTRA => '{{name}} 只能包含控制字符和 {{additionalChars}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含控制字符', + self::EXTRA => '{{name}} 不能包含控制字符或 {{additionalChars}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/CountableException.php b/vendor/workerman/validation/library/Exceptions/CountableException.php new file mode 100644 index 0000000..aeaec42 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/CountableException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author João Torquato + * @author William Espindola + */ +final class CountableException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是可数的', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是可数的', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/CountryCodeException.php b/vendor/workerman/validation/library/Exceptions/CountryCodeException.php new file mode 100644 index 0000000..4fb9b2d --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/CountryCodeException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author William Espindola + */ +final class CountryCodeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的国家/地区', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的国家/地区', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/CpfException.php b/vendor/workerman/validation/library/Exceptions/CpfException.php new file mode 100644 index 0000000..671e899 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/CpfException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Jair Henrique + * @author William Espindola + */ +final class CpfException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的CPF编号', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的CPF编号', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/CreditCardException.php b/vendor/workerman/validation/library/Exceptions/CreditCardException.php new file mode 100644 index 0000000..a60ef3b --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/CreditCardException.php @@ -0,0 +1,48 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +use Respect\Validation\Rules\CreditCard; + +/** + * @author Henrique Moody + * @author Jean Pimentel + * @author William Espindola + */ +final class CreditCardException extends ValidationException +{ + public const BRANDED = 'branded'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的信用卡号', + self::BRANDED => '{{name}} 必须是有效的 {{brand}} 信用卡号', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的信用卡号', + self::BRANDED => '{{name}} 不能是有效的 {{brand}} 信用卡号', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + if ($this->getParam('brand') === CreditCard::ANY) { + return self::STANDARD; + } + + return self::BRANDED; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/CurrencyCodeException.php b/vendor/workerman/validation/library/Exceptions/CurrencyCodeException.php new file mode 100644 index 0000000..7d4c75f --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/CurrencyCodeException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Justin Hook + * @author William Espindola + */ +final class CurrencyCodeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的货币', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的货币', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/DateException.php b/vendor/workerman/validation/library/Exceptions/DateException.php new file mode 100644 index 0000000..86d7c66 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/DateException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Bruno Luiz da Silva + * @author Henrique Moody + */ +final class DateException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是格式为 {{sample}} 的有效日期', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是格式为 {{sample}} 的日期', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/DateTimeException.php b/vendor/workerman/validation/library/Exceptions/DateTimeException.php new file mode 100644 index 0000000..44a6619 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/DateTimeException.php @@ -0,0 +1,41 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class DateTimeException extends ValidationException +{ + public const FORMAT = 'format'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的日期/时间', + self::FORMAT => '{{name}} 必须是格式为 {{sample}} 的有效日期/时间', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的日期/时间', + self::FORMAT => '{{name}} 不能是格式为 {{sample}} 的有效日期/时间', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + return $this->getParam('format') ? self::FORMAT : self::STANDARD; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/DecimalException.php b/vendor/workerman/validation/library/Exceptions/DecimalException.php new file mode 100644 index 0000000..69cf14d --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/DecimalException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class DecimalException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} must have {{decimals}} decimals', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} must not have {{decimals}} decimals', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/DigitException.php b/vendor/workerman/validation/library/Exceptions/DigitException.php new file mode 100644 index 0000000..d52c1d1 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/DigitException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class DigitException extends FilteredValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 只能包含数字(0-9)', + self::EXTRA => '{{name}} 只能包含数字(0-9)和 {{additionalChars}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含数字(0-9)', + self::EXTRA => '{{name}} 只能包含数字(0-9)和 {{additionalChars}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/DirectoryException.php b/vendor/workerman/validation/library/Exceptions/DirectoryException.php new file mode 100644 index 0000000..3b435ae --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/DirectoryException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author William Espindola + */ +final class DirectoryException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是目录', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是目录', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/DomainException.php b/vendor/workerman/validation/library/Exceptions/DomainException.php new file mode 100644 index 0000000..5656a63 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/DomainException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class DomainException extends NestedValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效域名', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效域名', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/EachException.php b/vendor/workerman/validation/library/Exceptions/EachException.php new file mode 100644 index 0000000..ece4c43 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/EachException.php @@ -0,0 +1,52 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author William Espindola + */ +final class EachException extends NestedValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 中的每个项都必须有效', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 中的每个项都必须无效', + ], + ]; + + /** + * {@inheritDoc} + * + * @todo This method shares too much with the parent implementation + */ + public function getMessages(array $templates = []): array + { + $messages = []; + $count = -1; + foreach ($this->getChildren() as $exception) { + $count++; + $id = $exception->getId(); + + $messages[$id . '.' . $count] = $this->renderMessage( + $exception, + $this->findTemplates($templates, $this->getId()) + ); + } + + return $messages; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/EmailException.php b/vendor/workerman/validation/library/Exceptions/EmailException.php new file mode 100644 index 0000000..ea2e4fd --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/EmailException.php @@ -0,0 +1,34 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exceptions thrown by email rule. + * + * @author Alexandre Gomes Gaigalas + * @author Andrey Kolyshkin + * @author Eduardo Gulias Davis + * @author Henrique Moody + * @author Paul Karikari + */ +final class EmailException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的电子邮件', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是电子邮件', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/EndsWithException.php b/vendor/workerman/validation/library/Exceptions/EndsWithException.php new file mode 100644 index 0000000..8c446ef --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/EndsWithException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author William Espindola + */ +final class EndsWithException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须以 {{endValue}} 结尾', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能以 {{endValue}} 结尾', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/EqualsException.php b/vendor/workerman/validation/library/Exceptions/EqualsException.php new file mode 100644 index 0000000..5c65c99 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/EqualsException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Ian Nisbet + */ +final class EqualsException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须等于 {{compareTo}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能等于 {{compareTo}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/EquivalentException.php b/vendor/workerman/validation/library/Exceptions/EquivalentException.php new file mode 100644 index 0000000..f2ebe0a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/EquivalentException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class EquivalentException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须与 {{compareTo}} 相等', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能与 {{compareTo}} 相等', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/EvenException.php b/vendor/workerman/validation/library/Exceptions/EvenException.php new file mode 100644 index 0000000..0c6be4c --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/EvenException.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exceptions to be thrown by the Even Rule. + * + * @author Henrique Moody + * @author Jean Pimentel + * @author Paul Karikari + */ +final class EvenException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是偶数', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是偶数', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/Exception.php b/vendor/workerman/validation/library/Exceptions/Exception.php new file mode 100644 index 0000000..ce707ea --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/Exception.php @@ -0,0 +1,20 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +use Throwable; + +/** + * @author Andy Wendt + * @author Henrique Moody + */ +interface Exception extends Throwable +{ +} diff --git a/vendor/workerman/validation/library/Exceptions/ExecutableException.php b/vendor/workerman/validation/library/Exceptions/ExecutableException.php new file mode 100644 index 0000000..45c5cd6 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ExecutableException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author William Espindola + */ +final class ExecutableException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是可执行文件', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是可执行文件', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ExistsException.php b/vendor/workerman/validation/library/Exceptions/ExistsException.php new file mode 100644 index 0000000..6aff72a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ExistsException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author William Espindola + */ +final class ExistsException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须存在', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能存在', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ExtensionException.php b/vendor/workerman/validation/library/Exceptions/ExtensionException.php new file mode 100644 index 0000000..76133cf --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ExtensionException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exception class for Extension rule. + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class ExtensionException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是 {{extension}} 后缀', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是 {{extension}} 后缀', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/FactorException.php b/vendor/workerman/validation/library/Exceptions/FactorException.php new file mode 100644 index 0000000..94f1755 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/FactorException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author David Meister + * @author Henrique Moody + */ +final class FactorException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是 {{dividend}} 的因子', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是 {{dividend}} 的因子', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/FalseValException.php b/vendor/workerman/validation/library/Exceptions/FalseValException.php new file mode 100644 index 0000000..3e2bce6 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/FalseValException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + */ +final class FalseValException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 不被视为 "False"', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 可视为 "False"', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/FibonacciException.php b/vendor/workerman/validation/library/Exceptions/FibonacciException.php new file mode 100644 index 0000000..15ca8f0 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/FibonacciException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + * @author Samuel Heinzmann + */ +final class FibonacciException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的斐波纳契数', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的斐波纳契数', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/FileException.php b/vendor/workerman/validation/library/Exceptions/FileException.php new file mode 100644 index 0000000..c5add8d --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/FileException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + */ +final class FileException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是文件', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是文件', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/FilterVarException.php b/vendor/workerman/validation/library/Exceptions/FilterVarException.php new file mode 100644 index 0000000..798b373 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/FilterVarException.php @@ -0,0 +1,17 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class FilterVarException extends ValidationException +{ +} diff --git a/vendor/workerman/validation/library/Exceptions/FilteredValidationException.php b/vendor/workerman/validation/library/Exceptions/FilteredValidationException.php new file mode 100644 index 0000000..d81f5e9 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/FilteredValidationException.php @@ -0,0 +1,26 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +class FilteredValidationException extends ValidationException +{ + public const EXTRA = 'extra'; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + return $this->getParam('additionalChars') ? self::EXTRA : self::STANDARD; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/FiniteException.php b/vendor/workerman/validation/library/Exceptions/FiniteException.php new file mode 100644 index 0000000..6121146 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/FiniteException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + */ +final class FiniteException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有限数', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有限数', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/FloatTypeException.php b/vendor/workerman/validation/library/Exceptions/FloatTypeException.php new file mode 100644 index 0000000..fa21625 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/FloatTypeException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exception class for FloatType rule. + * + * @author Henrique Moody + * @author Reginaldo Junior <76regi@gmail.com> + */ +final class FloatTypeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是float类型', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是float类型', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/FloatValException.php b/vendor/workerman/validation/library/Exceptions/FloatValException.php new file mode 100644 index 0000000..e8dd288 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/FloatValException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + */ +final class FloatValException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是浮点数', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是浮点数', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/GraphException.php b/vendor/workerman/validation/library/Exceptions/GraphException.php new file mode 100644 index 0000000..691cd14 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/GraphException.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Andre Ramaciotti + * @author Danilo Correa + * @author Henrique Moody + */ +final class GraphException extends FilteredValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 只能包含图形字符', + self::EXTRA => '{{name}} 只能包含图形字符和 {{additionalChars}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含图形字符', + self::EXTRA => '{{name}} 不能包含图形字符或 {{additionalChars}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/GreaterThanException.php b/vendor/workerman/validation/library/Exceptions/GreaterThanException.php new file mode 100644 index 0000000..cf2241a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/GreaterThanException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class GreaterThanException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须大于 {{compareTo}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能大于 {{compareTo}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/GroupedValidationException.php b/vendor/workerman/validation/library/Exceptions/GroupedValidationException.php new file mode 100644 index 0000000..555bbb4 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/GroupedValidationException.php @@ -0,0 +1,47 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +use function count; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +class GroupedValidationException extends NestedValidationException +{ + public const NONE = 'none'; + public const SOME = 'some'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::NONE => '所有必需的规则都必须传递给 {{name}}', + self::SOME => '这些规则必须传递给 {{name}}', + ], + self::MODE_NEGATIVE => [ + self::NONE => '所有规则都不能传递给 {{name}}', + self::SOME => '这些规则不能传递给 {{name}}', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + $numRules = $this->getParam('passed'); + $numFailed = count($this->getChildren()); + + return $numRules === $numFailed ? self::NONE : self::SOME; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/HexRgbColorException.php b/vendor/workerman/validation/library/Exceptions/HexRgbColorException.php new file mode 100644 index 0000000..4385e76 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/HexRgbColorException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Davide Pastore + * @author Henrique Moody + */ +final class HexRgbColorException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是十六进制RGB颜色', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是十六进制RGB颜色', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/IbanException.php b/vendor/workerman/validation/library/Exceptions/IbanException.php new file mode 100644 index 0000000..854163a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/IbanException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Mazen Touati + */ +final class IbanException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的IBAN', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的IBAN', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/IdenticalException.php b/vendor/workerman/validation/library/Exceptions/IdenticalException.php new file mode 100644 index 0000000..cb81dfa --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/IdenticalException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class IdenticalException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须与 {{compareTo}} 相同', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能与 {{compareTo}} 相同', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ImageException.php b/vendor/workerman/validation/library/Exceptions/ImageException.php new file mode 100644 index 0000000..f009df9 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ImageException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Benevides + * @author Guilherme Siani + * @author Henrique Moody + */ +final class ImageException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的图像', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的图像', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ImeiException.php b/vendor/workerman/validation/library/Exceptions/ImeiException.php new file mode 100644 index 0000000..f310615 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ImeiException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Benevides + * @author Diego Oliveira + * @author Henrique Moody + */ +final class ImeiException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的IMEI', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的IMEI', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/InException.php b/vendor/workerman/validation/library/Exceptions/InException.php new file mode 100644 index 0000000..8baf958 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/InException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + */ +final class InException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须在 {{haystack}} 中', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能在 {{haystack}} 中', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/InfiniteException.php b/vendor/workerman/validation/library/Exceptions/InfiniteException.php new file mode 100644 index 0000000..b02bd23 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/InfiniteException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Benevides + * @author Henrique Moody + */ +final class InfiniteException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是无穷大的数字', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是无穷大的数字', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/InstanceException.php b/vendor/workerman/validation/library/Exceptions/InstanceException.php new file mode 100644 index 0000000..4a19a11 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/InstanceException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + */ +final class InstanceException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是 {{instanceName}} 的实例', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是 {{instanceName}} 的实例', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/IntTypeException.php b/vendor/workerman/validation/library/Exceptions/IntTypeException.php new file mode 100644 index 0000000..901f6a6 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/IntTypeException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exception class for IntType rule. + * + * @author Henrique Moody + */ +final class IntTypeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是integer类型', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是integer类型', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/IntValException.php b/vendor/workerman/validation/library/Exceptions/IntValException.php new file mode 100644 index 0000000..c787c8b --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/IntValException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + */ +final class IntValException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是整数', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是整数', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/InvalidClassException.php b/vendor/workerman/validation/library/Exceptions/InvalidClassException.php new file mode 100644 index 0000000..72361fd --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/InvalidClassException.php @@ -0,0 +1,21 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exception for invalid classes. + * + * @since 2.0.0 + * + * @author Henrique Moody + */ +final class InvalidClassException extends ComponentException +{ +} diff --git a/vendor/workerman/validation/library/Exceptions/IpException.php b/vendor/workerman/validation/library/Exceptions/IpException.php new file mode 100644 index 0000000..a0fe793 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/IpException.php @@ -0,0 +1,47 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + * @author Luís Otávio Cobucci Oblonczyk + */ +final class IpException extends ValidationException +{ + public const NETWORK_RANGE = 'network_range'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是IP地址', + self::NETWORK_RANGE => '{{name}} 必须是 {{range}} 范围内的IP地址', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是IP地址', + self::NETWORK_RANGE => '{{name}} 不能是 {{range}} 范围内的IP地址', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + if (!$this->getParam('range')) { + return self::STANDARD; + } + + return self::NETWORK_RANGE; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/IsbnException.php b/vendor/workerman/validation/library/Exceptions/IsbnException.php new file mode 100644 index 0000000..657be01 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/IsbnException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Moritz Fromm + */ +final class IsbnException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是ISBN', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是ISBN', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/IterableTypeException.php b/vendor/workerman/validation/library/Exceptions/IterableTypeException.php new file mode 100644 index 0000000..ce78933 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/IterableTypeException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class IterableTypeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是可迭代的', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是可迭代的', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/JsonException.php b/vendor/workerman/validation/library/Exceptions/JsonException.php new file mode 100644 index 0000000..6395550 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/JsonException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + */ +final class JsonException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的JSON字符串', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的JSON字符串', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/KeyException.php b/vendor/workerman/validation/library/Exceptions/KeyException.php new file mode 100644 index 0000000..4f87fd6 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/KeyException.php @@ -0,0 +1,45 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exceptions to be thrown by the Attribute Rule. + * + * @author Alexandre Gomes Gaigalas + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class KeyException extends NestedValidationException implements NonOmissibleException +{ + public const NOT_PRESENT = 'not_present'; + public const INVALID = 'invalid'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::NOT_PRESENT => '键 {{name}} 必须存在', + self::INVALID => '键 {{name}} 必须有效', + ], + self::MODE_NEGATIVE => [ + self::NOT_PRESENT => '键 {{name}} 不能存在', + self::INVALID => '键 {{name}} 必须无效', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + return $this->getParam('hasReference') ? self::INVALID : self::NOT_PRESENT; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/KeyNestedException.php b/vendor/workerman/validation/library/Exceptions/KeyNestedException.php new file mode 100644 index 0000000..9fc0a59 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/KeyNestedException.php @@ -0,0 +1,45 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exceptions to be thrown by the Attribute Rule. + * + * @author Emmerson Siqueira + * @author Henrique Moody + * @author Ivan Zinovyev + */ +final class KeyNestedException extends NestedValidationException implements NonOmissibleException +{ + public const NOT_PRESENT = 'not_present'; + public const INVALID = 'invalid'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::NOT_PRESENT => '找不到密钥链 {{name}} 的项', + self::INVALID => '密钥链 {{name}} 无效', + ], + self::MODE_NEGATIVE => [ + self::NOT_PRESENT => '密钥链 {{name}} 的项不存在', + self::INVALID => '密钥链 {{name}} 必须无效', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + return $this->getParam('hasReference') ? self::INVALID : self::NOT_PRESENT; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/KeySetException.php b/vendor/workerman/validation/library/Exceptions/KeySetException.php new file mode 100644 index 0000000..dc8fa0c --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/KeySetException.php @@ -0,0 +1,50 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +use function count; + +/** + * @author Henrique Moody + */ +final class KeySetException extends GroupedValidationException implements NonOmissibleException +{ + public const STRUCTURE = 'structure'; + public const STRUCTURE_EXTRA = 'structure_extra'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::NONE => '所有必需的规则都必须传递给 {{name}}', + self::SOME => '这些规则必须传递给 {{name}}', + self::STRUCTURE => '必须有键 {{keys}}', + + self::STRUCTURE_EXTRA => '不能有键 {{keys}}', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + if (count($this->getParam('extraKeys'))) { + return self::STRUCTURE_EXTRA; + } + + if (count($this->getChildren()) === 0) { + return self::STRUCTURE; + } + + return parent::chooseTemplate(); + } +} diff --git a/vendor/workerman/validation/library/Exceptions/KeyValueException.php b/vendor/workerman/validation/library/Exceptions/KeyValueException.php new file mode 100644 index 0000000..473b4d6 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/KeyValueException.php @@ -0,0 +1,37 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class KeyValueException extends ValidationException +{ + public const COMPONENT = 'component'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '键 {{name}} 必须存在', + self::COMPONENT => '{{baseKey}} 必须有效才能验证 {{comparedKey}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '键 {{name}} 不能存在', + self::COMPONENT => '{{baseKey}} 必须无效才能验证 {{comparedKey}}', + ], + ]; + + protected function chooseTemplate(): string + { + return $this->getParam('component') ? self::COMPONENT : self::STANDARD; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/LanguageCodeException.php b/vendor/workerman/validation/library/Exceptions/LanguageCodeException.php new file mode 100644 index 0000000..e066ca3 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/LanguageCodeException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Benevides + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class LanguageCodeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的 ISO 639 {{set}} 语言编码', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的 ISO 639 {{set}} 语言编码', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/LeapDateException.php b/vendor/workerman/validation/library/Exceptions/LeapDateException.php new file mode 100644 index 0000000..d05e976 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/LeapDateException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Benevides + * @author Henrique Moody + */ +final class LeapDateException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是闰日', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是闰日', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/LeapYearException.php b/vendor/workerman/validation/library/Exceptions/LeapYearException.php new file mode 100644 index 0000000..a7b11c5 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/LeapYearException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + */ +final class LeapYearException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是闰年', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是闰年', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/LengthException.php b/vendor/workerman/validation/library/Exceptions/LengthException.php new file mode 100644 index 0000000..e106abe --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/LengthException.php @@ -0,0 +1,70 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Danilo Correa + * @author Henrique Moody + * @author Mazen Touati + */ +final class LengthException extends ValidationException +{ + public const BOTH = 'both'; + public const LOWER = 'lower'; + public const LOWER_INCLUSIVE = 'lower_inclusive'; + public const GREATER = 'greater'; + public const GREATER_INCLUSIVE = 'greater_inclusive'; + public const EXACT = 'exact'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::BOTH => '{{name}} 长度必须在 {{minValue}} 与 {{maxValue}} 之间', + self::LOWER => '{{name}} 长度必须大于 {{minValue}}', + self::LOWER_INCLUSIVE => '{{name}} 的长度必须大于或等于 {{minValue}}', + self::GREATER => '{{name}} 长度必须小于 {{maxValue}}', + self::GREATER_INCLUSIVE => '{{name}} 长度必须小于或等于 {{maxValue}}', + self::EXACT => '{{name}} 长度必须是 {{maxValue}}', + ], + self::MODE_NEGATIVE => [ + self::BOTH => '{{name}} 的长度不能介于 {{minValue}} 和 {{maxValue}} 之间', + self::LOWER => '{{name}} 长度不能大于 {{minValue}}', + self::LOWER_INCLUSIVE => '{{name}} 长度不能大于或等于 {{minValue}}', + self::GREATER => '{{name}} 长度不得小于 {{maxValue}}', + self::GREATER_INCLUSIVE => '{{name}} 长度不能小于或等于 {{maxValue}}', + self::EXACT => '{{name}} 长度不能是 {{maxValue}}', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + $isInclusive = $this->getParam('inclusive'); + + if (!$this->getParam('minValue')) { + return $isInclusive === true ? self::GREATER_INCLUSIVE : self::GREATER; + } + + if (!$this->getParam('maxValue')) { + return $isInclusive === true ? self::LOWER_INCLUSIVE : self::LOWER; + } + + if ($this->getParam('minValue') == $this->getParam('maxValue')) { + return self::EXACT; + } + + return self::BOTH; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/LessThanException.php b/vendor/workerman/validation/library/Exceptions/LessThanException.php new file mode 100644 index 0000000..dce1920 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/LessThanException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class LessThanException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须小于 {{compareTo}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能小于 {{compareTo}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/LowercaseException.php b/vendor/workerman/validation/library/Exceptions/LowercaseException.php new file mode 100644 index 0000000..e715f0a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/LowercaseException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Benevides + * @author Henrique Moody + * @author Jean Pimentel + */ +final class LowercaseException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是小写', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是小写', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/LuhnException.php b/vendor/workerman/validation/library/Exceptions/LuhnException.php new file mode 100644 index 0000000..e332cc5 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/LuhnException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexander Gorshkov + * @author Danilo Correa + * @author Henrique Moody + */ +final class LuhnException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的Luhn编号', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的Luhn编号', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/MacAddressException.php b/vendor/workerman/validation/library/Exceptions/MacAddressException.php new file mode 100644 index 0000000..da4b0d9 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/MacAddressException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Fábio da Silva Ribeiro + * @author Henrique Moody + */ +final class MacAddressException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的MAC地址', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的MAC地址', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/MaxAgeException.php b/vendor/workerman/validation/library/Exceptions/MaxAgeException.php new file mode 100644 index 0000000..c24bc0c --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/MaxAgeException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class MaxAgeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是 {{age}} 年或者更少', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是 {{age}} 年或者更少', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/MaxException.php b/vendor/workerman/validation/library/Exceptions/MaxException.php new file mode 100644 index 0000000..a4976d3 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/MaxException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Andrew Peters + * @author Henrique Moody + */ +final class MaxException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须小于或等于 {{compareTo}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能小于或等于 {{compareTo}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/MimetypeException.php b/vendor/workerman/validation/library/Exceptions/MimetypeException.php new file mode 100644 index 0000000..5a37c26 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/MimetypeException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exception class for Mimetype rule. + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class MimetypeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须具有 {{mimetype}} MIME类型', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能有 {{mimetype}} MIME类型', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/MinAgeException.php b/vendor/workerman/validation/library/Exceptions/MinAgeException.php new file mode 100644 index 0000000..489e339 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/MinAgeException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Emmerson Siqueira + * @author Henrique Moody + * @author Jean Pimentel + */ +final class MinAgeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是 {{age}} 必须是年或以上', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是 {{age}} 年或更长', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/MinException.php b/vendor/workerman/validation/library/Exceptions/MinException.php new file mode 100644 index 0000000..72c4779 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/MinException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class MinException extends ValidationException +{ + public const INCLUSIVE = 'inclusive'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须大于或等于 {{compareTo}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能大于或等于 {{compareTo}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/MultipleException.php b/vendor/workerman/validation/library/Exceptions/MultipleException.php new file mode 100644 index 0000000..fbd27f8 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/MultipleException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Benevides + * @author Henrique Moody + * @author Jean Pimentel + */ +final class MultipleException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是 {{multipleOf}} 的倍数', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是 {{multipleOf}} 的倍数', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/NegativeException.php b/vendor/workerman/validation/library/Exceptions/NegativeException.php new file mode 100644 index 0000000..840d4ba --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NegativeException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Ismael Elias + */ +final class NegativeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须为负数', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能为负数', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/NestedValidationException.php b/vendor/workerman/validation/library/Exceptions/NestedValidationException.php new file mode 100644 index 0000000..460afe9 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NestedValidationException.php @@ -0,0 +1,255 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +use IteratorAggregate; +use RecursiveIteratorIterator; +use SplObjectStorage; + +use function array_shift; +use function count; +use function current; +use function implode; +use function is_array; +use function spl_object_hash; +use function sprintf; +use function str_repeat; + +use const PHP_EOL; + +/** + * Exception for nested validations. + * + * This exception allows to have exceptions inside itself and providers methods + * to handle them and to retrieve nested messages based on itself and its + * children. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Jonathan Stewmon + * @author Wojciech Frącz + */ +class NestedValidationException extends ValidationException implements IteratorAggregate +{ + /** + * @var ValidationException[] + */ + private $exceptions = []; + + /** + * Returns the exceptions that are children of the exception. + * + * @return ValidationException[] + */ + public function getChildren(): array + { + return $this->exceptions; + } + + /** + * Adds a child to the exception. + */ + public function addChild(ValidationException $exception): self + { + $this->exceptions[spl_object_hash($exception)] = $exception; + + return $this; + } + + /** + * Adds children to the exception. + * + * @param ValidationException[] $exceptions + */ + public function addChildren(array $exceptions): self + { + foreach ($exceptions as $exception) { + $this->addChild($exception); + } + + return $this; + } + + /** + * @return SplObjectStorage + */ + public function getIterator(): SplObjectStorage + { + /** @var SplObjectStorage */ + $childrenExceptions = new SplObjectStorage(); + $recursiveIteratorIterator = $this->getRecursiveIterator(); + + $lastDepth = 0; + $lastDepthOriginal = 0; + $knownDepths = []; + foreach ($recursiveIteratorIterator as $childException) { + if ($this->isOmissible($childException)) { + continue; + } + + $currentDepth = $lastDepth; + $currentDepthOriginal = $recursiveIteratorIterator->getDepth() + 1; + + if (isset($knownDepths[$currentDepthOriginal])) { + $currentDepth = $knownDepths[$currentDepthOriginal]; + } elseif ($currentDepthOriginal > $lastDepthOriginal) { + ++$currentDepth; + } + + if (!isset($knownDepths[$currentDepthOriginal])) { + $knownDepths[$currentDepthOriginal] = $currentDepth; + } + + $lastDepth = $currentDepth; + $lastDepthOriginal = $currentDepthOriginal; + + $childrenExceptions->attach($childException, $currentDepth); + } + + return $childrenExceptions; + } + + /** + * Returns a key->value array with all the messages of the exception. + * + * In this array the "keys" are the ids of the exceptions (defined name or + * name of the rule) and the values are the message. + * + * Once templates are passed it overwrites the templates of the given + * messages. + * + * @param string[]|string[][] $templates + * + * @return string[] + */ + public function getMessages(array $templates = []): array + { + $messages = [$this->getId() => $this->renderMessage($this, $templates)]; + foreach ($this->getChildren() as $exception) { + $id = $exception->getId(); + if (!$exception instanceof self) { + $messages[$id] = $this->renderMessage( + $exception, + $this->findTemplates($templates, $this->getId()) + ); + continue; + } + + $messages[$id] = $exception->getMessages($this->findTemplates($templates, $id, $this->getId())); + if (count($messages[$id]) > 1) { + continue; + } + + $messages[$id] = current($messages[$exception->getId()]); + } + + if (count($messages) > 1) { + unset($messages[$this->getId()]); + } + + return $messages; + } + + /** + * Returns a string with all the messages of the exception. + */ + public function getFullMessage(): string + { + $messages = []; + $leveler = 1; + + if (!$this->isOmissible($this)) { + $leveler = 0; + $messages[] = sprintf('- %s', $this->getMessage()); + } + + $exceptions = $this->getIterator(); + /** @var ValidationException $exception */ + foreach ($exceptions as $exception) { + $messages[] = sprintf( + '%s- %s', + str_repeat(' ', (int) ($exceptions[$exception] - $leveler) * 2), + $exception->getMessage() + ); + } + + return implode(PHP_EOL, $messages); + } + + /** + * @param string[] $templates + */ + protected function renderMessage(ValidationException $exception, array $templates): string + { + if (isset($templates[$exception->getId()])) { + $exception->updateTemplate($templates[$exception->getId()]); + } + + return $exception->getMessage(); + } + + /** + * @param string[] $templates + * @param mixed ...$ids + * + * @return string[] + */ + protected function findTemplates(array $templates, ...$ids): array + { + while (count($ids) > 0) { + $id = array_shift($ids); + if (!isset($templates[$id])) { + continue; + } + + if (!is_array($templates[$id])) { + continue; + } + + $templates = $templates[$id]; + } + + return $templates; + } + + /** + * @return RecursiveIteratorIterator + */ + private function getRecursiveIterator(): RecursiveIteratorIterator + { + return new RecursiveIteratorIterator( + new RecursiveExceptionIterator($this), + RecursiveIteratorIterator::SELF_FIRST + ); + } + + private function isOmissible(Exception $exception): bool + { + if (!$exception instanceof self) { + return false; + } + + if (count($exception->getChildren()) !== 1) { + return false; + } + + /** @var ValidationException $childException */ + $childException = current($exception->getChildren()); + if ($childException->getMessage() === $exception->getMessage()) { + return true; + } + + if ($exception->hasCustomTemplate()) { + return $childException->hasCustomTemplate(); + } + + return !$childException instanceof NonOmissibleException; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/NfeAccessKeyException.php b/vendor/workerman/validation/library/Exceptions/NfeAccessKeyException.php new file mode 100644 index 0000000..ec6809e --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NfeAccessKeyException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Andrey Knupp Vital + * @author Danilo Correa + * @author Henrique Moody + */ +final class NfeAccessKeyException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的NFe访问密钥', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的NFe访问密钥', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/NifException.php b/vendor/workerman/validation/library/Exceptions/NifException.php new file mode 100644 index 0000000..7169fe2 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NifException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Julián Gutiérrez + */ +final class NifException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是NIF', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是NIF', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/NipException.php b/vendor/workerman/validation/library/Exceptions/NipException.php new file mode 100644 index 0000000..a444815 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NipException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Tomasz Regdos + */ +final class NipException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的波兰增值税标识号', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的波兰增值税标识号', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/NoException.php b/vendor/workerman/validation/library/Exceptions/NoException.php new file mode 100644 index 0000000..5831c18 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NoException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class NoException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 不被视为 "否"', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 被认为是 "否"', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/NoWhitespaceException.php b/vendor/workerman/validation/library/Exceptions/NoWhitespaceException.php new file mode 100644 index 0000000..dcc9cef --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NoWhitespaceException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + */ +final class NoWhitespaceException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 不能包含空格', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 必须包含空格', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/NonOmissibleException.php b/vendor/workerman/validation/library/Exceptions/NonOmissibleException.php new file mode 100644 index 0000000..adb95f4 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NonOmissibleException.php @@ -0,0 +1,18 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Andy Wendt + * @author Henrique Moody + */ +interface NonOmissibleException extends Exception +{ +} diff --git a/vendor/workerman/validation/library/Exceptions/NoneOfException.php b/vendor/workerman/validation/library/Exceptions/NoneOfException.php new file mode 100644 index 0000000..f737b42 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NoneOfException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class NoneOfException extends NestedValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '这些规则都不能传递给 {{name}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '所有这些规则都必须传递给 {{name}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/NotBlankException.php b/vendor/workerman/validation/library/Exceptions/NotBlankException.php new file mode 100644 index 0000000..27230da --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NotBlankException.php @@ -0,0 +1,45 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + */ +final class NotBlankException extends ValidationException +{ + public const NAMED = 'named'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '值不能为空', + self::NAMED => '{{name}} 不能为空', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '值必须为空', + self::NAMED => '{{name}} 必须为空', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + if ($this->getParam('input') || $this->getParam('name')) { + return self::NAMED; + } + + return self::STANDARD; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/NotEmojiException.php b/vendor/workerman/validation/library/Exceptions/NotEmojiException.php new file mode 100644 index 0000000..43f3237 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NotEmojiException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Mazen Touati + */ +final class NotEmojiException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 不能包含表情符号', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 必须包含表情符号', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/NotEmptyException.php b/vendor/workerman/validation/library/Exceptions/NotEmptyException.php new file mode 100644 index 0000000..cbff8f5 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NotEmptyException.php @@ -0,0 +1,46 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Bram Van der Sype + * @author Henrique Moody + */ +final class NotEmptyException extends ValidationException +{ + public const NAMED = 'named'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '值不能为空', + self::NAMED => '{{name}} 不能为空', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '值必须为空', + self::NAMED => '{{name}} 必须为空', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + if ($this->getParam('input') || $this->getParam('name')) { + return self::NAMED; + } + + return self::STANDARD; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/NotException.php b/vendor/workerman/validation/library/Exceptions/NotException.php new file mode 100644 index 0000000..9763d87 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NotException.php @@ -0,0 +1,18 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class NotException extends GroupedValidationException +{ +} diff --git a/vendor/workerman/validation/library/Exceptions/NotOptionalException.php b/vendor/workerman/validation/library/Exceptions/NotOptionalException.php new file mode 100644 index 0000000..ab2321f --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NotOptionalException.php @@ -0,0 +1,45 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + */ +final class NotOptionalException extends ValidationException +{ + public const NAMED = 'named'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '该值不能是可选的', + self::NAMED => '{{name}} 不能是可选的', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '该值必须是可选的', + self::NAMED => '{{name}} 必须是可选的', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + if ($this->getParam('input') || $this->getParam('name')) { + return self::NAMED; + } + + return self::STANDARD; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/NullTypeException.php b/vendor/workerman/validation/library/Exceptions/NullTypeException.php new file mode 100644 index 0000000..53641e7 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NullTypeException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exception class for NullType. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class NullTypeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须为 null', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能为 null', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/NullableException.php b/vendor/workerman/validation/library/Exceptions/NullableException.php new file mode 100644 index 0000000..803e7b9 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NullableException.php @@ -0,0 +1,45 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Jens Segers + */ +final class NullableException extends ValidationException +{ + public const NAMED = 'named'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '值必须为nullable', + self::NAMED => '{{name}} 必须为nullable', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '值不能为null', + self::NAMED => '{{name}} 不能为null', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + if ($this->getParam('input') || $this->getParam('name')) { + return self::NAMED; + } + + return self::STANDARD; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/NumberException.php b/vendor/workerman/validation/library/Exceptions/NumberException.php new file mode 100644 index 0000000..c52ff86 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NumberException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Ismael Elias + * @author Vitaliy + */ +final class NumberException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是数字', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能为数字', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/NumericValException.php b/vendor/workerman/validation/library/Exceptions/NumericValException.php new file mode 100644 index 0000000..89148af --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/NumericValException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Danilo Correa + * @author Henrique Moody + */ +final class NumericValException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是数字', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是数字', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ObjectTypeException.php b/vendor/workerman/validation/library/Exceptions/ObjectTypeException.php new file mode 100644 index 0000000..a3431ab --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ObjectTypeException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exception class for ObjectType rule. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class ObjectTypeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是object类型', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是object类型', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/OddException.php b/vendor/workerman/validation/library/Exceptions/OddException.php new file mode 100644 index 0000000..60ab77a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/OddException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Benevides + * @author Henrique Moody + * @author Jean Pimentel + */ +final class OddException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是奇数', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是奇数', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/OneOfException.php b/vendor/workerman/validation/library/Exceptions/OneOfException.php new file mode 100644 index 0000000..059ae83 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/OneOfException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Bradyn Poulsen + * @author Henrique Moody + */ +final class OneOfException extends NestedValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '这些规则中只有一个必须传递给 {{name}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '这些规则中只有一个不能传递给 {{name}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/OptionalException.php b/vendor/workerman/validation/library/Exceptions/OptionalException.php new file mode 100644 index 0000000..2ca4333 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/OptionalException.php @@ -0,0 +1,37 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class OptionalException extends ValidationException +{ + public const NAMED = 'named'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '该值必须是可选的', + self::NAMED => '{{name}} 必须是可选的', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '该值不能是可选的', + self::NAMED => '{{name}} 不能是可选的', + ], + ]; + + protected function chooseTemplate(): string + { + return $this->getParam('name') ? self::NAMED : self::STANDARD; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/PerfectSquareException.php b/vendor/workerman/validation/library/Exceptions/PerfectSquareException.php new file mode 100644 index 0000000..c01888a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PerfectSquareException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Benevides + * @author Henrique Moody + * @author Kleber Hamada Sato + */ +final class PerfectSquareException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的完全正方形', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的完全正方形', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/PeselException.php b/vendor/workerman/validation/library/Exceptions/PeselException.php new file mode 100644 index 0000000..4abe05a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PeselException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + * @author Tomasz Regdos + */ +final class PeselException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的比索', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的比索', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/PhoneException.php b/vendor/workerman/validation/library/Exceptions/PhoneException.php new file mode 100644 index 0000000..3d2fd61 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PhoneException.php @@ -0,0 +1,55 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +use Respect\Validation\Helpers\CountryInfo; + +/** + * @author Danilo Correa + * @author Henrique Moody + * @author Michael Firsikov + */ +final class PhoneException extends ValidationException +{ + public const FOR_COUNTRY = 'for_country'; + public const INTERNATIONAL = 'international'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::INTERNATIONAL => '{{name}} 必须是有效的电话号码', + self::FOR_COUNTRY => '{{name}} 必须是 {{countryName}} 的有效电话号码', + ], + self::MODE_NEGATIVE => [ + self::INTERNATIONAL => '{{name}} 不能是有效的电话号码', + self::FOR_COUNTRY => '{{name}} 必须是 {{countryName}} 的有效电话号码', + + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + $countryCode = $this->getParam('countryCode'); + + if (!$countryCode) { + return self::INTERNATIONAL; + } + + $countryInfo = new CountryInfo($countryCode); + $this->setParam('countryName', $countryInfo->getCountry()); + + return self::FOR_COUNTRY; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/PhpLabelException.php b/vendor/workerman/validation/library/Exceptions/PhpLabelException.php new file mode 100644 index 0000000..1b710bc --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PhpLabelException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class PhpLabelException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的PHP标签', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的PHP标签', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/PisException.php b/vendor/workerman/validation/library/Exceptions/PisException.php new file mode 100644 index 0000000..cd6f70f --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PisException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Bruno Koga + * @author Danilo Correa + * @author Henrique Moody + */ +final class PisException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的PIS编号', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的PIS编号', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/PolishIdCardException.php b/vendor/workerman/validation/library/Exceptions/PolishIdCardException.php new file mode 100644 index 0000000..1143754 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PolishIdCardException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class PolishIdCardException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的波兰身份证号码', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的波兰身份证号码r', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/PortugueseNifException.php b/vendor/workerman/validation/library/Exceptions/PortugueseNifException.php new file mode 100644 index 0000000..d3b991d --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PortugueseNifException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Gonçalo Andrade + */ +final class PortugueseNifException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} must be a Portuguese NIF', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} must not be a Portuguese NIF', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/PositiveException.php b/vendor/workerman/validation/library/Exceptions/PositiveException.php new file mode 100644 index 0000000..34d030c --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PositiveException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Ismael Elias + */ +final class PositiveException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须为正', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能为正', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/PostalCodeException.php b/vendor/workerman/validation/library/Exceptions/PostalCodeException.php new file mode 100644 index 0000000..9e69532 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PostalCodeException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class PostalCodeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是 {{countryCode}} 上的有效邮政编码', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是 {{countryCode}} 上的有效邮政编码', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/PrimeNumberException.php b/vendor/workerman/validation/library/Exceptions/PrimeNumberException.php new file mode 100644 index 0000000..f9358ab --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PrimeNumberException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Ismael Elias + * @author Kleber Hamada Sato + */ +final class PrimeNumberException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的质数', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的质数', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/PrintableException.php b/vendor/workerman/validation/library/Exceptions/PrintableException.php new file mode 100644 index 0000000..f6c50e4 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PrintableException.php @@ -0,0 +1,35 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exceptions to be thrown by the Printable Rule. + * + * @author Alexandre Gomes Gaigalas + * @author Andre Ramaciotti + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class PrintableException extends FilteredValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 只能包含可打印字符', + self::EXTRA => '{{name}} 只能包含可打印字符和 "{{additionalChars}}"', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含可打印字符', + self::EXTRA => '{{name}} 不能包含可打印字符或 "{{additionalChars}}"', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/PublicDomainSuffixException.php b/vendor/workerman/validation/library/Exceptions/PublicDomainSuffixException.php new file mode 100644 index 0000000..69e65cf --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PublicDomainSuffixException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +class PublicDomainSuffixException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} must be a public domain suffix', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} must be a public domain suffix', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/PunctException.php b/vendor/workerman/validation/library/Exceptions/PunctException.php new file mode 100644 index 0000000..6e251c9 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/PunctException.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Andre Ramaciotti + * @author Danilo Correa + * @author Henrique Moody + */ +final class PunctException extends FilteredValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 只能包含标点符号', + self::EXTRA => '{{name}} 只能包含标点符号和 {{additionalChars}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含标点符号', + self::EXTRA => '{{name}} 不能包含标点符号或 {{additionalChars}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ReadableException.php b/vendor/workerman/validation/library/Exceptions/ReadableException.php new file mode 100644 index 0000000..85ef1cb --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ReadableException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + */ +final class ReadableException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须可读', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能可读', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/RecursiveExceptionIterator.php b/vendor/workerman/validation/library/Exceptions/RecursiveExceptionIterator.php new file mode 100644 index 0000000..a95ef72 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/RecursiveExceptionIterator.php @@ -0,0 +1,85 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +use ArrayIterator; +use Countable; +use RecursiveIterator; +use UnexpectedValueException; + +/** + * @author Henrique Moody + * + * @implements RecursiveIterator + */ +final class RecursiveExceptionIterator implements RecursiveIterator, Countable +{ + /** + * @var ArrayIterator|ValidationException[] + */ + private $exceptions; + + public function __construct(NestedValidationException $parent) + { + $this->exceptions = new ArrayIterator($parent->getChildren()); + } + + public function count(): int + { + return $this->exceptions->count(); + } + + public function hasChildren(): bool + { + if (!$this->valid()) { + return false; + } + + return $this->current() instanceof NestedValidationException; + } + + public function getChildren(): self + { + $exception = $this->current(); + if (!$exception instanceof NestedValidationException) { + throw new UnexpectedValueException(); + } + + return new static($exception); + } + + /** + * @return ValidationException|NestedValidationException + */ + public function current(): ValidationException + { + return $this->exceptions->current(); + } + + public function key(): int + { + return $this->exceptions->key(); + } + + public function next(): void + { + $this->exceptions->next(); + } + + public function rewind(): void + { + $this->exceptions->rewind(); + } + + public function valid(): bool + { + return $this->exceptions->valid(); + } +} diff --git a/vendor/workerman/validation/library/Exceptions/RegexException.php b/vendor/workerman/validation/library/Exceptions/RegexException.php new file mode 100644 index 0000000..c6f4e28 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/RegexException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Danilo Correa + * @author Henrique Moody + */ +final class RegexException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 对照 {{regex}} 进行验证', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能针对 {{regex}} 进行验证', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ResourceTypeException.php b/vendor/workerman/validation/library/Exceptions/ResourceTypeException.php new file mode 100644 index 0000000..bbbeda6 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ResourceTypeException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exception class for ResourceType. + * + * @author Henrique Moody + */ +final class ResourceTypeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是资源', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是资源', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/RomanException.php b/vendor/workerman/validation/library/Exceptions/RomanException.php new file mode 100644 index 0000000..227d4eb --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/RomanException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Jean Pimentel + */ +final class RomanException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的罗马数字', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的罗马数字', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/ScalarValException.php b/vendor/workerman/validation/library/Exceptions/ScalarValException.php new file mode 100644 index 0000000..622b60d --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ScalarValException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class ScalarValException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是标量值', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是标量值', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/SfException.php b/vendor/workerman/validation/library/Exceptions/SfException.php new file mode 100644 index 0000000..d512265 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/SfException.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class SfException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须对 {{constraint}} 有效', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能对 {{constraint}} 有效', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/SizeException.php b/vendor/workerman/validation/library/Exceptions/SizeException.php new file mode 100644 index 0000000..5f441ff --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/SizeException.php @@ -0,0 +1,54 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exception class for Size rule. + * + * @author Henrique Moody + */ +final class SizeException extends NestedValidationException +{ + public const BOTH = 'both'; + public const LOWER = 'lower'; + public const GREATER = 'greater'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::BOTH => '{{name}} 必须介于 {{minSize}} 和 {{maxSize}} 之间', + self::LOWER => '{{name}} 必须大于 {{minSize}}', + self::GREATER => '{{name}} 必须小于 {{maxSize}}', + ], + self::MODE_NEGATIVE => [ + self::BOTH => '{{name}} 不能介于 {{minSize}} 和 {{maxSize}} 之间', + self::LOWER => '{{name}} 不能大于 {{minSize}}', + self::GREATER => '{{name}} 不能小于 {{maxSize}}', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + if (!$this->getParam('minValue')) { + return self::GREATER; + } + + if (!$this->getParam('maxValue')) { + return self::LOWER; + } + + return self::BOTH; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/SlugException.php b/vendor/workerman/validation/library/Exceptions/SlugException.php new file mode 100644 index 0000000..8b9263a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/SlugException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Carlos André Ferrari + * @author Danilo Correa + * @author Henrique Moody + */ +final class SlugException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的 slug', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的 slug', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/SortedException.php b/vendor/workerman/validation/library/Exceptions/SortedException.php new file mode 100644 index 0000000..16a00a0 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/SortedException.php @@ -0,0 +1,44 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +use Respect\Validation\Rules\Sorted; + +/** + * @author Henrique Moody + * @author Mikhail Vyrtsev + */ +final class SortedException extends ValidationException +{ + public const ASCENDING = 'ascending'; + public const DESCENDING = 'descending'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::ASCENDING => '{{name}} 必须按升序排序', + self::DESCENDING => '{{name}} 必须按降序排序', + ], + self::MODE_NEGATIVE => [ + self::ASCENDING => '{{name}} 不能按升序排序', + self::DESCENDING => '{{name}} 不能按降序排序', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + return $this->getParam('direction') === Sorted::ASCENDING ? self::ASCENDING : self::DESCENDING; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/SpaceException.php b/vendor/workerman/validation/library/Exceptions/SpaceException.php new file mode 100644 index 0000000..b893445 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/SpaceException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Andre Ramaciotti + * @author Henrique Moody + */ +final class SpaceException extends FilteredValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 只能包含空格字符', + self::EXTRA => '{{name}} 只能包含空格字符和 {{additionalChars}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含空格字符', + self::EXTRA => '{{name}} 不能包含空格字符或 {{additionalChars}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/StartsWithException.php b/vendor/workerman/validation/library/Exceptions/StartsWithException.php new file mode 100644 index 0000000..2c3adaf --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/StartsWithException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class StartsWithException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须以 {{startValue}} 开头', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能以 {{startValue}} 开头', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/StringTypeException.php b/vendor/workerman/validation/library/Exceptions/StringTypeException.php new file mode 100644 index 0000000..bf8ac4e --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/StringTypeException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class StringTypeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是string类型', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是string类型', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/StringValException.php b/vendor/workerman/validation/library/Exceptions/StringValException.php new file mode 100644 index 0000000..beca523 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/StringValException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + */ +final class StringValException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是字符串', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是字符串', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/SubdivisionCodeException.php b/vendor/workerman/validation/library/Exceptions/SubdivisionCodeException.php new file mode 100644 index 0000000..3d193b1 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/SubdivisionCodeException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +class SubdivisionCodeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是 {{countryName}} 的细分代码', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是 {{countryName}} 的细分代码', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/SubsetException.php b/vendor/workerman/validation/library/Exceptions/SubsetException.php new file mode 100644 index 0000000..c6a2dba --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/SubsetException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Singwai Chan + */ +final class SubsetException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是 {{superset}} 的子集', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是 {{superset}} 的子集', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/SymbolicLinkException.php b/vendor/workerman/validation/library/Exceptions/SymbolicLinkException.php new file mode 100644 index 0000000..5c33f02 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/SymbolicLinkException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Gus Antoniassi + * @author Henrique Moody + */ +final class SymbolicLinkException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是符号链接', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是符号链接', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/TimeException.php b/vendor/workerman/validation/library/Exceptions/TimeException.php new file mode 100644 index 0000000..27c9b2a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/TimeException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class TimeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是格式为 {{sample}} 的有效时间', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是格式为 {{sample}} 的有效时间', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/TldException.php b/vendor/workerman/validation/library/Exceptions/TldException.php new file mode 100644 index 0000000..439ee95 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/TldException.php @@ -0,0 +1,33 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exceptions thrown by Tld Rule. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Nick Lombard + * @author Paul Karikari + */ +final class TldException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的顶级域名', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的顶级域名', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/TrueValException.php b/vendor/workerman/validation/library/Exceptions/TrueValException.php new file mode 100644 index 0000000..8f676a7 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/TrueValException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exceptions thrown by TrueVal rule. + * + * @author Henrique Moody + * @author Paul Karikari + */ +final class TrueValException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 不被视为 "True"', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 被视为 "True"', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/TypeException.php b/vendor/workerman/validation/library/Exceptions/TypeException.php new file mode 100644 index 0000000..e61462f --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/TypeException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exceptions thrown by Type rule. + * + * @author Henrique Moody + * @author Paul Karikari + */ +final class TypeException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是 {{type}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是 {{type}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/UniqueException.php b/vendor/workerman/validation/library/Exceptions/UniqueException.php new file mode 100644 index 0000000..fa4f41d --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/UniqueException.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exceptions thrown by Unique rule. + * + * @author Henrique Moody + * @author Krzysztof Śmiałek + * @author Paul Karikari + */ +final class UniqueException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 不能包含重复项', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 必须包含重复项', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/UploadedException.php b/vendor/workerman/validation/library/Exceptions/UploadedException.php new file mode 100644 index 0000000..b29d6c2 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/UploadedException.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * Exceptions thrown by Uploaded rule. + * + * @author Fajar Khairil + * @author Henrique Moody + * @author Paul Karikari + */ +final class UploadedException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是上传的文件', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是上传的文件', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/UppercaseException.php b/vendor/workerman/validation/library/Exceptions/UppercaseException.php new file mode 100644 index 0000000..c715a2f --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/UppercaseException.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Benevides + * @author Henrique Moody + * @author Jean Pimentel + */ +final class UppercaseException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须大写', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能大写', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/UrlException.php b/vendor/workerman/validation/library/Exceptions/UrlException.php new file mode 100644 index 0000000..b0f5b19 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/UrlException.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class UrlException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是URL', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是URL', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/UuidException.php b/vendor/workerman/validation/library/Exceptions/UuidException.php new file mode 100644 index 0000000..a893d9b --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/UuidException.php @@ -0,0 +1,46 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Dick van der Heiden + * @author Henrique Moody + * @author Michael Weimann + */ +final class UuidException extends ValidationException +{ + public const VERSION = 'version'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的UUID', + self::VERSION => '{{name}} 必须是有效的UUID版本 {{version}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的UUID', + self::VERSION => '{{name}} 不能是有效的UUID版本 {{version}}', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + if ($this->getParam('version')) { + return self::VERSION; + } + + return self::STANDARD; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/ValidationException.php b/vendor/workerman/validation/library/Exceptions/ValidationException.php new file mode 100644 index 0000000..878ec3d --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ValidationException.php @@ -0,0 +1,158 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +use InvalidArgumentException; +use Respect\Validation\Message\Formatter; + +use function key; + +/** + * Default exception class for rule validations. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +class ValidationException extends InvalidArgumentException implements Exception +{ + public const MODE_DEFAULT = 'default'; + public const MODE_NEGATIVE = 'negative'; + public const STANDARD = 'standard'; + + /** + * Contains the default templates for exception message. + * + * @var string[][] + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须有效', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能有效', + ], + ]; + + /** + * @var mixed + */ + private $input; + + /** + * @var string + */ + private $id; + + /** + * @var string + */ + private $mode = self::MODE_DEFAULT; + + /** + * @var mixed[] + */ + private $params = []; + + /** + * @var Formatter + */ + private $formatter; + + /** + * @var string + */ + private $template; + + /** + * @param mixed $input + * @param mixed[] $params + */ + public function __construct($input, string $id, array $params, Formatter $formatter) + { + $this->input = $input; + $this->id = $id; + $this->params = $params; + $this->formatter = $formatter; + $this->template = $this->chooseTemplate(); + + parent::__construct($this->createMessage()); + } + + public function getId(): string + { + return $this->id; + } + + /** + * @return mixed[] + */ + public function getParams(): array + { + return $this->params; + } + + /** + * @return mixed|null + */ + public function getParam(string $name) + { + return $this->params[$name] ?? null; + } + + public function setParam(string $name, mixed $value): void + { + $this->params[$name] = $value; + } + + public function updateMode(string $mode): void + { + $this->mode = $mode; + $this->message = $this->createMessage(); + } + + public function updateTemplate(string $template): void + { + $this->template = $template; + $this->message = $this->createMessage(); + } + + /** + * @param mixed[] $params + */ + public function updateParams(array $params): void + { + $this->params = $params; + $this->message = $this->createMessage(); + } + + public function hasCustomTemplate(): bool + { + return isset($this->defaultTemplates[$this->mode][$this->template]) === false; + } + + protected function chooseTemplate(): string + { + return (string) key($this->defaultTemplates[$this->mode]); + } + + private function createMessage(): string + { + return $this->formatter->format( + $this->defaultTemplates[$this->mode][$this->template] ?? $this->template, + $this->input, + $this->params + ); + } + + public function __toString(): string + { + return $this->getMessage(); + } +} diff --git a/vendor/workerman/validation/library/Exceptions/ValidatorException.php b/vendor/workerman/validation/library/Exceptions/ValidatorException.php new file mode 100644 index 0000000..7a27c39 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/ValidatorException.php @@ -0,0 +1,17 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + */ +final class ValidatorException extends AllOfException +{ +} diff --git a/vendor/workerman/validation/library/Exceptions/VersionException.php b/vendor/workerman/validation/library/Exceptions/VersionException.php new file mode 100644 index 0000000..954687c --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/VersionException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + */ +final class VersionException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是版本', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是版本', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/VideoUrlException.php b/vendor/workerman/validation/library/Exceptions/VideoUrlException.php new file mode 100644 index 0000000..7135224 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/VideoUrlException.php @@ -0,0 +1,46 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + * @author Ricardo Gobbo + */ +final class VideoUrlException extends ValidationException +{ + public const SERVICE = 'service'; + + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是有效的视频URL', + self::SERVICE => '{{name}} 必须是有效的 {{service}} 视频URL', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是有效的视频URL', + self::SERVICE => '{{name}} 不能是有效的 {{service}} 视频URL', + ], + ]; + + /** + * {@inheritDoc} + */ + protected function chooseTemplate(): string + { + if ($this->getParam('service')) { + return self::SERVICE; + } + + return self::STANDARD; + } +} diff --git a/vendor/workerman/validation/library/Exceptions/VowelException.php b/vendor/workerman/validation/library/Exceptions/VowelException.php new file mode 100644 index 0000000..45186a3 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/VowelException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Henrique Moody + * @author Kleber Hamada Sato + */ +final class VowelException extends FilteredValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 只能包含元音', + self::EXTRA => '{{name}} 只能包含元音和 {{additionalChars}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含元音', + self::EXTRA => '{{name}} 不能包含元音或 {{additionalChars}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/WhenException.php b/vendor/workerman/validation/library/Exceptions/WhenException.php new file mode 100644 index 0000000..76d1812 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/WhenException.php @@ -0,0 +1,19 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Antonio Spinelli + * @author Danilo Correa + * @author Henrique Moody + */ +final class WhenException extends ValidationException +{ +} diff --git a/vendor/workerman/validation/library/Exceptions/WritableException.php b/vendor/workerman/validation/library/Exceptions/WritableException.php new file mode 100644 index 0000000..1680490 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/WritableException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Danilo Correa + * @author Henrique Moody + */ +final class WritableException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 必须是可写的', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能是可写的', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/XdigitException.php b/vendor/workerman/validation/library/Exceptions/XdigitException.php new file mode 100644 index 0000000..f0142c8 --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/XdigitException.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Andre Ramaciotti + * @author Henrique Moody + */ +final class XdigitException extends FilteredValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 只能包含十六进制数字', + self::EXTRA => '{{name}} 只能包含十六进制数字和 {{additionalChars}}', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 不能包含十六进制数字', + self::EXTRA => '{{name}} 不能包含十六进制数字或 {{additionalChars}}', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Exceptions/YesException.php b/vendor/workerman/validation/library/Exceptions/YesException.php new file mode 100644 index 0000000..ede9b1a --- /dev/null +++ b/vendor/workerman/validation/library/Exceptions/YesException.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Exceptions; + +/** + * @author Cameron Hall + * @author Henrique Moody + */ +final class YesException extends ValidationException +{ + /** + * {@inheritDoc} + */ + protected $defaultTemplates = [ + self::MODE_DEFAULT => [ + self::STANDARD => '{{name}} 不被认为是 "Yes"', + ], + self::MODE_NEGATIVE => [ + self::STANDARD => '{{name}} 被认为是 "Yes"', + ], + ]; +} diff --git a/vendor/workerman/validation/library/Factory.php b/vendor/workerman/validation/library/Factory.php new file mode 100644 index 0000000..5260556 --- /dev/null +++ b/vendor/workerman/validation/library/Factory.php @@ -0,0 +1,266 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation; + +use ReflectionClass; +use ReflectionException; +use ReflectionObject; +use Respect\Validation\Exceptions\ComponentException; +use Respect\Validation\Exceptions\InvalidClassException; +use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\Message\Formatter; +use Respect\Validation\Message\ParameterStringifier; +use Respect\Validation\Message\Stringifier\KeepOriginalStringName; + +use function array_merge; +use function lcfirst; +use function sprintf; +use function str_replace; +use function trim; +use function ucfirst; + +/** + * Factory of objects. + * + * @author Augusto Pascutti + * @author Henrique Moody + */ +final class Factory +{ + /** + * @var string[] + */ + private $rulesNamespaces = ['Respect\\Validation\\Rules']; + + /** + * @var string[] + */ + private $exceptionsNamespaces = ['Respect\\Validation\\Exceptions']; + + /** + * @var callable + */ + private $translator = 'strval'; + + /** + * @var ParameterStringifier + */ + private $parameterStringifier; + + /** + * Default instance of the Factory. + * + * @var Factory + */ + private static $defaultInstance; + + public function __construct() + { + $this->parameterStringifier = new KeepOriginalStringName(); + } + + /** + * Returns the default instance of the Factory. + */ + public static function getDefaultInstance(): self + { + if (self::$defaultInstance === null) { + self::$defaultInstance = new self(); + } + + return self::$defaultInstance; + } + + public function withRuleNamespace(string $rulesNamespace): self + { + $clone = clone $this; + $clone->rulesNamespaces[] = trim($rulesNamespace, '\\'); + + return $clone; + } + + public function withExceptionNamespace(string $exceptionsNamespace): self + { + $clone = clone $this; + $clone->exceptionsNamespaces[] = trim($exceptionsNamespace, '\\'); + + return $clone; + } + + public function withTranslator(callable $translator): self + { + $clone = clone $this; + $clone->translator = $translator; + + return $clone; + } + + public function withParameterStringifier(ParameterStringifier $parameterStringifier): self + { + $clone = clone $this; + $clone->parameterStringifier = $parameterStringifier; + + return $clone; + } + + /** + * Creates a rule. + * + * @param mixed[] $arguments + * + * @throws ComponentException + */ + public function rule(string $ruleName, array $arguments = []): Validatable + { + foreach ($this->rulesNamespaces as $namespace) { + try { + /** @var class-string $name */ + $name = $namespace . '\\' . ucfirst($ruleName); + /** @var Validatable $rule */ + $rule = $this + ->createReflectionClass($name, Validatable::class) + ->newInstanceArgs($arguments); + + return $rule; + } catch (ReflectionException $exception) { + continue; + } + } + + throw new ComponentException(sprintf('"%s" is not a valid rule name', $ruleName)); + } + + /** + * Creates an exception. + * + * @param mixed $input + * @param mixed[] $extraParams + * + * @throws ComponentException + */ + public function exception(Validatable $validatable, $input, array $extraParams = []): ValidationException + { + $formatter = new Formatter($this->translator, $this->parameterStringifier); + $reflection = new ReflectionObject($validatable); + $ruleName = $reflection->getShortName(); + $params = ['input' => $input] + $extraParams + $this->extractPropertiesValues($validatable, $reflection); + $id = lcfirst($ruleName); + if ($validatable->getName() !== null) { + /*$id = */$params['name'] = $validatable->getName(); + } + $exceptionNamespace = str_replace('\\Rules', '\\Exceptions', $reflection->getNamespaceName()); + foreach (array_merge([$exceptionNamespace], $this->exceptionsNamespaces) as $namespace) { + try { + /** @var class-string $exceptionName */ + $exceptionName = $namespace . '\\' . $ruleName . 'Exception'; + + return $this->createValidationException( + $exceptionName, + $id, + $input, + $params, + $formatter + ); + } catch (ReflectionException $exception) { + continue; + } + } + + return new ValidationException($input, $id, $params, $formatter); + } + + /** + * Define the default instance of the Factory. + */ + public static function setDefaultInstance(self $defaultInstance): void + { + self::$defaultInstance = $defaultInstance; + } + + /** + * Creates a reflection based on class name. + * + * @param class-string $name + * @param class-string $parentName + * + * @throws InvalidClassException + * @throws ReflectionException + * + * @return ReflectionClass + */ + private function createReflectionClass(string $name, string $parentName): ReflectionClass + { + $reflection = new ReflectionClass($name); + if (!$reflection->isSubclassOf($parentName) && $parentName !== $name) { + throw new InvalidClassException(sprintf('"%s" must be an instance of "%s"', $name, $parentName)); + } + + if (!$reflection->isInstantiable()) { + throw new InvalidClassException(sprintf('"%s" must be instantiable', $name)); + } + + return $reflection; + } + + /** + * Creates a Validation exception. + * + * @param class-string $exceptionName + * + * @param mixed $input + * @param mixed[] $params + * + * @throws InvalidClassException + * @throws ReflectionException + */ + private function createValidationException( + string $exceptionName, + string $id, + $input, + array $params, + Formatter $formatter + ): ValidationException { + /** @var ValidationException $exception */ + $exception = $this + ->createReflectionClass($exceptionName, ValidationException::class) + ->newInstance($input, $id, $params, $formatter); + if (isset($params['template'])) { + $exception->updateTemplate($params['template']); + } + + return $exception; + } + + /** + * @param ReflectionObject|ReflectionClass $reflection + * @return mixed[] + */ + private function extractPropertiesValues(Validatable $validatable, ReflectionClass $reflection): array + { + $values = []; + foreach ($reflection->getProperties() as $property) { + $property->setAccessible(true); + + $propertyValue = $property->getValue($validatable); + if ($propertyValue === null) { + continue; + } + + $values[$property->getName()] = $propertyValue; + } + + $parentReflection = $reflection->getParentClass(); + if ($parentReflection !== false) { + return $values + $this->extractPropertiesValues($validatable, $parentReflection); + } + + return $values; + } +} diff --git a/vendor/workerman/validation/library/Helpers/CanCompareValues.php b/vendor/workerman/validation/library/Helpers/CanCompareValues.php new file mode 100644 index 0000000..28dd8fa --- /dev/null +++ b/vendor/workerman/validation/library/Helpers/CanCompareValues.php @@ -0,0 +1,68 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Helpers; + +use Countable; +use DateTimeImmutable; +use DateTimeInterface; +use Throwable; + +use function is_numeric; +use function is_scalar; +use function is_string; +use function mb_strlen; + +/** + * Helps to deal with comparable values. + * + * @author Emmerson Siqueira + * @author Henrique Moody + */ +trait CanCompareValues +{ + /** + * Tries to convert a value into something that can be compared with PHP operators. + * + * @param mixed $value + * + * @return mixed + */ + private function toComparable($value) + { + if ($value instanceof Countable) { + return $value->count(); + } + + if ($value instanceof DateTimeInterface || !is_string($value) || is_numeric($value) || empty($value)) { + return $value; + } + + if (mb_strlen($value) === 1) { + return $value; + } + + try { + return new DateTimeImmutable($value); + } catch (Throwable $e) { + return $value; + } + } + + /** + * Returns whether the values can be compared or not. + * + * @param mixed $left + * @param mixed $right + */ + private function isAbleToCompareValues($left, $right): bool + { + return is_scalar($left) === is_scalar($right); + } +} diff --git a/vendor/workerman/validation/library/Helpers/CanValidateDateTime.php b/vendor/workerman/validation/library/Helpers/CanValidateDateTime.php new file mode 100644 index 0000000..306f419 --- /dev/null +++ b/vendor/workerman/validation/library/Helpers/CanValidateDateTime.php @@ -0,0 +1,86 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Helpers; + +use DateTime; +use DateTimeZone; + +use function checkdate; +use function date_default_timezone_get; +use function date_parse_from_format; +use function preg_match; + +/** + * Helper to handle date/time. + * + * @author Henrique Moody + */ +trait CanValidateDateTime +{ + /** + * Finds whether a value is a valid date/time in a specific format. + */ + private function isDateTime(string $format, string $value): bool + { + $exceptionalFormats = [ + 'c' => 'Y-m-d\TH:i:sP', + 'r' => 'D, d M Y H:i:s O', + ]; + + $format = $exceptionalFormats[$format] ?? $format; + + $info = date_parse_from_format($format, $value); + + if (!$this->isDateTimeParsable($info)) { + return false; + } + + if ($this->isDateFormat($format)) { + $formattedDate = DateTime::createFromFormat( + $format, + $value, + new DateTimeZone(date_default_timezone_get()) + ); + + if ($formattedDate === false || $value !== $formattedDate->format($format)) { + return false; + } + + return $this->isDateInformation($info); + } + + return true; + } + + /** + * @param mixed[] $info + */ + private function isDateTimeParsable(array $info): bool + { + return $info['error_count'] === 0 && $info['warning_count'] === 0; + } + + private function isDateFormat(string $format): bool + { + return preg_match('/[djSFmMnYy]/', $format) > 0; + } + + /** + * @param mixed[] $info + */ + private function isDateInformation(array $info): bool + { + if ($info['day']) { + return checkdate((int) $info['month'], $info['day'], (int) $info['year']); + } + + return checkdate($info['month'] ?: 1, 1, $info['year'] ?: 1); + } +} diff --git a/vendor/workerman/validation/library/Helpers/CanValidateIterable.php b/vendor/workerman/validation/library/Helpers/CanValidateIterable.php new file mode 100644 index 0000000..0de2c4e --- /dev/null +++ b/vendor/workerman/validation/library/Helpers/CanValidateIterable.php @@ -0,0 +1,33 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Helpers; + +use stdClass; +use Traversable; + +use function is_array; + +/** + * Helper to handle iterable values. + * + * @author Henrique Moody + */ +trait CanValidateIterable +{ + /** + * Returns whether the value is iterable or not. + * + * @param mixed $value + */ + public function isIterable($value): bool + { + return is_array($value) || $value instanceof stdClass || $value instanceof Traversable; + } +} diff --git a/vendor/workerman/validation/library/Helpers/CanValidateUndefined.php b/vendor/workerman/validation/library/Helpers/CanValidateUndefined.php new file mode 100644 index 0000000..019d089 --- /dev/null +++ b/vendor/workerman/validation/library/Helpers/CanValidateUndefined.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Helpers; + +use function in_array; + +/** + * Helper to identify values that Validation consider as "undefined". + * + * @author Henrique Moody + */ +trait CanValidateUndefined +{ + /** + * Finds whether the value is undefined or not. + * + * @param mixed $value + */ + private function isUndefined($value): bool + { + return in_array($value, [null, ''], true); + } +} diff --git a/vendor/workerman/validation/library/Helpers/CountryInfo.php b/vendor/workerman/validation/library/Helpers/CountryInfo.php new file mode 100644 index 0000000..094fcae --- /dev/null +++ b/vendor/workerman/validation/library/Helpers/CountryInfo.php @@ -0,0 +1,54 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Helpers; + +use Respect\Validation\Exceptions\ComponentException; + +use function file_exists; +use function sprintf; + +final class CountryInfo +{ + /** + * @var mixed[] + */ + private $data; + + /** + * @var mixed[] + */ + private static $runtimeCache = []; + + public function __construct(string $countryCode) + { + if (!isset(static::$runtimeCache[$countryCode])) { + $filename = __DIR__ . '/../../data/iso_3166-2/' . $countryCode . '.php'; + if (!file_exists($filename)) { + throw new ComponentException(sprintf('"%s" is not a supported country code', $countryCode)); + } + static::$runtimeCache[$countryCode] = require $filename; + } + + $this->data = static::$runtimeCache[$countryCode]; + } + + public function getCountry(): string + { + return $this->data['country']; + } + + /** + * @return string[] + */ + public function getSubdivisions(): array + { + return $this->data['subdivisions']; + } +} diff --git a/vendor/workerman/validation/library/Helpers/DomainInfo.php b/vendor/workerman/validation/library/Helpers/DomainInfo.php new file mode 100644 index 0000000..502da83 --- /dev/null +++ b/vendor/workerman/validation/library/Helpers/DomainInfo.php @@ -0,0 +1,46 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Helpers; + +use function file_exists; +use function mb_strtoupper; + +final class DomainInfo +{ + /** + * @var mixed[] + */ + private $data; + + /** + * @var mixed[] + */ + private static $runtimeCache = []; + + public function __construct(string $tld) + { + $tld = mb_strtoupper($tld); + + if (!isset(static::$runtimeCache[$tld])) { + $filename = __DIR__ . '/../../data/domain/public-suffix/' . $tld . '.php'; + static::$runtimeCache[$tld] = file_exists($filename) ? require $filename : []; + } + + $this->data = static::$runtimeCache[$tld]; + } + + /** + * @return array + */ + public function getPublicSuffixes(): array + { + return $this->data; + } +} diff --git a/vendor/workerman/validation/library/Message/Formatter.php b/vendor/workerman/validation/library/Message/Formatter.php new file mode 100644 index 0000000..e35d1a2 --- /dev/null +++ b/vendor/workerman/validation/library/Message/Formatter.php @@ -0,0 +1,54 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Message; + +use function call_user_func; +use function preg_replace_callback; +use function Respect\Stringifier\stringify; + +final class Formatter +{ + /** + * @var callable + */ + private $translator; + + /** + * @var ParameterStringifier + */ + private $parameterStringifier; + + public function __construct(callable $translator, ParameterStringifier $parameterStringifier) + { + $this->translator = $translator; + $this->parameterStringifier = $parameterStringifier; + } + + /** + * @param mixed $input + * @param mixed[] $parameters + */ + public function format(string $template, $input, array $parameters): string + { + $parameters['name'] = $parameters['name'] ?? stringify($input); + + return preg_replace_callback( + '/{{(\w+)}}/', + function ($match) use ($parameters) { + if (!isset($parameters[$match[1]])) { + return $match[0]; + } + + return $this->parameterStringifier->stringify($match[1], $parameters[$match[1]]); + }, + call_user_func($this->translator, $template) + ); + } +} diff --git a/vendor/workerman/validation/library/Message/ParameterStringifier.php b/vendor/workerman/validation/library/Message/ParameterStringifier.php new file mode 100644 index 0000000..90fb60b --- /dev/null +++ b/vendor/workerman/validation/library/Message/ParameterStringifier.php @@ -0,0 +1,18 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Message; + +interface ParameterStringifier +{ + /** + * @param mixed $value + */ + public function stringify(string $name, $value): string; +} diff --git a/vendor/workerman/validation/library/Message/Stringifier/KeepOriginalStringName.php b/vendor/workerman/validation/library/Message/Stringifier/KeepOriginalStringName.php new file mode 100644 index 0000000..808d224 --- /dev/null +++ b/vendor/workerman/validation/library/Message/Stringifier/KeepOriginalStringName.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Message\Stringifier; + +use Respect\Validation\Message\ParameterStringifier; + +use function is_string; +use function Respect\Stringifier\stringify; + +final class KeepOriginalStringName implements ParameterStringifier +{ + /** + * {@inheritDoc} + */ + public function stringify(string $name, $value): string + { + if ($name === 'name' && is_string($value)) { + return $value; + } + + return stringify($value); + } +} diff --git a/vendor/workerman/validation/library/NonNegatable.php b/vendor/workerman/validation/library/NonNegatable.php new file mode 100644 index 0000000..7fa5626 --- /dev/null +++ b/vendor/workerman/validation/library/NonNegatable.php @@ -0,0 +1,18 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation; + +/** Interface for validation rules */ +/** + * @author Alexandre Gomes Gaigalas + */ +interface NonNegatable +{ +} diff --git a/vendor/workerman/validation/library/Rules/AbstractAge.php b/vendor/workerman/validation/library/Rules/AbstractAge.php new file mode 100644 index 0000000..d8a7886 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AbstractAge.php @@ -0,0 +1,98 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Helpers\CanValidateDateTime; + +use function date; +use function date_parse_from_format; +use function is_scalar; +use function strtotime; +use function vsprintf; + +/** + * Abstract class to validate ages. + * + * @author Henrique Moody + */ +abstract class AbstractAge extends AbstractRule +{ + use CanValidateDateTime; + + /** + * @var int + */ + private $age; + + /** + * @var string|null + */ + private $format; + + /** + * @var int + */ + private $baseDate; + + /** + * Should compare the current base date with the given one. + * + * The dates are represented as integers in the format "Ymd". + */ + abstract protected function compare(int $baseDate, int $givenDate): bool; + + /** + * Initializes the rule. + */ + public function __construct(int $age, ?string $format = null) + { + $this->age = $age; + $this->format = $format; + $this->baseDate = (int) date('Ymd') - $this->age * 10000; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + if ($this->format === null) { + return $this->isValidWithoutFormat((string) $input); + } + + return $this->isValidWithFormat($this->format, (string) $input); + } + + private function isValidWithoutFormat(string $dateTime): bool + { + $timestamp = strtotime($dateTime); + if ($timestamp === false) { + return false; + } + + return $this->compare($this->baseDate, (int) date('Ymd', $timestamp)); + } + + private function isValidWithFormat(string $format, string $dateTime): bool + { + if (!$this->isDateTime($format, $dateTime)) { + return false; + } + + return $this->compare( + $this->baseDate, + (int) vsprintf('%d%02d%02d', date_parse_from_format($format, $dateTime)) + ); + } +} diff --git a/vendor/workerman/validation/library/Rules/AbstractComparison.php b/vendor/workerman/validation/library/Rules/AbstractComparison.php new file mode 100644 index 0000000..c65deb9 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AbstractComparison.php @@ -0,0 +1,60 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Helpers\CanCompareValues; + +/** + * Abstract class to help on creating rules that compare value. + * + * @author Henrique Moody + */ +abstract class AbstractComparison extends AbstractRule +{ + use CanCompareValues; + + /** + * @var mixed + */ + private $compareTo; + + /** + * Compare both values and return whether the comparison is valid or not. + * + * @param mixed $left + * @param mixed $right + */ + abstract protected function compare($left, $right): bool; + + /** + * Initializes the rule by setting the value to be compared to the input. + * + * @param mixed $maxValue + */ + public function __construct($maxValue) + { + $this->compareTo = $maxValue; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + $left = $this->toComparable($input); + $right = $this->toComparable($this->compareTo); + + if (!$this->isAbleToCompareValues($left, $right)) { + return false; + } + + return $this->compare($left, $right); + } +} diff --git a/vendor/workerman/validation/library/Rules/AbstractComposite.php b/vendor/workerman/validation/library/Rules/AbstractComposite.php new file mode 100644 index 0000000..bd8126b --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AbstractComposite.php @@ -0,0 +1,150 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\NestedValidationException; +use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\Validatable; + +use function array_filter; +use function array_map; + +/** + * Abstract class for rules that are composed by other rules. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Wojciech Frącz + */ +abstract class AbstractComposite extends AbstractRule +{ + /** + * @var Validatable[] + */ + private $rules = []; + + /** + * Initializes the rule adding other rules to the stack. + */ + public function __construct(Validatable ...$rules) + { + $this->rules = $rules; + } + + /** + * {@inheritDoc} + */ + public function setName(string $name): Validatable + { + $parentName = $this->getName(); + foreach ($this->rules as $rule) { + $ruleName = $rule->getName(); + if ($ruleName && $parentName !== $ruleName) { + continue; + } + + $rule->setName($name); + } + + return parent::setName($name); + } + + public function setDefault(string $default, bool $defaultType=false): Validatable + { + $parentDefault = $this->getDefault(); + foreach ($this->rules as $rule) { + $ruleDefault = $rule->getDefault(); + if ($ruleDefault && $parentDefault !== $ruleDefault) { + continue; + } + $rule->setDefault($default, $defaultType); + } + return parent::setDefault($default, $defaultType); + } + /** + * Append a rule into the stack of rules. + * + * @return AbstractComposite + */ + public function addRule(Validatable $rule): self + { + if ($this->shouldHaveNameOverwritten($rule) && $this->getName() !== null) { + $rule->setName($this->getName()); + } + + $this->rules[] = $rule; + + return $this; + } + + /** + * Returns all the rules in the stack. + * + * @return Validatable[] + */ + public function getRules(): array + { + return $this->rules; + } + + /** + * Returns all the exceptions throw when asserting all rules. + * + * @param mixed $input + * + * @return ValidationException[] + */ + protected function getAllThrownExceptions($input): array + { + return array_filter( + array_map( + function (Validatable $rule) use ($input): ?ValidationException { + try { + $rule->assert($input); + } catch (ValidationException $exception) { + $this->updateExceptionTemplate($exception); + + return $exception; + } + + return null; + }, + $this->getRules() + ) + ); + } + + private function shouldHaveNameOverwritten(Validatable $rule): bool + { + return $this->hasName($this) && !$this->hasName($rule); + } + + private function hasName(Validatable $rule): bool + { + return $rule->getName() !== null; + } + + private function updateExceptionTemplate(ValidationException $exception): void + { + if ($this->template === null || $exception->hasCustomTemplate()) { + return; + } + + $exception->updateTemplate($this->template); + + if (!$exception instanceof NestedValidationException) { + return; + } + + foreach ($exception->getChildren() as $childException) { + $this->updateExceptionTemplate($childException); + } + } +} diff --git a/vendor/workerman/validation/library/Rules/AbstractEnvelope.php b/vendor/workerman/validation/library/Rules/AbstractEnvelope.php new file mode 100644 index 0000000..650e48e --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AbstractEnvelope.php @@ -0,0 +1,61 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\Validatable; + +/** + * Abstract class that creates an envelope around another rule. + * + * This class is usefull when you want to create rules that use other rules, but + * having an custom message. + * + * @author Henrique Moody + */ +abstract class AbstractEnvelope extends AbstractRule +{ + /** + * @var Validatable + */ + private $validatable; + + /** + * @var mixed[] + */ + private $parameters; + + /** + * Initializes the rule. + * + * @param mixed[] $parameters + */ + public function __construct(Validatable $validatable, array $parameters = []) + { + $this->validatable = $validatable; + $this->parameters = $parameters; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return $this->validatable->validate($input); + } + + /** + * {@inheritDoc} + */ + public function reportError($input, array $extraParameters = []): ValidationException + { + return parent::reportError($input, $extraParameters + $this->parameters); + } +} diff --git a/vendor/workerman/validation/library/Rules/AbstractFilterRule.php b/vendor/workerman/validation/library/Rules/AbstractFilterRule.php new file mode 100644 index 0000000..2c658a0 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AbstractFilterRule.php @@ -0,0 +1,61 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function implode; +use function is_scalar; +use function str_replace; +use function str_split; + +/** + * @author Henrique Moody + * @author Nick Lombard + */ +abstract class AbstractFilterRule extends AbstractRule +{ + /** + * @var string + */ + private $additionalChars; + + abstract protected function validateFilteredInput(string $input): bool; + + /** + * Initializes the rule with a list of characters to be ignored by the validation. + */ + public function __construct(string ...$additionalChars) + { + $this->additionalChars = implode($additionalChars); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + $stringInput = (string) $input; + if ($stringInput === '') { + return false; + } + + $filteredInput = $this->filter($stringInput); + + return $filteredInput === '' || $this->validateFilteredInput($filteredInput); + } + + private function filter(string $input): string + { + return str_replace(str_split($this->additionalChars), '', $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/AbstractRelated.php b/vendor/workerman/validation/library/Rules/AbstractRelated.php new file mode 100644 index 0000000..8767552 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AbstractRelated.php @@ -0,0 +1,154 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\NestedValidationException; +use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\Validatable; + +use function is_scalar; + +/** + * @author Alexandre Gomes Gaigalas + * @author Emmerson Siqueira + * @author Henrique Moody + * @author Nick Lombard + */ +abstract class AbstractRelated extends AbstractRule +{ + /** + * @var bool + */ + private $mandatory = true; + + /** + * @var mixed + */ + private $reference; + + /** + * @var Validatable|null + */ + private $rule; + + /** + * @param mixed $input + */ + abstract public function hasReference($input): bool; + + /** + * @param mixed $input + * + * @return mixed + */ + abstract public function getReferenceValue($input); + + /** + * @param mixed $reference + */ + public function __construct($reference, ?Validatable $rule = null, bool $mandatory = true) + { + $this->reference = $reference; + $this->rule = $rule; + $this->mandatory = $mandatory; + + if ($rule && $rule->getName() !== null) { + $this->setName($rule->getName()); + } elseif (is_scalar($reference)) { + $this->setName((string) $reference); + } + } + + /** + * @return mixed + */ + public function getReference() + { + return $this->reference; + } + + public function isMandatory(): bool + { + return $this->mandatory; + } + + /** + * {@inheritDoc} + */ + public function setName(string $name): Validatable + { + parent::setName($name); + + if ($this->rule instanceof Validatable) { + $this->rule->setName($name); + } + + return $this; + } + + /** + * {@inheritDoc} + */ + public function assert($input): void + { + $hasReference = $this->hasReference($input); + if ($this->mandatory && !$hasReference) { + throw $this->reportError($input, ['hasReference' => false]); + } + + if ($this->rule === null || !$hasReference) { + return; + } + + try { + $this->rule->assert($this->getReferenceValue($input)); + } catch (ValidationException $validationException) { + /** @var NestedValidationException $nestedValidationException */ + $nestedValidationException = $this->reportError($this->reference, ['hasReference' => true]); + $nestedValidationException->addChild($validationException); + + throw $nestedValidationException; + } + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + $hasReference = $this->hasReference($input); + if ($this->mandatory && !$hasReference) { + throw $this->reportError($input, ['hasReference' => false]); + } + + if ($this->rule === null || !$hasReference) { + return; + } + + $this->rule->check($this->getReferenceValue($input)); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + $hasReference = $this->hasReference($input); + if ($this->mandatory && !$hasReference) { + return false; + } + + if ($this->rule === null || !$hasReference) { + return true; + } + + return $this->rule->validate($this->getReferenceValue($input)); + } +} diff --git a/vendor/workerman/validation/library/Rules/AbstractRule.php b/vendor/workerman/validation/library/Rules/AbstractRule.php new file mode 100644 index 0000000..4211df2 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AbstractRule.php @@ -0,0 +1,126 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\Factory; +use Respect\Validation\Validatable; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Nick Lombard + * @author Vicente Mendoza + */ +abstract class AbstractRule implements Validatable +{ + /** + * @var string|null + */ + protected $default; + /** + * @var bool|false + */ + protected $defaultType; + /** + * @var string|null + */ + protected $name; + + /** + * @var string|null + */ + protected $template; + + /** + * {@inheritDoc} + */ + public function assert($input): void + { + if ($this->validate($input)) { + return; + } + + throw $this->reportError($input); + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + $this->assert($input); + } + + /** + * {@inheritDoc} + */ + public function getName(): ?string + { + return $this->name; + } + + /** + * {@inheritDoc} + */ + public function reportError($input, array $extraParams = []): ValidationException + { + return Factory::getDefaultInstance()->exception($this, $input, $extraParams); + } + + /** + * {@inheritDoc} + */ + public function setName(string $name): Validatable + { + $this->name = $name; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setTemplate(string $template): Validatable + { + $this->template = $template; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function getDefault(): ?string + { + return $this->default; + } + /** + * {@inheritDoc} + */ + public function getDefaultType(): ?bool + { + return $this->defaultType; + } + public function setDefault(string $default, bool $defaultType=false): Validatable + { + $this->default = $default; + $this->defaultType = $defaultType; + return $this; + } + + /** + * @param mixed$input + */ + public function __invoke($input): bool + { + return $this->validate($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/AbstractSearcher.php b/vendor/workerman/validation/library/Rules/AbstractSearcher.php new file mode 100644 index 0000000..f73d963 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AbstractSearcher.php @@ -0,0 +1,50 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Helpers\CanValidateUndefined; + +use function in_array; +use function is_scalar; +use function mb_strtoupper; + +/** + * Abstract class for searches into arrays. + * + * @author Henrique Moody + */ +abstract class AbstractSearcher extends AbstractRule +{ + use CanValidateUndefined; + + /** + * @param mixed $input + * @return mixed[] + */ + abstract protected function getDataSource($input = null): array; + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + $dataSource = $this->getDataSource($input); + + if ($this->isUndefined($input) && empty($dataSource)) { + return true; + } + + if (!is_scalar($input)) { + return false; + } + + return in_array(mb_strtoupper((string) $input), $dataSource, true); + } +} diff --git a/vendor/workerman/validation/library/Rules/AbstractWrapper.php b/vendor/workerman/validation/library/Rules/AbstractWrapper.php new file mode 100644 index 0000000..75a5738 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AbstractWrapper.php @@ -0,0 +1,68 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Validatable; + +/** + * Abstract class to help on creating rules that wrap rules. + * + * @author Alasdair North + * @author Henrique Moody + */ +abstract class AbstractWrapper extends AbstractRule +{ + /** + * @var Validatable + */ + private $validatable; + + /** + * Initializes the rule. + */ + public function __construct(Validatable $validatable) + { + $this->validatable = $validatable; + } + + /** + * {@inheritDoc} + */ + public function assert($input): void + { + $this->validatable->assert($input); + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + $this->validatable->check($input); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return $this->validatable->validate($input); + } + + /** + * {@inheritDoc} + */ + public function setName(string $name): Validatable + { + $this->validatable->setName($name); + + return parent::setName($name); + } +} diff --git a/vendor/workerman/validation/library/Rules/AllOf.php b/vendor/workerman/validation/library/Rules/AllOf.php new file mode 100644 index 0000000..ebbc42c --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AllOf.php @@ -0,0 +1,67 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\AllOfException; + +use function count; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +class AllOf extends AbstractComposite +{ + /** + * {@inheritDoc} + */ + public function assert($input): void + { + $exceptions = $this->getAllThrownExceptions($input); + $numRules = count($this->getRules()); + $numExceptions = count($exceptions); + $summary = [ + 'total' => $numRules, + 'failed' => $numExceptions, + 'passed' => $numRules - $numExceptions, + ]; + if (!empty($exceptions)) { + /** @var AllOfException $allOfException */ + $allOfException = $this->reportError($input, $summary); + $allOfException->addChildren($exceptions); + + throw $allOfException; + } + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + foreach ($this->getRules() as $rule) { + $rule->check($input); + } + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + foreach ($this->getRules() as $rule) { + if (!$rule->validate($input)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/workerman/validation/library/Rules/Alnum.php b/vendor/workerman/validation/library/Rules/Alnum.php new file mode 100644 index 0000000..c52dd9b --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Alnum.php @@ -0,0 +1,33 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function ctype_alnum; + +/** + * Validates whether the input is alphanumeric or not. + * + * Alphanumeric is a combination of alphabetic (a-z and A-Z) and numeric (0-9) + * characters. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Nick Lombard + */ +final class Alnum extends AbstractFilterRule +{ + /** + * {@inheritDoc} + */ + protected function validateFilteredInput(string $input): bool + { + return ctype_alnum($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Alpha.php b/vendor/workerman/validation/library/Rules/Alpha.php new file mode 100644 index 0000000..c218768 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Alpha.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function ctype_alpha; + +/** + * Validates whether the input contains only alphabetic characters. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Nick Lombard + */ +final class Alpha extends AbstractFilterRule +{ + /** + * {@inheritDoc} + */ + protected function validateFilteredInput(string $input): bool + { + return ctype_alpha($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/AlwaysInvalid.php b/vendor/workerman/validation/library/Rules/AlwaysInvalid.php new file mode 100644 index 0000000..b3eb773 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AlwaysInvalid.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates any input as invalid. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author William Espindola + */ +final class AlwaysInvalid extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return false; + } +} diff --git a/vendor/workerman/validation/library/Rules/AlwaysValid.php b/vendor/workerman/validation/library/Rules/AlwaysValid.php new file mode 100644 index 0000000..c8a8212 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AlwaysValid.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates any input as valid. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author William Espindola + */ +final class AlwaysValid extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return true; + } +} diff --git a/vendor/workerman/validation/library/Rules/AnyOf.php b/vendor/workerman/validation/library/Rules/AnyOf.php new file mode 100644 index 0000000..ddf213c --- /dev/null +++ b/vendor/workerman/validation/library/Rules/AnyOf.php @@ -0,0 +1,78 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\AnyOfException; +use Respect\Validation\Exceptions\ValidationException; + +use function count; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class AnyOf extends AbstractComposite +{ + /** + * {@inheritDoc} + */ + public function assert($input): void + { + $validators = $this->getRules(); + $exceptions = $this->getAllThrownExceptions($input); + $numRules = count($validators); + $numExceptions = count($exceptions); + if ($numExceptions === $numRules) { + /** @var AnyOfException $anyOfException */ + $anyOfException = $this->reportError($input); + $anyOfException->addChildren($exceptions); + + throw $anyOfException; + } + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + foreach ($this->getRules() as $v) { + if ($v->validate($input)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + foreach ($this->getRules() as $v) { + try { + $v->check($input); + + return; + } catch (ValidationException $e) { + if (!isset($firstException)) { + $firstException = $e; + } + } + } + + if (isset($firstException)) { + throw $firstException; + } + + throw $this->reportError($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/ArrayType.php b/vendor/workerman/validation/library/Rules/ArrayType.php new file mode 100644 index 0000000..cbfe7d9 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/ArrayType.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_array; + +/** + * Validates whether the type of an input is array. + * + * @author Alexandre Gomes Gaigalas + * @author Emmerson Siqueira + * @author Henrique Moody + * @author João Torquato + */ +final class ArrayType extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_array($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/ArrayVal.php b/vendor/workerman/validation/library/Rules/ArrayVal.php new file mode 100644 index 0000000..53bd4e8 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/ArrayVal.php @@ -0,0 +1,35 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use ArrayAccess; +use SimpleXMLElement; + +use function is_array; + +/** + * Validates if the input is an array or if the input can be used as an array. + * + * Instance of `ArrayAccess` or `SimpleXMLElement` are also considered as valid. + * + * @author Alexandre Gomes Gaigalas + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class ArrayVal extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_array($input) || $input instanceof ArrayAccess || $input instanceof SimpleXMLElement; + } +} diff --git a/vendor/workerman/validation/library/Rules/Attribute.php b/vendor/workerman/validation/library/Rules/Attribute.php new file mode 100644 index 0000000..e1873f1 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Attribute.php @@ -0,0 +1,53 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use ReflectionException; +use ReflectionProperty; +use Respect\Validation\Validatable; + +use function is_object; +use function property_exists; + +/** + * Validates an object attribute, event private ones. + * + * @author Alexandre Gomes Gaigalas + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class Attribute extends AbstractRelated +{ + public function __construct(string $reference, ?Validatable $rule = null, bool $mandatory = true) + { + parent::__construct($reference, $rule, $mandatory); + } + + /** + * {@inheritDoc} + * + * @throws ReflectionException + */ + public function getReferenceValue($input) + { + $propertyMirror = new ReflectionProperty($input, (string) $this->getReference()); + $propertyMirror->setAccessible(true); + + return $propertyMirror->getValue($input); + } + + /** + * {@inheritDoc} + */ + public function hasReference($input): bool + { + return is_object($input) && property_exists($input, (string) $this->getReference()); + } +} diff --git a/vendor/workerman/validation/library/Rules/Base.php b/vendor/workerman/validation/library/Rules/Base.php new file mode 100644 index 0000000..03f6d77 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Base.php @@ -0,0 +1,64 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use function is_null; +use function mb_strlen; +use function mb_substr; +use function preg_match; +use function sprintf; + +/** + * Validate numbers in any base, even with non regular bases. + * + * @author Carlos André Ferrari + * @author Henrique Moody + * @author William Espindola + */ +final class Base extends AbstractRule +{ + /** + * @var string + */ + private $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + /** + * @var int + */ + private $base; + + /** + * Initializes the Base rule. + */ + public function __construct(int $base, ?string $chars = null) + { + if (!is_null($chars)) { + $this->chars = $chars; + } + + $max = mb_strlen($this->chars); + if ($base > $max) { + throw new ComponentException(sprintf('a base between 1 and %s is required', $max)); + } + $this->base = $base; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + $valid = mb_substr($this->chars, 0, $this->base); + + return (bool) preg_match('@^[' . $valid . ']+$@', (string) $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Base64.php b/vendor/workerman/validation/library/Rules/Base64.php new file mode 100644 index 0000000..11a0707 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Base64.php @@ -0,0 +1,40 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_string; +use function mb_strlen; +use function preg_match; + +/** + * Validate if a string is Base64-encoded. + * + * @author Henrique Moody + * @author Jens Segers + * @author William Espindola + */ +final class Base64 extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + if (!preg_match('#^[A-Za-z0-9+/\n\r]+={0,2}$#', $input)) { + return false; + } + + return mb_strlen($input) % 4 === 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/Between.php b/vendor/workerman/validation/library/Rules/Between.php new file mode 100644 index 0000000..8e17c29 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Between.php @@ -0,0 +1,50 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; +use Respect\Validation\Helpers\CanCompareValues; + +/** + * Validates whether the input is between two other values. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class Between extends AbstractEnvelope +{ + use CanCompareValues; + + /** + * Initializes the rule. + * + * @param mixed $minValue + * @param mixed $maxValue + * + * @throws ComponentException + */ + public function __construct($minValue, $maxValue) + { + if ($this->toComparable($minValue) >= $this->toComparable($maxValue)) { + throw new ComponentException('Minimum cannot be less than or equals to maximum'); + } + + parent::__construct( + new AllOf( + new Min($minValue), + new Max($maxValue) + ), + [ + 'minValue' => $minValue, + 'maxValue' => $maxValue, + ] + ); + } +} diff --git a/vendor/workerman/validation/library/Rules/BoolType.php b/vendor/workerman/validation/library/Rules/BoolType.php new file mode 100644 index 0000000..0f32be5 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/BoolType.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_bool; + +/** + * Validates whether the type of the input is boolean. + * + * @author Devin Torres + * @author Henrique Moody + */ +final class BoolType extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_bool($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/BoolVal.php b/vendor/workerman/validation/library/Rules/BoolVal.php new file mode 100644 index 0000000..b9cef69 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/BoolVal.php @@ -0,0 +1,34 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function filter_var; +use function is_bool; + +use const FILTER_NULL_ON_FAILURE; +use const FILTER_VALIDATE_BOOLEAN; + +/** + * Validates if the input results in a boolean value. + * + * @author Emmerson Siqueira + * @author Henrique Moody + * @author William Espindola + */ +final class BoolVal extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_bool(filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)); + } +} diff --git a/vendor/workerman/validation/library/Rules/Bsn.php b/vendor/workerman/validation/library/Rules/Bsn.php new file mode 100644 index 0000000..a5cb512 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Bsn.php @@ -0,0 +1,55 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function ctype_digit; +use function intval; +use function is_scalar; +use function mb_strlen; +use function strval; + +/** + * Validates a Dutch citizen service number (BSN). + * + * @see https://nl.wikipedia.org/wiki/Burgerservicenummer + * + * @author Henrique Moody + * @author Ronald Drenth + * @author William Espindola + */ +final class Bsn extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + $input = (string) $input; + + if (!ctype_digit($input)) { + return false; + } + + if (mb_strlen(strval($input)) !== 9) { + return false; + } + + $sum = -1 * intval($input[8]); + for ($i = 9; $i > 1; --$i) { + $sum += $i * intval($input[9 - $i]); + } + + return $sum !== 0 && $sum % 11 === 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/Call.php b/vendor/workerman/validation/library/Rules/Call.php new file mode 100644 index 0000000..9403239 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Call.php @@ -0,0 +1,107 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\Validatable; +use Throwable; + +use function call_user_func; +use function restore_error_handler; +use function set_error_handler; + +/** + * Validates the return of a callable for a given input. + * + * @author Alexandre Gomes Gaigalas + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class Call extends AbstractRule +{ + /** + * @var callable + */ + private $callable; + + /** + * @var Validatable + */ + private $rule; + + /** + * Initializes the rule with the callable to be executed after the input is passed. + */ + public function __construct(callable $callable, Validatable $rule) + { + $this->callable = $callable; + $this->rule = $rule; + } + + /** + * {@inheritDoc} + */ + public function assert($input): void + { + $this->setErrorHandler($input); + + try { + $this->rule->assert(call_user_func($this->callable, $input)); + } catch (ValidationException $exception) { + throw $exception; + } catch (Throwable $throwable) { + throw $this->reportError($input); + } finally { + restore_error_handler(); + } + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + $this->setErrorHandler($input); + + try { + $this->rule->check(call_user_func($this->callable, $input)); + } catch (ValidationException $exception) { + throw $exception; + } catch (Throwable $throwable) { + throw $this->reportError($input); + } finally { + restore_error_handler(); + } + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + try { + $this->check($input); + } catch (ValidationException $exception) { + return false; + } + + return true; + } + + /** + * @param mixed $input + */ + private function setErrorHandler($input): void + { + set_error_handler(function () use ($input): void { + throw $this->reportError($input); + }); + } +} diff --git a/vendor/workerman/validation/library/Rules/CallableType.php b/vendor/workerman/validation/library/Rules/CallableType.php new file mode 100644 index 0000000..0fa9eb1 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/CallableType.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_callable; + +/** + * Validates whether the pseudo-type of the input is callable. + * + * @author Henrique Moody + */ +final class CallableType extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_callable($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Callback.php b/vendor/workerman/validation/library/Rules/Callback.php new file mode 100644 index 0000000..3c057a2 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Callback.php @@ -0,0 +1,67 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function array_merge; +use function call_user_func_array; +use function count; + +/** + * Validates the input using the return of a given callable. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author William Espindola + */ +final class Callback extends AbstractRule +{ + /** + * @var callable + */ + private $callback; + + /** + * @var mixed[] + */ + private $arguments; + + /** + * Initializes the rule. + * + * @param mixed ...$arguments + */ + public function __construct(callable $callback, ...$arguments) + { + $this->callback = $callback; + $this->arguments = $arguments; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return (bool) call_user_func_array($this->callback, $this->getArguments($input)); + } + + /** + * @param mixed $input + * @return mixed[] + */ + private function getArguments($input): array + { + $arguments = [$input]; + if (count($this->arguments) === 0) { + return $arguments; + } + + return array_merge($arguments, $this->arguments); + } +} diff --git a/vendor/workerman/validation/library/Rules/Charset.php b/vendor/workerman/validation/library/Rules/Charset.php new file mode 100644 index 0000000..00640e2 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Charset.php @@ -0,0 +1,55 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use function array_diff; +use function in_array; +use function mb_detect_encoding; +use function mb_list_encodings; + +/** + * Validates if a string is in a specific charset. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author William Espindola + */ +final class Charset extends AbstractRule +{ + /** + * @var string[] + */ + private $charset; + + /** + * Initializes the rule. + * + * @throws ComponentException + */ + public function __construct(string ...$charset) + { + $available = mb_list_encodings(); + if (!empty(array_diff($charset, $available))) { + throw new ComponentException('Invalid charset'); + } + + $this->charset = $charset; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return in_array(mb_detect_encoding($input, $this->charset, true), $this->charset, true); + } +} diff --git a/vendor/workerman/validation/library/Rules/Cnh.php b/vendor/workerman/validation/library/Rules/Cnh.php new file mode 100644 index 0000000..4b180fd --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Cnh.php @@ -0,0 +1,59 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_scalar; +use function mb_strlen; +use function preg_replace; + +/** + * Validates a Brazilian driver's license. + * + * @author Gabriel Pedro + * @author Henrique Moody + * @author Kinn Coelho Julião + * @author William Espindola + */ +final class Cnh extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + // Canonicalize input + $input = (string) preg_replace('{\D}', '', (string) $input); + + // Validate length and invalid numbers + if (mb_strlen($input) != 11 || ((int) $input === 0)) { + return false; + } + + // Validate check digits using a modulus 11 algorithm + for ($c = $s1 = $s2 = 0, $p = 9; $c < 9; $c++, $p--) { + $s1 += (int) $input[$c] * $p; + $s2 += (int) $input[$c] * (10 - $p); + } + + $dv1 = $s1 % 11; + if ($input[9] != ($dv1 > 9) ? 0 : $dv1) { + return false; + } + + $dv2 = $s2 % 11 - ($dv1 > 9 ? 2 : 0); + $check = $dv2 < 0 ? $dv2 + 11 : ($dv2 > 9 ? 0 : $dv2); + + return $input[10] == $check; + } +} diff --git a/vendor/workerman/validation/library/Rules/Cnpj.php b/vendor/workerman/validation/library/Rules/Cnpj.php new file mode 100644 index 0000000..78081f2 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Cnpj.php @@ -0,0 +1,83 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function array_map; +use function array_sum; +use function count; +use function is_scalar; +use function preg_replace; +use function str_split; + +/** + * Validates if the input is a Brazilian National Registry of Legal Entities (CNPJ) number. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Jayson Reis + * @author Nick Lombard + * @author Renato Moura + * @author William Espindola + */ +final class Cnpj extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + // Code ported from jsfromhell.com + $bases = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; + $digits = $this->getDigits((string) $input); + + if (array_sum($digits) < 1) { + return false; + } + + if (count($digits) !== 14) { + return false; + } + + $n = 0; + for ($i = 0; $i < 12; ++$i) { + $n += $digits[$i] * $bases[$i + 1]; + } + + if ($digits[12] != (($n %= 11) < 2 ? 0 : 11 - $n)) { + return false; + } + + $n = 0; + for ($i = 0; $i <= 12; ++$i) { + $n += $digits[$i] * $bases[$i]; + } + + $check = ($n %= 11) < 2 ? 0 : 11 - $n; + + return $digits[13] == $check; + } + + /** + * @return int[] + */ + private function getDigits(string $input): array + { + return array_map( + 'intval', + str_split( + (string) preg_replace('/\D/', '', $input) + ) + ); + } +} diff --git a/vendor/workerman/validation/library/Rules/Consonant.php b/vendor/workerman/validation/library/Rules/Consonant.php new file mode 100644 index 0000000..f765ef0 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Consonant.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function preg_match; + +/** + * Validates if the input contains only consonants. + * + * @author Danilo Correa + * @author Henrique Moody + * @author Nick Lombard + */ +final class Consonant extends AbstractFilterRule +{ + /** + * {@inheritDoc} + */ + protected function validateFilteredInput(string $input): bool + { + return preg_match('/^(\s|[b-df-hj-np-tv-zB-DF-HJ-NP-TV-Z])*$/', $input) > 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/Contains.php b/vendor/workerman/validation/library/Rules/Contains.php new file mode 100644 index 0000000..0a92b92 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Contains.php @@ -0,0 +1,78 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function in_array; +use function is_array; +use function is_scalar; +use function mb_stripos; +use function mb_strpos; + +/** + * Validates if the input contains some value. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Marcelo Araujo + * @author William Espindola + */ +final class Contains extends AbstractRule +{ + /** + * @var mixed + */ + private $containsValue; + + /** + * @var bool + */ + private $identical; + + /** + * Initializes the Contains rule. + * + * @param mixed $containsValue Value that will be sought + * @param bool $identical Defines whether the value is identical, default is false + */ + public function __construct($containsValue, bool $identical = false) + { + $this->containsValue = $containsValue; + $this->identical = $identical; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (is_array($input)) { + return in_array($this->containsValue, $input, $this->identical); + } + + if (!is_scalar($input) || !is_scalar($this->containsValue)) { + return false; + } + + return $this->validateString((string) $input, (string) $this->containsValue); + } + + private function validateString(string $haystack, string $needle): bool + { + if ($needle === '') { + return false; + } + + if ($this->identical) { + return mb_strpos($haystack, $needle) !== false; + } + + return mb_stripos($haystack, $needle) !== false; + } +} diff --git a/vendor/workerman/validation/library/Rules/ContainsAny.php b/vendor/workerman/validation/library/Rules/ContainsAny.php new file mode 100644 index 0000000..87ec494 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/ContainsAny.php @@ -0,0 +1,50 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function array_map; + +/** + * Validates if the input contains at least one of defined values + * + * @author Henrique Moody + * @author Kirill Dlussky + */ +final class ContainsAny extends AbstractEnvelope +{ + /** + * Initializes the rule. + * + * @param mixed[] $needles At least one of the values provided must be found in input string or array + * @param bool $identical Defines whether the value should be compared strictly, when validating array + */ + public function __construct(array $needles, bool $identical = false) + { + parent::__construct( + new AnyOf(...$this->getRules($needles, $identical)), + ['needles' => $needles] + ); + } + + /** + * @param mixed[] $needles + * + * @return Contains[] + */ + private function getRules(array $needles, bool $identical): array + { + return array_map( + static function ($needle) use ($identical): Contains { + return new Contains($needle, $identical); + }, + $needles + ); + } +} diff --git a/vendor/workerman/validation/library/Rules/Control.php b/vendor/workerman/validation/library/Rules/Control.php new file mode 100644 index 0000000..1190550 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Control.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function ctype_cntrl; + +/** + * Validates if all of the characters in the provided string, are control characters. + * + * @author Andre Ramaciotti + * @author Danilo Correa + * @author Henrique Moody + * @author Nick Lombard + */ +final class Control extends AbstractFilterRule +{ + /** + * {@inheritDoc} + */ + protected function validateFilteredInput(string $input): bool + { + return ctype_cntrl($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Countable.php b/vendor/workerman/validation/library/Rules/Countable.php new file mode 100644 index 0000000..8f2d8d6 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Countable.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Countable as CountableInterface; + +use function is_array; + +/** + * Validates if the input is countable. + * + * @author Henrique Moody + * @author João Torquato + * @author William Espindola + */ +final class Countable extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_array($input) || $input instanceof CountableInterface; + } +} diff --git a/vendor/workerman/validation/library/Rules/CountryCode.php b/vendor/workerman/validation/library/Rules/CountryCode.php new file mode 100644 index 0000000..e9b90d2 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/CountryCode.php @@ -0,0 +1,375 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use function array_column; +use function array_keys; +use function implode; +use function sprintf; + +/** + * Validates whether the input is a country code in ISO 3166-1 standard. + * + * This rule supports the three sets of country codes (alpha-2, alpha-3, and numeric). + * + * @author Alexandre Gomes Gaigalas + * @author Felipe Martins + * @author Henrique Moody + * @author William Espindola + */ +final class CountryCode extends AbstractSearcher +{ + /** + * The ISO representation of a country code. + */ + public const ALPHA2 = 'alpha-2'; + + /** + * The ISO3 representation of a country code. + */ + public const ALPHA3 = 'alpha-3'; + + /** + * The ISO-number representation of a country code. + */ + public const NUMERIC = 'numeric'; + + /** + * Position of the indexes of each set in the list of country codes. + */ + private const SET_INDEXES = [ + self::ALPHA2 => 0, + self::ALPHA3 => 1, + self::NUMERIC => 2, + ]; + + /** + * @see https://salsa.debian.org/iso-codes-team/iso-codes + */ + private const COUNTRY_CODES = [ + // begin of auto-generated code + ['AD', 'AND', '020'], // Andorra + ['AE', 'ARE', '784'], // United Arab Emirates + ['AF', 'AFG', '004'], // Afghanistan + ['AG', 'ATG', '028'], // Antigua and Barbuda + ['AI', 'AFI', '262'], // French Afars and Issas + ['AI', 'AIA', '660'], // Anguilla + ['AL', 'ALB', '008'], // Albania + ['AM', 'ARM', '051'], // Armenia + ['AN', 'ANT', '530'], // Netherlands Antilles + ['AO', 'AGO', '024'], // Angola + ['AQ', 'ATA', '010'], // Antarctica + ['AR', 'ARG', '032'], // Argentina + ['AS', 'ASM', '016'], // American Samoa + ['AT', 'AUT', '040'], // Austria + ['AU', 'AUS', '036'], // Australia + ['AW', 'ABW', '533'], // Aruba + ['AX', 'ALA', '248'], // Åland Islands + ['AZ', 'AZE', '031'], // Azerbaijan + ['BA', 'BIH', '070'], // Bosnia and Herzegovina + ['BB', 'BRB', '052'], // Barbados + ['BD', 'BGD', '050'], // Bangladesh + ['BE', 'BEL', '056'], // Belgium + ['BF', 'BFA', '854'], // Burkina Faso + ['BG', 'BGR', '100'], // Bulgaria + ['BH', 'BHR', '048'], // Bahrain + ['BI', 'BDI', '108'], // Burundi + ['BJ', 'BEN', '204'], // Benin + ['BL', 'BLM', '652'], // Saint Barthélemy + ['BM', 'BMU', '060'], // Bermuda + ['BN', 'BRN', '096'], // Brunei Darussalam + ['BO', 'BOL', '068'], // Bolivia, Plurinational State of + ['BQ', 'ATB', null], // British Antarctic Territory + ['BQ', 'BES', '535'], // Bonaire, Sint Eustatius and Saba + ['BR', 'BRA', '076'], // Brazil + ['BS', 'BHS', '044'], // Bahamas + ['BT', 'BTN', '064'], // Bhutan + ['BU', 'BUR', '104'], // Burma, Socialist Republic of the Union of + ['BV', 'BVT', '074'], // Bouvet Island + ['BW', 'BWA', '072'], // Botswana + ['BY', 'BLR', '112'], // Belarus + ['BY', 'BYS', '112'], // Byelorussian SSR Soviet Socialist Republic + ['BZ', 'BLZ', '084'], // Belize + ['CA', 'CAN', '124'], // Canada + ['CC', 'CCK', '166'], // Cocos (Keeling) Islands + ['CD', 'COD', '180'], // Congo, The Democratic Republic of the + ['CF', 'CAF', '140'], // Central African Republic + ['CG', 'COG', '178'], // Congo + ['CH', 'CHE', '756'], // Switzerland + ['CI', 'CIV', '384'], // Côte d'Ivoire + ['CK', 'COK', '184'], // Cook Islands + ['CL', 'CHL', '152'], // Chile + ['CM', 'CMR', '120'], // Cameroon + ['CN', 'CHN', '156'], // China + ['CO', 'COL', '170'], // Colombia + ['CR', 'CRI', '188'], // Costa Rica + ['CS', 'CSK', '200'], // Czechoslovakia, Czechoslovak Socialist Republic + ['CS', 'SCG', '891'], // Serbia and Montenegro + ['CT', 'CTE', '128'], // Canton and Enderbury Islands + ['CU', 'CUB', '192'], // Cuba + ['CV', 'CPV', '132'], // Cabo Verde + ['CW', 'CUW', '531'], // Curaçao + ['CX', 'CXR', '162'], // Christmas Island + ['CY', 'CYP', '196'], // Cyprus + ['CZ', 'CZE', '203'], // Czechia + ['DD', 'DDR', '278'], // German Democratic Republic + ['DE', 'DEU', '276'], // Germany + ['DJ', 'DJI', '262'], // Djibouti + ['DK', 'DNK', '208'], // Denmark + ['DM', 'DMA', '212'], // Dominica + ['DO', 'DOM', '214'], // Dominican Republic + ['DY', 'DHY', '204'], // Dahomey + ['DZ', 'DZA', '012'], // Algeria + ['EC', 'ECU', '218'], // Ecuador + ['EE', 'EST', '233'], // Estonia + ['EG', 'EGY', '818'], // Egypt + ['EH', 'ESH', '732'], // Western Sahara + ['ER', 'ERI', '232'], // Eritrea + ['ES', 'ESP', '724'], // Spain + ['ET', 'ETH', '231'], // Ethiopia + ['FI', 'FIN', '246'], // Finland + ['FJ', 'FJI', '242'], // Fiji + ['FK', 'FLK', '238'], // Falkland Islands (Malvinas) + ['FM', 'FSM', '583'], // Micronesia, Federated States of + ['FO', 'FRO', '234'], // Faroe Islands + ['FQ', 'ATF', null], // French Southern and Antarctic Territories + ['FR', 'FRA', '250'], // France + ['FX', 'FXX', '249'], // France, Metropolitan + ['GA', 'GAB', '266'], // Gabon + ['GB', 'GBR', '826'], // United Kingdom + ['GD', 'GRD', '308'], // Grenada + ['GE', 'GEL', '296'], // Gilbert and Ellice Islands + ['GE', 'GEO', '268'], // Georgia + ['GF', 'GUF', '254'], // French Guiana + ['GG', 'GGY', '831'], // Guernsey + ['GH', 'GHA', '288'], // Ghana + ['GI', 'GIB', '292'], // Gibraltar + ['GL', 'GRL', '304'], // Greenland + ['GM', 'GMB', '270'], // Gambia + ['GN', 'GIN', '324'], // Guinea + ['GP', 'GLP', '312'], // Guadeloupe + ['GQ', 'GNQ', '226'], // Equatorial Guinea + ['GR', 'GRC', '300'], // Greece + ['GS', 'SGS', '239'], // South Georgia and the South Sandwich Islands + ['GT', 'GTM', '320'], // Guatemala + ['GU', 'GUM', '316'], // Guam + ['GW', 'GNB', '624'], // Guinea-Bissau + ['GY', 'GUY', '328'], // Guyana + ['HK', 'HKG', '344'], // Hong Kong + ['HM', 'HMD', '334'], // Heard Island and McDonald Islands + ['HN', 'HND', '340'], // Honduras + ['HR', 'HRV', '191'], // Croatia + ['HT', 'HTI', '332'], // Haiti + ['HU', 'HUN', '348'], // Hungary + ['HV', 'HVO', '854'], // Upper Volta, Republic of + ['ID', 'IDN', '360'], // Indonesia + ['IE', 'IRL', '372'], // Ireland + ['IL', 'ISR', '376'], // Israel + ['IM', 'IMN', '833'], // Isle of Man + ['IN', 'IND', '356'], // India + ['IO', 'IOT', '086'], // British Indian Ocean Territory + ['IQ', 'IRQ', '368'], // Iraq + ['IR', 'IRN', '364'], // Iran, Islamic Republic of + ['IS', 'ISL', '352'], // Iceland + ['IT', 'ITA', '380'], // Italy + ['JE', 'JEY', '832'], // Jersey + ['JM', 'JAM', '388'], // Jamaica + ['JO', 'JOR', '400'], // Jordan + ['JP', 'JPN', '392'], // Japan + ['JT', 'JTN', '396'], // Johnston Island + ['KE', 'KEN', '404'], // Kenya + ['KG', 'KGZ', '417'], // Kyrgyzstan + ['KH', 'KHM', '116'], // Cambodia + ['KI', 'KIR', '296'], // Kiribati + ['KM', 'COM', '174'], // Comoros + ['KN', 'KNA', '659'], // Saint Kitts and Nevis + ['KP', 'PRK', '408'], // Korea, Democratic People's Republic of + ['KR', 'KOR', '410'], // Korea, Republic of + ['KW', 'KWT', '414'], // Kuwait + ['KY', 'CYM', '136'], // Cayman Islands + ['KZ', 'KAZ', '398'], // Kazakhstan + ['LA', 'LAO', '418'], // Lao People's Democratic Republic + ['LB', 'LBN', '422'], // Lebanon + ['LC', 'LCA', '662'], // Saint Lucia + ['LI', 'LIE', '438'], // Liechtenstein + ['LK', 'LKA', '144'], // Sri Lanka + ['LR', 'LBR', '430'], // Liberia + ['LS', 'LSO', '426'], // Lesotho + ['LT', 'LTU', '440'], // Lithuania + ['LU', 'LUX', '442'], // Luxembourg + ['LV', 'LVA', '428'], // Latvia + ['LY', 'LBY', '434'], // Libya + ['MA', 'MAR', '504'], // Morocco + ['MC', 'MCO', '492'], // Monaco + ['MD', 'MDA', '498'], // Moldova, Republic of + ['ME', 'MNE', '499'], // Montenegro + ['MF', 'MAF', '663'], // Saint Martin (French part) + ['MG', 'MDG', '450'], // Madagascar + ['MH', 'MHL', '584'], // Marshall Islands + ['MI', 'MID', '488'], // Midway Islands + ['MK', 'MKD', '807'], // North Macedonia + ['ML', 'MLI', '466'], // Mali + ['MM', 'MMR', '104'], // Myanmar + ['MN', 'MNG', '496'], // Mongolia + ['MO', 'MAC', '446'], // Macao + ['MP', 'MNP', '580'], // Northern Mariana Islands + ['MQ', 'MTQ', '474'], // Martinique + ['MR', 'MRT', '478'], // Mauritania + ['MS', 'MSR', '500'], // Montserrat + ['MT', 'MLT', '470'], // Malta + ['MU', 'MUS', '480'], // Mauritius + ['MV', 'MDV', '462'], // Maldives + ['MW', 'MWI', '454'], // Malawi + ['MX', 'MEX', '484'], // Mexico + ['MY', 'MYS', '458'], // Malaysia + ['MZ', 'MOZ', '508'], // Mozambique + ['NA', 'NAM', '516'], // Namibia + ['NC', 'NCL', '540'], // New Caledonia + ['NE', 'NER', '562'], // Niger + ['NF', 'NFK', '574'], // Norfolk Island + ['NG', 'NGA', '566'], // Nigeria + ['NH', 'NHB', '548'], // New Hebrides + ['NI', 'NIC', '558'], // Nicaragua + ['NL', 'NLD', '528'], // Netherlands + ['NO', 'NOR', '578'], // Norway + ['NP', 'NPL', '524'], // Nepal + ['NQ', 'ATN', '216'], // Dronning Maud Land + ['NR', 'NRU', '520'], // Nauru + ['NT', 'NTZ', '536'], // Neutral Zone + ['NU', 'NIU', '570'], // Niue + ['NZ', 'NZL', '554'], // New Zealand + ['OM', 'OMN', '512'], // Oman + ['PA', 'PAN', '591'], // Panama + ['PC', 'PCI', '582'], // Pacific Islands (trust territory) + ['PE', 'PER', '604'], // Peru + ['PF', 'PYF', '258'], // French Polynesia + ['PG', 'PNG', '598'], // Papua New Guinea + ['PH', 'PHL', '608'], // Philippines + ['PK', 'PAK', '586'], // Pakistan + ['PL', 'POL', '616'], // Poland + ['PM', 'SPM', '666'], // Saint Pierre and Miquelon + ['PN', 'PCN', '612'], // Pitcairn + ['PR', 'PRI', '630'], // Puerto Rico + ['PS', 'PSE', '275'], // Palestine, State of + ['PT', 'PRT', '620'], // Portugal + ['PU', 'PUS', '849'], // US Miscellaneous Pacific Islands + ['PW', 'PLW', '585'], // Palau + ['PY', 'PRY', '600'], // Paraguay + ['PZ', 'PCZ', null], // Panama Canal Zone + ['QA', 'QAT', '634'], // Qatar + ['RE', 'REU', '638'], // Réunion + ['RH', 'RHO', '716'], // Southern Rhodesia + ['RO', 'ROU', '642'], // Romania + ['RS', 'SRB', '688'], // Serbia + ['RU', 'RUS', '643'], // Russian Federation + ['RW', 'RWA', '646'], // Rwanda + ['SA', 'SAU', '682'], // Saudi Arabia + ['SB', 'SLB', '090'], // Solomon Islands + ['SC', 'SYC', '690'], // Seychelles + ['SD', 'SDN', '729'], // Sudan + ['SE', 'SWE', '752'], // Sweden + ['SG', 'SGP', '702'], // Singapore + ['SH', 'SHN', '654'], // Saint Helena, Ascension and Tristan da Cunha + ['SI', 'SVN', '705'], // Slovenia + ['SJ', 'SJM', '744'], // Svalbard and Jan Mayen + ['SK', 'SKM', null], // Sikkim + ['SK', 'SVK', '703'], // Slovakia + ['SL', 'SLE', '694'], // Sierra Leone + ['SM', 'SMR', '674'], // San Marino + ['SN', 'SEN', '686'], // Senegal + ['SO', 'SOM', '706'], // Somalia + ['SR', 'SUR', '740'], // Suriname + ['SS', 'SSD', '728'], // South Sudan + ['ST', 'STP', '678'], // Sao Tome and Principe + ['SU', 'SUN', '810'], // USSR, Union of Soviet Socialist Republics + ['SV', 'SLV', '222'], // El Salvador + ['SX', 'SXM', '534'], // Sint Maarten (Dutch part) + ['SY', 'SYR', '760'], // Syrian Arab Republic + ['SZ', 'SWZ', '748'], // Eswatini + ['TC', 'TCA', '796'], // Turks and Caicos Islands + ['TD', 'TCD', '148'], // Chad + ['TF', 'ATF', '260'], // French Southern Territories + ['TG', 'TGO', '768'], // Togo + ['TH', 'THA', '764'], // Thailand + ['TJ', 'TJK', '762'], // Tajikistan + ['TK', 'TKL', '772'], // Tokelau + ['TL', 'TLS', '626'], // Timor-Leste + ['TM', 'TKM', '795'], // Turkmenistan + ['TN', 'TUN', '788'], // Tunisia + ['TO', 'TON', '776'], // Tonga + ['TP', 'TMP', '626'], // East Timor + ['TR', 'TUR', '792'], // Türkiye + ['TT', 'TTO', '780'], // Trinidad and Tobago + ['TV', 'TUV', '798'], // Tuvalu + ['TW', 'TWN', '158'], // Taiwan, Province of China + ['TZ', 'TZA', '834'], // Tanzania, United Republic of + ['UA', 'UKR', '804'], // Ukraine + ['UG', 'UGA', '800'], // Uganda + ['UM', 'UMI', '581'], // United States Minor Outlying Islands + ['US', 'USA', '840'], // United States + ['UY', 'URY', '858'], // Uruguay + ['UZ', 'UZB', '860'], // Uzbekistan + ['VA', 'VAT', '336'], // Holy See (Vatican City State) + ['VC', 'VCT', '670'], // Saint Vincent and the Grenadines + ['VD', 'VDR', null], // Viet-Nam, Democratic Republic of + ['VE', 'VEN', '862'], // Venezuela, Bolivarian Republic of + ['VG', 'VGB', '092'], // Virgin Islands, British + ['VI', 'VIR', '850'], // Virgin Islands, U.S. + ['VN', 'VNM', '704'], // Viet Nam + ['VU', 'VUT', '548'], // Vanuatu + ['WF', 'WLF', '876'], // Wallis and Futuna + ['WK', 'WAK', '872'], // Wake Island + ['WS', 'WSM', '882'], // Samoa + ['YD', 'YMD', '720'], // Yemen, Democratic, People's Democratic Republic of + ['YE', 'YEM', '887'], // Yemen + ['YT', 'MYT', '175'], // Mayotte + ['YU', 'YUG', '891'], // Yugoslavia, Socialist Federal Republic of + ['ZA', 'ZAF', '710'], // South Africa + ['ZM', 'ZMB', '894'], // Zambia + ['ZR', 'ZAR', '180'], // Zaire, Republic of + ['ZW', 'ZWE', '716'], // Zimbabwe + // end of auto-generated code + ]; + + /** + * @var string + */ + private $set; + + /** + * Initializes the rule. + * + * @throws ComponentException If $set is not a valid set + */ + public function __construct(string $set = self::ALPHA2) + { + if (!isset(self::SET_INDEXES[$set])) { + throw new ComponentException( + sprintf( + '"%s" is not a valid set for ISO 3166-1 (Available: %s)', + $set, + implode(', ', array_keys(self::SET_INDEXES)) + ) + ); + } + + $this->set = $set; + } + + /** + * {@inheritDoc} + */ + protected function getDataSource($input = null): array + { + return array_column(self::COUNTRY_CODES, self::SET_INDEXES[$this->set]); + } +} diff --git a/vendor/workerman/validation/library/Rules/Cpf.php b/vendor/workerman/validation/library/Rules/Cpf.php new file mode 100644 index 0000000..62f7503 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Cpf.php @@ -0,0 +1,59 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function intval; +use function mb_strlen; +use function preg_match; +use function preg_replace; + +/** + * Validates whether the input is a CPF (Brazilian Natural Persons Register) number. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Jair Henrique + * @author Jayson Reis + * @author Jean Pimentel + * @author William Espindola + */ +final class Cpf extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + // Code ported from jsfromhell.com + $c = preg_replace('/\D/', '', $input); + + if (mb_strlen($c) != 11 || preg_match('/^' . $c[0] . '{11}$/', $c) || $c === '01234567890') { + return false; + } + + $n = 0; + for ($s = 10, $i = 0; $s >= 2; ++$i, --$s) { + $n += intval($c[$i]) * $s; + } + + if ($c[9] != (($n %= 11) < 2 ? 0 : 11 - $n)) { + return false; + } + + $n = 0; + for ($s = 11, $i = 0; $s >= 2; ++$i, --$s) { + $n += intval($c[$i]) * $s; + } + + $check = ($n %= 11) < 2 ? 0 : 11 - $n; + + return $c[10] == $check; + } +} diff --git a/vendor/workerman/validation/library/Rules/CreditCard.php b/vendor/workerman/validation/library/Rules/CreditCard.php new file mode 100644 index 0000000..3008497 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/CreditCard.php @@ -0,0 +1,102 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use function array_keys; +use function implode; +use function is_scalar; +use function preg_match; +use function preg_replace; +use function sprintf; + +/** + * Validates whether the input is a credit card number. + * + * @author Alexander Gorshkov + * @author Andy Snell + * @author Henrique Moody + * @author Jean Pimentel + * @author Nick Lombard + * @author William Espindola + * @author Rakshit Arora + */ +final class CreditCard extends AbstractRule +{ + public const ANY = 'Any'; + + public const AMERICAN_EXPRESS = 'American Express'; + + public const DINERS_CLUB = 'Diners Club'; + + public const DISCOVER = 'Discover'; + + public const JCB = 'JCB'; + + public const MASTERCARD = 'MasterCard'; + + public const VISA = 'Visa'; + + public const RUPAY = 'RuPay'; + + private const BRAND_REGEX_LIST = [ + self::ANY => '/^[0-9]+$/', + self::AMERICAN_EXPRESS => '/^3[47]\d{13}$/', + self::DINERS_CLUB => '/^3(?:0[0-5]|[68]\d)\d{11}$/', + self::DISCOVER => '/^6(?:011|5\d{2})\d{12}$/', + self::JCB => '/^(?:2131|1800|35\d{3})\d{11}$/', + self::MASTERCARD => '/(5[1-5]|2[2-7])\d{14}$/', + self::VISA => '/^4\d{12}(?:\d{3})?$/', + self::RUPAY => '/^6(?!011)(?:0[0-9]{14}|52[12][0-9]{12})$/', + ]; + + /** + * @var string + */ + private $brand; + + /** + * Initializes the rule. + * + * @throws ComponentException + */ + public function __construct(string $brand = self::ANY) + { + if (!isset(self::BRAND_REGEX_LIST[$brand])) { + throw new ComponentException( + sprintf( + '"%s" is not a valid credit card brand (Available: %s)', + $brand, + implode(', ', array_keys(self::BRAND_REGEX_LIST)) + ) + ); + } + + $this->brand = $brand; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + $input = (string) preg_replace('/[ .-]/', '', (string) $input); + if (!(new Luhn())->validate($input)) { + return false; + } + + return preg_match(self::BRAND_REGEX_LIST[$this->brand], $input) > 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/CurrencyCode.php b/vendor/workerman/validation/library/Rules/CurrencyCode.php new file mode 100644 index 0000000..4c3ab89 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/CurrencyCode.php @@ -0,0 +1,212 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates currency codes in ISO 4217. + * + * @author Henrique Moody + * @author Justin Hook + * @author Tim Strijdhorst + * @author William Espindola + */ +final class CurrencyCode extends AbstractSearcher +{ + /** + * @see http://www.currency-iso.org/en/home/tables/table-a1.html + * + * {@inheritDoc} + */ + protected function getDataSource($input = null): array + { + return [ + 'AED', // UAE Dirham + 'AFN', // Afghani + 'ALL', // Lek + 'AMD', // Armenian Dram + 'ANG', // Netherlands Antillean Guilder + 'AOA', // Kwanza + 'ARS', // Argentine Peso + 'AUD', // Australian Dollar + 'AWG', // Aruban Florin + 'AZN', // Azerbaijan Manat + 'BAM', // Convertible Mark + 'BBD', // Barbados Dollar + 'BDT', // Taka + 'BGN', // Bulgarian Lev + 'BHD', // Bahraini Dinar + 'BIF', // Burundi Franc + 'BMD', // Bermudian Dollar + 'BND', // Brunei Dollar + 'BOB', // Boliviano + 'BOV', // Mvdol + 'BRL', // Brazilian Real + 'BSD', // Bahamian Dollar + 'BTN', // Ngultrum + 'BWP', // Pula + 'BYN', // Belarusian Ruble + 'BZD', // Belize Dollar + 'CAD', // Canadian Dollar + 'CDF', // Congolese Franc + 'CHE', // WIR Euro + 'CHF', // Swiss Franc + 'CHW', // WIR Franc + 'CLF', // Unidad de Fomento + 'CLP', // Chilean Peso + 'CNY', // Yuan Renminbi + 'COP', // Colombian Peso + 'COU', // Unidad de Valor Real + 'CRC', // Costa Rican Colon + 'CUC', // Peso Convertible + 'CUP', // Cuban Peso + 'CVE', // Cabo Verde Escudo + 'CZK', // Czech Koruna + 'DJF', // Djibouti Franc + 'DKK', // Danish Krone + 'DOP', // Dominican Peso + 'DZD', // Algerian Dinar + 'EGP', // Egyptian Pound + 'ERN', // Nakfa + 'ETB', // Ethiopian Birr + 'EUR', // Euro + 'FJD', // Fiji Dollar + 'FKP', // Falkland Islands Pound + 'GBP', // Pound Sterling + 'GEL', // Lari + 'GHS', // Ghana Cedi + 'GIP', // Gibraltar Pound + 'GMD', // Dalasi + 'GNF', // Guinean Franc + 'GTQ', // Quetzal + 'GYD', // Guyana Dollar + 'HKD', // Hong Kong Dollar + 'HNL', // Lempira + 'HTG', // Gourde + 'HUF', // Forint + 'IDR', // Rupiah + 'ILS', // New Israeli Sheqel + 'INR', // Indian Rupee + 'IQD', // Iraqi Dinar + 'IRR', // Iranian Rial + 'ISK', // Iceland Krona + 'JMD', // Jamaican Dollar + 'JOD', // Jordanian Dinar + 'JPY', // Yen + 'KES', // Kenyan Shilling + 'KGS', // Som + 'KHR', // Riel + 'KMF', // Comorian Franc + 'KPW', // North Korean Won + 'KRW', // Won + 'KWD', // Kuwaiti Dinar + 'KYD', // Cayman Islands Dollar + 'KZT', // Tenge + 'LAK', // Lao Kip + 'LBP', // Lebanese Pound + 'LKR', // Sri Lanka Rupee + 'LRD', // Liberian Dollar + 'LSL', // Loti + 'LYD', // Libyan Dinar + 'MAD', // Moroccan Dirham + 'MDL', // Moldovan Leu + 'MGA', // Malagasy Ariary + 'MKD', // Denar + 'MMK', // Kyat + 'MNT', // Tugrik + 'MOP', // Pataca + 'MRU', // Ouguiya + 'MUR', // Mauritius Rupee + 'MVR', // Rufiyaa + 'MWK', // Malawi Kwacha + 'MXN', // Mexican Peso + 'MXV', // Mexican Unidad de Inversion (UDI) + 'MYR', // Malaysian Ringgit + 'MZN', // Mozambique Metical + 'NAD', // Namibia Dollar + 'NGN', // Naira + 'NIO', // Cordoba Oro + 'NOK', // Norwegian Krone + 'NPR', // Nepalese Rupee + 'NZD', // New Zealand Dollar + 'OMR', // Rial Omani + 'PAB', // Balboa + 'PEN', // Sol + 'PGK', // Kina + 'PHP', // Philippine Peso + 'PKR', // Pakistan Rupee + 'PLN', // Zloty + 'PYG', // Guarani + 'QAR', // Qatari Rial + 'RON', // Romanian Leu + 'RSD', // Serbian Dinar + 'RUB', // Russian Ruble + 'RWF', // Rwanda Franc + 'SAR', // Saudi Riyal + 'SBD', // Solomon Islands Dollar + 'SCR', // Seychelles Rupee + 'SDG', // Sudanese Pound + 'SEK', // Swedish Krona + 'SGD', // Singapore Dollar + 'SHP', // Saint Helena Pound + 'SLE', // Leone + 'SLL', // Leone + 'SOS', // Somali Shilling + 'SRD', // Surinam Dollar + 'SSP', // South Sudanese Pound + 'STN', // Dobra + 'SVC', // El Salvador Colon + 'SYP', // Syrian Pound + 'SZL', // Lilangeni + 'THB', // Baht + 'TJS', // Somoni + 'TMT', // Turkmenistan New Manat + 'TND', // Tunisian Dinar + 'TOP', // Pa’anga + 'TRY', // Turkish Lira + 'TTD', // Trinidad and Tobago Dollar + 'TWD', // New Taiwan Dollar + 'TZS', // Tanzanian Shilling + 'UAH', // Hryvnia + 'UGX', // Uganda Shilling + 'USD', // US Dollar + 'USN', // US Dollar (Next day) + 'UYI', // Uruguay Peso en Unidades Indexadas (UI) + 'UYU', // Peso Uruguayo + 'UYW', // Unidad Previsional + 'UZS', // Uzbekistan Sum + 'VED', // Bolívar Soberano + 'VES', // Bolívar Soberano + 'VND', // Dong + 'VUV', // Vatu + 'WST', // Tala + 'XAF', // CFA Franc BEAC + 'XAG', // Silver + 'XAU', // Gold + 'XBA', // Bond Markets Unit European Composite Unit (EURCO) + 'XBB', // Bond Markets Unit European Monetary Unit (E.M.U.-6) + 'XBC', // Bond Markets Unit European Unit of Account 9 (E.U.A.-9) + 'XBD', // Bond Markets Unit European Unit of Account 17 (E.U.A.-17) + 'XCD', // East Caribbean Dollar + 'XDR', // SDR (Special Drawing Right) + 'XOF', // CFA Franc BCEAO + 'XPD', // Palladium + 'XPF', // CFP Franc + 'XPT', // Platinum + 'XSU', // Sucre + 'XTS', // Codes specifically reserved for testing purposes + 'XUA', // ADB Unit of Account + 'XXX', // The codes assigned for transactions where no currency is involved + 'YER', // Yemeni Rial + 'ZAR', // Rand + 'ZMW', // Zambian Kwacha + 'ZWL', // Zimbabwe Dollar + ]; + } +} diff --git a/vendor/workerman/validation/library/Rules/Date.php b/vendor/workerman/validation/library/Rules/Date.php new file mode 100644 index 0000000..d39a758 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Date.php @@ -0,0 +1,67 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; +use Respect\Validation\Helpers\CanValidateDateTime; + +use function date; +use function is_scalar; +use function preg_match; +use function sprintf; +use function strtotime; + +/** + * Validates if input is a date. + * + * @author Bruno Luiz da Silva + * @author Henrique Moody + */ +final class Date extends AbstractRule +{ + use CanValidateDateTime; + + /** + * @var string + */ + private $format; + + /** + * @var string + */ + private $sample; + + /** + * Initializes the rule. + * + * @throws ComponentException + */ + public function __construct(string $format = 'Y-m-d') + { + if (!preg_match('/^[djSFmMnYy\W]+$/', $format)) { + throw new ComponentException(sprintf('"%s" is not a valid date format', $format)); + } + + $this->format = $format; + $this->sample = date($format, strtotime('2005-12-30')); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + return $this->isDateTime($this->format, (string) $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/DateTime.php b/vendor/workerman/validation/library/Rules/DateTime.php new file mode 100644 index 0000000..cc1e6d2 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/DateTime.php @@ -0,0 +1,66 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use DateTimeInterface; +use Respect\Validation\Helpers\CanValidateDateTime; + +use function date; +use function is_scalar; +use function strtotime; + +/** + * @author Alexandre Gomes Gaigalas + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class DateTime extends AbstractRule +{ + use CanValidateDateTime; + + /** + * @var string|null + */ + private $format; + + /** + * @var string + */ + private $sample; + + /** + * Initializes the rule. + */ + public function __construct(?string $format = null) + { + $this->format = $format; + $this->sample = date($format ?: 'c', strtotime('2005-12-30 01:02:03')); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof DateTimeInterface) { + return $this->format === null; + } + + if (!is_scalar($input)) { + return false; + } + + if ($this->format === null) { + return strtotime((string) $input) !== false; + } + + return $this->isDateTime($this->format, (string) $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Decimal.php b/vendor/workerman/validation/library/Rules/Decimal.php new file mode 100644 index 0000000..49161ea --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Decimal.php @@ -0,0 +1,71 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_numeric; +use function is_string; +use function number_format; +use function preg_replace; +use function var_export; + +/** + * Validates the decimal + * + * @author Henrique Moody + */ +final class Decimal extends AbstractRule +{ + /** + * @var int + */ + private $decimals; + + public function __construct(int $decimals) + { + $this->decimals = $decimals; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_numeric($input)) { + return false; + } + + return $this->toFormattedString($input) === $this->toRawString($input); + } + + /** + * @param mixed $input + */ + private function toRawString($input): string + { + if (is_string($input)) { + return $input; + } + + return var_export($input, true); + } + + /** + * @param mixed $input + */ + private function toFormattedString($input): string + { + $formatted = number_format((float) $input, $this->decimals, '.', ''); + if (is_string($input)) { + return $formatted; + } + + return preg_replace('/^(\d+\.\d)0*$/', '$1', $formatted) ?: ''; + } +} diff --git a/vendor/workerman/validation/library/Rules/Digit.php b/vendor/workerman/validation/library/Rules/Digit.php new file mode 100644 index 0000000..acb0b7a --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Digit.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function ctype_digit; + +/** + * Validates whether the input contains only digits. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Nick Lombard + */ +final class Digit extends AbstractFilterRule +{ + /** + * {@inheritDoc} + */ + protected function validateFilteredInput(string $input): bool + { + return ctype_digit($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Directory.php b/vendor/workerman/validation/library/Rules/Directory.php new file mode 100644 index 0000000..70bd3d2 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Directory.php @@ -0,0 +1,45 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Directory as NativeDirectory; +use SplFileInfo; + +use function is_dir; +use function is_scalar; + +/** + * Validates if the given path is a directory. + * + * @author Henrique Moody + * @author William Espindola + */ +final class Directory extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof SplFileInfo) { + return $input->isDir(); + } + + if ($input instanceof NativeDirectory) { + return true; + } + + if (!is_scalar($input)) { + return false; + } + + return is_dir((string) $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Domain.php b/vendor/workerman/validation/library/Rules/Domain.php new file mode 100644 index 0000000..a731916 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Domain.php @@ -0,0 +1,180 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\DomainException; +use Respect\Validation\Exceptions\NestedValidationException; +use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\Validatable; + +use function array_merge; +use function array_pop; +use function count; +use function explode; +use function iterator_to_array; +use function mb_substr_count; + +/** + * Validates whether the input is a valid domain name or not. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Mehmet Tolga Avcioglu + * @author Nick Lombard + * @author Róbert Nagy + */ +final class Domain extends AbstractRule +{ + /** + * @var Validatable + */ + private $genericRule; + + /** + * @var Validatable + */ + private $tldRule; + + /** + * @var Validatable + */ + private $partsRule; + + public function __construct(bool $tldCheck = true) + { + $this->genericRule = $this->createGenericRule(); + $this->tldRule = $this->createTldRule($tldCheck); + $this->partsRule = $this->createPartsRule(); + } + + /** + * {@inheritDoc} + */ + public function assert($input): void + { + $exceptions = []; + + $this->collectAssertException($exceptions, $this->genericRule, $input); + $this->throwExceptions($exceptions, $input); + + $parts = explode('.', (string) $input); + if (count($parts) >= 2) { + $this->collectAssertException($exceptions, $this->tldRule, array_pop($parts)); + } + + foreach ($parts as $part) { + $this->collectAssertException($exceptions, $this->partsRule, $part); + } + + $this->throwExceptions($exceptions, $input); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + try { + $this->assert($input); + } catch (ValidationException $exception) { + return false; + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + try { + $this->assert($input); + } catch (NestedValidationException $exception) { + /** @var ValidationException $childException */ + foreach ($exception as $childException) { + throw $childException; + } + + throw $exception; + } + } + + /** + * @param ValidationException[] $exceptions + * @param mixed $input + */ + private function collectAssertException(array &$exceptions, Validatable $validator, $input): void + { + try { + $validator->assert($input); + } catch (NestedValidationException $nestedValidationException) { + $exceptions = array_merge( + $exceptions, + iterator_to_array($nestedValidationException) + ); + } catch (ValidationException $validationException) { + $exceptions[] = $validationException; + } + } + + private function createGenericRule(): Validatable + { + return new AllOf( + new StringType(), + new NoWhitespace(), + new Contains('.'), + new Length(3) + ); + } + + private function createTldRule(bool $realTldCheck): Validatable + { + if ($realTldCheck) { + return new Tld(); + } + + return new AllOf( + new Not(new StartsWith('-')), + new NoWhitespace(), + new Length(2) + ); + } + + private function createPartsRule(): Validatable + { + return new AllOf( + new Alnum('-'), + new Not(new StartsWith('-')), + new AnyOf( + new Not(new Contains('--')), + new Callback(static function ($str) { + return mb_substr_count($str, '--') == 1; + }) + ), + new Not(new EndsWith('-')) + ); + } + + /** + * @param ValidationException[] $exceptions + * @param mixed $input + */ + private function throwExceptions(array $exceptions, $input): void + { + if (count($exceptions)) { + /** @var DomainException $domainException */ + $domainException = $this->reportError($input); + $domainException->addChildren($exceptions); + + throw $domainException; + } + } +} diff --git a/vendor/workerman/validation/library/Rules/Each.php b/vendor/workerman/validation/library/Rules/Each.php new file mode 100644 index 0000000..2da5fe1 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Each.php @@ -0,0 +1,96 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\EachException; +use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\Helpers\CanValidateIterable; +use Respect\Validation\Validatable; + +/** + * Validates whether each value in the input is valid according to another rule. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Nick Lombard + * @author William Espindola + */ +final class Each extends AbstractRule +{ + use CanValidateIterable; + + /** + * @var Validatable + */ + private $rule; + + /** + * Initializes the constructor. + */ + public function __construct(Validatable $rule) + { + $this->rule = $rule; + } + + /** + * {@inheritDoc} + */ + public function assert($input): void + { + if (!$this->isIterable($input)) { + throw $this->reportError($input); + } + + $exceptions = []; + foreach ($input as $value) { + try { + $this->rule->assert($value); + } catch (ValidationException $exception) { + $exceptions[] = $exception; + } + } + + if (!empty($exceptions)) { + /** @var EachException $eachException */ + $eachException = $this->reportError($input); + $eachException->addChildren($exceptions); + + throw $eachException; + } + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + if (!$this->isIterable($input)) { + throw $this->reportError($input); + } + + foreach ($input as $value) { + $this->rule->check($value); + } + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + try { + $this->check($input); + } catch (ValidationException $exception) { + return false; + } + + return true; + } +} diff --git a/vendor/workerman/validation/library/Rules/Email.php b/vendor/workerman/validation/library/Rules/Email.php new file mode 100644 index 0000000..f638cb3 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Email.php @@ -0,0 +1,70 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Egulias\EmailValidator\EmailValidator; +use Egulias\EmailValidator\Validation\RFCValidation; + +use function class_exists; +use function filter_var; +use function is_string; + +use const FILTER_VALIDATE_EMAIL; + +/** + * Validates an email address. + * + * @author Andrey Kolyshkin + * @author Eduardo Gulias Davis + * @author Henrique Moody + * @author Paul Karikari + */ +final class Email extends AbstractRule +{ + /** + * @var EmailValidator|null + */ + private $validator; + + /** + * Initializes the rule assigning the EmailValidator instance. + * + * If the EmailValidator instance is not defined, tries to create one. + */ + public function __construct(?EmailValidator $validator = null) + { + $this->validator = $validator ?: $this->createEmailValidator(); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + if ($this->validator !== null) { + return $this->validator->isValid($input, new RFCValidation()); + } + + return (bool) filter_var($input, FILTER_VALIDATE_EMAIL); + } + + private function createEmailValidator(): ?EmailValidator + { + if (class_exists(EmailValidator::class)) { + return new EmailValidator(); + } + + return null; + } +} diff --git a/vendor/workerman/validation/library/Rules/EndsWith.php b/vendor/workerman/validation/library/Rules/EndsWith.php new file mode 100644 index 0000000..790db18 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/EndsWith.php @@ -0,0 +1,82 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function end; +use function is_array; +use function mb_strlen; +use function mb_strripos; +use function mb_strrpos; + +/** + * Validates only if the value is at the end of the input. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Hugo Hamon + * @author William Espindola + */ +final class EndsWith extends AbstractRule +{ + /** + * @var mixed + */ + private $endValue; + + /** + * @var bool + */ + private $identical; + + /** + * @param mixed $endValue + */ + public function __construct($endValue, bool $identical = false) + { + $this->endValue = $endValue; + $this->identical = $identical; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($this->identical) { + return $this->validateIdentical($input); + } + + return $this->validateEquals($input); + } + + /** + * @param mixed $input + */ + private function validateEquals($input): bool + { + if (is_array($input)) { + return end($input) == $this->endValue; + } + + return mb_strripos($input, $this->endValue) === mb_strlen($input) - mb_strlen($this->endValue); + } + + /** + * @param mixed $input + */ + private function validateIdentical($input): bool + { + if (is_array($input)) { + return end($input) === $this->endValue; + } + + return mb_strrpos($input, $this->endValue) === mb_strlen($input) - mb_strlen($this->endValue); + } +} diff --git a/vendor/workerman/validation/library/Rules/Equals.php b/vendor/workerman/validation/library/Rules/Equals.php new file mode 100644 index 0000000..012984b --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Equals.php @@ -0,0 +1,43 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates if the input is equal to some value. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Hugo Hamon + */ +final class Equals extends AbstractRule +{ + /** + * @var mixed + */ + private $compareTo; + + /** + * Initializes the rule. + * + * @param mixed $compareTo + */ + public function __construct($compareTo) + { + $this->compareTo = $compareTo; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return $input == $this->compareTo; + } +} diff --git a/vendor/workerman/validation/library/Rules/Equivalent.php b/vendor/workerman/validation/library/Rules/Equivalent.php new file mode 100644 index 0000000..3945f1c --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Equivalent.php @@ -0,0 +1,57 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_scalar; +use function mb_strtoupper; + +/** + * Validates if the input is equivalent to some value. + * + * @author Henrique Moody + */ +final class Equivalent extends AbstractRule +{ + /** + * @var mixed + */ + private $compareTo; + + /** + * Initializes the rule. + * + * @param mixed $compareTo + */ + public function __construct($compareTo) + { + $this->compareTo = $compareTo; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (is_scalar($input)) { + return $this->isStringEquivalent((string) $input); + } + + return $input == $this->compareTo; + } + + private function isStringEquivalent(string $input): bool + { + if (!is_scalar($this->compareTo)) { + return false; + } + + return mb_strtoupper((string) $input) === mb_strtoupper((string) $this->compareTo); + } +} diff --git a/vendor/workerman/validation/library/Rules/Even.php b/vendor/workerman/validation/library/Rules/Even.php new file mode 100644 index 0000000..b21268e --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Even.php @@ -0,0 +1,36 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function filter_var; + +use const FILTER_VALIDATE_INT; + +/** + * Validates whether the input is an even number or not. + * + * @author Henrique Moody + * @author Jean Pimentel + * @author Paul Karikari + */ +final class Even extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (filter_var($input, FILTER_VALIDATE_INT) === false) { + return false; + } + + return (int) $input % 2 === 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/Executable.php b/vendor/workerman/validation/library/Rules/Executable.php new file mode 100644 index 0000000..a1f2ce2 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Executable.php @@ -0,0 +1,40 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use SplFileInfo; + +use function is_executable; +use function is_scalar; + +/** + * Validates if a file is an executable. + * + * @author Henrique Moody + * @author William Espindola + */ +final class Executable extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof SplFileInfo) { + return $input->isExecutable(); + } + + if (!is_scalar($input)) { + return false; + } + + return is_executable((string) $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Exists.php b/vendor/workerman/validation/library/Rules/Exists.php new file mode 100644 index 0000000..c1bff93 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Exists.php @@ -0,0 +1,34 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use SplFileInfo; + +use function file_exists; +use function is_string; + +/** + * @author Henrique Moody + * @author William Espindola + */ +final class Exists extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof SplFileInfo) { + $input = $input->getPathname(); + } + + return is_string($input) && file_exists($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Extension.php b/vendor/workerman/validation/library/Rules/Extension.php new file mode 100644 index 0000000..ed2a9a4 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Extension.php @@ -0,0 +1,55 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use SplFileInfo; + +use function is_string; +use function pathinfo; + +use const PATHINFO_EXTENSION; + +/** + * Validate file extensions. + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class Extension extends AbstractRule +{ + /** + * @var string + */ + private $extension; + + /** + * Initializes the rule. + */ + public function __construct(string $extension) + { + $this->extension = $extension; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof SplFileInfo) { + return $this->extension === $input->getExtension(); + } + + if (!is_string($input)) { + return false; + } + + return $this->extension === pathinfo($input, PATHINFO_EXTENSION); + } +} diff --git a/vendor/workerman/validation/library/Rules/Factor.php b/vendor/workerman/validation/library/Rules/Factor.php new file mode 100644 index 0000000..170f99a --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Factor.php @@ -0,0 +1,61 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function abs; +use function is_integer; +use function is_numeric; + +/** + * Validates if the input is a factor of the defined dividend. + * + * @author Danilo Correa + * @author David Meister + * @author Henrique Moody + */ +final class Factor extends AbstractRule +{ + /** + * @var int + */ + private $dividend; + + /** + * Initializes the rule. + */ + public function __construct(int $dividend) + { + $this->dividend = $dividend; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + // Every integer is a factor of zero, and zero is the only integer that + // has zero for a factor. + if ($this->dividend === 0) { + return true; + } + + // Factors must be integers that are not zero. + if (!is_numeric($input) || (int) $input != $input || $input == 0) { + return false; + } + + $input = (int) abs((int) $input); + $dividend = (int) abs($this->dividend); + + // The dividend divided by the input must be an integer if input is a + // factor of the dividend. + return is_integer($dividend / $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/FalseVal.php b/vendor/workerman/validation/library/Rules/FalseVal.php new file mode 100644 index 0000000..aba23d5 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/FalseVal.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function filter_var; + +use const FILTER_NULL_ON_FAILURE; +use const FILTER_VALIDATE_BOOLEAN; + +/** + * Validates if a value is considered as false. + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class FalseVal extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) === false; + } +} diff --git a/vendor/workerman/validation/library/Rules/Fibonacci.php b/vendor/workerman/validation/library/Rules/Fibonacci.php new file mode 100644 index 0000000..c48476d --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Fibonacci.php @@ -0,0 +1,41 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_numeric; + +/** + * Validates whether the input follows the Fibonacci integer sequence. + * + * @author Danilo Correa + * @author Henrique Moody + * @author Samuel Heinzmann + */ +final class Fibonacci extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_numeric($input)) { + return false; + } + + $sequence = [0, 1]; + $position = 1; + while ($input > $sequence[$position]) { + ++$position; + $sequence[$position] = $sequence[$position - 1] + $sequence[$position - 2]; + } + + return $sequence[$position] === (int) $input; + } +} diff --git a/vendor/workerman/validation/library/Rules/File.php b/vendor/workerman/validation/library/Rules/File.php new file mode 100644 index 0000000..fcf8a7c --- /dev/null +++ b/vendor/workerman/validation/library/Rules/File.php @@ -0,0 +1,36 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use SplFileInfo; + +use function is_file; +use function is_string; + +/** + * Validates whether file input is as a regular filename. + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class File extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof SplFileInfo) { + return $input->isFile(); + } + + return is_string($input) && is_file($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/FilterVar.php b/vendor/workerman/validation/library/Rules/FilterVar.php new file mode 100644 index 0000000..23463e2 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/FilterVar.php @@ -0,0 +1,70 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use function array_key_exists; +use function filter_var; +use function is_array; +use function is_int; + +use const FILTER_VALIDATE_BOOLEAN; +use const FILTER_VALIDATE_DOMAIN; +use const FILTER_VALIDATE_EMAIL; +use const FILTER_VALIDATE_FLOAT; +use const FILTER_VALIDATE_INT; +use const FILTER_VALIDATE_IP; +use const FILTER_VALIDATE_REGEXP; +use const FILTER_VALIDATE_URL; + +/** + * Validates the input with the PHP's filter_var() function. + * + * @author Henrique Moody + */ +final class FilterVar extends AbstractEnvelope +{ + private const ALLOWED_FILTERS = [ + FILTER_VALIDATE_BOOLEAN => 'is_bool', + FILTER_VALIDATE_DOMAIN => 'is_string', + FILTER_VALIDATE_EMAIL => 'is_string', + FILTER_VALIDATE_FLOAT => 'is_float', + FILTER_VALIDATE_INT => 'is_int', + FILTER_VALIDATE_IP => 'is_string', + FILTER_VALIDATE_REGEXP => 'is_string', + FILTER_VALIDATE_URL => 'is_string', + ]; + + /** + * Initializes the rule. + * + * @param mixed $options + * + * @throws ComponentException + */ + public function __construct(int $filter, $options = []) + { + if (!array_key_exists($filter, self::ALLOWED_FILTERS)) { + throw new ComponentException('Cannot accept the given filter'); + } + + $arguments = [$filter]; + if (is_array($options) || is_int($options)) { + $arguments[] = $options; + } + + parent::__construct(new Callback(static function ($input) use ($filter, $arguments) { + return (self::ALLOWED_FILTERS[$filter])( + filter_var($input, ...$arguments) + ); + })); + } +} diff --git a/vendor/workerman/validation/library/Rules/Finite.php b/vendor/workerman/validation/library/Rules/Finite.php new file mode 100644 index 0000000..26f7754 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Finite.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_finite; +use function is_numeric; + +/** + * Validates if the input is a finite number. + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class Finite extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_numeric($input) && is_finite((float) $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/FloatType.php b/vendor/workerman/validation/library/Rules/FloatType.php new file mode 100644 index 0000000..70d4964 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/FloatType.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_float; + +/** + * Validates whether the type of the input is float. + * + * @author Henrique Moody + * @author Reginaldo Junior <76regi@gmail.com> + */ +final class FloatType extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_float($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/FloatVal.php b/vendor/workerman/validation/library/Rules/FloatVal.php new file mode 100644 index 0000000..2b7c55b --- /dev/null +++ b/vendor/workerman/validation/library/Rules/FloatVal.php @@ -0,0 +1,34 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function filter_var; +use function is_float; + +use const FILTER_VALIDATE_FLOAT; + +/** + * Validate whether the input value is float. + * + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + * @author Jayson Reis + */ +final class FloatVal extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_float(filter_var($input, FILTER_VALIDATE_FLOAT)); + } +} diff --git a/vendor/workerman/validation/library/Rules/Graph.php b/vendor/workerman/validation/library/Rules/Graph.php new file mode 100644 index 0000000..ca04fcf --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Graph.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function ctype_graph; + +/** + * Validates if all characters in the input are printable and actually creates visible output (no white space). + * + * @author Andre Ramaciotti + * @author Danilo Correa + * @author Henrique Moody + * @author Nick Lombard + */ +final class Graph extends AbstractFilterRule +{ + /** + * {@inheritDoc} + */ + protected function validateFilteredInput(string $input): bool + { + return ctype_graph($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/GreaterThan.php b/vendor/workerman/validation/library/Rules/GreaterThan.php new file mode 100644 index 0000000..d05abd7 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/GreaterThan.php @@ -0,0 +1,26 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates whether the input is less than a value. + * + * @author Henrique Moody + */ +final class GreaterThan extends AbstractComparison +{ + /** + * {@inheritDoc} + */ + protected function compare($left, $right): bool + { + return $left > $right; + } +} diff --git a/vendor/workerman/validation/library/Rules/HexRgbColor.php b/vendor/workerman/validation/library/Rules/HexRgbColor.php new file mode 100644 index 0000000..51af1bf --- /dev/null +++ b/vendor/workerman/validation/library/Rules/HexRgbColor.php @@ -0,0 +1,24 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates weather the input is a hex RGB color or not. + * + * @author Davide Pastore + * @author Henrique Moody + */ +final class HexRgbColor extends AbstractEnvelope +{ + public function __construct() + { + parent::__construct(new Regex('/^#?([0-9A-F]{3}|[0-9A-F]{6})$/i')); + } +} diff --git a/vendor/workerman/validation/library/Rules/Iban.php b/vendor/workerman/validation/library/Rules/Iban.php new file mode 100644 index 0000000..89484f4 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Iban.php @@ -0,0 +1,149 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function bcmod; +use function is_string; +use function ord; +use function preg_match; +use function preg_replace_callback; +use function str_replace; +use function strlen; +use function strval; +use function substr; + +/** + * Validates whether the input is a valid IBAN (International Bank Account Number) or not. + * + * @author Mazen Touati + */ +final class Iban extends AbstractRule +{ + private const COUNTRIES_LENGTHS = [ + 'AL' => 28, + 'AD' => 24, + 'AT' => 20, + 'AZ' => 28, + 'BH' => 22, + 'BE' => 16, + 'BA' => 20, + 'BR' => 29, + 'BG' => 22, + 'CR' => 21, + 'HR' => 21, + 'CY' => 28, + 'CZ' => 24, + 'DK' => 18, + 'DO' => 28, + 'EE' => 20, + 'FO' => 18, + 'FI' => 18, + 'FR' => 27, + 'GE' => 22, + 'DE' => 22, + 'GI' => 23, + 'GR' => 27, + 'GL' => 18, + 'GT' => 28, + 'HU' => 28, + 'IS' => 26, + 'IE' => 22, + 'IL' => 23, + 'IT' => 27, + 'JO' => 30, + 'KZ' => 20, + 'KW' => 30, + 'LV' => 21, + 'LB' => 28, + 'LI' => 21, + 'LT' => 20, + 'LU' => 20, + 'MK' => 19, + 'MT' => 31, + 'MR' => 27, + 'MU' => 30, + 'MD' => 24, + 'MC' => 27, + 'ME' => 22, + 'NL' => 18, + 'NO' => 15, + 'PK' => 24, + 'PL' => 28, + 'PS' => 29, + 'PT' => 25, + 'QA' => 29, + 'XK' => 20, + 'RO' => 24, + 'LC' => 32, + 'SM' => 27, + 'ST' => 25, + 'SA' => 24, + 'RS' => 22, + 'SC' => 31, + 'SK' => 24, + 'SI' => 19, + 'ES' => 24, + 'SE' => 24, + 'CH' => 21, + 'TL' => 23, + 'TN' => 24, + 'TR' => 26, + 'UA' => 29, + 'AE' => 23, + 'GB' => 22, + 'VG' => 24, + ]; + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + $iban = str_replace(' ', '', $input); + if (!preg_match('/[A-Z0-9]{15,34}/', $iban)) { + return false; + } + + $countryCode = substr($iban, 0, 2); + if (!$this->hasValidCountryLength($iban, $countryCode)) { + return false; + } + + $checkDigits = substr($iban, 2, 2); + $bban = substr($iban, 4); + $rearranged = $bban . $countryCode . $checkDigits; + + return bcmod($this->convertToIntegerAsString($rearranged), '97') === '1'; + } + + private function hasValidCountryLength(string $iban, string $countryCode): bool + { + if (!isset(self::COUNTRIES_LENGTHS[$countryCode])) { + return false; + } + + return strlen($iban) === self::COUNTRIES_LENGTHS[$countryCode]; + } + + private function convertToIntegerAsString(string $reArrangedIban): string + { + return (string) preg_replace_callback( + '/[A-Z]/', + static function (array $match): string { + return strval(ord($match[0]) - 55); + }, + $reArrangedIban + ); + } +} diff --git a/vendor/workerman/validation/library/Rules/Identical.php b/vendor/workerman/validation/library/Rules/Identical.php new file mode 100644 index 0000000..02ee01c --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Identical.php @@ -0,0 +1,41 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates if the input is identical to some value. + * + * @author Henrique Moody + */ +final class Identical extends AbstractRule +{ + /** + * @var mixed + */ + private $compareTo; + + /** + * Initializes the rule. + * + * @param mixed $compareTo + */ + public function __construct($compareTo) + { + $this->compareTo = $compareTo; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return $input === $this->compareTo; + } +} diff --git a/vendor/workerman/validation/library/Rules/Image.php b/vendor/workerman/validation/library/Rules/Image.php new file mode 100644 index 0000000..619d8c2 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Image.php @@ -0,0 +1,62 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use finfo; +use SplFileInfo; + +use function is_file; +use function is_string; +use function mb_strpos; + +use const FILEINFO_MIME_TYPE; + +/** + * Validates if the file is a valid image by checking its MIME type. + * + * @author Danilo Benevides + * @author Guilherme Siani + * @author Henrique Moody + */ +final class Image extends AbstractRule +{ + /** + * @var finfo + */ + private $fileInfo; + + /** + * Initializes the rule. + */ + public function __construct(?finfo $fileInfo = null) + { + $this->fileInfo = $fileInfo ?: new finfo(FILEINFO_MIME_TYPE); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof SplFileInfo) { + return $this->validate($input->getPathname()); + } + + if (!is_string($input)) { + return false; + } + + if (!is_file($input)) { + return false; + } + + return mb_strpos((string) $this->fileInfo->file($input), 'image/') === 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/Imei.php b/vendor/workerman/validation/library/Rules/Imei.php new file mode 100644 index 0000000..130e5c7 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Imei.php @@ -0,0 +1,46 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_scalar; +use function mb_strlen; +use function preg_replace; + +/** + * Validates is the input is a valid IMEI. + * + * @author Alexander Gorshkov + * @author Danilo Benevides + * @author Diego Oliveira + * @author Henrique Moody + */ +final class Imei extends AbstractRule +{ + private const IMEI_SIZE = 15; + + /** + * @see https://en.wikipedia.org/wiki/International_Mobile_Station_Equipment_Identity + * + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + $numbers = (string) preg_replace('/\D/', '', (string) $input); + if (mb_strlen($numbers) != self::IMEI_SIZE) { + return false; + } + + return (new Luhn())->validate($numbers); + } +} diff --git a/vendor/workerman/validation/library/Rules/In.php b/vendor/workerman/validation/library/Rules/In.php new file mode 100644 index 0000000..f737448 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/In.php @@ -0,0 +1,90 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function in_array; +use function is_array; +use function mb_stripos; +use function mb_strpos; + +/** + * Validates if the input can be found in a defined array or string. + * + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + */ +final class In extends AbstractRule +{ + /** + * @var mixed[]|mixed + */ + private $haystack; + + /** + * @var bool + */ + private $compareIdentical; + + /** + * Initializes the rule with the haystack and optionally compareIdentical flag. + * + * @param mixed[]|mixed $haystack + */ + public function __construct($haystack, bool $compareIdentical = false) + { + $this->haystack = $haystack; + $this->compareIdentical = $compareIdentical; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($this->compareIdentical) { + return $this->validateIdentical($input); + } + + return $this->validateEquals($input); + } + + /** + * @param mixed $input + */ + private function validateEquals($input): bool + { + if (is_array($this->haystack)) { + return in_array($input, $this->haystack); + } + + if ($input === null || $input === '') { + return $input == $this->haystack; + } + + return mb_stripos($this->haystack, (string) $input) !== false; + } + + /** + * @param mixed $input + */ + private function validateIdentical($input): bool + { + if (is_array($this->haystack)) { + return in_array($input, $this->haystack, true); + } + + if ($input === null || $input === '') { + return $input === $this->haystack; + } + + return mb_strpos($this->haystack, (string) $input) !== false; + } +} diff --git a/vendor/workerman/validation/library/Rules/Infinite.php b/vendor/workerman/validation/library/Rules/Infinite.php new file mode 100644 index 0000000..55c4706 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Infinite.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_infinite; +use function is_numeric; + +/** + * Validates if the input is an infinite number + * + * @author Danilo Benevides + * @author Henrique Moody + */ +final class Infinite extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_numeric($input) && is_infinite((float) $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Instance.php b/vendor/workerman/validation/library/Rules/Instance.php new file mode 100644 index 0000000..d7a6744 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Instance.php @@ -0,0 +1,41 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates if the input is an instance of the given class or interface. + * + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + */ +final class Instance extends AbstractRule +{ + /** + * @var string + */ + private $instanceName; + + /** + * Initializes the rule with the expected instance name. + */ + public function __construct(string $instanceName) + { + $this->instanceName = $instanceName; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return $input instanceof $this->instanceName; + } +} diff --git a/vendor/workerman/validation/library/Rules/IntType.php b/vendor/workerman/validation/library/Rules/IntType.php new file mode 100644 index 0000000..561dc43 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/IntType.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_int; + +/** + * Validates whether the type of the input is integer. + * + * @author Henrique Moody + */ +final class IntType extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_int($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/IntVal.php b/vendor/workerman/validation/library/Rules/IntVal.php new file mode 100644 index 0000000..d147a44 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/IntVal.php @@ -0,0 +1,42 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_int; +use function is_string; +use function preg_match; + +/** + * Validates if the input is an integer. + * + * @author Adam Benson + * @author Alexandre Gomes Gaigalas + * @author Andrei Drulchenko + * @author Danilo Benevides + * @author Henrique Moody + */ +final class IntVal extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (is_int($input)) { + return true; + } + + if (!is_string($input)) { + return false; + } + + return preg_match('/^-?\d+$/', $input) === 1; + } +} diff --git a/vendor/workerman/validation/library/Rules/Ip.php b/vendor/workerman/validation/library/Rules/Ip.php new file mode 100644 index 0000000..a725533 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Ip.php @@ -0,0 +1,209 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use function bccomp; +use function explode; +use function filter_var; +use function ip2long; +use function is_string; +use function long2ip; +use function mb_strpos; +use function mb_substr_count; +use function sprintf; +use function str_repeat; +use function str_replace; +use function strtr; + +use const FILTER_VALIDATE_IP; + +/** + * Validates whether the input is a valid IP address. + * + * This validator uses the native filter_var() PHP function. + * + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + * @author Luís Otávio Cobucci Oblonczyk + */ +final class Ip extends AbstractRule +{ + /** + * @var string|null + */ + private $range; + + /** + * @var int|null + */ + private $options; + + /** + * @var string|null + */ + private $startAddress; + + /** + * @var string|null + */ + private $endAddress; + + /** + * @var string|null + */ + private $mask; + + /** + * Initializes the rule defining the range and some options for filter_var(). + * + * @throws ComponentException In case the range is invalid + */ + public function __construct(string $range = '*', ?int $options = null) + { + $this->parseRange($range); + $this->range = $this->createRange(); + $this->options = $options; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + if (!$this->verifyAddress($input)) { + return false; + } + + if ($this->mask) { + return $this->belongsToSubnet($input); + } + + if ($this->startAddress && $this->endAddress) { + return $this->verifyNetwork($input); + } + + return true; + } + + private function createRange(): ?string + { + if ($this->startAddress && $this->endAddress) { + return $this->startAddress . '-' . $this->endAddress; + } + + if ($this->startAddress && $this->mask) { + return $this->startAddress . '/' . long2ip((int) $this->mask); + } + + return null; + } + + private function parseRange(string $input): void + { + if ($input == '*' || $input == '*.*.*.*' || $input == '0.0.0.0-255.255.255.255') { + return; + } + + if (mb_strpos($input, '-') !== false) { + [$this->startAddress, $this->endAddress] = explode('-', $input); + + if ($this->startAddress !== null && !$this->verifyAddress($this->startAddress)) { + throw new ComponentException('Invalid network range'); + } + + if ($this->endAddress !== null && !$this->verifyAddress($this->endAddress)) { + throw new ComponentException('Invalid network range'); + } + + return; + } + + if (mb_strpos($input, '*') !== false) { + $this->parseRangeUsingWildcards($input); + + return; + } + + if (mb_strpos($input, '/') !== false) { + $this->parseRangeUsingCidr($input); + + return; + } + + throw new ComponentException('Invalid network range'); + } + + private function fillAddress(string $address, string $fill = '*'): string + { + return $address . str_repeat('.' . $fill, 3 - mb_substr_count($address, '.')); + } + + private function parseRangeUsingWildcards(string $input): void + { + $address = $this->fillAddress($input); + + $this->startAddress = strtr($address, '*', '0'); + $this->endAddress = str_replace('*', '255', $address); + } + + private function parseRangeUsingCidr(string $input): void + { + $parts = explode('/', $input); + + $this->startAddress = $this->fillAddress($parts[0], '0'); + $isAddressMask = mb_strpos($parts[1], '.') !== false; + + if ($isAddressMask && $this->verifyAddress($parts[1])) { + $this->mask = sprintf('%032b', ip2long($parts[1])); + + return; + } + + if ($isAddressMask || $parts[1] < 8 || $parts[1] > 30) { + throw new ComponentException('Invalid network mask'); + } + + $this->mask = sprintf('%032b', ip2long((string) long2ip(~(2 ** (32 - (int) $parts[1]) - 1)))); + } + + private function verifyAddress(string $address): bool + { + return filter_var($address, FILTER_VALIDATE_IP, ['flags' => $this->options]) !== false; + } + + private function verifyNetwork(string $input): bool + { + $input = sprintf('%u', ip2long($input)); + + return $this->startAddress !== null + && $this->endAddress !== null + && bccomp($input, sprintf('%u', ip2long($this->startAddress))) >= 0 + && bccomp($input, sprintf('%u', ip2long($this->endAddress))) <= 0; + } + + private function belongsToSubnet(string $input): bool + { + if ($this->mask === null || $this->startAddress === null) { + return false; + } + + $min = sprintf('%032b', ip2long($this->startAddress)); + $input = sprintf('%032b', ip2long($input)); + + return ($input & $this->mask) === ($min & $this->mask); + } +} diff --git a/vendor/workerman/validation/library/Rules/Isbn.php b/vendor/workerman/validation/library/Rules/Isbn.php new file mode 100644 index 0000000..4c0dd14 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Isbn.php @@ -0,0 +1,45 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function implode; +use function is_scalar; +use function preg_match; +use function sprintf; + +/** + * Validates whether the input is a valid ISBN (International Standard Book Number) or not. + * + * @author Henrique Moody + * @author Moritz Fromm + */ +final class Isbn extends AbstractRule +{ + /** + * @see https://howtodoinjava.com/regex/java-regex-validate-international-standard-book-number-isbns + */ + private const PIECES = [ + '^(?:ISBN(?:-1[03])?:? )?(?=[0-9X]{10}$|(?=(?:[0-9]+[- ]){3})', + '[- 0-9X]{13}$|97[89][0-9]{10}$|(?=(?:[0-9]+[- ]){4})[- 0-9]{17}$)', + '(?:97[89][- ]?)?[0-9]{1,5}[- ]?[0-9]+[- ]?[0-9]+[- ]?[0-9X]$', + ]; + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + return preg_match(sprintf('/%s/', implode(self::PIECES)), (string) $input) > 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/IterableType.php b/vendor/workerman/validation/library/Rules/IterableType.php new file mode 100644 index 0000000..92bd300 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/IterableType.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Helpers\CanValidateIterable; + +/** + * Validates whether the pseudo-type of the input is iterable or not. + * + * @author Henrique Moody + */ +final class IterableType extends AbstractRule +{ + use CanValidateIterable; + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return $this->isIterable($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Json.php b/vendor/workerman/validation/library/Rules/Json.php new file mode 100644 index 0000000..5ce14fc --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Json.php @@ -0,0 +1,44 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function function_exists; +use function is_string; +use function json_decode; +use function json_last_error; + +use const JSON_ERROR_NONE; + +/** + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class Json extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input) || $input === '') { + return false; + } + + if (function_exists('json_validate')) { + return json_validate($input); + } + + json_decode($input); + + return json_last_error() === JSON_ERROR_NONE; + } +} diff --git a/vendor/workerman/validation/library/Rules/Key.php b/vendor/workerman/validation/library/Rules/Key.php new file mode 100644 index 0000000..fa84291 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Key.php @@ -0,0 +1,53 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; +use Respect\Validation\Validatable; + +use function array_key_exists; +use function is_array; +use function is_scalar; + +/** + * @author Alexandre Gomes Gaigalas + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class Key extends AbstractRelated +{ + /** + * @param mixed $reference + */ + public function __construct($reference, ?Validatable $rule = null, bool $mandatory = true) + { + if (!is_scalar($reference) || $reference === '') { + throw new ComponentException('Invalid array key name'); + } + + parent::__construct($reference, $rule, $mandatory); + } + + /** + * {@inheritDoc} + */ + public function getReferenceValue($input) + { + return $input[$this->getReference()]; + } + + /** + * {@inheritDoc} + */ + public function hasReference($input): bool + { + return is_array($input) && array_key_exists($this->getReference(), $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/KeyNested.php b/vendor/workerman/validation/library/Rules/KeyNested.php new file mode 100644 index 0000000..582e94d --- /dev/null +++ b/vendor/workerman/validation/library/Rules/KeyNested.php @@ -0,0 +1,146 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use ArrayAccess; +use Respect\Validation\Exceptions\ComponentException; + +use function array_key_exists; +use function array_shift; +use function explode; +use function is_array; +use function is_null; +use function is_object; +use function is_scalar; +use function property_exists; +use function rtrim; +use function sprintf; + +/** + * @author Alexandre Gomes Gaigalas + * @author Emmerson Siqueira + * @author Henrique Moody + * @author Ivan Zinovyev + */ +final class KeyNested extends AbstractRelated +{ + /** + * {@inheritDoc} + */ + public function hasReference($input): bool + { + try { + $this->getReferenceValue($input); + } catch (ComponentException $cex) { + return false; + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function getReferenceValue($input) + { + if (is_scalar($input)) { + $message = sprintf('Cannot select the %s in the given data', $this->getReference()); + throw new ComponentException($message); + } + + $keys = $this->getReferencePieces(); + $value = $input; + while (!is_null($key = array_shift($keys))) { + $value = $this->getValue($value, $key); + } + + return $value; + } + + /** + * @return string[] + */ + private function getReferencePieces(): array + { + return explode('.', rtrim((string) $this->getReference(), '.')); + } + + /** + * @param mixed[] $array + * @param mixed $key + * + * @return mixed + */ + private function getValueFromArray(array $array, $key) + { + if (!array_key_exists($key, $array)) { + $message = sprintf('Cannot select the key %s from the given array', $this->getReference()); + throw new ComponentException($message); + } + + return $array[$key]; + } + + /** + * @param ArrayAccess $array + * @param mixed $key + * + * @return mixed + */ + private function getValueFromArrayAccess(ArrayAccess $array, $key) + { + if (!$array->offsetExists($key)) { + $message = sprintf('Cannot select the key %s from the given array', $this->getReference()); + throw new ComponentException($message); + } + + return $array->offsetGet($key); + } + + /** + * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint + * + * + * @return mixed + */ + private function getValueFromObject(object $object, string $property) + { + if (empty($property) || !property_exists($object, $property)) { + $message = sprintf('Cannot select the property %s from the given object', $this->getReference()); + throw new ComponentException($message); + } + + return $object->{$property}; + } + + /** + * @param mixed $value + * @param mixed $key + * + * @return mixed + */ + private function getValue($value, $key) + { + if (is_array($value)) { + return $this->getValueFromArray($value, $key); + } + + if ($value instanceof ArrayAccess) { + return $this->getValueFromArrayAccess($value, $key); + } + + if (is_object($value)) { + return $this->getValueFromObject($value, $key); + } + + $message = sprintf('Cannot select the property %s from the given data', $this->getReference()); + throw new ComponentException($message); + } +} diff --git a/vendor/workerman/validation/library/Rules/KeySet.php b/vendor/workerman/validation/library/Rules/KeySet.php new file mode 100644 index 0000000..accc5ed --- /dev/null +++ b/vendor/workerman/validation/library/Rules/KeySet.php @@ -0,0 +1,142 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; +use Respect\Validation\NonNegatable; +use Respect\Validation\Validatable; + +use function array_key_exists; +use function array_map; +use function count; +use function current; +use function is_array; + +/** + * Validates a keys in a defined structure. + * + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class KeySet extends AbstractWrapper implements NonNegatable +{ + /** + * @var mixed[] + */ + private $keys; + + /** + * @var mixed[] + */ + private $extraKeys = []; + + /** + * @var Key[] + */ + private $keyRules; + + /** + * Initializes the rule. + * + * phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.UselessAnnotation + * @param Validatable ...$validatables + */ + public function __construct(Validatable ...$validatables) + { + $this->keyRules = array_map([$this, 'getKeyRule'], $validatables); + $this->keys = array_map([$this, 'getKeyReference'], $this->keyRules); + + parent::__construct(new AllOf(...$this->keyRules)); + } + + /** + * {@inheritDoc} + */ + public function assert($input): void + { + if (!$this->hasValidStructure($input)) { + throw $this->reportError($input); + } + + parent::assert($input); + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + if (!$this->hasValidStructure($input)) { + throw $this->reportError($input); + } + + parent::check($input); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!$this->hasValidStructure($input)) { + return false; + } + + return parent::validate($input); + } + + /** + * @throws ComponentException + */ + private function getKeyRule(Validatable $validatable): Key + { + if ($validatable instanceof Key) { + return $validatable; + } + + if (!$validatable instanceof AllOf || count($validatable->getRules()) !== 1) { + throw new ComponentException('KeySet rule accepts only Key rules'); + } + + return $this->getKeyRule(current($validatable->getRules())); + } + + /** + * @return mixed + */ + private function getKeyReference(Key $rule) + { + return $rule->getReference(); + } + + /** + * @param mixed $input + */ + private function hasValidStructure($input): bool + { + if (!is_array($input)) { + return false; + } + + foreach ($this->keyRules as $keyRule) { + if (!array_key_exists($keyRule->getReference(), $input) && $keyRule->isMandatory()) { + return false; + } + + unset($input[$keyRule->getReference()]); + } + + foreach ($input as $extraKey => &$ignoreValue) { + $this->extraKeys[] = $extraKey; + } + + return count($input) == 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/KeyValue.php b/vendor/workerman/validation/library/Rules/KeyValue.php new file mode 100644 index 0000000..dee38e3 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/KeyValue.php @@ -0,0 +1,144 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; +use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\Factory; +use Respect\Validation\Validatable; + +use function array_keys; +use function in_array; + +/** + * @author Henrique Moody + */ +final class KeyValue extends AbstractRule +{ + /** + * @var int|string + */ + private $comparedKey; + + /** + * @var string + */ + private $ruleName; + + /** + * @var int|string + */ + private $baseKey; + + /** + * @param int|string $comparedKey + * @param int|string $baseKey + */ + public function __construct($comparedKey, string $ruleName, $baseKey) + { + $this->comparedKey = $comparedKey; + $this->ruleName = $ruleName; + $this->baseKey = $baseKey; + } + + /** + * {@inheritDoc} + */ + public function assert($input): void + { + $rule = $this->getRule($input); + + try { + $rule->assert($input[$this->comparedKey]); + } catch (ValidationException $exception) { + throw $this->overwriteExceptionParams($exception); + } + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + $rule = $this->getRule($input); + + try { + $rule->check($input[$this->comparedKey]); + } catch (ValidationException $exception) { + throw $this->overwriteExceptionParams($exception); + } + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + try { + $rule = $this->getRule($input); + } catch (ValidationException $e) { + return false; + } + + return $rule->validate($input[$this->comparedKey]); + } + + /** + * {@inheritDoc} + */ + public function reportError($input, array $extraParams = []): ValidationException + { + try { + return $this->overwriteExceptionParams($this->getRule($input)->reportError($input)); + } catch (ValidationException $exception) { + return $this->overwriteExceptionParams($exception); + } + } + + /** + * @param mixed $input + */ + private function getRule($input): Validatable + { + if (!isset($input[$this->comparedKey])) { + throw parent::reportError($this->comparedKey); + } + + if (!isset($input[$this->baseKey])) { + throw parent::reportError($this->baseKey); + } + + try { + $rule = Factory::getDefaultInstance()->rule($this->ruleName, [$input[$this->baseKey]]); + $rule->setName((string) $this->comparedKey); + } catch (ComponentException $exception) { + throw parent::reportError($input, ['component' => true]); + } + + return $rule; + } + + private function overwriteExceptionParams(ValidationException $exception): ValidationException + { + $params = []; + foreach (array_keys($exception->getParams()) as $key) { + if (in_array($key, ['template', 'translator'])) { + continue; + } + + $params[$key] = $this->baseKey; + } + $params['name'] = $this->comparedKey; + + $exception->updateParams($params); + + return $exception; + } +} diff --git a/vendor/workerman/validation/library/Rules/LanguageCode.php b/vendor/workerman/validation/library/Rules/LanguageCode.php new file mode 100644 index 0000000..ffcec15 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/LanguageCode.php @@ -0,0 +1,549 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use function array_column; +use function array_filter; +use function array_search; +use function sprintf; + +/** + * Validates whether the input is language code based on ISO 639. + * + * @author Danilo Benevides + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class LanguageCode extends AbstractEnvelope +{ + public const ALPHA2 = 'alpha-2'; + public const ALPHA3 = 'alpha-3'; + + public const AVAILABLE_SETS = [self::ALPHA2, self::ALPHA3]; + + /** + * @see http://www.loc.gov/standards/iso639-2/ISO-639-2_utf-8.txt + */ + public const LANGUAGE_CODES = [ + // phpcs:disable Squiz.PHP.CommentedOutCode.Found + ['aa', 'aar'], // Afar + ['ab', 'abk'], // Abkhazian + ['', 'ace'], // Achinese + ['', 'ach'], // Acoli + ['', 'ada'], // Adangme + ['', 'ady'], // Adyghe; Adygei + ['', 'afa'], // Afro-Asiatic languages + ['', 'afh'], // Afrihili + ['af', 'afr'], // Afrikaans + ['', 'ain'], // Ainu + ['ak', 'aka'], // Akan + ['', 'akk'], // Akkadian + ['sq', 'alb'], // Albanian + ['', 'ale'], // Aleut + ['', 'alg'], // Algonquian languages + ['', 'alt'], // Southern Altai + ['am', 'amh'], // Amharic + ['', 'ang'], // English, Old (ca.450-1100) + ['', 'anp'], // Angika + ['', 'apa'], // Apache languages + ['ar', 'ara'], // Arabic + ['', 'arc'], // Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE) + ['an', 'arg'], // Aragonese + ['hy', 'arm'], // Armenian + ['', 'arn'], // Mapudungun; Mapuche + ['', 'arp'], // Arapaho + ['', 'art'], // Artificial languages + ['', 'arw'], // Arawak + ['as', 'asm'], // Assamese + ['', 'ast'], // Asturian; Bable; Leonese; Asturleonese + ['', 'ath'], // Athapascan languages + ['', 'aus'], // Australian languages + ['av', 'ava'], // Avaric + ['ae', 'ave'], // Avestan + ['', 'awa'], // Awadhi + ['ay', 'aym'], // Aymara + ['az', 'aze'], // Azerbaijani + ['', 'bad'], // Banda languages + ['', 'bai'], // Bamileke languages + ['ba', 'bak'], // Bashkir + ['', 'bal'], // Baluchi + ['bm', 'bam'], // Bambara + ['', 'ban'], // Balinese + ['eu', 'baq'], // Basque + ['', 'bas'], // Basa + ['', 'bat'], // Baltic languages + ['', 'bej'], // Beja; Bedawiyet + ['be', 'bel'], // Belarusian + ['', 'bem'], // Bemba + ['bn', 'ben'], // Bengali + ['', 'ber'], // Berber languages + ['', 'bho'], // Bhojpuri + ['bh', 'bih'], // Bihari languages + ['', 'bik'], // Bikol + ['', 'bin'], // Bini; Edo + ['bi', 'bis'], // Bislama + ['', 'bla'], // Siksika + ['', 'bnt'], // Bantu languages + ['bs', 'bos'], // Bosnian + ['', 'bra'], // Braj + ['br', 'bre'], // Breton + ['', 'btk'], // Batak languages + ['', 'bua'], // Buriat + ['', 'bug'], // Buginese + ['bg', 'bul'], // Bulgarian + ['my', 'bur'], // Burmese + ['', 'byn'], // Blin; Bilin + ['', 'cad'], // Caddo + ['', 'cai'], // Central American Indian languages + ['', 'car'], // Galibi Carib + ['ca', 'cat'], // Catalan; Valencian + ['', 'cau'], // Caucasian languages + ['', 'ceb'], // Cebuano + ['', 'cel'], // Celtic languages + ['ch', 'cha'], // Chamorro + ['', 'chb'], // Chibcha + ['ce', 'che'], // Chechen + ['', 'chg'], // Chagatai + ['zh', 'chi'], // Chinese + ['', 'chk'], // Chuukese + ['', 'chm'], // Mari + ['', 'chn'], // Chinook jargon + ['', 'cho'], // Choctaw + ['', 'chp'], // Chipewyan; Dene Suline + ['', 'chr'], // Cherokee + ['cu', 'chu'], // Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic + ['cv', 'chv'], // Chuvash + ['', 'chy'], // Cheyenne + ['', 'cmc'], // Chamic languages + ['', 'cnr'], // Montenegrin + ['', 'cop'], // Coptic + ['kw', 'cor'], // Cornish + ['co', 'cos'], // Corsican + ['', 'cpe'], // Creoles and pidgins, English based + ['', 'cpf'], // Creoles and pidgins, French-based + ['', 'cpp'], // Creoles and pidgins, Portuguese-based + ['cr', 'cre'], // Cree + ['', 'crh'], // Crimean Tatar; Crimean Turkish + ['', 'crp'], // Creoles and pidgins + ['', 'csb'], // Kashubian + ['', 'cus'], // Cushitic languages + ['cs', 'cze'], // Czech + ['', 'dak'], // Dakota + ['da', 'dan'], // Danish + ['', 'dar'], // Dargwa + ['', 'day'], // Land Dayak languages + ['', 'del'], // Delaware + ['', 'den'], // Slave (Athapascan) + ['', 'dgr'], // Dogrib + ['', 'din'], // Dinka + ['dv', 'div'], // Divehi; Dhivehi; Maldivian + ['', 'doi'], // Dogri + ['', 'dra'], // Dravidian languages + ['', 'dsb'], // Lower Sorbian + ['', 'dua'], // Duala + ['', 'dum'], // Dutch, Middle (ca.1050-1350) + ['nl', 'dut'], // Dutch; Flemish + ['', 'dyu'], // Dyula + ['dz', 'dzo'], // Dzongkha + ['', 'efi'], // Efik + ['', 'egy'], // Egyptian (Ancient) + ['', 'eka'], // Ekajuk + ['', 'elx'], // Elamite + ['en', 'eng'], // English + ['', 'enm'], // English, Middle (1100-1500) + ['eo', 'epo'], // Esperanto + ['et', 'est'], // Estonian + ['ee', 'ewe'], // Ewe + ['', 'ewo'], // Ewondo + ['', 'fan'], // Fang + ['fo', 'fao'], // Faroese + ['', 'fat'], // Fanti + ['fj', 'fij'], // Fijian + ['', 'fil'], // Filipino; Pilipino + ['fi', 'fin'], // Finnish + ['', 'fiu'], // Finno-Ugrian languages + ['', 'fon'], // Fon + ['fr', 'fre'], // French + ['', 'frm'], // French, Middle (ca.1400-1600) + ['', 'fro'], // French, Old (842-ca.1400) + ['', 'frr'], // Northern Frisian + ['', 'frs'], // Eastern Frisian + ['fy', 'fry'], // Western Frisian + ['ff', 'ful'], // Fulah + ['', 'fur'], // Friulian + ['', 'gaa'], // Ga + ['', 'gay'], // Gayo + ['', 'gba'], // Gbaya + ['', 'gem'], // Germanic languages + ['ka', 'geo'], // Georgian + ['de', 'ger'], // German + ['', 'gez'], // Geez + ['', 'gil'], // Gilbertese + ['gd', 'gla'], // Gaelic; Scottish Gaelic + ['ga', 'gle'], // Irish + ['gl', 'glg'], // Galician + ['gv', 'glv'], // Manx + ['', 'gmh'], // German, Middle High (ca.1050-1500) + ['', 'goh'], // German, Old High (ca.750-1050) + ['', 'gon'], // Gondi + ['', 'gor'], // Gorontalo + ['', 'got'], // Gothic + ['', 'grb'], // Grebo + ['', 'grc'], // Greek, Ancient (to 1453) + ['el', 'gre'], // Greek, Modern (1453-) + ['gn', 'grn'], // Guarani + ['', 'gsw'], // Swiss German; Alemannic; Alsatian + ['gu', 'guj'], // Gujarati + ['', 'gwi'], // Gwich'in + ['', 'hai'], // Haida + ['ht', 'hat'], // Haitian; Haitian Creole + ['ha', 'hau'], // Hausa + ['', 'haw'], // Hawaiian + ['he', 'heb'], // Hebrew + ['hz', 'her'], // Herero + ['', 'hil'], // Hiligaynon + ['', 'him'], // Himachali languages; Western Pahari languages + ['hi', 'hin'], // Hindi + ['', 'hit'], // Hittite + ['', 'hmn'], // Hmong; Mong + ['ho', 'hmo'], // Hiri Motu + ['hr', 'hrv'], // Croatian + ['', 'hsb'], // Upper Sorbian + ['hu', 'hun'], // Hungarian + ['', 'hup'], // Hupa + ['', 'iba'], // Iban + ['ig', 'ibo'], // Igbo + ['is', 'ice'], // Icelandic + ['io', 'ido'], // Ido + ['ii', 'iii'], // Sichuan Yi; Nuosu + ['', 'ijo'], // Ijo languages + ['iu', 'iku'], // Inuktitut + ['ie', 'ile'], // Interlingue; Occidental + ['', 'ilo'], // Iloko + ['ia', 'ina'], // Interlingua (International Auxiliary Language Association) + ['', 'inc'], // Indic languages + ['id', 'ind'], // Indonesian + ['', 'ine'], // Indo-European languages + ['', 'inh'], // Ingush + ['ik', 'ipk'], // Inupiaq + ['', 'ira'], // Iranian languages + ['', 'iro'], // Iroquoian languages + ['it', 'ita'], // Italian + ['jv', 'jav'], // Javanese + ['', 'jbo'], // Lojban + ['ja', 'jpn'], // Japanese + ['', 'jpr'], // Judeo-Persian + ['', 'jrb'], // Judeo-Arabic + ['', 'kaa'], // Kara-Kalpak + ['', 'kab'], // Kabyle + ['', 'kac'], // Kachin; Jingpho + ['kl', 'kal'], // Kalaallisut; Greenlandic + ['', 'kam'], // Kamba + ['kn', 'kan'], // Kannada + ['', 'kar'], // Karen languages + ['ks', 'kas'], // Kashmiri + ['kr', 'kau'], // Kanuri + ['', 'kaw'], // Kawi + ['kk', 'kaz'], // Kazakh + ['', 'kbd'], // Kabardian + ['', 'kha'], // Khasi + ['', 'khi'], // Khoisan languages + ['km', 'khm'], // Central Khmer + ['', 'kho'], // Khotanese; Sakan + ['ki', 'kik'], // Kikuyu; Gikuyu + ['rw', 'kin'], // Kinyarwanda + ['ky', 'kir'], // Kirghiz; Kyrgyz + ['', 'kmb'], // Kimbundu + ['', 'kok'], // Konkani + ['kv', 'kom'], // Komi + ['kg', 'kon'], // Kongo + ['ko', 'kor'], // Korean + ['', 'kos'], // Kosraean + ['', 'kpe'], // Kpelle + ['', 'krc'], // Karachay-Balkar + ['', 'krl'], // Karelian + ['', 'kro'], // Kru languages + ['', 'kru'], // Kurukh + ['kj', 'kua'], // Kuanyama; Kwanyama + ['', 'kum'], // Kumyk + ['ku', 'kur'], // Kurdish + ['', 'kut'], // Kutenai + ['', 'lad'], // Ladino + ['', 'lah'], // Lahnda + ['', 'lam'], // Lamba + ['lo', 'lao'], // Lao + ['la', 'lat'], // Latin + ['lv', 'lav'], // Latvian + ['', 'lez'], // Lezghian + ['li', 'lim'], // Limburgan; Limburger; Limburgish + ['ln', 'lin'], // Lingala + ['lt', 'lit'], // Lithuanian + ['', 'lol'], // Mongo + ['', 'loz'], // Lozi + ['lb', 'ltz'], // Luxembourgish; Letzeburgesch + ['', 'lua'], // Luba-Lulua + ['lu', 'lub'], // Luba-Katanga + ['lg', 'lug'], // Ganda + ['', 'lui'], // Luiseno + ['', 'lun'], // Lunda + ['', 'luo'], // Luo (Kenya and Tanzania) + ['', 'lus'], // Lushai + ['mk', 'mac'], // Macedonian + ['', 'mad'], // Madurese + ['', 'mag'], // Magahi + ['mh', 'mah'], // Marshallese + ['', 'mai'], // Maithili + ['', 'mak'], // Makasar + ['ml', 'mal'], // Malayalam + ['', 'man'], // Mandingo + ['mi', 'mao'], // Maori + ['', 'map'], // Austronesian languages + ['mr', 'mar'], // Marathi + ['', 'mas'], // Masai + ['ms', 'may'], // Malay + ['', 'mdf'], // Moksha + ['', 'mdr'], // Mandar + ['', 'men'], // Mende + ['', 'mga'], // Irish, Middle (900-1200) + ['', 'mic'], // Mi'kmaq; Micmac + ['', 'min'], // Minangkabau + ['', 'mis'], // Uncoded languages + ['', 'mkh'], // Mon-Khmer languages + ['mg', 'mlg'], // Malagasy + ['mt', 'mlt'], // Maltese + ['', 'mnc'], // Manchu + ['', 'mni'], // Manipuri + ['', 'mno'], // Manobo languages + ['', 'moh'], // Mohawk + ['mn', 'mon'], // Mongolian + ['', 'mos'], // Mossi + ['', 'mul'], // Multiple languages + ['', 'mun'], // Munda languages + ['', 'mus'], // Creek + ['', 'mwl'], // Mirandese + ['', 'mwr'], // Marwari + ['', 'myn'], // Mayan languages + ['', 'myv'], // Erzya + ['', 'nah'], // Nahuatl languages + ['', 'nai'], // North American Indian languages + ['', 'nap'], // Neapolitan + ['na', 'nau'], // Nauru + ['nv', 'nav'], // Navajo; Navaho + ['nr', 'nbl'], // Ndebele, South; South Ndebele + ['nd', 'nde'], // Ndebele, North; North Ndebele + ['ng', 'ndo'], // Ndonga + ['', 'nds'], // Low German; Low Saxon; German, Low; Saxon, Low + ['ne', 'nep'], // Nepali + ['', 'new'], // Nepal Bhasa; Newari + ['', 'nia'], // Nias + ['', 'nic'], // Niger-Kordofanian languages + ['', 'niu'], // Niuean + ['nn', 'nno'], // Norwegian Nynorsk; Nynorsk, Norwegian + ['nb', 'nob'], // Bokmål, Norwegian; Norwegian Bokmål + ['', 'nog'], // Nogai + ['', 'non'], // Norse, Old + ['no', 'nor'], // Norwegian + ['', 'nqo'], // N'Ko + ['', 'nso'], // Pedi; Sepedi; Northern Sotho + ['', 'nub'], // Nubian languages + ['', 'nwc'], // Classical Newari; Old Newari; Classical Nepal Bhasa + ['ny', 'nya'], // Chichewa; Chewa; Nyanja + ['', 'nym'], // Nyamwezi + ['', 'nyn'], // Nyankole + ['', 'nyo'], // Nyoro + ['', 'nzi'], // Nzima + ['oc', 'oci'], // Occitan (post 1500) + ['oj', 'oji'], // Ojibwa + ['or', 'ori'], // Oriya + ['om', 'orm'], // Oromo + ['', 'osa'], // Osage + ['os', 'oss'], // Ossetian; Ossetic + ['', 'ota'], // Turkish, Ottoman (1500-1928) + ['', 'oto'], // Otomian languages + ['', 'paa'], // Papuan languages + ['', 'pag'], // Pangasinan + ['', 'pal'], // Pahlavi + ['', 'pam'], // Pampanga; Kapampangan + ['pa', 'pan'], // Panjabi; Punjabi + ['', 'pap'], // Papiamento + ['', 'pau'], // Palauan + ['', 'peo'], // Persian, Old (ca.600-400 B.C.) + ['fa', 'per'], // Persian + ['', 'phi'], // Philippine languages + ['', 'phn'], // Phoenician + ['pi', 'pli'], // Pali + ['pl', 'pol'], // Polish + ['', 'pon'], // Pohnpeian + ['pt', 'por'], // Portuguese + ['', 'pra'], // Prakrit languages + ['', 'pro'], // Provençal, Old (to 1500); Occitan, Old (to 1500) + ['ps', 'pus'], // Pushto; Pashto + ['', 'qaaqtz'], // Reserved for local use + ['qu', 'que'], // Quechua + ['', 'raj'], // Rajasthani + ['', 'rap'], // Rapanui + ['', 'rar'], // Rarotongan; Cook Islands Maori + ['', 'roa'], // Romance languages + ['rm', 'roh'], // Romansh + ['', 'rom'], // Romany + ['ro', 'rum'], // Romanian; Moldavian; Moldovan + ['rn', 'run'], // Rundi + ['', 'rup'], // Aromanian; Arumanian; Macedo-Romanian + ['ru', 'rus'], // Russian + ['', 'sad'], // Sandawe + ['sg', 'sag'], // Sango + ['', 'sah'], // Yakut + ['', 'sai'], // South American Indian languages + ['', 'sal'], // Salishan languages + ['', 'sam'], // Samaritan Aramaic + ['sa', 'san'], // Sanskrit + ['', 'sas'], // Sasak + ['', 'sat'], // Santali + ['', 'scn'], // Sicilian + ['', 'sco'], // Scots + ['', 'sel'], // Selkup + ['', 'sem'], // Semitic languages + ['', 'sga'], // Irish, Old (to 900) + ['', 'sgn'], // Sign Languages + ['', 'shn'], // Shan + ['', 'sid'], // Sidamo + ['si', 'sin'], // Sinhala; Sinhalese + ['', 'sio'], // Siouan languages + ['', 'sit'], // Sino-Tibetan languages + ['', 'sla'], // Slavic languages + ['sk', 'slo'], // Slovak + ['sl', 'slv'], // Slovenian + ['', 'sma'], // Southern Sami + ['se', 'sme'], // Northern Sami + ['', 'smi'], // Sami languages + ['', 'smj'], // Lule Sami + ['', 'smn'], // Inari Sami + ['sm', 'smo'], // Samoan + ['', 'sms'], // Skolt Sami + ['sn', 'sna'], // Shona + ['sd', 'snd'], // Sindhi + ['', 'snk'], // Soninke + ['', 'sog'], // Sogdian + ['so', 'som'], // Somali + ['', 'son'], // Songhai languages + ['st', 'sot'], // Sotho, Southern + ['es', 'spa'], // Spanish; Castilian + ['sc', 'srd'], // Sardinian + ['', 'srn'], // Sranan Tongo + ['sr', 'srp'], // Serbian + ['', 'srr'], // Serer + ['', 'ssa'], // Nilo-Saharan languages + ['ss', 'ssw'], // Swati + ['', 'suk'], // Sukuma + ['su', 'sun'], // Sundanese + ['', 'sus'], // Susu + ['', 'sux'], // Sumerian + ['sw', 'swa'], // Swahili + ['sv', 'swe'], // Swedish + ['', 'syc'], // Classical Syriac + ['', 'syr'], // Syriac + ['ty', 'tah'], // Tahitian + ['', 'tai'], // Tai languages + ['ta', 'tam'], // Tamil + ['tt', 'tat'], // Tatar + ['te', 'tel'], // Telugu + ['', 'tem'], // Timne + ['', 'ter'], // Tereno + ['', 'tet'], // Tetum + ['tg', 'tgk'], // Tajik + ['tl', 'tgl'], // Tagalog + ['th', 'tha'], // Thai + ['bo', 'tib'], // Tibetan + ['', 'tig'], // Tigre + ['ti', 'tir'], // Tigrinya + ['', 'tiv'], // Tiv + ['', 'tkl'], // Tokelau + ['', 'tlh'], // Klingon; tlhIngan-Hol + ['', 'tli'], // Tlingit + ['', 'tmh'], // Tamashek + ['', 'tog'], // Tonga (Nyasa) + ['to', 'ton'], // Tonga (Tonga Islands) + ['', 'tpi'], // Tok Pisin + ['', 'tsi'], // Tsimshian + ['tn', 'tsn'], // Tswana + ['ts', 'tso'], // Tsonga + ['tk', 'tuk'], // Turkmen + ['', 'tum'], // Tumbuka + ['', 'tup'], // Tupi languages + ['tr', 'tur'], // Turkish + ['', 'tut'], // Altaic languages + ['', 'tvl'], // Tuvalu + ['tw', 'twi'], // Twi + ['', 'tyv'], // Tuvinian + ['', 'udm'], // Udmurt + ['', 'uga'], // Ugaritic + ['ug', 'uig'], // Uighur; Uyghur + ['uk', 'ukr'], // Ukrainian + ['', 'umb'], // Umbundu + ['', 'und'], // Undetermined + ['ur', 'urd'], // Urdu + ['uz', 'uzb'], // Uzbek + ['', 'vai'], // Vai + ['ve', 'ven'], // Venda + ['vi', 'vie'], // Vietnamese + ['vo', 'vol'], // Volapük + ['', 'vot'], // Votic + ['', 'wak'], // Wakashan languages + ['', 'wal'], // Wolaitta; Wolaytta + ['', 'war'], // Waray + ['', 'was'], // Washo + ['cy', 'wel'], // Welsh + ['', 'wen'], // Sorbian languages + ['wa', 'wln'], // Walloon + ['wo', 'wol'], // Wolof + ['', 'xal'], // Kalmyk; Oirat + ['xh', 'xho'], // Xhosa + ['', 'yao'], // Yao + ['', 'yap'], // Yapese + ['yi', 'yid'], // Yiddish + ['yo', 'yor'], // Yoruba + ['', 'ypk'], // Yupik languages + ['', 'zap'], // Zapotec + ['', 'zbl'], // Blissymbols; Blissymbolics; Bliss + ['', 'zen'], // Zenaga + ['', 'zgh'], // Standard Moroccan Tamazight + ['za', 'zha'], // Zhuang; Chuang + ['', 'znd'], // Zande languages + ['zu', 'zul'], // Zulu + ['', 'zun'], // Zuni + ['', 'zxx'], // No linguistic content; Not applicable + // phpcs:enable Squiz.PHP.CommentedOutCode.Found + ]; + + /** + * Initializes the rule defining the ISO 639 set. + * + * @throws ComponentException + */ + public function __construct(string $set = self::ALPHA2) + { + $index = array_search($set, self::AVAILABLE_SETS, true); + if ($index === false) { + throw new ComponentException(sprintf('"%s" is not a valid language set for ISO 639', $set)); + } + + parent::__construct(new In($this->getHaystack($index), true), ['set' => $set]); + } + + /** + * @return string[] + */ + private function getHaystack(int $index): array + { + return array_filter(array_column(self::LANGUAGE_CODES, $index)); + } +} diff --git a/vendor/workerman/validation/library/Rules/LeapDate.php b/vendor/workerman/validation/library/Rules/LeapDate.php new file mode 100644 index 0000000..fa432a8 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/LeapDate.php @@ -0,0 +1,54 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use DateTimeImmutable; +use DateTimeInterface; + +use function is_scalar; + +/** + * Validates if a date is leap. + * + * @author Danilo Benevides + * @author Henrique Moody + * @author Jayson Reis + */ +final class LeapDate extends AbstractRule +{ + /** + * @var string + */ + private $format; + + /** + * Initializes the rule with the expected format. + */ + public function __construct(string $format) + { + $this->format = $format; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof DateTimeInterface) { + return $input->format('m-d') === '02-29'; + } + + if (is_scalar($input)) { + return $this->validate(DateTimeImmutable::createFromFormat($this->format, (string) $input)); + } + + return false; + } +} diff --git a/vendor/workerman/validation/library/Rules/LeapYear.php b/vendor/workerman/validation/library/Rules/LeapYear.php new file mode 100644 index 0000000..0a964bb --- /dev/null +++ b/vendor/workerman/validation/library/Rules/LeapYear.php @@ -0,0 +1,50 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use DateTimeInterface; + +use function date; +use function is_numeric; +use function is_scalar; +use function sprintf; +use function strtotime; + +/** + * Validates if a year is leap. + * + * @author Danilo Correa + * @author Henrique Moody + * @author Jayson Reis + */ +final class LeapYear extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (is_numeric($input)) { + $date = strtotime(sprintf('%d-02-29', (int) $input)); + + return (bool) date('L', (int) $date); + } + + if (is_scalar($input)) { + return $this->validate((int) date('Y', (int) strtotime((string) $input))); + } + + if ($input instanceof DateTimeInterface) { + return $this->validate($input->format('Y')); + } + + return false; + } +} diff --git a/vendor/workerman/validation/library/Rules/Length.php b/vendor/workerman/validation/library/Rules/Length.php new file mode 100644 index 0000000..b815db1 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Length.php @@ -0,0 +1,130 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Countable as CountableInterface; +use Respect\Validation\Exceptions\ComponentException; + +use function count; +use function get_object_vars; +use function is_array; +use function is_int; +use function is_object; +use function is_string; +use function mb_strlen; +use function sprintf; + +/** + * Validates the length of the given input. + * + * @author Alexandre Gomes Gaigalas + * @author Blake Hair + * @author Danilo Correa + * @author Henrique Moody + * @author Hugo Hamon + * @author João Torquato + * @author Marcelo Araujo + */ +final class Length extends AbstractRule +{ + /** + * @var int|null + */ + private $minValue; + + /** + * @var int|null + */ + private $maxValue; + + /** + * @var bool + */ + private $inclusive; + + /** + * Creates the rule with a minimum and maximum value. + * + * @throws ComponentException + */ + public function __construct(?int $min = null, ?int $max = null, bool $inclusive = true) + { + $this->minValue = $min; + $this->maxValue = $max; + $this->inclusive = $inclusive; + + if ($max !== null && $min > $max) { + throw new ComponentException(sprintf('%d cannot be less than %d for validation', $min, $max)); + } + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + $length = $this->extractLength($input); + if ($length === null) { + return false; + } + + return $this->validateMin($length) && $this->validateMax($length); + } + + /** + * @param mixed $input + */ + private function extractLength($input): ?int + { + if (is_string($input)) { + return (int) mb_strlen($input); + } + + if (is_array($input) || $input instanceof CountableInterface) { + return count($input); + } + + if (is_object($input)) { + return $this->extractLength(get_object_vars($input)); + } + + if (is_int($input)) { + return $this->extractLength((string) $input); + } + + return null; + } + + private function validateMin(int $length): bool + { + if ($this->minValue === null) { + return true; + } + + if ($this->inclusive) { + return $length >= $this->minValue; + } + + return $length > $this->minValue; + } + + private function validateMax(int $length): bool + { + if ($this->maxValue === null) { + return true; + } + + if ($this->inclusive) { + return $length <= $this->maxValue; + } + + return $length < $this->maxValue; + } +} diff --git a/vendor/workerman/validation/library/Rules/LessThan.php b/vendor/workerman/validation/library/Rules/LessThan.php new file mode 100644 index 0000000..f55e034 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/LessThan.php @@ -0,0 +1,26 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates whether the input is less than a value. + * + * @author Henrique Moody + */ +final class LessThan extends AbstractComparison +{ + /** + * {@inheritDoc} + */ + protected function compare($left, $right): bool + { + return $left < $right; + } +} diff --git a/vendor/workerman/validation/library/Rules/Lowercase.php b/vendor/workerman/validation/library/Rules/Lowercase.php new file mode 100644 index 0000000..9318fa4 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Lowercase.php @@ -0,0 +1,36 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_string; +use function mb_strtolower; + +/** + * Validates whether the characters in the input are lowercase. + * + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + * @author Jean Pimentel + */ +final class Lowercase extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + return $input === mb_strtolower($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Luhn.php b/vendor/workerman/validation/library/Rules/Luhn.php new file mode 100644 index 0000000..e5c0cec --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Luhn.php @@ -0,0 +1,58 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function array_map; +use function count; +use function str_split; + +/** + * Validate whether a given input is a Luhn number. + * + * @see https://en.wikipedia.org/wiki/Luhn_algorithm + * + * @author Alexander Gorshkov + * @author Danilo Correa + * @author Henrique Moody + */ +final class Luhn extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!(new Digit())->validate($input)) { + return false; + } + + return $this->isValid((string) $input); + } + + private function isValid(string $input): bool + { + $sum = 0; + $digits = array_map('intval', str_split($input)); + $numDigits = count($digits); + $parity = $numDigits % 2; + for ($i = 0; $i < $numDigits; ++$i) { + $digit = $digits[$i]; + if ($parity == $i % 2) { + $digit <<= 1; + if (9 < $digit) { + $digit = $digit - 9; + } + } + $sum += $digit; + } + + return $sum % 10 == 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/MacAddress.php b/vendor/workerman/validation/library/Rules/MacAddress.php new file mode 100644 index 0000000..f82a583 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/MacAddress.php @@ -0,0 +1,36 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_string; +use function preg_match; + +/** + * Validates whether the input is a valid MAC address. + * + * @author Alexandre Gomes Gaigalas + * @author Danilo Correa + * @author Fábio da Silva Ribeiro + * @author Henrique Moody + */ +final class MacAddress extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + return preg_match('/^(([0-9a-fA-F]{2}-){5}|([0-9a-fA-F]{2}:){5})[0-9a-fA-F]{2}$/', $input) > 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/Max.php b/vendor/workerman/validation/library/Rules/Max.php new file mode 100644 index 0000000..f3a2aeb --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Max.php @@ -0,0 +1,27 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates whether the input is less than or equal to a value. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class Max extends AbstractComparison +{ + /** + * {@inheritDoc} + */ + protected function compare($left, $right): bool + { + return $left <= $right; + } +} diff --git a/vendor/workerman/validation/library/Rules/MaxAge.php b/vendor/workerman/validation/library/Rules/MaxAge.php new file mode 100644 index 0000000..fbbd8f5 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/MaxAge.php @@ -0,0 +1,27 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates a maximum age for a given date. + * + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class MaxAge extends AbstractAge +{ + /** + * {@inheritDoc} + */ + protected function compare(int $baseDate, int $givenDate): bool + { + return $baseDate <= $givenDate; + } +} diff --git a/vendor/workerman/validation/library/Rules/Mimetype.php b/vendor/workerman/validation/library/Rules/Mimetype.php new file mode 100644 index 0000000..05a537e --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Mimetype.php @@ -0,0 +1,66 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use finfo; +use SplFileInfo; + +use function is_file; +use function is_string; + +use const FILEINFO_MIME_TYPE; + +/** + * Validates if the input is a file and if its MIME type matches the expected one. + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class Mimetype extends AbstractRule +{ + /** + * @var string + */ + private $mimetype; + + /** + * @var finfo + */ + private $fileInfo; + + /** + * Initializes the rule by defining the expected mimetype from the input. + */ + public function __construct(string $mimetype, ?finfo $fileInfo = null) + { + $this->mimetype = $mimetype; + $this->fileInfo = $fileInfo ?: new finfo(); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof SplFileInfo) { + return $this->validate($input->getPathname()); + } + + if (!is_string($input)) { + return false; + } + + if (!is_file($input)) { + return false; + } + + return $this->mimetype === $this->fileInfo->file($input, FILEINFO_MIME_TYPE); + } +} diff --git a/vendor/workerman/validation/library/Rules/Min.php b/vendor/workerman/validation/library/Rules/Min.php new file mode 100644 index 0000000..a89bf7b --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Min.php @@ -0,0 +1,27 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates whether the input is greater than or equal to a value. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class Min extends AbstractComparison +{ + /** + * {@inheritDoc} + */ + protected function compare($left, $right): bool + { + return $left >= $right; + } +} diff --git a/vendor/workerman/validation/library/Rules/MinAge.php b/vendor/workerman/validation/library/Rules/MinAge.php new file mode 100644 index 0000000..7b2a64e --- /dev/null +++ b/vendor/workerman/validation/library/Rules/MinAge.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates a minimum age for a given date. + * + * @author Emmerson Siqueira + * @author Henrique Moody + * @author Jean Pimentel + * @author Kennedy Tedesco + */ +final class MinAge extends AbstractAge +{ + /** + * {@inheritDoc} + */ + protected function compare(int $baseDate, int $givenDate): bool + { + return $baseDate >= $givenDate; + } +} diff --git a/vendor/workerman/validation/library/Rules/Mobile.php b/vendor/workerman/validation/library/Rules/Mobile.php new file mode 100644 index 0000000..f7a69a0 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Mobile.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * @author Danilo Benevides + * @author Henrique Moody + * @author Jean Pimentel + */ +final class Multiple extends AbstractRule +{ + /** + * @var int + */ + private $multipleOf; + + public function __construct(int $multipleOf) + { + $this->multipleOf = $multipleOf; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($this->multipleOf == 0) { + return $input == 0; + } + + return $input % $this->multipleOf == 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/Negative.php b/vendor/workerman/validation/library/Rules/Negative.php new file mode 100644 index 0000000..937a5dd --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Negative.php @@ -0,0 +1,34 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_numeric; + +/** + * Validates whether the input is a negative number. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Ismael Elias + */ +final class Negative extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_numeric($input)) { + return false; + } + + return $input < 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/NfeAccessKey.php b/vendor/workerman/validation/library/Rules/NfeAccessKey.php new file mode 100644 index 0000000..fb74f2a --- /dev/null +++ b/vendor/workerman/validation/library/Rules/NfeAccessKey.php @@ -0,0 +1,56 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function array_map; +use function floor; +use function mb_strlen; +use function str_split; + +/** + * Validates the access key of the Brazilian electronic invoice (NFe). + * + * + * (pt-br) Valida chave de acesso de NFe, mais especificamente, relacionada ao DANFE. + * + * @see (pt-br) Manual de Integração do Contribuinte v4.0.1 em http://www.nfe.fazenda.gov.br + * + * @author Andrey Knupp Vital + * @author Danilo Correa + * @author Henrique Moody + */ +final class NfeAccessKey extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (mb_strlen($input) !== 44) { + return false; + } + + $digits = array_map('intval', str_split($input)); + $w = []; + for ($i = 0, $z = 5, $m = 43; $i <= $m; ++$i) { + $z = $i < $m ? $z - 1 == 1 ? 9 : $z - 1 : 0; + $w[] = $z; + } + + for ($i = 0, $s = 0, $k = 44; $i < $k; ++$i) { + $s += $digits[$i] * $w[$i]; + } + + $s -= 11 * floor($s / 11); + $v = $s == 0 || $s == 1 ? 0 : 11 - $s; + + return $v == $digits[43]; + } +} diff --git a/vendor/workerman/validation/library/Rules/Nif.php b/vendor/workerman/validation/library/Rules/Nif.php new file mode 100644 index 0000000..f47bcf0 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Nif.php @@ -0,0 +1,99 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function array_pop; +use function array_sum; +use function is_numeric; +use function is_string; +use function mb_substr; +use function preg_match; +use function str_split; + +/** + * Validates Spain's fiscal identification number (NIF). + * + * + * @see https://es.wikipedia.org/wiki/N%C3%BAmero_de_identificaci%C3%B3n_fiscal + * + * @author Henrique Moody + * @author Julián Gutiérrez + * @author Senén + */ +final class Nif extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + if (preg_match('/^(\d{8})([A-Z])$/', $input, $matches)) { + return $this->validateDni((int) $matches[1], $matches[2]); + } + + if (preg_match('/^([KLMXYZ])(\d{7})([A-Z])$/', $input, $matches)) { + return $this->validateNie($matches[1], $matches[2], $matches[3]); + } + + if (preg_match('/^([A-HJNP-SUVW])(\d{7})([0-9A-Z])$/', $input, $matches)) { + return $this->validateCif($matches[2], $matches[3]); + } + + return false; + } + + private function validateDni(int $number, string $control): bool + { + return mb_substr('TRWAGMYFPDXBNJZSQVHLCKE', $number % 23, 1) === $control; + } + + private function validateNie(string $prefix, string $number, string $control): bool + { + if ($prefix === 'Y') { + return $this->validateDni((int) ('1' . $number), $control); + } + + if ($prefix === 'Z') { + return $this->validateDni((int) ('2' . $number), $control); + } + + return $this->validateDni((int) $number, $control); + } + + private function validateCif(string $number, string $control): bool + { + $code = 0; + $position = 1; + /** @var int $digit */ + foreach (str_split($number) as $digit) { + $increaser = $digit; + if ($position % 2 !== 0) { + $increaser = array_sum(str_split((string) ($digit * 2))); + } + + $code += $increaser; + ++$position; + } + + $digits = str_split((string) $code); + $lastDigit = (int) array_pop($digits); + $key = $lastDigit === 0 ? 0 : 10 - $lastDigit; + + if (is_numeric($control)) { + return (int) $key === (int) $control; + } + + return mb_substr('JABCDEFGHI', $key % 10, 1) === $control; + } +} diff --git a/vendor/workerman/validation/library/Rules/Nip.php b/vendor/workerman/validation/library/Rules/Nip.php new file mode 100644 index 0000000..66bb76e --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Nip.php @@ -0,0 +1,54 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function array_map; +use function is_scalar; +use function preg_match; +use function str_split; + +/** + * Validates whether the input is a Polish VAT identification number (NIP). + * + * @see https://en.wikipedia.org/wiki/VAT_identification_number + * + * @author Henrique Moody + * @author Tomasz Regdos + */ +final class Nip extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + if (!preg_match('/^\d{10}$/', (string) $input)) { + return false; + } + + $weights = [6, 5, 7, 2, 3, 4, 5, 6, 7]; + $digits = array_map('intval', str_split((string) $input)); + + $targetControlNumber = $digits[9]; + $calculateControlNumber = 0; + + for ($i = 0; $i < 9; ++$i) { + $calculateControlNumber += $digits[$i] * $weights[$i]; + } + + $calculateControlNumber = $calculateControlNumber % 11; + + return $targetControlNumber == $calculateControlNumber; + } +} diff --git a/vendor/workerman/validation/library/Rules/No.php b/vendor/workerman/validation/library/Rules/No.php new file mode 100644 index 0000000..73f993b --- /dev/null +++ b/vendor/workerman/validation/library/Rules/No.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function nl_langinfo; + +use const NOEXPR; + +/** + * Validates if value is considered as "No". + * + * @author Henrique Moody + */ +final class No extends AbstractEnvelope +{ + public function __construct(bool $useLocale = false) + { + $pattern = '^n(o(t|pe)?|ix|ay)?$'; + if ($useLocale) { + $pattern = nl_langinfo(NOEXPR); + } + + parent::__construct(new Regex('/' . $pattern . '/i')); + } +} diff --git a/vendor/workerman/validation/library/Rules/NoWhitespace.php b/vendor/workerman/validation/library/Rules/NoWhitespace.php new file mode 100644 index 0000000..c8d848b --- /dev/null +++ b/vendor/workerman/validation/library/Rules/NoWhitespace.php @@ -0,0 +1,41 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_null; +use function is_scalar; +use function preg_match; + +/** + * Validates whether a string contains no whitespace (spaces, tabs and line breaks). + * + * @author Alexandre Gomes Gaigalas + * @author Augusto Pascutti + * @author Danilo Benevides + * @author Henrique Moody + */ +final class NoWhitespace extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (is_null($input)) { + return true; + } + + if (is_scalar($input) === false) { + return false; + } + + return !preg_match('#\s#', (string) $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/NoneOf.php b/vendor/workerman/validation/library/Rules/NoneOf.php new file mode 100644 index 0000000..80adde7 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/NoneOf.php @@ -0,0 +1,52 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\NoneOfException; + +use function count; + +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class NoneOf extends AbstractComposite +{ + /** + * {@inheritDoc} + */ + public function assert($input): void + { + $exceptions = $this->getAllThrownExceptions($input); + $numRules = count($this->getRules()); + $numExceptions = count($exceptions); + if ($numRules !== $numExceptions) { + /** @var NoneOfException $noneOfException */ + $noneOfException = $this->reportError($input); + $noneOfException->addChildren($exceptions); + + throw $noneOfException; + } + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + foreach ($this->getRules() as $rule) { + if ($rule->validate($input)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/workerman/validation/library/Rules/Not.php b/vendor/workerman/validation/library/Rules/Not.php new file mode 100644 index 0000000..919a902 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Not.php @@ -0,0 +1,128 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; +use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\NonNegatable; +use Respect\Validation\Validatable; + +use function array_shift; +use function count; +use function current; +use function get_class; +use function sprintf; + +/** + * @author Alexandre Gomes Gaigalas + * @author Caio César Tavares + * @author Henrique Moody + */ +final class Not extends AbstractRule +{ + /** + * @var Validatable + */ + private $rule; + + public function __construct(Validatable $rule) + { + $this->rule = $this->extractNegatedRule($rule); + } + + public function getNegatedRule(): Validatable + { + return $this->rule; + } + + public function setName(string $name): Validatable + { + $this->rule->setName($name); + + return parent::setName($name); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return $this->rule->validate($input) === false; + } + + /** + * {@inheritDoc} + */ + public function assert($input): void + { + if ($this->validate($input)) { + return; + } + + $rule = $this->rule; + if ($rule instanceof AllOf) { + $rule = $this->absorbAllOf($rule, $input); + } + + $exception = $rule->reportError($input); + $exception->updateMode(ValidationException::MODE_NEGATIVE); + + throw $exception; + } + + /** + * @param mixed $input + */ + private function absorbAllOf(AllOf $rule, $input): Validatable + { + $rules = $rule->getRules(); + while (($current = array_shift($rules))) { + $rule = $current; + if (!$rule instanceof AllOf) { + continue; + } + + if (!$rule->validate($input)) { + continue; + } + + $rules = $rule->getRules(); + } + + return $rule; + } + + private function extractNegatedRule(Validatable $rule): Validatable + { + if ($rule instanceof NonNegatable) { + throw new ComponentException( + sprintf( + '"%s" can not be wrapped in Not()', + get_class($rule) + ) + ); + } + + if ($rule instanceof self && $rule->getNegatedRule() instanceof self) { + return $this->extractNegatedRule($rule->getNegatedRule()->getNegatedRule()); + } + + if (!$rule instanceof AllOf) { + return $rule; + } + + $rules = $rule->getRules(); + if (count($rules) === 1) { + return $this->extractNegatedRule(current($rules)); + } + + return $rule; + } +} diff --git a/vendor/workerman/validation/library/Rules/NotBlank.php b/vendor/workerman/validation/library/Rules/NotBlank.php new file mode 100644 index 0000000..26fc4ea --- /dev/null +++ b/vendor/workerman/validation/library/Rules/NotBlank.php @@ -0,0 +1,51 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use stdClass; + +use function array_filter; +use function is_array; +use function is_numeric; +use function is_string; +use function trim; + +/** + * Validates if the given input is not a blank value (null, zeros, empty strings or empty arrays, recursively). + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class NotBlank extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (is_numeric($input)) { + return $input != 0; + } + + if (is_string($input)) { + $input = trim($input); + } + + if ($input instanceof stdClass) { + $input = (array) $input; + } + + if (is_array($input)) { + $input = array_filter($input, __METHOD__); + } + + return !empty($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/NotEmoji.php b/vendor/workerman/validation/library/Rules/NotEmoji.php new file mode 100644 index 0000000..87b4ef9 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/NotEmoji.php @@ -0,0 +1,206 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function implode; +use function is_string; +use function preg_match; + +/** + * Validates if the input does not contain an emoji. + * + * @author Mazen Touati + */ +final class NotEmoji extends AbstractRule +{ + private const RANGES = [ + '\x{0023}\x{FE0F}\x{20E3}', + '\x{0023}\x{20E3}', + '\x{002A}\x{FE0F}\x{20E3}', + '\x{002A}\x{20E3}', + '\x{0030}\x{FE0F}\x{20E3}', + '\x{0030}\x{20E3}', + '\x{0031}\x{FE0F}\x{20E3}', + '\x{0031}\x{20E3}', + '\x{0032}\x{FE0F}\x{20E3}', + '\x{0032}\x{20E3}', + '\x{0033}\x{FE0F}\x{20E3}', + '\x{0033}\x{20E3}', + '\x{0034}\x{FE0F}\x{20E3}', + '\x{0034}\x{20E3}', + '\x{0035}\x{FE0F}\x{20E3}', + '\x{0035}\x{20E3}', + '\x{0036}\x{FE0F}\x{20E3}', + '\x{0036}\x{20E3}', + '\x{0037}\x{FE0F}\x{20E3}', + '\x{0037}\x{20E3}', + '\x{0038}\x{FE0F}\x{20E3}', + '\x{0038}\x{20E3}', + '\x{0039}\x{FE0F}\x{20E3}', + '\x{0039}\x{20E3}', + '\x{1F004}', + '\x{1F0CF}', + '[\x{1F170}-\x{1F171}]', + '[\x{1F17E}-\x{1F17F}]', + '\x{1F18E}', + '[\x{1F191}-\x{1F19A}]', + '[\x{1F1E6}-\x{1F1FF}]', + '[\x{1F201}-\x{1F202}]', + '\x{1F21A}', + '\x{1F22F}', + '[\x{1F232}-\x{1F23A}]', + '[\x{1F250}-\x{1F251}]', + '[\x{1F300}-\x{1F321}]', + '[\x{1F324}-\x{1F393}]', + '[\x{1F396}-\x{1F397}]', + '[\x{1F399}-\x{1F39B}]', + '[\x{1F39E}-\x{1F3F0}]', + '[\x{1F3F3}-\x{1F3F5}]', + '[\x{1F3F7}-\x{1F4FD}]', + '[\x{1F4FF}-\x{1F53D}]', + '[\x{1F549}-\x{1F54E}]', + '[\x{1F550}-\x{1F567}]', + '[\x{1F56F}-\x{1F570}]', + '[\x{1F573}-\x{1F57A}]', + '\x{1F587}', + '[\x{1F58A}-\x{1F58D}]', + '\x{1F590}', + '[\x{1F595}-\x{1F596}]', + '[\x{1F5A4}-\x{1F5A5}]', + '\x{1F5A8}', + '[\x{1F5B1}-\x{1F5B2}]', + '\x{1F5BC}', + '[\x{1F5C2}-\x{1F5C4}]', + '[\x{1F5D1}-\x{1F5D3}]', + '[\x{1F5DC}-\x{1F5DE}]', + '\x{1F5E1}', + '\x{1F5E3}', + '\x{1F5E8}', + '\x{1F5EF}', + '\x{1F5F3}', + '[\x{1F5FA}-\x{1F64F}]', + '[\x{1F680}-\x{1F6C5}]', + '[\x{1F6CB}-\x{1F6D2}]', + '[\x{1F6E0}-\x{1F6E5}]', + '\x{1F6E9}', + '[\x{1F6EB}-\x{1F6EC}]', + '\x{1F6F0}', + '[\x{1F6F3}-\x{1F6F9}]', + '[\x{1F910}-\x{1F93A}]', + '[\x{1F93C}-\x{1F93E}]', + '[\x{1F940}-\x{1F945}]', + '[\x{1F947}-\x{1F970}]', + '[\x{1F973}-\x{1F976}]', + '\x{1F97A}', + '[\x{1F97C}-\x{1F9A2}]', + '[\x{1F9B0}-\x{1F9B9}]', + '[\x{1F9C0}-\x{1F9C2}]', + '[\x{1F9D0}-\x{1F9FF}]', + '\x{00A9}', + '\x{00AE}', + '\x{203C}', + '\x{2049}', + '\x{2122}', + '\x{2139}', + '[\x{2194}-\x{2199}]', + '[\x{21A9}-\x{21AA}]', + '[\x{231A}-\x{231B}]', + '\x{2328}', + '\x{23CF}', + '[\x{23E9}-\x{23F3}]', + '[\x{23F8}-\x{23FA}]', + '\x{24C2}', + '[\x{25AA}-\x{25AB}]', + '\x{25B6}', + '\x{25C0}', + '[\x{25FB}-\x{25FE}]', + '[\x{2600}-\x{2604}]', + '\x{260E}', + '\x{2611}', + '[\x{2614}-\x{2615}]', + '\x{2618}', + '\x{261D}', + '\x{2620}', + '[\x{2622}-\x{2623}]', + '\x{2626}', + '\x{262A}', + '[\x{262E}-\x{262F}]', + '[\x{2638}-\x{263A}]', + '\x{2640}', + '\x{2642}', + '[\x{2648}-\x{2653}]', + '[\x{265F}-\x{2660}]', + '\x{2663}', + '[\x{2665}-\x{2666}]', + '\x{2668}', + '\x{267B}', + '[\x{267E}-\x{267F}]', + '[\x{2692}-\x{2697}]', + '\x{2699}', + '[\x{269B}-\x{269C}]', + '[\x{26A0}-\x{26A1}]', + '[\x{26AA}-\x{26AB}]', + '[\x{26B0}-\x{26B1}]', + '[\x{26BD}-\x{26BE}]', + '[\x{26C4}-\x{26C5}]', + '\x{26C8}', + '[\x{26CE}-\x{26CF}]', + '\x{26D1}', + '[\x{26D3}-\x{26D4}]', + '\x{26EA}', + '[\x{26F0}-\x{26F5}]', + '[\x{26F7}-\x{26FA}]', + '\x{26FD}', + '\x{2702}', + '\x{2705}', + '[\x{2708}-\x{270D}]', + '\x{270F}', + '\x{2712}', + '\x{2714}', + '\x{2716}', + '\x{271D}', + '\x{2721}', + '\x{2728}', + '[\x{2733}-\x{2734}]', + '\x{2744}', + '\x{2747}', + '\x{26E9}', + '\x{274C}', + '\x{274E}', + '[\x{2753}-\x{2755}]', + '\x{2757}', + '[\x{2763}-\x{2764}]', + '[\x{2795}-\x{2797}]', + '\x{27A1}', + '\x{27B0}', + '\x{27BF}', + '[\x{2934}-\x{2935}]', + '[\x{2B05}-\x{2B07}]', + '[\x{2B1B}-\x{2B1C}]', + '\x{2B50}', + '\x{2B55}', + '\x{3030}', + '\x{303D}', + '\x{3297}', + '\x{3299}', + ]; + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + return preg_match('/' . implode('|', self::RANGES) . '/mu', $input) === 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/NotEmpty.php b/vendor/workerman/validation/library/Rules/NotEmpty.php new file mode 100644 index 0000000..2faaadc --- /dev/null +++ b/vendor/workerman/validation/library/Rules/NotEmpty.php @@ -0,0 +1,35 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_string; +use function trim; + +/** + * Validates whether the input is not empty + * + * @author Alexandre Gomes Gaigalas + * @author Bram Van der Sype + * @author Henrique Moody + */ +final class NotEmpty extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (is_string($input)) { + $input = trim($input); + } + + return !empty($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/NotOptional.php b/vendor/workerman/validation/library/Rules/NotOptional.php new file mode 100644 index 0000000..59fec58 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/NotOptional.php @@ -0,0 +1,33 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Helpers\CanValidateUndefined; + +/** + * Validates if the given input is not optional. + * + * By optional we consider null or an empty string (''). + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class NotOptional extends AbstractRule +{ + use CanValidateUndefined; + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return $this->isUndefined($input) === false; + } +} diff --git a/vendor/workerman/validation/library/Rules/NullType.php b/vendor/workerman/validation/library/Rules/NullType.php new file mode 100644 index 0000000..cd64750 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/NullType.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_null; + +/** + * Validates whether the input is null. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class NullType extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_null($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Nullable.php b/vendor/workerman/validation/library/Rules/Nullable.php new file mode 100644 index 0000000..526044c --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Nullable.php @@ -0,0 +1,54 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates the given input with a defined rule when input is not NULL. + * + * @author Jens Segers + */ +final class Nullable extends AbstractWrapper +{ + /** + * {@inheritDoc} + */ + public function assert($input): void + { + if ($input === null) { + return; + } + + parent::assert($input); + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + if ($input === null) { + return; + } + + parent::check($input); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input === null) { + return true; + } + + return parent::validate($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Number.php b/vendor/workerman/validation/library/Rules/Number.php new file mode 100644 index 0000000..6d6ac9f --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Number.php @@ -0,0 +1,35 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_nan; +use function is_numeric; + +/** + * Validates if the input is a number. + * + * @author Henrique Moody + * @author Ismael Elias + * @author Vitaliy + */ +final class Number extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_numeric($input)) { + return false; + } + + return !is_nan((float) $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/NumericVal.php b/vendor/workerman/validation/library/Rules/NumericVal.php new file mode 100644 index 0000000..0502999 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/NumericVal.php @@ -0,0 +1,30 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_numeric; + +/** + * Validates whether the input is numeric. + * + * @author Alexandre Gomes Gaigalas + * @author Danilo Correa + * @author Henrique Moody + */ +final class NumericVal extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_numeric($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/ObjectType.php b/vendor/workerman/validation/library/Rules/ObjectType.php new file mode 100644 index 0000000..747b1cb --- /dev/null +++ b/vendor/workerman/validation/library/Rules/ObjectType.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_object; + +/** + * Validates whether the input is an object. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class ObjectType extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_object($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Odd.php b/vendor/workerman/validation/library/Rules/Odd.php new file mode 100644 index 0000000..658260a --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Odd.php @@ -0,0 +1,41 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function filter_var; +use function is_numeric; + +use const FILTER_VALIDATE_INT; + +/** + * Validates whether the input is an odd number or not. + * + * @author Danilo Benevides + * @author Henrique Moody + * @author Jean Pimentel + */ +final class Odd extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_numeric($input)) { + return false; + } + + if (!filter_var($input, FILTER_VALIDATE_INT)) { + return false; + } + + return (int) $input % 2 !== 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/OneOf.php b/vendor/workerman/validation/library/Rules/OneOf.php new file mode 100644 index 0000000..e8434e6 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/OneOf.php @@ -0,0 +1,82 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\OneOfException; +use Respect\Validation\Exceptions\ValidationException; + +use function array_shift; +use function count; + +/** + * @author Bradyn Poulsen + * @author Henrique Moody + */ +final class OneOf extends AbstractComposite +{ + /** + * {@inheritDoc} + */ + public function assert($input): void + { + $validators = $this->getRules(); + $exceptions = $this->getAllThrownExceptions($input); + $numRules = count($validators); + $numExceptions = count($exceptions); + if ($numExceptions !== $numRules - 1) { + /** @var OneOfException $oneOfException */ + $oneOfException = $this->reportError($input); + $oneOfException->addChildren($exceptions); + + throw $oneOfException; + } + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + $rulesPassedCount = 0; + foreach ($this->getRules() as $rule) { + if (!$rule->validate($input)) { + continue; + } + + ++$rulesPassedCount; + } + + return $rulesPassedCount === 1; + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + $exceptions = []; + $rulesPassedCount = 0; + foreach ($this->getRules() as $rule) { + try { + $rule->check($input); + + ++$rulesPassedCount; + } catch (ValidationException $exception) { + $exceptions[] = $exception; + } + } + + if ($rulesPassedCount === 1) { + return; + } + + throw array_shift($exceptions) ?: $this->reportError($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Optional.php b/vendor/workerman/validation/library/Rules/Optional.php new file mode 100644 index 0000000..cb666f9 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Optional.php @@ -0,0 +1,56 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Helpers\CanValidateUndefined; + +/** + * @author Henrique Moody + */ +final class Optional extends AbstractWrapper +{ + use CanValidateUndefined; + + /** + * {@inheritDoc} + */ + public function assert($input): void + { + if ($this->isUndefined($input)) { + return; + } + + parent::assert($input); + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + if ($this->isUndefined($input)) { + return; + } + + parent::check($input); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($this->isUndefined($input)) { + return true; + } + + return parent::validate($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/PerfectSquare.php b/vendor/workerman/validation/library/Rules/PerfectSquare.php new file mode 100644 index 0000000..204da99 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/PerfectSquare.php @@ -0,0 +1,33 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function floor; +use function is_numeric; +use function sqrt; + +/** + * Validates whether the input is a perfect square. + * + * @author Danilo Benevides + * @author Henrique Moody + * @author Kleber Hamada Sato + * @author Nick Lombard + */ +final class PerfectSquare extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_numeric($input) && floor(sqrt((float) $input)) == sqrt((float) $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Pesel.php b/vendor/workerman/validation/library/Rules/Pesel.php new file mode 100644 index 0000000..48c8227 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Pesel.php @@ -0,0 +1,51 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_scalar; +use function preg_match; + +/** + * Validates PESEL (Polish human identification number). + * + * @author Danilo Correa + * @author Henrique Moody + * @author Tomasz Regdos + */ +final class Pesel extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + $stringInput = (string) $input; + if (!preg_match('/^\d{11}$/', (string) $stringInput)) { + return false; + } + + $weights = [1, 3, 7, 9, 1, 3, 7, 9, 1, 3]; + + $targetControlNumber = $stringInput[10]; + $calculateControlNumber = 0; + + for ($i = 0; $i < 10; ++$i) { + $calculateControlNumber += (int) $stringInput[$i] * $weights[$i]; + } + + $calculateControlNumber = (10 - $calculateControlNumber % 10) % 10; + + return $targetControlNumber == $calculateControlNumber; + } +} diff --git a/vendor/workerman/validation/library/Rules/Phone.php b/vendor/workerman/validation/library/Rules/Phone.php new file mode 100644 index 0000000..5dae4d0 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Phone.php @@ -0,0 +1,73 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use libphonenumber\NumberParseException; +use libphonenumber\PhoneNumberUtil; +use Respect\Validation\Exceptions\ComponentException; + +use function class_exists; +use function is_null; +use function is_scalar; +use function sprintf; + +/** + * Validates whether the input is a valid phone number. + * + * Validates an international or country-specific telephone number + * + * @author Alexandre Gomes Gaigalas + */ +final class Phone extends AbstractRule +{ + /** + * @var ?string + */ + private $countryCode; + + /** + * {@inheritDoc} + */ + public function __construct(?string $countryCode = null) + { + $this->countryCode = $countryCode; + + if (!is_null($countryCode) && !(new CountryCode())->validate($countryCode)) { + throw new ComponentException( + sprintf( + 'Invalid country code %s', + $countryCode + ) + ); + } + + if (!class_exists(PhoneNumberUtil::class)) { + throw new ComponentException('The phone validator requires giggsey/libphonenumber-for-php'); + } + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + try { + return PhoneNumberUtil::getInstance()->isValidNumber( + PhoneNumberUtil::getInstance()->parse((string) $input, $this->countryCode) + ); + } catch (NumberParseException $e) { + return false; + } + } +} diff --git a/vendor/workerman/validation/library/Rules/PhpLabel.php b/vendor/workerman/validation/library/Rules/PhpLabel.php new file mode 100644 index 0000000..7f64f77 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/PhpLabel.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_string; +use function preg_match; + +/** + * Validates if a value is considered a valid PHP Label, so that it can be used as a variable, function or class name. + * + * @author Danilo Correa + * @author Emmerson Siqueira + * @author Henrique Moody + */ +final class PhpLabel extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_string($input) && preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Pis.php b/vendor/workerman/validation/library/Rules/Pis.php new file mode 100644 index 0000000..683bd54 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Pis.php @@ -0,0 +1,53 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_scalar; +use function mb_strlen; +use function preg_match; +use function preg_replace; + +/** + * Validates a Brazilian PIS/NIS number. + * + * @author Bruno Koga + * @author Danilo Correa + * @author Henrique Moody + */ +final class Pis extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + $digits = (string) preg_replace('/\D/', '', (string) $input); + if (mb_strlen($digits) != 11 || preg_match('/^' . $digits[0] . '{11}$/', $digits)) { + return false; + } + + $multipliers = [3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; + + $summation = 0; + for ($position = 0; $position < 10; ++$position) { + $summation += (int) $digits[$position] * $multipliers[$position]; + } + + $checkDigit = (int) $digits[10]; + + $modulo = $summation % 11; + + return $checkDigit === ($modulo < 2 ? 0 : 11 - $modulo); + } +} diff --git a/vendor/workerman/validation/library/Rules/PolishIdCard.php b/vendor/workerman/validation/library/Rules/PolishIdCard.php new file mode 100644 index 0000000..f8d103d --- /dev/null +++ b/vendor/workerman/validation/library/Rules/PolishIdCard.php @@ -0,0 +1,63 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_scalar; +use function ord; +use function preg_match; + +/** + * Validates whether the input is a Polish identity card (Dowód Osobisty). + * + * @see https://en.wikipedia.org/wiki/Polish_identity_card + * + * @author Henrique Moody + */ +final class PolishIdCard extends AbstractRule +{ + private const ASCII_CODE_0 = 48; + private const ASCII_CODE_7 = 55; + private const ASCII_CODE_9 = 57; + private const ASCII_CODE_A = 65; + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + $input = (string) $input; + + if (!preg_match('/^[A-Z0-9]{9}$/', $input)) { + return false; + } + + $weights = [7, 3, 1, 0, 7, 3, 1, 7, 3]; + $weightedSum = 0; + for ($i = 0; $i < 9; ++$i) { + $code = ord($input[$i]); + if ($i < 3 && $code <= self::ASCII_CODE_9) { + return false; + } + + if ($i > 2 && $code >= self::ASCII_CODE_A) { + return false; + } + + $difference = $code <= self::ASCII_CODE_9 ? self::ASCII_CODE_0 : self::ASCII_CODE_7; + $weightedSum += ($code - $difference) * $weights[$i]; + } + + return $weightedSum % 10 == $input[3]; + } +} diff --git a/vendor/workerman/validation/library/Rules/PortugueseNif.php b/vendor/workerman/validation/library/Rules/PortugueseNif.php new file mode 100644 index 0000000..bfbdb6a --- /dev/null +++ b/vendor/workerman/validation/library/Rules/PortugueseNif.php @@ -0,0 +1,105 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function array_keys; +use function array_map; +use function array_pop; +use function array_sum; +use function intval; +use function is_numeric; +use function is_string; +use function str_split; +use function strlen; + +/** + * Validates Portugal's fiscal identification number (NIF) + * + * + * @see https://pt.wikipedia.org/wiki/N%C3%BAmero_de_identifica%C3%A7%C3%A3o_fiscal + * + * @author Gonçalo Andrade + */ +final class PortugueseNif extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + // Validate format and length + if (!is_string($input)) { + return false; + } + + if (!is_numeric($input)) { + return false; + } + + if (strlen($input) != 9) { + return false; + } + + $digits = array_map(static fn (string $digit) => intval($digit), str_split($input)); + + // Validate first and second digits + switch ($digits[0]) { + case 4: + switch ($digits[1]) { + case 5: + break; + default: + return false; + } + break; + case 7: + switch ($digits[1]) { + case 0: + case 1: + case 2: + case 4: + case 5: + case 7: + case 8: + case 9: + break; + default: + return false; + } + break; + case 9: + switch ($digits[1]) { + case 0: + case 1: + case 8: + case 9: + break; + default: + return false; + } + break; + default: + break; + } + + // Validate check digit + $checkDigit = array_pop($digits); + $digitKeys = array_keys($digits); + $sumTerms = array_map(static fn (int $digit, int $position) => $digit * (9 - $position), $digits, $digitKeys); + $sum = array_sum($sumTerms); + $modulus = $sum % 11; + + if ($modulus == 0 || $modulus == 1) { + return $checkDigit == 0; + } + + return $checkDigit == 11 - $modulus; + } +} diff --git a/vendor/workerman/validation/library/Rules/Positive.php b/vendor/workerman/validation/library/Rules/Positive.php new file mode 100644 index 0000000..d6857cf --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Positive.php @@ -0,0 +1,34 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_numeric; + +/** + * Validates whether the input is a positive number. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Ismael Elias + */ +final class Positive extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_numeric($input)) { + return false; + } + + return $input > 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/PostalCode.php b/vendor/workerman/validation/library/Rules/PostalCode.php new file mode 100644 index 0000000..d86c44a --- /dev/null +++ b/vendor/workerman/validation/library/Rules/PostalCode.php @@ -0,0 +1,222 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use function sprintf; + +/** + * Validates whether the input is a valid postal code or not. + * + * @see http://download.geonames.org/export/dump/countryInfo.txt + * + * @author Henrique Moody + */ +final class PostalCode extends AbstractEnvelope +{ + private const DEFAULT_PATTERN = '/^$/'; + + private const POSTAL_CODES_EXTRA = [ + // phpcs:disable Generic.Files.LineLength.TooLong + 'AM' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'BR' => ['/^\d\d\d\d\d-\d\d\d$/', '/^\d{5}-?\d{3}$/'], + 'EC' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'GR' => ['/^\d\d\d \d\d$/', '/^(\d{3}\s?\d{2})$/'], + 'GB' => ['/^\w\d \d\w\w|\w\d\d \d\w\w|\w\w\d \d\w\w|\w\w\d\d \d\w\w|\w\d\w \d\w\w|\w\w\d\w \d\w\w|GIR 0AA$/', '/^([Gg][Ii][Rr]\s?0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))\s?[0-9][A-Za-z]{2})$/'], + 'KH' => ['/^\d\d\d\d\d\d?$/', '/^(\d{5,6})$/'], + 'KY' => ['/^KY[1-3]-\d{4}$/', '/^KY[1-3]-?\d{4}$/'], + 'PT' => ['/^\d\d\d\d-\d\d\d$/', '/^\d{4}-?\d{3}\s?[a-zA-Z]{0,25}$/'], + 'RS' => ['/^\d\d\d\d\d\d?$/', '/^(\d{5,6})$/'], + // phpcs:enable Generic.Files.LineLength.TooLong + ]; + + private const POSTAL_CODES = [ + // phpcs:disable Generic.Files.LineLength.TooLong + 'AD' => ['/^AD\d\d\d$/', '/^(?:AD)*(\d{3})$/'], + 'AL' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'AM' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'AR' => ['/^\w\d\d\d\d\w\w\w$/', '/^[A-Z]?\d{4}[A-Z]{0,3}$/'], + 'AS' => ['/^\d\d\d\d\d-\d\d\d\d$/', '/96799/'], + 'AT' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'AU' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'AX' => ['/^\d\d\d\d\d$/', '/^(?:FI)*(\d{5})$/'], + 'AZ' => ['/^AZ \d\d\d\d$/', '/^(?:AZ)*(\d{4})$/'], + 'BA' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'BB' => ['/^BB\d\d\d\d\d$/', '/^(?:BB)*(\d{5})$/'], + 'BD' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'BE' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'BG' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'BH' => ['/^\d\d\d\d|\d\d\d$/', '/^(\d{3}\d?)$/'], + 'BL' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'BM' => ['/^\w\w \d\d$/', '/^([A-Z]{2}\d{2})$/'], + 'BN' => ['/^\w\w\d\d\d\d$/', '/^([A-Z]{2}\d{4})$/'], + 'BR' => ['/^\d\d\d\d\d-\d\d\d$/', '/^\d{5}-\d{3}$/'], + 'BY' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'CA' => ['/^\w\d\w \d\w\d$/', '/^([ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ]) ?(\d[ABCEGHJKLMNPRSTVWXYZ]\d)$/'], + 'CH' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'CL' => ['/^\d\d\d\d\d\d\d$/', '/^(\d{7})$/'], + 'CN' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'CO' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'CR' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'CS' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'CU' => ['/^CP \d\d\d\d\d$/', '/^(?:CP)*(\d{5})$/'], + 'CV' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'CX' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'CY' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'CZ' => ['/^\d\d\d \d\d$/', '/^\d{3}\s?\d{2}$/'], + 'DE' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'DK' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'DO' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'DZ' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'EC' => ['/^\w\d\d\d\d\w$/', '/^([a-zA-Z]\d{4}[a-zA-Z])$/'], + 'EE' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'EG' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'ES' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'ET' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'FI' => ['/^\d\d\d\d\d$/', '/^(?:FI)*(\d{5})$/'], + 'FM' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'FO' => ['/^\d\d\d$/', '/^(?:FO)*(\d{3})$/'], + 'FR' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'GB' => ['/^\w\d \d\w\w|\w\d\d \d\w\w|\w\w\d \d\w\w|\w\w\d\d \d\w\w|\w\d\w \d\w\w|\w\w\d\w \d\w\w|GIR0AA$/', '/^([Gg][Ii][Rr]\s?0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))\s?[0-9][A-Za-z]{2})$/'], + 'GE' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'GF' => ['/^\d\d\d\d\d$/', '/^((97|98)3\d{2})$/'], + 'GG' => ['/^\w\d \d\w\w|\w\d\d \d\w\w|\w\w\d \d\w\w|\w\w\d\d \d\w\w|\w\d\w \d\w\w|\w\w\d\w \d\w\w|GIR0AA$/', '/^((?:(?:[A-PR-UWYZ][A-HK-Y]\d[ABEHMNPRV-Y0-9]|[A-PR-UWYZ]\d[A-HJKPS-UW0-9])\s\d[ABD-HJLNP-UW-Z]{2})|GIR\s?0AA)$/'], + 'GL' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'GP' => ['/^\d\d\d\d\d$/', '/^((97|98)\d{3})$/'], + 'GR' => ['/^\d\d\d \d\d$/', '/^(\d{5})$/'], + 'GT' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'GU' => ['/^969\d\d$/', '/^(969\d{2})$/'], + 'GW' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'HN' => ['/^\w\w\d\d\d\d$/', '/^([A-Z]{2}\d{4})$/'], + 'HR' => ['/^\d\d\d\d\d$/', '/^(?:HR)*(\d{5})$/'], + 'HT' => ['/^HT\d\d\d\d$/', '/^(?:HT)*(\d{4})$/'], + 'HU' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'ID' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'IE' => ['/^\w\w\w \w\w\w\w$/', '/^(D6W|[AC-FHKNPRTV-Y][0-9]{2})\s?([AC-FHKNPRTV-Y0-9]{4})/'], + 'IL' => ['/^\d\d\d\d\d\d\d$/', '/^(\d{7}|\d{5})$/'], + 'IM' => ['/^\w\d \d\w\w|\w\d\d \d\w\w|\w\w\d \d\w\w|\w\w\d\d \d\w\w|\w\d\w \d\w\w|\w\w\d\w \d\w\w|GIR0AA$/', '/^((?:(?:[A-PR-UWYZ][A-HK-Y]\d[ABEHMNPRV-Y0-9]|[A-PR-UWYZ]\d[A-HJKPS-UW0-9])\s\d[ABD-HJLNP-UW-Z]{2})|GIR\s?0AA)$/'], + 'IN' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'IQ' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'IR' => ['/^\d\d\d\d\d\d\d\d\d\d$/', '/^(\d{10})$/'], + 'IS' => ['/^\d\d\d$/', '/^(\d{3})$/'], + 'IT' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'JE' => ['/^\w\d \d\w\w|\w\d\d \d\w\w|\w\w\d \d\w\w|\w\w\d\d \d\w\w|\w\d\w \d\w\w|\w\w\d\w \d\w\w|GIR0AA$/', '/^((?:(?:[A-PR-UWYZ][A-HK-Y]\d[ABEHMNPRV-Y0-9]|[A-PR-UWYZ]\d[A-HJKPS-UW0-9])\s\d[ABD-HJLNP-UW-Z]{2})|GIR\s?0AA)$/'], + 'JO' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'JP' => ['/^\d\d\d-\d\d\d\d$/', '/^\d{3}-\d{4}$/'], + 'KE' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'KG' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'KH' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'KP' => ['/^\d\d\d-\d\d\d$/', '/^(\d{6})$/'], + 'KR' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'KW' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'KZ' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'LA' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'LB' => ['/^\d\d\d\d \d\d\d\d|\d\d\d\d$/', '/^(\d{4}(\d{4})?)$/'], + 'LI' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'LK' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'LR' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'LS' => ['/^\d\d\d$/', '/^(\d{3})$/'], + 'LT' => ['/^LT-\d\d\d\d\d$/', '/^(?:LT)*(\d{5})$/'], + 'LU' => ['/^L-\d\d\d\d$/', '/^(?:L-)?\d{4}$/'], + 'LV' => ['/^LV-\d\d\d\d$/', '/^(?:LV)*(\d{4})$/'], + 'MA' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'MC' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'MD' => ['/^MD-\d\d\d\d$/', '/^MD-\d{4}$/'], + 'ME' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'MF' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'MG' => ['/^\d\d\d$/', '/^(\d{3})$/'], + 'MH' => ['/^\d\d\d\d\d-\d\d\d\d$/', '/^969\d{2}(-\d{4})$/'], + 'MK' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'MM' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'MN' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'MP' => ['/^\d\d\d\d\d$/', '/^9695\d{1}$/'], + 'MQ' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'MT' => ['/^\w\w\w \d\d\d\d$/', '/^[A-Z]{3}\s?\d{4}$/'], + 'MV' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'MW' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'MX' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'MY' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'MZ' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'NC' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'NE' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'NF' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'NG' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'NI' => ['/^\d\d\d-\d\d\d-\d$/', '/^(\d{7})$/'], + 'NL' => ['/^\d\d\d\d \w\w$/', '/^(\d{4}\s?[a-zA-Z]{2})$/'], + 'NO' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'NP' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'NZ' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'OM' => ['/^\d\d\d$/', '/^(\d{3})$/'], + 'PE' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'PF' => ['/^\d\d\d\d\d$/', '/^((97|98)7\d{2})$/'], + 'PG' => ['/^\d\d\d$/', '/^(\d{3})$/'], + 'PH' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'PK' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'PL' => ['/^\d\d-\d\d\d$/', '/^\d{2}-\d{3}$/'], + 'PM' => ['/^\d\d\d\d\d$/', '/^(97500)$/'], + 'PR' => ['/^\d\d\d\d\d-\d\d\d\d$/', '/^00[679]\d{2}(?:-\d{4})?$/'], + 'PT' => ['/^\d\d\d\d-\d\d\d$/', '/^\d{4}-\d{3}\s?[a-zA-Z]{0,25}$/'], + 'PW' => ['/^96940$/', '/^(96940)$/'], + 'PY' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'RE' => ['/^\d\d\d\d\d$/', '/^((97|98)(4|7|8)\d{2})$/'], + 'RO' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'RS' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'RU' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'SA' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'SD' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'SE' => ['/^\d\d\d \d\d$/', '/^(?:SE)?\d{3}\s\d{2}$/'], + 'SG' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'SH' => ['/^STHL 1ZZ$/', '/^(STHL1ZZ)$/'], + 'SI' => ['/^\d\d\d\d$/', '/^(?:SI)*(\d{4})$/'], + 'SJ' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'SK' => ['/^\d\d\d \d\d$/', '/^\d{3}\s?\d{2}$/'], + 'SM' => ['/^4789\d$/', '/^(4789\d)$/'], + 'SN' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'SO' => ['/^\w\w \d\d\d\d\d$/', '/^([A-Z]{2}\d{5})$/'], + 'SV' => ['/^CP \d\d\d\d$/', '/^(?:CP)*(\d{4})$/'], + 'SZ' => ['/^\w\d\d\d$/', '/^([A-Z]\d{3})$/'], + 'TC' => ['/^TKCA 1ZZ$/', '/^(TKCA 1ZZ)$/'], + 'TH' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'TJ' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'TM' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'TN' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'TR' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'TW' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'UA' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'US' => ['/^\d\d\d\d\d-\d\d\d\d$/', '/^\d{5}(-\d{4})?$/'], + 'UY' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'UZ' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'VA' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'VE' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'VI' => ['/^\d\d\d\d\d-\d\d\d\d$/', '/^008\d{2}(?:-\d{4})?$/'], + 'VN' => ['/^\d\d\d\d\d\d$/', '/^(\d{6})$/'], + 'WF' => ['/^\d\d\d\d\d$/', '/^(986\d{2})$/'], + 'YT' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + 'ZA' => ['/^\d\d\d\d$/', '/^(\d{4})$/'], + 'ZM' => ['/^\d\d\d\d\d$/', '/^(\d{5})$/'], + // phpcs:disable Generic.Files.LineLength.TooLong + ];//end + + public function __construct(string $countryCode, bool $formatted = false) + { + $countryCodeRule = new CountryCode(); + if (!$countryCodeRule->validate($countryCode)) { + throw new ComponentException(sprintf('Cannot validate postal code from "%s" country', $countryCode)); + } + + parent::__construct( + new Regex( + self::POSTAL_CODES_EXTRA[$countryCode][$formatted ? 0 : 1] ?? self::POSTAL_CODES[$countryCode][$formatted ? 0 : 1] ?? self::DEFAULT_PATTERN + ), + ['countryCode' => $countryCode] + ); + } +} diff --git a/vendor/workerman/validation/library/Rules/PrimeNumber.php b/vendor/workerman/validation/library/Rules/PrimeNumber.php new file mode 100644 index 0000000..c5aa2f9 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/PrimeNumber.php @@ -0,0 +1,48 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function ceil; +use function is_numeric; +use function sqrt; + +/** + * Validates whether the input is a prime number. + * + * @author Alexandre Gomes Gaigalas + * @author Camilo Teixeira de Melo + * @author Henrique Moody + * @author Ismael Elias + * @author Kleber Hamada Sato + */ +final class PrimeNumber extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_numeric($input) || $input <= 1) { + return false; + } + + if ($input != 2 && ($input % 2) == 0) { + return false; + } + + for ($i = 3; $i <= ceil(sqrt((float) $input)); $i += 2) { + if ($input % $i == 0) { + return false; + } + } + + return true; + } +} diff --git a/vendor/workerman/validation/library/Rules/Printable.php b/vendor/workerman/validation/library/Rules/Printable.php new file mode 100644 index 0000000..d757300 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Printable.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function ctype_print; + +/** + * Validates whether an input is printable character(s). + * + * @author Alexandre Gomes Gaigalas + * @author Andre Ramaciotti + * @author Emmerson Siqueira + * @author Henrique Moody + * @author Nick Lombard + */ +final class Printable extends AbstractFilterRule +{ + /** + * {@inheritDoc} + */ + protected function validateFilteredInput(string $input): bool + { + return ctype_print($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/PublicDomainSuffix.php b/vendor/workerman/validation/library/Rules/PublicDomainSuffix.php new file mode 100644 index 0000000..67f07ac --- /dev/null +++ b/vendor/workerman/validation/library/Rules/PublicDomainSuffix.php @@ -0,0 +1,37 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Helpers\DomainInfo; + +use function array_pop; +use function explode; + +final class PublicDomainSuffix extends AbstractSearcher +{ + /** + * @var string[] + */ + private $domainInfo; + + /** + * {@inheritDoc} + */ + protected function getDataSource($input = null): array + { + $parts = explode('.', $input); + $tld = array_pop($parts); + + $domainInfo = new DomainInfo($tld); + $this->domainInfo = $domainInfo->getPublicSuffixes(); + + return $this->domainInfo; + } +} diff --git a/vendor/workerman/validation/library/Rules/Punct.php b/vendor/workerman/validation/library/Rules/Punct.php new file mode 100644 index 0000000..6374d51 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Punct.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function ctype_punct; + +/** + * Validates whether the input composed by only punctuation characters. + * + * @author Andre Ramaciotti + * @author Danilo Correa + * @author Henrique Moody + * @author Nick Lombard + */ +final class Punct extends AbstractFilterRule +{ + /** + * {@inheritDoc} + */ + protected function validateFilteredInput(string $input): bool + { + return ctype_punct($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Readable.php b/vendor/workerman/validation/library/Rules/Readable.php new file mode 100644 index 0000000..c27b9a4 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Readable.php @@ -0,0 +1,41 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Psr\Http\Message\StreamInterface; +use SplFileInfo; + +use function is_readable; +use function is_string; + +/** + * Validates if the given data is a file exists and is readable. + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class Readable extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof SplFileInfo) { + return $input->isReadable(); + } + + if ($input instanceof StreamInterface) { + return $input->isReadable(); + } + + return is_string($input) && is_readable($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Regex.php b/vendor/workerman/validation/library/Rules/Regex.php new file mode 100644 index 0000000..2eba94e --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Regex.php @@ -0,0 +1,48 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_scalar; +use function preg_match; + +/** + * Validates whether the input matches a defined regular expression. + * + * @author Alexandre Gomes Gaigalas + * @author Danilo Correa + * @author Henrique Moody + */ +final class Regex extends AbstractRule +{ + /** + * @var string + */ + private $regex; + + /** + * Initializes the rule. + */ + public function __construct(string $regex) + { + $this->regex = $regex; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + return preg_match($this->regex, (string) $input) > 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/ResourceType.php b/vendor/workerman/validation/library/Rules/ResourceType.php new file mode 100644 index 0000000..d5cf062 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/ResourceType.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_resource; + +/** + * Validates whether the input is a resource. + * + * @author Henrique Moody + */ +final class ResourceType extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_resource($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Roman.php b/vendor/workerman/validation/library/Rules/Roman.php new file mode 100644 index 0000000..45cada4 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Roman.php @@ -0,0 +1,25 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +/** + * Validates if the input is a Roman numeral. + * + * @author Alexander Wühr + * @author Henrique Moody + * @author Jean Pimentel + */ +final class Roman extends AbstractEnvelope +{ + public function __construct() + { + parent::__construct(new Regex('/^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/')); + } +} diff --git a/vendor/workerman/validation/library/Rules/ScalarVal.php b/vendor/workerman/validation/library/Rules/ScalarVal.php new file mode 100644 index 0000000..955303b --- /dev/null +++ b/vendor/workerman/validation/library/Rules/ScalarVal.php @@ -0,0 +1,28 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_scalar; + +/** + * Validates whether the input is a scalar value or not. + * + * @author Henrique Moody + */ +final class ScalarVal extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_scalar($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Size.php b/vendor/workerman/validation/library/Rules/Size.php new file mode 100644 index 0000000..d7e1d47 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Size.php @@ -0,0 +1,124 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UploadedFileInterface; +use Respect\Validation\Exceptions\ComponentException; +use SplFileInfo; + +use function filesize; +use function floatval; +use function is_numeric; +use function is_string; +use function preg_match; +use function sprintf; + +/** + * Validates whether the input is a file that is of a certain size or not. + * + * @author Danilo Correa + * @author Henrique Moody + * @author Felipe Stival + */ +final class Size extends AbstractRule +{ + /** + * @var string|int|null + */ + private $minSize; + + /** + * @var float|null + */ + private $minValue; + + /** + * @var string|int|null + */ + private $maxSize; + + /** + * @var float|null + */ + private $maxValue; + + /** + * @param string|int|null $minSize + * @param string|int|null $maxSize + */ + public function __construct($minSize = null, $maxSize = null) + { + $this->minSize = $minSize; + $this->minValue = $minSize ? $this->toBytes((string) $minSize) : null; + $this->maxSize = $maxSize; + $this->maxValue = $maxSize ? $this->toBytes((string) $maxSize) : null; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof SplFileInfo) { + return $this->isValidSize((float) $input->getSize()); + } + + if ($input instanceof UploadedFileInterface) { + return $this->isValidSize((float) $input->getSize()); + } + + if ($input instanceof StreamInterface) { + return $this->isValidSize((float) $input->getSize()); + } + + if (is_string($input)) { + return $this->isValidSize((float) filesize($input)); + } + + return false; + } + + /** + * @todo Move it to a trait + * + */ + private function toBytes(string $size): float + { + $value = $size; + $units = ['b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb']; + foreach ($units as $exponent => $unit) { + if (!preg_match('/^(\d+(.\d+)?)' . $unit . '$/i', $size, $matches)) { + continue; + } + $value = floatval($matches[1]) * 1024 ** $exponent; + break; + } + + if (!is_numeric($value)) { + throw new ComponentException(sprintf('"%s" is not a recognized file size.', $size)); + } + + return (float) $value; + } + + private function isValidSize(float $size): bool + { + if ($this->minValue !== null && $this->maxValue !== null) { + return $size >= $this->minValue && $size <= $this->maxValue; + } + + if ($this->minValue !== null) { + return $size >= $this->minValue; + } + + return $size <= $this->maxValue; + } +} diff --git a/vendor/workerman/validation/library/Rules/Slug.php b/vendor/workerman/validation/library/Rules/Slug.php new file mode 100644 index 0000000..a8263eb --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Slug.php @@ -0,0 +1,41 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_string; +use function mb_strstr; +use function preg_match; + +/** + * Validates whether the input is a valid slug. + * + * @author Carlos André Ferrari + * @author Danilo Correa + * @author Henrique Moody + * @author Nick Lombard + */ +final class Slug extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input) || mb_strstr($input, '--')) { + return false; + } + + if (!preg_match('@^[0-9a-z\-]+$@', $input)) { + return false; + } + + return preg_match('@^-|-$@', $input) === 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/Sorted.php b/vendor/workerman/validation/library/Rules/Sorted.php new file mode 100644 index 0000000..84037f3 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Sorted.php @@ -0,0 +1,94 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use function array_values; +use function count; +use function is_array; +use function is_string; +use function sprintf; +use function str_split; + +/** + * Validates whether the input is sorted in a certain order or not. + * + * @author Henrique Moody + * @author Mikhail Vyrtsev + */ +final class Sorted extends AbstractRule +{ + public const ASCENDING = 'ASC'; + public const DESCENDING = 'DESC'; + + /** + * @var string + */ + private $direction; + + public function __construct(string $direction) + { + if ($direction !== self::ASCENDING && $direction !== self::DESCENDING) { + throw new ComponentException( + sprintf('Direction should be either "%s" or "%s"', self::ASCENDING, self::DESCENDING) + ); + } + + $this->direction = $direction; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_array($input) && !is_string($input)) { + return false; + } + + $values = $this->getValues($input); + $count = count($values); + for ($position = 1; $position < $count; ++$position) { + if (!$this->isSorted($values[$position], $values[$position - 1])) { + return false; + } + } + + return true; + } + + /** + * @param mixed $current + * @param mixed $last + */ + private function isSorted($current, $last): bool + { + if ($this->direction === self::ASCENDING) { + return $current > $last; + } + + return $current < $last; + } + + /** + * @param string|mixed[] $input + * + * @return mixed[] + */ + private function getValues($input): array + { + if (is_array($input)) { + return array_values($input); + } + + return str_split($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Space.php b/vendor/workerman/validation/library/Rules/Space.php new file mode 100644 index 0000000..b7a1eea --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Space.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function ctype_space; + +/** + * Validates whether the input contains only whitespaces characters. + * + * @author Andre Ramaciotti + * @author Danilo Correa + * @author Henrique Moody + * @author Nick Lombard + */ +final class Space extends AbstractFilterRule +{ + /** + * {@inheritDoc} + */ + protected function validateFilteredInput(string $input): bool + { + return ctype_space($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/StartsWith.php b/vendor/workerman/validation/library/Rules/StartsWith.php new file mode 100644 index 0000000..dc5e0a3 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/StartsWith.php @@ -0,0 +1,89 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_array; +use function is_string; +use function mb_stripos; +use function mb_strpos; +use function reset; + +/** + * Validates whether the input starts with a given value. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + * @author Marcelo Araujo + */ +final class StartsWith extends AbstractRule +{ + /** + * @var mixed + */ + private $startValue; + + /** + * @var bool + */ + private $identical; + + /** + * @param mixed $startValue + */ + public function __construct($startValue, bool $identical = false) + { + $this->startValue = $startValue; + $this->identical = $identical; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($this->identical) { + return $this->validateIdentical($input); + } + + return $this->validateEquals($input); + } + + /** + * @param mixed $input + */ + protected function validateEquals($input): bool + { + if (is_array($input)) { + return reset($input) == $this->startValue; + } + + if (is_string($input) && is_string($this->startValue)) { + return mb_stripos($input, $this->startValue) === 0; + } + + return false; + } + + /** + * @param mixed $input + */ + protected function validateIdentical($input): bool + { + if (is_array($input)) { + return reset($input) === $this->startValue; + } + + if (is_string($input) && is_string($this->startValue)) { + return mb_strpos($input, $this->startValue) === 0; + } + + return false; + } +} diff --git a/vendor/workerman/validation/library/Rules/StringType.php b/vendor/workerman/validation/library/Rules/StringType.php new file mode 100644 index 0000000..0dce1d5 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/StringType.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_string; + +/** + * Validates whether the type of an input is string or not. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class StringType extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_string($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/StringVal.php b/vendor/workerman/validation/library/Rules/StringVal.php new file mode 100644 index 0000000..469cec9 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/StringVal.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_object; +use function is_scalar; +use function method_exists; + +/** + * Validates whether the input can be used as a string. + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class StringVal extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return is_scalar($input) || (is_object($input) && method_exists($input, '__toString')); + } +} diff --git a/vendor/workerman/validation/library/Rules/SubdivisionCode.php b/vendor/workerman/validation/library/Rules/SubdivisionCode.php new file mode 100644 index 0000000..fe90a54 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/SubdivisionCode.php @@ -0,0 +1,52 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Helpers\CountryInfo; + +use function array_keys; + +/** + * Validates country subdivision codes according to ISO 3166-2. + * + * @see http://en.wikipedia.org/wiki/ISO_3166-2 + * @see http://www.geonames.org/countries/ + * + * @author Henrique Moody + * @author Mazen Touati + */ +final class SubdivisionCode extends AbstractSearcher +{ + /** + * @var string + */ + private $countryName; + + /** + * @var string[] + */ + private $countryInfo; + + public function __construct(string $countryCode) + { + $countryInfo = new CountryInfo($countryCode); + + $this->countryName = $countryInfo->getCountry(); + $this->countryInfo = array_keys($countryInfo->getSubdivisions()); + } + + /** + * {@inheritDoc} + */ + protected function getDataSource($input = null): array + { + return $this->countryInfo; + } +} diff --git a/vendor/workerman/validation/library/Rules/Subset.php b/vendor/workerman/validation/library/Rules/Subset.php new file mode 100644 index 0000000..fddc5d1 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Subset.php @@ -0,0 +1,49 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function array_diff; +use function is_array; + +/** + * Validates whether the input is a subset of a given value. + * + * @author Henrique Moody + * @author Singwai Chan + */ +final class Subset extends AbstractRule +{ + /** + * @var mixed[] + */ + private $superset; + + /** + * Initializes the rule. + * + * @param mixed[] $superset + */ + public function __construct(array $superset) + { + $this->superset = $superset; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_array($input)) { + return false; + } + + return array_diff($input, $this->superset) === []; + } +} diff --git a/vendor/workerman/validation/library/Rules/SymbolicLink.php b/vendor/workerman/validation/library/Rules/SymbolicLink.php new file mode 100644 index 0000000..6fe9d0f --- /dev/null +++ b/vendor/workerman/validation/library/Rules/SymbolicLink.php @@ -0,0 +1,36 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use SplFileInfo; + +use function is_link; +use function is_string; + +/** + * Validates if the given input is a symbolic link. + * + * @author Henrique Moody + * @author Gus Antoniassi + */ +final class SymbolicLink extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof SplFileInfo) { + return $input->isLink(); + } + + return is_string($input) && is_link($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Time.php b/vendor/workerman/validation/library/Rules/Time.php new file mode 100644 index 0000000..dc9f646 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Time.php @@ -0,0 +1,66 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; +use Respect\Validation\Helpers\CanValidateDateTime; + +use function date; +use function is_scalar; +use function preg_match; +use function sprintf; +use function strtotime; + +/** + * Validates whether an input is a time or not + * + * @author Henrique Moody + */ +final class Time extends AbstractRule +{ + use CanValidateDateTime; + + /** + * @var string + */ + private $format; + + /** + * @var string + */ + private $sample; + + /** + * Initializes the rule. + * + * @throws ComponentException + */ + public function __construct(string $format = 'H:i:s') + { + if (!preg_match('/^[gGhHisuvaA\W]+$/', $format)) { + throw new ComponentException(sprintf('"%s" is not a valid date format', $format)); + } + + $this->format = $format; + $this->sample = date($format, strtotime('23:59:59')); + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + return $this->isDateTime($this->format, (string) $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Tld.php b/vendor/workerman/validation/library/Rules/Tld.php new file mode 100644 index 0000000..42ce87e --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Tld.php @@ -0,0 +1,256 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function in_array; +use function is_scalar; +use function mb_strtoupper; + +/** + * Validates whether the input is a top-level domain. + * + * @author Alexandre Gomes Gaigalas + * @author Bogus + * @author Henrique Moody + * @author Paul Karikari + */ +final class Tld extends AbstractRule +{ + /** + * List extracted from https://data.iana.org/TLD/tlds-alpha-by-domain.txt + */ + private const TLD_LIST = [ + 'AAA', 'AARP', 'ABB', 'ABBOTT', 'ABBVIE', 'ABC', 'ABLE', 'ABOGADO', + 'ABUDHABI', 'AC', 'ACADEMY', 'ACCENTURE', 'ACCOUNTANT', 'ACCOUNTANTS', + 'ACO', 'ACTOR', 'AD', 'ADS', 'ADULT', 'AE', 'AEG', 'AERO', 'AETNA', + 'AF', 'AFL', 'AFRICA', 'AG', 'AGAKHAN', 'AGENCY', 'AI', 'AIG', + 'AIRBUS', 'AIRFORCE', 'AIRTEL', 'AKDN', 'AL', 'ALIBABA', 'ALIPAY', + 'ALLFINANZ', 'ALLSTATE', 'ALLY', 'ALSACE', 'ALSTOM', 'AM', 'AMAZON', + 'AMERICANEXPRESS', 'AMERICANFAMILY', 'AMEX', 'AMFAM', 'AMICA', + 'AMSTERDAM', 'ANALYTICS', 'ANDROID', 'ANQUAN', 'ANZ', 'AO', 'AOL', + 'APARTMENTS', 'APP', 'APPLE', 'AQ', 'AQUARELLE', 'AR', 'ARAB', + 'ARAMCO', 'ARCHI', 'ARMY', 'ARPA', 'ART', 'ARTE', 'AS', 'ASDA', 'ASIA', + 'ASSOCIATES', 'AT', 'ATHLETA', 'ATTORNEY', 'AU', 'AUCTION', 'AUDI', + 'AUDIBLE', 'AUDIO', 'AUSPOST', 'AUTHOR', 'AUTO', 'AUTOS', 'AVIANCA', + 'AW', 'AWS', 'AX', 'AXA', 'AZ', 'AZURE', 'BA', 'BABY', 'BAIDU', + 'BANAMEX', 'BANANAREPUBLIC', 'BAND', 'BANK', 'BAR', 'BARCELONA', + 'BARCLAYCARD', 'BARCLAYS', 'BAREFOOT', 'BARGAINS', 'BASEBALL', + 'BASKETBALL', 'BAUHAUS', 'BAYERN', 'BB', 'BBC', 'BBT', 'BBVA', 'BCG', + 'BCN', 'BD', 'BE', 'BEATS', 'BEAUTY', 'BEER', 'BENTLEY', 'BERLIN', + 'BEST', 'BESTBUY', 'BET', 'BF', 'BG', 'BH', 'BHARTI', 'BI', 'BIBLE', + 'BID', 'BIKE', 'BING', 'BINGO', 'BIO', 'BIZ', 'BJ', 'BLACK', + 'BLACKFRIDAY', 'BLOCKBUSTER', 'BLOG', 'BLOOMBERG', 'BLUE', 'BM', 'BMS', + 'BMW', 'BN', 'BNPPARIBAS', 'BO', 'BOATS', 'BOEHRINGER', 'BOFA', 'BOM', + 'BOND', 'BOO', 'BOOK', 'BOOKING', 'BOSCH', 'BOSTIK', 'BOSTON', 'BOT', + 'BOUTIQUE', 'BOX', 'BR', 'BRADESCO', 'BRIDGESTONE', 'BROADWAY', + 'BROKER', 'BROTHER', 'BRUSSELS', 'BS', 'BT', 'BUILD', 'BUILDERS', + 'BUSINESS', 'BUY', 'BUZZ', 'BV', 'BW', 'BY', 'BZ', 'BZH', 'CA', 'CAB', + 'CAFE', 'CAL', 'CALL', 'CALVINKLEIN', 'CAM', 'CAMERA', 'CAMP', 'CANON', + 'CAPETOWN', 'CAPITAL', 'CAPITALONE', 'CAR', 'CARAVAN', 'CARDS', 'CARE', + 'CAREER', 'CAREERS', 'CARS', 'CASA', 'CASE', 'CASH', 'CASINO', 'CAT', + 'CATERING', 'CATHOLIC', 'CBA', 'CBN', 'CBRE', 'CBS', 'CC', 'CD', + 'CENTER', 'CEO', 'CERN', 'CF', 'CFA', 'CFD', 'CG', 'CH', 'CHANEL', + 'CHANNEL', 'CHARITY', 'CHASE', 'CHAT', 'CHEAP', 'CHINTAI', 'CHRISTMAS', + 'CHROME', 'CHURCH', 'CI', 'CIPRIANI', 'CIRCLE', 'CISCO', 'CITADEL', + 'CITI', 'CITIC', 'CITY', 'CITYEATS', 'CK', 'CL', 'CLAIMS', 'CLEANING', + 'CLICK', 'CLINIC', 'CLINIQUE', 'CLOTHING', 'CLOUD', 'CLUB', 'CLUBMED', + 'CM', 'CN', 'CO', 'COACH', 'CODES', 'COFFEE', 'COLLEGE', 'COLOGNE', + 'COM', 'COMCAST', 'COMMBANK', 'COMMUNITY', 'COMPANY', 'COMPARE', + 'COMPUTER', 'COMSEC', 'CONDOS', 'CONSTRUCTION', 'CONSULTING', + 'CONTACT', 'CONTRACTORS', 'COOKING', 'COOKINGCHANNEL', 'COOL', 'COOP', + 'CORSICA', 'COUNTRY', 'COUPON', 'COUPONS', 'COURSES', 'CPA', 'CR', + 'CREDIT', 'CREDITCARD', 'CREDITUNION', 'CRICKET', 'CROWN', 'CRS', + 'CRUISE', 'CRUISES', 'CU', 'CUISINELLA', 'CV', 'CW', 'CX', 'CY', + 'CYMRU', 'CYOU', 'CZ', 'DABUR', 'DAD', 'DANCE', 'DATA', 'DATE', + 'DATING', 'DATSUN', 'DAY', 'DCLK', 'DDS', 'DE', 'DEAL', 'DEALER', + 'DEALS', 'DEGREE', 'DELIVERY', 'DELL', 'DELOITTE', 'DELTA', 'DEMOCRAT', + 'DENTAL', 'DENTIST', 'DESI', 'DESIGN', 'DEV', 'DHL', 'DIAMONDS', + 'DIET', 'DIGITAL', 'DIRECT', 'DIRECTORY', 'DISCOUNT', 'DISCOVER', + 'DISH', 'DIY', 'DJ', 'DK', 'DM', 'DNP', 'DO', 'DOCS', 'DOCTOR', 'DOG', + 'DOMAINS', 'DOT', 'DOWNLOAD', 'DRIVE', 'DTV', 'DUBAI', 'DUNLOP', + 'DUPONT', 'DURBAN', 'DVAG', 'DVR', 'DZ', 'EARTH', 'EAT', 'EC', 'ECO', + 'EDEKA', 'EDU', 'EDUCATION', 'EE', 'EG', 'EMAIL', 'EMERCK', 'ENERGY', + 'ENGINEER', 'ENGINEERING', 'ENTERPRISES', 'EPSON', 'EQUIPMENT', 'ER', + 'ERICSSON', 'ERNI', 'ES', 'ESQ', 'ESTATE', 'ET', 'ETISALAT', 'EU', + 'EUROVISION', 'EUS', 'EVENTS', 'EXCHANGE', 'EXPERT', 'EXPOSED', + 'EXPRESS', 'EXTRASPACE', 'FAGE', 'FAIL', 'FAIRWINDS', 'FAITH', + 'FAMILY', 'FAN', 'FANS', 'FARM', 'FARMERS', 'FASHION', 'FAST', 'FEDEX', + 'FEEDBACK', 'FERRARI', 'FERRERO', 'FI', 'FIDELITY', 'FIDO', 'FILM', + 'FINAL', 'FINANCE', 'FINANCIAL', 'FIRE', 'FIRESTONE', 'FIRMDALE', + 'FISH', 'FISHING', 'FIT', 'FITNESS', 'FJ', 'FK', 'FLICKR', 'FLIGHTS', + 'FLIR', 'FLORIST', 'FLOWERS', 'FLY', 'FM', 'FO', 'FOO', 'FOOD', + 'FOODNETWORK', 'FOOTBALL', 'FORD', 'FOREX', 'FORSALE', 'FORUM', + 'FOUNDATION', 'FOX', 'FR', 'FREE', 'FRESENIUS', 'FRL', 'FROGANS', + 'FRONTDOOR', 'FRONTIER', 'FTR', 'FUJITSU', 'FUN', 'FUND', 'FURNITURE', + 'FUTBOL', 'FYI', 'GA', 'GAL', 'GALLERY', 'GALLO', 'GALLUP', 'GAME', + 'GAMES', 'GAP', 'GARDEN', 'GAY', 'GB', 'GBIZ', 'GD', 'GDN', 'GE', + 'GEA', 'GENT', 'GENTING', 'GEORGE', 'GF', 'GG', 'GGEE', 'GH', 'GI', + 'GIFT', 'GIFTS', 'GIVES', 'GIVING', 'GL', 'GLASS', 'GLE', 'GLOBAL', + 'GLOBO', 'GM', 'GMAIL', 'GMBH', 'GMO', 'GMX', 'GN', 'GODADDY', 'GOLD', + 'GOLDPOINT', 'GOLF', 'GOO', 'GOODYEAR', 'GOOG', 'GOOGLE', 'GOP', 'GOT', + 'GOV', 'GP', 'GQ', 'GR', 'GRAINGER', 'GRAPHICS', 'GRATIS', 'GREEN', + 'GRIPE', 'GROCERY', 'GROUP', 'GS', 'GT', 'GU', 'GUARDIAN', 'GUCCI', + 'GUGE', 'GUIDE', 'GUITARS', 'GURU', 'GW', 'GY', 'HAIR', 'HAMBURG', + 'HANGOUT', 'HAUS', 'HBO', 'HDFC', 'HDFCBANK', 'HEALTH', 'HEALTHCARE', + 'HELP', 'HELSINKI', 'HERE', 'HERMES', 'HGTV', 'HIPHOP', 'HISAMITSU', + 'HITACHI', 'HIV', 'HK', 'HKT', 'HM', 'HN', 'HOCKEY', 'HOLDINGS', + 'HOLIDAY', 'HOMEDEPOT', 'HOMEGOODS', 'HOMES', 'HOMESENSE', 'HONDA', + 'HORSE', 'HOSPITAL', 'HOST', 'HOSTING', 'HOT', 'HOTELES', 'HOTELS', + 'HOTMAIL', 'HOUSE', 'HOW', 'HR', 'HSBC', 'HT', 'HU', 'HUGHES', 'HYATT', + 'HYUNDAI', 'IBM', 'ICBC', 'ICE', 'ICU', 'ID', 'IE', 'IEEE', 'IFM', + 'IKANO', 'IL', 'IM', 'IMAMAT', 'IMDB', 'IMMO', 'IMMOBILIEN', 'IN', + 'INC', 'INDUSTRIES', 'INFINITI', 'INFO', 'ING', 'INK', 'INSTITUTE', + 'INSURANCE', 'INSURE', 'INT', 'INTERNATIONAL', 'INTUIT', 'INVESTMENTS', + 'IO', 'IPIRANGA', 'IQ', 'IR', 'IRISH', 'IS', 'ISMAILI', 'IST', + 'ISTANBUL', 'IT', 'ITAU', 'ITV', 'JAGUAR', 'JAVA', 'JCB', 'JE', 'JEEP', + 'JETZT', 'JEWELRY', 'JIO', 'JLL', 'JM', 'JMP', 'JNJ', 'JO', 'JOBS', + 'JOBURG', 'JOT', 'JOY', 'JP', 'JPMORGAN', 'JPRS', 'JUEGOS', 'JUNIPER', + 'KAUFEN', 'KDDI', 'KE', 'KERRYHOTELS', 'KERRYLOGISTICS', + 'KERRYPROPERTIES', 'KFH', 'KG', 'KH', 'KI', 'KIA', 'KIDS', 'KIM', + 'KINDER', 'KINDLE', 'KITCHEN', 'KIWI', 'KM', 'KN', 'KOELN', 'KOMATSU', + 'KOSHER', 'KP', 'KPMG', 'KPN', 'KR', 'KRD', 'KRED', 'KUOKGROUP', 'KW', + 'KY', 'KYOTO', 'KZ', 'LA', 'LACAIXA', 'LAMBORGHINI', 'LAMER', + 'LANCASTER', 'LAND', 'LANDROVER', 'LANXESS', 'LASALLE', 'LAT', + 'LATINO', 'LATROBE', 'LAW', 'LAWYER', 'LB', 'LC', 'LDS', 'LEASE', + 'LECLERC', 'LEFRAK', 'LEGAL', 'LEGO', 'LEXUS', 'LGBT', 'LI', 'LIDL', + 'LIFE', 'LIFEINSURANCE', 'LIFESTYLE', 'LIGHTING', 'LIKE', 'LILLY', + 'LIMITED', 'LIMO', 'LINCOLN', 'LINK', 'LIPSY', 'LIVE', 'LIVING', 'LK', + 'LLC', 'LLP', 'LOAN', 'LOANS', 'LOCKER', 'LOCUS', 'LOL', 'LONDON', + 'LOTTE', 'LOTTO', 'LOVE', 'LPL', 'LPLFINANCIAL', 'LR', 'LS', 'LT', + 'LTD', 'LTDA', 'LU', 'LUNDBECK', 'LUXE', 'LUXURY', 'LV', 'LY', 'MA', + 'MADRID', 'MAIF', 'MAISON', 'MAKEUP', 'MAN', 'MANAGEMENT', 'MANGO', + 'MAP', 'MARKET', 'MARKETING', 'MARKETS', 'MARRIOTT', 'MARSHALLS', + 'MATTEL', 'MBA', 'MC', 'MCKINSEY', 'MD', 'ME', 'MED', 'MEDIA', 'MEET', + 'MELBOURNE', 'MEME', 'MEMORIAL', 'MEN', 'MENU', 'MERCKMSD', 'MG', 'MH', + 'MIAMI', 'MICROSOFT', 'MIL', 'MINI', 'MINT', 'MIT', 'MITSUBISHI', 'MK', + 'ML', 'MLB', 'MLS', 'MM', 'MMA', 'MN', 'MO', 'MOBI', 'MOBILE', 'MODA', + 'MOE', 'MOI', 'MOM', 'MONASH', 'MONEY', 'MONSTER', 'MORMON', + 'MORTGAGE', 'MOSCOW', 'MOTO', 'MOTORCYCLES', 'MOV', 'MOVIE', 'MP', + 'MQ', 'MR', 'MS', 'MSD', 'MT', 'MTN', 'MTR', 'MU', 'MUSEUM', 'MUSIC', + 'MUTUAL', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 'NAB', 'NAGOYA', 'NAME', + 'NATURA', 'NAVY', 'NBA', 'NC', 'NE', 'NEC', 'NET', 'NETBANK', + 'NETFLIX', 'NETWORK', 'NEUSTAR', 'NEW', 'NEWS', 'NEXT', 'NEXTDIRECT', + 'NEXUS', 'NF', 'NFL', 'NG', 'NGO', 'NHK', 'NI', 'NICO', 'NIKE', + 'NIKON', 'NINJA', 'NISSAN', 'NISSAY', 'NL', 'NO', 'NOKIA', + 'NORTHWESTERNMUTUAL', 'NORTON', 'NOW', 'NOWRUZ', 'NOWTV', 'NP', 'NR', + 'NRA', 'NRW', 'NTT', 'NU', 'NYC', 'NZ', 'OBI', 'OBSERVER', 'OFFICE', + 'OKINAWA', 'OLAYAN', 'OLAYANGROUP', 'OLDNAVY', 'OLLO', 'OM', 'OMEGA', + 'ONE', 'ONG', 'ONL', 'ONLINE', 'OOO', 'OPEN', 'ORACLE', 'ORANGE', + 'ORG', 'ORGANIC', 'ORIGINS', 'OSAKA', 'OTSUKA', 'OTT', 'OVH', 'PA', + 'PAGE', 'PANASONIC', 'PARIS', 'PARS', 'PARTNERS', 'PARTS', 'PARTY', + 'PASSAGENS', 'PAY', 'PCCW', 'PE', 'PET', 'PF', 'PFIZER', 'PG', 'PH', + 'PHARMACY', 'PHD', 'PHILIPS', 'PHONE', 'PHOTO', 'PHOTOGRAPHY', + 'PHOTOS', 'PHYSIO', 'PICS', 'PICTET', 'PICTURES', 'PID', 'PIN', 'PING', + 'PINK', 'PIONEER', 'PIZZA', 'PK', 'PL', 'PLACE', 'PLAY', 'PLAYSTATION', + 'PLUMBING', 'PLUS', 'PM', 'PN', 'PNC', 'POHL', 'POKER', 'POLITIE', + 'PORN', 'POST', 'PR', 'PRAMERICA', 'PRAXI', 'PRESS', 'PRIME', 'PRO', + 'PROD', 'PRODUCTIONS', 'PROF', 'PROGRESSIVE', 'PROMO', 'PROPERTIES', + 'PROPERTY', 'PROTECTION', 'PRU', 'PRUDENTIAL', 'PS', 'PT', 'PUB', 'PW', + 'PWC', 'PY', 'QA', 'QPON', 'QUEBEC', 'QUEST', 'RACING', 'RADIO', 'RE', + 'READ', 'REALESTATE', 'REALTOR', 'REALTY', 'RECIPES', 'RED', + 'REDSTONE', 'REDUMBRELLA', 'REHAB', 'REISE', 'REISEN', 'REIT', + 'RELIANCE', 'REN', 'RENT', 'RENTALS', 'REPAIR', 'REPORT', 'REPUBLICAN', + 'REST', 'RESTAURANT', 'REVIEW', 'REVIEWS', 'REXROTH', 'RICH', + 'RICHARDLI', 'RICOH', 'RIL', 'RIO', 'RIP', 'RO', 'ROCHER', 'ROCKS', + 'RODEO', 'ROGERS', 'ROOM', 'RS', 'RSVP', 'RU', 'RUGBY', 'RUHR', 'RUN', + 'RW', 'RWE', 'RYUKYU', 'SA', 'SAARLAND', 'SAFE', 'SAFETY', 'SAKURA', + 'SALE', 'SALON', 'SAMSCLUB', 'SAMSUNG', 'SANDVIK', 'SANDVIKCOROMANT', + 'SANOFI', 'SAP', 'SARL', 'SAS', 'SAVE', 'SAXO', 'SB', 'SBI', 'SBS', + 'SC', 'SCA', 'SCB', 'SCHAEFFLER', 'SCHMIDT', 'SCHOLARSHIPS', 'SCHOOL', + 'SCHULE', 'SCHWARZ', 'SCIENCE', 'SCOT', 'SD', 'SE', 'SEARCH', 'SEAT', + 'SECURE', 'SECURITY', 'SEEK', 'SELECT', 'SENER', 'SERVICES', 'SEVEN', + 'SEW', 'SEX', 'SEXY', 'SFR', 'SG', 'SH', 'SHANGRILA', 'SHARP', 'SHAW', + 'SHELL', 'SHIA', 'SHIKSHA', 'SHOES', 'SHOP', 'SHOPPING', 'SHOUJI', + 'SHOW', 'SHOWTIME', 'SI', 'SILK', 'SINA', 'SINGLES', 'SITE', 'SJ', + 'SK', 'SKI', 'SKIN', 'SKY', 'SKYPE', 'SL', 'SLING', 'SM', 'SMART', + 'SMILE', 'SN', 'SNCF', 'SO', 'SOCCER', 'SOCIAL', 'SOFTBANK', + 'SOFTWARE', 'SOHU', 'SOLAR', 'SOLUTIONS', 'SONG', 'SONY', 'SOY', 'SPA', + 'SPACE', 'SPORT', 'SPOT', 'SR', 'SRL', 'SS', 'ST', 'STADA', 'STAPLES', + 'STAR', 'STATEBANK', 'STATEFARM', 'STC', 'STCGROUP', 'STOCKHOLM', + 'STORAGE', 'STORE', 'STREAM', 'STUDIO', 'STUDY', 'STYLE', 'SU', + 'SUCKS', 'SUPPLIES', 'SUPPLY', 'SUPPORT', 'SURF', 'SURGERY', 'SUZUKI', + 'SV', 'SWATCH', 'SWISS', 'SX', 'SY', 'SYDNEY', 'SYSTEMS', 'SZ', 'TAB', + 'TAIPEI', 'TALK', 'TAOBAO', 'TARGET', 'TATAMOTORS', 'TATAR', 'TATTOO', + 'TAX', 'TAXI', 'TC', 'TCI', 'TD', 'TDK', 'TEAM', 'TECH', 'TECHNOLOGY', + 'TEL', 'TEMASEK', 'TENNIS', 'TEVA', 'TF', 'TG', 'TH', 'THD', 'THEATER', + 'THEATRE', 'TIAA', 'TICKETS', 'TIENDA', 'TIFFANY', 'TIPS', 'TIRES', + 'TIROL', 'TJ', 'TJMAXX', 'TJX', 'TK', 'TKMAXX', 'TL', 'TM', 'TMALL', + 'TN', 'TO', 'TODAY', 'TOKYO', 'TOOLS', 'TOP', 'TORAY', 'TOSHIBA', + 'TOTAL', 'TOURS', 'TOWN', 'TOYOTA', 'TOYS', 'TR', 'TRADE', 'TRADING', + 'TRAINING', 'TRAVEL', 'TRAVELCHANNEL', 'TRAVELERS', + 'TRAVELERSINSURANCE', 'TRUST', 'TRV', 'TT', 'TUBE', 'TUI', 'TUNES', + 'TUSHU', 'TV', 'TVS', 'TW', 'TZ', 'UA', 'UBANK', 'UBS', 'UG', 'UK', + 'UNICOM', 'UNIVERSITY', 'UNO', 'UOL', 'UPS', 'US', 'UY', 'UZ', 'VA', + 'VACATIONS', 'VANA', 'VANGUARD', 'VC', 'VE', 'VEGAS', 'VENTURES', + 'VERISIGN', 'VERSICHERUNG', 'VET', 'VG', 'VI', 'VIAJES', 'VIDEO', + 'VIG', 'VIKING', 'VILLAS', 'VIN', 'VIP', 'VIRGIN', 'VISA', 'VISION', + 'VIVA', 'VIVO', 'VLAANDEREN', 'VN', 'VODKA', 'VOLKSWAGEN', 'VOLVO', + 'VOTE', 'VOTING', 'VOTO', 'VOYAGE', 'VU', 'VUELOS', 'WALES', 'WALMART', + 'WALTER', 'WANG', 'WANGGOU', 'WATCH', 'WATCHES', 'WEATHER', + 'WEATHERCHANNEL', 'WEBCAM', 'WEBER', 'WEBSITE', 'WED', 'WEDDING', + 'WEIBO', 'WEIR', 'WF', 'WHOSWHO', 'WIEN', 'WIKI', 'WILLIAMHILL', 'WIN', + 'WINDOWS', 'WINE', 'WINNERS', 'WME', 'WOLTERSKLUWER', 'WOODSIDE', + 'WORK', 'WORKS', 'WORLD', 'WOW', 'WS', 'WTC', 'WTF', 'XBOX', 'XEROX', + 'XFINITY', 'XIHUAN', 'XIN', 'XN--11B4C3D', 'XN--1CK2E1B', + 'XN--1QQW23A', 'XN--2SCRJ9C', 'XN--30RR7Y', 'XN--3BST00M', + 'XN--3DS443G', 'XN--3E0B707E', 'XN--3HCRJ9C', 'XN--3PXU8K', + 'XN--42C2D9A', 'XN--45BR5CYL', 'XN--45BRJ9C', 'XN--45Q11C', + 'XN--4DBRK0CE', 'XN--4GBRIM', 'XN--54B7FTA0CC', 'XN--55QW42G', + 'XN--55QX5D', 'XN--5SU34J936BGSG', 'XN--5TZM5G', 'XN--6FRZ82G', + 'XN--6QQ986B3XL', 'XN--80ADXHKS', 'XN--80AO21A', 'XN--80AQECDR1A', + 'XN--80ASEHDB', 'XN--80ASWG', 'XN--8Y0A063A', 'XN--90A3AC', 'XN--90AE', + 'XN--90AIS', 'XN--9DBQ2A', 'XN--9ET52U', 'XN--9KRT00A', + 'XN--B4W605FERD', 'XN--BCK1B9A5DRE4C', 'XN--C1AVG', 'XN--C2BR7G', + 'XN--CCK2B3B', 'XN--CCKWCXETD', 'XN--CG4BKI', 'XN--CLCHC0EA0B2G2A9GCD', + 'XN--CZR694B', 'XN--CZRS0T', 'XN--CZRU2D', 'XN--D1ACJ3B', 'XN--D1ALF', + 'XN--E1A4C', 'XN--ECKVDTC9D', 'XN--EFVY88H', 'XN--FCT429K', + 'XN--FHBEI', 'XN--FIQ228C5HS', 'XN--FIQ64B', 'XN--FIQS8S', + 'XN--FIQZ9S', 'XN--FJQ720A', 'XN--FLW351E', 'XN--FPCRJ9C3D', + 'XN--FZC2C9E2C', 'XN--FZYS8D69UVGM', 'XN--G2XX48C', 'XN--GCKR3F0F', + 'XN--GECRJ9C', 'XN--GK3AT1E', 'XN--H2BREG3EVE', 'XN--H2BRJ9C', + 'XN--H2BRJ9C8C', 'XN--HXT814E', 'XN--I1B6B1A6A2E', 'XN--IMR513N', + 'XN--IO0A7I', 'XN--J1AEF', 'XN--J1AMH', 'XN--J6W193G', + 'XN--JLQ480N2RG', 'XN--JVR189M', 'XN--KCRX77D1X4A', 'XN--KPRW13D', + 'XN--KPRY57D', 'XN--KPUT3I', 'XN--L1ACC', 'XN--LGBBAT1AD8J', + 'XN--MGB9AWBF', 'XN--MGBA3A3EJT', 'XN--MGBA3A4F16A', + 'XN--MGBA7C0BBN0A', 'XN--MGBAAKC7DVF', 'XN--MGBAAM7A8H', + 'XN--MGBAB2BD', 'XN--MGBAH1A3HJKRD', 'XN--MGBAI9AZGQP6J', + 'XN--MGBAYH7GPA', 'XN--MGBBH1A', 'XN--MGBBH1A71E', 'XN--MGBC0A9AZCG', + 'XN--MGBCA7DZDO', 'XN--MGBCPQ6GPA1A', 'XN--MGBERP4A5D4AR', + 'XN--MGBGU82A', 'XN--MGBI4ECEXP', 'XN--MGBPL2FH', 'XN--MGBT3DHD', + 'XN--MGBTX2B', 'XN--MGBX4CD0AB', 'XN--MIX891F', 'XN--MK1BU44C', + 'XN--MXTQ1M', 'XN--NGBC5AZD', 'XN--NGBE9E0A', 'XN--NGBRX', 'XN--NODE', + 'XN--NQV7F', 'XN--NQV7FS00EMA', 'XN--NYQY26A', 'XN--O3CW4H', + 'XN--OGBPF8FL', 'XN--OTU796D', 'XN--P1ACF', 'XN--P1AI', 'XN--PGBS0DH', + 'XN--PSSY2U', 'XN--Q7CE6A', 'XN--Q9JYB4C', 'XN--QCKA1PMC', 'XN--QXA6A', + 'XN--QXAM', 'XN--RHQV96G', 'XN--ROVU88B', 'XN--RVC1E0AM3E', + 'XN--S9BRJ9C', 'XN--SES554G', 'XN--T60B56A', 'XN--TCKWE', + 'XN--TIQ49XQYJ', 'XN--UNUP4Y', 'XN--VERMGENSBERATER-CTB', + 'XN--VERMGENSBERATUNG-PWB', 'XN--VHQUV', 'XN--VUQ861B', + 'XN--W4R85EL8FHU5DNRA', 'XN--W4RS40L', 'XN--WGBH1C', 'XN--WGBL6A', + 'XN--XHQ521B', 'XN--XKC2AL3HYE2A', 'XN--XKC2DL3A5EE0H', 'XN--Y9A3AQ', + 'XN--YFRO4I67O', 'XN--YGBI2AMMX', 'XN--ZFR164B', 'XXX', 'XYZ', + 'YACHTS', 'YAHOO', 'YAMAXUN', 'YANDEX', 'YE', 'YODOBASHI', 'YOGA', + 'YOKOHAMA', 'YOU', 'YOUTUBE', 'YT', 'YUN', 'ZA', 'ZAPPOS', 'ZARA', + 'ZERO', 'ZIP', 'ZM', 'ZONE', 'ZUERICH', 'ZW', + ]; + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_scalar($input)) { + return false; + } + + return in_array(mb_strtoupper((string) $input), self::TLD_LIST); + } +} diff --git a/vendor/workerman/validation/library/Rules/TrueVal.php b/vendor/workerman/validation/library/Rules/TrueVal.php new file mode 100644 index 0000000..c9018e2 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/TrueVal.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function filter_var; + +use const FILTER_NULL_ON_FAILURE; +use const FILTER_VALIDATE_BOOLEAN; + +/** + * Validates if a value is considered as true. + * + * @author Henrique Moody + * @author Paul Karikari + */ +final class TrueVal extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + return filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) === true; + } +} diff --git a/vendor/workerman/validation/library/Rules/Type.php b/vendor/workerman/validation/library/Rules/Type.php new file mode 100644 index 0000000..f16d80a --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Type.php @@ -0,0 +1,86 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use function array_keys; +use function gettype; +use function implode; +use function is_callable; +use function sprintf; + +/** + * Validates the type of input. + * + * @author Gabriel Caruso + * @author Henrique Moody + * @author Paul Karikari + */ +final class Type extends AbstractRule +{ + /** + * Collection of available types for validation. + * + */ + private const AVAILABLE_TYPES = [ + 'array' => 'array', + 'bool' => 'boolean', + 'boolean' => 'boolean', + 'callable' => 'callable', + 'double' => 'double', + 'float' => 'double', + 'int' => 'integer', + 'integer' => 'integer', + 'null' => 'NULL', + 'object' => 'object', + 'resource' => 'resource', + 'string' => 'string', + ]; + + /** + * Type to validate input against. + * + * @var string + */ + private $type; + + /** + * Initializes the rule. + * + * @throws ComponentException When $type is not a valid one + */ + public function __construct(string $type) + { + if (!isset(self::AVAILABLE_TYPES[$type])) { + throw new ComponentException( + sprintf( + '"%s" is not a valid type (Available: %s)', + $type, + implode(', ', array_keys(self::AVAILABLE_TYPES)) + ) + ); + } + + $this->type = $type; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($this->type === 'callable') { + return is_callable($input); + } + + return self::AVAILABLE_TYPES[$this->type] === gettype($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Unique.php b/vendor/workerman/validation/library/Rules/Unique.php new file mode 100644 index 0000000..2ce865c --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Unique.php @@ -0,0 +1,37 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function array_unique; +use function is_array; + +use const SORT_REGULAR; + +/** + * Validates whether the input array contains only unique values. + * + * @author Henrique Moody + * @author Krzysztof Śmiałek + * @author Paul Karikari + */ +final class Unique extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_array($input)) { + return false; + } + + return $input == array_unique($input, SORT_REGULAR); + } +} diff --git a/vendor/workerman/validation/library/Rules/Uploaded.php b/vendor/workerman/validation/library/Rules/Uploaded.php new file mode 100644 index 0000000..9bc7eda --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Uploaded.php @@ -0,0 +1,45 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Psr\Http\Message\UploadedFileInterface; +use SplFileInfo; + +use function is_scalar; +use function is_uploaded_file; + +/** + * Validates if the given data is a file that was uploaded via HTTP POST. + * + * @author Henrique Moody + * @author Paul Karikari + */ +final class Uploaded extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof SplFileInfo) { + return $this->validate($input->getPathname()); + } + + if ($input instanceof UploadedFileInterface) { + return true; + } + + if (!is_scalar($input)) { + return false; + } + + return is_uploaded_file((string) $input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Uppercase.php b/vendor/workerman/validation/library/Rules/Uppercase.php new file mode 100644 index 0000000..58ae4e0 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Uppercase.php @@ -0,0 +1,36 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_string; +use function mb_strtoupper; + +/** + * Validates whether the characters in the input are uppercase. + * + * @author Alexandre Gomes Gaigalas + * @author Danilo Benevides + * @author Henrique Moody + * @author Jean Pimentel + */ +final class Uppercase extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + return $input === mb_strtoupper($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Url.php b/vendor/workerman/validation/library/Rules/Url.php new file mode 100644 index 0000000..f1775b3 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Url.php @@ -0,0 +1,32 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use const FILTER_VALIDATE_URL; + +/** + * Validates whether the input is a URL. + * + * @author Henrique Moody + */ +final class Url extends AbstractEnvelope +{ + /** + * Initializes the rule. + * + * @throws ComponentException + */ + public function __construct() + { + parent::__construct(new FilterVar(FILTER_VALIDATE_URL)); + } +} diff --git a/vendor/workerman/validation/library/Rules/Uuid.php b/vendor/workerman/validation/library/Rules/Uuid.php new file mode 100644 index 0000000..1ea7659 --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Uuid.php @@ -0,0 +1,80 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use function is_string; +use function preg_match; +use function sprintf; + +/** + * Validates whether the input is a valid UUID. + * + * It also supports validation of specific versions 1, 3, 4 and 5. + * + * @author Dick van der Heiden + * @author Henrique Moody + * @author Michael Weimann + */ +final class Uuid extends AbstractRule +{ + /** + * Placeholder in "sprintf()" format used to create the REGEX that validates inputs. + */ + private const PATTERN_FORMAT = '/^[0-9a-f]{8}-[0-9a-f]{4}-%s[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i'; + + /** + * The UUID version to validate for. + * + * @var int|null + */ + private $version; + + /** + * Initializes the rule with the desired version. + * + * @throws ComponentException when the version is not valid + */ + public function __construct(?int $version = null) + { + if ($version !== null && !$this->isSupportedVersion($version)) { + throw new ComponentException(sprintf('Only versions 1, 3, 4, and 5 are supported: %d given', $version)); + } + + $this->version = $version; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + return preg_match($this->getPattern(), $input) > 0; + } + + private function isSupportedVersion(int $version): bool + { + return $version >= 1 && $version <= 5 && $version !== 2; + } + + private function getPattern(): string + { + if ($this->version !== null) { + return sprintf(self::PATTERN_FORMAT, $this->version); + } + + return sprintf(self::PATTERN_FORMAT, '[13-5]'); + } +} diff --git a/vendor/workerman/validation/library/Rules/Version.php b/vendor/workerman/validation/library/Rules/Version.php new file mode 100644 index 0000000..6dfb0cf --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Version.php @@ -0,0 +1,36 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_string; +use function preg_match; + +/** + * Validates version numbers using Semantic Versioning. + * + * @see http://semver.org/ + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class Version extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + return preg_match('/^[0-9]+\.[0-9]+\.[0-9]+([+-][^+-][0-9A-Za-z-.]*)?$/', $input) > 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/VideoUrl.php b/vendor/workerman/validation/library/Rules/VideoUrl.php new file mode 100644 index 0000000..7ba7d3c --- /dev/null +++ b/vendor/workerman/validation/library/Rules/VideoUrl.php @@ -0,0 +1,90 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\ComponentException; + +use function array_keys; +use function is_string; +use function mb_strtolower; +use function preg_match; +use function sprintf; + +/** + * Validates if the input is a video URL value. + * + * @author Danilo Correa + * @author Emmerson Siqueira + * @author Henrique Moody + * @author Ricardo Gobbo + */ +final class VideoUrl extends AbstractRule +{ + private const SERVICES = [ + // phpcs:disable Generic.Files.LineLength.TooLong + 'youtube' => '@^https?://(www\.)?(?:youtube\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^\"&?/]{11})@i', + 'vimeo' => '@^https?://(www\.)?(player\.)?(vimeo\.com/)((channels/[A-z]+/)|(groups/[A-z]+/videos/)|(video/))?([0-9]+)@i', + 'twitch' => '@^https?://(((www\.)?twitch\.tv/videos/[0-9]+)|clips\.twitch\.tv/[a-zA-Z]+)$@i', + // phpcs:enable Generic.Files.LineLength.TooLong + ]; + + /** + * @var string|null + */ + private $service; + + /** + * Create a new instance VideoUrl. + * + * @throws ComponentException when the given service is not supported + */ + public function __construct(?string $service = null) + { + if ($service !== null && !$this->isSupportedService($service)) { + throw new ComponentException(sprintf('"%s" is not a recognized video service.', $service)); + } + + $this->service = $service; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + if ($this->service !== null) { + return $this->isValid($this->service, $input); + } + + foreach (array_keys(self::SERVICES) as $service) { + if (!$this->isValid($service, $input)) { + continue; + } + + return true; + } + + return false; + } + + private function isSupportedService(string $service): bool + { + return isset(self::SERVICES[mb_strtolower($service)]); + } + + private function isValid(string $service, string $input): bool + { + return preg_match(self::SERVICES[mb_strtolower($service)], $input) > 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/Vowel.php b/vendor/workerman/validation/library/Rules/Vowel.php new file mode 100644 index 0000000..e0988fa --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Vowel.php @@ -0,0 +1,29 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function preg_match; + +/** + * Validates whether the input contains only vowels. + * + * @author Henrique Moody + * @author Nick Lombard + */ +final class Vowel extends AbstractFilterRule +{ + /** + * {@inheritDoc} + */ + protected function validateFilteredInput(string $input): bool + { + return preg_match('/^[aeiouAEIOU]+$/', $input) > 0; + } +} diff --git a/vendor/workerman/validation/library/Rules/When.php b/vendor/workerman/validation/library/Rules/When.php new file mode 100644 index 0000000..292b47b --- /dev/null +++ b/vendor/workerman/validation/library/Rules/When.php @@ -0,0 +1,91 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Respect\Validation\Exceptions\AlwaysInvalidException; +use Respect\Validation\Validatable; + +/** + * A ternary validator that accepts three parameters. + * + * @author Alexandre Gomes Gaigalas + * @author Danilo Correa + * @author Henrique Moody + * @author Hugo Hamon + */ +final class When extends AbstractRule +{ + /** + * @var Validatable + */ + private $when; + + /** + * @var Validatable + */ + private $then; + + /** + * @var Validatable + */ + private $else; + + public function __construct(Validatable $when, Validatable $then, ?Validatable $else = null) + { + $this->when = $when; + $this->then = $then; + if ($else === null) { + $else = new AlwaysInvalid(); + $else->setTemplate(AlwaysInvalidException::SIMPLE); + } + + $this->else = $else; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($this->when->validate($input)) { + return $this->then->validate($input); + } + + return $this->else->validate($input); + } + + /** + * {@inheritDoc} + */ + public function assert($input): void + { + if ($this->when->validate($input)) { + $this->then->assert($input); + + return; + } + + $this->else->assert($input); + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + if ($this->when->validate($input)) { + $this->then->check($input); + + return; + } + + $this->else->check($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Writable.php b/vendor/workerman/validation/library/Rules/Writable.php new file mode 100644 index 0000000..7b87a8c --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Writable.php @@ -0,0 +1,41 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use Psr\Http\Message\StreamInterface; +use SplFileInfo; + +use function is_string; +use function is_writable; + +/** + * Validates if the given input is writable file. + * + * @author Danilo Correa + * @author Henrique Moody + */ +final class Writable extends AbstractRule +{ + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if ($input instanceof SplFileInfo) { + return $input->isWritable(); + } + + if ($input instanceof StreamInterface) { + return $input->isWritable(); + } + + return is_string($input) && is_writable($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Xdigit.php b/vendor/workerman/validation/library/Rules/Xdigit.php new file mode 100644 index 0000000..4322d6d --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Xdigit.php @@ -0,0 +1,27 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function ctype_xdigit; + +/** + * @author Andre Ramaciotti + * @author Henrique Moody + */ +final class Xdigit extends AbstractFilterRule +{ + /** + * {@inheritDoc} + */ + protected function validateFilteredInput(string $input): bool + { + return ctype_xdigit($input); + } +} diff --git a/vendor/workerman/validation/library/Rules/Yes.php b/vendor/workerman/validation/library/Rules/Yes.php new file mode 100644 index 0000000..fc8b27f --- /dev/null +++ b/vendor/workerman/validation/library/Rules/Yes.php @@ -0,0 +1,59 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Rules; + +use function is_string; +use function nl_langinfo; +use function preg_match; + +use const YESEXPR; + +/** + * Validates if the input considered as "Yes". + * + * @author Cameron Hall + * @author Henrique Moody + */ +final class Yes extends AbstractRule +{ + /** + * @var bool + */ + private $useLocale; + + /** + * Initializes the rule. + */ + public function __construct(bool $useLocale = false) + { + $this->useLocale = $useLocale; + } + + /** + * {@inheritDoc} + */ + public function validate($input): bool + { + if (!is_string($input)) { + return false; + } + + return preg_match($this->getPattern(), $input) > 0; + } + + private function getPattern(): string + { + if ($this->useLocale) { + return '/' . nl_langinfo(YESEXPR) . '/'; + } + + return '/^y(eah?|ep|es)?$/i'; + } +} diff --git a/vendor/workerman/validation/library/StaticValidator.php b/vendor/workerman/validation/library/StaticValidator.php new file mode 100644 index 0000000..9f2fee3 --- /dev/null +++ b/vendor/workerman/validation/library/StaticValidator.php @@ -0,0 +1,375 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation; + +use finfo; +use Respect\Validation\Rules\Key; + +interface StaticValidator +{ + public static function allOf(Validatable ...$rule): ChainedValidator; + + public static function alnum(string ...$additionalChars): ChainedValidator; + + public static function alpha(string ...$additionalChars): ChainedValidator; + + public static function alwaysInvalid(): ChainedValidator; + + public static function alwaysValid(): ChainedValidator; + + public static function anyOf(Validatable ...$rule): ChainedValidator; + + public static function arrayType(): ChainedValidator; + + public static function arrayVal(): ChainedValidator; + + public static function attribute( + string $reference, + ?Validatable $validator = null, + bool $mandatory = true + ): ChainedValidator; + + public static function base(int $base, ?string $chars = null): ChainedValidator; + + public static function base64(): ChainedValidator; + + /** + * @param mixed $minimum + * @param mixed $maximum + */ + public static function between($minimum, $maximum): ChainedValidator; + + public static function bic(string $countryCode): ChainedValidator; + + public static function boolType(): ChainedValidator; + + public static function boolVal(): ChainedValidator; + + public static function bsn(): ChainedValidator; + + public static function call(callable $callable, Validatable $rule): ChainedValidator; + + public static function callableType(): ChainedValidator; + + public static function callback(callable $callback): ChainedValidator; + + public static function charset(string ...$charset): ChainedValidator; + + public static function cnh(): ChainedValidator; + + public static function cnpj(): ChainedValidator; + + public static function control(string ...$additionalChars): ChainedValidator; + + public static function consonant(string ...$additionalChars): ChainedValidator; + + /** + * @param mixed $containsValue + */ + public static function contains($containsValue, bool $identical = false): ChainedValidator; + + /** + * @param mixed[] $needles + */ + public static function containsAny(array $needles, bool $strictCompareArray = false): ChainedValidator; + + public static function countable(): ChainedValidator; + + public static function countryCode(?string $set = null): ChainedValidator; + + public static function currencyCode(): ChainedValidator; + + public static function cpf(): ChainedValidator; + + public static function creditCard(?string $brand = null): ChainedValidator; + + public static function date(string $format = 'Y-m-d'): ChainedValidator; + + public static function dateTime(?string $format = null): ChainedValidator; + + public static function decimal(int $decimals): ChainedValidator; + + public static function digit(string ...$additionalChars): ChainedValidator; + + public static function directory(): ChainedValidator; + + public static function domain(bool $tldCheck = true): ChainedValidator; + + public static function each(Validatable $rule): ChainedValidator; + + public static function email(): ChainedValidator; + + /** + * @param mixed $endValue + */ + public static function endsWith($endValue, bool $identical = false): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public static function equals($compareTo): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public static function equivalent($compareTo): ChainedValidator; + + public static function even(): ChainedValidator; + + public static function executable(): ChainedValidator; + + public static function exists(): ChainedValidator; + + public static function extension(string $extension): ChainedValidator; + + public static function factor(int $dividend): ChainedValidator; + + public static function falseVal(): ChainedValidator; + + public static function fibonacci(): ChainedValidator; + + public static function file(): ChainedValidator; + + /** + * @param mixed[]|int $options + */ + public static function filterVar(int $filter, $options = null): ChainedValidator; + + public static function finite(): ChainedValidator; + + public static function floatVal(): ChainedValidator; + + public static function floatType(): ChainedValidator; + + public static function graph(string ...$additionalChars): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public static function greaterThan($compareTo): ChainedValidator; + + public static function hexRgbColor(): ChainedValidator; + + public static function iban(): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public static function identical($compareTo): ChainedValidator; + + public static function image(?finfo $fileInfo = null): ChainedValidator; + + public static function imei(): ChainedValidator; + + /** + * @param mixed[]|mixed $haystack + */ + public static function in($haystack, bool $compareIdentical = false): ChainedValidator; + + public static function infinite(): ChainedValidator; + + public static function instance(string $instanceName): ChainedValidator; + + public static function intVal(): ChainedValidator; + + public static function intType(): ChainedValidator; + + public static function ip(string $range = '*', ?int $options = null): ChainedValidator; + + public static function isbn(): ChainedValidator; + + public static function iterableType(): ChainedValidator; + + public static function json(): ChainedValidator; + + public static function key( + string $reference, + ?Validatable $referenceValidator = null, + bool $mandatory = true + ): ChainedValidator; + + public static function keyNested( + string $reference, + ?Validatable $referenceValidator = null, + bool $mandatory = true + ): ChainedValidator; + + public static function keySet(Key ...$rule): ChainedValidator; + + public static function keyValue(string $comparedKey, string $ruleName, string $baseKey): ChainedValidator; + + public static function languageCode(?string $set = null): ChainedValidator; + + public static function leapDate(string $format): ChainedValidator; + + public static function leapYear(): ChainedValidator; + + public static function length(?int $min = null, ?int $max = null, bool $inclusive = true): ChainedValidator; + + public static function lowercase(): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public static function lessThan($compareTo): ChainedValidator; + + public static function luhn(): ChainedValidator; + + public static function macAddress(): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public static function max($compareTo): ChainedValidator; + + public static function maxAge(int $age, ?string $format = null): ChainedValidator; + + public static function mimetype(string $mimetype): ChainedValidator; + + /** + * @param mixed $compareTo + */ + public static function min($compareTo): ChainedValidator; + + public static function minAge(int $age, ?string $format = null): ChainedValidator; + + public static function multiple(int $multipleOf): ChainedValidator; + + public static function negative(): ChainedValidator; + + public static function nfeAccessKey(): ChainedValidator; + + public static function nif(): ChainedValidator; + + public static function nip(): ChainedValidator; + + public static function no(bool $useLocale = false): ChainedValidator; + + public static function noneOf(Validatable ...$rule): ChainedValidator; + + public static function not(Validatable $rule): ChainedValidator; + + public static function notBlank(): ChainedValidator; + + public static function notEmoji(): ChainedValidator; + + public static function notEmpty(): ChainedValidator; + + public static function notOptional(): ChainedValidator; + + public static function noWhitespace(): ChainedValidator; + + public static function nullable(Validatable $rule): ChainedValidator; + + public static function nullType(): ChainedValidator; + + public static function number(): ChainedValidator; + + public static function numericVal(): ChainedValidator; + + public static function objectType(): ChainedValidator; + + public static function odd(): ChainedValidator; + + public static function oneOf(Validatable ...$rule): ChainedValidator; + + public static function optional(Validatable $rule): ChainedValidator; + + public static function perfectSquare(): ChainedValidator; + + public static function pesel(): ChainedValidator; + + public static function phone(): ChainedValidator; + + public static function mobile(): ChainedValidator; + public static function phpLabel(): ChainedValidator; + + public static function pis(): ChainedValidator; + + public static function polishIdCard(): ChainedValidator; + + public static function positive(): ChainedValidator; + + public static function postalCode(string $countryCode): ChainedValidator; + + public static function primeNumber(): ChainedValidator; + + public static function printable(string ...$additionalChars): ChainedValidator; + + public static function punct(string ...$additionalChars): ChainedValidator; + + public static function readable(): ChainedValidator; + + public static function regex(string $regex): ChainedValidator; + + public static function resourceType(): ChainedValidator; + + public static function roman(): ChainedValidator; + + public static function scalarVal(): ChainedValidator; + + public static function size(?string $minSize = null, ?string $maxSize = null): ChainedValidator; + + public static function slug(): ChainedValidator; + + public static function sorted(string $direction): ChainedValidator; + + public static function space(string ...$additionalChars): ChainedValidator; + + /** + * @param mixed $startValue + */ + public static function startsWith($startValue, bool $identical = false): ChainedValidator; + + public static function stringType(): ChainedValidator; + + public static function stringVal(): ChainedValidator; + + public static function subdivisionCode(string $countryCode): ChainedValidator; + + /** + * @param mixed[] $superset + */ + public static function subset(array $superset): ChainedValidator; + + public static function symbolicLink(): ChainedValidator; + + public static function time(string $format = 'H:i:s'): ChainedValidator; + + public static function tld(): ChainedValidator; + + public static function trueVal(): ChainedValidator; + + public static function type(string $type): ChainedValidator; + + public static function unique(): ChainedValidator; + + public static function uploaded(): ChainedValidator; + + public static function uppercase(): ChainedValidator; + + public static function url(): ChainedValidator; + + public static function uuid(?int $version = null): ChainedValidator; + + public static function version(): ChainedValidator; + + public static function videoUrl(?string $service = null): ChainedValidator; + + public static function vowel(string ...$additionalChars): ChainedValidator; + + public static function when(Validatable $if, Validatable $then, ?Validatable $else = null): ChainedValidator; + + public static function writable(): ChainedValidator; + + public static function xdigit(string ...$additionalChars): ChainedValidator; + + public static function yes(bool $useLocale = false): ChainedValidator; +} diff --git a/vendor/workerman/validation/library/Validatable.php b/vendor/workerman/validation/library/Validatable.php new file mode 100644 index 0000000..6f07aab --- /dev/null +++ b/vendor/workerman/validation/library/Validatable.php @@ -0,0 +1,48 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation; + +use Respect\Validation\Exceptions\ValidationException; + +/** Interface for validation rules */ +/** + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +interface Validatable +{ + /** + * @param mixed $input + */ + public function assert($input): void; + + /** + * @param mixed $input + */ + public function check($input): void; + + public function getName(): ?string; + + /** + * @param mixed $input + * @param mixed[] $extraParameters + */ + public function reportError($input, array $extraParameters = []): ValidationException; + + public function setName(string $name): Validatable; + + public function setTemplate(string $template): Validatable; + + public function setDefault(string $default, bool $defaultType=false): Validatable; + /** + * @param mixed $input + */ + public function validate($input): bool; +} diff --git a/vendor/workerman/validation/library/Validator.php b/vendor/workerman/validation/library/Validator.php new file mode 100644 index 0000000..1ffceb3 --- /dev/null +++ b/vendor/workerman/validation/library/Validator.php @@ -0,0 +1,99 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation; + +use Respect\Validation\Exceptions\ComponentException; +use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\Rules\AllOf; + +use function count; + +/** + * @mixin StaticValidator + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class Validator extends AllOf +{ + /** + * Create instance validator. + */ + public static function create(): self + { + return new self(); + } + + /** + * {@inheritDoc} + */ + public function check($input): void + { + try { + parent::check($input); + } catch (ValidationException $exception) { + if (count($this->getRules()) == 1 && $this->template) { + $exception->updateTemplate($this->template); + } + + throw $exception; + } + } + + /** + * Creates a new Validator instance with a rule that was called on the static method. + * + * @param mixed[] $arguments + * + * @throws ComponentException + */ + public static function __callStatic(string $ruleName, array $arguments): self + { + return self::create()->__call($ruleName, $arguments); + } + + /** + * Create a new rule by the name of the method and adds the rule to the chain. + * + * @param mixed[] $arguments + * + * @throws ComponentException + */ + public function __call(string $ruleName, array $arguments): self + { + $this->addRule(Factory::getDefaultInstance()->rule($ruleName, $arguments)); + + return $this; + } + + + /** + * 按照规则检查输入,如果不符合规则则抛出异常 + * + * @param array $input + * @param array $rules + * @return array + */ + public static function input(array $input, array $rules) + { + $values = []; + foreach ($rules as $field => $rule) { + if(is_array($rule) || !($rule instanceof \Respect\Validation\Validator)){ + $values[$field] = $rule; + }else{ + $value = $rule->defaultType?$rule->default:($input[$field] ?? $rule->default); + $rule->check($value); + $values[$field] = $value; + } + } + return $values; + } + +} diff --git a/vendor/workerman/webman-framework/.gitignore b/vendor/workerman/webman-framework/.gitignore new file mode 100644 index 0000000..3f2c43a --- /dev/null +++ b/vendor/workerman/webman-framework/.gitignore @@ -0,0 +1,5 @@ +composer.lock +vendor +vendor/ +.idea +.idea/ \ No newline at end of file diff --git a/vendor/workerman/webman-framework/README.md b/vendor/workerman/webman-framework/README.md new file mode 100644 index 0000000..96a900a --- /dev/null +++ b/vendor/workerman/webman-framework/README.md @@ -0,0 +1,5 @@ +# webman-framework +Note: This repository is the core code of the webman framework. If you want to build an application using webman, visit the main [webman](https://github.com/walkor/webman) repository. + +## LICENSE +MIT diff --git a/vendor/workerman/webman-framework/composer.json b/vendor/workerman/webman-framework/composer.json new file mode 100644 index 0000000..53c9308 --- /dev/null +++ b/vendor/workerman/webman-framework/composer.json @@ -0,0 +1,47 @@ +{ + "name": "workerman/webman-framework", + "type": "library", + "keywords": [ + "high performance", + "http service" + ], + "homepage": "https://www.workerman.net", + "license": "MIT", + "description": "High performance HTTP Service Framework.", + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net", + "homepage": "https://www.workerman.net", + "role": "Developer" + } + ], + "support": { + "email": "walkor@workerman.net", + "issues": "https://github.com/walkor/webman/issues", + "forum": "https://wenda.workerman.net/", + "wiki": "https://doc.workerman.net/", + "source": "https://github.com/walkor/webman-framework" + }, + "require": { + "php": ">=7.2", + "ext-json": "*", + "workerman/workerman": "^4.0.4 || ^5.0.0", + "nikic/fast-route": "^1.3", + "psr/container": ">=1.0" + }, + "suggest": { + "ext-event": "For better performance. " + }, + "autoload": { + "psr-4": { + "Webman\\": "./src", + "support\\": "./src/support", + "Support\\": "./src/support", + "Support\\Bootstrap\\": "./src/support/bootstrap", + "Support\\Exception\\": "./src/support/exception", + "Support\\View\\": "./src/support/view" + } + }, + "minimum-stability": "dev" +} diff --git a/vendor/workerman/webman-framework/src/App.php b/vendor/workerman/webman-framework/src/App.php new file mode 100644 index 0000000..f01db3c --- /dev/null +++ b/vendor/workerman/webman-framework/src/App.php @@ -0,0 +1,939 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman; + +use Closure; +use Exception; +use FastRoute\Dispatcher; +use InvalidArgumentException; +use Monolog\Logger; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\ContainerInterface; +use Psr\Container\NotFoundExceptionInterface; +use ReflectionClass; +use ReflectionException; +use ReflectionFunction; +use ReflectionFunctionAbstract; +use ReflectionMethod; +use Throwable; +use Webman\Exception\ExceptionHandler; +use Webman\Exception\ExceptionHandlerInterface; +use Webman\Http\Request; +use Webman\Http\Response; +use Webman\Route\Route as RouteObject; +use Workerman\Connection\TcpConnection; +use Workerman\Protocols\Http; +use Workerman\Worker; +use function array_merge; +use function array_pop; +use function array_reduce; +use function array_reverse; +use function array_splice; +use function array_values; +use function class_exists; +use function clearstatcache; +use function count; +use function current; +use function end; +use function explode; +use function file_get_contents; +use function get_class_methods; +use function gettype; +use function implode; +use function in_array; +use function is_a; +use function is_array; +use function is_dir; +use function is_file; +use function is_string; +use function key; +use function method_exists; +use function next; +use function ob_get_clean; +use function ob_start; +use function pathinfo; +use function scandir; +use function str_replace; +use function strpos; +use function strtolower; +use function substr; +use function trim; + +/** + * Class App + * @package Webman + */ +class App +{ + + /** + * @var callable[] + */ + protected static $callbacks = []; + + /** + * @var Worker + */ + protected static $worker = null; + + /** + * @var Logger + */ + protected static $logger = null; + + /** + * @var string + */ + protected static $appPath = ''; + + /** + * @var string + */ + protected static $publicPath = ''; + + /** + * @var string + */ + protected static $requestClass = ''; + + /** + * App constructor. + * @param string $requestClass + * @param Logger $logger + * @param string $appPath + * @param string $publicPath + */ + public function __construct(string $requestClass, Logger $logger, string $appPath, string $publicPath) + { + static::$requestClass = $requestClass; + static::$logger = $logger; + static::$publicPath = $publicPath; + static::$appPath = $appPath; + } + + /** + * OnMessage. + * @param TcpConnection|mixed $connection + * @param Request|mixed $request + * @return null + */ + public function onMessage($connection, $request) + { + try { + Context::set(Request::class, $request); + $path = $request->path(); + $key = $request->method() . $path; + if (isset(static::$callbacks[$key])) { + [$callback, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key]; + static::send($connection, $callback($request), $request); + return null; + } + + if ( + static::unsafeUri($connection, $path, $request) || + static::findFile($connection, $path, $key, $request) || + static::findRoute($connection, $path, $key, $request) + ) { + return null; + } + + $controllerAndAction = static::parseControllerAction($path); + $plugin = $controllerAndAction['plugin'] ?? static::getPluginByPath($path); + if (!$controllerAndAction || Route::hasDisableDefaultRoute($plugin)) { + $request->plugin = $plugin; + $callback = static::getFallback($plugin); + $request->app = $request->controller = $request->action = ''; + static::send($connection, $callback($request), $request); + return null; + } + $app = $controllerAndAction['app']; + $controller = $controllerAndAction['controller']; + $action = $controllerAndAction['action']; + $callback = static::getCallback($plugin, $app, [$controller, $action]); + static::collectCallbacks($key, [$callback, $plugin, $app, $controller, $action, null]); + [$callback, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key]; + static::send($connection, $callback($request), $request); + } catch (Throwable $e) { + static::send($connection, static::exceptionResponse($e, $request), $request); + } + return null; + } + + /** + * OnWorkerStart. + * @param $worker + * @return void + */ + public function onWorkerStart($worker) + { + static::$worker = $worker; + Http::requestClass(static::$requestClass); + } + + /** + * CollectCallbacks. + * @param string $key + * @param array $data + * @return void + */ + protected static function collectCallbacks(string $key, array $data) + { + static::$callbacks[$key] = $data; + if (count(static::$callbacks) >= 1024) { + unset(static::$callbacks[key(static::$callbacks)]); + } + } + + /** + * UnsafeUri. + * @param TcpConnection $connection + * @param string $path + * @param $request + * @return bool + */ + protected static function unsafeUri(TcpConnection $connection, string $path, $request): bool + { + if ( + !$path || + strpos($path, '..') !== false || + strpos($path, "\\") !== false || + strpos($path, "\0") !== false + ) { + $callback = static::getFallback(); + $request->plugin = $request->app = $request->controller = $request->action = ''; + static::send($connection, $callback($request), $request); + return true; + } + return false; + } + + /** + * GetFallback. + * @param string $plugin + * @return Closure + */ + protected static function getFallback(string $plugin = ''): Closure + { + // when route, controller and action not found, try to use Route::fallback + return Route::getFallback($plugin) ?: function () { + try { + $notFoundContent = file_get_contents(static::$publicPath . '/404.html'); + } catch (Throwable $e) { + $notFoundContent = '404 Not Found'; + } + return new Response(404, [], $notFoundContent); + }; + } + + /** + * ExceptionResponse. + * @param Throwable $e + * @param $request + * @return Response + */ + protected static function exceptionResponse(Throwable $e, $request): Response + { + try { + $app = $request->app ?: ''; + $plugin = $request->plugin ?: ''; + $exceptionConfig = static::config($plugin, 'exception'); + $defaultException = $exceptionConfig[''] ?? ExceptionHandler::class; + $exceptionHandlerClass = $exceptionConfig[$app] ?? $defaultException; + + /** @var ExceptionHandlerInterface $exceptionHandler */ + $exceptionHandler = static::container($plugin)->make($exceptionHandlerClass, [ + 'logger' => static::$logger, + 'debug' => static::config($plugin, 'app.debug') + ]); + $exceptionHandler->report($e); + $response = $exceptionHandler->render($request, $e); + $response->exception($e); + return $response; + } catch (Throwable $e) { + $response = new Response(500, [], static::config($plugin ?? '', 'app.debug') ? (string)$e : $e->getMessage()); + $response->exception($e); + return $response; + } + } + + /** + * GetCallback. + * @param string $plugin + * @param string $app + * @param $call + * @param array|null $args + * @param bool $withGlobalMiddleware + * @param RouteObject|null $route + * @return callable + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws ReflectionException + */ + protected static function getCallback(string $plugin, string $app, $call, array $args = null, bool $withGlobalMiddleware = true, RouteObject $route = null) + { + $args = $args === null ? null : array_values($args); + $middlewares = []; + if ($route) { + $routeMiddlewares = $route->getMiddleware(); + foreach ($routeMiddlewares as $className) { + $middlewares[] = [$className, 'process']; + } + } + $middlewares = array_merge($middlewares, Middleware::getMiddleware($plugin, $app, $withGlobalMiddleware)); + + foreach ($middlewares as $key => $item) { + $middleware = $item[0]; + if (is_string($middleware)) { + $middleware = static::container($plugin)->get($middleware); + } elseif ($middleware instanceof Closure) { + $middleware = call_user_func($middleware, static::container($plugin)); + } + if (!$middleware instanceof MiddlewareInterface) { + throw new InvalidArgumentException('Not support middleware type'); + } + $middlewares[$key][0] = $middleware; + } + + $needInject = static::isNeedInject($call, $args); + if (is_array($call) && is_string($call[0])) { + $controllerReuse = static::config($plugin, 'app.controller_reuse', true); + if (!$controllerReuse) { + if ($needInject) { + $call = function ($request, ...$args) use ($call, $plugin) { + $call[0] = static::container($plugin)->make($call[0]); + $reflector = static::getReflector($call); + $args = static::resolveMethodDependencies($plugin, $request, $args, $reflector); + return $call(...$args); + }; + $needInject = false; + } else { + $call = function ($request, ...$args) use ($call, $plugin) { + $call[0] = static::container($plugin)->make($call[0]); + return $call($request, ...$args); + }; + } + } else { + $call[0] = static::container($plugin)->get($call[0]); + } + } + + if ($needInject) { + $call = static::resolveInject($plugin, $call); + } + + if ($middlewares) { + $callback = array_reduce($middlewares, function ($carry, $pipe) { + return function ($request) use ($carry, $pipe) { + try { + return $pipe($request, $carry); + } catch (Throwable $e) { + return static::exceptionResponse($e, $request); + } + }; + }, function ($request) use ($call, $args) { + try { + if ($args === null) { + $response = $call($request); + } else { + $response = $call($request, ...$args); + } + } catch (Throwable $e) { + return static::exceptionResponse($e, $request); + } + if (!$response instanceof Response) { + if (!is_string($response)) { + $response = static::stringify($response); + } + $response = new Response(200, [], $response); + } + return $response; + }); + } else { + if ($args === null) { + $callback = $call; + } else { + $callback = function ($request) use ($call, $args) { + return $call($request, ...$args); + }; + } + } + return $callback; + } + + /** + * ResolveInject. + * @param string $plugin + * @param array|Closure $call + * @return Closure + * @see Dependency injection through reflection information + */ + protected static function resolveInject(string $plugin, $call): Closure + { + return function (Request $request, ...$args) use ($plugin, $call) { + $reflector = static::getReflector($call); + $args = static::resolveMethodDependencies($plugin, $request, $args, $reflector); + return $call(...$args); + }; + } + + /** + * Check whether inject is required. + * @param $call + * @param $args + * @return bool + * @throws ReflectionException + */ + protected static function isNeedInject($call, $args): bool + { + if (is_array($call) && !method_exists($call[0], $call[1])) { + return false; + } + $args = $args ?: []; + $reflector = static::getReflector($call); + $reflectionParameters = $reflector->getParameters(); + if (!$reflectionParameters) { + return false; + } + $firstParameter = current($reflectionParameters); + unset($reflectionParameters[key($reflectionParameters)]); + $adaptersList = ['int', 'string', 'bool', 'array', 'object', 'float', 'mixed', 'resource']; + foreach ($reflectionParameters as $parameter) { + if ($parameter->hasType() && !in_array($parameter->getType()->getName(), $adaptersList)) { + return true; + } + } + if (!$firstParameter->hasType()) { + return count($args) > count($reflectionParameters); + } + + if (!is_a(static::$requestClass, $firstParameter->getType()->getName())) { + return true; + } + + return false; + } + + /** + * Get reflector. + * @param $call + * @return ReflectionFunction|ReflectionMethod + * @throws ReflectionException + */ + protected static function getReflector($call) + { + if ($call instanceof Closure || is_string($call)) { + return new ReflectionFunction($call); + } + return new ReflectionMethod($call[0], $call[1]); + } + + /** + * Return dependent parameters + * @param string $plugin + * @param Request $request + * @param array $args + * @param ReflectionFunctionAbstract $reflector + * @return array + */ + protected static function resolveMethodDependencies(string $plugin, Request $request, array $args, ReflectionFunctionAbstract $reflector): array + { + // Specification parameter information + $args = array_values($args); + $parameters = []; + // An array of reflection classes for loop parameters, with each $parameter representing a reflection object of parameters + foreach ($reflector->getParameters() as $parameter) { + // Parameter quota consumption + if ($parameter->hasType()) { + $name = $parameter->getType()->getName(); + switch ($name) { + case 'int': + case 'string': + case 'bool': + case 'array': + case 'object': + case 'float': + case 'mixed': + case 'resource': + goto _else; + default: + if (is_a($request, $name)) { + //Inject Request + $parameters[] = $request; + } else { + $parameters[] = static::container($plugin)->make($name); + } + break; + } + } else { + _else: + // The variable parameter + if (null !== key($args)) { + $parameters[] = current($args); + } else { + // Indicates whether the current parameter has a default value. If yes, return true + $parameters[] = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null; + } + // Quota of consumption variables + next($args); + } + } + + // Returns the result of parameters replacement + return $parameters; + } + + /** + * Container. + * @param string $plugin + * @return ContainerInterface + */ + public static function container(string $plugin = '') + { + return static::config($plugin, 'container'); + } + + /** + * Get request. + * @return Request|\support\Request + */ + public static function request() + { + return Context::get(Request::class); + } + + /** + * Get worker. + * @return Worker + */ + public static function worker(): ?Worker + { + return static::$worker; + } + + /** + * Find Route. + * @param TcpConnection $connection + * @param string $path + * @param string $key + * @param Request|mixed $request + * @return bool + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws ReflectionException + */ + protected static function findRoute(TcpConnection $connection, string $path, string $key, $request): bool + { + $routeInfo = Route::dispatch($request->method(), $path); + if ($routeInfo[0] === Dispatcher::FOUND) { + $routeInfo[0] = 'route'; + $callback = $routeInfo[1]['callback']; + $route = clone $routeInfo[1]['route']; + $app = $controller = $action = ''; + $args = !empty($routeInfo[2]) ? $routeInfo[2] : null; + if ($args) { + $route->setParams($args); + } + if (is_array($callback)) { + $controller = $callback[0]; + $plugin = static::getPluginByClass($controller); + $app = static::getAppByController($controller); + $action = static::getRealMethod($controller, $callback[1]) ?? ''; + } else { + $plugin = static::getPluginByPath($path); + } + $callback = static::getCallback($plugin, $app, $callback, $args, true, $route); + static::collectCallbacks($key, [$callback, $plugin, $app, $controller ?: '', $action, $route]); + [$callback, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key]; + static::send($connection, $callback($request), $request); + return true; + } + return false; + } + + /** + * Find File. + * @param TcpConnection $connection + * @param string $path + * @param string $key + * @param Request|mixed $request + * @return bool + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws ReflectionException + */ + protected static function findFile(TcpConnection $connection, string $path, string $key, $request): bool + { + if (preg_match('/%[0-9a-f]{2}/i', $path)) { + $path = urldecode($path); + if (static::unsafeUri($connection, $path, $request)) { + return true; + } + } + + $pathExplodes = explode('/', trim($path, '/')); + $plugin = ''; + if (isset($pathExplodes[1]) && $pathExplodes[0] === 'app') { + $plugin = $pathExplodes[1]; + $publicDir = static::config($plugin, 'app.public_path') ?: BASE_PATH . "/plugin/$pathExplodes[1]/public"; + $path = substr($path, strlen("/app/$pathExplodes[1]/")); + } else { + $publicDir = static::$publicPath; + } + $file = "$publicDir/$path"; + if (!is_file($file)) { + return false; + } + + if (pathinfo($file, PATHINFO_EXTENSION) === 'php') { + if (!static::config($plugin, 'app.support_php_files', false)) { + return false; + } + static::collectCallbacks($key, [function () use ($file) { + return static::execPhpFile($file); + }, '', '', '', '', null]); + [, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key]; + static::send($connection, static::execPhpFile($file), $request); + return true; + } + + if (!static::config($plugin, 'static.enable', false)) { + return false; + } + + static::collectCallbacks($key, [static::getCallback($plugin, '__static__', function ($request) use ($file, $plugin) { + clearstatcache(true, $file); + if (!is_file($file)) { + $callback = static::getFallback($plugin); + return $callback($request); + } + return (new Response())->file($file); + }, null, false), '', '', '', '', null]); + [$callback, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key]; + static::send($connection, $callback($request), $request); + return true; + } + + /** + * Send. + * @param TcpConnection|mixed $connection + * @param mixed $response + * @param Request|mixed $request + * @return void + */ + protected static function send($connection, $response, $request) + { + $keepAlive = $request->header('connection'); + Context::destroy(); + if (($keepAlive === null && $request->protocolVersion() === '1.1') + || $keepAlive === 'keep-alive' || $keepAlive === 'Keep-Alive' + ) { + $connection->send($response); + return; + } + $connection->close($response); + } + + /** + * ParseControllerAction. + * @param string $path + * @return array|false + * @throws ReflectionException + */ + protected static function parseControllerAction(string $path) + { + $path = str_replace(['-', '//'], ['', '/'], $path); + static $cache = []; + if (isset($cache[$path])) { + return $cache[$path]; + } + $pathExplode = explode('/', trim($path, '/')); + $isPlugin = isset($pathExplode[1]) && $pathExplode[0] === 'app'; + $configPrefix = $isPlugin ? "plugin.$pathExplode[1]." : ''; + $pathPrefix = $isPlugin ? "/app/$pathExplode[1]" : ''; + $classPrefix = $isPlugin ? "plugin\\$pathExplode[1]" : ''; + $suffix = Config::get("{$configPrefix}app.controller_suffix", ''); + $relativePath = trim(substr($path, strlen($pathPrefix)), '/'); + $pathExplode = $relativePath ? explode('/', $relativePath) : []; + + $action = 'index'; + if (!$controllerAction = static::guessControllerAction($pathExplode, $action, $suffix, $classPrefix)) { + if (count($pathExplode) <= 1) { + return false; + } + $action = end($pathExplode); + unset($pathExplode[count($pathExplode) - 1]); + $controllerAction = static::guessControllerAction($pathExplode, $action, $suffix, $classPrefix); + } + if ($controllerAction && !isset($path[256])) { + $cache[$path] = $controllerAction; + if (count($cache) > 1024) { + unset($cache[key($cache)]); + } + } + return $controllerAction; + } + + /** + * GuessControllerAction. + * @param $pathExplode + * @param $action + * @param $suffix + * @param $classPrefix + * @return array|false + * @throws ReflectionException + */ + protected static function guessControllerAction($pathExplode, $action, $suffix, $classPrefix) + { + $map[] = trim("$classPrefix\\app\\controller\\" . implode('\\', $pathExplode), '\\'); + foreach ($pathExplode as $index => $section) { + $tmp = $pathExplode; + array_splice($tmp, $index, 1, [$section, 'controller']); + $map[] = trim("$classPrefix\\" . implode('\\', array_merge(['app'], $tmp)), '\\'); + } + foreach ($map as $item) { + $map[] = $item . '\\index'; + } + + foreach ($map as $controllerClass) { + // Remove xx\xx\controller + if (substr($controllerClass, -11) === '\\controller') { + continue; + } + $controllerClass .= $suffix; + if ($controllerAction = static::getControllerAction($controllerClass, $action)) { + return $controllerAction; + } + } + return false; + } + + /** + * GetControllerAction. + * @param string $controllerClass + * @param string $action + * @return array|false + * @throws ReflectionException + */ + protected static function getControllerAction(string $controllerClass, string $action) + { + // Disable calling magic methods + if (strpos($action, '__') === 0) { + return false; + } + if (($controllerClass = static::getController($controllerClass)) && ($action = static::getAction($controllerClass, $action))) { + return [ + 'plugin' => static::getPluginByClass($controllerClass), + 'app' => static::getAppByController($controllerClass), + 'controller' => $controllerClass, + 'action' => $action + ]; + } + return false; + } + + /** + * GetController. + * @param string $controllerClass + * @return string|false + * @throws ReflectionException + */ + protected static function getController(string $controllerClass) + { + if (class_exists($controllerClass)) { + return (new ReflectionClass($controllerClass))->name; + } + $explodes = explode('\\', strtolower(ltrim($controllerClass, '\\'))); + $basePath = $explodes[0] === 'plugin' ? BASE_PATH . '/plugin' : static::$appPath; + unset($explodes[0]); + $fileName = array_pop($explodes) . '.php'; + $found = true; + foreach ($explodes as $pathSection) { + if (!$found) { + break; + } + $dirs = Util::scanDir($basePath, false); + $found = false; + foreach ($dirs as $name) { + $path = "$basePath/$name"; + if (is_dir($path) && strtolower($name) === $pathSection) { + $basePath = $path; + $found = true; + break; + } + } + } + if (!$found) { + return false; + } + foreach (scandir($basePath) ?: [] as $name) { + if (strtolower($name) === $fileName) { + require_once "$basePath/$name"; + if (class_exists($controllerClass, false)) { + return (new ReflectionClass($controllerClass))->name; + } + } + } + return false; + } + + /** + * GetAction. + * @param string $controllerClass + * @param string $action + * @return string|false + */ + protected static function getAction(string $controllerClass, string $action) + { + $methods = get_class_methods($controllerClass); + $lowerAction = strtolower($action); + $found = false; + foreach ($methods as $candidate) { + if (strtolower($candidate) === $lowerAction) { + $action = $candidate; + $found = true; + break; + } + } + if ($found) { + return $action; + } + // Action is not public method + if (method_exists($controllerClass, $action)) { + return false; + } + if (method_exists($controllerClass, '__call')) { + return $action; + } + return false; + } + + /** + * GetPluginByClass. + * @param string $controllerClass + * @return mixed|string + */ + public static function getPluginByClass(string $controllerClass) + { + $controllerClass = trim($controllerClass, '\\'); + $tmp = explode('\\', $controllerClass, 3); + if ($tmp[0] !== 'plugin') { + return ''; + } + return $tmp[1] ?? ''; + } + + /** + * GetPluginByPath. + * @param string $path + * @return mixed|string + */ + public static function getPluginByPath(string $path) + { + $path = trim($path, '/'); + $tmp = explode('/', $path, 3); + if ($tmp[0] !== 'app') { + return ''; + } + return $tmp[1] ?? ''; + } + + /** + * GetAppByController. + * @param string $controllerClass + * @return mixed|string + */ + protected static function getAppByController(string $controllerClass) + { + $controllerClass = trim($controllerClass, '\\'); + $tmp = explode('\\', $controllerClass, 5); + $pos = $tmp[0] === 'plugin' ? 3 : 1; + if (!isset($tmp[$pos])) { + return ''; + } + return strtolower($tmp[$pos]) === 'controller' ? '' : $tmp[$pos]; + } + + /** + * ExecPhpFile. + * @param string $file + * @return false|string + */ + public static function execPhpFile(string $file) + { + ob_start(); + // Try to include php file. + try { + include $file; + } catch (Exception $e) { + echo $e; + } + return ob_get_clean(); + } + + /** + * GetRealMethod. + * @param string $class + * @param string $method + * @return string + */ + protected static function getRealMethod(string $class, string $method): string + { + $method = strtolower($method); + $methods = get_class_methods($class); + foreach ($methods as $candidate) { + if (strtolower($candidate) === $method) { + return $candidate; + } + } + return $method; + } + + /** + * Config. + * @param string $plugin + * @param string $key + * @param $default + * @return array|mixed|null + */ + protected static function config(string $plugin, string $key, $default = null) + { + return Config::get($plugin ? "plugin.$plugin.$key" : $key, $default); + } + + + /** + * @param mixed $data + * @return string + */ + protected static function stringify($data): string + { + $type = gettype($data); + switch ($type) { + case 'boolean': + return $data ? 'true' : 'false'; + case 'NULL': + return 'NULL'; + case 'array': + return 'Array'; + case 'object': + if (!method_exists($data, '__toString')) { + return 'Object'; + } + default: + return (string)$data; + } + } +} diff --git a/vendor/workerman/webman-framework/src/Bootstrap.php b/vendor/workerman/webman-framework/src/Bootstrap.php new file mode 100644 index 0000000..50774a1 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Bootstrap.php @@ -0,0 +1,28 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman; + +use Workerman\Worker; + +interface Bootstrap +{ + /** + * onWorkerStart + * + * @param Worker|null $worker + * @return mixed + */ + public static function start(?Worker $worker); +} diff --git a/vendor/workerman/webman-framework/src/Config.php b/vendor/workerman/webman-framework/src/Config.php new file mode 100644 index 0000000..b0f4918 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Config.php @@ -0,0 +1,296 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman; + +use FilesystemIterator; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use function array_replace_recursive; +use function array_reverse; +use function count; +use function explode; +use function in_array; +use function is_array; +use function is_dir; +use function is_file; +use function key; +use function str_replace; + +class Config +{ + + /** + * @var array + */ + protected static $config = []; + + /** + * @var string + */ + protected static $configPath = ''; + + /** + * @var bool + */ + protected static $loaded = false; + + /** + * Load. + * @param string $configPath + * @param array $excludeFile + * @param string|null $key + * @return void + */ + public static function load(string $configPath, array $excludeFile = [], string $key = null) + { + static::$configPath = $configPath; + if (!$configPath) { + return; + } + static::$loaded = false; + $config = static::loadFromDir($configPath, $excludeFile); + if (!$config) { + static::$loaded = true; + return; + } + if ($key !== null) { + foreach (array_reverse(explode('.', $key)) as $k) { + $config = [$k => $config]; + } + } + static::$config = array_replace_recursive(static::$config, $config); + static::formatConfig(); + static::$loaded = true; + } + + /** + * This deprecated method will certainly be removed in the future. + * @param string $configPath + * @param array $excludeFile + * @return void + * @deprecated + */ + public static function reload(string $configPath, array $excludeFile = []) + { + static::load($configPath, $excludeFile); + } + + /** + * Clear. + * @return void + */ + public static function clear() + { + static::$config = []; + } + + /** + * FormatConfig. + * @return void + */ + protected static function formatConfig() + { + $config = static::$config; + // Merge log config + foreach ($config['plugin'] ?? [] as $firm => $projects) { + if (isset($projects['app'])) { + foreach ($projects['log'] ?? [] as $key => $item) { + $config['log']["plugin.$firm.$key"] = $item; + } + } + foreach ($projects as $name => $project) { + if (!is_array($project)) { + continue; + } + foreach ($project['log'] ?? [] as $key => $item) { + $config['log']["plugin.$firm.$name.$key"] = $item; + } + } + } + // Merge database config + foreach ($config['plugin'] ?? [] as $firm => $projects) { + if (isset($projects['app'])) { + foreach ($projects['database']['connections'] ?? [] as $key => $connection) { + $config['database']['connections']["plugin.$firm.$key"] = $connection; + } + } + foreach ($projects as $name => $project) { + if (!is_array($project)) { + continue; + } + foreach ($project['database']['connections'] ?? [] as $key => $connection) { + $config['database']['connections']["plugin.$firm.$name.$key"] = $connection; + } + } + } + if (!empty($config['database']['connections'])) { + $config['database']['default'] = $config['database']['default'] ?? key($config['database']['connections']); + } + // Merge thinkorm config + foreach ($config['plugin'] ?? [] as $firm => $projects) { + if (isset($projects['app'])) { + foreach ($projects['thinkorm']['connections'] ?? [] as $key => $connection) { + $config['thinkorm']['connections']["plugin.$firm.$key"] = $connection; + } + } + foreach ($projects as $name => $project) { + if (!is_array($project)) { + continue; + } + foreach ($project['thinkorm']['connections'] ?? [] as $key => $connection) { + $config['thinkorm']['connections']["plugin.$firm.$name.$key"] = $connection; + } + } + } + if (!empty($config['thinkorm']['connections'])) { + $config['thinkorm']['default'] = $config['thinkorm']['default'] ?? key($config['thinkorm']['connections']); + } + // Merge redis config + foreach ($config['plugin'] ?? [] as $firm => $projects) { + if (isset($projects['app'])) { + foreach ($projects['redis'] ?? [] as $key => $connection) { + $config['redis']["plugin.$firm.$key"] = $connection; + } + } + foreach ($projects as $name => $project) { + if (!is_array($project)) { + continue; + } + foreach ($project['redis'] ?? [] as $key => $connection) { + $config['redis']["plugin.$firm.$name.$key"] = $connection; + } + } + } + static::$config = $config; + } + + /** + * LoadFromDir. + * @param string $configPath + * @param array $excludeFile + * @return array + */ + public static function loadFromDir(string $configPath, array $excludeFile = []): array + { + $allConfig = []; + $dirIterator = new RecursiveDirectoryIterator($configPath, FilesystemIterator::FOLLOW_SYMLINKS); + $iterator = new RecursiveIteratorIterator($dirIterator); + foreach ($iterator as $file) { + /** var SplFileInfo $file */ + if (is_dir($file) || $file->getExtension() != 'php' || in_array($file->getBaseName('.php'), $excludeFile)) { + continue; + } + $appConfigFile = $file->getPath() . '/app.php'; + if (!is_file($appConfigFile)) { + continue; + } + $relativePath = str_replace($configPath . DIRECTORY_SEPARATOR, '', substr($file, 0, -4)); + $explode = array_reverse(explode(DIRECTORY_SEPARATOR, $relativePath)); + if (count($explode) >= 2) { + $appConfig = include $appConfigFile; + if (empty($appConfig['enable'])) { + continue; + } + } + $config = include $file; + foreach ($explode as $section) { + $tmp = []; + $tmp[$section] = $config; + $config = $tmp; + } + $allConfig = array_replace_recursive($allConfig, $config); + } + return $allConfig; + } + + /** + * Get. + * @param string|null $key + * @param mixed $default + * @return array|mixed|void|null + */ + public static function get(string $key = null, $default = null) + { + if ($key === null) { + return static::$config; + } + $keyArray = explode('.', $key); + $value = static::$config; + $found = true; + foreach ($keyArray as $index) { + if (!isset($value[$index])) { + if (static::$loaded) { + return $default; + } + $found = false; + break; + } + $value = $value[$index]; + } + if ($found) { + return $value; + } + return static::read($key, $default); + } + + /** + * Read. + * @param string $key + * @param mixed $default + * @return array|mixed|null + */ + protected static function read(string $key, $default = null) + { + $path = static::$configPath; + if ($path === '') { + return $default; + } + $keys = $keyArray = explode('.', $key); + foreach ($keyArray as $index => $section) { + unset($keys[$index]); + if (is_file($file = "$path/$section.php")) { + $config = include $file; + return static::find($keys, $config, $default); + } + if (!is_dir($path = "$path/$section")) { + return $default; + } + } + return $default; + } + + /** + * Find. + * @param array $keyArray + * @param mixed $stack + * @param mixed $default + * @return array|mixed + */ + protected static function find(array $keyArray, $stack, $default) + { + if (!is_array($stack)) { + return $default; + } + $value = $stack; + foreach ($keyArray as $index) { + if (!isset($value[$index])) { + return $default; + } + $value = $value[$index]; + } + return $value; + } + +} diff --git a/vendor/workerman/webman-framework/src/Container.php b/vendor/workerman/webman-framework/src/Container.php new file mode 100644 index 0000000..ef39377 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Container.php @@ -0,0 +1,84 @@ +instances[$name])) { + if (isset($this->definitions[$name])) { + $this->instances[$name] = call_user_func($this->definitions[$name], $this); + } else { + if (!class_exists($name)) { + throw new NotFoundException("Class '$name' not found"); + } + $this->instances[$name] = new $name(); + } + } + return $this->instances[$name]; + } + + /** + * Has. + * @param string $name + * @return bool + */ + public function has(string $name): bool + { + return array_key_exists($name, $this->instances) + || array_key_exists($name, $this->definitions); + } + + /** + * Make. + * @param string $name + * @param array $constructor + * @return mixed + * @throws NotFoundException + */ + public function make(string $name, array $constructor = []) + { + if (!class_exists($name)) { + throw new NotFoundException("Class '$name' not found"); + } + return new $name(... array_values($constructor)); + } + + /** + * AddDefinitions. + * @param array $definitions + * @return $this + */ + public function addDefinitions(array $definitions): Container + { + $this->definitions = array_merge($this->definitions, $definitions); + return $this; + } + +} diff --git a/vendor/workerman/webman-framework/src/Context.php b/vendor/workerman/webman-framework/src/Context.php new file mode 100644 index 0000000..b72abf7 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Context.php @@ -0,0 +1,129 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman; + +use Fiber; +use SplObjectStorage; +use StdClass; +use Swow\Coroutine; +use WeakMap; +use Workerman\Events\Revolt; +use Workerman\Events\Swoole; +use Workerman\Events\Swow; +use Workerman\Worker; +use function property_exists; + +/** + * Class Context + * @package Webman + */ +class Context +{ + + /** + * @var SplObjectStorage|WeakMap + */ + protected static $objectStorage; + + /** + * @var StdClass + */ + protected static $object; + + /** + * @return StdClass + */ + protected static function getObject(): StdClass + { + if (!static::$objectStorage) { + static::$objectStorage = class_exists(WeakMap::class) ? new WeakMap() : new SplObjectStorage(); + static::$object = new StdClass; + } + $key = static::getKey(); + if (!isset(static::$objectStorage[$key])) { + static::$objectStorage[$key] = new StdClass; + } + return static::$objectStorage[$key]; + } + + /** + * @return mixed + */ + protected static function getKey() + { + switch (Worker::$eventLoopClass) { + case Revolt::class: + return Fiber::getCurrent(); + case Swoole::class: + return \Swoole\Coroutine::getContext(); + case Swow::class: + return Coroutine::getCurrent(); + } + return static::$object; + } + + /** + * @param string|null $key + * @return mixed + */ + public static function get(string $key = null) + { + $obj = static::getObject(); + if ($key === null) { + return $obj; + } + return $obj->$key ?? null; + } + + /** + * @param string $key + * @param $value + * @return void + */ + public static function set(string $key, $value): void + { + $obj = static::getObject(); + $obj->$key = $value; + } + + /** + * @param string $key + * @return void + */ + public static function delete(string $key): void + { + $obj = static::getObject(); + unset($obj->$key); + } + + /** + * @param string $key + * @return bool + */ + public static function has(string $key): bool + { + $obj = static::getObject(); + return property_exists($obj, $key); + } + + /** + * @return void + */ + public static function destroy(): void + { + unset(static::$objectStorage[static::getKey()]); + } +} diff --git a/vendor/workerman/webman-framework/src/Exception/ExceptionHandler.php b/vendor/workerman/webman-framework/src/Exception/ExceptionHandler.php new file mode 100644 index 0000000..b8f53e4 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Exception/ExceptionHandler.php @@ -0,0 +1,118 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman\Exception; + +use Psr\Log\LoggerInterface; +use Throwable; +use Webman\Http\Request; +use Webman\Http\Response; +use function json_encode; +use function nl2br; +use function trim; + +/** + * Class Handler + * @package support\exception + */ +class ExceptionHandler implements ExceptionHandlerInterface +{ + /** + * @var LoggerInterface + */ + protected $logger = null; + + /** + * @var bool + */ + protected $debug = false; + + /** + * @var array + */ + public $dontReport = []; + + /** + * ExceptionHandler constructor. + * @param $logger + * @param $debug + */ + public function __construct($logger, $debug) + { + $this->logger = $logger; + $this->debug = $debug; + } + + /** + * @param Throwable $exception + * @return void + */ + public function report(Throwable $exception) + { + if ($this->shouldntReport($exception)) { + return; + } + $logs = ''; + if ($request = \request()) { + $logs = $request->getRealIp() . ' ' . $request->method() . ' ' . trim($request->fullUrl(), '/'); + } + $this->logger->error($logs . PHP_EOL . $exception); + } + + /** + * @param Request $request + * @param Throwable $exception + * @return Response + */ + public function render(Request $request, Throwable $exception): Response + { + $code = $exception->getCode(); + if ($request->expectsJson()) { + $json = ['code' => $code ?: 500, 'msg' => $this->debug ? $exception->getMessage() : 'Server internal error']; + $this->debug && $json['traces'] = (string)$exception; + return new Response(200, ['Content-Type' => 'application/json'], + json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + } + $error = $this->debug ? nl2br((string)$exception) : 'Server internal error'; + return new Response(500, [], $error); + } + + /** + * @param Throwable $e + * @return bool + */ + protected function shouldntReport(Throwable $e): bool + { + foreach ($this->dontReport as $type) { + if ($e instanceof $type) { + return true; + } + } + return false; + } + + /** + * Compatible $this->_debug + * + * @param string $name + * @return bool|null + */ + public function __get(string $name) + { + if ($name === '_debug') { + return $this->debug; + } + return null; + } +} diff --git a/vendor/workerman/webman-framework/src/Exception/ExceptionHandlerInterface.php b/vendor/workerman/webman-framework/src/Exception/ExceptionHandlerInterface.php new file mode 100644 index 0000000..8868707 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Exception/ExceptionHandlerInterface.php @@ -0,0 +1,35 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman\Exception; + +use Throwable; +use Webman\Http\Request; +use Webman\Http\Response; + +interface ExceptionHandlerInterface +{ + /** + * @param Throwable $exception + * @return mixed + */ + public function report(Throwable $exception); + + /** + * @param Request $request + * @param Throwable $exception + * @return Response + */ + public function render(Request $request, Throwable $exception): Response; +} \ No newline at end of file diff --git a/vendor/workerman/webman-framework/src/Exception/FileException.php b/vendor/workerman/webman-framework/src/Exception/FileException.php new file mode 100644 index 0000000..fa5dbe6 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Exception/FileException.php @@ -0,0 +1,25 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman\Exception; + +use RuntimeException; + +/** + * Class FileException + * @package Webman\Exception + */ +class FileException extends RuntimeException +{ +} \ No newline at end of file diff --git a/vendor/workerman/webman-framework/src/Exception/NotFoundException.php b/vendor/workerman/webman-framework/src/Exception/NotFoundException.php new file mode 100644 index 0000000..735c1d4 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Exception/NotFoundException.php @@ -0,0 +1,25 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman\Exception; + +use Psr\Container\NotFoundExceptionInterface; + +/** + * Class NotFoundException + * @package Webman\Exception + */ +class NotFoundException extends \Exception implements NotFoundExceptionInterface +{ +} diff --git a/vendor/workerman/webman-framework/src/File.php b/vendor/workerman/webman-framework/src/File.php new file mode 100644 index 0000000..a43328c --- /dev/null +++ b/vendor/workerman/webman-framework/src/File.php @@ -0,0 +1,56 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman; + +use SplFileInfo; +use Webman\Exception\FileException; +use function chmod; +use function is_dir; +use function mkdir; +use function pathinfo; +use function restore_error_handler; +use function set_error_handler; +use function sprintf; +use function strip_tags; +use function umask; + +class File extends SplFileInfo +{ + + /** + * Move. + * @param string $destination + * @return File + */ + public function move(string $destination): File + { + set_error_handler(function ($type, $msg) use (&$error) { + $error = $msg; + }); + $path = pathinfo($destination, PATHINFO_DIRNAME); + if (!is_dir($path) && !mkdir($path, 0777, true)) { + restore_error_handler(); + throw new FileException(sprintf('Unable to create the "%s" directory (%s)', $path, strip_tags($error))); + } + if (!rename($this->getPathname(), $destination)) { + restore_error_handler(); + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $destination, strip_tags($error))); + } + restore_error_handler(); + @chmod($destination, 0666 & ~umask()); + return new self($destination); + } + +} \ No newline at end of file diff --git a/vendor/workerman/webman-framework/src/FileSessionHandler.php b/vendor/workerman/webman-framework/src/FileSessionHandler.php new file mode 100644 index 0000000..ed0b3f2 --- /dev/null +++ b/vendor/workerman/webman-framework/src/FileSessionHandler.php @@ -0,0 +1,25 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Webman; + +/** + * This deprecated class will certainly be removed in the future. + * Please use Webman\Session\FileSessionHandler + * @deprecated + * @package Webman + */ +class FileSessionHandler extends Session\FileSessionHandler +{ + +} diff --git a/vendor/workerman/webman-framework/src/Http/Request.php b/vendor/workerman/webman-framework/src/Http/Request.php new file mode 100644 index 0000000..42d0a62 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Http/Request.php @@ -0,0 +1,321 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman\Http; + +use Webman\Route\Route; +use function current; +use function filter_var; +use function ip2long; +use function is_array; +use function strpos; +use const FILTER_FLAG_IPV4; +use const FILTER_FLAG_NO_PRIV_RANGE; +use const FILTER_FLAG_NO_RES_RANGE; +use const FILTER_VALIDATE_IP; + +/** + * Class Request + * @package Webman\Http + */ +class Request extends \Workerman\Protocols\Http\Request +{ + /** + * @var string + */ + public $plugin = null; + + /** + * @var string + */ + public $app = null; + + /** + * @var string + */ + public $controller = null; + + /** + * @var string + */ + public $action = null; + + /** + * @var Route + */ + public $route = null; + + /** + * @return mixed|null + */ + public function all() + { + return $this->post() + $this->get(); + } + + /** + * Input + * @param string $name + * @param mixed $default + * @return mixed|null + */ + public function input(string $name, $default = null) + { + $post = $this->post(); + if (isset($post[$name])) { + return $post[$name]; + } + $get = $this->get(); + return $get[$name] ?? $default; + } + + /** + * Only + * @param array $keys + * @return array + */ + public function only(array $keys): array + { + $all = $this->all(); + $result = []; + foreach ($keys as $key) { + if (isset($all[$key])) { + $result[$key] = $all[$key]; + } + } + return $result; + } + + /** + * Except + * @param array $keys + * @return mixed|null + */ + public function except(array $keys) + { + $all = $this->all(); + foreach ($keys as $key) { + unset($all[$key]); + } + return $all; + } + + /** + * File + * @param string|null $name + * @return null|UploadFile[]|UploadFile + */ + public function file($name = null) + { + $files = parent::file($name); + if (null === $files) { + return $name === null ? [] : null; + } + if ($name !== null) { + // Multi files + if (is_array(current($files))) { + return $this->parseFiles($files); + } + return $this->parseFile($files); + } + $uploadFiles = []; + foreach ($files as $name => $file) { + // Multi files + if (is_array(current($file))) { + $uploadFiles[$name] = $this->parseFiles($file); + } else { + $uploadFiles[$name] = $this->parseFile($file); + } + } + return $uploadFiles; + } + + /** + * ParseFile + * @param array $file + * @return UploadFile + */ + protected function parseFile(array $file): UploadFile + { + return new UploadFile($file['tmp_name'], $file['name'], $file['type'], $file['error']); + } + + /** + * ParseFiles + * @param array $files + * @return array + */ + protected function parseFiles(array $files): array + { + $uploadFiles = []; + foreach ($files as $key => $file) { + if (is_array(current($file))) { + $uploadFiles[$key] = $this->parseFiles($file); + } else { + $uploadFiles[$key] = $this->parseFile($file); + } + } + return $uploadFiles; + } + + /** + * GetRemoteIp + * @return string + */ + public function getRemoteIp(): string + { + return $this->connection->getRemoteIp(); + } + + /** + * GetRemotePort + * @return int + */ + public function getRemotePort(): int + { + return $this->connection->getRemotePort(); + } + + /** + * GetLocalIp + * @return string + */ + public function getLocalIp(): string + { + return $this->connection->getLocalIp(); + } + + /** + * GetLocalPort + * @return int + */ + public function getLocalPort(): int + { + return $this->connection->getLocalPort(); + } + + /** + * GetRealIp + * @param bool $safeMode + * @return string + */ + public function getRealIp(bool $safeMode = true): string + { + $remoteIp = $this->getRemoteIp(); + if ($safeMode && !static::isIntranetIp($remoteIp)) { + return $remoteIp; + } + $ip = $this->header('x-real-ip', $this->header('x-forwarded-for', + $this->header('client-ip', $this->header('x-client-ip', + $this->header('via', $remoteIp))))); + if (is_string($ip)) { + $ip = current(explode(',', $ip)); + } + return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : $remoteIp; + } + + /** + * Url + * @return string + */ + public function url(): string + { + return '//' . $this->host() . $this->path(); + } + + /** + * FullUrl + * @return string + */ + public function fullUrl(): string + { + return '//' . $this->host() . $this->uri(); + } + + /** + * IsAjax + * @return bool + */ + public function isAjax(): bool + { + return $this->header('X-Requested-With') === 'XMLHttpRequest'; + } + + /** + * IsPjax + * @return bool + */ + public function isPjax(): bool + { + return (bool)$this->header('X-PJAX'); + } + + /** + * ExpectsJson + * @return bool + */ + public function expectsJson(): bool + { + return ($this->isAjax() && !$this->isPjax()) || $this->acceptJson(); + } + + /** + * AcceptJson + * @return bool + */ + public function acceptJson(): bool + { + return false !== strpos($this->header('accept', ''), 'json'); + } + + /** + * IsIntranetIp + * @param string $ip + * @return bool + */ + public static function isIntranetIp(string $ip): bool + { + // Not validate ip . + if (!filter_var($ip, FILTER_VALIDATE_IP)) { + return false; + } + // Is intranet ip ? For IPv4, the result of false may not be accurate, so we need to check it manually later . + if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { + return true; + } + // Manual check only for IPv4 . + if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + return false; + } + // Manual check . + $reservedIps = [ + 1681915904 => 1686110207, // 100.64.0.0 - 100.127.255.255 + 3221225472 => 3221225727, // 192.0.0.0 - 192.0.0.255 + 3221225984 => 3221226239, // 192.0.2.0 - 192.0.2.255 + 3227017984 => 3227018239, // 192.88.99.0 - 192.88.99.255 + 3323068416 => 3323199487, // 198.18.0.0 - 198.19.255.255 + 3325256704 => 3325256959, // 198.51.100.0 - 198.51.100.255 + 3405803776 => 3405804031, // 203.0.113.0 - 203.0.113.255 + 3758096384 => 4026531839, // 224.0.0.0 - 239.255.255.255 + ]; + $ipLong = ip2long($ip); + foreach ($reservedIps as $ipStart => $ipEnd) { + if (($ipLong >= $ipStart) && ($ipLong <= $ipEnd)) { + return true; + } + } + return false; + } + +} diff --git a/vendor/workerman/webman-framework/src/Http/Response.php b/vendor/workerman/webman-framework/src/Http/Response.php new file mode 100644 index 0000000..8d1d1a3 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Http/Response.php @@ -0,0 +1,87 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman\Http; + +use Throwable; +use Webman\App; +use function filemtime; +use function gmdate; + +/** + * Class Response + * @package Webman\Http + */ +class Response extends \Workerman\Protocols\Http\Response +{ + /** + * @var Throwable + */ + protected $exception = null; + + /** + * File + * @param string $file + * @return $this + */ + public function file(string $file): Response + { + if ($this->notModifiedSince($file)) { + return $this->withStatus(304); + } + return $this->withFile($file); + } + + /** + * Download + * @param string $file + * @param string $downloadName + * @return $this + */ + public function download(string $file, string $downloadName = ''): Response + { + $this->withFile($file); + if ($downloadName) { + $this->header('Content-Disposition', "attachment; filename=\"$downloadName\""); + } + return $this; + } + + /** + * NotModifiedSince + * @param string $file + * @return bool + */ + protected function notModifiedSince(string $file): bool + { + $ifModifiedSince = App::request()->header('if-modified-since'); + if ($ifModifiedSince === null || !is_file($file) || !($mtime = filemtime($file))) { + return false; + } + return $ifModifiedSince === gmdate('D, d M Y H:i:s', $mtime) . ' GMT'; + } + + /** + * Exception + * @param Throwable|null $exception + * @return Throwable|null + */ + public function exception(Throwable $exception = null): ?Throwable + { + if ($exception) { + $this->exception = $exception; + } + return $this->exception; + } +} diff --git a/vendor/workerman/webman-framework/src/Http/UploadFile.php b/vendor/workerman/webman-framework/src/Http/UploadFile.php new file mode 100644 index 0000000..8a63421 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Http/UploadFile.php @@ -0,0 +1,111 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman\Http; + +use Webman\File; +use function pathinfo; + +/** + * Class UploadFile + * @package Webman\Http + */ +class UploadFile extends File +{ + /** + * @var string + */ + protected $uploadName = null; + + /** + * @var string + */ + protected $uploadMimeType = null; + + /** + * @var int + */ + protected $uploadErrorCode = null; + + /** + * UploadFile constructor. + * + * @param string $fileName + * @param string $uploadName + * @param string $uploadMimeType + * @param int $uploadErrorCode + */ + public function __construct(string $fileName, string $uploadName, string $uploadMimeType, int $uploadErrorCode) + { + $this->uploadName = $uploadName; + $this->uploadMimeType = $uploadMimeType; + $this->uploadErrorCode = $uploadErrorCode; + parent::__construct($fileName); + } + + /** + * GetUploadName + * @return string + */ + public function getUploadName(): ?string + { + return $this->uploadName; + } + + /** + * GetUploadMimeType + * @return string + */ + public function getUploadMimeType(): ?string + { + return $this->uploadMimeType; + } + + /** + * GetUploadExtension + * @return string + */ + public function getUploadExtension(): string + { + return pathinfo($this->uploadName, PATHINFO_EXTENSION); + } + + /** + * GetUploadErrorCode + * @return int + */ + public function getUploadErrorCode(): ?int + { + return $this->uploadErrorCode; + } + + /** + * IsValid + * @return bool + */ + public function isValid(): bool + { + return $this->uploadErrorCode === UPLOAD_ERR_OK; + } + + /** + * GetUploadMineType + * @return string + * @deprecated + */ + public function getUploadMineType(): ?string + { + return $this->uploadMimeType; + } +} diff --git a/vendor/workerman/webman-framework/src/Install.php b/vendor/workerman/webman-framework/src/Install.php new file mode 100644 index 0000000..cc02c4c --- /dev/null +++ b/vendor/workerman/webman-framework/src/Install.php @@ -0,0 +1,59 @@ + 'start.php', + 'windows.php' => 'windows.php', + 'support/bootstrap.php' => 'support/bootstrap.php', + 'support/helpers.php' => 'support/helpers.php', + ]; + + /** + * Install + * @return void + */ + public static function install() + { + static::installByRelation(); + } + + /** + * Uninstall + * @return void + */ + public static function uninstall() + { + + } + + /** + * InstallByRelation + * @return void + */ + public static function installByRelation() + { + foreach (static::$pathRelation as $source => $dest) { + if ($pos = strrpos($dest, '/')) { + $parentDir = base_path() . '/' . substr($dest, 0, $pos); + if (!is_dir($parentDir)) { + mkdir($parentDir, 0777, true); + } + } + $sourceFile = __DIR__ . "/$source"; + copy_dir($sourceFile, base_path() . "/$dest", true); + echo "Create $dest\r\n"; + if (is_file($sourceFile)) { + @unlink($sourceFile); + } + } + } + +} diff --git a/vendor/workerman/webman-framework/src/Middleware.php b/vendor/workerman/webman-framework/src/Middleware.php new file mode 100644 index 0000000..57bf133 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Middleware.php @@ -0,0 +1,90 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman; + + +use RuntimeException; +use function array_merge; +use function array_reverse; +use function is_array; +use function method_exists; + +class Middleware +{ + + /** + * @var array + */ + protected static $instances = []; + + /** + * @param mixed $allMiddlewares + * @param string $plugin + * @return void + */ + public static function load($allMiddlewares, string $plugin = '') + { + if (!is_array($allMiddlewares)) { + return; + } + foreach ($allMiddlewares as $appName => $middlewares) { + if (!is_array($middlewares)) { + throw new RuntimeException('Bad middleware config'); + } + if ($appName === '@') { + $plugin = ''; + } + if (strpos($appName, 'plugin.') !== false) { + $explode = explode('.', $appName, 4); + $plugin = $explode[1]; + $appName = $explode[2] ?? ''; + } + foreach ($middlewares as $className) { + if (method_exists($className, 'process')) { + static::$instances[$plugin][$appName][] = [$className, 'process']; + } else { + // @todo Log + echo "middleware $className::process not exsits\n"; + } + } + } + } + + /** + * @param string $plugin + * @param string $appName + * @param bool $withGlobalMiddleware + * @return array|mixed + */ + public static function getMiddleware(string $plugin, string $appName, bool $withGlobalMiddleware = true) + { + $globalMiddleware = static::$instances['']['@'] ?? []; + $appGlobalMiddleware = $withGlobalMiddleware && isset(static::$instances[$plugin]['']) ? static::$instances[$plugin][''] : []; + if ($appName === '') { + return array_reverse(array_merge($globalMiddleware, $appGlobalMiddleware)); + } + $appMiddleware = static::$instances[$plugin][$appName] ?? []; + return array_reverse(array_merge($globalMiddleware, $appGlobalMiddleware, $appMiddleware)); + } + + /** + * @return void + * @deprecated + */ + public static function container($_) + { + + } +} diff --git a/vendor/workerman/webman-framework/src/MiddlewareInterface.php b/vendor/workerman/webman-framework/src/MiddlewareInterface.php new file mode 100644 index 0000000..47c6d51 --- /dev/null +++ b/vendor/workerman/webman-framework/src/MiddlewareInterface.php @@ -0,0 +1,30 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman; + +use Webman\Http\Request; +use Webman\Http\Response; + +interface MiddlewareInterface +{ + /** + * Process an incoming server request. + * + * Processes an incoming server request in order to produce a response. + * If unable to produce the response itself, it may delegate to the provided + * request handler to do so. + */ + public function process(Request $request, callable $handler): Response; +} diff --git a/vendor/workerman/webman-framework/src/Route.php b/vendor/workerman/webman-framework/src/Route.php new file mode 100644 index 0000000..8f94dc9 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Route.php @@ -0,0 +1,472 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman; + +use FastRoute\Dispatcher\GroupCountBased; +use FastRoute\RouteCollector; +use FilesystemIterator; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use Webman\Route\Route as RouteObject; +use function array_diff; +use function array_values; +use function class_exists; +use function explode; +use function FastRoute\simpleDispatcher; +use function in_array; +use function is_array; +use function is_callable; +use function is_file; +use function is_scalar; +use function is_string; +use function json_encode; +use function method_exists; +use function strpos; + +/** + * Class Route + * @package Webman + */ +class Route +{ + /** + * @var Route + */ + protected static $instance = null; + + /** + * @var GroupCountBased + */ + protected static $dispatcher = null; + + /** + * @var RouteCollector + */ + protected static $collector = null; + + /** + * @var null|callable + */ + protected static $fallback = []; + + /** + * @var array + */ + protected static $nameList = []; + + /** + * @var string + */ + protected static $groupPrefix = ''; + + /** + * @var bool + */ + protected static $disableDefaultRoute = []; + + /** + * @var RouteObject[] + */ + protected static $allRoutes = []; + + /** + * @var RouteObject[] + */ + protected $routes = []; + + /** + * @var Route[] + */ + protected $children = []; + + /** + * @param string $path + * @param callable|mixed $callback + * @return RouteObject + */ + public static function get(string $path, $callback): RouteObject + { + return static::addRoute('GET', $path, $callback); + } + + /** + * @param string $path + * @param callable|mixed $callback + * @return RouteObject + */ + public static function post(string $path, $callback): RouteObject + { + return static::addRoute('POST', $path, $callback); + } + + /** + * @param string $path + * @param callable|mixed $callback + * @return RouteObject + */ + public static function put(string $path, $callback): RouteObject + { + return static::addRoute('PUT', $path, $callback); + } + + /** + * @param string $path + * @param callable|mixed $callback + * @return RouteObject + */ + public static function patch(string $path, $callback): RouteObject + { + return static::addRoute('PATCH', $path, $callback); + } + + /** + * @param string $path + * @param callable|mixed $callback + * @return RouteObject + */ + public static function delete(string $path, $callback): RouteObject + { + return static::addRoute('DELETE', $path, $callback); + } + + /** + * @param string $path + * @param callable|mixed $callback + * @return RouteObject + */ + public static function head(string $path, $callback): RouteObject + { + return static::addRoute('HEAD', $path, $callback); + } + + /** + * @param string $path + * @param callable|mixed $callback + * @return RouteObject + */ + public static function options(string $path, $callback): RouteObject + { + return static::addRoute('OPTIONS', $path, $callback); + } + + /** + * @param string $path + * @param callable|mixed $callback + * @return RouteObject + */ + public static function any(string $path, $callback): RouteObject + { + return static::addRoute(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'], $path, $callback); + } + + /** + * @param $method + * @param string $path + * @param callable|mixed $callback + * @return RouteObject + */ + public static function add($method, string $path, $callback): RouteObject + { + return static::addRoute($method, $path, $callback); + } + + /** + * @param string|callable $path + * @param callable|null $callback + * @return static + */ + public static function group($path, callable $callback = null): Route + { + if ($callback === null) { + $callback = $path; + $path = ''; + } + $previousGroupPrefix = static::$groupPrefix; + static::$groupPrefix = $previousGroupPrefix . $path; + $previousInstance = static::$instance; + $instance = static::$instance = new static; + static::$collector->addGroup($path, $callback); + static::$groupPrefix = $previousGroupPrefix; + static::$instance = $previousInstance; + if ($previousInstance) { + $previousInstance->addChild($instance); + } + return $instance; + } + + /** + * @param string $name + * @param string $controller + * @param array $options + * @return void + */ + public static function resource(string $name, string $controller, array $options = []) + { + $name = trim($name, '/'); + if (is_array($options) && !empty($options)) { + $diffOptions = array_diff($options, ['index', 'create', 'store', 'update', 'show', 'edit', 'destroy', 'recovery']); + if (!empty($diffOptions)) { + foreach ($diffOptions as $action) { + static::any("/$name/{$action}[/{id}]", [$controller, $action])->name("$name.{$action}"); + } + } + // 注册路由 由于顺序不同会导致路由无效 因此不适用循环注册 + if (in_array('index', $options)) static::get("/$name", [$controller, 'index'])->name("$name.index"); + if (in_array('create', $options)) static::get("/$name/create", [$controller, 'create'])->name("$name.create"); + if (in_array('store', $options)) static::post("/$name", [$controller, 'store'])->name("$name.store"); + if (in_array('update', $options)) static::put("/$name/{id}", [$controller, 'update'])->name("$name.update"); + if (in_array('patch', $options)) static::patch("/$name/{id}", [$controller, 'patch'])->name("$name.patch"); + if (in_array('show', $options)) static::get("/$name/{id}", [$controller, 'show'])->name("$name.show"); + if (in_array('edit', $options)) static::get("/$name/{id}/edit", [$controller, 'edit'])->name("$name.edit"); + if (in_array('destroy', $options)) static::delete("/$name/{id}", [$controller, 'destroy'])->name("$name.destroy"); + if (in_array('recovery', $options)) static::put("/$name/{id}/recovery", [$controller, 'recovery'])->name("$name.recovery"); + } else { + //为空时自动注册所有常用路由 + if (method_exists($controller, 'index')) static::get("/$name", [$controller, 'index'])->name("$name.index"); + if (method_exists($controller, 'create')) static::get("/$name/create", [$controller, 'create'])->name("$name.create"); + if (method_exists($controller, 'store')) static::post("/$name", [$controller, 'store'])->name("$name.store"); + if (method_exists($controller, 'update')) static::put("/$name/{id}", [$controller, 'update'])->name("$name.update"); + if (method_exists($controller, 'patch')) static::patch("/$name/{id}", [$controller, 'patch'])->name("$name.patch"); + if (method_exists($controller, 'show')) static::get("/$name/{id}", [$controller, 'show'])->name("$name.show"); + if (method_exists($controller, 'edit')) static::get("/$name/{id}/edit", [$controller, 'edit'])->name("$name.edit"); + if (method_exists($controller, 'destroy')) static::delete("/$name/{id}", [$controller, 'destroy'])->name("$name.destroy"); + if (method_exists($controller, 'recovery')) static::put("/$name/{id}/recovery", [$controller, 'recovery'])->name("$name.recovery"); + } + } + + /** + * @return RouteObject[] + */ + public static function getRoutes(): array + { + return static::$allRoutes; + } + + /** + * disableDefaultRoute. + * + * @return void + */ + public static function disableDefaultRoute($plugin = '') + { + static::$disableDefaultRoute[$plugin] = true; + } + + /** + * @param string $plugin + * @return bool + */ + public static function hasDisableDefaultRoute(string $plugin = ''): bool + { + return static::$disableDefaultRoute[$plugin] ?? false; + } + + /** + * @param $middleware + * @return $this + */ + public function middleware($middleware): Route + { + foreach ($this->routes as $route) { + $route->middleware($middleware); + } + foreach ($this->getChildren() as $child) { + $child->middleware($middleware); + } + return $this; + } + + /** + * @param RouteObject $route + */ + public function collect(RouteObject $route) + { + $this->routes[] = $route; + } + + /** + * @param string $name + * @param RouteObject $instance + */ + public static function setByName(string $name, RouteObject $instance) + { + static::$nameList[$name] = $instance; + } + + /** + * @param string $name + * @return null|RouteObject + */ + public static function getByName(string $name): ?RouteObject + { + return static::$nameList[$name] ?? null; + } + + /** + * @param Route $route + * @return void + */ + public function addChild(Route $route) + { + $this->children[] = $route; + } + + /** + * @return Route[] + */ + public function getChildren() + { + return $this->children; + } + + /** + * @param string $method + * @param string $path + * @return array + */ + public static function dispatch(string $method, string $path): array + { + return static::$dispatcher->dispatch($method, $path); + } + + /** + * @param string $path + * @param callable|mixed $callback + * @return callable|false|string[] + */ + public static function convertToCallable(string $path, $callback) + { + if (is_string($callback) && strpos($callback, '@')) { + $callback = explode('@', $callback, 2); + } + + if (!is_array($callback)) { + if (!is_callable($callback)) { + $callStr = is_scalar($callback) ? $callback : 'Closure'; + echo "Route $path $callStr is not callable\n"; + return false; + } + } else { + $callback = array_values($callback); + if (!isset($callback[1]) || !class_exists($callback[0]) || !method_exists($callback[0], $callback[1])) { + echo "Route $path " . json_encode($callback) . " is not callable\n"; + return false; + } + } + + return $callback; + } + + /** + * @param array|string $methods + * @param string $path + * @param callable|mixed $callback + * @return RouteObject + */ + protected static function addRoute($methods, string $path, $callback): RouteObject + { + $route = new RouteObject($methods, static::$groupPrefix . $path, $callback); + static::$allRoutes[] = $route; + + if ($callback = static::convertToCallable($path, $callback)) { + static::$collector->addRoute($methods, $path, ['callback' => $callback, 'route' => $route]); + } + if (static::$instance) { + static::$instance->collect($route); + } + return $route; + } + + /** + * Load. + * @param mixed $paths + * @return void + */ + public static function load($paths) + { + if (!is_array($paths)) { + return; + } + static::$dispatcher = simpleDispatcher(function (RouteCollector $route) use ($paths) { + Route::setCollector($route); + foreach ($paths as $configPath) { + $routeConfigFile = $configPath . '/route.php'; + if (is_file($routeConfigFile)) { + require_once $routeConfigFile; + } + if (!is_dir($pluginConfigPath = $configPath . '/plugin')) { + continue; + } + $dirIterator = new RecursiveDirectoryIterator($pluginConfigPath, FilesystemIterator::FOLLOW_SYMLINKS); + $iterator = new RecursiveIteratorIterator($dirIterator); + foreach ($iterator as $file) { + if ($file->getBaseName('.php') !== 'route') { + continue; + } + $appConfigFile = pathinfo($file, PATHINFO_DIRNAME) . '/app.php'; + if (!is_file($appConfigFile)) { + continue; + } + $appConfig = include $appConfigFile; + if (empty($appConfig['enable'])) { + continue; + } + require_once $file; + } + } + }); + } + + /** + * SetCollector. + * @param RouteCollector $route + * @return void + */ + public static function setCollector(RouteCollector $route) + { + static::$collector = $route; + } + + /** + * Fallback. + * @param callable|mixed $callback + * @param string $plugin + * @return void + */ + public static function fallback(callable $callback, string $plugin = '') + { + static::$fallback[$plugin] = $callback; + } + + /** + * GetFallBack. + * @param string $plugin + * @return callable|null + */ + public static function getFallback(string $plugin = ''): ?callable + { + return static::$fallback[$plugin] ?? null; + } + + /** + * @return void + * @deprecated + */ + public static function container() + { + + } + +} diff --git a/vendor/workerman/webman-framework/src/Route/Route.php b/vendor/workerman/webman-framework/src/Route/Route.php new file mode 100644 index 0000000..a65e7b8 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Route/Route.php @@ -0,0 +1,199 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman\Route; + +use Webman\Route as Router; +use function array_merge; +use function count; +use function preg_replace_callback; +use function str_replace; + +/** + * Class Route + * @package Webman + */ +class Route +{ + /** + * @var string|null + */ + protected $name = null; + + /** + * @var array + */ + protected $methods = []; + + /** + * @var string + */ + protected $path = ''; + + /** + * @var callable + */ + protected $callback = null; + + /** + * @var array + */ + protected $middlewares = []; + + /** + * @var array + */ + protected $params = []; + + /** + * Route constructor. + * @param array $methods + * @param string $path + * @param callable $callback + */ + public function __construct($methods, string $path, $callback) + { + $this->methods = (array)$methods; + $this->path = $path; + $this->callback = $callback; + } + + /** + * Get name. + * @return string|null + */ + public function getName(): ?string + { + return $this->name ?? null; + } + + /** + * Name. + * @param string $name + * @return $this + */ + public function name(string $name): Route + { + $this->name = $name; + Router::setByName($name, $this); + return $this; + } + + /** + * Middleware. + * @param mixed $middleware + * @return $this|array + */ + public function middleware($middleware = null) + { + if ($middleware === null) { + return $this->middlewares; + } + $this->middlewares = array_merge($this->middlewares, is_array($middleware) ? array_reverse($middleware) : [$middleware]); + return $this; + } + + /** + * GetPath. + * @return string + */ + public function getPath(): string + { + return $this->path; + } + + /** + * GetMethods. + * @return array + */ + public function getMethods(): array + { + return $this->methods; + } + + /** + * GetCallback. + * @return callable|null + */ + public function getCallback() + { + return $this->callback; + } + + /** + * GetMiddleware. + * @return array + */ + public function getMiddleware(): array + { + return $this->middlewares; + } + + /** + * Param. + * @param string|null $name + * @param $default + * @return array|mixed|null + */ + public function param(string $name = null, $default = null) + { + if ($name === null) { + return $this->params; + } + return $this->params[$name] ?? $default; + } + + /** + * SetParams. + * @param array $params + * @return $this + */ + public function setParams(array $params): Route + { + $this->params = array_merge($this->params, $params); + return $this; + } + + /** + * Url. + * @param array $parameters + * @return string + */ + public function url(array $parameters = []): string + { + if (empty($parameters)) { + return $this->path; + } + $path = str_replace(['[', ']'], '', $this->path); + $path = preg_replace_callback('/\{(.*?)(?:\:[^\}]*?)*?\}/', function ($matches) use (&$parameters) { + if (!$parameters) { + return $matches[0]; + } + if (isset($parameters[$matches[1]])) { + $value = $parameters[$matches[1]]; + unset($parameters[$matches[1]]); + return $value; + } + $key = key($parameters); + if (is_int($key)) { + $value = $parameters[$key]; + unset($parameters[$key]); + return $value; + } + return $matches[0]; + }, $path); + return count($parameters) > 0 ? $path . '?' . http_build_query($parameters) : $path; + } + +} diff --git a/vendor/workerman/webman-framework/src/Session/FileSessionHandler.php b/vendor/workerman/webman-framework/src/Session/FileSessionHandler.php new file mode 100644 index 0000000..10fda70 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Session/FileSessionHandler.php @@ -0,0 +1,26 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman\Session; + +use Workerman\Protocols\Http\Session\FileSessionHandler as FileHandler; + +/** + * Class FileSessionHandler + * @package Webman + */ +class FileSessionHandler extends FileHandler +{ + +} diff --git a/vendor/workerman/webman-framework/src/Session/RedisClusterSessionHandler.php b/vendor/workerman/webman-framework/src/Session/RedisClusterSessionHandler.php new file mode 100644 index 0000000..c00efae --- /dev/null +++ b/vendor/workerman/webman-framework/src/Session/RedisClusterSessionHandler.php @@ -0,0 +1,22 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman\Session; + +use Workerman\Protocols\Http\Session\RedisClusterSessionHandler as RedisClusterHandler; + +class RedisClusterSessionHandler extends RedisClusterHandler +{ + +} diff --git a/vendor/workerman/webman-framework/src/Session/RedisSessionHandler.php b/vendor/workerman/webman-framework/src/Session/RedisSessionHandler.php new file mode 100644 index 0000000..c124cbd --- /dev/null +++ b/vendor/workerman/webman-framework/src/Session/RedisSessionHandler.php @@ -0,0 +1,26 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman\Session; + +use Workerman\Protocols\Http\Session\RedisSessionHandler as RedisHandler; + +/** + * Class FileSessionHandler + * @package Webman + */ +class RedisSessionHandler extends RedisHandler +{ + +} diff --git a/vendor/workerman/webman-framework/src/Util.php b/vendor/workerman/webman-framework/src/Util.php new file mode 100644 index 0000000..0a19632 --- /dev/null +++ b/vendor/workerman/webman-framework/src/Util.php @@ -0,0 +1,44 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman; + +use function array_diff; +use function array_map; +use function scandir; + +/** + * Class Util + * @package Webman + */ +class Util +{ + /** + * ScanDir. + * @param string $basePath + * @param bool $withBasePath + * @return array + */ + public static function scanDir(string $basePath, bool $withBasePath = true): array + { + if (!is_dir($basePath)) { + return []; + } + $paths = array_diff(scandir($basePath), array('.', '..')) ?: []; + return $withBasePath ? array_map(static function ($path) use ($basePath) { + return $basePath . DIRECTORY_SEPARATOR . $path; + }, $paths) : $paths; + } + +} diff --git a/vendor/workerman/webman-framework/src/View.php b/vendor/workerman/webman-framework/src/View.php new file mode 100644 index 0000000..b50cee0 --- /dev/null +++ b/vendor/workerman/webman-framework/src/View.php @@ -0,0 +1,27 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman; + +interface View +{ + /** + * Render. + * @param string $template + * @param array $vars + * @param string|null $app + * @return string + */ + public static function render(string $template, array $vars, string $app = null): string; +} diff --git a/vendor/workerman/webman-framework/src/support/App.php b/vendor/workerman/webman-framework/src/support/App.php new file mode 100644 index 0000000..53619dd --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/App.php @@ -0,0 +1,151 @@ +load(); + } else { + Dotenv::createMutable(run_path())->load(); + } + } + + static::loadAllConfig(['route', 'container']); + + $errorReporting = config('app.error_reporting'); + if (isset($errorReporting)) { + error_reporting($errorReporting); + } + if ($timezone = config('app.default_timezone')) { + date_default_timezone_set($timezone); + } + + $runtimeLogsPath = runtime_path() . DIRECTORY_SEPARATOR . 'logs'; + if (!file_exists($runtimeLogsPath) || !is_dir($runtimeLogsPath)) { + if (!mkdir($runtimeLogsPath, 0777, true)) { + throw new RuntimeException("Failed to create runtime logs directory. Please check the permission."); + } + } + + $runtimeViewsPath = runtime_path() . DIRECTORY_SEPARATOR . 'views'; + if (!file_exists($runtimeViewsPath) || !is_dir($runtimeViewsPath)) { + if (!mkdir($runtimeViewsPath, 0777, true)) { + throw new RuntimeException("Failed to create runtime views directory. Please check the permission."); + } + } + + Worker::$onMasterReload = function () { + if (function_exists('opcache_get_status')) { + if ($status = opcache_get_status()) { + if (isset($status['scripts']) && $scripts = $status['scripts']) { + foreach (array_keys($scripts) as $file) { + opcache_invalidate($file, true); + } + } + } + } + }; + + $config = config('server'); + Worker::$pidFile = $config['pid_file']; + Worker::$stdoutFile = $config['stdout_file']; + Worker::$logFile = $config['log_file']; + Worker::$eventLoopClass = $config['event_loop'] ?? ''; + TcpConnection::$defaultMaxPackageSize = $config['max_package_size'] ?? 10 * 1024 * 1024; + if (property_exists(Worker::class, 'statusFile')) { + Worker::$statusFile = $config['status_file'] ?? ''; + } + if (property_exists(Worker::class, 'stopTimeout')) { + Worker::$stopTimeout = $config['stop_timeout'] ?? 2; + } + + if ($config['listen']) { + $worker = new Worker($config['listen'], $config['context']); + $propertyMap = [ + 'name', + 'count', + 'user', + 'group', + 'reusePort', + 'transport', + 'protocol' + ]; + foreach ($propertyMap as $property) { + if (isset($config[$property])) { + $worker->$property = $config[$property]; + } + } + + $worker->onWorkerStart = function ($worker) { + require_once base_path() . '/support/bootstrap.php'; + $app = new \Webman\App(config('app.request_class', Request::class), Log::channel('default'), app_path(), public_path()); + $worker->onMessage = [$app, 'onMessage']; + call_user_func([$app, 'onWorkerStart'], $worker); + }; + } + + // Windows does not support custom processes. + if (DIRECTORY_SEPARATOR === '/') { + foreach (config('process', []) as $processName => $config) { + worker_start($processName, $config); + } + foreach (config('plugin', []) as $firm => $projects) { + foreach ($projects as $name => $project) { + if (!is_array($project)) { + continue; + } + foreach ($project['process'] ?? [] as $processName => $config) { + worker_start("plugin.$firm.$name.$processName", $config); + } + } + foreach ($projects['process'] ?? [] as $processName => $config) { + worker_start("plugin.$firm.$processName", $config); + } + } + } + + Worker::runAll(); + } + + /** + * LoadAllConfig. + * @param array $excludes + * @return void + */ + public static function loadAllConfig(array $excludes = []) + { + Config::load(config_path(), $excludes); + $directory = base_path() . '/plugin'; + foreach (Util::scanDir($directory, false) as $name) { + $dir = "$directory/$name/config"; + if (is_dir($dir)) { + Config::load($dir, $excludes, "plugin.$name"); + } + } + } + +} diff --git a/vendor/workerman/webman-framework/src/support/Cache.php b/vendor/workerman/webman-framework/src/support/Cache.php new file mode 100644 index 0000000..8cf5a44 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/Cache.php @@ -0,0 +1,50 @@ +client()); + self::$instance = new Psr16Cache($adapter); + } + return static::$instance; + } + + /** + * @param $name + * @param $arguments + * @return mixed + */ + public static function __callStatic($name, $arguments) + { + return static::instance()->{$name}(... $arguments); + } +} diff --git a/vendor/workerman/webman-framework/src/support/Container.php b/vendor/workerman/webman-framework/src/support/Container.php new file mode 100644 index 0000000..c3734c7 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/Container.php @@ -0,0 +1,48 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +use Webman\Config; + +/** + * Class Container + * @package support + * @method static mixed get($name) + * @method static mixed make($name, array $parameters) + * @method static bool has($name) + */ +class Container +{ + /** + * Instance + * @param string $plugin + * @return array|mixed|void|null + */ + public static function instance(string $plugin = '') + { + return Config::get($plugin ? "plugin.$plugin.container" : 'container'); + } + + /** + * @param string $name + * @param array $arguments + * @return mixed + */ + public static function __callStatic(string $name, array $arguments) + { + $plugin = \Webman\App::getPluginByClass($name); + return static::instance($plugin)->{$name}(... $arguments); + } +} diff --git a/vendor/workerman/webman-framework/src/support/Context.php b/vendor/workerman/webman-framework/src/support/Context.php new file mode 100644 index 0000000..7a8348b --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/Context.php @@ -0,0 +1,25 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +/** + * Class Context + * @package Webman + */ +class Context extends \Webman\Context +{ + +} diff --git a/vendor/workerman/webman-framework/src/support/Db.php b/vendor/workerman/webman-framework/src/support/Db.php new file mode 100644 index 0000000..2bce62f --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/Db.php @@ -0,0 +1,36 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +use Closure; +use Illuminate\Database\Capsule\Manager; + +/** + * Class Db + * @package support + * @method static array select(string $query, $bindings = [], $useReadPdo = true) + * @method static int insert(string $query, $bindings = []) + * @method static int update(string $query, $bindings = []) + * @method static int delete(string $query, $bindings = []) + * @method static bool statement(string $query, $bindings = []) + * @method static mixed transaction(Closure $callback, $attempts = 1) + * @method static void beginTransaction() + * @method static void rollBack($toLevel = null) + * @method static void commit() + */ +class Db extends Manager +{ + +} \ No newline at end of file diff --git a/vendor/workerman/webman-framework/src/support/Log.php b/vendor/workerman/webman-framework/src/support/Log.php new file mode 100644 index 0000000..11512de --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/Log.php @@ -0,0 +1,139 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Handler\FormattableHandlerInterface; +use Monolog\Handler\HandlerInterface; +use Monolog\Logger; +use function array_values; +use function config; +use function is_array; + +/** + * Class Log + * @package support + * + * @method static void log($level, $message, array $context = []) + * @method static void debug($message, array $context = []) + * @method static void info($message, array $context = []) + * @method static void notice($message, array $context = []) + * @method static void warning($message, array $context = []) + * @method static void error($message, array $context = []) + * @method static void critical($message, array $context = []) + * @method static void alert($message, array $context = []) + * @method static void emergency($message, array $context = []) + */ +class Log +{ + /** + * @var array + */ + protected static $instance = []; + + /** + * Channel. + * @param string $name + * @return Logger + */ + public static function channel(string $name = 'default'): Logger + { + if (!isset(static::$instance[$name])) { + $config = config('log', [])[$name]; + $handlers = self::handlers($config); + $processors = self::processors($config); + static::$instance[$name] = new Logger($name, $handlers, $processors); + } + return static::$instance[$name]; + } + + /** + * Handlers. + * @param array $config + * @return array + */ + protected static function handlers(array $config): array + { + $handlerConfigs = $config['handlers'] ?? [[]]; + $handlers = []; + foreach ($handlerConfigs as $value) { + $class = $value['class'] ?? []; + $constructor = $value['constructor'] ?? []; + + $formatterConfig = $value['formatter'] ?? []; + + $class && $handlers[] = self::handler($class, $constructor, $formatterConfig); + } + + return $handlers; + } + + /** + * Handler. + * @param string $class + * @param array $constructor + * @param array $formatterConfig + * @return HandlerInterface + */ + protected static function handler(string $class, array $constructor, array $formatterConfig): HandlerInterface + { + /** @var HandlerInterface $handler */ + $handler = new $class(... array_values($constructor)); + + if ($handler instanceof FormattableHandlerInterface && $formatterConfig) { + $formatterClass = $formatterConfig['class']; + $formatterConstructor = $formatterConfig['constructor']; + + /** @var FormatterInterface $formatter */ + $formatter = new $formatterClass(... array_values($formatterConstructor)); + + $handler->setFormatter($formatter); + } + + return $handler; + } + + /** + * Processors. + * @param array $config + * @return array + */ + protected static function processors(array $config): array + { + $result = []; + if (!isset($config['processors']) && isset($config['processor'])) { + $config['processors'] = [$config['processor']]; + } + + foreach ($config['processors'] ?? [] as $value) { + if (is_array($value) && isset($value['class'])) { + $value = new $value['class'](... array_values($value['constructor'] ?? [])); + } + $result[] = $value; + } + + return $result; + } + + /** + * @param string $name + * @param array $arguments + * @return mixed + */ + public static function __callStatic(string $name, array $arguments) + { + return static::channel()->{$name}(... $arguments); + } +} diff --git a/vendor/workerman/webman-framework/src/support/Model.php b/vendor/workerman/webman-framework/src/support/Model.php new file mode 100644 index 0000000..beb77df --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/Model.php @@ -0,0 +1,262 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +use Closure; +use Illuminate\Contracts\Pagination\CursorPaginator; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; +use Illuminate\Contracts\Pagination\Paginator; +use Illuminate\Database\Eloquent\Model as BaseModel; +use Illuminate\Database\Query\Builder; +use Illuminate\Database\Query\Expression; +use Illuminate\Database\Query\Grammars\Grammar; +use Illuminate\Database\Query\Processors\Processor; +use Illuminate\Support\Collection; +use Illuminate\Support\LazyCollection; + +/** + * @method static BaseModel make($attributes = []) + * @method static \Illuminate\Database\Eloquent\Builder|static withGlobalScope($identifier, $scope) + * @method static \Illuminate\Database\Eloquent\Builder|static withoutGlobalScope($scope) + * @method static \Illuminate\Database\Eloquent\Builder|static withoutGlobalScopes($scopes = null) + * @method static array removedScopes() + * @method static \Illuminate\Database\Eloquent\Builder|static whereKey($id) + * @method static \Illuminate\Database\Eloquent\Builder|static whereKeyNot($id) + * @method static \Illuminate\Database\Eloquent\Builder|static where($column, $operator = null, $value = null, $boolean = 'and') + * @method static BaseModel|null firstWhere($column, $operator = null, $value = null, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|static orWhere($column, $operator = null, $value = null) + * @method static \Illuminate\Database\Eloquent\Builder|static latest($column = null) + * @method static \Illuminate\Database\Eloquent\Builder|static oldest($column = null) + * @method static \Illuminate\Database\Eloquent\Collection|static hydrate($items) + * @method static \Illuminate\Database\Eloquent\Collection|static fromQuery($query, $bindings = []) + * @method static BaseModel|\Illuminate\Database\Eloquent\Collection|static[]|static|null find($id, $columns = []) + * @method static \Illuminate\Database\Eloquent\Collection|static findMany($ids, $columns = []) + * @method static BaseModel|\Illuminate\Database\Eloquent\Collection|static|static[] findOrFail($id, $columns = []) + * @method static BaseModel|static findOrNew($id, $columns = []) + * @method static BaseModel|static firstOrNew($attributes = [], $values = []) + * @method static BaseModel|static firstOrCreate($attributes = [], $values = []) + * @method static BaseModel|static updateOrCreate($attributes, $values = []) + * @method static BaseModel|static firstOrFail($columns = []) + * @method static BaseModel|static|mixed firstOr($columns = [], $callback = null) + * @method static BaseModel sole($columns = []) + * @method static mixed value($column) + * @method static \Illuminate\Database\Eloquent\Collection[]|static[] get($columns = []) + * @method static BaseModel[]|static[] getModels($columns = []) + * @method static array eagerLoadRelations($models) + * @method static LazyCollection cursor() + * @method static Collection pluck($column, $key = null) + * @method static LengthAwarePaginator paginate($perPage = null, $columns = [], $pageName = 'page', $page = null) + * @method static Paginator simplePaginate($perPage = null, $columns = [], $pageName = 'page', $page = null) + * @method static CursorPaginator cursorPaginate($perPage = null, $columns = [], $cursorName = 'cursor', $cursor = null) + * @method static BaseModel|$this create($attributes = []) + * @method static BaseModel|$this forceCreate($attributes) + * @method static int upsert($values, $uniqueBy, $update = null) + * @method static void onDelete($callback) + * @method static static|mixed scopes($scopes) + * @method static static applyScopes() + * @method static \Illuminate\Database\Eloquent\Builder|static without($relations) + * @method static \Illuminate\Database\Eloquent\Builder|static withOnly($relations) + * @method static BaseModel newModelInstance($attributes = []) + * @method static \Illuminate\Database\Eloquent\Builder|static withCasts($casts) + * @method static Builder getQuery() + * @method static \Illuminate\Database\Eloquent\Builder|static setQuery($query) + * @method static Builder toBase() + * @method static array getEagerLoads() + * @method static \Illuminate\Database\Eloquent\Builder|static setEagerLoads($eagerLoad) + * @method static BaseModel getModel() + * @method static \Illuminate\Database\Eloquent\Builder|static setModel($model) + * @method static Closure getMacro($name) + * @method static bool hasMacro($name) + * @method static Closure getGlobalMacro($name) + * @method static bool hasGlobalMacro($name) + * @method static static clone () + * @method static \Illuminate\Database\Eloquent\Builder|static has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null) + * @method static \Illuminate\Database\Eloquent\Builder|static orHas($relation, $operator = '>=', $count = 1) + * @method static \Illuminate\Database\Eloquent\Builder|static doesntHave($relation, $boolean = 'and', $callback = null) + * @method static \Illuminate\Database\Eloquent\Builder|static orDoesntHave($relation) + * @method static \Illuminate\Database\Eloquent\Builder|static whereHas($relation, $callback = null, $operator = '>=', $count = 1) + * @method static \Illuminate\Database\Eloquent\Builder|static orWhereHas($relation, $callback = null, $operator = '>=', $count = 1) + * @method static \Illuminate\Database\Eloquent\Builder|static whereDoesntHave($relation, $callback = null) + * @method static \Illuminate\Database\Eloquent\Builder|static orWhereDoesntHave($relation, $callback = null) + * @method static \Illuminate\Database\Eloquent\Builder|static hasMorph($relation, $types, $operator = '>=', $count = 1, $boolean = 'and', $callback = null) + * @method static \Illuminate\Database\Eloquent\Builder|static orHasMorph($relation, $types, $operator = '>=', $count = 1) + * @method static \Illuminate\Database\Eloquent\Builder|static doesntHaveMorph($relation, $types, $boolean = 'and', $callback = null) + * @method static \Illuminate\Database\Eloquent\Builder|static orDoesntHaveMorph($relation, $types) + * @method static \Illuminate\Database\Eloquent\Builder|static whereHasMorph($relation, $types, $callback = null, $operator = '>=', $count = 1) + * @method static \Illuminate\Database\Eloquent\Builder|static orWhereHasMorph($relation, $types, $callback = null, $operator = '>=', $count = 1) + * @method static \Illuminate\Database\Eloquent\Builder|static whereDoesntHaveMorph($relation, $types, $callback = null) + * @method static \Illuminate\Database\Eloquent\Builder|static orWhereDoesntHaveMorph($relation, $types, $callback = null) + * @method static \Illuminate\Database\Eloquent\Builder|static withAggregate($relations, $column, $function = null) + * @method static \Illuminate\Database\Eloquent\Builder|static withCount($relations) + * @method static \Illuminate\Database\Eloquent\Builder|static withMax($relation, $column) + * @method static \Illuminate\Database\Eloquent\Builder|static withMin($relation, $column) + * @method static \Illuminate\Database\Eloquent\Builder|static withSum($relation, $column) + * @method static \Illuminate\Database\Eloquent\Builder|static withAvg($relation, $column) + * @method static \Illuminate\Database\Eloquent\Builder|static withExists($relation) + * @method static \Illuminate\Database\Eloquent\Builder|static mergeConstraintsFrom($from) + * @method static Collection explain() + * @method static bool chunk($count, $callback) + * @method static Collection chunkMap($callback, $count = 1000) + * @method static bool each($callback, $count = 1000) + * @method static bool chunkById($count, $callback, $column = null, $alias = null) + * @method static bool eachById($callback, $count = 1000, $column = null, $alias = null) + * @method static LazyCollection lazy($chunkSize = 1000) + * @method static LazyCollection lazyById($chunkSize = 1000, $column = null, $alias = null) + * @method static BaseModel|object|static|null first($columns = []) + * @method static BaseModel|object|null baseSole($columns = []) + * @method static \Illuminate\Database\Eloquent\Builder|static tap($callback) + * @method static mixed when($value, $callback, $default = null) + * @method static mixed unless($value, $callback, $default = null) + * @method static Builder select($columns = []) + * @method static Builder selectSub($query, $as) + * @method static Builder selectRaw($expression, $bindings = []) + * @method static Builder fromSub($query, $as) + * @method static Builder fromRaw($expression, $bindings = []) + * @method static Builder addSelect($column) + * @method static Builder distinct() + * @method static Builder from($table, $as = null) + * @method static Builder join($table, $first, $operator = null, $second = null, $type = 'inner', $where = false) + * @method static Builder joinWhere($table, $first, $operator, $second, $type = 'inner') + * @method static Builder joinSub($query, $as, $first, $operator = null, $second = null, $type = 'inner', $where = false) + * @method static Builder leftJoin($table, $first, $operator = null, $second = null) + * @method static Builder leftJoinWhere($table, $first, $operator, $second) + * @method static Builder leftJoinSub($query, $as, $first, $operator = null, $second = null) + * @method static Builder rightJoin($table, $first, $operator = null, $second = null) + * @method static Builder rightJoinWhere($table, $first, $operator, $second) + * @method static Builder rightJoinSub($query, $as, $first, $operator = null, $second = null) + * @method static Builder crossJoin($table, $first = null, $operator = null, $second = null) + * @method static Builder crossJoinSub($query, $as) + * @method static void mergeWheres($wheres, $bindings) + * @method static array prepareValueAndOperator($value, $operator, $useDefault = false) + * @method static Builder whereColumn($first, $operator = null, $second = null, $boolean = 'and') + * @method static Builder orWhereColumn($first, $operator = null, $second = null) + * @method static Builder whereRaw($sql, $bindings = [], $boolean = 'and') + * @method static Builder orWhereRaw($sql, $bindings = []) + * @method static Builder whereIn($column, $values, $boolean = 'and', $not = false) + * @method static Builder orWhereIn($column, $values) + * @method static Builder whereNotIn($column, $values, $boolean = 'and') + * @method static Builder orWhereNotIn($column, $values) + * @method static Builder whereIntegerInRaw($column, $values, $boolean = 'and', $not = false) + * @method static Builder orWhereIntegerInRaw($column, $values) + * @method static Builder whereIntegerNotInRaw($column, $values, $boolean = 'and') + * @method static Builder orWhereIntegerNotInRaw($column, $values) + * @method static Builder whereNull($columns, $boolean = 'and', $not = false) + * @method static Builder orWhereNull($column) + * @method static Builder whereNotNull($columns, $boolean = 'and') + * @method static Builder whereBetween($column, $values, $boolean = 'and', $not = false) + * @method static Builder whereBetweenColumns($column, $values, $boolean = 'and', $not = false) + * @method static Builder orWhereBetween($column, $values) + * @method static Builder orWhereBetweenColumns($column, $values) + * @method static Builder whereNotBetween($column, $values, $boolean = 'and') + * @method static Builder whereNotBetweenColumns($column, $values, $boolean = 'and') + * @method static Builder orWhereNotBetween($column, $values) + * @method static Builder orWhereNotBetweenColumns($column, $values) + * @method static Builder orWhereNotNull($column) + * @method static Builder whereDate($column, $operator, $value = null, $boolean = 'and') + * @method static Builder orWhereDate($column, $operator, $value = null) + * @method static Builder whereTime($column, $operator, $value = null, $boolean = 'and') + * @method static Builder orWhereTime($column, $operator, $value = null) + * @method static Builder whereDay($column, $operator, $value = null, $boolean = 'and') + * @method static Builder orWhereDay($column, $operator, $value = null) + * @method static Builder whereMonth($column, $operator, $value = null, $boolean = 'and') + * @method static Builder orWhereMonth($column, $operator, $value = null) + * @method static Builder whereYear($column, $operator, $value = null, $boolean = 'and') + * @method static Builder orWhereYear($column, $operator, $value = null) + * @method static Builder whereNested($callback, $boolean = 'and') + * @method static Builder forNestedWhere() + * @method static Builder addNestedWhereQuery($query, $boolean = 'and') + * @method static Builder whereExists($callback, $boolean = 'and', $not = false) + * @method static Builder orWhereExists($callback, $not = false) + * @method static Builder whereNotExists($callback, $boolean = 'and') + * @method static Builder orWhereNotExists($callback) + * @method static Builder addWhereExistsQuery($query, $boolean = 'and', $not = false) + * @method static Builder whereRowValues($columns, $operator, $values, $boolean = 'and') + * @method static Builder orWhereRowValues($columns, $operator, $values) + * @method static Builder whereJsonContains($column, $value, $boolean = 'and', $not = false) + * @method static Builder orWhereJsonContains($column, $value) + * @method static Builder whereJsonDoesntContain($column, $value, $boolean = 'and') + * @method static Builder orWhereJsonDoesntContain($column, $value) + * @method static Builder whereJsonLength($column, $operator, $value = null, $boolean = 'and') + * @method static Builder orWhereJsonLength($column, $operator, $value = null) + * @method static Builder dynamicWhere($method, $parameters) + * @method static Builder groupBy(...$groups) + * @method static Builder groupByRaw($sql, $bindings = []) + * @method static Builder having($column, $operator = null, $value = null, $boolean = 'and') + * @method static Builder orHaving($column, $operator = null, $value = null) + * @method static Builder havingBetween($column, $values, $boolean = 'and', $not = false) + * @method static Builder havingRaw($sql, $bindings = [], $boolean = 'and') + * @method static Builder orHavingRaw($sql, $bindings = []) + * @method static Builder orderBy($column, $direction = 'asc') + * @method static Builder orderByDesc($column) + * @method static Builder inRandomOrder($seed = '') + * @method static Builder orderByRaw($sql, $bindings = []) + * @method static Builder skip($value) + * @method static Builder offset($value) + * @method static Builder take($value) + * @method static Builder limit($value) + * @method static Builder forPage($page, $perPage = 15) + * @method static Builder forPageBeforeId($perPage = 15, $lastId = 0, $column = 'id') + * @method static Builder forPageAfterId($perPage = 15, $lastId = 0, $column = 'id') + * @method static Builder reorder($column = null, $direction = 'asc') + * @method static Builder union($query, $all = false) + * @method static Builder unionAll($query) + * @method static Builder lock($value = true) + * @method static Builder lockForUpdate() + * @method static Builder sharedLock() + * @method static Builder beforeQuery($callback) + * @method static void applyBeforeQueryCallbacks() + * @method static string toSql() + * @method static int getCountForPagination($columns = []) + * @method static string implode($column, $glue = '') + * @method static bool exists() + * @method static bool doesntExist() + * @method static mixed existsOr($callback) + * @method static mixed doesntExistOr($callback) + * @method static int count($columns = '*') + * @method static mixed min($column) + * @method static mixed max($column) + * @method static mixed sum($column) + * @method static mixed avg($column) + * @method static mixed average($column) + * @method static mixed aggregate($function, $columns = []) + * @method static float|int numericAggregate($function, $columns = []) + * @method static bool insert($values) + * @method static int insertOrIgnore($values) + * @method static int insertGetId($values, $sequence = null) + * @method static int insertUsing($columns, $query) + * @method static bool updateOrInsert($attributes, $values = []) + * @method static void truncate() + * @method static Expression raw($value) + * @method static array getBindings() + * @method static array getRawBindings() + * @method static Builder setBindings($bindings, $type = 'where') + * @method static Builder addBinding($value, $type = 'where') + * @method static Builder mergeBindings($query) + * @method static array cleanBindings($bindings) + * @method static Processor getProcessor() + * @method static Grammar getGrammar() + * @method static Builder useWritePdo() + * @method static static cloneWithout($properties) + * @method static static cloneWithoutBindings($except) + * @method static Builder dump() + * @method static void dd() + * @method static void macro($name, $macro) + * @method static void mixin($mixin, $replace = true) + * @method static mixed macroCall($method, $parameters) + */ +class Model extends BaseModel +{ + +} diff --git a/vendor/workerman/webman-framework/src/support/Plugin.php b/vendor/workerman/webman-framework/src/support/Plugin.php new file mode 100644 index 0000000..7b296be --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/Plugin.php @@ -0,0 +1,108 @@ + $path) { + $pluginConst = "\\{$namespace}Install::WEBMAN_PLUGIN"; + if (!defined($pluginConst)) { + continue; + } + $installFunction = "\\{$namespace}Install::install"; + if (is_callable($installFunction)) { + $installFunction(true); + } + } + } + + /** + * Update. + * @param mixed $event + * @return void + */ + public static function update($event) + { + static::findHelper(); + $psr4 = static::getPsr4($event); + foreach ($psr4 as $namespace => $path) { + $pluginConst = "\\{$namespace}Install::WEBMAN_PLUGIN"; + if (!defined($pluginConst)) { + continue; + } + $updateFunction = "\\{$namespace}Install::update"; + if (is_callable($updateFunction)) { + $updateFunction(); + continue; + } + $installFunction = "\\{$namespace}Install::install"; + if (is_callable($installFunction)) { + $installFunction(false); + } + } + } + + /** + * Uninstall. + * @param mixed $event + * @return void + */ + public static function uninstall($event) + { + static::findHelper(); + $psr4 = static::getPsr4($event); + foreach ($psr4 as $namespace => $path) { + $pluginConst = "\\{$namespace}Install::WEBMAN_PLUGIN"; + if (!defined($pluginConst)) { + continue; + } + $uninstallFunction = "\\{$namespace}Install::uninstall"; + if (is_callable($uninstallFunction)) { + $uninstallFunction(); + } + } + } + + /** + * Get psr-4 info + * + * @param mixed $event + * @return array + */ + protected static function getPsr4($event) + { + $operation = $event->getOperation(); + $autoload = method_exists($operation, 'getPackage') ? $operation->getPackage()->getAutoload() : $operation->getTargetPackage()->getAutoload(); + return $autoload['psr-4'] ?? []; + } + + /** + * FindHelper. + * @return void + */ + protected static function findHelper() + { + // Plugin.php in vendor + $file = __DIR__ . '/../../../../../support/helpers.php'; + if (is_file($file)) { + require_once $file; + return; + } + // Plugin.php in webman + require_once __DIR__ . '/helpers.php'; + } +} diff --git a/vendor/workerman/webman-framework/src/support/Redis.php b/vendor/workerman/webman-framework/src/support/Redis.php new file mode 100644 index 0000000..de566c5 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/Redis.php @@ -0,0 +1,283 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +use Illuminate\Events\Dispatcher; +use Illuminate\Redis\Connections\Connection; +use Illuminate\Redis\RedisManager; +use Workerman\Timer; +use Workerman\Worker; +use function class_exists; +use function config; +use function in_array; + + +/** + * Class Redis + * @package support + * + * Strings methods + * @method static int append($key, $value) + * @method static int bitCount($key) + * @method static int decr($key, $value = 1) + * @method static int decrBy($key, $value) + * @method static string|bool get($key) + * @method static int getBit($key, $offset) + * @method static string getRange($key, $start, $end) + * @method static string getSet($key, $value) + * @method static int incr($key, $value = 1) + * @method static int incrBy($key, $value) + * @method static float incrByFloat($key, $value) + * @method static array mGet(array $keys) + * @method static array getMultiple(array $keys) + * @method static bool mSet($pairs) + * @method static bool mSetNx($pairs) + * @method static bool set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null) + * @method static bool setBit($key, $offset, $value) + * @method static bool setEx($key, $ttl, $value) + * @method static bool pSetEx($key, $ttl, $value) + * @method static bool setNx($key, $value) + * @method static string setRange($key, $offset, $value) + * @method static int strLen($key) + * Keys methods + * @method static int del(...$keys) + * @method static int unlink(...$keys) + * @method static false|string dump($key) + * @method static int exists(...$keys) + * @method static bool expire($key, $ttl) + * @method static bool pexpire($key, $ttl) + * @method static bool expireAt($key, $timestamp) + * @method static bool pexpireAt($key, $timestamp) + * @method static array keys($pattern) + * @method static bool|array scan($it) + * @method static void migrate($host, $port, $keys, $dbIndex, $timeout, $copy = false, $replace = false) + * @method static bool select($dbIndex) + * @method static bool move($key, $dbIndex) + * @method static string|int|bool object($information, $key) + * @method static bool persist($key) + * @method static string randomKey() + * @method static bool rename($srcKey, $dstKey) + * @method static bool renameNx($srcKey, $dstKey) + * @method static string type($key) + * @method static int|array sort($key, $options = []) + * @method static int ttl($key) + * @method static int pttl($key) + * @method static void restore($key, $ttl, $value) + * Hashes methods + * @method static false|int hSet($key, $hashKey, $value) + * @method static bool hSetNx($key, $hashKey, $value) + * @method static false|string hGet($key, $hashKey) + * @method static false|int hLen($key) + * @method static false|int hDel($key, ...$hashKeys) + * @method static array hKeys($key) + * @method static array hVals($key) + * @method static array hGetAll($key) + * @method static bool hExists($key, $hashKey) + * @method static int hIncrBy($key, $hashKey, $value) + * @method static float hIncrByFloat($key, $hashKey, $value) + * @method static bool hMSet($key, $members) + * @method static array hMGet($key, $memberKeys) + * @method static array hScan($key, $iterator, $pattern = '', $count = 0) + * @method static int hStrLen($key, $hashKey) + * Lists methods + * @method static array blPop($keys, $timeout) + * @method static array brPop($keys, $timeout) + * @method static false|string bRPopLPush($srcKey, $dstKey, $timeout) + * @method static false|string lIndex($key, $index) + * @method static int lInsert($key, $position, $pivot, $value) + * @method static false|string lPop($key) + * @method static false|int lPush($key, ...$entries) + * @method static false|int lPushx($key, $value) + * @method static array lRange($key, $start, $end) + * @method static false|int lRem($key, $count, $value) + * @method static bool lSet($key, $index, $value) + * @method static false|array lTrim($key, $start, $end) + * @method static false|string rPop($key) + * @method static false|string rPopLPush($srcKey, $dstKey) + * @method static false|int rPush($key, ...$entries) + * @method static false|int rPushX($key, $value) + * @method static false|int lLen($key) + * Sets methods + * @method static int sAdd($key, $value) + * @method static int sCard($key) + * @method static array sDiff($keys) + * @method static false|int sDiffStore($dst, $keys) + * @method static false|array sInter($keys) + * @method static false|int sInterStore($dst, $keys) + * @method static bool sIsMember($key, $member) + * @method static array sMembers($key) + * @method static bool sMove($src, $dst, $member) + * @method static false|string|array sPop($key, $count = 0) + * @method static false|string|array sRandMember($key, $count = 0) + * @method static int sRem($key, ...$members) + * @method static array sUnion(...$keys) + * @method static false|int sUnionStore($dst, ...$keys) + * @method static false|array sScan($key, $iterator, $pattern = '', $count = 0) + * Sorted sets methods + * @method static array bzPopMin($keys, $timeout) + * @method static array bzPopMax($keys, $timeout) + * @method static int zAdd($key, $score, $value) + * @method static int zCard($key) + * @method static int zCount($key, $start, $end) + * @method static double zIncrBy($key, $value, $member) + * @method static int zinterstore($keyOutput, $arrayZSetKeys, $arrayWeights = [], $aggregateFunction = '') + * @method static array zPopMin($key, $count) + * @method static array zPopMax($key, $count) + * @method static array zRange($key, $start, $end, $withScores = false) + * @method static array zRangeByScore($key, $start, $end, $options = []) + * @method static array zRevRangeByScore($key, $start, $end, $options = []) + * @method static array zRangeByLex($key, $min, $max, $offset = 0, $limit = 0) + * @method static int zRank($key, $member) + * @method static int zRevRank($key, $member) + * @method static int zRem($key, ...$members) + * @method static int zRemRangeByRank($key, $start, $end) + * @method static int zRemRangeByScore($key, $start, $end) + * @method static array zRevRange($key, $start, $end, $withScores = false) + * @method static double zScore($key, $member) + * @method static int zunionstore($keyOutput, $arrayZSetKeys, $arrayWeights = [], $aggregateFunction = '') + * @method static false|array zScan($key, $iterator, $pattern = '', $count = 0) + * HyperLogLogs methods + * @method static int pfAdd($key, $values) + * @method static int pfCount($keys) + * @method static bool pfMerge($dstKey, $srcKeys) + * Geocoding methods + * @method static int geoAdd($key, $longitude, $latitude, $member, ...$items) + * @method static array geoHash($key, ...$members) + * @method static array geoPos($key, ...$members) + * @method static double geoDist($key, $members, $unit = '') + * @method static int|array geoRadius($key, $longitude, $latitude, $radius, $unit, $options = []) + * @method static array geoRadiusByMember($key, $member, $radius, $units, $options = []) + * Streams methods + * @method static int xAck($stream, $group, $arrMessages) + * @method static string xAdd($strKey, $strId, $arrMessage, $iMaxLen = 0, $booApproximate = false) + * @method static array xClaim($strKey, $strGroup, $strConsumer, $minIdleTime, $arrIds, $arrOptions = []) + * @method static int xDel($strKey, $arrIds) + * @method static mixed xGroup($command, $strKey, $strGroup, $strMsgId, $booMKStream = null) + * @method static mixed xInfo($command, $strStream, $strGroup = null) + * @method static int xLen($stream) + * @method static array xPending($strStream, $strGroup, $strStart = 0, $strEnd = 0, $iCount = 0, $strConsumer = null) + * @method static array xRange($strStream, $strStart, $strEnd, $iCount = 0) + * @method static array xRead($arrStreams, $iCount = 0, $iBlock = null) + * @method static array xReadGroup($strGroup, $strConsumer, $arrStreams, $iCount = 0, $iBlock = null) + * @method static array xRevRange($strStream, $strEnd, $strStart, $iCount = 0) + * @method static int xTrim($strStream, $iMaxLen, $booApproximate = null) + * Pub/sub methods + * @method static mixed pSubscribe($patterns, $callback) + * @method static mixed publish($channel, $message) + * @method static mixed subscribe($channels, $callback) + * @method static mixed pubSub($keyword, $argument = null) + * Generic methods + * @method static mixed rawCommand(...$commandAndArgs) + * Transactions methods + * @method static \Redis multi() + * @method static mixed exec() + * @method static mixed discard() + * @method static mixed watch($keys) + * @method static mixed unwatch($keys) + * Scripting methods + * @method static mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) + * @method static mixed evalSha($scriptSha, $numkeys, ...$arguments) + * @method static mixed script($command, ...$scripts) + * @method static mixed client(...$args) + * @method static null|string getLastError() + * @method static bool clearLastError() + * @method static mixed _prefix($value) + * @method static mixed _serialize($value) + * @method static mixed _unserialize($value) + * Introspection methods + * @method static bool isConnected() + * @method static mixed getHost() + * @method static mixed getPort() + * @method static false|int getDbNum() + * @method static false|double getTimeout() + * @method static mixed getReadTimeout() + * @method static mixed getPersistentID() + * @method static mixed getAuth() + */ +class Redis +{ + + /** + * @var RedisManager + */ + protected static $instance = null; + + /** + * need to install phpredis extension + */ + const PHPREDIS_CLIENT = 'phpredis'; + + /** + * need to install the 'predis/predis' packgage. + * cmd: composer install predis/predis + */ + const PREDIS_CLIENT = 'predis'; + + /** + * Support client collection + */ + static $allowClient = [ + self::PHPREDIS_CLIENT, + self::PREDIS_CLIENT + ]; + + /** + * @return RedisManager + */ + public static function instance(): ?RedisManager + { + if (!static::$instance) { + $config = config('redis'); + $client = $config['client'] ?? self::PHPREDIS_CLIENT; + + if (!in_array($client, static::$allowClient)) { + $client = self::PHPREDIS_CLIENT; + } + + static::$instance = new RedisManager('', $client, $config); + } + return static::$instance; + } + + /** + * Connection. + * @param string $name + * @return Connection + */ + public static function connection(string $name = 'default'): Connection + { + static $timers = []; + $connection = static::instance()->connection($name); + if (!isset($timers[$name])) { + $timers[$name] = Worker::getAllWorkers() ? Timer::add(55, function () use ($connection) { + $connection->get('ping'); + }) : 1; + if (class_exists(Dispatcher::class)) { + $connection->setEventDispatcher(new Dispatcher()); + } + } + return $connection; + } + + /** + * @param string $name + * @param array $arguments + * @return mixed + */ + public static function __callStatic(string $name, array $arguments) + { + return static::connection()->{$name}(... $arguments); + } +} diff --git a/vendor/workerman/webman-framework/src/support/Request.php b/vendor/workerman/webman-framework/src/support/Request.php new file mode 100644 index 0000000..e3f6ac3 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/Request.php @@ -0,0 +1,24 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +/** + * Class Request + * @package support + */ +class Request extends \Webman\Http\Request +{ + +} \ No newline at end of file diff --git a/vendor/workerman/webman-framework/src/support/Response.php b/vendor/workerman/webman-framework/src/support/Response.php new file mode 100644 index 0000000..9bc4e1e --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/Response.php @@ -0,0 +1,24 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +/** + * Class Response + * @package support + */ +class Response extends \Webman\Http\Response +{ + +} \ No newline at end of file diff --git a/vendor/workerman/webman-framework/src/support/Translation.php b/vendor/workerman/webman-framework/src/support/Translation.php new file mode 100644 index 0000000..d3928c3 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/Translation.php @@ -0,0 +1,107 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +use FilesystemIterator; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use RegexIterator; +use Symfony\Component\Translation\Translator; +use Webman\Exception\NotFoundException; +use function basename; +use function config; +use function get_realpath; +use function pathinfo; +use function request; +use function substr; + +/** + * Class Translation + * @package support + * @method static string trans(?string $id, array $parameters = [], string $domain = null, string $locale = null) + * @method static void setLocale(string $locale) + * @method static string getLocale() + */ +class Translation +{ + + /** + * @var Translator[] + */ + protected static $instance = []; + + /** + * Instance. + * @param string $plugin + * @return Translator + * @throws NotFoundException + */ + public static function instance(string $plugin = ''): Translator + { + if (!isset(static::$instance[$plugin])) { + $config = config($plugin ? "plugin.$plugin.translation" : 'translation', []); + $paths = (array)($config['path'] ?? []); + + static::$instance[$plugin] = $translator = new Translator($config['locale']); + $translator->setFallbackLocales($config['fallback_locale']); + + $classes = [ + 'Symfony\Component\Translation\Loader\PhpFileLoader' => [ + 'extension' => '.php', + 'format' => 'phpfile' + ], + 'Symfony\Component\Translation\Loader\PoFileLoader' => [ + 'extension' => '.po', + 'format' => 'pofile' + ] + ]; + foreach ($paths as $path) { + // Phar support. Compatible with the 'realpath' function in the phar file. + if (!$translationsPath = get_realpath($path)) { + throw new NotFoundException("File {$path} not found"); + } + + foreach ($classes as $class => $opts) { + $translator->addLoader($opts['format'], new $class); + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($translationsPath, FilesystemIterator::SKIP_DOTS)); + $files = new RegexIterator($iterator, '/^.+' . preg_quote($opts['extension']) . '$/i', RegexIterator::GET_MATCH); + foreach ($files as $file) { + $file = $file[0]; + $domain = basename($file, $opts['extension']); + $dirName = pathinfo($file, PATHINFO_DIRNAME); + $locale = substr(strrchr($dirName, DIRECTORY_SEPARATOR), 1); + if ($domain && $locale) { + $translator->addResource($opts['format'], $file, $locale, $domain); + } + } + } + } + } + return static::$instance[$plugin]; + } + + /** + * @param string $name + * @param array $arguments + * @return mixed + * @throws NotFoundException + */ + public static function __callStatic(string $name, array $arguments) + { + $request = request(); + $plugin = $request->plugin ?? ''; + return static::instance($plugin)->{$name}(... $arguments); + } +} diff --git a/vendor/workerman/webman-framework/src/support/View.php b/vendor/workerman/webman-framework/src/support/View.php new file mode 100644 index 0000000..f98afe0 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/View.php @@ -0,0 +1,35 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +use function config; +use function request; + +class View +{ + /** + * Assign. + * @param mixed $name + * @param mixed $value + * @return void + */ + public static function assign($name, $value = null) + { + $request = request(); + $plugin = $request->plugin ?? ''; + $handler = config($plugin ? "plugin.$plugin.view.handler" : 'view.handler'); + $handler::assign($name, $value); + } +} \ No newline at end of file diff --git a/vendor/workerman/webman-framework/src/support/bootstrap/LaravelDb.php b/vendor/workerman/webman-framework/src/support/bootstrap/LaravelDb.php new file mode 100644 index 0000000..28928b8 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/bootstrap/LaravelDb.php @@ -0,0 +1,125 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support\bootstrap; + +use Illuminate\Container\Container as IlluminateContainer; +use Illuminate\Database\Capsule\Manager as Capsule; +use Illuminate\Database\MySqlConnection; +use Illuminate\Events\Dispatcher; +use Illuminate\Pagination\Paginator; +use Illuminate\Pagination\CursorPaginator; +use Illuminate\Pagination\Cursor; +use Jenssegers\Mongodb\Connection as JenssegersMongodbConnection; +use MongoDB\Laravel\Connection as LaravelMongodbConnection; +use support\Container; +use Throwable; +use Webman\Bootstrap; +use Workerman\Timer; +use Workerman\Worker; +use function class_exists; +use function config; + +/** + * Class Laravel + * @package support\Bootstrap + */ +class LaravelDb implements Bootstrap +{ + /** + * @param Worker|null $worker + * + * @return void + */ + public static function start(?Worker $worker) + { + if (!class_exists(Capsule::class)) { + return; + } + + $config = config('database', []); + $connections = $config['connections'] ?? []; + if (!$connections) { + return; + } + + $capsule = new Capsule(IlluminateContainer::getInstance()); + + $capsule->getDatabaseManager()->extend('mongodb', function ($config, $name) { + $config['name'] = $name; + return class_exists(LaravelMongodbConnection::class) ? new LaravelMongodbConnection($config) : new JenssegersMongodbConnection($config); + }); + + $default = $config['default'] ?? false; + if ($default) { + $defaultConfig = $connections[$config['default']] ?? false; + if ($defaultConfig) { + $capsule->addConnection($defaultConfig); + } + } + + foreach ($connections as $name => $config) { + $capsule->addConnection($config, $name); + } + + if (class_exists(Dispatcher::class) && !$capsule->getEventDispatcher()) { + $capsule->setEventDispatcher(Container::make(Dispatcher::class, [IlluminateContainer::getInstance()])); + } + + $capsule->setAsGlobal(); + + $capsule->bootEloquent(); + + // Heartbeat + if ($worker) { + Timer::add(55, function () use ($default, $connections, $capsule) { + foreach ($capsule->getDatabaseManager()->getConnections() as $connection) { + /* @var MySqlConnection $connection **/ + if ($connection->getConfig('driver') == 'mysql') { + try { + $connection->select('select 1'); + } catch (Throwable $e) {} + } + } + }); + } + + // Paginator + if (class_exists(Paginator::class)) { + if (method_exists(Paginator::class, 'queryStringResolver')) { + Paginator::queryStringResolver(function () { + $request = request(); + return $request ? $request->queryString() : null; + }); + } + Paginator::currentPathResolver(function () { + $request = request(); + return $request ? $request->path(): '/'; + }); + Paginator::currentPageResolver(function ($pageName = 'page') { + $request = request(); + if (!$request) { + return 1; + } + $page = (int)($request->input($pageName, 1)); + return $page > 0 ? $page : 1; + }); + if (class_exists(CursorPaginator::class)) { + CursorPaginator::currentCursorResolver(function ($cursorName = 'cursor') { + return Cursor::fromEncoded(request()->input($cursorName)); + }); + } + } + } +} diff --git a/vendor/workerman/webman-framework/src/support/bootstrap/Session.php b/vendor/workerman/webman-framework/src/support/bootstrap/Session.php new file mode 100644 index 0000000..7360d0e --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/bootstrap/Session.php @@ -0,0 +1,61 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support\bootstrap; + +use Webman\Bootstrap; +use Workerman\Protocols\Http; +use Workerman\Protocols\Http\Session as SessionBase; +use Workerman\Worker; +use function config; +use function property_exists; + +/** + * Class Session + * @package support + */ +class Session implements Bootstrap +{ + + /** + * @param Worker|null $worker + * @return void + */ + public static function start(?Worker $worker) + { + $config = config('session'); + if (property_exists(SessionBase::class, 'name')) { + SessionBase::$name = $config['session_name']; + } else { + Http::sessionName($config['session_name']); + } + SessionBase::handlerClass($config['handler'], $config['config'][$config['type']]); + $map = [ + 'auto_update_timestamp' => 'autoUpdateTimestamp', + 'cookie_lifetime' => 'cookieLifetime', + 'gc_probability' => 'gcProbability', + 'cookie_path' => 'cookiePath', + 'http_only' => 'httpOnly', + 'same_site' => 'sameSite', + 'lifetime' => 'lifetime', + 'domain' => 'domain', + 'secure' => 'secure', + ]; + foreach ($map as $key => $name) { + if (isset($config[$key]) && property_exists(SessionBase::class, $name)) { + SessionBase::${$name} = $config[$key]; + } + } + } +} diff --git a/vendor/workerman/webman-framework/src/support/exception/BusinessException.php b/vendor/workerman/webman-framework/src/support/exception/BusinessException.php new file mode 100644 index 0000000..aee7877 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/exception/BusinessException.php @@ -0,0 +1,38 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support\exception; + +use Exception; +use Webman\Http\Request; +use Webman\Http\Response; +use function json_encode; + +/** + * Class BusinessException + * @package support\exception + */ +class BusinessException extends Exception +{ + public function render(Request $request): ?Response + { + if ($request->expectsJson()) { + $code = $this->getCode(); + $json = ['code' => $code ?: 500, 'msg' => $this->getMessage()]; + return new Response(200, ['Content-Type' => 'application/json'], + json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + } + return new Response(200, [], $this->getMessage()); + } +} diff --git a/vendor/workerman/webman-framework/src/support/exception/Handler.php b/vendor/workerman/webman-framework/src/support/exception/Handler.php new file mode 100644 index 0000000..98dc618 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/exception/Handler.php @@ -0,0 +1,47 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support\exception; + +use Throwable; +use Webman\Exception\ExceptionHandler; +use Webman\Http\Request; +use Webman\Http\Response; + +/** + * Class Handler + * @package support\exception + */ +class Handler extends ExceptionHandler +{ + public $dontReport = [ + BusinessException::class, + ]; + + public function report(Throwable $exception) + { + parent::report($exception); + } + + public function render(Request $request, Throwable $exception): Response + { + if(($exception instanceof BusinessException) && ($response = $exception->render($request))) + { + return $response; + } + + return parent::render($request, $exception); + } + +} \ No newline at end of file diff --git a/vendor/workerman/webman-framework/src/support/view/Blade.php b/vendor/workerman/webman-framework/src/support/view/Blade.php new file mode 100644 index 0000000..99da551 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/view/Blade.php @@ -0,0 +1,73 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support\view; + +use Jenssegers\Blade\Blade as BladeView; +use Webman\View; +use function app_path; +use function array_merge; +use function base_path; +use function config; +use function is_array; +use function request; +use function runtime_path; + +/** + * Class Blade + * composer require jenssegers/blade + * @package support\view + */ +class Blade implements View +{ + /** + * Assign. + * @param string|array $name + * @param mixed $value + */ + public static function assign($name, $value = null) + { + $request = request(); + $request->_view_vars = array_merge((array) $request->_view_vars, is_array($name) ? $name : [$name => $value]); + } + + /** + * Render. + * @param string $template + * @param array $vars + * @param string|null $app + * @param string|null $plugin + * @return string + */ + public static function render(string $template, array $vars, string $app = null, string $plugin = null): string + { + static $views = []; + $request = request(); + $plugin = $plugin === null ? ($request->plugin ?? '') : $plugin; + $app = $app === null ? $request->app : $app; + $configPrefix = $plugin ? "plugin.$plugin." : ''; + $baseViewPath = $plugin ? base_path() . "/plugin/$plugin/app" : app_path(); + $key = "$plugin-$app"; + if (!isset($views[$key])) { + $viewPath = $app === '' ? "$baseViewPath/view" : "$baseViewPath/$app/view"; + $views[$key] = new BladeView($viewPath, runtime_path() . '/views'); + $extension = config("{$configPrefix}view.extension"); + if ($extension) { + $extension($views[$key]); + } + } + $vars = array_merge((array) $request->_view_vars, $vars); + return $views[$key]->render($template, $vars); + } +} diff --git a/vendor/workerman/webman-framework/src/support/view/Raw.php b/vendor/workerman/webman-framework/src/support/view/Raw.php new file mode 100644 index 0000000..8dd3c39 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/view/Raw.php @@ -0,0 +1,78 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support\view; + +use Throwable; +use Webman\View; +use function app_path; +use function array_merge; +use function base_path; +use function config; +use function extract; +use function is_array; +use function ob_end_clean; +use function ob_get_clean; +use function ob_start; +use function request; + +/** + * Class Raw + * @package support\view + */ +class Raw implements View +{ + /** + * Assign. + * @param string|array $name + * @param mixed $value + */ + public static function assign($name, $value = null) + { + $request = request(); + $request->_view_vars = array_merge((array) $request->_view_vars, is_array($name) ? $name : [$name => $value]); + } + + /** + * Render. + * @param string $template + * @param array $vars + * @param string|null $app + * @param string|null $plugin + * @return false|string + */ + public static function render(string $template, array $vars, string $app = null, string $plugin = null): string + { + $request = request(); + $plugin = $plugin === null ? ($request->plugin ?? '') : $plugin; + $configPrefix = $plugin ? "plugin.$plugin." : ''; + $viewSuffix = config("{$configPrefix}view.options.view_suffix", 'html'); + $app = $app === null ? $request->app : $app; + $baseViewPath = $plugin ? base_path() . "/plugin/$plugin/app" : app_path(); + $__template_path__ = $app === '' ? "$baseViewPath/view/$template.$viewSuffix" : "$baseViewPath/$app/view/$template.$viewSuffix"; + + extract((array) $request->_view_vars); + extract($vars); + ob_start(); + // Try to include php file. + try { + include $__template_path__; + } catch (Throwable $e) { + ob_end_clean(); + throw $e; + } + + return ob_get_clean(); + } +} diff --git a/vendor/workerman/webman-framework/src/support/view/ThinkPHP.php b/vendor/workerman/webman-framework/src/support/view/ThinkPHP.php new file mode 100644 index 0000000..6923d7e --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/view/ThinkPHP.php @@ -0,0 +1,75 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support\view; + +use think\Template; +use Webman\View; +use function app_path; +use function array_merge; +use function base_path; +use function config; +use function is_array; +use function ob_get_clean; +use function ob_start; +use function request; +use function runtime_path; + +/** + * Class Blade + * @package support\view + */ +class ThinkPHP implements View +{ + /** + * Assign. + * @param string|array $name + * @param mixed $value + */ + public static function assign($name, $value = null) + { + $request = request(); + $request->_view_vars = array_merge((array) $request->_view_vars, is_array($name) ? $name : [$name => $value]); + } + + /** + * Render. + * @param string $template + * @param array $vars + * @param string|null $app + * @param string|null $plugin + * @return false|string + */ + public static function render(string $template, array $vars, string $app = null, string $plugin = null): string + { + $request = request(); + $plugin = $plugin === null ? ($request->plugin ?? '') : $plugin; + $app = $app === null ? $request->app : $app; + $configPrefix = $plugin ? "plugin.$plugin." : ''; + $viewSuffix = config("{$configPrefix}view.options.view_suffix", 'html'); + $baseViewPath = $plugin ? base_path() . "/plugin/$plugin/app" : app_path(); + $viewPath = $app === '' ? "$baseViewPath/view/" : "$baseViewPath/$app/view/"; + $defaultOptions = [ + 'view_path' => $viewPath, + 'cache_path' => runtime_path() . '/views/', + 'view_suffix' => $viewSuffix + ]; + $options = array_merge($defaultOptions, config("{$configPrefix}view.options", [])); + $views = new Template($options); + ob_start(); + $vars = array_merge((array) $request->_view_vars, $vars); + $views->fetch($template, $vars); + return ob_get_clean(); + } +} diff --git a/vendor/workerman/webman-framework/src/support/view/Twig.php b/vendor/workerman/webman-framework/src/support/view/Twig.php new file mode 100644 index 0000000..c184655 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/view/Twig.php @@ -0,0 +1,76 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support\view; + +use Twig\Environment; +use Twig\Error\LoaderError; +use Twig\Error\RuntimeError; +use Twig\Error\SyntaxError; +use Twig\Loader\FilesystemLoader; +use Webman\View; +use function app_path; +use function array_merge; +use function base_path; +use function config; +use function is_array; +use function request; + +/** + * Class Blade + * @package support\view + */ +class Twig implements View +{ + /** + * Assign. + * @param string|array $name + * @param mixed $value + */ + public static function assign($name, $value = null) + { + $request = request(); + $request->_view_vars = array_merge((array) $request->_view_vars, is_array($name) ? $name : [$name => $value]); + } + + /** + * Render. + * @param string $template + * @param array $vars + * @param string|null $app + * @param string|null $plugin + * @return string + */ + public static function render(string $template, array $vars, string $app = null, string $plugin = null): string + { + static $views = []; + $request = request(); + $plugin = $plugin === null ? ($request->plugin ?? '') : $plugin; + $app = $app === null ? $request->app : $app; + $configPrefix = $plugin ? "plugin.$plugin." : ''; + $viewSuffix = config("{$configPrefix}view.options.view_suffix", 'html'); + $key = "$plugin-$app"; + if (!isset($views[$key])) { + $baseViewPath = $plugin ? base_path() . "/plugin/$plugin/app" : app_path(); + $viewPath = $app === '' ? "$baseViewPath/view/" : "$baseViewPath/$app/view/"; + $views[$key] = new Environment(new FilesystemLoader($viewPath), config("{$configPrefix}view.options", [])); + $extension = config("{$configPrefix}view.extension"); + if ($extension) { + $extension($views[$key]); + } + } + $vars = array_merge((array) $request->_view_vars, $vars); + return $views[$key]->render("$template.$viewSuffix", $vars); + } +} diff --git a/vendor/workerman/workerman/.github/FUNDING.yml b/vendor/workerman/workerman/.github/FUNDING.yml new file mode 100644 index 0000000..beae44f --- /dev/null +++ b/vendor/workerman/workerman/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +open_collective: workerman +patreon: walkor diff --git a/vendor/workerman/workerman/.gitignore b/vendor/workerman/workerman/.gitignore new file mode 100644 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 100644 index 0000000..7d760e9 --- /dev/null +++ b/vendor/workerman/workerman/Autoloader.php @@ -0,0 +1,69 @@ + + * @copyright walkor + * @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 100644 index 0000000..5bc8676 --- /dev/null +++ b/vendor/workerman/workerman/Connection/AsyncTcpConnection.php @@ -0,0 +1,378 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Connection; + +use StdClass; +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 callable|null + */ + 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 float + */ + 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, array $context_option = array()) + { + $address_info = \parse_url($remote_address); + if (!$address_info) { + list($scheme, $this->_remoteAddress) = \explode(':', $remote_address, 2); + if('unix' === strtolower($scheme)) { + $this->_remoteAddress = substr($remote_address, strpos($remote_address, '/') + 2); + } + if (!$this->_remoteAddress) { + Worker::safeEcho(new \Exception('bad remote_address')); + } + } else { + if (!isset($address_info['port'])) { + $address_info['port'] = 0; + } + if (!isset($address_info['path'])) { + $address_info['path'] = '/'; + } + if (!isset($address_info['query'])) { + $address_info['query'] = ''; + } else { + $address_info['query'] = '?' . $address_info['query']; + } + $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->_remoteAddress = 'unix' === strtolower($scheme) + ? substr($remote_address, strpos($remote_address, '/') + 2) + : $this->_remoteHost . ':' . $this->_remotePort; + } + + $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->maxPackageSize = self::$defaultMaxPackageSize; + $this->_contextOption = $context_option; + $this->context = new StdClass; + 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') { + if (!$this->_remotePort) { + $this->_remotePort = $this->transport === 'ssl' ? 443 : 80; + $this->_remoteAddress = $this->_remoteHost.':'.$this->_remotePort; + } + // 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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + } + + /** + * 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, false); + // 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); + if ($this->_sslHandshakeCompleted === false) { + return; + } + } 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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + // Try to emit protocol::onConnect + if ($this->protocol && \method_exists($this->protocol, 'onConnect')) { + try { + \call_user_func(array($this->protocol, 'onConnect'), $this); + } catch (\Exception $e) { + Worker::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + } 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 100644 index 0000000..745f060 --- /dev/null +++ b/vendor/workerman/workerman/Connection/AsyncUdpConnection.php @@ -0,0 +1,203 @@ + + * @copyright walkor + * @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; + +/** + * AsyncUdpConnection. + */ +class AsyncUdpConnection extends UdpConnection +{ + /** + * Emitted when socket connection is successfully established. + * + * @var callable + */ + public $onConnect = null; + + /** + * Emitted when socket connection closed. + * + * @var callable + */ + 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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + 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; + } + } + 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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + $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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + } + +} diff --git a/vendor/workerman/workerman/Connection/ConnectionInterface.php b/vendor/workerman/workerman/Connection/ConnectionInterface.php new file mode 100644 index 0000000..5d815d8 --- /dev/null +++ b/vendor/workerman/workerman/Connection/ConnectionInterface.php @@ -0,0 +1,126 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Connection; + +/** + * ConnectionInterface. + */ +#[\AllowDynamicProperties] +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 callable + */ + public $onMessage = null; + + /** + * Emitted when the other end of the socket sends a FIN packet. + * + * @var callable + */ + public $onClose = null; + + /** + * Emitted when an error occurs with connection. + * + * @var callable + */ + public $onError = null; + + /** + * Sends data on the connection. + * + * @param mixed $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 string|null $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 100644 index 0000000..740f01d --- /dev/null +++ b/vendor/workerman/workerman/Connection/TcpConnection.php @@ -0,0 +1,982 @@ + + * @copyright walkor + * @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 callable + */ + public $onMessage = null; + + /** + * Emitted when the other end of the socket sends a FIN packet. + * + * @var callable + */ + public $onClose = null; + + /** + * Emitted when an error occurs with connection. + * + * @var callable + */ + public $onError = null; + + /** + * Emitted when the send buffer becomes full. + * + * @var callable + */ + public $onBufferFull = null; + + /** + * Emitted when the send buffer becomes empty. + * + * @var callable + */ + 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; + + /** + * Context. + * + * @var object|null + */ + public $context = null; + + /** + * 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', + ); + + /** + * 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; + $this->context = new \stdClass; + } + + /** + * Get status. + * + * @param bool $raw_output + * + * @return int|string + */ + public function getStatus($raw_output = true) + { + if ($raw_output) { + return $this->_status; + } + return self::$_statusToString[$this->_status]; + } + + /** + * Sends data on the connection. + * + * @param mixed $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; + } + } + + if ($this->_status !== self::STATUS_ESTABLISHED || + ($this->transport === 'ssl' && $this->_sslHandshakeCompleted !== true) + ) { + if ($this->_sendBuffer && $this->bufferIsFull()) { + ++self::$statistics['send_fail']; + return false; + } + $this->_sendBuffer .= $send_buffer; + $this->checkBufferWillFull(); + return; + } + + // 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; + } + $len = 0; + try { + $len = @\fwrite($this->_socket, $send_buffer); + } catch (\Exception $e) { + Worker::log($e); + } catch (\Error $e) { + Worker::log($e); + } + // 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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + $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; + } + + 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 (string) \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() + { + if (!\is_resource($this->_socket)) { + return ''; + } + 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; + } + } + + $buffer = ''; + try { + $buffer = @\fread($socket, self::READ_BUFFER_SIZE); + } catch (\Exception $e) {} catch (\Error $e) {} + + // 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. + try { + $this->_currentPackageLength = $parser::input($this->_recvBuffer, $this); + } catch (\Exception $e) {} catch (\Error $e) {} + // 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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + 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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + // 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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + 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 resource $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 0; + } + if (isset($this->onSslHandshake)) { + try { + \call_user_func($this->onSslHandshake, $this); + } catch (\Exception $e) { + Worker::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + return true; + } + + /** + * This method pulls all the data out of a readable stream, and writes it to the supplied destination. + * + * @param self $dest + * @return void + */ + public function pipe(self $dest) + { + $source = $this; + $this->onMessage = function ($source, $data) use ($dest) { + $dest->send($data); + }; + $this->onClose = function ($source) use ($dest) { + $dest->close(); + }; + $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_CONNECTING){ + $this->destroy(); + return; + } + + if ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) { + return; + } + + 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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + } + } + + /** + * 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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + 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. + try { + @\fclose($this->_socket); + } catch (\Exception $e) {} catch (\Error $e) {} + + $this->_status = self::STATUS_CLOSED; + // Try to emit onClose callback. + if ($this->onClose) { + try { + \call_user_func($this->onClose, $this); + } catch (\Exception $e) { + Worker::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + // 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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + $this->_sendBuffer = $this->_recvBuffer = ''; + $this->_currentPackageLength = 0; + $this->_isPaused = $this->_sslHandshakeCompleted = false; + 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 100644 index 0000000..9cd95ba --- /dev/null +++ b/vendor/workerman/workerman/Connection/UdpConnection.php @@ -0,0 +1,208 @@ + + * @copyright walkor + * @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; + + /** + * Transport layer protocol. + * + * @var string + */ + public $transport = 'udp'; + + /** + * 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; + } + } + return \strlen($send_buffer) === \stream_socket_sendto($this->_socket, $send_buffer, 0, $this->isIpV6() ? '[' . $this->getRemoteIp() . ']:' . $this->getRemotePort() : $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; + } + + /** + * Get the real socket. + * + * @return resource + */ + public function getSocket() + { + return $this->_socket; + } +} diff --git a/vendor/workerman/workerman/Events/Ev.php b/vendor/workerman/workerman/Events/Ev.php new file mode 100644 index 0000000..8e21bc3 --- /dev/null +++ b/vendor/workerman/workerman/Events/Ev.php @@ -0,0 +1,189 @@ + + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events; + +use Workerman\Worker; +use \EvWatcher; + +/** + * 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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + }; + 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(EvWatcher $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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + + /** + * 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() + { + \Ev::stop(\Ev::BREAK_ALL); + } + + /** + * 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 100644 index 0000000..9e25521 --- /dev/null +++ b/vendor/workerman/workerman/Events/Event.php @@ -0,0 +1,215 @@ + + * @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 int|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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + + /** + * @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() + { + $this->_eventBase->exit(); + } + + /** + * 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 100644 index 0000000..e6f59c6 --- /dev/null +++ b/vendor/workerman/workerman/Events/EventInterface.php @@ -0,0 +1,107 @@ + + * @copyright walkor + * @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 array $args + * @return bool + */ + public function add($fd, $flag, $func, $args = array()); + + /** + * 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 100644 index 0000000..5f61e9c --- /dev/null +++ b/vendor/workerman/workerman/Events/Libevent.php @@ -0,0 +1,225 @@ + + * @copyright walkor + * @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::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + 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 100644 index 0000000..bce4f73 --- /dev/null +++ b/vendor/workerman/workerman/Events/React/Base.php @@ -0,0 +1,264 @@ + + * @copyright walkor + * @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; +use React\EventLoop\LoopInterface; + +/** + * Class StreamSelectLoop + * @package Workerman\Events\React + */ +class Base implements LoopInterface +{ + /** + * @var array + */ + protected $_timerIdMap = array(); + + /** + * @var int + */ + protected $_timerIdIndex = 0; + + /** + * @var array + */ + protected $_signalHandlerMap = array(); + + /** + * @var LoopInterface + */ + protected $_eventLoop = null; + + /** + * Base constructor. + */ + public function __construct() + { + $this->_eventLoop = new \React\EventLoop\StreamSelectLoop(); + } + + /** + * Add event listener to event loop. + * + * @param int $fd + * @param int $flag + * @param callable $func + * @param array $args + * @return bool + */ + public function add($fd, $flag, $func, array $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 100644 index 0000000..3dab25b --- /dev/null +++ b/vendor/workerman/workerman/Events/React/ExtEventLoop.php @@ -0,0 +1,27 @@ + + * @copyright walkor + * @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 100644 index 0000000..eb02b35 --- /dev/null +++ b/vendor/workerman/workerman/Events/React/ExtLibEventLoop.php @@ -0,0 +1,27 @@ + + * @copyright walkor + * @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 100644 index 0000000..7f5f94b --- /dev/null +++ b/vendor/workerman/workerman/Events/React/StreamSelectLoop.php @@ -0,0 +1,26 @@ + + * @copyright walkor + * @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 100644 index 0000000..ebc263e --- /dev/null +++ b/vendor/workerman/workerman/Events/Select.php @@ -0,0 +1,357 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events; + +use Throwable; +use Workerman\Worker; + +/** + * 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() + { + // 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; + $select_timeout = $select_timeout <= 0 ? 1 : $select_timeout; + if( $this->_selectTimeout > $select_timeout ){ + $this->_selectTimeout = (int) $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() + { + $tasks_to_insert = []; + 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 = (int) (($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]; + $tasks_to_insert[] = [$timer_id, -$next_run_time]; + } + try { + \call_user_func_array($task_data[0], $task_data[1]); + } catch (Throwable $e) { + Worker::stopAll(250, $e); + } + if (isset($this->_eventTimer[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) { + $this->del($timer_id, self::EV_TIMER_ONCE); + } + } else { + break; + } + } + foreach ($tasks_to_insert as $item) { + $this->_scheduler->insert($item[0], $item[1]); + } + if (!$this->_scheduler->isEmpty()) { + $scheduler_data = $this->_scheduler->top(); + $next_run_time = -$scheduler_data['priority']; + $time_now = \microtime(true); + $this->_selectTimeout = \max((int) (($next_run_time - $time_now) * 1000000), 0); + 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; + $ret = false; + + if ($read || $write || $except) { + // Waiting read/write/signal/timeout events. + try { + $ret = @stream_select($read, $write, $except, 0, $this->_selectTimeout); + } catch (\Exception $e) {} catch (\Error $e) {} + + } else { + $this->_selectTimeout >= 1 && usleep($this->_selectTimeout); + } + + 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 100644 index 0000000..fcd7472 --- /dev/null +++ b/vendor/workerman/workerman/Events/Swoole.php @@ -0,0 +1,230 @@ + + * @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 Workerman\Worker; +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 = 500; + + protected $_hasSignal = false; + + /** + * + * {@inheritdoc} + * + * @see \Workerman\Events\EventInterface::add() + */ + public function add($fd, $flag, $func, $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++; + $t = (int)($fd * 1000); + if ($t < 1) { + $t = 1; + } + $timer_id = Timer::$method($t, + function ($timer_id = null) use ($func, $args, $mapId) { + try { + \call_user_func_array($func, (array)$args); + } catch (\Exception $e) { + Worker::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + // 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(); + posix_kill(posix_getpid(), SIGINT); + } + + /** + * + * {@inheritdoc} + * + * @see \Workerman\Events\EventInterface::getTimerCount() + */ + public function getTimerCount() + { + return \count($this->_timer); + } +} diff --git a/vendor/workerman/workerman/Events/Uv.php b/vendor/workerman/workerman/Events/Uv.php new file mode 100644 index 0000000..49f0ddd --- /dev/null +++ b/vendor/workerman/workerman/Events/Uv.php @@ -0,0 +1,260 @@ + + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Events; + +use Workerman\Worker; + +/** + * libuv eventloop + */ +class Uv implements EventInterface +{ + /** + * Event Loop. + * @var object + */ + protected $_eventLoop = 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. + * + * @var array + */ + protected $_eventTimer = array(); + + /** + * Timer id. + * + * @var int + */ + protected static $_timerId = 1; + + /** + * @brief Constructor + * + * @param object $loop + * + * @return void + */ + public function __construct(\UVLoop $loop = null) + { + if(!extension_loaded('uv')) + { + throw new \Exception(__CLASS__ . ' requires the UV extension, but detected it has NOT been installed yet.'); + } + + if(empty($loop) || !$loop instanceof \UVLoop) + { + $this->_eventLoop = \uv_default_loop(); + return; + } + + $this->_eventLoop = $loop; + } + + /** + * @brief Add a timer + * + * @param resource $fd + * @param int $flag + * @param callback $func + * @param mixed $args + * + * @return mixed + */ + public function add($fd, $flag, $func, $args = null) + { + switch ($flag) + { + case self::EV_SIGNAL: + $signalCallback = function($watcher, $socket)use($func, $fd){ + try { + \call_user_func($func, $fd); + } catch (\Exception $e) { + Worker::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + }; + $signalWatcher = \uv_signal_init(); + \uv_signal_start($signalWatcher, $signalCallback, $fd); + $this->_eventSignal[$fd] = $signalWatcher; + return true; + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + $repeat = $flag === self::EV_TIMER_ONCE ? 0 : (int)($fd * 1000); + $param = array($func, (array)$args, $flag, $fd, self::$_timerId); + $timerWatcher = \uv_timer_init(); + \uv_timer_start($timerWatcher, ($flag === self::EV_TIMER_ONCE ? (int)($fd * 1000) :1), $repeat, function($watcher)use($param){ + call_user_func_array([$this, 'timerCallback'], [$param]); + }); + $this->_eventTimer[self::$_timerId] = $timerWatcher; + return self::$_timerId++; + case self::EV_READ: + case self::EV_WRITE: + $fd_key = (int)$fd; + $ioCallback = function($watcher, $status, $events, $fd)use($func){ + try { + \call_user_func($func, $fd); + } catch (\Exception $e) { + Worker::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + }; + $ioWatcher = \uv_poll_init($this->_eventLoop, $fd); + $real_flag = $flag === self::EV_READ ? \Uv::READABLE : \Uv::WRITABLE; + \uv_poll_start($ioWatcher, $real_flag, $ioCallback); + $this->_allEvents[$fd_key][$flag] = $ioWatcher; + return true; + default: + break; + } + } + + /** + * @brief Remove a timer + * + * @param resource $fd + * @param int $flag + * + * @return boolean + */ + 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])) { + $watcher = $this->_allEvents[$fd_key][$flag]; + \uv_is_active($watcher) && \uv_poll_stop($watcher); + 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])) { + $watcher = $this->_eventSignal[$fd_key]; + \uv_is_active($watcher) && \uv_signal_stop($watcher); + unset($this->_eventSignal[$fd_key]); + } + break; + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + if (isset($this->_eventTimer[$fd])) { + $watcher = $this->_eventTimer[$fd]; + \uv_is_active($watcher) && \uv_timer_stop($watcher); + unset($this->_eventTimer[$fd]); + } + break; + } + + return true; + } + + /** + * @brief Timer callback + * + * @param array $input + * + * @return void + */ + public function timerCallback($input) + { + if(!is_array($input)) return; + + $timer_id = $input[4]; + + if ($input[2] === self::EV_TIMER_ONCE) + { + $watcher = $this->_eventTimer[$timer_id]; + \uv_is_active($watcher) && \uv_timer_stop($watcher); + unset($this->_eventTimer[$timer_id]); + } + + try { + \call_user_func_array($input[0], $input[1]); + } catch (\Exception $e) { + Worker::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + + /** + * @brief Remove all timers + * + * @return void + */ + public function clearAllTimer() + { + if(!is_array($this->_eventTimer)) return; + + foreach($this->_eventTimer as $watcher) + { + \uv_is_active($watcher) && \uv_timer_stop($watcher); + } + + $this->_eventTimer = array(); + } + + /** + * @brief Start loop + * + * @return void + */ + public function loop() + { + \Uv_run(); + } + + /** + * @brief Destroy loop + * + * @return void + */ + public function destroy() + { + !empty($this->_eventLoop) && \uv_loop_delete($this->_eventLoop); + $this->_allEvents = []; + } + + /** + * @brief Get timer count + * + * @return integer + */ + public function getTimerCount() + { + return \count($this->_eventTimer); + } +} diff --git a/vendor/workerman/workerman/Lib/Constants.php b/vendor/workerman/workerman/Lib/Constants.php new file mode 100644 index 0000000..f5e2424 --- /dev/null +++ b/vendor/workerman/workerman/Lib/Constants.php @@ -0,0 +1,44 @@ + + * @copyright walkor + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * + * @link http://www.workerman.net/ + */ + +// Pcre.jit is not stable, temporarily disabled. +ini_set('pcre.jit', 0); + +// For onError callback. +const WORKERMAN_CONNECT_FAIL = 1; +// For onError callback. +const WORKERMAN_SEND_FAIL = 2; + +// Define OS Type +const OS_TYPE_LINUX = 'linux'; +const OS_TYPE_WINDOWS = 'windows'; + +// Compatible with php7 +if (!class_exists('Error')) { + class Error extends Exception + { + } +} + +if (!interface_exists('SessionHandlerInterface')) { + interface SessionHandlerInterface { + public function close(); + public function destroy($session_id); + public function gc($maxlifetime); + public function open($save_path ,$session_name); + public function read($session_id); + public function write($session_id , $session_data); + } +} diff --git a/vendor/workerman/workerman/Lib/Timer.php b/vendor/workerman/workerman/Lib/Timer.php new file mode 100644 index 0000000..b110051 --- /dev/null +++ b/vendor/workerman/workerman/Lib/Timer.php @@ -0,0 +1,22 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Lib; + +/** + * Do not use Workerman\Lib\Timer. + * Please use Workerman\Timer. + * This class is only used for compatibility with workerman 3.* + * @package Workerman\Lib + */ +class Timer extends \Workerman\Timer {} \ No newline at end of file diff --git a/vendor/workerman/workerman/MIT-LICENSE.txt b/vendor/workerman/workerman/MIT-LICENSE.txt new file mode 100644 index 0000000..fd6b1c8 --- /dev/null +++ b/vendor/workerman/workerman/MIT-LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2009-2015 walkor 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 100644 index 0000000..26b04de --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Frame.php @@ -0,0 +1,61 @@ + + * @copyright walkor + * @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 100644 index 0000000..9e5d928 --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Http.php @@ -0,0 +1,323 @@ + + * @copyright walkor + * @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\Protocols\Http\Request; +use Workerman\Protocols\Http\Response; +use Workerman\Protocols\Http\Session; +use Workerman\Protocols\Websocket; +use Workerman\Worker; + +/** + * Class Http. + * @package Workerman\Protocols + */ +class Http +{ + /** + * Request class name. + * + * @var string + */ + protected static $_requestClass = 'Workerman\Protocols\Http\Request'; + + /** + * Upload tmp dir. + * + * @var string + */ + protected static $_uploadTmpDir = ''; + + /** + * Open cache. + * + * @var bool. + */ + protected static $_enableCache = true; + + /** + * Get or set session name. + * + * @param string|null $name + * @return string + */ + public static function sessionName($name = null) + { + if ($name !== null && $name !== '') { + Session::$name = (string)$name; + } + return Session::$name; + } + + /** + * Get or set the request class name. + * + * @param string|null $class_name + * @return string + */ + public static function requestClass($class_name = null) + { + if ($class_name) { + static::$_requestClass = $class_name; + } + return static::$_requestClass; + } + + /** + * Enable or disable Cache. + * + * @param mixed $value + */ + public static function enableCache($value) + { + static::$_enableCache = (bool)$value; + } + + /** + * Check the integrity of the package. + * + * @param string $recv_buffer + * @param TcpConnection $connection + * @return int + */ + public static function input($recv_buffer, TcpConnection $connection) + { + static $input = []; + if (!isset($recv_buffer[512]) && isset($input[$recv_buffer])) { + return $input[$recv_buffer]; + } + $crlf_pos = \strpos($recv_buffer, "\r\n\r\n"); + if (false === $crlf_pos) { + // Judge whether the package length exceeds the limit. + if (\strlen($recv_buffer) >= 16384) { + $connection->close("HTTP/1.1 413 Request Entity Too Large\r\n\r\n", true); + return 0; + } + return 0; + } + + $length = $crlf_pos + 4; + $method = \strstr($recv_buffer, ' ', true); + + if (!\in_array($method, ['GET', 'POST', 'OPTIONS', 'HEAD', 'DELETE', 'PUT', 'PATCH'])) { + $connection->close("HTTP/1.1 400 Bad Request\r\n\r\n", true); + return 0; + } + + $header = \substr($recv_buffer, 0, $crlf_pos); + if ($pos = \strpos($header, "\r\nContent-Length: ")) { + $length = $length + (int)\substr($header, $pos + 18, 10); + $has_content_length = true; + } else if (\preg_match("/\r\ncontent-length: ?(\d+)/i", $header, $match)) { + $length = $length + $match[1]; + $has_content_length = true; + } else { + $has_content_length = false; + if (false !== stripos($header, "\r\nTransfer-Encoding:")) { + $connection->close("HTTP/1.1 400 Bad Request\r\n\r\n", true); + return 0; + } + } + + if ($has_content_length) { + if ($length > $connection->maxPackageSize) { + $connection->close("HTTP/1.1 413 Request Entity Too Large\r\n\r\n", true); + return 0; + } + } + + if (!isset($recv_buffer[512])) { + $input[$recv_buffer] = $length; + if (\count($input) > 512) { + unset($input[key($input)]); + } + } + + return $length; + } + + /** + * Http decode. + * + * @param string $recv_buffer + * @param TcpConnection $connection + * @return \Workerman\Protocols\Http\Request + */ + public static function decode($recv_buffer, TcpConnection $connection) + { + static $requests = array(); + $cacheable = static::$_enableCache && !isset($recv_buffer[512]); + if (true === $cacheable && isset($requests[$recv_buffer])) { + $request = $requests[$recv_buffer]; + $request->connection = $connection; + $connection->__request = $request; + $request->properties = array(); + return $request; + } + $request = new static::$_requestClass($recv_buffer); + $request->connection = $connection; + $connection->__request = $request; + if (true === $cacheable) { + $requests[$recv_buffer] = $request; + if (\count($requests) > 512) { + unset($requests[key($requests)]); + } + } + return $request; + } + + /** + * Http encode. + * + * @param string|Response $response + * @param TcpConnection $connection + * @return string + */ + public static function encode($response, TcpConnection $connection) + { + if (isset($connection->__request)) { + $connection->__request->session = null; + $connection->__request->connection = null; + $connection->__request = null; + } + if (!\is_object($response)) { + $ext_header = ''; + if (isset($connection->__header)) { + foreach ($connection->__header as $name => $value) { + if (\is_array($value)) { + foreach ($value as $item) { + $ext_header = "$name: $item\r\n"; + } + } else { + $ext_header = "$name: $value\r\n"; + } + } + unset($connection->__header); + } + $body_len = \strlen((string)$response); + return "HTTP/1.1 200 OK\r\nServer: workerman\r\n{$ext_header}Connection: keep-alive\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: $body_len\r\n\r\n$response"; + } + + if (isset($connection->__header)) { + $response->withHeaders($connection->__header); + unset($connection->__header); + } + + if (isset($response->file)) { + $file = $response->file['file']; + $offset = $response->file['offset']; + $length = $response->file['length']; + clearstatcache(); + $file_size = (int)\filesize($file); + $body_len = $length > 0 ? $length : $file_size - $offset; + $response->withHeaders(array( + 'Content-Length' => $body_len, + 'Accept-Ranges' => 'bytes', + )); + if ($offset || $length) { + $offset_end = $offset + $body_len - 1; + $response->header('Content-Range', "bytes $offset-$offset_end/$file_size"); + } + if ($body_len < 2 * 1024 * 1024) { + $connection->send((string)$response . file_get_contents($file, false, null, $offset, $body_len), true); + return ''; + } + $handler = \fopen($file, 'r'); + if (false === $handler) { + $connection->close(new Response(403, null, '403 Forbidden')); + return ''; + } + $connection->send((string)$response, true); + static::sendStream($connection, $handler, $offset, $length); + return ''; + } + + return (string)$response; + } + + /** + * Send remainder of a stream to client. + * + * @param TcpConnection $connection + * @param resource $handler + * @param int $offset + * @param int $length + */ + protected static function sendStream(TcpConnection $connection, $handler, $offset = 0, $length = 0) + { + $connection->bufferFull = false; + if ($offset !== 0) { + \fseek($handler, $offset); + } + $offset_end = $offset + $length; + // Read file content from disk piece by piece and send to client. + $do_write = function () use ($connection, $handler, $length, $offset_end) { + // Send buffer not full. + while ($connection->bufferFull === false) { + // Read from disk. + $size = 1024 * 1024; + if ($length !== 0) { + $tell = \ftell($handler); + $remain_size = $offset_end - $tell; + if ($remain_size <= 0) { + fclose($handler); + $connection->onBufferDrain = null; + return; + } + $size = $remain_size > $size ? $size : $remain_size; + } + + $buffer = \fread($handler, $size); + // Read eof. + if ($buffer === '' || $buffer === false) { + fclose($handler); + $connection->onBufferDrain = null; + 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(); + } + + /** + * Set or get uploadTmpDir. + * + * @return bool|string + */ + public static function uploadTmpDir($dir = null) + { + if (null !== $dir) { + static::$_uploadTmpDir = $dir; + } + if (static::$_uploadTmpDir === '') { + if ($upload_tmp_dir = \ini_get('upload_tmp_dir')) { + static::$_uploadTmpDir = $upload_tmp_dir; + } else if ($upload_tmp_dir = \sys_get_temp_dir()) { + static::$_uploadTmpDir = $upload_tmp_dir; + } + } + return static::$_uploadTmpDir; + } +} diff --git a/vendor/workerman/workerman/Protocols/Http/Chunk.php b/vendor/workerman/workerman/Protocols/Http/Chunk.php new file mode 100644 index 0000000..ab06a9c --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Http/Chunk.php @@ -0,0 +1,48 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http; + + +/** + * Class Chunk + * @package Workerman\Protocols\Http + */ +class Chunk +{ + /** + * Chunk buffer. + * + * @var string + */ + protected $_buffer = null; + + /** + * Chunk constructor. + * @param string $buffer + */ + public function __construct($buffer) + { + $this->_buffer = $buffer; + } + + /** + * __toString + * + * @return string + */ + public function __toString() + { + return \dechex(\strlen($this->_buffer))."\r\n$this->_buffer\r\n"; + } +} \ No newline at end of file diff --git a/vendor/workerman/workerman/Protocols/Http/Request.php b/vendor/workerman/workerman/Protocols/Http/Request.php new file mode 100644 index 0000000..d544ac0 --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Http/Request.php @@ -0,0 +1,694 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http; + +use Workerman\Connection\TcpConnection; +use Workerman\Protocols\Http\Session; +use Workerman\Protocols\Http; +use Workerman\Worker; + +/** + * Class Request + * @package Workerman\Protocols\Http + */ +class Request +{ + /** + * Connection. + * + * @var TcpConnection + */ + public $connection = null; + + /** + * Session instance. + * + * @var Session + */ + public $session = null; + + /** + * Properties. + * + * @var array + */ + public $properties = array(); + + /** + * @var int + */ + public static $maxFileUploads = 1024; + + /** + * Http buffer. + * + * @var string + */ + protected $_buffer = null; + + /** + * Request data. + * + * @var array + */ + protected $_data = null; + + /** + * Enable cache. + * + * @var bool + */ + protected static $_enableCache = true; + + /** + * Is safe. + * + * @var bool + */ + protected $_isSafe = true; + + + /** + * Request constructor. + * + * @param string $buffer + */ + public function __construct($buffer) + { + $this->_buffer = $buffer; + } + + /** + * $_GET. + * + * @param string|null $name + * @param mixed|null $default + * @return mixed|null + */ + public function get($name = null, $default = null) + { + if (!isset($this->_data['get'])) { + $this->parseGet(); + } + if (null === $name) { + return $this->_data['get']; + } + return isset($this->_data['get'][$name]) ? $this->_data['get'][$name] : $default; + } + + /** + * $_POST. + * + * @param string|null $name + * @param mixed|null $default + * @return mixed|null + */ + public function post($name = null, $default = null) + { + if (!isset($this->_data['post'])) { + $this->parsePost(); + } + if (null === $name) { + return $this->_data['post']; + } + return isset($this->_data['post'][$name]) ? $this->_data['post'][$name] : $default; + } + + /** + * Get header item by name. + * + * @param string|null $name + * @param mixed|null $default + * @return array|string|null + */ + public function header($name = null, $default = null) + { + if (!isset($this->_data['headers'])) { + $this->parseHeaders(); + } + if (null === $name) { + return $this->_data['headers']; + } + $name = \strtolower($name); + return isset($this->_data['headers'][$name]) ? $this->_data['headers'][$name] : $default; + } + + /** + * Get cookie item by name. + * + * @param string|null $name + * @param mixed|null $default + * @return array|string|null + */ + public function cookie($name = null, $default = null) + { + if (!isset($this->_data['cookie'])) { + $this->_data['cookie'] = array(); + \parse_str(\preg_replace('/; ?/', '&', $this->header('cookie', '')), $this->_data['cookie']); + } + if ($name === null) { + return $this->_data['cookie']; + } + return isset($this->_data['cookie'][$name]) ? $this->_data['cookie'][$name] : $default; + } + + /** + * Get upload files. + * + * @param string|null $name + * @return array|null + */ + public function file($name = null) + { + if (!isset($this->_data['files'])) { + $this->parsePost(); + } + if (null === $name) { + return $this->_data['files']; + } + return isset($this->_data['files'][$name]) ? $this->_data['files'][$name] : null; + } + + /** + * Get method. + * + * @return string + */ + public function method() + { + if (!isset($this->_data['method'])) { + $this->parseHeadFirstLine(); + } + return $this->_data['method']; + } + + /** + * Get http protocol version. + * + * @return string + */ + public function protocolVersion() + { + if (!isset($this->_data['protocolVersion'])) { + $this->parseProtocolVersion(); + } + return $this->_data['protocolVersion']; + } + + /** + * Get host. + * + * @param bool $without_port + * @return string + */ + public function host($without_port = false) + { + $host = $this->header('host'); + if ($host && $without_port) { + return preg_replace('/:\d{1,5}$/', '', $host); + } + return $host; + } + + /** + * Get uri. + * + * @return mixed + */ + public function uri() + { + if (!isset($this->_data['uri'])) { + $this->parseHeadFirstLine(); + } + return $this->_data['uri']; + } + + /** + * Get path. + * + * @return mixed + */ + public function path() + { + if (!isset($this->_data['path'])) { + $this->_data['path'] = (string)\parse_url($this->uri(), PHP_URL_PATH); + } + return $this->_data['path']; + } + + /** + * Get query string. + * + * @return mixed + */ + public function queryString() + { + if (!isset($this->_data['query_string'])) { + $this->_data['query_string'] = (string)\parse_url($this->uri(), PHP_URL_QUERY); + } + return $this->_data['query_string']; + } + + /** + * Get session. + * + * @return bool|\Workerman\Protocols\Http\Session + */ + public function session() + { + if ($this->session === null) { + $session_id = $this->sessionId(); + if ($session_id === false) { + return false; + } + $this->session = new Session($session_id); + } + return $this->session; + } + + /** + * Get/Set session id. + * + * @param $session_id + * @return string + */ + public function sessionId($session_id = null) + { + if ($session_id) { + unset($this->sid); + } + if (!isset($this->sid)) { + $session_name = Session::$name; + $sid = $session_id ? '' : $this->cookie($session_name); + if ($sid === '' || $sid === null) { + if ($this->connection === null) { + Worker::safeEcho('Request->session() fail, header already send'); + return false; + } + $sid = $session_id ? $session_id : static::createSessionId(); + $cookie_params = Session::getCookieParams(); + $this->connection->__header['Set-Cookie'] = array($session_name . '=' . $sid + . (empty($cookie_params['domain']) ? '' : '; Domain=' . $cookie_params['domain']) + . (empty($cookie_params['lifetime']) ? '' : '; Max-Age=' . $cookie_params['lifetime']) + . (empty($cookie_params['path']) ? '' : '; Path=' . $cookie_params['path']) + . (empty($cookie_params['samesite']) ? '' : '; SameSite=' . $cookie_params['samesite']) + . (!$cookie_params['secure'] ? '' : '; Secure') + . (!$cookie_params['httponly'] ? '' : '; HttpOnly')); + } + $this->sid = $sid; + } + return $this->sid; + } + + /** + * Get http raw head. + * + * @return string + */ + public function rawHead() + { + if (!isset($this->_data['head'])) { + $this->_data['head'] = \strstr($this->_buffer, "\r\n\r\n", true); + } + return $this->_data['head']; + } + + /** + * Get http raw body. + * + * @return string + */ + public function rawBody() + { + return \substr($this->_buffer, \strpos($this->_buffer, "\r\n\r\n") + 4); + } + + /** + * Get raw buffer. + * + * @return string + */ + public function rawBuffer() + { + return $this->_buffer; + } + + /** + * Enable or disable cache. + * + * @param mixed $value + */ + public static function enableCache($value) + { + static::$_enableCache = (bool)$value; + } + + /** + * Parse first line of http header buffer. + * + * @return void + */ + protected function parseHeadFirstLine() + { + $first_line = \strstr($this->_buffer, "\r\n", true); + $tmp = \explode(' ', $first_line, 3); + $this->_data['method'] = $tmp[0]; + $this->_data['uri'] = isset($tmp[1]) ? $tmp[1] : '/'; + } + + /** + * Parse protocol version. + * + * @return void + */ + protected function parseProtocolVersion() + { + $first_line = \strstr($this->_buffer, "\r\n", true); + $protoco_version = substr(\strstr($first_line, 'HTTP/'), 5); + $this->_data['protocolVersion'] = $protoco_version ? $protoco_version : '1.0'; + } + + /** + * Parse headers. + * + * @return void + */ + protected function parseHeaders() + { + static $cache = []; + $this->_data['headers'] = array(); + $raw_head = $this->rawHead(); + $end_line_position = \strpos($raw_head, "\r\n"); + if ($end_line_position === false) { + return; + } + $head_buffer = \substr($raw_head, $end_line_position + 2); + $cacheable = static::$_enableCache && !isset($head_buffer[2048]); + if ($cacheable && isset($cache[$head_buffer])) { + $this->_data['headers'] = $cache[$head_buffer]; + return; + } + $head_data = \explode("\r\n", $head_buffer); + foreach ($head_data as $content) { + if (false !== \strpos($content, ':')) { + list($key, $value) = \explode(':', $content, 2); + $key = \strtolower($key); + $value = \ltrim($value); + } else { + $key = \strtolower($content); + $value = ''; + } + if (isset($this->_data['headers'][$key])) { + $this->_data['headers'][$key] = "{$this->_data['headers'][$key]},$value"; + } else { + $this->_data['headers'][$key] = $value; + } + } + if ($cacheable) { + $cache[$head_buffer] = $this->_data['headers']; + if (\count($cache) > 128) { + unset($cache[key($cache)]); + } + } + } + + /** + * Parse head. + * + * @return void + */ + protected function parseGet() + { + static $cache = []; + $query_string = $this->queryString(); + $this->_data['get'] = array(); + if ($query_string === '') { + return; + } + $cacheable = static::$_enableCache && !isset($query_string[1024]); + if ($cacheable && isset($cache[$query_string])) { + $this->_data['get'] = $cache[$query_string]; + return; + } + \parse_str($query_string, $this->_data['get']); + if ($cacheable) { + $cache[$query_string] = $this->_data['get']; + if (\count($cache) > 256) { + unset($cache[key($cache)]); + } + } + } + + /** + * Parse post. + * + * @return void + */ + protected function parsePost() + { + static $cache = []; + $this->_data['post'] = $this->_data['files'] = array(); + $content_type = $this->header('content-type', ''); + if (\preg_match('/boundary="?(\S+)"?/', $content_type, $match)) { + $http_post_boundary = '--' . $match[1]; + $this->parseUploadFiles($http_post_boundary); + return; + } + $body_buffer = $this->rawBody(); + if ($body_buffer === '') { + return; + } + $cacheable = static::$_enableCache && !isset($body_buffer[1024]); + if ($cacheable && isset($cache[$body_buffer])) { + $this->_data['post'] = $cache[$body_buffer]; + return; + } + if (\preg_match('/\bjson\b/i', $content_type)) { + $this->_data['post'] = (array) json_decode($body_buffer, true); + } else { + \parse_str($body_buffer, $this->_data['post']); + } + if ($cacheable) { + $cache[$body_buffer] = $this->_data['post']; + if (\count($cache) > 256) { + unset($cache[key($cache)]); + } + } + } + + /** + * Parse upload files. + * + * @param string $http_post_boundary + * @return void + */ + protected function parseUploadFiles($http_post_boundary) + { + $http_post_boundary = \trim($http_post_boundary, '"'); + $buffer = $this->_buffer; + $post_encode_string = ''; + $files_encode_string = ''; + $files = []; + $boday_position = strpos($buffer, "\r\n\r\n") + 4; + $offset = $boday_position + strlen($http_post_boundary) + 2; + $max_count = static::$maxFileUploads; + while ($max_count-- > 0 && $offset) { + $offset = $this->parseUploadFile($http_post_boundary, $offset, $post_encode_string, $files_encode_string, $files); + } + if ($post_encode_string) { + parse_str($post_encode_string, $this->_data['post']); + } + + if ($files_encode_string) { + parse_str($files_encode_string, $this->_data['files']); + \array_walk_recursive($this->_data['files'], function (&$value) use ($files) { + $value = $files[$value]; + }); + } + } + + /** + * @param $boundary + * @param $section_start_offset + * @return int + */ + protected function parseUploadFile($boundary, $section_start_offset, &$post_encode_string, &$files_encode_str, &$files) + { + $file = []; + $boundary = "\r\n$boundary"; + if (\strlen($this->_buffer) < $section_start_offset) { + return 0; + } + $section_end_offset = \strpos($this->_buffer, $boundary, $section_start_offset); + if (!$section_end_offset) { + return 0; + } + $content_lines_end_offset = \strpos($this->_buffer, "\r\n\r\n", $section_start_offset); + if (!$content_lines_end_offset || $content_lines_end_offset + 4 > $section_end_offset) { + return 0; + } + $content_lines_str = \substr($this->_buffer, $section_start_offset, $content_lines_end_offset - $section_start_offset); + $content_lines = \explode("\r\n", trim($content_lines_str . "\r\n")); + $boundary_value = \substr($this->_buffer, $content_lines_end_offset + 4, $section_end_offset - $content_lines_end_offset - 4); + $upload_key = false; + foreach ($content_lines as $content_line) { + if (!\strpos($content_line, ': ')) { + return 0; + } + list($key, $value) = \explode(': ', $content_line); + switch (strtolower($key)) { + case "content-disposition": + // Is file data. + if (\preg_match('/name="(.*?)"; filename="(.*?)"/i', $value, $match)) { + $error = 0; + $tmp_file = ''; + $file_name = $match[2]; + $size = \strlen($boundary_value); + $tmp_upload_dir = HTTP::uploadTmpDir(); + if (!$tmp_upload_dir) { + $error = UPLOAD_ERR_NO_TMP_DIR; + } else if ($boundary_value === '' && $file_name === '') { + $error = UPLOAD_ERR_NO_FILE; + } else { + $tmp_file = \tempnam($tmp_upload_dir, 'workerman.upload.'); + if ($tmp_file === false || false === \file_put_contents($tmp_file, $boundary_value)) { + $error = UPLOAD_ERR_CANT_WRITE; + } + } + $upload_key = $match[1]; + // Parse upload files. + $file = [ + 'name' => $file_name, + 'tmp_name' => $tmp_file, + 'size' => $size, + 'error' => $error, + 'type' => '', + ]; + break; + } // Is post field. + else { + // Parse $_POST. + if (\preg_match('/name="(.*?)"$/', $value, $match)) { + $k = $match[1]; + $post_encode_string .= \urlencode($k) . "=" . \urlencode($boundary_value) . '&'; + } + return $section_end_offset + \strlen($boundary) + 2; + } + break; + case "content-type": + $file['type'] = \trim($value); + break; + } + } + if ($upload_key === false) { + return 0; + } + $files_encode_str .= \urlencode($upload_key) . '=' . \count($files) . '&'; + $files[] = $file; + + return $section_end_offset + \strlen($boundary) + 2; + } + + /** + * Create session id. + * + * @return string + */ + protected static function createSessionId() + { + return \bin2hex(\pack('d', \microtime(true)) . random_bytes(8)); + } + + /** + * Setter. + * + * @param string $name + * @param mixed $value + * @return void + */ + public function __set($name, $value) + { + $this->properties[$name] = $value; + } + + /** + * Getter. + * + * @param string $name + * @return mixed|null + */ + public function __get($name) + { + return isset($this->properties[$name]) ? $this->properties[$name] : null; + } + + /** + * Isset. + * + * @param string $name + * @return bool + */ + public function __isset($name) + { + return isset($this->properties[$name]); + } + + /** + * Unset. + * + * @param string $name + * @return void + */ + public function __unset($name) + { + unset($this->properties[$name]); + } + + /** + * __toString. + */ + public function __toString() + { + return $this->_buffer; + } + + /** + * __wakeup. + * + * @return void + */ + public function __wakeup() + { + $this->_isSafe = false; + } + + /** + * __destruct. + * + * @return void + */ + public function __destruct() + { + if (isset($this->_data['files']) && $this->_isSafe) { + \clearstatcache(); + \array_walk_recursive($this->_data['files'], function($value, $key){ + if ($key === 'tmp_name') { + if (\is_file($value)) { + \unlink($value); + } + } + }); + } + } +} diff --git a/vendor/workerman/workerman/Protocols/Http/Response.php b/vendor/workerman/workerman/Protocols/Http/Response.php new file mode 100644 index 0000000..e423727 --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Http/Response.php @@ -0,0 +1,458 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http; + +/** + * Class Response + * @package Workerman\Protocols\Http + */ +class Response +{ + /** + * Header data. + * + * @var array + */ + protected $_header = null; + + /** + * Http status. + * + * @var int + */ + protected $_status = null; + + /** + * Http reason. + * + * @var string + */ + protected $_reason = null; + + /** + * Http version. + * + * @var string + */ + protected $_version = '1.1'; + + /** + * Http body. + * + * @var string + */ + protected $_body = null; + + /** + * Send file info + * + * @var array + */ + public $file = null; + + /** + * Mine type map. + * @var array + */ + protected static $_mimeTypeMap = null; + + /** + * Phrases. + * + * @var array + */ + protected static $_phrases = 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', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Switch Proxy', + 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 Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Unordered Collection', + 426 => 'Upgrade Required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 451 => 'Unavailable For Legal Reasons', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 511 => 'Network Authentication Required', + ); + + /** + * Init. + * + * @return void + */ + public static function init() { + static::initMimeTypeMap(); + } + + /** + * Response constructor. + * + * @param int $status + * @param array $headers + * @param string $body + */ + public function __construct( + $status = 200, + $headers = array(), + $body = '' + ) { + $this->_status = $status; + $this->_header = $headers; + $this->_body = (string)$body; + } + + /** + * Set header. + * + * @param string $name + * @param string $value + * @return $this + */ + public function header($name, $value) { + $this->_header[$name] = $value; + return $this; + } + + /** + * Set header. + * + * @param string $name + * @param string $value + * @return Response + */ + public function withHeader($name, $value) { + return $this->header($name, $value); + } + + /** + * Set headers. + * + * @param array $headers + * @return $this + */ + public function withHeaders($headers) { + $this->_header = \array_merge_recursive($this->_header, $headers); + return $this; + } + + /** + * Remove header. + * + * @param string $name + * @return $this + */ + public function withoutHeader($name) { + unset($this->_header[$name]); + return $this; + } + + /** + * Get header. + * + * @param string $name + * @return null|array|string + */ + public function getHeader($name) { + if (!isset($this->_header[$name])) { + return null; + } + return $this->_header[$name]; + } + + /** + * Get headers. + * + * @return array + */ + public function getHeaders() { + return $this->_header; + } + + /** + * Set status. + * + * @param int $code + * @param string|null $reason_phrase + * @return $this + */ + public function withStatus($code, $reason_phrase = null) { + $this->_status = $code; + $this->_reason = $reason_phrase; + return $this; + } + + /** + * Get status code. + * + * @return int + */ + public function getStatusCode() { + return $this->_status; + } + + /** + * Get reason phrase. + * + * @return string + */ + public function getReasonPhrase() { + return $this->_reason; + } + + /** + * Set protocol version. + * + * @param int $version + * @return $this + */ + public function withProtocolVersion($version) { + $this->_version = $version; + return $this; + } + + /** + * Set http body. + * + * @param string $body + * @return $this + */ + public function withBody($body) { + $this->_body = $body; + return $this; + } + + /** + * Get http raw body. + * + * @return string + */ + public function rawBody() { + return $this->_body; + } + + /** + * Send file. + * + * @param string $file + * @param int $offset + * @param int $length + * @return $this + */ + public function withFile($file, $offset = 0, $length = 0) { + if (!\is_file($file)) { + return $this->withStatus(404)->withBody('

    404 Not Found

    '); + } + $this->file = array('file' => $file, 'offset' => $offset, 'length' => $length); + return $this; + } + + /** + * Set cookie. + * + * @param $name + * @param string $value + * @param int $max_age + * @param string $path + * @param string $domain + * @param bool $secure + * @param bool $http_only + * @param string $same_site + * @return $this + */ + public function cookie($name, $value = '', $max_age = null, $path = '', $domain = '', $secure = false, $http_only = false, $same_site = '') + { + $this->_header['Set-Cookie'][] = $name . '=' . \rawurlencode($value) + . (empty($domain) ? '' : '; Domain=' . $domain) + . ($max_age === null ? '' : '; Max-Age=' . $max_age) + . (empty($path) ? '' : '; Path=' . $path) + . (!$secure ? '' : '; Secure') + . (!$http_only ? '' : '; HttpOnly') + . (empty($same_site) ? '' : '; SameSite=' . $same_site); + return $this; + } + + /** + * Create header for file. + * + * @param array $file_info + * @return string + */ + protected function createHeadForFile($file_info) + { + $file = $file_info['file']; + $reason = $this->_reason ? $this->_reason : static::$_phrases[$this->_status]; + $head = "HTTP/{$this->_version} {$this->_status} $reason\r\n"; + $headers = $this->_header; + if (!isset($headers['Server'])) { + $head .= "Server: workerman\r\n"; + } + foreach ($headers as $name => $value) { + if (\is_array($value)) { + foreach ($value as $item) { + $head .= "$name: $item\r\n"; + } + continue; + } + $head .= "$name: $value\r\n"; + } + + if (!isset($headers['Connection'])) { + $head .= "Connection: keep-alive\r\n"; + } + + $file_info = \pathinfo($file); + $extension = isset($file_info['extension']) ? $file_info['extension'] : ''; + $base_name = isset($file_info['basename']) ? $file_info['basename'] : 'unknown'; + if (!isset($headers['Content-Type'])) { + if (isset(self::$_mimeTypeMap[$extension])) { + $head .= "Content-Type: " . self::$_mimeTypeMap[$extension] . "\r\n"; + } else { + $head .= "Content-Type: application/octet-stream\r\n"; + } + } + + if (!isset($headers['Content-Disposition']) && !isset(self::$_mimeTypeMap[$extension])) { + $head .= "Content-Disposition: attachment; filename=\"$base_name\"\r\n"; + } + + if (!isset($headers['Last-Modified'])) { + if ($mtime = \filemtime($file)) { + $head .= 'Last-Modified: '. \gmdate('D, d M Y H:i:s', $mtime) . ' GMT' . "\r\n"; + } + } + + return "{$head}\r\n"; + } + + /** + * __toString. + * + * @return string + */ + public function __toString() + { + if (isset($this->file)) { + return $this->createHeadForFile($this->file); + } + + $reason = $this->_reason ? $this->_reason : static::$_phrases[$this->_status]; + $body_len = \strlen($this->_body); + if (empty($this->_header)) { + return "HTTP/{$this->_version} {$this->_status} $reason\r\nServer: workerman\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: $body_len\r\nConnection: keep-alive\r\n\r\n{$this->_body}"; + } + + $head = "HTTP/{$this->_version} {$this->_status} $reason\r\n"; + $headers = $this->_header; + if (!isset($headers['Server'])) { + $head .= "Server: workerman\r\n"; + } + foreach ($headers as $name => $value) { + if (\is_array($value)) { + foreach ($value as $item) { + $head .= "$name: $item\r\n"; + } + continue; + } + $head .= "$name: $value\r\n"; + } + + if (!isset($headers['Connection'])) { + $head .= "Connection: keep-alive\r\n"; + } + + if (!isset($headers['Content-Type'])) { + $head .= "Content-Type: text/html;charset=utf-8\r\n"; + } else if ($headers['Content-Type'] === 'text/event-stream') { + return $head . $this->_body; + } + + if (!isset($headers['Transfer-Encoding'])) { + $head .= "Content-Length: $body_len\r\n\r\n"; + } else { + return $body_len ? "$head\r\n" . dechex($body_len) . "\r\n{$this->_body}\r\n" : "$head\r\n"; + } + + // The whole http package + return $head . $this->_body; + } + + /** + * Init mime map. + * + * @return void + */ + public static function initMimeTypeMap() + { + $mime_file = __DIR__ . '/mime.types'; + $items = \file($mime_file, \FILE_IGNORE_NEW_LINES | \FILE_SKIP_EMPTY_LINES); + foreach ($items as $content) { + if (\preg_match("/\s*(\S+)\s+(\S.+)/", $content, $match)) { + $mime_type = $match[1]; + $extension_var = $match[2]; + $extension_array = \explode(' ', \substr($extension_var, 0, -1)); + foreach ($extension_array as $file_extension) { + static::$_mimeTypeMap[$file_extension] = $mime_type; + } + } + } + } +} +Response::init(); diff --git a/vendor/workerman/workerman/Protocols/Http/ServerSentEvents.php b/vendor/workerman/workerman/Protocols/Http/ServerSentEvents.php new file mode 100644 index 0000000..a6e9e0d --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Http/ServerSentEvents.php @@ -0,0 +1,64 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http; + +/** + * Class ServerSentEvents + * @package Workerman\Protocols\Http + */ +class ServerSentEvents +{ + /** + * Data. + * @var array + */ + protected $_data = null; + + /** + * ServerSentEvents constructor. + * $data for example ['event'=>'ping', 'data' => 'some thing', 'id' => 1000, 'retry' => 5000] + * @param array $data + */ + public function __construct(array $data) + { + $this->_data = $data; + } + + /** + * __toString. + * + * @return string + */ + public function __toString() + { + $buffer = ''; + $data = $this->_data; + if (isset($data[''])) { + $buffer = ": {$data['']}\n"; + } + if (isset($data['event'])) { + $buffer .= "event: {$data['event']}\n"; + } + if (isset($data['id'])) { + $buffer .= "id: {$data['id']}\n"; + } + if (isset($data['retry'])) { + $buffer .= "retry: {$data['retry']}\n"; + } + if (isset($data['data'])) { + $buffer .= 'data: ' . str_replace("\n", "\ndata: ", $data['data']) . "\n"; + } + return $buffer . "\n"; + } +} \ No newline at end of file diff --git a/vendor/workerman/workerman/Protocols/Http/Session.php b/vendor/workerman/workerman/Protocols/Http/Session.php new file mode 100644 index 0000000..a0c2417 --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Http/Session.php @@ -0,0 +1,461 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Workerman\Protocols\Http; + +use Workerman\Protocols\Http\Session\SessionHandlerInterface; + +/** + * Class Session + * @package Workerman\Protocols\Http + */ +class Session +{ + /** + * Session andler class which implements SessionHandlerInterface. + * + * @var string + */ + protected static $_handlerClass = 'Workerman\Protocols\Http\Session\FileSessionHandler'; + + /** + * Parameters of __constructor for session handler class. + * + * @var null + */ + protected static $_handlerConfig = null; + + /** + * Session name. + * + * @var string + */ + public static $name = 'PHPSID'; + + /** + * Auto update timestamp. + * + * @var bool + */ + public static $autoUpdateTimestamp = false; + + /** + * Session lifetime. + * + * @var int + */ + public static $lifetime = 1440; + + /** + * Cookie lifetime. + * + * @var int + */ + public static $cookieLifetime = 1440; + + /** + * Session cookie path. + * + * @var string + */ + public static $cookiePath = '/'; + + /** + * Session cookie domain. + * + * @var string + */ + public static $domain = ''; + + /** + * HTTPS only cookies. + * + * @var bool + */ + public static $secure = false; + + /** + * HTTP access only. + * + * @var bool + */ + public static $httpOnly = true; + + /** + * Same-site cookies. + * + * @var string + */ + public static $sameSite = ''; + + /** + * Gc probability. + * + * @var int[] + */ + public static $gcProbability = [1, 1000]; + + /** + * Session handler instance. + * + * @var SessionHandlerInterface + */ + protected static $_handler = null; + + /** + * Session data. + * + * @var array + */ + protected $_data = []; + + /** + * Session changed and need to save. + * + * @var bool + */ + protected $_needSave = false; + + /** + * Session id. + * + * @var null + */ + protected $_sessionId = null; + + /** + * Is safe. + * + * @var bool + */ + protected $_isSafe = true; + + /** + * Session constructor. + * + * @param string $session_id + */ + public function __construct($session_id) + { + static::checkSessionId($session_id); + if (static::$_handler === null) { + static::initHandler(); + } + $this->_sessionId = $session_id; + if ($data = static::$_handler->read($session_id)) { + $this->_data = \unserialize($data); + } + } + + /** + * Get session id. + * + * @return string + */ + public function getId() + { + return $this->_sessionId; + } + + /** + * Get session. + * + * @param string $name + * @param mixed|null $default + * @return mixed|null + */ + public function get($name, $default = null) + { + return isset($this->_data[$name]) ? $this->_data[$name] : $default; + } + + /** + * Store data in the session. + * + * @param string $name + * @param mixed $value + */ + public function set($name, $value) + { + $this->_data[$name] = $value; + $this->_needSave = true; + } + + /** + * Delete an item from the session. + * + * @param string $name + */ + public function delete($name) + { + unset($this->_data[$name]); + $this->_needSave = true; + } + + /** + * Retrieve and delete an item from the session. + * + * @param string $name + * @param mixed|null $default + * @return mixed|null + */ + public function pull($name, $default = null) + { + $value = $this->get($name, $default); + $this->delete($name); + return $value; + } + + /** + * Store data in the session. + * + * @param string|array $key + * @param mixed|null $value + */ + public function put($key, $value = null) + { + if (!\is_array($key)) { + $this->set($key, $value); + return; + } + + foreach ($key as $k => $v) { + $this->_data[$k] = $v; + } + $this->_needSave = true; + } + + /** + * Remove a piece of data from the session. + * + * @param string $name + */ + public function forget($name) + { + if (\is_scalar($name)) { + $this->delete($name); + return; + } + if (\is_array($name)) { + foreach ($name as $key) { + unset($this->_data[$key]); + } + } + $this->_needSave = true; + } + + /** + * Retrieve all the data in the session. + * + * @return array + */ + public function all() + { + return $this->_data; + } + + /** + * Remove all data from the session. + * + * @return void + */ + public function flush() + { + $this->_needSave = true; + $this->_data = []; + } + + /** + * Determining If An Item Exists In The Session. + * + * @param string $name + * @return bool + */ + public function has($name) + { + return isset($this->_data[$name]); + } + + /** + * To determine if an item is present in the session, even if its value is null. + * + * @param string $name + * @return bool + */ + public function exists($name) + { + return \array_key_exists($name, $this->_data); + } + + /** + * Save session to store. + * + * @return void + */ + public function save() + { + if ($this->_needSave) { + if (empty($this->_data)) { + static::$_handler->destroy($this->_sessionId); + } else { + static::$_handler->write($this->_sessionId, \serialize($this->_data)); + } + } elseif (static::$autoUpdateTimestamp) { + static::refresh(); + } + $this->_needSave = false; + } + + /** + * Refresh session expire time. + * + * @return bool + */ + public function refresh() + { + static::$_handler->updateTimestamp($this->getId()); + } + + /** + * Init. + * + * @return void + */ + public static function init() + { + if (($gc_probability = (int)\ini_get('session.gc_probability')) && ($gc_divisor = (int)\ini_get('session.gc_divisor'))) { + static::$gcProbability = [$gc_probability, $gc_divisor]; + } + + if ($gc_max_life_time = \ini_get('session.gc_maxlifetime')) { + self::$lifetime = (int)$gc_max_life_time; + } + + $session_cookie_params = \session_get_cookie_params(); + static::$cookieLifetime = $session_cookie_params['lifetime']; + static::$cookiePath = $session_cookie_params['path']; + static::$domain = $session_cookie_params['domain']; + static::$secure = $session_cookie_params['secure']; + static::$httpOnly = $session_cookie_params['httponly']; + } + + /** + * Set session handler class. + * + * @param mixed|null $class_name + * @param mixed|null $config + * @return string + */ + public static function handlerClass($class_name = null, $config = null) + { + if ($class_name) { + static::$_handlerClass = $class_name; + } + if ($config) { + static::$_handlerConfig = $config; + } + return static::$_handlerClass; + } + + /** + * Get cookie params. + * + * @return array + */ + public static function getCookieParams() + { + return [ + 'lifetime' => static::$cookieLifetime, + 'path' => static::$cookiePath, + 'domain' => static::$domain, + 'secure' => static::$secure, + 'httponly' => static::$httpOnly, + 'samesite' => static::$sameSite, + ]; + } + + /** + * Init handler. + * + * @return void + */ + protected static function initHandler() + { + if (static::$_handlerConfig === null) { + static::$_handler = new static::$_handlerClass(); + } else { + static::$_handler = new static::$_handlerClass(static::$_handlerConfig); + } + } + + /** + * GC sessions. + * + * @return void + */ + public function gc() + { + static::$_handler->gc(static::$lifetime); + } + + /** + * __wakeup. + * + * @return void + */ + public function __wakeup() + { + $this->_isSafe = false; + } + + /** + * __destruct. + * + * @return void + */ + public function __destruct() + { + if (!$this->_isSafe) { + return; + } + $this->save(); + if (\random_int(1, static::$gcProbability[1]) <= static::$gcProbability[0]) { + $this->gc(); + } + } + + /** + * Check session id. + * + * @param string $session_id + */ + protected static function checkSessionId($session_id) + { + if (!\preg_match('/^[a-zA-Z0-9"]+$/', $session_id)) { + throw new SessionException("session_id $session_id is invalid"); + } + } +} + +/** + * Class SessionException + * @package Workerman\Protocols\Http + */ +class SessionException extends \RuntimeException +{ + +} + +// Init session. +Session::init(); diff --git a/vendor/workerman/workerman/Protocols/Http/Session/FileSessionHandler.php b/vendor/workerman/workerman/Protocols/Http/Session/FileSessionHandler.php new file mode 100644 index 0000000..a7cefbd --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Http/Session/FileSessionHandler.php @@ -0,0 +1,183 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http\Session; + +use Workerman\Protocols\Http\Session; + +/** + * Class FileSessionHandler + * @package Workerman\Protocols\Http\Session + */ +class FileSessionHandler implements SessionHandlerInterface +{ + /** + * Session save path. + * + * @var string + */ + protected static $_sessionSavePath = null; + + /** + * Session file prefix. + * + * @var string + */ + protected static $_sessionFilePrefix = 'session_'; + + /** + * Init. + */ + public static function init() { + $save_path = @\session_save_path(); + if (!$save_path || \strpos($save_path, 'tcp://') === 0) { + $save_path = \sys_get_temp_dir(); + } + static::sessionSavePath($save_path); + } + + /** + * FileSessionHandler constructor. + * @param array $config + */ + public function __construct($config = array()) { + if (isset($config['save_path'])) { + static::sessionSavePath($config['save_path']); + } + } + + /** + * {@inheritdoc} + */ + public function open($save_path, $name) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function read($session_id) + { + $session_file = static::sessionFile($session_id); + \clearstatcache(); + if (\is_file($session_file)) { + if (\time() - \filemtime($session_file) > Session::$lifetime) { + \unlink($session_file); + return ''; + } + $data = \file_get_contents($session_file); + return $data ? $data : ''; + } + return ''; + } + + /** + * {@inheritdoc} + */ + public function write($session_id, $session_data) + { + $temp_file = static::$_sessionSavePath . uniqid(bin2hex(random_bytes(8)), true); + if (!\file_put_contents($temp_file, $session_data)) { + return false; + } + return \rename($temp_file, static::sessionFile($session_id)); + } + + /** + * Update sesstion modify time. + * + * @see https://www.php.net/manual/en/class.sessionupdatetimestamphandlerinterface.php + * @see https://www.php.net/manual/zh/function.touch.php + * + * @param string $id Session id. + * @param string $data Session Data. + * + * @return bool + */ + public function updateTimestamp($id, $data = "") + { + $session_file = static::sessionFile($id); + if (!file_exists($session_file)) { + return false; + } + // set file modify time to current time + $set_modify_time = \touch($session_file); + // clear file stat cache + \clearstatcache(); + return $set_modify_time; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($session_id) + { + $session_file = static::sessionFile($session_id); + if (\is_file($session_file)) { + \unlink($session_file); + } + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) { + $time_now = \time(); + foreach (\glob(static::$_sessionSavePath . static::$_sessionFilePrefix . '*') as $file) { + if(\is_file($file) && $time_now - \filemtime($file) > $maxlifetime) { + \unlink($file); + } + } + } + + /** + * Get session file path. + * + * @param string $session_id + * @return string + */ + protected static function sessionFile($session_id) { + return static::$_sessionSavePath.static::$_sessionFilePrefix.$session_id; + } + + /** + * Get or set session file path. + * + * @param string $path + * @return string + */ + public static function sessionSavePath($path) { + if ($path) { + if ($path[\strlen($path)-1] !== DIRECTORY_SEPARATOR) { + $path .= DIRECTORY_SEPARATOR; + } + static::$_sessionSavePath = $path; + if (!\is_dir($path)) { + \mkdir($path, 0777, true); + } + } + return $path; + } +} + +FileSessionHandler::init(); \ No newline at end of file diff --git a/vendor/workerman/workerman/Protocols/Http/Session/RedisClusterSessionHandler.php b/vendor/workerman/workerman/Protocols/Http/Session/RedisClusterSessionHandler.php new file mode 100644 index 0000000..281759a --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Http/Session/RedisClusterSessionHandler.php @@ -0,0 +1,46 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Workerman\Protocols\Http\Session; + +use Workerman\Protocols\Http\Session; + +class RedisClusterSessionHandler extends RedisSessionHandler +{ + public function __construct($config) + { + $timeout = isset($config['timeout']) ? $config['timeout'] : 2; + $read_timeout = isset($config['read_timeout']) ? $config['read_timeout'] : $timeout; + $persistent = isset($config['persistent']) ? $config['persistent'] : false; + $auth = isset($config['auth']) ? $config['auth'] : ''; + if ($auth) { + $this->_redis = new \RedisCluster(null, $config['host'], $timeout, $read_timeout, $persistent, $auth); + } else { + $this->_redis = new \RedisCluster(null, $config['host'], $timeout, $read_timeout, $persistent); + } + if (empty($config['prefix'])) { + $config['prefix'] = 'redis_session_'; + } + $this->_redis->setOption(\Redis::OPT_PREFIX, $config['prefix']); + } + + /** + * {@inheritdoc} + */ + public function read($session_id) + { + return $this->_redis->get($session_id); + } + +} diff --git a/vendor/workerman/workerman/Protocols/Http/Session/RedisSessionHandler.php b/vendor/workerman/workerman/Protocols/Http/Session/RedisSessionHandler.php new file mode 100644 index 0000000..e1b5bd5 --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Http/Session/RedisSessionHandler.php @@ -0,0 +1,154 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http\Session; + +use Workerman\Protocols\Http\Session; +use Workerman\Timer; +use RedisException; + +/** + * Class RedisSessionHandler + * @package Workerman\Protocols\Http\Session + */ +class RedisSessionHandler implements SessionHandlerInterface +{ + + /** + * @var \Redis + */ + protected $_redis; + + /** + * @var array + */ + protected $_config; + + /** + * RedisSessionHandler constructor. + * @param array $config = [ + * 'host' => '127.0.0.1', + * 'port' => 6379, + * 'timeout' => 2, + * 'auth' => '******', + * 'database' => 2, + * 'prefix' => 'redis_session_', + * 'ping' => 55, + * ] + */ + public function __construct($config) + { + if (false === extension_loaded('redis')) { + throw new \RuntimeException('Please install redis extension.'); + } + + if (!isset($config['timeout'])) { + $config['timeout'] = 2; + } + + $this->_config = $config; + + $this->connect(); + + Timer::add(!empty($config['ping']) ? $config['ping'] : 55, function () { + $this->_redis->get('ping'); + }); + } + + public function connect() + { + $config = $this->_config; + + $this->_redis = new \Redis(); + if (false === $this->_redis->connect($config['host'], $config['port'], $config['timeout'])) { + throw new \RuntimeException("Redis connect {$config['host']}:{$config['port']} fail."); + } + if (!empty($config['auth'])) { + $this->_redis->auth($config['auth']); + } + if (!empty($config['database'])) { + $this->_redis->select($config['database']); + } + if (empty($config['prefix'])) { + $config['prefix'] = 'redis_session_'; + } + $this->_redis->setOption(\Redis::OPT_PREFIX, $config['prefix']); + } + + /** + * {@inheritdoc} + */ + public function open($save_path, $name) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function read($session_id) + { + try { + return $this->_redis->get($session_id); + } catch (RedisException $e) { + $msg = strtolower($e->getMessage()); + if ($msg === 'connection lost' || strpos($msg, 'went away')) { + $this->connect(); + return $this->_redis->get($session_id); + } + throw $e; + } + + } + + /** + * {@inheritdoc} + */ + public function write($session_id, $session_data) + { + return true === $this->_redis->setex($session_id, Session::$lifetime, $session_data); + } + + /** + * {@inheritdoc} + */ + public function updateTimestamp($id, $data = "") + { + return true === $this->_redis->expire($id, Session::$lifetime); + } + + /** + * {@inheritdoc} + */ + public function destroy($session_id) + { + $this->_redis->del($session_id); + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + return true; + } +} diff --git a/vendor/workerman/workerman/Protocols/Http/Session/SessionHandlerInterface.php b/vendor/workerman/workerman/Protocols/Http/Session/SessionHandlerInterface.php new file mode 100644 index 0000000..23a47f2 --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Http/Session/SessionHandlerInterface.php @@ -0,0 +1,114 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols\Http\Session; + +interface SessionHandlerInterface +{ + /** + * Close the session + * @link http://php.net/manual/en/sessionhandlerinterface.close.php + * @return bool

    + * The return value (usually TRUE on success, FALSE on failure). + * Note this value is returned internally to PHP for processing. + *

    + * @since 5.4.0 + */ + public function close(); + + /** + * Destroy a session + * @link http://php.net/manual/en/sessionhandlerinterface.destroy.php + * @param string $session_id The session ID being destroyed. + * @return bool

    + * The return value (usually TRUE on success, FALSE on failure). + * Note this value is returned internally to PHP for processing. + *

    + * @since 5.4.0 + */ + public function destroy($session_id); + + /** + * Cleanup old sessions + * @link http://php.net/manual/en/sessionhandlerinterface.gc.php + * @param int $maxlifetime

    + * Sessions that have not updated for + * the last maxlifetime seconds will be removed. + *

    + * @return bool

    + * The return value (usually TRUE on success, FALSE on failure). + * Note this value is returned internally to PHP for processing. + *

    + * @since 5.4.0 + */ + public function gc($maxlifetime); + + /** + * Initialize session + * @link http://php.net/manual/en/sessionhandlerinterface.open.php + * @param string $save_path The path where to store/retrieve the session. + * @param string $name The session name. + * @return bool

    + * The return value (usually TRUE on success, FALSE on failure). + * Note this value is returned internally to PHP for processing. + *

    + * @since 5.4.0 + */ + public function open($save_path, $name); + + + /** + * Read session data + * @link http://php.net/manual/en/sessionhandlerinterface.read.php + * @param string $session_id The session id to read data for. + * @return string

    + * Returns an encoded string of the read data. + * If nothing was read, it must return an empty string. + * Note this value is returned internally to PHP for processing. + *

    + * @since 5.4.0 + */ + public function read($session_id); + + /** + * Write session data + * @link http://php.net/manual/en/sessionhandlerinterface.write.php + * @param string $session_id The session id. + * @param string $session_data

    + * The encoded session data. This data is the + * result of the PHP internally encoding + * the $_SESSION superglobal to a serialized + * string and passing it as this parameter. + * Please note sessions use an alternative serialization method. + *

    + * @return bool

    + * The return value (usually TRUE on success, FALSE on failure). + * Note this value is returned internally to PHP for processing. + *

    + * @since 5.4.0 + */ + public function write($session_id, $session_data); + + /** + * Update sesstion modify time. + * + * @see https://www.php.net/manual/en/class.sessionupdatetimestamphandlerinterface.php + * + * @param string $id Session id. + * @param string $data Session Data. + * + * @return bool + */ + public function updateTimestamp($id, $data = ""); + +} diff --git a/vendor/workerman/workerman/Protocols/Http/mime.types b/vendor/workerman/workerman/Protocols/Http/mime.types new file mode 100644 index 0000000..e6ccf0a --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Http/mime.types @@ -0,0 +1,90 @@ + +types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/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/font-woff woff; + application/java-archive jar war ear; + application/json json; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.apple.mpegurl m3u8; + application/vnd.ms-excel xls; + application/vnd.ms-fontobject eot; + 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/xspf+xml xspf; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream iso img; + application/octet-stream msi msp msm; + + application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; + application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; + + audio/midi mid midi kar; + audio/mpeg mp3; + audio/ogg ogg; + audio/x-m4a m4a; + audio/x-realaudio ra; + + video/3gpp 3gpp 3gp; + video/mp2t ts; + 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; + font/ttf ttf; +} diff --git a/vendor/workerman/workerman/Protocols/ProtocolInterface.php b/vendor/workerman/workerman/Protocols/ProtocolInterface.php new file mode 100644 index 0000000..4fea87d --- /dev/null +++ b/vendor/workerman/workerman/Protocols/ProtocolInterface.php @@ -0,0 +1,52 @@ + + * @copyright walkor + * @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 string $recv_buffer + * @param ConnectionInterface $connection + * @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 string $recv_buffer + * @param ConnectionInterface $connection + * @return mixed + */ + public static function decode($recv_buffer, ConnectionInterface $connection); + + /** + * Encode package brefore sending to client. + * + * @param mixed $data + * @param ConnectionInterface $connection + * @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 100644 index 0000000..407ea2d --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Text.php @@ -0,0 +1,70 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman\Protocols; + +use Workerman\Connection\ConnectionInterface; + +/** + * Text Protocol. + */ +class Text +{ + /** + * Check the integrity of the package. + * + * @param string $buffer + * @param ConnectionInterface $connection + * @return int + */ + public static function input($buffer, ConnectionInterface $connection) + { + // Judge whether the package length exceeds the limit. + if (isset($connection->maxPackageSize) && \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"); + } +} \ No newline at end of file diff --git a/vendor/workerman/workerman/Protocols/Websocket.php b/vendor/workerman/workerman/Protocols/Websocket.php new file mode 100644 index 0000000..0f94de6 --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Websocket.php @@ -0,0 +1,564 @@ + + * @copyright walkor + * @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\Protocols\Http\Request; +use Workerman\Worker; + +/** + * WebSocket protocol. + */ +class Websocket implements \Workerman\Protocols\ProtocolInterface +{ + /** + * Websocket blob type. + * + * @var string + */ + const BINARY_TYPE_BLOB = "\x81"; + + /** + * Websocket blob type. + * + * @var string + */ + const BINARY_TYPE_BLOB_DEFLATE = "\xc1"; + + /** + * Websocket arraybuffer type. + * + * @var string + */ + const BINARY_TYPE_ARRAYBUFFER = "\x82"; + + /** + * Websocket arraybuffer type. + * + * @var string + */ + const BINARY_TYPE_ARRAYBUFFER_DEFLATE = "\xc2"; + + /** + * 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->context->websocketHandshake)) { + return static::dealHandshake($buffer, $connection); + } + + // Buffer websocket frame data. + if ($connection->context->websocketCurrentFrameLength) { + // We need more frame data. + if ($connection->context->websocketCurrentFrameLength > $recv_len) { + // Return 0, because it is not clear the full packet length, waiting for the frame of fin=1. + return 0; + } + } else { + $first_byte = \ord($buffer[0]); + $second_byte = \ord($buffer[1]); + $data_len = $second_byte & 127; + $is_fin_frame = $first_byte >> 7; + $masked = $second_byte >> 7; + + if (!$masked) { + Worker::safeEcho("frame not masked so close the connection\n"); + $connection->close(); + return 0; + } + + $opcode = $first_byte & 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. + $close_cb = $connection->onWebSocketClose ?? $connection->worker->onWebSocketClose ?? false; + if ($close_cb) { + try { + $close_cb($connection); + } catch (\Throwable $e) { + Worker::stopAll(250, $e); + } + } // Close connection. + else { + $connection->close("\x88\x02\x03\xe8", 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->context->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"; + $ping_cb = $connection->onWebSocketPing ?? $connection->worker->onWebSocketPing ?? false; + if ($ping_cb) { + try { + $ping_cb($connection, $ping_data); + } catch (\Throwable $e) { + Worker::stopAll(250, $e); + } + } 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. + $pong_cb = $connection->onWebSocketPong ?? $connection->worker->onWebSocketPong ?? false; + if ($pong_cb) { + try { + $pong_cb($connection, $pong_data); + } catch (\Throwable $e) { + Worker::stopAll(250, $e); + } + } + $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->context->websocketCurrentFrameLength = $current_frame_length; + } + } + + // Received just a frame length data. + if ($connection->context->websocketCurrentFrameLength === $recv_len) { + static::decode($buffer, $connection); + $connection->consumeRecvBuffer($connection->context->websocketCurrentFrameLength); + $connection->context->websocketCurrentFrameLength = 0; + return 0; + } // The length of the received data is greater than the length of a frame. + elseif ($connection->context->websocketCurrentFrameLength < $recv_len) { + static::decode(\substr($buffer, 0, $connection->context->websocketCurrentFrameLength), $connection); + $connection->consumeRecvBuffer($connection->context->websocketCurrentFrameLength); + $current_frame_length = $connection->context->websocketCurrentFrameLength; + $connection->context->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. "); + } + + if (empty($connection->websocketType)) { + $connection->websocketType = static::BINARY_TYPE_BLOB; + } + + // permessage-deflate + if (\ord($connection->websocketType) & 64) { + $buffer = static::deflate($connection, $buffer); + } + + $first_byte = $connection->websocketType; + $len = \strlen($buffer); + + 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->context->websocketHandshake)) { + if (empty($connection->context->tmpWebsocketData)) { + $connection->context->tmpWebsocketData = ''; + } + // If buffer has already full then discard the current package. + if (\strlen($connection->context->tmpWebsocketData) > $connection->maxSendBufferSize) { + if ($connection->onError) { + try { + ($connection->onError)($connection, WORKERMAN_SEND_FAIL, 'send buffer full and drop package'); + } catch (\Throwable $e) { + Worker::stopAll(250, $e); + } + } + return ''; + } + $connection->context->tmpWebsocketData .= $encode_buffer; + // Check buffer is full. + if ($connection->maxSendBufferSize <= \strlen($connection->context->tmpWebsocketData)) { + if ($connection->onBufferFull) { + try { + ($connection->onBufferFull)($connection); + } catch (\Throwable $e) { + Worker::stopAll(250, $e); + } + } + } + // Return empty string. + return ''; + } + + return $encode_buffer; + } + + /** + * Websocket decode. + * + * @param string $buffer + * @param ConnectionInterface $connection + * @return string + */ + public static function decode($buffer, ConnectionInterface $connection) + { + $first_byte = \ord($buffer[0]); + $second_byte = \ord($buffer[1]); + $len = $second_byte & 127; + $is_fin_frame = $first_byte >> 7; + $rsv1 = 64 === ($first_byte & 64); + + 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); + } + } + $dataLength = \strlen($data); + $masks = \str_repeat($masks, \floor($dataLength / 4)) . \substr($masks, 0, $dataLength % 4); + $decoded = $data ^ $masks; + if ($connection->context->websocketCurrentFrameLength) { + $connection->context->websocketDataBuffer .= $decoded; + if ($rsv1) { + return static::inflate($connection, $connection->context->websocketDataBuffer, $is_fin_frame); + } + return $connection->context->websocketDataBuffer; + } else { + if ($connection->context->websocketDataBuffer !== '') { + $decoded = $connection->context->websocketDataBuffer . $decoded; + $connection->context->websocketDataBuffer = ''; + } + if ($rsv1) { + return static::inflate($connection, $decoded, $is_fin_frame); + } + return $decoded; + } + } + + /** + * Inflate. + * + * @param $connection + * @param $buffer + * @param $is_fin_frame + * @return false|string + */ + protected static function inflate($connection, $buffer, $is_fin_frame) + { + if (!isset($connection->context->inflator)) { + $connection->context->inflator = \inflate_init( + \ZLIB_ENCODING_RAW, + [ + 'level' => -1, + 'memory' => 8, + 'window' => 9, + 'strategy' => \ZLIB_DEFAULT_STRATEGY + ] + ); + } + if ($is_fin_frame) { + $buffer .= "\x00\x00\xff\xff"; + } + return \inflate_add($connection->context->inflator, $buffer); + } + + /** + * Deflate. + * + * @param $connection + * @param $buffer + * @return false|string + */ + protected static function deflate($connection, $buffer) + { + if (!isset($connection->context->deflator)) { + $connection->context->deflator = \deflate_init( + \ZLIB_ENCODING_RAW, + [ + 'level' => -1, + 'memory' => 8, + 'window' => 9, + 'strategy' => \ZLIB_DEFAULT_STRATEGY + ] + ); + } + return \substr(\deflate_add($connection->context->deflator, $buffer), 0, -4); + } + + /** + * Websocket handshake. + * + * @param string $buffer + * @param TcpConnection $connection + * @return int + */ + public static function dealHandshake($buffer, $connection) + { + // HTTP protocol. + if (0 === \strpos($buffer, 'GET')) { + // Find \r\n\r\n. + $header_end_pos = \strpos($buffer, "\r\n\r\n"); + if (!$header_end_pos) { + return 0; + } + $header_length = $header_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->close("HTTP/1.1 200 WebSocket\r\nServer: workerman/" . Worker::VERSION . "\r\n\r\n

    WebSocket


    workerman/" . Worker::VERSION . "
    ", + true); + 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" + . "Upgrade: websocket\r\n" + . "Sec-WebSocket-Version: 13\r\n" + . "Connection: Upgrade\r\n" + . "Sec-WebSocket-Accept: " . $new_key . "\r\n"; + + // Websocket data buffer. + $connection->context->websocketDataBuffer = ''; + // Current websocket frame length. + $connection->context->websocketCurrentFrameLength = 0; + // Current websocket frame data. + $connection->context->websocketCurrentFrameBuffer = ''; + // Consume handshake data. + $connection->consumeRecvBuffer($header_length); + + // Try to emit onWebSocketConnect callback. + $on_websocket_connect = $connection->onWebSocketConnect ?? $connection->worker->onWebSocketConnect ?? false; + if ($on_websocket_connect) { + static::parseHttpHeader($buffer); + try { + \call_user_func($on_websocket_connect, $connection, $buffer); + } catch (\Exception $e) { + Worker::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + if (!empty($_SESSION) && \class_exists('\GatewayWorker\Lib\Context')) { + $connection->session = \GatewayWorker\Lib\Context::sessionEncode($_SESSION); + } + $_GET = $_SERVER = $_SESSION = $_COOKIE = array(); + } + + // blob or arraybuffer + if (empty($connection->websocketType)) { + $connection->websocketType = static::BINARY_TYPE_BLOB; + } + + $has_server_header = false; + + if (isset($connection->headers)) { + if (\is_array($connection->headers)) { + foreach ($connection->headers as $header) { + if (\stripos($header, 'Server:') === 0) { + $has_server_header = true; + } + $handshake_message .= "$header\r\n"; + } + } else { + if (\stripos($connection->headers, 'Server:') !== false) { + $has_server_header = true; + } + $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->context->websocketHandshake = true; + + // There are data waiting to be sent. + if (!empty($connection->context->tmpWebsocketData)) { + $connection->send($connection->context->tmpWebsocketData, true); + $connection->context->tmpWebsocketData = ''; + } + if (\strlen($buffer) > $header_length) { + return static::input(\substr($buffer, $header_length), $connection); + } + return 0; + } + // Bad websocket handshake request. + $connection->close("HTTP/1.1 200 WebSocket\r\nServer: workerman/" . Worker::VERSION . "\r\n\r\n

    WebSocket


    workerman/" . Worker::VERSION . "
    ", + true); + 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 100644 index 0000000..3db887e --- /dev/null +++ b/vendor/workerman/workerman/Protocols/Ws.php @@ -0,0 +1,432 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Workerman\Protocols; + +use Workerman\Worker; +use Workerman\Timer; +use Workerman\Connection\TcpConnection; +use Workerman\Connection\ConnectionInterface; + +/** + * 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, ConnectionInterface $connection) + { + if (empty($connection->context->handshakeStep)) { + Worker::safeEcho("recv data before handshake. Buffer:" . \bin2hex($buffer) . "\n"); + return false; + } + // Recv handshake response + if ($connection->context->handshakeStep === 1) { + return self::dealHandshake($buffer, $connection); + } + $recvLen = \strlen($buffer); + if ($recvLen < 2) { + return 0; + } + // Buffer websocket frame data. + if ($connection->context->websocketCurrentFrameLength) { + // We need more frame data. + if ($connection->context->websocketCurrentFrameLength > $recvLen) { + // 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]); + $dataLen = $secondbyte & 127; + $isFinFrame = $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: + // Blob type. + case 0x1: + // Arraybuffer type. + case 0x2: + // Ping package. + case 0x9: + // Pong package. + case 0xa: + break; + // Close package. + case 0x8: + // Try to emit onWebSocketClose callback. + if (isset($connection->onWebSocketClose)) { + try { + ($connection->onWebSocketClose)($connection); + } catch (\Throwable $e) { + Worker::stopAll(250, $e); + } + } // Close connection. + else { + $connection->close(); + } + return 0; + // Wrong opcode. + default : + Worker::safeEcho("error opcode $opcode and close websocket connection. Buffer:" . $buffer . "\n"); + $connection->close(); + return 0; + } + // Calculate packet length. + if ($dataLen === 126) { + if (\strlen($buffer) < 4) { + return 0; + } + $pack = \unpack('nn/ntotal_len', $buffer); + $currentFrameLength = $pack['total_len'] + 4; + } else if ($dataLen === 127) { + if (\strlen($buffer) < 10) { + return 0; + } + $arr = \unpack('n/N2c', $buffer); + $currentFrameLength = $arr['c1'] * 4294967296 + $arr['c2'] + 10; + } else { + $currentFrameLength = $dataLen + 2; + } + + $totalPackageSize = \strlen($connection->context->websocketDataBuffer) + $currentFrameLength; + if ($totalPackageSize > $connection->maxPackageSize) { + Worker::safeEcho("error package. package_length=$totalPackageSize\n"); + $connection->close(); + return 0; + } + + if ($isFinFrame) { + if ($opcode === 0x9) { + if ($recvLen >= $currentFrameLength) { + $pingData = static::decode(\substr($buffer, 0, $currentFrameLength), $connection); + $connection->consumeRecvBuffer($currentFrameLength); + $tmpConnectionType = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB; + $connection->websocketType = "\x8a"; + if (isset($connection->onWebSocketPing)) { + try { + ($connection->onWebSocketPing)($connection, $pingData); + } catch (\Throwable $e) { + Worker::stopAll(250, $e); + } + } else { + $connection->send($pingData); + } + $connection->websocketType = $tmpConnectionType; + if ($recvLen > $currentFrameLength) { + return static::input(\substr($buffer, $currentFrameLength), $connection); + } + } + return 0; + + } else if ($opcode === 0xa) { + if ($recvLen >= $currentFrameLength) { + $pongData = static::decode(\substr($buffer, 0, $currentFrameLength), $connection); + $connection->consumeRecvBuffer($currentFrameLength); + $tmpConnectionType = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB; + $connection->websocketType = "\x8a"; + // Try to emit onWebSocketPong callback. + if (isset($connection->onWebSocketPong)) { + try { + ($connection->onWebSocketPong)($connection, $pongData); + } catch (\Throwable $e) { + Worker::stopAll(250, $e); + } + } + $connection->websocketType = $tmpConnectionType; + if ($recvLen > $currentFrameLength) { + return static::input(\substr($buffer, $currentFrameLength), $connection); + } + } + return 0; + } + return $currentFrameLength; + } else { + $connection->context->websocketCurrentFrameLength = $currentFrameLength; + } + } + // Received just a frame length data. + if ($connection->context->websocketCurrentFrameLength === $recvLen) { + self::decode($buffer, $connection); + $connection->consumeRecvBuffer($connection->context->websocketCurrentFrameLength); + $connection->context->websocketCurrentFrameLength = 0; + return 0; + } // The length of the received data is greater than the length of a frame. + elseif ($connection->context->websocketCurrentFrameLength < $recvLen) { + self::decode(\substr($buffer, 0, $connection->context->websocketCurrentFrameLength), $connection); + $connection->consumeRecvBuffer($connection->context->websocketCurrentFrameLength); + $currentFrameLength = $connection->context->websocketCurrentFrameLength; + $connection->context->websocketCurrentFrameLength = 0; + // Continue to read next frame. + return self::input(\substr($buffer, $currentFrameLength), $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, ConnectionInterface $connection) + { + if (empty($connection->websocketType)) { + $connection->websocketType = self::BINARY_TYPE_BLOB; + } + $payload = (string)$payload; + if (empty($connection->context->handshakeStep)) { + static::sendHandshake($connection); + } + + $maskKey = "\x00\x00\x00\x00"; + $length = \strlen($payload); + + if (strlen($payload) < 126) { + $head = chr(0x80 | $length); + } elseif ($length < 0xFFFF) { + $head = chr(0x80 | 126) . pack("n", $length); + } else { + $head = chr(0x80 | 127) . pack("N", 0) . pack("N", $length); + } + + $frame = $connection->websocketType . $head . $maskKey; + // append payload to frame: + $maskKey = \str_repeat($maskKey, \floor($length / 4)) . \substr($maskKey, 0, $length % 4); + $frame .= $payload ^ $maskKey; + if ($connection->context->handshakeStep === 1) { + // If buffer has already full then discard the current package. + if (\strlen($connection->context->tmpWebsocketData) > $connection->maxSendBufferSize) { + if ($connection->onError) { + try { + ($connection->onError)($connection, WORKERMAN_SEND_FAIL, 'send buffer full and drop package'); + } catch (\Throwable $e) { + Worker::stopAll(250, $e); + } + } + return ''; + } + $connection->context->tmpWebsocketData = $connection->context->tmpWebsocketData . $frame; + // Check buffer is full. + if ($connection->maxSendBufferSize <= \strlen($connection->context->tmpWebsocketData)) { + if ($connection->onBufferFull) { + try { + ($connection->onBufferFull)($connection); + } catch (\Throwable $e) { + Worker::stopAll(250, $e); + } + } + } + return ''; + } + return $frame; + } + + /** + * Websocket decode. + * + * @param string $buffer + * @param ConnectionInterface $connection + * @return string + */ + public static function decode($bytes, ConnectionInterface $connection) + { + $dataLength = \ord($bytes[1]); + + if ($dataLength === 126) { + $decodedData = \substr($bytes, 4); + } else if ($dataLength === 127) { + $decodedData = \substr($bytes, 10); + } else { + $decodedData = \substr($bytes, 2); + } + if ($connection->context->websocketCurrentFrameLength) { + $connection->context->websocketDataBuffer .= $decodedData; + return $connection->context->websocketDataBuffer; + } else { + if ($connection->context->websocketDataBuffer !== '') { + $decodedData = $connection->context->websocketDataBuffer . $decodedData; + $connection->context->websocketDataBuffer = ''; + } + return $decodedData; + } + } + + /** + * Send websocket handshake data. + * + * @return void + */ + public static function onConnect($connection) + { + static::sendHandshake($connection); + } + + /** + * Clean + * + * @param TcpConnection $connection + */ + public static function onClose($connection) + { + $connection->context->handshakeStep = null; + $connection->context->websocketCurrentFrameLength = 0; + $connection->context->tmpWebsocketData = ''; + $connection->context->websocketDataBuffer = ''; + if (!empty($connection->context->websocketPingTimer)) { + Timer::del($connection->context->websocketPingTimer); + $connection->context->websocketPingTimer = null; + } + } + + /** + * Send websocket handshake. + * + * @param TcpConnection $connection + * @return void + */ + public static function sendHandshake(ConnectionInterface $connection) + { + if (!empty($connection->context->handshakeStep)) { + return; + } + // Get Host. + $port = $connection->getRemotePort(); + $host = $port === 80 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port; + // Handshake header. + $connection->context->websocketSecKey = \base64_encode(random_bytes(16)); + $userHeader = $connection->headers ?? null; + $userHeaderStr = ''; + if (!empty($userHeader)) { + if (\is_array($userHeader)) { + foreach ($userHeader as $k => $v) { + $userHeaderStr .= "$k: $v\r\n"; + } + } else { + $userHeaderStr .= $userHeader; + } + $userHeaderStr = "\r\n" . \trim($userHeaderStr); + } + $header = 'GET ' . $connection->getRemoteURI() . " HTTP/1.1\r\n" . + (!\preg_match("/\nHost:/i", $userHeaderStr) ? "Host: $host\r\n" : '') . + "Connection: Upgrade\r\n" . + "Upgrade: websocket\r\n" . + (isset($connection->websocketOrigin) ? "Origin: " . $connection->websocketOrigin . "\r\n" : '') . + (isset($connection->websocketClientProtocol) ? "Sec-WebSocket-Protocol: " . $connection->websocketClientProtocol . "\r\n" : '') . + "Sec-WebSocket-Version: 13\r\n" . + "Sec-WebSocket-Key: " . $connection->context->websocketSecKey . $userHeaderStr . "\r\n\r\n"; + $connection->send($header, true); + $connection->context->handshakeStep = 1; + $connection->context->websocketCurrentFrameLength = 0; + $connection->context->websocketDataBuffer = ''; + $connection->context->tmpWebsocketData = ''; + } + + /** + * Websocket handshake. + * + * @param string $buffer + * @param TcpConnection $connection + * @return int + */ + public static function dealHandshake($buffer, ConnectionInterface $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->context->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->websocketServerProtocol = \trim($match[1]); + } + + $connection->context->handshakeStep = 2; + $handshakeResponseLength = $pos + 4; + // Try to emit onWebSocketConnect callback. + if (isset($connection->onWebSocketConnect)) { + try { + ($connection->onWebSocketConnect)($connection, \substr($buffer, 0, $handshakeResponseLength)); + } catch (\Throwable $e) { + Worker::stopAll(250, $e); + } + } + // Headbeat. + if (!empty($connection->websocketPingInterval)) { + $connection->context->websocketPingTimer = Timer::add($connection->websocketPingInterval, function () use ($connection) { + if (false === $connection->send(\pack('H*', '898000000000'), true)) { + Timer::del($connection->context->websocketPingTimer); + $connection->context->websocketPingTimer = null; + } + }); + } + + $connection->consumeRecvBuffer($handshakeResponseLength); + if (!empty($connection->context->tmpWebsocketData)) { + $connection->send($connection->context->tmpWebsocketData, true); + $connection->context->tmpWebsocketData = ''; + } + if (\strlen($buffer) > $handshakeResponseLength) { + return self::input(\substr($buffer, $handshakeResponseLength), $connection); + } + } + return 0; + } + +} diff --git a/vendor/workerman/workerman/README.md b/vendor/workerman/workerman/README.md new file mode 100644 index 0000000..6038c02 --- /dev/null +++ b/vendor/workerman/workerman/README.md @@ -0,0 +1,342 @@ +# 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 to build fast and scalable network applications. +Workerman supports HTTP, Websocket, SSL and other custom protocols. +Workerman supports event extension. + +## Requires +PHP 7.0 or Higher +A POSIX compatible operating system (Linux, OSX, BSD) +POSIX and PCNTL extensions required +Event extension recommended for better performance + +## Installation + +``` +composer require workerman/workerman +``` + +## Basic Usage + +### A websocket server +```php +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 +count = 4; + +// Emitted when data received +$http_worker->onMessage = function ($connection, $request) { + //$request->get(); + //$request->post(); + //$request->header(); + //$request->cookie(); + //$request->session(); + //$request->uri(); + //$request->path(); + //$request->method(); + + // Send data to client + $connection->send("Hello World"); +}; + +// Run all workers +Worker::runAll(); +``` + +### A tcp server +```php +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 connection is closed +$tcp_worker->onClose = function ($connection) { + echo "Connection closed\n"; +}; + +Worker::runAll(); +``` + +### A udp server + +```php +count = 4; + +// Emitted when data received +$worker->onMessage = function($connection, $data) +{ + $connection->send($data); +}; + +Worker::runAll(); +``` + +### Enable SSL +```php + 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 +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 +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 +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(); +``` + + + +## 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](https://www.workerman.net) + +中文文档: [https://www.workerman.net/doc/workerman](https://www.workerman.net/doc/workerman) + +Documentation:[https://github.com/walkor/workerman-manual](https://github.com/walkor/workerman-manual/blob/master/english/SUMMARY.md) + +# Benchmarks +https://www.techempower.com/benchmarks/#section=data-r20&hw=ph&test=db&l=yyku7z-e7&a=2 +![image](https://user-images.githubusercontent.com/6073368/146704320-1559fe97-aa67-4ee3-95d6-61e341b3c93b.png) + +## Sponsors +[opencollective.com/walkor](https://opencollective.com/walkor) + +[patreon.com/walkor](https://patreon.com/walkor) + +## Donate + +
    + +## Other links with workerman + +[webman](https://github.com/walkor/webman) +[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) + +## LICENSE + +Workerman is released under the [MIT license](https://github.com/walkor/workerman/blob/master/MIT-LICENSE.txt). diff --git a/vendor/workerman/workerman/Timer.php b/vendor/workerman/workerman/Timer.php new file mode 100644 index 0000000..9f152f3 --- /dev/null +++ b/vendor/workerman/workerman/Timer.php @@ -0,0 +1,220 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Workerman; + +use Workerman\Events\EventInterface; +use Workerman\Worker; +use \Exception; + +/** + * Timer. + * + * example: + * Workerman\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 EventInterface + */ + protected static $_event = null; + + /** + * timer id + * + * @var int + */ + protected static $_timerId = 0; + + /** + * timer status + * [ + * timer_id1 => bool, + * timer_id2 => bool, + * ...................., + * ] + * + * @var array + */ + protected static $_status = array(); + + /** + * Init. + * + * @param EventInterface $event + * @return void + */ + public static function init($event = null) + { + if ($event) { + self::$_event = $event; + return; + } + 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|bool + */ + 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 ($args === null) { + $args = array(); + } + + if (self::$_event) { + return self::$_event->add($time_interval, + $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args); + } + + // If not workerman runtime just return. + if (!Worker::getAllWorkers()) { + return; + } + + if (!\is_callable($func)) { + Worker::safeEcho(new Exception("not callable")); + return false; + } + + if (empty(self::$_tasks)) { + \pcntl_alarm(1); + } + + $run_time = \time() + $time_interval; + if (!isset(self::$_tasks[$run_time])) { + self::$_tasks[$run_time] = array(); + } + + self::$_timerId = self::$_timerId == \PHP_INT_MAX ? 1 : ++self::$_timerId; + self::$_status[self::$_timerId] = true; + self::$_tasks[$run_time][self::$_timerId] = array($func, (array)$args, $persistent, $time_interval); + + return self::$_timerId; + } + + + /** + * 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 && !empty(self::$_status[$index])) { + $new_run_time = \time() + $time_interval; + if(!isset(self::$_tasks[$new_run_time])) self::$_tasks[$new_run_time] = array(); + self::$_tasks[$new_run_time][$index] = array($task_func, (array)$task_args, $persistent, $time_interval); + } + } + 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); + } + + foreach(self::$_tasks as $run_time => $task_data) + { + if(array_key_exists($timer_id, $task_data)) unset(self::$_tasks[$run_time][$timer_id]); + } + + if(array_key_exists($timer_id, self::$_status)) unset(self::$_status[$timer_id]); + + return true; + } + + /** + * Remove all timers. + * + * @return void + */ + public static function delAll() + { + self::$_tasks = self::$_status = array(); + if (\function_exists('pcntl_alarm')) { + \pcntl_alarm(0); + } + if (self::$_event) { + self::$_event->clearAllTimer(); + } + } +} diff --git a/vendor/workerman/workerman/Worker.php b/vendor/workerman/workerman/Worker.php new file mode 100644 index 0000000..860d728 --- /dev/null +++ b/vendor/workerman/workerman/Worker.php @@ -0,0 +1,2672 @@ + + * @copyright walkor + * @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 + */ +#[\AllowDynamicProperties] +class Worker +{ + /** + * Version. + * + * @var string + */ + const VERSION = '4.1.15'; + + /** + * 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; + + /** + * 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 callable + */ + public $onWorkerStart = null; + + /** + * Emitted when a socket connection is successfully established. + * + * @var callable + */ + public $onConnect = null; + + /** + * Emitted when data is received. + * + * @var callable + */ + public $onMessage = null; + + /** + * Emitted when the other end of the socket sends a FIN packet. + * + * @var callable + */ + public $onClose = null; + + /** + * Emitted when an error occurs with connection. + * + * @var callable + */ + public $onError = null; + + /** + * Emitted when the send buffer becomes full. + * + * @var callable + */ + public $onBufferFull = null; + + /** + * Emitted when the send buffer becomes empty. + * + * @var callable + */ + public $onBufferDrain = null; + + /** + * Emitted when worker processes stopped. + * + * @var callable + */ + public $onWorkerStop = null; + + /** + * Emitted when worker processes get reload signal. + * + * @var callable + */ + public $onWorkerReload = null; + + /** + * Emitted when worker processes exited. + * + * @var callable + */ + public $onWorkerExit = 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 = ''; + + /** + * The file used to store the master process status file. + * + * @var string + */ + public static $statusFile = ''; + + /** + * Log file. + * + * @var mixed + */ + public static $logFile = ''; + + /** + * Global event loop. + * + * @var EventInterface + */ + public static $globalEvent = null; + + /** + * Emitted when the master process get reload signal. + * + * @var callable + */ + public static $onMasterReload = null; + + /** + * Emitted when the master process terminated. + * + * @var callable + */ + public static $onMasterStop = null; + + /** + * EventLoopClass + * + * @var string + */ + public static $eventLoopClass = ''; + + /** + * Process title + * + * @var string + */ + public static $processTitle = 'WorkerMan'; + + /** + * After sending the stop command to the child process stopTimeout seconds, + * if the process is still living then forced to kill. + * + * @var int + */ + public static $stopTimeout = 2; + + /** + * 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 = ''; + + /** parse from _socketName avoid parse again in master or worker + * LocalSocket The format is like tcp://0.0.0.0:8080 + * @var string + */ + + protected $_localSocket=null; + + /** + * 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( + 'event' => '\Workerman\Events\Event', + 'libevent' => '\Workerman\Events\Libevent' + ); + + /** + * PHP built-in protocols. + * + * @var array + */ + protected static $_builtinTransports = array( + 'tcp' => 'tcp', + 'udp' => 'udp', + 'unix' => 'unix', + 'ssl' => 'tcp' + ); + + /** + * PHP built-in error types. + * + * @var array + */ + protected static $_errorType = array( + \E_ERROR => 'E_ERROR', // 1 + \E_WARNING => 'E_WARNING', // 2 + \E_PARSE => 'E_PARSE', // 4 + \E_NOTICE => 'E_NOTICE', // 8 + \E_CORE_ERROR => 'E_CORE_ERROR', // 16 + \E_CORE_WARNING => 'E_CORE_WARNING', // 32 + \E_COMPILE_ERROR => 'E_COMPILE_ERROR', // 64 + \E_COMPILE_WARNING => 'E_COMPILE_WARNING', // 128 + \E_USER_ERROR => 'E_USER_ERROR', // 256 + \E_USER_WARNING => 'E_USER_WARNING', // 512 + \E_USER_NOTICE => 'E_USER_NOTICE', // 1024 + \E_STRICT => 'E_STRICT', // 2048 + \E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', // 4096 + \E_DEPRECATED => 'E_DEPRECATED', // 8192 + \E_USER_DEPRECATED => 'E_USER_DEPRECATED' // 16384 + ); + + /** + * Graceful stop or not. + * + * @var bool + */ + 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::parseCommand(); + static::lock(); + static::daemonize(); + static::initWorkers(); + static::installSignal(); + static::saveMasterPid(); + static::lock(\LOCK_UN); + static::displayUI(); + static::forkWorkers(); + static::resetStd(); + static::monitorWorkers(); + } + + /** + * Check sapi. + * + * @return void + */ + protected static function checkSapiEnv() + { + // Only for cli and micro. + if (!in_array(\PHP_SAPI, ['cli', 'micro'])) { + 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(); + + // Process title. + static::setProcessTitle(static::$processTitle . ': master process start_file=' . static::$_startFile); + + // Init data for worker id. + static::initId(); + + // Timer init. + Timer::init(); + } + + /** + * Lock. + * + * @return void + */ + protected static function lock($flag = \LOCK_EX) + { + static $fd; + if (\DIRECTORY_SEPARATOR !== '/') { + return; + } + $lock_file = static::$pidFile . '.lock'; + $fd = $fd ?: \fopen($lock_file, 'a+'); + if ($fd) { + flock($fd, $flag); + if ($flag === \LOCK_UN) { + fclose($fd); + $fd = null; + clearstatcache(); + if (\is_file($lock_file)) { + unlink($lock_file); + } + } + } + } + + /** + * Init All worker instances. + * + * @return void + */ + protected static function initWorkers() + { + if (static::$_OS !== \OS_TYPE_LINUX) { + return; + } + + static::$_statisticsFile = static::$statusFile ? static::$statusFile : __DIR__ . '/../workerman-' .posix_getpid().'.status'; + + 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 = ' [OK] '; + + // Get column mapping for UI + foreach(static::getUiColumns() as $column_name => $prop){ + !isset($worker->{$prop}) && $worker->{$prop} = 'NNNN'; + $prop_length = \strlen((string) $worker->{$prop}); + $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength'; + static::$$key = \max(static::$$key, $prop_length); + } + + // Listen. + if (!$worker->reusePort) { + $worker->listen(); + } + } + } + + /** + * Reload all worker instances. + * + * @return void + */ + public static function reloadAllWorkers() + { + static::init(); + static::initWorkers(); + static::displayUI(); + static::$_status = static::STATUS_RELOADING; + } + + /** + * 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 < 1 ? 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'] ?? 'unknown'; + } + + /** + * 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; + $line_version .= \str_pad('Event-Loop:', 22, ' ', \STR_PAD_LEFT) . static::getEventLoopName() . \PHP_EOL; + !\defined('LINE_VERSIOIN_LENGTH') && \define('LINE_VERSIOIN_LENGTH', \strlen($line_version)); + $total_length = static::getSingleLineTotalLength(); + $line_one = '' . \str_pad(' WORKERMAN ', $total_length + \strlen(''), '-', \STR_PAD_BOTH) . ''. \PHP_EOL; + $line_two = \str_pad(' WORKERS ' , $total_length + \strlen(''), '-', \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.= "{$column_name}" . \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>||<\/w>||<\/g>)/is", (string) $worker->{$prop}, $matches); + $place_holder_length = !empty($matches) ? \strlen(\implode('', $matches[0])) : 0; + $content .= \str_pad((string) $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) { + $tmpArgv = $argv; + foreach ($tmpArgv as $index => $value) { + if ($value == '-d') { + unset($tmpArgv[$index]); + } elseif ($value == 'start' || $value == 'restart') { + $tmpArgv[$index] = 'stop'; + } + } + static::safeEcho("Input \"php ".implode(' ', $tmpArgv)."\" 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() + { + return array( + 'proto' => 'transport', + 'user' => 'user', + 'worker' => 'name', + 'socket' => 'socket', + 'processes' => 'count', + 'status' => 'status', + ); + } + + /** + * 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]; + $usage = "Usage: php yourfile [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"; + $available_commands = array( + 'start', + 'stop', + 'restart', + 'reload', + 'status', + 'connections', + ); + $available_mode = array( + '-d', + '-g' + ); + $command = $mode = ''; + foreach ($argv as $value) { + if (\in_array($value, $available_commands)) { + $command = $value; + } elseif (\in_array($value, $available_mode)) { + $mode = $value; + } + } + + if (!$command) { + exit($usage); + } + + // Start command. + $mode_str = ''; + if ($command === 'start') { + if ($mode === '-d' || static::$daemonize) { + $mode_str = 'in DAEMON mode'; + } else { + $mode_str = 'in DEBUG mode'; + } + } + static::log("Workerman[$start_file] $command $mode_str"); + + // Get master process PID. + $master_pid = \is_file(static::$pidFile) ? (int)\file_get_contents(static::$pidFile) : 0; + // Master is still alive? + if (static::checkMasterIsAlive($master_pid)) { + if ($command === 'start') { + static::log("Workerman[$start_file] already running"); + exit; + } + } elseif ($command !== 'start' && $command !== 'restart') { + static::log("Workerman[$start_file] not run"); + exit; + } + + $statistics_file = static::$statusFile ? static::$statusFile : __DIR__ . "/../workerman-$master_pid.status"; + + // execute command. + switch ($command) { + case 'start': + if ($mode === '-d') { + static::$daemonize = true; + } + break; + case 'status': + while (1) { + if (\is_file($statistics_file)) { + @\unlink($statistics_file); + } + // Master process will send SIGIOT signal to all child processes. + \posix_kill($master_pid, SIGIOT); + // Sleep 1 second. + \sleep(1); + // Clear terminal. + if ($mode === '-d') { + static::safeEcho("\33[H\33[2J\33(B\33[m", true); + } + // Echo status data. + static::safeEcho(static::formatStatusData($statistics_file)); + if ($mode !== '-d') { + exit(0); + } + static::safeEcho("\nPress Ctrl+C to quit.\n\n"); + } + exit(0); + case 'connections': + if (\is_file($statistics_file) && \is_writable($statistics_file)) { + \unlink($statistics_file); + } + // 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($statistics_file)) { + \readfile($statistics_file); + } + exit(0); + case 'restart': + case 'stop': + if ($mode === '-g') { + static::$_gracefulStop = true; + $sig = \SIGQUIT; + 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 = static::$stopTimeout + 3; + $start_time = \time(); + // Check master process is still alive? + while (1) { + $master_is_alive = $master_pid && \posix_kill((int) $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 ($mode === '-d') { + static::$daemonize = true; + } + break; + } + break; + case 'reload': + if($mode === '-g'){ + $sig = \SIGUSR2; + }else{ + $sig = \SIGUSR1; + } + \posix_kill($master_pid, $sig); + exit; + default : + if (isset($command)) { + static::safeEcho('Unknown command: ' . $command . "\n"); + } + exit($usage); + } + } + + /** + * Format status data. + * + * @param $statistics_file + * @return string + */ + protected static function formatStatusData($statistics_file) + { + static $total_request_cache = array(); + if (!\is_readable($statistics_file)) { + return ''; + } + $info = \file($statistics_file, \FILE_IGNORE_NEW_LINES); + if (!$info) { + return ''; + } + $status_str = ''; + $current_total_request = array(); + $workerInfo = []; + try { + $workerInfo = unserialize($info[0], ['allowed_classes' => false]); + } catch (Throwable $exception) {} + if (!is_array($workerInfo)) { + $workerInfo = []; + } + \ksort($workerInfo, 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($workerInfo 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; + } + $signalHandler = '\Workerman\Worker::signalHandler'; + // stop + \pcntl_signal(\SIGINT, $signalHandler, false); + // stop + \pcntl_signal(\SIGTERM, $signalHandler, false); + // stop + \pcntl_signal(\SIGHUP, $signalHandler, false); + // stop + \pcntl_signal(\SIGTSTP, $signalHandler, false); + // graceful stop + \pcntl_signal(\SIGQUIT, $signalHandler, false); + // reload + \pcntl_signal(\SIGUSR1, $signalHandler, false); + // graceful reload + \pcntl_signal(\SIGUSR2, $signalHandler, false); + // status + \pcntl_signal(\SIGIOT, $signalHandler, false); + // connection status + \pcntl_signal(\SIGIO, $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; + } + $signalHandler = '\Workerman\Worker::signalHandler'; + // uninstall stop signal handler + \pcntl_signal(\SIGINT, \SIG_IGN, false); + // uninstall stop signal handler + \pcntl_signal(\SIGTERM, \SIG_IGN, false); + // uninstall stop signal handler + \pcntl_signal(\SIGHUP, \SIG_IGN, false); + // uninstall stop signal handler + \pcntl_signal(\SIGTSTP, \SIG_IGN, false); + // uninstall graceful stop signal handler + \pcntl_signal(\SIGQUIT, \SIG_IGN, false); + // uninstall reload signal handler + \pcntl_signal(\SIGUSR1, \SIG_IGN, false); + // uninstall graceful reload signal handler + \pcntl_signal(\SIGUSR2, \SIG_IGN, false); + // uninstall status signal handler + \pcntl_signal(\SIGIOT, \SIG_IGN, false); + // uninstall connections status signal handler + \pcntl_signal(\SIGIO, \SIG_IGN, false); + // reinstall stop signal handler + static::$globalEvent->add(\SIGINT, EventInterface::EV_SIGNAL, $signalHandler); + // reinstall graceful stop signal handler + static::$globalEvent->add(\SIGQUIT, EventInterface::EV_SIGNAL, $signalHandler); + // reinstall graceful stop signal handler + static::$globalEvent->add(\SIGHUP, EventInterface::EV_SIGNAL, $signalHandler); + // reinstall graceful stop signal handler + static::$globalEvent->add(\SIGTSTP, EventInterface::EV_SIGNAL, $signalHandler); + // reinstall reload signal handler + static::$globalEvent->add(\SIGUSR1, EventInterface::EV_SIGNAL, $signalHandler); + // reinstall graceful reload signal handler + static::$globalEvent->add(\SIGUSR2, EventInterface::EV_SIGNAL, $signalHandler); + // reinstall status signal handler + static::$globalEvent->add(\SIGIOT, EventInterface::EV_SIGNAL, $signalHandler); + // reinstall connection status signal handler + static::$globalEvent->add(\SIGIO, EventInterface::EV_SIGNAL, $signalHandler); + } + + /** + * Signal handler. + * + * @param int $signal + */ + public static function signalHandler($signal) + { + switch ($signal) { + // Stop. + case \SIGINT: + case \SIGTERM: + case \SIGHUP: + case \SIGTSTP: + static::$_gracefulStop = false; + static::stopAll(); + break; + // Graceful stop. + case \SIGQUIT: + static::$_gracefulStop = true; + static::stopAll(); + break; + // Reload. + case \SIGUSR2: + case \SIGUSR1: + if (static::$_status === static::STATUS_SHUTDOWN || static::$_status === static::STATUS_RELOADING) { + return; + } + static::$_gracefulStop = $signal === \SIGUSR2; + static::$_pidsToRestart = static::getAllWorkerPids(); + static::reload(); + break; + // Show status. + case \SIGIOT: + static::writeStatisticsToStatusFile(); + break; + // Show connection status. + case \SIGIO: + static::writeConnectionsStatisticsToStatusFile(); + break; + } + } + + /** + * Run as daemon 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 || \DIRECTORY_SEPARATOR !== '/') { + return; + } + global $STDOUT, $STDERR; + $handle = \fopen(static::$stdoutFile, "a"); + if ($handle) { + unset($handle); + \set_error_handler(function(){}); + if ($STDOUT) { + \fclose($STDOUT); + } + if ($STDERR) { + \fclose($STDERR); + } + if (\is_resource(\STDOUT)) { + \fclose(\STDOUT); + } + if (\is_resource(\STDERR)) { + \fclose(\STDERR); + } + $STDOUT = \fopen(static::$stdoutFile, "a"); + $STDERR = \fopen(static::$stdoutFile, "a"); + // Fix standard output cannot redirect of PHP 8.1.8's bug + if (\function_exists('posix_isatty') && \posix_isatty(2)) { + \ob_start(function ($string) { + \file_put_contents(static::$stdoutFile, $string, FILE_APPEND); + }, 1); + } + // change output stream + static::$_outputStream = null; + static::outputStream($STDOUT); + \restore_error_handler(); + return; + } + + 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) { + static::$eventLoopClass = static::$_availableEventLoops[$loop_name]; + } else { + static::$eventLoopClass = '\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("@@@ See http://doc.workerman.net/faq/multi-woker-for-windows.html @@@\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); + + \Workerman\Timer::delAll(); + + //Update process state. + static::$_status = static::STATUS_RUNNING; + + // Register shutdown function for checking errors. + \register_shutdown_function([__CLASS__, 'checkErrors']); + + // Create a global event loop. + if (!static::$globalEvent) { + $eventLoopClass = static::getEventLoopName(); + static::$globalEvent = new $eventLoopClass; + } + + // Reinstall signal. + static::reinstallSignal(); + + // Init Timer. + Timer::init(static::$globalEvent); + + \restore_error_handler(); + + // Add an empty timer to prevent the event-loop from exiting. + Timer::add(1000000, function (){}); + + // Display UI. + static::safeEcho(\str_pad($worker->name, 48) . \str_pad($worker->getSocketName(), 36) . \str_pad('1', 10) . " [ok]\n"); + $worker->listen(); + $worker->run(); + static::$globalEvent->loop(); + if (static::$_status !== self::STATUS_SHUTDOWN) { + $err = new Exception('event-loop exited'); + static::log($err); + exit(250); + } + exit(0); + } + 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); + + $descriptorspec = array( + STDIN, STDOUT, STDOUT + ); + + $pipes = array(); + $process = \proc_open("php \"$start_file\" -q", $descriptorspec, $pipes); + + if (empty(static::$globalEvent)) { + static::$globalEvent = new Select(); + Timer::init(static::$globalEvent); + } + + // 保存子进程句柄 + static::$_processForWindows[$start_file] = array($process, $start_file); + } + + /** + * 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]; + $status = \proc_get_status($process); + if(isset($status['running'])) + { + if(!$status['running']) + { + static::safeEcho("process $start_file terminated and try to restart\n"); + \proc_close($process); + static::forkOneWorkerForWindows($start_file); + } + } + else + { + static::safeEcho("proc_get_status fail\n"); + } + } + } + + + /** + * Fork one worker process. + * + * @param self $worker + * @throws Exception + */ + protected static function forkOneWorkerForLinux(self $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(); + static::$_gracefulStop = false; + 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(); + //Update process state. + static::$_status = static::STATUS_RUNNING; + + // Register shutdown function for checking errors. + \register_shutdown_function(array("\\Workerman\\Worker", 'checkErrors')); + + // Create a global event loop. + if (!static::$globalEvent) { + $event_loop_class = static::getEventLoopName(); + static::$globalEvent = new $event_loop_class; + } + + // Reinstall signal. + static::reinstallSignal(); + + // Init Timer. + Timer::init(static::$globalEvent); + + \restore_error_handler(); + + static::setProcessTitle(self::$processTitle . ': worker process ' . $worker->name . ' ' . $worker->getSocketName()); + $worker->setUserAndGroup(); + $worker->id = $id; + $worker->run(); + // Main loop. + static::$globalEvent->loop(); + if (strpos(static::$eventLoopClass, 'Workerman\Events\Swoole') !== false) { + exit(0); + } + $err = new Exception('event-loop exited'); + static::log($err); + exit(250); + } else { + throw new Exception("forkOneWorker fail"); + } + } + + /** + * Get worker id. + * + * @param string $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 exists"); + 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 exists"); + 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 which worker process exited. + foreach (static::$_pidMap as $worker_id => $worker_pid_array) { + if (isset($worker_pid_array[$pid])) { + $worker = static::$_workers[$worker_id]; + // Fix exit with status 2 for php8.2 + if ($status === \SIGINT && static::$_status === static::STATUS_SHUTDOWN) { + $status = 0; + } + // Exit status. + if ($status !== 0) { + static::log("worker[{$worker->name}:$pid] exit with status $status"); + } + + // onWorkerExit + if ($worker->onWorkerExit) { + try { + ($worker->onWorkerExit)($worker, $status, $pid); + } catch (\Throwable $exception) { + static::log("worker[{$worker->name}] onWorkerExit $exception"); + } + } + + // 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); + $address = substr($address, strpos($address, '/') + 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()) { + if (static::$_gracefulStop) { + $sig = \SIGUSR2; + } else { + $sig = \SIGUSR1; + } + // 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::stopAll(250, $e); + } catch (\Error $e) { + static::stopAll(250, $e); + } + static::initId(); + } + + // 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::$stopTimeout seconds try to kill it. + if(!static::$_gracefulStop){ + Timer::add(static::$stopTimeout, '\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::stopAll(250, $e); + } catch (\Error $e) { + static::stopAll(250, $e); + } + } + + if ($worker->reloadable) { + static::stopAll(); + } + } + } + + /** + * Stop all. + * + * @param int $code + * @param string $log + */ + public static function stopAll($code = 0, $log = '') + { + if ($log) { + static::log($log); + } + + static::$_status = static::STATUS_SHUTDOWN; + // For master process. + if (\DIRECTORY_SEPARATOR === '/' && 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 = \SIGQUIT; + } else { + $sig = \SIGINT; + } + foreach ($worker_pid_array as $worker_pid) { + if (static::$daemonize) { + \posix_kill($worker_pid, $sig); + } else { + Timer::add(1, '\posix_kill', array($worker_pid, $sig), false); + } + if(!static::$_gracefulStop){ + Timer::add(static::$stopTimeout, '\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. + $workers = array_reverse(static::$_workers); + foreach ($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(); + } + + try { + exit($code); + } catch (Exception $e) { + + } + } + } + } + + /** + * 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 bool + */ + 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, \serialize($all_worker_info)."\n", \FILE_APPEND); + $loadavg = \function_exists('sys_getloadavg') ? \array_map('round', \sys_getloadavg(), array(2,2,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, \SIGIOT); + } + return; + } + + // For child processes. + \gc_collect_cycles(); + if (\function_exists('gc_mem_caches')) { + \gc_mem_caches(); + } + \reset(static::$_workers); + /** @var \Workerman\Worker $worker */ + $worker = current(static::$_workers); + $worker_status_str = \posix_getpid() . "\t" . \str_pad(round(memory_get_usage(false) / (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) + { + if(isset(self::$_errorType[$type])) { + return self::$_errorType[$type]; + } + + 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 string $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('', '', ''), array($line, $white, $green), $msg); + $msg = \str_replace(array('', '', ''), $end, $msg); + } elseif (!static::$_outputDecorated) { + return false; + } + \fwrite($stream, $msg); + \fflush($stream); + return true; + } + + /** + * @param resource|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) { + return false; + } + 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 = '', array $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']); + Autoloader::setRootPath($this->_autoloadRootPath); + + // 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); + } + + // Turn reusePort on. + /*if (static::$_OS === \OS_TYPE_LINUX // if linux + && \version_compare(\PHP_VERSION,'7.0.0', 'ge') // if php >= 7.0.0 + && \version_compare(php_uname('r'), '3.9', 'ge') // if kernel >=3.9 + && \strtolower(\php_uname('s')) !== 'darwin' // if not Mac OS + && strpos($socket_name,'unix') !== 0) { // if not unix socket + + $this->reusePort = true; + }*/ + } + + + /** + * Listen. + * + * @throws Exception + */ + public function listen() + { + if (!$this->_socketName) { + return; + } + + // Autoload. + Autoloader::setRootPath($this->_autoloadRootPath); + + if (!$this->_mainSocket) { + + $local_socket = $this->parseSocketAddress(); + + // 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') { + $socket_file = \substr($local_socket, 7); + if ($this->user) { + \chown($socket_file, $this->user); + } + if ($this->group) { + \chgrp($socket_file, $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, false); + } + + $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; + } + } + + /** + * Parse local socket address. + * + * @throws Exception + */ + protected function parseSocketAddress() { + if (!$this->_socketName) { + return; + } + // 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 + return static::$_builtinTransports[$this->transport] . ":" . $address; + } + + /** + * 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 + * @throws Exception + */ + public function run() + { + $this->listen(); + + // Try to emit onWorkerStart callback. + if ($this->onWorkerStart) { + try { + \call_user_func($this->onWorkerStart, $this); + } catch (\Exception $e) { + // Avoid rapid infinite loop exit. + sleep(1); + static::stopAll(250, $e); + } catch (\Error $e) { + // Avoid rapid infinite loop exit. + sleep(1); + static::stopAll(250, $e); + } + } + } + + /** + * 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::stopAll(250, $e); + } catch (\Error $e) { + static::stopAll(250, $e); + } + } + // Remove listener for server socket. + $this->unlisten(); + // Close all connections for the worker. + if (!static::$_gracefulStop) { + foreach ($this->connections as $connection) { + $connection->close(); + } + } + // Remove worker. + foreach(static::$_workers as $key => $one_worker) { + if ($one_worker->workerId === $this->workerId) { + unset(static::$_workers[$key]); + } + } + + // 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::stopAll(250, $e); + } catch (\Error $e) { + static::stopAll(250, $e); + } + } + } + + /** + * 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 ($parser && \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::stopAll(250, $e); + } catch (\Error $e) { + static::stopAll(250, $e); + } + } + return true; + } + + /** + * Check master process is alive + * + * @param int $master_pid + * @return bool + */ + protected static function checkMasterIsAlive($master_pid) + { + if (empty($master_pid)) { + return false; + } + + $master_is_alive = $master_pid && \posix_kill((int) $master_pid, 0) && \posix_getpid() !== $master_pid; + if (!$master_is_alive) { + return false; + } + + $cmdline = "/proc/{$master_pid}/cmdline"; + if (!is_readable($cmdline) || empty(static::$processTitle)) { + return true; + } + + $content = file_get_contents($cmdline); + if (empty($content)) { + return true; + } + + return stripos($content, static::$processTitle) !== false || stripos($content, 'php') !== false; + } +} + diff --git a/vendor/workerman/workerman/composer.json b/vendor/workerman/workerman/composer.json new file mode 100644 index 0000000..19e2984 --- /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://doc.workerman.net/", + "source": "https://github.com/walkor/workerman" + }, + "require": { + "php": ">=7.0" + }, + "suggest": { + "ext-event": "For better performance. " + }, + "autoload": { + "psr-4": { + "Workerman\\": "./" + } + }, + "minimum-stability": "dev" +} diff --git a/vendor/yzh52521/easyhttp/LICENSE b/vendor/yzh52521/easyhttp/LICENSE new file mode 100644 index 0000000..8cc32f9 --- /dev/null +++ b/vendor/yzh52521/easyhttp/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014-present rap2hpoutre + +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/yzh52521/easyhttp/README.md b/vendor/yzh52521/easyhttp/README.md new file mode 100644 index 0000000..17354cd --- /dev/null +++ b/vendor/yzh52521/easyhttp/README.md @@ -0,0 +1,415 @@ +EasyHttp 是一个轻量级、语义化、对IDE友好的HTTP客户端,支持常见的HTTP请求、异步请求和并发请求,让你可以快速地使用 HTTP 请求与其他 Web 应用进行通信。 + +> EasyHttp并不强制依赖于cURL,如果没有安装cURL,EasyHttp会自动选择使用PHP流处理,或者你也可以提供自己的发送HTTP请求的处理方式。 + +如果您觉得EasyHttp对您有用的话,别忘了给点个赞哦^_^ ! + +github:[github.com/yzh52521/easyhttp](https://github.com/yzh52521/easyhttp "github.com/yzh52521/easyhttp") + +gitee:[gitee.com/yzh52521/easyhttp](https://gitee.com/yzh52521/easyhttp "gitee.com/yzh52521/easyhttp") + +本包是基于 [ gouguoyin/easyhttp ](https://gitee.com/gouguoyin/easyhttp "gitee.com/gouguoyin/easyhttp") 进行扩展开发,主要实现了以下扩展: + +1. 增加 retry() 重试机制。 +2. 增加 debug 日志调试功能。 +3. 增加 withHost 指定服务端base_url +4. 增加 withBody 发送原始数据(Raw)请求 +5. 增加 withMiddleware/withRequestMiddleware/withResponseMiddleware Guzzle 中间件 +6. 增加 connectTimeout 设置等待服务器响应超时 +7. 增加 sink 响应的主体部分将要保存的位置 +8. 增加 maxRedirects 请求的重定向行为最大次数 + + +# 安装说明 + +#### 环境依赖 + +- PHP >= 7.2.5 +- 如果使用PHP流处理,allow_url_fopen 必须在php.ini中启用。 +- 如果使用cURL处理,cURL >= 7.19.4,并且编译了OpenSSL 与 zlib。 + +#### 一键安装 + + composer require yzh52521/easyhttp + +## 发起请求 + +#### 同步请求 + +###### 常规请求 + +```php +$response = Http::get('http://httpbin.org/get'); + +$response = Http::get('http://httpbin.org/get?name=yzh52521'); + +$response = Http::get('http://httpbin.org/get?name=yzh52521', ['age' => 18]); + +$response = Http::post('http://httpbin.org/post'); + +$response = Http::post('http://httpbin.org/post', ['name' => 'yzh52521']); + +$response = Http::patch(...); + +$response = Http::put(...); + +$response = Http::delete(...); + +$response = Http::head(...); + +$response = Http::options(...); + +``` + +###### 指定服务端base_url的请求 + +```php +// 指定服务端base_url地址,最终请求地址为 https://serv.yzh52521.com/login +$response = Http::withHost('https://serv.yzh52521.com')->post('/login'); + +``` +##### 发送原始数据(Raw)请求 +```php +$response = Http::withBody( + base64_encode($photo), 'image/jpeg' +)->post(...); +``` +###### 发送 Content-Type 编码请求 + +```php +// application/x-www-form-urlencoded(默认) +$response = Http::asForm()->post(...); + +// application/json +$response = Http::asJson()->post(...); +``` + +###### 发送 Multipart 表单请求 + +```php +$response = Http::asMultipart( + 'file_input_name', file_get_contents('photo1.jpg'), 'photo2.jpg' +)->post('http://test.com/attachments'); + +$response = Http::asMultipart( + 'file_input_name', fopen('photo1.jpg', 'r'), 'photo2.jpg' +)->post(...); + +$response = Http::attach( + 'file_input_name', file_get_contents('photo1.jpg'), 'photo2.jpg' +)->post(...); + +$response = Http::attach( + 'file_input_name', fopen('photo1.jpg', 'r'), 'photo2.jpg' +)->post(...); +``` +> 表单enctype属性需要设置成 multipart/form-data + +###### 携带请求头的请求 + +```php +$response = Http::withHeaders([ + 'x-powered-by' => 'yzh52521' +])->post(...); +``` + +###### 携带重定向的请求 + +```php +// 默认 +$response = Http::withRedirect(false)->post(...); + +$response = Http::withRedirect([ + 'max' => 5, + 'strict' => false, + 'referer' => true, + 'protocols' => ['http', 'https'], + 'track_redirects' => false +])->post(...); + +$response = Http::maxRedirects(5)->post(...); +``` + + + +###### 携带认证的请求 + +```php +// Basic认证 +$response = Http::withBasicAuth('username', 'password')->post(...); + +// Digest认证(需要被HTTP服务器支持) +$response = Http::withDigestAuth('username', 'password')->post(...); +``` + +###### 携带 User-Agent 的请求 +```php +$response = Http::withUA('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36')->post(...); +``` + +###### 携带Token令牌的请求 + +```php +$response = Http::withToken('token')->post(...); +``` + +###### 携带认证文件的请求 + +```php +$response = Http::withCert('/path/server.pem', 'password')->post(...); +``` + +###### 携带SSL证书的请求 + +```php +// 默认 +$response = Http::withVerify(false)->post(...); + +$response = Http::withVerify('/path/to/cert.pem')->post(...); +``` + +###### 携带COOKIE的请求 + +```php +$response = Http::withCookies(array $cookies, string $domain)->post(...); +``` + +###### 携带协议版本的请求 + +```php +$response = Http::withVersion(1.1)->post(...); +``` + +###### 携带代理的请求 + +```php +$response = Http::withProxy('tcp://localhost:8125')->post(...); + +$response = Http::withProxy([ + 'http' => 'tcp://localhost:8125', // Use this proxy with "http" + 'https' => 'tcp://localhost:9124', // Use this proxy with "https", + 'no' => ['.com.cn', 'yzh52521.cn'] // Don't use a proxy with these +])->post(...); +``` + +###### 设置超时时间(单位秒) + +```php +$response = Http::timeout(60)->post(...); +``` + +###### 设置等待服务器响应超时的最大值(单位秒) + +```php +$response = Http::connectTimeout(60)->post(...); +``` + +###### 设置延迟时间(单位秒) + +```php +$response = Http::delay(60)->post(...); +``` + +###### 设置并发次数 + +```php +$response = Http::concurrency(10)->promise(...); +``` + +###### 重发请求,设置retry方法。重试次数/两次重试之间的时间间隔(毫秒): + +```php +$response = Http::retry(3, 100)->post(...); +``` +##### 响应的主体部分将要保存的位置 +```php +$response = Http::sink('/path/to/file')->post(...); +``` + +#### Guzzle 中间件 + +```php +use GuzzleHttp\Middleware; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +$response = Http::withMiddleware( + Middleware::mapRequest(function (RequestInterface $request) { + $request = $request->withHeader('X-Example', 'Value'); + return $request; + }) +)->get('http://example.com'); + +……………… +$response = Http::withRequestMiddleware( + function (RequestInterface $request) { + $request = $request->withHeader('X-Example', 'Value'); + return $request; + } +)->get('http://example.com'); + +……………… + +$response = Http::withResponseMiddleware( + function (RequestInterface $response) { + $response = $response->getHeader('X-Example'); + return $response; + } +)->get('http://example.com'); +``` + +#### 异步请求 + +```php +use yzh52521\EasyHttp\Response; +use yzh52521\EasyHttp\RequestException; + +$promise = Http::getAsync('http://easyhttp.yzh52521.cn/api/sleep3.json', ['token' => TOKEN], function (Response $response) { + echo '异步请求成功,响应内容:' . $response->body() . PHP_EOL; +}, function (RequestException $e) { + echo '异步请求异常,错误码:' . $e->getCode() . ',错误信息:' . $e->getMessage() . PHP_EOL; +}); + +$promise->wait(); +echo json_encode(['code' => 200, 'msg' => '请求成功'], JSON_UNESCAPED_UNICODE) . PHP_EOL; + +//输出 +{"code":200,"msg":"请求成功"} +异步请求成功,响应内容:{"code":200,"msg":"success","second":3} + +$promise = Http::getAsync('http1://easyhttp.yzh52521.cn/api/sleep3.json', function (Response $response) { + echo '异步请求成功,响应内容:' . $response->body() . PHP_EOL; +}, function (RequestException $e) { + echo '异步请求异常,错误信息:' . $e->getMessage() . PHP_EOL; +}); + +$promise->wait(); +echo json_encode(['code' => 200, 'msg' => '请求成功'], JSON_UNESCAPED_UNICODE) . PHP_EOL; + +//输出 +{"code":200,"msg":"请求成功"} +异步请求异常,错误信息:cURL error 1: Protocol "http1" not supported or disabled in libcurl (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) + +Http::postAsync(...); + +Http::patchAsync(...); + +Http::putAsync(...); + +Http::deleteAsync(...); + +Http::headAsync(...); + +Http::optionsAsync(...); + +使用 等待异步回调处理完成 +Http::wait(); +``` + +#### 异步并发请求 + +```php +use yzh52521\EasyHttp\Response; +use yzh52521\EasyHttp\RequestException; + +$promises = [ + Http::getAsync('http://easyhttp.yzh52521.cn/api/sleep3.json'), + Http::getAsync('http1://easyhttp.yzh52521.cn/api/sleep1.json', ['name' => 'yzh52521']), + Http::postAsync('http://easyhttp.yzh52521.cn/api/sleep2.json', ['name' => 'yzh52521']), +]; + +$pool=Http::concurrency(10)->multiAsync($promises, function (Response $response, $index) { + echo "发起第 $index 个异步请求,请求时长:" . $response->json()->second . '秒' . PHP_EOL; +}, function (RequestException $e, $index) { + echo "发起第 $index 个请求失败,失败原因:" . $e->getMessage() . PHP_EOL; +}); + +$promise = $pool->promise(); +$promise->wait(); + +//输出 +发起第 1 个请求失败,失败原因:cURL error 1: Protocol "http1" not supported or disabled in libcurl (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) +发起第 2 个异步请求,请求时长:2 秒 +发起第 0 个异步请求,请求时长:3 秒 +``` +> 如果未调用concurrency()方法,并发次数默认为$promises的元素个数,$promises数组里必须是异步请求 + +## 使用响应 + +发起请求后会返回一个 yzh52521\EasyHttp\Response $response的实例,该实例提供了以下方法来检查请求的响应: + +```php +$response->body() : string; +$response->json() : object; +$response->array() : array; +$response->status() : int; +$response->ok() : bool; +$response->successful() : bool; +$response->serverError() : bool; +$response->clientError() : bool; +$response->headers() : array; +$response->header($header) : string; +``` + +## 异常处理 + +请求在发生客户端或服务端错误时会抛出 yzh52521\EasyHttp\RequestException $e异常,该实例提供了以下方法来返回异常信息: + +```php +$e->getCode() : int; +$e->getMessage() : string; +$e->getFile() : string; +$e->getLine() : int; +$e->getTrace() : array; +$e->getTraceAsString() : string; +``` +## 调试日志 + +有时候难免要对 Http 的请求和响应包体进行记录以方便查找问题或做什么 + +```php +//传递一个日志类 thinkphp \think\facade\Log laravel Illuminate\Support\Facades\Log +Http::debug(Log::class)->post(...); +``` + + +## 更新日志 +### 2023-08-31 +* 新增 withBody 可以发送原始数据(Raw)请求 +* 新增 withMiddleware/withRequestMiddleware/withResponseMiddleware 支持Guzzle中间件 +* 新增 connectTimeout 设置等待服务器响应超时 +* 新增 sink 响应的主体部分将要保存的位置 +* 新增 maxRedirects 请求的重定向行为最大次数 +### 2022-05-11 +* 新增removeBodyFormat() 用于withOptions 指定body时,清除原由的bodyFromat +### 2022-05-10 +* 新增发送原生请求的方法client() +* 新增发送原生异步请求的方法clientASync() +### 2021-09-03 +* 新增 debug() 调试日志 +* 新增 retry() 重试机制 +* 修复header重叠的bug +### 2020-03-30 +* 修复部分情况下IDE不能智能提示的BUG +* get()、getAsync()方法支持带参数的url +* 新增withUA()方法 +* 新增withStream()方法 +* 新增asMultipart()方法,attach()的别名 +* 新增multiAsync()异步并发请求方法 + +### 2020-03-20 +* 新增异步请求getAsync()方法 +* 新增异步请求postAsync()方法 +* 新增异步请求patchAsync()方法 +* 新增异步请求putAsync()方法 +* 新增异步请求deleteAsync()方法 +* 新增异步请求headAsync()方法 +* 新增异步请求optionsAsync()方法 + +## Todo List + - [x] 异步请求 + - [x] 并发请求 + - [x] 重试机制 + - [ ] 支持http2 + - [ ] 支持swoole +# easyhttp diff --git a/vendor/yzh52521/easyhttp/composer.json b/vendor/yzh52521/easyhttp/composer.json new file mode 100644 index 0000000..862a47f --- /dev/null +++ b/vendor/yzh52521/easyhttp/composer.json @@ -0,0 +1,33 @@ +{ + "name": "yzh52521/easyhttp", + "description": "EasyHttp 是一个轻量级、语义化、对IDE友好的HTTP客户端,支持常见的HTTP请求、异步请求和并发请求,让你可以快速地使用 HTTP 请求与其他 Web 应用进行通信。", + "license": "MIT", + "keywords": [ + "easyhttp", + "EasyHttp", + "php-http", + "phphttp", + "easy-http", + "php", + "http", + "curl" + ], + "homepage": "https://github.com/yzh52521/easyhttp", + "authors": [ + { + "name": "yzh52521", + "email": "396751927@qq.com" + } + ], + "require": { + "php": ">=7.2.5", + "guzzlehttp/guzzle": "^6.0|^7.0", + "psr/log":"^1.0|^2.0|^3.0" + }, + "autoload": { + "psr-4": { + "yzh52521\\EasyHttp\\": "src/" + } + }, + "minimum-stability": "stable" +} diff --git a/vendor/yzh52521/easyhttp/src/ConnectionException.php b/vendor/yzh52521/easyhttp/src/ConnectionException.php new file mode 100644 index 0000000..cdbf363 --- /dev/null +++ b/vendor/yzh52521/easyhttp/src/ConnectionException.php @@ -0,0 +1,10 @@ +facade = new $this->facade; + } + + public function __call($name, $params) + { + if (method_exists($this->facade, 'removeOptions')) { + call_user_func_array([$this->facade, 'removeOptions'], []); + } + return call_user_func_array([$this->facade, $name], $params); + } + + public static function __callStatic($name, $params) + { + return call_user_func_array([new static(), $name], $params); + } +} diff --git a/vendor/yzh52521/easyhttp/src/Http.php b/vendor/yzh52521/easyhttp/src/Http.php new file mode 100644 index 0000000..4685e05 --- /dev/null +++ b/vendor/yzh52521/easyhttp/src/Http.php @@ -0,0 +1,64 @@ +setLogger($logger); + $this->setFormatter($formatter ?: $this->getDefaultFormatter()); + } + + /** + * Returns the default formatter; + * + * @return MessageFormatter + */ + protected function getDefaultFormatter() + { + return new MessageFormatter(); + } + + /** + * Sets whether requests should be logged before the response is received. + * + * @param boolean $logRequests + */ + public function setRequestLoggingEnabled($logRequests = true) + { + $this->logRequests = (bool) $logRequests; + } + + /** + * Sets the logger, which can be a PSR-3 logger or a callable that accepts + * a log level, message, and array context. + * + * @param LoggerInterface|callable $logger + * + * @throws InvalidArgumentException + */ + public function setLogger($logger) + { + if ($logger instanceof LoggerInterface || is_callable($logger)) { + $this->logger = $logger; + } else { + throw new InvalidArgumentException( + "Logger has to be a Psr\Log\LoggerInterface or callable" + ); + } + } + + /** + * Sets the formatter, which can be a MessageFormatter or callable that + * accepts a request, response, and a reason if an error has occurred. + * + * @param MessageFormatter|callable $formatter + * + * @throws InvalidArgumentException + */ + public function setFormatter($formatter) + { + if ($formatter instanceof MessageFormatter || is_callable($formatter)) { + $this->formatter = $formatter; + } else { + throw new InvalidArgumentException( + "Formatter has to be a \GuzzleHttp\MessageFormatter or callable" + ); + } + } + + /** + * Sets the log level to use, which can be either a string or a callable + * that accepts a response (which could be null). A log level could also + * be null, which indicates that the default log level should be used. + * + * @param string|callable|null + */ + public function setLogLevel($logLevel) + { + $this->logLevel = $logLevel; + } + + /** + * Logs a request and/or a response. + * + * @param RequestInterface $request + * @param ResponseInterface|null $response + * @param $reason + * @return mixed + */ + protected function log( + RequestInterface $request, + ResponseInterface $response = null, + $reason = null + ) { + if ($reason instanceof RequestException) { + $response = $reason->getResponse(); + } + + $level = $this->getLogLevel($response); + $message = $this->getLogMessage($request, $response, $reason); + $context = compact('request', 'response', 'reason'); + + // Make sure that the content of the body is available again. + if ($response) { + $response->getBody()->seek(0);; + } + + if (is_callable($this->logger)) { + return call_user_func($this->logger, $level, $message, $context); + } + + $this->logger->log($level, $message, $context); + } + + /** + * Formats a request and response as a log message. + * + * @param RequestInterface $request + * @param ResponseInterface|null $response + * @param mixed $reason + * + * @return string The formatted message. + */ + protected function getLogMessage( + RequestInterface $request, + ResponseInterface $response = null, + $reason = null + ) { + if ($this->formatter instanceof MessageFormatter) { + return $this->formatter->format( + $request, + $response, + $reason + ); + } + + return call_user_func($this->formatter, $request, $response, $reason); + } + + /** + * Returns a log level for a given response. + * + * @param ResponseInterface $response The response being logged. + * + * @return string LogLevel + */ + protected function getLogLevel(ResponseInterface $response = null) + { + if ( ! $this->logLevel) { + return $this->getDefaultLogLevel($response); + } + + if (is_callable($this->logLevel)) { + return call_user_func($this->logLevel, $response); + } + + return (string) $this->logLevel; + } + + /** + * Returns the default log level for a response. + * + * @param ResponseInterface $response + * + * @return string LogLevel + */ + protected function getDefaultLogLevel(ResponseInterface $response = null) { + if ($response && $response->getStatusCode() >= 300) { + return LogLevel::NOTICE; + } + + return LogLevel::INFO; + } + + /** + * Returns a function which is handled when a request was successful. + * + * @param RequestInterface $request + * + * @return \Closure + */ + protected function onSuccess(RequestInterface $request) + { + return function ($response) use ($request) { + $this->log($request, $response); + return $response; + }; + } + + /** + * Returns a function which is handled when a request was rejected. + * + * @param RequestInterface $request + * + * @return \Closure + */ + protected function onFailure(RequestInterface $request) + { + return function ($reason) use ($request) { + + // Only log a rejected request if it hasn't already been logged. + if ( ! $this->logRequests) { + $this->log($request, null, $reason); + } + + return Promise\rejection_for($reason); + }; + } + + /** + * Called when the middleware is handled by the client. + * + * @param callable $handler + * + * @return \Closure + */ + public function __invoke(callable $handler) + { + return function ($request, array $options) use ($handler) { + + // Only log requests if explicitly set to do so + if ($this->logRequests) { + $this->log($request); + } + + return $handler($request, $options)->then( + $this->onSuccess($request), + $this->onFailure($request) + ); + }; + } +} diff --git a/vendor/yzh52521/easyhttp/src/Request.php b/vendor/yzh52521/easyhttp/src/Request.php new file mode 100644 index 0000000..a84df3d --- /dev/null +++ b/vendor/yzh52521/easyhttp/src/Request.php @@ -0,0 +1,711 @@ +client = $this->getInstance(); + + $this->bodyFormat = 'form_params'; + $this->options = [ + 'http_errors' => false, + ]; + $this->handlerStack = HandlerStack::create(new CurlHandler()); + } + + /** + * Request destructor. + */ + public function __destruct() + { + + } + + /** + * 获取单例 + * @return mixed + */ + public function getInstance() + { + $name = get_called_class(); + + if (!isset(self::$instances[$name])) { + self::$instances[$name] = new Client(); + } + + return self::$instances[$name]; + } + + public function removeOptions() + { + $this->bodyFormat = 'form_params'; + $this->isRemoveBodyFormat = false; + $this->options = [ + 'http_errors' => false, + 'verify' => false + ]; + return $this; + } + + public function asForm() + { + $this->bodyFormat = 'form_params'; + $this->withHeaders(['Content-Type' => 'application/x-www-form-urlencoded']); + + return $this; + } + + public function asJson() + { + $this->bodyFormat = 'json'; + $this->withHeaders(['Content-Type' => 'application/json']); + + return $this; + } + + public function asMultipart(string $name, string $contents, string $filename = null, array $headers = []) + { + $this->bodyFormat = 'multipart'; + + $this->options = array_filter([ + 'name' => $name, + 'contents' => $contents, + 'headers' => $headers, + 'filename' => $filename, + ]); + + return $this; + } + + public function withMiddleware(callable $middleware) + { + $this->handlerStack->push($middleware); + + $this->options['handler'] = $this->handlerStack; + + return $this; + } + + public function withRequestMiddleware(callable $middleware) + { + $this->handlerStack->push(Middleware::mapRequest($middleware)); + + $this->options['handler'] = $this->handlerStack; + + return $this; + } + + public function withResponseMiddleware(callable $middleware) + { + $this->handlerStack->push(Middleware::mapResponse($middleware)); + + $this->options['handler'] = $this->handlerStack; + + return $this; + } + + public function withHost(string $host) + { + $this->options['base_uri'] = $host; + + return $this; + } + + public function withOptions(array $options) + { + unset($this->options[$this->bodyFormat], $this->options['body']); + + $this->options = array_merge_recursive($this->options, $options); + + return $this; + } + + public function withCert(string $path, string $password) + { + $this->options['cert'] = [$path, $password]; + + return $this; + } + + public function withHeaders(array $headers) + { + $this->options = array_merge_recursive($this->options, [ + 'headers' => $headers, + ]); + + return $this; + } + + public function withBody($content, $contentType = 'application/json') + { + $this->bodyFormat = 'body'; + + $this->options['headers']['Content-Type'] = $contentType; + + $this->pendingBody = $content; + + return $this; + } + + public function withBasicAuth(string $username, string $password) + { + $this->options['auth'] = [$username, $password]; + + return $this; + } + + public function withDigestAuth(string $username, string $password) + { + $this->options['auth'] = [$username, $password, 'digest']; + + return $this; + } + + public function withUA(string $ua) + { + $this->options['headers']['User-Agent'] = trim($ua); + + return $this; + } + + public function withToken(string $token, string $type = 'Bearer') + { + $this->options['headers']['Authorization'] = trim($type . ' ' . $token); + + return $this; + } + + public function withCookies(array $cookies, string $domain) + { + $this->options = array_merge_recursive($this->options, [ + 'cookies' => CookieJar::fromArray($cookies, $domain), + ]); + + return $this; + } + + public function withProxy($proxy) + { + $this->options['proxy'] = $proxy; + + return $this; + } + + public function withVersion($version) + { + $this->options['version'] = $version; + + return $this; + } + + public function maxRedirects(int $max) + { + $this->options['allow_redirects']['max'] = $max; + + return $this; + } + + public function withRedirect($redirect = false) + { + $this->options['allow_redirects'] = $redirect; + + return $this; + } + + public function withVerify($verify = false) + { + $this->options['verify'] = $verify; + + return $this; + } + + public function withStream($boolean = false) + { + $this->options['stream'] = $boolean; + + return $this; + } + + public function concurrency(int $times) + { + $this->concurrency = $times; + + return $this; + } + + public function retry(int $retries = 1, int $sleep = 0) + { + $this->handlerStack->push((new Retry())->handle($retries, $sleep)); + + $this->options['handler'] = $this->handlerStack; + + return $this; + } + + public function delay(int $seconds) + { + $this->options['delay'] = $seconds * 1000; + + return $this; + } + + public function timeout(float $seconds) + { + $this->options['timeout'] = $seconds; + + return $this; + } + + public function connectTimeout(float $seconds) + { + $this->options['connect_timeout'] = $seconds; + + return $this; + } + + /** + * @param string|resource $to + * @return $this + */ + public function sink($to) + { + $this->options['sink'] = $to; + + return $this; + } + + public function removeBodyFormat() + { + $this->isRemoveBodyFormat = true; + return $this; + } + + public function debug($class) + { + $logger = new Logger(function ($level, $message, array $context) use ($class) { + $class::log($level, $message); + }, function ($request, $response, $reason) { + $requestBody = $request->getBody(); + $requestBody->rewind(); + + //请求头 + $requestHeaders = []; + + foreach ((array)$request->getHeaders() as $k => $vs) { + foreach ($vs as $v) { + $requestHeaders[] = "$k: $v"; + } + } + + //响应头 + $responseHeaders = []; + + foreach ((array)$response->getHeaders() as $k => $vs) { + foreach ($vs as $v) { + $responseHeaders[] = "$k: $v"; + } + } + + $uri = $request->getUri(); + $path = $uri->getPath(); + + if ($query = $uri->getQuery()) { + $path .= '?' . $query; + } + + return sprintf( + "Request %s\n%s %s HTTP/%s\r\n%s\r\n\r\n%s\r\n--------------------\r\nHTTP/%s %s %s\r\n%s\r\n\r\n%s", + $uri, + $request->getMethod(), + $path, + $request->getProtocolVersion(), + join("\r\n", $requestHeaders), + $requestBody->getContents(), + $response->getProtocolVersion(), + $response->getStatusCode(), + $response->getReasonPhrase(), + join("\r\n", $responseHeaders), + $response->getBody()->getContents() + ); + }); + $this->handlerStack->push($logger); + $this->options['handler'] = $this->handlerStack; + + return $this; + } + + public function attach(string $name, string $contents, string $filename = null, array $headers = []) + { + $this->options['multipart'] = array_filter([ + 'name' => $name, + 'contents' => $contents, + 'headers' => $headers, + 'filename' => $filename, + ]); + + return $this; + } + + public function get(string $url, array $query = []) + { + $params = parse_url($url, PHP_URL_QUERY); + + parse_str($params ?: '', $result); + + $this->options['query'] = array_merge($result, $query); + + return $this->request('GET', $url, $query); + } + + public function post(string $url, array $data = []) + { + $this->options[$this->bodyFormat] = $data; + + return $this->request('POST', $url, $data); + } + + public function patch(string $url, array $data = []) + { + $this->options[$this->bodyFormat] = $data; + + return $this->request('PATCH', $url, $data); + } + + public function put(string $url, array $data = []) + { + $this->options[$this->bodyFormat] = $data; + + return $this->request('PUT', $url, $data); + } + + public function delete(string $url, array $data = []) + { + $this->options[$this->bodyFormat] = $data; + + return $this->request('DELETE', $url, $data); + } + + public function head(string $url, array $data = []) + { + $this->options[$this->bodyFormat] = $data; + + return $this->request('HEAD', $url, $data); + } + + public function options(string $url, array $data = []) + { + $this->options[$this->bodyFormat] = $data; + + return $this->request('OPTIONS', $url, $data); + } + + public function getAsync(string $url, $query = null, callable $success = null, callable $fail = null) + { + is_callable($query) || $this->options['query'] = $query; + + return $this->requestAsync('GET', $url, $query, $success, $fail); + } + + public function postAsync(string $url, $data = null, callable $success = null, callable $fail = null) + { + is_callable($data) || $this->options[$this->bodyFormat] = $data; + + return $this->requestAsync('POST', $url, $data, $success, $fail); + } + + public function patchAsync(string $url, $data = null, callable $success = null, callable $fail = null) + { + is_callable($data) || $this->options[$this->bodyFormat] = $data; + + return $this->requestAsync('PATCH', $url, $data, $success, $fail); + } + + public function putAsync(string $url, $data = null, callable $success = null, callable $fail = null) + { + is_callable($data) || $this->options[$this->bodyFormat] = $data; + + return $this->requestAsync('PUT', $url, $data, $success, $fail); + } + + public function deleteAsync(string $url, $data = null, callable $success = null, callable $fail = null) + { + is_callable($data) || $this->options[$this->bodyFormat] = $data; + + return $this->requestAsync('DELETE', $url, $data, $success, $fail); + } + + public function headAsync(string $url, $data = null, callable $success = null, callable $fail = null) + { + is_callable($data) || $this->options[$this->bodyFormat] = $data; + + return $this->requestAsync('HEAD', $url, $data, $success, $fail); + } + + public function optionsAsync(string $url, $data = null, callable $success = null, callable $fail = null) + { + is_callable($data) || $this->options[$this->bodyFormat] = $data; + + return $this->requestAsync('OPTIONS', $url, $data, $success, $fail); + } + + public function multiAsync(array $promises, callable $success = null, callable $fail = null) + { + $count = count($promises); + + $this->concurrency = $this->concurrency ?: $count; + + $requests = function () use ($promises) { + foreach ($promises as $promise) { + yield function () use ($promise) { + return $promise; + }; + } + }; + + $fulfilled = function ($response, $index) use ($success) { + if (!is_null($success)) { + $response = $this->response($response); + call_user_func_array($success, [$response, $index]); + } + }; + + $rejected = function ($exception, $index) use ($fail) { + if (!is_null($fail)) { + $exception = $this->exception($exception); + call_user_func_array($fail, [$exception, $index]); + } + }; + + $pool = new Pool($this->client, $requests(), [ + 'concurrency' => $this->concurrency, + 'fulfilled' => $fulfilled, + 'rejected' => $rejected, + ]); + + $pool->promise(); + + return $pool; + } + + protected function request(string $method, string $url, array $options = []) + { + if (isset($this->options[$this->bodyFormat])) { + $this->options[$this->bodyFormat] = $options; + } else { + $this->options[$this->bodyFormat] = $this->pendingBody; + } + if ($this->isRemoveBodyFormat) { + unset($this->options[$this->bodyFormat]); + } + try { + $response = $this->client->request($method, $url, $this->options); + return $this->response($response); + } catch (ConnectException $e) { + throw new ConnectionException($e->getMessage(), 0, $e); + } + } + + /** + * 原生请求 + * @param string $method + * @param string $url + * @param array $options + * @return Response + * @throws ConnectionException + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function client(string $method, string $url, array $options = []) + { + if (isset($this->options[$this->bodyFormat])) { + $this->options[$this->bodyFormat] = $options; + } else { + $this->options[$this->bodyFormat] = $this->pendingBody; + } + if ($this->isRemoveBodyFormat) { + unset($this->options[$this->bodyFormat]); + } + try { + if (empty($options)) { + $options = $this->options; + } + $response = $this->client->request($method, $url, $options); + return $this->response($response); + } catch (ConnectException $e) { + throw new ConnectionException($e->getMessage(), 0, $e); + } + } + + /** + * 原生异步请求 + * @param string $method + * @param string $url + * @param array $options + * @return Response + * @throws ConnectionException + */ + public function clientAsync(string $method, string $url, array $options = []) + { + if (isset($this->options[$this->bodyFormat])) { + $this->options[$this->bodyFormat] = $options; + } else { + $this->options[$this->bodyFormat] = $this->pendingBody; + } + if ($this->isRemoveBodyFormat) { + unset($this->options[$this->bodyFormat]); + } + try { + if (empty($options)) { + $options = $this->options; + } + $response = $this->client->requestAsync($method, $url, $options); + return $this->response($response); + } catch (ConnectException $e) { + throw new ConnectionException($e->getMessage(), 0, $e); + } + } + + + protected function requestAsync(string $method, string $url, $options = null, callable $success = null, callable $fail = null) + { + if (is_callable($options)) { + $successCallback = $options; + $failCallback = $success; + } else { + $successCallback = $success; + $failCallback = $fail; + } + + if (isset($this->options[$this->bodyFormat])) { + $this->options[$this->bodyFormat] = $options; + } else { + $this->options[$this->bodyFormat] = $this->pendingBody; + } + + if ($this->isRemoveBodyFormat) { + unset($this->options[$this->bodyFormat]); + } + + try { + $promise = $this->client->requestAsync($method, $url, $this->options); + + $fulfilled = function ($response) use ($successCallback) { + if (!is_null($successCallback)) { + $response = $this->response($response); + call_user_func_array($successCallback, [$response]); + } + }; + + $rejected = function ($exception) use ($failCallback) { + if (!is_null($failCallback)) { + $exception = $this->exception($exception); + call_user_func_array($failCallback, [$exception]); + } + }; + + $promise->then($fulfilled, $rejected); + + $this->promises[] = $promise; + + return $promise; + } catch (ConnectException $e) { + throw new ConnectionException($e->getMessage(), 0, $e); + } + } + + public function wait() + { + if (!empty($this->promises)) { + \GuzzleHttp\Promise\Utils($this->promises)->wait(); + } + $this->promises = []; + } + + protected function response($response) + { + return new Response($response); + } + + protected function exception($exception) + { + return new RequestException($exception); + } + +} diff --git a/vendor/yzh52521/easyhttp/src/RequestException.php b/vendor/yzh52521/easyhttp/src/RequestException.php new file mode 100644 index 0000000..29b1dca --- /dev/null +++ b/vendor/yzh52521/easyhttp/src/RequestException.php @@ -0,0 +1,43 @@ +exception = $exception; + } + + public function getCode() + { + return $this->exception->getCode(); + } + + public function getMessage() + { + return $this->exception->getMessage(); + } + + public function getFile() + { + return $this->exception->getFile(); + } + + public function getLine() + { + return $this->exception->getLine(); + } + + public function getTrace() + { + return $this->exception->getTrace(); + } + + public function getTraceAsString() + { + return $this->exception->getTraceAsString(); + } +} diff --git a/vendor/yzh52521/easyhttp/src/Response.php b/vendor/yzh52521/easyhttp/src/Response.php new file mode 100644 index 0000000..73d7f02 --- /dev/null +++ b/vendor/yzh52521/easyhttp/src/Response.php @@ -0,0 +1,212 @@ +response = $response; + } + + /** + * Get the body of the response. + * @return string + */ + public function body() + { + return (string)$this->response->getBody(); + } + + /** + * Get the Array decoded body of the response. + * @return array|mixed + */ + public function array() + { + if (!$this->decoded) { + $this->decoded = json_decode( (string)$this->response->getBody(),true ); + } + + return $this->decoded; + } + + /** + * Get the JSON decoded body of the response. + * @return object|mixed + */ + public function json() + { + if (!$this->decoded) { + $this->decoded = json_decode( (string)$this->response->getBody() ); + } + + return $this->decoded; + } + + /** + * Get a header from the response. + * @param string $header + * @return mixed + */ + public function header(string $header) + { + return $this->response->getHeaderLine( $header ); + } + + /** + * Get the headers from the response. + * @return mixed + */ + public function headers() + { + return $this->mapWithKeys( $this->response->getHeaders(),function ($v,$k) { + return [$k => $v]; + } )->response; + } + + /** + * Get the status code of the response. + * @return int + */ + public function status() + { + return (int)$this->response->getStatusCode(); + } + + /** + * Determine if the request was successful. + * @return bool + */ + public function successful() + { + return $this->status() >= 200 && $this->status() < 300; + } + + /** + * Determine if the response code was "OK". + * @return bool + */ + public function ok() + { + return $this->status() === 200; + } + + /** + * Determine if the response was a redirect. + * @return bool + */ + public function redirect() + { + return $this->status() >= 300 && $this->status() < 400; + } + + /** + * Determine if the response indicates a client error occurred. + * @return bool + */ + public function clientError() + { + return $this->status() >= 400 && $this->status() < 500; + } + + /** + * Determine if the response indicates a server error occurred. + * @return bool + */ + public function serverError() + { + return $this->status() >= 500; + } + + /** + * Determine if the given offset exists. + * + * @param string $offset + * @return mixed + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) + { + return array_key_exists( $offset,$this->json() ); + } + + /** + * Get the value for a given offset. + * + * @param string $offset + * @return mixed + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->json()[$offset]; + } + + /** + * Set the value at the given offset. + * + * @param string $offset + * @param mixed $value + * @return void + * + * @throws \LogicException + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset,$value) + { + throw new LogicException( 'Response data may not be mutated using array access.' ); + } + + /** + * Unset the value at the given offset. + * + * @param string $offset + * @return void + * + * @throws \LogicException + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset) + { + throw new LogicException( 'Response data may not be mutated using array access.' ); + } + + /** + * Get the body of the response. + * + * @return string + */ + public function __toString() + { + return $this->body(); + } + + protected function mapWithKeys($items,callable $callback) + { + $result = []; + + foreach ( $items as $key => $value ) { + $assoc = $callback( $value,$key ); + + foreach ( $assoc as $mapKey => $mapValue ) { + $result[$mapKey] = $mapValue; + } + } + + return new static( $result ); + } + +} diff --git a/vendor/yzh52521/easyhttp/src/Retry.php b/vendor/yzh52521/easyhttp/src/Retry.php new file mode 100644 index 0000000..e9982a7 --- /dev/null +++ b/vendor/yzh52521/easyhttp/src/Retry.php @@ -0,0 +1,46 @@ +decider($retries), $this->delay($sleep)); + } + + protected function decider(int $times) + { + return function ( + $retries, + Request $request, + Response $response = null, + RequestException $exception = null + ) use ($times) { + // 超过最大重试次数,不再重试 + if ($retries >= $times) { + return false; + } + return $exception instanceof ConnectException || $exception instanceof ServerException || ($response && $response->getStatusCode() >= 500); + }; + } + + /** + * 返回一个匿名函数,该匿名函数返回下次重试的时间(毫秒) + * @param int $retry_delay + * @return \Closure + */ + protected function delay(int $retry_delay) + { + return function ($retries) use ($retry_delay) { + return $retry_delay * $retries; + }; + } +} diff --git a/webmaninit/.gitignore b/webmaninit/.gitignore new file mode 100644 index 0000000..516299c --- /dev/null +++ b/webmaninit/.gitignore @@ -0,0 +1,8 @@ +/runtime +/.idea +/.vscode +/vendor +*.log +.env +/tests/tmp +/tests/.phpunit.result.cache diff --git a/windows.bat b/windows.bat new file mode 100644 index 0000000..f07ce53 --- /dev/null +++ b/windows.bat @@ -0,0 +1,3 @@ +CHCP 65001 +php windows.php +pause \ No newline at end of file diff --git a/windows.php b/windows.php new file mode 100644 index 0000000..21fa888 --- /dev/null +++ b/windows.php @@ -0,0 +1,118 @@ +load(); + } else { + Dotenv::createMutable(base_path())->load(); + } +} + +App::loadAllConfig(['route']); + +$errorReporting = config('app.error_reporting'); +if (isset($errorReporting)) { + error_reporting($errorReporting); +} + +$runtimeProcessPath = runtime_path() . DIRECTORY_SEPARATOR . '/windows'; +if (!is_dir($runtimeProcessPath)) { + mkdir($runtimeProcessPath); +} +$processFiles = [ + __DIR__ . DIRECTORY_SEPARATOR . 'start.php' +]; +foreach (config('process', []) as $processName => $config) { + $processFiles[] = write_process_file($runtimeProcessPath, $processName, ''); +} + +foreach (config('plugin', []) as $firm => $projects) { + foreach ($projects as $name => $project) { + if (!is_array($project)) { + continue; + } + foreach ($project['process'] ?? [] as $processName => $config) { + $processFiles[] = write_process_file($runtimeProcessPath, $processName, "$firm.$name"); + } + } + foreach ($projects['process'] ?? [] as $processName => $config) { + $processFiles[] = write_process_file($runtimeProcessPath, $processName, $firm); + } +} + +function write_process_file($runtimeProcessPath, $processName, $firm): string +{ + $processParam = $firm ? "plugin.$firm.$processName" : $processName; + $configParam = $firm ? "config('plugin.$firm.process')['$processName']" : "config('process')['$processName']"; + $fileContent = << true]); + if (!$resource) { + exit("Can not execute $cmd\r\n"); + } + return $resource; +} + +$resource = popen_processes($processFiles); +echo "\r\n"; +while (1) { + sleep(1); + if (!empty($monitor) && $monitor->checkAllFilesChange()) { + $status = proc_get_status($resource); + $pid = $status['pid']; + shell_exec("taskkill /F /T /PID $pid"); + proc_close($resource); + $resource = popen_processes($processFiles); + } +}